Mercurial > hg > index.cgi
changeset 0:2c24602be78f
Initial import from lwtools 3.0.1 version, with new hand built build system and file reorganization
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 ("<") for forced +eight bit modes, and the greater than sign (">") 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 "&" 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 "/", 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__