aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorH. Peter Anvin <hpa@zytor.com>2006-03-11 07:46:53 +0000
committerH. Peter Anvin <hpa@zytor.com>2006-03-11 07:46:53 +0000
commit781045be55cc20cf9d9150a761450f003867ff7c (patch)
tree04ec2a6d4c08c41e2e06515a9f8a8fbafeb393fe
parent7ae535589fbde4cec35ec39a5c38c8e773964e36 (diff)
downloadabc80-781045be55cc20cf9d9150a761450f003867ff7c.tar.gz
abc80-781045be55cc20cf9d9150a761450f003867ff7c.tar.xz
abc80-781045be55cc20cf9d9150a761450f003867ff7c.zip
Add files for Z80 assembler
-rw-r--r--tools/z80asm/BUGS3
-rw-r--r--tools/z80asm/COPYING18
-rw-r--r--tools/z80asm/ChangeLog45
-rw-r--r--tools/z80asm/GPL2341
-rw-r--r--tools/z80asm/INSTALL9
-rw-r--r--tools/z80asm/Makefile40
-rw-r--r--tools/z80asm/NEWS4
-rw-r--r--tools/z80asm/README100
-rw-r--r--tools/z80asm/TODO6
-rw-r--r--tools/z80asm/jr.diff81
-rw-r--r--tools/z80asm/z80asm.c2772
11 files changed, 3419 insertions, 0 deletions
diff --git a/tools/z80asm/BUGS b/tools/z80asm/BUGS
new file mode 100644
index 0000000..a748101
--- /dev/null
+++ b/tools/z80asm/BUGS
@@ -0,0 +1,3 @@
+known BUGS in shevek's Z80 assembler:
+- out (c),0
+- shift/rotate (i[xy]+d),[bcdehla]
diff --git a/tools/z80asm/COPYING b/tools/z80asm/COPYING
new file mode 100644
index 0000000..be85328
--- /dev/null
+++ b/tools/z80asm/COPYING
@@ -0,0 +1,18 @@
+COPYING - Copyright information for z80asm, the assembler for the z80 by
+Bas Wijnen.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+Version 2 of the GPL is included in the distribution in a file named GPL2.
diff --git a/tools/z80asm/ChangeLog b/tools/z80asm/ChangeLog
new file mode 100644
index 0000000..ceaf821
--- /dev/null
+++ b/tools/z80asm/ChangeLog
@@ -0,0 +1,45 @@
+2004-09-29 Bas Wijnen <b.wijnen@phys.rug.nl>
+
+ * 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 <hpa@users.sourceforge.net>
+
+ * Makefile: Fixed "make clean", removed -Werror for cygwin.
+ * z80asm.c (rd_out): implemented out (c),0.
+
+2004-09-28 H. Peter Anvin <hpa@users.sourceforge.net>
+
+ * 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 <b.wijnen@phys.rug.nl>
+
+ * z80asm.c (assemble): Bugfixes to make ld (nn),a and ld sp,nn work.
+
+2003-11-19 Bas Wijnen <b.wijnen@phys.rug.nl>
+
+ * z80asm.c: Changed expression handling, bugfixes.
+
+2003-10-30 Bas Wijnen <b.wijnen@phys.rug.nl>
+
+ * z80asm.c: Added binary output as hex to listfile.
+
+2003-10-30 Bas Wijnen <b.wijnen@phys.rug.nl>
+
+ * 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 <b.wijnen@phys.rug.nl>
+
+ * z80asm.c (rd_expr): Added equations in expr and bugfix
+ (assemble): Added if/else/endif
+
+2002-04-28 Bas Wijnen <b.wijnen@phys.rug.nl>
+
+ * assembler.c: Started changelog
diff --git a/tools/z80asm/GPL2 b/tools/z80asm/GPL2
new file mode 100644
index 0000000..a52b16e
--- /dev/null
+++ b/tools/z80asm/GPL2
@@ -0,0 +1,341 @@
+
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) 19yy <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) 19yy name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/tools/z80asm/INSTALL b/tools/z80asm/INSTALL
new file mode 100644
index 0000000..94f5637
--- /dev/null
+++ b/tools/z80asm/INSTALL
@@ -0,0 +1,9 @@
+How 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)
diff --git a/tools/z80asm/Makefile b/tools/z80asm/Makefile
new file mode 100644
index 0000000..75c8011
--- /dev/null
+++ b/tools/z80asm/Makefile
@@ -0,0 +1,40 @@
+# Makefile for the Z80 assembler by shevek
+# Copyright (C) 2002,2004 Bas Wijnen
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+CC = gcc
+LDFLAGS = -g -O2 -Wall
+CFLAGS = -g -O2 -Wall -Wwrite-strings -Wcast-qual -Wcast-align -Wstrict-prototypes -Wmissing-prototypes -Wmissing-declarations -Wredundant-decls -Wnested-externs -Winline -pedantic -ansi -Wshadow -g -W -Ignulib
+
+all:z80asm
+
+z80asm:z80asm.c Makefile gnulib/getopt.o gnulib/getopt1.o
+ $(CC) $(CFLAGS) $(LDFLAGS) $< gnulib/getopt.o gnulib/getopt1.o -o $@
+
+gnulib/%.o:gnulib/%.c gnulib/getopt.h Makefile
+ $(CC) $(CFLAGS) -c $< -o $@
+
+clean:
+ for i in . gnulib ; do \
+ rm -f $$i/a.bin $$i/a.out $$i/core $$i/*~ $$i/\#* $$i/*.o ; \
+ done
+ rm -f z80asm z80asm.exe
+
+dist: clean
+ rm -rf /tmp/z80asm
+ tar cf - -C .. z80asm | tar xf - -C /tmp
+ find /tmp/z80asm -name CVS | xargs rm -rf
+ tar cvzf ../z80asm-`date +%Y%m%d`00.tar.gz -C /tmp z80asm
diff --git a/tools/z80asm/NEWS b/tools/z80asm/NEWS
new file mode 100644
index 0000000..2fb5ecf
--- /dev/null
+++ b/tools/z80asm/NEWS
@@ -0,0 +1,4 @@
+List files now include the output in hex.
+defb/db accepts quoted strings in double quotes.
+
+See README for a (hopefully complete) list of features
diff --git a/tools/z80asm/README b/tools/z80asm/README
new file mode 100644
index 0000000..d1570e5
--- /dev/null
+++ b/tools/z80asm/README
@@ -0,0 +1,100 @@
+README for shevek's Z80 assembler
+
+See INSTALL for installation instructions
+
+The assembler works like gcc. You need an external editor to create your
+source file. Then you assemble it to a z80 binary with this assembler.
+
+By default, the assembler uses stdin as its only input file and stdout as
+its output file. Any arguments with no flags are taken as input files.
+If more than one input file is specified, the files are concatenated.
+The following flags are supported:
+
+-h --help give a help text
+
+-V --version give version information
+
+-v --verbose be verbose. specify more than once to be more verbose.
+ If you want verbosity about parsing the commandline,
+ then this should be the the first argument.
+
+-l --list file specify file to write out the input, with lines on
+ which files are read and every source line prefixed
+ with the address. (list file)
+
+-L --label file specify file to write all labels to, in assembler
+ readable format (human readable as well). Every line is
+ of the form:
+ label: equ value
+ so it can be included in source.
+
+-i --input file specify an input file.
+
+-o --output file specify the output file.
+
+Some notes on the assembler directives and mnemonics:
+All text is case insensitive, except when quoted.
+Undocumented opcodes are as much as possible supported. There are some errors
+in the implementation, that are being worked on (see BUGS). Names for
+undocumented opcodes are:
+sll and sli are equal and can both be used.
+ixh, ixl, iyh and iyl can be used.
+
+Assembler directives are:
+defb and db arg,arg,arg,...
+defw and dw arg,arg,arg,...
+defs and ds count,value=0
+org address
+if expression
+else
+else
+...
+endif
+include 'file'
+ Note: 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.)
+
+defb/db can also take strings, when enclosed in double quotes:
+defb "This text should be in a buffer", 0
+
+All expressions can use the following operators, in order of precedence:
+(a, b and c denote subexpressions)
+a?b:c If a is not zero, return b, otherwise c
+a|b bitwise or
+a^b bitwise xor
+a&b bitwise and
+a==b a!=b equality
+a<=b a>=b a<b a>b inequality
+a<<b a>>b shift
+a+b a-b addition and subtraction
+a*b a/b a%b multiplication, division and modulo
+~a +a -a bitwise not, no effect and negation
+(a) parenthesis
+
+Literals in expressions may be written as: (case does not matter)
+14, 14d decimal number
+016, 16o, 16q octal number
+0xE, 0Eh hexadecimal number (first character must be 0-9)
+%1110, 1110b binary number
+@c11 base 13 number (specified by 'c' so c+1 == 10)
+'s' ASCII code of 's'
+$ address of first byte of current command
+
+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:
+- The argument of org
+- The argument of equ (eg, a label definition)
+- The first argument of ds
+- The argument of if
+In all other expression, labels which are defined later may be used.
+
+Warning: parts that are not compiled because of an if statement 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).
diff --git a/tools/z80asm/TODO b/tools/z80asm/TODO
new file mode 100644
index 0000000..557fb43
--- /dev/null
+++ b/tools/z80asm/TODO
@@ -0,0 +1,6 @@
+TODO for shevek's Z80 assembler:
+- fix the bugs from BUGS :-)
+- 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.
diff --git a/tools/z80asm/jr.diff b/tools/z80asm/jr.diff
new file mode 100644
index 0000000..f3c25b5
--- /dev/null
+++ b/tools/z80asm/jr.diff
@@ -0,0 +1,81 @@
+? z80asm
+Index: z80asm.c
+===================================================================
+RCS file: /cvsroot/z80asm/z80asm/z80asm.c,v
+retrieving revision 1.10
+diff -u -r1.10 z80asm.c
+--- z80asm.c 29 Sep 2004 13:09:48 -0000 1.10
++++ z80asm.c 1 Oct 2004 06:36:41 -0000
+@@ -525,6 +525,7 @@
+ fprintf (listfile, " %02x", b);
+ listdepth += 3;
+ }
++ addr++;
+ }
+
+ /* write byte to outfile and possibly some index things as well */
+@@ -541,7 +542,6 @@
+ indexed);
+ write_one_byte (indexed, 1);
+ indexed = 0;
+- addr++;
+ }
+ if (writebyte)
+ {
+@@ -560,7 +560,6 @@
+ else
+ {
+ write_one_byte (b, 1);
+- addr++;
+ }
+ if (indexjmp)
+ {
+@@ -1200,6 +1199,8 @@
+ firstreference->prev = tmp;
+ tmp->prev = NULL;
+ firstreference = tmp;
++ /* Dummy value which should not give warnings */
++ value = (type == TYPE_RELB) ? ds_count : 0;
+ }
+ wrt_ref (value, type, ds_count);
+ }
+@@ -1783,23 +1784,19 @@
+ return;
+ }
+ write_one_byte (val + 0xC7, 1);
+- addr++;
+ return;
+ case TYPE_ABSW:
+ write_one_byte (val & 0xff, 1);
+ write_one_byte ( (val >> 8) & 0xff, 1);
+- addr++;
+ return;
+ case TYPE_ABSB:
+ write_one_byte (val & 0xff, 1);
+- addr++;
+ return;
+ case TYPE_DS:
+ if (havelist) fprintf (listfile, " %02x...", val & 0xff);
+ while (count--)
+ {
+ write_one_byte (val & 0xff, 0);
+- addr++;
+ }
+ return;
+ case TYPE_BSR:
+@@ -1818,7 +1815,6 @@
+ printerr ("Warning: relative jump out of range (%d).\n", val);
+ }
+ write_one_byte (val & 0xff, 1);
+- addr++;
+ }
+ }
+
+@@ -2558,7 +2554,6 @@
+ for (i = 0; i < r; i++)
+ {
+ write_one_byte (0, 0);
+- ++addr;
+ }
+ break;
+ case ORG:
diff --git a/tools/z80asm/z80asm.c b/tools/z80asm/z80asm.c
new file mode 100644
index 0000000..a1fa4c4
--- /dev/null
+++ b/tools/z80asm/z80asm.c
@@ -0,0 +1,2772 @@
+/* Z80 assembler by shevek
+
+ Copyright (C) 2002-2004 Bas Wijnen <b.wijnen@phys.rug.nl>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#define VERSION "0.2"
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <ctype.h>
+#include <stdarg.h>
+#include <getopt.h>
+
+/* 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 20 /* stack size for include command */
+#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,
+ 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,
+ INCLUDE,IF,ELSE,ENDIF
+};
+
+/* types of reference */
+enum reftype
+{
+ TYPE_BSR, /* bit value (0-7) for bit, set and res mnemonics */
+ TYPE_DS, /* ds reference (byte count and value) */
+ TYPE_RST, /* rst reference: 0-0x38, with val & 0x38 == val */
+ TYPE_ABSW, /* absolute word (2 bytes) */
+ TYPE_ABSB, /* absolute byte */
+ TYPE_RELB /* relative byte */
+};
+
+/* filetypes that can appear on the input. object files are on the todo list */
+enum filetype
+{
+ FILETYPE_ASM
+};
+
+/* 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 comma; /* comma when reference was set */
+ int count; /* only for ds: number of items */
+ int infile; /* index in infile[], current infile */
+ char input[1]; /* variable size buffer containing formula */
+};
+
+/* 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 */
+ char *addr; /* 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;
+};
+
+/* elements on the file stack */
+struct stack
+{
+ const char *name; /* filename (for errors). may be malloced */
+ FILE *file; /* the handle */
+ int line; /* the current line number (for errors) */
+};
+
+/* filenames must be remembered for references */
+struct name
+{
+ struct name *next, *prev;
+ char name[1];
+};
+
+/* 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","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","include","if","else","endif",NULL
+};
+
+/* linked lists */
+static struct reference *firstreference = NULL;
+static struct label *firstlabel = NULL, *lastlabel = NULL;
+static struct name *firstname = NULL;
+
+/* files */
+static FILE *realoutputfile, *outfile, *listfile, *labelfile;
+static struct infile *infile;
+/* prefix for labels in labelfile */
+static const char *labelprefix = "";
+/* bools to see if files are opened */
+static int havelist = 0, label = 0;
+/* number of infiles in array */
+static int infilecount;
+
+/* number of errors seen so far */
+static int errors = 0;
+
+/* current line, address and file */
+static int line = 0, addr = 0, file;
+/* current number of characters in list file, for indentation */
+static int listdepth;
+
+/* use readbyte instead of (hl) if writebyte is true */
+static int writebyte;
+static const char *readbyte;
+/* variables which are filled by rd_* functions and used later,
+ * like readbyte */
+static const char *readword, *indexjmp, *bitsetres;
+
+/* 0, 0xdd or 0xfd depening on which index prefix should be given */
+static int indexed;
+
+/* increased for every -v option on the command line */
+static int verbose = 0;
+
+/* read commas after indx() if comma > 1. increase for every call */
+static int comma;
+
+/* address at start of line (for references) */
+static int baseaddr;
+
+/* set by readword and readbyte, used for new_reference */
+static char mem_delimiter;
+
+/* line currently being parsed */
+static char buffer[BUFLEN];
+
+/* print an error message, including current line and file */
+static void
+printerr (const char *fmt, ...)
+{
+ va_list l;
+ va_start (l, fmt);
+ fprintf (stderr, "%s:%5d (%04X): ", infile[file].name, line, addr);
+ vfprintf (stderr, fmt, l);
+ va_end (l);
+}
+
+/* skip over spaces in string */
+static 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 ("`,' expected. Remainder of line: %s.\n", *p);
+ errors++;
+ }
+ *p = delspc ((*p) + 1);
+}
+
+/* During assembly, most 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)
+{
+ int depth = 0;
+ char c;
+ while (1)
+ {
+ switch (c = (*((*pos)++)))
+ {
+ case '\0':
+ if (depth > 0)
+ {
+ printerr ("Unexpected end of line.\n");
+ errors++;
+ }
+ (*pos)--;
+ return;
+ case '(':
+ depth++;
+ break;
+ case ')':
+ if (--depth < 0)
+ {
+ if (delimiter == ')')
+ return;
+ printerr ("Unexpected `)'.\n");
+ errors++;
+ }
+ break;
+ default:
+ if (delimiter == c && depth == 0)
+ return;
+ }
+ }
+}
+
+/* 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;
+}
+
+/* queue a file to be opened for reading */
+static void
+open_infile (const char *name)
+{
+ if (!(infile = realloc (infile, sizeof (struct infile) * (infilecount + 1))))
+ {
+ fprintf (stderr, "Error: insufficient memory\n");
+ exit (1);
+ }
+ /* only asm is currently supported */
+ infile[infilecount].type = FILETYPE_ASM;
+ infile[infilecount].name = name;
+ if (verbose > 2)
+ fprintf (stderr, "queued inputfile %s\n", infile[infilecount].name);
+ infilecount++;
+}
+
+/* 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", required_argument, NULL, 'l'},
+ {"label", required_argument, NULL, 'L'},
+ {"input", required_argument, NULL, 'i'},
+ {"output", required_argument, NULL, 'o'},
+ {"label-prefix", required_argument, NULL, 'p'}
+ };
+ const char *short_opts = "hVvl:L:i:o:p:";
+ int done = 0, i, out = 0;
+ infile = NULL;
+ while (!done)
+ {
+ switch (getopt_long (argc, argv, short_opts, opts, NULL) )
+ {
+ case 'h':
+ 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\tWrite a list file.\n"
+ "-L\t--label\tWrite a label file.\n"
+ "-p\t-label-prefix\tprefix all labels with this prefix.\n"
+ "-i\t--input\tSpecify an input file (-i may be omitted).\n"
+ "-o\t--output\tSpecify the output file.\n"
+ "Please send bug reports and feature requests to "
+ "<b.wijnen@phys.rug.nl>\n", argv[0]);
+ exit (0);
+ case 'V':
+ printf ("Z80 assembler version " VERSION "\n"
+ "Copyright (C) 2002-2003 Bas Wijnen "
+ "<b.wijnen@phys.rug.nl>.\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"
+ "For more information about these matters, see the file\n"
+ "named COPYING.\n");
+ exit (0);
+ case 'v':
+ verbose++;
+ if (verbose > 2)
+ fprintf (stderr, "Verbosity increased to level %d\n", verbose);
+ break;
+ case 'o':
+ realoutputfile =
+ openfile (&out, "output file", stdout, optarg, "wb");
+ if (verbose > 2) fprintf (stderr, "Opened outputfile\n");
+ break;
+ case 'i':
+ open_infile (optarg);
+ break;
+ case 'l':
+ listfile = openfile (&havelist, "list file", NULL, optarg, "w");
+ if (verbose > 2) fprintf (stderr, "Opened list file\n");
+ break;
+ case 'L':
+ labelfile = openfile (&label, "label file", NULL, optarg, "w");
+ if (verbose > 2) fprintf (stderr, "Opened label file\n");
+ break;
+ case 'p':
+ labelprefix = optarg;
+ 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 = stdout;
+ if (!(outfile = tmpfile ()))
+ {
+ fprintf (stderr, "Error: Unable to open temporary file: %s\n",
+ strerror (errno));
+ exit (1);
+ }
+}
+
+/* return first differing character from two strings, or 0 if they are equal */
+static int
+compare (const char *a, const char *b, size_t size)
+{
+ size_t i;
+ int j, z;
+ for (i = 0; i < size; ++i)
+ if ( (j = tolower (z = *a++) - tolower (*b++) ) || !z ) return j;
+ return 0;
+}
+
+/* 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)
+{
+ int i, l;
+ *ptr = delspc (*ptr);
+ if (!**ptr)
+ {
+ if (error)
+ {
+ printerr ("Unexpected end of line.\n");
+ errors++;
+ return 0;
+ }
+ else
+ return 0;
+ }
+ if (comma > 1)
+ rd_comma (ptr);
+ for (i = 0; list[i]; i++)
+ {
+ l = strlen (list[i]);
+ if (list[i][0] && !compare (*ptr, list[i], l)
+ && (!isalnum ((*ptr)[l]) || !isalnum (list[i][l - 1])))
+ {
+ (*ptr) += l;
+ if (verbose >= 2)
+ fprintf (stderr, "%5d (%04x): Piece of code found:%s\n", line,
+ addr, list[i]);
+ if (verbose >= 4)
+ fprintf (stderr, "%5d (%04x): Remainder of line=%s.\n", line, addr,
+ *ptr);
+ comma++;
+ return i + 1;
+ }
+ }
+ if (error)
+ {
+ printerr ("Parse error. Remainder of line=%s\n", *ptr);
+ if (verbose >= 1)
+ {
+ fprintf (stderr, "When looking for any of:\n");
+ for (i = 0; list[i]; i++)
+ fprintf (stderr, "%s\t", list[i]);
+ fprintf (stderr, "\n");
+ }
+ errors++;
+ }
+ return 0;
+}
+
+/* read a mnemonic */
+static int
+readcommand (const char **p)
+{
+ return indx (p, mnemonics, 0);
+}
+
+/* try to read a label and store it in the list */
+static void
+readlabel (const char **p)
+{
+ const char *c, *pos;
+ int i;
+ struct label *buf, *tmp, *prv;
+ for (c = *p; *c && !strchr (" \r\n\t", *c); ++c) {}
+ pos = strchr (*p, ':');
+ if (!pos || pos > c) return;
+ if (pos == *p)
+ {
+ printerr ("Error: `:' found without a label.");
+ errors++;
+ return;
+ }
+ c = pos + 1;
+ for (prv = NULL, tmp = firstlabel; tmp; prv = tmp, tmp = tmp->next)
+ {
+ i = compare (tmp->name, *p, c - *p);
+ if (i == 0)
+ {
+ printerr ( "Duplicate definition of label %s.\n", *p);
+ errors++;
+ *p = c + 1;
+ return;
+ }
+ if (i > 0)
+ break;
+ }
+ if (NULL == (buf = malloc (sizeof (struct label) + c - *p)))
+ {
+ printerr ("Not enough memory to store label %s.\n", *p);
+ *p = c + 1;
+ return;
+ }
+ strncpy (buf->name, *p, c - *p - 1);
+ buf->name[c - *p - 1] = 0;
+ if (verbose >= 1)
+ fprintf (stderr, "%5d (%04x): Label found: %s\n", line, addr, buf->name);
+ *p = c + 1;
+ buf->value = addr;
+ lastlabel = buf;
+ buf->next = tmp;
+ buf->prev = prv;
+ buf->valid = 1;
+ buf->busy = 0;
+ if (buf->prev)
+ buf->prev->next = buf;
+ else
+ firstlabel = 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)
+{
+ b &= 0xff;
+ putc (b, outfile);
+ if (list && havelist)
+ {
+ fprintf (listfile, " %02x", b);
+ listdepth += 3;
+ }
+ addr++;
+}
+
+/* write byte to outfile and possibly some index things as well */
+static void
+wrtb (int b)
+{
+ if (verbose >= 2)
+ fprintf (stderr, "%5d (%04x): wrtb called with argument %2x\n", line, addr,
+ b);
+ if (indexed)
+ {
+ if (verbose >= 3)
+ fprintf (stderr, "%5d (%04x): writing indexed byte %2x\n", line, addr,
+ indexed);
+ write_one_byte (indexed, 1);
+ indexed = 0;
+ }
+ if (writebyte)
+ {
+ if (verbose >= 3)
+ fprintf (stderr, "%5d (%04x): using a xor on byte because there is a "
+ "writebyte.\n", line, addr);
+ b ^= 0x40;
+ }
+ if (verbose >= 3)
+ fprintf (stderr, "%5d (%04x): writing byte %2x\n", 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 >= 3)
+ fprintf (stderr, "%5d (%04x): Making reference for index/jump %s\n",
+ line, addr, indexjmp);
+ new_reference (indexjmp, TYPE_ABSB, ')', 1);
+ indexjmp = NULL;
+ }
+ if (writebyte)
+ {
+ if (verbose >= 3)
+ fprintf (stderr, "%5d (%04x): writing argument byte for padding\n",
+ line, addr);
+ writebyte = 0;
+ new_reference (readbyte, TYPE_ABSB, mem_delimiter, 1);
+ }
+}
+
+/* reading expressions. The following operators are supported
+ * in order of precedence, with function name:
+ * expr?expr:expr 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
+rd_number (const char **p, const char **endp, int base)
+{
+ int result = 0, i;
+ char *c, num[] = "0123456789abcdefghijklmnopqrstuvwxyz";
+ if (verbose >= 4)
+ fprintf (stderr, "%5d (%04x): Starting to read number of base %d"
+ "(string=%s).\n", line, addr, base, *p);
+ num[base] = '\0';
+ *p = delspc (*p);
+ while (**p && (c = strchr (num, tolower (**p))))
+ {
+ i = c - num;
+ if (verbose >= 5)
+ fprintf (stderr, "%5d (%04x): Digit found:%1x.\n", line, addr, i);
+ result = result * base + i;
+ (*p)++;
+ }
+ if ( endp )
+ *endp = *p;
+ *p = delspc (*p);
+ if (verbose >= 5)
+ fprintf (stderr, "%5d (%04x): rd_number returned %d (%04X).\n",
+ line, addr, result, result);
+ return result;
+}
+
+static int
+rd_otherbasenumber (const char **p)
+{
+ char c;
+ if (verbose >= 4)
+ fprintf (stderr, "%5d (%04x): Starting to read basenumber (string=%s).\n",
+ line, addr, *p);
+ (*p)++;
+ if (!**p)
+ {
+ printerr ("Unexpected end of line after `@'.\n");
+ errors++;
+ return 0;
+ }
+ if (**p == '0' || !isalnum (**p))
+ {
+ printerr ("Base must be between 1 and z.\n");
+ errors++;
+ 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);
+}
+
+static int
+rd_character (const char **p)
+{
+ int i;
+ if (verbose >= 4)
+ fprintf (stderr, "%5d (%04x): Starting to read character (string=%s).\n",
+ line, addr, *p);
+ (*p)++;
+ i = **p;
+ if (!i)
+ {
+ printerr ("Unexpected end of line after opening quote.\n");
+ errors++;
+ return 0;
+ }
+ if (i == '\\')
+ {
+ (*p)++;
+ switch (**p)
+ {
+ case 'n':
+ i = 10;
+ break;
+ case 'r':
+ i = 13;
+ break;
+ case 't':
+ i = 9;
+ break;
+ case 'a':
+ i = 7;
+ break;
+ case '\'':
+ printerr ("Empty literal character.\n");
+ errors++;
+ return 0;
+ case 0:
+ printerr ("Unexpected end of line after "
+ "opening opening quote and backslash.\n");
+ errors++;
+ return 0;
+ default:
+ i = **p;
+ }
+ }
+ (*p)++;
+ if (**p != '\'')
+ {
+ printerr ("Missing closing quote.\n");
+ errors++;
+ return 0;
+ }
+ (*p)++;
+ if (verbose >= 5)
+ fprintf (stderr, "%5d (%04x): rd_character returned %d (%c).\n",
+ line, addr, i, i);
+ return i;
+}
+
+static int rd_expr (const char **p, char delimiter, int *valid);
+
+static void compute_label (struct label *l)
+{
+ int value, valid;
+ const char *c;
+ if (l->busy) return; /* don't loop */
+ l->busy = 1;
+ c = l->addr;
+ if (verbose >= 4)
+ fprintf (stderr, "Trying to compute label %s.\n", l->name);
+ value = rd_expr (&c, '\0', &valid);
+ if (valid)
+ {
+ l->value = value;
+ l->valid = 1;
+ free (l->addr);
+ }
+ l->busy = 0;
+}
+
+static int
+rd_label (const char **p, int *exists)
+{
+ int found = 0;
+ const char *where = NULL;
+ struct label *l;
+ if (exists) *exists = 0;
+ if (verbose >= 4)
+ fprintf (stderr, "%5d (%04x): Starting to read label (string=%s).\n", line,
+ addr, *p);
+ for (l = firstlabel; l; l = l->next)
+ {
+ if (!strncmp (l->name, *p, strlen (l->name)))
+ {
+ const char *c;
+ c = *p + strlen (l->name);
+ if (!(isalnum (*c) || *c == '_'))
+ {
+ found = 1;
+ where = *p;
+ *p = delspc (c);
+ /* if label is not valid, compute it */
+ if (!l->valid) compute_label (l);
+ if (!l->valid)
+ {
+ /* label was not valid, and isn't computable. tell the
+ * caller that it doesn't exist, so it will try again later.
+ * Just in case some other label matches the same input,
+ * continue trying all the other labels. */
+ if (verbose >= 4)
+ fprintf (stderr,
+ "%5d (%04x): returning invalid label %s.\n",
+ line, addr, l->name);
+ continue;
+ }
+ if (exists) *exists = 1;
+ if (verbose >= 5)
+ fprintf (stderr, "%5d (%04x): rd_label returned %d (%04X).\n",
+ line, addr, l->value, l->value);
+ return l->value;
+ }
+ }
+ }
+ if (exists)
+ {
+ /* this was only a check for existance, skip the label */
+ if (verbose >= 4)
+ fprintf (stderr, "%5d (%04x): Returning invalid for unknown label.\n",
+ line, addr);
+ while (isalnum (**p) || **p == '_') ++*p;
+ }
+ else
+ {
+ if (found)
+ {
+ printerr ("Uncomputable label found: %.*s.\n", *p, where - *p);
+ }
+ else
+ {
+ printerr ("Syntax error (label or number expected): %s.\n", *p);
+ }
+ errors++;
+ *p = "";
+ }
+ return 0;
+}
+
+static int
+rd_value (const char **p, int *valid)
+{
+ int sign = 1, not = 0, base, v;
+ const char *p0, *p1, *p2;
+ if (verbose >= 4)
+ fprintf (stderr, "%5d (%04x): Starting to read value (string=%s).\n", 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 */
+ switch (**p)
+ {
+ int exist;
+ case '(':
+ (*p)++;
+ return not ^ (sign * rd_expr (p, ')', valid));
+ 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 ) {
+ printerr("invalid character in number: \'%c\'\n", *p2);
+ errors++;
+ }
+ return not ^ (sign * v);
+ case '$':
+ (*p)++;
+ return not ^ (sign * addr);
+ case '%':
+ (*p)++;
+ return not ^ (sign * rd_number (p, NULL, 2) );
+ case '\'':
+ return not ^ (sign * rd_character (p) );
+ case '@':
+ return not ^ (sign * rd_otherbasenumber (p) );
+ case '!':
+ rd_label (p, &exist);
+ return not ^ (sign * exist);
+ default:
+ return not ^ (sign * rd_label (p, valid) );
+ }
+}
+
+static int
+rd_factor (const char **p, int *valid)
+{
+ /* read a factor of an expression */
+ int result;
+ if (verbose >= 4)
+ fprintf (stderr, "%5d (%04x): Starting to read factor (string=%s).\n",
+ line, addr, *p);
+ result = rd_value (p, valid);
+ *p = delspc (*p);
+ while (**p == '*' || **p == '/')
+ {
+ if (**p == '*')
+ {
+ (*p)++;
+ result *= rd_value (p, valid);
+ }
+ else if (**p == '/')
+ {
+ (*p)++;
+ result /= rd_value (p, valid);
+ }
+ *p = delspc (*p);
+ }
+ if (verbose >= 5)
+ fprintf (stderr, "%5d (%04x): rd_factor returned %d (%04X).\n",
+ line, addr, result, result);
+ return result;
+}
+
+static int
+rd_term (const char **p, int *valid)
+{
+ /* read a term of an expression */
+ int result;
+ if (verbose >= 4)
+ fprintf (stderr, "%5d (%04x): Starting to read term (string=%s).\n", line,
+ addr, *p);
+ result = rd_factor (p, valid);
+ *p = delspc (*p);
+ while (**p == '+' || **p == '-')
+ {
+ if (**p == '+')
+ {
+ (*p)++;
+ result += rd_factor (p, valid);
+ }
+ else if (**p == '-')
+ {
+ (*p)++;
+ result -= rd_factor (p, valid);
+ }
+ *p = delspc (*p);
+ }
+ if (verbose >= 5)
+ fprintf (stderr, "%5d (%04x): rd_term returned %d (%04X).\n",
+ line, addr, result, result);
+ return result;
+}
+
+static int
+rd_expr_shift (const char **p, int *valid)
+{
+ int result;
+ if (verbose >= 4)
+ fprintf (stderr, "%5d (%04x): Starting to read shift expression "
+ "(string=%s).\n", line, addr, *p);
+ result = rd_term (p, valid);
+ *p = delspc (*p);
+ while ( (**p == '<' || **p == '>') && (*p)[1] == **p)
+ {
+ if (**p == '<')
+ {
+ (*p) += 2;
+ result <<= rd_term (p, valid);
+ }
+ else if (**p == '>')
+ {
+ (*p) += 2;
+ result >>= rd_term (p, valid);
+ }
+ *p = delspc (*p);
+ }
+ if (verbose >= 5)
+ fprintf (stderr, "%5d (%04x): rd_shift returned %d (%04X).\n",
+ line, addr, result, result);
+ return result;
+}
+
+static int
+rd_expr_unequal (const char **p, int *valid)
+{
+ int result;
+ if (verbose >= 4)
+ fprintf (stderr, "%5d (%04x): Starting to read "
+ "unequality expression (string=%s).\n", line, addr, *p);
+ result = rd_expr_shift (p, valid);
+ *p = delspc (*p);
+ if (**p == '<' && (*p)[1] == '=')
+ {
+ (*p) += 2;
+ return result <= rd_expr_unequal (p, valid);
+ }
+ else if (**p == '>' && (*p)[1] == '=')
+ {
+ (*p) += 2;
+ return result >= rd_expr_unequal (p, valid);
+ }
+ if (**p == '<' && (*p)[1] != '<')
+ {
+ (*p)++;
+ return result < rd_expr_unequal (p, valid);
+ }
+ else if (**p == '>' && (*p)[1] != '>')
+ {
+ (*p)++;
+ return result > rd_expr_unequal (p, valid);
+ }
+ if (verbose >= 5)
+ fprintf (stderr, "%5d (%04x): rd_shift returned %d (%04X).\n",
+ line, addr, result, result);
+ return result;
+}
+
+static int
+rd_expr_equal (const char **p, int *valid)
+{
+ int result;
+ if (verbose >= 4)
+ fprintf (stderr, "%5d (%04x): Starting to read equality epression "
+ "(string=%s).\n", line, addr, *p);
+ result = rd_expr_unequal (p, valid);
+ *p = delspc (*p);
+ if (**p == '=' && (*p)[1] == '=')
+ {
+ (*p) += 2;
+ return result == rd_expr_equal (p, valid);
+ }
+ else if (**p == '!' && (*p)[1] == '=')
+ {
+ (*p) += 2;
+ return result != rd_expr_equal (p, valid);
+ }
+ if (verbose >= 5)
+ fprintf (stderr, "%5d (%04x): rd_equal returned %d (%04X).\n",
+ line, addr, result, result);
+ return result;
+}
+
+static int
+rd_expr_and (const char **p, int *valid)
+{
+ int result;
+ if (verbose >= 4)
+ fprintf (stderr, "%5d (%04x): Starting to read and expression "
+ "(string=%s).\n", line, addr, *p);
+ result = rd_expr_equal (p, valid);
+ *p = delspc (*p);
+ if (**p == '&')
+ {
+ (*p)++;
+ result &= rd_expr_and (p, valid);
+ }
+ if (verbose >= 5)
+ fprintf (stderr, "%5d (%04x): rd_expr_and returned %d (%04X).\n",
+ line, addr, result, result);
+ return result;
+}
+
+static int
+rd_expr_xor (const char **p, int *valid)
+{
+ int result;
+ if (verbose >= 4)
+ fprintf (stderr, "%5d (%04x): Starting to read xor expression "
+ "(string=%s).\n", line, addr, *p);
+ result = rd_expr_and (p, valid);
+ if (verbose >= 5)
+ fprintf (stderr, "%5d (%04x): rd_expr_xor: rd_expr_and returned %d "
+ "(%04X).\n", line, addr, result, result);
+ *p = delspc (*p);
+ if (**p == '^')
+ {
+ (*p)++;
+ result ^= rd_expr_xor (p, valid);
+ }
+ if (verbose >= 5)
+ fprintf (stderr, "%5d (%04x): rd_expr_xor returned %d (%04X).\n",
+ line, addr, result, result);
+ return result;
+}
+
+static int
+rd_expr_or (const char **p, int *valid)
+{
+ int result;
+ if (verbose >= 4)
+ fprintf (stderr, "%5d (%04x): Starting to read or expression "
+ "(string=%s).\n", line, addr, *p);
+ result = rd_expr_xor (p, valid);
+ if (verbose >= 5)
+ fprintf (stderr, "%5d (%04x): rd_expr_or: rd_expr_xor returned %d "
+ "(%04X).\n", line, addr, result, result);
+ *p = delspc (*p);
+ if (**p == '|')
+ {
+ (*p)++;
+ result |= rd_expr_or (p, valid);
+ }
+ if (verbose >= 5)
+ fprintf (stderr, "%5d (%04x): rd_expr_or returned %d (%04X).\n",
+ line, addr, result, result);
+ return result;
+}
+
+static int
+rd_expr (const char **p, char delimiter, int *valid)
+{
+ /* read an expression. delimiter can _not_ be '?' */
+ int result = 0;
+ if (verbose >= 4)
+ fprintf (stderr, "%5d (%04x): Starting to read expression (string=%s).\n",
+ line, addr, *p);
+ if (valid) *valid = 1;
+ *p = delspc (*p);
+ if (!**p || **p == delimiter)
+ {
+ printerr ("Error: Expression expected (not %s)\n", *p);
+ errors++;
+ return 0;
+ }
+ result = rd_expr_or (p, valid);
+ *p = delspc (*p);
+ if (**p == '?')
+ {
+ (*p)++;
+ if (result)
+ {
+ result = rd_expr (p, ':', valid);
+ if (**p) (*p)++;
+ rd_expr (p, delimiter, valid);
+ }
+ else
+ {
+ rd_expr (p, ':', valid);
+ if (**p) (*p)++;
+ result = rd_expr (p, delimiter, valid);
+ }
+ }
+ if (verbose >= 5)
+ {
+ fprintf (stderr, "%5d (%04x): rd_expr returned %d (%04X).\n",
+ line, addr, result, result);
+ if (valid && !*valid)
+ fprintf (stderr, "%5d (%04x): Returning invalid result.\n",
+ line, addr);
+ }
+ return result;
+}
+
+static void wrt_ref (int val, int type, int count);
+
+/* Create a new reference, to be resolved after assembling (so all labels are
+ * known.) */
+void
+new_reference (const char *p, int type, char delimiter, int ds_count)
+{
+ struct reference *tmp;
+ long opos, lpos;
+ int valid, value;
+ const char *c;
+ c = p;
+ value = rd_expr (&c, delimiter, &valid);
+ if (valid)
+ {
+ if (verbose >= 3)
+ {
+ fprintf (stderr,
+ "%5d (%04x): Using calculated value %d (%x) immediately.\n",
+ line, addr, value, value);
+ }
+ }
+ else
+ {
+ /* the expression is not valid (yet), we need to make a real reference.
+ */
+ if (NULL == (tmp = malloc (sizeof (struct reference) + strlen (p))))
+ {
+ printerr ("Warning: unable to allocate memory for reference %s.\n",
+ p);
+ return;
+ }
+ opos = ftell (outfile);
+ lpos = havelist ? ftell (listfile) : 0;
+ if (verbose >= 1)
+ fprintf (stderr, "%5d (%04x): reference set to %s (delimiter=%c)\n",
+ line, addr, p, delimiter);
+ strcpy (tmp->input, p);
+ tmp->addr = baseaddr;
+ tmp->count = ds_count;
+ tmp->line = line;
+ tmp->infile = file;
+ tmp->comma = comma++;
+ tmp->oseekpos = opos;
+ tmp->lseekpos = lpos;
+ tmp->delimiter = delimiter;
+ tmp->type = type;
+ tmp->next = firstreference;
+ if (firstreference)
+ firstreference->prev = tmp;
+ tmp->prev = NULL;
+ firstreference = tmp;
+ /* Dummy value which should not give warnings */
+ value = (type == TYPE_RELB) ? ds_count : 0;
+ }
+ wrt_ref (value, type, ds_count);
+}
+
+/* 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 indexjump byte (for (ix + nn) commands) */
+static void
+rd_index (const char **p)
+{
+ rd_byte (p, ')');
+ indexjmp = readbyte;
+ writebyte = 0;
+ return;
+}
+
+/* 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);
+}
+
+/* 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);
+}
+
+/* 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);
+}
+
+/* read (c) or (nn) */
+static int
+rd_nnc (const char **p)
+{
+#define C 1
+ int i;
+ const char *list[] = {"(", NULL};
+ i = indx (p, list, 1);
+ if (!i) return 0;
+ *p = delspc (*p);
+ if (tolower (**p) == 'c')
+ {
+ *p = delspc ((*p) + 1);
+ if (**p != ')')
+ {
+ printerr ("Missing closing parenthesis\n");
+ errors++;
+ return 0;
+ }
+ (*p)++;
+ return C;
+ }
+ if (tolower (**p) == 'a')
+ {
+ *p = delspc ((*p) + 1);
+ if (**p != ',')
+ {
+ printerr ("Missing ','\n");
+ errors++;
+ return 0;
+ }
+ *p = delspc ((*p) + 1);
+ }
+ rd_byte (p, ')');
+ writebyte = 0;
+ return 2;
+}
+
+/* read (C) */
+static int
+rd_c (const char **p)
+{
+ const char *list[] = {"(c)", "(bc)", NULL};
+ return indx (p, list, 1);
+}
+
+/* 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);
+}
+
+/* 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};
+ i = indx (p, list, 1);
+ 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)
+ {
+ rd_index (p);
+ indexed = i == ld_IX ? 0xDD : 0xFD;
+ return ld_HL;
+ }
+ if (i == ld_NN)
+ rd_word (p, ')');
+ 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);
+ 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);
+}
+
+/* read A */
+static int
+rd_a (const char **p)
+{
+ const char *list[] = {"a", NULL};
+ return indx (p, list, 1);
+}
+
+/* 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);
+ if (i < 5)
+ return i;
+ indexed = 0xDD + 0x20 * (i - 5);
+ return 3;
+}
+
+/* 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);
+ if (i < 2)
+ return i;
+ if (i == 2)
+ return 2;
+ indexed = 0xDD + 0x20 * (i - 3);
+ return 2;
+}
+
+/* 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);
+}
+
+/* read bc,de,hl|ix|iy,sp. hl|ix|iy only if it is already indexed. */
+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);
+ case 0xFD:
+ return indx (p, listy, 1);
+ default:
+ return indx (p, list, 1);
+ }
+}
+
+/* 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};
+ i = indx (p, list, 0);
+ if (!i)
+ {
+ rd_byte (p, '\0');
+ 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);
+ rd_index (p);
+ 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);
+ if (i < 9)
+ return i;
+ indexed = 0xDD + 0x20 * (i - 9);
+ rd_index (p);
+ 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 || **p == ';') 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);
+}
+
+/* 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);
+ if (!i)
+ return 0;
+ if (i < 16 && i > 2)
+ return 7 - i;
+ if (i > 15)
+ {
+ rd_index (p);
+ 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);
+}
+
+/* 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);
+ switch (i)
+ {
+ case 0:
+ return 0;
+ case 1:
+ return 1;
+ default:
+ 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);
+}
+
+/* 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);
+}
+
+/* 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);
+ if (i)
+ return i;
+ rd_byte (p, '\0');
+ 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);
+ 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};
+ i = indx (p, list, 0);
+ if (i == 2 || i == 5)
+ {
+ indexed = (i == 2) ? 0xFD : 0xDD;
+ rd_index (p);
+ return 7;
+ }
+ if (!i)
+ {
+ rd_byte (p, '\0');
+ return 7;
+ }
+ if (i == 16)
+ {
+ rd_word (p, ')');
+ }
+ return i - 5;
+}
+
+/* read argument of 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};
+ i = indx (p, list, 0);
+ if (i > 10)
+ {
+ int x;
+ x = 0xdd + 0x20 * (i > 12);
+ if (indexed && indexed != x)
+ {
+ printerr ("Syntax error: illegal use of index registers.\n");
+ errors++;
+ return 0;
+ }
+ indexed = x;
+ return 6 - (i & 1);
+ }
+ if (i > 8)
+ {
+ if (indexed)
+ {
+ printerr ("Syntax error: illegal use of index registers.\n");
+ errors++;
+ return 0;
+ }
+ indexed = 0xDD + 0x20 * (i == 10);
+ rd_index (p);
+ return 7;
+ }
+ if (i)
+ return i;
+ rd_byte (p, '\0');
+ return 7;
+}
+
+/* read nnnn, or (nnnn) */
+static int
+rd_nn_nn (const char **p)
+{
+#define _NN 1
+ int i;
+ const char *list[] = {"(", NULL};
+ i = indx (p, list, 0);
+ if (i)
+ {
+ rd_word (p, ')');
+ return 1;
+ }
+ rd_word (p, '\0');
+ return 0;
+}
+
+/* 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[] = {"(", "ix", "iy", "hl", NULL};
+ i = indx (p, list, 0);
+ switch (i)
+ {
+ case 0:
+ rd_word (p, '\0');
+ return 0;
+ case 1:
+ rd_word (p, ')');
+ return 2;
+ }
+ if (i == 4)
+ return 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 ("Error: incorrect RST value %d (%02x).\n", val, val);
+ errors++;
+ return;
+ }
+ write_one_byte (val + 0xC7, 1);
+ return;
+ case TYPE_ABSW:
+ write_one_byte (val & 0xff, 1);
+ write_one_byte ( (val >> 8) & 0xff, 1);
+ return;
+ case TYPE_ABSB:
+ write_one_byte (val & 0xff, 1);
+ return;
+ case TYPE_DS:
+ if (havelist) fprintf (listfile, " %02x...", val & 0xff);
+ while (count--)
+ {
+ write_one_byte (val & 0xff, 0);
+ }
+ return;
+ case TYPE_BSR:
+ if (val & ~7)
+ {
+ printerr ("Error: incorrect BIT/SET/RES value %d.\n", val);
+ errors++;
+ return;
+ }
+ write_one_byte (0x08 * val + count, 1);
+ return;
+ case TYPE_RELB:
+ val -= count;
+ if (val < -128 || val > 127)
+ {
+ printerr ("Warning: relative jump out of range (%d).\n", val);
+ }
+ write_one_byte (val & 0xff, 1);
+ }
+}
+
+/* do the actual work */
+static void
+assemble (void)
+{
+ int ifcount = 0, noifcount = 0;
+ const char *ptr;
+ char *bufptr;
+ struct reference *tmp;
+ int r, s, shouldclose, sp; /* r,s for registers, sp is stack pointer */
+ struct stack stack[MAX_INCLUDE]; /* maximum level of includes */
+ /* continue assembling until the last input file is done */
+ for (file = 0; file < infilecount; ++file)
+ {
+ sp = 0; /* clear stack */
+ stack[sp].line = 0;
+ shouldclose = 0;
+ stack[sp].name = infile[file].name;
+ 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 ("Error: unable to open %s. skipping\n",
+ infile[file].name);
+ errors++;
+ continue;
+ }
+ shouldclose = 1;
+ }
+ if (havelist) fprintf (listfile, "# File %s\n", stack[sp].name);
+ buffer[0] = 0;
+ /* loop until this source file is done */
+ while (1)
+ {
+ int cmd, cont = 1;
+ if (havelist)
+ {
+ if (buffer[0] != 0)
+ {
+ int i, tabs;
+ 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;
+ }
+ while (NULL == fgets (buffer, BUFLEN, stack[sp].file))
+ {
+ if (verbose > 3) fprintf (stderr, "finished reading file %s\n",
+ stack[sp].name);
+ if (havelist) fprintf (listfile, "# End of file %s\n",
+ stack[sp].name);
+ if (shouldclose) fclose (stack[sp].file);
+ 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;
+ line++;
+ ptr = delspc (ptr);
+ if (!*ptr) continue;
+ if (!noifcount) readlabel (&ptr);
+ 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++;
+ }
+ case ENDIF:
+ noifcount--;
+ }
+ continue;
+ }
+ switch (cmd)
+ {
+ int i, valid;
+ const char *c;
+ 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_a_hlx (&ptr)))
+ break;
+ if (r == HL)
+ {
+ if (!(r = rd_rrxx (&ptr)))
+ break;
+ wrtb (0x09 + 0x10 * --r);
+ break;
+ }
+ if (!(r = rd_r (&ptr)))
+ break;
+ wrtb (0x80 + --r);
+ break;
+ case AND:
+ if (!(r = rd_r (&ptr)))
+ break;
+ wrtb (0xA0 + --r);
+ break;
+ case BIT:
+ if (!rd_0_7 (&ptr))
+ break;
+ 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 ("EQU without label.\n");
+ errors++;
+ break;
+ }
+ c = ptr;
+ lastlabel->value = rd_expr (&ptr, '\0', &valid);
+ lastlabel->valid = 1;
+ if (!valid)
+ {
+ lastlabel->addr = malloc (ptr - c + 1);
+ if (lastlabel->addr == NULL)
+ {
+ printerr ("No memory to store label reference.\n");
+ errors++;
+ break;
+ }
+ strncpy (lastlabel->addr, c, ptr - c);
+ lastlabel->addr[ptr - c] = 0;
+ lastlabel->valid = 0;
+ lastlabel->busy = 0;
+ }
+ if (verbose >= 2)
+ {
+ 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);
+ }
+ 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 *tmp2;
+ if (!(r = rd_nnc (&ptr)))
+ break;
+ if (r == C)
+ {
+ wrtb (0xED);
+ wrtb (0x40 + 8 * --r);
+ break;
+ }
+ tmp2 = readbyte;
+ wrtb (0xDB);
+ new_reference (tmp2, 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 *tmp2 = readbyte;
+ wrtb (0xD3);
+ new_reference (tmp2, 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;
+ 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);
+ 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;
+ 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;
+ wrtb (0x90 + --r);
+ break;
+ case XOR:
+ if (!(r = rd_r (&ptr)))
+ break;
+ wrtb (0xA8 + --r);
+ break;
+ case DEFB:
+ case DB:
+ ptr = delspc (ptr);
+ if (*ptr != '"' && !(r = rd_byte (&ptr, ',')))
+ break;
+ do
+ {
+ if (*ptr == '"')
+ {
+ while (*++ptr != '"')
+ {
+ if (*ptr == 0)
+ {
+ printerr ("Error: end of line in quoted "
+ "string\n");
+ errors++;
+ break;
+ }
+ wrtb (*ptr);
+ }
+ if (!*ptr) break;
+ ++ptr;
+ ptr = delspc (ptr);
+ if (!*ptr) break;
+ if (*ptr++ != ',')
+ {
+ printerr ("Error: expected end of line or ','");
+ errors++;
+ break;
+ }
+ continue;
+ }
+ new_reference (readbyte, TYPE_ABSB, ',', 1);
+ }
+ while ((r = rd_byte (&ptr, ',')));
+ writebyte = 0;
+ break;
+ case DEFW:
+ case DW:
+ if (!(r = rd_word (&ptr, ',')))
+ break;
+ do
+ {
+ new_reference (readword, TYPE_ABSW, ',', 1);
+ }
+ while ((r = rd_word (&ptr, ',')));
+ break;
+ case DEFS:
+ case DS:
+ r = rd_expr (&ptr, ',', NULL);
+ if (r < 0)
+ {
+ printerr ("ds should have its first argument >=0"
+ " (not %d).\n", r);
+ errors++;
+ break;
+ }
+ ptr = delspc (ptr);
+ if (*ptr)
+ {
+ rd_comma (&ptr);
+ 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 ORG:
+ addr = rd_expr (&ptr, '\0', NULL);
+ break;
+ case INCLUDE:
+ if (sp + 1 >= MAX_INCLUDE)
+ {
+ printerr ("stack overflow (circular include?)");
+ if (verbose > 2)
+ {
+ 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;
+ }
+ ptr = delspc (ptr);
+ {
+ int pos = 0;
+ char quote;
+ struct name *nm,
+ *name = malloc (sizeof (struct name) + strlen (ptr));
+ if (!name)
+ {
+ printerr ("Unable to allocate memory for filename %s\n",
+ name);
+ errors++;
+ break;
+ }
+ if (*ptr == ';' || !*ptr)
+ {
+ printerr ("include without filename\n");
+ free (name);
+ errors++;
+ break;
+ }
+ quote = *ptr++;
+ while (*ptr != quote)
+ {
+ if (!*ptr)
+ {
+ printerr ("filename without closing quote (%c)\n",
+ quote);
+ free (name);
+ errors++;
+ break;
+ }
+ name->name[pos++] = *ptr++;
+ }
+ name->name[pos] = 0;
+ nm =
+ realloc (name, sizeof (struct name) + strlen (name->name));
+ if (nm) name = nm;
+ sp++;
+ stack[sp].name = name->name;
+ stack[sp].line = 0;
+ stack[sp].file = fopen (name->name, "r");
+ if (!stack[sp].file)
+ {
+ printerr ("Unable to open file %s: %s\n",
+ name->name, strerror (errno));
+ errors++;
+ free (name);
+ sp--;
+ break;
+ }
+ name->next = firstname;
+ name->prev = NULL;
+ if (name->next) name->next->prev = name;
+ firstname = name;
+ if (verbose > 1)
+ fprintf (stderr, "Reading file %s\n", name->name);
+ }
+ break;
+ case IF:
+ if (rd_expr (&ptr, '\0', NULL)) ifcount++; else noifcount++;
+ break;
+ case ELSE:
+ if (ifcount == 0)
+ {
+ printerr ("Error: else without if.\n");
+ errors++;
+ break;
+ }
+ noifcount = 1;
+ ifcount--;
+ break;
+ case ENDIF:
+ if (noifcount == 0 && ifcount == 0)
+ {
+ printerr ("Error: endif without if.\n");
+ errors++;
+ break;
+ }
+ if (noifcount) noifcount--; else ifcount--;
+ break;
+ default:
+ printerr ("Syntax error: command or comment "
+ "expected (was %s).\n", ptr);
+ errors++;
+ }
+ }
+ }
+ if (ifcount || noifcount) printerr ("Reached EOF at IF level %d\n",
+ ifcount + noifcount);
+ if (havelist)
+ {
+ fprintf (listfile, "%04X\n", addr);
+ }
+ {
+ struct reference *next;
+ for (tmp = firstreference; tmp; tmp = next)
+ {
+ int ref;
+ char *c;
+ next = tmp->next;
+ fseek (outfile, tmp->oseekpos, SEEK_SET);
+ if (havelist) fseek (listfile, tmp->lseekpos, SEEK_SET);
+ addr = tmp->addr;
+ line = tmp->line;
+ comma = tmp->comma;
+ file = tmp->infile;
+ if ((c = strchr (tmp->input, '\n')))
+ *c = 0;
+ if (verbose >= 1)
+ fprintf (stderr, "%5d (%04x): Making reference to %s.\n", line, addr,
+ tmp->input);
+ ptr = tmp->input;
+ ref = rd_expr (&ptr, tmp->delimiter, NULL);
+ if (verbose >= 2)
+ fprintf (stderr, "%5d (%04x): Reference is %d (%04x).\n", line, addr,
+ ref, ref);
+ wrt_ref (ref, tmp->type, tmp->count);
+ free (tmp);
+ }
+ }
+ if (havelist) fclose (listfile);
+ if (label)
+ {
+ /* write all labels */
+ struct label *l;
+ for (l = firstlabel; l; l = l->next)
+ {
+ if (!l->valid) compute_label (l);
+ if (!l->valid)
+ {
+ printerr ("Label %s at end of code still uncomputable.\n",
+ l->name);
+ errors++;
+ continue;
+ }
+ fprintf (labelfile, "%s%s:\tequ %04xh\n", labelprefix, l->name,
+ l->value);
+ }
+ fclose (labelfile);
+ }
+ while (firstlabel)
+ {
+ struct label *l;
+ l = firstlabel->next;
+ free (firstlabel);
+ firstlabel = l;
+ }
+ {
+ int l, size, len = 0;
+ rewind (outfile);
+ while (1)
+ {
+ clearerr (outfile);
+ errno = 0;
+ len = fread (buffer, 1, BUFLEN, outfile);
+ if (len == 0 && feof (outfile)) break;
+ if (len <= 0)
+ {
+ fprintf (stderr, "error reading temp file: %s\n",
+ strerror (errno));
+ exit (1);
+ }
+ l = 0;
+ while (l < len)
+ {
+ clearerr (realoutputfile);
+ size = fwrite (&buffer[l], 1, len - l, realoutputfile);
+ if (size <= 0)
+ {
+ fprintf (stderr, "error writing output file: %s\n",
+ strerror (errno));
+ exit (1);
+ }
+ l += size;
+ }
+ }
+ }
+ fclose (outfile);
+ fclose (realoutputfile);
+ free (infile);
+}
+
+int
+main (int argc, char **argv)
+{
+ parse_commandline (argc, argv);
+ assemble ();
+ if (errors)
+ {
+ if (errors == 1)
+ fprintf (stderr, "*** 1 error found ***\n");
+ else
+ fprintf (stderr, "*** %d error found ***\n", errors);
+ return 1;
+ }
+ else return 0;
+}