z80asm-1.8/0000755000175000017500000000000011170052703012034 5ustar shevekshevekz80asm-1.8/.gitignore0000644000175000017500000000007711170052703014030 0ustar shevekshevekexpressions.o gnulib/getopt.o gnulib/getopt1.o z80asm z80asm.o z80asm-1.8/COPYING0000644000175000017500000000143011170052703013065 0ustar shevekshevekCOPYING - Copyright information for z80asm, the assembler for the z80 by Bas Wijnen. This file is part of z80asm. Z80asm is free software; you can 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. Z80asm is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 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 . Version 3 of the GPL is included in the distribution in a file named GPL3. z80asm-1.8/ChangeLog0000644000175000017500000001652611170052703013620 0ustar shevekshevek2009-04-11 Bas Wijnen * z80asm.c: Fixed bug that defw/dw didn't accept more than one argument. 2009-01-18 Bas Wijnen * expressions.c: Fix incorrect valid declaration of expressions when only the last label is valid. Thanks to Tomaž Å olc for reporting. 2007-09-09 Bas Wijnen * z80asm.c: Wrap program counter consistently. * z80asm.c, z80asm.h: Fix error reporting in references. * all files: Update license to GPL version 3 or later. * Makefile: Improve make dist, and make it work with git. 2007-06-14 Bas Wijnen * VERSION: Updated to 1.5 * z80asm.c, z80asm.h: Fix handling of $ in stored expressions (thanks to Tomaz Solc for reporting and analysing this. * Makefile: Remove executable if tests fail. * tests/pass.asm, tests/pass.correct-bin: Add some tests. * z80asm.c: Make unresolvable references a normal error (instead of an internal assembler problem). 2007-06-05 Bas Wijnen * VERSION: Updated to 1.4 * z80asm.c: Fix bit/set/res instructions. 2007-05-24 Bas Wijnen * VERSION: Updated to 1.3. * tests, tests/pass.asm, tests/pass.correct-bin, tests/pass.correct-err, tests/Makefile: Add test suite to spot regressions. * expressions.c, z80asm.h: New files, split from z80asm.c. * Makefile: Updated. * expressions.c, z80asm.c: Warn for value truncation, warn for expressions fully in parenthesis, improve skipword so it doesn't break on comments with parentheses, improve expression parsing so "ld a, (1) + 1" doesn't result in an error. * examples/macro.asm: Fixed to make it usable as a rom. * z80asm.1: Updated. 2005-11-30 Jan Wilmans * z80asm.c: Added unoffical syntax of ADD A,r as ADD r * z80asm.c: Added unoffical syntax of SUB r as SUB A,r 2005-11-30 Jan Wilmans * z80asm.c: Added new seek command 2005-11-29 Jan Wilmans * z80asm.c: Added two extra verbosity levels. 2005-11-13 Bas Wijnen * z80asm.c: Run indent. 2005-10-14 Bas Wijnen * z80asm.c (struct reference, compute_ref, new_reference): Removed copy of stack frame from struct reference. 2005-09-14 Bas Wijnen * z80asm.c (open_include_file, assemble): Open incbin'd files as binary. 2005-06-16 Bas Wijnen * Makefile: Fix incorrect version. * z80asm.c (enum mnemonic, mnemonics, assemble), examples/hello.asm: Rename bininclude to incbin. * z80asm.c (readcommand, assemble), examples/macro.asm: Allow labels before endm and endif. 2005-05-19 Bas Wijnen * z80asm.c (struct reference, printerr, compute_ref, new_reference, assemble): Added stack frame to reference. * z80asm.c (labelfilename, parse_commandline): Remove output files when compilation fails. * z80asm.c (use_force, parse_commandline, assemble, main): Implement --force. * z80asm.c (parse_commandline): Make a.bin the default target. * z80asm.c (check_label, rd_label, rd_value, rd_expr, get_include_name, assemble): Give errors about junk at end of line or expression. * z80asm.c (rd_value): Support double quotes for character constants. * z80asm.c (rd_expr_equal): Support = for equality. * z80asm.1: Updated manpage. 2005-05-18 Bas Wijnen * z80asm.c (enum mnemonic, enum reftype, struct reference, struct label, struct stack, struct macro_arg, struct macro_line, struct macro, mnemonics, firstmacro, define_macro, sp, stack, readlabel, rd_expr, compute_ref, check_label, rd_label, rd_value, rd_factor, rd_term, rd_expr_shift, rd_expr_unequal, rd_expr_equal, rd_expr_and, rd_expr_xor, rd_expr_or, new_reference, wrt_ref, read_line, get_macro_args, assemble): Added macro support. * z80asm.c (rd_value): Added several radix notations. * examples/macro.asm: New file. 2005-05-17 Bas Wijnen * Makefile (clean): Clean examples and headers. * Makefile (dist): Make versioned tarballs. * z80asm.c (buffer, try_use_real_file, flush_to_real_file, parse_commandline, assemble): Generalise writing to non-seekable files. * z80asm.c (reallistfile, parse_commandline, assemble): Use it for list file. * z80asm.c (read_line, assemble): Remove arbitrary line length limit. 2005-05-13 Bas Wijnen * examples/Makefile, examples/hello.asm, headers/msx-bios.asm, headers/msx2-bios.asm, headers/msx2+-bios.asm, headers/msxturbor-bios.asm: New files. * z80asm.c (struct includedir, firstincludedir, open_include_file, add_include, parse_commandline, assemble, main): Use include path. * z80asm (parse_commandline): Make stderr default for list and label files. * z80asm.c (enum mnemonic, mnemonics, get_include_name, assemble): Add bininclude directive. * z80asm.c (printerr, assemble): Make error output parsable. * z80asm.1: Updated. 2005-05-11 Bas Wijnen * Makefile (top level, dist): Use versions instead of dates. * VERSION: New file. * BUGS, NEWS, README: Removed. 2005-05-10 Bas Wijnen * z80asm.1: New file. * z80asm.c (enum mnemonic, mnemonic, assemble): Support END directive. * z80asm.c (compare, indx, readlabel): Use strncasecmp instead of compare. * z80asm.c (assemble): Fix bug with strings in DEFB. * z80asm.c (assemble): Allow backslash-escapes in DEFB strings. * z80asm.c (rd_character): Allow octal escapes in strings. 2005-03-11 Bas Wijnen * z80asm.c (rd_r): Fixed index prefix. * z80asm.c (assemble): Fixed register count errors. 2004-10-01 Bas Wijnen * z80asm (rd_comma): Fixed possible buffer overflow. * z80asm (parse_commandline): Improved --help output. * z80asm (new_reference): Removed useless ++. * z80asm (wrt_ref, assemble): Improved list output for ds. 2004-10-01 H. Peter Anvin * z80asm.c (enum mnemonic, mnemonics, assemble): Added dm/defm as an alias for db/defb. * README: Updated documentation accordingly. * z80asm.c (write_one_byte, wrtb, new_reference, wrt_ref, assemble): Fixed bug regarding addr increments. 2004-09-29 Bas Wijnen * z80asm.c: added global variable labelprefix. * z80asm.c (parse_commandline, assemble): Support prefixing labels. * z80asm.c (assemble): fixed bug in label output. 2004-09-29 H. Peter Anvin * Makefile: Fixed "make clean", removed -Werror for cygwin. * z80asm.c (rd_out): implemented out (c),0. 2004-09-28 H. Peter Anvin * z80asm.c (rd_number): Added ouput parameter endptr. Changed all callers. * z80asm.c (rd_value): Added support for Zilog-style base specification. * README: Updated documentation. 2004-09-22 Bas Wijnen * z80asm.c (assemble): Bugfixes to make ld (nn),a and ld sp,nn work. 2003-11-19 Bas Wijnen * z80asm.c: Changed expression handling, bugfixes. 2003-10-30 Bas Wijnen * z80asm.c: Added binary output as hex to listfile. 2003-10-30 Bas Wijnen * z80asm.c: Added support for quoted strings in defb, added comments, added long option support and help/version information. * README: Updated documentation. 2002-05-15 Bas Wijnen * z80asm.c (rd_expr): Added equations in expr and bugfix (assemble): Added if/else/endif 2002-04-28 Bas Wijnen * assembler.c: Started changelog z80asm-1.8/GPL30000644000175000017500000010451311170052703012470 0ustar shevekshevek GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. 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. Copyright (C) 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 . 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: Copyright (C) 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 . 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 . z80asm-1.8/INSTALL0000644000175000017500000000044611170052703013071 0ustar shevekshevekHow to install shevek's Z80 assembler To compile: go to the source directory type ``make'' (without the quotes) On some systems, you may need gmake To install, copy the file z80asm into your search path, for example in /usr/local/bin or in ~/bin (you may need to put this in the search path) z80asm-1.8/Makefile0000644000175000017500000000360711170052703013502 0ustar shevekshevek# Makefile for the Z80 assembler by shevek # Copyright 2002-2007 Bas Wijnen # # This file is part of z80asm. # # Z80asm is free software; you can 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. # # Z80asm is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR 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 . CC = gcc CFLAGS = -O0 -Wall -Wwrite-strings -Wcast-qual -Wcast-align -Wstrict-prototypes -Wmissing-prototypes -Wmissing-declarations -Wredundant-decls -Wnested-externs -Winline -pedantic -ansi -Wshadow -ggdb3 -W -Ignulib SHELL = /bin/bash VERSION ?= $(shell echo -n `cat VERSION | cut -d. -f1`. ; echo $$[`cat VERSION | cut -d. -f2` + 1]) all:z80asm z80asm: z80asm.o expressions.o Makefile gnulib/getopt.o gnulib/getopt1.o $(CC) $(LDFLAGS) $(filter %.o,$^) -o $@ $(MAKE) -C tests || rm $@ %.o:%.c z80asm.h gnulib/getopt.h Makefile $(CC) $(CFLAGS) -c $< -o $@ -DVERSION=\"$(shell cat VERSION)\" clean: for i in . gnulib examples headers ; do \ rm -f $$i/core $$i/*~ $$i/\#* $$i/*.o $$i/*.rom ; \ done rm -f z80asm z80asm.exe dist: clean ! git status | grep modified echo $(VERSION) > VERSION git add VERSION -git commit -m "Release version $(VERSION)" rm -rf /tmp/z80asm-$(VERSION) git archive --format=tar --prefix=z80asm-$(VERSION)/ HEAD | tar xf - -C /tmp tar cvzf ../z80asm-$(VERSION).tar.gz -C /tmp z80asm-$(VERSION) rm -r /tmp/z80asm-$(VERSION) cd .. && gpg -b z80asm-$(VERSION).tar.gz scp ../z80asm-$(VERSION).tar.gz* dl.sv.nongnu.org:/releases/z80asm/ git push z80asm-1.8/TODO0000644000175000017500000000041211170052703012521 0ustar shevekshevekTODO for shevek's Z80 assembler: - make header files containing MSX system constants - a flag for warnings about unofficial instructions would be nice - an "export" assembler directive to specify the labels to be exported with -L - add internationalization support. z80asm-1.8/VERSION0000644000175000017500000000000411170052703013076 0ustar shevekshevek1.8 z80asm-1.8/examples/0000755000175000017500000000000011170052703013652 5ustar shevekshevekz80asm-1.8/examples/Makefile0000644000175000017500000000150311170052703015311 0ustar shevekshevek# Makefile using z80asm # Copyright 2005-2007 Bas Wijnen # # This file is part of z80asm. # # Z80asm is free software; you can 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. # # Z80asm is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR 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 . # The output of the assembler can be parsed by vim or emacs. all: hello.rom macro.rom %.rom: %.asm z80asm $< -o $@ z80asm-1.8/examples/hello.asm0000644000175000017500000000237411170052703015465 0ustar shevekshevek; Hello world. Load as a cartridge in openmsx. (openmsx hello.rom) ; Copyright 2005 Bas Wijnen ; ; This file is part of z80asm. ; ; Z80asm is free software; you can 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. ; ; Z80asm is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR 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 . include 'msx-bios.asm' ; symbolic names for bios calls ; header for a rom org 0x4000 db "AB" dw start text: db "This is the source which generated this program:\r\n" incbin 'hello.asm' db 0 start: ld a, 80 ld (0xf3ae), a ; width 80 xor a call CHGMOD ; screen 0 ld hl, text loop: ld a, (hl) and a ; set the z flag if A is 0 jr z,stop ; and return in that case call CHPUT inc hl jr loop stop: jr stop ds 0x8000 - $ ; fill up the rest of the page z80asm-1.8/examples/macro.asm0000644000175000017500000000252611170052703015462 0ustar shevekshevek; macro.asm - usage of macros ; Copyright 2005 Bas Wijnen ; ; This file is part of z80asm. ; ; Z80asm is free software; you can 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. ; ; Z80asm is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR 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 . include "msx-bios.asm" ; Cartridge programs need to start at 0x4000 org 0x4000 ; header to make this a cartridge db "AB" dw start ; macro definition print: macro string ld hl, string ; this label (and .finish) must be local to prevent duplicate definitions ; when the macro is used more than once. .start: ld a, (hl) and a jr z, .finish call CHPUT inc hl jr .start .finish:endm ; Call the macros start: print text print text2 ; Stop doing things di loop: halt jr loop ; The text strings text: db "This is some text\r\n", 0 text2: db "This is some more text\r\n", 0 ; Fill up the rest of the page ds 0x8000 - $ z80asm-1.8/examples/seek.asm0000644000175000017500000000260111170052703015302 0ustar shevekshevek; seek.asm - seek example ; Copyright 2005 jancasper ; ; This file is part of z80asm. ; ; Z80asm is free software; you can 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. ; ; Z80asm is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR 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 . ; this org not strictly necessary org $4000 ; load an existing rom, 16384 bytes in size, which was originally assembled at $4000 incbin 'disk.rom' ; seek back to absolute position 16 in the output file seek $10 ; since the rom is loaded at addres $4000, code at $10 should be referred to as $4010 org $4010 ; the following instructions will overwrite the code at $10 jp diskio jp dskchg jp getdpb jp choice jp dskfmt jp drvoff seek $3410 org $7410 diskio: dskchg: getdpb: choice: dskfmt: drvoff: add a,2 add 2 sub a,2 sub 2 ei ret ; The result of this will be a new, patched binary rom file that is still exactly 16384 bytes in size. z80asm-1.8/expressions.c0000644000175000017500000004424711170052703014575 0ustar shevekshevek/* Z80 assembler by shevek Copyright (C) 2002-2009 Bas Wijnen Copyright (C) 2005 Jan Wilmans This file is part of z80asm. Z80asm is free software; you can 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. Z80asm is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 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 . */ #include "z80asm.h" /* reading expressions. The following operators are supported * in order of precedence, with function name: * expr?expr:expr do_rd_expr * | rd_expr_or * ^ rd_expr_xor * & rd_expr_and * == != rd_expr_equal * >= <= > < rd_expr_unequal * << >> rd_expr_shift * + - (binary) rd_term * * / % rd_factor * ~ + - (unary) rd_factor */ static int do_rd_expr (const char **p, char delimiter, int *valid, int level, int *check, int print_errors); static int rd_number (const char **p, const char **endp, int base) { int result = 0, i; char *c, num[] = "0123456789abcdefghijklmnopqrstuvwxyz"; if (verbose >= 6) fprintf (stderr, "%5d (0x%04x): Starting to read number of base %d" "(string=%s).\n", stack[sp].line, addr, base, *p); num[base] = '\0'; *p = delspc (*p); while (**p && (c = strchr (num, tolower (**p)))) { i = c - num; if (verbose >= 7) fprintf (stderr, "%5d (0x%04x): Digit found:%1x.\n", stack[sp].line, addr, i); result = result * base + i; (*p)++; } if (endp) *endp = *p; *p = delspc (*p); if (verbose >= 7) fprintf (stderr, "%5d (0x%04x): rd_number returned %d (%04x).\n", stack[sp].line, addr, result, result); return result; } static int rd_otherbasenumber (const char **p, int *valid, int print_errors) { char c; if (verbose >= 6) fprintf (stderr, "%5d (0x%04x): Starting to read basenumber (string=%s).\n", stack[sp].line, addr, *p); (*p)++; if (!**p) { if (valid) *valid = 0; else if (print_errors) printerr (1, "unexpected end of line after `@'\n"); return 0; } if (**p == '0' || !isalnum (**p)) { if (valid) *valid = 0; else if (print_errors) printerr (1, "base must be between 1 and z\n"); return 0; } c = **p; (*p)++; if (isalpha (**p)) return rd_number (p, NULL, tolower (c) - 'a' + 1); return rd_number (p, NULL, c - '0' + 1); } int rd_character (const char **p, int *valid, int print_errors) { int i; if (verbose >= 6) fprintf (stderr, "%5d (0x%04x): Starting to read character (string=%s).\n", stack[sp].line, addr, *p); i = **p; if (!i) { if (valid) *valid = 0; else if (print_errors) printerr (1, "unexpected end of line in string constant\n"); return 0; } if (i == '\\') { (*p)++; if (**p >= '0' && **p <= '7') { int b, num_digits; i = 0; if ((*p)[1] >= '0' && (*p)[1] <= '7') { if (**p <= '3' && (*p)[2] >= '0' && (*p)[2] <= '7') num_digits = 3; else num_digits = 2; } else num_digits = 1; for (b = 0; b < num_digits; ++b) { int bit = (*p)[num_digits - 1 - b] - '0'; i += (1 << (b * 3)) * bit; } *p += num_digits; } else { switch (**p) { case 'n': i = 10; break; case 'r': i = 13; break; case 't': i = 9; break; case 'a': i = 7; break; case '\'': if (valid) *valid = 0; else if (print_errors) printerr (1, "empty literal character\n"); return 0; case 0: if (valid) *valid = 0; else if (print_errors) printerr (1, "unexpected end of line after " "backslash in string constant\n"); return 0; default: i = **p; } (*p)++; } } else (*p)++; if (verbose >= 7) fprintf (stderr, "%5d (0x%04x): rd_character returned %d (%c).\n", stack[sp].line, addr, i, i); return i; } static int check_label (struct label *labels, const char **p, struct label **ret, struct label **previous, int force_skip) { struct label *l; const char *c; unsigned s2; *p = delspc (*p); for (c = *p; isalnum (*c) || *c == '_' || *c == '.'; ++c) { } s2 = c - *p; for (l = labels; l; l = l->next) { unsigned s1, s; int cmp; s1 = strlen (l->name); s = s1 < s2 ? s1 : s2; cmp = strncmp (l->name, *p, s); if (cmp > 0 || (cmp == 0 && s1 > s)) { if (force_skip) *p = c; return 0; } if (cmp < 0 || s2 > s) { if (previous) *previous = l; continue; } *p = c; /* if label is not valid, compute it */ if (l->ref) { compute_ref (l->ref, 1); if (!l->ref->done) { /* label was not valid, and isn't computable. tell the * caller that it doesn't exist, so it will try again later. * Set ret to show actual existence. */ if (verbose >= 6) fprintf (stderr, "%5d (0x%04x): returning invalid label %s.\n", stack[sp].line, addr, l->name); *ret = l; return 0; } } *ret = l; return 1; } if (force_skip) *p = c; return 0; } int rd_label (const char **p, int *exists, struct label **previous, int level, int print_errors) { struct label *l = NULL; int s; if (exists) *exists = 0; if (previous) *previous = NULL; if (verbose >= 6) fprintf (stderr, "%5d (0x%04x): Starting to read label (string=%s).\n", stack[sp].line, addr, *p); for (s = level; s >= 0; --s) { if (check_label (stack[s].labels, p, &l, (**p == '.' && s == sp) ? previous : NULL, 0)) break; } if (s < 0) { /* not yet found */ const char *old_p = *p; if (!check_label (firstlabel, p, &l, **p != '.' ? previous : NULL, 1)) { /* label does not exist, or is invalid. This is an error if there * is no existance check. */ if (!exists && print_errors) printerr (1, "using undefined label %.*s\n", *p - old_p, old_p); /* Return a value to discriminate between non-existing and invalid */ if (verbose >= 7) fprintf (stderr, "rd_label returns invalid value\n"); return l != NULL; } } if (exists) *exists = 1; if (verbose >= 7) fprintf (stderr, "rd_label returns valid value 0x%x\n", l->value); return l->value; } static int rd_value (const char **p, int *valid, int level, int *check, int print_errors) { int sign = 1, not = 0, base, v; const char *p0, *p1, *p2; if (verbose >= 6) fprintf (stderr, "%5d (0x%04x): Starting to read value (string=%s).\n", stack[sp].line, addr, *p); *p = delspc (*p); while (**p && strchr ("+-~", **p)) { if (**p == '-') sign = -sign; else if (**p == '~') not = ~not; (*p)++; *p = delspc (*p); } base = 10; /* Default base for suffixless numbers */ /* Check for parenthesis around full expression: not if no parenthesis */ if (**p != '(') *check = 0; switch (**p) { int exist, retval; char quote; int dummy_check; case '(': (*p)++; dummy_check = 0; retval = not ^ (sign * do_rd_expr (p, ')', valid, level, &dummy_check, print_errors)); ++*p; return retval; case '0': if ((*p)[1] == 'x') { (*p) += 2; return not ^ (sign * rd_number (p, NULL, 0x10)); } base = 8; /* If first digit it 0, assume octal unless suffix */ /* fall through */ case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': p0 = *p; rd_number (p, &p1, 36); /* Advance to end of numeric string */ p1--; /* Last character in numeric string */ switch (*p1) { case 'h': case 'H': base = 16; break; case 'b': case 'B': base = 2; break; case 'o': case 'O': case 'q': case 'Q': base = 8; break; case 'd': case 'D': base = 10; break; default: /* No suffix */ p1++; break; } v = rd_number (&p0, &p2, base); if (p1 != p2) { if (valid) *valid = 0; else if (print_errors) printerr (1, "invalid character in number: \'%c\'\n", *p2); } return not ^ (sign * v); case '$': ++*p; *p = delspc (*p); p0 = *p; v = rd_number (&p0, &p2, 0x10); if (p2 == *p) { v = baseaddr; } else *p = p2; return not ^ (sign * v); case '%': (*p)++; return not ^ (sign * rd_number (p, NULL, 2)); case '\'': case '"': quote = **p; ++*p; retval = not ^ (sign * rd_character (p, valid, print_errors)); if (**p != quote) { if (valid) *valid = 0; else if (print_errors) printerr (1, "missing closing quote (%c)\n", quote); return 0; } ++*p; return retval; case '@': return not ^ (sign * rd_otherbasenumber (p, valid, print_errors)); case '?': rd_label (p, &exist, NULL, level, 0); return not ^ (sign * exist); case '&': { ++*p; switch (**p) { case 'h': case 'H': base = 0x10; break; case 'o': case 'O': base = 010; break; case 'b': case 'B': base = 2; break; default: if (valid) *valid = 0; else if (print_errors) printerr (1, "invalid literal starting with &%c\n", **p); return 0; } ++*p; return not ^ (sign * rd_number (p, NULL, base)); } default: { int value; exist = 1; value = rd_label (p, valid ? &exist : NULL, NULL, level, print_errors); if (!exist) *valid = 0; return not ^ (sign * value); } } } static int rd_factor (const char **p, int *valid, int level, int *check, int print_errors) { /* read a factor of an expression */ int result; if (verbose >= 6) fprintf (stderr, "%5d (0x%04x): Starting to read factor (string=%s).\n", stack[sp].line, addr, *p); result = rd_value (p, valid, level, check, print_errors); *p = delspc (*p); while (**p == '*' || **p == '/') { *check = 0; if (**p == '*') { (*p)++; result *= rd_value (p, valid, level, check, print_errors); } else if (**p == '/') { (*p)++; result /= rd_value (p, valid, level, check, print_errors); } *p = delspc (*p); } if (verbose >= 7) fprintf (stderr, "%5d (0x%04x): rd_factor returned %d (%04x).\n", stack[sp].line, addr, result, result); return result; } static int rd_term (const char **p, int *valid, int level, int *check, int print_errors) { /* read a term of an expression */ int result; if (verbose >= 6) fprintf (stderr, "%5d (0x%04x): Starting to read term (string=%s).\n", stack[sp].line, addr, *p); result = rd_factor (p, valid, level, check, print_errors); *p = delspc (*p); while (**p == '+' || **p == '-') { *check = 0; if (**p == '+') { (*p)++; result += rd_factor (p, valid, level, check, print_errors); } else if (**p == '-') { (*p)++; result -= rd_factor (p, valid, level, check, print_errors); } *p = delspc (*p); } if (verbose >= 7) fprintf (stderr, "%5d (0x%04x): rd_term returned %d (%04x).\n", stack[sp].line, addr, result, result); return result; } static int rd_expr_shift (const char **p, int *valid, int level, int *check, int print_errors) { int result; if (verbose >= 6) fprintf (stderr, "%5d (0x%04x): Starting to read shift expression " "(string=%s).\n", stack[sp].line, addr, *p); result = rd_term (p, valid, level, check, print_errors); *p = delspc (*p); while ((**p == '<' || **p == '>') && (*p)[1] == **p) { *check = 0; if (**p == '<') { (*p) += 2; result <<= rd_term (p, valid, level, check, print_errors); } else if (**p == '>') { (*p) += 2; result >>= rd_term (p, valid, level, check, print_errors); } *p = delspc (*p); } if (verbose >= 7) fprintf (stderr, "%5d (0x%04x): rd_shift returned %d (%04x).\n", stack[sp].line, addr, result, result); return result; } static int rd_expr_unequal (const char **p, int *valid, int level, int *check, int print_errors) { int result; if (verbose >= 6) fprintf (stderr, "%5d (0x%04x): Starting to read " "unequality expression (string=%s).\n", stack[sp].line, addr, *p); result = rd_expr_shift (p, valid, level, check, print_errors); *p = delspc (*p); if (**p == '<' && (*p)[1] == '=') { *check = 0; (*p) += 2; return result <= rd_expr_unequal (p, valid, level, check, print_errors); } else if (**p == '>' && (*p)[1] == '=') { *check = 0; (*p) += 2; return result >= rd_expr_unequal (p, valid, level, check, print_errors); } if (**p == '<' && (*p)[1] != '<') { *check = 0; (*p)++; return result < rd_expr_unequal (p, valid, level, check, print_errors); } else if (**p == '>' && (*p)[1] != '>') { *check = 0; (*p)++; return result > rd_expr_unequal (p, valid, level, check, print_errors); } if (verbose >= 7) fprintf (stderr, "%5d (0x%04x): rd_shift returned %d (%04x).\n", stack[sp].line, addr, result, result); return result; } static int rd_expr_equal (const char **p, int *valid, int level, int *check, int print_errors) { int result; if (verbose >= 6) fprintf (stderr, "%5d (0x%04x): Starting to read equality epression " "(string=%s).\n", stack[sp].line, addr, *p); result = rd_expr_unequal (p, valid, level, check, print_errors); *p = delspc (*p); if (**p == '=') { *check = 0; ++*p; if (**p == '=') ++ * p; return result == rd_expr_equal (p, valid, level, check, print_errors); } else if (**p == '!' && (*p)[1] == '=') { *check = 0; (*p) += 2; return result != rd_expr_equal (p, valid, level, check, print_errors); } if (verbose >= 7) fprintf (stderr, "%5d (0x%04x): rd_equal returned %d (%04x).\n", stack[sp].line, addr, result, result); return result; } static int rd_expr_and (const char **p, int *valid, int level, int *check, int print_errors) { int result; if (verbose >= 6) fprintf (stderr, "%5d (0x%04x): Starting to read and expression " "(string=%s).\n", stack[sp].line, addr, *p); result = rd_expr_equal (p, valid, level, check, print_errors); *p = delspc (*p); if (**p == '&') { *check = 0; (*p)++; result &= rd_expr_and (p, valid, level, check, print_errors); } if (verbose >= 7) fprintf (stderr, "%5d (0x%04x): rd_expr_and returned %d (%04x).\n", stack[sp].line, addr, result, result); return result; } static int rd_expr_xor (const char **p, int *valid, int level, int *check, int print_errors) { int result; if (verbose >= 6) fprintf (stderr, "%5d (0x%04x): Starting to read xor expression " "(string=%s).\n", stack[sp].line, addr, *p); result = rd_expr_and (p, valid, level, check, print_errors); if (verbose >= 7) fprintf (stderr, "%5d (0x%04x): rd_expr_xor: rd_expr_and returned %d " "(%04x).\n", stack[sp].line, addr, result, result); *p = delspc (*p); if (**p == '^') { *check = 0; (*p)++; result ^= rd_expr_xor (p, valid, level, check, print_errors); } if (verbose >= 7) fprintf (stderr, "%5d (0x%04x): rd_expr_xor returned %d (%04x).\n", stack[sp].line, addr, result, result); return result; } static int rd_expr_or (const char **p, int *valid, int level, int *check, int print_errors) { int result; if (verbose >= 6) fprintf (stderr, "%5d (0x%04x): Starting to read or expression " "(string=%s).\n", stack[sp].line, addr, *p); result = rd_expr_xor (p, valid, level, check, print_errors); if (verbose >= 7) fprintf (stderr, "%5d (0x%04x): rd_expr_or: rd_expr_xor returned %d " "(%04x).\n", stack[sp].line, addr, result, result); *p = delspc (*p); if (**p == '|') { *check = 0; (*p)++; result |= rd_expr_or (p, valid, level, check, print_errors); } if (verbose >= 7) fprintf (stderr, "%5d (0x%04x): rd_expr_or returned %d (%04x).\n", stack[sp].line, addr, result, result); return result; } static int do_rd_expr (const char **p, char delimiter, int *valid, int level, int *check, int print_errors) { /* read an expression. delimiter can _not_ be '?' */ int result = 0; if (verbose >= 6) fprintf (stderr, "%5d (0x%04x): Starting to read expression " "(string=%s, delimiter=%c).\n", stack[sp].line, addr, *p, delimiter ? delimiter : ' '); *p = delspc (*p); if (!**p || **p == delimiter) { if (valid) *valid = 0; else if (print_errors) printerr (1, "expression expected (not %s)\n", *p); return 0; } result = rd_expr_or (p, valid, level, check, print_errors); *p = delspc (*p); if (**p == '?') { *check = 0; (*p)++; if (result) { result = do_rd_expr (p, ':', valid, level, check, print_errors); if (**p) (*p)++; do_rd_expr (p, delimiter, valid, level, check, print_errors); } else { do_rd_expr (p, ':', valid, level, check, print_errors); if (**p) (*p)++; result = do_rd_expr (p, delimiter, valid, level, check, print_errors); } } *p = delspc (*p); if (**p && **p != delimiter) { if (valid) *valid = 0; else if (print_errors) printerr (1, "junk at end of expression: %s\n", *p); } if (verbose >= 7) { fprintf (stderr, "%5d (0x%04x): rd_expr returned %d (%04x).\n", stack[sp].line, addr, result, result); if (valid && !*valid) fprintf (stderr, "%5d (0x%04x): Returning invalid result.\n", stack[sp].line, addr); } return result; } int rd_expr (const char **p, char delimiter, int *valid, int level, int print_errors) { int check = 1; int result; if (valid) *valid = 1; result = do_rd_expr (p, delimiter, valid, level, &check, print_errors); if (print_errors && (!valid || *valid) && check) printerr (0, "expression fully enclosed in parenthesis\n"); return result; } z80asm-1.8/gnulib/0000755000175000017500000000000011170052703013314 5ustar shevekshevekz80asm-1.8/gnulib/getopt.c0000644000175000017500000010004311170052703014760 0ustar shevekshevek/* Getopt for GNU. NOTE: getopt is now part of the C library, so if you don't know what "Keep this file name-space clean" means, talk to drepper@gnu.org before changing it! Copyright (C) 1987, 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, 2002, 2003 Free Software Foundation, Inc. This file is part of the GNU C Library. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ /* This tells Alpha OSF/1 not to define a getopt prototype in . Ditto for AIX 3.2 and . */ #ifndef _NO_PROTO # define _NO_PROTO #endif #ifdef HAVE_CONFIG_H # include #endif #include /* Comment out all this code if we are using the GNU C Library, and are not actually compiling the library itself. This code is part of the GNU C Library, but also included in many other GNU distributions. Compiling and linking in this code is a waste when using the GNU C library (especially if it is a shared library). Rather than having every GNU program understand `configure --with-gnu-libc' and omit the object files, it is simpler to just do this in the source for each such file. */ #define GETOPT_INTERFACE_VERSION 2 #if !defined _LIBC && defined __GLIBC__ && __GLIBC__ >= 2 # include # if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION # define ELIDE_CODE # endif #endif #ifndef ELIDE_CODE /* This needs to come after some library #include to get __GNU_LIBRARY__ defined. */ #ifdef __GNU_LIBRARY__ /* Don't include stdlib.h for non-GNU C libraries because some of them contain conflicting prototypes for getopt. */ # include # include #endif /* GNU C library. */ #include #ifdef VMS # include #endif #ifdef _LIBC # include #else /* This is for other GNU distributions with internationalized messages. */ # include "gettext.h" #endif #define _(msgid) gettext (msgid) #if defined _LIBC && defined USE_IN_LIBIO # include #endif #ifndef attribute_hidden # define attribute_hidden #endif /* This version of `getopt' appears to the caller like standard Unix `getopt' but it behaves differently for the user, since it allows the user to intersperse the options with the other arguments. As `getopt' works, it permutes the elements of ARGV so that, when it is done, all the options precede everything else. Thus all application programs are extended to handle flexible argument order. Setting the environment variable POSIXLY_CORRECT disables permutation. Then the behavior is completely standard. GNU application programs can use a third alternative mode in which they can distinguish the relative order of options and other arguments. */ #include "getopt.h" /* For communication from `getopt' to the caller. When `getopt' finds an option that takes an argument, the argument value is returned here. Also, when `ordering' is RETURN_IN_ORDER, each non-option ARGV-element is returned here. */ char *optarg; /* Index in ARGV of the next element to be scanned. This is used for communication to and from the caller and for communication between successive calls to `getopt'. On entry to `getopt', zero means this is the first call; initialize. When `getopt' returns -1, this is the index of the first of the non-option elements that the caller should itself scan. Otherwise, `optind' communicates from one call to the next how much of ARGV has been scanned so far. */ /* 1003.2 says this must be 1 before any call. */ int optind = 1; /* Formerly, initialization of getopt depended on optind==0, which causes problems with re-calling getopt as programs generally don't know that. */ int __getopt_initialized attribute_hidden; /* The next char to be scanned in the option-element in which the last option character we returned was found. This allows us to pick up the scan where we left off. If this is zero, or a null string, it means resume the scan by advancing to the next ARGV-element. */ static char *nextchar; /* Callers store zero here to inhibit the error message for unrecognized options. */ int opterr = 1; /* Set to an option character which was unrecognized. This must be initialized on some systems to avoid linking in the system's own getopt implementation. */ int optopt = '?'; /* Describe how to deal with options that follow non-option ARGV-elements. If the caller did not specify anything, the default is REQUIRE_ORDER if the environment variable POSIXLY_CORRECT is defined, PERMUTE otherwise. REQUIRE_ORDER means don't recognize them as options; stop option processing when the first non-option is seen. This is what Unix does. This mode of operation is selected by either setting the environment variable POSIXLY_CORRECT, or using `+' as the first character of the list of option characters. PERMUTE is the default. We permute the contents of ARGV as we scan, so that eventually all the non-options are at the end. This allows options to be given in any order, even with programs that were not written to expect this. RETURN_IN_ORDER is an option available to programs that were written to expect options and other ARGV-elements in any order and that care about the ordering of the two. We describe each non-option ARGV-element as if it were the argument of an option with character code 1. Using `-' as the first character of the list of option characters selects this mode of operation. The special argument `--' forces an end of option-scanning regardless of the value of `ordering'. In the case of RETURN_IN_ORDER, only `--' can cause `getopt' to return -1 with `optind' != ARGC. */ static enum { REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER } ordering; /* Value of POSIXLY_CORRECT environment variable. */ static char *posixly_correct; #ifndef __GNU_LIBRARY__ /* Avoid depending on library functions or files whose names are inconsistent. */ #ifndef getenv extern char *getenv (); #endif #endif /* not __GNU_LIBRARY__ */ /* Handle permutation of arguments. */ /* Describe the part of ARGV that contains non-options that have been skipped. `first_nonopt' is the index in ARGV of the first of them; `last_nonopt' is the index after the last of them. */ static int first_nonopt; static int last_nonopt; #ifdef _LIBC /* Stored original parameters. XXX This is no good solution. We should rather copy the args so that we can compare them later. But we must not use malloc(3). */ extern int __libc_argc; extern char **__libc_argv; /* Bash 2.0 gives us an environment variable containing flags indicating ARGV elements that should not be considered arguments. */ # ifdef USE_NONOPTION_FLAGS /* Defined in getopt_init.c */ extern char *__getopt_nonoption_flags; static int nonoption_flags_max_len; static int nonoption_flags_len; # endif # ifdef USE_NONOPTION_FLAGS # define SWAP_FLAGS(ch1, ch2) \ if (nonoption_flags_len > 0) \ { \ char __tmp = __getopt_nonoption_flags[ch1]; \ __getopt_nonoption_flags[ch1] = __getopt_nonoption_flags[ch2]; \ __getopt_nonoption_flags[ch2] = __tmp; \ } # else # define SWAP_FLAGS(ch1, ch2) # endif #else /* !_LIBC */ # define SWAP_FLAGS(ch1, ch2) #endif /* _LIBC */ /* Exchange two adjacent subsequences of ARGV. One subsequence is elements [first_nonopt,last_nonopt) which contains all the non-options that have been skipped so far. The other is elements [last_nonopt,optind), which contains all the options processed since those non-options were skipped. `first_nonopt' and `last_nonopt' are relocated so that they describe the new indices of the non-options in ARGV after they are moved. */ static void exchange (char **argv) { int bottom = first_nonopt; int middle = last_nonopt; int top = optind; char *tem; /* Exchange the shorter segment with the far end of the longer segment. That puts the shorter segment into the right place. It leaves the longer segment in the right place overall, but it consists of two parts that need to be swapped next. */ #if defined _LIBC && defined USE_NONOPTION_FLAGS /* First make sure the handling of the `__getopt_nonoption_flags' string can work normally. Our top argument must be in the range of the string. */ if (nonoption_flags_len > 0 && top >= nonoption_flags_max_len) { /* We must extend the array. The user plays games with us and presents new arguments. */ char *new_str = malloc (top + 1); if (new_str == NULL) nonoption_flags_len = nonoption_flags_max_len = 0; else { memset (__mempcpy (new_str, __getopt_nonoption_flags, nonoption_flags_max_len), '\0', top + 1 - nonoption_flags_max_len); nonoption_flags_max_len = top + 1; __getopt_nonoption_flags = new_str; } } #endif while (top > middle && middle > bottom) { if (top - middle > middle - bottom) { /* Bottom segment is the short one. */ int len = middle - bottom; register int i; /* Swap it with the top part of the top segment. */ for (i = 0; i < len; i++) { tem = argv[bottom + i]; argv[bottom + i] = argv[top - (middle - bottom) + i]; argv[top - (middle - bottom) + i] = tem; SWAP_FLAGS (bottom + i, top - (middle - bottom) + i); } /* Exclude the moved bottom segment from further swapping. */ top -= len; } else { /* Top segment is the short one. */ int len = top - middle; register int i; /* Swap it with the bottom part of the bottom segment. */ for (i = 0; i < len; i++) { tem = argv[bottom + i]; argv[bottom + i] = argv[middle + i]; argv[middle + i] = tem; SWAP_FLAGS (bottom + i, middle + i); } /* Exclude the moved top segment from further swapping. */ bottom += len; } } /* Update records for the slots the non-options now occupy. */ first_nonopt += (optind - last_nonopt); last_nonopt = optind; } /* Initialize the internal data when the first call is made. */ static const char * _getopt_initialize (int argc, char *const *argv, const char *optstring) { /* Start processing options with ARGV-element 1 (since ARGV-element 0 is the program name); the sequence of previously skipped non-option ARGV-elements is empty. */ first_nonopt = last_nonopt = optind; nextchar = NULL; posixly_correct = getenv ("POSIXLY_CORRECT"); /* Determine how to handle the ordering of options and nonoptions. */ if (optstring[0] == '-') { ordering = RETURN_IN_ORDER; ++optstring; } else if (optstring[0] == '+') { ordering = REQUIRE_ORDER; ++optstring; } else if (posixly_correct != NULL) ordering = REQUIRE_ORDER; else ordering = PERMUTE; #if defined _LIBC && defined USE_NONOPTION_FLAGS if (posixly_correct == NULL && argc == __libc_argc && argv == __libc_argv) { if (nonoption_flags_max_len == 0) { if (__getopt_nonoption_flags == NULL || __getopt_nonoption_flags[0] == '\0') nonoption_flags_max_len = -1; else { const char *orig_str = __getopt_nonoption_flags; int len = nonoption_flags_max_len = strlen (orig_str); if (nonoption_flags_max_len < argc) nonoption_flags_max_len = argc; __getopt_nonoption_flags = (char *) malloc (nonoption_flags_max_len); if (__getopt_nonoption_flags == NULL) nonoption_flags_max_len = -1; else memset (__mempcpy (__getopt_nonoption_flags, orig_str, len), '\0', nonoption_flags_max_len - len); } } nonoption_flags_len = nonoption_flags_max_len; } else nonoption_flags_len = 0; #endif return optstring; } /* Scan elements of ARGV (whose length is ARGC) for option characters given in OPTSTRING. If an element of ARGV starts with '-', and is not exactly "-" or "--", then it is an option element. The characters of this element (aside from the initial '-') are option characters. If `getopt' is called repeatedly, it returns successively each of the option characters from each of the option elements. If `getopt' finds another option character, it returns that character, updating `optind' and `nextchar' so that the next call to `getopt' can resume the scan with the following option character or ARGV-element. If there are no more option characters, `getopt' returns -1. Then `optind' is the index in ARGV of the first ARGV-element that is not an option. (The ARGV-elements have been permuted so that those that are not options now come last.) OPTSTRING is a string containing the legitimate option characters. If an option character is seen that is not listed in OPTSTRING, return '?' after printing an error message. If you set `opterr' to zero, the error message is suppressed but we still return '?'. If a char in OPTSTRING is followed by a colon, that means it wants an arg, so the following text in the same ARGV-element, or the text of the following ARGV-element, is returned in `optarg'. Two colons mean an option that wants an optional arg; if there is text in the current ARGV-element, it is returned in `optarg', otherwise `optarg' is set to zero. If OPTSTRING starts with `-' or `+', it requests different methods of handling the non-option ARGV-elements. See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above. Long-named options begin with `--' instead of `-'. Their names may be abbreviated as long as the abbreviation is unique or is an exact match for some defined option. If they have an argument, it follows the option name in the same ARGV-element, separated from the option name by a `=', or else the in next ARGV-element. When `getopt' finds a long-named option, it returns 0 if that option's `flag' field is nonzero, the value of the option's `val' field if the `flag' field is zero. The elements of ARGV aren't really const, because we permute them. But we pretend they're const in the prototype to be compatible with other systems. LONGOPTS is a vector of `struct option' terminated by an element containing a name which is zero. LONGIND returns the index in LONGOPT of the long-named option found. It is only valid when a long-named option has been found by the most recent call. If LONG_ONLY is nonzero, '-' as well as '--' can introduce long-named options. */ int _getopt_internal (int argc, char *const *argv, const char *optstring, const struct option *longopts, int *longind, int long_only) { int print_errors = opterr; if (optstring[0] == ':') print_errors = 0; if (argc < 1) return -1; optarg = NULL; if (optind == 0 || !__getopt_initialized) { if (optind == 0) optind = 1; /* Don't scan ARGV[0], the program name. */ optstring = _getopt_initialize (argc, argv, optstring); __getopt_initialized = 1; } /* Test whether ARGV[optind] points to a non-option argument. Either it does not have option syntax, or there is an environment flag from the shell indicating it is not an option. The later information is only used when the used in the GNU libc. */ #if defined _LIBC && defined USE_NONOPTION_FLAGS # define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0' \ || (optind < nonoption_flags_len \ && __getopt_nonoption_flags[optind] == '1')) #else # define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0') #endif if (nextchar == NULL || *nextchar == '\0') { /* Advance to the next ARGV-element. */ /* Give FIRST_NONOPT & LAST_NONOPT rational values if OPTIND has been moved back by the user (who may also have changed the arguments). */ if (last_nonopt > optind) last_nonopt = optind; if (first_nonopt > optind) first_nonopt = optind; if (ordering == PERMUTE) { /* If we have just processed some options following some non-options, exchange them so that the options come first. */ if (first_nonopt != last_nonopt && last_nonopt != optind) exchange ((char **) argv); else if (last_nonopt != optind) first_nonopt = optind; /* Skip any additional non-options and extend the range of non-options previously skipped. */ while (optind < argc && NONOPTION_P) optind++; last_nonopt = optind; } /* The special ARGV-element `--' means premature end of options. Skip it like a null option, then exchange with previous non-options as if it were an option, then skip everything else like a non-option. */ if (optind != argc && !strcmp (argv[optind], "--")) { optind++; if (first_nonopt != last_nonopt && last_nonopt != optind) exchange ((char **) argv); else if (first_nonopt == last_nonopt) first_nonopt = optind; last_nonopt = argc; optind = argc; } /* If we have done all the ARGV-elements, stop the scan and back over any non-options that we skipped and permuted. */ if (optind == argc) { /* Set the next-arg-index to point at the non-options that we previously skipped, so the caller will digest them. */ if (first_nonopt != last_nonopt) optind = first_nonopt; return -1; } /* If we have come to a non-option and did not permute it, either stop the scan or describe it to the caller and pass it by. */ if (NONOPTION_P) { if (ordering == REQUIRE_ORDER) return -1; optarg = argv[optind++]; return 1; } /* We have found another option-ARGV-element. Skip the initial punctuation. */ nextchar = (argv[optind] + 1 + (longopts != NULL && argv[optind][1] == '-')); } /* Decode the current option-ARGV-element. */ /* Check whether the ARGV-element is a long option. If long_only and the ARGV-element has the form "-f", where f is a valid short option, don't consider it an abbreviated form of a long option that starts with f. Otherwise there would be no way to give the -f short option. On the other hand, if there's a long option "fubar" and the ARGV-element is "-fu", do consider that an abbreviation of the long option, just like "--fu", and not "-f" with arg "u". This distinction seems to be the most useful approach. */ if (longopts != NULL && (argv[optind][1] == '-' || (long_only && (argv[optind][2] || !strchr (optstring, argv[optind][1]))))) { char *nameend; const struct option *p; const struct option *pfound = NULL; int exact = 0; int ambig = 0; int indfound = -1; int option_index; for (nameend = nextchar; *nameend && *nameend != '='; nameend++) /* Do nothing. */ ; /* Test all long options for either exact match or abbreviated matches. */ for (p = longopts, option_index = 0; p->name; p++, option_index++) if (!strncmp (p->name, nextchar, nameend - nextchar)) { if ((unsigned int) (nameend - nextchar) == (unsigned int) strlen (p->name)) { /* Exact match found. */ pfound = p; indfound = option_index; exact = 1; break; } else if (pfound == NULL) { /* First nonexact match found. */ pfound = p; indfound = option_index; } else if (long_only || pfound->has_arg != p->has_arg || pfound->flag != p->flag || pfound->val != p->val) /* Second or later nonexact match found. */ ambig = 1; } if (ambig && !exact) { if (print_errors) { #if defined _LIBC && defined USE_IN_LIBIO char *buf; if (__asprintf (&buf, _("%s: option `%s' is ambiguous\n"), argv[0], argv[optind]) >= 0) { if (_IO_fwide (stderr, 0) > 0) __fwprintf (stderr, L"%s", buf); else fputs (buf, stderr); free (buf); } #else fprintf (stderr, _("%s: option `%s' is ambiguous\n"), argv[0], argv[optind]); #endif } nextchar += strlen (nextchar); optind++; optopt = 0; return '?'; } if (pfound != NULL) { option_index = indfound; optind++; if (*nameend) { /* Don't test has_arg with >, because some C compilers don't allow it to be used on enums. */ if (pfound->has_arg) optarg = nameend + 1; else { if (print_errors) { #if defined _LIBC && defined USE_IN_LIBIO char *buf; int n; #endif if (argv[optind - 1][1] == '-') { /* --option */ #if defined _LIBC && defined USE_IN_LIBIO n = __asprintf (&buf, _("\ %s: option `--%s' doesn't allow an argument\n"), argv[0], pfound->name); #else fprintf (stderr, _("\ %s: option `--%s' doesn't allow an argument\n"), argv[0], pfound->name); #endif } else { /* +option or -option */ #if defined _LIBC && defined USE_IN_LIBIO n = __asprintf (&buf, _("\ %s: option `%c%s' doesn't allow an argument\n"), argv[0], argv[optind - 1][0], pfound->name); #else fprintf (stderr, _("\ %s: option `%c%s' doesn't allow an argument\n"), argv[0], argv[optind - 1][0], pfound->name); #endif } #if defined _LIBC && defined USE_IN_LIBIO if (n >= 0) { if (_IO_fwide (stderr, 0) > 0) __fwprintf (stderr, L"%s", buf); else fputs (buf, stderr); free (buf); } #endif } nextchar += strlen (nextchar); optopt = pfound->val; return '?'; } } else if (pfound->has_arg == 1) { if (optind < argc) optarg = argv[optind++]; else { if (print_errors) { #if defined _LIBC && defined USE_IN_LIBIO char *buf; if (__asprintf (&buf, _("\ %s: option `%s' requires an argument\n"), argv[0], argv[optind - 1]) >= 0) { if (_IO_fwide (stderr, 0) > 0) __fwprintf (stderr, L"%s", buf); else fputs (buf, stderr); free (buf); } #else fprintf (stderr, _("%s: option `%s' requires an argument\n"), argv[0], argv[optind - 1]); #endif } nextchar += strlen (nextchar); optopt = pfound->val; return optstring[0] == ':' ? ':' : '?'; } } nextchar += strlen (nextchar); if (longind != NULL) *longind = option_index; if (pfound->flag) { *(pfound->flag) = pfound->val; return 0; } return pfound->val; } /* Can't find it as a long option. If this is not getopt_long_only, or the option starts with '--' or is not a valid short option, then it's an error. Otherwise interpret it as a short option. */ if (!long_only || argv[optind][1] == '-' || strchr (optstring, *nextchar) == NULL) { if (print_errors) { #if defined _LIBC && defined USE_IN_LIBIO char *buf; int n; #endif if (argv[optind][1] == '-') { /* --option */ #if defined _LIBC && defined USE_IN_LIBIO n = __asprintf (&buf, _("%s: unrecognized option `--%s'\n"), argv[0], nextchar); #else fprintf (stderr, _("%s: unrecognized option `--%s'\n"), argv[0], nextchar); #endif } else { /* +option or -option */ #if defined _LIBC && defined USE_IN_LIBIO n = __asprintf (&buf, _("%s: unrecognized option `%c%s'\n"), argv[0], argv[optind][0], nextchar); #else fprintf (stderr, _("%s: unrecognized option `%c%s'\n"), argv[0], argv[optind][0], nextchar); #endif } #if defined _LIBC && defined USE_IN_LIBIO if (n >= 0) { if (_IO_fwide (stderr, 0) > 0) __fwprintf (stderr, L"%s", buf); else fputs (buf, stderr); free (buf); } #endif } nextchar = (char *) ""; optind++; optopt = 0; return '?'; } } /* Look at and handle the next short option-character. */ { char c = *nextchar++; char *temp = strchr (optstring, c); /* Increment `optind' when we start to process its last character. */ if (*nextchar == '\0') ++optind; if (temp == NULL || c == ':') { if (print_errors) { #if defined _LIBC && defined USE_IN_LIBIO char *buf; int n; #endif if (posixly_correct) { /* 1003.2 specifies the format of this message. */ #if defined _LIBC && defined USE_IN_LIBIO n = __asprintf (&buf, _("%s: illegal option -- %c\n"), argv[0], c); #else fprintf (stderr, _("%s: illegal option -- %c\n"), argv[0], c); #endif } else { #if defined _LIBC && defined USE_IN_LIBIO n = __asprintf (&buf, _("%s: invalid option -- %c\n"), argv[0], c); #else fprintf (stderr, _("%s: invalid option -- %c\n"), argv[0], c); #endif } #if defined _LIBC && defined USE_IN_LIBIO if (n >= 0) { if (_IO_fwide (stderr, 0) > 0) __fwprintf (stderr, L"%s", buf); else fputs (buf, stderr); free (buf); } #endif } optopt = c; return '?'; } /* Convenience. Treat POSIX -W foo same as long option --foo */ if (temp[0] == 'W' && temp[1] == ';') { char *nameend; const struct option *p; const struct option *pfound = NULL; int exact = 0; int ambig = 0; int indfound = 0; int option_index; /* This is an option that requires an argument. */ if (*nextchar != '\0') { optarg = nextchar; /* If we end this ARGV-element by taking the rest as an arg, we must advance to the next element now. */ optind++; } else if (optind == argc) { if (print_errors) { /* 1003.2 specifies the format of this message. */ #if defined _LIBC && defined USE_IN_LIBIO char *buf; if (__asprintf (&buf, _("%s: option requires an argument -- %c\n"), argv[0], c) >= 0) { if (_IO_fwide (stderr, 0) > 0) __fwprintf (stderr, L"%s", buf); else fputs (buf, stderr); free (buf); } #else fprintf (stderr, _("%s: option requires an argument -- %c\n"), argv[0], c); #endif } optopt = c; if (optstring[0] == ':') c = ':'; else c = '?'; return c; } else /* We already incremented `optind' once; increment it again when taking next ARGV-elt as argument. */ optarg = argv[optind++]; /* optarg is now the argument, see if it's in the table of longopts. */ for (nextchar = nameend = optarg; *nameend && *nameend != '='; nameend++) /* Do nothing. */ ; /* Test all long options for either exact match or abbreviated matches. */ for (p = longopts, option_index = 0; p->name; p++, option_index++) if (!strncmp (p->name, nextchar, nameend - nextchar)) { if ((unsigned int) (nameend - nextchar) == strlen (p->name)) { /* Exact match found. */ pfound = p; indfound = option_index; exact = 1; break; } else if (pfound == NULL) { /* First nonexact match found. */ pfound = p; indfound = option_index; } else /* Second or later nonexact match found. */ ambig = 1; } if (ambig && !exact) { if (print_errors) { #if defined _LIBC && defined USE_IN_LIBIO char *buf; if (__asprintf (&buf, _("%s: option `-W %s' is ambiguous\n"), argv[0], argv[optind]) >= 0) { if (_IO_fwide (stderr, 0) > 0) __fwprintf (stderr, L"%s", buf); else fputs (buf, stderr); free (buf); } #else fprintf (stderr, _("%s: option `-W %s' is ambiguous\n"), argv[0], argv[optind]); #endif } nextchar += strlen (nextchar); optind++; return '?'; } if (pfound != NULL) { option_index = indfound; if (*nameend) { /* Don't test has_arg with >, because some C compilers don't allow it to be used on enums. */ if (pfound->has_arg) optarg = nameend + 1; else { if (print_errors) { #if defined _LIBC && defined USE_IN_LIBIO char *buf; if (__asprintf (&buf, _("\ %s: option `-W %s' doesn't allow an argument\n"), argv[0], pfound->name) >= 0) { if (_IO_fwide (stderr, 0) > 0) __fwprintf (stderr, L"%s", buf); else fputs (buf, stderr); free (buf); } #else fprintf (stderr, _("\ %s: option `-W %s' doesn't allow an argument\n"), argv[0], pfound->name); #endif } nextchar += strlen (nextchar); return '?'; } } else if (pfound->has_arg == 1) { if (optind < argc) optarg = argv[optind++]; else { if (print_errors) { #if defined _LIBC && defined USE_IN_LIBIO char *buf; if (__asprintf (&buf, _("\ %s: option `%s' requires an argument\n"), argv[0], argv[optind - 1]) >= 0) { if (_IO_fwide (stderr, 0) > 0) __fwprintf (stderr, L"%s", buf); else fputs (buf, stderr); free (buf); } #else fprintf (stderr, _("%s: option `%s' requires an argument\n"), argv[0], argv[optind - 1]); #endif } nextchar += strlen (nextchar); return optstring[0] == ':' ? ':' : '?'; } } nextchar += strlen (nextchar); if (longind != NULL) *longind = option_index; if (pfound->flag) { *(pfound->flag) = pfound->val; return 0; } return pfound->val; } nextchar = NULL; return 'W'; /* Let the application handle it. */ } if (temp[1] == ':') { if (temp[2] == ':') { /* This is an option that accepts an argument optionally. */ if (*nextchar != '\0') { optarg = nextchar; optind++; } else optarg = NULL; nextchar = NULL; } else { /* This is an option that requires an argument. */ if (*nextchar != '\0') { optarg = nextchar; /* If we end this ARGV-element by taking the rest as an arg, we must advance to the next element now. */ optind++; } else if (optind == argc) { if (print_errors) { /* 1003.2 specifies the format of this message. */ #if defined _LIBC && defined USE_IN_LIBIO char *buf; if (__asprintf (&buf, _("\ %s: option requires an argument -- %c\n"), argv[0], c) >= 0) { if (_IO_fwide (stderr, 0) > 0) __fwprintf (stderr, L"%s", buf); else fputs (buf, stderr); free (buf); } #else fprintf (stderr, _("%s: option requires an argument -- %c\n"), argv[0], c); #endif } optopt = c; if (optstring[0] == ':') c = ':'; else c = '?'; } else /* We already incremented `optind' once; increment it again when taking next ARGV-elt as argument. */ optarg = argv[optind++]; nextchar = NULL; } } return c; } } int getopt (int argc, char *const *argv, const char *optstring) { return _getopt_internal (argc, argv, optstring, (const struct option *) 0, (int *) 0, 0); } #endif /* Not ELIDE_CODE. */ #ifdef TEST /* Compile with -DTEST to make an executable for use in testing the above definition of `getopt'. */ int main (int argc, char **argv) { int c; int digit_optind = 0; while (1) { int this_option_optind = optind ? optind : 1; c = getopt (argc, argv, "abc:d:0123456789"); if (c == -1) break; switch (c) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': if (digit_optind != 0 && digit_optind != this_option_optind) printf ("digits occur in two different argv-elements.\n"); digit_optind = this_option_optind; printf ("option %c\n", c); break; case 'a': printf ("option a\n"); break; case 'b': printf ("option b\n"); break; case 'c': printf ("option c with value `%s'\n", optarg); break; case '?': break; default: printf ("?? getopt returned character code 0%o ??\n", c); } } if (optind < argc) { printf ("non-option ARGV-elements: "); while (optind < argc) printf ("%s ", argv[optind++]); printf ("\n"); } exit (0); } #endif /* TEST */ z80asm-1.8/gnulib/getopt.h0000644000175000017500000001374111170052703014775 0ustar shevekshevek/* Declarations for getopt. Copyright (C) 1989, 1990, 1991, 1992, 1993, 1994, 1996, 1997, 1998, 1999, 2001, 2003 Free Software Foundation, Inc. This file is part of the GNU C Library. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef _GETOPT_H #ifndef __need_getopt # define _GETOPT_H 1 #endif /* If __GNU_LIBRARY__ is not already defined, either we are being used standalone, or this is the first header included in the source file. If we are being used with glibc, we need to include , but that does not exist if we are standalone. So: if __GNU_LIBRARY__ is not defined, include , which will pull in for us if it's from glibc. (Why ctype.h? It's guaranteed to exist and it doesn't flood the namespace with stuff the way some other headers do.) */ #if !defined __GNU_LIBRARY__ # include #endif #ifdef __cplusplus extern "C" { #endif /* For communication from `getopt' to the caller. When `getopt' finds an option that takes an argument, the argument value is returned here. Also, when `ordering' is RETURN_IN_ORDER, each non-option ARGV-element is returned here. */ extern char *optarg; /* Index in ARGV of the next element to be scanned. This is used for communication to and from the caller and for communication between successive calls to `getopt'. On entry to `getopt', zero means this is the first call; initialize. When `getopt' returns -1, this is the index of the first of the non-option elements that the caller should itself scan. Otherwise, `optind' communicates from one call to the next how much of ARGV has been scanned so far. */ extern int optind; /* Callers store zero here to inhibit the error message `getopt' prints for unrecognized options. */ extern int opterr; /* Set to an option character which was unrecognized. */ extern int optopt; #ifndef __need_getopt /* Describe the long-named options requested by the application. The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector of `struct option' terminated by an element containing a name which is zero. The field `has_arg' is: no_argument (or 0) if the option does not take an argument, required_argument (or 1) if the option requires an argument, optional_argument (or 2) if the option takes an optional argument. If the field `flag' is not NULL, it points to a variable that is set to the value given in the field `val' when the option is found, but left unchanged if the option is not found. To have a long-named option do something other than set an `int' to a compiled-in constant, such as set a value from `optarg', set the option's `flag' field to zero and its `val' field to a nonzero value (the equivalent single-letter option character, if there is one). For long options that have a zero `flag' field, `getopt' returns the contents of the `val' field. */ struct option { const char *name; /* has_arg can't be an enum because some compilers complain about type mismatches in all the code that assumes it is an int. */ int has_arg; int *flag; int val; }; /* Names for the values of the `has_arg' field of `struct option'. */ # define no_argument 0 # define required_argument 1 # define optional_argument 2 #endif /* need getopt */ /* Get definitions and prototypes for functions to process the arguments in ARGV (ARGC of them, minus the program name) for options given in OPTS. Return the option character from OPTS just read. Return -1 when there are no more options. For unrecognized options, or options missing arguments, `optopt' is set to the option letter, and '?' is returned. The OPTS string is a list of characters which are recognized option letters, optionally followed by colons, specifying that that letter takes an argument, to be placed in `optarg'. If a letter in OPTS is followed by two colons, its argument is optional. This behavior is specific to the GNU `getopt'. The argument `--' causes premature termination of argument scanning, explicitly telling `getopt' that there are no more options. If OPTS begins with `--', then non-option arguments are treated as arguments to the option '\0'. This behavior is specific to the GNU `getopt'. */ #ifdef __GNU_LIBRARY__ /* Many other libraries have conflicting prototypes for getopt, with differences in the consts, in stdlib.h. To avoid compilation errors, only prototype getopt for the GNU C library. */ extern int getopt (int ___argc, char *const *___argv, const char *__shortopts); #else /* not __GNU_LIBRARY__ */ extern int getopt (); #endif /* __GNU_LIBRARY__ */ #ifndef __need_getopt extern int getopt_long (int ___argc, char *const *___argv, const char *__shortopts, const struct option *__longopts, int *__longind); extern int getopt_long_only (int ___argc, char *const *___argv, const char *__shortopts, const struct option *__longopts, int *__longind); /* Internal only. Users should not call this directly. */ extern int _getopt_internal (int ___argc, char *const *___argv, const char *__shortopts, const struct option *__longopts, int *__longind, int __long_only); #endif #ifdef __cplusplus } #endif /* Make sure we later can get all the definitions and declarations. */ #undef __need_getopt #endif /* getopt.h */ z80asm-1.8/gnulib/getopt1.c0000644000175000017500000001037311170052703015047 0ustar shevekshevek/* getopt_long and getopt_long_only entry points for GNU getopt. Copyright (C) 1987, 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1996, 1997, 1998, 2003 Free Software Foundation, Inc. This file is part of the GNU C Library. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifdef HAVE_CONFIG_H #include #endif #ifdef _LIBC # include #else # include "getopt.h" #endif #include /* Comment out all this code if we are using the GNU C Library, and are not actually compiling the library itself. This code is part of the GNU C Library, but also included in many other GNU distributions. Compiling and linking in this code is a waste when using the GNU C library (especially if it is a shared library). Rather than having every GNU program understand `configure --with-gnu-libc' and omit the object files, it is simpler to just do this in the source for each such file. */ #define GETOPT_INTERFACE_VERSION 2 #if !defined _LIBC && defined __GLIBC__ && __GLIBC__ >= 2 #include #if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION #define ELIDE_CODE #endif #endif #ifndef ELIDE_CODE /* This needs to come after some library #include to get __GNU_LIBRARY__ defined. */ #ifdef __GNU_LIBRARY__ #include #endif #ifndef NULL #define NULL 0 #endif int getopt_long (int argc, char *const *argv, const char *options, const struct option *long_options, int *opt_index) { return _getopt_internal (argc, argv, options, long_options, opt_index, 0); } /* Like getopt_long, but '-' as well as '--' can indicate a long option. If an option that starts with '-' (not '--') doesn't match a long option, but does match a short option, it is parsed as a short option instead. */ int getopt_long_only (int argc, char *const *argv, const char *options, const struct option *long_options, int *opt_index) { return _getopt_internal (argc, argv, options, long_options, opt_index, 1); } # ifdef _LIBC libc_hidden_def (getopt_long) libc_hidden_def (getopt_long_only) # endif #endif /* Not ELIDE_CODE. */ #ifdef TEST #include int main (int argc, char **argv) { int c; int digit_optind = 0; while (1) { int this_option_optind = optind ? optind : 1; int option_index = 0; static struct option long_options[] = { {"add", 1, 0, 0}, {"append", 0, 0, 0}, {"delete", 1, 0, 0}, {"verbose", 0, 0, 0}, {"create", 0, 0, 0}, {"file", 1, 0, 0}, {0, 0, 0, 0} }; c = getopt_long (argc, argv, "abc:d:0123456789", long_options, &option_index); if (c == -1) break; switch (c) { case 0: printf ("option %s", long_options[option_index].name); if (optarg) printf (" with arg %s", optarg); printf ("\n"); break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': if (digit_optind != 0 && digit_optind != this_option_optind) printf ("digits occur in two different argv-elements.\n"); digit_optind = this_option_optind; printf ("option %c\n", c); break; case 'a': printf ("option a\n"); break; case 'b': printf ("option b\n"); break; case 'c': printf ("option c with value `%s'\n", optarg); break; case 'd': printf ("option d with value `%s'\n", optarg); break; case '?': break; default: printf ("?? getopt returned character code 0%o ??\n", c); } } if (optind < argc) { printf ("non-option ARGV-elements: "); while (optind < argc) printf ("%s ", argv[optind++]); printf ("\n"); } exit (0); } #endif /* TEST */ z80asm-1.8/gnulib/gettext.h0000644000175000017500000000572111170052703015156 0ustar shevekshevek/* Convenience header for conditional use of GNU . Copyright (C) 1995-1998, 2000-2002 Free Software Foundation, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef _LIBGETTEXT_H #define _LIBGETTEXT_H 1 /* NLS can be disabled through the configure --disable-nls option. */ #if ENABLE_NLS /* Get declarations of GNU message catalog functions. */ # include #else /* Solaris /usr/include/locale.h includes /usr/include/libintl.h, which chokes if dcgettext is defined as a macro. So include it now, to make later inclusions of a NOP. We don't include as well because people using "gettext.h" will not include , and also including would fail on SunOS 4, whereas is OK. */ #if defined(__sun) # include #endif /* Disabled NLS. The casts to 'const char *' serve the purpose of producing warnings for invalid uses of the value returned from these functions. On pre-ANSI systems without 'const', the config.h file is supposed to contain "#define const". */ # define gettext(Msgid) ((const char *) (Msgid)) # define dgettext(Domainname, Msgid) ((const char *) (Msgid)) # define dcgettext(Domainname, Msgid, Category) ((const char *) (Msgid)) # define ngettext(Msgid1, Msgid2, N) \ ((N) == 1 ? (const char *) (Msgid1) : (const char *) (Msgid2)) # define dngettext(Domainname, Msgid1, Msgid2, N) \ ((N) == 1 ? (const char *) (Msgid1) : (const char *) (Msgid2)) # define dcngettext(Domainname, Msgid1, Msgid2, N, Category) \ ((N) == 1 ? (const char *) (Msgid1) : (const char *) (Msgid2)) # define textdomain(Domainname) ((const char *) (Domainname)) # define bindtextdomain(Domainname, Dirname) ((const char *) (Dirname)) # define bind_textdomain_codeset(Domainname, Codeset) ((const char *) (Codeset)) #endif /* A pseudo function call that serves as a marker for the automated extraction of messages, but does not call gettext(). The run-time translation is done at a different place in the code. The argument, String, should be a literal string. Concatenated strings and other string expressions won't work. The macro's expansion is not parenthesized, so that it is suitable as initializer for static 'char[]' or 'const char[]' variables. */ #define gettext_noop(String) String #endif /* _LIBGETTEXT_H */ z80asm-1.8/headers/0000755000175000017500000000000011170052703013447 5ustar shevekshevekz80asm-1.8/headers/msx-bios.asm0000644000175000017500000000531611170052703015717 0ustar shevekshevek; msx-bios.asm - BIOS calls for the MSX computer ; Copyright 2005 Bas Wijnen ; ; This file is part of z80asm. ; ; Z80asm is free software; you can 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. ; ; Z80asm is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR 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 . CHKRAM: equ 0x0000 SYNCHR: equ 0x0008 RDSLT: equ 0x000C CHRGTR: equ 0x0010 WRSLT: equ 0x0014 OUTDO: equ 0x0018 CALSLT: equ 0x001C DCOMPR: equ 0x0020 ENASLT: equ 0x0024 GETYPR: equ 0x0028 CALLF: equ 0x0030 KEYINT: equ 0x0038 INITIO: equ 0x003B INIFNK: equ 0x003E DISSCR: equ 0x0041 ENASCR: equ 0x0044 WRTVDP: equ 0x0047 RDVRM: equ 0x004A WRTVRM: equ 0x004D SETRD: equ 0x0050 SETWRT: equ 0x0053 FILVRM: equ 0x0056 LDIRMV: equ 0x0059 LDIRVM: equ 0x005C CHGMOD: equ 0x005F CHGCLR: equ 0x0062 NMI: equ 0x0066 CLRSPR: equ 0x0069 INITXT: equ 0x006C INIT32: equ 0x006F INIGRP: equ 0x0072 INIMLT: equ 0x0075 SETTXT: equ 0x0078 SETT32: equ 0x007B SETGRP: equ 0x007E SETMLT: equ 0x0081 CALPAT: equ 0x0084 CALATR: equ 0x0087 GSPSIZ: equ 0x008A GRPPRT: equ 0x008D GICINI: equ 0x0090 WRTPSG: equ 0x0093 RDPSG: equ 0x0096 STRTMS: equ 0x0099 CHSNS: equ 0x009C CHGET: equ 0x009F CHPUT: equ 0x00A2 LPTOUT: equ 0x00A5 LPTSTT: equ 0x00A8 CNVCHR: equ 0x00AB PINLIN: equ 0x00AE INLIN: equ 0x00B1 QINLIN: equ 0x00B4 BREAKX: equ 0x00B7 ISCNTC: equ 0x00BA CKCNTC: equ 0x00BD BEEP: equ 0x00C0 CLS: equ 0x00C3 POSIT: equ 0x00C6 FNKSB: equ 0x00C9 ERAFNK: equ 0x00CC DSPFNK: equ 0x00CF TOTEXT: equ 0x00D2 GTSTCK: equ 0x00D5 GTTRIG: equ 0x00D8 GTPAD: equ 0x00DB GTPDL: equ 0x00DE TAPION: equ 0x00E1 TAPIN: equ 0x00E4 TAPIOF: equ 0x00E7 TAPOON: equ 0x00EA TAPOUT: equ 0x00ED TAPOOF: equ 0x00F0 STMOTR: equ 0x00F3 LFTQ: equ 0x00F6 PUTQ: equ 0x00F9 RIGHTC: equ 0x00FC LEFTC: equ 0x00FF UPC: equ 0x0102 TUPC: equ 0x0105 DOWNC: equ 0x0108 TDOWNC: equ 0x010B SCALXY: equ 0x010E MAPXY: equ 0x0111 FETCHC: equ 0x0114 STOREC: equ 0x0117 SETATR: equ 0x011A READC: equ 0x011D SETC: equ 0x0120 NSETCX: equ 0x0123 GTASPC: equ 0x0126 PNTINI: equ 0x0129 SCANR: equ 0x012C SCANL: equ 0x012F CHGCAP: equ 0x0132 CHGSND: equ 0x0135 RSLREG: equ 0x0138 WSLREG: equ 0x013B RDVDP: equ 0x013E SNSMAT: equ 0x0141 PHYDIO: equ 0x0144 FORMAT: equ 0x0147 ISFLIO: equ 0x014A OUTDLP: equ 0x014D GETVCP: equ 0x0150 GETVC2: equ 0x0153 KILBUF: equ 0x0156 CALBAS: equ 0x0159 z80asm-1.8/headers/msx2+-bios.asm0000644000175000017500000000146511170052703016055 0ustar shevekshevek; msx2+-bios.asm - BIOS calls for the MSX computer ; Copyright 2005 Bas Wijnen ; ; This file is part of z80asm. ; ; Z80asm is free software; you can 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. ; ; Z80asm is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR 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 . include 'msx2-bios.asm' RDBTST: equ 0x017A WRBTST: equ 0x017D z80asm-1.8/headers/msx2-bios.asm0000644000175000017500000000171011170052703015773 0ustar shevekshevek; msx2-bios.asm - BIOS calls for the MSX computer ; Copyright 2005 Bas Wijnen ; ; This file is part of z80asm. ; ; Z80asm is free software; you can 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. ; ; Z80asm is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR 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 . include 'msx-bios.asm' SUBROM: equ 0x015C EXTROM: equ 0x015F CHKSLZ: equ 0x0162 CHKNEW: equ 0x0165 EOL: equ 0x0168 BIGFIL: equ 0x016B NSETRD: equ 0x016E NSTWRT: equ 0x0171 NRDVRM: equ 0x0174 NWRVRM: equ 0x0177 z80asm-1.8/headers/msx2-subrom.asm0000644000175000017500000000567411170052703016363 0ustar shevekshevek; msx2-subrom.asm - BIOS calls for the MSX computer ; Copyright 2005 Bas Wijnen ; ; This file is part of z80asm. ; ; Z80asm is free software; you can 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. ; ; Z80asm is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR 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 . SUBROM_PAINT: equ 0x0069 SUBROM_PSET: equ 0x006D SUBROM_ATRSCN: equ 0x0071 SUBROM_GLINE: equ 0x0075 SUBROM_DOBOXF: equ 0x0079 SUBROM_DOLINE: equ 0x007D SUBROM_BOXLIN: equ 0x0081 SUBROM_DOGRPH: equ 0x0085 SUBROM_GRPPRT: equ 0x0089 SUBROM_SCALXY: equ 0x008D SUBROM_MAPXYC: equ 0x0091 SUBROM_READC: equ 0x0095 SUBROM_SETATR: equ 0x0099 SUBROM_SETC: equ 0x009D SUBROM_TRIGHT: equ 0x00A1 SUBROM_RIGHTC: equ 0x00A5 SUBROM_TLEFTC: equ 0x00A9 SUBROM_LEFTC: equ 0x00AD SUBROM_TDOWNC: equ 0x00B1 SUBROM_DOWNC: equ 0x00B5 SUBROM_TUPC: equ 0x00B9 SUBROM_UPC: equ 0x00BD SUBROM_SCANR: equ 0x00C1 SUBROM_SCANL: equ 0x00C5 SUBROM_NVBXLN: equ 0x00C9 SUBROM_NVBXFL: equ 0x00CD SUBROM_CHGMOD: equ 0x00D1 SUBROM_INITXT: equ 0x00D5 SUBROM_INIT32: equ 0x00D9 SUBROM_INIGRP: equ 0x00DD SUBROM_INIMLT: equ 0x00E1 SUBROM_SETTXT: equ 0x00E5 SUBROM_SETT32: equ 0x00E9 SUBROM_SETGRP: equ 0x00ED SUBROM_SETMLT: equ 0x00F1 SUBROM_CLRSPR: equ 0x00F5 SUBROM_CALPAT: equ 0x00F9 SUBROM_CALATR: equ 0x00FD SUBROM_GSPSIZ: equ 0x0101 SUBROM_GETPAT: equ 0x0105 SUBROM_WTRVRM: equ 0x0109 SUBROM_RDVRM: equ 0x010D SUBROM_CHGCLR: equ 0x0111 SUBROM_CLS: equ 0x0115 SUBROM_CLRTXT: equ 0x0119 SUBROM_DSPFNK: equ 0x011D SUBROM_DELLNO: equ 0x0121 SUBROM_INSLNO: equ 0x0125 SUBROM_PUTVRM: equ 0x0129 SUBROM_WRTVDP: equ 0x012D SUBROM_VDPSTA: equ 0x0131 SUBROM_KYKLOK: equ 0x0135 SUBROM_PUTCHR: equ 0x0139 SUBROM_SETPAG: equ 0x013D SUBROM_INIPLT: equ 0x0141 SUBROM_RSTPLT: equ 0x0145 SUBROM_GETPLT: equ 0x0149 SUBROM_SETPLT: equ 0x014D SUBROM_PUTSPRT: equ 0x0151 SUBROM_COLOR: equ 0x0155 SUBROM_SCREEN: equ 0x0159 SUBROM_WIDTHS: equ 0x015D SUBROM_VDP: equ 0x0161 SUBROM_VDPF: equ 0x0165 SUBROM_BASE: equ 0x0169 SUBROM_BASEF: equ 0x016D SUBROM_VPOKE: equ 0x0171 SUBROM_VPEEK: equ 0x0175 SUBROM_SETS: equ 0x0179 SUBROM_BEEP: equ 0x017D SUBROM_PROMPT: equ 0x0181 SUBROM_SDFSCR: equ 0x0185 SUBROM_SETSCR: equ 0x0189 SUBROM_SCOPY: equ 0x018D SUBROM_BLTVV: equ 0x0191 SUBROM_BLTVM: equ 0x0195 SUBROM_BLTMV: equ 0x0199 SUBROM_BLTVD: equ 0x019D SUBROM_BLTDV: equ 0x01A1 SUBROM_BLTMD: equ 0x01A5 SUBROM_BLTDM: equ 0x01A9 SUBROM_NEWPAD: equ 0x01AD SUBROM_GETPUT: equ 0x01B1 SUBROM_CHGMDP: equ 0x01B5 SUBROM_RESVI: equ 0x01B9 SUBROM_KNJPRT: equ 0x01BD SUBROM_REDCLK: equ 0x01F5 SUBROM_WRTCLK: equ 0x01F9 z80asm-1.8/headers/msxturbor-bios.asm0000644000175000017500000000154011170052703017150 0ustar shevekshevek; msxturbor-bios.asm - BIOS calls for the MSX computer ; Copyright 2005 Bas Wijnen ; ; This file is part of z80asm. ; ; Z80asm is free software; you can 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. ; ; Z80asm is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR 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 . include 'msx2+-bios.asm' CHGCPU: equ 0x0180 GETCPU: equ 0x0183 PCMPLY: equ 0x0186 PCMREC: equ 0x0189 z80asm-1.8/tests/0000755000175000017500000000000011170052703013176 5ustar shevekshevekz80asm-1.8/tests/Makefile0000644000175000017500000000201311170052703014632 0ustar shevekshevek# Makefile using z80asm # Copyright 2007 Bas Wijnen # # This file is part of z80asm. # # Z80asm is free software; you can 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. # # Z80asm is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR 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 . # The output of the assembler can be parsed by vim or emacs. all: pass %: %.asm %.correct-err %.correct-bin ../z80asm Makefile ../z80asm -I ../headers $< -o $@.bin 2> $@.err diff $@.correct-bin $@.bin diff $@.correct-err $@.err rm $@.bin $@.err clean: rm -f *-actual.err *.bin .PHONY: clean all z80asm-1.8/tests/pass.asm0000644000175000017500000000240611170052703014650 0ustar shevekshevek; pass.asm - test program which should pass compilation (with warnings) ; Copyright 2002-2007 Bas Wijnen ; ; This file is part of z80asm. ; ; Z80asm is free software; you can 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. ; ; Z80asm is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR 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 . ; Some constructs which should compile properly ld a, (0xc0) ld a, (0xc0) + 3 ; this is the same as ld a, 0xc3 ld b, (0xc0) ; warning: expression in parentheses ld a, -3 ld c, 0xc003 ; warning: 8-bit value truncated ld bc, 0xc003 ld de, 0x12345 ; warning: 16-bit value truncated ld bc, -0x8000 ld de, -0x8001 ; warning: 16-bit value truncated ld b, -0x81 ; warning: 8-bit value truncated ld a, -0x80 halt add hl, de ex af, af' jr z, $ + 4 defb 0, 4, 5, label - $ label: jr label + 2 db ';("', "'" z80asm-1.8/tests/pass.correct-bin0000644000175000017500000000005211170052703016272 0ustar shevekshevek:À>ÃÀ>ýÀE#€ÿ>€v(;("'z80asm-1.8/tests/pass.correct-err0000644000175000017500000000046011170052703016315 0ustar shevekshevekpass.asm:23: warning: expression fully enclosed in parenthesis pass.asm:25: warning: byte value 49155 (0xc003) truncated pass.asm:27: warning: word value 74565 (0x12345) truncated pass.asm:29: warning: word value -32769 (0xffff7fff) truncated pass.asm:30: warning: byte value -129 (0xffffff7f) truncated z80asm-1.8/z80asm.10000644000175000017500000002333011170052703013241 0ustar shevekshevek.\" Hey, EMACS: -*- nroff -*- .\" z80asm.1 - manual page .\" Copyright 2005-2007 Bas Wijnen .\" This file is part of z80asm. .\" .\" Z80asm is free software; you can 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. .\" .\" Z80asm is distributed in the hope that it will be useful, .\" but WITHOUT ANY WARRANTY; without even the implied warranty of .\" MERCHANTABILITY or FITNESS FOR A PARTICULAR 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 . .\" First parameter, NAME, should be all caps .\" Second parameter, SECTION, should be 1-8, maybe w/ subsection .\" other parameters are allowed: see man(7), man(1) .TH Z80ASM 1 "May 10, 2005" .\" Please adjust this date whenever revising the manpage. .\" .\" Some roff macros, for reference: .\" .nh disable hyphenation .\" .hy enable hyphenation .\" .ad l left justify .\" .ad b justify to both left and right margins .\" .nf disable filling .\" .fi enable filling .\" .br insert line break .\" .sp insert n+1 empty lines .\" for manpage-specific macros, see man(7) .SH NAME z80asm \- assembler for the Z80 microprocessor .SH SYNOPSIS .B z80asm .RI [ options ] " " [ "files..." ] .SH DESCRIPTION Z80asm is an assembler for Z80 assembly. If no input files are specified, stdin is used. If no output file is specified, "a.bin" is used. If "-" is specified as output file, stdout is used. This makes it possible to use the assembler in a pipeline. .PP When multiple input files are specified, the assembler first uses all files which were specified with \-i or \-\-input, in the order given. After that, all files which were specified as non\-option arguments are assembled, also in the order given. .SH OPTIONS .TP .B \-h, \-\-help Show summary of options and exit. .TP .B \-V, \-\-version Display version information and exit. .TP .B \-v, \-\-verbose Be verbose. Specify multiple times to be more verbose. Messages are sent to standard error. .TP .BR "\-l, \-\-list" [=filename] Write a list file. No filename or '-' means stderr. .TP .BR "\-L, \-\-label" [=filename] Write a label file. No filename or '-' means stderr. .TP .BR "\-p, \-\-label\-prefix" =prefix prefix all labels with this prefix. .TP .BR "\-i, \-\-input" =filename Specify an input file (\-i may be omitted). '-' means stdin. .TP .BR "\-o, \-\-output" =filename Specify the output file. '-' or completely omitting the option means stdout. .TP .BR "\-I, \-\-includepath" =dirname Add a directory to the include path. The order in which the directories are tried is from back to front: the last directory specified has the highest priority. "/usr/share/z80asm" is always in the include path (with lowest priority), you don't have to specify it. .TP .B \-f, \-\-force Produce output even in case of errors. Normally the output, list and label files are removed when assembly is unsuccesful. .SH ASSEMBLER DIRECTIVES All mnemonics and registers are case insensitive. All other text (in particular, labels and macros) are not. Undocumented opcodes are as much as possible supported: .TP sll and sli are equal and can both be used. .TP ixh, ixl, iyh and iyl can be used. .PP Assembler directives are: .TP .BR incbin " 'filename'" Include a binary file into the resulting assembled file. This can be used to include text files, or images, sound files, etc. The filename is searched for in the current directory, and then in the include path, just like for include. Also like for include, the quotes can be any character (but must match) and no substitution is performed (so ~ is not your home directory). .TP .BR defb " or " db " arg, arg, arg, ..." Define bytes. .TP .BR defm " or " dm " " "" """String""" "" ", 'String'" Define message. Each character in the string is stored as one byte. Backslash escapes are allowed, as in characters in expressions. Unlike the argument for include, the quotes must really be quotes (but they can be single or double quotes. The closing quote must match the opening quote.) .PP defb/db and defm/dm are really aliases; either can take both quoted strings and numbers: .br defb "This text should be in a buffer\\r\\n", 0 .TP .BR defs " or " ds " count [, value]" Define space. count bytes are reserved. Each of them is initialised to the specified value, or 0 if no value is specified. .TP .BR defw " or " dw " arg, arg, arg, ..." Define words. Each argument is stored as two bytes, the low order byte first. .TP .B end End assembly of this source file. Any remaining lines are copied into the list file (if present), but not assembled. .TP .RB "label: " equ " expression" Define label to have value expression. .PP .BR if " expression" .br code block 1 .br .B else .br code block 2 .br .B else .br code block 3 .br .B ... .br code block n .br .B endif .RS Conditionally assemble code. If expression is not 0, all odd code blocks are assembled, if expression is 0, all even blocks are assembled. Usually only one or two code blocks are present. .RE .TP .BR include " 'file'" Include file into the source. The quotes around the file for include are mandatory, but you can choose the quotes yourself. eg, you may use % or even a letter as a quote. The filename does not undergo any expansion, so \\, ~, $, etc are passed as written (which means ~ will not be your home directory.) The filename is used as specified, and then prefixed with each directory in the include path, until it can be opened. .PP .RB "label: " macro " arg1, arg2, ..." .br code block .br .B endif .RS Define a macro. The macro can be used where an opcode is expected. The code block is then substituted, with the given values for the arguments. This is a textual substitution, so the following example is valid: .RE makelabel name .br label_name: .br endm .RS This will generate a label with a constructed name (it's not a very useful example, but it shows the possiblities). .RE .TP .BR org " address" Set the "program counter" to address. This does not add any bytes to the resulting binary, it only determines how the rest of the code is interpreted (in particular, the value of labels and .BR $ ). .TP .BR seek " offset" Seek to position offset in the output file. This can be used for overwiting previously assembled code, for example for patching a binary which was included using .BR incbin . .SH EXPRESSIONS All expressions can use the following operators, in order of precedence: .RB ( a ", " b " and " c " denote subexpressions)" .TP .B a ? b : c If a is not zero, return b, otherwise c .TP .B a | b bitwise or .TP .B a ^ b bitwise xor .TP .B a & b bitwise and .TP .B a == b, a = b, a != b equality .TP .B a <= b, a >= b, a < b, a > b inequality .TP .B a << b, a >> b bit shift .TP .B a + b, a \- b addition and subtraction .TP .B a * b, a / b, a % b multiplication, division and modulo .TP .B ~a, +a, \-a bitwise not, no effect and negation .TP .BR ? label 1 if label exists, 0 if it does not. This does not generate an error if label does not exist. Note that this is usually evaluated immediately (if the rest of the expression permits), and it does not check if the label is defined later. This means it can be used as the argument of .B if , to get the functionality of #ifdef in C. .TP .B (a) parenthesis .PP Literals in expressions may be written as: (case does not matter) .TP .B @c11 arbitrary base number (specified by 'c' so c+1 == 10: here base is 13) .TP .B 14, 14d, @914 decimal number .TP .B 016, 16o, 16q, &o16, @716 octal number .TP .B 0Eh, 0xE, &hE, $E, @FE hexadecimal number (for the first notations, the first character must be 0\-9) .TP .B %1110, 1110b, &b1110, @11110 binary number .TP .B 's' ASCII code of 's' .TP .B '\\\\n', '\\\\r', '\\\\a', '\\\\t' Newline, carriage return, alert, tab .TP .B '\\\\nnn' Octal ASCII code .TP .B $ address of first byte of current command .SH LABELS In all expressions, labels may be used. However, there are some expressions where the value must be computable at once, and therefore only previously defined labels may be used. This is the case for: .TP \- The argument of org .TP \- The argument of seek .TP \- The argument of equ (eg, a label definition) .TP \- The first argument of ds .TP \- The argument of if .PP In all other expressions, labels which are defined later may be used. .PP Labels must consist of letters, digits, underscores and periods, and must not start with a digit. Labels are case sensitive. .PP Labels starting with a period (.) are .B local , which means their scope is only the current include file or macro definition (and files included/macros called from it). This is particularly useful for macros, to prevent duplicate definitions when using a macro more than once. .SH EXIT STATUS If assembly was successful, no output is produced (except the result, and messages triggered by --verbose) and 0 is returned. At any error, there is output on the standard error and 1 is returned. .SH NOTES Parts that are not assembled because of an if statement and macros which are defined but never used are only checked to have a correct command. The argument is not parsed. This means that if the file passes through the assembler with no warnings or errors, it may still not assemble correctly in a different setting (where the if's give different results). .SH BUGS If you find a bug, or want to send comments, please use the web interface at http://savannah.nongnu.org/projects/z80asm/ or send an e\-mail to wijnen@debian.org. .SH AUTHOR Z80asm was written by Bas Wijnen . Some patches were provided by Jan Wilmans z80asm-1.8/z80asm.c0000644000175000017500000017107711170052703013337 0ustar shevekshevek/* Z80 assembler by shevek Copyright (C) 2002-2009 Bas Wijnen Copyright (C) 2005 Jan Wilmans This file is part of z80asm. Z80asm is free software; you can 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. Z80asm is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 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 . */ #include "z80asm.h" /* global variables */ /* mnemonics, used as argument to indx() in assemble */ const char *mnemonics[] = { "call", "cpdr", "cpir", "djnz", "halt", "indr", "inir", "lddr", "ldir", "otdr", "otir", "outd", "outi", "push", "reti", "retn", "rlca", "rrca", "defb", "defw", "defs", "defm", "adc", "add", "and", "bit", "ccf", "cpd", "cpi", "cpl", "daa", "dec", "equ", "exx", "inc", "ind", "ini", "ldd", "ldi", "neg", "nop", "out", "pop", "res", "ret", "rla", "rlc", "rld", "rra", "rrc", "rrd", "rst", "sbc", "scf", "set", "sla", "sll", "sli", "sra", "srl", "sub", "xor", "org", "cp", "di", "ei", "ex", "im", "in", "jp", "jr", "ld", "or", "rl", "rr", "db", "dw", "ds", "dm", "include", "incbin", "if", "else", "endif", "end", "macro", "endm", "seek", NULL }; /* linked lists */ struct reference *firstreference = NULL; struct label *firstlabel = NULL, *lastlabel = NULL; struct name *firstname = NULL; struct includedir *firstincludedir = NULL; struct macro *firstmacro = NULL; /* files */ FILE *realoutputfile, *outfile, *reallistfile, *listfile, *labelfile; const char *realoutputfilename; const char *labelfilename; struct infile *infile; /* prefix for labels in labelfile */ const char *labelprefix = ""; /* bools to see if files are opened */ int havelist = 0, label = 0; /* number of infiles in array */ int infilecount; /* number of errors seen so far */ int errors = 0; /* current line, address and file */ int addr = 0, file; /* current number of characters in list file, for indentation */ int listdepth; /* use readbyte instead of (hl) if writebyte is true */ int writebyte; const char *readbyte; /* variables which are filled by rd_* functions and used later, * like readbyte */ const char *readword, *indexjmp, *bitsetres; /* 0, 0xdd or 0xfd depening on which index prefix should be given */ int indexed; /* increased for every -v option on the command line */ int verbose = 0; /* read commas after indx() if comma > 1. increase for every call */ int comma; /* address at start of line (for references) */ int baseaddr; /* set by readword and readbyte, used for new_reference */ char mem_delimiter; /* line currently being parsed */ char *buffer = NULL; /* if a macro is currently being defined */ int define_macro = 0; /* file (and macro) stack */ int sp; struct stack stack[MAX_INCLUDE]; /* maximum level of includes */ /* Produce output even with errors. */ int use_force = 0; /* print an error message, including current line and file */ void printerr (int error, const char *fmt, ...) { va_list l; va_start (l, fmt); if ((sp < 0) || (stack[sp].name == 0)) { fprintf (stderr, "internal assembler error, sp == %i\n", sp); vfprintf (stderr, fmt, l); exit (2); } fprintf (stderr, "%s%s:%d: %s: ", stack[sp].dir ? stack[sp].dir->name : "", stack[sp].name, stack[sp].line, error ? "error" : "warning"); vfprintf (stderr, fmt, l); va_end (l); if (error) errors++; } /* skip over spaces in string */ const char * delspc (const char *ptr) { while (*ptr && isspace (*ptr)) ptr++; if (*ptr == ';') ptr = ""; return ptr; } /* read away a comma, error if there is none */ static void rd_comma (const char **p) { *p = delspc (*p); if (**p != ',') { printerr (1, "`,' expected. Remainder of line: %s\n", *p); return; } *p = delspc ((*p) + 1); } /* look ahead for a comma, no error if not found */ static int has_argument (const char **p) { const char *q = delspc (*p); return (*q == ','); } /* During assembly, many literals are not parsed. Instead, they are saved * until all labels are read. After that, they are parsed. This function * is used during assembly, to find the place where the command continues. */ static void skipword (const char **pos, char delimiter) { /* rd_expr will happily read the expression, and possibly return * an invalid result. It will update pos, which is what we need. */ /* Pass valid to allow using undefined labels without errors. */ int valid; rd_expr (pos, delimiter, &valid, sp, 0); } /* callback function for argument parser, used to open output files. */ static FILE * openfile (int *done, /* flag to check that a file is opened only once. */ const char *type, /* name of filetype for error message */ FILE * def, /* default value, in case "-" is specified */ const char *name, /* filename to open */ const char *flags) /* open flags */ { FILE *retval; if (*done) { fprintf (stderr, "Error: more than one %s specified\n", type); exit (1); } *done = 1; if (def && (!name || (name[0] == '-' && name[1] == 0))) { return def; } if (!name || !name[0]) { fprintf (stderr, "Error: no %s specified\n", type); exit (1); } if (!(retval = fopen (name, flags))) { fprintf (stderr, "Unable to open %s %s: %s\n", type, name, strerror (errno)); exit (1); } return retval; } /* open an included file, searching the path */ static FILE * open_include_file (const char *name, struct includedir **dir, const char *flags) { FILE *result; struct includedir *i; /* always try the current directory first */ result = fopen (name, flags); if (result) { if (dir) *dir = NULL; return result; } for (i = firstincludedir; i != NULL; i = i->next) { char *tmp = malloc (strlen (i->name) + strlen (name) + 1); if (!tmp) { printerr (1, "not enough memory trying to open include file\n"); return NULL; } strcpy (tmp, i->name); strcat (tmp, name); result = fopen (tmp, flags); free (tmp); if (result) { if (dir) *dir = i; return result; } } return NULL; } /* queue a file to be opened for reading */ static void open_infile (const char *name) { infile = realloc (infile, sizeof (struct infile) * (infilecount + 1)); if (!infile) { fprintf (stderr, "Error: insufficient memory\n"); exit (1); } /* only asm is currently supported */ infile[infilecount].type = FILETYPE_ASM; infile[infilecount].name = name; if (verbose >= 5) fprintf (stderr, "queued inputfile %s\n", infile[infilecount].name); infilecount++; } /* add a directory to the include search path */ static void add_include (const char *name) { struct includedir *i; i = malloc (sizeof (struct includedir) + strlen (name) + 1); if (!i) { fprintf (stderr, "Error: insufficient memory\n"); exit (1); } strcpy (i->name, name); if (name[strlen (name) - 1] != '/') strcat (i->name, "/"); i->next = firstincludedir; firstincludedir = i; } static void try_use_real_file (FILE * real, FILE ** backup) { fpos_t pos; if (fgetpos (real, &pos) == 0) { *backup = real; return; } if (!(*backup = tmpfile ())) { fprintf (stderr, "Error: Unable to open temporary file: %s\n", strerror (errno)); exit (1); } } static void flush_to_real_file (FILE * real, FILE * tmp) { int l, size, len = 0; char buf[BUFLEN]; if (tmp == real) { return; } rewind (tmp); while (1) { clearerr (tmp); errno = 0; len = fread (buf, 1, BUFLEN, tmp); if (len == 0 && feof (tmp)) break; if (len <= 0) { fprintf (stderr, "error reading temp file: %s\n", strerror (errno)); exit (1); } l = 0; while (l < len) { clearerr (real); size = fwrite (&buf[l], 1, len - l, real); if (size <= 0) { fprintf (stderr, "error writing final file: %s\n", strerror (errno)); exit (1); } l += size; } } } /* parse commandline arguments */ static void parse_commandline (int argc, char **argv) { const struct option opts[] = { {"help", no_argument, NULL, 'h'}, {"version", no_argument, NULL, 'V'}, {"verbose", no_argument, NULL, 'v'}, {"list", optional_argument, NULL, 'l'}, {"label", optional_argument, NULL, 'L'}, {"input", required_argument, NULL, 'i'}, {"output", required_argument, NULL, 'o'}, {"label-prefix", required_argument, NULL, 'p'}, {"includepath", required_argument, NULL, 'I'}, {"force", no_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; const char *short_opts = "hVvl::L::i:o:p:I:f"; int done = 0, i, out = 0; infile = NULL; while (!done) { switch (getopt_long (argc, argv, short_opts, opts, NULL)) { case 'h': /* split in two, to avoid too long string constant */ printf ("Usage: %s [options] [input files]\n" "\n" "Possible options are:\n" "-h\t--help\t\tDisplay this help text and exit.\n" "-V\t--version\tDisplay version information and exit.\n" "-v\t--verbose\tBe verbose. " "Specify again to be more verbose.\n" "-l\t--list\t\tWrite a list file.\n" "-L\t--label\t\tWrite a label file.\n", argv[0]); printf ("-p\t--label-prefix\tprefix all labels with this prefix.\n" "-i\t--input\t\tSpecify an input file (-i may be omitted).\n" "-o\t--output\tSpecify the output file.\n" "-I\t--includepath\tAdd a directory to the include path.\n" "Please send bug reports and feature requests to " "\n"); exit (0); case 'V': printf ("Z80 assembler version " VERSION "\n" "Copyright (C) 2002-2007 Bas Wijnen " ".\n" "Copyright (C) 2005 Jan Wilmans " ".\n" "This program comes with ABSOLUTELY NO WARRANTY.\n" "You may distribute copies of the program under the terms\n" "of the GNU General Public License as published by\n" "the Free Software Foundation; either version 2 of the\n" "License, or (at your option) any later version.\n\n" "The complete text of the GPL can be found in\n" "/usr/share/common-licenses/GPL.\n"); exit (0); case 'v': verbose++; if (verbose >= 5) fprintf (stderr, "Verbosity increased to level %d\n", verbose); break; case 'o': realoutputfile = openfile (&out, "output file", stdout, optarg, "wb"); realoutputfilename = optarg; if (verbose >= 5) fprintf (stderr, "Opened outputfile\n"); break; case 'i': open_infile (optarg); break; case 'l': reallistfile = openfile (&havelist, "list file", stderr, optarg, "w"); if (verbose >= 5) fprintf (stderr, "Opened list file\n"); break; case 'L': labelfile = openfile (&label, "label file", stderr, optarg, "w"); labelfilename = optarg; if (verbose >= 5) fprintf (stderr, "Opened label file\n"); break; case 'p': labelprefix = optarg; break; case 'I': add_include (optarg); break; case 'f': use_force = 1; break; case -1: done = 1; break; default: /* errors are handled by getopt_long */ break; } } for (i = optind; i < argc; ++i) open_infile (argv[i]); if (!infilecount) open_infile ("-"); if (!out) realoutputfile = openfile (&out, "output file", stdout, "a.bin", "wb"); try_use_real_file (realoutputfile, &outfile); if (havelist) try_use_real_file (reallistfile, &listfile); } /* find any of the list[] entries as the start of ptr and return index */ static int indx (const char **ptr, const char **list, int error, const char **expr) { int i, l; *ptr = delspc (*ptr); if (!**ptr) { if (error) { printerr (1, "unexpected end of line\n"); return 0; } else return 0; } if (comma > 1) rd_comma (ptr); for (i = 0; list[i]; i++) { const char *input = *ptr; const char *check = list[i]; int had_expr = 0; if (!list[i][0]) continue; l = strlen (list[i]); while (*check) { if (*check == ' ') { input = delspc (input); } else if (*check == '*') { *expr = input; mem_delimiter = check[1]; rd_expr (&input, mem_delimiter, NULL, sp, 0); had_expr = 1; } else if (*check == '+') { if (*input == '+' || *input == '-') { *expr = input; mem_delimiter = check[1]; rd_expr (&input, mem_delimiter, NULL, sp, 0); } } else if (*check == *input || (*check >= 'a' && *check <= 'z' && *check - 'a' + 'A' == *input)) ++input; else break; ++check; } if (*check || (isalnum (check[-1]) && isalnum (input[0]))) continue; if (had_expr) { input = delspc (input); if (*input && *input != ',') continue; } *ptr = input; if (verbose >= 4) fprintf (stderr, "%5d (0x%04x): Piece of code found:%s\n", stack[sp].line, addr, list[i]); if (verbose >= 6) fprintf (stderr, "%5d (0x%04x): Remainder of line=%s.\n", stack[sp].line, addr, *ptr); comma++; return i + 1; } if (error) { printerr (1, "parse error. Remainder of line=%s\n", *ptr); if (verbose >= 3) { fprintf (stderr, "When looking for any of:\n"); for (i = 0; list[i]; i++) fprintf (stderr, "%s\t", list[i]); fprintf (stderr, "\n"); } } return 0; } /* read a mnemonic */ static int readcommand (const char **p) { return indx (p, mnemonics, 0, NULL); } /* try to read a label and optionally store it in the list */ static void readlabel (const char **p, int store) { const char *c, *d, *pos, *dummy; int i, j; struct label *buf, *previous, **thefirstlabel; for (d = *p; *d && *d != ';'; ++d) { } for (c = *p; !strchr (" \r\n\t", *c) && c < d; ++c) { } pos = strchr (*p, ':'); if (!pos || pos >= c) return; if (pos == *p) { printerr (1, "`:' found without a label"); return; } if (!store) { *p = pos + 1; return; } c = pos + 1; dummy = *p; j = rd_label (&dummy, &i, &previous, sp, 0); if (i || j) { printerr (1, "duplicate definition of label %s\n", *p); *p = c; return; } if (NULL == (buf = malloc (sizeof (struct label) + c - *p))) { printerr (1, "not enough memory to store label %s\n", *p); *p = c; return; } strncpy (buf->name, *p, c - *p - 1); buf->name[c - *p - 1] = 0; if (verbose >= 3) fprintf (stderr, "%5d (0x%04x): Label found: %s\n", stack[sp].line, addr, buf->name); *p = c; buf->value = addr; lastlabel = buf; if (buf->name[0] == '.') thefirstlabel = &stack[sp].labels; else thefirstlabel = &firstlabel; if (previous) buf->next = previous->next; else buf->next = *thefirstlabel; buf->prev = previous; buf->valid = 1; buf->busy = 0; buf->ref = NULL; if (buf->prev) buf->prev->next = buf; else *thefirstlabel = buf; if (buf->next) buf->next->prev = buf; } static void new_reference (const char *data, int type, char delimiter, int ds_count); /* write one byte to the outfile, and add it to the list file as well */ static void write_one_byte (int b, int list) { if (verbose >= 4) fprintf (stderr, "%5d (0x%04x): write_one_byte called with argument 0x%02x\n", stack[sp].line, addr, b); b &= 0xff; putc (b, outfile); if (list && havelist) { fprintf (listfile, " %02x", b); listdepth += 3; } addr++; addr &= 0xffff; } /* write byte to outfile and possibly some index things as well */ static void wrtb (int b) { if (verbose >= 4) fprintf (stderr, "%5d (0x%04x): wrtb called with argument 0x%02x\n", stack[sp].line, addr, b); if (indexed) { if (verbose >= 5) fprintf (stderr, "%5d (0x%04x): writing indexed byte 0x%02x\n", stack[sp].line, addr, indexed); write_one_byte (indexed, 1); indexed = 0; } if (writebyte) { if (verbose >= 5) fprintf (stderr, "%5d (0x%04x): using a xor on byte because there is " "a writebyte.\n", stack[sp].line, addr); b ^= 0x40; } if (verbose >= 5) fprintf (stderr, "%5d (0x%04x): writing byte 0x%02x\n", stack[sp].line, addr, b); if (bitsetres && b != 0xCB) { new_reference (bitsetres, TYPE_BSR, ',', b); bitsetres = NULL; } else { write_one_byte (b, 1); } if (indexjmp) { if (verbose >= 5) fprintf (stderr, "%5d (0x%04x): Making reference for index/jump %s\n", stack[sp].line, addr, indexjmp); new_reference (indexjmp, TYPE_ABSB, ')', 1); indexjmp = NULL; } if (writebyte) { if (verbose >= 5) fprintf (stderr, "%5d (0x%04x): writing argument byte for padding\n", stack[sp].line, addr); writebyte = 0; new_reference (readbyte, TYPE_ABSB, mem_delimiter, 1); } } int compute_ref (struct reference *ref, int allow_invalid) { const char *ptr; int valid = 0; int backup_addr = addr; int backup_baseaddr = baseaddr; int backup_comma = comma; int backup_file = file; int backup_sp = sp; sp = ref->level; addr = ref->addr; baseaddr = ref->baseaddr; comma = ref->comma; file = ref->infile; if (verbose >= 3) fprintf (stderr, "%5d (0x%04x): Making reference to %s (done=%d, " "computed=%d)\n", stack[sp].line, addr, ref->input, ref->done, ref->computed_value); ptr = ref->input; if (!ref->done) { ref->computed_value = rd_expr (&ptr, ref->delimiter, allow_invalid ? &valid : NULL, ref->level, 1); if (valid) ref->done = 1; } if (verbose >= 4) fprintf (stderr, "%5d (0x%04x): Reference is %d (0x%04x).\n", stack[sp].line, addr, ref->computed_value, ref->computed_value); sp = backup_sp; addr = backup_addr; baseaddr = backup_baseaddr; comma = backup_comma; file = backup_file; return ref->computed_value; } static void wrt_ref (int val, int type, int count); /* Create a new reference, to be resolved after assembling (so all labels are * known.) */ static void new_reference (const char *p, int type, char delimiter, int ds_count) { struct reference *tmp = NULL; long opos, lpos; int valid, value; const char *c; c = p; value = rd_expr (&c, delimiter, &valid, sp, 1); if (valid) { if (verbose >= 5) { fprintf (stderr, "%5d (0x%04x): Using calculated value %d (%x) " "immediately.\n", stack[sp].line, addr, value, value); } } else { /* the expression is not valid (yet), we need to make a real reference. */ tmp = malloc (sizeof (struct reference) + strlen (p)); if (!tmp) { printerr (1, "unable to allocate memory for reference %s\n", p); return; } tmp->file = malloc (strlen (stack[sp].name) + 1); if (!tmp->file) { printerr (1, "unable to allocate memory for reference filename\n"); free (tmp); return; } strcpy (tmp->file, stack[sp].name); if (stack[sp].dir) { tmp->dir = malloc (strlen (stack[sp].dir->name) + sizeof (struct includedir)); if (!tmp->dir) { printerr (1, "unable to allocate memory for reference dir\n"); free (tmp->file); free (tmp); return; } strcpy (tmp->dir->name, stack[sp].dir->name); } else tmp->dir = NULL; opos = ftell (outfile); lpos = havelist ? ftell (listfile) : 0; if (verbose >= 3) fprintf (stderr, "%5d (0x%04x): reference set to %s (delimiter=%c, " "sp=%d)\n", stack[sp].line, addr, p, delimiter, sp); strcpy (tmp->input, p); tmp->line = stack[sp].line; tmp->addr = addr; tmp->baseaddr = baseaddr; tmp->count = ds_count; tmp->infile = file; tmp->comma = comma; tmp->oseekpos = opos; tmp->lseekpos = lpos; tmp->delimiter = delimiter; tmp->type = type; tmp->next = firstreference; tmp->done = 0; tmp->level = sp; if (type != TYPE_LABEL) { if (firstreference) firstreference->prev = tmp; tmp->prev = NULL; firstreference = tmp; } /* Dummy value which should not give warnings */ value = (type == TYPE_RELB) ? ds_count : 0; } if (type != TYPE_LABEL) { wrt_ref (value, type, ds_count); } else { lastlabel->ref = tmp; lastlabel->valid = valid; lastlabel->value = value; } } /* write the last read word to file */ static void write_word (void) { new_reference (readword, TYPE_ABSW, mem_delimiter, 1); } /* write the last read byte to file (relative) */ static void write_rel (void) { new_reference (readbyte, TYPE_RELB, mem_delimiter, (addr + 1) & 0xffff); writebyte = 0; } /* read a word from input and store it in readword. return 1 on success */ static int rd_word (const char **p, char delimiter) { *p = delspc (*p); if (**p == 0) return 0; readword = *p; mem_delimiter = delimiter; skipword (p, delimiter); return 1; } /* read a byte from input and store it in readbyte. return 1 on success */ static int rd_byte (const char **p, char delimiter) { *p = delspc (*p); if (**p == 0) return 0; readbyte = *p; writebyte = 1; mem_delimiter = delimiter; skipword (p, delimiter); return 1; } /* read an address from infile and put it in reference table. * so that it will be written here afterwards */ static void rd_wrt_addr (const char **p, char delimiter) { if (!rd_word (p, delimiter)) return; write_word (); } /* like rd_wrt_addr, but for a relative jump */ static void rd_wrt_jr (const char **p, char delimiter) { if (!rd_byte (p, delimiter)) return; write_rel (); } /* read (SP), DE, or AF */ static int rd_ex1 (const char **p) { #define DE 2 #define AF 3 const char *list[] = { "( sp )", "de", "af", NULL }; return indx (p, list, 1, NULL); } /* read first argument of IN */ static int rd_in (const char **p) { #define A 8 const char *list[] = { "b", "c", "d", "e", "h", "l", "f", "a", NULL }; return indx (p, list, 1, NULL); } /* read second argument of out (c),x */ static int rd_out (const char **p) { const char *list[] = { "b", "c", "d", "e", "h", "l", "0", "a", NULL }; return indx (p, list, 1, NULL); } /* read (c) or (nn) */ static int rd_nnc (const char **p) { #define C 1 int i; const char *list[] = { "( c )", "(*)", "a , (*)", NULL }; i = indx (p, list, 1, &readbyte); if (i < 2) return i; return 2; } /* read (C) */ static int rd_c (const char **p) { const char *list[] = { "( c )", "( bc )", NULL }; return indx (p, list, 1, NULL); } /* read a or hl */ static int rd_a_hl (const char **p) { #define HL 2 const char *list[] = { "a", "hl", NULL }; return indx (p, list, 1, NULL); } /* read first argument of ld */ static int rd_ld (const char **p) { #define ldBC 1 #define ldDE 2 #define ldHL 3 #define ldSP 4 #define ldIX 5 #define ldIY 6 #define ldB 7 #define ldC 8 #define ldD 9 #define ldE 10 #define ldH 11 #define ldL 12 #define ld_HL 13 #define ldA 14 #define ldI 15 #define ldR 16 #define ld_BC 17 #define ld_DE 18 #define ld_IX 19 #define ld_IY 20 #define ld_NN 21 int i; const char *list[] = { "ixh", "ixl", "iyh", "iyl", "bc", "de", "hl", "sp", "ix", "iy", "b", "c", "d", "e", "h", "l", "( hl )", "a", "i", "r", "( bc )", "( de )", "( ix +)", "(iy +)", "(*)", NULL }; const char *nn; i = indx (p, list, 1, &nn); if (!i) return 0; if (i <= 2) { indexed = 0xdd; return ldH + (i == 2); } if (i <= 4) { indexed = 0xfd; return ldH + (i == 4); } i -= 4; if (i == ldIX || i == ldIY) { indexed = i == ldIX ? 0xDD : 0xFD; return ldHL; } if (i == ld_IX || i == ld_IY) { indexjmp = nn; indexed = i == ld_IX ? 0xDD : 0xFD; return ld_HL; } if (i == ld_NN) readword = nn; return i; } /* read first argument of JP */ static int rd_jp (const char **p) { int i; const char *list[] = { "nz", "z", "nc", "c", "po", "pe", "p", "m", "( ix )", "( iy )", "(hl)", NULL }; i = indx (p, list, 0, NULL); if (i < 9) return i; if (i == 11) return -1; indexed = 0xDD + 0x20 * (i - 9); return -1; } /* read first argument of JR */ static int rd_jr (const char **p) { const char *list[] = { "nz", "z", "nc", "c", NULL }; return indx (p, list, 0, NULL); } /* read A */ static int rd_a (const char **p) { const char *list[] = { "a", NULL }; return indx (p, list, 1, NULL); } /* read bc,de,hl,af */ static int rd_stack (const char **p) { int i; const char *list[] = { "bc", "de", "hl", "af", "ix", "iy", NULL }; i = indx (p, list, 1, NULL); if (i < 5) return i; indexed = 0xDD + 0x20 * (i - 5); return 3; } #if 0 /* read a or hl(2) or i[xy](2) with variables set */ static int rd_a_hlx (const char **p) { int i; const char *list[] = { "a", "hl", "ix", "iy", NULL }; i = indx (p, list, 1, NULL); if (i < 2) return i; if (i == 2) return 2; indexed = 0xDD + 0x20 * (i - 3); return 2; } #endif /* read b,c,d,e,h,l,(hl),a,(ix+nn),(iy+nn),nn * but now with extra hl or i[xy](15) for add-instruction * and set variables accordingly */ static int rd_r_add (const char **p) { #define addHL 15 int i; const char *list[] = { "ixl", "ixh", "iyl", "iyh", "b", "c", "d", "e", "h", "l", "( hl )", "a", "( ix +)", "( iy +)", "hl", "ix", "iy", "*", NULL }; const char *nn; i = indx (p, list, 0, &nn); if (i == 18) /* expression */ { readbyte = nn; writebyte = 1; return 7; } if (i > 14) /* hl, ix, iy */ { if (i > 15) indexed = 0xDD + 0x20 * (i - 16); return addHL; } if (i <= 4) /* i[xy][hl] */ { indexed = 0xdd + 0x20 * (i > 2); return 6 - (i & 1); } i -= 4; if (i < 9) return i; indexed = 0xDD + 0x20 * (i - 9); /* (i[xy] +) */ indexjmp = nn; return 7; } /* read bc,de,hl, or sp */ static int rd_rr_ (const char **p) { const char *list[] = { "bc", "de", "hl", "sp", NULL }; return indx (p, list, 1, NULL); } /* read bc,de,hl|ix|iy,sp. hl|ix|iy only if it is already indexed the same. */ static int rd_rrxx (const char **p) { const char *listx[] = { "bc", "de", "ix", "sp", NULL }; const char *listy[] = { "bc", "de", "iy", "sp", NULL }; const char *list[] = { "bc", "de", "hl", "sp", NULL }; switch (indexed) { case 0xDD: return indx (p, listx, 1, NULL); case 0xFD: return indx (p, listy, 1, NULL); default: return indx (p, list, 1, NULL); } } /* read b,c,d,e,h,l,(hl),a,(ix+nn),(iy+nn),nn * and set variables accordingly */ static int rd_r (const char **p) { int i; const char *list[] = { "ixl", "ixh", "iyl", "iyh", "b", "c", "d", "e", "h", "l", "( hl )", "a", "( ix +)", "( iy +)", "*", NULL }; const char *nn; i = indx (p, list, 0, &nn); if (i == 15) /* expression */ { readbyte = nn; writebyte = 1; return 7; } if (i <= 4) { indexed = 0xdd + 0x20 * (i > 2); return 6 - (i & 1); } i -= 4; if (i < 9) return i; indexed = 0xDD + 0x20 * (i - 9); indexjmp = nn; return 7; } /* like rd_r(), but without nn */ static int rd_r_ (const char **p) { int i; const char *list[] = { "b", "c", "d", "e", "h", "l", "( hl )", "a", "( ix +)", "( iy +)", NULL }; i = indx (p, list, 1, &indexjmp); if (i < 9) return i; indexed = 0xDD + 0x20 * (i - 9); return 7; } /* read a number from 0 to 7, for bit, set or res */ static int rd_0_7 (const char **p) { *p = delspc (*p); if (**p == 0) return 0; bitsetres = *p; skipword (p, ','); return 1; } /* read long condition. do not error if not found. */ static int rd_cc (const char **p) { const char *list[] = { "nz", "z", "nc", "c", "po", "pe", "p", "m", NULL }; return indx (p, list, 0, NULL); } /* read long or short register, */ static int rd_r_rr (const char **p) { int i; const char *list[] = { "iy", "ix", "sp", "hl", "de", "bc", "", "b", "c", "d", "e", "h", "l", "( hl )", "a", "( ix +)", "( iy +)", NULL }; i = indx (p, list, 1, &indexjmp); if (!i) return 0; if (i < 16 && i > 2) return 7 - i; if (i > 15) { indexed = 0xDD + (i - 16) * 0x20; return -7; } indexed = 0xDD + (2 - i) * 0x20; return 3; } /* read hl */ static int rd_hl (const char **p) { const char *list[] = { "hl", NULL }; return indx (p, list, 1, NULL); } /* read hl, ix, or iy */ static int rd_hlx (const char **p) { int i; const char *list[] = { "hl", "ix", "iy", NULL }; i = indx (p, list, 1, NULL); if (i < 2) return i; indexed = 0xDD + 0x20 * (i - 2); return 1; } /* read af' */ static int rd_af_ (const char **p) { const char *list[] = { "af'", NULL }; return indx (p, list, 1, NULL); } /* read 0(1), 1(3), or 2(4) */ static int rd_0_2 (const char **p) { const char *list[] = { "0", "", "1", "2", NULL }; return indx (p, list, 1, NULL); } /* read argument of ld (hl), */ static int rd_ld_hl (const char **p) { int i; const char *list[] = { "b", "c", "d", "e", "h", "l", "", "a", "*", NULL }; i = indx (p, list, 0, &readbyte); if (i < 9) return i; writebyte = 1; return 7; } /* read argument of ld (nnnn), */ static int rd_ld_nn (const char **p) { #define ld_nnHL 5 #define ld_nnA 6 int i; const char *list[] = { "bc", "de", "", "sp", "hl", "a", "ix", "iy", NULL }; i = indx (p, list, 1, NULL); if (i < 7) return i; indexed = 0xdd + 0x20 * (i == 8); return ld_nnHL; } /* read argument of ld a, */ static int rd_lda (const char **p) { #define A_I 9 #define A_R 10 #define A_NN 11 int i; const char *list[] = { "( sp )", "( iy +)", "( de )", "( bc )", "( ix +)", "b", "c", "d", "e", "h", "l", "( hl )", "a", "i", "r", "(*)", "*", NULL }; const char *nn; i = indx (p, list, 0, &nn); if (i == 2 || i == 5) { indexed = (i == 2) ? 0xFD : 0xDD; indexjmp = nn; return 7; } if (i == 17) { readbyte = nn; writebyte = 1; return 7; } if (i == 16) { readword = nn; } return i - 5; } /* read argument of ld b|c|d|e|h|l */ static int rd_ldbcdehla (const char **p) { int i; const char *list[] = { "b", "c", "d", "e", "h", "l", "( hl )", "a", "( ix +)", "( iy +)", "ixh", "ixl", "iyh", "iyl", "*", NULL }; const char *nn; i = indx (p, list, 0, &nn); if (i == 15) { readbyte = nn; writebyte = 1; return 7; } if (i > 10) { int x; x = 0xdd + 0x20 * (i > 12); if (indexed && indexed != x) { printerr (1, "illegal use of index registers\n"); return 0; } indexed = x; return 6 - (i & 1); } if (i > 8) { if (indexed) { printerr (1, "illegal use of index registers\n"); return 0; } indexed = 0xDD + 0x20 * (i == 10); indexjmp = nn; return 7; } return i; } /* read nnnn, or (nnnn) */ static int rd_nn_nn (const char **p) { #define _NN 1 const char *list[] = { "(*)", "*", NULL }; return 2 - indx (p, list, 0, &readword); } /* read {HL|IX|IY},nnnn, or (nnnn) */ static int rd_sp (const char **p) { #define SPNN 0 #define SPHL 1 int i; const char *list[] = { "hl", "ix", "iy", "(*)", "*", NULL }; const char *nn; i = indx (p, list, 0, &nn); if (i > 3) { readword = nn; return i == 4 ? 2 : 0; } if (i != 1) indexed = 0xDD + 0x20 * (i - 2); return 1; } /* write a reference after it has been computed */ static void wrt_ref (int val, int type, int count) { switch (type) { case TYPE_RST: if ((val & 0x38) != val) { printerr (1, "incorrect RST value %d (0x%02x)\n", val, val); return; } write_one_byte (val + 0xC7, 1); return; case TYPE_ABSW: if (val < -0x8000 || val >= 0x10000) printerr (0, "word value %d (0x%x) truncated\n", val, val); write_one_byte (val & 0xff, 1); write_one_byte ((val >> 8) & 0xff, 1); return; case TYPE_ABSB: if (val < -0x80 || val >= 0x100) printerr (0, "byte value %d (0x%x) truncated\n", val, val); write_one_byte (val & 0xff, 1); return; case TYPE_DS: if (val < -0x80 || val >= 0x100) printerr (0, "byte value %d (0x%x) truncated\n", val, val); if (havelist) { fprintf (listfile, " 0x%02x...", val & 0xff); listdepth += 6; } while (count--) { write_one_byte (val & 0xff, 0); } return; case TYPE_BSR: if (val & ~7) { printerr (1, "incorrect BIT/SET/RES value %d\n", val); return; } write_one_byte (0x08 * val + count, 1); return; case TYPE_RELB: val -= count; if (val & 0xff80 && ~val & 0xff80) { printerr (1, "relative jump out of range (%d)\n", val); } write_one_byte (val & 0xff, 1); return; case TYPE_LABEL: printerr (1, "bug in the assembler: trying to write label reference. " "Please report.\n"); return; } } static char * get_include_name (const char **ptr) { int pos = 0; char quote; char *name; *ptr = delspc (*ptr); name = malloc (strlen (*ptr)); if (!name) { printerr (1, "unable to allocate memory for filename %.*s\n", strlen (*ptr) - 1, *ptr); return NULL; } if (!**ptr) { printerr (1, "include without filename\n"); free (name); return NULL; } quote = *(*ptr)++; while (**ptr != quote) { if (!**ptr) { printerr (1, "filename without closing quote (%c)\n", quote); free (name); return NULL; } name[pos++] = *(*ptr)++; } name[pos] = 0; ++*ptr; return name; } static int read_line (void) { unsigned pos, newpos, size; struct macro_arg *arg; if (stack[sp].file) { FILE *f = stack[sp].file; static char short_buffer[BUFLEN + 1]; if (buffer && buffer != short_buffer) free (buffer); buffer = NULL; if (!fgets (short_buffer, BUFLEN + 1, f)) return 0; if (strlen (short_buffer) < BUFLEN) { buffer = short_buffer; return 1; } size = 2 * BUFLEN; buffer = malloc (size + 1); if (!buffer) { printerr (1, "out of memory reading line\n"); return 0; } memcpy (buffer, short_buffer, BUFLEN + 1); while (1) { char *b; if (!fgets (&buffer[size - BUFLEN], BUFLEN + 1, f) || (buffer[strlen (buffer) - 1] == '\n')) { return 1; } size += BUFLEN; b = realloc (buffer, size + 1); if (!b) { printerr (1, "out of memory reading line\n"); return 0; } buffer = b; } } /* macro line */ if (!stack[sp].macro_line) { unsigned i; for (i = 0; i < stack[sp].macro->numargs; ++i) free (stack[sp].macro_args[i]); free (stack[sp].macro_args); return 0; } size = strlen (stack[sp].macro_line->line) + 1; for (arg = stack[sp].macro_line->args; arg; arg = arg->next) size += strlen (stack[sp].macro_args[arg->which]); buffer = malloc (size); if (!buffer) { printerr (1, "out of memory\n"); return 0; } pos = 0; newpos = 0; for (arg = stack[sp].macro_line->args; arg; arg = arg->next) { memcpy (&buffer[newpos], &stack[sp].macro_line->line[pos], arg->pos - pos); newpos += arg->pos - pos; strcpy (&buffer[newpos], stack[sp].macro_args[arg->which]); newpos += strlen (stack[sp].macro_args[arg->which]); pos = arg->pos + 1; } strcpy (&buffer[newpos], &stack[sp].macro_line->line[pos]); stack[sp].macro_line = stack[sp].macro_line->next; return 1; } static unsigned get_macro_args (const char **ptr, char ***ret_args, int allow_empty) { unsigned numargs = 0; *ret_args = NULL; while (1) { char **args; const char *c; *ptr = delspc (*ptr); if (!**ptr) break; c = *ptr; for (; **ptr && !strchr (" \r\n\t,;", **ptr); ++*ptr) { } if (*ptr == c && !allow_empty) { printerr (1, "empty macro argument\n"); break; } ++numargs; args = realloc (*ret_args, sizeof (char *) * numargs); if (!args) { printerr (1, "out of memory\n"); --numargs; break; } *ret_args = args; args[numargs - 1] = malloc (*ptr - c + 1); if (!args[numargs - 1]) { printerr (1, "out of memory\n"); --numargs; break; } memcpy (args[numargs - 1], c, *ptr - c); args[numargs - 1][*ptr - c] = 0; } return numargs; } /* do the actual work */ static void assemble (void) { int ifcount = 0, noifcount = 0; const char *ptr; struct label *l; char *bufptr; int r, s; /* registers */ /* continue assembling until the last input file is done */ for (file = 0; file < infilecount; ++file) { int file_ended = 0; sp = 0; /* clear stack */ stack[sp].line = 0; stack[sp].shouldclose = 0; stack[sp].name = infile[file].name; stack[sp].dir = NULL; if (infile[file].name[0] == '-' && infile[file].name[1] == 0) { stack[sp].file = stdin; } else { stack[sp].file = fopen (infile[file].name, "r"); if (!stack[sp].file) { printerr (1, "unable to open %s. skipping\n", infile[file].name); continue; } stack[sp].shouldclose = 1; } if (havelist) fprintf (listfile, "# File %s\n", stack[sp].name); if (buffer) buffer[0] = 0; /* loop until this source file is done */ while (1) { int cmd, cont = 1; if (havelist) { if (buffer && buffer[0] != 0) { int i, tabs; ptr = delspc (ptr); if (*ptr != 0) { printerr (1, "junk at end of line: %s\n", ptr); } if (listdepth < 8) tabs = 3; else if (listdepth < 16) tabs = 2; else tabs = 1; for (i = 0; i < tabs; ++i) fputc ('\t', listfile); fprintf (listfile, "%s\n", buffer); } listdepth = 4; } /* throw away the rest of the file after end */ if (file_ended) { while (read_line ()) { if (havelist) fprintf (listfile, "\t\t\t%s\n", buffer); } file_ended = 0; } while (!read_line ()) { struct reference *ref, *nextref; struct label *next; if (verbose >= 6) fprintf (stderr, "finished reading file %s\n", stack[sp].name); if (havelist) { if (stack[sp].file) fprintf (listfile, "# End of file %s\n", stack[sp].name); else fprintf (listfile, "# End of macro %s\n", stack[sp].name); } if (stack[sp].shouldclose) fclose (stack[sp].file); /* the top of stack is about to be popped off, throwing all * local labels out of scope. All references at this level * which aren't computable are errors. */ for (ref = firstreference; ref; ref = nextref) { nextref = ref->next; compute_ref (ref, 1); if (ref->done) continue; if (ref->level == sp) if (!ref->level--) { printerr (1, "unable to resolve reference: %s\n", ref->input); if (ref->prev) ref->prev->next = ref->next; else firstreference = ref->next; if (ref->next) ref->next->prev = ref->prev; free (ref); } } /* Ok, now junk all local labels of the top stack level */ for (l = stack[sp].labels; l; l = next) { next = l->next; if (l->ref) free (l->ref); free (l); } stack[sp].labels = NULL; if (!sp--) { cont = 0; break; } } if (!cont) break; /* break to next source file */ if (havelist) fprintf (listfile, "%04x", addr); for (bufptr = buffer; (bufptr = strchr (bufptr, '\n'));) *bufptr = ' '; for (bufptr = buffer; (bufptr = strchr (bufptr, '\r'));) *bufptr = ' '; ptr = buffer; lastlabel = NULL; baseaddr = addr; ++stack[sp].line; ptr = delspc (ptr); if (!*ptr) continue; if (!noifcount && !define_macro) readlabel (&ptr, 1); else readlabel (&ptr, 0); ptr = delspc (ptr); if (!*ptr) continue; comma = 0; indexed = 0; indexjmp = 0; writebyte = 0; readbyte = 0; readword = 0; cmd = readcommand (&ptr) - 1; if (noifcount) { switch (cmd) { case IF: noifcount++; break; case ELSE: if (noifcount == 1) { noifcount = 0; ifcount++; } break; case ENDIF: noifcount--; } ptr = ""; continue; } if (define_macro) { char *newptr; struct macro_line **current_line; for (current_line = &firstmacro->lines; *current_line; current_line = &(*current_line)->next) { } *current_line = malloc (sizeof (struct macro_line)); if (!*current_line) { printerr (1, "out of memory\n"); continue; } (*current_line)->next = NULL; (*current_line)->args = NULL; (*current_line)->line = malloc (strlen (buffer) + 1); if (!(*current_line)->line) { printerr (1, "out of memory\n"); free (*current_line); *current_line = NULL; continue; } ptr = buffer; newptr = (*current_line)->line; while (*ptr) { unsigned p; struct macro_arg **last_arg = &(*current_line)->args; for (p = 0; p < firstmacro->numargs; ++p) { if (strncmp (ptr, firstmacro->args[p], strlen (firstmacro->args[p])) == 0) { struct macro_arg *newarg; newarg = malloc (sizeof (struct macro_arg)); if (!newarg) { printerr (1, "out of memory\n"); break; } newarg->next = NULL; *last_arg = newarg; last_arg = &newarg->next; newarg->pos = newptr - (*current_line)->line; newarg->which = p; /* leave one character so two macros following each * other keep their order. */ ptr += strlen (firstmacro->args[p]) - 1; break; } } *newptr++ = *ptr++; } *newptr = 0; if (verbose >= 7) fprintf (stderr, "added line to macro (cmd = %d): %s\n", cmd, (*current_line)->line); if (cmd == ENDM) define_macro = 0; continue; } switch (cmd) { int i, have_quote; case ADC: if (!(r = rd_a_hl (&ptr))) break; if (r == HL) { if (!(r = rd_rr_ (&ptr))) break; wrtb (0xED); wrtb (0x4A + 0x10 * --r); break; } if (!(r = rd_r (&ptr))) break; wrtb (0x88 + --r); break; case ADD: if (!(r = rd_r_add (&ptr))) break; if (r == addHL) { if (!(r = rd_rrxx (&ptr))) break; wrtb (0x09 + 0x10 * --r); /* ADD HL/IX/IY, qq */ break; } if (has_argument (&ptr)) { if (r != A) { printerr (1, "parse error before: %s\n", ptr); break; } if (!(r = rd_r (&ptr))) break; wrtb (0x80 + --r); /* ADD A,r */ break; } wrtb (0x80 + --r); /* ADD r */ break; case AND: if (!(r = rd_r (&ptr))) break; wrtb (0xA0 + --r); break; case BIT: if (!rd_0_7 (&ptr)) break; rd_comma (&ptr); if (!(r = rd_r_ (&ptr))) break; wrtb (0xCB); wrtb (0x40 + (r - 1)); break; case CALL: if (!(r = rd_cc (&ptr))) { wrtb (0xCD); } else { wrtb (0xC4 + 8 * --r); rd_comma (&ptr); } rd_wrt_addr (&ptr, '\0'); break; case CCF: wrtb (0x3F); break; case CP: if (!(r = rd_r (&ptr))) break; wrtb (0xB8 + --r); break; case CPD: wrtb (0xED); wrtb (0xA9); break; case CPDR: wrtb (0xED); wrtb (0xB9); break; case CPI: wrtb (0xED); wrtb (0xA1); break; case CPIR: wrtb (0xED); wrtb (0xB1); break; case CPL: wrtb (0x2F); break; case DAA: wrtb (0x27); break; case DEC: if (!(r = rd_r_rr (&ptr))) break; if (r < 0) { wrtb (0x05 - 8 * ++r); break; } wrtb (0x0B + 0x10 * --r); break; case DI: wrtb (0xF3); break; case DJNZ: wrtb (0x10); rd_wrt_jr (&ptr, '\0'); break; case EI: wrtb (0xFB); break; case EQU: if (!lastlabel) { printerr (1, "EQU without label\n"); break; } new_reference (ptr, TYPE_LABEL, 0, 0); if (verbose >= 4) { if (lastlabel->valid) fprintf (stderr, "Assigned value %d to label %s.\n", lastlabel->value, lastlabel->name); else fprintf (stderr, "Scheduled label %s for later computation.\n", lastlabel->name); } ptr = ""; break; case EX: if (!(r = rd_ex1 (&ptr))) break; switch (r) { case DE: if (!rd_hl (&ptr)) break; wrtb (0xEB); break; case AF: if (!rd_af_ (&ptr)) break; wrtb (0x08); break; default: if (!rd_hlx (&ptr)) break; wrtb (0xE3); } break; case EXX: wrtb (0xD9); break; case HALT: wrtb (0x76); break; case IM: if (!(r = rd_0_2 (&ptr))) break; wrtb (0xED); wrtb (0x46 + 8 * --r); break; case IN: if (!(r = rd_in (&ptr))) break; if (r == A) { const char *tmp; if (!(r = rd_nnc (&ptr))) break; if (r == C) { wrtb (0xED); wrtb (0x40 + 8 * (A - 1)); break; } tmp = readbyte; wrtb (0xDB); new_reference (tmp, TYPE_ABSB, ')', 1); break; } if (!rd_c (&ptr)) break; wrtb (0xED); wrtb (0x40 + 8 * --r); break; case INC: if (!(r = rd_r_rr (&ptr))) break; if (r < 0) { wrtb (0x04 - 8 * ++r); break; } wrtb (0x03 + 0x10 * --r); break; case IND: wrtb (0xED); wrtb (0xAA); break; case INDR: wrtb (0xED); wrtb (0xBA); break; case INI: wrtb (0xED); wrtb (0xA2); break; case INIR: wrtb (0xED); wrtb (0xB2); break; case JP: r = rd_jp (&ptr); if (r < 0) { wrtb (0xE9); break; } if (r == 0) { wrtb (0xC3); } else { wrtb (0xC2 + 8 * --r); rd_comma (&ptr); } rd_wrt_addr (&ptr, '\0'); break; case JR: r = rd_jr (&ptr); if (r) rd_comma (&ptr); wrtb (0x18 + 8 * r); rd_wrt_jr (&ptr, '\0'); break; case LD: if (!(r = rd_ld (&ptr))) break; switch (r) { case ld_BC: case ld_DE: if (!rd_a (&ptr)) break; wrtb (0x02 + 0x10 * (r == ld_DE)); break; case ld_HL: r = rd_ld_hl (&ptr); wrtb (0x70 + --r); break; case ld_NN: if (!(r = rd_ld_nn (&ptr))) break; if (r == ld_nnA || r == ld_nnHL) { wrtb (0x22 + 0x10 * (r == ld_nnA)); write_word (); break; } wrtb (0xED); wrtb (0x43 + 0x10 * --r); write_word (); break; case ldA: if (!(r = rd_lda (&ptr))) break; if (r == A_NN) { wrtb (0x3A); write_word (); break; } if (r == A_I || r == A_R) { wrtb (0xED); wrtb (0x57 + 8 * (r == A_R)); break; } if (r < 0) { wrtb (0x0A - 0x10 * ++r); break; } wrtb (0x78 + --r); break; case ldB: case ldC: case ldD: case ldE: case ldH: case ldL: if (!(s = rd_ldbcdehla (&ptr))) break; wrtb (0x40 + 0x08 * (r - 7) + (s - 1)); break; case ldBC: case ldDE: s = rd_nn_nn (&ptr); if (s == _NN) { wrtb (0xED); wrtb (0x4B + 0x10 * (r == ldDE)); write_word (); break; } wrtb (0x01 + (r == ldDE) * 0x10); write_word (); break; case ldHL: r = rd_nn_nn (&ptr); wrtb (0x21 + (r == _NN) * 9); write_word (); break; case ldI: case ldR: if (!rd_a (&ptr)) break; wrtb (0xED); wrtb (0x47 + 0x08 * (r == ldR)); break; case ldSP: r = rd_sp (&ptr); if (r == SPHL) { wrtb (0xF9); break; } if (r == SPNN) { wrtb (0x31); write_word (); break; } wrtb (0xED); wrtb (0x7B); write_word (); break; } break; case LDD: wrtb (0xED); wrtb (0xA8); break; case LDDR: wrtb (0xED); wrtb (0xB8); break; case LDI: wrtb (0xED); wrtb (0xA0); break; case LDIR: wrtb (0xED); wrtb (0xB0); break; case NEG: wrtb (0xED); wrtb (0x44); break; case NOP: wrtb (0x00); break; case OR: if (!(r = rd_r (&ptr))) break; wrtb (0xB0 + --r); break; case OTDR: wrtb (0xED); wrtb (0xBB); break; case OTIR: wrtb (0xED); wrtb (0xB3); break; case OUT: if (!(r = rd_nnc (&ptr))) break; if (r == C) { if (!(r = rd_out (&ptr))) break; wrtb (0xED); wrtb (0x41 + 8 * --r); break; } if (!rd_a (&ptr)) break; { const char *tmp = readbyte; wrtb (0xD3); new_reference (tmp, TYPE_ABSB, ')', 1); } break; case OUTD: wrtb (0xED); wrtb (0xAB); break; case OUTI: wrtb (0xED); wrtb (0xA3); break; case POP: if (!(r = rd_stack (&ptr))) break; wrtb (0xC1 + 0x10 * --r); break; case PUSH: if (!(r = rd_stack (&ptr))) break; wrtb (0xC5 + 0x10 * --r); break; case RES: if (!rd_0_7 (&ptr)) break; rd_comma (&ptr); if (!(r = rd_r_ (&ptr))) break; wrtb (0xCB); wrtb (0x80 + --r); break; case RET: if (!(r = rd_cc (&ptr))) { wrtb (0xC9); break; } wrtb (0xC0 + 8 * --r); break; case RETI: wrtb (0xED); wrtb (0x4D); break; case RETN: wrtb (0xED); wrtb (0x45); break; case RL: if (!(r = rd_r_ (&ptr))) break; wrtb (0xCB); wrtb (0x10 + --r); break; case RLA: wrtb (0x17); break; case RLC: if (!(r = rd_r_ (&ptr))) break; wrtb (0xCB); wrtb (0x00 + --r); break; case RLCA: wrtb (0x07); break; case RLD: wrtb (0xED); wrtb (0x6F); break; case RR: if (!(r = rd_r_ (&ptr))) break; wrtb (0xCB); wrtb (0x18 + --r); break; case RRA: wrtb (0x1F); break; case RRC: if (!(r = rd_r_ (&ptr))) break; wrtb (0xCB); wrtb (0x08 + --r); break; case RRCA: wrtb (0x0F); break; case RRD: wrtb (0xED); wrtb (0x67); break; case RST: new_reference (ptr, TYPE_RST, '\0', 1); ptr = ""; break; case SBC: if (!(r = rd_a_hl (&ptr))) break; if (r == HL) { if (!(r = rd_rr_ (&ptr))) break; wrtb (0xED); wrtb (0x42 + 0x10 * --r); break; } if (!(r = rd_r (&ptr))) break; wrtb (0x98 + --r); break; case SCF: wrtb (0x37); break; case SET: if (!rd_0_7 (&ptr)) break; rd_comma (&ptr); if (!(r = rd_r_ (&ptr))) break; wrtb (0xCB); wrtb (0xC0 + --r); break; case SLA: if (!(r = rd_r_ (&ptr))) break; wrtb (0xCB); wrtb (0x20 + --r); break; case SLI: if (!(r = rd_r_ (&ptr))) break; wrtb (0xCB); wrtb (0x30 + --r); break; case SRA: if (!(r = rd_r_ (&ptr))) break; wrtb (0xCB); wrtb (0x28 + --r); break; case SRL: if (!(r = rd_r_ (&ptr))) break; wrtb (0xCB); wrtb (0x38 + --r); break; case SUB: if (!(r = rd_r (&ptr))) break; if (has_argument (&ptr)) /* SUB A,r ? */ { if (r != A) { printerr (1, "parse error before: %s\n", ptr); break; } if (!(r = rd_r (&ptr))) break; } wrtb (0x90 + --r); break; case XOR: if (!(r = rd_r (&ptr))) break; wrtb (0xA8 + --r); break; case DEFB: case DB: case DEFM: case DM: ptr = delspc (ptr); while (1) { have_quote = (*ptr == '"' || *ptr == '\''); if (have_quote) { /* Read string. */ int quote = *ptr; if (listfile) { fprintf (listfile, " .."); listdepth += 3; } ++ptr; while (*ptr != quote) { write_one_byte (rd_character (&ptr, NULL, 1), 0); if (*ptr == 0) { printerr (1, "end of line in quoted string\n"); break; } } ++ptr; } else { /* Read expression. */ new_reference (ptr, TYPE_ABSB, ',', 1); skipword (&ptr, ','); } ptr = delspc (ptr); if (*ptr == ',') { ++ptr; continue; } if (*ptr != 0) printerr (1, "junk in byte definition: %s\n", ptr); break; } break; case DEFW: case DW: if (!(r = rd_word (&ptr, ','))) { printerr (1, "No data for word definition\n"); break; } while (1) { new_reference (readword, TYPE_ABSW, ',', 1); ptr = delspc (ptr); if (*ptr != ',') break; ++ptr; if (!(r = rd_word (&ptr, ','))) printerr (1, "Missing expression in defw\n"); } break; case DEFS: case DS: r = rd_expr (&ptr, ',', NULL, sp, 1); if (r < 0) { printerr (1, "ds should have its first argument >=0" " (not -0x%x)\n", -r); break; } ptr = delspc (ptr); if (*ptr) { rd_comma (&ptr); readbyte = 0; rd_byte (&ptr, '\0'); writebyte = 0; new_reference (readbyte, TYPE_DS, '\0', r); break; } if (havelist) { fprintf (listfile, " 00..."); listdepth += 6; } for (i = 0; i < r; i++) { write_one_byte (0, 0); } break; case END: file_ended = 1; break; case ORG: addr = rd_expr (&ptr, '\0', NULL, sp, 1) & 0xffff; break; case INCLUDE: if (sp + 1 >= MAX_INCLUDE) { printerr (1, "stack overflow (circular include?)"); if (verbose >= 5) { int x; fprintf (stderr, "Stack dump:\nframe line file\n"); for (x = 0; x < MAX_INCLUDE; ++x) fprintf (stderr, "%5d %5d %s\n", x, stack[x].line, stack[x].name); } break; } { struct name *name; char *nm = get_include_name (&ptr); if (!nm) break; name = malloc (sizeof (struct name) + strlen (nm)); if (!name) { printerr (1, "out of memory while allocating name\n"); free (nm); break; } strcpy (name->name, nm); free (nm); ++sp; stack[sp].name = name->name; stack[sp].shouldclose = 1; stack[sp].line = 0; stack[sp].file = open_include_file (name->name, &stack[sp].dir, "r"); if (!stack[sp].file) { printerr (1, "unable to open file %s\n", name->name); free (name); --sp; break; } name->next = firstname; name->prev = NULL; if (name->next) name->next->prev = name; firstname = name; if (verbose >= 4) fprintf (stderr, "Reading file %s\n", name->name); } break; case INCBIN: { FILE *incfile; char *name = get_include_name (&ptr); if (!name) break; incfile = open_include_file (name, NULL, "rb"); if (!incfile) { printerr (1, "unable to open binary file %s\n", name); free (name); break; } while (1) { char filebuffer[4096]; size_t num = fread (filebuffer, 1, 4096, incfile); if (num == 0) break; if (num != fwrite (filebuffer, 1, num, outfile)) { printerr (1, "error including binary file %s: %s\n", name, strerror (errno)); break; } addr += num; addr &= 0xffff; } fclose (incfile); free (name); break; } case IF: if (rd_expr (&ptr, '\0', NULL, sp, 1)) ifcount++; else noifcount++; break; case ELSE: if (ifcount == 0) { printerr (1, "else without if\n"); break; } noifcount = 1; ifcount--; break; case ENDIF: if (noifcount == 0 && ifcount == 0) { printerr (1, "endif without if\n"); break; } if (noifcount) noifcount--; else ifcount--; break; case MACRO: if (!lastlabel) { printerr (1, "macro without label\n"); break; } if (define_macro) { printerr (1, "nested macro definition\n"); break; } { struct macro *m; for (m = firstmacro; m; m = m->next) { if (strcmp (m->name, lastlabel->name) == 0) { printerr (1, "duplicate macro definition\n"); break; } } m = malloc (sizeof (struct macro)); if (!m) { printerr (1, "out of memory\n"); break; } m->name = malloc (strlen (lastlabel->name) + 1); if (!m->name) { printerr (1, "out of memory\n"); free (m); break; } strcpy (m->name, lastlabel->name); if (lastlabel->prev) lastlabel->prev->next = lastlabel->next; else firstlabel = lastlabel->next; if (lastlabel->next) lastlabel->next->prev = lastlabel->prev; free (lastlabel); m->next = firstmacro; firstmacro = m; m->lines = NULL; m->numargs = get_macro_args (&ptr, &m->args, 0); define_macro = 1; } break; case ENDM: if (stack[sp].file) printerr (1, "endm outside macro definition\n"); break; case SEEK: { unsigned int seekaddr = rd_expr (&ptr, '\0', NULL, sp, 1); if (verbose >= 2) { fprintf (stderr, "%s%s:%d: ", stack[sp].dir ? stack[sp].dir->name : "", stack[sp].name, stack[sp].line); fprintf (stderr, "[Message] seeking to 0x%0X \n", seekaddr); } fseek (outfile, seekaddr, SEEK_SET); break; } default: { struct macro *m; for (m = firstmacro; m; m = m->next) { if (strncmp (m->name, ptr, strlen (m->name)) == 0) { unsigned numargs; if (sp + 1 >= MAX_INCLUDE) { printerr (1, "stack overflow (circular include?)\n"); if (verbose >= 5) { int x; fprintf (stderr, "Stack dump:\nframe line file\n"); for (x = 0; x < MAX_INCLUDE; ++x) fprintf (stderr, "%5d %5d %s\n", x, stack[x].line, stack[x].name); } break; } ++sp; ptr += strlen (m->name); numargs = get_macro_args (&ptr, &stack[sp].macro_args, 1); if (numargs != m->numargs) { unsigned a; printerr (1, "invalid number of arguments for macro " "(is %d, must be %d)\n", numargs, m->numargs); for (a = 0; a < numargs; ++a) free (stack[sp].macro_args[a]); free (stack[sp].macro_args); break; } stack[sp].name = m->name; stack[sp].file = NULL; stack[sp].line = 0; stack[sp].macro = m; stack[sp].macro_line = m->lines; stack[sp].shouldclose = 0; stack[sp].dir = NULL; break; } } if (m) break; } printerr (1, "command or comment expected (was %s)\n", ptr); } } } if (ifcount || noifcount) { printerr (1, "reached EOF at IF level %d\n", ifcount + noifcount); } if (havelist) { fprintf (listfile, "%04x\n", addr); } { struct reference *next; struct reference *tmp; /* Add a stack frame for error reporting. */ ++sp; for (tmp = firstreference; tmp; tmp = next) { int ref; next = tmp->next; fseek (outfile, tmp->oseekpos, SEEK_SET); if (havelist) fseek (listfile, tmp->lseekpos, SEEK_SET); stack[sp].name = tmp->file; stack[sp].dir = tmp->dir; stack[sp].line = tmp->line; ref = compute_ref (tmp, 0); wrt_ref (ref, tmp->type, tmp->count); if (tmp->dir) free (tmp->dir); free (tmp->file); free (tmp); } } if (!errors || use_force) { flush_to_real_file (realoutputfile, outfile); if (havelist) flush_to_real_file (reallistfile, listfile); } /* write all labels */ if (label) fseek (labelfile, 0, SEEK_END); for (l = firstlabel; l; l = l->next) { if (l->ref) { compute_ref (l->ref, 0); } if (label) { fprintf (labelfile, "%s%s:\tequ $%04x\n", labelprefix, l->name, l->value); } } if (label) fclose (labelfile); while (firstlabel) { l = firstlabel->next; free (firstlabel); firstlabel = l; } fclose (outfile); if (outfile != realoutputfile) fclose (realoutputfile); if (havelist) { fclose (listfile); if (listfile != reallistfile && reallistfile != stderr) fclose (reallistfile); } free (infile); } int main (int argc, char **argv) { /* default include file location */ add_include ("/usr/share/z80asm/headers/"); parse_commandline (argc, argv); if (verbose >= 1) fprintf (stderr, "Assembling....\n"); assemble (); if (errors) { if (errors == 1) fprintf (stderr, "*** 1 error found ***\n"); else fprintf (stderr, "*** %d errors found ***\n", errors); if (realoutputfile == outfile && !use_force) { unlink (realoutputfilename); unlink (labelfilename); } return 1; } else { if (verbose >= 1) fprintf (stderr, "Assembly succesful.\n"); return 0; } } z80asm-1.8/z80asm.h0000644000175000017500000001601511170052703013332 0ustar shevekshevek/* Z80 assembler by shevek Copyright (C) 2002-2007 Bas Wijnen Copyright (C) 2005 Jan Wilmans This file is part of z80asm. Z80asm is free software; you can 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. Z80asm is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 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 . */ #ifndef Z80ASM_H #define Z80ASM_H #include #include #include #include #include #include #include #include #include /* defines which are not function-specific */ #ifndef BUFLEN #define BUFLEN 300 /* size of readbuffer for file i/o */ #endif #ifndef MAX_INCLUDE #define MAX_INCLUDE 200 /* stack size for include command and macros */ #endif /* types */ /* mnemonics. THESE MUST BE IN THE SAME ORDER AS const char *mnemonic[]! */ enum mnemonic { CALL, CPDR, CPIR, DJNZ, HALT, INDR, INIR, LDDR, LDIR, OTDR, OTIR, OUTD, OUTI, PUSH, RETI, RETN, RLCA, RRCA, DEFB, DEFW, DEFS, DEFM, ADC, ADD, AND, BIT, CCF, CPD, CPI, CPL, DAA, DEC, EQU, EXX, INC, IND, INI, LDD, LDI, NEG, NOP, OUT, POP, RES, RET, RLA, RLC, RLD, RRA, RRC, RRD, RST, SBC, SCF, SET, SLA, SLL, SLI, SRA, SRL, SUB, XOR, ORG, CP, DI, EI, EX, IM, IN, JP, JR, LD, OR, RL, RR, DB, DW, DS, DM, INCLUDE, INCBIN, IF, ELSE, ENDIF, END, MACRO, ENDM, SEEK }; /* types of reference */ enum reftype { TYPE_BSR, /* bit value (0-7) for bit, set and res */ TYPE_DS, /* ds reference (byte count and value) */ TYPE_RST, /* rst reference: val & 0x38 == val */ TYPE_ABSW, /* absolute word (2 bytes) */ TYPE_ABSB, /* absolute byte */ TYPE_RELB, /* relative byte */ TYPE_LABEL /* equ expression */ }; /* filetypes that can appear on the input. object files are on the todo list */ enum filetype { FILETYPE_ASM }; /* labels (will be malloced) */ struct label { struct label *next, *prev; /* linked list */ int value; /* value */ int valid; /* if it is valid, or not yet computed */ int busy; /* if it is currently being computed */ struct reference *ref; /* mallocced memory to value for computation */ char name[1]; /* space with name in it */ }; /* files that were given on the commandline */ struct infile { const char *name; enum filetype type; }; /* filenames must be remembered for references */ struct name { struct name *next, *prev; char name[1]; }; /* the include path */ struct includedir { struct includedir *next; char name[1]; }; /* macro stuff */ struct macro_arg { struct macro_arg *next; unsigned pos; unsigned which; }; struct macro_line { struct macro_line *next; char *line; struct macro_arg *args; }; struct macro { struct macro *next; char *name; unsigned numargs; char **args; struct macro_line *lines; }; /* elements on the context stack */ struct stack { const char *name; /* filename (for errors). may be malloced */ struct includedir *dir; /* directory where it comes from, if any */ FILE *file; /* the handle */ int line; /* the current line number (for errors) */ int shouldclose; /* if this file should be closed when done */ struct label *labels; /* local labels for this stack level */ /* if file is NULL, this is a macro entry */ struct macro *macro; struct macro_line *macro_line; char **macro_args; /* arguments given to the macro */ }; /* these structs will be malloced for each reference */ struct reference { struct reference *next, *prev; enum reftype type; /* type of reference */ long oseekpos; /* position in outfile for data */ long lseekpos; /* position in listfile for data */ char delimiter; /* delimiter for parser */ int addr, line; /* address and line of reference */ int baseaddr; /* address at start of line of reference */ int comma; /* comma when reference was set */ int count; /* only for ds: number of items */ int infile; /* index in infile[], current infile */ int done; /* if this reference has been computed */ int computed_value; /* value (only valid if done = true) */ int level; /* maximum stack level of labels to use */ struct includedir *dir; /* dirname of file (for error reporting) */ char *file; /* filename (for error reporting) */ char input[1]; /* variable size buffer containing formula */ }; /* global variables */ /* mnemonics, used as argument to indx() in assemble */ extern const char *mnemonics[]; /* linked lists */ extern struct reference *firstreference; extern struct label *firstlabel, *lastlabel; extern struct name *firstname; extern struct includedir *firstincludedir; extern struct macro *firstmacro; /* files */ extern FILE *realoutputfile, *outfile, *reallistfile, *listfile, *labelfile; extern const char *realoutputfilename; extern const char *labelfilename; extern struct infile *infile; /* prefix for labels in labelfile */ extern const char *labelprefix; /* bools to see if files are opened */ extern int havelist, label; /* number of infiles in array */ extern int infilecount; /* number of errors seen so far */ extern int errors; /* current line, address and file */ extern int addr, file; /* current number of characters in list file, for indentation */ extern int listdepth; /* use readbyte instead of (hl) if writebyte is true */ extern int writebyte; extern const char *readbyte; /* variables which are filled by rd_* functions and used later, * like readbyte */ extern const char *readword, *indexjmp, *bitsetres; /* 0, 0xdd or 0xfd depening on which index prefix should be given */ extern int indexed; /* increased for every -v option on the command line */ extern int verbose; /* read commas after indx() if comma > 1. increase for every call */ extern int comma; /* address at start of line (for references) */ extern int baseaddr; /* set by readword and readbyte, used for new_reference */ extern char mem_delimiter; /* line currently being parsed */ extern char *buffer; /* if a macro is currently being defined */ extern int define_macro; /* file (and macro) stack */ extern int sp; extern struct stack stack[MAX_INCLUDE]; /* maximum level of includes */ /* Produce output even with errors. */ extern int use_force; /* print an error message, including current line and file */ void printerr (int error, const char *fmt, ...); /* skip over spaces in string */ const char *delspc (const char *ptr); int rd_expr (const char **p, char delimiter, int *valid, int level, int print_errors); int rd_label (const char **p, int *exists, struct label **previous, int level, int print_errors); int rd_character (const char **p, int *valid, int print_errors); int compute_ref (struct reference *ref, int allow_invalid); #endif