changeset 0:2c24602be78f

Initial import from lwtools 3.0.1 version, with new hand built build system and file reorganization
author lost@l-w.ca
date Wed, 19 Jan 2011 22:27:17 -0700
parents
children 96c4dc89016e
files 00README.txt COPYING Makefile docs/README docs/internals.txt docs/lwasm.txt docs/manual.docbook.sgml docs/manual/README docs/readme-4.0.txt lwar/add.c lwar/extract.c lwar/list.c lwar/lwar.c lwar/lwar.h lwar/main.c lwar/remove.c lwar/replace.c lwar/rules.make lwar/util.c lwar/util.h lwasm/debug.c lwasm/input.c lwasm/input.h lwasm/insn_bitbit.c lwasm/insn_gen.c lwasm/insn_indexed.c lwasm/insn_inh.c lwasm/insn_logicmem.c lwasm/insn_rel.c lwasm/insn_rlist.c lwasm/insn_rtor.c lwasm/insn_tfm.c lwasm/instab.c lwasm/instab.h lwasm/list.c lwasm/lwasm.c lwasm/lwasm.h lwasm/macro.c lwasm/main.c lwasm/os9.c lwasm/output.c lwasm/pass1.c lwasm/pass2.c lwasm/pass3.c lwasm/pass4.c lwasm/pass5.c lwasm/pass6.c lwasm/pass7.c lwasm/pragma.c lwasm/pseudo.c lwasm/rules.make lwasm/section.c lwasm/struct.c lwasm/symbol.c lwlib/lw_alloc.c lwlib/lw_alloc.h lwlib/lw_error.c lwlib/lw_error.h lwlib/lw_expr.c lwlib/lw_expr.h lwlib/lw_stack.c lwlib/lw_stack.h lwlib/lw_string.c lwlib/lw_string.h lwlib/lw_stringlist.c lwlib/lw_stringlist.h lwlib/rules.make lwlink/expr.c lwlink/expr.h lwlink/link.c lwlink/lwlink.c lwlink/lwlink.h lwlink/main.c lwlink/map.c lwlink/objdump.c lwlink/output.c lwlink/readfiles.c lwlink/rules.make lwlink/script.c lwlink/util.c lwlink/util.h
diffstat 81 files changed, 17769 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/00README.txt	Wed Jan 19 22:27:17 2011 -0700
@@ -0,0 +1,9 @@
+This is LWTOOLS, a cross development system targetting the 6809 CPU.
+
+It consists of an assembler, lwasm, a linker, lwlink, and an archiver,
+lwar which should compile on any reasonably modern POSIX environment.
+
+To see if a quick build will work, just type "make". If it works, you're
+ready to go ahead with "make install". This will install in /usr/local/bin.
+
+See docs/ for additional information.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/COPYING	Wed Jan 19 22:27:17 2011 -0700
@@ -0,0 +1,674 @@
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+  The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works.  By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.  We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors.  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
+them 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 prevent others from denying you
+these rights or asking you to surrender the rights.  Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received.  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.
+
+  Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+  For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software.  For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+  Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so.  This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software.  The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable.  Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products.  If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+  Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary.  To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                       TERMS AND CONDITIONS
+
+  0. Definitions.
+
+  "This License" refers to version 3 of the GNU General Public License.
+
+  "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+  "The Program" refers to any copyrightable work licensed under this
+License.  Each licensee is addressed as "you".  "Licensees" and
+"recipients" may be individuals or organizations.
+
+  To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy.  The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+  A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+  To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy.  Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+  To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies.  Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+  An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License.  If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+  1. Source Code.
+
+  The "source code" for a work means the preferred form of the work
+for making modifications to it.  "Object code" means any non-source
+form of a work.
+
+  A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+  The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form.  A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+  The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities.  However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work.  For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+  The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+  The Corresponding Source for a work in source code form is that
+same work.
+
+  2. Basic Permissions.
+
+  All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met.  This License explicitly affirms your unlimited
+permission to run the unmodified Program.  The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work.  This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+  You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force.  You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright.  Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+  Conveying under any other circumstances is permitted solely under
+the conditions stated below.  Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+  3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+  No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+  When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+  4. Conveying Verbatim Copies.
+
+  You may convey 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;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+  You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+  5. Conveying Modified Source Versions.
+
+  You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+    a) The work must carry prominent notices stating that you modified
+    it, and giving a relevant date.
+
+    b) The work must carry prominent notices stating that it is
+    released under this License and any conditions added under section
+    7.  This requirement modifies the requirement in section 4 to
+    "keep intact all notices".
+
+    c) You must license the entire work, as a whole, under this
+    License to anyone who comes into possession of a copy.  This
+    License will therefore apply, along with any applicable section 7
+    additional terms, to the whole of the work, and all its parts,
+    regardless of how they are packaged.  This License gives no
+    permission to license the work in any other way, but it does not
+    invalidate such permission if you have separately received it.
+
+    d) If the work has interactive user interfaces, each must display
+    Appropriate Legal Notices; however, if the Program has interactive
+    interfaces that do not display Appropriate Legal Notices, your
+    work need not make them do so.
+
+  A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit.  Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+  6. Conveying Non-Source Forms.
+
+  You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+    a) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by the
+    Corresponding Source fixed on a durable physical medium
+    customarily used for software interchange.
+
+    b) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by a
+    written offer, valid for at least three years and valid for as
+    long as you offer spare parts or customer support for that product
+    model, to give anyone who possesses the object code either (1) a
+    copy of the Corresponding Source for all the software in the
+    product that is covered by this License, on a durable physical
+    medium customarily used for software interchange, for a price no
+    more than your reasonable cost of physically performing this
+    conveying of source, or (2) access to copy the
+    Corresponding Source from a network server at no charge.
+
+    c) Convey individual copies of the object code with a copy of the
+    written offer to provide the Corresponding Source.  This
+    alternative is allowed only occasionally and noncommercially, and
+    only if you received the object code with such an offer, in accord
+    with subsection 6b.
+
+    d) Convey the object code by offering access from a designated
+    place (gratis or for a charge), and offer equivalent access to the
+    Corresponding Source in the same way through the same place at no
+    further charge.  You need not require recipients to copy the
+    Corresponding Source along with the object code.  If the place to
+    copy the object code is a network server, the Corresponding Source
+    may be on a different server (operated by you or a third party)
+    that supports equivalent copying facilities, provided you maintain
+    clear directions next to the object code saying where to find the
+    Corresponding Source.  Regardless of what server hosts the
+    Corresponding Source, you remain obligated to ensure that it is
+    available for as long as needed to satisfy these requirements.
+
+    e) Convey the object code using peer-to-peer transmission, provided
+    you inform other peers where the object code and Corresponding
+    Source of the work are being offered to the general public at no
+    charge under subsection 6d.
+
+  A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+  A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling.  In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage.  For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product.  A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+  "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source.  The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+  If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information.  But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+  The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed.  Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+  Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+  7. Additional Terms.
+
+  "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law.  If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+  When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it.  (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.)  You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+  Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+    a) Disclaiming warranty or limiting liability differently from the
+    terms of sections 15 and 16 of this License; or
+
+    b) Requiring preservation of specified reasonable legal notices or
+    author attributions in that material or in the Appropriate Legal
+    Notices displayed by works containing it; or
+
+    c) Prohibiting misrepresentation of the origin of that material, or
+    requiring that modified versions of such material be marked in
+    reasonable ways as different from the original version; or
+
+    d) Limiting the use for publicity purposes of names of licensors or
+    authors of the material; or
+
+    e) Declining to grant rights under trademark law for use of some
+    trade names, trademarks, or service marks; or
+
+    f) Requiring indemnification of licensors and authors of that
+    material by anyone who conveys the material (or modified versions of
+    it) with contractual assumptions of liability to the recipient, for
+    any liability that these contractual assumptions directly impose on
+    those licensors and authors.
+
+  All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10.  If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term.  If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+  If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+  Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+  8. Termination.
+
+  You may not propagate or modify a covered work except as expressly
+provided under this License.  Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+  However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+  Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+  Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License.  If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+  9. Acceptance Not Required for Having Copies.
+
+  You are not required to accept this License in order to receive or
+run a copy of the Program.  Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance.  However,
+nothing other than this License grants you permission to propagate or
+modify any covered work.  These actions infringe copyright if you do
+not accept this License.  Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+  10. Automatic Licensing of Downstream Recipients.
+
+  Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License.  You are not responsible
+for enforcing compliance by third parties with this License.
+
+  An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations.  If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+  You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License.  For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+  11. Patents.
+
+  A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based.  The
+work thus licensed is called the contributor's "contributor version".
+
+  A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version.  For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+  Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+  In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement).  To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+  If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients.  "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+  If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+  A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License.  You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+  Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+  12. No Surrender of Others' Freedom.
+
+  If 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 convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all.  For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+  13. Use with the GNU Affero General Public License.
+
+  Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work.  The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+  14. Revised Versions of this License.
+
+  The Free Software Foundation may publish revised and/or new versions of
+the GNU 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 that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation.  If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+  If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+  Later license versions may give you additional or different
+permissions.  However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+  15. Disclaimer of Warranty.
+
+  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.
+
+  16. Limitation of Liability.
+
+  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+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.
+
+  17. Interpretation of Sections 15 and 16.
+
+  If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+                     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
+state 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) <year>  <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 3 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, see <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+  If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+    <program>  Copyright (C) <year>  <name of author>
+    This program 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, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+  You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<http://www.gnu.org/licenses/>.
+
+  The GNU 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 Lesser General
+Public License instead of this License.  But first, please read
+<http://www.gnu.org/philosophy/why-not-lgpl.html>.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Makefile	Wed Jan 19 22:27:17 2011 -0700
@@ -0,0 +1,70 @@
+CC := gcc
+
+CPPFLAGS += -I lwlib -DPACKAGE_STRING='"lwtools 4.0-pre"' -DPACKAGE_BUGREPORT='"lost@l-w.ca"'
+
+LDFLAGS += -L$(PWD)/lwlib -llw
+
+MAIN_TARGETS := lwasm/lwasm lwlink/lwlink lwar/lwar
+
+.PHONY: all
+all: $(MAIN_TARGETS)
+
+subdirs := lwasm lwlink lwar lwlib
+
+-include $(subdirs:=/rules.make)
+
+lwasm_objs := $(lwasm_srcs:.c=.o)
+lwlink_objs := $(lwlink_srcs:.c=.o)
+lwar_objs := $(lwar_srcs:.c=.o)
+lwlib_objs := $(lwlib_srcs:.c=.o)
+
+lwasm_deps := $(lwasm_srcs:.c=.d)
+lwlink_deps := $(lwlink_srcs:.c=.d)
+lwar_deps := $(lwar_srcs:.c=.d)
+lwlib_deps := $(lwlib_srcs:.c=.d)
+lwobjdump_deps := $(lwobjdump_srcs:.c=.d)
+
+,PHONY: lwlink lwasm lwar
+lwlink: lwlink/lwlink
+lwasm: lwasm/lwasm
+lwar: lwar/lwar
+lwobjdump: lwlink/lwobjdump
+
+lwasm/lwasm: $(lwasm_objs) lwlib
+	$(CC) -o $@ $(lwasm_objs) $(LDFLAGS)
+
+lwlink/lwlink: $(lwlink_objs)
+	$(CC) -o $@ $(lwlink_objs)
+
+lwlink/lwobjdump: $(lwobjdump_objs)
+	$(CC) -o $@ $(lwobjdump_objs)
+
+lwar/lwar: $(lwar_objs)
+	$(CC) -o $@ $(lwar_objs)
+
+
+.phony: lwlib
+lwlib: lwlib/liblw.a
+
+lwlib/liblw.a: $(lwlib_objs)
+	$(AR) rc $@ $^
+
+%.d: %.c
+	@echo "Building dependencies for $@"
+	@$(CC) -MM $(CPPFLAGS) -o $*.d $<
+	@mv -f $*.d $*.d.tmp
+	@sed -e 's|.*:|$*.o $*.d:|' < $*.d.tmp > $*.d
+	@sed -e 's/.*://' -e 's/\\$$//' < $*.d.tmp | fmt -1 | sed -e 's/^ *//' -e 's/$$/:/' >> $*.d
+	@rm -f $*.d.tmp
+
+-include $(lwasm_deps) $(lwlink_deps) $(lwar_deps) $(lwlib_deps) $(lwobjdump_deps)
+
+extra_clean := $(extra_clean) *~ */*~
+
+.PHONY: clean
+clean:
+	rm -f $(lwasm_deps) $(lwlink_deps) $(lwar_deps) $(lwlib_deps) $(lwobjdump_deps)
+	rm -f lwlib/liblw.a lwasm/lwasm lwlink/lwlink lwlink/lwobjdump lwar/lwar
+	rm -f $(lwasm_objs) $(lwlink_objs) $(lwar_objs) $(lwlib_objs) $(lwobjdump_objs)
+	rm -f $(extra_clean)
+	
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/docs/README	Wed Jan 19 22:27:17 2011 -0700
@@ -0,0 +1,16 @@
+If there are no html files in the "manual" directory and there is no
+"manual.html" file, it means that you have either checked out the source
+repository on a non-release branch or the packager messed up.
+
+In either case, if you have "docbook2html" installed, you should be able
+to build the manual with one of the following:
+
+docbook2html -o manual manual.docbook.sgml
+
+or
+
+docbook2html -u manual.docbook.sgml && mv manual.docbook.html manual/manual.html
+
+PDF can be generated by doing:
+
+docbook2pdf -u manual.docbook.sgml && mv manual.docbook.pdf manual/manual.pdf
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/docs/internals.txt	Wed Jan 19 22:27:17 2011 -0700
@@ -0,0 +1,57 @@
+LWASM Internals
+===============
+
+LWASM is a table-driven assembler that notionally uses two passes. However,
+it implements its assembly in several passes as follows.
+
+Pass 1
+------
+
+This pass reads the entire source code and parses each line into an internal
+representation. Macros, file inclusions, and conditional assembly
+instructions are resolved at this point as well. Instructions with known
+sizes will have their sizes resolved at this point.
+
+Pass 2
+------
+
+Check all exported symbols for validity and set them as imports if the
+assembler state says so. Also resolve all symbol references in all
+expressions to be direct references either to the symbol table or
+to the import list.
+
+Pass 3
+------
+
+This pass resolves all instruction sizes that can be resolved without
+forcing any instruction sizes. This pass will run repeatedly until no
+no new resolution occurs.
+
+Pass 4
+------
+
+Work through all un-resolved instructions and force sizes. After each size
+is forced, try re-resolving all other instructions. This is done starting
+at the beginning of the source and working forward. If any instruction does
+not resolve when forced, an error will be thrown.
+
+Pass 5
+------
+
+Constantize all line addresses and throw errors if any cannot be. This
+pass will repeat until no further lines addresses are reduced to constants
+at which time all lines will be checked for constant-ness.
+
+Pass 6
+------
+
+Finalize all expressions related to instructions. Carp about any that
+cannot be reduced to a usable form. That means, for the non-object target
+all expressions must resolve to a constant. For the object form, all
+expressions must resolve to symbol references and constants. Those symbol
+references may be internal or external.
+
+Pass 7
+------
+
+Emit object code for each line for later output.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/docs/lwasm.txt	Wed Jan 19 22:27:17 2011 -0700
@@ -0,0 +1,43 @@
+LWASM 2.0
+=========
+
+LWASM is a cross-assembler for the MC6809 and HD6309 CPUs. It should
+assemble most reasonable EDTASM compatible source code. This document is not
+intended to teach assembly language for these CPUs but rather to document
+the behaviour of LWASM.
+
+
+TARGETS
+-------
+
+LWASM supports several targets for assembly. These are decb, raw, and obj.
+
+The raw target generates a raw binary output. This is useful for building
+ROMs and other items that are not intended to be loaded by any kind of
+loader. In this mode, the ORG directive is merely advisory and does not
+affect the output except for the addresses symbols are defined to have.
+
+The decb target generates output that can be loaded with the CLOADM or LOADM
+commands in Color Basic. There will be approximately one segment in the
+output file for every ORG statement after which any code is emitted. (That
+is, two ORG statements in a row will not generate two output segments.)
+This is approximately equivalent to running A/AO in EDTASM.
+
+The obj target generates output that is intended to be linked later with
+LWLINK. This target disallows the use of ORG for defining anything other
+than constants. In this target, source files consist of a number of sections
+(SECTION/ENDSECTION). Nothing outside of a section is permitted to cause any
+output at all. Use of an ORG statement within a section is an error. This
+target also permits tagging symbols for export (EXPORT) and marking a symbol
+as externally defined (IMPORT/EXTERN). The linker will resolve any external
+references at link time. Additionally, any inter-section references will be
+resolved by the linker. All code in each section is assembled with an
+implicit origin of 0. SETDP has no effect because the assembler has no idea
+what address the linker will assign to the code when it is linked. Any
+direct addressing modes will default to extended to allow for the linker to
+perform relocations. Intersegment references and external references will
+use 16 bit relative addressing but intrasegment internal references may use
+8 bit relative addressing. Forced 8 bit direct modes are probably an error
+but are permitted on the theory that the programmer might know something the
+assembler doesn't.
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/docs/manual.docbook.sgml	Wed Jan 19 22:27:17 2011 -0700
@@ -0,0 +1,2180 @@
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook V4.5//EN">
+<book>
+<bookinfo>
+<title>LW Tool Chain</title>
+<author><firstname>William</firstname><surname>Astle</surname></author>
+<copyright><year>2009, 2010</year><holder>William Astle</holder></copyright>
+</bookinfo>
+<chapter>
+
+<title>Introduction</title>
+
+<para>
+The LW tool chain provides utilities for building binaries for MC6809 and
+HD6309 CPUs. The tool chain includes a cross-assembler and a cross-linker
+which support several styles of output.
+</para>
+
+<section>
+<title>History</title>
+<para>
+For a long time, I have had an interest in creating an operating system for
+the Coco3. I finally started working on that project around the beginning of
+2006. I had a number of assemblers I could choose from. Eventually, I settled
+on one and started tinkering. After a while, I realized that assembler was not
+going to be sufficient due to lack of macros and issues with forward references.
+Then I tried another which handled forward references correctly but still did
+not support macros. I looked around at other assemblers and they all lacked
+one feature or another that I really wanted for creating my operating system.
+</para>
+
+<para>
+The solution seemed clear at that point. I am a fair programmer so I figured
+I could write an assembler that would do everything I wanted an assembler to
+do. Thus the LWASM probject was born. After more than two years of on and off
+work, version 1.0 of LWASM was released in October of 2008.
+</para>
+
+<para>
+As the aforementioned operating system project progressed further, it became
+clear that while assembling the whole project through a single file was doable,
+it was not practical. When I found myself playing some fancy games with macros
+in a bid to simulate sections, I realized I needed a means of assembling
+source files separately and linking them later. This spawned a major development
+effort to add an object file support to LWASM. It also spawned the LWLINK
+project to provide a means to actually link the files.
+</para>
+
+</section>
+
+</chapter>
+
+<chapter>
+<title>Output Formats</title>
+
+<para>
+The LW tool chain supports multiple output formats. Each format has its
+advantages and disadvantages. Each format is described below.
+</para>
+
+<section>
+<title>Raw Binaries</title>
+<para>
+A raw binary is simply a string of bytes. There are no headers or other
+niceties. Both LWLINK and LWASM support generating raw binaries. ORG directives
+in the source code only serve to set the addresses that will be used for
+symbols but otherwise have no direct impact on the resulting binary.
+</para>
+
+</section>
+<section>
+<title>DECB Binaries</title>
+
+<para>A DECB binary is compatible with the LOADM command in Disk Extended
+Color Basic on the CoCo. They are also compatible with CLOADM from Extended
+Color Basic. These binaries include the load address of the binary as well
+as encoding an execution address. These binaries may contain multiple loadable
+sections, each of which has its own load address.</para>
+
+<para>
+Each binary starts with a preamble. Each preamble is five bytes long. The
+first byte is zero. The next two bytes specify the number of bytes to load
+and the last two bytes specify the address to load the bytes at. Then, a
+string of bytes follows. After this string of bytes, there may be another
+preamble or a postamble. A postamble is also five bytes in length. The first
+byte of the postamble is $FF, the next two are zero, and the last two are
+the execution address for the binary.
+</para>
+
+<para>
+Both LWASM and LWLINK can output this format.
+</para>
+</section>
+
+<section>
+<title>OS9 Modules</title>
+<para>
+
+Since version 2.5, LWASM is able to generate OS9 modules. The syntax is
+basically the same as for other assemblers.  A module starts with the MOD
+directive and ends with the EMOD directive.  The OS9 directive is provided
+as a shortcut for writing system calls.
+
+</para>
+
+<para>
+
+LWASM does NOT provide an OS9Defs file. You must provide your own. Also note
+that the common practice of using "ifp1" around the inclusion of the OS9Defs
+file is discouraged as it is pointless and can lead to unintentional
+problems and phasing errors.  Because LWASM reads each file exactly once,
+there is no benefit to restricting the inclusion to the first assembly pass.
+
+</para>
+
+<para>
+
+It is also critical to understand that unlike many OS9 assemblers, LWASM
+does NOT maintain a separate data address counter.  Thus, you must define
+all your data offsets and so on outside of the mod/emod segment.  It is,
+therefore, likely that source code targeted at other assemblers will require
+edits to build correctly.
+
+</para>
+
+<para>
+
+LWLINK does not, yet, have the ability to create OS9 modules from object
+files.
+
+</para>
+</section>
+
+<section>
+<title>Object Files</title>
+<para>LWASM supports generating a proprietary object file format which is
+described in <xref linkend="objchap">. LWLINK is then used to link these
+object files into a final binary in any of LWLINK's supported binary
+formats.</para>
+
+<para>Object files also support the concept of sections which are not valid
+for other output types. This allows related code from each object file
+linked to be collapsed together in the final binary.</para> 
+
+<para>
+Object files are very flexible in that they allow references that are not
+known at assembly time to be resolved at link time.  However, because the
+addresses of such references are not known at assembly time, there is no way
+for the assembler to deduce that an eight bit addressing mode is possible. 
+That means the assember will default to using sixteen bit addressing
+whenever an external or cross-section reference is used.
+</para>
+
+<para>
+As of LWASM 2.4, it is possible to force direct page addressing for an
+external reference.  Care must be taken to ensure the resulting addresses
+are really in the direct page since the linker does not know what the direct
+page is supposed to be and does not emit errors for byte overflows.
+</para>
+
+<para>
+It is also possible to use external references in an eight bit immediate
+mode instruction.  In this case, only the low order eight bits will be used. 
+Again, no byte overflows will be flagged.
+</para>
+
+
+</section>
+
+</chapter>
+
+<chapter>
+<title>LWASM</title>
+<para>
+The LWTOOLS assembler is called LWASM. This chapter documents the various
+features of the assembler. It is not, however, a tutorial on 6x09 assembly
+language programming.
+</para>
+
+<section>
+<title>Command Line Options</title>
+<para>
+The binary for LWASM is called "lwasm". Note that the binary is in lower
+case. lwasm takes the following command line arguments.
+</para>
+
+<variablelist>
+
+<varlistentry>
+<term><option>--6309</option></term>
+<term><option>-3</option></term>
+<listitem>
+<para>
+This will cause the assembler to accept the additional instructions available
+on the 6309 processor. This is the default mode; this option is provided for
+completeness and to override preset command arguments.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><option>--6809</option></term>
+<term><option>-9</option></term>
+<listitem>
+<para>
+This will cause the assembler to reject instructions that are only available
+on the 6309 processor.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><option>--decb</option></term>
+<term><option>-b</option></term>
+<listitem>
+<para>
+Select the DECB output format target. Equivalent to <option>--format=decb</option>.
+</para>
+<para>While this is the default output format currently, it is not safe to rely
+on that fact. Future versions may have different defaults. It is also trivial
+to modify the source code to change the default. Thus, it is recommended to specify
+this option if you need DECB output.
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><option>--format=type</option></term>
+<term><option>-f type</option></term>
+<listitem>
+<para>
+Select the output format. Valid values are <option>obj</option> for the
+object file target, <option>decb</option> for the DECB LOADM format,
+<option>os9</option> for creating OS9 modules, and <option>raw</option> for
+a raw binary.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><option>--list[=file]</option></term>
+<term><option>-l[file]</option></term>
+<listitem>
+<para>
+Cause LWASM to generate a listing. If <option>file</option> is specified,
+the listing will go to that file. Otherwise it will go to the standard output
+stream. By default, no listing is generated. Unless <option>--symbols</option>
+is specified, the list will not include the symbol table.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><option>--symbols</option></term>
+<term><option>-s</option></term>
+<listitem>
+<para>
+Causes LWASM to generate a list of symbols when generating a listing.
+It has no effect unless a listing is being generated.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><option>--obj</option></term>
+<listitem>
+<para>
+Select the proprietary object file format as the output target.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><option>--output=FILE</option></term>
+<term><option>-o FILE</option></term>
+<listitem>
+<para>
+This option specifies the name of the output file. If not specified, the
+default is <option>a.out</option>.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><option>--pragma=pragma</option></term>
+<term><option>-p pragma</option></term>
+<listitem>
+<para>
+Specify assembler pragmas. Multiple pragmas are separated by commas. The
+pragmas accepted are the same as for the PRAGMA assembler directive described
+below.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><option>--raw</option></term>
+<term><option>-r</option></term>
+<listitem>
+<para>
+Select raw binary as the output target.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><option>--includedir=path</option></term>
+<term><option>-I path</option></term>
+<listitem>
+<para>
+Add <option>path</option> to the end of the include path.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><option>--help</option></term>
+<term><option>-?</option></term>
+<listitem>
+<para>
+Present a help screen describing the command line options.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><option>--usage</option></term>
+<listitem>
+<para>
+Provide a summary of the command line options.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><option>--version</option></term>
+<term><option>-V</option></term>
+<listitem>
+<para>
+Display the software version.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><option>--debug</option></term>
+<term><option>-d</option></term>
+<listitem>
+<para>
+Increase the debugging level. Only really useful to people hacking on the
+LWASM source code itself.
+</para>
+</listitem>
+</varlistentry>
+
+</variablelist>
+
+</section>
+
+<section>
+<title>Dialects</title>
+<para>
+LWASM supports all documented MC6809 instructions as defined by Motorola. 
+It also supports all known HD6309 instructions.  While there is general
+agreement on the pneumonics for most of the 6309 instructions, there is some
+variance with the block transfer instructions. TFM for all four variations
+seems to have gained the most traction and, thus, this is the form that is
+recommended for LWASM. However, it also supports COPY, COPY-, IMP, EXP,
+TFRP, TFRM, TFRS, and TFRR. It further adds COPY+ as a synomym for COPY,
+IMPLODE for IMP, and EXPAND for EXP.
+</para>
+
+<para>By default, LWASM accepts 6309 instructions. However, using the
+<parameter>--6809</parameter> parameter, you can cause it to throw errors on
+6309 instructions instead.</para>
+
+<para>
+The standard addressing mode specifiers are supported. These are the
+hash sign ("#") for immediate mode, the less than sign ("&lt;") for forced
+eight bit modes, and the greater than sign ("&gt;") for forced sixteen bit modes.
+</para>
+
+<para>
+Additionally, LWASM supports using the asterisk ("*") to indicate
+base page addressing. This should not be used in hand-written source code,
+however, because it is non-standard and may or may not be present in future
+versions of LWASM.
+</para>
+
+</section>
+
+<section>
+<title>Source Format</title>
+
+<para>
+LWASM accepts plain text files in a relatively free form. It can handle
+lines terminated with CR, LF, CRLF, or LFCR which means it should be able
+to assemble files on any platform on which it compiles.
+</para>
+<para>
+Each line may start with a symbol. If a symbol is present, there must not
+be any whitespace preceding it. It is legal for a line to contain nothing
+but a symbol.</para>
+<para>
+The op code is separated from the symbol by whitespace. If there is
+no symbol, there must be at least one white space character preceding it.
+If applicable, the operand follows separated by whitespace. Following the
+opcode and operand is an optional comment.
+</para>
+
+<para> It is important to note that operands cannot contain any whitespace
+except in the case of delimited strings.  This is because the first
+whitespace character will be interpreted as the separator between the
+operand column and the comment.  This behaviour is required for approximate
+source compatibility with other 6x09 assemblers.  </para>
+
+<para>
+A comment can also be introduced with a * or a ;. The comment character is
+optional for end of statement comments. However, if a symbol is the only
+thing present on the line other than the comment, the comment character is
+mandatory to prevent the assembler from interpreting the comment as an opcode.
+</para>
+
+<para>
+For compatibility with the output generated by some C preprocessors, LWASM
+will also ignore lines that begin with a #. This should not be used as a general
+comment character, however.
+</para>
+
+<para>
+The opcode is not treated case sensitively. Neither are register names in
+the operand fields. Symbols, however, are case sensitive.
+</para>
+
+<para> As of version 2.6, LWASM supports files with line numbers.  If line
+numbers are present, the line must start with a digit.  The line number
+itself must consist only of digits.  The line number must then be followed
+by either the end of the line or exactly one white space character.  After
+that white space character, the lines are interpreted exactly as above. 
+</para>
+
+</section>
+
+<section>
+<title>Symbols</title>
+
+<para>
+Symbols have no length restriction. They may contain letters, numbers, dots,
+dollar signs, and underscores. They must start with a letter, dot, or
+underscore.
+</para>
+
+<para>
+LWASM also supports the concept of a local symbol. A local symbol is one
+which contains either a "?" or a "@", which can appear anywhere in the symbol.
+The scope of a local symbol is determined by a number of factors. First,
+each included file gets its own local symbol scope. A blank line will also
+be considered a local scope barrier. Macros each have their own local symbol
+scope as well (which has a side effect that you cannot use a local symbol
+as an argument to a macro). There are other factors as well. In general,
+a local symbol is restricted to the block of code it is defined within.
+</para>
+
+<para>
+By default, unless assembling to the os9 target, a "$" in the symbol will
+also make it local.  This can be controlled by the "dollarlocal" and
+"nodollarlocal" pragmas.  In the absence of a pragma to the contrary, for
+the os9 target, a "$" in the symbol will not make it considered local while
+for all other targets it will.
+</para>
+
+</section>
+
+<section>
+<title>Numbers and Expressions</title>
+<para>
+
+Numbers can be expressed in binary, octal, decimal, or hexadecimal. Binary
+numbers may be prefixed with a "%" symbol or suffixed with a "b" or "B".
+Octal numbers may be prefixed with "@" or suffixed with "Q", "q", "O", or
+"o". Hexadecimal numbers may be prefixed with "$", "0x" or "0X", or suffixed
+with "H". No prefix or suffix is required for decimal numbers but they can
+be prefixed with "&amp;" if desired. Any constant which begins with a letter
+must be expressed with the correct prefix base identifier or be prefixed
+with a 0. Thus hexadecimal FF would have to be written either 0FFH or $FF.
+Numbers are not case sensitive.
+
+</para>
+
+<para> A symbol may appear at any point where a number is acceptable. The
+special symbol "*" can be used to represent the starting address of the
+current source line within expressions. </para>
+
+<para>The ASCII value of a character can be included by prefixing it with a
+single quote ('). The ASCII values of two characters can be included by
+prefixing the characters with a quote (").</para>
+
+<para>
+
+LWASM supports the following basic binary operators: +, -, *, /, and %. 
+These represent addition, subtraction, multiplication, division, and
+modulus.  It also supports unary negation and unary 1's complement (- and ^
+respectively).  It is also possible to use ~ for the unary 1's complement
+operator.  For completeness, a unary positive (+) is supported though it is
+a no-op.  LWASM also supports using |, &, and ^ for bitwise or, bitwise and,
+and bitwise exclusive or respectively.
+
+</para>
+
+<para>
+
+Operator precedence follows the usual rules. Multiplication, division, and
+modulus take precedence over addition and subtraction.  Unary operators take
+precedence over binary operators.  Bitwise operators are lower precdence
+than addition and subtraction.  To force a specific order of evaluation,
+parentheses can be used in the usual manner.
+
+</para>
+
+<para>
+
+As of LWASM 2.5, the operators && and || are recognized for boolean and and
+boolean or respectively.  They will return either 0 or 1 (false or true). 
+They have the lowest precedence of all the binary operators.
+
+</para>
+
+</section>
+
+<section>
+<title>Assembler Directives</title>
+<para>
+Various directives can be used to control the behaviour of the
+assembler or to include non-code/data in the resulting output. Those directives
+that are not described in detail in other sections of this document are
+described below.
+</para>
+
+<section>
+<title>Data Directives</title>
+<variablelist>
+<varlistentry><term>FCB <parameter>expr[,...]</parameter></term>
+<term>.DB <parameter>expr[,...]</parameter></term>
+<term>.BYTE <parameter>expr[,...]</parameter></term>
+<listitem>
+<para>Include one or more constant bytes (separated by commas) in the output.</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>FDB <parameter>expr[,...]</parameter></term>
+<term>.DW <parameter>expr[,...]</parameter></term>
+<term>.WORD <parameter>expr[,...]</parameter></term>
+<listitem>
+<para>Include one or more words (separated by commas) in the output.</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>FQB <parameter>expr[,...]</parameter></term>
+<term>.QUAD <parameter>expr[,...]</parameter></term>
+<term>.4BYTE <parameter>expr[,...]</parameter></term>
+<listitem>
+<para>Include one or more double words (separated by commas) in the output.</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>FCC <parameter>string</parameter></term>
+<term>.ASCII <parameter>string</parameter></term>
+<term>.STR <parameter>string</parameter></term>
+<listitem>
+<para>
+Include a string of text in the output. The first character of the operand
+is the delimiter which must appear as the last character and cannot appear
+within the string. The string is included with no modifications>
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>FCN <parameter>string</parameter></term>
+<term>.ASCIZ <parameter>string</parameter></term>
+<term>.STRZ <parameter>string</parameter></term>
+<listitem>
+<para>
+Include a NUL terminated string of text in the output. The first character of
+the operand is the delimiter which must appear as the last character and
+cannot appear within the string. A NUL byte is automatically appended to
+the string.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>FCS <parameter>string</parameter></term>
+<term>.ASCIS <parameter>string</parameter></term>
+<term>.STRS <parameter>string</parameter></term>
+<listitem>
+<para>
+Include a string of text in the output with bit 7 of the final byte set. The
+first character of the operand is the delimiter which must appear as the last
+character and cannot appear within the string.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry><term>ZMB <parameter>expr</parameter></term>
+<listitem>
+<para>
+Include a number of NUL bytes in the output. The number must be fully resolvable
+during pass 1 of assembly so no forward or external references are permitted.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry><term>ZMD <parameter>expr</parameter></term>
+<listitem>
+<para>
+Include a number of zero words in the output. The number must be fully
+resolvable during pass 1 of assembly so no forward or external references are
+permitted.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry><term>ZMQ <parameter>expr<parameter></term>
+<listitem>
+<para>
+Include a number of zero double-words in the output. The number must be fully
+resolvable during pass 1 of assembly so no forward or external references are
+permitted.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>RMB <parameter>expr</parameter></term>
+<term>.BLKB <parameter>expr</parameter></term>
+<term>.DS <parameter>expr</parameter></term>
+<term>.RS <parameter>expr</parameter></term>
+<listitem>
+<para>
+Reserve a number of bytes in the output. The number must be fully resolvable
+during pass 1 of assembly so no forward or external references are permitted.
+The value of the bytes is undefined.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry><term>RMD <parameter>expr</parameter></term>
+<listitem>
+<para>
+Reserve a number of words in the output. The number must be fully
+resolvable during pass 1 of assembly so no forward or external references are
+permitted. The value of the words is undefined.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry><term>RMQ <parameter>expr</parameter></term>
+<listitem>
+<para>
+Reserve a number of double-words in the output. The number must be fully
+resolvable during pass 1 of assembly so no forward or external references are
+permitted. The value of the double-words is undefined.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>INCLUDEBIN <parameter>filename</parameter></term>
+<listitem>
+<para>
+Treat the contents of <parameter>filename</parameter> as a string of bytes to
+be included literally at the current assembly point. This has the same effect
+as converting the file contents to a series of FCB statements and including
+those at the current assembly point.
+</para>
+
+<para> If <parameter>filename</parameter> beings with a /, the file name
+will be taken as absolute.  Otherwise, the current directory will be
+searched followed by the search path in the order specified.</para>
+
+<para> Please note that absolute path detection including drive letters will
+not function correctly on Windows platforms.  Non-absolute inclusion will
+work, however.</para>
+
+</listitem>
+</varlistentry>
+
+</variablelist>
+
+</section>
+
+<section>
+<title>Address Definition</title>
+<para>The directives in this section all control the addresses of symbols
+or the assembly process itself.</para>
+
+<variablelist>
+<varlistentry><term>ORG <parameter>expr</parameter></term>
+<listitem>
+<para>Set the assembly address. The address must be fully resolvable on the
+first pass so no external or forward references are permitted. ORG is not
+permitted within sections when outputting to object files. For the DECB
+target, each ORG directive after which output is generated will cause
+a new preamble to be output. ORG is only used to determine the addresses
+of symbols when the raw target is used.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><parameter>sym</parameter> EQU <parameter>expr</parameter></term>
+<term><parameter>sym</parameter> = <parameter>expr</parameter></term>
+<listitem>
+<para>Define the value of <parameter>sym</parameter> to be <parameter>expr</parameter>.
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><parameter>sym</parameter> SET <parameter>expr</parameter></term>
+<listitem>
+<para>Define the value of <parameter>sym</parameter> to be <parameter>expr</parameter>.
+Unlike EQU, SET permits symbols to be defined multiple times as long as SET
+is used for all instances. Use of the symbol before the first SET statement
+that sets its value is undefined.</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>SETDP <parameter>expr</parameter></term>
+<listitem>
+<para>Inform the assembler that it can assume the DP register contains
+<parameter>expr</parameter>. This directive is only advice to the assembler
+to determine whether an address is in the direct page and has no effect
+on the contents of the DP register. The value must be fully resolved during
+the first assembly pass because it affects the sizes of subsequent instructions.
+</para>
+<para>This directive has no effect in the object file target.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>ALIGN <parameter>expr</parameter>[,<parameter>value</parameter>]</term>
+<listitem>
+
+<para>Force the current assembly address to be a multiple of
+<parameter>expr</parameter>.  If <parameter>value</parameter> is not
+specified, a series of NUL bytes is output to force the alignment, if
+required.  Otherwise, the low order 8 bits of <parameter>value</parameter>
+will be used as the fill.  The alignment value must be fully resolved on the
+first pass because it affects the addresses of subsquent instructions. 
+However, <parameter>value</parameter> may include forward references; as
+long as it resolves to a constant for the second pass, the value will be
+accepted.</para>
+
+<para>Unless <parameter>value</parameter> is specified as something like $12,
+this directive is not suitable for inclusion in the middle of actual code. 
+The default padding value is $00 which is intended to be used within data
+blocks.  </para>
+
+</listitem>
+</varlistentry>
+
+</variablelist>
+
+</section>
+
+<section>
+<title>Conditional Assembly</title>
+<para>
+Portions of the source code can be excluded or included based on conditions
+known at assembly time. Conditionals can be nested arbitrarily deeply. The
+directives associated with conditional assembly are described in this section.
+</para>
+<para>All conditionals must be fully bracketed. That is, every conditional
+statement must eventually be followed by an ENDC at the same level of nesting.
+</para>
+<para>Conditional expressions are only evaluated on the first assembly pass.
+It is not possible to game the assembly process by having a conditional
+change its value between assembly passes. Due to the underlying architecture
+of LWASM, there is no possible utility to IFP1 and IFP2, nor can they, as of LWASM 3.0, actually
+be implemented meaningfully. Thus there is not and never will
+be any equivalent of IFP1 or IFP2 as provided by other assemblers. Use of those opcodes
+will throw a warning and be ignored.</para>
+
+<para>It is important to note that if a conditional does not resolve to a constant
+during the first parsing pass, an error will be thrown. This is unavoidable because the assembler
+must make a decision about which source to include and which source to exclude at this stage.
+Thus, expressions that work normally elsewhere will not work for conditions.</para>
+
+<variablelist>
+<varlistentry>
+<term>IFEQ <parameter>expr</parameter></term>
+<listitem>
+<para>If <parameter>expr</parameter> evaluates to zero, the conditional
+will be considered true.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>IFNE <parameter>expr</parameter></term>
+<term>IF <parameter>expr</parameter></term>
+<listitem>
+<para>If <parameter>expr</parameter> evaluates to a non-zero value, the conditional
+will be considered true.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>IFGT <parameter>expr</parameter></term>
+<listitem>
+<para>If <parameter>expr</parameter> evaluates to a value greater than zero, the conditional
+will be considered true.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>IFGE <parameter>expr</parameter></term>
+<listitem>
+<para>If <parameter>expr</parameter> evaluates to a value greater than or equal to zero, the conditional
+will be considered true.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>IFLT <parameter>expr</parameter></term>
+<listitem>
+<para>If <parameter>expr</parameter> evaluates to a value less than zero, the conditional
+will be considered true.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>IFLE <parameter>expr</parameter></term>
+<listitem>
+<para>If <parameter>expr</parameter> evaluates to a value less than or equal to zero , the conditional
+will be considered true.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>IFDEF <parameter>sym</parameter></term>
+<listitem>
+<para>If <parameter>sym</parameter> is defined at this point in the assembly
+process, the conditional
+will be considered true.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>IFNDEF <parameter>sym</parameter></term>
+<listitem>
+<para>If <parameter>sym</parameter> is not defined at this point in the assembly
+process, the conditional
+will be considered true.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>ELSE</term>
+<listitem>
+<para>
+If the preceding conditional at the same level of nesting was false, the
+statements following will be assembled. If the preceding conditional at
+the same level was true, the statements following will not be assembled.
+Note that the preceding conditional might have been another ELSE statement
+although this behaviour is not guaranteed to be supported in future versions
+of LWASM.
+</para>
+</listitem>
+
+<varlistentry>
+<term>ENDC</term>
+<listitem>
+<para>
+This directive marks the end of a conditional construct. Every conditional
+construct must end with an ENDC directive.
+</para>
+</listitem>
+</varlistentry>
+
+</variablelist>
+</section>
+
+<section>
+<title>OS9 Target Directives</title>
+
+<para>This section includes directives that apply solely to the OS9
+target.</para>
+
+<variablelist>
+
+<varlistentry>
+<term>OS9 <parameter>syscall</parameter></term>
+<listitem>
+<para>
+
+This directive generates a call to the specified system call. <parameter>syscall</parameter> may be an arbitrary expression.
+
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>MOD <parameter>size</parameter>,<parameter>name</parameter>,<parameter>type</parameter>,<parameter>flags</parameter>,<parameter>execoff</parameter>,<parameter>datasize</parameter></term>
+<listitem>
+<para>
+
+This tells LWASM that the beginning of the actual module is here. It will
+generate a module header based on the parameters specified.  It will also
+begin calcuating the module CRC.
+
+</para>
+
+<para>
+
+The precise meaning of the various parameters is beyond the scope of this
+document since it is not a tutorial on OS9 module programming.
+
+</para>
+
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>EMOD</term>
+<listitem>
+<para>
+
+This marks the end of a module and causes LWASM to emit the calculated CRC
+for the module.
+
+</para>
+</varlistentry>
+
+</variablelist>
+</section>
+
+<section>
+<title>Miscelaneous Directives</title>
+
+<para>This section includes directives that do not fit into the other
+categories.</para>
+
+<variablelist>
+
+<varlistentry>
+<term>INCLUDE <parameter>filename</parameter></term>
+<term>USE <parameter>filename</parameter></term>
+
+<listitem> <para> Include the contents of <parameter>filename</parameter> at
+this point in the assembly as though it were a part of the file currently
+being processed.  Note that if whitespace appears in the name of the file,
+you must enclose <parameter>filename</parameter> in quotes.
+</para>
+
+<para>
+Note that the USE variation is provided only for compatibility with other
+assemblers. It is recommended to use the INCLUDE variation.</para>
+
+<para>If <parameter>filename</parameter> begins with a &quot;/&quot;, it is
+interpreted as an absolute path. If it does not, the search path will be used
+to find the file. First, the directory containing the file that contains this
+directive. (Includes within an included file are relative to the included file,
+not the file that included it.) If the file is not found there, the include path
+is searched. If it is still not found, an error will be thrown. Note that the
+current directory as understood by your shell or operating system is not searched.
+</para>
+
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>END <parameter>[expr]</parameter></term>
+<listitem>
+<para>
+This directive causes the assembler to stop assembling immediately as though
+it ran out of input. For the DECB target only, <parameter>expr</parameter>
+can be used to set the execution address of the resulting binary. For all
+other targets, specifying <parameter>expr</parameter> will cause an error.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>ERROR <parameter>string</parameter></term>
+<listitem>
+<para>
+Causes a custom error message to be printed at this line. This will cause
+assembly to fail. This directive is most useful inside conditional constructs
+to cause assembly to fail if some condition that is known bad happens. Everything
+from the directive to the end of the line is considered the error message.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>WARNING <parameter>string</parameter></term>
+<listitem>
+<para>
+Causes a custom warning message to be printed at this line. This will not cause
+assembly to fail. This directive is most useful inside conditional constructs
+or include files to alert the programmer to a deprecated feature being used
+or some other condition that may cause trouble later, but which may, in fact,
+not cause any trouble.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>.MODULE <parameter>string</parameter></term>
+<listitem>
+<para>
+This directive is ignored for most output targets. If the output target
+supports encoding a module name into it, <parameter>string</parameter>
+will be used as the module name.
+</para>
+<para>
+As of version 3.0, no supported output targets support this directive.
+</para>
+</listitem>
+</varlistentry>
+
+</variablelist>
+</section>
+
+</section>
+
+<section>
+<title>Macros</title>
+<para>
+LWASM is a macro assembler. A macro is simply a name that stands in for a
+series of instructions. Once a macro is defined, it is used like any other
+assembler directive. Defining a macro can be considered equivalent to adding
+additional assembler directives.
+</para>
+<para>Macros may accept parameters. These parameters are referenced within
+a macro by the a backslash ("\") followed by a digit 1 through 9 for the first
+through ninth parameters. They may also be referenced by enclosing the
+decimal parameter number in braces ("{num}"). These parameter references
+are replaced with the verbatim text of the parameter passed to the macro. A
+reference to a non-existent parameter will be replaced by an empty string.
+Macro parameters are expanded everywhere on each source line. That means
+the parameter to a macro could be used as a symbol or it could even appear
+in a comment or could cause an entire source line to be commented out
+when the macro is expanded.
+</para>
+<para>
+Parameters passed to a macro are separated by commas and the parameter list
+is terminated by any whitespace. This means that neither a comma nor whitespace
+may be included in a macro parameter.
+</para>
+<para>
+Macro expansion is done recursively. That is, within a macro, macros are
+expanded. This can lead to infinite loops in macro expansion. If the assembler
+hangs for a long time while assembling a file that uses macros, this may be
+the reason.</para>
+
+<para>Each macro expansion receives its own local symbol context which is not
+inherited by any macros called by it nor is it inherited from the context
+the macro was instantiated in. That means it is possible to use local symbols
+within macros without having them collide with symbols in other macros or
+outside the macro itself. However, this also means that using a local symbol
+as a parameter to a macro, while legal, will not do what it would seem to do
+as it will result in looking up the local symbol in the macro's symbol context
+rather than the enclosing context where it came from, likely yielding either
+an undefined symbol error or bizarre assembly results.
+</para>
+<para>
+Note that there is no way to define a macro as local to a symbol context. All
+macros are part of the global macro namespace. However, macros have a separate
+namespace from symbols so it is possible to have a symbol with the same name
+as a macro.
+</para>
+
+<para>
+Macros are defined only during the first pass. Macro expansion also
+only occurs during the first pass. On the second pass, the macro
+definition is simply ignored. Macros must be defined before they are used.
+</para>
+
+<para>The following directives are used when defining macros.</para>
+
+<variablelist>
+<varlistentry>
+<term><parameter>macroname</parameter> MACRO</term>
+<listitem>
+<para>This directive is used to being the definition of a macro called
+<parameter>macroname</parameter>. If <parameter>macroname</parameter> already
+exists, it is considered an error. Attempting to define a macro within a
+macro is undefined. It may work and it may not so the behaviour should not
+be relied upon.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>ENDM</term>
+<listitem>
+<para>
+This directive indicates the end of the macro currently being defined. It
+causes the assembler to resume interpreting source lines as normal.
+</para>
+</listitem>
+</variablelist>
+
+</section>
+
+<section>
+<title>Structures</title>
+<para>
+
+Structures are used to group related data in a fixed structure. A structure
+consists a number of fields, defined in sequential order and which take up
+specified size.  The assembler does not enforce any means of access within a
+structure; it assumes that whatever you are doing, you intended to do. 
+There are two pseudo ops that are used for defining structures.
+
+</para>
+
+<variablelist>
+<varlistentry>
+<term><parameter>structname</parameter> STRUCT</term>
+<listitem>
+<para>
+
+This directive is used to begin the definition of a structure with name
+<parameter>structname</parameter>.  Subsequent statements all form part of
+the structure definition until the end of the structure is declared.
+
+</para>
+</listitem>
+</varlistentry>
+<varlistentry>
+<term>ENDSTRUCT</term>
+<term>ENDS</term>
+<listitem>
+<para>
+This directive ends the definition of the structure. ENDSTRUCT is the
+preferred form. Prior to version 3.0 of LWASM, ENDS was used to end a
+section instead of a structure.
+</para>
+</listitem>
+</varlistentry>
+</variablelist>
+
+<para>
+
+Within a structure definition, only reservation pseudo ops are permitted.
+Anything else will cause an assembly error.
+</para>
+
+<para> Once a structure is defined, you can reserve an area of memory in the
+same structure by using the structure name as the opcode.  Structures can
+also contain fields that are themselves structures.  See the example
+below.</para>
+
+<programlisting>
+tstruct2  STRUCT
+f1        rmb 1
+f2        rmb 1
+          ENDSTRUCT
+
+tstruct   STRUCT
+field1    rmb 2
+field2    rmb 3
+field3    tstruct2
+          ENDSTRUCT
+
+          ORG $2000
+var1      tstruct
+var2      tstruct2
+</programlisting>
+
+<para>Fields are referenced using a dot (.) as a separator. To refer to the
+generic offset within a structure, use the structure name to the left of the
+dot.  If referring to a field within an actual variable, use the variable's
+symbol name to the left of the dot.</para>
+
+<para>You can also refer to the actual size of a structure (or a variable
+declared as a structure) using the special symbol sizeof{structname} where
+structname will be the name of the structure or the name of the
+variable.</para>
+
+<para>Essentially, structures are a shortcut for defining a vast number of
+symbols.  When a structure is defined, the assembler creates symbols for the
+various fields in the form structname.fieldname as well as the appropriate
+sizeof{structname} symbol.  When a variable is declared as a structure, the
+assembler does the same thing using the name of the variable.  You will see
+these symbols in the symbol table when the assembler is instructed to
+provide a listing.  For instance, the above listing will create the
+following symbols (symbol values in parentheses): tstruct2.f1 (0),
+tstruct2.f2 (1), sizeof{tstruct2} (2), tstruct.field1 (0), tstruct.field2
+(2), tstruct.field3 (5), tstruct.field3.f1 (5), tstruct.field3.f2 (6),
+sizeof{tstruct.field3} (2), sizeof{tstruct} (7), var1 {$2000}, var1.field1
+{$2000}, var1.field2 {$2002}, var1.field3 {$2005}, var1.field3.f1 {$2005},
+var1.field3.f2 {$2006}, sizeof(var1.field3} (2), sizeof{var1} (7), var2
+($2007), var2.f1 ($2007), var2.f2 ($2008), sizeof{var2} (2).  </para>
+
+</section>
+
+<section>
+<title>Object Files and Sections</title>
+<para>
+The object file target is very useful for large project because it allows
+multiple files to be assembled independently and then linked into the final
+binary at a later time. It allows only the small portion of the project
+that was modified to be re-assembled rather than requiring the entire set
+of source code to be available to the assembler in a single assembly process.
+This can be particularly important if there are a large number of macros,
+symbol definitions, or other metadata that uses resources at assembly time.
+By far the largest benefit, however, is keeping the source files small enough
+for a mere mortal to find things in them.
+</para>
+
+<para>
+With multi-file projects, there needs to be a means of resolving references to
+symbols in other source files. These are known as external references. The
+addresses of these symbols cannot be known until the linker joins all the
+object files into a single binary. This means that the assembler must be
+able to output the object code without knowing the value of the symbol. This
+places some restrictions on the code generated by the assembler. For
+example, the assembler cannot generate direct page addressing for instructions
+that reference external symbols because the address of the symbol may not
+be in the direct page. Similarly, relative branches and PC relative addressing
+cannot be used in their eight bit forms. Everything that must be resolved
+by the linker must be assembled to use the largest address size possible to
+allow the linker to fill in the correct value at link time. Note that the
+same problem applies to absolute address references as well, even those in
+the same source file, because the address is not known until link time.
+</para>
+
+<para>
+It is often desired in multi-file projects to have code of various types grouped
+together in the final binary generated by the linker as well. The same applies
+to data. In order for the linker to do that, the bits that are to be grouped
+must be tagged in some manner. This is where the concept of sections comes in.
+Each chunk of code or data is part of a section in the object file. Then,
+when the linker reads all the object files, it coalesces all sections of the
+same name into a single section and then considers it as a unit.
+</para>
+
+<para>
+The existence of sections, however, raises a problem for symbols even
+within the same source file. Thus, the assembler must treat symbols from
+different sections within the same source file in the same manner as external
+symbols. That is, it must leave them for the linker to resolve at link time,
+with all the limitations that entails.
+</para>
+
+<para>
+In the object file target mode, LWASM requires all source lines that
+cause bytes to be output to be inside a section. Any directives that do
+not cause any bytes to be output can appear outside of a section. This includes
+such things as EQU or RMB. Even ORG can appear outside a section. ORG, however,
+makes no sense within a section because it is the linker that determines
+the starting address of the section's code, not the assembler.
+</para>
+
+<para>
+All symbols defined globally in the assembly process are local to the 
+source file and cannot be exported. All symbols defined within a section are
+considered local to the source file unless otherwise explicitly exported.
+Symbols referenced from external source files must be declared external,
+either explicitly or by asking the assembler to assume that all undefined
+symbols are external.
+</para>
+
+<para>
+It is often handy to define a number of memory addresses that will be
+used for data at run-time but which need not be included in the binary file.
+These memory addresses are not initialized until run-time, either by the
+program itself or by the program loader, depending on the operating environment.
+Such sections are often known as BSS sections. LWASM supports generating
+sections with a BSS attribute set which causes the section definition including
+symbols exported from that section and those symbols required to resolve
+references from the local file, but with no actual code in the object file.
+It is illegal for any source lines within a BSS flagged section to cause any
+bytes to be output.
+</para>
+
+<para>The following directives apply to section handling.</para>
+
+<variablelist>
+<varlistentry>
+<term>SECTION <parameter>name[,flags]</parameter></term>
+<term>SECT <parameter>name[,flags]</parameter></term>
+<term>.AREA <parameter>name[,flags]</parameter></term>
+<listitem>
+<para>
+Instructs the assembler that the code following this directive is to be
+considered part of the section <parameter>name</parameter>. A section name
+may appear multiple times in which case it is as though all the code from
+all the instances of that section appeared adjacent within the source file.
+However, <parameter>flags</parameter> may only be specified on the first
+instance of the section.
+</para>
+<para>There is a single flag supported in <parameter>flags</parameter>. The
+flag <parameter>bss</parameter> will cause the section to be treated as a BSS
+section and, thus, no code will be included in the object file nor will any
+bytes be permitted to be output.</para>
+<para>
+If the section name is "bss" or ".bss" in any combination of upper and
+lower case, the section is assumed to be a BSS section. In that case,
+the flag <parameter>!bss</parameter> can be used to override this assumption.
+</para>
+<para>
+If assembly is already happening within a section, the section is implicitly
+ended and the new section started. This is not considered an error although
+it is recommended that all sections be explicitly closed.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>ENDSECTION</term>
+<term>ENDSECT</term>
+<listitem>
+<para>
+This directive ends the current section. This puts assembly outside of any
+sections until the next SECTION directive. ENDSECTION is the preferred form.
+Prior to version 3.0 of LWASM, ENDS could also be used to end a section but
+as of version 3.0, it is now an alias for ENDSTRUCT instead.
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><parameter>sym</parameter> EXTERN</term>
+<term><parameter>sym</parameter> EXTERNAL</term>
+<term><parameter>sym</parameter> IMPORT</term>
+<listitem>
+<para>
+This directive defines <parameter>sym</parameter> as an external symbol.
+This directive may occur at any point in the source code. EXTERN definitions
+are resolved on the first pass so an EXTERN definition anywhere in the
+source file is valid for the entire file. The use of this directive is
+optional when the assembler is instructed to assume that all undefined
+symbols are external. In fact, in that mode, if the symbol is referenced
+before the EXTERN directive, an error will occur.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><parameter>sym</parameter> EXPORT</term>
+<term><parameter>sym</parameter> .GLOBL</term>
+
+<term>EXPORT <parameter>sym</parameter></term>
+<term>.GLOBL <parameter>sym</parameter></term>
+
+<listitem>
+<para>
+This directive defines <parameter>sym</parameter> as an exported symbol.
+This directive may occur at any point in the source code, even before the
+definition of the exported symbol.
+</para>
+<para>
+Note that <parameter>sym</parameter> may appear as the operand or as the
+statement's symbol. If there is a symbol on the statement, that will
+take precedence over any operand that is present.
+</para>
+</listitem>
+
+</varlistentry>
+
+<varlistentry>
+<term><parameter>sym</parameter> EXTDEP</term>
+<listitem>
+
+<para>This directive forces an external dependency on
+<parameter>sym</parameter>, even if it is never referenced anywhere else in
+this file.</para>
+
+</listitem>
+</varlistentry>
+</variablelist>
+
+</section>
+
+<section>
+<title>Assembler Modes and Pragmas</title>
+<para>
+There are a number of options that affect the way assembly is performed.
+Some of these options can only be specified on the command line because
+they determine something absolute about the assembly process. These include
+such things as the output target. Other things may be switchable during
+the assembly process. These are known as pragmas and are, by definition,
+not portable between assemblers.
+</para>
+
+<para>LWASM supports a number of pragmas that affect code generation or
+otherwise affect the behaviour of the assembler. These may be specified by
+way of a command line option or by assembler directives. The directives
+are as follows.
+</para>
+
+<variablelist>
+<varlistentry>
+<term>PRAGMA <parameter>pragma[,...]</parameter></term>
+<listitem>
+<para>
+Specifies that the assembler should bring into force all <parameter>pragma</parameter>s
+specified. Any unrecognized pragma will cause an assembly error. The new
+pragmas will take effect immediately. This directive should be used when
+the program will assemble incorrectly if the pragma is ignored or not supported.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>*PRAGMA <parameter>pragma[,...]</parameter></term>
+<listitem>
+<para>
+This is identical to the PRAGMA directive except no error will occur with
+unrecognized or unsupported pragmas. This directive, by virtue of starting
+with a comment character, will also be ignored by assemblers that do not
+support this directive. Use this variation if the pragma is not required
+for correct functioning of the code.
+</para>
+</listitem>
+</varlistentry>
+</variablelist>
+
+<para>Each pragma supported has a positive version and a negative version.
+The positive version enables the pragma while the negative version disables
+it. The negatitve version is simply the positive version with "no" prefixed
+to it. For instance, "pragma" vs. "nopragma". Only the positive version is
+listed below.</para>
+
+<para>Pragmas are not case sensitive.</para>
+
+<variablelist>
+<varlistentry>
+<term>index0tonone</term>
+<listitem>
+<para>
+When in force, this pragma enables an optimization affecting indexed addressing
+modes. When the offset expression in an indexed mode evaluates to zero but is
+not explicity written as 0, this will replace the operand with the equivalent
+no offset mode, thus creating slightly faster code. Because of the advantages
+of this optimization, it is enabled by default.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>cescapes</term>
+<listitem>
+<para>
+This pragma will cause strings in the FCC, FCS, and FCN pseudo operations to
+have C-style escape sequences interpreted. The one departure from the official
+spec is that unrecognized escape sequences will return either the character
+immediately following the backslash or some undefined value. Do not rely
+on the behaviour of undefined escape sequences.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>importundefexport</term>
+<listitem>
+<para>
+This pragma is only valid for targets that support external references. When
+in force, it will cause the EXPORT directive to act as IMPORT if the symbol
+to be exported is not defined.  This is provided for compatibility with the
+output of gcc6809 and should not be used in hand written code.  Because of
+the confusion this pragma can cause, it is disabled by default.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>undefextern</term>
+<listitem>
+<para>
+This pragma is only valid for targets that support external references. When in
+force, if the assembler sees an undefined symbol on the second pass, it will
+automatically define it as an external symbol. This automatic definition will
+apply for the remainder of the assembly process, even if the pragma is
+subsequently turned off. Because this behaviour would be potentially surprising,
+this pragma defaults to off.
+</para>
+<para>
+The primary use for this pragma is for projects that share a large number of
+symbols between source files. In such cases, it is impractical to enumerate
+all the external references in every source file. This allows the assembler
+and linker to do the heavy lifting while not preventing a particular source
+module from defining a local symbol of the same name as an external symbol
+if it does not need the external symbol. (This pragma will not cause an
+automatic external definition if there is already a locally defined symbol.)
+</para>
+<para>
+This pragma will often be specified on the command line for large projects.
+However, depending on the specific dynamics of the project, it may be sufficient
+for one or two files to use this pragma internally.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>dollarlocal</term>
+<listitem>
+
+<para>When set, a "$" in a symbol makes it local. When not set, "$" does not
+cause a symbol to be local.  It is set by default except when using the OS9
+target.</para>
+
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>dollarnotlocal</term>
+<listitem>
+
+<para> This is the same as the "dollarlocal" pragma except its sense is
+reversed.  That is, "dollarlocal" and "nodollarnotlocal" are equivalent and
+"nodollarlocal" and "dollarnotlocal" are equivalent.  </para>
+
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>pcaspcr</term>
+<listitem>
+
+<para> Normally, LWASM makes a distinction between PC and PCR in program
+counter relative addressing. In particular, the use of PC means an absolute
+offset from PC while PCR causes the assembler to calculate the offset to the
+specified operand and use that as the offset from PC. By setting this
+pragma, you can have PC treated the same as PCR. </para>
+
+
+</listitem>
+</varlistentry>
+
+</variablelist>
+
+</section>
+
+</chapter>
+
+<chapter>
+<title>LWLINK</title>
+<para>
+The LWTOOLS linker is called LWLINK. This chapter documents the various features
+of the linker.
+</para>
+
+<section>
+<title>Command Line Options</title>
+<para>
+The binary for LWLINK is called "lwlink". Note that the binary is in lower
+case. lwlink takes the following command line arguments.
+</para>
+<variablelist>
+<varlistentry>
+<term><option>--decb</option></term>
+<term><option>-b</option></term>
+<listitem>
+<para>
+Selects the DECB output format target. This is equivalent to <option>--format=decb</option>
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><option>--output=FILE</option></term>
+<term><option>-o FILE</option></term>
+<listitem>
+<para>
+This option specifies the name of the output file. If not specified, the
+default is <option>a.out</option>.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><option>--format=TYPE</option></term>
+<term><option>-f TYPE</option></term>
+<listitem>
+<para>
+This option specifies the output format. Valid values are <option>decb</option>
+and <option>raw</option>
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><option>--raw</option></term>
+<term><option>-r</option></term>
+<listitem>
+<para>
+This option specifies the raw output format.
+It is equivalent to <option>--format=raw</option>
+and <option>-f raw</option>
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><option>--script=FILE</option></term>
+<term><option>-s</option></term>
+<listitem>
+<para>
+This option allows specifying a linking script to override the linker's
+built in defaults.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><option>--section-base=SECT=BASE</option></term>
+<listitem>
+<para>
+Cause section SECT to load at base address BASE. This will be prepended
+to the built-in link script. It is ignored if a link script is provided.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><option>--map=FILE</option></term>
+<term><option>-m FILE</option></term>
+<listitem>
+<para>
+This will output a description of the link result to FILE.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><option>--library=LIBSPEC</option></term>
+<term><option>-l LIBSPEC</option></term>
+<listitem>
+<para>
+Load a library using the library search path. LIBSPEC will have "lib" prepended
+and ".a" appended.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><option>--library-path=DIR</option></term>
+<term><option>-L DIR</option></term>
+<listitem>
+<para>
+Add DIR to the library search path.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><option>--debug</option></term>
+<term><option>-d</option></term>
+<listitem>
+<para>
+This option increases the debugging level. It is only useful for LWTOOLS
+developers.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><option>--help</option></term>
+<term><option>-?</option></term>
+<listitem>
+<para>
+This provides a listing of command line options and a brief description
+of each.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><option>--usage</option></term>
+<listitem>
+<para>
+This will display a usage summary
+of each command line option.
+</para>
+</listitem>
+</varlistentry>
+
+
+<varlistentry>
+<term><option>--version</option></term>
+<term><option>-V</option></term>
+<listitem>
+<para>
+This will display the version of LWLINK.
+</para>
+</listitem>
+</varlistentry>
+
+</section>
+
+<section>
+<title>Linker Operation</title>
+
+<para>
+
+LWLINK takes one or more files in supported input formats and links them
+into a single binary. Currently supported formats are the LWTOOLS object
+file format and the archive format used by LWAR. While the precise method is
+slightly different, linking can be conceptualized as the following steps.
+
+</para>
+
+<orderedlist>
+<listitem>
+<para>
+First, the linker loads a linking script. If no script is specified, it
+loads a built-in default script based on the output format selected. This
+script tells the linker how to lay out the various sections in the final
+binary.
+</para>
+</listitem>
+
+<listitem>
+<para>
+Next, the linker reads all the input files into memory. At this time, it
+flags any format errors in those files. It constructs a table of symbols
+for each object at this time.
+</para>
+</listitem>
+
+<listitem>
+<para>
+The linker then proceeds with organizing the sections loaded from each file
+according to the linking script. As it does so, it is able to assign addresses
+to each symbol defined in each object file. At this time, the linker may
+also collapse different instances of the same section name into a single
+section by appending the data from each subsequent instance of the section
+to the first instance of the section.
+</para>
+</listitem>
+
+<listitem>
+<para>
+Next, the linker looks through every object file for every incomplete reference.
+It then attempts to fully resolve that reference. If it cannot do so, it
+throws an error. Once a reference is resolved, the value is placed into
+the binary code at the specified section. It should be noted that an
+incomplete reference can reference either a symbol internal to the object
+file or an external symbol which is in the export list of another object
+file.
+</para>
+</listitem>
+
+<listitem>
+<para>
+If all of the above steps are successful, the linker opens the output file
+and actually constructs the binary.
+</para>
+</listitem>
+</orderedlist>
+
+</section>
+
+<section
+<title>Linking Scripts</title>
+<para>
+A linker script is used to instruct the linker about how to assemble the
+various sections into a completed binary. It consists of a series of
+directives which are considered in the order they are encountered.
+</para>
+<para>
+The sections will appear in the resulting binary in the order they are
+specified in the script file. If a referenced section is not found, the linker will behave as though the
+section did exist but had a zero size, no relocations, and no exports.
+A section should only be referenced once. Any subsequent references will have
+an undefined effect.
+</para>
+
+<para>
+All numbers are in linking scripts are specified in hexadecimal. All directives
+are case sensitive although the hexadecimal numbers are not.
+</para>
+
+<para>A section name can be specified as a "*", then any section not
+already matched by the script will be matched. The "*" can be followed
+by a comma and a flag to narrow the section down slightly, also.
+If the flag is "!bss", then any section that is not flagged as a bss section
+will be matched. If the flag is "bss", then any section that is flagged as
+bss will be matched.
+</para>
+
+<para>The following directives are understood in a linker script.</para>
+<variablelist>
+<varlistentry>
+<term>section <parameter>name</parameter> load <parameter>addr</parameter></term>
+<listitem><para>
+
+This causes the section <parameter>name</parameter> to load at
+<parameter>addr</parameter>. For the raw target, only one "load at" entry is
+allowed for non-bss sections and it must be the first one. For raw targets,
+it affects the addresses the linker assigns to symbols but has no other
+affect on the output. bss sections may all have separate load addresses but
+since they will not appear in the binary anyway, this is okay.
+</para><para>
+For the decb target, each "load" entry will cause a new "block" to be
+output to the binary which will contain the load address. It is legal for
+sections to overlap in this manner - the linker assumes the loader will sort
+everything out.
+</para></listitem>
+</varlistentry>
+
+<varlistentry>
+<term>section <parameter>name</parameter></term>
+<listitem><para>
+
+This will cause the section <parameter>name</parameter> to load after the previously listed
+section.
+</para></listitem></varlistentry>
+<varlistentry>
+<term>exec <parameter>addr or sym</parameter></term>
+<listitem>
+<para>
+This will cause the execution address (entry point) to be the address
+specified (in hex) or the specified symbol name. The symbol name must
+match a symbol that is exported by one of the object files being linked.
+This has no effect for targets that do not encode the entry point into the
+resulting file. If not specified, the entry point is assumed to be address 0
+which is probably not what you want. The default link scripts for targets
+that support this directive automatically starts at the beginning of the
+first section (usually "init" or "code") that is emitted in the binary.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>pad <parameter>size</parameter></term>
+<listitem><para>
+This will cause the output file to be padded with NUL bytes to be exactly
+<parameter>size</parameter> bytes in length. This only makes sense for a raw target.
+</para>
+</listitem>
+</varlistentry>
+</variablelist>
+
+
+
+</section>
+
+</chapter>
+
+<chapter>
+<title>Libraries and LWAR</title>
+
+<para>
+LWTOOLS also includes a tool for managing libraries. These are analogous to
+the static libraries created with the "ar" tool on POSIX systems. Each library
+file contains one or more object files. The linker will treat the object
+files within a library as though they had been specified individually on
+the command line except when resolving external references. External references
+are looked up first within the object files within the library and then, if
+not found, the usual lookup based on the order the files are specified on
+the command line occurs.
+</para>
+
+<para>
+The tool for creating these libary files is called LWAR.
+</para>
+
+<section>
+<title>Command Line Options</title>
+<para>
+The binary for LWAR is called "lwar". Note that the binary is in lower
+case. The options lwar understands are listed below. For archive manipulation
+options, the first non-option argument is the name of the archive. All other
+non-option arguments are the names of files to operate on.
+</para>
+
+<variablelist>
+<varlistentry>
+<term><option>--add</option></term>
+<term><option>-a</option></term>
+<listitem>
+<para>
+This option specifies that an archive is going to have files added to it.
+If the archive does not already exist, it is created. New files are added
+to the end of the archive.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><option>--create</option></term>
+<term><option>-c</option></term>
+<listitem>
+<para>
+This option specifies that an archive is going to be created and have files
+added to it. If the archive already exists, it is truncated.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><option>--merge</option></term>
+<term><option>-m</option></term>
+<listitem>
+<para>
+If specified, any files specified to be added to an archive will be checked
+to see if they are archives themselves. If so, their constituent members are
+added to the archive. This is useful for avoiding archives containing archives.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><option>--list</option></term>
+<term><option>-l</option></term>
+<listitem>
+<para>
+This will display a list of the files contained in the archive.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><option>--debug</option></term>
+<term><option>-d</option></term>
+<listitem>
+<para>
+This option increases the debugging level. It is only useful for LWTOOLS
+developers.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><option>--help</option></term>
+<term><option>-?</option></term>
+<listitem>
+<para>
+This provides a listing of command line options and a brief description
+of each.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><option>--usage</option></term>
+<listitem>
+<para>
+This will display a usage summary
+of each command line option.
+</para>
+</listitem>
+</varlistentry>
+
+
+<varlistentry>
+<term><option>--version</option></term>
+<term><option>-V</option></term>
+<listitem>
+<para>
+This will display the version of LWLINK.
+of each.
+</para>
+</listitem>
+</varlistentry>
+
+</section>
+
+</chapter>
+
+<chapter id="objchap">
+<title>Object Files</title>
+<para>
+LWTOOLS uses a proprietary object file format. It is proprietary in the sense
+that it is specific to LWTOOLS, not that it is a hidden format. It would be
+hard to keep it hidden in an open source tool chain anyway. This chapter
+documents the object file format.
+</para>
+
+<para>
+An object file consists of a series of sections each of which contains a
+list of exported symbols, a list of incomplete references, and a list of
+"local" symbols which may be used in calculating incomplete references. Each
+section will obviously also contain the object code.
+</para>
+
+<para>
+Exported symbols must be completely resolved to an address within the
+section it is exported from. That is, an exported symbol must be a constant
+rather than defined in terms of other symbols.</para>
+
+<para>
+Each object file starts with a magic number and version number. The magic
+number is the string "LWOBJ16" for this 16 bit object file format. The only
+defined version number is currently 0. Thus, the first 8 bytes of the object
+file are <code>4C574F424A313600</code>
+</para>
+
+<para>
+Each section has the following items in order:
+</para>
+
+<itemizedlist>
+<listitem><para>section name</para></listitem>
+<listitem><para>flags</para></listitem>
+<listitem><para>list of local symbols (and addresses within the section)</para></listitem>
+<listitem><para>list of exported symbols (and addresses within the section)</para></listitem>
+<listitem><para>list of incomplete references along with the expressions to calculate them</para></listitem>
+<listitem><para>the actual object code (for non-BSS sections)</para></listitem>
+</itemizedlist>
+
+<para>
+The section starts with the name of the section with a NUL termination
+followed by a series of flag bytes terminated by NUL. There are only two
+flag bytes defined. A NUL (0) indicates no more flags and a value of 1
+indicates the section is a BSS section. For a BSS section, no actual
+code is included in the object file.
+</para>
+
+<para>
+Either a NULL section name or end of file indicate the presence of no more
+sections.
+</para>
+
+<para>
+Each entry in the exported and local symbols table consists of the symbol
+(NUL terminated) followed by two bytes which contain the value in big endian
+order. The end of a symbol table is indicated by a NULL symbol name.
+</para>
+
+<para>
+Each entry in the incomplete references table consists of an expression
+followed by a 16 bit offset where the reference goes. Expressions are
+defined as a series of terms up to an "end of expression" term. Each term
+consists of a single byte which identifies the type of term (see below)
+followed by any data required by the term. Then end of the list is flagged
+by a NULL expression (only an end of expression term).
+</para>
+
+<table frame="all"><title>Object File Term Types</title>
+<tgroup cols="2">
+<thead>
+<row>
+<entry>TERMTYPE</entry>
+<entry>Meaning</entry>
+</row>
+</thead>
+<tbody>
+<row>
+<entry>00</entry>
+<entry>end of expression</entry>
+</row>
+
+<row>
+<entry>01</entry>
+<entry>integer (16 bit in big endian order follows)</entry>
+</row>
+<row>
+<entry>02</entry>
+<entry>	external symbol reference (NUL terminated symbol name follows)</entry>
+</row>
+
+<row>
+<entry>03</entry>
+<entry>local symbol reference (NUL terminated symbol name follows)</entry>
+</row>
+
+<row>
+<entry>04</entry>
+<entry>operator (1 byte operator number)</entry>
+</row>
+<row>
+<entry>05</entry>
+<entry>section base address reference</entry>
+</row>
+
+<row>
+<entry>FF</entry>
+<entry>This term will set flags for the expression. Each one of these terms will set a single flag. All of them should be specified first in an expression. If they are not, the behaviour is undefined. The byte following is the flag. Flag 01 indicates an 8 bit relocation. Flag 02 indicates a zero-width relocation (see the EXTDEP pseudo op in LWASM).</entry>
+</row>
+</tbody>
+</tgroup>
+</table>
+
+
+<para>
+External references are resolved using other object files while local
+references are resolved using the local symbol table(s) from this file. This
+allows local symbols that are not exported to have the same names as
+exported symbols or external references.
+</para>
+
+<table frame="all"><title>Object File Operator Numbers</title>
+<tgroup cols="2">
+<thead>
+<row>
+<entry>Number</entry>
+<entry>Operator</entry>
+</row>
+</thead>
+<tbody>
+<row>
+<entry>01</entry>
+<entry>addition (+)</entry>
+</row>
+<row>
+<entry>02</entry>
+<entry>subtraction (-)</entry>
+</row>
+<row>
+<entry>03</entry>
+<entry>multiplication (*)</entry>
+</row>
+<row>
+<entry>04</entry>
+<entry>division (/)</entry>
+</row>
+<row>
+<entry>05</entry>
+<entry>modulus (%)</entry>
+</row>
+<row>
+<entry>06</entry>
+<entry>integer division (\) (same as division)</entry>
+</row>
+
+<row>
+<entry>07</entry>
+<entry>bitwise and</entry>
+</row>
+
+<row>
+<entry>08</entry>
+<entry>bitwise or</entry>
+</row>
+
+<row>
+<entry>09</entry>
+<entry>bitwise xor</entry>
+</row>
+
+<row>
+<entry>0A</entry>
+<entry>boolean and</entry>
+</row>
+
+<row>
+<entry>0B</entry>
+<entry>boolean or</entry>
+</row>
+
+<row>
+<entry>0C</entry>
+<entry>unary negation, 2's complement (-)</entry>
+</row>
+
+<row>
+<entry>0D</entry>
+<entry>unary 1's complement (^)</entry>
+</row>
+</tbody>
+</tgroup>
+</table>
+
+<para>
+An expression is represented in a postfix manner with both operands for
+binary operators preceding the operator and the single operand for unary
+operators preceding the operator.
+</para>
+
+</chapter>
+</book>
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/docs/manual/README	Wed Jan 19 22:27:17 2011 -0700
@@ -0,0 +1,3 @@
+This folder contains the manual in various forms which may or may
+not be present unless this is an actual release. Even then, they may
+or may not be present.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/docs/readme-4.0.txt	Wed Jan 19 22:27:17 2011 -0700
@@ -0,0 +1,17 @@
+With LWTOOLS 4.0, a substantial reorganization of the project has occurred.
+This document serves to explain the reasoning behind the various changes.
+
+The most obvious change is that the gnu auto tools have been eliminated.
+While they proved useful for initial distribution of the software,
+particularly for construction of the win32 binaries, they have since proved
+to add an unacceptable level of complexity to every aspect of development
+from merely tinkering with source files to doing complete releases. Thus,
+the auto tools have been ditched in favour of specific hand tuned help where
+required.
+
+The other substantial change is that the source code repository has been
+recreated from scratch. The old repository was full of cruft from various
+revision control systems that were used over the years (CVS, Subversion, and
+Mercurial). It was felt that starting a new Mercurial repository with a
+completely clean slate would simplify matters substantially. Thus, the old
+repository now serves as an archive.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lwar/add.c	Wed Jan 19 22:27:17 2011 -0700
@@ -0,0 +1,193 @@
+/*
+add.c
+Copyright © 2009 William Astle
+
+This file is part of LWAR.
+
+LWAR 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 3 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, see <http://www.gnu.org/licenses/>.
+
+
+Implements the program startup code
+
+*/
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "lwar.h"
+
+void do_add(void)
+{
+	FILE *f;
+	unsigned char buf[8];
+	long l;
+	int c;
+	FILE *f2;
+	int i;
+	
+	f = fopen(archive_file, "r+");
+	if (!f)
+	{
+		if (errno == ENOENT)
+		{
+			f = fopen(archive_file, "w");
+			if (f)
+			{
+				fputs("LWAR1V", f);
+				goto doadd;
+			}
+		}
+		perror("Cannot open archive file");
+	}
+	
+	fread(buf, 1, 6, f);
+	if (memcmp("LWAR1V", buf, 6))
+	{
+		fprintf(stderr, "%s is not a valid archive file.\n", archive_file);
+		exit(1);
+	}
+
+	for (;;)
+	{
+		c = fgetc(f);
+		if (c == EOF && ferror(f))
+		{
+			perror("Reading archive file");
+			exit(1);
+		}
+		if (c == EOF)
+			goto doadd;
+		
+		if (!c)
+		{
+			fseek(f, -1, SEEK_CUR);
+			goto doadd;
+		}
+		
+		// find the end of the file name
+		while (c)
+		{
+			c = fgetc(f);
+			if (c == EOF || ferror(f))
+			{
+				fprintf(stderr, "Bad archive file\n");
+				exit(1);
+			}
+		}
+		
+		// get length of archive member
+		l = 0;
+		c = fgetc(f);
+		l = c << 24;
+		c = fgetc(f);
+		l |= c << 16;
+		c = fgetc(f);
+		l |= c << 8;
+		c = fgetc(f);
+		l |= c;
+		
+		fseek(f, l, SEEK_CUR);
+	}
+	// back up to the NUL byte at the end of the file
+	fseek(f, -1, SEEK_CUR);
+doadd:
+	for (i = 0; i < nfiles; i++)
+	{
+		f2 = fopen(files[i], "r");
+		if (!f2)
+		{
+			fprintf(stderr, "Cannot open file %s:", files[i]);
+			perror("");
+			exit(1);
+		}
+		fread(buf, 1, 6, f2);
+		if (mergeflag && !memcmp("LWAR1V", buf, 6))
+		{
+			// add archive contents...
+			for (;;)
+			{
+				c = fgetc(f2);
+				if (c == EOF || ferror(f2))
+				{
+					perror("Reading input archive file");
+					exit(1);
+				}
+				if (c == EOF)
+					break;
+		
+				if (!c)
+				{
+					break;
+				}
+		
+				// find the end of the file name
+				while (c)
+				{
+					fputc(c, f);
+					c = fgetc(f2);
+					if (c == EOF || ferror(f))
+					{
+						fprintf(stderr, "Bad input archive file\n");
+						exit(1);
+					}
+				}
+				fputc(0, f);
+				
+				// get length of archive member
+				l = 0;
+				c = fgetc(f2);
+				fputc(c, f);
+				l = c << 24;
+				c = fgetc(f2);
+				fputc(c, f);
+				l |= c << 16;
+				c = fgetc(f2);
+				fputc(c, f);
+				l |= c << 8;
+				c = fgetc(f2);
+				fputc(c, f);
+				l |= c;
+		
+				while (l)
+				{
+					c = fgetc(f2);
+					fputc(c, f);
+					l--;
+				}
+			}
+			
+			fclose(f2);
+			continue;
+		}
+		fseek(f2, 0, SEEK_END);
+		l = ftell(f2);
+		fseek(f2, 0, SEEK_SET);
+		fputs(files[i], f);
+		fputc(0, f);
+		fputc(l >> 24, f);
+		fputc((l >> 16) & 0xff, f);
+		fputc((l >> 8) & 0xff, f);
+		fputc(l & 0xff, f);
+		while (l)
+		{
+			c = fgetc(f2);
+			fputc(c, f);
+			l--;
+		}
+	}
+	
+	// flag end of file
+	fputc(0, f);	
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lwar/extract.c	Wed Jan 19 22:27:17 2011 -0700
@@ -0,0 +1,121 @@
+/*
+extract.c
+Copyright © 2009 William Astle
+
+This file is part of LWAR.
+
+LWAR 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 3 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, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "lwar.h"
+
+void do_extract(void)
+{
+	FILE *f;
+	char buf[8];
+	long l;
+	int c;
+	char fnbuf[1024];
+	int i;
+	FILE *nf;
+	
+	f = fopen(archive_file, "r");
+	if (!f)
+	{
+		perror("Opening archive file");
+		exit(1);
+	}
+	
+	fread(buf, 1, 6, f);
+	if (memcmp("LWAR1V", buf, 6))
+	{
+		fprintf(stderr, "%s is not a valid archive file.\n", archive_file);
+		exit(1);
+	}
+
+	for (;;)
+	{
+		c = fgetc(f);
+		if (ferror(f))
+		{
+			perror("Reading archive file");
+			exit(1);
+		}
+		if (c == EOF)
+			return;
+		
+		
+		// find the end of the file name
+		if (!c)
+			return;
+		
+		i = 0;
+		while (c)
+		{
+			fnbuf[i++] = c;
+			c = fgetc(f);
+			if (c == EOF || ferror(f))
+			{
+				fprintf(stderr, "Bad archive file\n");
+				exit(1);
+			}
+		}
+		fnbuf[i] = 0;
+		
+		// get length of archive member
+		l = 0;
+		c = fgetc(f);
+		l = c << 24;
+		c = fgetc(f);
+		l |= c << 16;
+		c = fgetc(f);
+		l |= c << 8;
+		c = fgetc(f);
+		l |= c;
+		
+		for (i = 0; i < nfiles; i++)
+		{
+			if (!strcmp(files[i], fnbuf))
+				break;
+		}
+		if (i < nfiles || nfiles == 0)
+		{
+			// extract the file
+			nf = fopen(fnbuf, "w");
+			if (!nf)
+			{
+				fprintf(stderr, "Cannot extract '%s': %s\n", fnbuf, strerror(errno));
+				exit(1);
+			}
+			while (l)
+			{
+				c = fgetc(f);
+				fputc(c, nf);
+				l--;
+			}
+			fclose(nf);
+		}
+		else
+		{
+			// skip the file
+			fseek(f, l, SEEK_CUR);
+		}
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lwar/list.c	Wed Jan 19 22:27:17 2011 -0700
@@ -0,0 +1,93 @@
+/*
+list.c
+Copyright © 2009 William Astle
+
+This file is part of LWAR.
+
+LWAR 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 3 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, see <http://www.gnu.org/licenses/>.
+
+
+Implements the program startup code
+
+*/
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "lwar.h"
+
+void do_list(void)
+{
+	FILE *f;
+	char buf[8];
+	long l;
+	int c;
+		
+	f = fopen(archive_file, "r");
+	if (!f)
+	{
+		perror("Opening archive file");
+		exit(1);
+	}
+	
+	fread(buf, 1, 6, f);
+	if (memcmp("LWAR1V", buf, 6))
+	{
+		fprintf(stderr, "%s is not a valid archive file.\n", archive_file);
+		exit(1);
+	}
+
+	for (;;)
+	{
+		c = fgetc(f);
+		if (ferror(f))
+		{
+			perror("Reading archive file");
+			exit(1);
+		}
+		if (c == EOF)
+			return;
+		
+		
+		// find the end of the file name
+		if (!c)
+			return;
+
+		while (c)
+		{
+			putchar(c);
+			c = fgetc(f);
+			if (c == EOF || ferror(f))
+			{
+				fprintf(stderr, "Bad archive file\n");
+				exit(1);
+			}
+		}
+		
+		// get length of archive member
+		l = 0;
+		c = fgetc(f);
+		l = c << 24;
+		c = fgetc(f);
+		l |= c << 16;
+		c = fgetc(f);
+		l |= c << 8;
+		c = fgetc(f);
+		l |= c;
+		printf(": %04lx bytes\n", l);
+		fseek(f, l, SEEK_CUR);
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lwar/lwar.c	Wed Jan 19 22:27:17 2011 -0700
@@ -0,0 +1,53 @@
+/*
+lwar.c
+Copyright © 2009 William Astle
+
+This file is part of LWAR.
+
+LWAR 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 3 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, see <http://www.gnu.org/licenses/>.
+
+
+Implements the program startup code
+
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#define __lwar_c_seen__
+#include "lwar.h"
+#include "util.h"
+
+typedef struct
+{
+	FILE *f;
+} arhandle_real;
+
+int debug_level = 0;
+int operation = 0;
+int nfiles = 0;
+char *archive_file = NULL;
+int mergeflag = 0;
+
+char **files = NULL;
+
+void add_file_name(char *fn)
+{
+	files = lw_realloc(files, sizeof(char *) * (nfiles + 1));
+	files[nfiles] = fn;
+	nfiles++;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lwar/lwar.h	Wed Jan 19 22:27:17 2011 -0700
@@ -0,0 +1,62 @@
+/*
+lwar.h
+Copyright © 2009 William Astle
+
+This file is part of LWAR.
+
+LWAR 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 3 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, see <http://www.gnu.org/licenses/>.
+
+Contains the main defs used by the linker
+*/
+
+
+#define LWAR_OP_LIST	1
+#define LWAR_OP_ADD		2
+#define LWAR_OP_REMOVE	3
+#define LWAR_OP_CREATE	4
+#define LWAR_OP_EXTRACT	5
+#define LWAR_OP_REPLACE	6
+
+#ifndef __lwar_h_seen__
+#define __lwar_h_seen__
+
+#ifndef __lwar_c_seen__
+
+extern char *archive_file;
+extern int debug_level;
+extern int operation;
+extern int nfiles;
+extern char **files;
+extern int mergeflag;
+
+//typedef void * ARHANDLE;
+
+#define AR_MODE_RD		1
+#define AR_MODE_WR		2
+#define AR_MODE_RW		3
+#define AR_MODE_CREATE	4
+
+
+#define __lwar_E__ extern
+#else
+#define __lwar_E__
+#endif // __lwar_c_seen__
+
+__lwar_E__ void add_file_name(char *fn);
+
+//__lwar_E__ ARHANDLE open_archive(char *fn, int mode);
+
+#undef __lwar_E__
+
+#endif //__lwar_h_seen__
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lwar/main.c	Wed Jan 19 22:27:17 2011 -0700
@@ -0,0 +1,206 @@
+/*
+main.c
+Copyright © 2009 William Astle
+
+This file is part of LWAR.
+
+LWAR 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 3 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, see <http://www.gnu.org/licenses/>.
+
+
+Implements the program startup code
+
+*/
+
+#include <argp.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "lwar.h"
+
+// command line option handling
+const char *argp_program_version = "LWAR from " PACKAGE_STRING;
+const char *argp_program_bug_address = PACKAGE_BUGREPORT;
+char *program_name;
+
+static error_t parse_opts(int key, char *arg, struct argp_state *state)
+{
+	switch (key)
+	{
+	case 'd':
+		// debug
+		debug_level++;
+		break;
+	
+	case 'a':
+		// add members
+		operation = LWAR_OP_ADD;
+		break;
+	
+	case 'c':
+		// create archive
+		operation = LWAR_OP_CREATE;
+		break;
+	
+	case 'm':
+		mergeflag = 1;
+		break;
+
+	case 'r':
+		// replace members
+		operation = LWAR_OP_REPLACE;
+		break;
+	
+	case 'l':
+		// list members
+		operation = LWAR_OP_LIST;
+		break;
+	
+	case 'x':
+		// extract members
+		operation = LWAR_OP_EXTRACT;
+		break;
+
+	case ARGP_KEY_ARG:
+		if (archive_file)
+		{
+			// add archive member to list
+			add_file_name(arg);
+		}
+		else
+			archive_file = arg;
+		break;
+		
+	default:
+		return ARGP_ERR_UNKNOWN;
+	}
+	return 0;
+}
+
+static struct argp_option options[] =
+{
+	{ "replace",	'r',	0,		0,
+				"Add or replace archive members" },
+	{ "extract",	'x',	0,		0,
+				"Extract members from the archive" },
+	{ "add",		'a',	0,		0,
+				"Add members to the archive" },
+	{ "list",		'l',	0,		0,
+				"List the contents of the archive" },
+	{ "create",		'c',	0,		0,
+				"Create new archive (or truncate existing one)" },
+	{ "merge",		'm',	0,		0,
+				"Add the contents of archive arguments instead of the archives themselves" },
+	{ "debug",		'd',	0,		0,
+				"Set debug mode"},
+	{ 0 }
+};
+
+static struct argp argp =
+{
+	options,
+	parse_opts,
+	"<archive> [<file> ...]",
+	"LWAR, a library file manager for LWLINK"
+};
+
+extern void do_list(void);
+extern void do_add(void);
+extern void do_remove(void);
+extern void do_replace(void);
+extern void do_extract(void);
+
+// main function; parse command line, set up assembler state, and run the
+// assembler on the first file
+int main(int argc, char **argv)
+{
+	program_name = argv[0];
+	argp_parse(&argp, argc, argv, 0, 0, NULL);
+	if (archive_file == NULL)
+	{
+		fprintf(stderr, "You must specify an archive file.\n");
+		exit(1);
+	}
+
+	if (operation == 0)
+	{
+		fprintf(stderr, "You must specify an operation.\n");
+		exit(1);
+	}
+
+	if (operation == LWAR_OP_LIST || operation == LWAR_OP_REMOVE || operation == LWAR_OP_EXTRACT)
+	{
+		struct stat stbuf;
+		// make sure the archive exists
+		if (stat(archive_file, &stbuf) < 0)
+		{
+			fprintf(stderr, "Cannot open archive file %s:\n", archive_file);
+			perror("");
+			exit(2);
+		}
+	}
+	if (operation == LWAR_OP_CREATE)
+	{
+		struct stat stbuf;
+		// check if the archive exists
+		if (stat(archive_file, &stbuf) < 0)
+		{
+			if (errno != ENOENT)
+			{
+				fprintf(stderr, "Cannot create archive file %s:\n", archive_file);
+				perror("");
+				exit(2);
+			}
+		}
+		else
+		{
+			if (unlink(archive_file) < 0)
+			{
+				fprintf(stderr, "Cannot create archive file %s:\n", archive_file);
+				perror("");
+				exit(2);
+			}
+				
+		}
+	}
+	
+	switch (operation)
+	{
+	case LWAR_OP_LIST:
+		do_list();
+		break;
+	
+	case LWAR_OP_ADD:
+	case LWAR_OP_CREATE:
+		do_add();
+		break;
+	
+	case LWAR_OP_REMOVE:
+		do_remove();
+		break;
+	
+	case LWAR_OP_REPLACE:
+		do_replace();
+		break;
+	
+	case LWAR_OP_EXTRACT:
+		do_extract();
+		break;
+	}
+
+	exit(0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lwar/remove.c	Wed Jan 19 22:27:17 2011 -0700
@@ -0,0 +1,29 @@
+/*
+remove.c
+Copyright © 2009 William Astle
+
+This file is part of LWAR.
+
+LWAR 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 3 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, see <http://www.gnu.org/licenses/>.
+
+
+Implements the program startup code
+
+*/
+
+#include "lwar.h"
+
+void do_remove(void)
+{
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lwar/replace.c	Wed Jan 19 22:27:17 2011 -0700
@@ -0,0 +1,241 @@
+/*
+replace.c
+Copyright © 2009 William Astle
+
+This file is part of LWAR.
+
+LWAR 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 3 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, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "lwar.h"
+
+void do_replace(void)
+{
+	FILE *f;
+	FILE *nf;
+	unsigned char buf[8];
+	long l;
+	int c;
+	FILE *f2;
+	int i;
+	char fnbuf[1024];
+	char fnbuf2[1024];
+		
+	sprintf(fnbuf, "%s.tmp", archive_file);
+	
+	f = fopen(archive_file, "r+");
+	if (!f)
+	{
+		if (errno == ENOENT)
+		{
+			nf = fopen(fnbuf, "w");
+			if (nf)
+			{
+				fputs("LWAR1V", nf);
+				goto doadd;
+			}
+		}
+		perror("Cannot open archive file");
+	}
+	
+	fread(buf, 1, 6, f);
+	if (memcmp("LWAR1V", buf, 6))
+	{
+		fprintf(stderr, "%s is not a valid archive file.\n", archive_file);
+		exit(1);
+	}
+
+	nf = fopen(fnbuf, "w");
+	if (!nf)
+	{
+		perror("Cannot create temp archive file");
+		exit(1);
+	}
+
+	fputs("LWAR1V", nf);
+
+	for (;;)
+	{
+		c = fgetc(f);
+		if (c == EOF && ferror(f))
+		{
+			perror("Reading archive file");
+			exit(1);
+		}
+		if (c == EOF)
+			goto doadd;
+		
+		if (!c)
+		{
+			goto doadd;
+		}
+		
+		// find the end of the file name
+		i = 0;
+		while (c)
+		{
+			fnbuf2[i++] = c;
+			c = fgetc(f);
+			if (c == EOF || ferror(f))
+			{
+				fprintf(stderr, "Bad archive file\n");
+				exit(1);
+			}
+		}
+		fnbuf2[i] = 0;
+		
+		// get length of archive member
+		l = 0;
+		c = fgetc(f);
+		l = c << 24;
+		c = fgetc(f);
+		l |= c << 16;
+		c = fgetc(f);
+		l |= c << 8;
+		c = fgetc(f);
+		l |= c;
+		
+		// is it a file we are replacing? if so, do not copy it
+		for (i = 0; i < nfiles; i++)
+		{
+			if (!strcmp(files[i], fnbuf2))
+				break;
+		}
+		if (i < nfiles)
+		{
+			fseek(f, l, SEEK_CUR);
+		}
+		else
+		{
+			// otherwise, copy it
+			fprintf(nf, "%s", fnbuf2);
+			fputc(0, nf);
+			fputc(l >> 24, nf);
+			fputc((l >> 16) & 0xff, nf);
+			fputc((l >> 8) & 0xff, nf);
+			fputc(l & 0xff, nf);
+			while (l)
+			{
+				c = fgetc(f);
+				fputc(c, nf);
+				l--;
+			}
+		}
+	}
+	
+	// done with the original file
+	fclose(f);
+doadd:
+	for (i = 0; i < nfiles; i++)
+	{
+		f2 = fopen(files[i], "r");
+		if (!f2)
+		{
+			fprintf(stderr, "Cannot open file %s:", files[i]);
+			perror("");
+			exit(1);
+		}
+		fread(buf, 1, 6, f2);
+		if (mergeflag && !memcmp("LWAR1V", buf, 6))
+		{
+			// add archive contents...
+			for (;;)
+			{
+				c = fgetc(f2);
+				if (c == EOF || ferror(f2))
+				{
+					perror("Reading input archive file");
+					exit(1);
+				}
+				if (c == EOF)
+					break;
+		
+				if (!c)
+				{
+					break;
+				}
+		
+				// find the end of the file name
+				while (c)
+				{
+					fputc(c, nf);
+					c = fgetc(f2);
+					if (c == EOF || ferror(f))
+					{
+						fprintf(stderr, "Bad input archive file\n");
+						exit(1);
+					}
+				}
+				fputc(0, nf);
+				
+				// get length of archive member
+				l = 0;
+				c = fgetc(f2);
+				fputc(c, nf);
+				l = c << 24;
+				c = fgetc(f2);
+				fputc(c, nf);
+				l |= c << 16;
+				c = fgetc(f2);
+				fputc(c, nf);
+				l |= c << 8;
+				c = fgetc(f2);
+				fputc(c, nf);
+				l |= c;
+		
+				while (l)
+				{
+					c = fgetc(f2);
+					fputc(c, nf);
+					l--;
+				}
+			}
+			
+			fclose(f2);
+			continue;
+		}
+		fseek(f2, 0, SEEK_END);
+		l = ftell(f2);
+		fseek(f2, 0, SEEK_SET);
+		fputs(files[i], nf);
+		fputc(0, nf);
+		fputc(l >> 24, nf);
+		fputc((l >> 16) & 0xff, nf);
+		fputc((l >> 8) & 0xff, nf);
+		fputc(l & 0xff, nf);
+		while (l)
+		{
+			c = fgetc(f2);
+			fputc(c, nf);
+			l--;
+		}
+	}
+	
+	// flag end of file
+	fputc(0, nf);
+	
+	fclose(nf);
+	
+	if (rename(fnbuf, archive_file) < 0)
+	{
+		perror("Cannot replace old archive file");
+		unlink(fnbuf);
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lwar/rules.make	Wed Jan 19 22:27:17 2011 -0700
@@ -0,0 +1,5 @@
+dirname := $(dir $(lastword $(MAKEFILE_LIST)))
+
+lwar_srcs_local := add.c extract.c list.c lwar.c main.c remove.c replace.c util.c
+
+lwar_srcs := $(lwar_srcs) $(addprefix $(dirname),$(lwar_srcs_local))
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lwar/util.c	Wed Jan 19 22:27:17 2011 -0700
@@ -0,0 +1,87 @@
+/*
+util.c
+Copyright © 2009 William Astle
+
+This file is part of LWAR.
+
+LWAR 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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+Utility functions
+*/
+
+#define __util_c_seen__
+
+#include <malloc.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "util.h"
+
+void *lw_malloc(int size)
+{
+	void *ptr;
+	
+	ptr = malloc(size);
+	if (!ptr)
+	{
+		// bail out; memory allocation error
+		fprintf(stderr, "lw_malloc(): Memory allocation error\n");
+		exit(1);
+	}
+	return ptr;
+}
+
+void *lw_realloc(void *optr, int size)
+{
+	void *ptr;
+	
+	if (size == 0)
+	{
+		lw_free(optr);
+		return;
+	}
+	
+	ptr = realloc(optr, size);
+	if (!ptr)
+	{
+		fprintf(stderr, "lw_realloc(): memory allocation error\n");
+		exit(1);
+	}
+}
+
+void lw_free(void *ptr)
+{
+	if (ptr)
+		free(ptr);
+}
+
+char *lw_strdup(const char *s)
+{
+	char *d;
+	
+	if (!s)
+		return NULL;
+
+	d = strdup(s);
+	if (!d)
+	{
+		fprintf(stderr, "lw_strdup(): memory allocation error\n");
+		exit(1);
+	}
+	
+	return d;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lwar/util.h	Wed Jan 19 22:27:17 2011 -0700
@@ -0,0 +1,44 @@
+/*
+util.h
+Copyright © 2009 William Astle
+
+This file is part of LWAR.
+
+LWAR 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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+Utility functions
+*/
+
+#ifndef __util_h_seen__
+#define __util_h_seen__
+
+#ifndef __util_c_seen__
+#define __util_E__ extern
+#else
+#define __util_E__
+#endif
+
+// allocate memory
+__util_E__ void *lw_malloc(int size);
+__util_E__ void lw_free(void *ptr);
+__util_E__ void *lw_realloc(void *optr, int size);
+
+// string stuff
+__util_E__ char *lw_strdup(const char *s);
+
+#undef __util_E__
+
+#endif // __util_h_seen__
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lwasm/debug.c	Wed Jan 19 22:27:17 2011 -0700
@@ -0,0 +1,90 @@
+/*
+debug.c
+
+Copyright © 2010 William Astle
+
+This file is part of LWTOOLS.
+
+LWTOOLS 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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+
+#include "lwasm.h"
+#include "instab.h"
+
+/*
+
+Various debug utilities
+
+*/
+void dump_state(asmstate_t *as)
+{
+	line_t *cl;
+	exportlist_t *ex;
+	struct symtabe *s;
+	importlist_t *im;
+	struct line_expr_s *le;
+	lwasm_error_t *e;
+	
+	debug_message(as, 100, "Lines:");
+	
+	for (cl = as -> line_head; cl; cl = cl -> next)
+	{
+		debug_message(as, 100, "%p INSN %d (%s) LEN %d", cl, cl -> insn, (cl -> insn >= 0) ? instab[cl -> insn].opcode : "<none>", cl -> len);
+		debug_message(as, 100, "    ADDR: %s", lw_expr_print(cl -> addr));
+		debug_message(as, 100, "    PB: %02X; LINT: %X; LINT2: %X", cl -> pb, cl -> lint, cl -> lint2);
+		for (le = cl -> exprs; le; le = le -> next)
+		{
+			debug_message(as, 100, "    EXPR %d: %s", le -> id, lw_expr_print(le -> expr));
+		}
+		if (cl -> outputl > 0)
+		{
+			int i;
+			for (i = 0; i < cl -> outputl; i++)
+			{
+				debug_message(as, 100, "    OBYTE %02X: %02X", i, cl -> output[i]);
+			}
+		}
+		for (e = cl -> err; e; e = e -> next)
+		{
+			debug_message(as, 100, "    ERR: %s", e -> mess);
+		}
+		for (e = cl -> warn; e; e = e -> next)
+		{
+			debug_message(as, 100, "    WARN: %s", e -> mess);
+		}
+	}
+}
+
+void debug_message(asmstate_t *as, int level, const char *fmt, ...)
+{
+	va_list args;
+
+	if (as -> debug_level < level)
+		return;
+
+	if (as -> debug_file == NULL)
+		as -> debug_file = stderr;
+
+	va_start(args, fmt);
+	
+	fprintf(as -> debug_file, "DEBUG %03d: ", level);
+	vfprintf(as -> debug_file, fmt, args);
+	fputc('\n', as -> debug_file);
+
+	va_end(args);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lwasm/input.c	Wed Jan 19 22:27:17 2011 -0700
@@ -0,0 +1,413 @@
+/*
+input.c
+
+Copyright © 2010 William Astle
+
+This file is part of LWTOOLS.
+
+LWTOOLS 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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+This file is used to handle reading input files. It serves to encapsulate
+the entire input system to make porting to different media and systems
+less difficult.
+*/
+
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <lw_alloc.h>
+#include <lw_stringlist.h>
+#include <lw_string.h>
+#include "lwasm.h"
+
+/*
+Data type for storing input buffers
+*/
+
+enum input_types_e
+{
+	input_type_file,			// regular file, no search path
+	input_type_include,			// include path, start from "local"
+	input_type_string,			// input from a string
+
+	input_type_error			// invalid input type
+};
+
+struct input_stack
+{
+	struct input_stack *next;
+	int type;
+	void *data;
+	int data2;
+	char *filespec;
+};
+
+#define IS	((struct input_stack *)(as -> input_data))
+
+void input_init(asmstate_t *as)
+{
+	struct input_stack *t;
+
+	if (as -> file_dir)
+		lw_stack_destroy(as -> file_dir);
+	as -> file_dir = lw_stack_create(lw_free);
+	as -> includelist = lw_stack_create(lw_free);
+	lw_stringlist_reset(as -> input_files);
+	while (IS)
+	{
+		t = IS;
+		as -> input_data = IS -> next;
+		lw_free(t);
+	}
+}
+
+void input_pushpath(asmstate_t *as, char *fn)
+{
+	/* take apart fn into path and filename then push the path */
+	/* onto the current file path stack */
+	
+	/* also add it to the list of files included */
+	char *dn, *dp;
+	int o;
+
+	dn = lw_strdup(fn);
+	lw_stack_push(as -> includelist, dn);
+
+	dn = lw_strdup(fn);
+	dp = dn + strlen(dn);
+	
+	while (--dp != dn)
+	{
+		if (*dp == '/')
+			break;
+	}
+	if (*dp == '/')
+		*dp = '\0';
+	
+	if (dp == dn)
+	{
+		lw_free(dn);
+		dn = lw_strdup(".");
+		lw_stack_push(as -> file_dir, dn);
+		return;
+	}
+	dp = lw_strdup(dn);
+	lw_free(dn);
+	lw_stack_push(as -> file_dir, dp);
+}
+
+void input_openstring(asmstate_t *as, char *s, char *str)
+{
+	struct input_stack *t;
+	
+	t = lw_alloc(sizeof(struct input_stack));
+	t -> filespec = lw_strdup(s);
+
+	t -> type = input_type_string;
+	t -> data = lw_strdup(str);
+	t -> data2 = 0;
+	t -> next = IS;
+	as -> input_data = t;
+	t -> filespec = lw_strdup(s);
+}
+
+void input_open(asmstate_t *as, char *s)
+{
+	struct input_stack *t;
+	char *s2;
+	char *p, *p2;
+
+	t = lw_alloc(sizeof(struct input_stack));
+	t -> filespec = lw_strdup(s);
+
+	for (s2 = s; *s2 && (*s2 != ':'); s2++)
+		/* do nothing */ ;
+	if (!*s2)
+	{
+		t -> type = input_type_file;
+	}
+	else
+	{
+		char *ts;
+		
+		ts = lw_strndup(s, s2 - s);
+		s = s2 + 1;
+		if (!strcmp(ts, "include"))
+			t -> type = input_type_include;
+		else if (!strcmp(ts, "file"))
+			t -> type = input_type_file;
+		else
+			t -> type = input_type_error;
+		
+		lw_free(ts);
+	}
+	t -> next = as -> input_data;
+	as -> input_data = t;
+	
+	switch (IS -> type)
+	{
+	case input_type_include:
+		/* first check for absolute path and if so, skip path */
+		if (*s == '/')
+		{
+			/* absolute path */
+			IS -> data = fopen(s, "rb");
+			debug_message(as, 1, "Opening (abs) %s", s);
+			if (!IS -> data)
+			{
+				lw_error("Cannot open file '%s': %s", s, strerror(errno));
+			}
+			input_pushpath(as, s);
+			return;
+		}
+		
+		/* relative path, check relative to "current file" directory */
+		p = lw_stack_top(as -> file_dir);
+		0 == asprintf(&p2, "%s/%s", p, s);
+		debug_message(as, 1, "Open: (cd) %s\n", p2);
+		IS -> data = fopen(p2, "rb");
+		if (IS -> data)
+		{
+			input_pushpath(as, p2);
+			lw_free(p2);
+			return;
+		}
+		debug_message(as, 2, "Failed to open: (cd) %s (%s)\n", p2, strerror(errno));
+		lw_free(p2);
+
+		/* now check relative to entries in the search path */
+		lw_stringlist_reset(as -> include_list);
+		while (p = lw_stringlist_current(as -> include_list))
+		{
+			0 == asprintf(&p2, "%s/%s", p, s);
+		debug_message(as, 1, "Open (sp): %s\n", p2);
+			IS -> data = fopen(p2, "rb");
+			if (IS -> data)
+			{
+				input_pushpath(as, p2);
+				lw_free(p2);
+				return;
+			}
+		debug_message(as, 2, "Failed to open: (sp) %s (%s)\n", p2, strerror(errno));
+			lw_free(p2);
+			lw_stringlist_next(as -> include_list);
+		}
+		lw_error("Cannot open include file '%s': %s", s, strerror(errno));
+		break;
+		
+	case input_type_file:
+		debug_message(as, 1, "Opening (reg): %s\n", s);
+		IS -> data = fopen(s, "rb");
+
+		if (!IS -> data)
+		{
+			lw_error("Cannot open file '%s': %s", s, strerror(errno));
+		}
+		input_pushpath(as, s);
+		return;
+	}
+
+	lw_error("Cannot figure out how to open '%s'.", t -> filespec);
+}
+
+FILE *input_open_standalone(asmstate_t *as, char *s)
+{
+	char *s2;
+	FILE *fp;
+	char *p, *p2;
+
+	/* first check for absolute path and if so, skip path */
+	if (*s == '/')
+	{
+		/* absolute path */
+		debug_message(as, 2, "Open file (st abs) %s", s);
+		fp = fopen(s, "rb");
+		if (!fp)
+		{
+			return NULL;
+		}
+		return fp;
+	}
+
+	/* relative path, check relative to "current file" directory */
+	p = lw_stack_top(as -> file_dir);
+	0 == asprintf(&p2, "%s/%s", p, s);
+	debug_message(as, 2, "Open file (st cd) %s", p2);
+	fp = fopen(p2, "rb");
+	if (fp)
+	{
+		lw_free(p2);
+		return fp;
+	}
+	lw_free(p2);
+
+	/* now check relative to entries in the search path */
+	lw_stringlist_reset(as -> include_list);
+	while (p = lw_stringlist_current(as -> include_list))
+	{
+		0 == asprintf(&p2, "%s/%s", p, s);
+		debug_message(as, 2, "Open file (st ip) %s", p2);
+		fp = fopen(p2, "rb");
+		if (fp)
+		{
+			lw_free(p2);
+			return fp;
+		}
+		lw_free(p2);
+		lw_stringlist_next(as -> include_list);
+	}
+	
+	return NULL;
+}
+
+char *input_readline(asmstate_t *as)
+{
+	char *s;
+	char linebuff[2049];
+	int lbloc;
+	int eol = 0;
+	
+	/* if no file is open, open one */
+nextfile:
+	if (!IS) {
+		s = lw_stringlist_current(as -> input_files);
+		if (!s)
+			return NULL;
+		lw_stringlist_next(as -> input_files);
+		input_open(as, s);
+	}
+	
+	switch (IS -> type)
+	{
+	case input_type_file:
+	case input_type_include:
+		/* read from a file */
+		lbloc = 0;
+		for (;;)
+		{
+			int c, c2;
+			c = fgetc(IS -> data);
+			if (c == EOF)
+			{
+				if (lbloc == 0)
+				{
+					struct input_stack *t;
+					fclose(IS -> data);
+					lw_free(lw_stack_pop(as -> file_dir));
+					lw_free(IS -> filespec);
+					t = IS -> next;
+					lw_free(IS);
+					as -> input_data = t;
+					goto nextfile;
+				}
+				linebuff[lbloc] = '\0';
+				eol = 1;
+			}
+			else if (c == '\r')
+			{
+				linebuff[lbloc] = '\0';
+				eol = 1;
+				c2 = fgetc(IS -> data);
+				if (c2 == EOF)
+					c = EOF;  
+				else if (c2 != '\n')
+					ungetc(c2, IS -> data);  
+			}
+			else if (c == '\n')
+			{
+				linebuff[lbloc] = '\0';
+				eol = 1;
+				c2 = fgetc(IS -> data);
+				if (c2 == EOF)
+					c = EOF;  
+				else if (c2 != '\r')
+					ungetc(c2, IS -> data);  
+			}
+			else
+			{
+				if (lbloc < 2048)
+					linebuff[lbloc++] = c;
+			}
+			if (eol)
+			{
+				s = lw_strdup(linebuff);
+				return s;
+			}
+		}
+
+	case input_type_string:
+		/* read from a string */
+		if (((char *)(IS -> data))[IS -> data2] == '\0')
+		{
+			struct input_stack *t;
+			lw_free(IS -> data);
+			lw_free(IS -> filespec);
+			t = IS -> next;
+			lw_free(IS);
+			as -> input_data = t;
+			goto nextfile;
+		}
+		s = (char *)(IS -> data);
+		lbloc = 0;
+		for (;;)
+		{
+			int c;
+			c = s[IS -> data2];
+			if (c)
+				IS -> data2++;
+			if (c == '\0')
+			{
+				linebuff[lbloc] = '\0';
+				eol = 1;
+			}
+			else if (c == '\r')
+			{
+				linebuff[lbloc] = '\0';
+				eol = 1;
+				if (s[IS -> data2] == '\n')
+					IS -> data2++;
+			}
+			else if (c == '\n')
+			{
+				linebuff[lbloc] = '\0';
+				eol = 1;
+				if (s[IS -> data2] == '\r')
+					IS -> data2++;
+			}
+			else
+			{
+				if (lbloc < 2048)
+					linebuff[lbloc++] = c;
+			}
+			if (eol)
+			{
+				s = lw_strdup(linebuff);
+				return s;
+			}
+		}
+	
+	default:
+		lw_error("Problem reading from unknown input type");
+	}
+}
+
+char *input_curspec(asmstate_t *as)
+{
+	if (IS)
+		return IS -> filespec;
+	return NULL;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lwasm/input.h	Wed Jan 19 22:27:17 2011 -0700
@@ -0,0 +1,34 @@
+/*
+input.h
+
+Copyright © 2010 William Astle
+
+This file is part of LWTOOLS.
+
+LWTOOLS 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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef ___input_h_seen___
+#define ___input_h_seen___
+
+#include "lwasm.h"
+
+extern void input_init(asmstate_t *as);
+extern void input_openstring(asmstate_t *as, char *s, char *str);
+extern void input_open(asmstate_t *as, char *s);
+extern char *input_readline(asmstate_t *as);
+extern char *input_curspec(asmstate_t *as);
+extern FILE *input_open_standalone(asmstate_t *as, char *s);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lwasm/insn_bitbit.c	Wed Jan 19 22:27:17 2011 -0700
@@ -0,0 +1,146 @@
+/*
+insn_bitbit.c
+Copyright © 2009 William Astle
+
+This file is part of LWASM.
+
+LWASM 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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdlib.h>
+
+#include <lw_expr.h>
+
+#include "lwasm.h"
+#include "instab.h"
+
+// these instructions cannot tolerate external references
+PARSEFUNC(insn_parse_bitbit)
+{
+	int r;
+	lw_expr_t e;
+	int v1;
+	int tv;
+
+	r = toupper(*(*p)++);
+	if (r == 'A')
+		r = 1;
+	else if (r == 'B')
+		r = 2;
+	else if (r == 'C' && toupper(**p) == 'C')
+	{
+		r = 0;
+		(*p)++;
+	}
+	else
+	{
+		lwasm_register_error(as, l, "Bad register");
+		return;
+	}
+	
+	if (*(*p)++ != ',')
+	{
+		lwasm_register_error(as, l, "Bad operand");
+		return;
+	}
+	e = lwasm_parse_expr(as, p);
+	if (!e)
+	{
+		lwasm_register_error(as, l, "Bad operand");
+		return;
+	}
+	lwasm_save_expr(l, 0, e);
+	if (*(*p)++ != ',')
+	{
+		lwasm_register_error(as, l, "Bad operand");
+		return;
+	}
+
+	e = lwasm_parse_expr(as, p);
+	if (!e)
+	{
+		lwasm_register_error(as, l, "Bad operand");
+		return;
+	}
+	lwasm_save_expr(l, 1, e);
+
+	if (*(*p)++ != ',')
+	{
+		lwasm_register_error(as, l, "Bad operand");
+		return;
+	}
+
+	// ignore base page address modifier
+	if (**p == '<')
+		(*p)++;
+			
+	e = lwasm_parse_expr(as, p);
+	if (!e)
+	{
+		lwasm_register_error(as, l, "Bad operand");
+		return;
+	}
+	lwasm_save_expr(l, 2, e);
+
+	l -> lint = r;
+	l -> len = OPLEN(instab[l -> insn].ops[0]) + 2;
+}
+
+EMITFUNC(insn_emit_bitbit)
+{
+	int v1, v2;
+	lw_expr_t e;
+	
+	e = lwasm_fetch_expr(l, 0);
+	if (!lw_expr_istype(e, lw_expr_type_int))
+	{
+		lwasm_register_error(as, l, "Bit number must be fully resolved");
+		return;
+	}
+	v1 = lw_expr_intval(e);
+	if (v1 < 0 || v1 > 7)
+	{
+		lwasm_register_error(as, l, "Invalid bit number");
+		v1 = 0;
+	}
+
+	e = lwasm_fetch_expr(l, 1);
+	if (!lw_expr_istype(e, lw_expr_type_int))
+	{
+		lwasm_register_error(as, l, "Bit number must be fully resolved");
+		return;
+	}
+	v2 = lw_expr_intval(e);
+	if (v2 < 0 || v2 > 7)
+	{
+		lwasm_register_error(as, l, "Invalid bit number");
+		v2 = 0;
+	}
+	l -> pb = (l -> lint << 6) | (v1 << 3) | v2;
+	
+	e = lwasm_fetch_expr(l, 2);
+	if (lw_expr_istype(e, lw_expr_type_int))
+	{
+		v1 = lw_expr_intval(e) & 0xFFFF;
+		v2 = v1 - ((l -> dpval) << 8);
+		if (v2 > 0xFF || v2 < 0)
+		{
+			lwasm_register_error(as, l, "Byte overflow");
+			return;
+		}
+	}
+	lwasm_emitop(l, instab[l -> insn].ops[0]);
+	lwasm_emit(l, l -> pb);
+	lwasm_emitexpr(l, e, 1);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lwasm/insn_gen.c	Wed Jan 19 22:27:17 2011 -0700
@@ -0,0 +1,441 @@
+/*
+insn_gen.c, Copyright © 2009 William Astle
+
+This file is part of LWASM.
+
+LWASM 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 3 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, see <http://www.gnu.org/licenses/>.
+
+Contains code for parsing general addressing modes (IMM+DIR+EXT+IND)
+*/
+
+#include <ctype.h>
+#include <stdlib.h>
+
+#include <lw_expr.h>
+
+#include "lwasm.h"
+#include "instab.h"
+
+extern void insn_indexed_parse_aux(asmstate_t *as, line_t *l, char **p);
+extern void insn_indexed_resolve_aux(asmstate_t *as, line_t *l, int force, int elen);
+extern void insn_indexed_emit_aux(asmstate_t *as, line_t *l);
+
+// "extra" is required due to the way OIM, EIM, TIM, and AIM work
+void insn_parse_gen_aux(asmstate_t *as, line_t *l, char **p)
+{
+	const char *optr2;
+	int v1, tv, rval;
+	lw_expr_t s;
+
+	optr2 = *p;
+	while (*optr2 && !isspace(*optr2) && *optr2 != ',') optr2++
+		/* do nothing */ ;
+
+	if (*optr2 == ',' || **p == '[')
+	{
+		l -> lint = -1;
+		l -> lint2 = 1;
+		insn_parse_indexed_aux(as, l, p);
+		goto out;
+	}
+
+	if (**p == '<')
+	{
+		(*p)++;
+		l -> lint2 = 0;
+	}
+
+	// for compatibility with asxxxx
+	// * followed by a digit, alpha, or _, or ., or ?, or another * is "f8"
+	else if (**p == '*')
+	{
+		tv = *(*p + 1);
+		if (isdigit(tv) || isalpha(tv) || tv == '_' || tv == '.' || tv == '?' || tv == '@' || tv == '*' || tv == '+' || tv == '-')
+		{
+			l -> lint2 = 0;
+			(*p)++;
+		}
+	}
+	else if (**p == '>')
+	{
+		(*p)++;
+		l -> lint2 = 2;
+	}
+	else
+	{
+		l -> lint2 = -1;
+	}
+
+	s = lwasm_parse_expr(as, p);
+	if (!s)
+	{
+		lwasm_register_error(as, l, "Bad operand");
+		return;
+	}
+	
+	lwasm_save_expr(l, 0, s);
+
+	if (as -> output_format == OUTPUT_OBJ && l -> lint2 == -1)
+	{
+		l -> lint2 = 2;
+		goto out;
+	}
+
+	if (l -> lint2 != -1)
+		goto out;
+
+	// if we have a constant now, figure out dp vs nondp
+	if (lw_expr_istype(s, lw_expr_type_int))
+	{
+		v1 = lw_expr_intval(s);
+		if (((v1 >> 8) & 0xff) == (l -> dpval & 0xff))
+		{
+			l -> lint2 = 0;
+			goto out;
+		}
+		l -> lint2 = 2;
+	}
+
+out:
+	if (l -> lint2 != -1)
+	{
+		if (l -> lint2 == 0)
+		{
+			l -> len = OPLEN(instab[l -> insn].ops[0]) + 1;
+		}
+		else if (l -> lint2 == 2)
+		{
+			l -> len = OPLEN(instab[l -> insn].ops[2]) + 2;
+		}
+		else if (l -> lint2 == 1 && l -> lint != -1)
+		{
+			l -> len = OPLEN(instab[l -> insn].ops[1]) + l -> lint + 1;
+		}
+	}
+}
+
+void insn_resolve_gen_aux(asmstate_t *as, line_t *l, int force, int elen)
+{
+	lw_expr_t e;
+	
+	if (l -> lint2 == 1)
+	{
+		// indexed
+		insn_resolve_indexed_aux(as, l, force, elen);
+		goto out;
+	}
+	
+	if (l -> lint2 != -1)
+		return;
+	
+	e = lwasm_fetch_expr(l, 0);
+	if (lw_expr_istype(e, lw_expr_type_int))
+	{
+		int v;
+		
+		v = lw_expr_intval(e);
+
+		if (((v >> 8) & 0xff) == (l -> dpval & 0xff))
+		{
+			l -> lint2 = 0;
+			goto out;
+		}
+		l -> lint2 = 2;
+		goto out;
+	}
+
+	if (force)
+	{
+		l -> lint2 = 2;
+	}
+
+out:
+	if (l -> lint2 != -1)
+	{
+		if (l -> lint2 == 0)
+		{
+			l -> len = OPLEN(instab[l -> insn].ops[0]) + 1;
+		}
+		else if (l -> lint2 == 2)
+		{
+			l -> len = OPLEN(instab[l -> insn].ops[2]) + 2;
+		}
+		else if (l -> lint2 == 1 && l -> lint != -1)
+		{
+			l -> len = OPLEN(instab[l -> insn].ops[1]) + l -> lint + 1;
+		}
+	}
+}
+
+void insn_emit_gen_aux(asmstate_t *as, line_t *l, int extra)
+{
+	lw_expr_t e;
+	
+	e = lwasm_fetch_expr(l, 0);
+	lwasm_emitop(l, instab[l -> insn].ops[l -> lint2]);
+	
+	if (extra != -1)
+		lwasm_emit(l, extra);
+	
+	if (l -> lint2 == 1)
+	{
+		lwasm_emit(l, l -> pb);
+		if (l -> lint > 0)
+			lwasm_emitexpr(l, e, l -> lint);
+		return;
+	}
+	
+	if (l -> lint2 == 2)
+		lwasm_emitexpr(l, e, 2);
+	else
+		lwasm_emitexpr(l, e, 1);
+}
+
+// the various insn_gen? functions have an immediate mode of ? bits
+PARSEFUNC(insn_parse_gen0)
+{
+	if (**p == '#')
+	{
+		lwasm_register_error(as, l, "Immediate mode not allowed");
+		return;
+	}
+	
+	// handle non-immediate
+	insn_parse_gen_aux(as, l, p);
+}
+
+RESOLVEFUNC(insn_resolve_gen0)
+{
+	if (l -> len != -1)
+		return;
+
+	// handle non-immediate
+	insn_resolve_gen_aux(as, l, force, 0);
+}
+
+EMITFUNC(insn_emit_gen0)
+{
+	insn_emit_gen_aux(as, l, -1);
+}
+
+PARSEFUNC(insn_parse_gen8)
+{
+	if (**p == '#')
+	{
+		lw_expr_t e;
+		
+		(*p)++;
+		e = lwasm_parse_expr(as, p);
+		if (!e)
+		{
+			lwasm_register_error(as, l, "Bad operand");
+			return;
+		}
+		l -> len = OPLEN(instab[l -> insn].ops[3]) + 1;
+		l -> lint2 = 3;
+		lwasm_save_expr(l, 0, e);
+		return;
+	}
+	
+	// handle non-immediate
+	insn_parse_gen_aux(as, l, p);
+	if (l -> lint2 != -1)
+	{
+		if (l -> lint2 == 0)
+		{
+			l -> len = OPLEN(instab[l -> insn].ops[0]) + 1;
+		}
+		else if (l -> lint2 == 2)
+		{
+			l -> len = OPLEN(instab[l -> insn].ops[2]) + 2;
+		}
+		else if (l -> lint2 == 1 && l -> lint != -1)
+		{
+			l -> len = OPLEN(instab[l -> insn].ops[1]) + l -> lint + 1;
+		}
+	}
+}
+
+RESOLVEFUNC(insn_resolve_gen8)
+{
+	if (l -> len != -1)
+		return;
+
+	// handle non-immediate
+	insn_resolve_gen_aux(as, l, force, 0);
+}
+
+EMITFUNC(insn_emit_gen8)
+{
+	if (l -> lint2 == 3)
+	{
+		lw_expr_t e;
+		e = lwasm_fetch_expr(l, 0);
+		lwasm_emitop(l, instab[l -> insn].ops[3]);
+		lwasm_emitexpr(l, e, 1);
+		return;
+	}
+
+	insn_emit_gen_aux(as, l, -1);
+}
+
+PARSEFUNC(insn_parse_gen16)
+{
+	if (**p == '#')
+	{
+		lw_expr_t e;
+		
+		(*p)++;
+		e = lwasm_parse_expr(as, p);
+		if (!e)
+		{
+			lwasm_register_error(as, l, "Bad operand");
+			return;
+		}
+		l -> len = OPLEN(instab[l -> insn].ops[3]) + 2;
+		l -> lint2 = 3;
+		lwasm_save_expr(l, 0, e);
+		return;
+	}
+	
+	// handle non-immediate
+	insn_parse_gen_aux(as, l, p);
+	if (l -> lint2 != -1)
+	{
+		if (l -> lint2 == 0)
+		{
+			l -> len = OPLEN(instab[l -> insn].ops[0]) + 1;
+		}
+		else if (l -> lint2 == 2)
+		{
+			l -> len = OPLEN(instab[l -> insn].ops[2]) + 2;
+		}
+		else if (l -> lint2 == 1 && l -> lint != -1)
+		{
+			l -> len = OPLEN(instab[l -> insn].ops[1]) + l -> lint + 1;
+		}
+	}
+}
+
+RESOLVEFUNC(insn_resolve_gen16)
+{
+	if (l -> len != -1)
+		return;
+
+	// handle non-immediate
+	insn_resolve_gen_aux(as, l, force, 0);
+}
+
+EMITFUNC(insn_emit_gen16)
+{
+	if (l -> lint2 == 3)
+	{
+		lw_expr_t e;
+		e = lwasm_fetch_expr(l, 0);
+		lwasm_emitop(l, instab[l -> insn].ops[3]);
+		lwasm_emitexpr(l, e, 2);
+		return;
+	}
+
+	insn_emit_gen_aux(as, l, -1);
+}
+
+PARSEFUNC(insn_parse_gen32)
+{
+	if (**p == '#')
+	{
+		lw_expr_t e;
+		
+		(*p)++;
+		e = lwasm_parse_expr(as, p);
+		if (!e)
+		{
+			lwasm_register_error(as, l, "Bad operand");
+			return;
+		}
+		l -> len = OPLEN(instab[l -> insn].ops[3]) + 4;
+		l -> lint2 = 3;
+		lwasm_save_expr(l, 0, e);
+		return;
+	}
+	
+	// handle non-immediate
+	insn_parse_gen_aux(as, l, p);
+	if (l -> lint2 != -1)
+	{
+		if (l -> lint2 == 0)
+		{
+			l -> len = OPLEN(instab[l -> insn].ops[0]) + 1;
+		}
+		else if (l -> lint2 == 2)
+		{
+			l -> len = OPLEN(instab[l -> insn].ops[2]) + 2;
+		}
+		else if (l -> lint2 == 1 && l -> lint != -1)
+		{
+			l -> len = OPLEN(instab[l -> insn].ops[1]) + l -> lint + 1;
+		}
+	}
+}
+
+RESOLVEFUNC(insn_resolve_gen32)
+{
+	if (l -> len != -1)
+		return;
+
+	// handle non-immediate
+	insn_resolve_gen_aux(as, l, force, 0);
+}
+
+EMITFUNC(insn_emit_gen32)
+{
+	if (l -> lint2 == 3)
+	{
+		lw_expr_t e;
+		e = lwasm_fetch_expr(l, 0);
+		lwasm_emitop(l, instab[l -> insn].ops[3]);
+		lwasm_emitexpr(l, e, 4);
+		return;
+	}
+
+	insn_emit_gen_aux(as, l, -1);
+}
+
+PARSEFUNC(insn_parse_imm8)
+{
+	lw_expr_t e;
+	
+	if (**p == '#')
+	{
+		(*p)++;
+
+		e = lwasm_parse_expr(as, p);
+		if (!e)
+		{
+			lwasm_register_error(as, l, "Bad operand");
+			return;
+		}
+		l -> len = OPLEN(instab[l -> insn].ops[0]) + 1;
+		lwasm_save_expr(l, 0, e);
+	}
+}
+
+EMITFUNC(insn_emit_imm8)
+{
+	lw_expr_t e;
+	
+	lwasm_emitop(l, instab[l -> insn].ops[0]);
+	e = lwasm_fetch_expr(l, 0);
+	lwasm_emitexpr(l, e, 1);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lwasm/insn_indexed.c	Wed Jan 19 22:27:17 2011 -0700
@@ -0,0 +1,546 @@
+/*
+insn_indexed.c
+Copyright © 2009 William Astle
+
+This file is part of LWASM.
+
+LWASM 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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+for handling indexed mode instructions
+*/
+
+#include <ctype.h>
+#include <string.h>
+
+#include <lw_expr.h>
+
+#include "lwasm.h"
+#include "instab.h"
+
+/*
+l -> lint: size of operand (0, 1, 2, -1 if not determined)
+l -> pb: actual post byte (from "resolve" stage) or info passed
+	forward to the resolve stage (if l -> line is -1); 0x80 is indir
+	bits 0-2 are register number
+*/
+void insn_parse_indexed_aux(asmstate_t *as, line_t *l, char **p)
+{
+	struct opvals { char *opstr; int pb; };
+	
+	static const char *regs = "X  Y  U  S  W  PCRPC ";
+	static const struct opvals simpleindex[] =
+	{
+		{",x", 0x84},		{",y", 0xa4},		{",u", 0xc4},		{",s", 0xe4},
+		{",x+", 0x80},		{",y+", 0xa0},		{",u+", 0xc0},		{",s+", 0xe0},
+		{",x++", 0x81},		{",y++", 0xa1},		{",u++", 0xc1},		{",s++", 0xe1},
+		{",-x", 0x82},		{",-y", 0xa2},		{",-u", 0xc2},		{",-s", 0xe2},
+		{",--x", 0x83},		{",--y", 0xa3},		{",--u", 0xc3},		{",--s", 0xe3},
+		{"a,x", 0x86},		{"a,y", 0xa6},		{"a,u", 0xc6},		{"a,s", 0xe6},
+		{"b,x", 0x85},		{"b,y", 0xa5},		{"b,u", 0xc5},		{"b,s", 0xe5},
+		{"e,x", 0x87},		{"e,y", 0xa7},		{"e,u", 0xc7},		{"e,s", 0xe7},
+		{"f,x",	0x8a},		{"f,y",	0xaa},		{"f,u", 0xca},		{"f,s", 0xea},
+		{"d,x", 0x8b},		{"d,y", 0xab},		{"d,u", 0xcb},		{"d,s", 0xed},
+		{"w,x", 0x8e},		{"w,y", 0xae},		{"w,u", 0xce},		{"w,s", 0xee},
+		{",w", 0x8f},							{",w++", 0xcf},		{",--w", 0xef},
+		
+		{"[,x]", 0x94},		{"[,y]", 0xb4},		{"[,u", 0xd4},		{"[,s]", 0xf4},
+		{"[,x++]", 0x91},	{"[,y++]", 0xb1},	{"[,u++]", 0xd1},	{"[,s++]", 0xf1},
+		{"[,--x]", 0x93},	{"[,--y]", 0xb3},	{"[,--u]", 0xd3},	{"[,--s]", 0xf3},
+		{"[a,x]", 0x96},	{"[a,y]", 0xb6},	{"[a,u]", 0xd6},	{"[a,s]", 0xf6},
+		{"[b,x]", 0x95},	{"[b,y]", 0xb5},	{"[b,u]", 0xd5},	{"[b,s]", 0xf5},
+		{"[e,x]", 0x97},	{"[e,y]", 0xb7},	{"[e,u]", 0xd7},	{"[e,s]", 0xf7},
+		{"[f,x]", 0x9a},	{"[f,y]", 0xba},	{"[f,u]", 0xda},	{"[f,s]", 0xfa},
+		{"[d,x]", 0x9b},	{"[d,y]", 0xbb},	{"[d,u]", 0xdb},	{"[d,s]", 0xfd},
+		{"[w,x]", 0x9e},	{"[w,y]", 0xbe},	{"[w,u]", 0xde},	{"[w,s]", 0xfe},
+		{"[,w]", 0x90},							{"[,w++]", 0xd0},	{"[,--w]", 0xf0},
+		
+		{ "", -1 }
+	};
+
+	static const char *regs9 = "X  Y  U  S     PCRPC ";
+	static const struct opvals simpleindex9[] =
+	{
+		{",x", 0x84},		{",y", 0xa4},		{",u", 0xc4},		{",s", 0xe4},
+		{",x+", 0x80},		{",y+", 0xa0},		{",u+", 0xc0},		{",s+", 0xe0},
+		{",x++", 0x81},		{",y++", 0xa1},		{",u++", 0xc1},		{",s++", 0xe1},
+		{",-x", 0x82},		{",-y", 0xa2},		{",-u", 0xc2},		{",-s", 0xe2},
+		{",--x", 0x83},		{",--y", 0xa3},		{",--u", 0xc3},		{",--s", 0xe3},
+		{"a,x", 0x86},		{"a,y", 0xa6},		{"a,u", 0xc6},		{"a,s", 0xe6},
+		{"b,x", 0x85},		{"b,y", 0xa5},		{"b,u", 0xc5},		{"b,s", 0xe5},
+		{"d,x", 0x8b},		{"d,y", 0xab},		{"d,u", 0xcb},		{"d,s", 0xed},
+		
+		{"[,x]", 0x94},		{"[,y]", 0xb4},		{"[,u", 0xd4},		{"[,s]", 0xf4},
+		{"[,x++]", 0x91},	{"[,y++]", 0xb1},	{"[,u++]", 0xd1},	{"[,s++]", 0xf1},
+		{"[,--x]", 0x93},	{"[,--y]", 0xb3},	{"[,--u]", 0xd3},	{"[,--s]", 0xf3},
+		{"[a,x]", 0x96},	{"[a,y]", 0xb6},	{"[a,u]", 0xd6},	{"[a,s]", 0xf6},
+		{"[b,x]", 0x95},	{"[b,y]", 0xb5},	{"[b,u]", 0xd5},	{"[b,s]", 0xf5},
+		{"[d,x]", 0x9b},	{"[d,y]", 0xbb},	{"[d,u]", 0xdb},	{"[d,s]", 0xfd},
+		
+		{ "", -1 }
+	};
+	char stbuf[25];
+	int i, j, rn;
+	int indir = 0;
+	int f0 = 1;
+	const struct opvals *simples;
+	const char *reglist;
+	lw_expr_t e;
+		
+	if (as -> target == TARGET_6809)
+	{
+		simples = simpleindex9;
+		reglist = regs9;
+	}
+	else
+	{
+		simples = simpleindex;
+		reglist = regs;
+	}
+	
+	// fetch out operand for lookup
+	for (i = 0; i < 24; i++)
+	{
+		if (*((*p) + i) && !isspace(*((*p) + i)))
+			stbuf[i] = *((*p) + i);
+		else
+			break;
+	}
+	stbuf[i] = '\0';
+	
+	// now look up operand in "simple" table
+	if (!*((*p) + i) || isspace(*((*p) + i)))
+	{
+		// do simple lookup
+		for (j = 0; simples[j].opstr[0]; j++)
+		{
+			if (!strcasecmp(stbuf, simples[j].opstr))
+				break;
+		}
+		if (simples[j].opstr[0])
+		{
+			l -> pb = simples[j].pb;
+			l -> lint = 0;
+			(*p) += i;
+			return;
+		}
+	}
+
+	// now do the "hard" ones
+
+	// is it indirect?
+	if (**p == '[')
+	{
+		indir = 1;
+		(*p)++;
+	}
+	
+	// look for a "," - all indexed modes have a "," except extended indir
+	rn = 0;
+	for (i = 0; (*p)[i] && !isspace((*p)[i]); i++)
+	{
+		if ((*p)[i] == ',')
+		{
+			rn = 1;
+			break;
+		}
+	}
+
+	// if no "," and indirect, do extended indir
+	if (!rn && indir)
+	{
+		// extended indir
+		l -> pb = 0x9f;
+		e = lwasm_parse_expr(as, p);
+		if (!e || **p != ']')
+		{
+			lwasm_register_error(as, l, "Bad operand");
+			return;
+		}
+		lwasm_save_expr(l, 0, e);
+		
+		(*p)++;
+		l -> lint = 2;
+		return;
+	}
+
+	if (**p == '<')
+	{
+		l -> lint = 1;
+		(*p)++;
+	}
+	else if (**p == '>')
+	{
+		l -> lint = 2;
+		(*p)++;
+	}
+
+	if (**p == '0' && *(*p+1) == ',')
+	{
+		f0 = 1;
+	}
+	
+	// now we have to evaluate the expression
+	e = lwasm_parse_expr(as, p);
+	if (!e)
+	{
+		lwasm_register_error(as, l, "Bad operand");
+		return;
+	}
+	lwasm_save_expr(l, 0, e);
+
+	// now look for a comma; if not present, explode
+	if (*(*p)++ != ',')
+	{
+		lwasm_register_error(as, l, "Bad operand");
+		return;
+	}
+	
+	// now get the register
+	rn = lwasm_lookupreg3(reglist, p);
+	if (rn < 0)
+	{
+		lwasm_register_error(as, l, "Bad register");
+		return;
+	}
+	
+	if (indir)
+	{
+		if (**p != ']')
+		{
+			lwasm_register_error(as, l, "Bad operand");
+			return;
+		}
+		else
+			(*p)++;
+	}
+
+	// nnnn,W is only 16 bit (or 0 bit)
+	if (rn == 4)
+	{
+		if (l -> lint == 1)
+		{
+			lwasm_register_error(as, l, "n,W cannot be 8 bit");
+			return;
+		}
+
+		if (l -> lint == 2)
+		{
+			l -> pb = indir ? 0xb0 : 0xcf;
+			l -> lint = 2;
+			return;
+		}
+		
+		l -> pb = (0x80 * indir) | rn;
+
+/* [,w] and ,w
+			if (indir)
+				*b1 = 0x90;
+			else
+				*b1 = 0x8f;
+*/
+		return;
+	}
+	
+	// PCR? then we have PC relative addressing (like B??, LB??)
+	if (rn == 5 || (rn == 6 && CURPRAGMA(l, PRAGMA_PCASPCR)))
+	{
+		lw_expr_t e1, e2;
+		// external references are handled exactly the same as for
+		// relative addressing modes
+		// on pass 1, adjust the expression for a subtraction of the
+		// current address
+		// e - (addr + linelen) => e - addr - linelen
+		
+		e2 = lw_expr_build(lw_expr_type_special, lwasm_expr_linelen, l);
+		e1 = lw_expr_build(lw_expr_type_oper, lw_expr_oper_minus, e, e2);
+		lw_expr_destroy(e2);
+		e2 = lw_expr_build(lw_expr_type_oper, lw_expr_oper_minus, e1, l -> addr);
+		lw_expr_destroy(e1);
+		lwasm_save_expr(l, 0, e2);
+		if (l -> lint == 1)
+		{
+			l -> pb = indir ? 0x9C : 0x8C;
+			return;
+		}
+		if (l -> lint == 2)
+		{
+			l -> pb = indir ? 0x9D : 0x8D;
+			return;
+		}
+	}
+	
+	if (rn == 6)
+	{
+		if (l -> lint == 1)
+		{
+			l -> pb = indir ? 0x9C : 0x8C;
+			return;
+		}
+		if (l -> lint == 2)
+		{
+			l -> pb = indir ? 0x9D : 0x8D;
+			return;
+		}
+	}
+
+	l -> pb = (indir * 0x80) | rn | (f0 * 0x40);
+}
+
+PARSEFUNC(insn_parse_indexed)
+{
+	l -> lint = -1;
+	insn_parse_indexed_aux(as, l, p);
+
+	if (l -> lint != -1)
+	{
+		l -> len = OPLEN(instab[l -> insn].ops[0]) + l -> lint + 1;
+	}
+}
+
+void insn_resolve_indexed_aux(asmstate_t *as, line_t *l, int force, int elen)
+{
+	// here, we have an expression which needs to be
+	// resolved; the post byte is determined here as well
+	lw_expr_t e, e2, e3;
+	int pb = -1;
+	int v;
+	
+	if (l -> len != -1)
+		return;
+	
+	e = lwasm_fetch_expr(l, 0);
+	if (!lw_expr_istype(e, lw_expr_type_int))
+	{
+		// temporarily set the instruction length to see if we get a
+		// constant for our expression; if so, we can select an instruction
+		// size
+		e2 = lw_expr_copy(e);
+		// magic 2 for 8 bit (post byte + offset)
+		l -> len = OPLEN(instab[l -> insn].ops[0]) + elen + 2;
+		lwasm_reduce_expr(as, e2);
+//		l -> len += 1;
+//		e3 = lw_expr_copy(e);
+//		lwasm_reduce_expr(as, e3);
+		l -> len = -1;
+		if (lw_expr_istype(e2, lw_expr_type_int))
+		{
+			v = lw_expr_intval(e2);
+			// we have a reducible expression here which depends on
+			// the size of this instruction
+			if (v < -128 || v > 127)
+			{
+				l -> lint = 2;
+				switch (l -> pb & 0x07)
+				{
+				case 0:
+				case 1:
+				case 2:
+				case 3:
+					pb = 0x89 | ((l -> pb & 0x03) << 5) | (0x10 * (l -> pb & 0x80));
+					break;
+			
+				case 4: // W
+					pb = (l -> pb & 0x80) ? 0xD0 : 0xCF;
+					break;
+				
+				case 5: // PCR
+				case 6: // PC
+					pb = (l -> pb & 0x80) ? 0x9D : 0x8D;
+					break;
+				}
+				
+				l -> pb = pb;
+				lw_expr_destroy(e2);
+//				lw_expr_destroy(e3);
+				return;
+			}
+			else if ((l -> pb & 0x80) || ((l -> pb & 0x07) > 3) || v < -16 || v > 15)
+			{
+				// if not a 5 bit value, is indirect, or is not X,Y,U,S
+				l -> lint = 1;
+				switch (l -> pb & 0x07)
+				{
+				case 0:
+				case 1:
+				case 2:
+				case 3:
+					pb = 0x88 | ((l -> pb & 0x03) << 5) | (0x10 * (l -> pb & 0x80));
+					break;
+			
+				case 4: // W
+					// use 16 bit because W doesn't have 8 bit, unless 0
+					if (v == 0 && !(CURPRAGMA(l, PRAGMA_NOINDEX0TONONE) || l -> pb & 0x40))
+					{
+						pb = (l -> pb & 0x80) ? 0x90 : 0x8F;
+						l -> lint = 0;
+					}
+					else
+					{
+						pb = (l -> pb & 0x80) ? 0xD0 : 0xCF;
+						l -> lint = 2;
+					}
+					break;
+				
+				case 5: // PCR
+				case 6: // PC
+					pb = (l -> pb & 0x80) ? 0x9C : 0x8C;
+					break;
+				}
+			
+				l -> pb = pb;
+				return;
+			}
+			else
+			{
+				// we have X,Y,U,S and a possible 16 bit here
+				l -> lint = 0;
+				
+				if (v == 0 && !(CURPRAGMA(l, PRAGMA_NOINDEX0TONONE) || l -> pb & 0x40))
+				{
+					pb = (l -> pb & 0x03) << 5 | 0x84;
+				}	
+				else
+				{
+					pb = (l -> pb & 0x03) << 5 | v & 0x1F;
+				}
+				l -> pb = pb;
+				return;
+			}
+		}
+	}
+		
+	if (lw_expr_istype(e, lw_expr_type_int))
+	{
+		// we know how big it is
+		v = lw_expr_intval(e);
+		if (v < -128 || v > 127)
+		{
+		do16bit:
+			l -> lint = 2;
+			switch (l -> pb & 0x07)
+			{
+			case 0:
+			case 1:
+			case 2:
+			case 3:
+				pb = 0x89 | (l -> pb & 0x03) << 5 | (0x10 * (l -> pb & 0x80));
+				break;
+			
+			case 4: // W
+				pb = (l -> pb & 0x80) ? 0xD0 : 0xCF;
+				break;
+				
+			case 5: // PCR
+			case 6: // PC
+				pb = (l -> pb & 0x80) ? 0x9D : 0x8D;
+				break;
+			}
+			
+			l -> pb = pb;
+			return;
+		}
+		else if ((l -> pb & 0x80) || ((l -> pb & 0x07) > 3) || v < -16 || v > 15)
+		{
+			// if not a 5 bit value, is indirect, or is not X,Y,U,S
+			l -> lint = 1;
+			switch (l -> pb & 0x07)
+			{
+			case 0:
+			case 1:
+			case 2:
+			case 3:
+				pb = 0x88 | (l -> pb & 0x03) << 5 | (0x10 * (l -> pb & 0x80));
+				break;
+			
+			case 4: // W
+				// use 16 bit because W doesn't have 8 bit, unless 0
+				if (v == 0 && !(CURPRAGMA(l, PRAGMA_NOINDEX0TONONE) || l -> pb & 0x40))
+				{
+					pb = (l -> pb & 0x80) ? 0x90 : 0x8F;
+					l -> lint = 0;
+				}
+				else
+				{
+					pb = (l -> pb & 0x80) ? 0xD0 : 0xCF;
+					l -> lint = 2;
+				}
+				break;
+				
+			case 5: // PCR
+			case 6: // PC
+				pb = (l -> pb & 0x80) ? 0x9C : 0x8C;
+				break;
+			}
+			
+			l -> pb = pb;
+			return;
+		}
+		else
+		{
+			// we have X,Y,U,S and a possible 16 bit here
+			l -> lint = 0;
+			
+			if (v == 0 && !(CURPRAGMA(l, PRAGMA_NOINDEX0TONONE) || l -> pb & 0x40))
+			{
+				pb = (l -> pb & 0x03) << 5 | 0x84;
+			}
+			else
+			{
+				pb = (l -> pb & 0x03) << 5 | v & 0x1F;
+			}
+			l -> pb = pb;
+			return;
+		}
+	}
+	else
+	{
+		// we don't know how big it is
+		if (!force)
+			return;
+		// force 16 bit if we don't know
+		l -> lint = 2;
+		goto do16bit;
+	}
+}
+
+RESOLVEFUNC(insn_resolve_indexed)
+{
+	if (l -> lint == -1)
+		insn_resolve_indexed_aux(as, l, force, 0);
+	
+	if (l -> lint != -1 && l -> pb != -1)
+	{
+		l -> len = OPLEN(instab[l -> insn].ops[0]) + l -> lint + 1;
+	}
+}
+
+void insn_emit_indexed_aux(asmstate_t *as, line_t *l)
+{
+	lw_expr_t e;
+	
+	lwasm_emitop(l, instab[l -> insn].ops[0]);
+	lwasm_emitop(l, l -> pb);
+	if (l -> lint > 0)
+	{
+		e = lwasm_fetch_expr(l, 0);
+		lwasm_emitexpr(l, e, l -> lint);
+	}
+}
+
+EMITFUNC(insn_emit_indexed)
+{
+	insn_emit_indexed_aux(as, l);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lwasm/insn_inh.c	Wed Jan 19 22:27:17 2011 -0700
@@ -0,0 +1,34 @@
+/*
+insn_inh.c
+Copyright © 2010 William Astle
+
+This file is part of LWASM.
+
+LWASM 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 3 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, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "lwasm.h"
+#include "instab.h"
+
+PARSEFUNC(insn_parse_inh)
+{
+	l -> len = OPLEN(instab[l -> insn].ops[0]);
+	skip_operand(p);
+}
+
+EMITFUNC(insn_emit_inh)
+{
+	lwasm_emitop(l, instab[l -> insn].ops[0]);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lwasm/insn_logicmem.c	Wed Jan 19 22:27:17 2011 -0700
@@ -0,0 +1,93 @@
+/*
+insn_logicmem.c
+Copyright © 2009 William Astle
+
+This file is part of LWASM.
+
+LWASM 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 3 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, see <http://www.gnu.org/licenses/>.
+
+Contains code for handling logic/mem instructions
+*/
+
+#include <ctype.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <lw_expr.h>
+
+#include "lwasm.h"
+#include "instab.h"
+
+extern void insn_parse_gen_aux(asmstate_t *as, line_t *l, char **optr);
+extern void insn_resolve_gen_aux(asmstate_t *as, line_t *l, int force, int elen);
+extern void insn_emit_gen_aux(asmstate_t *as, line_t *l, int extra);
+
+// for aim, oim, eim, tim
+PARSEFUNC(insn_parse_logicmem)
+{
+	const char *p2;
+	lw_expr_t s;
+	
+	if (**p == '#')
+		(*p)++;
+	
+	s = lwasm_parse_expr(as, p);
+	if (!s)
+	{
+		lwasm_register_error(as, l, "Bad operand");
+		return;
+	}
+	
+	lwasm_save_expr(l, 100, s);
+	if (**p != ',' && **p != ';')
+	{
+		lwasm_register_error(as, l, "Bad operand");
+		return;
+	}
+	
+	(*p)++;
+
+	// now we have a general addressing mode - call for it
+	insn_parse_gen_aux(as, l, p);
+}
+
+RESOLVEFUNC(insn_resolve_logicmem)
+{
+	if (l -> len != -1)
+		return;
+	
+	insn_resolve_gen_aux(as, l, force, 1);
+}
+
+EMITFUNC(insn_emit_logicmem)
+{
+	lw_expr_t e;
+	int v;
+	
+	e = lwasm_fetch_expr(l, 100);
+	if (!lw_expr_istype(e, lw_expr_type_int))
+	{
+		lwasm_register_error(as, l, "Immediate byte must be fully resolved");
+		return;
+	}
+	
+	v = lw_expr_intval(e);
+	if (v < -128 || v > 255)
+	{
+		lwasm_register_error(as, l, "Byte overflow");
+		return;
+	}
+	
+	insn_emit_gen_aux(as, l, v);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lwasm/insn_rel.c	Wed Jan 19 22:27:17 2011 -0700
@@ -0,0 +1,116 @@
+/*
+insn_rel.c
+Copyright © 2009 William Astle
+
+This file is part of LWASM.
+
+LWASM 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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+for handling relative mode instructions
+*/
+
+#include <stdlib.h>
+
+#include <lw_expr.h>
+
+#include "lwasm.h"
+#include "instab.h"
+
+PARSEFUNC(insn_parse_rel8)
+{
+	int v;
+	lw_expr_t t, e1, e2;
+	int r;
+
+	// sometimes there is a "#", ignore if there
+	if (**p == '#')
+		(*p)++;
+
+	t = lwasm_parse_expr(as, p);
+	if (!t)
+	{
+		lwasm_register_error(as, l, "Bad operand");
+		return;
+	}
+	l -> len = OPLEN(instab[l -> insn].ops[0]) + 1;
+	
+	e1 = lw_expr_build(lw_expr_type_special, lwasm_expr_linelen, l);
+	e2 = lw_expr_build(lw_expr_type_oper, lw_expr_oper_plus, e1, l -> addr);
+	lw_expr_destroy(e1);
+	e1 = lw_expr_build(lw_expr_type_oper, lw_expr_oper_minus, t, e2);
+	lw_expr_destroy(e2);
+	lwasm_save_expr(l, 0, e1);
+}
+
+EMITFUNC(insn_emit_rel8)
+{
+	lw_expr_t e;
+	int offs;
+	
+	e = lwasm_fetch_expr(l, 0);
+	if (!lw_expr_istype(e, lw_expr_type_int))
+	{
+		lwasm_register_error(as, l, "Illegal non-constant expression");
+		return;
+	}
+	
+	offs = lw_expr_intval(e);
+	if (offs < -128 || offs > 127)
+	{
+		lwasm_register_error(as, l, "Byte overflow");
+		return;
+	}
+	
+	lwasm_emitop(l, instab[l -> insn].ops[0]);
+	lwasm_emit(l, offs);
+}
+
+PARSEFUNC(insn_parse_rel16)
+{
+	int v;
+	lw_expr_t t, e1, e2;
+	int r;
+
+	// sometimes there is a "#", ignore if there
+	if (**p == '#')
+		(*p)++;
+
+	t = lwasm_parse_expr(as, p);
+	if (!t)
+	{
+		lwasm_register_error(as, l, "Bad operand");
+		return;
+	}
+	l -> len = OPLEN(instab[l -> insn].ops[0]) + 2;
+	
+	e1 = lw_expr_build(lw_expr_type_special, lwasm_expr_linelen, l);
+	e2 = lw_expr_build(lw_expr_type_oper, lw_expr_oper_plus, e1, l -> addr);
+	lw_expr_destroy(e1);
+	e1 = lw_expr_build(lw_expr_type_oper, lw_expr_oper_minus, t, e2);
+	lw_expr_destroy(e2);
+	lwasm_save_expr(l, 0, e1);
+}
+
+EMITFUNC(insn_emit_rel16)
+{
+	lw_expr_t e;
+	int offs;
+	
+	e = lwasm_fetch_expr(l, 0);
+	
+	lwasm_emitop(l, instab[l -> insn].ops[0]);
+	lwasm_emitexpr(l, e, 2);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lwasm/insn_rlist.c	Wed Jan 19 22:27:17 2011 -0700
@@ -0,0 +1,64 @@
+/*
+insn_rlist.c
+Copyright © 2009 William Astle
+
+This file is part of LWASM.
+
+LWASM 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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+for handling inherent mode instructions
+*/
+
+#include "lwasm.h"
+#include "instab.h"
+
+PARSEFUNC(insn_parse_rlist)
+{
+	int rb = 0;
+	int rn;
+	static const char *regs = "CCA B DPX Y U PCD S ";
+
+	while (**p && !isspace(**p))
+	{
+		rn = lwasm_lookupreg2(regs, p);
+		if (rn < 0)
+		{
+			lwasm_register_error(as, l, "Bad register '%s'", *p);
+			return;
+		}
+		if (**p && **p != ',' && !isspace(**p))
+		{
+			lwasm_register_error(as, l, "Bad operand");
+		}
+		if (**p == ',')
+			(*p)++;
+		if (rn == 8)
+			rn = 6;
+		else if (rn == 9)
+			rn = 0x40;
+		else
+			rn = 1 << rn;
+		rb |= rn;
+	}
+	l -> len = OPLEN(instab[l -> insn].ops[0]) + 1;
+	l -> pb = rb;
+}
+
+EMITFUNC(insn_emit_rlist)
+{
+	lwasm_emitop(l, instab[l -> insn].ops[0]);
+	lwasm_emit(l, l -> pb);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lwasm/insn_rtor.c	Wed Jan 19 22:27:17 2011 -0700
@@ -0,0 +1,60 @@
+/*
+insn_rtor.c
+Copyright © 2010 William Astle
+
+This file is part of LWASM.
+
+LWASM 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 3 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, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "lwasm.h"
+#include "instab.h"
+
+PARSEFUNC(insn_parse_rtor)
+{
+	int r0, r1;
+
+	static const char *regs = "D X Y U S PCW V A B CCDP0 0 E F ";
+	static const char *regs9 = "D X Y U S PC    A B CCDP        ";
+		
+	// register to register (r0,r1)
+	// registers are in order:
+	// D,X,Y,U,S,PC,W,V
+	// A,B,CC,DP,0,0,E,F
+
+	r0 = lwasm_lookupreg2((as -> target == TARGET_6309) ? regs9 : regs, p);
+	if (r0 < 0 || *(*p)++ != ',')
+	{
+		lwasm_register_error(as, l, "Bad operand");
+		r0 = r1 = 0;
+	}
+	else
+	{
+		r1 = lwasm_lookupreg2((as -> target = TARGET_6309) ? regs9 : regs, p);
+		if (r1 < 0)
+		{
+			lwasm_register_error(as, l, "Bad operand");
+			r0 = r1 = 0;
+		}
+	}
+	l -> pb = (r0 << 4) | r1;
+	l -> len = OPLEN(instab[l -> insn].ops[0]) + 1;
+}
+
+EMITFUNC(insn_emit_rtor)
+{
+	lwasm_emitop(l, instab[l -> insn].ops[0]);
+	lwasm_emit(l, l -> pb);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lwasm/insn_tfm.c	Wed Jan 19 22:27:17 2011 -0700
@@ -0,0 +1,150 @@
+/*
+insn_tfm.c
+Copyright © 2009 William Astle
+
+This file is part of LWASM.
+
+LWASM 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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <ctype.h>
+#include <string.h>
+
+#include "lwasm.h"
+#include "instab.h"
+
+PARSEFUNC(insn_parse_tfm)
+{
+	static const char *reglist = "DXYUS   AB  00EF";
+	int r0, r1;
+	char *c;
+	int tfm = 0;
+			
+	c = strchr(reglist, toupper(*(*p)++));
+	if (!c)
+	{
+		lwasm_register_error(as, l, "Unknown operation");
+		return;
+	}
+	r0 = c - reglist;
+	if (**p == '+')
+	{
+		(*p)++;
+		tfm = 1;
+	}
+	else if (**p == '-')
+	{
+		(*p)++;
+		tfm = 2;
+	}
+	if (*(*p)++ != ',')
+	{
+		lwasm_register_error(as, l, "Unknown operation");
+		return;
+	}
+	c = strchr(reglist, toupper(*(*p)++));
+	if (!c)
+	{
+		lwasm_register_error(as, l, "Unknown operation");
+		return;
+	}
+	r1 = c - reglist;
+
+	if (**p == '+')
+	{
+		(*p)++;
+		tfm |= 4;
+	}
+	else if (**p == '-')
+	{
+		(*p)++;
+		tfm |= 8;
+	}
+	
+	if (**p && !isspace(**p))
+	{
+		lwasm_register_error(as, l, "Bad operand");
+		return;
+	}
+			
+	// valid values of tfm here are:
+	// 1: r0+,r1 (2)
+	// 4: r0,r1+ (3)
+	// 5: r0+,r1+ (0)
+	// 10: r0-,r1- (1)
+	switch (tfm)
+	{
+	case 5: //r0+,r1+
+		l -> lint =  instab[l -> insn].ops[0];
+		break;
+
+	case 10: //r0-,r1-
+		l -> lint = instab[l -> insn].ops[1];
+		break;
+
+	case 1: // r0+,r1
+		l -> lint = instab[l -> insn].ops[2];
+		break;
+
+	case 4: // r0,r1+
+		l -> lint = instab[l -> insn].ops[3];
+		break;
+
+	default:
+		lwasm_register_error(as, l, "Unknown operation");
+		return;
+	}
+	l -> pb = (r0 << 4) | r1;
+	l -> len = OPLEN(l -> lint);
+}
+
+EMITFUNC(insn_emit_tfm)
+{
+	lwasm_emitop(l, l -> lint);
+	lwasm_emit(l, l -> pb);
+}
+
+PARSEFUNC(insn_parse_tfmrtor)
+{
+	int r0, r1;
+	static const char *regs = "D X Y U S       A B     0 0 E F ";
+	
+	// register to register (r0,r1)
+	// registers are in order:
+	// D,X,Y,U,S,PC,W,V
+	// A,B,CC,DP,0,0,E,F
+	r0 = lwasm_lookupreg2(regs, p);
+	if (r0 < 0 || *(*p)++ != ',')
+	{
+		lwasm_register_error(as, l, "Bad operand");
+		r0 = r1 = 0;
+	}
+	else
+	{
+		r1 = lwasm_lookupreg2(regs, p);
+		if (r1 < 0)
+		{
+			lwasm_register_error(as, l, "Bad operand");
+			r0 = r1 = 0;
+		}
+	}
+	l -> len = instab[l -> insn].ops[0] + 1;
+	l -> pb = (r0 << 4) | r1;
+}
+
+EMITFUNC(insn_emit_tfmrtor)
+{
+	lwasm_emitop(l, instab[l -> insn].ops[0]);
+	lwasm_emit(l, l -> pb);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lwasm/instab.c	Wed Jan 19 22:27:17 2011 -0700
@@ -0,0 +1,677 @@
+/*
+instab.c
+Copyright © 2008 William Astle
+
+This file is part of LWASM.
+
+LWASM 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 3 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, see <http://www.gnu.org/licenses/>.
+
+
+Contains the instruction table for assembling code
+*/
+#include <stdlib.h>
+#define __instab_c_seen__
+#include "instab.h"
+
+// inherent
+extern PARSEFUNC(insn_parse_inh);
+#define insn_resolve_inh NULL
+extern EMITFUNC(insn_emit_inh);
+
+// register to register
+extern PARSEFUNC(insn_parse_rtor);
+#define insn_resolve_rtor NULL
+extern EMITFUNC(insn_emit_rtor);
+
+// TFM and variants
+extern PARSEFUNC(insn_parse_tfmrtor);
+#define insn_resolve_tfmrtor NULL
+extern EMITFUNC(insn_emit_tfmrtor);
+extern PARSEFUNC(insn_parse_tfm);
+#define insn_resolve_tfm NULL
+extern EMITFUNC(insn_emit_tfm);
+
+// register list
+extern PARSEFUNC(insn_parse_rlist);
+#define insn_resolve_rlist NULL
+extern EMITFUNC(insn_emit_rlist);
+
+// indexed
+extern PARSEFUNC(insn_parse_indexed);
+extern RESOLVEFUNC(insn_resolve_indexed);
+extern EMITFUNC(insn_emit_indexed);
+
+// generic 32 bit immediate
+extern PARSEFUNC(insn_parse_gen32);
+extern RESOLVEFUNC(insn_resolve_gen32);
+extern EMITFUNC(insn_emit_gen32);
+
+// generic 16 bit immediate
+extern PARSEFUNC(insn_parse_gen16);
+extern RESOLVEFUNC(insn_resolve_gen16);
+extern EMITFUNC(insn_emit_gen16);
+
+// generic 8 bit immediate
+extern PARSEFUNC(insn_parse_gen8);
+extern RESOLVEFUNC(insn_resolve_gen8);
+extern EMITFUNC(insn_emit_gen8);
+
+// generic no immediate
+extern PARSEFUNC(insn_parse_gen0);
+extern RESOLVEFUNC(insn_resolve_gen0);
+extern EMITFUNC(insn_emit_gen0);
+
+// logic memory
+extern PARSEFUNC(insn_parse_logicmem);
+extern RESOLVEFUNC(insn_resolve_logicmem);
+extern EMITFUNC(insn_emit_logicmem);
+
+// 8 bit immediate only
+extern PARSEFUNC(insn_parse_imm8);
+#define insn_resolve_imm8 NULL
+extern EMITFUNC(insn_emit_imm8);
+
+// bit to bit ops
+extern PARSEFUNC(insn_parse_bitbit);
+#define insn_resolve_bitbit NULL
+extern EMITFUNC(insn_emit_bitbit);
+
+// 8 bit relative
+extern PARSEFUNC(insn_parse_rel8);
+#define insn_resolve_rel8 NULL
+extern EMITFUNC(insn_emit_rel8);
+
+// 16 bit relative
+extern PARSEFUNC(insn_parse_rel16);
+#define insn_resolve_rel16 NULL
+extern EMITFUNC(insn_emit_rel16);
+
+// MACRO pseudo op
+extern PARSEFUNC(pseudo_parse_macro);
+#define pseudo_resolve_macro	NULL
+#define pseudo_emit_macro NULL
+
+// ENDM pseudo op
+extern PARSEFUNC(pseudo_parse_endm);
+#define pseudo_resolve_endm NULL
+#define pseudo_emit_endm NULL
+
+#define pseudo_parse_noop NULL
+#define pseudo_resolve_noop NULL
+#define pseudo_emit_noop NULL
+
+extern PARSEFUNC(pseudo_parse_end);
+#define pseudo_resolve_end NULL
+extern EMITFUNC(pseudo_emit_end);
+
+extern PARSEFUNC(pseudo_parse_fcb);
+#define pseudo_resolve_fcb NULL
+extern EMITFUNC(pseudo_emit_fcb);
+
+extern PARSEFUNC(pseudo_parse_fdb);
+#define pseudo_resolve_fdb NULL
+extern EMITFUNC(pseudo_emit_fdb);
+
+extern PARSEFUNC(pseudo_parse_fqb);
+#define pseudo_resolve_fqb NULL
+extern EMITFUNC(pseudo_emit_fqb);
+
+extern PARSEFUNC(pseudo_parse_fcc);
+#define pseudo_resolve_fcc NULL
+extern EMITFUNC(pseudo_emit_fcc);
+
+extern PARSEFUNC(pseudo_parse_fcs);
+#define pseudo_resolve_fcs NULL
+extern EMITFUNC(pseudo_emit_fcs);
+
+extern PARSEFUNC(pseudo_parse_fcn);
+#define pseudo_resolve_fcn NULL
+extern EMITFUNC(pseudo_emit_fcn);
+
+extern PARSEFUNC(pseudo_parse_rmb);
+extern RESOLVEFUNC(pseudo_resolve_rmb);
+extern EMITFUNC(pseudo_emit_rmb);
+
+extern PARSEFUNC(pseudo_parse_rmd);
+extern RESOLVEFUNC(pseudo_resolve_rmd);
+extern EMITFUNC(pseudo_emit_rmd);
+
+extern PARSEFUNC(pseudo_parse_rmq);
+extern RESOLVEFUNC(pseudo_resolve_rmq);
+extern EMITFUNC(pseudo_emit_rmq);
+
+extern PARSEFUNC(pseudo_parse_zmb);
+extern RESOLVEFUNC(pseudo_resolve_zmb);
+extern EMITFUNC(pseudo_emit_zmb);
+
+extern PARSEFUNC(pseudo_parse_zmd);
+extern RESOLVEFUNC(pseudo_resolve_zmd);
+extern EMITFUNC(pseudo_emit_zmd);
+
+extern PARSEFUNC(pseudo_parse_zmq);
+extern RESOLVEFUNC(pseudo_resolve_zmq);
+extern EMITFUNC(pseudo_emit_zmq);
+
+extern PARSEFUNC(pseudo_parse_org);
+#define pseudo_resolve_org NULL
+#define pseudo_emit_org NULL
+
+extern PARSEFUNC(pseudo_parse_equ);
+#define pseudo_resolve_equ NULL
+#define pseudo_emit_equ NULL
+
+extern PARSEFUNC(pseudo_parse_set);
+#define pseudo_resolve_set NULL
+#define pseudo_emit_set NULL
+
+extern PARSEFUNC(pseudo_parse_setdp);
+#define pseudo_resolve_setdp NULL
+#define pseudo_emit_setdp NULL
+
+extern PARSEFUNC(pseudo_parse_ifp1);
+#define pseudo_resolve_ifp1 NULL
+#define pseudo_emit_ifp1 NULL
+
+extern PARSEFUNC(pseudo_parse_ifp2);
+#define pseudo_resolve_ifp2 NULL
+#define pseudo_emit_ifp2 NULL
+
+extern PARSEFUNC(pseudo_parse_ifne);
+#define pseudo_resolve_ifne NULL
+#define pseudo_emit_ifne NULL
+
+extern PARSEFUNC(pseudo_parse_ifeq);
+#define pseudo_resolve_ifeq NULL
+#define pseudo_emit_ifeq NULL
+
+extern PARSEFUNC(pseudo_parse_iflt);
+#define pseudo_resolve_iflt NULL
+#define pseudo_emit_iflt NULL
+
+extern PARSEFUNC(pseudo_parse_ifle);
+#define pseudo_resolve_ifle NULL
+#define pseudo_emit_ifle NULL
+
+extern PARSEFUNC(pseudo_parse_ifgt);
+#define pseudo_resolve_ifgt NULL
+#define pseudo_emit_ifgt NULL
+
+extern PARSEFUNC(pseudo_parse_ifge);
+#define pseudo_resolve_ifge NULL
+#define pseudo_emit_ifge NULL
+
+extern PARSEFUNC(pseudo_parse_ifdef);
+#define pseudo_resolve_ifdef NULL
+#define pseudo_emit_ifdef NULL
+
+extern PARSEFUNC(pseudo_parse_ifndef);
+#define pseudo_resolve_ifndef NULL
+#define pseudo_emit_ifndef NULL
+
+extern PARSEFUNC(pseudo_parse_endc);
+#define pseudo_resolve_endc NULL
+#define pseudo_emit_endc NULL
+
+extern PARSEFUNC(pseudo_parse_else);
+#define pseudo_resolve_else NULL
+#define pseudo_emit_else NULL
+
+extern PARSEFUNC(pseudo_parse_pragma);
+#define pseudo_resolve_pragma NULL
+#define pseudo_emit_pragma NULL
+
+extern PARSEFUNC(pseudo_parse_starpragma);
+#define pseudo_resolve_starpragma NULL
+#define pseudo_emit_starpragma NULL
+
+extern PARSEFUNC(pseudo_parse_section);
+#define pseudo_resolve_section NULL
+#define pseudo_emit_section NULL
+
+extern PARSEFUNC(pseudo_parse_endsection);
+#define pseudo_resolve_endsection NULL
+#define pseudo_emit_endsection NULL
+
+extern PARSEFUNC(pseudo_parse_error);
+#define pseudo_resolve_error NULL
+#define pseudo_emit_error NULL
+
+extern PARSEFUNC(pseudo_parse_warning);
+#define pseudo_resolve_warning NULL
+#define pseudo_emit_warning NULL
+
+extern PARSEFUNC(pseudo_parse_os9);
+#define pseudo_resolve_os9 NULL
+extern EMITFUNC(pseudo_emit_os9);
+
+extern PARSEFUNC(pseudo_parse_mod);
+#define pseudo_resolve_mod NULL
+extern EMITFUNC(pseudo_emit_mod);
+
+extern PARSEFUNC(pseudo_parse_emod);
+#define pseudo_resolve_emod NULL
+extern EMITFUNC(pseudo_emit_emod);
+
+extern PARSEFUNC(pseudo_parse_extdep);
+#define pseudo_resolve_extdep NULL
+#define pseudo_emit_extdep NULL
+
+extern PARSEFUNC(pseudo_parse_extern);
+#define pseudo_resolve_extern NULL
+#define pseudo_emit_extern NULL
+
+extern PARSEFUNC(pseudo_parse_export);
+#define pseudo_resolve_export NULL
+#define pseudo_emit_export NULL
+
+extern PARSEFUNC(pseudo_parse_includebin);
+#define pseudo_resolve_includebin NULL
+extern EMITFUNC(pseudo_emit_includebin);
+
+extern PARSEFUNC(pseudo_parse_include);
+#define pseudo_resolve_include NULL
+#define pseudo_emit_include NULL
+
+extern PARSEFUNC(pseudo_parse_align);
+extern RESOLVEFUNC(pseudo_resolve_align);
+extern EMITFUNC(pseudo_emit_align);
+
+extern PARSEFUNC(pseudo_parse_struct);
+#define pseudo_resolve_struct NULL
+#define pseudo_emit_struct NULL
+
+extern PARSEFUNC(pseudo_parse_endstruct);
+#define pseudo_resolve_endstruct NULL
+#define pseudo_emit_endstruct NULL
+
+instab_t instab[] =
+{
+
+	{ "abx",		{	0x3a,	-1,		-1,		-1	},	insn_parse_inh,			insn_resolve_inh,				insn_emit_inh,				lwasm_insn_normal},
+	{ "adca",		{	0x99,	0xa9,	0xb9,	0x89},	insn_parse_gen8,		insn_resolve_gen8,				insn_emit_gen8,				lwasm_insn_normal},
+	{ "adcb",		{	0xd9,	0xe9,	0xf9,	0xc9},	insn_parse_gen8,		insn_resolve_gen8,				insn_emit_gen8,				lwasm_insn_normal},
+	{ "adcd",		{	0x1099,	0x10a9,	0x10b9,	0x1089},insn_parse_gen16,		insn_resolve_gen16,				insn_emit_gen16,			lwasm_insn_is6309},
+	{ "adcr",		{	0x1031,	-1,		-1,		-1	},	insn_parse_rtor,		insn_resolve_rtor,				insn_emit_rtor,				lwasm_insn_is6309},
+	{ "adda",		{	0x9b,	0xab,	0xbb,	0x8b},	insn_parse_gen8,		insn_resolve_gen8,				insn_emit_gen8,				lwasm_insn_normal},
+	{ "addb",		{	0xdb,	0xeb,	0xfb,	0xcb},	insn_parse_gen8,		insn_resolve_gen8,				insn_emit_gen8,				lwasm_insn_normal},
+	{ "addd",		{	0xd3,	0xe3,	0xf3,	0xc3},	insn_parse_gen16,		insn_resolve_gen16,				insn_emit_gen16,			lwasm_insn_normal},
+	{ "adde",		{	0x119b,	0x11ab,	0x11bb,	0x118b},insn_parse_gen8,		insn_resolve_gen8,				insn_emit_gen8,				lwasm_insn_is6309},
+	{ "addf",		{	0x11db,	0x11eb,	0x11fb,	0x11cb},insn_parse_gen8,		insn_resolve_gen8,				insn_emit_gen8,				lwasm_insn_is6309},
+	{ "addr",		{	0x1030,	-1,		-1,		-1	},	insn_parse_rtor,		insn_resolve_rtor,				insn_emit_rtor,				lwasm_insn_is6309},
+	{ "addw",		{	0x109b,	0x10ab,	0x10bb,	0x108b},insn_parse_gen16,		insn_resolve_gen16,				insn_emit_gen16,			lwasm_insn_is6309},
+	{ "aim",		{	0x02,	0x62,	0x72,	-1	},	insn_parse_logicmem,	insn_resolve_logicmem,			insn_emit_logicmem,			lwasm_insn_is6309},
+	{ "anda",		{	0x94,	0xa4,	0xb4,	0x84},	insn_parse_gen8,		insn_resolve_gen8,				insn_emit_gen8,				lwasm_insn_normal},
+	{ "andb",		{	0xd4,	0xe4,	0xf4,	0xc4},	insn_parse_gen8,		insn_resolve_gen8,				insn_emit_gen8,				lwasm_insn_normal},
+	{ "andcc",		{	0x1c,	-1,		-1,		0x1c},	insn_parse_imm8,		insn_resolve_imm8,				insn_emit_imm8,				lwasm_insn_normal},
+	{ "andd",		{	0x1094,	0x10a4,	0x10b4,	0x1084},insn_parse_gen16,		insn_resolve_gen16,				insn_emit_gen16,			lwasm_insn_is6309},
+	{ "andr",		{	0x1034,	-1,		-1,		-1	},	insn_parse_rtor,		insn_resolve_rtor,				insn_emit_rtor,				lwasm_insn_is6309},
+	{ "asl",		{	0x08,	0x68,	0x78,	-1	},	insn_parse_gen0,		insn_resolve_gen0,				insn_emit_gen0,				lwasm_insn_normal},
+	{ "asla",		{	0x48,	-1,		-1,		-1	},	insn_parse_inh,			insn_resolve_inh,				insn_emit_inh,				lwasm_insn_normal},
+	{ "aslb",		{	0x58,	-1,		-1,		-1	},	insn_parse_inh,			insn_resolve_inh,				insn_emit_inh,				lwasm_insn_normal},
+	{ "asld",		{	0x1048,	-1,		-1,		-1	},	insn_parse_inh,			insn_resolve_inh,				insn_emit_inh,				lwasm_insn_is6309},
+	{ "asr",		{	0x07,	0x67,	0x77,	-1	},	insn_parse_gen0,		insn_resolve_gen0,				insn_emit_gen0,				lwasm_insn_normal},
+	{ "asra",		{	0x47,	-1,		-1,		-1	}, 	insn_parse_inh,			insn_resolve_inh,				insn_emit_inh,				lwasm_insn_normal},
+	{ "asrb",		{	0x57,	-1,		-1,		-1	},	insn_parse_inh,			insn_resolve_inh,				insn_emit_inh,				lwasm_insn_normal},
+	{ "asrd",		{	0x1047,	-1,		-1,		-1	},	insn_parse_inh,			insn_resolve_inh,				insn_emit_inh,				lwasm_insn_is6309},
+
+	{ "band",		{	0x1130,	-1,		-1,		-1	},	insn_parse_bitbit,		insn_resolve_bitbit,			insn_emit_bitbit,			lwasm_insn_is6309},
+	{ "bcc",		{	0x24,	-1,		-1,		-1	},	insn_parse_rel8,		insn_resolve_rel8,				insn_emit_rel8,				lwasm_insn_normal},
+	{ "bcs",		{	0x25,	-1,		-1,		-1	},	insn_parse_rel8,		insn_resolve_rel8,				insn_emit_rel8,				lwasm_insn_normal},
+	{ "beor",		{	0x1134,	-1,		-1,		-1	},	insn_parse_bitbit,		insn_resolve_bitbit,			insn_emit_bitbit,			lwasm_insn_is6309},
+	{ "beq",		{	0x27,	-1,		-1,		-1	},	insn_parse_rel8,		insn_resolve_rel8,				insn_emit_rel8,				lwasm_insn_normal},
+	{ "bge",		{	0x2c,	-1,		-1,		-1	},	insn_parse_rel8,		insn_resolve_rel8,				insn_emit_rel8,				lwasm_insn_normal},
+	{ "bgt",		{	0x2e,	-1,		-1,		-1	},	insn_parse_rel8,		insn_resolve_rel8,				insn_emit_rel8,				lwasm_insn_normal},
+	{ "bhi",		{	0x22,	-1,		-1,		-1	},	insn_parse_rel8,		insn_resolve_rel8,				insn_emit_rel8,				lwasm_insn_normal},
+	{ "bhs",		{	0x24,	-1,		-1,		-1	},	insn_parse_rel8,		insn_resolve_rel8,				insn_emit_rel8,				lwasm_insn_normal},
+	{ "biand",		{	0x1131,	-1,		-1,		-1	},	insn_parse_bitbit,		insn_resolve_bitbit,			insn_emit_bitbit,			lwasm_insn_is6309},
+	{ "bieor",		{	0x1135,	-1,		-1,		-1	},	insn_parse_bitbit,		insn_resolve_bitbit,			insn_emit_bitbit,			lwasm_insn_is6309},
+	{ "bior",		{	0x1133, -1,		-1,		-1	},	insn_parse_bitbit,		insn_resolve_bitbit,			insn_emit_bitbit,			lwasm_insn_is6309},
+	{ "bita",		{	0x95,	0xa5,	0xb5,	0x85},	insn_parse_gen8,		insn_resolve_gen8,				insn_emit_gen8,				lwasm_insn_normal},
+	{ "bitb",		{	0xd5,	0xe5,	0xf5,	0xc5},	insn_parse_gen8,		insn_resolve_gen8,				insn_emit_gen8,				lwasm_insn_normal},
+	{ "bitd",		{	0x1095,	0x10a5,	0x10b5,	0x1085},insn_parse_gen16,		insn_resolve_gen16,				insn_emit_gen16,			lwasm_insn_is6309},
+	{ "bitmd",		{	0x113c, -1,		-1,		0x113c},insn_parse_imm8,		insn_resolve_imm8,				insn_emit_imm8,				lwasm_insn_is6309},
+	{ "ble",		{	0x2f,	-1,		-1,		-1	},	insn_parse_rel8,		insn_resolve_rel8,				insn_emit_rel8,				lwasm_insn_normal},
+	{ "blo",		{	0x25,	-1,		-1,		-1	},	insn_parse_rel8,		insn_resolve_rel8,				insn_emit_rel8,				lwasm_insn_normal},
+	{ "bls",		{	0x23,	-1,		-1,		-1	},	insn_parse_rel8,		insn_resolve_rel8,				insn_emit_rel8,				lwasm_insn_normal},
+	{ "blt",		{	0x2d,	-1,		-1,		-1	},	insn_parse_rel8,		insn_resolve_rel8,				insn_emit_rel8,				lwasm_insn_normal},
+	{ "bmi",		{	0x2b,	-1,		-1,		-1	},	insn_parse_rel8,		insn_resolve_rel8,				insn_emit_rel8,				lwasm_insn_normal},
+	{ "bne",		{	0x26, 	-1,		-1,		-1	},	insn_parse_rel8,		insn_resolve_rel8,				insn_emit_rel8,				lwasm_insn_normal},
+	{ "bor",		{	0x1132,	-1,		-1,		-1	},	insn_parse_bitbit,		insn_resolve_bitbit,			insn_emit_bitbit,			lwasm_insn_is6309},
+	{ "bpl",		{	0x2a,	-1,		-1,		-1	},	insn_parse_rel8,		insn_resolve_rel8,				insn_emit_rel8,				lwasm_insn_normal},
+	{ "bra",		{	0x20,	-1,		-1,		-1	},	insn_parse_rel8,		insn_resolve_rel8,				insn_emit_rel8,				lwasm_insn_normal},
+	{ "brn",		{	0x21,	-1,		-1,		-1	},	insn_parse_rel8,		insn_resolve_rel8,				insn_emit_rel8,				lwasm_insn_normal},
+	{ "bsr",		{	0x8d,	-1,		-1,		-1	},	insn_parse_rel8,		insn_resolve_rel8,				insn_emit_rel8,				lwasm_insn_normal},
+	{ "bvc",		{	0x28,	-1,		-1,		-1	},	insn_parse_rel8,		insn_resolve_rel8,				insn_emit_rel8,				lwasm_insn_normal},
+	{ "bvs",		{	0x29,	-1,		-1,		-1	},	insn_parse_rel8,		insn_resolve_rel8,				insn_emit_rel8,				lwasm_insn_normal},
+
+	{ "clr",		{	0x0f,	0x6f,	0x7f,	-1	},	insn_parse_gen0,		insn_resolve_gen0,				insn_emit_gen0,				lwasm_insn_normal},
+	{ "clra",		{	0x4f,	-1,		-1,		-1	},	insn_parse_inh,			insn_resolve_inh,				insn_emit_inh,				lwasm_insn_normal},
+	{ "clrb",		{	0x5f,	-1,		-1,		-1	},	insn_parse_inh,			insn_resolve_inh,				insn_emit_inh,				lwasm_insn_normal},
+	{ "clrd",		{	0x104f,	-1,		-1,		-1	},	insn_parse_inh,			insn_resolve_inh,				insn_emit_inh,				lwasm_insn_is6309},
+	{ "clre",		{	0x114f,	-1,		-1,		-1	},	insn_parse_inh,			insn_resolve_inh,				insn_emit_inh,				lwasm_insn_is6309},
+	{ "clrf",		{	0x115f,	-1,		-1,		-1	},	insn_parse_inh,			insn_resolve_inh,				insn_emit_inh,				lwasm_insn_is6309},
+	{ "clrw",		{	0x105f,	-1,		-1,		-1	},	insn_parse_inh,			insn_resolve_inh,				insn_emit_inh,				lwasm_insn_is6309},
+	{ "cmpa",		{	0x91,	0xa1,	0xb1,	0x81},	insn_parse_gen8,		insn_resolve_gen8,				insn_emit_gen8,				lwasm_insn_normal},
+	{ "cmpb",		{	0xd1,	0xe1,	0xf1,	0xc1},	insn_parse_gen8,		insn_resolve_gen8,				insn_emit_gen8,				lwasm_insn_normal},
+	{ "cmpd",		{	0x1093,	0x10a3,	0x10b3,	0x1083},insn_parse_gen16,		insn_resolve_gen16,				insn_emit_gen16,			lwasm_insn_normal},
+	{ "cmpe",		{	0x1191,	0x11a1,	0x11b1,	0x1181},insn_parse_gen8,		insn_resolve_gen8,				insn_emit_gen8,				lwasm_insn_is6309},
+	{ "cmpf",		{	0x11d1,	0x11e1,	0x11f1,	0x11c1},insn_parse_gen8,		insn_resolve_gen8,				insn_emit_gen8,				lwasm_insn_is6309},
+	{ "cmpr",		{	0x1037,	-1,		-1,		-1	},	insn_parse_rtor,		insn_resolve_rtor,				insn_emit_rtor,				lwasm_insn_is6309},
+	{ "cmps",		{	0x119c,	0x11ac,	0x11bc,	0x118c},insn_parse_gen16,		insn_resolve_gen16,				insn_emit_gen16,			lwasm_insn_normal},
+	{ "cmpu",		{	0x1193,	0x11a3,	0x11b3,	0x1183},insn_parse_gen16,		insn_resolve_gen16,				insn_emit_gen16,			lwasm_insn_normal},
+	{ "cmpw",		{	0x1091,	0x10a1,	0x10b1,	0x1081},insn_parse_gen16,		insn_resolve_gen16,				insn_emit_gen16,			lwasm_insn_is6309},
+	{ "cmpx",		{	0x9c,	0xac,	0xbc,	0x8c}, 	insn_parse_gen16,		insn_resolve_gen16,				insn_emit_gen16,			lwasm_insn_normal},
+	{ "cmpy",		{	0x109c,	0x10ac,	0x10bc,	0x108c},insn_parse_gen16,		insn_resolve_gen16,				insn_emit_gen16,			lwasm_insn_normal},
+	{ "com",		{	0x03,	0x63,	0x73,	-1	},	insn_parse_gen0,		insn_resolve_gen0,				insn_emit_gen0,				lwasm_insn_normal},
+	{ "coma",		{	0x43,	-1,		-1,		-1	},	insn_parse_inh,			insn_resolve_inh,				insn_emit_inh,				lwasm_insn_normal},
+	{ "comb",		{	0x53,	-1,		-1,		-1	},	insn_parse_inh,			insn_resolve_inh,				insn_emit_inh,				lwasm_insn_normal},
+	{ "comd",		{	0x1043,	-1,		-1,		-1	},	insn_parse_inh,			insn_resolve_inh,				insn_emit_inh,				lwasm_insn_is6309},
+	{ "come",		{	0x1143,	-1, 	-1,		-1	},	insn_parse_inh,			insn_resolve_inh,				insn_emit_inh,				lwasm_insn_is6309},
+	{ "comf", 		{	0x1153, -1,		-1,		-1	}, 	insn_parse_inh,			insn_resolve_inh,				insn_emit_inh,				lwasm_insn_is6309},
+	{ "comw",		{	0x1053,	-1,		-1,		-1	},	insn_parse_inh,			insn_resolve_inh,				insn_emit_inh,				lwasm_insn_is6309},
+	{ "cwai",		{	0x3c, 	-1,		-1,		-1	},	insn_parse_imm8,		insn_resolve_imm8,				insn_emit_imm8,				lwasm_insn_normal},
+	
+	{ "daa",		{	0x19,	-1,		-1,		-1	},	insn_parse_inh,			insn_resolve_inh,				insn_emit_inh,				lwasm_insn_normal},
+	{ "dec",		{	0x0a,	0x6a,	0x7a,	-1	},	insn_parse_gen0,		insn_resolve_gen0,				insn_emit_gen0,				lwasm_insn_normal},
+	{ "deca",		{	0x4a,	-1,		-1,		-1	},	insn_parse_inh,			insn_resolve_inh,				insn_emit_inh,				lwasm_insn_normal},
+	{ "decb",		{	0x5a,	-1,		-1,		-1	},	insn_parse_inh,			insn_resolve_inh,				insn_emit_inh,				lwasm_insn_normal},
+	{ "decd",		{	0x104a,	-1,		-1,		-1	},	insn_parse_inh,			insn_resolve_inh,				insn_emit_inh,				lwasm_insn_is6309},
+	{ "dece",		{	0x114a,	-1,		-1,		-1	},	insn_parse_inh,			insn_resolve_inh,				insn_emit_inh,				lwasm_insn_is6309},
+	{ "decf",		{	0x115a,	-1,		-1,		-1	},	insn_parse_inh,			insn_resolve_inh,				insn_emit_inh,				lwasm_insn_is6309},
+	{ "decw",		{	0x105a,	-1,		-1,		-1	},	insn_parse_inh,			insn_resolve_inh,				insn_emit_inh,				lwasm_insn_is6309},
+	{ "divd",		{	0x118d,	0x119d,	0x11ad,	0x11bd},insn_parse_gen8,		insn_resolve_gen8,				insn_emit_gen8,				lwasm_insn_is6309},
+	{ "divq",		{	0x118e,	0x119e,	0x11ae,	0x11be},insn_parse_gen16,		insn_resolve_gen16,				insn_emit_gen16,			lwasm_insn_is6309},
+
+	{ "eim",		{	0x05,	0x65,	0x75,	-1	},	insn_parse_logicmem,	insn_resolve_logicmem,			insn_emit_logicmem,			lwasm_insn_is6309},
+	{ "eora",		{	0x98,	0xa8,	0xb8,	0x88},	insn_parse_gen8,		insn_resolve_gen8,				insn_emit_gen8,				lwasm_insn_normal},
+	{ "eorb",		{	0xd8,	0xe8,	0xf8,	0xc8},	insn_parse_gen8,		insn_resolve_gen8,				insn_emit_gen8,				lwasm_insn_normal},
+	{ "eord",		{	0x1098,	0x10a8,	0x10b8,	0x1088},insn_parse_gen16,		insn_resolve_gen16,				insn_emit_gen16,			lwasm_insn_is6309},
+	{ "eorr",		{	0x1036,	-1,		-1,		-1	},	insn_parse_rtor,		insn_resolve_rtor,				insn_emit_rtor,				lwasm_insn_is6309},
+	{ "exg",		{	0x1e,	-1,		-1,		-1	},	insn_parse_rtor,		insn_resolve_rtor,				insn_emit_rtor,				lwasm_insn_normal},
+
+	{ "inc",		{	0x0c,	0x6c,	0x7c,	-1	},	insn_parse_gen0,		insn_resolve_gen0,				insn_emit_gen0,				lwasm_insn_normal},
+	{ "inca",		{	0x4c,	-1,		-1,		-1	},	insn_parse_inh,			insn_resolve_inh,				insn_emit_inh,				lwasm_insn_normal},
+	{ "incb",		{	0x5c,	-1,		-1,		-1	},	insn_parse_inh,			insn_resolve_inh,				insn_emit_inh,				lwasm_insn_normal},
+	{ "incd",		{	0x104c,	-1,		-1,		-1	},	insn_parse_inh,			insn_resolve_inh,				insn_emit_inh,				lwasm_insn_is6309},
+	{ "ince",		{	0x114c,	-1,		-1,		-1	},	insn_parse_inh,			insn_resolve_inh,				insn_emit_inh,				lwasm_insn_is6309},
+	{ "incf",		{	0x115c,	-1,		-1,		-1	},	insn_parse_inh,			insn_resolve_inh,				insn_emit_inh,				lwasm_insn_is6309},
+	{ "incw",		{	0x105c,	-1,		-1,		-1	},	insn_parse_inh,			insn_resolve_inh,				insn_emit_inh,				lwasm_insn_is6309},
+	
+	{ "jmp",		{	0x0e,	0x6e,	0x7e,	-1	},	insn_parse_gen0,		insn_resolve_gen0,				insn_emit_gen0,				lwasm_insn_normal},
+	{ "jsr",		{	0x9d,	0xad,	0xbd,	-1	}, 	insn_parse_gen0,		insn_resolve_gen0,				insn_emit_gen0,				lwasm_insn_normal},
+	
+	{ "lbcc",		{	0x1024,	-1,		-1,		-1	},	insn_parse_rel16,		insn_resolve_rel16,				insn_emit_rel16,			lwasm_insn_normal},
+	{ "lbcs",		{	0x1025,	-1,		-1,		-1	},	insn_parse_rel16,		insn_resolve_rel16,				insn_emit_rel16,			lwasm_insn_normal},
+	{ "lbeq",		{	0x1027,	-1,		-1,		-1	},	insn_parse_rel16,		insn_resolve_rel16,				insn_emit_rel16,			lwasm_insn_normal},
+	{ "lbge",		{	0x102c,	-1,		-1,		-1	},	insn_parse_rel16,		insn_resolve_rel16,				insn_emit_rel16,			lwasm_insn_normal},
+	{ "lbgt",		{	0x102e,	-1,		-1,		-1	},	insn_parse_rel16,		insn_resolve_rel16,				insn_emit_rel16,			lwasm_insn_normal},
+	{ "lbhi",		{	0x1022,	-1,		-1,		-1	},	insn_parse_rel16,		insn_resolve_rel16,				insn_emit_rel16,			lwasm_insn_normal},
+	{ "lbhs",		{	0x1024,	-1,		-1,		-1	},	insn_parse_rel16,		insn_resolve_rel16,				insn_emit_rel16,			lwasm_insn_normal},
+	{ "lble",		{	0x102f,	-1,		-1,		-1	},	insn_parse_rel16,		insn_resolve_rel16,				insn_emit_rel16,			lwasm_insn_normal},
+	{ "lblo",		{	0x1025,	-1,		-1,		-1	},	insn_parse_rel16,		insn_resolve_rel16,				insn_emit_rel16,			lwasm_insn_normal},
+	{ "lbls",		{	0x1023,	-1,		-1,		-1	},	insn_parse_rel16,		insn_resolve_rel16,				insn_emit_rel16,			lwasm_insn_normal},
+	{ "lblt",		{	0x102d, -1,		-1,		-1	},	insn_parse_rel16,		insn_resolve_rel16,				insn_emit_rel16,			lwasm_insn_normal},
+	{ "lbmi",		{	0x102b,	-1,		-1,		-1	},	insn_parse_rel16,		insn_resolve_rel16,				insn_emit_rel16,			lwasm_insn_normal},
+	{ "lbne",		{	0x1026,	-1,		-1,		-1	},	insn_parse_rel16,		insn_resolve_rel16,				insn_emit_rel16,			lwasm_insn_normal},
+	{ "lbpl",		{	0x102a,	-1,		-1,		-1	},	insn_parse_rel16,		insn_resolve_rel16,				insn_emit_rel16,			lwasm_insn_normal},
+	{ "lbra",		{	0x16,	-1,		-1,		-1	},	insn_parse_rel16,		insn_resolve_rel16,				insn_emit_rel16,			lwasm_insn_normal},
+	{ "lbrn",		{	0x1021,	-1,		-1,		-1	},	insn_parse_rel16,		insn_resolve_rel16,				insn_emit_rel16,			lwasm_insn_normal},
+	{ "lbsr",		{	0x17,	-1,		-1,		-1	},	insn_parse_rel16,		insn_resolve_rel16,				insn_emit_rel16,			lwasm_insn_normal},
+	{ "lbvc",		{	0x1028,	-1,		-1,		-1	},	insn_parse_rel16,		insn_resolve_rel16,				insn_emit_rel16,			lwasm_insn_normal},
+	{ "lbvs",		{	0x1029,	-1,		-1,		-1	},	insn_parse_rel16,		insn_resolve_rel16,				insn_emit_rel16,			lwasm_insn_normal},
+	{ "lda",		{	0x96,	0xa6,	0xb6,	0x86},	insn_parse_gen8,		insn_resolve_gen8,				insn_emit_gen8,				lwasm_insn_normal},
+	{ "ldb",		{	0xd6,	0xe6,	0xf6,	0xc6},	insn_parse_gen8,		insn_resolve_gen8,				insn_emit_gen8,				lwasm_insn_normal},
+	{ "ldbt",		{	0x1136,	-1,		-1,		-1	},	insn_parse_bitbit,		insn_resolve_bitbit,			insn_emit_bitbit,			lwasm_insn_is6309},
+	{ "ldd",		{	0xdc,	0xec,	0xfc,	0xcc},	insn_parse_gen16,		insn_resolve_gen16,				insn_emit_gen16,			lwasm_insn_normal},
+	{ "lde",		{	0x1196,	0x11a6,	0x11b6,	0x1186},insn_parse_gen8,		insn_resolve_gen8,				insn_emit_gen8,				lwasm_insn_is6309},
+	{ "ldf",		{	0x11d6,	0x11e6,	0x11f6,	0x11c6},insn_parse_gen8,		insn_resolve_gen8,				insn_emit_gen8,				lwasm_insn_is6309},
+	{ "ldq",		{	0x10dc,	0x10ec,	0x10fc,	0xcd},	insn_parse_gen32,		insn_resolve_gen32,				insn_emit_gen32,			lwasm_insn_is6309},
+	{ "lds",		{	0x10de,	0x10ee,	0x10fe,	0x10ce},insn_parse_gen16,		insn_resolve_gen16,				insn_emit_gen16,			lwasm_insn_normal},
+	{ "ldu",		{ 	0xde,	0xee,	0xfe,	0xce},	insn_parse_gen16,		insn_resolve_gen16,				insn_emit_gen16,			lwasm_insn_normal},
+	{ "ldw",		{	0x1096,	0x10a6,	0x10b6,	0x1086},insn_parse_gen16,		insn_resolve_gen16,				insn_emit_gen16,			lwasm_insn_is6309},
+	{ "ldx",		{	0x9e,	0xae,	0xbe,	0x8e},	insn_parse_gen16,		insn_resolve_gen16,				insn_emit_gen16,			lwasm_insn_normal},
+	{ "ldy",		{	0x109e,	0x10ae,	0x10be,	0x108e},insn_parse_gen16,		insn_resolve_gen16,				insn_emit_gen16,			lwasm_insn_normal},
+	{ "ldmd",		{	0x113d, -1,		-1,		0x113d},insn_parse_imm8,		insn_resolve_imm8,				insn_emit_imm8,				lwasm_insn_is6309},
+	{ "leas",		{	0x32,	-1,		-1,		-1	},	insn_parse_indexed,		insn_resolve_indexed,			insn_emit_indexed,			lwasm_insn_normal},
+	{ "leau",		{	0x33,	-1,		-1,		-1	},	insn_parse_indexed,		insn_resolve_indexed,			insn_emit_indexed,			lwasm_insn_normal},
+	{ "leax",		{	0x30,	-1,		-1,		-1	},	insn_parse_indexed,		insn_resolve_indexed,			insn_emit_indexed,			lwasm_insn_normal},
+	{ "leay",		{	0x31,	-1,		-1,		-1	},	insn_parse_indexed,		insn_resolve_indexed,			insn_emit_indexed,			lwasm_insn_normal},
+	{ "lsl",		{	0x08,	0x68,	0x78,	-1	},	insn_parse_gen0,		insn_resolve_gen0,				insn_emit_gen0,				lwasm_insn_normal},
+	{ "lsla",		{	0x48,	-1,		-1,		-1	},	insn_parse_inh,			insn_resolve_inh,				insn_emit_inh,				lwasm_insn_normal},
+	{ "lslb",		{	0x58,	-1,		-1,		-1	},	insn_parse_inh,			insn_resolve_inh,				insn_emit_inh,				lwasm_insn_normal},
+	{ "lsld",		{	0x1048,	-1,		-1,		-1	},	insn_parse_inh,			insn_resolve_inh,				insn_emit_inh,				lwasm_insn_is6309},
+	{ "lsr",		{	0x04,	0x64,	0x74,	-1	},	insn_parse_gen0,		insn_resolve_gen0,				insn_emit_gen0,				lwasm_insn_normal},
+	{ "lsra",		{	0x44,	-1,		-1,		-1	},	insn_parse_inh,			insn_resolve_inh,				insn_emit_inh,				lwasm_insn_normal},
+	{ "lsrb",		{	0x54,	-1,		-1,		-1	},	insn_parse_inh,			insn_resolve_inh,				insn_emit_inh,				lwasm_insn_normal},
+	{ "lsrd",		{	0x1044,	-1,		-1,		-1	},	insn_parse_inh,			insn_resolve_inh,				insn_emit_inh,				lwasm_insn_is6309},
+	{ "lsrw",		{	0x1054,	-1,		-1,		-1	},	insn_parse_inh,			insn_resolve_inh,				insn_emit_inh,				lwasm_insn_is6309},
+
+	{ "mul",		{	0x3d,	-1,		-1,		-1	},	insn_parse_inh,			insn_resolve_inh,				insn_emit_inh,				lwasm_insn_normal},
+	{ "muld",		{	0x118f,	0x119f,	0x11af,	0x11bf},insn_parse_gen16,		insn_resolve_gen16,				insn_emit_gen16,			lwasm_insn_is6309},
+	
+	{ "neg",		{	0x00,	0x60,	0x70,	-1	},	insn_parse_gen0,		insn_resolve_gen0,				insn_emit_gen0,				lwasm_insn_normal},
+	{ "nega",		{	0x40,	-1,		-1,		-1	},	insn_parse_inh,			insn_resolve_inh,				insn_emit_inh,				lwasm_insn_normal},
+	{ "negb",		{	0x50,	-1,		-1,		-1	},	insn_parse_inh,			insn_resolve_inh,				insn_emit_inh,				lwasm_insn_normal},
+	{ "negd",		{	0x1040,	-1,		-1,		-1	},	insn_parse_inh,			insn_resolve_inh,				insn_emit_inh,				lwasm_insn_is6309},
+
+	{ "nop",		{	0x12,	-1,		-1,		-1	},	insn_parse_inh,			insn_resolve_inh,				insn_emit_inh,				lwasm_insn_normal},
+	
+	{ "oim",		{	0x01,	0x61,	0x71,	-1	},	insn_parse_logicmem,	insn_resolve_logicmem,			insn_emit_logicmem,			lwasm_insn_is6309},
+	{ "ora",		{	0x9a,	0xaa,	0xba,	0x8a},	insn_parse_gen8,		insn_resolve_gen8,				insn_emit_gen8,				lwasm_insn_normal},
+	{ "orb",		{	0xda,	0xea,	0xfa,	0xca},	insn_parse_gen8,		insn_resolve_gen8,				insn_emit_gen8,				lwasm_insn_normal},
+	{ "orcc",		{	0x1a,	-1,		-1,	0x1a	},	insn_parse_imm8,		insn_resolve_imm8,				insn_emit_imm8,				lwasm_insn_normal},
+	{ "ord",		{	0x109a,	0x10aa,	0x10ba,	0x108a},insn_parse_gen16,		insn_resolve_gen16,				insn_emit_gen16,			lwasm_insn_is6309},
+	{ "orr",		{	0x1035,	-1,		-1,		-1	},	insn_parse_rtor,		insn_resolve_rtor,				insn_emit_rtor,				lwasm_insn_is6309},
+	
+	{ "pshs",		{	0x34,	-1,		-1,		-1	},	insn_parse_rlist,		insn_resolve_rlist,				insn_emit_rlist,				lwasm_insn_normal},
+	{ "pshsw",		{	0x1038,	-1,		-1,		-1	},	insn_parse_inh,			insn_resolve_inh,				insn_emit_inh,				lwasm_insn_is6309},
+	{ "pshu",		{	0x36,	-1,		-1,		-1	},	insn_parse_rlist,		insn_resolve_rlist,				insn_emit_rlist,				lwasm_insn_normal},
+	{ "pshuw",		{	0x103a,	-1,		-1,		-1	},	insn_parse_inh,			insn_resolve_inh,				insn_emit_inh,				lwasm_insn_is6309},
+	{ "puls",		{	0x35,	-1,		-1,		-1	},	insn_parse_rlist,		insn_resolve_rlist,				insn_emit_rlist,				lwasm_insn_normal},
+	{ "pulsw",		{	0x1039,	-1,		-1,		-1	},	insn_parse_inh,			insn_resolve_inh,				insn_emit_inh,				lwasm_insn_is6309},
+	{ "pulu",		{	0x37,	-1,		-1,		-1	},	insn_parse_rlist,		insn_resolve_rlist,				insn_emit_rlist,				lwasm_insn_normal},
+	{ "puluw",		{	0x103b,	-1,		-1,		-1	},	insn_parse_inh,			insn_resolve_inh,				insn_emit_inh,				lwasm_insn_is6309},
+	
+	{ "rol",		{	0x09,	0x69,	0x79,	-1	},	insn_parse_gen0,		insn_resolve_gen0,				insn_emit_gen0,				lwasm_insn_normal},
+	{ "rola",		{	0x49,	-1,		-1,		-1	},	insn_parse_inh,			insn_resolve_inh,				insn_emit_inh,				lwasm_insn_normal},
+	{ "rolb",		{	0x59,	-1,		-1,		-1	},	insn_parse_inh,			insn_resolve_inh,				insn_emit_inh,				lwasm_insn_normal},
+	{ "rold",		{	0x1049,	-1,		-1,		-1	},	insn_parse_inh,			insn_resolve_inh,				insn_emit_inh,				lwasm_insn_is6309},
+	{ "rolw",		{	0x1059,	-1,		-1,		-1	},	insn_parse_inh,			insn_resolve_inh,				insn_emit_inh,				lwasm_insn_is6309},
+	{ "ror",		{	0x06,	0x66,	0x76,	-1	},	insn_parse_gen0,		insn_resolve_gen0,				insn_emit_gen0,				lwasm_insn_normal},
+	{ "rora",		{	0x46,	-1,		-1,		-1	},	insn_parse_inh,			insn_resolve_inh,				insn_emit_inh,				lwasm_insn_normal},
+	{ "rorb",		{	0x56,	-1,		-1,		-1	},	insn_parse_inh,			insn_resolve_inh,				insn_emit_inh,				lwasm_insn_normal},
+	{ "rord",		{	0x1046,	-1,		-1,		-1	},	insn_parse_inh,			insn_resolve_inh,				insn_emit_inh,				lwasm_insn_is6309},
+	{ "rorw",		{	0x1056,	-1,		-1,		-1	},	insn_parse_inh,			insn_resolve_inh,				insn_emit_inh,				lwasm_insn_is6309},
+	{ "rti",		{	0x3b,	-1,		-1,		-1	},	insn_parse_inh,			insn_resolve_inh,				insn_emit_inh,				lwasm_insn_normal},
+	{ "rts",		{	0x39,	-1,		-1,		-1	},	insn_parse_inh,			insn_resolve_inh,				insn_emit_inh,				lwasm_insn_normal},
+	
+	{ "sbca",		{	0x92,	0xa2,	0xb2,	0x82},	insn_parse_gen8,		insn_resolve_gen8,				insn_emit_gen8,				lwasm_insn_normal},
+	{ "sbcb",		{	0xd2,	0xe2,	0xf2,	0xc2},	insn_parse_gen8,		insn_resolve_gen8,				insn_emit_gen8,				lwasm_insn_normal},
+	{ "sbcd",		{	0x1092,	0x10a2,	0x10b2,	0x1082},insn_parse_gen16,		insn_resolve_gen16,				insn_emit_gen16,			lwasm_insn_is6309},
+	{ "sbcr",		{	0x1033,	-1,		-1,		-1	},	insn_parse_rtor,		insn_resolve_rtor,				insn_emit_rtor,				lwasm_insn_is6309},
+	{ "sex",		{	0x1d,	-1,		-1,		-1	},	insn_parse_inh,			insn_resolve_inh,				insn_emit_inh,				lwasm_insn_normal},
+	{ "sexw",		{	0x14,	-1,		-1,		-1	},	insn_parse_inh,			insn_resolve_inh,				insn_emit_inh,				lwasm_insn_is6309},
+	{ "sta",		{	0x97,	0xa7,	0xb7,	-1	},	insn_parse_gen0,		insn_resolve_gen0,				insn_emit_gen0,				lwasm_insn_normal},
+	{ "stb",		{	0xd7,	0xe7,	0xf7,	-1	},	insn_parse_gen0,		insn_resolve_gen0,				insn_emit_gen0,				lwasm_insn_normal},
+	{ "stbt",		{	0x1137,	-1,		-1,		-1	},	insn_parse_bitbit,		insn_resolve_bitbit,			insn_emit_bitbit,			lwasm_insn_is6309},
+	{ "std",		{	0xdd,	0xed,	0xfd,	-1	},	insn_parse_gen0,		insn_resolve_gen0,				insn_emit_gen0,				lwasm_insn_normal},
+	{ "ste",		{	0x1197,	0x11a7,	0x11b7,	-1	},	insn_parse_gen0,		insn_resolve_gen0,				insn_emit_gen0,				lwasm_insn_is6309},
+	{ "stf",		{	0x11d7,	0x11e7,	0x11f7,	-1	},	insn_parse_gen0,		insn_resolve_gen0,				insn_emit_gen0,				lwasm_insn_is6309},
+	{ "stq",		{	0x10dd,	0x10ed,	0x10fd,	-1	},	insn_parse_gen0,		insn_resolve_gen0,				insn_emit_gen0,				lwasm_insn_is6309},
+	{ "sts",		{	0x10df,	0x10ef,	0x10ff,	-1	},	insn_parse_gen0,		insn_resolve_gen0,				insn_emit_gen0,				lwasm_insn_normal},
+	{ "stu",		{	0xdf,	0xef,	0xff,	-1	},	insn_parse_gen0,		insn_resolve_gen0,				insn_emit_gen0,				lwasm_insn_normal},
+	{ "stw",		{	0x1097,	0x10a7,	0x10b7,	-1	},	insn_parse_gen0,		insn_resolve_gen0,				insn_emit_gen0,				lwasm_insn_is6309},
+	{ "stx",		{	0x9f,	0xaf,	0xbf,	-1	},	insn_parse_gen0,		insn_resolve_gen0,				insn_emit_gen0,				lwasm_insn_normal},
+	{ "sty",		{	0x109f,	0x10af,	0x10bf,	-1	},	insn_parse_gen0,		insn_resolve_gen0,				insn_emit_gen0,				lwasm_insn_normal},
+	{ "suba",		{	0x90,	0xa0,	0xb0,	0x80},	insn_parse_gen8,		insn_resolve_gen8,				insn_emit_gen8,				lwasm_insn_normal},
+	{ "subb",		{	0xd0,	0xe0,	0xf0,	0xc0},	insn_parse_gen8,		insn_resolve_gen8,				insn_emit_gen8,				lwasm_insn_normal},
+	{ "subd",		{	0x93,	0xa3,	0xb3,	0x83},	insn_parse_gen16,		insn_resolve_gen16,				insn_emit_gen16,			lwasm_insn_normal},
+	{ "sube",		{	0x1190,	0x11a0,	0x11b0,	0x1180},insn_parse_gen8,		insn_resolve_gen8,				insn_emit_gen8,				lwasm_insn_is6309},
+	{ "subf",		{	0x11d0,	0x11e0,	0x11f0,	0x11c0},insn_parse_gen8,		insn_resolve_gen8,				insn_emit_gen8,				lwasm_insn_is6309},
+	{ "subr",		{	0x1032,	-1,		-1,		-1	},	insn_parse_rtor,		insn_resolve_rtor,				insn_emit_rtor,				lwasm_insn_is6309},
+	{ "subw",		{	0x1090,	0x10a0,	0x1090,	0x1080},insn_parse_gen8,		insn_resolve_gen8,				insn_emit_gen8,				lwasm_insn_is6309},
+	{ "swi",		{	0x3f,	-1,		-1,		-1	},	insn_parse_inh,			insn_resolve_inh,				insn_emit_inh,				lwasm_insn_normal},
+	{ "swi2",		{	0x103f,	-1,		-1,		-1	},	insn_parse_inh,			insn_resolve_inh,				insn_emit_inh,				lwasm_insn_normal},
+	{ "swi3",		{	0x113f,	-1,		-1,		-1	},	insn_parse_inh,			insn_resolve_inh,				insn_emit_inh,				lwasm_insn_normal},
+	{ "sync",		{	0x13,	-1,		-1,		-1	},	insn_parse_inh,			insn_resolve_inh,				insn_emit_inh,				lwasm_insn_normal},
+	
+	// note: 			r+,r+	r-,r-	r+,r	r,r+
+	{ "tfm",		{	0x1138,	0x1139,	0x113a,	0x113b },insn_parse_tfm,		insn_resolve_tfm,				insn_emit_tfm,				lwasm_insn_is6309},
+
+	// compatibility opcodes for tfm in other assemblers
+	{ "copy",		{	0x1138, -1, 	-1, 	-1},	insn_parse_tfmrtor,		insn_resolve_tfmrtor,			insn_emit_tfmrtor,			lwasm_insn_is6309},
+	{ "copy+",		{	0x1138, -1, 	-1, 	-1},	insn_parse_tfmrtor,		insn_resolve_tfmrtor,			insn_emit_tfmrtor,			lwasm_insn_is6309},
+	{ "tfrp",		{	0x1138, -1, 	-1, 	-1},	insn_parse_tfmrtor,		insn_resolve_tfmrtor,			insn_emit_tfmrtor,			lwasm_insn_is6309},
+	
+	{ "copy-",		{	0x1139, -1, 	-1, 	-1},	insn_parse_tfmrtor,		insn_resolve_tfmrtor,			insn_emit_tfmrtor,			lwasm_insn_is6309},
+	{ "tfrm",		{	0x1139, -1, 	-1, 	-1},	insn_parse_tfmrtor,		insn_resolve_tfmrtor,			insn_emit_tfmrtor,			lwasm_insn_is6309},
+	
+	{ "imp",		{	0x113a, -1, 	-1, 	-1},	insn_parse_tfmrtor,		insn_resolve_tfmrtor,			insn_emit_tfmrtor,			lwasm_insn_is6309},
+	{ "implode",	{	0x113a, -1, 	-1, 	-1},	insn_parse_tfmrtor,		insn_resolve_tfmrtor,			insn_emit_tfmrtor,			lwasm_insn_is6309},
+	{ "tfrs",		{	0x113a, -1, 	-1, 	-1},	insn_parse_tfmrtor,		insn_resolve_tfmrtor,			insn_emit_tfmrtor,			lwasm_insn_is6309},
+	
+	{ "exp",		{	0x113b, -1, 	-1, 	-1},	insn_parse_tfmrtor,		insn_resolve_tfmrtor,			insn_emit_tfmrtor,			lwasm_insn_is6309},
+	{ "expand",		{	0x113b, -1, 	-1, 	-1},	insn_parse_tfmrtor,		insn_resolve_tfmrtor,			insn_emit_tfmrtor,			lwasm_insn_is6309},
+	{ "tfrr",		{	0x113b, -1, 	-1, 	-1},	insn_parse_tfmrtor,		insn_resolve_tfmrtor,			insn_emit_tfmrtor,			lwasm_insn_is6309},
+
+	{ "tfr",		{	0x1f,	-1,		-1,		-1	},	insn_parse_rtor,		insn_resolve_rtor,				insn_emit_rtor,				lwasm_insn_normal},
+	{ "tim",		{	0x0b,	0x6b,	0x7b,	-1	},	insn_parse_logicmem,	insn_resolve_logicmem,			insn_emit_logicmem,			lwasm_insn_is6309},
+	{ "tst",		{	0x0d,	0x6d,	0x7d,	-1	},	insn_parse_gen0,		insn_resolve_gen0,				insn_emit_gen0,				lwasm_insn_normal},
+	{ "tsta",		{	0x4d,	-1,		-1,		-1	},	insn_parse_inh,			insn_resolve_inh,				insn_emit_inh,				lwasm_insn_normal},
+	{ "tstb",		{	0x5d,	-1,		-1,		-1	},	insn_parse_inh,			insn_resolve_inh,				insn_emit_inh,				lwasm_insn_normal},
+	{ "tstd",		{	0x104d,	-1,		-1,		-1	},	insn_parse_inh,			insn_resolve_inh,				insn_emit_inh,				lwasm_insn_is6309},
+	{ "tste",		{	0x114d,	-1,		-1,		-1	},	insn_parse_inh,			insn_resolve_inh,				insn_emit_inh,				lwasm_insn_is6309},
+	{ "tstf",		{	0x115d,	-1,		-1,		-1	},	insn_parse_inh,			insn_resolve_inh,				insn_emit_inh,				lwasm_insn_is6309},
+	{ "tstw",		{	0x105d,	-1,		-1,		-1	},	insn_parse_inh,			insn_resolve_inh,				insn_emit_inh,				lwasm_insn_is6309},
+
+	{ "org",		{	-1, 	-1, 	-1, 	-1 },	pseudo_parse_org,		pseudo_resolve_org,				pseudo_emit_org,			lwasm_insn_normal},
+	{ "equ",		{	-1, 	-1, 	-1, 	-1 },	pseudo_parse_equ,		pseudo_resolve_equ,				pseudo_emit_equ,			lwasm_insn_setsym},
+	{ "=",			{	-1, 	-1, 	-1, 	-1 },	pseudo_parse_equ,		pseudo_resolve_equ,				pseudo_emit_equ,			lwasm_insn_setsym},
+
+	{ "extern",		{	-1, 	-1, 	-1, 	-1 },	pseudo_parse_extern,	pseudo_resolve_extern,			pseudo_emit_extern,			lwasm_insn_setsym},
+	{ "external",	{	-1, 	-1, 	-1, 	-1 },	pseudo_parse_extern,	pseudo_resolve_extern,			pseudo_emit_extern,			lwasm_insn_setsym},
+	{ "import",		{	-1, 	-1, 	-1, 	-1 },	pseudo_parse_extern,	pseudo_resolve_extern,			pseudo_emit_extern,			lwasm_insn_setsym},
+	{ "export",		{	-1, 	-1, 	-1, 	-1 },	pseudo_parse_export,	pseudo_resolve_export,			pseudo_emit_export,			lwasm_insn_setsym},
+	{ "extdep",		{	-1,		-1,		-1,		-1 },	pseudo_parse_extdep,	pseudo_resolve_extdep,			pseudo_emit_extdep,			lwasm_insn_setsym},
+
+	{ "rmb", 		{	-1, 	-1, 	-1, 	-1 },	pseudo_parse_rmb,		pseudo_resolve_rmb,				pseudo_emit_rmb,			lwasm_insn_struct},
+	{ "rmd", 		{	-1, 	-1, 	-1, 	-1 },	pseudo_parse_rmd,		pseudo_resolve_rmd,				pseudo_emit_rmd,			lwasm_insn_struct},
+	{ "rmw", 		{	-1, 	-1, 	-1, 	-1 },	pseudo_parse_rmd,		pseudo_resolve_rmd,				pseudo_emit_rmd,			lwasm_insn_struct},
+	{ "rmq", 		{	-1, 	-1, 	-1, 	-1 },	pseudo_parse_rmq,		pseudo_resolve_rmq,				pseudo_emit_rmq,			lwasm_insn_struct},
+
+	{ "zmb", 		{	-1, 	-1, 	-1, 	-1 },	pseudo_parse_zmb,		pseudo_resolve_zmb,				pseudo_emit_zmb,			lwasm_insn_normal},
+	{ "zmd", 		{	-1, 	-1, 	-1, 	-1 },	pseudo_parse_zmd,		pseudo_resolve_zmd,				pseudo_emit_zmd,			lwasm_insn_normal},
+	{ "zmq", 		{	-1, 	-1, 	-1, 	-1 },	pseudo_parse_zmq,		pseudo_resolve_zmq,				pseudo_emit_zmq,			lwasm_insn_normal},
+
+	{ "fcc",		{	-1, 	-1, 	-1, 	-1 },	pseudo_parse_fcc,		pseudo_resolve_fcc,				pseudo_emit_fcc,			lwasm_insn_normal},
+	{ "fcn",		{	-1, 	-1, 	-1, 	-1 },	pseudo_parse_fcn,		pseudo_resolve_fcn,				pseudo_emit_fcn,			lwasm_insn_normal},
+	{ "fcs",		{	-1, 	-1, 	-1, 	-1 },	pseudo_parse_fcs,		pseudo_resolve_fcs,				pseudo_emit_fcs,			lwasm_insn_normal},
+
+	{ "fcb",		{	-1, 	-1, 	-1, 	-1 },	pseudo_parse_fcb,		pseudo_resolve_fcb,				pseudo_emit_fcb,			lwasm_insn_normal},
+	{ "fdb",		{	-1, 	-1, 	-1, 	-1 },	pseudo_parse_fdb,		pseudo_resolve_fdb,				pseudo_emit_fdb,			lwasm_insn_normal},
+	{ "fqb",		{	-1, 	-1, 	-1, 	-1 },	pseudo_parse_fqb,		pseudo_resolve_fqb,				pseudo_emit_fqb,			lwasm_insn_normal},
+	{ "end", 		{	-1, 	-1, 	-1, 	-1 },	pseudo_parse_end,		pseudo_resolve_end,				pseudo_emit_end,			lwasm_insn_normal},
+
+	{ "includebin", {	-1, 	-1, 	-1, 	-1},	pseudo_parse_includebin,pseudo_resolve_includebin,		pseudo_emit_includebin,		lwasm_insn_normal},
+	{ "include",	{	-1, 	-1, 	-1, 	-1 },	pseudo_parse_include,	pseudo_resolve_include,			pseudo_emit_include,		lwasm_insn_normal},
+	{ "use",		{	-1, 	-1, 	-1, 	-1 },	pseudo_parse_include,	pseudo_resolve_include,			pseudo_emit_include,		lwasm_insn_normal},
+	
+	{ "align", 		{	-1, 	-1, 	-1, 	-1 },	pseudo_parse_align,		pseudo_resolve_align,			pseudo_emit_align,			lwasm_insn_normal},
+
+	{ "error",		{	-1, 	-1, 	-1, 	-1},	pseudo_parse_error,		pseudo_resolve_error,			pseudo_emit_error,			lwasm_insn_normal},
+	{ "warning",	{	-1, 	-1, 	-1, 	-1},	pseudo_parse_warning,	pseudo_resolve_warning,			pseudo_emit_warning,		lwasm_insn_normal},
+
+	// these are *dangerous*
+	{ "ifp1",		{	-1, 	-1, 	-1, 	-1},	pseudo_parse_ifp1,		pseudo_resolve_ifp1,			pseudo_emit_ifp1,			lwasm_insn_cond},
+	{ "ifp2",		{	-1, 	-1, 	-1, 	-1},	pseudo_parse_ifp2,		pseudo_resolve_ifp2,			pseudo_emit_ifp2,			lwasm_insn_cond},
+
+	{ "ifeq",		{	-1, 	-1, 	-1, 	-1}, 	pseudo_parse_ifeq,		pseudo_resolve_ifeq,			pseudo_emit_ifeq,			lwasm_insn_cond},
+	{ "ifne",		{	-1, 	-1, 	-1, 	-1}, 	pseudo_parse_ifne,		pseudo_resolve_ifne,			pseudo_emit_ifne,			lwasm_insn_cond},
+	{ "if",			{	-1, 	-1, 	-1, 	-1}, 	pseudo_parse_ifne,		pseudo_resolve_ifne,			pseudo_emit_ifne,			lwasm_insn_cond},
+	{ "ifgt",		{	-1, 	-1, 	-1, 	-1}, 	pseudo_parse_ifgt,		pseudo_resolve_ifgt,			pseudo_emit_ifgt,			lwasm_insn_cond},
+	{ "ifge",		{	-1, 	-1, 	-1, 	-1}, 	pseudo_parse_ifge,		pseudo_resolve_ifge,			pseudo_emit_ifge,			lwasm_insn_cond},
+	{ "iflt",		{	-1, 	-1, 	-1, 	-1}, 	pseudo_parse_iflt,		pseudo_resolve_iflt,			pseudo_emit_iflt,			lwasm_insn_cond},
+	{ "ifle",		{	-1, 	-1, 	-1, 	-1}, 	pseudo_parse_ifle,		pseudo_resolve_ifle,			pseudo_emit_ifle,			lwasm_insn_cond},
+	{ "endc",		{	-1, 	-1, 	-1, 	-1}, 	pseudo_parse_endc,		pseudo_resolve_endc,			pseudo_emit_endc,			lwasm_insn_cond},
+	{ "else",		{	-1, 	-1, 	-1, 	-1}, 	pseudo_parse_else,		pseudo_resolve_else,			pseudo_emit_else,			lwasm_insn_cond},
+	{ "ifdef",		{	-1, 	-1, 	-1, 	-1},	pseudo_parse_ifdef,		pseudo_resolve_ifdef,			pseudo_emit_ifdef,			lwasm_insn_cond},
+	{ "ifndef",		{	-1, 	-1, 	-1, 	-1},	pseudo_parse_ifndef,	pseudo_resolve_ifndef,			pseudo_emit_ifndef,			lwasm_insn_cond},
+
+	{ "macro",		{	-1, 	-1, 	-1, 	-1}, 	pseudo_parse_macro,		pseudo_resolve_macro,			pseudo_emit_macro,			lwasm_insn_cond | lwasm_insn_setsym},
+	{ "endm",		{	-1, 	-1, 	-1, 	-1},	pseudo_parse_endm,		pseudo_resolve_endm,			pseudo_emit_endm,			lwasm_insn_cond | lwasm_insn_setsym | lwasm_insn_endm},
+
+	{ "setdp", 		{	-1, 	-1, 	-1, 	-1},	pseudo_parse_setdp,		pseudo_resolve_setdp,			pseudo_emit_setdp,			lwasm_insn_normal},
+	{ "set",		{	-1, 	-1, 	-1, 	-1},	pseudo_parse_set,		pseudo_resolve_set,				pseudo_emit_set,			lwasm_insn_setsym},
+
+
+	{ "section",	{	-1, 	-1, 	-1, 	-1},	pseudo_parse_section,	pseudo_resolve_section,			pseudo_emit_section,		lwasm_insn_normal},
+	{ "sect",		{	-1, 	-1, 	-1, 	-1},	pseudo_parse_section,	pseudo_resolve_section,			pseudo_emit_section,		lwasm_insn_normal},
+	{ "endsect",	{	-1, 	-1, 	-1, 	-1},	pseudo_parse_endsection,pseudo_resolve_endsection,		pseudo_emit_endsection,		lwasm_insn_normal},
+	{ "endsection",	{	-1,		-1, 	-1, 	-1},	pseudo_parse_endsection,pseudo_resolve_endsection,		pseudo_emit_endsection,		lwasm_insn_normal},
+
+	{ "struct",		{	-1,		-1,		-1,		-1},	pseudo_parse_struct,	pseudo_resolve_struct,			pseudo_emit_struct,			lwasm_insn_normal},
+	{ "ends",		{	-1, 	-1, 	-1, 	-1},	pseudo_parse_endstruct,	pseudo_resolve_endstruct,		pseudo_emit_endstruct,		lwasm_insn_struct},
+	{ "endstruct",	{	-1,		-1,		-1,		-1},	pseudo_parse_endstruct,	pseudo_resolve_endstruct,		pseudo_emit_endstruct,		lwasm_insn_struct},
+	
+
+	{ "pragma",		{	-1, 	-1, 	-1, 	-1},	pseudo_parse_pragma,	pseudo_resolve_pragma,			pseudo_emit_pragma,			lwasm_insn_normal},
+	{ "*pragma",	{	-1, 	-1, 	-1, 	-1},	pseudo_parse_starpragma,pseudo_resolve_starpragma,		pseudo_emit_starpragma,		lwasm_insn_normal},
+	
+	// for os9 target
+	{ "os9",		{	-1, 	-1, 	-1, 	-1 },	pseudo_parse_os9,		pseudo_resolve_os9,				pseudo_emit_os9,			lwasm_insn_normal},
+	{ "mod",		{	-1, 	-1, 	-1, 	-1 },	pseudo_parse_mod,		pseudo_resolve_mod,				pseudo_emit_mod,			lwasm_insn_normal},
+	{ "emod",		{	-1, 	-1, 	-1, 	-1 },	pseudo_parse_emod,		pseudo_resolve_emod,			pseudo_emit_emod,			lwasm_insn_normal},
+
+	// for compatibility with gcc6809 output...
+
+	{ ".area",		{	-1, 	-1, 	-1, 	-1},	pseudo_parse_section,	pseudo_resolve_section,			pseudo_emit_section,		lwasm_insn_normal},
+	{ ".globl",		{	-1, 	-1, 	-1, 	-1}, 	pseudo_parse_export,	pseudo_resolve_export,			pseudo_emit_export,			lwasm_insn_normal},
+
+	{ ".module",	{	-1, 	-1, 	-1, 	-1},	pseudo_parse_noop,		pseudo_resolve_noop,			pseudo_emit_noop,			lwasm_insn_normal},
+	
+	{ ".4byte",		{	-1, 	-1, 	-1, 	-1},	pseudo_parse_fqb,		pseudo_resolve_fqb,				pseudo_emit_fqb,			lwasm_insn_normal},
+	{ ".quad",		{	-1, 	-1, 	-1, 	-1},	pseudo_parse_fqb,		pseudo_resolve_fqb,				pseudo_emit_fqb,			lwasm_insn_normal},
+	
+	{ ".word",		{	-1, 	-1, 	-1, 	-1},	pseudo_parse_fdb,		pseudo_resolve_fdb,				pseudo_emit_fdb,			lwasm_insn_normal},
+	{ ".dw",		{	-1, 	-1, 	-1, 	-1},	pseudo_parse_fdb,		pseudo_resolve_fdb,				pseudo_emit_fdb,			lwasm_insn_normal},
+
+	{ ".byte",		{	-1, 	-1, 	-1, 	-1},	pseudo_parse_fcb,		pseudo_resolve_fcb,				pseudo_emit_fcb,			lwasm_insn_normal},
+	{ ".db",		{	-1, 	-1, 	-1, 	-1},	pseudo_parse_fcb,		pseudo_resolve_fcb,				pseudo_emit_fcb,			lwasm_insn_normal},
+
+	{ ".ascii",		{	-1, 	-1, 	-1, 	-1},	pseudo_parse_fcc,		pseudo_resolve_fcc,				pseudo_emit_fcc,			lwasm_insn_normal},
+	{ ".str",		{	-1, 	-1, 	-1, 	-1},	pseudo_parse_fcc,		pseudo_resolve_fcc,				pseudo_emit_fcc,			lwasm_insn_normal},
+	
+	{ ".ascis",		{	-1, 	-1, 	-1, 	-1},	pseudo_parse_fcs,		pseudo_resolve_fcs,				pseudo_emit_fcs,			lwasm_insn_normal},
+	{ ".strs",		{	-1, 	-1, 	-1, 	-1},	pseudo_parse_fcs,		pseudo_resolve_fcs,				pseudo_emit_fcs,			lwasm_insn_normal},
+	
+	{ ".asciz",		{	-1, 	-1, 	-1, 	-1},	pseudo_parse_fcn,		pseudo_resolve_fcn,				pseudo_emit_fcn,			lwasm_insn_normal},
+	{ ".strz",		{	-1, 	-1, 	-1, 	-1},	pseudo_parse_fcn,		pseudo_resolve_fcn,				pseudo_emit_fcn,			lwasm_insn_normal},
+
+	{ ".blkb",		{	-1, 	-1, 	-1, 	-1},	pseudo_parse_rmb,		pseudo_resolve_rmb,				pseudo_emit_rmb,			lwasm_insn_struct},
+	{ ".ds",		{	-1, 	-1, 	-1, 	-1},	pseudo_parse_rmb,		pseudo_resolve_rmb,				pseudo_emit_rmb,			lwasm_insn_struct},
+	{ ".rs",		{	-1, 	-1, 	-1, 	-1},	pseudo_parse_rmb,		pseudo_resolve_rmb,				pseudo_emit_rmb,			lwasm_insn_struct},
+
+	// for compatibility
+	{ ".end", 		{	-1, 	-1, 	-1, 	-1 },	pseudo_parse_end,		pseudo_resolve_end,				pseudo_emit_end,			lwasm_insn_normal},
+
+	// extra ops that are ignored because they are generally only for
+	// pretty printing the listing
+	{ "nam",		{	-1, 	-1, 	-1, 	-1 },	pseudo_parse_noop,		pseudo_resolve_noop,			pseudo_emit_noop,			lwasm_insn_normal},
+	{ "pag",		{	-1, 	-1, 	-1, 	-1 },	pseudo_parse_noop,		pseudo_resolve_noop,			pseudo_emit_noop,			lwasm_insn_normal},
+	{ "ttl",		{	-1, 	-1, 	-1, 	-1 },	pseudo_parse_noop,		pseudo_resolve_noop,			pseudo_emit_noop,			lwasm_insn_normal},
+	{ ".bank",		{	-1, 	-1, 	-1, 	-1 },	pseudo_parse_noop,		pseudo_resolve_noop,			pseudo_emit_noop,			lwasm_insn_normal},
+
+	// flag end of table
+	{ NULL,			{	-1, 	-1, 	-1, 	-1 },	NULL,					NULL,							lwasm_insn_normal}
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lwasm/instab.h	Wed Jan 19 22:27:17 2011 -0700
@@ -0,0 +1,58 @@
+/*
+instab.h
+Copyright © 2008 William Astle
+
+This file is part of LWASM.
+
+LWASM 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 3 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, see <http://www.gnu.org/licenses/>.
+
+
+Contains definitions for the instruction table
+*/
+
+#ifndef __instab_h_seen__
+#define __instab_h_seen__
+
+#include "lwasm.h"
+
+typedef struct
+{
+	char *opcode;				/* the mneumonic */
+	int ops[4];					/* opcode values for up to four addr modes */
+	void (*parse)(asmstate_t *as, line_t *l, char **optr);	/* parse operand for insn */
+	void (*resolve)(asmstate_t *as, line_t *l, int force);				/* resolve instruction to code */
+	void (*emit)(asmstate_t *as, line_t *l);				/* resolve instruction to code */
+	int flags;					/* flag for this instruction */
+} instab_t;
+
+enum
+{
+	lwasm_insn_cond = 1,		/* conditional instruction */
+	lwasm_insn_endm = 2,		/* end of macro */
+	lwasm_insn_setsym = 4,		/* insn sets symbol address */
+	lwasm_insn_is6309 = 8,		/* insn is 6309 only */
+	lwasm_insn_struct = 16,		/* insn allowed in a struct */
+	lwasm_insn_normal = 0
+};
+
+
+#define PARSEFUNC(fn)	void (fn)(asmstate_t *as, line_t *l, char **p)
+#define RESOLVEFUNC(fn)	void (fn)(asmstate_t *as, line_t *l, int force)
+#define EMITFUNC(fn)	void (fn)(asmstate_t *as, line_t *l)
+
+#ifndef __instab_c_seen__
+extern instab_t instab[];
+#endif //__instab_c_seen__
+
+#endif //__instab_h_seen__
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lwasm/list.c	Wed Jan 19 22:27:17 2011 -0700
@@ -0,0 +1,140 @@
+/*
+list.c
+
+Copyright © 2010 William Astle
+
+This file is part of LWTOOLS.
+
+LWTOOLS 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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdio.h>
+#include <string.h>
+
+#include <lw_alloc.h>
+#include <lw_string.h>
+
+#include "lwasm.h"
+#include "instab.h"
+
+void list_symbols(asmstate_t *as, FILE *of);
+
+/*
+Do listing
+*/
+void do_list(asmstate_t *as)
+{
+	line_t *cl;
+	FILE *of;
+	int i;
+	
+	if (!(as -> flags & FLAG_LIST))
+		return;
+
+	if (as -> list_file)
+		of = fopen(as -> list_file, "w");
+	else
+		of = stdout;
+	if (!of)
+	{
+		fprintf(stderr, "Cannot open list file; list not generated\n");
+		return;
+	}
+	for (cl = as -> line_head; cl; cl = cl -> next)
+	{
+		if (cl -> len < 1)
+		{
+			if (cl -> soff >= 0)
+			{
+				fprintf(of, "%04X                  ", cl -> soff & 0xffff);
+			}
+			else if (cl -> dshow >= 0)
+			{
+				if (cl -> dsize == 1)
+				{
+					fprintf(of, "     %02X                 ", cl -> dshow & 0xff);
+				}
+				else
+				{
+					fprintf(of, "     %04X               ", cl -> dshow & 0xff);
+				}
+			}
+			else if (cl -> dptr)
+			{
+				lw_expr_t te;
+				te = lw_expr_copy(cl -> dptr -> value);
+				as -> exportcheck = 1;
+				as -> csect = cl -> csect;
+				lwasm_reduce_expr(as, te);
+				as -> exportcheck = 0;
+				if (lw_expr_istype(te, lw_expr_type_int))
+				{
+					fprintf(of, "     %04X             ", lw_expr_intval(te) & 0xffff);
+				}
+				else
+				{
+					fprintf(of, "     ????             ");
+				}
+				lw_expr_destroy(te);
+			}
+			else
+			{
+				fprintf(of, "                      ");
+			}
+		}
+		else
+		{
+			lw_expr_t te;
+			te = lw_expr_copy(cl -> addr);
+			as -> exportcheck = 1;
+			as -> csect = cl -> csect;
+			lwasm_reduce_expr(as, te);
+			as -> exportcheck = 0;
+//			fprintf(of, "%s\n", lw_expr_print(te));
+			fprintf(of, "%04X ", lw_expr_intval(te) & 0xffff);
+			lw_expr_destroy(te);
+			for (i = 0; i < cl -> outputl && i < 8; i++)
+			{
+				fprintf(of, "%02X", cl -> output[i]);
+			}
+			for (; i < 8; i++)
+			{
+				fprintf(of, "  ");
+			}
+			fprintf(of, " ");
+		}
+		/* the 34.34 below is deliberately chosen so that the start of the line text is at
+		   a multiple of 8 from the start of the list line */
+		fprintf(of, "(%34.34s):%05d %s\n", cl -> linespec, cl -> lineno, cl -> ltext);
+		if (cl -> outputl > 8)
+		{
+			for (i = 8; i < cl -> outputl; i++)
+			{
+				if (i % 8 == 0)
+				{
+					if (i != 8)
+						fprintf(of, "\n     ");
+					else
+						fprintf(of, "     ");
+				}
+				fprintf(of, "%02X", cl -> output[i]);
+			}
+			if (i > 8)
+				fprintf(of, "\n");
+		}
+	}
+	
+	if (as -> flags & FLAG_SYMBOLS)
+		list_symbols(as, of);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lwasm/lwasm.c	Wed Jan 19 22:27:17 2011 -0700
@@ -0,0 +1,866 @@
+/*
+lwasm.c
+
+Copyright © 2010 William Astle
+
+This file is part of LWTOOLS.
+
+LWTOOLS 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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#define ___lwasm_c_seen___
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+
+#include <lw_expr.h>
+#include <lw_alloc.h>
+#include <lw_string.h>
+
+#include "lwasm.h"
+
+void lwasm_register_error(asmstate_t *as, line_t *l, const char *msg, ...);
+
+int lwasm_expr_exportable(asmstate_t *as, lw_expr_t expr)
+{
+	return 0;
+}
+
+int lwasm_expr_exportval(asmstate_t *as, lw_expr_t expr)
+{
+	return 0;
+}
+
+lw_expr_t lwasm_evaluate_var(char *var, void *priv)
+{
+	asmstate_t *as = (asmstate_t *)priv;
+	lw_expr_t e;
+	importlist_t *im;
+	struct symtabe *s;
+	
+	s = lookup_symbol(as, as -> cl, var);
+	if (s)
+	{
+		e = lw_expr_build(lw_expr_type_special, lwasm_expr_syment, s);
+		return e;
+	}
+	
+	// undefined here is undefied unless output is object
+	if (as -> output_format != OUTPUT_OBJ)
+		goto nomatch;
+	
+	// check for import
+	for (im = as -> importlist; im; im = im -> next)
+	{
+		if (!strcmp(im -> symbol, var))
+			break;
+	}
+	
+	// check for "undefined" to import automatically
+	if (!im && CURPRAGMA(as -> cl, PRAGMA_UNDEFEXTERN))
+	{
+		im = lw_alloc(sizeof(importlist_t));
+		im -> symbol = lw_strdup(var);
+		im -> next = as -> importlist;
+		as -> importlist = im;
+	}
+	
+	if (!im)
+		goto nomatch;
+
+	e = lw_expr_build(lw_expr_type_special, lwasm_expr_import, im);
+	return e;
+
+nomatch:
+	if (as -> badsymerr)
+	{
+		lwasm_register_error(as, as -> cl, "Undefined symbol %s", var);
+	}
+	return NULL;
+}
+
+lw_expr_t lwasm_evaluate_special(int t, void *ptr, void *priv)
+{
+	switch (t)
+	{
+	case lwasm_expr_secbase:
+		{
+//			sectiontab_t *s = priv;
+			asmstate_t *as = priv;
+			if (as -> exportcheck && ptr == as -> csect)
+				return lw_expr_build(lw_expr_type_int, 0);
+			return NULL;
+		}
+			
+	case lwasm_expr_linelen:
+		{
+			line_t *cl = ptr;
+			if (cl -> len == -1)
+				return NULL;
+			return lw_expr_build(lw_expr_type_int, cl -> len);
+		}
+		break;
+		
+	case lwasm_expr_lineaddr:
+		{
+			line_t *cl = ptr;
+			if (cl -> addr)
+				return lw_expr_copy(cl -> addr);
+			else
+				return NULL;
+		}
+	
+	case lwasm_expr_syment:
+		{
+			struct symtabe *sym = ptr;
+			return lw_expr_copy(sym -> value);
+		}
+	
+	case lwasm_expr_import:
+		{
+			return NULL;
+		}
+	
+	case lwasm_expr_nextbp:
+		{
+			line_t *cl = ptr;
+			for (cl = cl -> next; cl; cl = cl -> next)
+			{
+				if (cl -> isbrpt)
+					break;
+			}
+			if (cl)
+			{
+				return lw_expr_copy(cl -> addr);
+			}
+			return NULL;
+		}
+	
+	case lwasm_expr_prevbp:
+		{
+			line_t *cl = ptr;
+			for (cl = cl -> prev; cl; cl = cl -> prev)
+			{
+				if (cl -> isbrpt)
+					break;
+			}
+			if (cl)
+			{
+				return lw_expr_copy(cl -> addr);
+			}
+			return NULL;
+		}
+	}
+	return NULL;
+}
+
+void lwasm_register_error(asmstate_t *as, line_t *l, const char *msg, ...)
+{
+	lwasm_error_t *e;
+	va_list args;
+	char errbuff[1024];
+	int r;
+	
+	if (!l)
+		return;
+
+	va_start(args, msg);
+	
+	e = lw_alloc(sizeof(lwasm_error_t));
+	
+	e -> next = l -> err;
+	l -> err = e;
+	
+	as -> errorcount++;
+	
+	r = vsnprintf(errbuff, 1024, msg, args);
+	e -> mess = lw_strdup(errbuff);
+	
+	va_end(args);
+}
+
+void lwasm_register_warning(asmstate_t *as, line_t *l, const char *msg, ...)
+{
+	lwasm_error_t *e;
+	va_list args;
+	char errbuff[1024];
+	int r;
+	
+	if (!l)
+		return;
+
+	va_start(args, msg);
+	
+	e = lw_alloc(sizeof(lwasm_error_t));
+	
+	e -> next = l -> err;
+	l -> err = e;
+	
+	as -> errorcount++;
+	
+	r = vsnprintf(errbuff, 1024, msg, args);
+	e -> mess = lw_strdup(errbuff);
+	
+	va_end(args);
+}
+
+int lwasm_next_context(asmstate_t *as)
+{
+	int r;
+	r = as -> nextcontext;
+	as -> nextcontext++;
+	return r;
+}
+
+void lwasm_emit(line_t *cl, int byte)
+{
+	if (cl -> outputl < 0)
+		cl -> outputl = 0;
+
+	if (cl -> outputl == cl -> outputbl)
+	{
+		cl -> output = lw_realloc(cl -> output, cl -> outputbl + 8);
+		cl -> outputbl += 8;
+	}
+	cl -> output[cl -> outputl++] = byte & 0xff;
+	
+	if (cl -> inmod)
+	{
+		asmstate_t *as = cl -> as;
+		// update module CRC
+		// this is a direct transliteration from the nitros9 asm source
+		// to C; it can, no doubt, be optimized for 32 bit processing  
+		byte &= 0xff;
+
+		byte ^= (as -> crc)[0];
+		(as -> crc)[0] = (as -> crc)[1];
+		(as -> crc)[1] = (as -> crc)[2];
+		(as -> crc)[1] ^= (byte >> 7);
+		(as -> crc)[2] = (byte << 1); 
+		(as -> crc)[1] ^= (byte >> 2);
+		(as -> crc)[2] ^= (byte << 6);
+		byte ^= (byte << 1);
+		byte ^= (byte << 2);
+		byte ^= (byte << 4);
+		if (byte & 0x80) 
+		{
+			(as -> crc)[0] ^= 0x80;
+		    (as -> crc)[2] ^= 0x21;
+		}
+	}
+}
+
+void lwasm_emitop(line_t *cl, int opc)
+{
+	if (opc > 0x100)
+		lwasm_emit(cl, opc >> 8);
+	lwasm_emit(cl, opc);
+}
+
+lw_expr_t lwasm_parse_term(char **p, void *priv)
+{
+	asmstate_t *as = priv;
+	int val;
+	
+	if (!**p)
+		return NULL;
+	
+	if (**p == '*' || (
+			**p == '.'
+			&& !((*p)[1] >= 'A' && (*p)[1] <= 'Z')
+			&& !((*p)[1] >= 'a' && (*p)[1] <= 'z')
+			&& !((*p)[1] >= '0' && (*p)[1] <= '9')
+		))
+	{
+		// special "symbol" for current line addr (*, .)
+		(*p)++;
+		return lw_expr_build(lw_expr_type_special, lwasm_expr_lineaddr, as -> cl);
+	}
+	
+	// branch points
+	if (**p == '<')
+	{
+		(*p)++;
+		return lw_expr_build(lw_expr_type_special, lwasm_expr_prevbp, as -> cl);
+	}
+	if (**p == '>')
+	{
+		(*p)++;
+		return lw_expr_build(lw_expr_type_special, lwasm_expr_nextbp, as -> cl);
+	}
+	
+	// double ascii constant
+	if (**p == '"')
+	{
+		int v;
+		(*p)++;
+		if (!**p)
+			return NULL;
+		if (!*((*p)+1))
+			return NULL;
+		v = (unsigned char)**p << 8 | (unsigned char)*((*p)+1);
+		(*p) += 2;
+		return lw_expr_build(lw_expr_type_int, v);
+	}
+	
+	if (**p == '\'')
+	{
+		int v;
+		
+		(*p)++;
+		if (!**p)
+			return NULL;
+		
+		v = (unsigned char)**p;
+		(*p)++;
+		return lw_expr_build(lw_expr_type_int, v);
+	}
+	
+	if (**p == '&')
+	{
+		// decimal constant
+		int v = 0;
+		(*p)++;
+
+		if (!strchr("0123456789", **p))
+			return NULL;
+
+		while (**p && strchr("0123456789", **p))
+		{
+			val = val * 10 + (**p - '0');
+			(*p)++;
+		}
+		return lw_expr_build(lw_expr_type_int, v);
+	}
+
+	if (**p == '%')
+	{
+		// binary constant
+		int v = 0;
+		(*p)++;
+
+		if (**p != '0' && **p != '1')
+			return NULL;
+
+		while (**p && (**p == '0' || **p == '1'))
+		{
+			val = val * 2 + (**p - '0');
+			(*p)++;
+		}
+		return lw_expr_build(lw_expr_type_int, v);
+	}
+	
+	if (**p == '$')
+	{
+		// hexadecimal constant
+		int v = 0, v2;
+		(*p)++;
+		if (!strchr("0123456789abcdefABCDEF", **p))
+			return NULL;
+
+		while (**p && strchr("0123456789abcdefABCDEF", **p))
+		{
+			v2 = toupper(**p) - '0';
+			if (v2 > 9)
+				v2 -= 7;
+			v = v * 16 + v2;
+			(*p)++;
+		}
+		return lw_expr_build(lw_expr_type_int, v);
+	}
+	
+	if (**p == '0' && (*((*p)+1) == 'x' || *((*p)+1) == 'X'))
+	{
+		// hexadecimal constant, C style
+		int v = 0, v2;
+		(*p)+=2;
+
+		if (!strchr("0123456789abcdefABCDEF", **p))
+			return NULL;
+
+		while (**p && strchr("0123456789abcdefABCDEF", **p))
+		{
+			v2 = toupper(**p) - '0';
+			if (v2 > 9)
+				v2 -= 7;
+			v = v * 16 + v2;
+			(*p)++;
+		}
+		return lw_expr_build(lw_expr_type_int, v);
+	}
+	
+	if (**p == '@' && (*((*p)+1) >= '0' && *((*p)+1) <= '7'))
+	{
+		// octal constant
+		int v = 0;
+		(*p)++;
+
+		if (!strchr("01234567", **p))
+			return NULL;
+
+		while (**p && strchr("01234567", **p))
+		{
+			v = v * 8 + (**p - '0');
+			(*p)++;
+		}
+		return lw_expr_build(lw_expr_type_int, v);
+	}
+	
+
+	// symbol or bare decimal or suffix constant here
+	do
+	{
+		int havedol = 0;
+		int l = 0;
+		
+		while ((*p)[l] && strchr(SYMCHARS, (*p)[l]))
+		{
+			if ((*p)[l] == '$')
+				havedol = 1;
+			l++;
+		}
+		if (l == 0)
+			return NULL;
+
+		if ((*p)[l] == '{')
+		{
+			while ((*p)[l] && (*p)[l] != '}')
+				l++;
+			l++;
+		}
+		
+		if (havedol || **p < '0' || **p > '9')
+		{
+			// have a symbol here
+			char *sym;
+			lw_expr_t term;
+			
+			sym = lw_strndup(*p, l);
+			(*p) += l;
+			term = lw_expr_build(lw_expr_type_var, sym);
+			lw_free(sym);
+			return term;
+		}
+	} while (0);
+	
+	if (!**p)
+		return NULL;
+	
+	// we have a numeric constant here, either decimal or postfix base notation
+	{
+		int decval = 0, binval = 0, hexval = 0, octval = 0;
+		int valtype = 15; // 1 = bin, 2 = oct, 4 = dec, 8 = hex
+		int bindone = 0;
+		int val;
+		int dval;
+		
+		while (1)
+		{
+			if (!**p || !strchr("0123456789ABCDEFabcdefqhoQHO", **p))
+			{
+				// we can legally be bin or decimal here
+				if (bindone)
+				{
+					// just finished a binary value
+					val = binval;
+					break;
+				}
+				else if (valtype & 4)
+				{
+					val = decval;
+					break;
+				}
+				else
+				{
+					// bad value
+					return NULL;
+				}
+			}
+			
+			dval = toupper(**p);
+			(*p)++;
+			
+			if (bindone)
+			{
+				// any characters past "B" means it is not binary
+				bindone = 0;
+				valtype &= 14;
+			}
+			
+			switch (dval)
+			{
+			case 'Q':
+			case 'O':
+				if (valtype & 2)
+				{
+					val = octval;
+					valtype = -1;
+					break;
+				}
+				else
+				{
+					return NULL;
+				}
+				/* can't get here */
+			
+			case 'H':
+				if (valtype & 8)
+				{
+					val = hexval;
+					valtype = -1;
+					break;
+				}
+				else
+				{
+					return NULL;
+				}
+				/* can't get here */
+			
+			case 'B':
+				// this is a bit of a sticky one since B may be a
+				// hex number instead of the end of a binary number
+				// so it falls through to the digit case
+				if (valtype & 1)
+				{
+					// could still be binary of hex
+					bindone = 1;
+					valtype = 9;
+				}
+				/* fall through intented */
+			
+			default:
+				// digit
+				dval -= '0';
+				if (dval > 9)
+					dval -= 7;
+				if (valtype & 8)
+					hexval = hexval * 16 + dval;
+				if (valtype & 4)
+				{
+					if (dval > 9)
+						valtype &= 11;
+					else
+						decval = decval * 10 + dval;
+				}
+				if (valtype & 2)
+				{
+					if (dval > 7)
+						valtype &= 13;
+					else
+						octval = octval * 8 + dval;
+				}
+				if (valtype & 1)
+				{
+					if (dval > 1)
+						valtype &= 14;
+					else
+						binval = binval * 2 + dval;
+				}
+			}
+			if (valtype == -1)
+				break;
+			
+			// return if no more valid types
+			if (valtype == 0)
+				return NULL;
+			
+			val = decval; // in case we fall through	
+		} 
+		
+		// get here if we have a value
+		return lw_expr_build(lw_expr_type_int, val);
+	}
+	// can't get here
+}
+
+lw_expr_t lwasm_parse_expr(asmstate_t *as, char **p)
+{
+	lw_expr_t e;
+	
+	e = lw_expr_parse(p, as);
+	
+	return e;
+}
+
+int lwasm_reduce_expr(asmstate_t *as, lw_expr_t expr)
+{
+	lw_expr_simplify(expr, as);
+}
+
+void lwasm_save_expr(line_t *cl, int id, lw_expr_t expr)
+{
+	struct line_expr_s *e;
+	
+	for (e = cl -> exprs; e; e = e -> next)
+	{
+		if (e -> id == id)
+		{
+			lw_expr_destroy(e -> expr);
+			e -> expr = expr;
+			return;
+		}
+	}
+	
+	e = lw_alloc(sizeof(struct line_expr_s));
+	e -> expr = expr;
+	e -> id = id;
+	e -> next = cl -> exprs;
+	cl -> exprs = e;
+}
+
+lw_expr_t lwasm_fetch_expr(line_t *cl, int id)
+{
+	struct line_expr_s *e;
+	
+	for (e = cl -> exprs; e; e = e -> next)
+	{
+		if (e -> id == id)
+		{
+			return e -> expr;
+		}
+	}
+	return NULL;
+}
+
+void skip_operand(char **p)
+{
+	for (; **p && !isspace(**p); (*p)++)
+		/* do nothing */ ;
+}
+
+int lwasm_emitexpr(line_t *l, lw_expr_t expr, int size)
+{
+	int v = 0;
+	int ol;
+	
+	ol = l -> outputl;
+	if (ol == -1)
+		ol = 0;
+		
+	if (lw_expr_istype(expr, lw_expr_type_int))
+	{
+		v = lw_expr_intval(expr);
+	}
+	// handle external/cross-section/incomplete references here
+	else
+	{
+		if (l -> as -> output_format == OUTPUT_OBJ)
+		{
+			reloctab_t *re;
+			lw_expr_t te;
+			
+			if (size == 4)
+			{
+				// create a two part reference because lwlink doesn't
+				// support 32 bit references
+				lw_expr_t te2;
+				te = lw_expr_build(lw_expr_type_int, 0x10000);
+				te2 = lw_expr_build(lw_expr_type_oper, lw_expr_oper_divide, expr, te);
+				lw_expr_destroy(te);
+				
+				re = lw_alloc(sizeof(reloctab_t));
+				re -> next = l -> csect -> reloctab;
+				l -> csect -> reloctab = re;
+				te = lw_expr_build(lw_expr_type_int, ol);
+				re -> offset = lw_expr_build(lw_expr_type_oper, lw_expr_oper_plus, l -> addr, te);
+				lw_expr_destroy(te);
+				lwasm_reduce_expr(l -> as, re -> offset);
+				re -> expr = te2;
+				re -> size = 2;
+
+				te = lw_expr_build(lw_expr_type_int, 0xFFFF);
+				te2 = lw_expr_build(lw_expr_type_oper, lw_expr_oper_bwand, expr, te);
+				lw_expr_destroy(te);
+				
+				re = lw_alloc(sizeof(reloctab_t));
+				re -> next = l -> csect -> reloctab;
+				l -> csect -> reloctab = re;
+				te = lw_expr_build(lw_expr_type_int, ol + 2);
+				re -> offset = lw_expr_build(lw_expr_type_oper, lw_expr_oper_plus, l -> addr, te);
+				lw_expr_destroy(te);
+				lwasm_reduce_expr(l -> as, re -> offset);
+				re -> expr = te2;
+				re -> size = 2;
+			}
+			else
+			{
+				// add "expression" record to section table
+				re = lw_alloc(sizeof(reloctab_t));
+				re -> next = l -> csect -> reloctab;
+				l -> csect -> reloctab = re;
+				te = lw_expr_build(lw_expr_type_int, ol);
+				re -> offset = lw_expr_build(lw_expr_type_oper, lw_expr_oper_plus, l -> addr, te);
+				lw_expr_destroy(te);
+				lwasm_reduce_expr(l -> as, re -> offset);
+				re -> size = size;
+				re -> expr = lw_expr_copy(expr);
+			}
+			for (v = 0; v < size; v++)
+				lwasm_emit(l, 0);
+			return 0;
+		}
+		lwasm_register_error(l -> as, l, "Expression not fully resolved");
+		return -1;
+	}
+	
+	switch (size)
+	{
+	case 4:
+		lwasm_emit(l, v >> 24);
+		lwasm_emit(l, v >> 16);
+		/* fallthrough intended */
+			
+	case 2:
+		lwasm_emit(l, v >> 8);
+		/* fallthrough intended */
+		
+	case 1:
+		lwasm_emit(l, v);
+	}
+	
+	return 0;
+}
+
+int lwasm_lookupreg2(const char *regs, char **p)
+{
+	int rval = 0;
+	
+	while (*regs)
+	{
+		if (toupper(**p) == *regs)
+		{
+			if (regs[1] == ' ' && !isalpha(*(*p + 1)))
+				break;
+			if (toupper(*(*p + 1)) == regs[1])
+				break;
+		}
+		regs += 2;
+		rval++;
+	}
+	if (!*regs)
+		return -1;
+	if (regs[1] == ' ')
+		(*p)++;
+	else
+		(*p) += 2;
+	return rval;
+}
+
+int lwasm_lookupreg3(const char *regs, char **p)
+{
+	int rval = 0;
+	
+	while (*regs)
+	{
+		if (toupper(**p) == *regs)
+		{
+			if (regs[1] == ' ' && !isalpha(*(*p + 1)))
+				break;
+			if (toupper(*(*p + 1)) == regs[1])
+			{
+				if (regs[2] == ' ' && !isalpha(*(*p + 2)))
+					break;
+				if (toupper(*(*p + 2)) == regs[2])
+					break;
+			}
+		}
+		regs += 3;
+		rval++;
+	}
+	if (!*regs)
+		return -1;
+	if (regs[1] == ' ')
+		(*p)++;
+	else if (regs[2] == ' ')
+		(*p) += 2;
+	else
+		(*p) += 3;
+	return rval;
+}
+
+void lwasm_show_errors(asmstate_t *as)
+{
+	line_t *cl;
+	lwasm_error_t *e;
+	
+	for (cl = as -> line_head; cl; cl = cl -> next)
+	{
+		if (!(cl -> err) && !(cl -> warn))
+			continue;
+		for (e = cl -> err; e; e = e -> next)
+		{
+			fprintf(stderr, "ERROR: %s\n", e -> mess);
+		}
+		for (e = cl -> warn; e; e = e -> next)
+		{
+			fprintf(stderr, "WARNING: %s\n", e -> mess);
+		}
+		fprintf(stderr, "%s:%05d %s\n\n", cl -> linespec, cl -> lineno, cl -> ltext);
+	}
+}
+
+/*
+this does any passes and other gymnastics that might be useful
+to see if an expression reduces early
+*/
+extern void do_pass3(asmstate_t *as);
+extern void do_pass4_aux(asmstate_t *as, int force);
+
+void lwasm_interim_reduce(asmstate_t *as)
+{
+	do_pass3(as);
+//	do_pass4_aux(as, 0);
+}
+
+lw_expr_t lwasm_parse_cond(asmstate_t *as, char **p)
+{
+	lw_expr_t e;
+
+	debug_message(as, 250, "Parsing condition");
+	e = lwasm_parse_expr(as, p);
+	debug_message(as, 250, "COND EXPR: %s", lw_expr_print(e));
+	
+	if (!e)
+	{
+		lwasm_register_error(as, as -> cl, "Bad expression");
+		return NULL;
+	}
+
+	/* we need to simplify the expression here */
+	debug_message(as, 250, "Doing interim reductions");
+	lwasm_interim_reduce(as);
+	debug_message(as, 250, "COND EXPR: %s", lw_expr_print(e));
+	debug_message(as, 250, "Reducing expression");
+	lwasm_reduce_expr(as, e);
+	debug_message(as, 250, "COND EXPR: %s", lw_expr_print(e));
+/*	lwasm_reduce_expr(as, e);
+	debug_message(as, 250, "COND EXPR: %s", lw_expr_print(e));
+	lwasm_reduce_expr(as, e);
+	debug_message(as, 250, "COND EXPR: %s", lw_expr_print(e));
+	lwasm_reduce_expr(as, e);
+	debug_message(as, 250, "COND EXPR: %s", lw_expr_print(e));
+*/
+
+	lwasm_save_expr(as -> cl, 4242, e);
+
+	if (!lw_expr_istype(e, lw_expr_type_int))
+	{
+		debug_message(as, 250, "Non-constant expression");
+		lwasm_register_error(as, as -> cl, "Conditions must be constant on pass 1");
+		return NULL;
+	}
+	debug_message(as, 250, "Returning expression");
+	return e;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lwasm/lwasm.h	Wed Jan 19 22:27:17 2011 -0700
@@ -0,0 +1,317 @@
+/*
+lwasm.h
+
+Copyright © 2010 William Astle
+
+This file is part of LWTOOLS.
+
+LWTOOLS 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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef ___lwasm_h_seen___
+#define ___lwasm_h_seen___
+
+#include <lw_expr.h>
+#include <lw_stringlist.h>
+#include <lw_stack.h>
+
+
+// these are allowed chars BELOW 0x80 for symbols
+// first is symbol start chars, second is anywhere in symbol
+#define SSYMCHARS "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_@$"
+#define SYMCHARS SSYMCHARS ".?0123456789"
+
+typedef struct asmstate_s asmstate_t;
+
+enum
+{
+	lwasm_expr_linelen = 1,			// length of ref'd line
+	lwasm_expr_lineaddr = 2,		// addr of ref'd line
+	lwasm_expr_nextbp = 3,			// next branch point
+	lwasm_expr_prevbp = 4,			// previous branch point
+	lwasm_expr_syment = 5,			// symbol table entry
+	lwasm_expr_import = 6,			// symbol import entry
+	lwasm_expr_secbase = 7			// section base address
+};
+
+enum lwasm_output_e
+{
+	OUTPUT_DECB = 0,	// DECB multirecord format
+	OUTPUT_RAW,			// raw sequence of bytes
+	OUTPUT_OBJ,			// proprietary object file format
+	OUTPUT_RAWREL,		// raw bytes where ORG causes a SEEK in the file
+	OUTPUT_OS9			// os9 module target
+};
+
+enum lwasm_target_e
+{
+	TARGET_6309 = 0,	// target 6309 CPU
+	TARGET_6809			// target 6809 CPU (no 6309 ops)
+};
+
+enum lwasm_flags_e
+{
+	FLAG_LIST = 0x0001,
+	FLAG_DEPEND = 0x0002,
+	FLAG_SYMBOLS = 0x004,
+	FLAG_NONE = 0
+};
+
+enum lwasm_pragmas_e
+{
+	PRAGMA_NONE = 0,					// no pragmas in effect
+	PRAGMA_DOLLARNOTLOCAL = 0x0001,		// dollar sign does not make a symbol local
+	PRAGMA_NOINDEX0TONONE = 0x0002,		// do not change implicit 0,R to ,R
+	PRAGMA_UNDEFEXTERN = 0x0004,		// undefined symbols are considered to be external
+	PRAGMA_CESCAPES = 0x0008,			// allow C style escapes in fcc, fcs, fcn, etc.
+	PRAGMA_IMPORTUNDEFEXPORT = 0x0010,	// imports symbol if undefined upon export
+	PRAGMA_PCASPCR = 0x0020				// treats ,PC as ,PCR instead of constant offset
+};
+
+
+enum
+{
+	section_flag_bss = 1,				// BSS section
+	section_flag_none = 0				// no flags
+};
+
+typedef struct reloctab_s reloctab_t;
+struct reloctab_s
+{
+	lw_expr_t offset;					// offset of relocation
+	int size;							// size of relocation
+	lw_expr_t expr;						// relocation expression
+	reloctab_t *next;
+};
+
+typedef struct sectiontab_s sectiontab_t;
+struct sectiontab_s
+{
+	char *name;							// section name
+	int flags;							// section flags;
+	lw_expr_t offset;					// offset for next instance
+	int oblen;							// size of section output
+	int obsize;							// size of output buffer
+	unsigned char *obytes;				// output buffer
+	reloctab_t *reloctab;				// table of relocations
+	sectiontab_t *next;
+};
+
+typedef struct lwasm_error_s lwasm_error_t;
+struct lwasm_error_s
+{
+	char *mess;							// actual error message
+	lwasm_error_t *next;				// ptr to next error
+};
+
+struct line_expr_s
+{
+	lw_expr_t expr;
+	int id;
+	struct line_expr_s *next;
+};
+
+typedef struct line_s line_t;
+
+typedef struct exportlist_s exportlist_t;
+struct exportlist_s
+{
+	char *symbol;						// symbol to export
+	struct symtabe *se;					// symbol table entry
+	line_t *line;						// line the export is on
+	exportlist_t *next;					// next in the export list
+};
+
+typedef struct importlist_s importlist_t;
+struct importlist_s
+{
+	char *symbol;						// symbol to import
+	importlist_t *next;					// next in the import list
+};
+
+struct line_s
+{
+	lw_expr_t addr;						// assembly address of the line
+	int len;							// the "size" this line occupies (address space wise) (-1 if unknown)
+	int insn;							// number of insn in insn table
+	int symset;							// set if the line symbol was consumed by the instruction
+	char *sym;							// symbol, if any, on the line
+	unsigned char *output;				// output bytes
+	int outputl;						// size of output
+	int outputbl;						// size of output buffer
+	int dpval;							// direct page value
+	lwasm_error_t *err;					// list of errors
+	lwasm_error_t *warn;				// list of errors
+	line_t *prev;						// previous line
+	line_t *next;						// next line
+	int inmod;							// inside a module?
+	sectiontab_t *csect;				// which section are we in?
+	struct line_expr_s *exprs;			// expressions used during parsing
+	char *lstr;							// string passed forward
+	int pb;								// pass forward post byte
+	int lint;							// pass forward integer
+	int lint2;							// another pass forward integer
+	asmstate_t *as;						// assembler state data ptr
+	int pragmas;						// pragmas in effect for the line
+	int context;						// the symbol context number
+	char *ltext;						// line number
+	char *linespec;						// line spec
+	int lineno;							// line number
+	int soff;							// struct offset (for listings)
+	int dshow;							// data value to show (for listings)
+	int dsize;							// set to 1 for 8 bit dshow value
+	int isbrpt;							// set to 1 if this line is a branch point
+	struct symtabe *dptr;				// symbol value to display
+};
+
+enum
+{
+	symbol_flag_set = 1,				// symbol was used with "set"
+	symbol_flag_nocheck = 2,			// do not check symbol characters
+	symbol_flag_none = 0				// no flags
+};
+
+struct symtabe
+{
+	char *symbol;						// the name of the symbol
+	int context;						// symbol context (-1 for global)
+	int version;						// version of the symbol (for "set")
+	int flags;							// flags for the symbol
+	sectiontab_t *section;				// section the symbol is defined in
+	lw_expr_t value;					// symbol value
+	struct symtabe *next;				// next symbol in the table
+};
+
+typedef struct
+{
+	struct symtabe *head;				// start of symbol table
+} symtab_t;
+
+typedef struct macrotab_s macrotab_t;
+struct macrotab_s
+{
+	char *name;							// name of macro
+	char **lines;						// macro lines
+	int numlines;						// number lines in macro
+	macrotab_t *next;					// next macro in list
+};
+
+typedef struct structtab_s structtab_t;
+typedef struct structtab_field_s structtab_field_t;
+
+struct structtab_field_s
+{
+	char *name;							// structure field name - NULL for anonymous
+	int size;							// structure field size
+	structtab_t *substruct;				// sub structure if there is one
+	structtab_field_t *next;			// next field entry
+};
+
+struct structtab_s
+{
+	char *name;							// name of structure
+	int size;							// number of bytes taken by struct
+	structtab_field_t *fields;			// fields in the structure
+	structtab_t *next;					// next structure
+};
+
+struct asmstate_s
+{
+	int output_format;					// output format
+	int target;							// assembly target
+	int debug_level;					// level of debugging requested
+	FILE *debug_file;					// FILE * to output debug messages to
+	int flags;							// assembly flags
+	int pragmas;						// pragmas currently in effect
+	int errorcount;						// number of errors encountered
+	int inmacro;						// are we in a macro?
+	int instruct;						// are w in a structure?
+	int skipcond;						// skipping a condition?
+	int skipcount;						// depth of "skipping"
+	int skipmacro;						// are we skipping in a macro?	
+	int endseen;						// have we seen an "end" pseudo?
+	int execaddr;						// address from "end"
+	int inmod;							// inside an os9 module?
+	unsigned char crc[3];				// crc accumulator
+	int badsymerr;						// throw error on undef sym if set
+
+	line_t *line_head;					// start of lines list
+	line_t *line_tail;					// tail of lines list
+
+	line_t *cl;							// current line pointer
+	
+	sectiontab_t *csect;				// current section
+	
+	int context;						// the current "context"
+	int nextcontext;					// the next available context
+	
+	symtab_t symtab;					// meta data for the symbol table
+	macrotab_t *macros;					// macro table
+	sectiontab_t *sections;				// section table
+	exportlist_t *exportlist;			// list of exported symbols
+	importlist_t *importlist;			// list of imported symbols
+	char *list_file;					// name of file to list to
+	char *output_file;					// output file name	
+	lw_stringlist_t input_files;		// files to assemble
+	void *input_data;					// opaque data used by the input system
+	lw_stringlist_t include_list;		// include paths
+	lw_stack_t file_dir;				// stack of the "current file" dir
+	lw_stack_t includelist;
+
+	structtab_t *structs;				// defined structures
+	structtab_t *cstruct;				// current structure
+	
+	int exportcheck;					// set if we need to collapse out the section base to 0
+};
+
+#ifndef ___symbol_c_seen___
+
+extern struct symtabe *register_symbol(asmstate_t *as, line_t *cl, char *sym, lw_expr_t value, int flags);
+extern struct symtabe *lookup_symbol(asmstate_t *as, line_t *cl, char *sym);
+
+#endif
+
+#ifndef ___lwasm_c_seen___
+
+extern void lwasm_register_error(asmstate_t *as, line_t *cl, const char *msg, ...);
+extern void lwasm_register_warning(asmstate_t *as, line_t *cl, const char *msg, ...);
+extern int lwasm_next_context(asmstate_t *as);
+extern void lwasm_emit(line_t *cl, int byte);
+extern void lwasm_emitop(line_t *cl, int opc);
+
+extern void lwasm_save_expr(line_t *cl, int id, lw_expr_t expr);
+extern lw_expr_t lwasm_fetch_expr(line_t *cl, int id);
+extern lw_expr_t lwasm_parse_expr(asmstate_t *as, char **p);
+extern int lwasm_emitexpr(line_t *cl, lw_expr_t expr, int s);
+
+extern void skip_operand(char **p);
+
+extern int lwasm_lookupreg2(const char *rlist, char **p);
+extern int lwasm_lookupreg3(const char *rlist, char **p);
+
+extern void lwasm_show_errors(asmstate_t *as);
+
+extern int lwasm_reduce_expr(asmstate_t *as, lw_expr_t expr);
+
+extern void debug_message(asmstate_t *as, int level, const char *fmt, ...);
+extern void dump_state(asmstate_t *as);
+
+extern lw_expr_t lwasm_parse_cond(asmstate_t *as, char **p);
+
+#endif
+
+#define OPLEN(op) (((op)>0xFF)?2:1)
+#define CURPRAGMA(l,p)	(((l)->pragmas & (p)) ? 1 : 0)
+
+#endif /* ___lwasm_h_seen___ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lwasm/macro.c	Wed Jan 19 22:27:17 2011 -0700
@@ -0,0 +1,292 @@
+/*
+macro.c
+Copyright © 2008 William Astle
+
+This file is part of LWASM.
+
+LWASM 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 3 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, see <http://www.gnu.org/licenses/>.
+
+Contains stuff associated with macro processing
+*/
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#include <lw_alloc.h>
+#include <lw_string.h>
+
+#include "lwasm.h"
+#include "input.h"
+#include "instab.h"
+
+PARSEFUNC(pseudo_parse_macro)
+{
+	macrotab_t *m;
+	
+	l -> len = 0;
+	
+	if (as -> skipcond)
+	{
+		as -> skipmacro = 1;
+		return;
+	}
+	
+	if (as -> inmacro)
+	{
+		lwasm_register_error(as, l, "Attempt to define a macro inside a macro");
+		return;
+	}
+	
+	if (!(l -> sym))
+	{
+		lwasm_register_error(as, l, "Missing macro name");
+		return;
+	}
+
+	for (m = as -> macros; m; m = m -> next)
+	{
+		if (!strcmp(m -> name, l -> sym))
+			break;
+	}
+	if (m)
+	{
+		lwasm_register_error(as, l, "Duplicate macro definition");
+		return;
+	}
+	
+	m = lw_alloc(sizeof(macrotab_t));
+	m -> name = lw_strdup(l -> sym);
+	m -> next = as -> macros;
+	m -> lines = NULL;
+	m -> numlines = 0;
+	as -> macros = m;
+	
+	while (**p && !isspace(**p))
+		(*p)++;
+	
+	as -> inmacro = 1;
+}
+
+PARSEFUNC(pseudo_parse_endm)
+{
+	l -> len = 0;
+
+	if (as -> skipcond)
+	{
+		as -> skipmacro = 0;
+		return;
+	}
+	
+	if (!as -> inmacro)
+	{
+		lwasm_register_error(as, l, "ENDM without MACRO");
+		return;
+	}
+	
+	as -> inmacro = 0;
+	
+	// a macro definition counts as a context break for local symbols
+	as -> context = lwasm_next_context(as);
+}
+
+// the current macro will ALWAYS be the first one in the table
+int add_macro_line(asmstate_t *as, char *optr)
+{
+	if (!as -> inmacro)
+		return 0;
+	
+	as -> macros -> lines = lw_realloc(as -> macros -> lines, sizeof(char *) * (as -> macros -> numlines + 1));
+	as -> macros -> lines[as -> macros -> numlines] = lw_strdup(optr);
+	as -> macros -> numlines += 1;
+	return 1;
+}
+
+void macro_add_to_buff(char **buff, int *loc, int *len, char c)
+{
+	if (*loc == *len)
+	{
+		*buff = lw_realloc(*buff, *len + 32);
+		*len += 32;
+	}
+	(*buff)[(*loc)++] = c;
+}
+
+// this is just like a regular operation function
+/*
+macro args are referenced by "\n" where 1 <= n <= 9
+or by "\{n}"; a \ can be included by writing \\
+a comma separates argument but one can be included with "\,"
+whitespace ends argument list but can be included with "\ " or the like
+
+*/
+int expand_macro(asmstate_t *as, line_t *l, char **p, char *opc)
+{
+	int lc;
+	line_t *cl, *nl;
+	int oldcontext;
+	macrotab_t *m;
+
+	char **args = NULL;		// macro arguments
+	int nargs = 0;			// number of arguments
+
+	char *p2, *p3;
+	
+	int bloc, blen;
+	char *linebuff;
+
+	for (m = as -> macros; m; m = m -> next)
+	{
+		if (!strcmp(opc, m -> name))
+			break;
+	}
+	// signal no macro expansion
+	if (!m)
+		return -1;
+	
+	// save current symbol context for after macro expansion
+	oldcontext = as -> context;
+
+	cl = l;
+
+	as -> context = lwasm_next_context(as);
+
+	while (**p && !isspace(**p) && **p != ',')
+	{
+		p2 = *p;
+		while (*p2 && !isspace(*p2) && *p2 != ',')
+		{
+			if (*p2 == '\\')
+			{
+				if (p2[1])
+					p2++;
+			}
+			p2++;
+		}
+
+		// have arg here
+		args = lw_realloc(args, sizeof(char *) * (nargs + 1));
+		args[nargs] = lw_alloc(p2 - *p + 1);
+		args[nargs][p2 - *p] = '\0';
+		memcpy(args[nargs], *p, p2 - *p);
+		*p = p2;
+
+		// now collapse out "\" characters
+		for (p3 = p2 = args[nargs]; *p2; p2++, p3++)
+		{
+			if (*p2 == '\\' && p2[1])
+			{
+				p2++;
+			}
+			*p3 = *p2;
+		}
+		*p3 = '\0';
+		
+		nargs++;
+		if (**p == ',')
+			(*p)++;
+	}
+	
+
+	// now create a string for the macro
+	// and push it into the front of the input stack
+	bloc = blen = 0;
+	linebuff = NULL;
+	
+	for (lc = 0; lc < m -> numlines; lc++)
+	{
+		for (p2 = m -> lines[lc]; *p2; p2++)
+		{
+			if (*p2 == '\\' && isdigit(p2[1]))
+			{
+				int n;
+					
+				p2++;
+				n = *p2 - '0';
+				if (n == 0)
+				{
+					for (p3 = m -> name; *p3; p3++)
+						macro_add_to_buff(&linebuff, &bloc, &blen, *p3);
+					continue;
+				}
+				if (n < 1 || n > nargs)
+					continue;
+				for (p3 = args[n - 1]; *p3; p3++)
+					macro_add_to_buff(&linebuff, &bloc, &blen, *p3);
+				continue;
+			}
+			else if (*p2 == '{')
+			{
+				int n = 0, n2;
+				p2++;
+				while (*p2 && isdigit(*p2))
+				{
+					n2 = *p2 - '0';
+					if (n2 < 0 || n2 > 9)
+						n2 = 0;
+					n = n * 10 + n2;
+					p2++;
+				}
+				if (*p2 == '}')
+					p2++;
+				 
+				if (n == 0)
+				{
+					for (p3 = m -> name; *p3; p3++)
+						macro_add_to_buff(&linebuff, &bloc, &blen, *p3);
+					continue;
+				}
+				if (n < 1 || n > nargs)
+					continue;
+				for (p3 = args[n - 1]; *p3; p3++)
+					macro_add_to_buff(&linebuff, &bloc, &blen, *p3);
+				continue;
+			}
+			else
+			{
+				macro_add_to_buff(&linebuff, &bloc, &blen, *p2);
+			}
+		}
+
+		macro_add_to_buff(&linebuff, &bloc, &blen, '\n');
+		
+	}
+
+	{
+		char ctcbuf[100];
+		char *p;
+		snprintf(ctcbuf, 100, "\001\001SETCONTEXT %d\n", oldcontext);
+		for (p = ctcbuf; *p; p++)
+			macro_add_to_buff(&linebuff, &bloc, &blen, *p);
+	}
+	
+	// push the macro into the front of the stream
+	input_openstring(as, opc, linebuff);
+
+	lw_free(linebuff);
+
+	// clean up
+	if (args)
+	{
+		while (nargs)
+		{
+			lw_free(args[--nargs]);
+		}
+		lw_free(args);
+	}
+
+	// indicate a macro was expanded
+	return 0;	
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lwasm/main.c	Wed Jan 19 22:27:17 2011 -0700
@@ -0,0 +1,268 @@
+/*
+main.c
+
+Copyright © 2010 William Astle
+
+This file is part of LWTOOLS.
+
+LWTOOLS 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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <argp.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <lw_alloc.h>
+#include <lw_string.h>
+#include <lw_stringlist.h>
+#include <lw_expr.h>
+
+#include "lwasm.h"
+#include "input.h"
+
+extern int parse_pragma_string(asmstate_t *as, char *str, int ignoreerr);
+
+/* command line option handling */
+const char *argp_program_version = "lwasm from " PACKAGE_STRING;
+const char *argp_program_bug_address = PACKAGE_BUGREPORT;
+char *program_name;
+
+static struct argp_option options[] =
+{
+	{ "output",		'o',	"FILE",		0,						"Output to FILE"},
+	{ "debug",		'd',	"LEVEL",	OPTION_ARG_OPTIONAL,	"Set debug mode"},
+	{ "format",		'f',	"TYPE",		0,						"Select output format: decb, raw, obj, os9"},
+	{ "list",		'l',	"FILE",		OPTION_ARG_OPTIONAL,	"Generate list [to FILE]"},
+	{ "symbols",	's',	0,			OPTION_ARG_OPTIONAL,	"Generate symbol list in listing, no effect without --list"},
+	{ "decb",		'b',	0,			0,						"Generate DECB .bin format output, equivalent of --format=decb"},
+	{ "raw",		'r',	0,			0,						"Generate raw binary format output, equivalent of --format=raw"},
+	{ "obj",		0x100,	0,			0,						"Generate proprietary object file format for later linking, equivalent of --format=obj" },
+	{ "depend",		0x101,	0,			0,						"Output a dependency list to stdout; do not do any actual output though assembly is completed as usual" },
+	{ "pragma",		'p',	"PRAGMA",	0,						"Set an assembler pragma to any value understood by the \"pragma\" pseudo op"},
+	{ "6809",		'9',	0,			0,						"Set assembler to 6809 only mode" },
+	{ "6309",		'3',	0,			0,						"Set assembler to 6309 mode (default)" },
+	{ "includedir",	'I',	"PATH",		0,						"Add entry to include path" },
+	{ 0 }
+};
+
+
+static error_t parse_opts(int key, char *arg, struct argp_state *state)
+{
+	asmstate_t *as = state -> input;
+	
+	switch (key)
+	{
+	case 'I':
+		lw_stringlist_addstring(as -> include_list, arg);
+		break;
+
+	case 'o':
+		if (as -> output_file)
+			lw_free(as -> output_file);
+		as -> output_file = lw_strdup(arg);
+		break;
+
+	case 'd':
+		if (!arg)
+			as -> debug_level = 50;
+		else
+			as -> debug_level = atoi(arg);
+		break;
+
+	case 'l':
+		if (as -> list_file)
+			lw_free(as -> list_file);
+		if (!arg)
+			as -> list_file = NULL;
+		else
+			as -> list_file = lw_strdup(arg);
+		as -> flags |= FLAG_LIST;
+		break;
+
+	case 's':
+		as -> flags |= FLAG_SYMBOLS;
+		break;
+		
+	case 'b':
+		as -> output_format = OUTPUT_DECB;
+		break;
+
+	case 'r':
+		as -> output_format = OUTPUT_RAW;
+		break;
+
+	case 0x100:
+		as -> output_format = OUTPUT_OBJ;
+		break;
+
+	case 0x101:
+		as -> flags |= FLAG_DEPEND;
+		break;
+
+	case 'f':
+		if (!strcasecmp(arg, "decb"))
+			as -> output_format = OUTPUT_DECB;
+		else if (!strcasecmp(arg, "raw"))
+			as -> output_format = OUTPUT_RAW;
+		else if (!strcasecmp(arg, "obj"))
+			as -> output_format = OUTPUT_OBJ;
+		else if (!strcasecmp(arg, "os9"))
+		{
+			as -> pragmas |= PRAGMA_DOLLARNOTLOCAL;
+			as -> output_format = OUTPUT_OS9;
+		}
+		else
+		{
+			fprintf(stderr, "Invalid output format: %s\n", arg);
+			exit(1);
+		}
+		break;
+		
+	case 'p':
+		if (parse_pragma_string(as, arg, 0) == 0)
+		{
+			fprintf(stderr, "Unrecognized pragma string: %s\n", arg);
+			exit(1);
+		}
+		break;
+
+	case '9':
+		as -> target = TARGET_6809;
+		break;
+
+	case '3':
+		as -> target = TARGET_6309;
+		break;
+
+	case ARGP_KEY_END:
+		break;
+	
+	case ARGP_KEY_ARG:
+		lw_stringlist_addstring(as -> input_files, arg);
+		break;
+		
+	default:
+		return ARGP_ERR_UNKNOWN;
+	}
+	return 0;
+}
+
+static struct argp argp =
+{
+	options,
+	parse_opts,
+	"<input file>",
+	"LWASM, a HD6309 and MC6809 cross-assembler"
+};
+
+/*
+main function; parse command line, set up assembler state, and run the 
+assembler on the first file
+*/
+extern void do_pass1(asmstate_t *as);
+extern void do_pass2(asmstate_t *as);
+extern void do_pass3(asmstate_t *as);
+extern void do_pass4(asmstate_t *as);
+extern void do_pass5(asmstate_t *as);
+extern void do_pass6(asmstate_t *as);
+extern void do_pass7(asmstate_t *as);
+extern void do_output(asmstate_t *as);
+extern void do_list(asmstate_t *as);
+extern lw_expr_t lwasm_evaluate_special(int t, void *ptr, void *priv);
+extern lw_expr_t lwasm_evaluate_var(char *var, void *priv);
+extern lw_expr_t lwasm_parse_term(char **p, void *priv);
+
+struct passlist_s
+{
+	char *passname;
+	void (*fn)(asmstate_t *as);
+	int fordep;
+} passlist[] = {
+	{ "parse", do_pass1, 1 },
+	{ "symcheck", do_pass2 },
+	{ "resolve1", do_pass3 },
+	{ "resolve2", do_pass4 },
+	{ "addressresolve", do_pass5 },
+	{ "finalize", do_pass6 },
+	{ "emit", do_pass7 },
+	{ NULL, NULL }
+};
+
+
+int main(int argc, char **argv)
+{
+	int passnum;
+
+	/* assembler state */
+	asmstate_t asmstate = { 0 };
+	program_name = argv[0];
+
+	lw_expr_set_special_handler(lwasm_evaluate_special);
+	lw_expr_set_var_handler(lwasm_evaluate_var);
+	lw_expr_set_term_parser(lwasm_parse_term);
+
+	/* initialize assembler state */
+	asmstate.include_list = lw_stringlist_create();
+	asmstate.input_files = lw_stringlist_create();
+	asmstate.nextcontext = 1;
+
+	/* parse command line arguments */	
+	argp_parse(&argp, argc, argv, 0, 0, &asmstate);
+
+	if (!asmstate.output_file)
+	{
+		asmstate.output_file = lw_strdup("a.out");
+	}
+
+	input_init(&asmstate);
+
+	for (passnum = 0; passlist[passnum].fn; passnum++)
+	{
+		if ((asmstate.flags & FLAG_DEPEND) && passlist[passnum].fordep == 0)
+			continue;
+		debug_message(&asmstate, 50, "Doing pass %d (%s)\n", passnum, passlist[passnum].passname);
+		(passlist[passnum].fn)(&asmstate);
+		debug_message(&asmstate, 50, "After pass %d (%s)\n", passnum, passlist[passnum].passname);
+		dump_state(&asmstate);
+		
+		if (asmstate.errorcount > 0)
+		{
+			lwasm_show_errors(&asmstate);
+			exit(1);
+		}
+	}
+
+	if (asmstate.flags & FLAG_DEPEND)
+	{
+		// output dependencies
+		char *n;
+		
+		while (n = lw_stack_pop(asmstate.includelist))
+		{
+			fprintf(stdout, "%s\n", n);
+			lw_free(n);
+		}
+	}	
+	else
+	{
+		debug_message(&asmstate, 50, "Doing output");
+		do_output(&asmstate);
+	}
+	
+	debug_message(&asmstate, 50, "Done assembly");
+	
+	do_list(&asmstate);
+	
+	exit(0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lwasm/os9.c	Wed Jan 19 22:27:17 2011 -0700
@@ -0,0 +1,191 @@
+/*
+os9.c
+Copyright © 2009 William Astle
+
+This file is part of LWASM.
+
+LWASM 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 3 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, see <http://www.gnu.org/licenses/>.
+
+
+This file implements the various pseudo operations related to OS9 target
+*/
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <lw_expr.h>
+
+#include "lwasm.h"
+#include "instab.h"
+
+
+// OS9 syscall
+PARSEFUNC(pseudo_parse_os9)
+{
+	lw_expr_t e;
+	
+	if (as -> output_format != OUTPUT_OS9)
+	{
+		lwasm_register_error(as, l, "os9 directive only valid for OS9 target");
+		return;
+	}
+	
+	// fetch immediate value
+	e = lwasm_parse_expr(as, p);
+	if (!e)
+	{
+		lwasm_register_error(as, l, "Bad operand");
+		return;
+	}
+	lwasm_save_expr(l, 0, e);
+	l -> len = 3;
+}
+
+EMITFUNC(pseudo_emit_os9)
+{
+	lw_expr_t e;
+	
+	e = lwasm_fetch_expr(l, 0);
+
+	lwasm_emitop(l, 0x103f);
+	lwasm_emitexpr(l, e, 1);
+}
+
+PARSEFUNC(pseudo_parse_mod)
+{
+	lw_expr_t e;
+	int i;
+	
+	if (as -> output_format != OUTPUT_OS9)
+	{
+		lwasm_register_error(as, l,  "mod directive only valid for OS9 target");
+		return;
+	}
+	
+	if (as -> inmod)
+	{
+		lwasm_register_error(as, l,  "Already in a module!");
+		return;
+	}
+
+	// parse 6 expressions...
+	for (i = 0; i < 5; i++)
+	{
+		e = lwasm_parse_expr(as, p);
+		if (!e)
+		{
+			lwasm_register_error(as, l, "Bad operand");
+			return;
+		}
+
+		lwasm_save_expr(l, i, e);
+
+		if (**p != ',')
+		{
+			lwasm_register_error(as, l, "Bad operand");
+			return;
+		}
+		(*p)++;
+	}
+
+	e = lwasm_parse_expr(as, p);
+	if (!e)
+	{
+		lwasm_register_error(as, l, "Bad operand");
+		return;
+	}
+	lwasm_save_expr(l, 5, e);
+
+	l -> inmod = 1;
+	
+	// we have an implicit ORG 0 with "mod"
+	lw_expr_destroy(l -> addr);
+	l -> addr = lw_expr_build(lw_expr_type_int, 0);
+
+	// init crc
+	as -> inmod = 1;
+}
+
+EMITFUNC(pseudo_emit_mod)
+{
+	lw_expr_t e1, e2, e3, e4;
+	int csum;
+	
+	as -> crc[0] = 0xff;
+	as -> crc[1] = 0xff;
+	as -> crc[2] = 0xff;
+
+	// sync bytes
+	lwasm_emit(l, 0x87);
+	lwasm_emit(l, 0xcd);
+	
+	// mod length
+	lwasm_emitexpr(l, e1 = lwasm_fetch_expr(l, 0), 2);
+	
+	// name offset
+	lwasm_emitexpr(l, e2 = lwasm_fetch_expr(l, 1), 2);
+	
+	// type
+	lwasm_emitexpr(l, e3 = lwasm_fetch_expr(l, 2), 1);
+	
+	// flags/rev
+	lwasm_emitexpr(l, e4 = lwasm_fetch_expr(l, 3), 1);
+	
+	// header check
+	csum = ~(0x87 ^ 0xCD ^(lw_expr_intval(e1) >> 8) ^ (lw_expr_intval(e1) & 0xff)
+		^ (lw_expr_intval(e2) >> 8) ^ (lw_expr_intval(e2) & 0xff)
+		^ lw_expr_intval(e3) ^ lw_expr_intval(e4));
+	lwasm_emit(l, csum);
+
+	// module type specific output
+	// note that these are handled the same for all so
+	// there need not be any special casing
+	
+	// exec offset or fmgr name offset
+	lwasm_emitexpr(l, lwasm_fetch_expr(l, 4), 2);
+	
+	// data size or drvr name offset
+	lwasm_emitexpr(l, lwasm_fetch_expr(l, 5), 2);
+}
+
+PARSEFUNC(pseudo_parse_emod)
+{
+	if (as -> output_format != OUTPUT_OS9)
+	{
+		lwasm_register_error(as, l, "emod directive only valid for OS9 target");
+		return;
+	}
+	
+	if (!(as -> inmod))
+	{
+		lwasm_register_error(as, l, "not in a module!");
+		return;
+	}
+	
+	as -> inmod = 0;
+}
+
+EMITFUNC(pseudo_emit_emod)
+{
+	unsigned char tcrc[3];
+	
+	// don't mess with CRC!
+	tcrc[0] = as -> crc[0] ^ 0xff;
+	tcrc[1] = as -> crc[1] ^ 0xff;
+	tcrc[2] = as -> crc[2] ^ 0xff;
+	lwasm_emit(l, tcrc[0]);
+	lwasm_emit(l, tcrc[1]);
+	lwasm_emit(l, tcrc[2]);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lwasm/output.c	Wed Jan 19 22:27:17 2011 -0700
@@ -0,0 +1,593 @@
+/*
+output.c
+Copyright © 2009, 2010 William Astle
+
+This file is part of LWASM.
+
+LWASM 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 3 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, see <http://www.gnu.org/licenses/>.
+
+
+Contains the code for actually outputting the assembled code
+*/
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <lw_alloc.h>
+#include <lw_expr.h>
+
+#include "lwasm.h"
+
+void write_code_raw(asmstate_t *as, FILE *of);
+void write_code_decb(asmstate_t *as, FILE *of);
+void write_code_rawrel(asmstate_t *as, FILE *of);
+void write_code_obj(asmstate_t *as, FILE *of);
+void write_code_os9(asmstate_t *as, FILE *of);
+
+// this prevents warnings about not using the return value of fwrite()
+#define writebytes(s, l, c, f)	do { int r; r = fwrite((s), (l), (c), (f)); } while (0)
+
+void do_output(asmstate_t *as)
+{
+	FILE *of;
+	
+	if (as -> errorcount > 0)
+	{
+		fprintf(stderr, "Not doing output due to assembly errors.\n");
+		return;
+	}
+	
+	of = fopen(as -> output_file, "wb");
+	if (!of)
+	{
+		fprintf(stderr, "Cannot open '%s' for output", as -> output_file);
+		perror("");
+		return;
+	}
+
+	switch (as -> output_format)
+	{
+	case OUTPUT_RAW:
+		write_code_raw(as, of);
+		break;
+		
+	case OUTPUT_DECB:
+		write_code_decb(as, of);
+		break;
+		
+	case OUTPUT_RAWREL:
+		write_code_rawrel(as, of);
+		break;
+	
+	case OUTPUT_OBJ:
+		write_code_obj(as, of);
+		break;
+
+	case OUTPUT_OS9:
+		write_code_os9(as, of);
+		break;
+
+	default:
+		fprintf(stderr, "BUG: unrecognized output format when generating output file\n");
+		fclose(of);
+		unlink(as -> output_file);
+		return;
+	}
+
+	fclose(of);
+}
+
+/*
+rawrel output treats an ORG directive as an offset from the start of the
+file. Undefined results will occur if an ORG directive moves the output
+pointer backward. This particular implementation uses "fseek" to handle
+ORG requests and to skip over RMBs.
+
+This simple brain damanged method simply does an fseek before outputting
+each instruction.
+*/
+void write_code_rawrel(asmstate_t *as, FILE *of)
+{
+	line_t *cl;
+	
+	for (cl = as -> line_head; cl; cl = cl -> next)
+	{
+		if (cl -> outputl <= 0)
+			continue;
+		
+		fseek(of, lw_expr_intval(cl -> addr), SEEK_SET);
+		writebytes(cl -> output, cl -> outputl, 1, of);
+	}
+}
+
+/*
+raw merely writes all the bytes directly to the file as is. ORG is just a
+reference for the assembler to handle absolute references. Multiple ORG
+statements will produce mostly useless results
+*/
+void write_code_raw(asmstate_t *as, FILE *of)
+{
+	line_t *cl;
+	
+	for (cl = as -> line_head; cl; cl = cl -> next)
+	{
+		if (cl -> len > 0 && cl -> outputl == 0)
+		{
+			int i;
+			for (i = 0; i < cl -> len; i++)
+				writebytes("\0", 1, 1, of);
+			continue;
+		}
+		else if (cl -> outputl > 0)
+			writebytes(cl -> output, cl -> outputl, 1, of);
+	}
+}
+
+
+/*
+OS9 target also just writes all the bytes in order. No need for anything
+else.
+*/
+void write_code_os9(asmstate_t *as, FILE *of)
+{
+	line_t *cl;
+	
+	for (cl = as -> line_head; cl; cl = cl -> next)
+	{
+		if (cl -> inmod == 0)
+			continue;
+		if (cl -> len > 0 && cl -> outputl == 0)
+		{
+			int i;
+			for (i = 0; i < cl -> len; i++)
+				writebytes("\0", 1, 1, of);
+			continue;
+		}
+		else if (cl -> outputl > 0)
+			writebytes(cl -> output, cl -> outputl, 1, of);
+	}
+}
+
+void write_code_decb(asmstate_t *as, FILE *of)
+{
+	long preambloc;
+	line_t *cl;
+	int blocklen = -1;
+	int nextcalc = -1;
+	unsigned char outbuf[5];
+	int caddr;
+	
+	for (cl = as -> line_head; cl; cl = cl -> next)
+	{
+		if (cl -> outputl < 0)
+			continue;
+		caddr = lw_expr_intval(cl -> addr);
+		if (caddr != nextcalc && cl -> outputl > 0)
+		{
+			// need preamble here
+			if (blocklen > 0)
+			{
+				// update previous preamble if needed
+				fseek(of, preambloc, SEEK_SET);
+				outbuf[0] = (blocklen >> 8) & 0xFF;
+				outbuf[1] = blocklen & 0xFF;
+				writebytes(outbuf, 2, 1, of);
+				fseek(of, 0, SEEK_END);
+			}
+			blocklen = 0;
+			nextcalc = caddr;
+			outbuf[0] = 0x00;
+			outbuf[1] = 0x00;
+			outbuf[2] = 0x00;
+			outbuf[3] = (nextcalc >> 8) & 0xFF;
+			outbuf[4] = nextcalc & 0xFF;
+			preambloc = ftell(of) + 1;
+			writebytes(outbuf, 5, 1, of);
+		}
+		nextcalc += cl -> outputl;
+		writebytes(cl -> output, cl -> outputl, 1, of);
+		blocklen += cl -> outputl;
+	}
+	if (blocklen > 0)
+	{
+		fseek(of, preambloc, SEEK_SET);
+		outbuf[0] = (blocklen >> 8) & 0xFF;
+		outbuf[1] = blocklen & 0xFF;
+		writebytes(outbuf, 2, 1, of);
+		fseek(of, 0, SEEK_END);
+	}
+	
+	// now write postamble
+	outbuf[0] = 0xFF;
+	outbuf[1] = 0x00;
+	outbuf[2] = 0x00;
+	outbuf[3] = (as -> execaddr >> 8) & 0xFF;
+	outbuf[4] = (as -> execaddr) & 0xFF;
+	writebytes(outbuf, 5, 1, of);
+}
+
+void write_code_obj_sbadd(sectiontab_t *s, unsigned char b)
+{
+	if (s -> oblen >= s -> obsize)
+	{
+		s -> obytes = lw_realloc(s -> obytes, s -> obsize + 128);
+		s -> obsize += 128;
+	}
+	s -> obytes[s -> oblen] = b;
+	s -> oblen += 1;
+}
+
+
+int write_code_obj_expraux(lw_expr_t e, void *of)
+{
+	int tt;
+	int v;
+	unsigned char buf[16];
+	
+	tt = lw_expr_type(e);
+
+	switch (tt)
+	{
+	case lw_expr_type_oper:
+		buf[0] =  0x04;
+		switch (lw_expr_whichop(e))
+		{
+		case lw_expr_oper_plus:
+			buf[1] = 0x01;
+			break;
+		
+		case lw_expr_oper_minus:
+			buf[1] = 0x02;
+			break;
+		
+		case lw_expr_oper_times:
+			buf[1] = 0x03;
+			break;
+		
+		case lw_expr_oper_divide:
+			buf[1] = 0x04;
+			break;
+		
+		case lw_expr_oper_mod:
+			buf[1] = 0x05;
+			break;
+		
+		case lw_expr_oper_intdiv:
+			buf[1] = 0x06;
+			break;
+		
+		case lw_expr_oper_bwand:
+			buf[1] = 0x07;
+			break;
+		
+		case lw_expr_oper_bwor:
+			buf[1] = 0x08;
+			break;
+		
+		case lw_expr_oper_bwxor:
+			buf[1] = 0x09;
+			break;
+		
+		case lw_expr_oper_and:
+			buf[1] = 0x0A;
+			break;
+		
+		case lw_expr_oper_or:
+			buf[1] = 0x0B;
+			break;
+		
+		case lw_expr_oper_neg:
+			buf[1] = 0x0C;
+			break;
+		
+		case lw_expr_oper_com:
+			buf[1] = 0x0D;
+			break;
+
+		default:
+			buf[1] = 0xff;
+		}
+		writebytes(buf, 2, 1, of);
+		return 0;
+
+	case lw_expr_type_int:
+		v = lw_expr_intval(e);
+		buf[0] = 0x01;
+		buf[1] = (v >> 8) & 0xff;
+		buf[2] = v & 0xff;
+		writebytes(buf, 3, 1, of);
+		return 0;
+		
+	case lw_expr_type_special:
+		v = lw_expr_specint(e);
+		switch (v)
+		{
+		case lwasm_expr_secbase:
+			{
+				// replaced with a synthetic symbol
+				sectiontab_t *se;
+				se = lw_expr_specptr(e);
+				
+				writebytes("\x03\x02", 2, 1, of);
+				writebytes(se -> name, strlen(se -> name) + 1, 1, of);
+				return 0;
+			}	
+		case lwasm_expr_import:
+			{
+				importlist_t *ie;
+				ie = lw_expr_specptr(e);
+				buf[0] = 0x02;
+				writebytes(buf, 1, 1, of);
+				writebytes(ie -> symbol, strlen(ie -> symbol) + 1, 1, of);
+				return 0;
+			}
+		case lwasm_expr_syment:
+			{
+				struct symtabe *se;
+				se = lw_expr_specptr(e);
+				buf[0] = 0x03;
+				writebytes(buf, 1, 1, of);
+				writebytes(se -> symbol, strlen(se -> symbol), 1, of);
+				if (se -> context != -1)
+				{
+					sprintf(buf, "\x01%d", se -> context);
+					writebytes(buf, strlen(buf), 1, of);
+				}
+				writebytes("", 1, 1, of);
+				return 0;
+			}
+		}
+			
+	default:
+		// unrecognized term type - replace with integer 0
+//		fprintf(stderr, "Unrecognized term type: %s\n", lw_expr_print(e));
+		buf[0] = 0x01;
+		buf[1] = 0x00;
+		buf[2] = 0x00;
+		writebytes(buf, 3, 1, of);
+		break;
+	}
+	return 0;
+}
+
+
+void write_code_obj(asmstate_t *as, FILE *of)
+{
+	line_t *l;
+	sectiontab_t *s;
+	reloctab_t *re;
+	exportlist_t *ex;
+	struct symtabe *se;
+
+	int i;
+	unsigned char buf[16];
+
+	// output the magic number and file header
+	// the 8 is NOT an error
+	writebytes("LWOBJ16", 8, 1, of);
+	
+	// run through the entire system and build the byte streams for each
+	// section; at the same time, generate a list of "local" symbols to
+	// output for each section
+	// NOTE: for "local" symbols, we will append \x01 and the ascii string
+	// of the context identifier (so sym in context 1 would be "sym\x011"
+	// we can do this because the linker can handle symbols with any
+	// character other than NUL.
+	// also we will generate a list of incomplete references for each
+	// section along with the actual definition that will be output
+	
+	// once all this information is generated, we will output each section
+	// to the file
+	
+	// NOTE: we build everything in memory then output it because the
+	// assembler accepts multiple instances of the same section but the
+	// linker expects only one instance of each section in the object file
+	// so we need to collect all the various pieces of a section together
+	// (also, the assembler treated multiple instances of the same section
+	// as continuations of previous sections so we would need to collect
+	// them together anyway.
+	
+	for (l = as -> line_head; l; l = l -> next)
+	{
+		if (l -> csect)
+		{
+			// we're in a section - need to output some bytes
+			if (l -> outputl > 0)
+				for (i = 0; i < l -> outputl; i++)
+					write_code_obj_sbadd(l -> csect, l -> output[i]);
+			else if (l -> outputl == 0 || l -> outputl == -1)
+				for (i = 0; i < l -> len; i++)
+					write_code_obj_sbadd(l -> csect, 0);
+		}
+	}
+	
+	// run through the sections
+	for (s = as -> sections; s; s = s -> next)
+	{
+		// write the name
+		writebytes(s -> name, strlen(s -> name) + 1, 1, of);
+		
+		// write the flags
+		if (s -> flags & section_flag_bss)
+			writebytes("\x01", 1, 1, of);
+		
+		// indicate end of flags - the "" is NOT an error
+		writebytes("", 1, 1, of);
+		
+		// now the local symbols
+		
+		// a symbol for section base address
+		writebytes("\x02", 1, 1, of);
+		writebytes(s -> name, strlen(s -> name) + 1, 1, of);
+		// address 0; "\0" is not an error
+		writebytes("\0", 2, 1, of);
+		for (se = as -> symtab.head; se; se = se -> next)
+		{
+			lw_expr_t te;
+			
+			debug_message(as, 200, "Consider symbol %s (%p) for export in section %p", se -> symbol, se -> section, s);
+			
+			// ignore symbols not in this section
+			if (se -> section != s)
+				continue;
+			
+			debug_message(as, 200, "  In section");
+			
+			if (se -> flags & symbol_flag_set)
+				continue;
+			
+			debug_message(as, 200, "  Not symbol_flag_set");
+			
+			te = lw_expr_copy(se -> value);
+			debug_message(as, 200, "  Value=%s", lw_expr_print(te));
+			as -> exportcheck = 1;
+			as -> csect = s;
+			lwasm_reduce_expr(as, te);
+			as -> exportcheck = 0;
+
+			debug_message(as, 200, "  Value2=%s", lw_expr_print(te));
+			
+			// don't output non-constant symbols
+			if (!lw_expr_istype(te, lw_expr_type_int))
+			{
+				lw_expr_destroy(te);
+				continue;
+			}
+
+			writebytes(se -> symbol, strlen(se -> symbol), 1, of);
+			if (se -> context >= 0)
+			{
+				writebytes("\x01", 1, 1, of);
+				sprintf(buf, "%d", se -> context);
+				writebytes(buf, strlen(buf), 1, of);
+			}
+			// the "" is NOT an error
+			writebytes("", 1, 1, of);
+			
+			// write the address
+			buf[0] = (lw_expr_intval(te) >> 8) & 0xff;
+			buf[1] = lw_expr_intval(te) & 0xff;
+			writebytes(buf, 2, 1, of);
+			lw_expr_destroy(te);
+		}	
+		// flag end of local symbol table - "" is NOT an error
+		writebytes("", 1, 1, of);
+		
+		// now the exports -- FIXME
+		for (ex = as -> exportlist; ex; ex = ex -> next)
+		{
+			int eval;
+			lw_expr_t te;
+			line_t tl;
+			
+			if (ex -> se == NULL)
+				continue;
+			if (ex -> se -> section != s)
+				continue;
+			te = lw_expr_copy(ex -> se -> value);
+			as -> csect = ex -> se -> section;
+			as -> exportcheck = 1;
+			tl.as = as;
+			as -> cl = &tl;
+			lwasm_reduce_expr(as, te);
+			as -> exportcheck = 0;
+			as -> cl = NULL;
+			if (!lw_expr_istype(te, lw_expr_type_int))
+			{
+				lw_expr_destroy(te);
+				continue;
+			}
+			eval = lw_expr_intval(te);
+			lw_expr_destroy(te);
+			writebytes(ex -> symbol, strlen(ex -> symbol) + 1, 1, of);
+			buf[0] = (eval >> 8) & 0xff;
+			buf[1] = eval & 0xff;
+			writebytes(buf, 2, 1, of);
+		}
+	
+		// flag end of exported symbols - "" is NOT an error
+		writebytes("", 1, 1, of);
+		
+		// FIXME - relocation table
+		for (re = s -> reloctab; re; re = re -> next)
+		{
+			int offset;
+			lw_expr_t te;
+			line_t tl;
+			
+			tl.as = as;
+			as -> cl = &tl;
+			as -> csect = s;
+			as -> exportcheck = 1;
+			
+			if (re -> expr == NULL)
+			{
+				// this is an error but we'll simply ignore it
+				// and not output this expression
+				continue;
+			}
+			
+			// work through each term in the expression and output
+			// the proper equivalent to the object file
+			if (re -> size == 1)
+			{
+				// flag an 8 bit relocation (low 8 bits will be used)
+				buf[0] = 0xFF;
+				buf[1] = 0x01;
+				writebytes(buf, 2, 1, of);
+			}
+			
+			te = lw_expr_copy(re -> offset);
+			lwasm_reduce_expr(as, te);
+			if (!lw_expr_istype(te, lw_expr_type_int))
+			{
+				lw_expr_destroy(te);
+				continue;
+			}
+			offset = lw_expr_intval(te);
+			lw_expr_destroy(te);
+			
+			// output expression
+			lw_expr_testterms(re -> expr, write_code_obj_expraux, of);
+			
+			// flag end of expressions
+			writebytes("", 1, 1, of);
+			
+			// write the offset
+			buf[0] = (offset >> 8) & 0xff;
+			buf[1] = offset & 0xff;
+			writebytes(buf, 2, 1, of);
+		}
+
+		// flag end of incomplete references list
+		writebytes("", 1, 1, of);
+		
+		// now blast out the code
+		
+		// length
+		buf[0] = s -> oblen >> 8 & 0xff;
+		buf[1] = s -> oblen & 0xff;
+		writebytes(buf, 2, 1, of);
+		
+		if (!(s -> flags & section_flag_bss))
+		{
+			writebytes(s -> obytes, s -> oblen, 1, of);
+		}
+	}
+	
+	// flag no more sections
+	// the "" is NOT an error
+	writebytes("", 1, 1, of);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lwasm/pass1.c	Wed Jan 19 22:27:17 2011 -0700
@@ -0,0 +1,316 @@
+/*
+pass1.c
+
+Copyright © 2010 William Astle
+
+This file is part of LWTOOLS.
+
+LWTOOLS 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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdio.h>
+#include <string.h>
+
+#include <lw_alloc.h>
+#include <lw_string.h>
+
+#include "lwasm.h"
+#include "instab.h"
+#include "input.h"
+
+extern int expand_macro(asmstate_t *as, line_t *l, char **p, char *opc);
+extern int expand_struct(asmstate_t *as, line_t *l, char **p, char *opc);
+
+/*
+pass 1: parse the lines
+
+line format:
+
+[<symbol>] <opcode> <operand>[ <comment>]
+
+If <symbol> is followed by a :, whitespace may precede the symbol
+
+A line may optionally start with a number which must not be preceded by
+white space and must be followed by a single whitespace character. After
+that whitespace character, the line is parsed as if it had no line number.
+
+*/
+void do_pass1(asmstate_t *as)
+{
+	char *line;
+	line_t *cl;
+	char *p1;
+	int stspace;
+	char *tok, *sym;
+	int opnum;
+	int lc = 1;
+	for (;;)
+	{
+		sym = NULL;
+		line = input_readline(as);
+		if (!line)
+			break;
+		if (line[0] == 1 && line[1] == 1)
+		{
+			// special internal directive
+			// these DO NOT appear in the output anywhere
+			// they are generated by the parser to pass information
+			// forward
+			for (p1 = line + 2; *p1 && !isspace(*p1); p1++)
+				/* do nothing */ ;
+			*p1++ = '\0';
+			if (!strcmp(line + 2, "SETCONTEXT"))
+			{
+				as -> context = strtol(p1, NULL, 10);
+			}
+			lw_free(line);
+			lc = 1;
+			continue;
+		}
+		debug_message(as, 75, "Read line: %s", line);
+		
+		cl = lw_alloc(sizeof(line_t));
+		memset(cl, 0, sizeof(line_t));
+		cl -> outputl = -1;
+		cl -> linespec = lw_strdup(input_curspec(as));
+		cl -> prev = as -> line_tail;
+		cl -> insn = -1;
+		cl -> as = as;
+		cl -> inmod = as -> inmod;
+		cl -> csect = as -> csect;
+		cl -> pragmas = as -> pragmas;
+		cl -> context = as -> context;
+		cl -> ltext = lw_strdup(line);
+		cl -> soff = -1;
+		cl -> dshow = -1;
+		cl -> dsize = 0;
+		cl -> dptr = NULL;
+		cl -> isbrpt = 0;
+		as -> cl = cl;
+		if (!as -> line_tail)
+		{
+			as -> line_head = cl;
+			cl -> addr = lw_expr_build(lw_expr_type_int, 0);
+		}
+		else
+		{
+			lw_expr_t te;
+
+			cl -> lineno = as -> line_tail -> lineno + 1;
+			as -> line_tail -> next = cl;
+
+			// set the line address
+			te = lw_expr_build(lw_expr_type_special, lwasm_expr_linelen, cl -> prev);
+			cl -> addr = lw_expr_build(lw_expr_type_oper, lw_expr_oper_plus, cl -> prev -> addr, te);
+			lw_expr_destroy(te);
+			lwasm_reduce_expr(as, cl -> addr);
+//			lw_expr_simplify(cl -> addr, as);
+
+			// carry DP value forward
+			cl -> dpval = cl -> prev -> dpval;
+			
+		}
+		if (!lc && strcmp(cl -> linespec, cl -> prev -> linespec))
+			lc = 1;
+		if (lc)
+		{
+			cl -> lineno = 1;
+			lc = 0;
+		}
+		as -> line_tail = cl;
+		// blank lines don't count for anything
+		// except a local symbol context break
+		if (!*line)
+		{
+			as -> context = lwasm_next_context(as);
+			goto nextline;
+		}
+	
+		// skip comments
+		// commends do not create a context break
+		if (*line == '*' || *line == ';' || *line == '#')
+			goto nextline;
+
+		p1 = line;
+		if (isdigit(*p1))
+		{
+			// skip line number
+			while (*p1 && isdigit(*p1))
+				p1++;
+			if (!*p1 && !isspace(*p1))
+				p1 = line;
+			else if (*p1 && !isspace(*p1))
+				p1 = line;
+			else if (*p1 && isspace(*p1))
+				p1++;
+		}
+
+		// blank line - context break
+		if (!*p1)
+		{
+			as -> context = lwasm_next_context(as);
+			goto nextline;
+		}
+
+		// comment - no context break
+		if (*p1 == '*' || *p1 == ';' || *p1 == '#')
+			goto nextline;
+
+		if (isspace(*p1))
+		{
+			for (; *p1 && isspace(*p1); p1++)
+				/* do nothing */ ;
+			stspace = 1;
+		}
+		else
+			stspace = 0;
+
+		if (*p1 == '*' || *p1 == ';' || *p1 == '#')
+			goto nextline;
+		if (!*p1)
+		{
+			// nothing but whitespace - context break
+			as -> context = lwasm_next_context(as);
+			goto nextline;
+		}
+
+		// find the end of the first token
+		for (tok = p1; *p1 && !isspace(*p1) && *p1 != ':' && *p1 != '='; p1++)
+			/* do nothing */ ;
+		
+		if (*p1 == ':' || *p1 == '=' || stspace == 0)
+		{
+			// have a symbol here
+			sym = lw_strndup(tok, p1 - tok);
+			if (*p1 == ':')
+				p1++;
+			for (; *p1 && isspace(*p1); p1++)
+				/* do nothing */ ;
+			
+			if (*p1 == '=')
+			{
+				tok = p1++;
+			}
+			else
+			{
+				for (tok = p1; *p1 && !isspace(*p1); p1++)
+					/* do nothing */ ;
+			}
+		}
+		if (sym && strcmp(sym, "!") == 0)
+			cl -> isbrpt = 1;
+		else if (sym)
+			cl -> sym = lw_strdup(sym);
+		cl -> symset = 0;
+		
+		// tok points to the opcode for the line or NUL if none
+		if (*tok)
+		{
+			// look up operation code
+			sym = lw_strndup(tok, p1 - tok);
+			for (; *p1 && isspace(*p1); p1++)
+				/* do nothing */ ;
+
+			for (opnum = 0; instab[opnum].opcode; opnum++)
+			{
+				if (!strcasecmp(instab[opnum].opcode, sym))
+					break;
+			}
+			
+			// p1 points to the start of the operand
+			
+			// if we're inside a macro definition and not at ENDM,
+			// add the line to the macro definition and continue
+			if (as -> inmacro && !(instab[opnum].flags & lwasm_insn_endm))
+			{
+				add_macro_line(as, line);
+				goto linedone;
+			}
+			
+			// if skipping a condition and the operation code doesn't
+			// operate within a condition (not a conditional)
+			// do nothing
+			if (as -> skipcond && !(instab[opnum].flags & lwasm_insn_cond))
+				goto linedone;
+        	
+			if (instab[opnum].opcode == NULL)
+			{
+				cl -> insn = -1;
+				if (*tok != ';' && *tok != '*')
+				{
+					// bad opcode; check for macro here
+					if (expand_macro(as, cl, &p1, sym) != 0)
+					{
+						// macro expansion failed
+						if (expand_struct(as, cl, &p1, sym) != 0)
+						{
+							// structure expansion failed
+							lwasm_register_error(as, cl, "Bad opcode");
+						}
+					}
+				}
+			}
+			else
+			{
+				cl -> insn = opnum;
+				// no parse func means operand doesn't matter
+				if (instab[opnum].parse)
+				{
+					if (as -> instruct == 0 || instab[opnum].flags & lwasm_insn_struct)
+					{
+						cl -> len = -1;
+						// call parse function
+						(instab[opnum].parse)(as, cl, &p1);
+					
+						if (*p1 && !isspace(*p1))
+						{
+							// flag bad operand error
+							lwasm_register_error(as, cl, "Bad operand (%s)", p1);
+						}
+					}
+					else if (as -> instruct == 1)
+					{
+						lwasm_register_error(as, cl, "Bad operand (%s)", p1);
+					}
+				}
+			}
+		}
+	
+	linedone:
+		lw_free(sym);
+		
+		if (!as -> skipcond && !as -> inmacro)
+		{
+			if (cl -> sym && cl -> symset == 0)
+			{
+				debug_message(as, 50, "Register symbol %s: %s", cl -> sym, lw_expr_print(cl -> addr));
+	
+				// register symbol at line address
+				if (!register_symbol(as, cl, cl -> sym, cl -> addr, symbol_flag_none))
+				{
+					// symbol error
+					// lwasm_register_error(as, cl, "Bad symbol '%s'", cl -> sym);
+				}
+			}
+			debug_message(as, 40, "Line address: %s", lw_expr_print(cl -> addr));
+		}
+		
+	nextline:
+		lw_free(line);
+		
+		// if we've hit the "end" bit, finish out
+		if (as -> endseen)
+			return;
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lwasm/pass2.c	Wed Jan 19 22:27:17 2011 -0700
@@ -0,0 +1,94 @@
+/*
+pass2.c
+
+Copyright © 2010 William Astle
+
+This file is part of LWTOOLS.
+
+LWTOOLS 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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdio.h>
+#include <string.h>
+
+#include <lw_alloc.h>
+#include <lw_string.h>
+
+#include "lwasm.h"
+#include "instab.h"
+
+/*
+pass 2: deal with undefined symbols and do a simplification pass
+on all the expressions. Handle PRAGMA_IMPORTUNDEFEXPORT
+
+*/
+void do_pass2(asmstate_t *as)
+{
+	line_t *cl;
+	exportlist_t *ex;
+	struct symtabe *s;
+	importlist_t *im;
+	struct line_expr_s *le;
+
+	// verify the export list
+	if (as -> output_format == OUTPUT_OBJ)
+	{	
+		for (ex = as -> exportlist; ex; ex = ex -> next)
+		{
+			s = lookup_symbol(as, NULL, ex -> symbol);
+			if (!s)
+			{
+				if (CURPRAGMA(ex -> line, PRAGMA_IMPORTUNDEFEXPORT))
+				{
+					for (im = as -> importlist; im; im = im -> next)
+					{
+						if (!strcmp(ex -> symbol, im -> symbol))
+							break;
+					}
+					if (!im)
+					{
+						im = lw_alloc(sizeof(importlist_t));
+						im -> symbol = lw_strdup(ex -> symbol);
+						im -> next = as -> importlist;
+						as -> importlist = im;
+					}
+				}
+				else
+				{
+					// undefined export - register error
+					lwasm_register_error(as, ex -> line, "Undefined exported symbol");
+				}
+			}
+			ex -> se = s;
+		}
+		if (as -> errorcount > 0)
+			return;
+	}
+
+	// we want to throw errors on undefined symbols here
+	as -> badsymerr = 1;
+	
+	// now do some reductions on expressions
+	for (cl = as -> line_head; cl; cl = cl -> next)
+	{
+		as -> cl = cl;
+		
+		// simplify address
+		lwasm_reduce_expr(as, cl -> addr);
+		
+		// simplify each expression
+		for (le = cl -> exprs; le; le = le -> next)
+			lwasm_reduce_expr(as, le -> expr);
+	}	
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lwasm/pass3.c	Wed Jan 19 22:27:17 2011 -0700
@@ -0,0 +1,73 @@
+/*
+pass3.c
+
+Copyright © 2010 William Astle
+
+This file is part of LWTOOLS.
+
+LWTOOLS 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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdio.h>
+#include <string.h>
+
+#include <lw_alloc.h>
+#include <lw_string.h>
+
+#include "lwasm.h"
+#include "instab.h"
+
+/*
+Resolve1 Pass
+
+repeatedly resolve instruction sizes and line addresses
+until nothing more reduces
+
+*/
+void do_pass3(asmstate_t *as)
+{
+	int rc;
+	line_t *cl;
+	struct line_expr_s *le;
+	
+	do
+	{
+		rc = 0;
+		for (cl = as -> line_head; cl; cl = cl -> next)
+		{
+			as -> cl = cl;
+			
+			// simplify address
+			lwasm_reduce_expr(as, cl -> addr);
+		
+			// simplify each expression
+			for (le = cl -> exprs; le; le = le -> next)
+				lwasm_reduce_expr(as, le -> expr);
+			
+			if (cl -> len == -1)
+			{
+				// try resolving the instruction length
+				// but don't force resolution
+				if (cl -> insn >= 0 && instab[cl -> insn].resolve)
+				{
+					(instab[cl -> insn].resolve)(as, cl, 0);
+					if (cl -> len != -1)
+						rc++;
+				}
+			}
+		}
+		if (as -> errorcount > 0)
+			return;
+	} while (rc > 0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lwasm/pass4.c	Wed Jan 19 22:27:17 2011 -0700
@@ -0,0 +1,127 @@
+/*
+pass4.c
+
+Copyright © 2010 William Astle
+
+This file is part of LWTOOLS.
+
+LWTOOLS 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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdio.h>
+#include <string.h>
+
+#include <lw_alloc.h>
+#include <lw_string.h>
+
+#include "lwasm.h"
+#include "instab.h"
+
+/*
+Resolve2 Pass
+
+Force resolution of instruction sizes.
+
+*/
+void do_pass4_aux(asmstate_t *as, int force)
+{
+	int rc;
+	int cnt;
+	line_t *cl, *sl;
+	struct line_expr_s *le;
+
+	// first, count the number of unresolved instructions
+	for (cnt = 0, cl = as -> line_head; cl; cl = cl -> next)
+	{
+		if (cl -> len == -1)
+			cnt++;
+	}
+
+	sl = as -> line_head;
+	while (cnt > 0)
+	{
+		// find an unresolved instruction
+		for ( ; sl && sl -> len != -1; sl = sl -> next)
+		{
+			as -> cl = sl;
+			lwasm_reduce_expr(as, sl -> addr);
+		
+			// simplify each expression
+			for (le = sl -> exprs; le; le = le -> next)
+				lwasm_reduce_expr(as, le -> expr);
+		}
+				
+		// simplify address
+		as -> cl = sl;
+		lwasm_reduce_expr(as, sl -> addr);
+		
+		// simplify each expression
+		for (le = sl -> exprs; le; le = le -> next)
+			lwasm_reduce_expr(as, le -> expr);
+
+
+		if (sl -> len == -1 && sl -> insn >= 0 && instab[sl -> insn].resolve)
+		{
+			(instab[sl -> insn].resolve)(as, sl, 1);
+			if (force && sl -> len == -1)
+			{
+				lwasm_register_error(as, sl, "Instruction failed to resolve.");
+				return;
+			}
+		}
+		cnt--;
+		if (cnt == 0)
+			return;
+
+		do
+		{
+			rc = 0;
+			for (cl = sl; cl; cl = cl -> next)
+			{
+				as -> cl = cl;
+			
+				// simplify address
+				lwasm_reduce_expr(as, cl -> addr);
+		
+				// simplify each expression
+				for (le = cl -> exprs; le; le = le -> next)
+					lwasm_reduce_expr(as, le -> expr);
+			
+				if (cl -> len == -1)
+				{
+					// try resolving the instruction length
+					// but don't force resolution
+					if (cl -> insn >= 0 && instab[cl -> insn].resolve)
+					{
+						(instab[cl -> insn].resolve)(as, cl, 0);
+						if (cl -> len != -1)
+						{
+							rc++;
+							cnt--;
+							if (cnt == 0)
+								return;
+						}
+					}
+				}
+			}
+			if (as -> errorcount > 0)
+				return;
+		} while (rc > 0);
+	}
+}
+
+void do_pass4(asmstate_t *as)
+{
+	do_pass4_aux(as, 1);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lwasm/pass5.c	Wed Jan 19 22:27:17 2011 -0700
@@ -0,0 +1,119 @@
+/*
+pass5.c
+
+Copyright © 2010 William Astle
+
+This file is part of LWTOOLS.
+
+LWTOOLS 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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdio.h>
+#include <string.h>
+
+#include <lw_alloc.h>
+#include <lw_string.h>
+
+#include "lwasm.h"
+#include "instab.h"
+
+/*
+AssignAddresses Pass
+
+Force resolution of all line addresses
+
+*/
+
+static int exprok_aux(lw_expr_t e, void *priv)
+{
+	asmstate_t *as = priv;
+	
+	if (lw_expr_istype(e, lw_expr_type_int))
+		return 0;
+	if (lw_expr_istype(e, lw_expr_type_oper))
+		return 0;
+	if (lw_expr_istype(e, lw_expr_type_special) && as -> output_format == OUTPUT_OBJ)
+	{
+		int t;
+		t = lw_expr_specint(e);
+		if (t == lwasm_expr_secbase)
+			return 0;
+	}
+	
+	return 1;
+}
+
+static int exprok(asmstate_t *as, lw_expr_t e)
+{
+	if (lw_expr_testterms(e, exprok_aux, as))
+		return 0;
+	return 1;
+}
+
+void do_pass5(asmstate_t *as)
+{
+	int rc;
+	int cnt;
+	int ocnt;
+	line_t *cl, *sl;
+	struct line_expr_s *le;
+
+	// first, count the number of non-constant addresses; do
+	// a reduction first on each one
+	for (cnt = 0, cl = as -> line_head; cl; cl = cl -> next)
+	{
+		as -> cl = cl;
+		lwasm_reduce_expr(as, cl -> addr);
+		if (!exprok(as, cl -> addr))
+			cnt++;
+	}
+
+	sl = as -> line_head;
+	while (cnt > 0)
+	{
+		ocnt = cnt;
+		
+		// find an unresolved address
+		for ( ; sl && exprok(as, sl -> addr); sl = sl -> next)
+			/* do nothing */ ;
+
+		// simplify address
+		for (cl = sl; cl; cl = cl -> next)
+		{
+			as -> cl = sl;
+			lwasm_reduce_expr(as, sl -> addr);
+		
+			if (exprok(as, cl -> addr))
+			{
+				if (0 == --cnt);
+					return;
+			}
+		}
+		
+		if (cnt == ocnt)
+			break;
+	}
+	
+	if (cnt)
+	{
+		// we have non-resolved line addresses here
+		for (cl = sl; cl; cl = cl -> next)
+		{
+			if (!exprok(as, cl -> addr))
+			{
+				lwasm_register_error(as, cl, "Cannot resolve line address");
+			}
+		}
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lwasm/pass6.c	Wed Jan 19 22:27:17 2011 -0700
@@ -0,0 +1,90 @@
+/*
+pass6.c
+
+Copyright © 2010 William Astle
+
+This file is part of LWTOOLS.
+
+LWTOOLS 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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdio.h>
+#include <string.h>
+
+#include <lw_alloc.h>
+#include <lw_string.h>
+
+#include "lwasm.h"
+#include "instab.h"
+
+/*
+Finalize Pass
+
+Reduce all expressions in a final pass.
+
+Observation:
+
+Everything should reduce as far as it is going to in a single pass
+because all line addresses are now constant (or section-base offset)
+*/
+
+static int exprok_aux(lw_expr_t e, void *priv)
+{
+	asmstate_t *as = priv;
+	
+	if (lw_expr_istype(e, lw_expr_type_int))
+		return 0;
+	
+	if (as -> output_format == OUTPUT_OBJ)
+	{
+		if (lw_expr_istype(e, lw_expr_type_oper))
+			return 0;
+		if (lw_expr_istype(e, lw_expr_type_special) && as -> output_format == OUTPUT_OBJ)
+		{
+			int t;
+			t = lw_expr_specint(e);
+			if (t == lwasm_expr_secbase || t == lwasm_expr_syment || t == lwasm_expr_import)
+				return 0;
+		}
+	}
+	
+	return 1;
+}
+
+static int exprok(asmstate_t *as, lw_expr_t e)
+{
+	if (lw_expr_testterms(e, exprok_aux, as))
+		return 0;
+	return 1;
+}
+
+
+void do_pass6(asmstate_t *as)
+{
+	line_t *cl;
+	struct line_expr_s *le;
+
+	for (cl = as -> line_head; cl; cl = cl -> next)
+	{
+		as -> cl = cl;
+		for (le = cl -> exprs; le; le = le -> next)
+		{
+			lwasm_reduce_expr(as, le -> expr);
+			if (!exprok(as, le -> expr))
+			{
+				lwasm_register_error(as, cl, "Invalid expression");
+			}
+		}
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lwasm/pass7.c	Wed Jan 19 22:27:17 2011 -0700
@@ -0,0 +1,51 @@
+/*
+pass7.c
+
+Copyright © 2010 William Astle
+
+This file is part of LWTOOLS.
+
+LWTOOLS 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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdio.h>
+#include <string.h>
+
+#include <lw_alloc.h>
+#include <lw_string.h>
+
+#include "lwasm.h"
+#include "instab.h"
+
+/*
+emit pass
+
+Generate object code
+*/
+void do_pass7(asmstate_t *as)
+{
+	line_t *cl;
+
+	for (cl = as -> line_head; cl; cl = cl -> next)
+	{
+		as -> cl = cl;
+		if (cl -> insn != -1)
+		{
+			if (instab[cl -> insn].emit)
+			{
+				(instab[cl -> insn].emit)(as, cl);
+			}
+		}
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lwasm/pragma.c	Wed Jan 19 22:27:17 2011 -0700
@@ -0,0 +1,124 @@
+/*
+pragma.c
+
+Copyright © 2010 William Astle
+
+This file is part of LWTOOLS.
+
+LWTOOLS 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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <lw_string.h>
+
+#include "lwasm.h"
+#include "instab.h"
+
+struct pragma_list
+{
+	const char *str;
+	int flag;
+};
+
+static const struct pragma_list set_pragmas[] =
+{
+	{ "dollarnotlocal", PRAGMA_DOLLARNOTLOCAL },
+	{ "noindex0tonone", PRAGMA_NOINDEX0TONONE },
+	{ "undefextern", PRAGMA_UNDEFEXTERN },
+	{ "cescapes", PRAGMA_CESCAPES },
+	{ "importundefexport", PRAGMA_IMPORTUNDEFEXPORT },
+	{ "pcaspcr", PRAGMA_PCASPCR },
+	{ 0, 0 }
+};
+
+static const struct pragma_list reset_pragmas[] =
+{
+	{ "nodollarnotlocal", PRAGMA_DOLLARNOTLOCAL },
+	{ "index0tonone", PRAGMA_NOINDEX0TONONE },
+	{ "noundefextern", PRAGMA_UNDEFEXTERN },
+	{ "nocescapes", PRAGMA_CESCAPES },
+	{ "noimportundefexport", PRAGMA_IMPORTUNDEFEXPORT },
+	{ "nopcaspcr", PRAGMA_PCASPCR },
+	{ 0, 0 }
+};
+
+int parse_pragma_string(asmstate_t *as, char *str, int ignoreerr)
+{
+	char *p;
+	int i;
+	const char *np = str;
+	int pragmas = as -> pragmas;
+
+	while (np)
+	{
+		p = lw_token(np, ',', &np);
+		for (i = 0; set_pragmas[i].str; i++)
+		{
+			if (!strcasecmp(p, set_pragmas[i].str))
+			{
+				pragmas |= set_pragmas[i].flag;
+				goto out;
+			}
+		}
+		for (i = 0; reset_pragmas[i].str; i++)
+		{
+			if (!strcasecmp(p, reset_pragmas[i].str))
+			{
+				pragmas &= ~(reset_pragmas[i].flag);
+				goto out;
+			}
+		}
+		/* unrecognized pragma here */
+		if (!ignoreerr)
+		{
+			lw_free(p);
+			return 0;
+		}
+	out:	
+		lw_free(p);
+	}
+	as -> pragmas = pragmas;
+	return 1;
+}
+
+PARSEFUNC(pseudo_parse_pragma)
+{
+	char *ps, *t;
+	
+	for (t = *p; *t && !isspace(*t); t++)
+		/* do nothing */ ;
+	
+	ps = lw_strndup(*p, t - *p);
+	*p = t;
+
+	if (parse_pragma_string(as, ps, 0) == 0)
+	{
+		lwasm_register_error(as, l, "Unrecognized pragma string");
+	}
+	lw_free(ps);
+}
+
+PARSEFUNC(pseudo_parse_starpragma)
+{
+	char *ps, *t;
+	
+	for (t = *p; *t && !isspace(*t); t++)
+		/* do nothing */ ;
+	
+	ps = lw_strndup(*p, t - *p);
+	*p = t;
+
+	// *pragma must NEVER throw an error
+	parse_pragma_string(as, ps, 1);
+	lw_free(ps);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lwasm/pseudo.c	Wed Jan 19 22:27:17 2011 -0700
@@ -0,0 +1,1143 @@
+/*
+pseudo.c
+Copyright © 2010 William Astle
+
+This file is part of LWASM.
+
+LWASM 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 3 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, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include <stdio.h>
+
+#include "lwasm.h"
+#include "instab.h"
+#include "input.h"
+
+#include "lw_string.h"
+
+extern void register_struct_entry(asmstate_t *as, line_t *l, int size, structtab_t *ss);
+
+// for "end"
+PARSEFUNC(pseudo_parse_end)
+{
+	lw_expr_t addr;
+	
+	as -> endseen = 1;
+	l -> len = 0;
+	
+	if (as -> output_format != OUTPUT_DECB)
+	{
+		skip_operand(p);
+		return;
+	}
+	
+	if (!**p)
+	{
+		addr = lw_expr_build(lw_expr_type_int, 0);
+	}
+	else
+	{
+		addr = lwasm_parse_expr(as, p);
+	}
+	if (!addr)
+	{
+		lwasm_register_error(as, l, "Bad expression");
+		addr = lw_expr_build(lw_expr_type_int, 0);
+	}
+	lwasm_save_expr(l, 0, addr);
+}
+
+EMITFUNC(pseudo_emit_end)
+{
+	lw_expr_t addr;
+	
+	addr = lwasm_fetch_expr(l, 0);
+	
+	if (addr)
+	{
+		if (!lw_expr_istype(addr, lw_expr_type_int))
+			lwasm_register_error(as, l, "Exec address not constant!");
+		else
+			as -> execaddr = lw_expr_intval(addr);
+	}
+	as -> endseen = 1;
+}
+
+PARSEFUNC(pseudo_parse_fcb)
+{
+	int i = 0;
+	lw_expr_t e;
+	
+	for (;;)
+	{
+		e = lwasm_parse_expr(as, p);
+		if (!e)
+		{
+			lwasm_register_error(as, l, "Bad expression (#%s)", i);
+			break;
+		}
+		lwasm_save_expr(l, i++, e);
+		if (**p != ',')
+			break;
+		(*p)++;
+	}
+	
+	l -> len = i;
+}
+
+EMITFUNC(pseudo_emit_fcb)
+{
+	int i;
+	lw_expr_t e;
+	int v;
+	
+	for (i = 0; i < l -> len; i++)
+	{
+		e = lwasm_fetch_expr(l, i);
+		lwasm_emitexpr(l, e, 1);
+	}
+}
+
+PARSEFUNC(pseudo_parse_fdb)
+{
+	int i = 0;
+	lw_expr_t e;
+	
+	for (;;)
+	{
+		e = lwasm_parse_expr(as, p);
+		if (!e)
+		{
+			lwasm_register_error(as, l, "Bad expression (#%d)", i);
+			break;
+		}
+		lwasm_save_expr(l, i++, e);
+		if (**p != ',')
+			break;
+		(*p)++;
+	}
+	
+	l -> len = i * 2;
+}
+
+EMITFUNC(pseudo_emit_fdb)
+{
+	int i;
+	lw_expr_t e;
+	int v;
+	
+	for (i = 0; i < (l -> len)/2; i++)
+	{
+		e = lwasm_fetch_expr(l, i);
+		lwasm_emitexpr(l, e, 2);
+	}
+}
+
+PARSEFUNC(pseudo_parse_fqb)
+{
+	int i = 0;
+	lw_expr_t e;
+	
+	for (;;)
+	{
+		e = lwasm_parse_expr(as, p);
+		if (!e)
+		{
+			lwasm_register_error(as, l, "Bad expression (#%s)", i);
+			break;
+		}
+		lwasm_save_expr(l, i++, e);
+		if (**p != ',')
+			break;
+		(*p)++;
+	}
+	
+	l -> len = i * 4;
+}
+
+EMITFUNC(pseudo_emit_fqb)
+{
+	int i;
+	lw_expr_t e;
+	int v;
+	
+	for (i = 0; i < (l -> len)/4; i++)
+	{
+		e = lwasm_fetch_expr(l, i);
+		lwasm_emitexpr(l, e, 4);
+	}
+}
+
+PARSEFUNC(pseudo_parse_fcc)
+{
+	char delim;
+	int i;
+	
+	if (!**p)
+	{
+		lwasm_register_error(as, l, "Bad operand");
+		return;
+	}
+	
+	delim = **p;
+	(*p)++;
+	
+	for (i = 0; (*p)[i] && (*p)[i] != delim; i++)
+		/* do nothing */ ;
+	
+	if ((*p)[i] != delim)
+	{
+		lwasm_register_error(as, l, "Bad operand");
+		return;
+	}
+	
+	l -> lstr = lw_strndup(*p, i);
+	(*p) += i + 1;
+	
+	l -> len = i;
+}
+
+EMITFUNC(pseudo_emit_fcc)
+{
+	int i;
+	
+	for (i = 0; i < l -> len; i++)
+		lwasm_emit(l, l -> lstr[i]);
+}
+
+PARSEFUNC(pseudo_parse_fcs)
+{
+	char delim;
+	int i;
+	
+	if (!**p)
+	{
+		lwasm_register_error(as, l, "Bad operand");
+		return;
+	}
+	
+	delim = **p;
+	(*p)++;
+	
+	for (i = 0; (*p)[i] && (*p)[i] != delim; i++)
+		/* do nothing */ ;
+	
+	if ((*p)[i] != delim)
+	{
+		lwasm_register_error(as, l, "Bad operand");
+		return;
+	}
+	
+	l -> lstr = lw_strndup(*p, i);
+	(*p) += i + 1;
+	
+	l -> len = i;
+}
+
+EMITFUNC(pseudo_emit_fcs)
+{
+	int i;
+	
+	for (i = 0; i < l -> len - 1; i++)
+		lwasm_emit(l, l -> lstr[i]);
+	lwasm_emit(l, l -> lstr[i] | 0x80);
+}
+
+PARSEFUNC(pseudo_parse_fcn)
+{
+	char delim;
+	int i;
+	
+	if (!**p)
+	{
+		lwasm_register_error(as, l, "Bad operand");
+		return;
+	}
+	
+	delim = **p;
+	(*p)++;
+	
+	for (i = 0; (*p)[i] && (*p)[i] != delim; i++)
+		/* do nothing */ ;
+	
+	if ((*p)[i] != delim)
+	{
+		lwasm_register_error(as, l, "Bad operand");
+		return;
+	}
+	
+	l -> lstr = lw_strndup(*p, i);
+	(*p) += i + 1;
+	
+	l -> len = i + 1;
+}
+
+EMITFUNC(pseudo_emit_fcn)
+{
+	int i;
+	
+	for (i = 0; i < (l -> len - 1); i++)
+		lwasm_emit(l, l -> lstr[i]);
+	lwasm_emit(l, 0);
+}
+
+PARSEFUNC(pseudo_parse_rmb)
+{
+	lw_expr_t expr;
+	
+	expr = lwasm_parse_expr(as, p);
+	if (!expr)
+	{
+		lwasm_register_error(as, l, "Bad expression");
+	}
+
+	l -> lint = 0;
+	if (as -> instruct)
+	{
+		lwasm_reduce_expr(as, expr);
+		if (!lw_expr_istype(expr, lw_expr_type_int))
+		{
+			lwasm_register_error(as, l, "Expression must be constant at parse time");
+		}
+		else
+		{
+			int e;
+			e = lw_expr_intval(expr);
+			register_struct_entry(as, l, e, NULL);
+			l -> len = 0;
+			l -> lint = 1;
+			l -> symset = 1;
+		}
+	}
+	
+	lwasm_save_expr(l, 0, expr);
+}
+
+RESOLVEFUNC(pseudo_resolve_rmb)
+{
+	lw_expr_t expr;
+	
+	if (l -> lint)
+		return;
+	
+	if (l -> len >= 0)
+		return;
+	
+	expr = lwasm_fetch_expr(l, 0);
+	
+	if (lw_expr_istype(expr, lw_expr_type_int))
+	{
+		l -> len = lw_expr_intval(expr);
+	}
+}
+
+EMITFUNC(pseudo_emit_rmb)
+{
+	if (l -> lint)
+		return;
+
+	if (l -> len < 0)
+		lwasm_register_error(as, l, "Expression not constant");
+}
+
+PARSEFUNC(pseudo_parse_rmd)
+{
+	lw_expr_t expr;
+	
+	l -> lint = 0;
+	expr = lwasm_parse_expr(as, p);
+	if (!expr)
+	{
+		lwasm_register_error(as, l, "Bad expression");
+	}
+	
+	if (as -> instruct)
+	{
+		lwasm_reduce_expr(as, expr);
+		if (!lw_expr_istype(expr, lw_expr_type_int))
+		{
+			lwasm_register_error(as, l, "Expression must be constant at parse time");
+		}
+		else
+		{
+			int e;
+			e = lw_expr_intval(expr) * 2;
+			register_struct_entry(as, l, e, NULL);
+			l -> len = 0;
+			l -> symset = 1;
+			l -> lint = 1;
+		}
+	}
+	lwasm_save_expr(l, 0, expr);
+}
+
+RESOLVEFUNC(pseudo_resolve_rmd)
+{
+	lw_expr_t expr;
+	
+	if (l -> lint)
+		return;
+	
+	if (l -> len >= 0)
+		return;
+	
+	expr = lwasm_fetch_expr(l, 0);
+	
+	if (lw_expr_istype(expr, lw_expr_type_int))
+	{
+		l -> len = lw_expr_intval(expr) * 2;
+	}
+}
+
+EMITFUNC(pseudo_emit_rmd)
+{
+	if (l -> lint)
+		return;
+
+	if (l -> len < 0)
+		lwasm_register_error(as, l, "Expression not constant");
+}
+
+
+PARSEFUNC(pseudo_parse_rmq)
+{
+	lw_expr_t expr;
+	
+	l -> lint = 0;
+	expr = lwasm_parse_expr(as, p);
+	if (!expr)
+	{
+		lwasm_register_error(as, l, "Bad expression");
+	}
+	if (as -> instruct)
+	{
+		lwasm_reduce_expr(as, expr);
+		if (!lw_expr_istype(expr, lw_expr_type_int))
+		{
+			lwasm_register_error(as, l, "Expression must be constant at parse time");
+		}
+		else
+		{
+			int e;
+			e = lw_expr_intval(expr) * 4;
+			register_struct_entry(as, l, e, NULL);
+			l -> len = 0;
+			l -> symset = 1;
+			l -> lint = 1;
+		}
+	}
+	
+	lwasm_save_expr(l, 0, expr);
+}
+
+RESOLVEFUNC(pseudo_resolve_rmq)
+{
+	lw_expr_t expr;
+
+	if (l -> lint)
+		return;
+	
+	if (l -> len >= 0)
+		return;
+	
+	expr = lwasm_fetch_expr(l, 0);
+	
+	if (lw_expr_istype(expr, lw_expr_type_int))
+	{
+		l -> len = lw_expr_intval(expr) * 4;
+	}
+}
+
+EMITFUNC(pseudo_emit_rmq)
+{
+	if (l -> lint)
+		return;
+
+	if (l -> len < 0)
+		lwasm_register_error(as, l, "Expression not constant");
+}
+
+
+PARSEFUNC(pseudo_parse_zmq)
+{
+	lw_expr_t expr;
+	
+	expr = lwasm_parse_expr(as, p);
+	if (!expr)
+	{
+		lwasm_register_error(as, l, "Bad expression");
+	}
+	
+	lwasm_save_expr(l, 0, expr);
+}
+
+RESOLVEFUNC(pseudo_resolve_zmq)
+{
+	lw_expr_t expr;
+	
+	if (l -> len >= 0)
+		return;
+	
+	expr = lwasm_fetch_expr(l, 0);
+	
+	if (lw_expr_istype(expr, lw_expr_type_int))
+	{
+		l -> len = lw_expr_intval(expr) * 4;
+	}
+}
+
+EMITFUNC(pseudo_emit_zmq)
+{
+	int i;
+
+	if (l -> len < 0)
+	{
+		lwasm_register_error(as, l, "Expression not constant");
+		return;
+	}
+
+	for (i = 0; i < l -> len; i++)
+		lwasm_emit(l, 0);	
+}
+
+
+PARSEFUNC(pseudo_parse_zmd)
+{
+	lw_expr_t expr;
+	
+	expr = lwasm_parse_expr(as, p);
+	if (!expr)
+	{
+		lwasm_register_error(as, l, "Bad expression");
+	}
+	
+	lwasm_save_expr(l, 0, expr);
+}
+
+RESOLVEFUNC(pseudo_resolve_zmd)
+{
+	lw_expr_t expr;
+	
+	if (l -> len >= 0)
+		return;
+	
+	expr = lwasm_fetch_expr(l, 0);
+	
+	if (lw_expr_istype(expr, lw_expr_type_int))
+	{
+		l -> len = lw_expr_intval(expr) * 2;
+	}
+}
+
+EMITFUNC(pseudo_emit_zmd)
+{
+	int i;
+
+	if (l -> len < 0)
+	{
+		lwasm_register_error(as, l, "Expression not constant");
+		return;
+	}
+
+	for (i = 0; i < l -> len; i++)
+		lwasm_emit(l, 0);	
+}
+
+PARSEFUNC(pseudo_parse_zmb)
+{
+	lw_expr_t expr;
+	
+	expr = lwasm_parse_expr(as, p);
+	if (!expr)
+	{
+		lwasm_register_error(as, l, "Bad expression");
+	}
+	
+	lwasm_save_expr(l, 0, expr);
+}
+
+RESOLVEFUNC(pseudo_resolve_zmb)
+{
+	lw_expr_t expr;
+	
+	if (l -> len >= 0)
+		return;
+	
+	expr = lwasm_fetch_expr(l, 0);
+	
+	if (lw_expr_istype(expr, lw_expr_type_int))
+	{
+		l -> len = lw_expr_intval(expr);
+	}
+}
+
+EMITFUNC(pseudo_emit_zmb)
+{
+	int i;
+
+	if (l -> len < 0)
+	{
+		lwasm_register_error(as, l, "Expression not constant");
+		return;
+	}
+
+	for (i = 0; i < l -> len; i++)
+		lwasm_emit(l, 0);	
+}
+
+PARSEFUNC(pseudo_parse_org)
+{
+	lw_expr_t e;
+	
+	l -> len = 0;
+	
+	e = lwasm_parse_expr(as, p);
+	if (!e)
+	{
+		lwasm_register_error(as, l, "Bad operand");
+		return;
+	}
+	
+	lw_expr_destroy(l -> addr);
+	l -> addr = e;
+	l -> len = 0;
+}
+
+PARSEFUNC(pseudo_parse_equ)
+{
+	lw_expr_t e;
+	
+	l -> len = 0;
+	
+	if (!(l -> sym))
+	{
+		lwasm_register_error(as, l, "Missing symbol");
+		return;
+	}
+	
+	e = lwasm_parse_expr(as, p);
+	if (!e)
+	{
+		lwasm_register_error(as, l, "Bad operand");
+		return;
+	}
+	
+	register_symbol(as, l, l -> sym, e, symbol_flag_none);
+	l -> symset = 1;
+	l -> dptr = lookup_symbol(as, l, l -> sym);
+}
+
+PARSEFUNC(pseudo_parse_set)
+{
+	lw_expr_t e;
+	
+	l -> len = 0;
+	
+	if (!(l -> sym))
+	{
+		lwasm_register_error(as, l, "Missing symbol");
+		return;
+	}
+	
+	e = lwasm_parse_expr(as, p);
+	if (!e)
+	{
+		lwasm_register_error(as, l, "Bad operand");
+		return;
+	}
+	
+	register_symbol(as, l, l -> sym, e, symbol_flag_set);
+	l -> symset = 1;
+	l -> dptr = lookup_symbol(as, l, l -> sym);
+}
+
+PARSEFUNC(pseudo_parse_setdp)
+{
+	lw_expr_t e;
+
+	l -> len = 0;
+	
+	if (as -> output_format == OUTPUT_OBJ)
+	{
+		lwasm_register_error(as, l, "SETDP not permitted for object target");
+		return;
+	}
+	
+	e = lwasm_parse_expr(as, p);
+	if (!e)
+	{
+		lwasm_register_error(as, l, "Bad operand");
+		return;
+	}
+	
+	if (!lw_expr_istype(e, lw_expr_type_int))
+	{
+		lwasm_register_error(as, l, "SETDP must be constant on pass 1");
+		return;
+	}
+	l -> dpval = lw_expr_intval(e) & 0xff;
+	l -> dshow = l -> dpval;
+	l -> dsize = 1;
+}
+
+PARSEFUNC(pseudo_parse_ifp1)
+{
+	l -> len = 0;
+	
+	if (as -> skipcond && !(as -> skipmacro))
+	{
+		as -> skipcount++;
+		skip_operand(p);
+		return;
+	}
+	
+	lwasm_register_warning(as, l, "IFP1 if is not supported; ignoring");
+	
+}
+
+PARSEFUNC(pseudo_parse_ifp2)
+{
+	l -> len = 0;
+	
+	if (as -> skipcond && !(as -> skipmacro))
+	{
+		as -> skipcount++;
+		skip_operand(p);
+		return;
+	}
+	
+	lwasm_register_warning(as, l, "IFP2 if is not supported; ignoring");
+}
+
+PARSEFUNC(pseudo_parse_ifeq)
+{
+	lw_expr_t e;
+	
+	l -> len = 0;
+	
+	if (as -> skipcond && !(as -> skipmacro))
+	{
+		as -> skipcount++;
+		skip_operand(p);
+		return;
+	}
+
+	e = lwasm_parse_cond(as, p);
+	if (e && lw_expr_intval(e) != 0)
+	{
+		as -> skipcond = 1;
+		as -> skipcount = 1;
+	}
+}
+
+PARSEFUNC(pseudo_parse_ifne)
+{
+	lw_expr_t e;
+	
+	l -> len = 0;
+	
+	if (as -> skipcond && !(as -> skipmacro))
+	{
+		as -> skipcount++;
+		skip_operand(p);
+		return;
+	}
+
+	e = lwasm_parse_cond(as, p);
+	if (e && lw_expr_intval(e) == 0)
+	{
+		as -> skipcond = 1;
+		as -> skipcount = 1;
+	}
+}
+
+
+PARSEFUNC(pseudo_parse_ifgt)
+{
+	lw_expr_t e;
+	
+	l -> len = 0;
+	
+	if (as -> skipcond && !(as -> skipmacro))
+	{
+		as -> skipcount++;
+		skip_operand(p);
+		return;
+	}
+
+	e = lwasm_parse_cond(as, p);
+	if (e && lw_expr_intval(e) <= 0)
+	{
+		as -> skipcond = 1;
+		as -> skipcount = 1;
+	}
+}
+
+PARSEFUNC(pseudo_parse_ifge)
+{
+	lw_expr_t e;
+	
+	l -> len = 0;
+	
+	if (as -> skipcond && !(as -> skipmacro))
+	{
+		as -> skipcount++;
+		skip_operand(p);
+		return;
+	}
+
+	e = lwasm_parse_cond(as, p);
+	if (e && lw_expr_intval(e) < 0)
+	{
+		as -> skipcond = 1;
+		as -> skipcount = 1;
+	}
+}
+
+PARSEFUNC(pseudo_parse_iflt)
+{
+	lw_expr_t e;
+	
+	l -> len = 0;
+	
+	if (as -> skipcond && !(as -> skipmacro))
+	{
+		as -> skipcount++;
+		skip_operand(p);
+		return;
+	}
+
+	e = lwasm_parse_cond(as, p);
+	if (e && lw_expr_intval(e) >= 0)
+	{
+		as -> skipcond = 1;
+		as -> skipcount = 1;
+	}
+}
+
+PARSEFUNC(pseudo_parse_ifle)
+{
+	lw_expr_t e;
+
+	l -> len = 0;
+	
+	if (as -> skipcond && !(as -> skipmacro))
+	{
+		as -> skipcount++;
+		skip_operand(p);
+		return;
+	}
+
+	e = lwasm_parse_cond(as, p);
+	if (e && lw_expr_intval(e) > 0)
+	{
+		as -> skipcond = 1;
+		as -> skipcount = 1;
+	}
+}
+
+PARSEFUNC(pseudo_parse_endc)
+{
+	l -> len = 0;
+	if (as -> skipcond && !(as -> skipmacro))
+	{
+		as -> skipcount--;
+		if (as -> skipcount <= 0)
+			as -> skipcond = 0;
+	}
+}
+
+PARSEFUNC(pseudo_parse_else)
+{
+	l -> len = 0;
+	
+	if (as -> skipmacro)
+		return;
+	
+	if (as -> skipcond)
+	{
+		if (as -> skipcount == 1)
+		{
+			as -> skipcount = 0;
+			as -> skipcond = 0;
+		}
+		return;
+	}
+	as -> skipcond = 1;
+	as -> skipcount = 1;
+}
+
+PARSEFUNC(pseudo_parse_ifdef)
+{
+	char *sym;
+	int i;
+	struct symtabe *s;
+	
+	l -> len = 0;
+	
+	if (as -> skipcond && !(as -> skipmacro))
+	{
+		as -> skipcount++;
+		skip_operand(p);
+		return;
+	}
+
+	for (i = 0; (*p)[i] && !isspace((*p)[i]); i++)
+		/* do nothing */ ;
+	
+	sym = lw_strndup(*p, i);
+	
+	s = lookup_symbol(as, l, sym);
+	
+	lw_free(sym);
+	
+	if (!s)
+	{
+		as -> skipcond = 1;
+		as -> skipcount = 1;
+	}
+}
+
+PARSEFUNC(pseudo_parse_ifndef)
+{
+	char *sym;
+	int i;
+	struct symtabe *s;
+	
+	l -> len = 0;
+	
+	if (as -> skipcond && !(as -> skipmacro))
+	{
+		as -> skipcount++;
+		skip_operand(p);
+		return;
+	}
+
+	for (i = 0; (*p)[i] && !isspace((*p)[i]); i++)
+		/* do nothing */ ;
+	
+	sym = lw_strndup(*p, i);
+	(*p) += i;
+	
+	s = lookup_symbol(as, l, sym);
+	
+	lw_free(sym);
+
+	if (s)
+	{
+		as -> skipcond = 1;
+		as -> skipcount = 1;
+	}
+}
+
+PARSEFUNC(pseudo_parse_error)
+{
+	lwasm_register_error(as, l, "User error: %s", *p);
+	skip_operand(p);
+}
+
+PARSEFUNC(pseudo_parse_warning)
+{
+	lwasm_register_warning(as, l, "User warning: %s", *p);
+	skip_operand(p);
+}
+
+PARSEFUNC(pseudo_parse_includebin)
+{
+	char *fn, *p2;
+	int delim = 0;
+	FILE *fp;
+	long flen;
+	
+	if (!**p)
+	{
+		lwasm_register_error(as, l, "Missing filename");
+		return;
+	}
+	
+	if (**p == '"' || **p == '\'')
+	{
+		delim = **p;
+		(*p)++;
+
+		for (p2 = *p; *p2 && *p2 != delim; p2++)
+			/* do nothing */ ;
+	}
+	else
+	{
+		for (p2 = *p; *p2 && !isspace(*p2); p2++)
+			/* do nothing */ ;
+	}
+	fn = lw_strndup(*p, p2 - *p);
+	
+	if (delim && **p)
+		(*p)++;
+	
+	fp = input_open_standalone(as, fn);
+	if (!fp)
+	{
+		lwasm_register_error(as, l, "Cannot open file");
+		lw_free(fn);
+		return;
+	}
+	
+	l -> lstr = fn;
+	
+	fseek(fp, 0, SEEK_END);
+	flen = ftell(fp);
+	fclose(fp);
+
+	l -> len = flen;
+}
+
+EMITFUNC(pseudo_emit_includebin)
+{
+	FILE *fp;
+	int c;
+	
+	fp = input_open_standalone(as, l -> lstr);
+	if (!fp)
+	{
+		lwasm_register_error(as, l, "Cannot open file (emit)!");
+		return;
+	}
+	
+	for (;;)
+	{
+		c = fgetc(fp);
+		if (c == EOF)
+		{
+			fclose(fp);
+			return;
+		}
+		lwasm_emit(l, c);
+	}
+}
+
+PARSEFUNC(pseudo_parse_include)
+{
+	char *fn, *p2;
+	char *p3;
+	int delim = 0;
+	
+	if (!**p)
+	{
+		lwasm_register_error(as, l, "Missing filename");
+		return;
+	}
+	
+	if (**p == '"' || **p == '\'')
+	{
+		delim = **p;
+		(*p)++;
+
+		for (p2 = *p; *p2 && *p2 != delim; p2++)
+			/* do nothing */ ;
+	}
+	else
+	{
+		for (p2 = *p; *p2 && !isspace(*p2); p2++)
+			/* do nothing */ ;
+	}
+	fn = lw_strndup(*p, p2 - *p);
+	(*p) = p2;
+	if (delim && **p)
+		(*p)++;
+	
+	0 == asprintf(&p3, "include:%s", fn);
+	input_open(as, p3);
+	lw_free(p3);
+
+	l -> len = 0;
+}
+
+PARSEFUNC(pseudo_parse_align)
+{
+	lw_expr_t e;
+	if (!**p)
+	{
+		lwasm_register_error(as, l, "Bad operand");
+		return;
+	}
+	
+	e = lwasm_parse_expr(as, p);
+	
+	if (!e)
+	{
+		lwasm_register_error(as, l, "Bad operand");
+		return;
+	}
+	
+	lwasm_save_expr(l, 0, e);
+	
+	if (**p == ',')
+	{
+		e = lwasm_parse_expr(as, p);
+	}
+	else
+	{
+		e = lw_expr_build(lw_expr_type_int, 0);
+	}
+	if (!e)
+	{
+		lwasm_register_error(as, l, "Bad padding");
+		return;
+	}
+	
+	lwasm_save_expr(l, 1, e);
+}
+
+RESOLVEFUNC(pseudo_resolve_align)
+{
+	lw_expr_t e;
+	int align;
+
+	e = lwasm_fetch_expr(l, 0);
+	
+	if (lw_expr_istype(e, lw_expr_type_int))
+	{
+		align = lw_expr_intval(e);
+		if (align < 1)
+		{
+			lwasm_register_error(as, l, "Invalid alignment");
+			return;
+		}
+	}
+	
+	if (lw_expr_istype(l -> addr, lw_expr_type_int))
+	{
+		int a;
+		a = lw_expr_intval(l -> addr);
+		if (a % align == 0)
+		{
+			l -> len = 0;
+			return;
+		}
+		l -> len = align - (a % align);
+		return;
+	}
+}
+
+EMITFUNC(pseudo_emit_align)
+{
+	lw_expr_t e;
+	int i;
+	
+	e = lwasm_fetch_expr(l, 1);
+	for (i = 0; i < l -> len; i++)
+	{
+		lwasm_emitexpr(l, e, 1);
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lwasm/rules.make	Wed Jan 19 22:27:17 2011 -0700
@@ -0,0 +1,10 @@
+dirname := $(dir $(lastword $(MAKEFILE_LIST)))
+
+lwasm_srcs_local :=  debug.c input.c insn_bitbit.c insn_gen.c insn_indexed.c \
+	insn_inh.c insn_logicmem.c insn_rel.c insn_rlist.c insn_rtor.c insn_tfm.c \
+	instab.c list.c lwasm.c macro.c main.c os9.c output.c pass1.c pass2.c \
+	pass3.c pass4.c pass5.c pass6.c pass7.c pragma.c pseudo.c section.c \
+	struct.c symbol.c
+
+lwasm_srcs := $(lwasm_srcs) $(addprefix $(dirname),$(lwasm_srcs_local))
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lwasm/section.c	Wed Jan 19 22:27:17 2011 -0700
@@ -0,0 +1,351 @@
+/*
+section.c
+
+Copyright © 2010 William Astle
+
+This file is part of LWTOOLS.
+
+LWTOOLS 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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <string.h>
+
+#include <lw_string.h>
+#include <lw_alloc.h>
+
+#include "lwasm.h"
+#include "instab.h"
+
+PARSEFUNC(pseudo_parse_section)
+{
+	char *p2;
+	char *sn;
+	char *opts = NULL;
+	sectiontab_t *s;
+
+	if (as -> output_format != OUTPUT_OBJ)
+	{
+		lwasm_register_error(as, l, "Cannot use sections unless using the object target");
+		return;
+	}
+	
+	if (!**p)
+	{
+		lwasm_register_error(as, l, "Need section name");
+		return;
+	}
+
+	l -> len = 0;
+
+	if (as -> csect)
+	{
+		lw_expr_destroy(as -> csect -> offset);
+		as -> csect -> offset = lw_expr_copy(l -> addr);
+		as -> csect = NULL;
+	}
+	
+	for (p2 = *p; *p2 && *p2 != ',' && !isspace(*p2); p2++)
+		/* do nothing */ ;
+	
+	sn = lw_strndup(*p, p2 - *p);
+	*p = p2;
+	
+	if (**p == ',')
+	{
+		// have opts
+		(*p)++;
+		
+		for (p2 = *p; *p2 && !isspace(*p2); p2++)
+			/* do nothing */ ;
+		
+		opts = lw_strndup(*p, p2 - *p);
+		*p = p2;
+	}
+
+	for (s = as -> sections; s; s = s -> next)
+	{
+		if (!strcmp(s -> name, sn))
+			break;
+	}
+	if (s && opts)
+	{
+		lwasm_register_warning(as, l, "Section flags can only be specified the first time; ignoring duplicate definition");
+	}
+	if (!s)
+	{
+		// create section data structure
+		s = lw_alloc(sizeof(sectiontab_t));
+		s -> oblen = 0;
+		s -> obsize = 0;
+		s -> obytes = NULL;
+		s -> name = lw_strdup(sn);
+		s -> offset = lw_expr_build(lw_expr_type_special, lwasm_expr_secbase, s);
+		s -> flags = section_flag_none;
+		s -> reloctab = NULL;
+		if (!strcasecmp(sn, "bss") || !strcasecmp(sn, ".bss"))
+		{
+			s -> flags |= section_flag_bss;
+		}
+		// parse options
+		if (opts)
+		{
+			// only one option ("bss" or "!bss")
+			if (!strcasecmp(opts, "bss"))
+			{
+				s -> flags |= section_flag_bss;
+			}
+			else if (!strcasecmp(opts, "!bss"))
+			{
+				s -> flags &= ~section_flag_bss;
+			}
+			else
+			{
+				lwasm_register_error(as, l, "Unrecognized section flag");
+				lw_free(sn);
+				lw_free(opts);
+				lw_free(s -> name);
+				lw_expr_destroy(s -> offset);
+				lw_free(s);
+				return;
+			}
+		}
+		s -> next = as -> sections;
+		as -> sections = s;
+	}
+	
+	lw_expr_destroy(l -> addr);
+	l -> addr = lw_expr_copy(s -> offset);
+	
+	as -> csect = s;
+	as -> context = lwasm_next_context(as);
+
+	l -> len = 0;
+	
+	lw_free(opts);
+	lw_free(sn);
+}
+
+PARSEFUNC(pseudo_parse_endsection)
+{
+	if (as -> output_format != OUTPUT_OBJ)
+	{
+		lwasm_register_error(as, l, "Cannot use sections unless using the object target");
+		return;
+	}
+
+	l -> len = 0;
+
+	if (!(as -> csect))
+	{
+		lwasm_register_error(as, l, "ENDSECTION without SECTION");
+		return;
+	}
+
+	// save offset in case another instance of the section appears	
+	lw_expr_destroy(as -> csect -> offset);
+	as -> csect -> offset = l -> addr;
+	
+	// reset address to 0
+	l -> addr = lw_expr_build(lw_expr_type_int, 0);
+	as -> csect = NULL;
+	
+	// end of section is a context break
+	as -> context = lwasm_next_context(as);
+
+	skip_operand(p);
+}
+
+PARSEFUNC(pseudo_parse_export)
+{
+	int after = 0;
+	char *sym = NULL;
+	exportlist_t *e;
+	
+	if (as -> output_format != OUTPUT_OBJ)
+	{
+		lwasm_register_error(as, l, "EXPORT only supported for object target");
+		return;
+	}
+
+	l -> len = 0;
+	
+	if (l -> sym)
+	{
+		sym = lw_strdup(l -> sym);
+		l -> symset = 1;
+	}
+	
+	if (l -> sym)
+	{
+		skip_operand(p);
+	}
+
+again:
+	if (after || !sym)
+	{
+		char *p2;
+		
+		after = 1;
+		for (p2 = *p; *p2 && *p2 != ',' && !isspace(*p2); p2++)
+			/* do nothing */ ;
+		
+		sym = lw_strndup(*p, p2 - *p);
+		*p = p2;
+	}
+	if (!sym)
+	{
+		lwasm_register_error(as, l, "No symbol for EXPORT");
+		return;
+	}
+	
+	// add the symbol to the "export" list (which will be resolved
+	// after the parse pass is complete
+	e = lw_alloc(sizeof(exportlist_t));
+	e -> next = as -> exportlist;
+	e -> symbol = lw_strdup(sym);
+	e -> line = l;
+	e -> se = NULL;
+	as -> exportlist = e;
+	lw_free(sym);
+	
+	if (after && **p == ',')
+	{
+		(*p)++;
+		for (; **p && isspace(**p); (*p)++)
+			/* do nothing */ ;
+		goto again;
+	}
+}
+
+PARSEFUNC(pseudo_parse_extern)
+{
+	int after = 0;
+	char *sym = NULL;
+	importlist_t *e;
+	
+	if (as -> output_format != OUTPUT_OBJ)
+	{
+		lwasm_register_error(as, l, "IMPORT only supported for object target");
+		return;
+	}
+	
+	l -> len = 0;
+	
+	if (l -> sym)
+	{
+		sym = lw_strdup(l -> sym);
+		l -> symset = 1;
+	}
+	
+	if (l -> sym)
+	{
+		skip_operand(p);
+	}
+
+again:
+	if (after || !sym)
+	{
+		char *p2;
+		
+		after = 1;
+		for (p2 = *p; *p2 && *p2 != ',' && !isspace(*p2); p2++)
+			/* do nothing */ ;
+		
+		sym = lw_strndup(*p, p2 - *p);
+		*p = p2;
+	}
+	if (!sym)
+	{
+		lwasm_register_error(as, l, "No symbol for IMPORT");
+		return;
+	}
+	
+	// add the symbol to the "export" list (which will be resolved
+	// after the parse pass is complete
+	e = lw_alloc(sizeof(importlist_t));
+	e -> next = as -> importlist;
+	e -> symbol = lw_strdup(sym);
+	as -> importlist = e;
+	lw_free(sym);
+	
+	if (after && **p == ',')
+	{
+		(*p)++;
+		for (; **p && isspace(**p); (*p)++)
+			/* do nothing */ ;
+		goto again;
+	}
+}
+
+PARSEFUNC(pseudo_parse_extdep)
+{
+	int after = 0;
+	char *sym = NULL;
+	importlist_t *e;
+	
+	if (as -> output_format != OUTPUT_OBJ)
+	{
+		lwasm_register_error(as, l, "EXTDEP only supported for object target");
+		return;
+	}
+	
+	if (!as -> csect)
+	{
+		lwasm_register_error(as, l, "EXTDEP must be within a section");
+		return;
+	}
+	
+	l -> len = 0;
+	
+	if (l -> sym)
+		sym = lw_strdup(l -> sym);
+	
+	if (l -> sym)
+	{
+		skip_operand(p);
+	}
+
+again:
+	if (after || !sym)
+	{
+		char *p2;
+		
+		after = 1;
+		for (p2 = *p; *p2 && *p2 != ',' && !isspace(*p2); p2++)
+			/* do nothing */ ;
+		
+		sym = lw_strndup(*p, p2 - *p);
+	}
+	if (!sym)
+	{
+		lwasm_register_error(as, l, "No symbol for EXTDEP");
+		return;
+	}
+	
+	// create a zero-width dependency
+	{
+		lw_expr_t e;
+		e = lw_expr_build(lw_expr_type_int, 0);
+		lwasm_emitexpr(l, e, 0);
+		lw_expr_destroy(e);
+	}
+	
+	if (after && **p == ',')
+	{
+		(*p)++;
+		for (; **p && isspace(**p); (*p)++)
+			/* do nothing */ ;
+		goto again;
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lwasm/struct.c	Wed Jan 19 22:27:17 2011 -0700
@@ -0,0 +1,216 @@
+/*
+struct.c
+Copyright © 2010 William Astle
+
+This file is part of LWASM.
+
+LWASM 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 3 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, see <http://www.gnu.org/licenses/>.
+
+Contains stuff associated with structure processing
+*/
+
+#include <string.h>
+
+#include <lw_alloc.h>
+#include <lw_string.h>
+
+#include "lwasm.h"
+#include "instab.h"
+
+PARSEFUNC(pseudo_parse_struct)
+{
+	structtab_t *s;
+	
+	if (as -> instruct)
+	{
+		lwasm_register_error(as, l, "Attempt to define a structure inside a structure");
+		return;
+	}
+	
+	if (l -> sym == NULL)
+	{
+		lwasm_register_error(as, l, "Structure definition with no effect - no symbol");
+		return;
+	}
+	
+	for (s = as -> structs; s; s = s -> next)
+	{
+		if (!strcmp(s -> name, l -> sym))	
+			break;
+	}
+	
+	if (s)
+	{
+		lwasm_register_error(as, l, "Duplicate structure definition");
+		return;
+	}
+	
+	as -> instruct = 1;
+	
+	s = lw_alloc(sizeof(structtab_t));
+	s -> name = lw_strdup(l -> sym);
+	s -> next = as -> structs;
+	s -> fields = NULL;
+	s -> size = 0;
+	as -> structs = s;
+	as -> cstruct = s;
+	
+	skip_operand(p);
+	
+	l -> len = 0;
+	l -> symset = 1;
+}
+
+void pseudo_endstruct_aux(asmstate_t *as, line_t *l, structtab_field_t *e, const char *prefix, int *coff)
+{
+	char *symname = NULL;
+	lw_expr_t te1, te2;
+	
+	while (e)
+	{
+		if (e -> name)
+			0 == asprintf(&symname, "%s.%s", prefix, e -> name);
+		else
+			0 == asprintf(&symname, "%s.____%d", prefix, *coff);
+		
+		// register the symbol
+		te1 = lw_expr_build(lw_expr_type_int, *coff);
+		te2 = lw_expr_build(lw_expr_type_oper, lw_expr_oper_plus, te1, l -> addr);
+		register_symbol(as, l, symname, te2, symbol_flag_nocheck);
+		lw_expr_destroy(te2);
+		lw_expr_destroy(te1);
+		
+		if (e -> substruct)
+		{
+			char *t;
+			0 == asprintf(&t, "sizeof{%s}", symname);
+			te1 = lw_expr_build(lw_expr_type_int, e -> substruct -> size);
+			register_symbol(as, l, t, te1, symbol_flag_nocheck);
+			lw_expr_destroy(te1);
+			lw_free(t);
+			pseudo_endstruct_aux(as, l, e -> substruct -> fields, symname, coff);
+		}
+		else
+		{
+			*coff += e -> size;
+		}
+		e = e -> next;
+	}
+}
+
+
+PARSEFUNC(pseudo_parse_endstruct)
+{
+	char *t;
+	int coff = 0;
+	lw_expr_t te;
+	
+	if (as -> instruct == 0)
+	{
+		lwasm_register_warning(as, l, "endstruct without struct");
+		skip_operand(p);
+		return;
+	}
+	
+	0 == asprintf(&t, "sizeof{%s}", as -> cstruct -> name);
+	te = lw_expr_build(lw_expr_type_int, as -> cstruct -> size);
+	register_symbol(as, l, t, te, symbol_flag_nocheck);
+	lw_expr_destroy(te);
+	lw_free(t);
+	
+	l -> soff = as -> cstruct -> size;
+	as -> instruct = 0;
+	
+	skip_operand(p);
+	
+	pseudo_endstruct_aux(as, l, as -> cstruct -> fields, as -> cstruct -> name, &coff);
+	
+	l -> len = 0;
+}
+
+void register_struct_entry(asmstate_t *as, line_t *l, int size, structtab_t *ss)
+{
+	structtab_field_t *e, *e2;
+	
+	l -> soff = as -> cstruct -> size;
+	e = lw_alloc(sizeof(structtab_field_t));
+	e -> next = NULL;
+	e -> size = size;
+	if (l -> sym)
+		e -> name = lw_strdup(l -> sym);
+	else
+		e -> name = NULL;
+	e -> substruct = ss;
+	if (as -> cstruct -> fields)
+	{
+		for (e2 = as -> cstruct -> fields; e2 -> next; e2 = e2 -> next)
+			/* do nothing */ ;
+		e2 -> next = e;
+	}
+	else
+	{
+		as -> cstruct -> fields = e;
+	}
+	as -> cstruct -> size += size;
+}
+
+int expand_struct(asmstate_t *as, line_t *l, char **p, char *opc)
+{
+	structtab_t *s;
+	char *t;
+	lw_expr_t te;
+	int addr = 0;
+
+	debug_message(as, 200, "Checking for structure expansion: %s", opc);
+
+	for (s = as -> structs; s; s = s -> next)
+	{
+		if (!strcmp(opc, s -> name))
+			break;
+	}
+	
+	if (!s)
+		return -1;
+	
+	debug_message(as, 10, "Expanding structure: %s", opc);
+	
+	if (!(l -> sym))
+	{
+		lwasm_register_error(as, l, "Cannot declare a structure without a symbol name.");
+		return;
+	}
+	
+	l -> len = s -> size;
+
+	if (as -> instruct)
+		0 == asprintf(&t, "sizeof(%s.%s}", as -> cstruct -> name, l -> sym);
+	else
+		0 == asprintf(&t, "sizeof{%s}", l -> sym);
+	te = lw_expr_build(lw_expr_type_int, s -> size);
+	register_symbol(as, l, t, te, symbol_flag_nocheck);
+	lw_expr_destroy(te);
+	lw_free(t);
+	
+	if (as -> instruct)
+		0 == asprintf(&t, "%s.%s", as -> cstruct -> name, l -> sym);
+	else
+		t = lw_strdup(l -> sym);
+	pseudo_endstruct_aux(as, l, s -> fields, t, &addr);
+	lw_free(t);
+	l -> symset = 1;
+	if (as -> instruct)
+		register_struct_entry(as, l, s -> size, s);
+	return 0;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lwasm/symbol.c	Wed Jan 19 22:27:17 2011 -0700
@@ -0,0 +1,326 @@
+/*
+symbol.c
+
+Copyright © 2010 William Astle
+
+This file is part of LWTOOLS.
+
+LWTOOLS 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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <lw_alloc.h>
+#include <lw_expr.h>
+#include <lw_string.h>
+
+#include "lwasm.h"
+
+struct symtabe *symbol_findprev(asmstate_t *as, struct symtabe *se)
+{
+	struct symtabe *se1, *se2;
+	int i;
+	
+	for (se2 = NULL, se1 = as -> symtab.head; se1; se1 = se1 -> next)
+	{
+		debug_message(as, 200, "Sorting; looking at symbol %s (%p) for %s", se1 -> symbol, se1, se -> symbol);
+		/* compare se with se1 */
+		i = strcasecmp(se -> symbol, se1 -> symbol);
+		
+		/* if the symbol sorts before se1, we just need to return */
+		if (i < 0)
+			return se2;
+		
+		if (i == 0)
+		{
+			/* symbol name matches; compare other things */
+			
+			/*if next version is greater than this one, return */
+			if (se -> version > se1 -> version)
+				return se2;
+			/* if next context is great than this one, return */ 
+			if (se -> context > se1 -> context)
+				return se2;
+			
+			/* if section name is greater, return */
+			/* if se has no section but se1 does, we go first */
+			if (se -> section == NULL && se1 -> section != NULL)
+				return se2;
+			if (se -> section != NULL && se -> section != NULL)
+			{
+				/* compare section names and if se < se1, return */
+				i = strcasecmp(se -> section -> name, se1 -> section -> name);
+				if (i < 0)
+					return se2;
+			}
+		}
+		
+		se2 = se1;
+	}
+	return se2;
+}
+
+struct symtabe *register_symbol(asmstate_t *as, line_t *cl, char *sym, lw_expr_t val, int flags)
+{
+	struct symtabe *se;
+	struct symtabe *sprev;
+	int islocal = 0;
+	int context = -1;
+	int version = -1;
+	char *cp;
+
+	debug_message(as, 200, "Register symbol %s (%02X), %s", sym, flags, lw_expr_print(val));
+
+	if (!(flags & symbol_flag_nocheck))
+	{
+		if (!sym || !*sym)
+		{
+			lwasm_register_error(as, cl, "Bad symbol (%s)", sym);
+			return NULL;
+		}
+		if (*sym < 0x80 && (!strchr(SSYMCHARS, *sym) && !strchr(sym + 1, '$') && !strchr(sym + 1, '@') && !strchr(sym + 1, '?')))
+		{
+			lwasm_register_error(as, cl, "Bad symbol (%s)", sym);
+			return NULL;
+		}
+
+		if ((*sym == '$' || *sym == '@') && (sym[1] >= '0' && sym[1] <= '9'))
+		{
+			lwasm_register_error(as, cl, "Bad symbol (%s)", sym);
+			return NULL;
+		}
+	}
+
+	for (cp = sym; *cp; cp++)
+	{
+		if (*cp == '@' || *cp == '?')
+			islocal = 1;
+		if (*cp == '$' && !(CURPRAGMA(cl, PRAGMA_DOLLARNOTLOCAL)))
+			islocal = 1;
+		
+		// bad symbol
+		if (!(flags & symbol_flag_nocheck) && *cp < 0x80 && !strchr(SYMCHARS, *cp))
+		{
+			lwasm_register_error(as, cl, "Bad symbol (%s)", sym);
+			return NULL;
+		}
+	}
+
+	if (islocal)
+		context = cl -> context;
+	
+	// first, look up symbol to see if it is already defined
+	for (se = as -> symtab.head; se; se = se -> next)
+	{
+		debug_message(as, 300, "Symbol add lookup: %p, %p", se, se -> next);
+		if (!strcmp(sym, se -> symbol))
+		{
+			if (se -> context != context)
+				continue;
+			if ((flags & symbol_flag_set) && (se -> flags & symbol_flag_set))
+			{
+				if (version < se -> version)
+					version = se -> version;
+					continue;
+			}
+			break;
+		}
+	}
+
+	if (se)
+	{
+		// multiply defined symbol
+		lwasm_register_error(as, cl, "Multiply defined symbol (%s)", sym);
+		return NULL;
+	}
+
+	if (flags & symbol_flag_set)
+	{
+		version++;
+	}
+	
+	// symplify the symbol expression - replaces "SET" symbols with
+	// symbol table entries
+	lwasm_reduce_expr(as, val);
+
+	se = lw_alloc(sizeof(struct symtabe));
+	se -> context = context;
+	se -> version = version;
+	se -> flags = flags;
+	se -> value = lw_expr_copy(val);
+	se -> symbol = lw_strdup(sym);
+	se -> section = cl -> csect;
+	sprev = symbol_findprev(as, se);
+	if (!sprev)
+	{
+		debug_message(as, 200, "Adding symbol at head of symbol table");
+		se -> next = as -> symtab.head;
+		as -> symtab.head = se;
+	}
+	else
+	{
+		debug_message(as, 200, "Adding symbol in middle of symbol table");
+		se -> next = sprev -> next;
+		sprev -> next = se;
+	}
+	return se;
+}
+
+// for "SET" symbols, always returns the LAST definition of the
+// symbol. This works because the lwasm_reduce_expr() call in 
+// register_symbol will ensure there are no lingering "var" references
+// to the set symbol anywhere in the symbol table; they will all be
+// converted to direct references
+// NOTE: this means that for a forward reference to a SET symbol,
+// the LAST definition will be the one used.
+// This arrangement also ensures that any reference to the symbol
+// itself inside a "set" definition will refer to the previous version
+// of the symbol.
+struct symtabe * lookup_symbol(asmstate_t *as, line_t *cl, char *sym)
+{
+	int local = 0;
+	struct symtabe *s, *s2;
+
+	// check if this is a local symbol
+	if (strchr(sym, '@') || strchr(sym, '?'))
+		local = 1;
+	
+	if (cl && !CURPRAGMA(cl, PRAGMA_DOLLARNOTLOCAL) && strchr(sym, '$'))
+		local = 1;
+	if (!cl && !(as -> pragmas & PRAGMA_DOLLARNOTLOCAL) && strchr(sym, '$'))
+		local = 1;
+	
+	// cannot look up local symbol in global context!!!!!
+	if (!cl && local)
+		return NULL;
+	
+	for (s = as -> symtab.head, s2 = NULL; s; s = s -> next)
+	{
+		if (!strcmp(sym, s -> symbol))
+		{
+			if (local && s -> context != cl -> context)
+				continue;
+			
+			if (s -> flags & symbol_flag_set)
+			{
+				// look for highest version of symbol
+				if (s -> version > s2 -> version)
+					s2 = s;
+				continue;
+			}
+			break;
+		}
+	}
+	if (!s && s2)
+		s = s2;
+	
+	return s;
+}
+
+struct listinfo
+{
+	sectiontab_t *sect;
+	asmstate_t *as;
+	int complex;
+};
+
+int list_symbols_test(lw_expr_t e, void *p)
+{
+	struct listinfo *li = p;
+	
+	if (li -> complex)
+		return 0;
+	
+	if (lw_expr_istype(e, lw_expr_type_special))
+	{
+		if (lw_expr_specint(e) == lwasm_expr_secbase)
+		{
+			if (li -> sect)
+			{
+				li -> complex = 1;
+			}
+			else
+			{
+				li -> sect = lw_expr_specptr(e);
+			}
+		}
+	}
+	return 0;
+}
+
+void list_symbols(asmstate_t *as, FILE *of)
+{
+	struct symtabe *s;
+	lw_expr_t te;
+	struct listinfo li;
+
+	li.as = as;
+		
+	fprintf(of, "\nSymbol Table:\n");
+	
+	for (s = as -> symtab.head; s; s = s -> next)
+	{
+		lwasm_reduce_expr(as, s -> value);
+		fputc('[', of);
+		if (s -> flags & symbol_flag_set)
+			fputc('S', of);
+		else
+			fputc(' ', of);
+		if (as -> output_format == OUTPUT_OBJ)
+		{
+			if (lw_expr_istype(s -> value, lw_expr_type_int))
+				fputc('c', of);
+			else
+				fputc('s', of);
+		}
+		if (s -> context < 0)
+			fputc('G', of);
+		else
+			fputc('L', of);
+
+		fputc(']', of);
+		fputc(' ', of);
+		fprintf(of, "%-32s ", s -> symbol);
+		
+		te = lw_expr_copy(s -> value);
+		li.complex = 0;
+		li.sect = NULL;
+		lw_expr_testterms(te, list_symbols_test, &li);
+		if (li.sect)
+		{
+			as -> exportcheck = 1;
+			as -> csect = li.sect;
+			lwasm_reduce_expr(as, te);
+			as -> exportcheck = 0;
+		}
+		
+		if (lw_expr_istype(te, lw_expr_type_int))
+		{
+			fprintf(of, "%04X", lw_expr_intval(te));
+			if (li.sect)
+			{
+				fprintf(of, " (%s)", li.sect -> name);
+			}
+			fprintf(of, "\n");
+		}
+		else
+		{
+			fprintf(of, "<<incomplete>>\n");
+//			fprintf(of, "%s\n", lw_expr_print(s -> value));
+		}
+		lw_expr_destroy(te);
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lwlib/lw_alloc.c	Wed Jan 19 22:27:17 2011 -0700
@@ -0,0 +1,66 @@
+/*
+lw_alloc.c
+
+Copyright © 2010 William Astle
+
+This file is part of LWTOOLS.
+
+LWTOOLS 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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdlib.h>
+
+#define ___lw_alloc_c_seen___
+#include "lw_alloc.h"
+
+void lw_free(void *P)
+{
+	if (P)
+		free(P);
+}
+
+void *lw_alloc(int size)
+{
+	void *r;
+	
+	r = malloc(size);
+	if (!r)
+	{
+		abort();
+	}
+	return r;
+}
+
+void *lw_realloc(void *P, int S)
+{
+	void *r;
+
+	if (!P)
+	{
+		return lw_alloc(S);
+	}
+	
+	if (!S)
+	{
+		lw_free(P);
+		return NULL;
+	}
+	
+	r = realloc(P, S);
+	if (!r)
+	{
+		abort();
+	}
+	return r;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lwlib/lw_alloc.h	Wed Jan 19 22:27:17 2011 -0700
@@ -0,0 +1,36 @@
+/*
+lw_alloc.h
+
+Copyright © 2010 William Astle
+
+This file is part of LWTOOLS.
+
+LWTOOLS 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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef ___lw_alloc_h_seen___
+#define ___lw_alloc_h_seen___
+
+
+#ifdef ___lw_alloc_c_seen___
+
+#else /* def ___lw_alloc_c_seen___ */
+
+extern void lw_free(void *P);
+extern void *lw_alloc(int S);
+extern void *lw_realloc(void *P, int S);
+
+#endif /* def ___lw_alloc_c_seen___ */
+
+#endif /* ___lw_alloc_h_seen___ */
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lwlib/lw_error.c	Wed Jan 19 22:27:17 2011 -0700
@@ -0,0 +1,46 @@
+/*
+lw_error.c
+
+Copyright © 2010 William Astle
+
+This file is part of LWTOOLS.
+
+LWTOOLS 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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#define ___lw_error_c_seen___
+#include "lw_error.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+
+static void (*lw_error_func)(const char *fmt, ...) = NULL;
+
+void lw_error(const char *fmt, ...)
+{
+	va_list args;
+	va_start(args, fmt);
+	if (lw_error_func)
+		(*lw_error_func)(fmt, args);
+	else
+		vfprintf(stderr, fmt, args);
+	va_end(args);
+	exit(1);
+}
+
+void lw_error_setfunc(void (*f)(const char *fmt, ...))
+{
+	lw_error_func = f;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lwlib/lw_error.h	Wed Jan 19 22:27:17 2011 -0700
@@ -0,0 +1,34 @@
+/*
+lw_error.h
+
+Copyright © 2010 William Astle
+
+This file is part of LWTOOLS.
+
+LWTOOLS 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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef ___lw_error_h_seen___
+#define ___lw_error_h_seen___
+
+
+#ifdef ___lw_error_c_seen___
+
+#else /* def ___lw_error_c_seen___ */
+
+extern void lw_error(const char *fmt, ...);
+extern void lw_error_setfunc(void (*f)(const char *fmt, ...));
+#endif /* def ___lw_error_c_seen___ */
+
+#endif /* ___lw_error_h_seen___ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lwlib/lw_expr.c	Wed Jan 19 22:27:17 2011 -0700
@@ -0,0 +1,1273 @@
+/*
+lwexpr.c
+
+Copyright © 2010 William Astle
+
+This file is part of LWTOOLS.
+
+LWTOOLS 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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+
+#define ___lw_expr_c_seen___
+#include "lw_alloc.h"
+#include "lw_expr.h"
+#include "lw_error.h"
+#include "lw_string.h"
+
+static lw_expr_fn_t *evaluate_special = NULL;
+static lw_expr_fn2_t *evaluate_var = NULL;
+static lw_expr_fn3_t *parse_term = NULL;
+
+/* Q&D to break out of infinite recursion */
+static int level = 0;
+static int bailing = 0;
+
+int lw_expr_istype(lw_expr_t e, int t)
+{
+	if (e -> type == t)
+		return 1;
+	return 0;
+}
+
+int lw_expr_intval(lw_expr_t e)
+{
+	if (e -> type == lw_expr_type_int)
+		return e -> value;
+	return -1;
+}
+
+int lw_expr_whichop(lw_expr_t e)
+{
+	if (e -> type == lw_expr_type_oper)
+		return e -> value;
+	return -1;
+}
+
+int lw_expr_specint(lw_expr_t e)
+{
+	if (e -> type == lw_expr_type_special)
+		return e -> value;
+	return -1;
+}
+
+void lw_expr_set_term_parser(lw_expr_fn3_t *fn)
+{
+	parse_term = fn;
+}
+
+void lw_expr_set_special_handler(lw_expr_fn_t *fn)
+{
+	evaluate_special = fn;
+}
+
+void lw_expr_set_var_handler(lw_expr_fn2_t *fn)
+{
+	evaluate_var = fn;
+}
+
+lw_expr_t lw_expr_create(void)
+{
+	lw_expr_t r;
+	
+	r = lw_alloc(sizeof(struct lw_expr_priv));
+	r -> operands = NULL;
+
+	return r;
+}
+
+void lw_expr_destroy(lw_expr_t E)
+{
+	struct lw_expr_opers *o;
+	if (!E)
+		return;
+	while (E -> operands)
+	{
+		o = E -> operands;
+		E -> operands = o -> next;
+		lw_expr_destroy(o -> p);
+		lw_free(o);
+	}
+	if (E -> type == lw_expr_type_var)
+		lw_free(E -> value2);
+	lw_free(E);
+}
+
+/* actually duplicates the entire expression */
+void lw_expr_add_operand(lw_expr_t E, lw_expr_t O);
+lw_expr_t lw_expr_copy(lw_expr_t E)
+{
+	lw_expr_t r, t;
+	struct lw_expr_opers *o;
+	
+	r = lw_alloc(sizeof(struct lw_expr_priv));
+	*r = *E;
+	r -> operands = NULL;
+	
+	if (E -> type == lw_expr_type_var)
+		r -> value2 = lw_strdup(E -> value2);
+	for (o = E -> operands; o; o = o -> next)
+	{
+		lw_expr_add_operand(r, o -> p);
+	}
+	
+	return r;
+}
+
+void lw_expr_add_operand(lw_expr_t E, lw_expr_t O)
+{
+	struct lw_expr_opers *o, *t;
+	
+	o = lw_alloc(sizeof(struct lw_expr_opers));
+	o -> p = lw_expr_copy(O);
+	o -> next = NULL;
+	for (t = E -> operands; t && t -> next; t = t -> next)
+		/* do nothing */ ;
+	
+	if (t)
+		t -> next = o;
+	else
+		E -> operands = o;
+}
+
+lw_expr_t lw_expr_build_aux(int exprtype, va_list args)
+{
+	lw_expr_t r;
+	int t;
+	void *p;
+	
+	lw_expr_t te1, te2;
+
+	r = lw_expr_create();
+
+	switch (exprtype)
+	{
+	case lw_expr_type_int:
+		t = va_arg(args, int);
+		r -> type = lw_expr_type_int;
+		r -> value = t;
+		break;
+
+	case lw_expr_type_var:
+		p = va_arg(args, char *);
+		r -> type = lw_expr_type_var;
+		r -> value2 = lw_strdup(p);
+		break;
+
+	case lw_expr_type_special:
+		t = va_arg(args, int);
+		p = va_arg(args, char *);
+		r -> type = lw_expr_type_special;
+		r -> value = t;
+		r -> value2 = p;
+		break;
+
+	case lw_expr_type_oper:
+		t = va_arg(args, int);
+		te1 = va_arg(args, lw_expr_t);
+		if (t != lw_expr_oper_com && t != lw_expr_oper_neg)
+			te2 = va_arg(args, lw_expr_t);
+		else
+			te2 = NULL;
+		
+		r -> type = lw_expr_type_oper;
+		r -> value = t;
+		lw_expr_add_operand(r, te1);
+		if (te2)
+			lw_expr_add_operand(r, te2);
+		break;
+	
+	default:
+		lw_error("Invalid expression type specified to lw_expr_build");
+	}
+	
+	return r;
+}
+
+lw_expr_t lw_expr_build(int exprtype, ...)
+{
+	va_list args;
+	lw_expr_t r;
+	
+	va_start(args, exprtype);
+	r = lw_expr_build_aux(exprtype, args);
+	va_end(args);
+	return r;
+}
+
+void lw_expr_print_aux(lw_expr_t E, char **obuf, int *buflen, int *bufloc)
+{
+	struct lw_expr_opers *o;
+	int c = 0;
+	char buf[256];
+
+	if (!E)
+	{
+		strcpy(buf, "(NULL)");
+		return;
+	}
+	for (o = E -> operands; o; o = o -> next)
+	{
+		c++;
+		lw_expr_print_aux(o -> p, obuf, buflen, bufloc);
+	}
+	
+	switch (E -> type)
+	{
+	case lw_expr_type_int:
+		if (E -> value < 0)
+			snprintf(buf, 256, "-%#x ", -(E -> value));
+		else
+			snprintf(buf, 256, "%#x ", E -> value);
+		break;
+	
+	case lw_expr_type_var:
+		snprintf(buf, 256, "V(%s) ", (char *)(E -> value2));
+		break;
+	
+	case lw_expr_type_special:
+		snprintf(buf, 256, "S(%d,%p) ", E -> value, E -> value2);
+		break;
+
+	case lw_expr_type_oper:
+		snprintf(buf, 256, "[%d]", c);
+		switch (E -> value)
+		{
+		case lw_expr_oper_plus:
+			strcat(buf, "+ ");
+			break;
+			
+		case lw_expr_oper_minus:
+			strcat(buf, "- ");
+			break;
+			
+		case lw_expr_oper_times:
+			strcat(buf, "* ");
+			break;
+			
+		case lw_expr_oper_divide:
+			strcat(buf, "/ ");
+			break;
+			
+		case lw_expr_oper_mod:
+			strcat(buf, "% ");
+			break;
+			
+		case lw_expr_oper_intdiv:
+			strcat(buf, "\\ ");
+			break;
+			
+		case lw_expr_oper_bwand:
+			strcat(buf, "BWAND ");
+			break;
+			
+		case lw_expr_oper_bwor:
+			strcat(buf, "BWOR ");
+			break;
+			
+		case lw_expr_oper_bwxor:
+			strcat(buf, "BWXOR ");
+			break;
+			
+		case lw_expr_oper_and:
+			strcat(buf, "AND ");
+			break;
+			
+		case lw_expr_oper_or:
+			strcat(buf, "OR ");
+			break;
+			
+		case lw_expr_oper_neg:
+			strcat(buf, "NEG ");
+			break;
+			
+		case lw_expr_oper_com:
+			strcat(buf, "COM ");
+			break;
+			
+		default:
+			strcat(buf, "OPER ");
+			break;
+		}
+		break;
+	default:
+		snprintf(buf, 256, "ERR ");
+		break;
+	}
+	
+	c = strlen(buf);
+	if (*bufloc + c >= *buflen)
+	{
+		*buflen += 128;
+		*obuf = lw_realloc(*obuf, *buflen);
+	}
+	strcpy(*obuf + *bufloc, buf);
+	*bufloc += c;
+}
+
+char *lw_expr_print(lw_expr_t E)
+{
+	static char *obuf = NULL;
+	static int obufsize = 0;
+
+	int obufloc = 0;
+
+	lw_expr_print_aux(E, &obuf, &obufsize, &obufloc);
+
+	return obuf;
+}
+
+/*
+Return:
+nonzero if expressions are the same (identical pointers or matching values)
+zero if expressions are not the same
+
+*/
+int lw_expr_compare(lw_expr_t E1, lw_expr_t E2)
+{
+	struct lw_expr_opers *o1, *o2;
+
+	if (E1 == E2)
+		return 1;
+
+	if (!E1 || !E2)
+		return 0;
+
+	if (!(E1 -> type == E2 -> type && E1 -> value == E2 -> value))
+		return 0;
+
+	if (E1 -> type == lw_expr_type_var)
+	{
+		if (!strcmp(E1 -> value2, E2 -> value2))
+			return 1;
+		else
+			return 0;
+	}
+	
+	if (E1 -> type == lw_expr_type_special)
+	{
+		if (E1 -> value2 == E2 -> value2)
+			return 1;
+		else
+			return 0;
+	}
+	
+	for (o1 = E1 -> operands, o2 = E2 -> operands; o1 && o2; o1 = o1 -> next, o2 = o2 -> next)
+		if (lw_expr_compare(o1 -> p, o2 -> p) == 0)
+			return 0;
+	if (o1 || o2)
+		return 0;	
+
+	return 1;
+}
+
+/* return true if E is an operator of type oper */
+int lw_expr_isoper(lw_expr_t E, int oper)
+{
+	if (E -> type == lw_expr_type_oper && E -> value == oper)
+		return 1;
+	return 0;
+}
+
+
+void lw_expr_simplify_sortconstfirst(lw_expr_t E)
+{
+	struct lw_expr_opers *o;
+
+	if (E -> type != lw_expr_type_oper)
+		return;
+	if (E -> value != lw_expr_oper_times && E -> value != lw_expr_oper_plus)
+		return;
+
+	for (o = E -> operands; o; o = o -> next)
+	{
+		if (o -> p -> type == lw_expr_type_oper && (o -> p -> value == lw_expr_oper_times || o -> p -> value == lw_expr_oper_plus))
+			lw_expr_simplify_sortconstfirst(o -> p);
+	}
+	
+	for (o = E -> operands; o; o = o -> next)
+	{
+		if (o -> p -> type == lw_expr_type_int && o != E -> operands)
+		{
+			struct lw_expr_opers *o2;
+			for (o2 = E -> operands; o2 -> next != o; o2 = o2 -> next)
+				/* do nothing */ ;
+			o2 -> next = o -> next;
+			o -> next = E -> operands;
+			E -> operands = o;
+			o = o2;
+		}
+	}
+}
+
+void lw_expr_sortoperandlist(struct lw_expr_opers **o)
+{
+//	fprintf(stderr, "lw_expr_sortoperandlist() not yet implemented\n");
+}
+
+// return 1 if the operand lists match, 0 if not
+// may re-order the argument lists
+int lw_expr_simplify_compareoperandlist(struct lw_expr_opers **ol1, struct lw_expr_opers **ol2)
+{
+	struct lw_expr_opers *o1, *o2;
+	
+	lw_expr_sortoperandlist(ol1);
+	lw_expr_sortoperandlist(ol2);
+	
+	for (o1 = *ol1, o2 = *ol2; o1 &&  o2; o1 = o1 -> next, o2 = o2 -> next)
+	{
+		if (!lw_expr_compare(o1 -> p, o2 -> p))
+			return 0;
+	}
+	if (o1 || o2)
+		return 0;
+	return 1;
+}
+
+int lw_expr_simplify_isliketerm(lw_expr_t e1, lw_expr_t e2)
+{
+	// first term is a "times"
+	if (e1 -> type == lw_expr_type_oper && e1 -> value == lw_expr_oper_times)
+	{
+		// second term is a "times"
+		if (e2 -> type == lw_expr_type_oper && e2 -> value == lw_expr_oper_times)
+		{
+			// both times - easy check
+			struct lw_expr_opers *o1, *o2;
+			for (o1 = e1 -> operands; o1; o1 = o1 -> next)
+				if (o1 -> p -> type != lw_expr_type_int)
+					break;
+			
+			for (o2 = e2 -> operands; o2; o2 = o2 -> next)
+				if (o2 -> p -> type != lw_expr_type_int)
+					break;
+			
+			if (lw_expr_simplify_compareoperandlist(&o1, &o2))
+				return 1;
+			return 0;
+		}
+		
+		// not a times - have to assume it's the operand list
+		// with a "1 *" in front if it
+		if (!e1 -> operands -> next)
+			return 0;
+		if (e1 -> operands -> next -> next)
+			return 0;
+		if (!lw_expr_compare(e1 -> operands -> next -> p, e2))
+			return 0;
+		return 1;
+	}
+	
+	// e1 is not a times
+	if (e2 -> type == lw_expr_type_oper && e2 -> value == lw_expr_oper_times)
+	{
+		// e2 is a times
+		if (e2 -> operands -> next -> next)
+			return 0;
+		if (!lw_expr_compare(e1, e2 -> operands -> next -> p))
+			return 0;
+		return 1;
+	}
+	
+	// neither are times
+	if (!lw_expr_compare(e1, e2))
+		return 0;
+	return 1;
+}
+
+int lw_expr_contains(lw_expr_t E, lw_expr_t E1)
+{
+	struct lw_expr_opers *o;
+	
+	// NULL expr contains nothing :)
+	if (!E)
+		return 0;
+	
+	if (E1 -> type != lw_expr_type_var && E1 -> type != lw_expr_type_special)
+		return 0;
+	
+	if (lw_expr_compare(E, E1))
+		return 1;
+	
+	for (o = E -> operands; o; o = o -> next)
+	{
+		if (lw_expr_contains(o -> p, E1))
+			return 1;
+	}
+	return 0;
+}
+
+void lw_expr_simplify_l(lw_expr_t E, void *priv);
+
+void lw_expr_simplify_go(lw_expr_t E, void *priv)
+{
+	struct lw_expr_opers *o;
+
+	// replace subtraction with O1 + -1(O2)...
+	// needed for like term collection
+	if (E -> type == lw_expr_type_oper && E -> value == lw_expr_oper_minus)
+	{
+		for (o = E -> operands -> next; o; o = o -> next)
+		{
+			lw_expr_t e1, e2;
+			
+			e2 = lw_expr_build(lw_expr_type_int, -1);
+			e1 = lw_expr_build(lw_expr_type_oper, lw_expr_oper_times, e2, o -> p);
+			lw_expr_destroy(o -> p);
+			lw_expr_destroy(e2);
+			o -> p = e1;
+		}
+		E -> value = lw_expr_oper_plus;
+	}
+
+	// turn "NEG" into -1(O) - needed for like term collection
+	if (E -> type == lw_expr_type_oper && E -> value == lw_expr_oper_neg)
+	{
+		lw_expr_t e1;
+		
+		E -> value = lw_expr_oper_times;
+		e1 = lw_expr_build(lw_expr_type_int, -1);
+		lw_expr_add_operand(E, e1);
+		lw_expr_destroy(e1);
+	}
+	
+again:
+	// try to resolve non-constant terms to constants here
+	if (E -> type == lw_expr_type_special && evaluate_special)
+	{
+		lw_expr_t te;
+		
+		te = evaluate_special(E -> value, E -> value2, priv);
+		if (lw_expr_contains(te, E))
+			lw_expr_destroy(te);
+		if (te)
+		{
+			for (o = E -> operands; o; o = o -> next)
+				lw_expr_destroy(o -> p);
+			if (E -> type == lw_expr_type_var)
+				lw_free(E -> value2);
+			*E = *te;
+			E -> operands = NULL;
+	
+			if (te -> type == lw_expr_type_var)
+				E -> value2 = lw_strdup(te -> value2);
+			for (o = te -> operands; o; o = o -> next)
+			{
+				lw_expr_add_operand(E, lw_expr_copy(o -> p));
+			}
+			lw_expr_destroy(te);
+			goto again;
+		}
+		return;
+	}
+
+	if (E -> type == lw_expr_type_var && evaluate_var)
+	{
+		lw_expr_t te;
+		
+		te = evaluate_var(E -> value2, priv);
+		if (lw_expr_contains(te, E))
+			lw_expr_destroy(te);
+		else if (te)
+		{
+			for (o = E -> operands; o; o = o -> next)
+				lw_expr_destroy(o -> p);
+			if (E -> type == lw_expr_type_var)
+				lw_free(E -> value2);
+			*E = *te;
+			E -> operands = NULL;
+	
+			if (te -> type == lw_expr_type_var)
+				E -> value2 = lw_strdup(te -> value2);
+			for (o = te -> operands; o; o = o -> next)
+			{
+				lw_expr_add_operand(E, lw_expr_copy(o -> p));
+			}
+			lw_expr_destroy(te);
+			goto again;
+		}
+		return;
+	}
+
+	// non-operators have no simplification to do!
+	if (E -> type != lw_expr_type_oper)
+		return;
+
+	// merge plus operations
+	if (E -> value == lw_expr_oper_plus)
+	{
+		lw_expr_t e2;
+
+	tryagainplus:
+		for (o = E -> operands; o; o = o -> next)
+		{
+			if (o -> p -> type == lw_expr_type_oper && o -> p -> value == lw_expr_oper_plus)
+			{
+				struct lw_expr_opers *o2, *o3;
+				// we have a + operation - bring operands up
+				
+				for (o2 = E -> operands; o2 && o2 -> next != o; o2 = o2 -> next)
+					/* do nothing */ ;
+				if (o2)
+					o2 -> next = o -> p -> operands;
+				else
+					E -> operands = o -> p -> operands;
+				for (o2 = o -> p -> operands; o2 -> next; o2 = o2 -> next)
+					/* do nothing */ ;
+				o2 -> next = o -> next;
+				o -> p -> operands = NULL;
+				lw_expr_destroy(o -> p);
+				lw_free(o);
+				goto tryagainplus;
+			}
+		}
+	}
+	
+	// merge times operations
+	if (E -> value == lw_expr_oper_times)
+	{
+		lw_expr_t e2;
+
+	tryagaintimes:
+		for (o = E -> operands; o; o = o -> next)
+		{
+			if (o -> p -> type == lw_expr_type_oper && o -> p -> value == lw_expr_oper_times)
+			{
+				struct lw_expr_opers *o2, *o3;
+				// we have a + operation - bring operands up
+				
+				for (o2 = E -> operands; o2 && o2 -> next != o; o2 = o2 -> next)
+					/* do nothing */ ;
+				if (o2)
+					o2 -> next = o -> p -> operands;
+				else
+					E -> operands = o -> p -> operands;
+				for (o2 = o -> p -> operands; o2 -> next; o2 = o2 -> next)
+					/* do nothing */ ;
+				o2 -> next = o -> next;
+				o -> p -> operands = NULL;
+				lw_expr_destroy(o -> p);
+				lw_free(o);
+				goto tryagaintimes;
+			}
+		}
+	}
+	
+	// simplify operands
+	for (o = E -> operands; o; o = o -> next)
+		lw_expr_simplify_l(o -> p, priv);
+
+	for (o = E -> operands; o; o = o -> next)
+	{
+		if (o -> p -> type != lw_expr_type_int)
+			break;
+	}
+
+	if (!o)
+	{
+		// we can do the operation here!
+		int tr = -42424242;
+		
+		switch (E -> value)
+		{
+		case lw_expr_oper_neg:
+			tr = -(E -> operands -> p -> value);
+			break;
+
+		case lw_expr_oper_com:
+			tr = ~(E -> operands -> p -> value);
+			break;
+		
+		case lw_expr_oper_plus:
+			tr = E -> operands -> p -> value;
+			for (o = E -> operands -> next; o; o = o -> next)
+				tr += o -> p -> value;
+			break;
+
+		case lw_expr_oper_minus:
+			tr = E -> operands -> p -> value;
+			for (o = E -> operands -> next; o; o = o -> next)
+				tr -= o -> p -> value;
+			break;
+
+		case lw_expr_oper_times:
+			tr = E -> operands -> p -> value;
+			for (o = E -> operands -> next; o; o = o -> next)
+				tr *= o -> p -> value;
+			break;
+
+		case lw_expr_oper_divide:
+			tr = E -> operands -> p -> value / E -> operands -> next -> p -> value;
+			break;
+		
+		case lw_expr_oper_mod:
+			tr = E -> operands -> p -> value % E -> operands -> next -> p -> value;
+			break;
+		
+		case lw_expr_oper_intdiv:
+			tr = E -> operands -> p -> value / E -> operands -> next -> p -> value;
+			break;
+
+		case lw_expr_oper_bwand:
+			tr = E -> operands -> p -> value & E -> operands -> next -> p -> value;
+			break;
+
+		case lw_expr_oper_bwor:
+			tr = E -> operands -> p -> value | E -> operands -> next -> p -> value;
+			break;
+
+		case lw_expr_oper_bwxor:
+			tr = E -> operands -> p -> value ^ E -> operands -> next -> p -> value;
+			break;
+
+		case lw_expr_oper_and:
+			tr = E -> operands -> p -> value && E -> operands -> next -> p -> value;
+			break;
+
+		case lw_expr_oper_or:
+			tr = E -> operands -> p -> value || E -> operands -> next -> p -> value;
+			break;
+		
+		}
+		
+		while (E -> operands)
+		{
+			o = E -> operands;
+			E -> operands = o -> next;
+			lw_expr_destroy(o -> p);
+			lw_free(o);
+		}
+		E -> type = lw_expr_type_int;
+		E -> value = tr;
+		return;
+	}
+
+	if (E -> value == lw_expr_oper_plus)
+	{
+		lw_expr_t e1;
+		int cval = 0;
+		
+		e1 = lw_expr_create();
+		e1 -> operands = E -> operands;
+		E -> operands = 0;
+		
+		for (o = e1 -> operands; o; o = o -> next)
+		{
+			if (o -> p -> type == lw_expr_type_int)
+				cval += o -> p -> value;
+			else
+				lw_expr_add_operand(E, o -> p);
+		}
+		lw_expr_destroy(e1);
+		if (cval)
+		{
+			e1 = lw_expr_build(lw_expr_type_int, cval);
+			lw_expr_add_operand(E, e1);
+			lw_expr_destroy(e1);
+		}
+	}
+
+	if (E -> value == lw_expr_oper_times)
+	{
+		lw_expr_t e1;
+		int cval = 1;
+		
+		e1 = lw_expr_create();
+		e1 -> operands = E -> operands;
+		E -> operands = 0;
+		
+		for (o = e1 -> operands; o; o = o -> next)
+		{
+			if (o -> p -> type == lw_expr_type_int)
+				cval *= o -> p -> value;
+			else
+				lw_expr_add_operand(E, o -> p);
+		}
+		lw_expr_destroy(e1);
+		if (cval != 1)
+		{
+			e1 = lw_expr_build(lw_expr_type_int, cval);
+			lw_expr_add_operand(E, e1);
+			lw_expr_destroy(e1);
+		}
+	}
+
+	if (E -> value == lw_expr_oper_times)
+	{
+		for (o = E -> operands; o; o = o -> next)
+		{
+			if (o -> p -> type == lw_expr_type_int && o -> p -> value == 0)
+			{
+				// one operand of times is 0, replace operation with 0
+				while (E -> operands)
+				{
+					o = E -> operands;
+					E -> operands = o -> next;
+					lw_expr_destroy(o -> p);
+					lw_free(o);
+				}
+				E -> type = lw_expr_type_int;
+				E -> value = 0;
+				return;
+			}
+		}
+	}
+	
+	// sort "constants" to the start of each operand list for + and *
+	if (E -> value == lw_expr_oper_plus || E -> value == lw_expr_oper_times)
+		lw_expr_simplify_sortconstfirst(E);
+	
+	// look for like terms and collect them together
+	if (E -> value == lw_expr_oper_plus)
+	{
+		struct lw_expr_opers *o2;
+		for (o = E -> operands; o; o = o -> next)
+		{
+			// skip constants
+			if (o -> p -> type == lw_expr_type_int)
+				continue;
+			
+			// we have a term to match
+			// (o -> p) is first term
+			for (o2 = o -> next; o2; o2 = o2 -> next)
+			{
+				lw_expr_t e1, e2;
+				
+				if (o2 -> p -> type == lw_expr_type_int)
+					continue;
+
+				if (lw_expr_simplify_isliketerm(o -> p, o2 -> p))
+				{
+					int coef, coef2;
+					
+					// we have a like term here
+					// do something about it
+					if (o -> p -> type == lw_expr_type_oper && o -> p -> value == lw_expr_oper_times)
+					{
+						if (o -> p -> operands -> p -> type == lw_expr_type_int)
+							coef = o -> p -> operands -> p -> value;
+						else
+							coef = 1;
+					}
+					else
+						coef = 1;
+					if (o2 -> p -> type == lw_expr_type_oper && o2 -> p -> value == lw_expr_oper_times)
+					{
+						if (o2 -> p -> operands -> p -> type == lw_expr_type_int)
+							coef2 = o2 -> p -> operands -> p -> value;
+						else
+							coef2 = 1;
+					}
+					else
+						coef2 = 1;
+					coef += coef2;
+					e1 = lw_expr_create();
+					e1 -> type = lw_expr_type_oper;
+					e1 -> value = lw_expr_oper_times;
+					if (coef != 1)
+					{
+						e2 = lw_expr_build(lw_expr_type_int, coef);
+						lw_expr_add_operand(e1, e2);
+						lw_expr_destroy(e2);
+					}
+					lw_expr_destroy(o -> p);
+					o -> p = e1;
+					for (o = o2 -> p -> operands; o; o = o -> next)
+					{
+						if (o -> p -> type == lw_expr_type_int)
+							continue;
+						lw_expr_add_operand(e1, o -> p);
+					}
+					lw_expr_destroy(o2 -> p);
+					o2 -> p = lw_expr_build(lw_expr_type_int, 0);
+					goto again;
+				}
+			}
+		}
+	}
+
+
+	if (E -> value == lw_expr_oper_plus)
+	{
+		int c = 0, t = 0;
+		for (o = E -> operands; o; o = o -> next)
+		{
+			t++;
+			if (!(o -> p -> type == lw_expr_type_int && o -> p -> value == 0))
+			{
+				c++;
+			}
+		}
+		if (c == 1)
+		{
+			lw_expr_t r;
+			// find the value and "move it up"
+			while (E -> operands)
+			{
+				o = E -> operands;
+				if (o -> p -> type != lw_expr_type_int || o -> p -> value != 0)
+				{
+					r = lw_expr_copy(o -> p);
+				}
+				E -> operands = o -> next;
+				lw_expr_destroy(o -> p);
+				lw_free(o);
+			}
+			*E = *r;
+			return;
+		}
+		else if (c == 0)
+		{
+			// replace with 0
+			while (E -> operands)
+			{
+				o = E -> operands;
+				E -> operands = o -> next;
+				lw_expr_destroy(o -> p);
+				lw_free(o);
+			}
+			E -> type = lw_expr_type_int;
+			E -> value = 0;
+			return;
+		}
+		else if (c != t)
+		{
+			// collapse out zero terms
+			struct lw_expr_opers *o2;
+			
+			for (o = E -> operands; o; o = o -> next)
+			{
+				if (o -> p -> type == lw_expr_type_int && o -> p -> value == 0)
+				{
+					if (o == E -> operands)
+					{
+						E -> operands = o -> next;
+						lw_expr_destroy(o -> p);
+						lw_free(o);
+						o = E -> operands;
+					}
+					else
+					{
+						for (o2 = E -> operands; o2 -> next == o; o2 = o2 -> next)
+							/* do nothing */ ;
+						o2 -> next = o -> next;
+						lw_expr_destroy(o -> p);
+						lw_free(o);
+						o = o2;
+					}
+				}
+			}
+		}
+		return;
+	}
+	
+	/* handle <int> times <plus> - expand the terms - only with exactly two operands */
+	if (E -> value == lw_expr_oper_times)
+	{
+		lw_expr_t t1;
+		lw_expr_t E2;
+		lw_expr_t E3;
+		if (E -> operands && E -> operands -> next && !(E -> operands -> next -> next))
+		{
+			if (E -> operands -> p -> type  == lw_expr_type_int)
+			{
+				/* <int> TIMES <other> */
+				E2 = E -> operands -> next -> p;
+				E3 = E -> operands -> p;
+				if (E2 -> type == lw_expr_type_oper && E2 -> value == lw_expr_oper_plus)
+				{
+					lw_free(E -> operands -> next);
+					lw_free(E -> operands);
+					E -> operands = NULL;
+					E -> value = lw_expr_oper_plus;
+					
+					for (o = E2 -> operands; o; o = o -> next)
+					{
+						t1 = lw_expr_build(lw_expr_type_oper, lw_expr_oper_times, E3, o -> p);
+						lw_expr_add_operand(E, t1);
+					}
+					
+					lw_expr_destroy(E2);
+					lw_expr_destroy(E3);
+				}
+			}
+			else if (E -> operands -> next -> p -> type == lw_expr_type_int)
+			{
+				/* <other> TIMES <int> */
+				E2 = E -> operands -> p;
+				E3 = E -> operands -> next -> p;
+				if (E2 -> type == lw_expr_type_oper && E2 -> value == lw_expr_oper_plus)
+				{
+					lw_free(E -> operands -> next);
+					lw_free(E -> operands);
+					E -> operands = NULL;
+					E -> value = lw_expr_oper_plus;
+					
+					for (o = E2 -> operands; o; o = o -> next)
+					{
+						t1 = lw_expr_build(lw_expr_type_oper, lw_expr_oper_times, E3, o -> p);
+						lw_expr_add_operand(E, t1);
+					}
+					
+					lw_expr_destroy(E2);
+					lw_expr_destroy(E3);
+				}
+			}
+		}
+	}
+}
+
+void lw_expr_simplify_l(lw_expr_t E, void *priv)
+{
+	lw_expr_t te;
+	int c;
+	
+	(level)++;
+	// bail out if the level gets too deep
+	if (level >= 500 || bailing)
+	{
+		bailing = 1;
+		level--;
+		if (level == 0)
+			bailing = 0;
+		return;
+	}
+	do
+	{
+		te = lw_expr_copy(E);
+		lw_expr_simplify_go(E, priv);
+		c = 0;
+		if (lw_expr_compare(te, E) == 0)
+			c = 1;
+		lw_expr_destroy(te);
+	}
+	while (c);
+	(level)--;
+}
+
+void lw_expr_simplify(lw_expr_t E, void *priv)
+{
+	lw_expr_simplify_l(E, priv);
+}
+
+/*
+
+The following two functions are co-routines which evaluate an infix
+expression.  lw_expr_parse_term checks for unary prefix operators then, if
+none found, passes the string off the the defined helper function to
+determine what the term really is. It also handles parentheses.
+
+lw_expr_parse_expr evaluates actual expressions with infix operators. It
+respects the order of operations.
+
+The end of an expression is determined by the presence of any of the
+following conditions:
+
+1. a NUL character
+2. a whitespace character
+3. a )
+4. a ,
+5. any character that is not recognized as a term
+
+lw_expr_parse_term returns NULL if there is no term (end of expr, etc.)
+
+lw_expr_parse_expr returns NULL if there is no expression or on a syntax
+error.
+
+*/
+
+lw_expr_t lw_expr_parse_expr(char **p, void *priv, int prec);
+
+lw_expr_t lw_expr_parse_term(char **p, void *priv)
+{
+	lw_expr_t term, term2;
+	
+eval_next:
+	if (!**p || isspace(**p) || **p == ')' || **p == ']')
+		return NULL;
+
+	// parentheses
+	if (**p == '(')
+	{
+		(*p)++;
+		term = lw_expr_parse_expr(p, priv, 0);
+		if (**p != ')')
+		{
+			lw_expr_destroy(term);
+			return NULL;
+		}
+		(*p)++;
+		return term;
+	}
+	
+	// unary +
+	if (**p == '+')
+	{
+		(*p)++;
+		goto eval_next;
+	}
+	
+	// unary - (prec 200)
+	if (**p == '-')
+	{
+		(*p)++;
+		term = lw_expr_parse_expr(p, priv, 200);
+		if (!term)
+			return NULL;
+		
+		term2 = lw_expr_build(lw_expr_type_oper, lw_expr_oper_neg, term);
+		lw_expr_destroy(term);
+		return term2;
+	}
+	
+	// unary ^ or ~ (complement, prec 200)
+	if (**p == '^' || **p == '~')
+	{
+		(*p)++;
+		term = lw_expr_parse_expr(p, priv, 200);
+		if (!term)
+			return NULL;
+		
+		term2 = lw_expr_build(lw_expr_type_oper, lw_expr_oper_com, term);
+		lw_expr_destroy(term);
+		return term2;
+	}
+	
+	// non-operator - pass to caller
+	return parse_term(p, priv);
+}
+
+lw_expr_t lw_expr_parse_expr(char **p, void *priv, int prec)
+{
+	static const struct operinfo
+	{
+		int opernum;
+		char *operstr;
+		int operprec;
+	} operators[] =
+	{
+		{ lw_expr_oper_plus, "+", 100 },
+		{ lw_expr_oper_minus, "-", 100 },
+		{ lw_expr_oper_times, "*", 150 },
+		{ lw_expr_oper_divide, "/", 150 },
+		{ lw_expr_oper_mod, "%", 150 },
+		{ lw_expr_oper_intdiv, "\\", 150 },
+		
+		{ lw_expr_oper_and, "&&", 25 },
+		{ lw_expr_oper_or, "||", 25 },
+		
+		{ lw_expr_oper_bwand, "&", 50 },
+		{ lw_expr_oper_bwor, "|", 50 },
+		{ lw_expr_oper_bwxor, "^", 50 },
+		
+		{ lw_expr_oper_none, "", 0 }
+	};
+	
+	int opern, i;
+	lw_expr_t term1, term2, term3;
+	
+	if (!**p || isspace(**p) || **p == ')' || **p == ',' || **p == ']')
+		return NULL;
+
+	term1 = lw_expr_parse_term(p, priv);
+	if (!term1)
+		return NULL;
+
+eval_next:
+	if (!**p || isspace(**p) || **p == ')' || **p == ',' || **p == ']')
+		return term1;
+	
+	// expecting an operator here
+	for (opern = 0; operators[opern].opernum != lw_expr_oper_none; opern++)
+	{
+		for (i = 0; (*p)[i] && operators[opern].operstr[i] && ((*p)[i] == operators[opern].operstr[i]); i++)
+			/* do nothing */;
+		if (operators[opern].operstr[i] == '\0')
+			break;
+	}
+	
+	if (operators[opern].opernum == lw_expr_oper_none)
+	{
+		// unrecognized operator
+		lw_expr_destroy(term1);
+		return NULL;
+	}
+
+	// operator number is in opern, length of oper in i
+	
+	// logic:
+	// if the precedence of this operation is <= to the "prec" flag,
+	// we simply return without advancing the input pointer; the operator
+	// will be evaluated again in the enclosing function call
+	if (operators[opern].operprec <= prec)
+		return term1;
+	
+	// logic:
+	// we have a higher precedence operator here so we will advance the
+	// input pointer to the next term and let the expression evaluator
+	// loose on it after which time we will push our operator onto the
+	// stack and then go on with the expression evaluation
+	(*p) += i;
+	
+	// evaluate next expression(s) of higher precedence
+	term2 = lw_expr_parse_expr(p, priv, operators[opern].operprec);
+	if (!term2)
+	{
+		lw_expr_destroy(term1);
+		return NULL;
+	}
+	
+	// now create operator
+	term3 = lw_expr_build(lw_expr_type_oper, operators[opern].opernum, term1, term2);
+	lw_expr_destroy(term1);
+	lw_expr_destroy(term2);
+	
+	// the new "expression" is the next "left operand"
+	term1 = term3;
+	
+	// continue evaluating
+	goto eval_next;
+}
+
+lw_expr_t lw_expr_parse(char **p, void *priv)
+{
+	return lw_expr_parse_expr(p, priv, 0);
+}
+
+int lw_expr_testterms(lw_expr_t e, lw_expr_testfn_t *fn, void *priv)
+{
+	struct lw_expr_opers *o;
+	int r;
+	
+	for (o = e -> operands; o; o = o -> next)
+	{
+		r = lw_expr_testterms(o -> p, fn, priv);
+		if (r)
+			return r;
+	}
+	return (fn)(e, priv);
+}
+
+int lw_expr_type(lw_expr_t e)
+{
+	return e -> type;
+}
+
+void *lw_expr_specptr(lw_expr_t e)
+{
+	return e -> value2;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lwlib/lw_expr.h	Wed Jan 19 22:27:17 2011 -0700
@@ -0,0 +1,115 @@
+/*
+lwexpr.h
+
+Copyright © 2010 William Astle
+
+This file is part of LWTOOLS.
+
+LWTOOLS 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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef ___lw_expr_h_seen___
+#define ___lw_expr_h_seen___
+
+#include <stdio.h>
+
+enum
+{
+	lw_expr_type_oper,			// operator term
+	lw_expr_type_int,			// integer
+	lw_expr_type_var,			// a "variable" (string for the name)
+	lw_expr_type_special		// a "special" reference (user defined)
+};
+
+enum
+{
+	lw_expr_oper_plus = 1,		// addition
+	lw_expr_oper_minus,			// subtraction
+	lw_expr_oper_times,			// multiplication
+	lw_expr_oper_divide,		// division
+	lw_expr_oper_mod,			// modulus
+	lw_expr_oper_intdiv,		// integer division
+	lw_expr_oper_bwand,			// bitwise and
+	lw_expr_oper_bwor,			// bitwise or
+	lw_expr_oper_bwxor,			// bitwise xor
+	lw_expr_oper_and,			// boolean and
+	lw_expr_oper_or,			// boolean or
+	lw_expr_oper_neg,			// unary negation, 2's complement
+	lw_expr_oper_com,			// unary 1's complement
+	lw_expr_oper_none = 0
+};
+
+#ifdef ___lw_expr_c_seen___
+
+typedef struct lw_expr_priv * lw_expr_t;
+
+struct lw_expr_opers
+{
+	lw_expr_t p;
+	struct lw_expr_opers *next;
+};
+
+struct lw_expr_priv
+{
+	int type;							// type of term
+	int value;							// integer value
+	void *value2;						// misc pointer value
+	struct lw_expr_opers *operands;		// ptr to list of operands (for operators)
+};
+
+typedef lw_expr_t lw_expr_fn_t(int t, void *ptr, void *priv);
+typedef lw_expr_t lw_expr_fn2_t(char *var, void *priv);
+typedef lw_expr_t lw_expr_fn3_t(char **p, void *priv);
+typedef int lw_expr_testfn_t(lw_expr_t e, void *priv);
+
+#else /* def ___lw_expr_c_seen___ */
+
+typedef void * lw_expr_t;
+
+extern lw_expr_t lwexpr_create(void);
+extern void lw_expr_destroy(lw_expr_t E);
+extern lw_expr_t lw_expr_copy(lw_expr_t E);
+extern void lw_expr_add_operand(lw_expr_t E, lw_expr_t O);
+extern lw_expr_t lw_expr_build(int exprtype, ...);
+extern char *lw_expr_print(lw_expr_t E);
+extern int lw_expr_compare(lw_expr_t E1, lw_expr_t E2);
+extern void lw_expr_simplify(lw_expr_t E, void *priv);
+
+typedef lw_expr_t lw_expr_fn_t(int t, void *ptr, void *priv);
+typedef lw_expr_t lw_expr_fn2_t(char *var, void *priv);
+typedef lw_expr_t lw_expr_fn3_t(char **p, void *priv);
+
+extern void lw_expr_set_special_handler(lw_expr_fn_t *fn);
+extern void lw_expr_set_var_handler(lw_expr_fn2_t *fn);
+extern void lw_expr_set_term_parser(lw_expr_fn3_t *fn);
+
+extern lw_expr_t lw_expr_parse(char **p, void *priv);
+extern int lw_expr_istype(lw_expr_t e, int t);
+extern int lw_expr_intval(lw_expr_t e);
+extern int lw_expr_specint(lw_expr_t e);
+extern void *lw_expr_specptr(lw_expr_t e);
+extern int lw_expr_whichop(lw_expr_t e);
+
+extern int lw_expr_type(lw_expr_t e);
+
+typedef int lw_expr_testfn_t(lw_expr_t e, void *priv);
+
+// run a function on all terms in an expression; if the function
+// returns non-zero for any term, return non-zero, else return
+// zero
+extern int lw_expr_testterms(lw_expr_t e, lw_expr_testfn_t *fn, void *priv);
+
+#endif /* def ___lw_expr_c_seen___ */
+
+#endif /* ___lw_expr_h_seen___ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lwlib/lw_stack.c	Wed Jan 19 22:27:17 2011 -0700
@@ -0,0 +1,78 @@
+/*
+lw_stack.c
+
+Copyright © 2010 William Astle
+
+This file is part of LWTOOLS.
+
+LWTOOLS 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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#define ___lw_stack_c_seen___
+#include "lw_stack.h"
+#include "lw_alloc.h"
+
+/* this is technically valid but dubious */
+#define NULL 0
+
+lw_stack_t lw_stack_create(void (*freefn)(void *d))
+{
+	lw_stack_t S;
+	
+	S = lw_alloc(sizeof(lw_stack_t));
+	S -> head = NULL;
+	S -> freefn = freefn;
+	return S;
+}
+
+void *lw_stack_pop(lw_stack_t S)
+{
+	if (S -> head)
+	{
+		void *ret, *r2;
+		
+		ret = S -> head -> data;
+		r2 = S -> head;
+		S -> head = S -> head -> next;
+		lw_free(r2);
+		return ret;
+	}
+	return NULL;
+}
+
+void lw_stack_destroy(lw_stack_t S)
+{
+	void *d;
+	
+	while (d = lw_stack_pop(S))
+		(S->freefn)(d);
+	lw_free(S);
+}
+
+void *lw_stack_top(lw_stack_t S)
+{
+	if (S -> head)
+		return S -> head -> data;
+	return NULL;
+}
+
+void lw_stack_push(lw_stack_t S, void *item)
+{
+	struct lw_stack_node_priv *t;
+	
+	t = lw_alloc(sizeof(struct lw_stack_node_priv));
+	t -> next = S -> head;
+	S -> head = t;
+	t -> data = item;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lwlib/lw_stack.h	Wed Jan 19 22:27:17 2011 -0700
@@ -0,0 +1,53 @@
+/*
+lw_stack.h
+
+Copyright © 2010 William Astle
+
+This file is part of LWTOOLS.
+
+LWTOOLS 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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef ___lw_stack_h_seen___
+#define ___lw_stack_h_seen___
+
+
+#ifdef ___lw_stack_c_seen___
+
+struct lw_stack_node_priv
+{
+	void *data;
+	struct lw_stack_node_priv *next;
+};
+
+struct lw_stack_priv
+{
+	struct lw_stack_node_priv *head;
+	void (*freefn)(void *d);
+};
+
+typedef struct lw_stack_priv * lw_stack_t;
+
+#else /* def ___lw_stack_c_seen___ */
+
+typedef void * lw_stack_t;
+extern lw_stack_t lw_stack_create(void (*freefn)(void *d));
+extern void lw_stack_destroy(lw_stack_t S);
+extern void *lw_stack_top(lw_stack_t S);
+extern void *lw_stack_pop(lw_stack_t S);
+extern void lw_stack_push(lw_stack_t S, void *item);
+
+#endif /* def ___lw_stack_c_seen___ */
+
+#endif /* ___lw_stack_h_seen___ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lwlib/lw_string.c	Wed Jan 19 22:27:17 2011 -0700
@@ -0,0 +1,83 @@
+/*
+lw_string.c
+
+Copyright © 2010 William Astle
+
+This file is part of LWTOOLS.
+
+LWTOOLS 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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <string.h>
+#include <stdlib.h>
+
+#define ___lw_string_c_seen___
+#include "lw_alloc.h"
+#include "lw_string.h"
+
+char *lw_strdup(const char *s)
+{
+	char *r;
+	
+	if (!s)
+		s = "(null)";
+
+	r = lw_alloc(strlen(s) + 1);
+	strcpy(r, s);
+	return r;
+}
+
+char *lw_strndup(const char *s, int len)
+{
+	char *r;
+	int sl;
+	
+	sl = strlen(s);
+	if (sl > len)
+		sl = len;
+	
+	r = lw_alloc(sl + 1);
+	memmove(r, s, sl);
+	r[sl] = '\0';
+	return r;
+}
+
+char *lw_token(const char *s, int sep, const char **ap)
+{
+	const char *p;
+	char *r;
+
+	if (!s)
+		return NULL;
+	
+	p = strchr(s, sep);
+	if (!p)
+	{
+		if (ap)
+			*ap = NULL;
+		return lw_strdup(s);
+	}
+	
+	r = lw_alloc(p - s + 1);
+	strncpy(r, (char *)s, p - s);
+	r[p - s] = '\0';
+	
+	if (ap)
+	{
+		while (*p && *p == sep)
+			p++;
+		*ap = p;
+	}
+	return r;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lwlib/lw_string.h	Wed Jan 19 22:27:17 2011 -0700
@@ -0,0 +1,36 @@
+/*
+lw_string.h
+
+Copyright © 2010 William Astle
+
+This file is part of LWTOOLS.
+
+LWTOOLS 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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef ___lw_string_h_seen___
+#define ___lw_string_h_seen___
+
+
+#ifdef ___lw_string_c_seen___
+
+#else /* def ___lw_string_c_seen___ */
+
+extern char *lw_strdup(const char *s);
+extern char *lw_strndup(const char *s, int len);
+extern char *lw_token(const char *s, int sep, const char **ap);
+
+#endif /* def ___lw_string_c_seen___ */
+
+#endif /* ___lw_string_h_seen___ */
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lwlib/lw_stringlist.c	Wed Jan 19 22:27:17 2011 -0700
@@ -0,0 +1,102 @@
+/*
+lw_stringlist.c
+
+Copyright © 2010 William Astle
+
+This file is part of LWTOOLS.
+
+LWTOOLS 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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdlib.h>
+
+#define ___lw_stringlist_c_seen___
+#include "lw_stringlist.h"
+#include "lw_string.h"
+#include "lw_alloc.h"
+
+lw_stringlist_t lw_stringlist_create(void)
+{
+	lw_stringlist_t s;
+	
+	
+	s = lw_alloc(sizeof(struct lw_stringlist_priv));
+	s -> strings = NULL;
+	s -> nstrings = 0;
+	s -> cstring = 0;
+	
+	return s;
+}
+
+void lw_stringlist_destroy(lw_stringlist_t S)
+{
+	if (S)
+	{
+		int i;
+		for (i = 0; i < S -> nstrings; i++)
+		{
+			lw_free(S -> strings[i]);
+		}
+		lw_free(S);
+	}
+}
+
+void lw_stringlist_addstring(lw_stringlist_t S, char *str)
+{
+	S -> strings = lw_realloc(S -> strings, sizeof(char *) * (S -> nstrings + 1));
+	S -> strings[S -> nstrings] = lw_strdup(str);
+	S -> nstrings++;
+}
+
+void lw_stringlist_reset(lw_stringlist_t S)
+{
+	S -> cstring = 0;
+}
+
+char *lw_stringlist_current(lw_stringlist_t S)
+{
+	if (S -> cstring >= S -> nstrings)
+		return NULL;
+	return S -> strings[S -> cstring];
+}
+
+char *lw_stringlist_next(lw_stringlist_t S)
+{
+	S -> cstring++;
+	return lw_stringlist_current(S);
+}
+
+int lw_stringlist_nstrings(lw_stringlist_t S)
+{
+	return S -> nstrings;
+}
+
+lw_stringlist_t lw_stringlist_copy(lw_stringlist_t S)
+{
+	lw_stringlist_t r;
+	
+	r = lw_alloc(sizeof(lw_stringlist_t));
+	r -> nstrings = S -> nstrings;
+	if (S -> nstrings)
+	{
+		int i;
+		
+		r -> strings = lw_alloc(sizeof(char *) * S -> nstrings);
+		for (i = 0; i < S -> nstrings; i++)
+		{
+			r -> strings[i] = lw_strdup(S -> strings[i]);
+		}
+	}
+	return r;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lwlib/lw_stringlist.h	Wed Jan 19 22:27:17 2011 -0700
@@ -0,0 +1,50 @@
+/*
+lw_stringlist.h
+
+Copyright © 2010 William Astle
+
+This file is part of LWTOOLS.
+
+LWTOOLS 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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef ___lw_stringlist_h_seen___
+#define ___lw_stringlist_h_seen___
+
+
+#ifdef ___lw_stringlist_c_seen___
+
+struct lw_stringlist_priv
+{
+	char **strings;
+	int nstrings;
+	int cstring;
+};
+typedef struct lw_stringlist_priv * lw_stringlist_t;
+
+#else /* def ___lw_stringlist_c_seen___ */
+
+typedef void * lw_stringlist_t;
+extern lw_stringlist_t lw_stringlist_create(void);
+extern void lw_stringlist_destroy(lw_stringlist_t S);
+extern void lw_stringlist_addstring(lw_stringlist_t S, char *str);
+extern void lw_stringlist_reset(lw_stringlist_t S);
+extern char *lw_stringlist_current(lw_stringlist_t S);
+extern char *lw_stringlist_next(lw_stringlist_t S);
+extern int lw_stringlist_nstrings(lw_stringlist_t S);
+extern lw_stringlist_t lw_stringlist_copy(lw_stringlist_t S);
+
+#endif /* def ___lw_stringlist_c_seen___ */
+
+#endif /* ___lw_stringlist_h_seen___ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lwlib/rules.make	Wed Jan 19 22:27:17 2011 -0700
@@ -0,0 +1,6 @@
+dirname := $(dir $(lastword $(MAKEFILE_LIST)))
+
+lwlib_srcs_local := lw_alloc.c lw_error.c lw_expr.c lw_stack.c \
+	lw_string.c lw_stringlist.c
+
+lwlib_srcs := $(lwlib_srcs) $(addprefix $(dirname),$(lwlib_srcs_local))
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lwlink/expr.c	Wed Jan 19 22:27:17 2011 -0700
@@ -0,0 +1,369 @@
+/*
+expr.c
+Copyright © 2009 William Astle
+
+This file is part of LWLINK.
+
+LWLINK 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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+This file contains the actual expression evaluator
+*/
+
+#define __expr_c_seen__
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "expr.h"
+#include "util.h"
+
+lw_expr_stack_t *lw_expr_stack_create(void)
+{
+	lw_expr_stack_t *s;
+	
+	s = lw_malloc(sizeof(lw_expr_stack_t));
+	s -> head = NULL;
+	s -> tail = NULL;
+	return s;
+}
+
+void lw_expr_stack_free(lw_expr_stack_t *s)
+{
+	while (s -> head)
+	{
+		s -> tail = s -> head;
+		s -> head = s -> head -> next;
+		lw_expr_term_free(s -> tail -> term);
+		lw_free(s -> tail);
+	}
+	lw_free(s);
+}
+
+lw_expr_stack_t *lw_expr_stack_dup(lw_expr_stack_t *s)
+{
+	lw_expr_stack_node_t *t;
+	lw_expr_stack_t *s2;
+		
+	s2 = lw_expr_stack_create();	
+	for (t = s -> head; t; t = t -> next)
+	{
+		lw_expr_stack_push(s2, t -> term);
+	}
+	return s2;
+}
+
+void lw_expr_term_free(lw_expr_term_t *t)
+{
+	if (t)
+	{
+		if (t -> term_type == LW_TERM_SYM)
+			lw_free(t -> symbol);
+		lw_free(t);
+	}
+}
+
+lw_expr_term_t *lw_expr_term_create_oper(int oper)
+{
+	lw_expr_term_t *t;
+
+	t = lw_malloc(sizeof(lw_expr_term_t));
+	t -> term_type = LW_TERM_OPER;
+	t -> value = oper;
+	return t;
+}
+
+lw_expr_term_t *lw_expr_term_create_int(int val)
+{
+	lw_expr_term_t *t;
+	
+	t = lw_malloc(sizeof(lw_expr_term_t));
+	t -> term_type = LW_TERM_INT;
+	t -> value = val;
+	return t;
+}
+
+lw_expr_term_t *lw_expr_term_create_sym(char *sym, int symtype)
+{
+	lw_expr_term_t *t;
+	
+	t = lw_malloc(sizeof(lw_expr_term_t));
+	t -> term_type = LW_TERM_SYM;
+	t -> symbol = lw_strdup(sym);
+	t -> value = symtype;
+	return t;
+}
+
+lw_expr_term_t *lw_expr_term_dup(lw_expr_term_t *t)
+{
+	switch (t -> term_type)
+	{
+	case LW_TERM_INT:
+		return lw_expr_term_create_int(t -> value);
+		
+	case LW_TERM_OPER:
+		return lw_expr_term_create_oper(t -> value);
+		
+	case LW_TERM_SYM:
+		return lw_expr_term_create_sym(t -> symbol, t -> value);
+	
+	default:
+		exit(1);
+	}
+// can't get here
+}
+
+void lw_expr_stack_push(lw_expr_stack_t *s, lw_expr_term_t *t)
+{
+	lw_expr_stack_node_t *n;
+
+	if (!s)
+	{
+		exit(1);
+	}
+	
+	n = lw_malloc(sizeof(lw_expr_stack_node_t));
+	n -> next = NULL;
+	n -> prev = s -> tail;
+	n -> term = lw_expr_term_dup(t);
+	
+	if (s -> head)
+	{
+		s -> tail -> next = n;
+		s -> tail = n;
+	}
+	else
+	{
+		s -> head = n;
+		s -> tail = n;
+	}
+}
+
+lw_expr_term_t *lw_expr_stack_pop(lw_expr_stack_t *s)
+{
+	lw_expr_term_t *t;
+	lw_expr_stack_node_t *n;
+	
+	if (!(s -> tail))
+		return NULL;
+	
+	n = s -> tail;
+	s -> tail = n -> prev;
+	if (!(n -> prev))
+	{
+		s -> head = NULL;
+	}
+	
+	t = n -> term;
+	n -> term = NULL;
+	
+	lw_free(n);
+	
+	return t;
+}
+
+/*
+take an expression stack s and scan for operations that can be completed
+
+return -1 on error, 0 on no error
+
+possible errors are: division by zero or unknown operator
+
+theory of operation:
+
+scan the stack for an operator which has two constants preceding it (binary)
+or 1 constant preceding it (unary) and if found, perform the calculation
+and replace the operator and its operands with the result
+
+repeat the scan until no futher simplications are found or if there are no
+further operators or only a single term remains
+
+*/
+int lw_expr_reval(lw_expr_stack_t *s, lw_expr_stack_t *(*sfunc)(char *sym, int stype, void *state), void *state)
+{
+	lw_expr_stack_node_t *n, *n2;
+	lw_expr_stack_t *ss;
+	int c;
+	
+next_iter_sym:
+	// resolve symbols
+	// symbols that do not resolve to an expression are left alone
+	for (c = 0, n = s -> head; n; n = n -> next)
+	{
+		if (n -> term -> term_type == LW_TERM_SYM)
+		{
+			ss = sfunc(n -> term -> symbol, n -> term -> value, state);
+			if (ss)
+			{
+				c++;
+				// splice in the result stack
+				if (n -> prev)
+				{
+					n -> prev -> next = ss -> head;
+				}
+				else
+				{
+					s -> head = ss -> head;
+				}
+				ss -> head -> prev = n -> prev;
+				ss -> tail -> next = n -> next;
+				if (n -> next)
+				{
+					n -> next -> prev = ss -> tail;
+				}
+				else
+				{
+					s -> tail = ss -> tail;
+				}
+				lw_expr_term_free(n -> term);
+				lw_free(n);
+				n = ss -> tail;
+				
+				ss -> head = NULL;
+				ss -> tail = NULL;
+				lw_expr_stack_free(ss);
+			}
+		}
+	}
+	if (c)
+		goto next_iter_sym;
+
+next_iter:	
+	// a single term
+	if (s -> head == s -> tail)
+		return 0;
+	
+	// search for an operator
+	for (n = s -> head; n; n = n -> next)
+	{
+		if (n -> term -> term_type == LW_TERM_OPER)
+		{
+			if (n -> term -> value == LW_OPER_NEG
+				|| n -> term -> value == LW_OPER_COM
+				)
+			{
+				// unary operator
+				if (n -> prev && n -> prev -> term -> term_type == LW_TERM_INT)
+				{
+					// a unary operator we can resolve
+					// we do the op then remove the term "n" is pointing at
+					if (n -> term -> value == LW_OPER_NEG)
+					{
+						n -> prev -> term -> value = -(n -> prev -> term -> value);
+					}
+					else if (n -> term -> value == LW_OPER_COM)
+					{
+						n -> prev -> term -> value = ~(n -> prev -> term -> value);
+					}
+					n -> prev -> next = n -> next;
+					if (n -> next)
+						n -> next -> prev = n -> prev;
+					else
+						s -> tail = n -> prev;	
+					
+					lw_expr_term_free(n -> term);
+					lw_free(n);
+					break;
+				}
+			}
+			else
+			{
+				// binary operator
+				if (n -> prev && n -> prev -> prev && n -> prev -> term -> term_type == LW_TERM_INT && n -> prev -> prev -> term -> term_type == LW_TERM_INT)
+				{
+					// a binary operator we can resolve
+					switch (n -> term -> value)
+					{
+					case LW_OPER_PLUS:
+						n -> prev -> prev -> term -> value += n -> prev -> term -> value;
+						break;
+
+					case LW_OPER_MINUS:
+						n -> prev -> prev -> term -> value -= n -> prev -> term -> value;
+						break;
+
+					case LW_OPER_TIMES:
+						n -> prev -> prev -> term -> value *= n -> prev -> term -> value;
+						break;
+
+					case LW_OPER_DIVIDE:
+						if (n -> prev -> term -> value == 0)
+							return -1;
+						n -> prev -> prev -> term -> value /= n -> prev -> term -> value;
+						break;
+
+					case LW_OPER_MOD:
+						if (n -> prev -> term -> value == 0)
+							return -1;
+						n -> prev -> prev -> term -> value %= n -> prev -> term -> value;
+						break;
+
+					case LW_OPER_INTDIV:
+						if (n -> prev -> term -> value == 0)
+							return -1;
+						n -> prev -> prev -> term -> value /= n -> prev -> term -> value;
+						break;
+
+					case LW_OPER_BWAND:
+						n -> prev -> prev -> term -> value &= n -> prev -> term -> value;
+						break;
+
+					case LW_OPER_BWOR:
+						n -> prev -> prev -> term -> value |= n -> prev -> term -> value;
+						break;
+
+					case LW_OPER_BWXOR:
+						n -> prev -> prev -> term -> value ^= n -> prev -> term -> value;
+						break;
+
+					case LW_OPER_AND:
+						n -> prev -> prev -> term -> value = (n -> prev -> term -> value && n -> prev -> prev -> term -> value) ? 1 : 0;
+						break;
+
+					case LW_OPER_OR:
+						n -> prev -> prev -> term -> value = (n -> prev -> term -> value || n -> prev -> prev -> term -> value) ? 1 : 0;
+						break;
+
+					default:
+						// return error if unknown operator!
+						return -1;
+					}
+
+					// now remove the two unneeded entries from the stack
+					n -> prev -> prev -> next = n -> next;
+					if (n -> next)
+						n -> next -> prev = n -> prev -> prev;
+					else
+						s -> tail = n -> prev -> prev;	
+					
+					lw_expr_term_free(n -> term);
+					lw_expr_term_free(n -> prev -> term);
+					lw_free(n -> prev);
+					lw_free(n);
+					break;
+				}
+			}
+		}
+	}
+	// note for the terminally confused about dynamic memory and pointers:
+	// n will not be NULL even after the lw_free calls above so
+	// this test will still work (n will be a dangling pointer)
+	// (n will only be NULL if we didn't find any operators to simplify)
+	if (n)
+		goto next_iter;
+	
+	return 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lwlink/expr.h	Wed Jan 19 22:27:17 2011 -0700
@@ -0,0 +1,108 @@
+/*
+expr.h
+Copyright © 2009 William Astle
+
+This file is part of LWLINK.
+
+LWLINK 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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+Definitions for expression evaluator
+*/
+
+#ifndef __expr_h_seen__
+#define __expr_h_seen__
+
+#ifndef __expr_c_seen__
+#define __expr_E__ extern
+#else
+#define __expr_E__
+#endif
+
+// term types
+#define LW_TERM_NONE		0
+#define LW_TERM_OPER		1	// an operator
+#define LW_TERM_INT		2	// 32 bit signed integer
+#define LW_TERM_SYM		3	// symbol reference
+
+// operator types
+#define LW_OPER_NONE		0
+#define LW_OPER_PLUS		1	// +
+#define LW_OPER_MINUS	2	// -
+#define LW_OPER_TIMES	3	// *
+#define LW_OPER_DIVIDE	4	// /
+#define LW_OPER_MOD		5	// %
+#define LW_OPER_INTDIV	6	// \ (don't end line with \)
+#define LW_OPER_BWAND	7	// bitwise AND
+#define LW_OPER_BWOR		8	// bitwise OR
+#define LW_OPER_BWXOR	9	// bitwise XOR
+#define LW_OPER_AND		10	// boolean AND
+#define LW_OPER_OR		11	// boolean OR
+#define LW_OPER_NEG		12	// - unary negation (2's complement)
+#define LW_OPER_COM		13	// ^ unary 1's complement
+
+
+// term structure
+typedef struct lw_expr_term_s
+{
+	int term_type;		// type of term (see above)
+	char *symbol;		// name of a symbol
+	int value;			// value of the term (int) or operator number (OPER)
+} lw_expr_term_t;
+
+// type for an expression evaluation stack
+typedef struct lw_expr_stack_node_s lw_expr_stack_node_t;
+struct lw_expr_stack_node_s
+{
+	lw_expr_term_t		*term;
+	lw_expr_stack_node_t	*prev;
+	lw_expr_stack_node_t	*next;	
+};
+
+typedef struct lw_expr_stack_s
+{
+	lw_expr_stack_node_t *head;
+	lw_expr_stack_node_t *tail;
+} lw_expr_stack_t;
+
+__expr_E__ void lw_expr_term_free(lw_expr_term_t *t);
+__expr_E__ lw_expr_term_t *lw_expr_term_create_oper(int oper);
+__expr_E__ lw_expr_term_t *lw_expr_term_create_sym(char *sym, int symtype);
+__expr_E__ lw_expr_term_t *lw_expr_term_create_int(int val);
+__expr_E__ lw_expr_term_t *lw_expr_term_dup(lw_expr_term_t *t);
+
+__expr_E__ void lw_expr_stack_free(lw_expr_stack_t *s);
+__expr_E__ lw_expr_stack_t *lw_expr_stack_create(void);
+__expr_E__ lw_expr_stack_t *lw_expr_stack_dup(lw_expr_stack_t *s);
+
+__expr_E__ void lw_expr_stack_push(lw_expr_stack_t *s, lw_expr_term_t *t);
+__expr_E__ lw_expr_term_t *lw_expr_stack_pop(lw_expr_stack_t *s);
+
+// simplify expression
+__expr_E__ int lw_expr_reval(lw_expr_stack_t *s, lw_expr_stack_t *(*sfunc)(char *sym, int symtype, void *state), void *state);
+
+// useful macros
+// is the expression "simple" (one term)?
+#define lw_expr_is_simple(s) ((s) -> head == (s) -> tail)
+
+// is the expression constant?
+#define lw_expr_is_constant(s) (lw_expr_is_simple(s) && (!((s) -> head) || (s) -> head -> term -> term_type == LW_TERM_INT))
+
+// get the constant value of an expression or 0 if not constant
+#define lw_expr_get_value(s) (lw_expr_is_constant(s) ? ((s) -> head ? (s) -> head -> term -> value : 0) : 0)
+
+#undef __expr_E__
+
+#endif // __expr_h_seen__
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lwlink/link.c	Wed Jan 19 22:27:17 2011 -0700
@@ -0,0 +1,460 @@
+/*
+link.c
+Copyright © 2009 William Astle
+
+This file is part of LWLINK.
+
+LWLINK 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 3 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, see <http://www.gnu.org/licenses/>.
+
+
+Resolve section and symbol addresses; handle incomplete references
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "expr.h"
+#include "lwlink.h"
+#include "util.h"
+
+struct section_list *sectlist = NULL;
+int nsects = 0;
+static int nforced = 0;
+
+void check_section_name(char *name, int *base, fileinfo_t *fn)
+{
+	int sn;
+
+	if (fn -> forced == 0)
+		return;
+
+	for (sn = 0; sn < fn -> nsections; sn++)
+	{
+		if (!strcmp(name, fn -> sections[sn].name))
+		{
+			// we have a match
+			sectlist = lw_realloc(sectlist, sizeof(struct section_list) * (nsects + 1));
+			sectlist[nsects].ptr = &(fn -> sections[sn]);
+					
+			fn -> sections[sn].processed = 1;
+			fn -> sections[sn].loadaddress = *base;
+			*base += fn -> sections[sn].codesize;
+			nsects++;
+		}
+	}
+	for (sn = 0; sn < fn -> nsubs; sn++)
+	{
+		check_section_name(name, base, fn -> subs[sn]);
+	}
+}
+
+void add_matching_sections(char *name, int yesflags, int noflags, int *base);
+void check_section_flags(int yesflags, int noflags, int *base, fileinfo_t *fn)
+{
+	int sn;
+
+	if (fn -> forced == 0)
+		return;
+
+	for (sn = 0; sn < fn -> nsections; sn++)
+	{
+		// ignore if the noflags tell us to
+		if (noflags && (fn -> sections[sn].flags & noflags))
+			continue;
+		// ignore unless the yesflags tell us not to
+		if (yesflags && (fn -> sections[sn].flags & yesflags == 0))
+			continue;
+		// ignore it if already processed
+		if (fn -> sections[sn].processed)
+			continue;
+
+		// we have a match - now collect *all* sections of the same name!
+		add_matching_sections(fn -> sections[sn].name, 0, 0, base);
+		
+		// and then continue looking for sections
+	}
+	for (sn = 0; sn < fn -> nsubs; sn++)
+	{
+		check_section_flags(yesflags, noflags, base, fn -> subs[sn]);
+	}
+}
+
+
+
+void add_matching_sections(char *name, int yesflags, int noflags, int *base)
+{
+	int fn;
+	if (name)
+	{
+		// named section
+		// look for all instances of a section by the specified name
+		// and resolve base addresses and add to the list
+		for (fn = 0; fn < ninputfiles; fn++)
+		{
+			check_section_name(name, base, inputfiles[fn]);
+		}
+	}
+	else
+	{
+		// wildcard section
+		// named section
+		// look for all instances of a section by the specified name
+		// and resolve base addresses and add to the list
+		for (fn = 0; fn < ninputfiles; fn++)
+		{
+			check_section_flags(yesflags, noflags, base, inputfiles[fn]);
+		}
+	}
+}
+
+// work out section load order and resolve base addresses for each section
+// make a list of sections to load in order
+void resolve_sections(void)
+{
+	int laddr = 0;
+	int ln, sn, fn;
+	
+	for (ln = 0; ln < linkscript.nlines; ln++)
+	{
+		if (linkscript.lines[ln].loadat >= 0)
+			laddr = linkscript.lines[ln].loadat;
+		add_matching_sections(linkscript.lines[ln].sectname, linkscript.lines[ln].yesflags, linkscript.lines[ln].noflags, &laddr);
+		
+		if (linkscript.lines[ln].sectname)
+		{
+		}
+		else
+		{
+			// wildcard section
+			// look for all sections not yet processed that match flags
+
+			int f = 0;
+			int fn0, sn0;
+			char *sname;
+			
+			// named section
+			// look for all instances of a section by the specified name
+			// and resolve base addresses and add to the list
+			for (fn0 = 0; fn0 < ninputfiles; fn0++)
+			{
+				for (sn0 = 0; sn0 < inputfiles[fn0] -> nsections; sn0++)
+				{
+					// ignore if the "no flags" bit says to
+					if (linkscript.lines[ln].noflags && (inputfiles[fn0] -> sections[sn0].flags & linkscript.lines[ln].noflags))
+						continue;
+					// ignore unless the yes flags tell us not to
+					if (linkscript.lines[ln].yesflags && (inputfiles[fn0] -> sections[sn0].flags & linkscript.lines[ln].yesflags == 0))
+						continue;
+					if (inputfiles[fn0] -> sections[sn0].processed == 0)
+					{
+						sname = inputfiles[fn0] -> sections[sn0].name;
+						for (fn = 0; fn < ninputfiles; fn++)
+						{
+							for (sn = 0; sn < inputfiles[fn] -> nsections; sn++)
+							{
+								if (!strcmp(sname, inputfiles[fn] -> sections[sn].name))
+								{
+									// we have a match
+									sectlist = lw_realloc(sectlist, sizeof(struct section_list) * (nsects + 1));
+									sectlist[nsects].ptr = &(inputfiles[fn] -> sections[sn]);
+						
+									inputfiles[fn] -> sections[sn].processed = 1;
+									if (!f && linkscript.lines[ln].loadat >= 0)
+									{
+										f = 1;
+										sectlist[nsects].forceaddr = 1;
+										laddr = linkscript.lines[ln].loadat;
+									}
+									else
+									{
+										sectlist[nsects].forceaddr = 0;
+									}
+									inputfiles[fn] -> sections[sn].loadaddress = laddr;
+									laddr += inputfiles[fn] -> sections[sn].codesize;
+									nsects++;
+								}
+							}
+						}
+					}
+				}
+			}
+		}
+	}
+	
+	// theoretically, all the base addresses are set now
+}
+
+lw_expr_stack_t *find_external_sym_recurse(char *sym, fileinfo_t *fn)
+{
+	int sn;
+	lw_expr_stack_t *r;
+	lw_expr_term_t *term;
+	symtab_t *se;
+	int val;
+	
+	for (sn = 0; sn < fn -> nsections; sn++)
+	{
+		for (se = fn -> sections[sn].exportedsyms; se; se = se -> next)
+		{
+			if (!strcmp(sym, se -> sym))
+			{
+				if (!(fn -> forced))
+				{
+					fn -> forced = 1;
+					nforced = 1;
+				}
+				val = se -> offset + fn -> sections[sn].loadaddress;
+				r = lw_expr_stack_create();
+				term = lw_expr_term_create_int(val & 0xffff);
+				lw_expr_stack_push(r, term);
+				lw_expr_term_free(term);
+				return r;
+			}
+		}
+	}
+		
+	for (sn = 0; sn < fn -> nsubs; sn++)
+	{
+		r = find_external_sym_recurse(sym, fn -> subs[sn]);
+		if (r)
+		{
+			if (!(fn -> forced))
+			{
+				nforced = 1;
+				fn -> forced = 1;
+			}
+			return r;
+		}
+	}
+	return NULL;
+}
+
+// resolve all incomplete references now
+// anything that is unresolvable at this stage will throw an error
+// because we know the load address of every section now
+lw_expr_stack_t *resolve_sym(char *sym, int symtype, void *state)
+{
+	section_t *sect = state;
+	lw_expr_term_t *term;
+	int val = 0, i, fn;
+	lw_expr_stack_t *s;
+	symtab_t *se;
+	fileinfo_t *fp;
+
+	if (symtype == 1)
+	{
+		// local symbol
+		if (!sym)
+		{
+			val = sect -> loadaddress;
+			goto out;
+		}
+		
+		// start with this section
+		for (se = sect -> localsyms; se; se = se -> next)
+		{
+			if (!strcmp(se -> sym, sym))
+			{
+				val = se -> offset + sect -> loadaddress;
+				goto out;
+			}
+		}
+		// not in this section - check all sections in this file
+		for (i = 0; i < sect -> file -> nsections; i++)
+		{
+			for (se = sect -> file -> sections[i].localsyms; se; se = se -> next)
+			{
+				if (!strcmp(se -> sym, sym))
+				{
+					val = se -> offset + sect -> file -> sections[i].loadaddress;
+					goto out;
+				}
+			}
+		}
+		// not found
+		symerr = 1;
+		fprintf(stderr, "Local symbol %s not found in %s:%s\n", sanitize_symbol(sym), sect -> file -> filename, sect -> name);
+		goto outerr;
+	}
+	else
+	{
+		// external symbol
+		// read all files in order until found (or not found)
+		if (sect)
+		{
+			for (fp = sect -> file; fp; fp = fp -> parent)
+			{
+				s = find_external_sym_recurse(sym, fp);
+				if (s)
+					return s;
+			}
+		}
+
+		for (fn = 0; fn < ninputfiles; fn++)
+		{
+			s = find_external_sym_recurse(sym, inputfiles[fn]);
+			if (s)
+				return s;
+		}
+		if (sect)
+		{
+			fprintf(stderr, "External symbol %s not found in %s:%s\n", sanitize_symbol(sym), sect -> file -> filename, sect -> name);
+		}
+		else
+		{
+			fprintf(stderr, "External symbol %s not found\n", sym);
+		}
+		symerr = 1;
+		goto outerr;
+	}
+	fprintf(stderr, "Shouldn't ever get here!!!\n");
+	exit(88);
+out:
+	s = lw_expr_stack_create();
+	term = lw_expr_term_create_int(val & 0xffff);
+	lw_expr_stack_push(s, term);
+	lw_expr_term_free(term);
+	return s;
+outerr:
+	return NULL;
+}
+
+void resolve_references(void)
+{
+	int sn;
+	reloc_t *rl;
+	int rval;
+
+	// resolve entry point if required
+	// this must resolve to an *exported* symbol and will resolve to the
+	// first instance of that symbol
+	if (linkscript.execsym)
+	{
+		lw_expr_stack_t *s;
+		
+		s = resolve_sym(linkscript.execsym, 0, NULL);
+		if (!s)
+		{
+			fprintf(stderr, "Cannot resolve exec address '%s'\n", linkscript.execsym);
+			symerr = 1;
+		}
+		else
+		{
+			linkscript.execaddr = lw_expr_get_value(s);
+			lw_expr_stack_free(s);
+		}
+	}
+	
+	for (sn = 0; sn < nsects; sn++)
+	{
+		for (rl = sectlist[sn].ptr -> incompletes; rl; rl = rl -> next)
+		{
+			// do a "simplify" on the expression
+			rval = lw_expr_reval(rl -> expr, resolve_sym, sectlist[sn].ptr);
+
+			// is it constant? error out if not
+			if (rval != 0 || !lw_expr_is_constant(rl -> expr))
+			{
+				fprintf(stderr, "Incomplete reference at %s:%s+%02X\n", sectlist[sn].ptr -> file -> filename, sectlist[sn].ptr -> name, rl -> offset);
+				symerr = 1;
+			}
+			else
+			{
+				// put the value into the relocation address
+				rval = lw_expr_get_value(rl -> expr);
+				if (rl -> flags & RELOC_8BIT)
+				{
+					sectlist[sn].ptr -> code[rl -> offset] = rval & 0xff;
+				}
+				else
+				{
+					sectlist[sn].ptr -> code[rl -> offset] = (rval >> 8) & 0xff;
+					sectlist[sn].ptr -> code[rl -> offset + 1] = rval & 0xff;
+				}
+			}
+		}
+	}
+	
+	if (symerr)
+		exit(1);
+}
+
+/*
+This is just a pared down version of the algo for resolving references.
+*/
+void resolve_files(void)
+{
+	int sn;
+	int fn;
+	reloc_t *rl;
+	lw_expr_stack_t *te;
+	
+	int rval;
+
+	// resolve entry point if required
+	// this must resolve to an *exported* symbol and will resolve to the
+	// first instance of that symbol
+	if (linkscript.execsym)
+	{
+		lw_expr_stack_t *s;
+		
+		s = resolve_sym(linkscript.execsym, 0, NULL);
+		if (!s)
+		{
+			fprintf(stderr, "Cannot resolve exec address '%s'\n", sanitize_symbol(linkscript.execsym));
+			symerr = 1;
+		}
+	}
+	
+	do
+	{
+		nforced = 0;
+		for (fn = 0; fn < ninputfiles; fn++)
+		{
+			if (inputfiles[fn] -> forced == 0)
+				continue;
+	
+			for (sn = 0; sn < inputfiles[fn] -> nsections; sn++)
+			{
+				for (rl = inputfiles[fn] -> sections[sn].incompletes; rl; rl = rl -> next)
+				{
+					// do a "simplify" on the expression
+					te = lw_expr_stack_dup(rl -> expr);
+					rval = lw_expr_reval(te, resolve_sym, &(inputfiles[fn] -> sections[sn]));
+					
+					// is it constant? error out if not
+					if (rval != 0 || !lw_expr_is_constant(te))
+					{
+						fprintf(stderr, "Incomplete reference at %s:%s+%02X\n", inputfiles[fn] -> filename, inputfiles[fn] -> sections[sn].name, rl -> offset);
+						symerr = 1;
+					}
+					lw_expr_stack_free(te);
+				}
+			}
+		}
+	}
+	while (nforced == 1);
+
+	if (symerr)
+		exit(1);
+	
+	// theoretically, all files referenced by other files now have "forced" set to 1
+	for (fn = 0; fn < ninputfiles; fn++)
+	{
+		if (inputfiles[fn] -> forced == 1)
+			continue;
+		
+		fprintf(stderr, "Warning: %s (%d) does not resolve any symbols\n", inputfiles[fn] -> filename, fn);
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lwlink/lwlink.c	Wed Jan 19 22:27:17 2011 -0700
@@ -0,0 +1,142 @@
+/*
+lwlink.c
+Copyright © 2009 William Astle
+
+This file is part of LWLINK.
+
+LWLINK 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 3 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, see <http://www.gnu.org/licenses/>.
+
+
+
+*/
+
+#define __lwlink_c_seen__
+
+#include <argp.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "lwlink.h"
+#include "util.h"
+
+int debug_level = 0;
+int outformat = OUTPUT_DECB;
+char *outfile = NULL;
+char *scriptfile = NULL;
+int symerr = 0;
+char *map_file = NULL;
+
+fileinfo_t **inputfiles = NULL;
+int ninputfiles = 0;
+
+int nlibdirs = 0;
+char **libdirs = NULL;
+
+int nscriptls = 0;
+char **scriptls = NULL;
+
+void add_input_file(char *fn)
+{
+	inputfiles = lw_realloc(inputfiles, sizeof(fileinfo_t *) * (ninputfiles + 1));
+	inputfiles[ninputfiles] = lw_malloc(sizeof(fileinfo_t));
+	memset(inputfiles[ninputfiles], 0, sizeof(fileinfo_t));
+	inputfiles[ninputfiles] -> forced = 1;
+	inputfiles[ninputfiles++] -> filename = lw_strdup(fn);
+}
+
+void add_input_library(char *libname)
+{
+	inputfiles = lw_realloc(inputfiles, sizeof(fileinfo_t *) * (ninputfiles + 1));
+	inputfiles[ninputfiles] = lw_malloc(sizeof(fileinfo_t));
+	memset(inputfiles[ninputfiles], 0, sizeof(fileinfo_t));
+	inputfiles[ninputfiles] -> islib = 1;
+	inputfiles[ninputfiles] -> forced = 0;
+	inputfiles[ninputfiles++] -> filename = lw_strdup(libname);	
+}
+
+void add_library_search(char *libdir)
+{
+	libdirs = lw_realloc(libdirs, sizeof(char*) * (nlibdirs + 1));
+	libdirs[nlibdirs] = lw_strdup(libdir);
+	nlibdirs++;
+}
+
+void add_section_base(char *sectspec)
+{
+	char *base;
+	int baseaddr;
+	char *t;
+	int l;
+	
+	base = strchr(sectspec, '=');
+	if (!base)
+	{
+		l = strlen(sectspec);
+		baseaddr = 0;
+	}
+	else
+	{
+		baseaddr = strtol(base + 1, NULL, 16);
+		l = base - sectspec;
+		*base = '\0';
+	}
+	baseaddr = baseaddr & 0xffff;
+	
+	t = lw_malloc(l + 25);
+	sprintf(t, "section %s load %04X", sectspec, baseaddr);
+	if (base)
+		*base = '=';
+	
+	scriptls = lw_realloc(scriptls, sizeof(char *) * (nscriptls + 1));
+	scriptls[nscriptls++] = t;
+}
+
+char *sanitize_symbol(char *symbol)
+{
+	static char symbuf[2048];
+	char *sym = symbol;
+	char *tp = symbuf;
+	
+	for (; *sym; sym++)
+	{
+		int c1 = *sym;
+		if (c1 == '\\')
+		{
+			*tp++ = '\\';
+			*tp++ = '\\';
+			continue;
+		}
+		if (c1 < 32 || c1 > 126)
+		{
+			int c;
+			*tp++ = '\\';
+			c = c1 >> 4;
+			c += 48;
+			if (c > 57)
+				c += 7;
+			*tp++ = c;
+			c = c1 & 15;
+			c += 48;
+			if (c > 57)
+				c += 7;
+			*tp++ = c;
+			continue;
+		}
+		*tp++ = c1;
+	}
+	*tp = 0;
+	return symbuf;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lwlink/lwlink.h	Wed Jan 19 22:27:17 2011 -0700
@@ -0,0 +1,156 @@
+/*
+lwlink.h
+Copyright © 2009 William Astle
+
+This file is part of LWLINK.
+
+LWLINK 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 3 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, see <http://www.gnu.org/licenses/>.
+
+Contains the main defs used by the linker
+*/
+
+
+#ifndef __lwlink_h_seen__
+#define __lwlink_h_seen__
+
+#include "expr.h"
+
+#define OUTPUT_DECB		0	// DECB multirecord format
+#define OUTPUT_RAW		1	// raw sequence of bytes
+#define OUTPUT_LWEX0	2	// LWOS LWEX binary version 0
+
+typedef struct symtab_s symtab_t;
+struct symtab_s
+{
+	unsigned char *sym;		// symbol name
+	int offset;				// local offset
+//	int realval;			// resolved value
+	symtab_t *next;			// next symbol
+};
+
+#define RELOC_NORM	0		// all default (16 bit)
+#define RELOC_8BIT	1		// only use the low 8 bits for the reloc
+typedef struct reloc_s reloc_t;
+struct reloc_s
+{
+	int offset;				// where in the section
+	int flags;				// flags for the relocation
+	lw_expr_stack_t *expr;	// the expression to calculate it
+	reloc_t *next;			// ptr to next relocation
+};
+
+typedef struct fileinfo_s fileinfo_t;
+
+#define SECTION_BSS		1
+typedef struct
+{
+	unsigned char *name;	// name of the section
+	int flags;				// section flags
+	int codesize;			// size of the code
+	unsigned char *code;	// pointer to the code
+	int loadaddress;		// the actual load address of the section
+	int processed;			// was the section processed yet?
+		
+	symtab_t *localsyms;	// local symbol table
+	symtab_t *exportedsyms;	// exported symbols table
+	
+	reloc_t *incompletes;	// table of incomplete references
+	
+	fileinfo_t *file;		// the file we are in
+} section_t;
+
+struct fileinfo_s
+{
+	char *filename;
+	unsigned char *filedata;
+	long filesize;
+	section_t *sections;
+	int nsections;
+	int islib;				// set to true if the file is a "-l" option
+
+	int forced;				// set to true if the file is a "forced" include
+
+	// "sub" files (like in archives or libraries)
+	int nsubs;
+	fileinfo_t **subs;
+	fileinfo_t *parent;
+};
+
+struct section_list
+{
+	section_t *ptr;		// ptr to section structure
+	int forceaddr;		// was this force to an address by the link script?
+};
+
+#ifndef __link_c_seen__
+extern struct section_list *sectlist;
+extern int nsects;
+#endif
+
+
+#ifndef __lwlink_c_seen__
+
+extern int debug_level;
+extern int outformat;
+extern char *outfile;
+extern int ninputfiles;
+extern fileinfo_t **inputfiles;
+extern char *scriptfile;
+
+extern int nlibdirs;
+extern char **libdirs;
+
+extern int nscriptls;
+extern char **scriptls;
+
+extern int symerr;
+
+extern char *map_file;
+
+#define __lwlink_E__ extern
+#else
+#define __lwlink_E__
+#endif // __lwlink_c_seen__
+
+__lwlink_E__ void add_input_file(char *fn);
+__lwlink_E__ void add_input_library(char *fn);
+__lwlink_E__ void add_library_search(char *fn);
+__lwlink_E__ void add_section_base(char *fn);
+__lwlink_E__ char *sanitize_symbol(char *sym);
+
+#undef __lwlink_E__
+
+struct scriptline_s
+{
+	char *sectname;				// name of section, NULL for wildcard
+	int loadat;					// address to load at (or -1)
+	int noflags;				// flags to NOT have
+	int yesflags;				// flags to HAVE
+};
+
+typedef struct
+{
+	int nlines;					// number of lines in the script
+	struct scriptline_s *lines;	// the parsed script lines (section)
+	int padsize;				// the size to pad to, -1 for none
+	char *execsym;				// entry point symbol
+	int execaddr;				// execution address (entry point)
+	int stacksize;				// stack size
+} linkscript_t;
+
+#ifndef __script_c_seen__
+extern linkscript_t linkscript;
+#endif
+
+#endif //__lwlink_h_seen__
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lwlink/main.c	Wed Jan 19 22:27:17 2011 -0700
@@ -0,0 +1,194 @@
+/*
+main.c
+Copyright © 2009 William Astle
+
+This file is part of LWLINK.
+
+LWLINK 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 3 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, see <http://www.gnu.org/licenses/>.
+
+
+Implements the program startup code
+
+*/
+
+#include <argp.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "lwlink.h"
+
+char *program_name;
+
+// command line option handling
+const char *argp_program_version = "LWLINK from " PACKAGE_STRING;
+const char *argp_program_bug_address = PACKAGE_BUGREPORT;
+
+static error_t parse_opts(int key, char *arg, struct argp_state *state)
+{
+	switch (key)
+	{
+	case 'o':
+		// output
+		outfile = arg;
+		break;
+	
+	case 's':
+		// script file
+		scriptfile = arg;
+		break;
+
+	case 'd':
+		// debug
+		debug_level++;
+		break;
+	
+	case 'b':
+		// decb output
+		outformat = OUTPUT_DECB;
+		break;
+	
+	case 'r':
+		// raw binary output
+		outformat = OUTPUT_RAW;
+		break;
+	
+	case 'f':
+		// output format
+		if (!strcasecmp(arg, "decb"))
+			outformat = OUTPUT_DECB;
+		else if (!strcasecmp(arg, "raw"))
+			outformat = OUTPUT_RAW;
+		else if (!strcasecmp(arg, "lwex0") || !strcasecmp(arg, "lwex"))
+			outformat = OUTPUT_LWEX0;
+		else
+		{
+			fprintf(stderr, "Invalid output format: %s\n", arg);
+			exit(1);
+		}
+		break;
+	case ARGP_KEY_END:
+		// done; sanity check
+		if (!outfile)
+			outfile = "a.out";
+		break;
+	
+	case 'l':
+		add_input_library(arg);
+		break;
+	
+	case 'L':
+		add_library_search(arg);
+		break;
+	
+	case 0x100:
+		add_section_base(arg);
+		break;
+	
+	case 'm':
+		map_file = arg;
+		break;
+	
+	case ARGP_KEY_ARG:
+		add_input_file(arg);
+		break;
+		
+	default:
+		return ARGP_ERR_UNKNOWN;
+	}
+	return 0;
+}
+
+static struct argp_option options[] =
+{
+	{ "output",		'o',	"FILE",	0,
+				"Output to FILE"},
+	{ "debug",		'd',	0,		0,
+				"Set debug mode"},
+	{ "format",		'f',	"TYPE",	0,
+				"Select output format: decb, raw, lwex"},
+	{ "decb",		'b',	0,		0,
+				"Generate DECB .bin format output, equivalent of --format=decb"},
+	{ "raw",		'r',	0,		0,
+				"Generate raw binary format output, equivalent of --format=raw"},
+	{ "script",		's',	"FILE",		0,
+				"Specify the linking script (overrides the built in defaults)"},
+	{ "library",	'l',	"LIBSPEC",	0,
+				"Read library libLIBSPEC.a from the search path" },
+	{ "library-path", 'L',	"DIR",		0,
+				"Add DIR to the library search path" },
+	{ "section-base", 0x100,	"SECT=BASE",	0,
+				"Load section SECT at BASE" },
+	{ "map",		'm',	"FILE",		0,
+				"Output informaiton about the link" },
+	{ 0 }
+};
+
+static struct argp argp =
+{
+	options,
+	parse_opts,
+	"<input file> ...",
+	"LWLINK, a HD6309 and MC6809 cross-linker"
+};
+
+extern void read_files(void);
+extern void setup_script(void);
+extern void resolve_files(void);
+extern void resolve_sections(void);
+extern void resolve_references(void);
+extern void do_output(void);
+extern void display_map(void);
+
+// main function; parse command line, set up assembler state, and run the
+// assembler on the first file
+int main(int argc, char **argv)
+{
+	program_name = argv[0];
+
+	argp_parse(&argp, argc, argv, 0, 0, NULL);
+	if (ninputfiles == 0)
+	{
+		fprintf(stderr, "No input files\n");
+		exit(1);
+	}
+
+	unlink(outfile);
+
+	// handle the linker script
+	setup_script();
+
+	// read the input files
+	read_files();
+
+	// trace unresolved references and determine which non-forced
+	// objects must be included
+	resolve_files();
+	
+	// resolve section bases and section order
+	resolve_sections();
+	
+	// resolve incomplete references
+	resolve_references();
+	
+	// do the actual output
+	do_output();
+
+	// display/output the link map
+	if (map_file)
+		display_map();
+
+	exit(0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lwlink/map.c	Wed Jan 19 22:27:17 2011 -0700
@@ -0,0 +1,113 @@
+/*
+map.c
+Copyright © 2009 William Astle
+
+This file is part of LWLINK.
+
+LWLINK 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 3 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, see <http://www.gnu.org/licenses/>.
+
+
+Output information about the linking process
+*/
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "lwlink.h"
+#include "util.h"
+
+struct symliste
+{
+	char *name;
+	char *fn;
+	int addr;
+	int ext;
+	struct symliste *next;
+};
+
+void display_map(void)
+{
+	FILE *of;
+	int sn;
+	int std = 0;
+	struct symliste *slist = NULL;
+	struct symliste *ce, *pe, *ne;
+	symtab_t *sym;
+	int i;
+	
+	if (!strcmp(map_file, "-"))
+	{
+		std = 1;
+		of = stdout;
+	}
+	else
+	{
+		of = fopen(map_file, "w");
+		if (!of)
+		{
+			fprintf(stderr, "Cannot open map file - using stdout\n");
+			std = 1;
+			of = stdout;
+		}
+	}
+
+	// display section list
+	for (sn = 0; sn < nsects; sn++)
+	{
+		fprintf(of, "Section: %s (%s) load at %04X, length %04X\n",
+				sanitize_symbol(sectlist[sn].ptr -> name),
+				sectlist[sn].ptr -> file -> filename,
+				sectlist[sn].ptr -> loadaddress,
+				sectlist[sn].ptr -> codesize
+			);
+	}
+
+	// generate a sorted list of symbols and display it
+	for (sn = 0; sn < nsects; sn++)
+	{
+		for (sym = sectlist[sn].ptr -> localsyms; sym; sym = sym -> next)
+		{
+			for (pe = NULL, ce = slist; ce; ce = ce -> next)
+			{
+				i = strcmp(ce -> name, sym -> sym);
+				if (i == 0)
+				{
+					i = strcmp(ce -> fn, sectlist[sn].ptr -> file -> filename);
+				}
+				if (i > 0)
+					break;
+				pe = ce;
+			}
+			ne = lw_malloc(sizeof(struct symliste));
+			ne -> ext = 0;
+			ne -> addr = sym -> offset + sectlist[sn].ptr -> loadaddress;
+			ne -> next = ce;
+			ne -> name = sym -> sym;
+			ne -> fn = sectlist[sn].ptr -> file -> filename;
+			if (pe)
+				pe -> next = ne;
+			else
+				slist = ne;
+		}
+	}
+	
+	for (ce = slist; ce; ce = ce -> next)
+	{
+		fprintf(of, "Symbol: %s (%s) = %04X\n", sanitize_symbol(ce -> name), ce -> fn, ce -> addr);
+	}
+
+	if (!std)
+		fclose(of);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lwlink/objdump.c	Wed Jan 19 22:27:17 2011 -0700
@@ -0,0 +1,346 @@
+/*
+objdump.c
+Copyright © 2009 William Astle
+
+This file is part of LWLINK
+
+LWLINK 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 3 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, see <http://www.gnu.org/licenses/>.
+
+
+A standalone program to dump an object file in a text form to stdout
+
+*/
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "util.h"
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+void read_lwobj16v0(unsigned char *filedata, long filesize);
+char *program_name;
+
+char *string_cleanup(char *sym)
+{
+	static char symbuf[4096];
+	int optr = 0;
+	while (*sym)
+	{
+		if (*sym < 33 || *sym > 126)
+		{
+			int c;
+			symbuf[optr++] = '\\';
+			c = *sym >> 4;
+			c+= 48;
+			if (c > 57)
+				c += 7;
+			symbuf[optr++] = c;
+			c = *sym & 15;
+			c += 48;
+			if (c > 57)
+				c += 7;
+			symbuf[optr++] = c;
+		}
+		else if (*sym == '\\')
+		{
+			symbuf[optr++] = '\\';
+			symbuf[optr++] = '\\';
+		}
+		else
+		{
+			symbuf[optr++] = *sym;
+		}
+		sym++;
+	}
+	symbuf[optr] = '\0';
+	return symbuf;
+}
+
+/*
+The logic of reading the entire file into memory is simple. All the symbol
+names in the file are NUL terminated strings and can be used directly without
+making additional copies.
+*/
+int main(int argc, char **argv)
+{
+	int i;
+	long size;
+	FILE *f;
+	long bread;
+	unsigned char *filedata;
+
+	program_name = argv[0];	
+	if (argc != 2)
+	{
+		fprintf(stderr, "Must specify exactly one input file.\n");
+		exit(1);
+	}
+
+	f = fopen(argv[1], "rb");
+	if (!f)
+	{
+		fprintf(stderr, "Can't open file %s:", argv[1]);
+		perror("");
+		exit(1);
+	}
+	fseek(f, 0, SEEK_END);
+	size = ftell(f);
+	rewind(f);
+		
+	filedata = lw_malloc(size);
+		
+	bread = fread(filedata, 1, size, f);
+	if (bread < size)
+	{
+		fprintf(stderr, "Short read on file %s (%ld/%ld):", argv[1], bread, size);
+		perror("");
+		exit(1);
+	}
+			
+	fclose(f);
+		
+	if (!memcmp(filedata, "LWOBJ16", 8))
+	{
+		// read v0 LWOBJ16 file
+		read_lwobj16v0(filedata, size);
+	}
+	else
+	{
+		fprintf(stderr, "%s: unknown file format\n", argv[1]);
+		exit(1);
+	}
+	exit(0);
+}
+
+// this macro is used to bail out if we run off the end of the file data
+// while parsing - it keeps the code below cleaner
+#define NEXTBYTE()	do { cc++; if (cc > filesize) { fprintf(stderr, "***invalid file format\n"); exit(1); } } while (0)
+// this macro is used to refer to the current byte in the stream
+#define CURBYTE()	(filedata[cc < filesize ? cc : filesize - 1])
+// this one will leave the input pointer past the trailing NUL
+#define CURSTR()	read_lwobj16v0_str(&cc, &filedata, filesize)
+unsigned char *read_lwobj16v0_str(long *cc1, unsigned char **filedata1, long filesize)
+{
+	int cc = *cc1;
+	unsigned char *filedata = *filedata1;
+	unsigned char *fp;
+	fp = &CURBYTE();
+	while (CURBYTE())
+		NEXTBYTE();
+	NEXTBYTE();
+	*cc1 = cc;
+	*filedata1 = filedata;
+	return fp;
+}
+// the function below can be switched to dealing with data coming from a
+// source other than an in-memory byte pool by adjusting the input data
+// in "fn" and the above two macros
+void read_lwobj16v0(unsigned char *filedata, long filesize)
+{
+	unsigned char *fp;
+	long cc;
+	int val;
+	int bss;
+
+	static char *opernames[] = {
+		"?",
+		"PLUS",
+		"MINUS",
+		"TIMES",
+		"DIVIDE",
+		"MOD",
+		"INTDIV",
+		"BWAND",
+		"BWOR",
+		"BWXOR",
+		"AND",
+		"OR",
+		"NEG",
+		"COM"
+	};
+	static const int numopers = 13;
+		
+	// start reading *after* the magic number
+	cc = 8;
+	
+	while (1)
+	{
+		bss = 0;
+		
+		// bail out if no more sections
+		if (!(CURBYTE()))
+			break;
+		
+		fp = CURSTR();
+		
+		printf("SECTION %s\n", fp);
+		
+		// read flags
+		while (CURBYTE())
+		{
+			switch (CURBYTE())
+			{
+			case 0x01:
+				printf("    FLAG: BSS\n");
+				bss = 1;
+				break;
+
+			default:
+				printf("    FLAG: %02X (unknown)\n", CURBYTE());
+				break;
+			}
+			NEXTBYTE();
+		}
+		// skip NUL terminating flags
+		NEXTBYTE();
+		
+		printf("    Local symbols:\n");
+		// now parse the local symbol table
+		while (CURBYTE())
+		{
+			fp = CURSTR();
+
+			// fp is the symbol name
+			val = (CURBYTE()) << 8;
+			NEXTBYTE();
+			val |= (CURBYTE());
+			NEXTBYTE();
+			// val is now the symbol value
+			
+			printf("        %s=%04X\n", string_cleanup(fp), val);
+			
+		}
+		// skip terminating NUL
+		NEXTBYTE();
+
+		printf("    Exported symbols\n");
+				
+		// now parse the exported symbol table
+		while (CURBYTE())
+		{
+			fp = CURSTR();
+
+			// fp is the symbol name
+			val = (CURBYTE()) << 8;
+			NEXTBYTE();
+			val |= (CURBYTE());
+			NEXTBYTE();
+			// val is now the symbol value
+			
+			printf("        %s=%04X\n", string_cleanup(fp), val);
+		}
+		// skip terminating NUL
+		NEXTBYTE();
+		
+		// now parse the incomplete references and make a list of
+		// external references that need resolution
+		printf("    Incomplete references\n");
+		while (CURBYTE())
+		{
+			printf("        (");
+			// parse the expression
+			while (CURBYTE())
+			{
+				int tt = CURBYTE();
+				NEXTBYTE();
+				switch (tt)
+				{
+				case 0x01:
+					// 16 bit integer
+					tt = CURBYTE() << 8;
+					NEXTBYTE();
+					tt |= CURBYTE();
+					NEXTBYTE();
+					// normalize for negatives...
+					if (tt > 0x7fff)
+						tt -= 0x10000;
+					printf(" I16=%d", tt);
+					break;
+				
+				case 0x02:
+					// external symbol reference
+					printf(" ES=%s", string_cleanup(CURSTR()));
+					break;
+					
+				case 0x03:
+					// internal symbol reference
+					printf(" IS=%s", string_cleanup(CURSTR()));
+					break;
+				
+				case 0x04:
+					// operator
+					if (CURBYTE() > 0 && CURBYTE() <= numopers)
+						printf(" OP=%s", opernames[CURBYTE()]);
+					else
+						printf(" OP=?");
+					NEXTBYTE();
+					break;
+
+				case 0x05:
+					// section base reference (NULL internal reference is
+					// the section base address
+					printf(" SB");
+					break;
+				
+				case 0xFF:
+					// section flags
+					printf(" FLAGS=%02X", CURBYTE());
+					NEXTBYTE();
+					break;
+					
+				default:
+					printf(" ERR");
+				}
+			}
+			// skip the NUL
+			NEXTBYTE();
+			
+			// fetch the offset
+			val = CURBYTE() << 8;
+			NEXTBYTE();
+			val |= CURBYTE() & 0xff;
+			NEXTBYTE();
+			printf(" ) @ %04X\n", val);
+		}
+		// skip the NUL terminating the relocations
+		NEXTBYTE();
+				
+		// now set code location and size and verify that the file
+		// contains data going to the end of the code (if !SECTION_BSS)
+		val = CURBYTE() << 8;
+		NEXTBYTE();
+		val |= CURBYTE();
+		NEXTBYTE();
+		
+		printf("    CODE %04X bytes", val);
+		
+		// skip the code if we're not in a BSS section
+		if (!bss)
+		{
+			int i;
+			for (i = 0; i < val; i++)
+			{
+				if (! (i % 16))
+				{
+					printf("\n    %04X ", i);
+				}
+				printf("%02X", CURBYTE());
+				NEXTBYTE();
+			}
+		}
+		printf("\n");
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lwlink/output.c	Wed Jan 19 22:27:17 2011 -0700
@@ -0,0 +1,215 @@
+/*
+output.c
+Copyright © 2009 William Astle
+
+This file is part of LWLINK.
+
+LWLINK 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 3 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, see <http://www.gnu.org/licenses/>.
+
+
+Actually output the binary
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "lwlink.h"
+
+// this prevents warnings about not using the return value of fwrite()
+// and, theoretically, can be replaced with a function that handles things
+// better in the future
+#define writebytes(s, l, c, f)	do { int r; r = fwrite((s), (l), (c), (f)); } while (0)
+
+void do_output_decb(FILE *of);
+void do_output_raw(FILE *of);
+void do_output_lwex0(FILE *of);
+
+void do_output(void)
+{
+	FILE *of;
+	
+	of = fopen(outfile, "wb");
+	if (!of)
+	{
+		fprintf(stderr, "Cannot open output file %s: ", outfile);
+		perror("");
+		exit(1);
+	}
+	
+	switch (outformat)
+	{
+	case OUTPUT_DECB:
+		do_output_decb(of);
+		break;
+	
+	case OUTPUT_RAW:
+		do_output_raw(of);
+		break;
+
+	case OUTPUT_LWEX0:
+		do_output_lwex0(of);
+		break;
+	
+	default:
+		fprintf(stderr, "Unknown output format doing output!\n");
+		exit(111);
+	}
+	
+	fclose(of);
+}
+
+void do_output_decb(FILE *of)
+{
+	int sn, sn2;
+	int cloc, olen;
+	unsigned char buf[5];
+	
+	for (sn = 0; sn < nsects; sn++)
+	{
+		if (sectlist[sn].ptr -> flags & SECTION_BSS)
+		{
+			// no output for a BSS section
+			continue;
+		}
+		if (sectlist[sn].ptr -> codesize == 0)
+		{
+			// don't generate output for a zero size section
+			continue;
+		}
+		
+		// calculate the length of this output block
+		cloc = sectlist[sn].ptr -> loadaddress;
+		olen = 0;
+		for (sn2 = sn; sn2 < nsects; sn2++)
+		{
+			// ignore BSS sections
+			if (sectlist[sn2].ptr -> flags & SECTION_BSS)
+				continue;
+			// ignore zero length sections
+			if (sectlist[sn2].ptr -> codesize == 0)
+				continue;
+			if (cloc != sectlist[sn2].ptr -> loadaddress)
+				break;
+			olen += sectlist[sn2].ptr -> codesize;
+			cloc += sectlist[sn2].ptr -> codesize;
+		}
+		
+		// write a preamble
+		buf[0] = 0x00;
+		buf[1] = olen >> 8;
+		buf[2] = olen & 0xff;
+		buf[3] = sectlist[sn].ptr -> loadaddress >> 8;
+		buf[4] = sectlist[sn].ptr -> loadaddress & 0xff;
+		writebytes(buf, 1, 5, of);
+		for (; sn < sn2; sn++)
+		{
+			if (sectlist[sn].ptr -> flags & SECTION_BSS)
+				continue;
+			if (sectlist[sn].ptr -> codesize == 0)
+				continue;
+			writebytes(sectlist[sn].ptr -> code, 1, sectlist[sn].ptr -> codesize, of);
+		}
+		sn--;
+	}
+	// write a postamble
+	buf[0] = 0xff;
+	buf[1] = 0x00;
+	buf[2] = 0x00;
+	buf[3] = linkscript.execaddr >> 8;
+	buf[4] = linkscript.execaddr & 0xff;
+	writebytes(buf, 1, 5, of);
+}
+
+void do_output_raw(FILE *of)
+{
+	int nskips = 0;		// used to output blanks for BSS inline
+	int sn;
+	
+	for (sn = 0; sn < nsects; sn++)
+	{
+		if (sectlist[sn].ptr -> flags & SECTION_BSS)
+		{
+			// no output for a BSS section
+			nskips += sectlist[sn].ptr -> codesize;
+			continue;
+		}
+		while (nskips > 0)
+		{
+			// the "" is not an error - it turns into a single NUL byte!
+			writebytes("", 1, 1, of);
+			nskips--;
+		}
+		writebytes(sectlist[sn].ptr -> code, 1, sectlist[sn].ptr -> codesize, of);
+	}
+}
+
+void do_output_lwex0(FILE *of)
+{
+	int nskips = 0;		// used to output blanks for BSS inline
+	int sn;
+	int codedatasize = 0;
+	unsigned char buf[32];
+	
+	// calculate items for the file header
+	for (sn = 0; sn < nsects; sn++)
+	{
+		if (sectlist[sn].ptr -> flags & SECTION_BSS)
+		{
+			// no output for a BSS section
+			nskips += sectlist[sn].ptr -> codesize;
+			continue;
+		}
+		codedatasize += nskips;
+		nskips = 0;
+		codedatasize += sectlist[sn].ptr -> codesize;
+	}
+
+	// output the file header
+	buf[0] = 'L';
+	buf[1] = 'W';
+	buf[2] = 'E';
+	buf[3] = 'X';
+	buf[4] = 0;		// version 0
+	buf[5] = 0;		// low stack
+	buf[6] = linkscript.stacksize / 256;
+	buf[7] = linkscript.stacksize & 0xff;
+	buf[8] = nskips / 256;
+	buf[9] = nskips & 0xff;
+	buf[10] = codedatasize / 256;
+	buf[11] = codedatasize & 0xff;
+	buf[12] = linkscript.execaddr / 256;
+	buf[13] = linkscript.execaddr & 0xff;
+	memset(buf + 14, 0, 18);
+	
+	writebytes(buf, 1, 32, of);
+	// output the data
+	// NOTE: disjoint load addresses will not work correctly!!!!!
+	for (sn = 0; sn < nsects; sn++)
+	{
+		if (sectlist[sn].ptr -> flags & SECTION_BSS)
+		{
+			// no output for a BSS section
+			nskips += sectlist[sn].ptr -> codesize;
+			continue;
+		}
+		while (nskips > 0)
+		{
+			// the "" is not an error - it turns into a single NUL byte!
+			writebytes("", 1, 1, of);
+			nskips--;
+		}
+		writebytes(sectlist[sn].ptr -> code, 1, sectlist[sn].ptr -> codesize, of);
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lwlink/readfiles.c	Wed Jan 19 22:27:17 2011 -0700
@@ -0,0 +1,423 @@
+/*
+readfiles.c
+Copyright © 2009 William Astle
+
+This file is part of LWLINK.
+
+LWLINK 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 3 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, see <http://www.gnu.org/licenses/>.
+
+
+Reads input files
+
+*/
+
+#include <argp.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "lwlink.h"
+#include "util.h"
+
+void read_lwobj16v0(fileinfo_t *fn);
+void read_lwar1v(fileinfo_t *fn);
+
+/*
+The logic of reading the entire file into memory is simple. All the symbol
+names in the file are NUL terminated strings and can be used directly without
+making additional copies.
+*/
+void read_file(fileinfo_t *fn)
+{
+	if (!memcmp(fn -> filedata, "LWOBJ16", 8))
+		{
+			// read v0 LWOBJ16 file
+			read_lwobj16v0(fn);
+		}
+		else if (!memcmp(fn -> filedata, "LWAR1V", 6))
+		{
+			// archive file
+			read_lwar1v(fn);
+		}
+		else
+		{
+			fprintf(stderr, "%s: unknown file format\n", fn -> filename);
+			exit(1);
+		}
+}
+
+void read_files(void)
+{
+	int i;
+	long size;
+	FILE *f;
+	long bread;
+	for (i = 0; i < ninputfiles; i++)
+	{
+		if (inputfiles[i] -> islib)
+		{
+			char *tf;
+			int s;
+			int j;
+			
+			f = NULL;
+			
+			for (j = 0; j < nlibdirs; j++)
+			{
+				s = strlen(libdirs[j]) + 7 + strlen(inputfiles[i] -> filename);
+				tf = lw_malloc(s + 1);
+				sprintf(tf, "%s/lib%s.a", libdirs[j], inputfiles[i] -> filename);
+				f = fopen(tf, "rb");
+				if (!f)
+				{
+					free(tf);
+					continue;
+				}
+				free(tf);
+				break;
+			}
+			if (!f)
+			{
+				fprintf(stderr, "Can't open library: -l%s\n", inputfiles[i] -> filename);
+				exit(1);
+			}
+		}
+		else
+		{
+			f = fopen(inputfiles[i] -> filename, "rb");
+			if (!f)
+			{
+				fprintf(stderr, "Can't open file %s:", inputfiles[i] -> filename);
+				perror("");
+				exit(1);
+			}
+		}
+		fseek(f, 0, SEEK_END);
+		size = ftell(f);
+		rewind(f);
+		
+		inputfiles[i] -> filedata = lw_malloc(size);
+		inputfiles[i] -> filesize = size;
+		
+		bread = fread(inputfiles[i] -> filedata, 1, size, f);
+		if (bread < size)
+		{
+			fprintf(stderr, "Short read on file %s (%ld/%ld):", inputfiles[i] -> filename, bread, size);
+			perror("");
+			exit(1);
+		}
+			
+		fclose(f);
+		
+		read_file(inputfiles[i]);
+	}
+}
+
+// this macro is used to bail out if we run off the end of the file data
+// while parsing - it keeps the code below cleaner
+#define NEXTBYTE()	do { cc++; if (cc > fn -> filesize) { fprintf(stderr, "%s: invalid file format\n", fn -> filename); exit(1); } } while (0)
+// this macro is used to refer to the current byte in the stream
+#define CURBYTE()	(fn -> filedata[cc < fn -> filesize ? cc : fn -> filesize - 1])
+// this one will leave the input pointer past the trailing NUL
+#define CURSTR()	read_lwobj16v0_str(&cc, fn)
+unsigned char *read_lwobj16v0_str(long *cc1, fileinfo_t *fn)
+{
+	int cc = *cc1;
+	unsigned char *fp;
+	fp = &CURBYTE();
+	while (CURBYTE())
+		NEXTBYTE();
+	NEXTBYTE();
+	*cc1 = cc;
+	return fp;
+}
+// the function below can be switched to dealing with data coming from a
+// source other than an in-memory byte pool by adjusting the input data
+// in "fn" and the above two macros
+
+void read_lwobj16v0(fileinfo_t *fn)
+{
+	unsigned char *fp;
+	long cc;
+	section_t *s;
+	int val;
+	symtab_t *se;
+	
+	// start reading *after* the magic number
+	cc = 8;
+	
+	// init data
+	fn -> sections = NULL;
+	fn -> nsections = 0;
+
+	while (1)
+	{
+//		NEXTBYTE();
+		// bail out if no more sections
+		if (!(CURBYTE()))
+			break;
+		
+		fp = CURSTR();
+		
+		// we now have a section name in fp
+		// create new section entry
+		fn -> sections = lw_realloc(fn -> sections, sizeof(section_t) * (fn -> nsections + 1));
+		s = &(fn -> sections[fn -> nsections]);
+		fn -> nsections += 1;
+		
+		s -> localsyms = NULL;
+		s -> flags = 0;
+		s -> codesize = 0;
+		s -> name = fp;
+		s -> loadaddress = 0;
+		s -> localsyms = NULL;
+		s -> exportedsyms = NULL;
+		s -> incompletes = NULL;
+		s -> processed = 0;
+		s -> file = fn;
+		
+		// read flags
+		while (CURBYTE())
+		{
+			switch (CURBYTE())
+			{
+			case 0x01:
+				s -> flags |= SECTION_BSS;
+				break;
+
+			default:
+				fprintf(stderr, "%s (%s): unrecognized section flag %02X\n", fn -> filename, s -> name, (int)(CURBYTE()));
+				exit(1);
+			}
+			NEXTBYTE();
+		}
+		// skip NUL terminating flags
+		NEXTBYTE();
+		
+		// now parse the local symbol table
+		while (CURBYTE())
+		{
+			fp = CURSTR();
+
+			// fp is the symbol name
+			val = (CURBYTE()) << 8;
+			NEXTBYTE();
+			val |= (CURBYTE());
+			NEXTBYTE();
+			// val is now the symbol value
+			
+			// create symbol table entry
+			se = lw_malloc(sizeof(symtab_t));
+			se -> next = s -> localsyms;
+			s -> localsyms = se;
+			se -> sym = fp;
+			se -> offset = val;
+		}
+		// skip terminating NUL
+		NEXTBYTE();
+		
+		// now parse the exported symbol table
+		while (CURBYTE())
+		{
+			fp = CURSTR();
+
+			// fp is the symbol name
+			val = (CURBYTE()) << 8;
+			NEXTBYTE();
+			val |= (CURBYTE());
+			NEXTBYTE();
+			// val is now the symbol value
+			
+			// create symbol table entry
+			se = lw_malloc(sizeof(symtab_t));
+			se -> next = s -> exportedsyms;
+			s -> exportedsyms = se;
+			se -> sym = fp;
+			se -> offset = val;
+		}
+		// skip terminating NUL
+		NEXTBYTE();
+		
+		// now parse the incomplete references and make a list of
+		// external references that need resolution
+		while (CURBYTE())
+		{
+			reloc_t *rp;
+			lw_expr_term_t *term;
+			
+			// we have a reference
+			rp = lw_malloc(sizeof(reloc_t));
+			rp -> next = s -> incompletes;
+			s -> incompletes = rp;
+			rp -> offset = 0;
+			rp -> expr = lw_expr_stack_create();
+			rp -> flags = RELOC_NORM;
+			
+			// parse the expression
+			while (CURBYTE())
+			{
+				int tt = CURBYTE();
+				NEXTBYTE();
+				switch (tt)
+				{
+				case 0xFF:
+					// a flag specifier
+					tt = CURBYTE();
+					rp -> flags = tt;
+					NEXTBYTE();
+					term = NULL;
+					break;
+					
+				case 0x01:
+					// 16 bit integer
+					tt = CURBYTE() << 8;
+					NEXTBYTE();
+					tt |= CURBYTE();
+					NEXTBYTE();
+					// normalize for negatives...
+					if (tt > 0x7fff)
+						tt -= 0x10000;
+					term = lw_expr_term_create_int(tt);
+					break;
+				
+				case 0x02:
+					// external symbol reference
+					term = lw_expr_term_create_sym(CURSTR(), 0);
+					break;
+					
+				case 0x03:
+					// internal symbol reference
+					term = lw_expr_term_create_sym(CURSTR(), 1);
+					break;
+				
+				case 0x04:
+					// operator
+					term = lw_expr_term_create_oper(CURBYTE());
+					NEXTBYTE();
+					break;
+
+				case 0x05:
+					// section base reference (NULL internal reference is
+					// the section base address
+					term = lw_expr_term_create_sym(NULL, 1);
+					break;
+					
+				default:
+					fprintf(stderr, "%s (%s): bad relocation expression (%02X)\n", fn -> filename, s -> name, tt);
+					exit(1);
+				}
+				if (term)
+				{
+					lw_expr_stack_push(rp -> expr, term);
+					lw_expr_term_free(term);
+				}
+			}
+			// skip the NUL
+			NEXTBYTE();
+			
+			// fetch the offset
+			rp -> offset = CURBYTE() << 8;
+			NEXTBYTE();
+			rp -> offset |= CURBYTE() & 0xff;
+			NEXTBYTE();
+		}
+		// skip the NUL terminating the relocations
+		NEXTBYTE();
+				
+		// now set code location and size and verify that the file
+		// contains data going to the end of the code (if !SECTION_BSS)
+		s -> codesize = CURBYTE() << 8;
+		NEXTBYTE();
+		s -> codesize |= CURBYTE();
+		NEXTBYTE();
+		
+		s -> code = &(CURBYTE());
+		
+		// skip the code if we're not in a BSS section
+		if (!(s -> flags & SECTION_BSS))
+		{
+			int i;
+			for (i = 0; i < s -> codesize; i++)
+				NEXTBYTE();
+		}
+	}
+}
+
+/*
+Read an archive file - this will create a "sub" record and farm out the
+parsing of the sub files to the regular file parsers
+
+The archive file format consists of the 6 byte magic number followed by a
+series of records as follows:
+
+- NUL terminated file name
+- 32 bit file length in big endian order
+- the file data
+
+An empty file name indicates the end of the file.
+
+*/
+void read_lwar1v(fileinfo_t *fn)
+{
+	unsigned long cc = 6;
+	unsigned long flen;
+	unsigned long l;
+	for (;;)
+	{
+		if (cc >= fn -> filesize || !(fn -> filedata[cc]))
+			return;
+
+		for (l = cc; cc < fn -> filesize && fn -> filedata[cc]; cc++)
+			/* do nothing */ ;
+
+		cc++;
+
+		if (cc >= fn -> filesize)
+		{
+			fprintf(stderr, "Malformed archive file %s.\n", fn -> filename);
+			exit(1);
+		}
+
+		if (cc + 4 > fn -> filesize)
+			return;
+
+		flen = (fn -> filedata[cc++] << 24);
+		flen |= (fn -> filedata[cc++] << 16);
+		flen |= (fn -> filedata[cc++] << 8);
+		flen |= (fn -> filedata[cc++]);
+
+		if (flen == 0)
+			return;
+		
+		if (cc + flen > fn -> filesize)
+		{
+			fprintf(stderr, "Malformed archive file %s.\n", fn -> filename);
+			exit(1);
+		}
+		
+		// add the "sub" input file
+		fn -> subs = lw_realloc(fn -> subs, sizeof(fileinfo_t *) * (fn -> nsubs + 1));
+		fn -> subs[fn -> nsubs] = lw_malloc(sizeof(fileinfo_t));
+		memset(fn -> subs[fn -> nsubs], 0, sizeof(fileinfo_t));
+		fn -> subs[fn -> nsubs] -> filedata = fn -> filedata + cc;
+		fn -> subs[fn -> nsubs] -> filesize = flen;
+		fn -> subs[fn -> nsubs] -> filename = lw_strdup(fn -> filedata + l);
+		fn -> subs[fn -> nsubs] -> parent = fn;
+		fn -> subs[fn -> nsubs] -> forced = fn -> forced;		
+		read_file(fn -> subs[fn -> nsubs]);
+		fn -> nsubs++;
+		cc += flen;
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lwlink/rules.make	Wed Jan 19 22:27:17 2011 -0700
@@ -0,0 +1,8 @@
+dirname := $(dir $(lastword $(MAKEFILE_LIST)))
+
+lwlink_srcs_local := main.c lwlink.c util.c readfiles.c expr.c script.c link.c output.c map.c
+lwobjdump_srcs_local := objdump.c util.c
+
+
+lwlink_srcs := $(lwlink_srcs) $(addprefix $(dirname),$(lwlink_srcs_local))
+lwobjdump_srcs := $(lwobjdump_srcs) $(addprefix $(dirname),$(lwobjdump_srcs_local))
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lwlink/script.c	Wed Jan 19 22:27:17 2011 -0700
@@ -0,0 +1,295 @@
+/*
+script.c
+Copyright © 2009 William Astle
+
+This file is part of LWLINK.
+
+LWLINK 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 3 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, see <http://www.gnu.org/licenses/>.
+
+
+Read and parse linker scripts
+*/
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "lwlink.h"
+#include "util.h"
+
+// the built-in DECB target linker script
+static char *decb_script =
+	"section init load 2000\n"
+	"section code\n"
+	"section *,!bss\n"
+	"section *,bss\n"
+	"entry 2000\n"
+	;
+
+// the built-in RAW target linker script
+static char *raw_script = 
+	"section init load 0000\n"
+	"section code\n"
+	"section *,!bss\n"
+	"section *,bss\n"
+	;
+
+static char *lwex0_script =
+	"section init load 0100\n"
+	"section .text\n"
+	"section .data\n"
+	"section *,!bss\n"
+	"section *,bss\n"
+	"entry __start\n"
+	"stacksize 0100\n"		// default 256 byte stack
+	;
+
+// the "simple" script
+static char *simple_script = 
+	"section *,!bss\n"
+	"section *,bss\n"
+	;
+
+linkscript_t linkscript = { 0, NULL, -1 };
+
+void setup_script()
+{
+	char *script, *oscript;
+	long size;
+
+	// read the file if needed
+	if (scriptfile)
+	{
+		FILE *f;
+		long bread;
+		
+		f = fopen(scriptfile, "rb");
+		if (!f)
+		{
+			fprintf(stderr, "Can't open file %s:", scriptfile);
+			perror("");
+			exit(1);
+		}
+		fseek(f, 0, SEEK_END);
+		size = ftell(f);
+		rewind(f);
+		
+		script = lw_malloc(size + 2);
+		
+		bread = fread(script, 1, size, f);
+		if (bread < size)
+		{
+			fprintf(stderr, "Short read on file %s (%ld/%ld):", scriptfile, bread, size);
+			perror("");
+			exit(1);
+		}
+		fclose(f);
+		
+		script[size] = '\n';
+		script[size + 1] = '\0';
+	}
+	else
+	{
+		// fetch defaults based on output mode
+		switch (outformat)
+		{
+		case OUTPUT_RAW:
+			script = raw_script;
+			break;
+		
+		case OUTPUT_DECB:
+			script = decb_script;
+			break;
+		
+		case OUTPUT_LWEX0:
+			script = lwex0_script;
+			break;
+			
+		default:
+			script = simple_script;
+			break;
+		}
+		
+		size = strlen(script);
+		if (nscriptls)
+		{
+			char *rscript;
+			int i;
+			// prepend the "extra" script lines
+			for (i = 0; i < nscriptls; i++)
+				size += strlen(scriptls[i]) + 1;
+			
+			rscript = lw_malloc(size + 1);
+			oscript = rscript;
+			for (i = 0; i < nscriptls; i++)
+			{
+				oscript += sprintf(oscript, "%s\n", scriptls[i]);
+			}
+			strcpy(oscript, script);
+			script = rscript;
+		}
+	}
+
+	if (outformat == OUTPUT_LWEX0)
+		linkscript.stacksize = 0x100;
+
+	oscript = script;
+	// now parse the script file
+	while (*script)
+	{
+		char *ptr, *ptr2, *line;
+
+		for (ptr = script; *ptr && *ptr != '\n' && *ptr != '\r'; ptr++)
+			/* do nothing */ ;
+		
+		line = lw_malloc(ptr - script + 1);
+		memcpy(line, script, ptr - script);
+		line[ptr - script] = '\0';
+
+		// skip line terms
+		for (script = ptr + 1; *script == '\n' || *script == '\r'; script++)
+			/* do nothing */ ;
+		
+		// ignore leading whitespace
+		for (ptr = line; *ptr && isspace(*ptr); ptr++)
+			/* do nothing */ ;
+		
+		// ignore blank lines
+		if (!*ptr)
+			continue;
+		
+		for (ptr = line; *ptr && !isspace(*ptr); ptr++)
+			/* do nothing */ ;
+		
+		// now ptr points to the char past the first word
+		// NUL it out
+		if (*ptr)
+			*ptr++ = '\0';
+		
+		// skip spaces after the first word
+		for ( ; *ptr && isspace(*ptr); ptr++)
+			/* do nothing */ ;
+		
+		if (!strcmp(line, "pad"))
+		{
+			// padding
+			// parse the hex number and stow it
+			linkscript.padsize = strtol(ptr, NULL, 16);
+			if (linkscript.padsize < 0)
+				linkscript.padsize = 0;
+		}
+		else if (!strcmp(line, "stacksize"))
+		{
+			// stack size for targets that support it
+			// parse the hex number and stow it
+			linkscript.padsize = strtol(ptr, NULL, 16);
+			if (linkscript.stacksize < 0)
+				linkscript.stacksize = 0x100;
+		}
+		else if (!strcmp(line, "entry"))
+		{
+			int eaddr;
+			
+			eaddr = strtol(ptr, &ptr2, 16);
+			if (*ptr2)
+			{
+				linkscript.execaddr = -1;
+				linkscript.execsym = lw_strdup(ptr);
+			}
+			else
+			{
+				linkscript.execaddr = eaddr;
+				linkscript.execsym = NULL;
+			}
+		}
+		else if (!strcmp(line, "section"))
+		{
+			// section
+			// parse out the section name and flags
+			for (ptr2 = ptr; *ptr2 && !isspace(*ptr2); ptr2++)
+				/* do nothing */ ;
+			
+			if (*ptr2)
+				*ptr2++ = '\0';
+			
+			while (*ptr2 && isspace(*ptr2))
+				ptr2++;
+				
+			// ptr now points to the section name and flags and ptr2
+			// to the first non-space character following
+			
+			// then look for "load <addr>" clause
+			if (*ptr2)
+			{
+				if (!strncmp(ptr2, "load", 4))
+				{
+					ptr2 += 4;
+					while (*ptr2 && isspace(*ptr2))
+						ptr2++;
+					
+				}
+				else
+				{
+					fprintf(stderr, "%s: bad script\n", scriptfile);
+					exit(1);
+				}
+			}
+			
+			// now ptr2 points to the load address if there is one
+			// or NUL if not
+			linkscript.lines = lw_realloc(linkscript.lines, sizeof(struct scriptline_s) * (linkscript.nlines + 1));
+
+			linkscript.lines[linkscript.nlines].noflags = 0;
+			linkscript.lines[linkscript.nlines].yesflags = 0;
+			if (*ptr2)
+				linkscript.lines[linkscript.nlines].loadat = strtol(ptr2, NULL, 16);
+			else
+				linkscript.lines[linkscript.nlines].loadat = -1;
+			for (ptr2 = ptr; *ptr2 && *ptr2 != ','; ptr2++)
+				/* do nothing */ ;
+			if (*ptr2)
+			{
+				*ptr2++ = '\0';
+				if (!strcmp(ptr2, "!bss"))
+				{
+					linkscript.lines[linkscript.nlines].noflags = SECTION_BSS;
+				}
+				else if (!strcmp(ptr2, "bss"))
+				{
+					linkscript.lines[linkscript.nlines].yesflags = SECTION_BSS;
+				}
+				else
+				{
+					fprintf(stderr, "%s: bad script\n", scriptfile);
+					exit(1);
+				}
+			}
+			if (ptr[0] == '*' && ptr[1] == '\0')
+				linkscript.lines[linkscript.nlines].sectname = NULL;
+			else
+				linkscript.lines[linkscript.nlines].sectname = lw_strdup(ptr);
+			linkscript.nlines++;
+		}
+		else
+		{
+			fprintf(stderr, "%s: bad script\n", scriptfile);
+			exit(1);
+		}
+		lw_free(line);
+	}
+	
+	if (scriptfile || nscriptls)
+		lw_free(oscript);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lwlink/util.c	Wed Jan 19 22:27:17 2011 -0700
@@ -0,0 +1,86 @@
+/*
+util.c
+Copyright © 2009 William Astle
+
+This file is part of LWLINK.
+
+LWLINK 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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+Utility functions
+*/
+
+#define __util_c_seen__
+#include <malloc.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "util.h"
+
+void *lw_malloc(int size)
+{
+	void *ptr;
+	
+	ptr = malloc(size);
+	if (!ptr)
+	{
+		// bail out; memory allocation error
+		fprintf(stderr, "lw_malloc(): Memory allocation error\n");
+		exit(1);
+	}
+	return ptr;
+}
+
+void *lw_realloc(void *optr, int size)
+{
+	void *ptr;
+	
+	if (size == 0)
+	{
+		lw_free(optr);
+		return;
+	}
+	
+	ptr = realloc(optr, size);
+	if (!ptr)
+	{
+		fprintf(stderr, "lw_realloc(): memory allocation error\n");
+		exit(1);
+	}
+}
+
+void lw_free(void *ptr)
+{
+	if (ptr)
+		free(ptr);
+}
+
+char *lw_strdup(const char *s)
+{
+	char *d;
+	
+	if (!s)
+		return NULL;
+
+	d = strdup(s);
+	if (!d)
+	{
+		fprintf(stderr, "lw_strdup(): memory allocation error\n");
+		exit(1);
+	}
+	
+	return d;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lwlink/util.h	Wed Jan 19 22:27:17 2011 -0700
@@ -0,0 +1,44 @@
+/*
+util.h
+Copyright © 2009 William Astle
+
+This file is part of LWLINK.
+
+LWLINK 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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+Utility functions
+*/
+
+#ifndef __util_h_seen__
+#define __util_h_seen__
+
+#ifndef __util_c_seen__
+#define __util_E__ extern
+#else
+#define __util_E__
+#endif
+
+// allocate memory
+__util_E__ void *lw_malloc(int size);
+__util_E__ void lw_free(void *ptr);
+__util_E__ void *lw_realloc(void *optr, int size);
+
+// string stuff
+__util_E__ char *lw_strdup(const char *s);
+
+#undef __util_E__
+
+#endif // __util_h_seen__