diff options
author | Juan J. Martinez <jjm@usebox.net> | 2023-11-05 11:22:55 +0000 |
---|---|---|
committer | Juan J. Martinez <jjm@usebox.net> | 2023-11-05 11:31:28 +0000 |
commit | 2fbdf974338bde8576efdae40a819a76b2391033 (patch) | |
tree | 64d41a37470143f142344f9a439d96de3e7918c2 /tools | |
download | kitsunes-curse-2fbdf974338bde8576efdae40a819a76b2391033.tar.gz kitsunes-curse-2fbdf974338bde8576efdae40a819a76b2391033.zip |
Initial import of the open source release
Diffstat (limited to 'tools')
142 files changed, 52468 insertions, 0 deletions
diff --git a/tools/2CDT/2cdt.cbp b/tools/2CDT/2cdt.cbp new file mode 100644 index 0000000..41deaf1 --- /dev/null +++ b/tools/2CDT/2cdt.cbp @@ -0,0 +1,43 @@ +<?xml version="1.0" encoding="UTF-8" standalone="yes" ?> +<CodeBlocks_project_file> + <FileVersion major="1" minor="4" /> + <Project> + <Option title="2cdt" /> + <Option makefile="makefile" /> + <Option pch_mode="0" /> + <Option compiler="gcc" /> + <Build> + <Target title="default"> + <Option output="C:\2cdt\2cdt.exe" /> + <Option type="1" /> + <Option compiler="gcc" /> + <Option parameters="-m 2 -r snake c:\blocks.bin c:\tester.cdt" /> + <Option includeInTargetAll="1" /> + <Option projectResourceIncludeDirsRelation="1" /> + </Target> + </Build> + <Compiler> + <Add option="-g" /> + </Compiler> + <Unit filename="src\2cdt.c"> + <Option compilerVar="CC" /> + <Option target="default" /> + </Unit> + <Unit filename="src\defs.h"> + <Option compilerVar="CC" /> + <Option compile="0" /> + <Option link="0" /> + <Option target="default" /> + </Unit> + <Unit filename="src\tzxfile.c"> + <Option compilerVar="CC" /> + <Option target="default" /> + </Unit> + <Unit filename="src\tzxfile.h"> + <Option compilerVar="CC" /> + <Option compile="0" /> + <Option link="0" /> + <Option target="default" /> + </Unit> + </Project> +</CodeBlocks_project_file>
\ No newline at end of file diff --git a/tools/2CDT/2cdt.depend b/tools/2CDT/2cdt.depend new file mode 100644 index 0000000..7427b7c --- /dev/null +++ b/tools/2CDT/2cdt.depend @@ -0,0 +1,22 @@ +# depslib dependency file v1.0
+1137940710 source:d:\www\download\2cdt\2cdt\src\tzxfile.c
+ <stdio.h>
+ <stdlib.h>
+ <memory.h>
+ "tzxfile.h"
+
+991048958 d:\www\download\2cdt\2cdt\src\tzxfile.h
+ "defs.h"
+
+1014464942 d:\www\download\2cdt\2cdt\src\defs.h
+
+1280219172 source:d:\www\download\2cdt\2cdt\src\2cdt.c
+ <stdio.h>
+ <stdlib.h>
+ <string.h>
+ <sys/io.h>
+ <io.h>
+ "defs.h"
+ "tzxfile.h"
+ "getopt.h"
+
diff --git a/tools/2CDT/2cdt.layout b/tools/2CDT/2cdt.layout new file mode 100644 index 0000000..b5fceb8 --- /dev/null +++ b/tools/2CDT/2cdt.layout @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="UTF-8" standalone="yes" ?> +<CodeBlocks_layout_file> + <ActiveTarget name="All" /> + <File name="src\2cdt.c" open="1" top="1" tabpos="1"> + <Cursor position="26859" topLine="1012" /> + </File> +</CodeBlocks_layout_file> diff --git a/tools/2CDT/COPYING b/tools/2CDT/COPYING new file mode 100644 index 0000000..196760e --- /dev/null +++ b/tools/2CDT/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) 19yy <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) 19yy name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/tools/2CDT/Makefile b/tools/2CDT/Makefile new file mode 100644 index 0000000..379c9c6 --- /dev/null +++ b/tools/2CDT/Makefile @@ -0,0 +1,24 @@ +# Makefile for 2cdt utility + +.PHONY: clean +CC = gcc +BIND = gcc +RM = rm + +# CFLAGS flags for C compile +# LFLAGS1 flags after output file spec, before obj file list +# LFLAGS2 flags after obj file list (libraries, etc) + +CFLAGS = -O2 -O3 -DUNIX +LFLAGS1 = +LFLAGS2 = -s + +CDT_O= src/2cdt.o src/tzxfile.o + +2cdt: $(CDT_O) + $(BIND) $(CDT_O) -o 2cdt $(LFLAGS1) $(LFLAGS2) $(LIBS) + +clean: + rm -rf src/*.o + rm -f 2cdt + rm -f 2cdt.exe diff --git a/tools/2CDT/Makefile.win b/tools/2CDT/Makefile.win new file mode 100644 index 0000000..39714b8 --- /dev/null +++ b/tools/2CDT/Makefile.win @@ -0,0 +1,33 @@ +# Project: 2cdt
+# Makefile created by Dev-C++ 4.9.9.2
+
+CPP = g++.exe
+CC = gcc.exe
+WINDRES = windres.exe
+RES =
+OBJ = src/2cdt.o src/tzxfile.o $(RES)
+LINKOBJ = src/2cdt.o src/tzxfile.o $(RES)
+LIBS = -L"C:/Dev-Cpp/lib"
+INCS = -I"C:/Dev-Cpp/include"
+CXXINCS = -I"C:/Dev-Cpp/lib/gcc/mingw32/3.4.2/include" -I"C:/Dev-Cpp/include/c++/3.4.2/backward" -I"C:/Dev-Cpp/include/c++/3.4.2/mingw32" -I"C:/Dev-Cpp/include/c++/3.4.2" -I"C:/Dev-Cpp/include"
+BIN = 2cdt.exe
+CXXFLAGS = $(CXXINCS)
+CFLAGS = $(INCS)
+RM = rm -f
+
+.PHONY: all all-before all-after clean clean-custom
+
+all: all-before 2cdt.exe all-after
+
+
+clean: clean-custom
+ ${RM} $(OBJ) $(BIN)
+
+$(BIN): $(OBJ)
+ $(CC) $(LINKOBJ) -o "2cdt.exe" $(LIBS)
+
+src/2cdt.o: src/2cdt.c
+ $(CC) -c src/2cdt.c -o src/2cdt.o $(CFLAGS)
+
+src/tzxfile.o: src/tzxfile.c
+ $(CC) -c src/tzxfile.c -o src/tzxfile.o $(CFLAGS)
diff --git a/tools/2CDT/file_id.diz b/tools/2CDT/file_id.diz new file mode 100644 index 0000000..e1ff8f5 --- /dev/null +++ b/tools/2CDT/file_id.diz @@ -0,0 +1,16 @@ +2CDT
+Copyright (C) Kevin Thacker, 2000-2002
+
+23-February-2002
+
+2CDT is a tool to create a ".CDT"
+tape image from a file in the Amstrad
+operating system cassette format.
+It can be used to test support
+for ".TZX" in Amstrad emulators
+and a starting point for a Amstrad
+".CDT" tool.
+
+Source included.
+
+makefile included for UNIX
\ No newline at end of file diff --git a/tools/2CDT/readme.txt b/tools/2CDT/readme.txt new file mode 100644 index 0000000..b10fcc2 --- /dev/null +++ b/tools/2CDT/readme.txt @@ -0,0 +1,135 @@ +2CDT
+====
+
+(c) Kevin Thacker
+
+(Original code written in May 2000, fixed and released in May 2001)
+
+2CDT is a utility to transfer files into a ".CDT" Tape-Image.
+
+A ".CDT" is a tape-image file which describes the data stored on a cassette tape.
+
+This file format is very powerful and can describe fast and custom loaders as well
+as standard operating system formats.
+
+The ".CDT" file format is identical to the ".TZX" format. The extension is used
+to differentiate between Spectrum and Amstrad Tape-Images.
+
+The ".TZX" file format was originally designed to store Spectrum tape programs,
+it's format can be found from various sources, one of these is "World Of Spectrum":
+http://www.void.jump.org/
+
+There are a number of tools which already create .TZX files, Taper, Voc2TZX and MakeTZX.
+However, these are designed to recognise Spectrum tape loaders, and so do not do well
+at creating a tape-image for Amstrad formats.
+
+This tool has been designed as a starting point for furthur Amstrad CDT tools,
+and as a program to generate reference tape-images which can be used by emulator
+authors to support this tape-image format in their programs.
+
+This tool is designed to "inject" one or more file into a ".CDT" in the format written
+by the Amstrad operating system. The tool allows the user to define the ".CDT" "recording"
+method and baud rate.
+
+2CDT [parameters] <source file> <output CDT filename>
+
+parameters:
+ -n
+ - Blank CDT file before use
+
+ Use this to create a new CDT, otherwise file
+ will be added to the end of an existing CDT.
+
+ -s <speed write>
+ - Write data with 'speed write' 0 or 1.
+
+ This is the same as typing "SPEED WRITE 0" or "SPEED WRITE 1"
+ in BASIC. Speed Write '0' is 1000 baud. Speed write '1' is
+ 2000 baud.
+
+ -b <baud rate>
+ - Specify Baud Rate.
+
+ Allows you to specify faster or slower loading.
+
+ -t <method>
+ - Method to write data into TZX (for Amstrad blocks)
+ 0 = Pure Data Block
+ 1 = Turbo Loading Data Block (default)
+
+ -m <method>
+ - Method to write CPC data
+ 0 = blocks (default)
+ 1 = headerless (Firmware function CAS READ for reading, CAS WRITE for writing )
+ To be loaded with firmware function CAS READ.
+ Not readable from BASIC. Allows entire program
+ to be stored as a single continuous block.
+ 2 = Spectrum ROM loader
+
+ -r <tape filename>
+ - Give the tape file a name. Up to 16 characters.
+
+ This allows you to give the file on tape a different
+ name to the name given on your local filesystem.
+
+ If no name is defined, the file will be unnamed.
+ This option applies to CPC 'blocks' methods only.
+
+ -X <address>
+ Define execution address (where file doesn't have header), or
+ override execution address (where file has a header)
+
+ -L <address>
+ Define load address (where file doesn't have header), or
+ override load address (where file has a header)
+
+ -T <address>
+ Define type (where file doesn't have header), or
+ override type address (where file has a header)
+ 0 = BASIC, 2 = BINARY
+
+ -p <number>
+ Set initial pause (default 3000ms)
+
+ -P
+ Add a small initial pause block for buggy emulators (e.g. old versions of Arnold)
+ Not recommended. Please use newer version of this emulator.
+
+Examples:
+
+The following in a bacth file will create the master tape for 'Stranded':
+
+REM create new CDT and put binary loader
+2cdt -n -r stranded strandlod.bin stranded.cdt
+REM add screen to existing CDT data
+2cdt -r screen loading.bin stranded.cdt
+REM add code to existing CDT data
+2cdt -r code stranded.bin stranded.cdt
+
+If you wanted to create a master tape for a game called 'Columns':
+
+REM create new CDT and put binary loader
+2cdt -n -r loader colload.bin columns.cdt
+REM put binary file as headerless
+2cdt -m 1 colcode.bin columns.cdt
+
+
+
+This archive contains a Windows command-line executable that will run under Win95, Win98,
+Win2000 and WinNT.
+
+License:
+
+ 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., 675 Mass Ave, Cambridge, MA 02139, USA.
\ No newline at end of file diff --git a/tools/2CDT/src/2cdt.c b/tools/2CDT/src/2cdt.c new file mode 100644 index 0000000..613662b --- /dev/null +++ b/tools/2CDT/src/2cdt.c @@ -0,0 +1,1243 @@ +/* + * 2CDT Copyright (c) Kevin Thacker + * + * 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. + */ + + +/* The following program is designed to create a .tzx/.cdt from a tape-file stored +on the PC */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#ifdef UNIX +#include <sys/io.h> +#else +#include <io.h> +#endif +#include "defs.h" +#include "tzxfile.h" +#include "getopt.h" +#include <ctype.h> + +#if defined(_MSC_VER) || defined(__BORLANDC__) +typedef unsigned __int64 ulong64; +typedef signed __int64 long64; +#else +typedef unsigned long long ulong64; +typedef signed long long long64; +#endif + +enum +{ + CPC_METHOD_BLOCKS = 0, + CPC_METHOD_HEADERLESS, + CPC_METHOD_SPECTRUM, +}; + +static int ExecutionAddress; +static BOOL ExecutionAddressOverride; +static int LoadAddress; +static BOOL LoadAddressOverride; +static int Type; +static BOOL TypeOverride; +static int Pause; +static BOOL BuggyEmuExtraPause; + +#define MAXFILELEN 16 + +static int BaudRate; /* baud rate to write data */ +static int TZXWriteMethod; /* method to write data into TZX file */ +static BOOL BlankBeforeUse; /* blank existing CDT file before use */ +static int CPCMethod = CPC_METHOD_BLOCKS; + +/* I am using a enum, so that I can poke data into structures without +worrying how the compiler has aligned it */ +enum +{ + CPC_TAPE_HEADER_FILENAME_BYTE0 = 0, + CPC_TAPE_HEADER_FILENAME_BYTE1, + CPC_TAPE_HEADER_FILENAME_BYTE2, + CPC_TAPE_HEADER_FILENAME_BYTE3, + CPC_TAPE_HEADER_FILENAME_BYTE4, + CPC_TAPE_HEADER_FILENAME_BYTE5, + CPC_TAPE_HEADER_FILENAME_BYTE6, + CPC_TAPE_HEADER_FILENAME_BYTE7, + CPC_TAPE_HEADER_FILENAME_BYTE8, + CPC_TAPE_HEADER_FILENAME_BYTE9, + CPC_TAPE_HEADER_FILENAME_BYTE10, + CPC_TAPE_HEADER_FILENAME_BYTE11, + CPC_TAPE_HEADER_FILENAME_BYTE12, + CPC_TAPE_HEADER_FILENAME_BYTE13, + CPC_TAPE_HEADER_FILENAME_BYTE14, + CPC_TAPE_HEADER_FILENAME_BYTE15, + CPC_TAPE_HEADER_BLOCK_NUMBER, + CPC_TAPE_HEADER_LAST_BLOCK_FLAG, + CPC_TAPE_HEADER_FILE_TYPE, + CPC_TAPE_HEADER_DATA_LENGTH_LOW, + CPC_TAPE_HEADER_DATA_LENGTH_HIGH, + CPC_TAPE_HEADER_DATA_LOCATION_LOW, + CPC_TAPE_HEADER_DATA_LOCATION_HIGH, + CPC_TAPE_HEADER_FIRST_BLOCK_FLAG, + CPC_TAPE_HEADER_DATA_LOGICAL_LENGTH_LOW, + CPC_TAPE_HEADER_DATA_LOGICAL_LENGTH_HIGH, + CPC_TAPE_HEADER_DATA_EXECUTION_ADDRESS_LOW, + CPC_TAPE_HEADER_DATA_EXECUTION_ADDRESS_HIGH, +} CPC_TAPE_HEADER_ENUM; + +/* size of header */ +#define CPC_TAPE_HEADER_SIZE 64 + +/* load a file into memory */ +BOOL Host_LoadFile(const char *Filename, unsigned char **pLocation, unsigned long *pLength) +{ + FILE *fh; + unsigned char *pData; + + *pLocation = NULL; + *pLength = 0; + + if (Filename != NULL) + { + if (strlen(Filename) != 0) + { + fh = fopen(Filename, "rb"); + + if (fh != NULL) + { + int FileSize; + +#ifdef WIN32 + int FNo; + + FNo = _fileno(fh); + FileSize = _filelength(FNo); +#else + unsigned long CurrentPosition; + CurrentPosition = ftell(fh); + fseek(fh, 0, SEEK_END); + FileSize = ftell(fh); + fseek(fh, CurrentPosition, SEEK_SET); +#endif + if (FileSize != 0) + { + pData = (unsigned char *)malloc(FileSize); + + if (pData != NULL) + { + if (fread(pData, 1, FileSize, fh) == FileSize) + { + *pLocation = pData; + *pLength = FileSize; + fclose(fh); + return TRUE; + } + + free(pData); + } + } + + fclose(fh); + } + } + } + + return FALSE; +} + + +/* calculate checksum as AMSDOS would for the first 66 bytes of a datablock */ +/* this is used to determine if a file has a AMSDOS header */ +unsigned int AMSDOS_CalculateChecksum(unsigned char *pHeader) +{ + unsigned int Checksum; + int i; + + Checksum = 0; + + for (i = 0; i < 67; i++) + { + unsigned int CheckSumByte; + + CheckSumByte = pHeader[i] & 0x0ff; + + Checksum += CheckSumByte; + } + + return Checksum; +} + +/* CRC code shamelessly taken from Pierre Guerrier's AIFF decoder! */ +#define kCRCpoly 4129 /* used for binary long division in CRC */ + +/* CRC polynomial: X^16+X^12+X^5+1 */ +unsigned int CRCupdate(unsigned int CRC, unsigned char new) +{ + unsigned int aux = CRC ^ (new << 8); + int i; + + for (i = 0; i < 8; i++) + if (aux & 0x8000) + aux = (aux <<= 1) ^ kCRCpoly; + else + aux <<= 1; + + return (aux); +} + +/* +ID : 11 - Turbo loading data block +------- + This block is very similar to the normal TAP block but with some + additional info on the timings and other important differences. + The same tape encoding is used as for the standard speed data block. + If a block should use some non-standard sync or pilot tones (for + example all sorts of protection schemes) then use the next three blocks + to describe it. + +00 2 Length of PILOT pulse [2168] +02 2 Length of SYNC First pulse [667] +04 2 Length of SYNC Second pulse [735] +06 2 Length of ZERO bit pulse [855] +08 2 Length of ONE bit pulse [1710] +0A 2 Length of PILOT tone (in PILOT pulses) [8064 Header, 3220 Data] +0C 1 Used bits in last byte (other bits should be 0) [8] + i.e. if this is 6 then the bits (x) used in last byte are: xxxxxx00 +0D 2 Pause After this block in milliseconds (ms) [1000] +0F 3 Length of following data +12 x Data; format is as for TAP (MSb first) + +- Length: [0F,10,11]+12 +*/ + +/* 2 pulses per bit, tone is composed of 1 bits */ +#define CPC_PILOT_TONE_NUM_WAVES (2048) +#define CPC_PILOT_TONE_NUM_PULSES (CPC_PILOT_TONE_NUM_WAVES*2) + +#define CPC_NOPS_PER_FRAME (19968) +#define CPC_NOPS_PER_SECOND (CPC_NOPS_PER_FRAME*50) +#define CPC_T_STATES (CPC_NOPS_PER_SECOND*4) + +#define T_STATE_CONVERSION_FACTOR (TZX_T_STATES<<8)/(CPC_T_STATES>>8) +/* pause between each block */ +#define CPC_PAUSE_AFTER_BLOCK_IN_MS 2500 +/* pause between tape header and data for block */ +#define CPC_PAUSE_AFTER_HEADER_IN_MS 14 + +void InitialiseStandardSpeedDataBlock(TZX_BLOCK *pBlock, int Pause) +{ + unsigned char *pHeader = TZX_GetBlockHeaderPtr(pBlock); + + if (pHeader != NULL) + { + /* check it is a turbo-loading data block */ + if (pHeader[0] == TZX_STANDARD_SPEED_DATA_BLOCK) + { + pHeader++; + + pHeader[0x00] = (Pause & 0x0ff); + pHeader[0x01] = (Pause >> 8); + + + } + } +} + +void CPC_InitialiseTurboLoadingDataBlock(TZX_BLOCK *pBlock, int BaudRate, int Pause) +{ + unsigned char *pHeader = TZX_GetBlockHeaderPtr(pBlock); + + if (pHeader != NULL) + { + /* check it is a turbo-loading data block */ + if (pHeader[0] == TZX_TURBO_LOADING_DATA_BLOCK) + { + int ZeroPulseLengthInMicroseconds; + long64 ZeroPulseLengthInCPCTStates; + int OnePulseLength; + int ZeroPulseLength; + + pHeader++; + /* equation from CPC firmware guide: + Average baud rate: = 1 000 000/(3*half zero length) = 333 333/Half zero length + */ + + ZeroPulseLengthInMicroseconds = 333333 / BaudRate; + ZeroPulseLengthInCPCTStates = ZeroPulseLengthInMicroseconds << 2; + + ZeroPulseLength = (ZeroPulseLengthInCPCTStates * + (T_STATE_CONVERSION_FACTOR >> 8)) >> 8; + + /* one pulse is twice the size of a zero pulse */ + OnePulseLength = ZeroPulseLength << 1; + + /* PILOT pulse on CPC is a one bit */ + pHeader[0x00] = (unsigned char)OnePulseLength; + pHeader[0x01] = (unsigned char)(OnePulseLength >> 8); + + /* SYNC on CPC is a zero bit, both sync pulses will be the same */ + pHeader[0x02] = pHeader[0x04] = (unsigned char)ZeroPulseLength; + pHeader[0x03] = pHeader[0x05] = (unsigned char)(ZeroPulseLength >> 8); + + /* write zero pulse length */ + pHeader[0x06] = (unsigned char)ZeroPulseLength; + pHeader[0x07] = (unsigned char)(ZeroPulseLength >> 8); + + /* write one pulse length */ + pHeader[0x08] = (unsigned char)OnePulseLength; + pHeader[0x09] = (unsigned char)(OnePulseLength >> 8); + + /* PILOT pulse is same as 1 Pulse */ + /* pilot tone is 2048 bits long */ + pHeader[0x0a] = CPC_PILOT_TONE_NUM_PULSES & 0x0ff; + pHeader[0x0b] = (CPC_PILOT_TONE_NUM_PULSES >> 8); + + /* the end of the block will be the trailer bytes. Say all bits are + used, although, because it doesn't contain useful data it doesn't matter */ + pHeader[0x0c] = 8; + + pHeader[0x0d] = (Pause & 0x0ff); + pHeader[0x0e] = (Pause >> 8); + + + } + } +} + + + +void WriteStandardSpeedDataBlock(TZX_FILE *pFile, unsigned char SyncPattern, unsigned char *pData, int DataSize, int Pause) +{ + TZX_BLOCK *pBlock; + unsigned char *pBlockData; + + pBlock = TZX_CreateBlock(TZX_STANDARD_SPEED_DATA_BLOCK); + InitialiseStandardSpeedDataBlock(pBlock, Pause); + + if (pBlock != NULL) + { + /* one byte for sync, one byte for checksum */ + int TZX_DataBlockSize = DataSize + 2; + + /* add block to end of file */ + TZX_AddBlockToEndOfFile(pFile, pBlock); + + /* allocate data in block */ + TZX_AddDataToBlock(pBlock, TZX_DataBlockSize); + + pBlockData = TZX_GetBlockDataPtr(pBlock); + + if (pBlockData != NULL) + { + char CheckSum = SyncPattern; + int i; + + /* write pattern */ + *pBlockData = SyncPattern; + ++pBlockData; + + for (i = 0; i < DataSize; i++) + { + char ch = *pData; + ++pData; + CheckSum ^= ch; + *pBlockData = ch; + ++pBlockData; + } + *pBlockData = CheckSum & 0x0ff; + } + } +} + + +#define CPC_DATA_CHUNK_SIZE 256 +#define CPC_DATA_BLOCK_SIZE 2048 + +/* write a block of data to a file */ +void CPC_WriteTurboLoadingDataBlock(TZX_FILE *pFile, unsigned char SyncPattern, unsigned char *pData, int DataSize, int Pause) +{ + TZX_BLOCK *pBlock; + unsigned char *pBlockData; + + int NumChunks; + int TZX_DataBlockSize; + + /* divide into complete 256 byte blocks */ + NumChunks = (DataSize + 255) >> 8; + + /* each tape block is split into 256 chunks, each chunk has a CRC */ + + /* size of all chunks, plus CRC's for each block */ + TZX_DataBlockSize = + /* size of all chunks */ + (NumChunks << 8) + + /* size of CRC's for all chunks */ + (NumChunks << 1) + + /* size of trailer in bytes */ + 4 + + /* size of sync pattern */ + 1; + + + + pBlock = TZX_CreateBlock(TZX_TURBO_LOADING_DATA_BLOCK); + CPC_InitialiseTurboLoadingDataBlock(pBlock, BaudRate, Pause); + + + if (pBlock != NULL) + { + /* add block to end of file */ + TZX_AddBlockToEndOfFile(pFile, pBlock); + + /* allocate data in block */ + TZX_AddDataToBlock(pBlock, TZX_DataBlockSize); + + pBlockData = TZX_GetBlockDataPtr(pBlock); + + if (pBlockData != NULL) + { + int i, j; + unsigned char *pDataPtr; + int DataSizeRemaining; + unsigned char *pBlockPtr; + unsigned short CRC; + + pDataPtr = pData; + DataSizeRemaining = DataSize; + pBlockPtr = pBlockData; + + /* write pattern */ + pBlockPtr[0] = SyncPattern; + pBlockPtr++; + + /* write each chunk in turn and calculate CRC */ + for (i = 0; i < NumChunks; i++) + { + /* copy data into block */ + if (DataSizeRemaining < CPC_DATA_CHUNK_SIZE) + { + /* less than CPC_DATA_CHUNK_SIZE */ + /* copy data, and fill rest with zeros */ + + /* copy less than 256 bytes */ + memcpy(pBlockPtr, pDataPtr, DataSizeRemaining); + /* fill reset of chunk with zero's */ + memset(pBlockPtr + DataSizeRemaining, 0, CPC_DATA_CHUNK_SIZE - DataSizeRemaining); + /* update source pointer */ + pDataPtr += DataSizeRemaining; + /* update size remaining - nothing */ + DataSizeRemaining = 0; + } + else + { + /* greater or equal to CPC_DATA_CHUNK_SIZE */ + /* copy CPC_DATA_CHUNK_SIZE max */ + memcpy(pBlockPtr, pDataPtr, CPC_DATA_CHUNK_SIZE); + /* update source pointer */ + pDataPtr += CPC_DATA_CHUNK_SIZE; + /* update size remaining */ + DataSizeRemaining -= CPC_DATA_CHUNK_SIZE; + } + + /* reset CRC */ + CRC = 0x0ffff; + + /* calculate CRC for block */ + for (j = 0; j < CPC_DATA_CHUNK_SIZE; j++) + { + char ch; + + ch = pBlockPtr[0]; + pBlockPtr++; + CRC = CRCupdate(CRC, ch); + } + + /* store CRC inverted */ + pBlockPtr[0] = (CRC >> 8) ^ 0x0ff; + pBlockPtr++; + pBlockPtr[0] = CRC ^ 0x0ff; + pBlockPtr++; + } + + + /* write trailer */ + memset(pBlockPtr, 0x0ff, 4); + } + } +} + +/* +ID : 14 - Pure data block +------- + This is the same as in the turbo loading data block, except that it has + no pilot or sync pulses. + +00 2 Length of ZERO bit pulse +02 2 Length of ONE bit pulse +04 1 Used bits in LAST Byte +05 2 Pause after this block in milliseconds (ms) +07 3 Length of following data +0A x Data +*/ + +void CPC_InitialisePureDataBlock(TZX_BLOCK *pBlock, int BaudRate, int Pause) +{ + unsigned char *pHeader = TZX_GetBlockHeaderPtr(pBlock); + + if (pHeader != NULL) + { + /* check it is a turbo-loading data block */ + if (pHeader[0] == TZX_PURE_DATA_BLOCK) + { + int ZeroPulseLengthInMicroseconds; + long64 ZeroPulseLengthInCPCTStates; + int OnePulseLength; + int ZeroPulseLength; + + pHeader++; + /* equation from CPC firmware guide: + Average baud rate: = 1 000 000/(3*half zero length) = 333 333/Half zero length + */ + + ZeroPulseLengthInMicroseconds = 333333 / BaudRate; + ZeroPulseLengthInCPCTStates = ZeroPulseLengthInMicroseconds << 2; + + ZeroPulseLength = (ZeroPulseLengthInCPCTStates * + (T_STATE_CONVERSION_FACTOR >> 8)) >> 8; + + /* one pulse is twice the size of a zero pulse */ + OnePulseLength = ZeroPulseLength << 1; + /* write zero pulse length */ + pHeader[0x00] = ZeroPulseLength; + pHeader[0x01] = ZeroPulseLength >> 8; + + /* write one pulse length */ + pHeader[0x02] = OnePulseLength; + pHeader[0x03] = OnePulseLength >> 8; + + /* the end of the block will be the trailer bytes. Say all bits are + used, although, because it doesn't contain useful data it doesn't matter */ + pHeader[0x04] = 8; + + /* write pause */ + pHeader[0x05] = (Pause & 0x0ff); + pHeader[0x06] = (Pause >> 8) & 0x0ff; + + + } + } +} + + +/* the following is for a bitstream */ +unsigned char *pData; +unsigned long ByteCount; +unsigned long BitCount; + +/* initialise bit stream with buffer to write data to */ +void BitStream_Initialise(unsigned char *pBuffer) +{ + pData = pBuffer; + ByteCount = 0; + BitCount = 0; +} + +/* write bit to stream */ +void BitStream_WriteBit(int Bit) +{ + unsigned char Data; + + /* get current data written */ + Data = pData[ByteCount]; + Data &= ~(1 << (7 - BitCount)); + Data |= (Bit << (7 - BitCount)); + pData[ByteCount] = Data; + + /* increment bit count */ + BitCount++; + /* if we overrun 8-bits, then bit 3 will be set, add this on */ + ByteCount += (BitCount >> 3); + /* mask off bit count */ + BitCount &= 0x07; +} + +/* write byte to stream */ +void BitStream_WriteByte(unsigned char Byte) +{ + int b; + int Bit; + unsigned char LocalByte; + + LocalByte = Byte; + + for (b = 0; b < 8; b++) + { + Bit = LocalByte & 0x080; + Bit = Bit >> 7; + BitStream_WriteBit(Bit); + LocalByte = LocalByte << 1; + } +} + + + +/* write a block of data to a file */ +void CPC_WritePureDataBlock(TZX_FILE *pFile, unsigned char SyncPattern, unsigned char *pData, int DataSize, int Pause) +{ + TZX_BLOCK *pBlock; + unsigned char *pBlockData; + + int NumChunks; + int TZX_DataBlockSize; + + /* divide into complete 256 byte blocks */ + NumChunks = (DataSize + 255) >> 8; + + /* each tape block is split into 256 chunks, each chunk has a CRC */ + + /* size of all chunks, plus CRC's for each block */ + TZX_DataBlockSize = + /* size of all chunks */ + (NumChunks << 8) + + /* size of CRC's for all chunks */ + (NumChunks << 1) + + /* size of trailer in bytes */ + 4 + + /* size of sync pattern */ + 1; + + TZX_DataBlockSize += + /* pilot tone - CPC_PILOT_TONE_NUM_WAVES 1 bit's, a zero bit then data as before ... */ + ((CPC_PILOT_TONE_NUM_WAVES + 1) + 7) >> 3; + + pBlock = TZX_CreateBlock(TZX_PURE_DATA_BLOCK); + CPC_InitialisePureDataBlock(pBlock, BaudRate, Pause); + + + if (pBlock != NULL) + { + /* add block to end of file */ + TZX_AddBlockToEndOfFile(pFile, pBlock); + + /* allocate data in block */ + TZX_AddDataToBlock(pBlock, TZX_DataBlockSize); + + pBlockData = TZX_GetBlockDataPtr(pBlock); + + if (pBlockData != NULL) + { + int i, j; + unsigned char *pDataPtr; + int DataSizeRemaining; + unsigned char *pBlockPtr; + unsigned short CRC; + + pDataPtr = pData; + DataSizeRemaining = DataSize; + pBlockPtr = pBlockData; + + BitStream_Initialise(pBlockPtr); + + /* write leader */ + for (i = 0; i < CPC_PILOT_TONE_NUM_WAVES; i++) + BitStream_WriteBit(1); + + BitStream_WriteBit(0); + + + BitStream_WriteByte(SyncPattern); + + /* write each chunk in turn and calculate CRC */ + for (i = 0; i < NumChunks; i++) + { + int BlockSizeToWrite; + + /* copy data into block */ + if (DataSizeRemaining < CPC_DATA_CHUNK_SIZE) + BlockSizeToWrite = DataSizeRemaining; + else + BlockSizeToWrite = CPC_DATA_CHUNK_SIZE; + + CRC = 0x0ffff; + + for (j = 0; j < BlockSizeToWrite; j++) + { + char ch; + + /* get byte */ + ch = pDataPtr[0]; + pDataPtr++; + /* update CRC */ + CRC = CRCupdate(CRC, ch); + /* write byte to stream */ + BitStream_WriteByte(ch); + } + + if (BlockSizeToWrite != CPC_DATA_CHUNK_SIZE) + { + /* write padding zero's */ + for (j = 0; j < (CPC_DATA_CHUNK_SIZE - BlockSizeToWrite); j++) + { + char ch; + + ch = 0; + /* update CRC */ + CRC = CRCupdate(CRC, ch); + /* write byte to stream */ + BitStream_WriteByte(ch); + } + } + + DataSizeRemaining -= BlockSizeToWrite; + + CRC = CRC ^ 0x0ffff; + + BitStream_WriteByte((CRC >> 8)); + BitStream_WriteByte(CRC); + } + + /* write trailer */ + for (i = 0; i < 32; i++) + BitStream_WriteBit(1); + } + } +} + +/* write a data block in format specified */ +void CPC_WriteDataBlock(TZX_FILE *pFile, unsigned char SyncByte, unsigned char *pData, unsigned long DataSize, int Pause) +{ + switch (TZXWriteMethod) + { + case TZX_TURBO_LOADING_DATA_BLOCK: + { + CPC_WriteTurboLoadingDataBlock(pFile, SyncByte, pData, DataSize, Pause); + } + break; + + case TZX_PURE_DATA_BLOCK: + { + /* write header */ + CPC_WritePureDataBlock(pFile, SyncByte, pData, DataSize, Pause); + } + break; + } +} + +#define UTILITY_NAME "2CDT" + +void DisplayInfo() +{ + printf("%s will transfer files into a .CDT/.TZX tape image, in Amstrad CPC/CPC+\r\n", UTILITY_NAME); + printf("KC Compact form.\r\n\r\n"); + printf("Usage: %s [arguments] <input filename> <.cdt image>\r\n\r\n", UTILITY_NAME); + printf("-n - Blank CDT file before use\n"); + printf("-b <number> - Specify Baud rate (default 2000)\n"); + printf("-s <0 or 1> - Specify 'Speed Write'.\n"); + printf(" 0 = 1000 baud, 1 = 2000 baud (default)\n"); + printf("-t <method> - TZX Block Write Method.\n"); + printf(" 0 = Pure Data, 1 = Turbo Loading (default)\n"); + printf("-m <method> - Data method\n"); + printf(" 0 = blocks (default)\n"); + printf(" 1 = headerless (Firmware function: CAS READ - &BCA1) \n"); + printf(" 2 = spectrum \n"); + printf(" 3 = Two blocks. First block of 2K, second block has remainder\n"); + printf(" 4 = Two blocks. First block of 1 byte, second block has remainder\n"); + printf("-X <number> = Define or override execution address (default is &1000 if no header)\r\n"); + printf("-L <number> = Define or override load address (default is &1000 if no header)\r\n"); + printf("-F <number> = Define or override file type (0=BASIC, 2=Binary (default if no header)) etc. Applies to Data method 0\r\n"); + printf("-p <number> = Set initial pause in milliseconds (default 3000ms)\r\n"); + printf("-P = Add a 1ms pause for buggy emulators that ignore first block\r\n"); + printf("-r <tape filename>\n"); + printf(" - Add <input filename> as <tape filename> to CDT (rename file)\n"); +} + +extern char *optarg; + +int ReadNumberParameter(char *param) +{ + int Length = strlen(param); + BOOL bIsHex = FALSE; + int Offset = 0; + unsigned long Value = 0; + char ch; + + if (Length == 0) + return 0; + + /* check for common prefixs for hex numbers */ + if ((Length > 1) && ((param[0] == '&') || (param[0] == '$'))) + { + Offset = 1; + bIsHex = TRUE; + } + else if ((Length > 2) && (param[0] == '0') && ((param[1] == 'x') || (param[1] == 'X'))) + { + Offset = 2; + bIsHex = TRUE; + } + + if (!bIsHex) + return atoi(param); + + ch = param[Offset]; + while (ch != '\0') + { + Value = Value << 4; + if ((ch >= '0') && (ch <= '9')) + Value = Value | (ch - '0'); + else if ((ch >= 'a') && (ch <= 'f')) + Value = Value | ((ch - 'a') + 10); + else if ((ch >= 'A') && (ch <= 'F')) + Value = Value | ((ch - 'A') + 10); + Offset++; + ch = param[Offset]; + } + + return Value; +} + +int main(int argc, char *argv[]) +{ + unsigned char *pTapeFilename = NULL; + + if (argc == 1) + DisplayInfo(); + else + { + TZX_FILE *pTZXFile; + unsigned char *pSourceFilename; + unsigned char *pDestFilename; + unsigned char *pData; + unsigned long DataLength; + int c; + + /* initialise defaults */ + BaudRate = 2000; + Pause = 3000; + Type = 2; + TypeOverride = FALSE; + LoadAddressOverride = FALSE; + TZXWriteMethod = TZX_TURBO_LOADING_DATA_BLOCK; + BlankBeforeUse = FALSE; + ExecutionAddress = LoadAddress = 0x01000; + ExecutionAddressOverride = FALSE; + LoadAddressOverride = FALSE; + + printf("-n - Blank CDT file before use\n"); + printf("-b <number> - Specify Baud rate (default 2000)\n"); + printf("-s <0 or 1> - Specify 'Speed Write'.\n"); + printf(" 0 = 1000 baud, 1 = 2000 baud (default)\n"); + printf("-t <method> - TZX Block Write Method.\n"); + printf(" 0 = Pure Data, 1 = Turbo Loading (default)\n"); + printf("-m <method> - Data method\n"); + printf(" 0 = blocks (default)\n"); + printf(" 1 = headerless (Firmware function: CAS READ - &BCA1) \n"); + printf(" 2 = spectrum \n"); + printf(" 3 = Two blocks. First block of 2K, second block has remainder\n"); + printf(" 4 = Two blocks. First block of 1 byte, second block has remainder\n"); + printf("-X <number> = Define or override execution address (default is &1000 if no header)\r\n"); + printf("-L <number> = Define or override load address (default is &1000 if no header)\r\n"); + printf("-F <number> = Define or override file type (0=BASIC, 2=Binary (default if no header)) etc. Applies to Data method 0\r\n"); + printf("-p <number> = Set initial pause in milliseconds (default 3000ms)\r\n"); + printf("-P = Add a 1ms pause for buggy emulators that ignore first block\r\n"); + printf("-r <tape filename>\n"); + printf(" - Add <input filename> as <tape filename> to CDT (rename file)\n"); + + + do + { + c = getopt(argc, argv, "r:nb:p:m:t:F:L:s:X:p:P"); + + switch (c) + { + case 'm': + { + int nMethod = atoi(optarg); + CPCMethod = nMethod; + + } + break; + + case 'p': + { + Pause = atoi(optarg); + if (Pause < 0) + Pause = 0; + } + break; + + case 'r': + { + pTapeFilename = optarg; + } + break; + + case 'n': + { + BlankBeforeUse = TRUE; + } + break; + + case 'b': + { + int Baud = atoi(optarg); + if ((Baud > 0) && (Baud < 6000)) + BaudRate = Baud; + } + break; + + case 'X': + { + ExecutionAddress = ReadNumberParameter(optarg) & 0x0ffff; + ExecutionAddressOverride = TRUE; + } + break; + + case 'L': + { + LoadAddress = ReadNumberParameter(optarg) & 0x0ffff; + LoadAddressOverride = TRUE; + } + break; + + case 's': + { + int SpeedWrite = atoi(optarg); + if (SpeedWrite == 1) + BaudRate = 2000; + else + BaudRate = 1000; + } + break; + + case 'F': + { + Type = atoi(optarg) & 0x0ff; + TypeOverride = TRUE; + + + } + break; + + + case 'P': + { + BuggyEmuExtraPause = TRUE; + + } + break; + + case 't': + { + int nMethod = atoi(optarg); + if (nMethod == 0) + TZXWriteMethod = TZX_PURE_DATA_BLOCK; + else if (nMethod == 1) + TZXWriteMethod = TZX_TURBO_LOADING_DATA_BLOCK; + else if (nMethod == 2) + TZXWriteMethod = TZX_STANDARD_SPEED_DATA_BLOCK; + } + break; + + default: + { + } + break; + } + } + while (c != -1); + + if ((argc - optind) == 0) + { + printf("No source file or destination file have been specified!\n"); + exit(1); + } + + if ((argc - optind) == 1) + { + printf("No destination file has been specified\n"); + exit(1); + } + + pSourceFilename = argv[optind]; + pDestFilename = argv[optind + 1]; + + /* create TZX file */ + pTZXFile = TZX_CreateFile(TZX_VERSION_MAJOR, TZX_VERSION_MINOR); + + if (pTZXFile != NULL) + { + int nFile; + + + if (BlankBeforeUse) + { + TZX_BLOCK *pBlock; + + /* if buggy emu, add an extra small pause */ + if (BuggyEmuExtraPause) + { + pBlock = TZX_CreateBlock(TZX_PAUSE_BLOCK); + + if (pBlock != NULL) + { + /* add a 1ms initial pause for buggy emus */ + TZX_SetupPauseBlock(pBlock, 1); + TZX_AddBlockToEndOfFile(pTZXFile, pBlock); + } + } + + + /* correct pause */ + pBlock = TZX_CreateBlock(TZX_PAUSE_BLOCK); + + if (pBlock != NULL) + { + TZX_SetupPauseBlock(pBlock, Pause); + TZX_AddBlockToEndOfFile(pTZXFile, pBlock); + } + } + + + if (Host_LoadFile(pSourceFilename, &pData, &DataLength)) + { + int FileOffset; + int FileLengthRemaining; + int TapeBlockSize; + BOOL FirstBlock, LastBlock; + int BlockIndex; + unsigned short BlockLocation; + + /* header for tape file */ + unsigned char TapeHeader[CPC_TAPE_HEADER_SIZE]; + + /* calculate checksum from loaded file */ + unsigned short CalculatedChecksum = (unsigned short)AMSDOS_CalculateChecksum(pData); + + /* get stored checksum */ + unsigned short StoredChecksum = + (pData[67] & 0x0ff) | + (pData[68] & 0x0ff) << 8; + + FileOffset = 0; + FileLengthRemaining = DataLength; + BlockIndex = 1; + FirstBlock = TRUE; + + /* insert a pause block - 1 second, this is added onto the end of the previous block */ + /* if (BlankBeforeUse == FALSE) + { + TZX_BLOCK *pBlock; + + pBlock = TZX_CreateBlock(TZX_PAUSE_BLOCK); + + if (pBlock!=NULL) + { + TZX_SetupPauseBlock(pBlock, 2000); + TZX_AddBlockToEndOfFile(pTZXFile,pBlock); + } + } + */ + + + /* clear tape header */ + memset(TapeHeader, 0, CPC_TAPE_HEADER_SIZE); + + /* checksum's match? */ + if (CalculatedChecksum == StoredChecksum) + { + /* copy file type */ + TapeHeader[CPC_TAPE_HEADER_FILE_TYPE] = pData[CPC_TAPE_HEADER_FILE_TYPE]; + /* copy execution address */ + TapeHeader[CPC_TAPE_HEADER_DATA_EXECUTION_ADDRESS_LOW] = pData[CPC_TAPE_HEADER_DATA_EXECUTION_ADDRESS_LOW]; + TapeHeader[CPC_TAPE_HEADER_DATA_EXECUTION_ADDRESS_HIGH] = pData[CPC_TAPE_HEADER_DATA_EXECUTION_ADDRESS_HIGH]; + /* copy data location */ + TapeHeader[CPC_TAPE_HEADER_DATA_LOCATION_LOW] = pData[CPC_TAPE_HEADER_DATA_LOCATION_LOW]; + TapeHeader[CPC_TAPE_HEADER_DATA_LOCATION_HIGH] = pData[CPC_TAPE_HEADER_DATA_LOCATION_HIGH]; + + FileOffset += 128; + FileLengthRemaining -= 128; + + /* override execution address? */ + if (ExecutionAddressOverride) + { + TapeHeader[CPC_TAPE_HEADER_DATA_EXECUTION_ADDRESS_LOW] = ExecutionAddress & 0xFF; + TapeHeader[CPC_TAPE_HEADER_DATA_EXECUTION_ADDRESS_HIGH] = (ExecutionAddress >> 8) & 0xFF; + } + + /* override type? */ + if (TypeOverride) + TapeHeader[CPC_TAPE_HEADER_FILE_TYPE] = Type; + + /* override load address? */ + if (LoadAddressOverride) + { + TapeHeader[CPC_TAPE_HEADER_DATA_LOCATION_LOW] = LoadAddress & 0xFF; + TapeHeader[CPC_TAPE_HEADER_DATA_LOCATION_HIGH] = (LoadAddress >> 8) & 0xFF; + } + + } + else + { + /* set type */ + TapeHeader[CPC_TAPE_HEADER_FILE_TYPE] = Type; + + /* set execution address */ + TapeHeader[CPC_TAPE_HEADER_DATA_EXECUTION_ADDRESS_LOW] = ExecutionAddress & 0xFF; + TapeHeader[CPC_TAPE_HEADER_DATA_EXECUTION_ADDRESS_HIGH] = (ExecutionAddress >> 8) & 0xFF; + + /* set load address */ + TapeHeader[CPC_TAPE_HEADER_DATA_LOCATION_LOW] = LoadAddress & 0xFF; + TapeHeader[CPC_TAPE_HEADER_DATA_LOCATION_HIGH] = (LoadAddress >> 8) & 0xFF; + } + + if (pTapeFilename != NULL) + { + int i; + int nLength = strlen(pTapeFilename); + if (nLength > 16) + nLength = 16; + for (i = 0; i < nLength; i++) + TapeHeader[i] = toupper(pTapeFilename[i]); + } + TapeHeader[CPC_TAPE_HEADER_DATA_LOGICAL_LENGTH_LOW] = (FileLengthRemaining & 0x0ff); + TapeHeader[CPC_TAPE_HEADER_DATA_LOGICAL_LENGTH_HIGH] = (FileLengthRemaining >> 8) & 0x0ff; + + + BlockLocation = TapeHeader[CPC_TAPE_HEADER_DATA_LOCATION_LOW] | + (TapeHeader[CPC_TAPE_HEADER_DATA_LOCATION_HIGH] << 8); + + if (CPCMethod == CPC_METHOD_SPECTRUM) + { + /* write data into block */ + WriteStandardSpeedDataBlock(pTZXFile, 0x0ff, &pData[FileOffset], FileLengthRemaining, 1000); + } + else + { + do + { + unsigned char Flag; + /* + CPC can't handle this one + if (CPCMethod == CPC_METHOD_2BLOCKS) + { + if (FirstBlock) + { + TapeBlockSize=CPC_DATA_BLOCK_SIZE; + LastBlock = FALSE; + } + else + { + TapeBlockSize = FileLengthRemaining; + LastBlock = TRUE; + } + } + else + */ + if (CPCMethod == CPC_METHOD_BLOCKS) + { + /* calc size of tape data block */ + if (FileLengthRemaining > CPC_DATA_BLOCK_SIZE) + { + TapeBlockSize = CPC_DATA_BLOCK_SIZE; + LastBlock = FALSE; + } + else + { + TapeBlockSize = FileLengthRemaining; + LastBlock = TRUE; + } + } + else if (CPCMethod == CPC_METHOD_HEADERLESS) + TapeBlockSize = FileLengthRemaining; + + + + /**** HEADER ****/ + /* SETUP TAPE RELATED DATA */ + /* block index */ + TapeHeader[CPC_TAPE_HEADER_BLOCK_NUMBER] = BlockIndex; + + /* first block? */ + if (FirstBlock) + { + FirstBlock = FALSE; + + Flag = 0x0ff; + } + else + Flag = 0; + + TapeHeader[CPC_TAPE_HEADER_FIRST_BLOCK_FLAG] = Flag; + + /* last block? */ + if (LastBlock) + Flag = 0x0ff; + else + Flag = 0; + + TapeHeader[CPC_TAPE_HEADER_LAST_BLOCK_FLAG] = Flag; + + /* size of data following */ + TapeHeader[CPC_TAPE_HEADER_DATA_LENGTH_LOW] = (unsigned char)TapeBlockSize; + TapeHeader[CPC_TAPE_HEADER_DATA_LENGTH_HIGH] = (unsigned char)(TapeBlockSize >> 8); + + /* location of block */ + TapeHeader[CPC_TAPE_HEADER_DATA_LOCATION_LOW] = (unsigned char)BlockLocation; + TapeHeader[CPC_TAPE_HEADER_DATA_LOCATION_HIGH] = (unsigned char)(BlockLocation >> 8); + + /* don't write a header if headerless */ + if (CPCMethod != CPC_METHOD_HEADERLESS) + { + /* write header */ + CPC_WriteDataBlock(pTZXFile, 0x02c, TapeHeader, CPC_TAPE_HEADER_SIZE, 10); + } + + /* write data into block */ + CPC_WriteDataBlock(pTZXFile, 0x016, &pData[FileOffset], TapeBlockSize, CPC_PAUSE_AFTER_BLOCK_IN_MS); + + BlockLocation += TapeBlockSize; + BlockIndex++; + FileOffset += TapeBlockSize; + FileLengthRemaining -= TapeBlockSize; + } + while (FileLengthRemaining != 0); + } + + free(pData); + } + + /* write file */ + if (BlankBeforeUse) + TZX_WriteFile(pTZXFile, pDestFilename); + else + TZX_AppendFile(pTZXFile, pDestFilename); + + /* free it */ + TZX_FreeFile(pTZXFile); + } + else + { + printf("Failed to open output file!\r\n"); + exit(1); + } + + } + + exit(0); + + return 0; +} diff --git a/tools/2CDT/src/defs.h b/tools/2CDT/src/defs.h new file mode 100644 index 0000000..f042dde --- /dev/null +++ b/tools/2CDT/src/defs.h @@ -0,0 +1,36 @@ +/* + * 2CDT Copyright (c) Kevin Thacker + * + * 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. + */ +#ifndef __DEFS_HEADER_INCLUDED__ +#define __DEFS_HEADER_INCLUDED__ + + +typedef int BOOL; + +#ifndef TRUE +#define TRUE (1==1) +#endif + +#ifndef FALSE +#define FALSE (1==0) +#endif + +#ifndef NULL +#define NULL 0 +#endif + +#endif diff --git a/tools/2CDT/src/tzxfile.c b/tools/2CDT/src/tzxfile.c new file mode 100644 index 0000000..3f550b0 --- /dev/null +++ b/tools/2CDT/src/tzxfile.c @@ -0,0 +1,445 @@ +/*
+ * 2CDT Copyright (c) Kevin Thacker
+ *
+ * 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.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <memory.h>
+/* TZX file support */
+#include "tzxfile.h"
+/* header for TZX file */
+const unsigned char *TZX_FileHeader = (const unsigned char *)"ZXTape!\x1a";
+
+/* the internal format for the TZX is a link-list system.
+Blocks can be edited. A new file can be written with the new data */
+
+
+
+/**************************************/
+/* insert block after block specified */
+void TZX_InsertBlockAfter(TZX_FILE *pFile,TZX_BLOCK *pBlock, TZX_BLOCK *pPrev)
+{
+ /* setup block pointers */
+ pBlock->pPrev = pPrev;
+ pBlock->pNext = pPrev->pNext;
+
+ /* setup pointers for block before */
+ pPrev->pNext = pBlock;
+}
+
+/***************************************/
+/* insert block before block specified */
+void TZX_InsertBlockBefore(TZX_FILE *pFile, TZX_BLOCK *pBlock, TZX_BLOCK *pNext)
+{
+ /* setup block pointers */
+ pBlock->pNext = pNext;
+ pBlock->pPrev = pNext->pPrev;
+
+ /* setup pointers for block before */
+ pNext->pPrev = pBlock;
+
+ /* is block we are inserting after the first in the list? */
+ if (pFile->pFirstBlock == pNext)
+ {
+ /* yes. New block is now the start of the list */
+ pFile->pFirstBlock = pBlock;
+ }
+}
+
+
+/*****************************************/
+/* insert a block at the end of the file */
+void TZX_AddBlockToEndOfFile(TZX_FILE *pFile, TZX_BLOCK *pBlock)
+{
+ /* blocks in file? */
+ if (pFile->pFirstBlock==NULL)
+ {
+ /* no */
+ pFile->pFirstBlock = pBlock;
+ }
+ else
+ {
+ /* yes */
+ TZX_BLOCK *pCurrentBlock;
+
+ /* search for last block in list */
+ pCurrentBlock = pFile->pFirstBlock;
+
+ while (pCurrentBlock->pNext!=NULL)
+ pCurrentBlock = pCurrentBlock->pNext;
+
+ TZX_InsertBlockAfter(pFile,pBlock, pCurrentBlock);
+ }
+}
+
+/********************************/
+/* detach block from block list */
+void TZX_DetachBlock(TZX_FILE *pFile,TZX_BLOCK *pBlock)
+{
+ /* block before this block? */
+ if (pBlock->pPrev!=NULL)
+ {
+ /* yes */
+ pBlock->pPrev->pNext = pBlock->pNext;
+ }
+ else
+ {
+ /* no, this block is first in list */
+ pFile->pFirstBlock = pBlock->pNext;
+ }
+
+ /* block after this block? */
+ if (pBlock->pNext!=NULL)
+ {
+ /* yes */
+ pBlock->pNext->pPrev = pBlock->pPrev;
+ }
+}
+
+/************************/
+/* free data in a block */
+void TZX_FreeBlock(TZX_BLOCK *pBlock)
+{
+ /* free it */
+ free(pBlock);
+}
+
+
+
+/******************************/
+/* create a internal TZX file */
+
+TZX_FILE *TZX_CreateFile(unsigned char VersionMajor, unsigned char VersionMinor)
+{
+ TZX_FILE *pTZXFile;
+
+ /* alloc the header */
+ pTZXFile = malloc(sizeof(TZX_FILE));
+
+ if (pTZXFile!=NULL)
+ {
+ /* set version */
+ pTZXFile->VersionMajor = VersionMajor;
+ pTZXFile->VersionMinor = VersionMinor;
+
+ /* initialise block linked list */
+ pTZXFile->pFirstBlock = NULL;
+ }
+
+ return pTZXFile;
+}
+
+/*******************/
+/* free a TZX file */
+
+void TZX_FreeFile(TZX_FILE *pFile)
+{
+ TZX_BLOCK *pBlock;
+
+ pBlock = pFile->pFirstBlock;
+
+ while (pBlock!=NULL)
+ {
+ TZX_BLOCK *pNextBlock = pBlock->pNext;
+
+ /* remove block from list */
+ TZX_DetachBlock(pFile, pBlock);
+
+ /* free TZX block */
+ TZX_FreeBlock(pBlock);
+
+ pBlock = pNextBlock;
+ }
+
+ /* free TZX file header */
+ free(pFile);
+}
+
+/*****************************************************************************************************/
+/* write a TZX file */
+
+void TZX_WriteBlocks(TZX_FILE *pTZXFile, FILE *fh)
+{
+ TZX_BLOCK *pBlock;
+
+ /* get pointer to first block */
+ pBlock = pTZXFile->pFirstBlock;
+
+ /* write each block in turn to file */
+ while (pBlock!=NULL)
+ {
+ if (pBlock->pBlockHeader!=NULL)
+ {
+ BOOL BlockHasData;
+ int BlockHeaderSize;
+ unsigned char BlockID;
+
+ /* get block ID */
+ BlockID = pBlock->pBlockHeader[0];
+ /* get size of header */
+ BlockHeaderSize = TZX_GetBlockHeaderSize(BlockID);
+ /* does block have additional data ? */
+ BlockHasData = TZX_BlockHasData(BlockID);
+
+ if ((!BlockHasData) || ((BlockHasData) && (pBlock->pBlockData!=NULL)))
+ {
+ /* block requires data and has data, or block doesn't require data */
+
+ /* write the header */
+ fwrite(pBlock->pBlockHeader, BlockHeaderSize, sizeof(unsigned char), fh);
+
+ if (pBlock->pBlockData!=NULL)
+ {
+ /* write the data */
+
+ fwrite(pBlock->pBlockData, pBlock->DataBlockSize, sizeof(unsigned char), fh);
+ }
+ }
+ }
+
+ /* next block */
+ pBlock = pBlock->pNext;
+ }
+}
+
+
+void TZX_AppendFile(TZX_FILE *pTZXFile, unsigned char *pFilename)
+{
+ FILE *fh;
+
+ /* open TZX file */
+ fh = fopen((const char *)pFilename,"r+b");
+
+ if (fh!=NULL)
+ {
+ TZX_BLOCK *pBlock;
+
+ fseek(fh, 0, SEEK_END);
+
+ TZX_WriteBlocks(pTZXFile, fh);
+
+ /* close TZX file */
+ fclose(fh);
+ }
+}
+
+
+void TZX_WriteFile(TZX_FILE *pTZXFile, unsigned char *pFilename)
+{
+ FILE *fh;
+
+ /* open TZX file */
+ fh = fopen((const char *)pFilename,"wb");
+
+ if (fh!=NULL)
+ {
+ TZX_BLOCK *pBlock;
+
+ /* write header */
+ fwrite(TZX_FileHeader, 8, sizeof(unsigned char), fh);
+ /* write version numbers */
+ fwrite(&pTZXFile->VersionMajor, 1, sizeof(unsigned char), fh);
+ fwrite(&pTZXFile->VersionMinor, 1, sizeof(unsigned char), fh);
+
+ TZX_WriteBlocks(pTZXFile, fh);
+
+ /* close TZX file */
+ fclose(fh);
+ }
+}
+
+void TZX_SetupPauseBlock(TZX_BLOCK *pBlock,unsigned long PauseInMilliseconds)
+{
+ unsigned char *pHeader = TZX_GetBlockHeaderPtr(pBlock);
+
+ if (pHeader!=NULL)
+ {
+ if (pHeader[0] == TZX_PAUSE_BLOCK)
+ {
+ pHeader[1] = (unsigned char)PauseInMilliseconds;
+ pHeader[2] = (unsigned char)(PauseInMilliseconds>>8);
+ }
+ }
+}
+
+/*****************************************************************************************************/
+
+/* given a TZX block ID, this returns the size of the header */
+int TZX_GetBlockHeaderSize(unsigned char ID)
+{
+ switch (ID)
+ {
+ case TZX_STANDARD_SPEED_DATA_BLOCK:
+ return 4+1;
+ case TZX_TURBO_LOADING_DATA_BLOCK:
+ return 18+1;
+
+ case TZX_PAUSE_BLOCK:
+ return 2+1;
+ case TZX_PURE_DATA_BLOCK:
+ return 0x0a + 1;
+ case TZX_DIRECT_RECORDING_BLOCK:
+ return 0x08 + 1;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+/*****************************************************************************************************/
+/* return TRUE if the block has additional data, false if not */
+BOOL TZX_BlockHasData(unsigned char ID)
+{
+ switch (ID)
+ {
+ case TZX_STANDARD_SPEED_DATA_BLOCK:
+ case TZX_TURBO_LOADING_DATA_BLOCK:
+ case TZX_PURE_DATA_BLOCK:
+ case TZX_DIRECT_RECORDING_BLOCK:
+ return TRUE;
+
+ case TZX_PAUSE_BLOCK:
+ return FALSE;
+
+ default:
+ break;
+ }
+
+ return FALSE;
+
+}
+
+/*****************************************************************************************************/
+/* set block size */
+void TZX_SetBlockSizeInHeader(TZX_BLOCK *pBlock, unsigned long Size)
+{
+ unsigned char ID = pBlock->pBlockHeader[0];
+ unsigned char *pBlockData;
+
+ pBlockData = &pBlock->pBlockHeader[1];
+
+ switch (ID)
+ {
+ case TZX_STANDARD_SPEED_DATA_BLOCK:
+ {
+ pBlockData[2] = (unsigned char)Size;
+ pBlockData[3] = (unsigned char)(Size>>8);
+
+ }
+ break;
+
+ case TZX_TURBO_LOADING_DATA_BLOCK:
+ {
+ pBlockData[0x0f] = (unsigned char)Size;
+ pBlockData[0x010] = (unsigned char)(Size>>8);
+ pBlockData[0x011] = (unsigned char)(Size>>16);
+
+ }
+ break;
+
+ case TZX_PURE_DATA_BLOCK:
+ {
+ pBlockData[0x07] = (unsigned char)Size;
+ pBlockData[0x08] = (unsigned char)(Size>>8);
+ pBlockData[0x09] = (unsigned char)(Size>>16);
+ }
+ break;
+
+ case TZX_DIRECT_RECORDING_BLOCK:
+ {
+ pBlockData[0x05] = (unsigned char)Size;
+ pBlockData[0x06] = (unsigned char)(Size>>8);
+ pBlockData[0x07] = (unsigned char)(Size>>16);
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+
+/*****************************************************************************************************/
+/* create a block of the specified ID in the TZX image file */
+TZX_BLOCK *TZX_CreateBlock(unsigned char ID)
+{
+ TZX_BLOCK *pBlock;
+
+ pBlock = malloc(sizeof(TZX_BLOCK));
+
+ if (pBlock!=NULL)
+ {
+ int BlockHeaderSize;
+
+ /* reset block data */
+ memset(pBlock, 0, sizeof(TZX_BLOCK));
+
+ BlockHeaderSize = TZX_GetBlockHeaderSize(ID);
+
+ pBlock->pBlockHeader = malloc(BlockHeaderSize);
+
+ if (pBlock->pBlockHeader!=NULL)
+ {
+ pBlock->pBlockHeader[0] = ID;
+ }
+ }
+
+ return pBlock;
+}
+
+/*****************************************************************************************************/
+/* create a block of the specified ID in the TZX image file */
+void TZX_AddDataToBlock(TZX_BLOCK *pBlock, int DataSize)
+{
+ /* only add data if a header exists */
+ if (pBlock->pBlockHeader!=NULL)
+ {
+ /* get block id */
+ unsigned char BlockID = pBlock->pBlockHeader[0];
+
+ /* does this block ID have additional data? */
+ if (TZX_BlockHasData(BlockID))
+ {
+ /* allocate memory for the additional data */
+ pBlock->pBlockData = malloc(DataSize);
+
+ if (pBlock->pBlockData!=NULL)
+ {
+ pBlock->DataBlockSize = DataSize;
+
+ /* set size in TZX header */
+ TZX_SetBlockSizeInHeader(pBlock, DataSize);
+ }
+ }
+ }
+}
+
+/*****************************************************************************************************/
+/* get pointer to TZX data block */
+unsigned char *TZX_GetBlockDataPtr(TZX_BLOCK *pBlock)
+{
+ return pBlock->pBlockData;
+}
+
+/*****************************************************************************************************/
+/* get pointer to TZX data block */
+unsigned char *TZX_GetBlockHeaderPtr(TZX_BLOCK *pBlock)
+{
+ return pBlock->pBlockHeader;
+}
diff --git a/tools/2CDT/src/tzxfile.h b/tools/2CDT/src/tzxfile.h new file mode 100644 index 0000000..397f5d1 --- /dev/null +++ b/tools/2CDT/src/tzxfile.h @@ -0,0 +1,79 @@ +/*
+ * 2CDT Copyright (c) Kevin Thacker
+ *
+ * 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.
+ */
+
+#ifndef __TZX_FILE_HEADER__
+#define __TZX_FILE_HEADER__
+
+#include "defs.h"
+
+#define TZX_VERSION_MAJOR 1
+#define TZX_VERSION_MINOR 10
+
+#define TZX_STANDARD_SPEED_DATA_BLOCK 0x010
+#define TZX_TURBO_LOADING_DATA_BLOCK 0x011
+#define TZX_PAUSE_BLOCK 0x020
+#define TZX_PURE_DATA_BLOCK 0x014
+#define TZX_DIRECT_RECORDING_BLOCK 0x015
+
+#define TZX_T_STATES 3500000
+
+
+#define TZX_HARDWARE_COMPUTER_CPC464 0x015 /* Amstrad CPC 464 */
+#define TZX_HARDWARE_COMPUTER_CPC664 0x016 /* Amstrad CPC 664 */
+#define TZX_HARDWARE_COMPUTER_CPC6128 0x017 /* Amstrad CPC 6128 */
+#define TZX_HARDWARE_COMPUTER_CPC464PLUS 0x018 /* Amstrad CPC 464+ */
+#define TZX_HARDWARE_COMPUTER_CPC6128PLUS 0x019 /* Amstrad CPC 6128+ */
+
+typedef struct TZX_BLOCK
+{
+ /* previous TZX block */
+ struct TZX_BLOCK *pPrev;
+ /* next TZX block */
+ struct TZX_BLOCK *pNext;
+ /* pointer to header data allocated for this block */
+ unsigned char *pBlockHeader;
+ /* size of data block */
+ unsigned long DataBlockSize;
+ /* pointer to data added to this block */
+ unsigned char *pBlockData;
+} TZX_BLOCK;
+
+typedef struct TZX_FILE
+{
+ /* version of TZX file */
+ unsigned char VersionMajor;
+ unsigned char VersionMinor;
+ /* pointer to first block */
+ TZX_BLOCK *pFirstBlock;
+} TZX_FILE;
+
+void TZX_WriteFile(TZX_FILE *pTZXFile, unsigned char *pFilename);
+unsigned char *TZX_GetBlockDataPtr(TZX_BLOCK *);
+unsigned char *TZX_GetBlockHeaderPtr(TZX_BLOCK *);
+
+TZX_FILE *TZX_CreateFile(unsigned char VersionMajor, unsigned char VersionMinor);
+void TZX_FreeFile(TZX_FILE *pFile);
+void TZX_AddDataToBlock(TZX_BLOCK *pBlock, int DataSize);
+unsigned char *TZX_GetDataBlockPtr(TZX_BLOCK *pBlock);
+void TZX_AddBlockToEndOfFile(TZX_FILE *pFile, TZX_BLOCK *pBlock);
+int TZX_GetBlockHeaderSize(unsigned char ID);
+BOOL TZX_BlockHasData(unsigned char ID);
+TZX_BLOCK *TZX_CreateBlock(unsigned char ID);
+void TZX_SetupPauseBlock(TZX_BLOCK *pBlock,unsigned long PauseInMilliseconds);
+#endif
+
diff --git a/tools/Makefile b/tools/Makefile new file mode 100644 index 0000000..c5c2cd7 --- /dev/null +++ b/tools/Makefile @@ -0,0 +1,44 @@ +BIN=idsk 2cdt apultra png2crtc hex2bin rasm + +all: $(BIN) +CC=gcc +CFLAGS=-s -O3 -Wall + +idsk: + mkdir iDSK/build + cd iDSK/build && cmake ../ + make -C iDSK/build + cp iDSK/build/iDSK idsk + rm -rf iDSK/build + +rasm: + make -C rasm_src + make -C rasm_src clean + +2cdt: + make -C 2CDT + cp 2CDT/2cdt . + +png2crtc: + make -C gfx2crtc + cp gfx2crtc/png2crtc . + +%.o: %.c + $(CC) $(CFLAGS) $< -o $@ -c + +apultra: + make -C apultra_src + cp apultra_src/apultra . + +hex2bin: + make -C hex2bin-2.0 + cp hex2bin-2.0/bin/hex2bin . + +.PHONY: clean +clean: + make -C 2CDT clean + make -C gfx2crtc clean + make -C hex2bin-2.0 cleanall + make -C apultra_src clean + rm -f $(BIN) *.o + diff --git a/tools/apultra_src/LICENSE b/tools/apultra_src/LICENSE new file mode 100644 index 0000000..213be1a --- /dev/null +++ b/tools/apultra_src/LICENSE @@ -0,0 +1,3 @@ +The apultra code is available under the Zlib license, except for src/matchfinder.c which is placed under the Creative Commons CC0 license. + +Please consult LICENSE.zlib.md and LICENSE.CC0.md for more information. diff --git a/tools/apultra_src/LICENSE.cc0.md b/tools/apultra_src/LICENSE.cc0.md new file mode 100644 index 0000000..139c68e --- /dev/null +++ b/tools/apultra_src/LICENSE.cc0.md @@ -0,0 +1,43 @@ +## creative commons + +# CC0 1.0 Universal + +CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED HEREUNDER. + +### Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer exclusive Copyright and Related Rights (defined below) upon the creator and subsequent owner(s) (each and all, an "owner") of an original work of authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for the purpose of contributing to a commons of creative, cultural and scientific works ("Commons") that the public can reliably and without fear of later claims of infringement build upon, modify, incorporate in other works, reuse and redistribute as freely as possible in any form whatsoever and for any purposes, including without limitation commercial purposes. These owners may contribute to the Commons to promote the ideal of a free culture and the further production of creative, cultural and scientific works, or to gain reputation or greater distribution for their Work in part through the use and efforts of others. + +For these and/or other purposes and motivations, and without any expectation of additional consideration or compensation, the person associating CC0 with a Work (the "Affirmer"), to the extent that he or she is an owner of Copyright and Related Rights in the Work, voluntarily elects to apply CC0 to the Work and publicly distribute the Work under its terms, with knowledge of his or her Copyright and Related Rights in the Work and the meaning and intended legal effect of CC0 on those rights. + +1. __Copyright and Related Rights.__ A Work made available under CC0 may be protected by copyright and related or neighboring rights ("Copyright and Related Rights"). Copyright and Related Rights include, but are not limited to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, communicate, and translate a Work; + + ii. moral rights retained by the original author(s) and/or performer(s); + + iii. publicity and privacy rights pertaining to a person's image or likeness depicted in a Work; + + iv. rights protecting against unfair competition in regards to a Work, subject to the limitations in paragraph 4(a), below; + + v. rights protecting the extraction, dissemination, use and reuse of data in a Work; + + vi. database rights (such as those arising under Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, and under any national implementation thereof, including any amended or successor version of such directive); and + + vii. other similar, equivalent or corresponding rights throughout the world based on applicable law or treaty, and any national implementations thereof. + +2. __Waiver.__ To the greatest extent permitted by, but not in contravention of, applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and unconditionally waives, abandons, and surrenders all of Affirmer's Copyright and Related Rights and associated claims and causes of action, whether now known or unknown (including existing as well as future claims and causes of action), in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each member of the public at large and to the detriment of Affirmer's heirs and successors, fully intending that such Waiver shall not be subject to revocation, rescission, cancellation, termination, or any other legal or equitable action to disrupt the quiet enjoyment of the Work by the public as contemplated by Affirmer's express Statement of Purpose. + +3. __Public License Fallback.__ Should any part of the Waiver for any reason be judged legally invalid or ineffective under applicable law, then the Waiver shall be preserved to the maximum extent permitted taking into account Affirmer's express Statement of Purpose. In addition, to the extent the Waiver is so judged Affirmer hereby grants to each affected person a royalty-free, non transferable, non sublicensable, non exclusive, irrevocable and unconditional license to exercise Affirmer's Copyright and Related Rights in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "License"). The License shall be deemed effective as of the date CC0 was applied by Affirmer to the Work. Should any part of the License for any reason be judged legally invalid or ineffective under applicable law, such partial invalidity or ineffectiveness shall not invalidate the remainder of the License, and in such case Affirmer hereby affirms that he or she will not (i) exercise any of his or her remaining Copyright and Related Rights in the Work or (ii) assert any associated claims and causes of action with respect to the Work, in either case contrary to Affirmer's express Statement of Purpose. + +4. __Limitations and Disclaimers.__ + + a. No trademark or patent rights held by Affirmer are waived, abandoned, surrendered, licensed or otherwise affected by this document. + + b. Affirmer offers the Work as-is and makes no representations or warranties of any kind concerning the Work, express, implied, statutory or otherwise, including without limitation warranties of title, merchantability, fitness for a particular purpose, non infringement, or the absence of latent or other defects, accuracy, or the present or absence of errors, whether or not discoverable, all to the greatest extent permissible under applicable law. + + c. Affirmer disclaims responsibility for clearing rights of other persons that may apply to the Work or any use thereof, including without limitation any person's Copyright and Related Rights in the Work. Further, Affirmer disclaims responsibility for obtaining any necessary consents, permissions or other rights required for any use of the Work. + + d. Affirmer understands and acknowledges that Creative Commons is not a party to this document and has no duty or obligation with respect to this CC0 or use of the Work. diff --git a/tools/apultra_src/LICENSE.zlib.md b/tools/apultra_src/LICENSE.zlib.md new file mode 100644 index 0000000..e1296a1 --- /dev/null +++ b/tools/apultra_src/LICENSE.zlib.md @@ -0,0 +1,19 @@ +Copyright (c) 2019 Emmanuel Marty + +This software is provided 'as-is', without any express or implied warranty. In +no event will the authors be held liable for any damages arising from the use of +this software. + +Permission is granted to anyone to use this software for any purpose, including +commercial applications, and to alter it and redistribute it freely, subject to +the following restrictions: + +1. The origin of this software must not be misrepresented; you must not claim + that you wrote the original software. If you use this software in a product, + an acknowledgment in the product documentation would be appreciated but is + not required. + +2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + +3. This notice may not be removed or altered from any source distribution. diff --git a/tools/apultra_src/Makefile b/tools/apultra_src/Makefile new file mode 100644 index 0000000..0ed5dea --- /dev/null +++ b/tools/apultra_src/Makefile @@ -0,0 +1,29 @@ +CC=gcc +CFLAGS=-O3 -s -fomit-frame-pointer -Isrc/libdivsufsort/include -Isrc +OBJDIR=obj +LDFLAGS= + +$(OBJDIR)/%.o: src/../%.c + @mkdir -p '$(@D)' + $(CC) $(CFLAGS) -c $< -o $@ + +APP := apultra + +OBJS += $(OBJDIR)/src/apultra.o +OBJS += $(OBJDIR)/src/expand.o +OBJS += $(OBJDIR)/src/matchfinder.o +OBJS += $(OBJDIR)/src/shrink.o +OBJS += $(OBJDIR)/src/libdivsufsort/lib/divsufsort.o +OBJS += $(OBJDIR)/src/libdivsufsort/lib/divsufsort_utils.o +OBJS += $(OBJDIR)/src/libdivsufsort/lib/sssort.o +OBJS += $(OBJDIR)/src/libdivsufsort/lib/trsort.o + +all: $(APP) + +$(APP): $(OBJS) + @mkdir -p ../../bin/posix + $(CC) $^ $(LDFLAGS) -o $(APP) + +clean: + @rm -rf $(APP) $(OBJDIR) + diff --git a/tools/apultra_src/README.md b/tools/apultra_src/README.md new file mode 100644 index 0000000..37bab33 --- /dev/null +++ b/tools/apultra_src/README.md @@ -0,0 +1,30 @@ +apultra -- a new, opensource optimal compressor for the apLib format
+====================================================================
+
+apultra is a command-line tool and a library that compresses bitstreams in the apLib format.
+
+The tool produces files that are 5 to 7% smaller on average than appack, the apLib compressor. Unlike the similar [cap](https://github.com/svendahl/cap) compressor, apultra can compress files larger than 64K (for files smaller than 64K, cap compresses 0.05% better on average).
+
+apultra is written in portable C. It is fully open-source under a liberal license. You can continue to use the regular apLib decompression libraries for your target environment. You can do whatever you like with it.
+
+ Example compression with vmlinux-5.3.0-1-amd64
+
+ original 27923676 (100,00%)
+ appack 7370129 (26,39%)
+ gzip 1.8 7166179 (25,66%)
+ apultra 1.0.8 6910793 (24,75%)
+
+
+The output is fully compatible with the original [aPLib](http://ibsensoftware.com/products_aPLib.html) by Jørgen Ibsen.
+
+Inspirations:
+
+ * [cap](https://github.com/svendahl/cap) by Sven-Ã…ke Dahl.
+ * [Charles Bloom](http://cbloomrants.blogspot.com/)'s compression blog.
+ * [LZ4](https://github.com/lz4/lz4) by Yann Collet.
+ * spke for help and support
+
+License:
+
+* The apultra code is available under the Zlib license.
+* The match finder (matchfinder.c) is available under the CC0 license due to using portions of code from Eric Bigger's Wimlib in the suffix array-based matchfinder.
diff --git a/tools/apultra_src/VS2017/apultra.sln b/tools/apultra_src/VS2017/apultra.sln new file mode 100644 index 0000000..4a644c7 --- /dev/null +++ b/tools/apultra_src/VS2017/apultra.sln @@ -0,0 +1,31 @@ +
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 15
+VisualStudioVersion = 15.0.28307.489
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "apultra", "apultra.vcxproj", "{3F30FEE8-63C5-4D39-A175-EDD7EA93E9B8}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|x64 = Debug|x64
+ Debug|x86 = Debug|x86
+ Release|x64 = Release|x64
+ Release|x86 = Release|x86
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {3F30FEE8-63C5-4D39-A175-EDD7EA93E9B8}.Debug|x64.ActiveCfg = Debug|x64
+ {3F30FEE8-63C5-4D39-A175-EDD7EA93E9B8}.Debug|x64.Build.0 = Debug|x64
+ {3F30FEE8-63C5-4D39-A175-EDD7EA93E9B8}.Debug|x86.ActiveCfg = Debug|Win32
+ {3F30FEE8-63C5-4D39-A175-EDD7EA93E9B8}.Debug|x86.Build.0 = Debug|Win32
+ {3F30FEE8-63C5-4D39-A175-EDD7EA93E9B8}.Release|x64.ActiveCfg = Release|x64
+ {3F30FEE8-63C5-4D39-A175-EDD7EA93E9B8}.Release|x64.Build.0 = Release|x64
+ {3F30FEE8-63C5-4D39-A175-EDD7EA93E9B8}.Release|x86.ActiveCfg = Release|Win32
+ {3F30FEE8-63C5-4D39-A175-EDD7EA93E9B8}.Release|x86.Build.0 = Release|Win32
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {A1E1655C-AA9F-41F0-80C9-18DD0B859D7C}
+ EndGlobalSection
+EndGlobal
diff --git a/tools/apultra_src/VS2017/apultra.vcxproj b/tools/apultra_src/VS2017/apultra.vcxproj new file mode 100644 index 0000000..5ae0b83 --- /dev/null +++ b/tools/apultra_src/VS2017/apultra.vcxproj @@ -0,0 +1,203 @@ +<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <VCProjectVersion>15.0</VCProjectVersion>
+ <ProjectGuid>{3F30FEE8-63C5-4D39-A175-EDD7EA93E9B8}</ProjectGuid>
+ <Keyword>Win32Proj</Keyword>
+ <RootNamespace>apultra</RootNamespace>
+ <WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v141</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v141</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v141</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v141</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="Shared">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(ProjectDir)bin\</OutDir>
+ <TargetName>$(ProjectName)_debug</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(ProjectDir)bin\</OutDir>
+ <TargetName>$(ProjectName)_debug</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(ProjectDir)bin\</OutDir>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(ProjectDir)bin\</OutDir>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <PrecompiledHeader>NotUsing</PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <SDLCheck>true</SDLCheck>
+ <PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <ConformanceMode>true</ConformanceMode>
+ <PrecompiledHeaderFile>
+ </PrecompiledHeaderFile>
+ <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+ <AdditionalIncludeDirectories>..\src\libdivsufsort\include;..\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ </ClCompile>
+ <Link>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <OutputFile>$(ProjectDir)bin\$(TargetName)$(TargetExt)</OutputFile>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <PrecompiledHeader>NotUsing</PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <SDLCheck>true</SDLCheck>
+ <PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <ConformanceMode>true</ConformanceMode>
+ <PrecompiledHeaderFile>
+ </PrecompiledHeaderFile>
+ <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+ <AdditionalIncludeDirectories>..\src\libdivsufsort\include;..\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ </ClCompile>
+ <Link>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <OutputFile>$(ProjectDir)bin\$(TargetName)$(TargetExt)</OutputFile>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <PrecompiledHeader>NotUsing</PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <SDLCheck>true</SDLCheck>
+ <PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <ConformanceMode>true</ConformanceMode>
+ <PrecompiledHeaderFile>
+ </PrecompiledHeaderFile>
+ <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+ <FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
+ <AdditionalIncludeDirectories>..\src\libdivsufsort\include;..\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <OmitFramePointers>true</OmitFramePointers>
+ </ClCompile>
+ <Link>
+ <SubSystem>Console</SubSystem>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <OutputFile>$(ProjectDir)bin\$(TargetName)$(TargetExt)</OutputFile>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <PrecompiledHeader>NotUsing</PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <SDLCheck>true</SDLCheck>
+ <PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <ConformanceMode>true</ConformanceMode>
+ <PrecompiledHeaderFile>
+ </PrecompiledHeaderFile>
+ <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+ <FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
+ <AdditionalIncludeDirectories>..\src\libdivsufsort\include;..\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <OmitFramePointers>true</OmitFramePointers>
+ </ClCompile>
+ <Link>
+ <SubSystem>Console</SubSystem>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <OutputFile>$(ProjectDir)bin\$(TargetName)$(TargetExt)</OutputFile>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ClInclude Include="..\src\format.h" />
+ <ClInclude Include="..\src\expand.h" />
+ <ClInclude Include="..\src\libapultra.h" />
+ <ClInclude Include="..\src\libdivsufsort\include\divsufsort_config.h" />
+ <ClInclude Include="..\src\libdivsufsort\include\divsufsort.h" />
+ <ClInclude Include="..\src\libdivsufsort\include\divsufsort_private.h" />
+ <ClInclude Include="..\src\matchfinder.h" />
+ <ClInclude Include="..\src\shrink.h" />
+ <ClInclude Include="pch.h" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="..\src\expand.c" />
+ <ClCompile Include="..\src\libdivsufsort\lib\divsufsort.c" />
+ <ClCompile Include="..\src\libdivsufsort\lib\sssort.c" />
+ <ClCompile Include="..\src\libdivsufsort\lib\trsort.c" />
+ <ClCompile Include="..\src\libdivsufsort\lib\divsufsort_utils.c" />
+ <ClCompile Include="..\src\apultra.c" />
+ <ClCompile Include="..\src\matchfinder.c" />
+ <ClCompile Include="..\src\shrink.c" />
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
\ No newline at end of file diff --git a/tools/apultra_src/VS2017/apultra.vcxproj.filters b/tools/apultra_src/VS2017/apultra.vcxproj.filters new file mode 100644 index 0000000..3e88982 --- /dev/null +++ b/tools/apultra_src/VS2017/apultra.vcxproj.filters @@ -0,0 +1,81 @@ +<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Fichiers sources">
+ <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
+ <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
+ </Filter>
+ <Filter Include="Fichiers d%27en-tête">
+ <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
+ <Extensions>h;hh;hpp;hxx;hm;inl;inc;ipp;xsd</Extensions>
+ </Filter>
+ <Filter Include="Fichiers de ressources">
+ <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
+ <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
+ </Filter>
+ <Filter Include="Fichiers sources\libdivsufsort">
+ <UniqueIdentifier>{5ec09c0d-19f7-4a6f-b524-f405fb99e48c}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Fichiers sources\libdivsufsort\lib">
+ <UniqueIdentifier>{a922f475-1322-496d-8a6d-7f1c6b92423d}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Fichiers sources\libdivsufsort\include">
+ <UniqueIdentifier>{bd05c6e8-af92-4ab8-8916-0424cd8d186b}</UniqueIdentifier>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="pch.h">
+ <Filter>Fichiers d%27en-tête</Filter>
+ </ClInclude>
+ <ClInclude Include="..\src\format.h">
+ <Filter>Fichiers sources</Filter>
+ </ClInclude>
+ <ClInclude Include="..\src\libdivsufsort\include\divsufsort.h">
+ <Filter>Fichiers sources\libdivsufsort\include</Filter>
+ </ClInclude>
+ <ClInclude Include="..\src\libdivsufsort\include\divsufsort_private.h">
+ <Filter>Fichiers sources\libdivsufsort\include</Filter>
+ </ClInclude>
+ <ClInclude Include="..\src\matchfinder.h">
+ <Filter>Fichiers sources</Filter>
+ </ClInclude>
+ <ClInclude Include="..\src\libdivsufsort\include\divsufsort_config.h">
+ <Filter>Fichiers sources\libdivsufsort\include</Filter>
+ </ClInclude>
+ <ClInclude Include="..\src\shrink.h">
+ <Filter>Fichiers sources</Filter>
+ </ClInclude>
+ <ClInclude Include="..\src\expand.h">
+ <Filter>Fichiers sources</Filter>
+ </ClInclude>
+ <ClInclude Include="..\src\libapultra.h">
+ <Filter>Fichiers sources</Filter>
+ </ClInclude>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="..\src\libdivsufsort\lib\divsufsort.c">
+ <Filter>Fichiers sources\libdivsufsort\lib</Filter>
+ </ClCompile>
+ <ClCompile Include="..\src\libdivsufsort\lib\sssort.c">
+ <Filter>Fichiers sources\libdivsufsort\lib</Filter>
+ </ClCompile>
+ <ClCompile Include="..\src\libdivsufsort\lib\trsort.c">
+ <Filter>Fichiers sources\libdivsufsort\lib</Filter>
+ </ClCompile>
+ <ClCompile Include="..\src\matchfinder.c">
+ <Filter>Fichiers sources</Filter>
+ </ClCompile>
+ <ClCompile Include="..\src\apultra.c">
+ <Filter>Fichiers sources</Filter>
+ </ClCompile>
+ <ClCompile Include="..\src\libdivsufsort\lib\divsufsort_utils.c">
+ <Filter>Fichiers sources\libdivsufsort\lib</Filter>
+ </ClCompile>
+ <ClCompile Include="..\src\shrink.c">
+ <Filter>Fichiers sources</Filter>
+ </ClCompile>
+ <ClCompile Include="..\src\expand.c">
+ <Filter>Fichiers sources</Filter>
+ </ClCompile>
+ </ItemGroup>
+</Project>
\ No newline at end of file diff --git a/tools/apultra_src/VS2017/apultra.vcxproj.user b/tools/apultra_src/VS2017/apultra.vcxproj.user new file mode 100644 index 0000000..969fd88 --- /dev/null +++ b/tools/apultra_src/VS2017/apultra.vcxproj.user @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LocalDebuggerCommand>$(TargetPath)</LocalDebuggerCommand>
+ <LocalDebuggerCommandArguments>bad.bin bad.lz3 -stats</LocalDebuggerCommandArguments>
+ <DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
+ <LocalDebuggerWorkingDirectory>$(ProjectDir)..\</LocalDebuggerWorkingDirectory>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <LocalDebuggerCommand>$(TargetPath)</LocalDebuggerCommand>
+ <LocalDebuggerCommandArguments>bad.bin bad.lz3 -stats</LocalDebuggerCommandArguments>
+ <DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
+ <LocalDebuggerWorkingDirectory>$(ProjectDir)..\</LocalDebuggerWorkingDirectory>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <LocalDebuggerCommand>$(TargetPath)</LocalDebuggerCommand>
+ <LocalDebuggerCommandArguments>bad.bin bad.lz3 -stats</LocalDebuggerCommandArguments>
+ <DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
+ <LocalDebuggerWorkingDirectory>$(ProjectDir)..\</LocalDebuggerWorkingDirectory>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <LocalDebuggerCommand>$(TargetPath)</LocalDebuggerCommand>
+ <LocalDebuggerCommandArguments>bad.bin bad.lz3 -stats</LocalDebuggerCommandArguments>
+ <DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
+ <LocalDebuggerWorkingDirectory>$(ProjectDir)..\</LocalDebuggerWorkingDirectory>
+ </PropertyGroup>
+</Project>
\ No newline at end of file diff --git a/tools/apultra_src/asm/6502/aplib_6502.asm b/tools/apultra_src/asm/6502/aplib_6502.asm new file mode 100644 index 0000000..219ac86 --- /dev/null +++ b/tools/apultra_src/asm/6502/aplib_6502.asm @@ -0,0 +1,371 @@ +; *************************************************************************** +; *************************************************************************** +; +; aplib_6502.s +; +; NMOS 6502 decompressor for data stored in Jorgen Ibsen's aPLib format. +; +; Includes support for Emmanuel Marty's enhancements to the aPLib format. +; +; The code is 252 bytes long for standard format, 270 for enhanced format. +; +; This code is written for the ACME assembler. +; +; Copyright John Brandwood 2019. +; +; Distributed under the Boost Software License, Version 1.0. +; (See accompanying file LICENSE_1_0.txt or copy at +; http://www.boost.org/LICENSE_1_0.txt) +; +; *************************************************************************** +; *************************************************************************** + + + +; *************************************************************************** +; *************************************************************************** +; +; Decompression Options & Macros +; + + ; + ; Use the enhanced format from Emmanuel Marty's APULTRA? + ; + ; The enhancements speed up decompression on an 8-bit CPU. + ; + ; This gives an 11% improvement in decompresison speed, but + ; breaks compatibility with standard aPLib encoders. + ; + +APL_ENHANCED = 0 + + ; + ; Assume that we're decompessing from a large multi-bank + ; compressed data file, and that the next bank may need to + ; paged in when a page-boundary is crossed. + ; + +APL_FROM_BANK = 0 + + ; + ; Macro to increment the source pointer to the next page. + ; + + !if APL_FROM_BANK { + !macro APL_INC_PAGE { + jsr .next_page + } + } else { + !macro APL_INC_PAGE { + inc <apl_srcptr + 1 + } + } + + ; + ; Macro to read a byte from the compressed source data. + ; + + !macro APL_GET_SRC { + lda (apl_srcptr),y + inc <apl_srcptr + 0 + bne .skip + +APL_INC_PAGE +.skip: + } + + + +; *************************************************************************** +; *************************************************************************** +; +; Data usage is last 12 bytes of zero-page. +; + + !if APL_ENHANCED { +apl_nibflg = $F4 ; 1 byte. +apl_nibble = $F5 ; 1 byte. +apl_egamma = $F6 ; 1 byte. + } + +apl_bitbuf = $F7 ; 1 byte. +apl_offset = $F8 ; 1 word. +apl_winptr = $FA ; 1 word. +apl_srcptr = $FC ; 1 word. +apl_dstptr = $FE ; 1 word. +apl_length = apl_winptr + + +; *************************************************************************** +; *************************************************************************** +; +; apl_decompress - Decompress data stored in Jorgen Ibsen's aPLib format. +; +; Args: apl_srcptr = ptr to compessed data +; Args: apl_dstptr = ptr to output buffer +; Uses: lots! +; +; If compiled with APL_FROM_BANK, then apl_srcptr should be within the bank +; window range. +; +; As an optimization, the code to handle window offsets > 64768 bytes has +; been removed, since these don't occur with a 16-bit address range. +; +; As an optimization, the code to handle window offsets > 32000 bytes can +; be commented-out, since these don't occur in typical 8-bit computer usage. +; + +apl_decompress: ldy #0 ; Initialize source index. + + lda #$80 ; Initialize an empty + sta <apl_bitbuf ; bit-buffer. + + !if APL_ENHANCED { + sta <apl_egamma ; Bit-buffer for gamma pairs. + sty <apl_nibflg ; Reset the flag. + } + + ; + ; 0 bbbbbbbb - One byte from compressed data, i.e. a "literal". + ; + +.literal: +APL_GET_SRC + +.write_byte: ldx #0 ; LWM=0. + + sta (apl_dstptr),y ; Write the byte directly to + inc <apl_dstptr + 0 ; the output. + bne .next_tag + inc <apl_dstptr + 1 + +.next_tag: asl <apl_bitbuf ; 0 bbbbbbbb + bne .skip0 + jsr .load_bit +.skip0: bcc .literal + +.skip1: asl <apl_bitbuf ; 1 0 <offset> <length> + bne .skip2 + jsr .load_bit +.skip2: bcc .copy_large + + asl <apl_bitbuf ; 1 1 0 dddddddn + bne .skip3 + jsr .load_bit +.skip3: bcc .copy_normal + + ; 1 1 1 dddd - Copy 1 byte within 15 bytes (or zero). + + !if APL_ENHANCED { + +.copy_short: lsr <apl_nibflg ; Is there a nibble waiting? + lda <apl_nibble ; Extract the lo-nibble. + bcs .skip4 + + inc <apl_nibflg ; Reset the flag. + +APL_GET_SRC + sta <apl_nibble ; Preserve for next time. + lsr ; Extract the hi-nibble. + lsr + lsr + lsr + +.skip4: and #$0F ; Current nibble. + beq .write_byte ; Offset=0 means write zero. + + } else { + +.copy_short: lda #$10 +.nibble_loop: asl <apl_bitbuf + bne .skip4 + pha + jsr .load_bit + pla +.skip4: rol + bcc .nibble_loop + beq .write_byte ; Offset=0 means write zero. + + } + + eor #$FF ; Read the byte directly from + tay ; the destination window. + iny + dec <apl_dstptr + 1 + lda (apl_dstptr),y + inc <apl_dstptr + 1 + ldy #0 + beq .write_byte + + ; + ; 1 1 0 dddddddn - Copy 2 or 3 within 128 bytes. + ; + +.copy_normal: +APL_GET_SRC ; 1 1 0 dddddddn + lsr + beq .finished ; Offset 0 == EOF. + + sta <apl_offset + 0 ; Preserve offset. + sty <apl_offset + 1 + tya ; Y == 0. + tax ; Bits 8..15 of length. + adc #2 ; Bits 0...7 of length. + bne .do_match ; NZ from previous ADC. + + ; + ; Subroutines for byte & bit handling. + ; + + !if APL_ENHANCED { + +.get_gamma: lda #1 ; Get a gamma-coded value. +.gamma_loop: asl <apl_egamma + bne .rotate_gamma + pha + +APL_GET_SRC ; Reload an empty bit-buffer + rol ; from the compressed source. + sta <apl_egamma + pla +.rotate_gamma: rol + bcs .big_gamma ; Got 8 bits, now read rest. + asl <apl_egamma + bcc .gamma_loop + rts ; Always returns CS. + +.big_gamma: pha ; Read remaining bits of length + tya ; larger than 255. This is very + jsr .rotate_gamma ; rare, so it saves cycles on + tax ; the 6502 to do it this way. + pla + +.finished: rts ; All decompressed! + + } else { + +.get_gamma: lda #1 ; Get a gamma-coded value. +.gamma_loop: asl <apl_bitbuf + bne .skip5 + pha + jsr .load_bit + pla +.skip5: rol + rol <apl_length + 1 + asl <apl_bitbuf + bne .skip6 + pha + jsr .load_bit + pla +.skip6: bcs .gamma_loop + +.finished: rts ; All decompressed! + + } + + ; + ; 1 0 <offset> <length> - gamma-coded LZSS pair. + ; + + !if APL_ENHANCED { + +.copy_large: jsr .get_gamma ; Bits 8..15 of offset (min 2). + + cpx #1 ; CC if LWM==0, CS if LWM==1. + ldx #0 ; Clear hi-byte of length. + sbc #2 ; -3 if LWM==0, -2 if LWM==1. + bcs .normal_pair ; CC if LWM==0 && offset==2. + + jsr .get_gamma ; Get length (A=lo-byte & CS). + bcs .do_match ; Use previous Offset. + +.normal_pair: sta <apl_offset + 1 ; Save bits 8..15 of offset. + + +APL_GET_SRC + sta <apl_offset + 0 ; Save bits 0...7 of offset. + + jsr .get_gamma ; Get length (A=lo-byte & CS). + + } else { + +.copy_large: jsr .get_gamma ; Bits 8..15 of offset (min 2). + sty <apl_length + 1 ; Clear hi-byte of length. + + cpx #1 ; CC if LWM==0, CS if LWM==1. + sbc #2 ; -3 if LWM==0, -2 if LWM==1. + bcs .normal_pair ; CC if LWM==0 && offset==2. + + jsr .get_gamma ; Get length (A=lo-byte & CC). + ldx <apl_length + 1 + bcc .do_match ; Use previous Offset. + +.normal_pair: sta <apl_offset + 1 ; Save bits 8..15 of offset. + + +APL_GET_SRC + sta <apl_offset + 0 ; Save bits 0...7 of offset. + + jsr .get_gamma ; Get length (A=lo-byte & CC). + ldx <apl_length + 1 + + } + + ldy <apl_offset + 1 ; If offset < 256. + beq .lt256 + cpy #$7D ; If offset >= 32000, length += 2. + bcs .match_plus2 + cpy #$05 ; If offset >= 1280, length += 1. + bcs .match_plus1 + bcc .do_match +.lt256: ldy <apl_offset + 0 ; If offset < 128, length += 2. + bmi .do_match + + !if APL_ENHANCED { + } else { + sec ; aPLib gamma returns with CC. + } + +.match_plus2: adc #1 ; CS, so ADC #2. + bcs .match_plus256 + +.match_plus1: adc #0 ; CS, so ADC #1, or CC if fall + bcc .do_match ; through from .match_plus2. + +.match_plus256: inx + +.do_match: eor #$FF ; Negate the lo-byte of length + tay ; and check for zero. + iny + beq .calc_addr + eor #$FF + + inx ; Increment # of pages to copy. + + clc ; Calc destination for partial + adc <apl_dstptr + 0 ; page. + sta <apl_dstptr + 0 + bcs .calc_addr + dec <apl_dstptr + 1 + +.calc_addr: sec ; Calc address of match. + lda <apl_dstptr + 0 + sbc <apl_offset + 0 + sta <apl_winptr + 0 + lda <apl_dstptr + 1 + sbc <apl_offset + 1 + sta <apl_winptr + 1 + +.copy_page: lda (apl_winptr),y + sta (apl_dstptr),y + iny + bne .copy_page + inc <apl_winptr + 1 + inc <apl_dstptr + 1 + dex ; Any full pages left to copy? + bne .copy_page + + inx ; LWM=1. + jmp .next_tag + + ; + ; Subroutines for byte & bit handling. + ; + +.load_bit: +APL_GET_SRC ; Reload an empty bit-buffer + rol ; from the compressed source. + sta <apl_bitbuf + rts diff --git a/tools/apultra_src/asm/ARM7TDMI/aplib_arm.s b/tools/apultra_src/asm/ARM7TDMI/aplib_arm.s new file mode 100644 index 0000000..b6d0cef --- /dev/null +++ b/tools/apultra_src/asm/ARM7TDMI/aplib_arm.s @@ -0,0 +1,150 @@ +@APlib ARM7 decompressor by Dan Weiss, based on the original C version +@Takes in raw apacked data, NOT data created by the 'safe' compressor. +@Code is from the PocketNES NES Emulator for GBA + +@Code is formatted for GNU Assembler + + src .req r0 + dest .req r1 + byte .req r2 + mask .req r3 + gamma .req r4 + lwm .req r6 + recentoff .req r7 + temp .req r8 + +.global depack +.type depack STT_FUNC + +@r0 = src +@r1 = dest +@r2 = byte +@r3 = rotating bit mask +@r4 = increasing gamma +@r6 = lwm +@r7 = recentoff +@r8 = lr copy/scratch + + .macro GETBIT @3 instructions + movs mask,mask,ror #1 + ldrcsb byte,[src],#1 + tst byte,mask + .endm + + .macro GETBITGAMMA @5 instructions + mov gamma,gamma,lsl #1 + GETBIT + addne gamma,gamma,#1 + .endm + +@This initilaiztion code can go into slow memory + +depack: + stmfd sp!,{r4-r10,lr} + ldrb temp,[src],#1 + strb temp,[dest],#1 + ldr mask,=0x01010101 + b aploop_nolwm + +@This inner-loop code should be placed into fast memory + + @depack enters here +aploop_nolwm: + mov lwm,#0 +aploop: + GETBIT + bne apbranch1 + ldrb temp,[src],#1 + strb temp,[dest],#1 + b aploop_nolwm +apbranch1: + GETBIT + beq apbranch2 + GETBIT + beq apbranch3 + @get an offset + mov gamma,#0 + GETBIT + addne gamma,gamma,#1 + GETBITGAMMA + GETBITGAMMA + GETBITGAMMA + cmp gamma,#0 + ldrneb gamma,[dest,-gamma] + strb gamma,[dest],#1 + b aploop_nolwm +apbranch3: + @use 7 bit offset, length = 2 or 3 + @if a zero is encountered here, it's EOF + ldrb gamma,[src],#1 + movs recentoff,gamma,lsr #1 + beq done + ldrcsb temp,[dest,-recentoff] + strcsb temp,[dest],#1 + ldrb temp,[dest,-recentoff] + strb temp,[dest],#1 + ldrb temp,[dest,-recentoff] + strb temp,[dest],#1 + mov lwm,#1 + b aploop +apbranch2: + @use a gamma code * 256 for offset, another gamma code for length + + bl ap_getgamma + sub gamma,gamma,#2 + cmp lwm,#0 + bne ap_is_lwm + mov lwm,#1 + cmp gamma,#0 + bne ap_not_zero_gamma + + @if gamma code is 2, use old recent offset, and a new gamma code for length + bl ap_getgamma +copyloop1: + ldrb temp,[dest,-recentoff] + strb temp,[dest],#1 + subs gamma,gamma,#1 + bne copyloop1 + b aploop + +ap_not_zero_gamma: + sub gamma,gamma,#1 +ap_is_lwm: + ldrb temp,[src],#1 + add recentoff,temp,gamma,lsl #8 + bl ap_getgamma + @gamma=length + cmp recentoff,#32000 + addge gamma,gamma,#1 + cmp recentoff,#1280 + addge gamma,gamma,#1 + cmp recentoff,#128 + addlt gamma,gamma,#2 +copyloop2: + ldrb temp,[dest,-recentoff] + strb temp,[dest],#1 + subs gamma,gamma,#1 + bne copyloop2 + b aploop + +ap_getgamma: + mov gamma,#1 +ap_getgammaloop: + GETBITGAMMA + GETBIT + bne ap_getgammaloop + bx lr + +done: + ldmfd sp!,{r4-r10,lr} + bx lr + +.unreq src +.unreq dest +.unreq byte +.unreq mask +.unreq gamma +.unreq lwm +.unreq recentoff +.unreq temp + diff --git a/tools/apultra_src/asm/Z80/aplib_z80.asm b/tools/apultra_src/asm/Z80/aplib_z80.asm new file mode 100644 index 0000000..6843a14 --- /dev/null +++ b/tools/apultra_src/asm/Z80/aplib_z80.asm @@ -0,0 +1,190 @@ +;Z80 Version by Dan Weiss +;Call depack. +;hl = source +;de = dest + +ap_bits: .db 0 +ap_byte: .db 0 +lwm: .db 0 +r0: .dw 0 + +ap_getbit: + push bc + ld bc,(ap_bits) + rrc c + jr nc,ap_getbit_continue + ld b,(hl) + inc hl +ap_getbit_continue: + ld a,c + and b + ld (ap_bits),bc + pop bc + ret + +ap_getbitbc: ;doubles BC and adds the read bit + sla c + rl b + call ap_getbit + ret z + inc bc + ret + +ap_getgamma: + ld bc,1 +ap_getgammaloop: + call ap_getbitbc + call ap_getbit + jr nz,ap_getgammaloop + ret + + +depack: + ;hl = source + ;de = dest + ldi + xor a + ld (lwm),a + inc a + ld (ap_bits),a + +aploop: + call ap_getbit + jp z, apbranch1 + call ap_getbit + jr z, apbranch2 + call ap_getbit + jr z, apbranch3 + ;LWM = 0 + xor a + ld (lwm),a + ;get an offset + ld bc,0 + call ap_getbitbc + call ap_getbitbc + call ap_getbitbc + call ap_getbitbc + ld a,b + or c + jr nz,apbranch4 + xor a ;write a 0 + ld (de),a + inc de + jr aploop +apbranch4: + ex de,hl ;write a previous bit (1-15 away from dest) + push hl + sbc hl,bc + ld a,(hl) + pop hl + ld (hl),a + inc hl + ex de,hl + jr aploop +apbranch3: + ;use 7 bit offset, length = 2 or 3 + ;if a zero is encountered here, it's EOF + ld c,(hl) + inc hl + rr c + ret z + ld b,2 + jr nc,ap_dont_inc_b + inc b +ap_dont_inc_b: + ;LWM = 1 + ld a,1 + ld (lwm),a + + push hl + ld a,b + ld b,0 + ;R0 = c + ld (r0),bc + ld h,d + ld l,e + or a + sbc hl,bc + ld c,a + ldir + pop hl + jr aploop +apbranch2: + ;use a gamma code * 256 for offset, another gamma code for length + call ap_getgamma + dec bc + dec bc + ld a,(lwm) + or a + jr nz,ap_not_lwm + ;bc = 2? + ld a,b + or c + jr nz,ap_not_zero_gamma + ;if gamma code is 2, use old r0 offset, and a new gamma code for length + call ap_getgamma + push hl + ld h,d + ld l,e + push bc + ld bc,(r0) + sbc hl,bc + pop bc + ldir + pop hl + jr ap_finishup + +ap_not_zero_gamma: + dec bc +ap_not_lwm: + ;do I even need this code? + ;bc=bc*256+(hl), lazy 16bit way + ld b,c + ld c,(hl) + inc hl + ld (r0),bc + push bc + call ap_getgamma + ex (sp),hl + ;bc = len, hl=offs + push de + ex de,hl + ;some comparison junk for some reason + ld hl,31999 + or a + sbc hl,de + jr nc,skip1 + inc bc +skip1: + ld hl,1279 + or a + sbc hl,de + jr nc,skip2 + inc bc +skip2: + ld hl,127 + or a + sbc hl,de + jr c,skip3 + inc bc + inc bc +skip3: + ;bc = len, de = offs, hl=junk + pop hl + push hl + or a + sbc hl,de + pop de + ;hl=dest-offs, bc=len, de = dest + ldir + pop hl +ap_finishup: + ld a,1 + ld (lwm),a + jp aploop + +apbranch1: + ldi + xor a + ld (lwm),a + jp aploop diff --git a/tools/apultra_src/src/apultra.c b/tools/apultra_src/src/apultra.c new file mode 100644 index 0000000..e7440e9 --- /dev/null +++ b/tools/apultra_src/src/apultra.c @@ -0,0 +1,1088 @@ +/*
+ * apultra.c - command line compression utility for the apultra library
+ *
+ * Copyright (C) 2019 Emmanuel Marty
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty. In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ */
+
+/*
+ * Uses the libdivsufsort library Copyright (c) 2003-2008 Yuta Mori
+ *
+ * Inspired by cap by Sven-Åke Dahl. https://github.com/svendahl/cap
+ * Also inspired by Charles Bloom's compression blog. http://cbloomrants.blogspot.com/
+ * With ideas from LZ4 by Yann Collet. https://github.com/lz4/lz4
+ * With help and support from spke <zxintrospec@gmail.com>
+ *
+ */
+
+#include <stdio.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#ifdef _WIN32
+#include <windows.h>
+#include <sys/timeb.h>
+#else
+#include <sys/time.h>
+#endif
+#include "libapultra.h"
+
+#define OPT_VERBOSE 1
+#define OPT_STATS 2
+#define OPT_ENHANCED 4
+
+#define TOOL_VERSION "1.0.9"
+
+/*---------------------------------------------------------------------------*/
+
+#ifdef _WIN32
+LARGE_INTEGER hpc_frequency;
+BOOL hpc_available = FALSE;
+#endif
+
+static void do_init_time() {
+#ifdef _WIN32
+ hpc_frequency.QuadPart = 0;
+ hpc_available = QueryPerformanceFrequency(&hpc_frequency);
+#endif
+}
+
+static long long do_get_time() {
+ long long nTime;
+
+#ifdef _WIN32
+ if (hpc_available) {
+ LARGE_INTEGER nCurTime;
+
+ /* Use HPC hardware for best precision */
+ QueryPerformanceCounter(&nCurTime);
+ nTime = (long long)(nCurTime.QuadPart * 1000000LL / hpc_frequency.QuadPart);
+ }
+ else {
+ struct _timeb tb;
+ _ftime(&tb);
+
+ nTime = ((long long)tb.time * 1000LL + (long long)tb.millitm) * 1000LL;
+ }
+#else
+ struct timeval tm;
+ gettimeofday(&tm, NULL);
+
+ nTime = (long long)tm.tv_sec * 1000000LL + (long long)tm.tv_usec;
+#endif
+ return nTime;
+}
+
+/*---------------------------------------------------------------------------*/
+
+static void compression_progress(long long nOriginalSize, long long nCompressedSize) {
+ if (nOriginalSize >= 512 * 1024) {
+ fprintf(stdout, "\r%lld => %lld (%g %%) \b\b\b\b\b", nOriginalSize, nCompressedSize, (double)(nCompressedSize * 100.0 / nOriginalSize));
+ fflush(stdout);
+ }
+}
+
+static int do_compress(const char *pszInFilename, const char *pszOutFilename, const char *pszDictionaryFilename, const unsigned int nOptions, const unsigned int nMaxWindowSize) {
+ long long nStartTime = 0LL, nEndTime = 0LL;
+ size_t nOriginalSize = 0L, nCompressedSize = 0L, nMaxCompressedSize;
+ int nSafeDist = 0;
+ int nFlags;
+ apultra_stats stats;
+ unsigned char *pDecompressedData;
+ unsigned char *pCompressedData;
+
+ nFlags = (nOptions & OPT_ENHANCED) ? APULTRA_FLAG_ENHANCED : 0;
+
+ if (nOptions & OPT_VERBOSE) {
+ nStartTime = do_get_time();
+ }
+
+ /* Read the whole original file in memory */
+
+ FILE *f_in = fopen(pszInFilename, "rb");
+ if (!f_in) {
+ fprintf(stderr, "error opening '%s' for reading\n", pszInFilename);
+ return 100;
+ }
+
+ fseek(f_in, 0, SEEK_END);
+ nOriginalSize = (size_t)ftell(f_in);
+ fseek(f_in, 0, SEEK_SET);
+
+ pDecompressedData = (unsigned char*)malloc(nOriginalSize);
+ if (!pDecompressedData) {
+ fclose(f_in);
+ fprintf(stderr, "out of memory for reading '%s', %zd bytes needed\n", pszInFilename, nOriginalSize);
+ return 100;
+ }
+
+ if (fread(pDecompressedData, 1, nOriginalSize, f_in) != nOriginalSize) {
+ free(pDecompressedData);
+ fclose(f_in);
+ fprintf(stderr, "I/O error while reading '%s'\n", pszInFilename);
+ return 100;
+ }
+
+ fclose(f_in);
+
+ /* Allocate max compressed size */
+
+ nMaxCompressedSize = apultra_get_max_compressed_size(nOriginalSize);
+
+ pCompressedData = (unsigned char*)malloc(nMaxCompressedSize);
+ if (!pCompressedData) {
+ free(pDecompressedData);
+ fprintf(stderr, "out of memory for compressing '%s', %zd bytes needed\n", pszInFilename, nMaxCompressedSize);
+ return 100;
+ }
+
+ memset(pCompressedData, 0, nMaxCompressedSize);
+
+ nCompressedSize = apultra_compress(pDecompressedData, pCompressedData, nOriginalSize, nMaxCompressedSize, nFlags, nMaxWindowSize, compression_progress, &stats);
+
+ if ((nOptions & OPT_VERBOSE)) {
+ nEndTime = do_get_time();
+ }
+
+ if (nCompressedSize == -1) {
+ free(pCompressedData);
+ free(pDecompressedData);
+ fprintf(stderr, "compression error for '%s'\n", pszInFilename);
+ return 100;
+ }
+
+ if (pszOutFilename) {
+ FILE *f_out;
+
+ /* Write whole compressed file out */
+
+ f_out = fopen(pszOutFilename, "wb");
+ if (f_out) {
+ fwrite(pCompressedData, 1, nCompressedSize, f_out);
+ fclose(f_out);
+ }
+ }
+
+ free(pCompressedData);
+ free(pDecompressedData);
+
+ if ((nOptions & OPT_VERBOSE)) {
+ double fDelta = ((double)(nEndTime - nStartTime)) / 1000000.0;
+ double fSpeed = ((double)nOriginalSize / 1048576.0) / fDelta;
+ fprintf(stdout, "\rCompressed '%s' in %g seconds, %.02g Mb/s, %d tokens (%g bytes/token), %d into %d bytes ==> %g %%\n",
+ pszInFilename, fDelta, fSpeed, stats.commands_divisor, (double)nOriginalSize / (double)stats.commands_divisor,
+ (int)nOriginalSize, (int)nCompressedSize, (double)(nCompressedSize * 100.0 / nOriginalSize));
+ }
+
+ if (nOptions & OPT_STATS) {
+ fprintf(stdout, "Tokens: literals: %d short matches: %d normal matches: %d large matches: %d rep matches: %d\n",
+ stats.num_literals, stats.num_4bit_matches, stats.num_7bit_matches, stats.num_variable_matches, stats.num_rep_matches);
+ if (stats.match_divisor > 0) {
+ fprintf(stdout, "Offsets: min: %d avg: %d max: %d count: %d\n", stats.min_offset, (int)(stats.total_offsets / (long long)stats.match_divisor), stats.max_offset, stats.match_divisor);
+ fprintf(stdout, "Match lens: min: %d avg: %d max: %d count: %d\n", stats.min_match_len, stats.total_match_lens / stats.match_divisor, stats.max_match_len, stats.match_divisor);
+ }
+ else {
+ fprintf(stdout, "Offsets: none\n");
+ fprintf(stdout, "Match lens: none\n");
+ }
+ if (stats.rle1_divisor > 0) {
+ fprintf(stdout, "RLE1 lens: min: %d avg: %d max: %d count: %d\n", stats.min_rle1_len, stats.total_rle1_lens / stats.rle1_divisor, stats.max_rle1_len, stats.rle1_divisor);
+ }
+ else {
+ fprintf(stdout, "RLE1 lens: none\n");
+ }
+ if (stats.rle2_divisor > 0) {
+ fprintf(stdout, "RLE2 lens: min: %d avg: %d max: %d count: %d\n", stats.min_rle2_len, stats.total_rle2_lens / stats.rle2_divisor, stats.max_rle2_len, stats.rle2_divisor);
+ }
+ else {
+ fprintf(stdout, "RLE2 lens: none\n");
+ }
+ }
+ return 0;
+}
+
+/*---------------------------------------------------------------------------*/
+
+static int do_decompress(const char *pszInFilename, const char *pszOutFilename, const char *pszDictionaryFilename, const unsigned int nOptions) {
+ long long nStartTime = 0LL, nEndTime = 0LL;
+ size_t nCompressedSize, nMaxDecompressedSize, nOriginalSize;
+ unsigned char *pCompressedData;
+ unsigned char *pDecompressedData;
+ int nFlags;
+
+ nFlags = (nOptions & OPT_ENHANCED) ? APULTRA_FLAG_ENHANCED : 0;
+
+ /* Read the whole compressed file in memory */
+
+ FILE *f_in = fopen(pszInFilename, "rb");
+ if (!f_in) {
+ fprintf(stderr, "error opening '%s' for reading\n", pszInFilename);
+ return 100;
+ }
+
+ fseek(f_in, 0, SEEK_END);
+ nCompressedSize = (size_t)ftell(f_in);
+ fseek(f_in, 0, SEEK_SET);
+
+ pCompressedData = (unsigned char*)malloc(nCompressedSize);
+ if (!pCompressedData) {
+ fclose(f_in);
+ fprintf(stderr, "out of memory for reading '%s', %zd bytes needed\n", pszInFilename, nCompressedSize);
+ return 100;
+ }
+
+ if (fread(pCompressedData, 1, nCompressedSize, f_in) != nCompressedSize) {
+ free(pCompressedData);
+ fclose(f_in);
+ fprintf(stderr, "I/O error while reading '%s'\n", pszInFilename);
+ return 100;
+ }
+
+ fclose(f_in);
+
+ /* Allocate max decompressed size */
+
+ nMaxDecompressedSize = apultra_get_max_decompressed_size(pCompressedData, nCompressedSize, nFlags);
+ if (nMaxDecompressedSize == -1) {
+ free(pCompressedData);
+ fprintf(stderr, "invalid compressed format for file '%s'\n", pszInFilename);
+ return 100;
+ }
+
+ pDecompressedData = (unsigned char*)malloc(nMaxDecompressedSize);
+ if (!pDecompressedData) {
+ free(pCompressedData);
+ fprintf(stderr, "out of memory for decompressing '%s', %zd bytes needed\n", pszInFilename, nMaxDecompressedSize);
+ return 100;
+ }
+
+ memset(pDecompressedData, 0, nMaxDecompressedSize);
+
+ if (nOptions & OPT_VERBOSE) {
+ nStartTime = do_get_time();
+ }
+
+ nOriginalSize = apultra_decompress(pCompressedData, pDecompressedData, nCompressedSize, nMaxDecompressedSize, nFlags);
+ if (nOriginalSize == -1) {
+ free(pDecompressedData);
+ free(pCompressedData);
+
+ fprintf(stderr, "decompression error for '%s'\n", pszInFilename);
+ return 100;
+ }
+
+ if (pszOutFilename) {
+ FILE *f_out;
+
+ /* Write whole decompressed file out */
+
+ f_out = fopen(pszOutFilename, "wb");
+ if (f_out) {
+ fwrite(pDecompressedData, 1, nOriginalSize, f_out);
+ fclose(f_out);
+ }
+ }
+
+ free(pDecompressedData);
+ free(pCompressedData);
+
+ if (nOptions & OPT_VERBOSE) {
+ nEndTime = do_get_time();
+ double fDelta = ((double)(nEndTime - nStartTime)) / 1000000.0;
+ double fSpeed = ((double)nOriginalSize / 1048576.0) / fDelta;
+ fprintf(stdout, "Decompressed '%s' in %g seconds, %g Mb/s\n",
+ pszInFilename, fDelta, fSpeed);
+ }
+
+ return 0;
+}
+
+/*---------------------------------------------------------------------------*/
+
+static int do_compare(const char *pszInFilename, const char *pszOutFilename, const char *pszDictionaryFilename, const unsigned int nOptions) {
+ long long nStartTime = 0LL, nEndTime = 0LL;
+ size_t nCompressedSize, nMaxDecompressedSize, nOriginalSize, nDecompressedSize;
+ unsigned char *pCompressedData = NULL;
+ unsigned char *pOriginalData = NULL;
+ unsigned char *pDecompressedData = NULL;
+ int nFlags;
+
+ nFlags = (nOptions & OPT_ENHANCED) ? APULTRA_FLAG_ENHANCED : 0;
+
+ /* Read the whole compressed file in memory */
+
+ FILE *f_in = fopen(pszInFilename, "rb");
+ if (!f_in) {
+ fprintf(stderr, "error opening '%s' for reading\n", pszInFilename);
+ return 100;
+ }
+
+ fseek(f_in, 0, SEEK_END);
+ nCompressedSize = (size_t)ftell(f_in);
+ fseek(f_in, 0, SEEK_SET);
+
+ pCompressedData = (unsigned char*)malloc(nCompressedSize);
+ if (!pCompressedData) {
+ fclose(f_in);
+ fprintf(stderr, "out of memory for reading '%s', %zd bytes needed\n", pszInFilename, nCompressedSize);
+ return 100;
+ }
+
+ if (fread(pCompressedData, 1, nCompressedSize, f_in) != nCompressedSize) {
+ free(pCompressedData);
+ fclose(f_in);
+ fprintf(stderr, "I/O error while reading '%s'\n", pszInFilename);
+ return 100;
+ }
+
+ fclose(f_in);
+
+ /* Read the whole original file in memory */
+
+ f_in = fopen(pszOutFilename, "rb");
+ if (!f_in) {
+ free(pCompressedData);
+ fprintf(stderr, "error opening '%s' for reading\n", pszInFilename);
+ return 100;
+ }
+
+ fseek(f_in, 0, SEEK_END);
+ nOriginalSize = (size_t)ftell(f_in);
+ fseek(f_in, 0, SEEK_SET);
+
+ pOriginalData = (unsigned char*)malloc(nOriginalSize);
+ if (!pOriginalData) {
+ fclose(f_in);
+ free(pCompressedData);
+ fprintf(stderr, "out of memory for reading '%s', %zd bytes needed\n", pszInFilename, nOriginalSize);
+ return 100;
+ }
+
+ if (fread(pOriginalData, 1, nOriginalSize, f_in) != nOriginalSize) {
+ free(pOriginalData);
+ fclose(f_in);
+ free(pCompressedData);
+ fprintf(stderr, "I/O error while reading '%s'\n", pszInFilename);
+ return 100;
+ }
+
+ fclose(f_in);
+
+ /* Allocate max decompressed size */
+
+ nMaxDecompressedSize = apultra_get_max_decompressed_size(pCompressedData, nCompressedSize, nFlags);
+ if (nMaxDecompressedSize == -1) {
+ free(pOriginalData);
+ free(pCompressedData);
+ fprintf(stderr, "invalid compressed format for file '%s'\n", pszInFilename);
+ return 100;
+ }
+
+ pDecompressedData = (unsigned char*)malloc(nMaxDecompressedSize);
+ if (!pDecompressedData) {
+ free(pOriginalData);
+ free(pCompressedData);
+ fprintf(stderr, "out of memory for decompressing '%s', %zd bytes needed\n", pszInFilename, nMaxDecompressedSize);
+ return 100;
+ }
+
+ memset(pDecompressedData, 0, nMaxDecompressedSize);
+
+ if (nOptions & OPT_VERBOSE) {
+ nStartTime = do_get_time();
+ }
+
+ nDecompressedSize = apultra_decompress(pCompressedData, pDecompressedData, nCompressedSize, nMaxDecompressedSize, nFlags);
+ if (nDecompressedSize == -1) {
+ free(pDecompressedData);
+ free(pOriginalData);
+ free(pCompressedData);
+
+ fprintf(stderr, "decompression error for '%s'\n", pszInFilename);
+ return 100;
+ }
+
+ if (nDecompressedSize != nOriginalSize || memcmp(pDecompressedData, pOriginalData, nOriginalSize)) {
+ fprintf(stderr, "error comparing compressed file '%s' with original '%s'\n", pszInFilename, pszOutFilename);
+ return 100;
+ }
+
+ free(pDecompressedData);
+ free(pOriginalData);
+ free(pCompressedData);
+
+ if (nOptions & OPT_VERBOSE) {
+ nEndTime = do_get_time();
+ double fDelta = ((double)(nEndTime - nStartTime)) / 1000000.0;
+ double fSpeed = ((double)nOriginalSize / 1048576.0) / fDelta;
+ fprintf(stdout, "Compared '%s' in %g seconds, %g Mb/s\n",
+ pszInFilename, fDelta, fSpeed);
+ }
+
+ return 0;
+}
+
+/*---------------------------------------------------------------------------*/
+
+static void generate_compressible_data(unsigned char *pBuffer, size_t nBufferSize, unsigned int nSeed, int nNumLiteralValues, float fMatchProbability) {
+ size_t nIndex = 0;
+ int nMatchProbability = (int)(fMatchProbability * 1023.0f);
+
+ srand(nSeed);
+
+ if (nIndex >= nBufferSize) return;
+ pBuffer[nIndex++] = rand() % nNumLiteralValues;
+
+ while (nIndex < nBufferSize) {
+ if ((rand() & 1023) >= nMatchProbability) {
+ size_t nLiteralCount = rand() & 127;
+ if (nLiteralCount > (nBufferSize - nIndex))
+ nLiteralCount = nBufferSize - nIndex;
+
+ while (nLiteralCount--)
+ pBuffer[nIndex++] = rand() % nNumLiteralValues;
+ }
+ else {
+ size_t nMatchLength = MIN_MATCH_SIZE + (rand() & 1023);
+ size_t nMatchOffset;
+
+ if (nMatchLength > (nBufferSize - nIndex))
+ nMatchLength = nBufferSize - nIndex;
+ if (nMatchLength > nIndex)
+ nMatchLength = nIndex;
+
+ if (nMatchLength < nIndex)
+ nMatchOffset = rand() % (nIndex - nMatchLength);
+ else
+ nMatchOffset = 0;
+
+ while (nMatchLength--) {
+ pBuffer[nIndex] = pBuffer[nIndex - nMatchOffset];
+ nIndex++;
+ }
+ }
+ }
+}
+
+static void xor_data(unsigned char *pBuffer, size_t nBufferSize, unsigned int nSeed, float fXorProbability) {
+ size_t nIndex = 0;
+ int nXorProbability = (int)(fXorProbability * 1023.0f);
+
+ srand(nSeed);
+
+ if (nIndex >= nBufferSize) return;
+
+ while (nIndex < nBufferSize) {
+ if ((rand() & 1023) < nXorProbability) {
+ pBuffer[nIndex] ^= 0xff;
+ }
+ nIndex++;
+ }
+}
+
+static int do_self_test(const unsigned int nOptions, const unsigned int nMaxWindowSize, const int nIsQuickTest) {
+ unsigned char *pGeneratedData;
+ unsigned char *pCompressedData;
+ unsigned char *pTmpCompressedData;
+ unsigned char *pTmpDecompressedData;
+ size_t nGeneratedDataSize;
+ size_t nMaxCompressedDataSize;
+ unsigned int nSeed = 123;
+ int nFlags;
+ int i;
+
+ nFlags = (nOptions & OPT_ENHANCED) ? APULTRA_FLAG_ENHANCED : 0;
+
+ pGeneratedData = (unsigned char*)malloc(4 * BLOCK_SIZE);
+ if (!pGeneratedData) {
+ fprintf(stderr, "out of memory, %d bytes needed\n", 4 * BLOCK_SIZE);
+ return 100;
+ }
+
+ nMaxCompressedDataSize = apultra_get_max_compressed_size(4 * BLOCK_SIZE);
+ pCompressedData = (unsigned char*)malloc(nMaxCompressedDataSize);
+ if (!pCompressedData) {
+ free(pGeneratedData);
+ pGeneratedData = NULL;
+
+ fprintf(stderr, "out of memory, %zd bytes needed\n", nMaxCompressedDataSize);
+ return 100;
+ }
+
+ pTmpCompressedData = (unsigned char*)malloc(nMaxCompressedDataSize);
+ if (!pTmpCompressedData) {
+ free(pCompressedData);
+ pCompressedData = NULL;
+ free(pGeneratedData);
+ pGeneratedData = NULL;
+
+ fprintf(stderr, "out of memory, %zd bytes needed\n", nMaxCompressedDataSize);
+ return 100;
+ }
+
+ pTmpDecompressedData = (unsigned char*)malloc(4 * BLOCK_SIZE);
+ if (!pTmpDecompressedData) {
+ free(pTmpCompressedData);
+ pTmpCompressedData = NULL;
+ free(pCompressedData);
+ pCompressedData = NULL;
+ free(pGeneratedData);
+ pGeneratedData = NULL;
+
+ fprintf(stderr, "out of memory, %d bytes needed\n", 4 * BLOCK_SIZE);
+ return 100;
+ }
+
+ memset(pGeneratedData, 0, 4 * BLOCK_SIZE);
+ memset(pCompressedData, 0, nMaxCompressedDataSize);
+ memset(pTmpCompressedData, 0, nMaxCompressedDataSize);
+
+ /* Test compressing with a too small buffer to do anything, expect to fail cleanly */
+ for (i = 0; i < 12; i++) {
+ generate_compressible_data(pGeneratedData, i, nSeed, 256, 0.5f);
+ apultra_compress(pGeneratedData, pCompressedData, i, i, nFlags, nMaxWindowSize, NULL, NULL);
+ }
+
+ size_t nDataSizeStep = 128;
+ float fProbabilitySizeStep = nIsQuickTest ? 0.005f : 0.0005f;
+
+ for (nGeneratedDataSize = 1024; nGeneratedDataSize <= (nIsQuickTest ? 1024 : (4 * BLOCK_SIZE)); nGeneratedDataSize += nDataSizeStep) {
+ float fMatchProbability;
+
+ fprintf(stdout, "size %zd", nGeneratedDataSize);
+ for (fMatchProbability = 0; fMatchProbability <= 0.995f; fMatchProbability += fProbabilitySizeStep) {
+ int nNumLiteralValues[12] = { 1, 2, 3, 15, 30, 56, 96, 137, 178, 191, 255, 256 };
+ float fXorProbability;
+
+ fputc('.', stdout);
+ fflush(stdout);
+
+ for (i = 0; i < 12; i++) {
+ /* Generate data to compress */
+ generate_compressible_data(pGeneratedData, nGeneratedDataSize, nSeed, nNumLiteralValues[i], fMatchProbability);
+
+ /* Try to compress it, expected to succeed */
+ size_t nActualCompressedSize = apultra_compress(pGeneratedData, pCompressedData, nGeneratedDataSize, apultra_get_max_compressed_size(nGeneratedDataSize),
+ nFlags, nMaxWindowSize, NULL, NULL);
+ if (nActualCompressedSize == -1 || nActualCompressedSize < (1 + 1 + 1 /* footer */)) {
+ free(pTmpDecompressedData);
+ pTmpDecompressedData = NULL;
+ free(pTmpCompressedData);
+ pTmpCompressedData = NULL;
+ free(pCompressedData);
+ pCompressedData = NULL;
+ free(pGeneratedData);
+ pGeneratedData = NULL;
+
+ fprintf(stderr, "\nself-test: error compressing size %zd, seed %d, match probability %f, literals range %d\n", nGeneratedDataSize, nSeed, fMatchProbability, nNumLiteralValues[i]);
+ return 100;
+ }
+
+ /* Try to decompress it, expected to succeed */
+ size_t nActualDecompressedSize;
+ nActualDecompressedSize = apultra_decompress(pCompressedData, pTmpDecompressedData, nActualCompressedSize, nGeneratedDataSize, nFlags);
+ if (nActualDecompressedSize == -1) {
+ free(pTmpDecompressedData);
+ pTmpDecompressedData = NULL;
+ free(pTmpCompressedData);
+ pTmpCompressedData = NULL;
+ free(pCompressedData);
+ pCompressedData = NULL;
+ free(pGeneratedData);
+ pGeneratedData = NULL;
+
+ fprintf(stderr, "\nself-test: error decompressing size %zd, seed %d, match probability %f, literals range %d\n", nGeneratedDataSize, nSeed, fMatchProbability, nNumLiteralValues[i]);
+ return 100;
+ }
+
+ if (memcmp(pGeneratedData, pTmpDecompressedData, nGeneratedDataSize)) {
+ free(pTmpDecompressedData);
+ pTmpDecompressedData = NULL;
+ free(pTmpCompressedData);
+ pTmpCompressedData = NULL;
+ free(pCompressedData);
+ pCompressedData = NULL;
+ free(pGeneratedData);
+ pGeneratedData = NULL;
+
+ fprintf(stderr, "\nself-test: error comparing decompressed and original data, size %zd, seed %d, match probability %f, literals range %d\n", nGeneratedDataSize, nSeed, fMatchProbability, nNumLiteralValues[i]);
+ return 100;
+ }
+
+ /* Try to decompress corrupted data, expected to fail cleanly, without crashing or corrupting memory outside the output buffer */
+ for (fXorProbability = 0.05f; fXorProbability <= 0.5f; fXorProbability += 0.05f) {
+ memcpy(pTmpCompressedData, pCompressedData, nActualCompressedSize);
+ xor_data(pTmpCompressedData, nActualCompressedSize, nSeed, fXorProbability);
+ apultra_decompress(pTmpCompressedData, pGeneratedData, nActualCompressedSize, nGeneratedDataSize, nFlags);
+ }
+ }
+
+ nSeed++;
+ }
+
+ fputc(10, stdout);
+ fflush(stdout);
+
+ nDataSizeStep <<= 1;
+ if (nDataSizeStep > (128 * 4096))
+ nDataSizeStep = 128 * 4096;
+ fProbabilitySizeStep *= 1.25;
+ if (fProbabilitySizeStep > (0.0005f * 4096))
+ fProbabilitySizeStep = 0.0005f * 4096;
+ }
+
+ free(pTmpDecompressedData);
+ pTmpDecompressedData = NULL;
+
+ free(pTmpCompressedData);
+ pTmpCompressedData = NULL;
+
+ free(pCompressedData);
+ pCompressedData = NULL;
+
+ free(pGeneratedData);
+ pGeneratedData = NULL;
+
+ fprintf(stdout, "All tests passed.\n");
+ return 0;
+}
+
+/*---------------------------------------------------------------------------*/
+
+static int do_compr_benchmark(const char *pszInFilename, const char *pszOutFilename, const char *pszDictionaryFilename, const unsigned int nOptions, const unsigned int nMaxWindowSize) {
+ size_t nFileSize, nMaxCompressedSize;
+ unsigned char *pFileData;
+ unsigned char *pCompressedData;
+ int nFlags;
+ int i;
+
+ nFlags = (nOptions & OPT_ENHANCED) ? APULTRA_FLAG_ENHANCED : 0;
+
+ if (pszDictionaryFilename) {
+ fprintf(stderr, "in-memory benchmarking does not support dictionaries\n");
+ return 100;
+ }
+
+ /* Read the whole original file in memory */
+
+ FILE *f_in = fopen(pszInFilename, "rb");
+ if (!f_in) {
+ fprintf(stderr, "error opening '%s' for reading\n", pszInFilename);
+ return 100;
+ }
+
+ fseek(f_in, 0, SEEK_END);
+ nFileSize = (size_t)ftell(f_in);
+ fseek(f_in, 0, SEEK_SET);
+
+ pFileData = (unsigned char*)malloc(nFileSize);
+ if (!pFileData) {
+ fclose(f_in);
+ fprintf(stderr, "out of memory for reading '%s', %zd bytes needed\n", pszInFilename, nFileSize);
+ return 100;
+ }
+
+ if (fread(pFileData, 1, nFileSize, f_in) != nFileSize) {
+ free(pFileData);
+ fclose(f_in);
+ fprintf(stderr, "I/O error while reading '%s'\n", pszInFilename);
+ return 100;
+ }
+
+ fclose(f_in);
+
+ /* Allocate max compressed size */
+
+ nMaxCompressedSize = apultra_get_max_compressed_size(nFileSize);
+
+ pCompressedData = (unsigned char*)malloc(nMaxCompressedSize + 2048);
+ if (!pCompressedData) {
+ free(pFileData);
+ fprintf(stderr, "out of memory for compressing '%s', %zd bytes needed\n", pszInFilename, nMaxCompressedSize);
+ return 100;
+ }
+
+ memset(pCompressedData + 1024, 0, nMaxCompressedSize);
+
+ long long nBestCompTime = -1;
+
+ size_t nActualCompressedSize = 0;
+ size_t nRightGuardPos = nMaxCompressedSize;
+
+ for (i = 0; i < 5; i++) {
+ unsigned char nGuard = 0x33 + i;
+ int j;
+
+ /* Write guard bytes around the output buffer, to help check for writes outside of it by the compressor */
+ memset(pCompressedData, nGuard, 1024);
+ memset(pCompressedData + 1024 + nRightGuardPos, nGuard, 1024);
+
+ long long t0 = do_get_time();
+ nActualCompressedSize = apultra_compress(pFileData, pCompressedData + 1024, nFileSize, nRightGuardPos, nFlags, nMaxWindowSize, NULL, NULL);
+ long long t1 = do_get_time();
+ if (nActualCompressedSize == -1) {
+ free(pCompressedData);
+ free(pFileData);
+ fprintf(stderr, "compression error\n");
+ return 100;
+ }
+
+ long long nCurDecTime = t1 - t0;
+ if (nBestCompTime == -1 || nBestCompTime > nCurDecTime)
+ nBestCompTime = nCurDecTime;
+
+ /* Check guard bytes before the output buffer */
+ for (j = 0; j < 1024; j++) {
+ if (pCompressedData[j] != nGuard) {
+ free(pCompressedData);
+ free(pFileData);
+ fprintf(stderr, "error, wrote outside of output buffer at %d!\n", j - 1024);
+ return 100;
+ }
+ }
+
+ /* Check guard bytes after the output buffer */
+ for (j = 0; j < 1024; j++) {
+ if (pCompressedData[1024 + nRightGuardPos + j] != nGuard) {
+ free(pCompressedData);
+ free(pFileData);
+ fprintf(stderr, "error, wrote outside of output buffer at %d!\n", j);
+ return 100;
+ }
+ }
+
+ nRightGuardPos = nActualCompressedSize;
+ }
+
+ if (pszOutFilename) {
+ FILE *f_out;
+
+ /* Write whole compressed file out */
+
+ f_out = fopen(pszOutFilename, "wb");
+ if (f_out) {
+ fwrite(pCompressedData + 1024, 1, nActualCompressedSize, f_out);
+ fclose(f_out);
+ }
+ }
+
+ free(pCompressedData);
+ free(pFileData);
+
+ fprintf(stdout, "compressed size: %zd bytes\n", nActualCompressedSize);
+ fprintf(stdout, "compression time: %lld microseconds (%g Mb/s)\n", nBestCompTime, ((double)nActualCompressedSize / 1024.0) / ((double)nBestCompTime / 1000.0));
+
+ return 0;
+}
+
+/*---------------------------------------------------------------------------*/
+
+static int do_dec_benchmark(const char *pszInFilename, const char *pszOutFilename, const char *pszDictionaryFilename, const unsigned int nOptions) {
+ size_t nFileSize, nMaxDecompressedSize;
+ unsigned char *pFileData;
+ unsigned char *pDecompressedData;
+ int nFlags;
+ int i;
+
+ nFlags = (nOptions & OPT_ENHANCED) ? APULTRA_FLAG_ENHANCED : 0;
+
+ if (pszDictionaryFilename) {
+ fprintf(stderr, "in-memory benchmarking does not support dictionaries\n");
+ return 100;
+ }
+
+ /* Read the whole compressed file in memory */
+
+ FILE *f_in = fopen(pszInFilename, "rb");
+ if (!f_in) {
+ fprintf(stderr, "error opening '%s' for reading\n", pszInFilename);
+ return 100;
+ }
+
+ fseek(f_in, 0, SEEK_END);
+ nFileSize = (size_t)ftell(f_in);
+ fseek(f_in, 0, SEEK_SET);
+
+ pFileData = (unsigned char*)malloc(nFileSize);
+ if (!pFileData) {
+ fclose(f_in);
+ fprintf(stderr, "out of memory for reading '%s', %zd bytes needed\n", pszInFilename, nFileSize);
+ return 100;
+ }
+
+ if (fread(pFileData, 1, nFileSize, f_in) != nFileSize) {
+ free(pFileData);
+ fclose(f_in);
+ fprintf(stderr, "I/O error while reading '%s'\n", pszInFilename);
+ return 100;
+ }
+
+ fclose(f_in);
+
+ /* Allocate max decompressed size */
+
+ nMaxDecompressedSize = apultra_get_max_decompressed_size(pFileData, nFileSize, nFlags);
+ if (nMaxDecompressedSize == -1) {
+ free(pFileData);
+ fprintf(stderr, "invalid compressed format for file '%s'\n", pszInFilename);
+ return 100;
+ }
+
+ pDecompressedData = (unsigned char*)malloc(nMaxDecompressedSize);
+ if (!pDecompressedData) {
+ free(pFileData);
+ fprintf(stderr, "out of memory for decompressing '%s', %zd bytes needed\n", pszInFilename, nMaxDecompressedSize);
+ return 100;
+ }
+
+ memset(pDecompressedData, 0, nMaxDecompressedSize);
+
+ long long nBestDecTime = -1;
+
+ size_t nActualDecompressedSize = 0;
+ for (i = 0; i < 50; i++) {
+ long long t0 = do_get_time();
+ nActualDecompressedSize = apultra_decompress(pFileData, pDecompressedData, nFileSize, nMaxDecompressedSize, nFlags);
+ long long t1 = do_get_time();
+ if (nActualDecompressedSize == -1) {
+ free(pDecompressedData);
+ free(pFileData);
+ fprintf(stderr, "decompression error\n");
+ return 100;
+ }
+
+ long long nCurDecTime = t1 - t0;
+ if (nBestDecTime == -1 || nBestDecTime > nCurDecTime)
+ nBestDecTime = nCurDecTime;
+ }
+
+ if (pszOutFilename) {
+ FILE *f_out;
+
+ /* Write whole decompressed file out */
+
+ f_out = fopen(pszOutFilename, "wb");
+ if (f_out) {
+ fwrite(pDecompressedData, 1, nActualDecompressedSize, f_out);
+ fclose(f_out);
+ }
+ }
+
+ free(pDecompressedData);
+ free(pFileData);
+
+ fprintf(stdout, "decompressed size: %zd bytes\n", nActualDecompressedSize);
+ fprintf(stdout, "decompression time: %lld microseconds (%g Mb/s)\n", nBestDecTime, ((double)nActualDecompressedSize / 1024.0) / ((double)nBestDecTime / 1000.0));
+
+ return 0;
+}
+
+/*---------------------------------------------------------------------------*/
+
+int main(int argc, char **argv) {
+ int i;
+ const char *pszInFilename = NULL;
+ const char *pszOutFilename = NULL;
+ const char *pszDictionaryFilename = NULL;
+ bool bArgsError = false;
+ bool bCommandDefined = false;
+ bool bVerifyCompression = false;
+ bool bMinMatchDefined = false;
+ bool bFormatVersionDefined = false;
+ char cCommand = 'z';
+ unsigned int nOptions = 0;
+ unsigned int nMaxWindowSize = 0;
+
+ for (i = 1; i < argc; i++) {
+ if (!strcmp(argv[i], "-d")) {
+ if (!bCommandDefined) {
+ bCommandDefined = true;
+ cCommand = 'd';
+ }
+ else
+ bArgsError = true;
+ }
+ else if (!strcmp(argv[i], "-z")) {
+ if (!bCommandDefined) {
+ bCommandDefined = true;
+ cCommand = 'z';
+ }
+ else
+ bArgsError = true;
+ }
+ else if (!strcmp(argv[i], "-c")) {
+ if (!bVerifyCompression) {
+ bVerifyCompression = true;
+ }
+ else
+ bArgsError = true;
+ }
+ else if (!strcmp(argv[i], "-cbench")) {
+ if (!bCommandDefined) {
+ bCommandDefined = true;
+ cCommand = 'B';
+ }
+ else
+ bArgsError = true;
+ }
+ else if (!strcmp(argv[i], "-dbench")) {
+ if (!bCommandDefined) {
+ bCommandDefined = true;
+ cCommand = 'b';
+ }
+ else
+ bArgsError = true;
+ }
+ else if (!strcmp(argv[i], "-test")) {
+ if (!bCommandDefined) {
+ bCommandDefined = true;
+ cCommand = 't';
+ }
+ else
+ bArgsError = true;
+ }
+ else if (!strcmp(argv[i], "-quicktest")) {
+ if (!bCommandDefined) {
+ bCommandDefined = true;
+ cCommand = 'T';
+ }
+ else
+ bArgsError = true;
+ }
+ else if (!strcmp(argv[i], "-D")) {
+ if (!pszDictionaryFilename && (i + 1) < argc) {
+ pszDictionaryFilename = argv[i + 1];
+ i++;
+ }
+ else
+ bArgsError = true;
+ }
+ else if (!strncmp(argv[i], "-D", 2)) {
+ if (!pszDictionaryFilename) {
+ pszDictionaryFilename = argv[i] + 2;
+ }
+ else
+ bArgsError = true;
+ }
+ else if (!strcmp(argv[i], "-v")) {
+ if ((nOptions & OPT_VERBOSE) == 0) {
+ nOptions |= OPT_VERBOSE;
+ }
+ else
+ bArgsError = true;
+ }
+ else if (!strcmp(argv[i], "-e")) {
+ if ((nOptions & OPT_ENHANCED) == 0) {
+ nOptions |= OPT_ENHANCED;
+ }
+ else
+ bArgsError = true;
+ }
+ else if (!strcmp(argv[i], "-w")) {
+ if (!nMaxWindowSize && (i + 1) < argc) {
+ char *pEnd = NULL;
+ nMaxWindowSize = (int)strtol(argv[i + 1], &pEnd, 10);
+ if (pEnd && pEnd != argv[i + 1] && (nMaxWindowSize >= 16 && nMaxWindowSize <= 0x200000)) {
+ i++;
+ }
+ else {
+ bArgsError = true;
+ }
+ }
+ else
+ bArgsError = true;
+ }
+ else if (!strncmp(argv[i], "-w", 2)) {
+ if (!nMaxWindowSize) {
+ char *pEnd = NULL;
+ nMaxWindowSize = (int)strtol(argv[i] + 2, &pEnd, 10);
+ if (pEnd && pEnd != (argv[i] + 2) && (nMaxWindowSize >= 16 && nMaxWindowSize <= 0x200000)) {
+ bFormatVersionDefined = true;
+ }
+ else {
+ bArgsError = true;
+ }
+ }
+ else
+ bArgsError = true;
+ }
+ else if (!strcmp(argv[i], "-stats")) {
+ if ((nOptions & OPT_STATS) == 0) {
+ nOptions |= OPT_STATS;
+ }
+ else
+ bArgsError = true;
+ }
+ else {
+ if (!pszInFilename)
+ pszInFilename = argv[i];
+ else {
+ if (!pszOutFilename)
+ pszOutFilename = argv[i];
+ else
+ bArgsError = true;
+ }
+ }
+ }
+
+ if (!bArgsError && cCommand == 't') {
+ return do_self_test(nOptions, nMaxWindowSize, 0);
+ }
+ else if (!bArgsError && cCommand == 'T') {
+ return do_self_test(nOptions, nMaxWindowSize, 1);
+ }
+
+ if (bArgsError || !pszInFilename || !pszOutFilename) {
+ fprintf(stderr, "apultra command-line tool v" TOOL_VERSION " by Emmanuel Marty and spke\n");
+ fprintf(stderr, "usage: %s [-c] [-d] [-v] [-r] <infile> <outfile>\n", argv[0]);
+ fprintf(stderr, " -c: check resulting stream after compressing\n");
+ fprintf(stderr, " -d: decompress (default: compress)\n");
+ fprintf(stderr, " -e: use enhanced (incompatible) format for 8-bit micros\n");
+ fprintf(stderr, " -w <size>: maximum window size, in bytes (16..2097152), defaults to maximum\n");
+ fprintf(stderr, " -cbench: benchmark in-memory compression\n");
+ fprintf(stderr, " -dbench: benchmark in-memory decompression\n");
+ fprintf(stderr, " -test: run full automated self-tests\n");
+ fprintf(stderr, "-quicktest: run quick automated self-tests\n");
+ fprintf(stderr, " -stats: show compressed data stats\n");
+ fprintf(stderr, " -v: be verbose\n");
+ return 100;
+ }
+
+ do_init_time();
+
+ if (cCommand == 'z') {
+ int nResult = do_compress(pszInFilename, pszOutFilename, pszDictionaryFilename, nOptions, nMaxWindowSize);
+ if (nResult == 0 && bVerifyCompression) {
+ return do_compare(pszOutFilename, pszInFilename, pszDictionaryFilename, nOptions);
+ } else {
+ return nResult;
+ }
+ }
+ else if (cCommand == 'd') {
+ return do_decompress(pszInFilename, pszOutFilename, pszDictionaryFilename, nOptions);
+ }
+ else if (cCommand == 'B') {
+ return do_compr_benchmark(pszInFilename, pszOutFilename, pszDictionaryFilename, nOptions, nMaxWindowSize);
+ }
+ else if (cCommand == 'b') {
+ return do_dec_benchmark(pszInFilename, pszOutFilename, pszDictionaryFilename, nOptions);
+ }
+ else {
+ return 100;
+ }
+}
diff --git a/tools/apultra_src/src/expand.c b/tools/apultra_src/src/expand.c new file mode 100644 index 0000000..1e07bda --- /dev/null +++ b/tools/apultra_src/src/expand.c @@ -0,0 +1,445 @@ +/*
+ * expand.c - decompressor implementation
+ *
+ * Copyright (C) 2019 Emmanuel Marty
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty. In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ */
+
+/*
+ * Uses the libdivsufsort library Copyright (c) 2003-2008 Yuta Mori
+ *
+ * Inspired by cap by Sven-Åke Dahl. https://github.com/svendahl/cap
+ * Also inspired by Charles Bloom's compression blog. http://cbloomrants.blogspot.com/
+ * With ideas from LZ4 by Yann Collet. https://github.com/lz4/lz4
+ * With help and support from spke <zxintrospec@gmail.com>
+ *
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include "format.h"
+#include "expand.h"
+#include "libapultra.h"
+
+#ifdef _MSC_VER
+#define FORCE_INLINE __forceinline
+#else /* _MSC_VER */
+#define FORCE_INLINE __attribute__((always_inline))
+#endif /* _MSC_VER */
+
+static inline FORCE_INLINE int apultra_read_bit(const unsigned char **ppInBlock, const unsigned char *pDataEnd, int *nCurBitMask, unsigned char *bits, const int nBitBufferIdx) {
+ const unsigned char *pInBlock = *ppInBlock;
+ int nBit;
+
+ if (nCurBitMask[nBitBufferIdx] == 0) {
+ if (pInBlock >= pDataEnd) return -1;
+ bits[nBitBufferIdx] = *pInBlock++;
+ nCurBitMask[nBitBufferIdx] = 128;
+ }
+
+ nBit = (bits[nBitBufferIdx] & 128) ? 1 : 0;
+
+ bits[nBitBufferIdx] <<= 1;
+ nCurBitMask[nBitBufferIdx] >>= 1;
+
+ *ppInBlock = pInBlock;
+ return nBit;
+}
+
+static inline FORCE_INLINE int apultra_read_gamma2(const unsigned char **ppInBlock, const unsigned char *pDataEnd, int *nCurBitMask, unsigned char *bits, const int nBitBufferIdx) {
+ int bit;
+ unsigned int v = 1;
+
+ if (nBitBufferIdx == 0) {
+ /* Standard aPLib encoding. */
+ do {
+ v = (v << 1) + apultra_read_bit(ppInBlock, pDataEnd, nCurBitMask, bits, nBitBufferIdx);
+ bit = apultra_read_bit(ppInBlock, pDataEnd, nCurBitMask, bits, nBitBufferIdx);
+ if (bit < 0) return bit;
+ } while (bit);
+ }
+ else {
+ /* Enhanced encoding for 8-bit microprocessors ...
+ * 1) Write out values of 256 and higher lo-byte first (so the gamma2 decoder only needs to rotate a byte).
+ * 2) Swap meaning of continue/stop bits (saves a byte on the 6502, with no effect on other platforms).
+ */
+ int l = 0;
+
+ do {
+ if ((l == 0) && (v >= 256)) {
+ l = v;
+ v = 1;
+ }
+ v = (v << 1) + apultra_read_bit(ppInBlock, pDataEnd, nCurBitMask, bits, nBitBufferIdx);
+ bit = apultra_read_bit(ppInBlock, pDataEnd, nCurBitMask, bits, nBitBufferIdx);
+ if (bit < 0) return bit;
+ } while (bit == 0);
+
+ if (l != 0) {
+ v = (v << 8) + (l & 255);
+ }
+ }
+
+ return v;
+}
+
+/**
+ * Get maximum decompressed size of compressed data
+ *
+ * @param pInputData compressed data
+ * @param nInputSize compressed size in bytes
+ * @param nFlags compression flags (a bitmask of APULTRA_FLAG_xxx, or 0)
+ *
+ * @return maximum decompressed size
+ */
+size_t apultra_get_max_decompressed_size(const unsigned char *pInputData, size_t nInputSize, const unsigned int nFlags) {
+ const unsigned char *pInputDataEnd = pInputData + nInputSize;
+ int nCurBitMask[3] = { 0, 0, 0 };
+ unsigned char bits[3] = { 0, 0, 0 };
+ int nMatchOffset = 1;
+ int nFollowsLiteral = 1;
+ size_t nDecompressedSize = 0;
+ int nSingleBitBufferIdx = 0;
+ int nGammaBitBufferIdx = (nFlags & APULTRA_FLAG_ENHANCED) ? 1 : 0;
+ int nNibblesBitBufferIdx = (nFlags & APULTRA_FLAG_ENHANCED) ? 2 : 0;
+
+ if (pInputData >= pInputDataEnd)
+ return -1;
+ pInputData++;
+ nDecompressedSize++;
+
+ while (1) {
+ unsigned int nResult;
+
+ nResult = apultra_read_bit(&pInputData, pInputDataEnd, nCurBitMask, bits, nSingleBitBufferIdx);
+ if (nResult < 0) return -1;
+
+ if (!nResult) {
+ /* '0': literal */
+ if (pInputData < pInputDataEnd) {
+ pInputData++;
+ nDecompressedSize++;
+ nFollowsLiteral = 1;
+ }
+ else {
+ return -1;
+ }
+ }
+ else {
+ nResult = apultra_read_bit(&pInputData, pInputDataEnd, nCurBitMask, bits, nSingleBitBufferIdx);
+ if (nResult < 0) return -1;
+
+ if (nResult == 0) {
+ unsigned int nMatchLen;
+ unsigned int nMatchLenBias = 0;
+ unsigned int nIsRepMatch = 0;
+
+ /* '10': 8+n bits offset */
+ unsigned int nMatchOffsetHi = apultra_read_gamma2(&pInputData, pInputDataEnd, nCurBitMask, bits, nGammaBitBufferIdx);
+ if (nFollowsLiteral == 0 || nMatchOffsetHi != 2) {
+ if (nFollowsLiteral)
+ nMatchOffset = (nMatchOffsetHi - 3) << 8;
+ else
+ nMatchOffset = (nMatchOffsetHi - 2) << 8;
+ nMatchOffset |= (unsigned int)(*pInputData++);
+
+ if (nMatchOffset < 128)
+ nMatchLenBias = 2;
+ }
+ else {
+ /* else rep-match */
+ nIsRepMatch = 1;
+ }
+
+ nFollowsLiteral = 0;
+ nMatchLen = apultra_read_gamma2(&pInputData, pInputDataEnd, nCurBitMask, bits, nGammaBitBufferIdx);
+
+ if (!nIsRepMatch) {
+ if (nMatchOffset >= MINMATCH3_OFFSET)
+ nMatchLen++;
+ if (nMatchOffset >= MINMATCH4_OFFSET)
+ nMatchLen++;
+ }
+
+ nMatchLen += nMatchLenBias;
+ nDecompressedSize += nMatchLen;
+ }
+ else {
+ nResult = apultra_read_bit(&pInputData, pInputDataEnd, nCurBitMask, bits, nSingleBitBufferIdx);
+ if (nResult < 0) return -1;
+
+ if (nResult == 0) {
+ unsigned int nCommand;
+ unsigned int nMatchLen;
+
+ /* '110': 7 bits offset + 1 bit length */
+ nCommand = (unsigned int)(*pInputData++);
+ if (nCommand == 0x00) {
+ /* EOD. No match len follows. */
+ break;
+ }
+
+ /* Bits 7-1: offset; bit 0: length */
+ nMatchOffset = (nCommand >> 1);
+ nMatchLen = (nCommand & 1) + 2;
+
+ nFollowsLiteral = 0;
+ nDecompressedSize += nMatchLen;
+ }
+ else {
+ unsigned int nShortMatchOffset;
+
+ /* '111': 4 bit offset */
+ nResult = apultra_read_bit(&pInputData, pInputDataEnd, nCurBitMask, bits, nNibblesBitBufferIdx);
+ if (nResult < 0) return -1;
+ nShortMatchOffset = nResult << 3;
+
+ nResult = apultra_read_bit(&pInputData, pInputDataEnd, nCurBitMask, bits, nNibblesBitBufferIdx);
+ if (nResult < 0) return -1;
+ nShortMatchOffset |= nResult << 2;
+
+ nResult = apultra_read_bit(&pInputData, pInputDataEnd, nCurBitMask, bits, nNibblesBitBufferIdx);
+ if (nResult < 0) return -1;
+ nShortMatchOffset |= nResult << 1;
+
+ nResult = apultra_read_bit(&pInputData, pInputDataEnd, nCurBitMask, bits, nNibblesBitBufferIdx);
+ if (nResult < 0) return -1;
+ nShortMatchOffset |= nResult << 0;
+
+ nFollowsLiteral = 1;
+ nDecompressedSize++;
+ }
+ }
+ }
+ }
+
+ return nDecompressedSize;
+}
+
+/**
+ * Decompress data in memory
+ *
+ * @param pInputData compressed data
+ * @param pOutBuffer buffer for decompressed data
+ * @param nInputSize compressed size in bytes
+ * @param nMaxOutBufferSize maximum capacity of decompression buffer
+ * @param nFlags compression flags (a bitmask of APULTRA_FLAG_xxx, or 0)
+ *
+ * @return actual decompressed size, or -1 for error
+ */
+size_t apultra_decompress(const unsigned char *pInputData, unsigned char *pOutData, size_t nInputSize, size_t nMaxOutBufferSize, const unsigned int nFlags) {
+ const unsigned char *pInputDataEnd = pInputData + nInputSize;
+ unsigned char *pCurOutData = pOutData;
+ const unsigned char *pOutDataEnd = pCurOutData + nMaxOutBufferSize;
+ const unsigned char *pOutDataFastEnd = pOutDataEnd - 20;
+ int nCurBitMask[3] = { 0, 0, 0 };
+ unsigned char bits[3] = { 0, 0, 0 };
+ int nMatchOffset = 1;
+ int nFollowsLiteral = 1;
+ int nSingleBitBufferIdx = 0;
+ int nGammaBitBufferIdx = (nFlags & APULTRA_FLAG_ENHANCED) ? 1 : 0;
+ int nNibblesBitBufferIdx = (nFlags & APULTRA_FLAG_ENHANCED) ? 2 : 0;
+
+ if (pInputData >= pInputDataEnd && pCurOutData < pOutDataEnd)
+ return -1;
+ *pCurOutData++ = *pInputData++;
+
+ while (1) {
+ unsigned int nResult;
+
+ nResult = apultra_read_bit(&pInputData, pInputDataEnd, nCurBitMask, bits, nSingleBitBufferIdx);
+ if (nResult < 0) return -1;
+
+ if (!nResult) {
+ /* '0': literal */
+ if (pInputData < pInputDataEnd && pCurOutData < pOutDataEnd) {
+ *pCurOutData++ = *pInputData++;
+ nFollowsLiteral = 1;
+ }
+ else {
+ return -1;
+ }
+ }
+ else {
+ nResult = apultra_read_bit(&pInputData, pInputDataEnd, nCurBitMask, bits, nSingleBitBufferIdx);
+ if (nResult < 0) return -1;
+
+ if (nResult == 0) {
+ unsigned int nMatchLen;
+ unsigned int nMatchLenBias = 0;
+ unsigned int nIsRepMatch = 0;
+
+ /* '10': 8+n bits offset */
+ unsigned int nMatchOffsetHi = apultra_read_gamma2(&pInputData, pInputDataEnd, nCurBitMask, bits, nGammaBitBufferIdx);
+ if (nFollowsLiteral == 0 || nMatchOffsetHi != 2) {
+ if (nFollowsLiteral)
+ nMatchOffset = (nMatchOffsetHi - 3) << 8;
+ else
+ nMatchOffset = (nMatchOffsetHi - 2) << 8;
+ nMatchOffset |= (unsigned int)(*pInputData++);
+
+ if (nMatchOffset < 128)
+ nMatchLenBias = 2;
+ }
+ else {
+ /* else rep-match */
+ nIsRepMatch = 1;
+ }
+
+ nFollowsLiteral = 0;
+ const unsigned char *pSrc = pCurOutData - nMatchOffset;
+ if (pSrc >= pOutData) {
+ nMatchLen = apultra_read_gamma2(&pInputData, pInputDataEnd, nCurBitMask, bits, nGammaBitBufferIdx);
+
+ if (!nIsRepMatch) {
+ if (nMatchOffset >= MINMATCH3_OFFSET)
+ nMatchLen++;
+ if (nMatchOffset >= MINMATCH4_OFFSET)
+ nMatchLen++;
+ }
+
+ nMatchLen += nMatchLenBias;
+
+ if (nMatchLen < 11 && nMatchOffset >= 8 && pCurOutData < pOutDataFastEnd) {
+ memcpy(pCurOutData, pSrc, 8);
+ memcpy(pCurOutData + 8, pSrc + 8, 2);
+ pCurOutData += nMatchLen;
+ }
+ else {
+ if ((pCurOutData + nMatchLen) <= pOutDataEnd && (pSrc + nMatchLen) <= pOutDataEnd) {
+ /* Do a deterministic, left to right byte copy instead of memcpy() so as to handle overlaps */
+
+ if (nMatchOffset >= 16 && (pCurOutData + nMatchLen) < (pOutDataFastEnd - 15)) {
+ const unsigned char *pCopySrc = pSrc;
+ unsigned char *pCopyDst = pCurOutData;
+ const unsigned char *pCopyEndDst = pCurOutData + nMatchLen;
+
+ do {
+ memcpy(pCopyDst, pCopySrc, 16);
+ pCopySrc += 16;
+ pCopyDst += 16;
+ } while (pCopyDst < pCopyEndDst);
+
+ pCurOutData += nMatchLen;
+ }
+ else {
+ while (nMatchLen) {
+ *pCurOutData++ = *pSrc++;
+ nMatchLen--;
+ }
+ }
+ }
+ else {
+ return -1;
+ }
+ }
+ }
+ else {
+ return -1;
+ }
+ }
+ else {
+ nResult = apultra_read_bit(&pInputData, pInputDataEnd, nCurBitMask, bits, nSingleBitBufferIdx);
+ if (nResult < 0) return -1;
+
+ if (nResult == 0) {
+ unsigned int nCommand;
+ unsigned int nMatchLen;
+
+ /* '110': 7 bits offset + 1 bit length */
+ nCommand = (unsigned int)(*pInputData++);
+ if (nCommand == 0x00) {
+ /* EOD. No match len follows. */
+ break;
+ }
+
+ /* Bits 7-1: offset; bit 0: length */
+ nMatchOffset = (nCommand >> 1);
+ nMatchLen = (nCommand & 1) + 2;
+
+ nFollowsLiteral = 0;
+ const unsigned char *pSrc = pCurOutData - nMatchOffset;
+ if (pSrc >= pOutData && (pSrc + nMatchLen) <= pOutDataEnd) {
+ if (nMatchOffset >= 8 && pCurOutData < pOutDataFastEnd) {
+ memcpy(pCurOutData, pSrc, 8);
+ memcpy(pCurOutData + 8, pSrc + 8, 2);
+ pCurOutData += nMatchLen;
+ }
+ else {
+ if ((pCurOutData + nMatchLen) <= pOutDataEnd) {
+ while (nMatchLen) {
+ *pCurOutData++ = *pSrc++;
+ nMatchLen--;
+ }
+ }
+ else {
+ return -1;
+ }
+ }
+ }
+ else {
+ return -1;
+ }
+ }
+ else {
+ unsigned int nShortMatchOffset;
+
+ /* '111': 4 bit offset */
+ nResult = apultra_read_bit(&pInputData, pInputDataEnd, nCurBitMask, bits, nNibblesBitBufferIdx);
+ if (nResult < 0) return -1;
+ nShortMatchOffset = nResult << 3;
+
+ nResult = apultra_read_bit(&pInputData, pInputDataEnd, nCurBitMask, bits, nNibblesBitBufferIdx);
+ if (nResult < 0) return -1;
+ nShortMatchOffset |= nResult << 2;
+
+ nResult = apultra_read_bit(&pInputData, pInputDataEnd, nCurBitMask, bits, nNibblesBitBufferIdx);
+ if (nResult < 0) return -1;
+ nShortMatchOffset |= nResult << 1;
+
+ nResult = apultra_read_bit(&pInputData, pInputDataEnd, nCurBitMask, bits, nNibblesBitBufferIdx);
+ if (nResult < 0) return -1;
+ nShortMatchOffset |= nResult << 0;
+
+ nFollowsLiteral = 1;
+ if (nShortMatchOffset) {
+ /* Short offset, 1-15 */
+ const unsigned char *pSrc = pCurOutData - nShortMatchOffset;
+ if (pSrc >= pOutData && (pCurOutData + 1) <= pOutDataEnd && (pSrc + 1) <= pOutDataEnd) {
+ *pCurOutData++ = *pSrc++;
+ }
+ else {
+ return -1;
+ }
+ }
+ else {
+ /* Write zero */
+ if ((pCurOutData + 1) <= pOutDataEnd) {
+ *pCurOutData++ = 0;
+ }
+ else {
+ return -1;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return (size_t)(pCurOutData - pOutData);
+}
diff --git a/tools/apultra_src/src/expand.h b/tools/apultra_src/src/expand.h new file mode 100644 index 0000000..f56b233 --- /dev/null +++ b/tools/apultra_src/src/expand.h @@ -0,0 +1,70 @@ +/*
+ * expand.h - decompressor definitions
+ *
+ * Copyright (C) 2019 Emmanuel Marty
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty. In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ */
+
+/*
+ * Uses the libdivsufsort library Copyright (c) 2003-2008 Yuta Mori
+ *
+ * Inspired by cap by Sven-Åke Dahl. https://github.com/svendahl/cap
+ * Also inspired by Charles Bloom's compression blog. http://cbloomrants.blogspot.com/
+ * With ideas from LZ4 by Yann Collet. https://github.com/lz4/lz4
+ * With help and support from spke <zxintrospec@gmail.com>
+ *
+ */
+
+#ifndef _EXPAND_H
+#define _EXPAND_H
+
+#include <stdlib.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Get maximum decompressed size of compressed data
+ *
+ * @param pInputData compressed data
+ * @param nInputSize compressed size in bytes
+ * @param nFlags compression flags (a bitmask of APULTRA_FLAG_xxx, or 0)
+ *
+ * @return maximum decompressed size
+ */
+size_t apultra_get_max_decompressed_size(const unsigned char *pInputData, size_t nInputSize, const unsigned int nFlags);
+
+/**
+ * Decompress data in memory
+ *
+ * @param pInputData compressed data
+ * @param pOutBuffer buffer for decompressed data
+ * @param nInputSize compressed size in bytes
+ * @param nMaxOutBufferSize maximum capacity of decompression buffer
+ * @param nFlags compression flags (a bitmask of APULTRA_FLAG_xxx, or 0)
+ *
+ * @return actual decompressed size, or -1 for error
+ */
+size_t apultra_decompress(const unsigned char *pInputData, unsigned char *pOutBuffer, size_t nInputSize, size_t nMaxOutBufferSize, const unsigned int nFlags);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _EXPAND_H */
diff --git a/tools/apultra_src/src/format.h b/tools/apultra_src/src/format.h new file mode 100644 index 0000000..cf949b5 --- /dev/null +++ b/tools/apultra_src/src/format.h @@ -0,0 +1,47 @@ +/*
+ * format.h - byte stream format definitions
+ *
+ * Copyright (C) 2019 Emmanuel Marty
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty. In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ */
+
+/*
+ * Uses the libdivsufsort library Copyright (c) 2003-2008 Yuta Mori
+ *
+ * Inspired by cap by Sven-Åke Dahl. https://github.com/svendahl/cap
+ * Also inspired by Charles Bloom's compression blog. http://cbloomrants.blogspot.com/
+ * With ideas from LZ4 by Yann Collet. https://github.com/lz4/lz4
+ * With help and support from spke <zxintrospec@gmail.com>
+ *
+ */
+
+#ifndef _FORMAT_H
+#define _FORMAT_H
+
+#define MIN_OFFSET 1
+#define MAX_OFFSET 0x1fffff
+
+#define MAX_VARLEN 0x1fffff
+
+#define BLOCK_SIZE 0x100000
+
+#define MIN_MATCH_SIZE 1
+#define MINMATCH3_OFFSET 1280
+#define MINMATCH4_OFFSET 32000
+
+#endif /* _FORMAT_H */
diff --git a/tools/apultra_src/src/libapultra.h b/tools/apultra_src/src/libapultra.h new file mode 100644 index 0000000..f97e336 --- /dev/null +++ b/tools/apultra_src/src/libapultra.h @@ -0,0 +1,40 @@ +/*
+ * libapultra.h - library definitions
+ *
+ * Copyright (C) 2019 Emmanuel Marty
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty. In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ */
+
+/*
+ * Uses the libdivsufsort library Copyright (c) 2003-2008 Yuta Mori
+ *
+ * Inspired by cap by Sven-Åke Dahl. https://github.com/svendahl/cap
+ * Also inspired by Charles Bloom's compression blog. http://cbloomrants.blogspot.com/
+ * With ideas from LZ4 by Yann Collet. https://github.com/lz4/lz4
+ * With help and support from spke <zxintrospec@gmail.com>
+ *
+ */
+
+#ifndef _LIB_APULTRA_H
+#define _LIB_APULTRA_H
+
+#include "format.h"
+#include "shrink.h"
+#include "expand.h"
+
+#endif /* _LIB_APULTRA_H */
diff --git a/tools/apultra_src/src/libdivsufsort/CHANGELOG.md b/tools/apultra_src/src/libdivsufsort/CHANGELOG.md new file mode 100644 index 0000000..fe9d004 --- /dev/null +++ b/tools/apultra_src/src/libdivsufsort/CHANGELOG.md @@ -0,0 +1,21 @@ +# libdivsufsort Change Log + +See full changelog at: https://github.com/y-256/libdivsufsort/commits + +## [2.0.1] - 2010-11-11 +### Fixed +* Wrong variable used in `divbwt` function +* Enclose some string variables with double quotation marks in include/CMakeLists.txt +* Fix typo in include/CMakeLists.txt + +## 2.0.0 - 2008-08-23 +### Changed +* Switch the build system to [CMake](http://www.cmake.org/) +* Improve the performance of the suffix-sorting algorithm + +### Added +* OpenMP support +* 64-bit version of divsufsort + +[Unreleased]: https://github.com/y-256/libdivsufsort/compare/2.0.1...HEAD +[2.0.1]: https://github.com/y-256/libdivsufsort/compare/2.0.0...2.0.1 diff --git a/tools/apultra_src/src/libdivsufsort/CMakeLists.txt b/tools/apultra_src/src/libdivsufsort/CMakeLists.txt new file mode 100644 index 0000000..7859943 --- /dev/null +++ b/tools/apultra_src/src/libdivsufsort/CMakeLists.txt @@ -0,0 +1,99 @@ +### cmake file for building libdivsufsort Package ### +cmake_minimum_required(VERSION 2.4.4) +set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMakeModules") +include(AppendCompilerFlags) + +## Project information ## +project(libdivsufsort C) +set(PROJECT_VENDOR "Yuta Mori") +set(PROJECT_CONTACT "yuta.256@gmail.com") +set(PROJECT_URL "https://github.com/y-256/libdivsufsort") +set(PROJECT_DESCRIPTION "A lightweight suffix sorting library") +include(VERSION.cmake) + +## CPack configuration ## +set(CPACK_GENERATOR "TGZ;TBZ2;ZIP") +set(CPACK_SOURCE_GENERATOR "TGZ;TBZ2;ZIP") +include(ProjectCPack) + +## Project options ## +option(BUILD_SHARED_LIBS "Set to OFF to build static libraries" ON) +option(BUILD_EXAMPLES "Build examples" ON) +option(BUILD_DIVSUFSORT64 "Build libdivsufsort64" OFF) +option(USE_OPENMP "Use OpenMP for parallelization" OFF) +option(WITH_LFS "Enable Large File Support" ON) + +## Installation directories ## +set(LIB_SUFFIX "" CACHE STRING "Define suffix of directory name (32 or 64)") + +set(CMAKE_INSTALL_RUNTIMEDIR "" CACHE PATH "Specify the output directory for dll runtimes (default is bin)") +if(NOT CMAKE_INSTALL_RUNTIMEDIR) + set(CMAKE_INSTALL_RUNTIMEDIR "${CMAKE_INSTALL_PREFIX}/bin") +endif(NOT CMAKE_INSTALL_RUNTIMEDIR) + +set(CMAKE_INSTALL_LIBDIR "" CACHE PATH "Specify the output directory for libraries (default is lib)") +if(NOT CMAKE_INSTALL_LIBDIR) + set(CMAKE_INSTALL_LIBDIR "${CMAKE_INSTALL_PREFIX}/lib${LIB_SUFFIX}") +endif(NOT CMAKE_INSTALL_LIBDIR) + +set(CMAKE_INSTALL_INCLUDEDIR "" CACHE PATH "Specify the output directory for header files (default is include)") +if(NOT CMAKE_INSTALL_INCLUDEDIR) + set(CMAKE_INSTALL_INCLUDEDIR "${CMAKE_INSTALL_PREFIX}/include") +endif(NOT CMAKE_INSTALL_INCLUDEDIR) + +set(CMAKE_INSTALL_PKGCONFIGDIR "" CACHE PATH "Specify the output directory for pkgconfig files (default is lib/pkgconfig)") +if(NOT CMAKE_INSTALL_PKGCONFIGDIR) + set(CMAKE_INSTALL_PKGCONFIGDIR "${CMAKE_INSTALL_LIBDIR}/pkgconfig") +endif(NOT CMAKE_INSTALL_PKGCONFIGDIR) + +## Build type ## +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE "Release") +elseif(CMAKE_BUILD_TYPE STREQUAL "Debug") + set(CMAKE_VERBOSE_MAKEFILE ON) +endif(NOT CMAKE_BUILD_TYPE) + +## Compiler options ## +if(MSVC) + append_c_compiler_flags("/W4" "VC" CMAKE_C_FLAGS) + append_c_compiler_flags("/Oi;/Ot;/Ox;/Oy" "VC" CMAKE_C_FLAGS_RELEASE) + if(USE_OPENMP) + append_c_compiler_flags("/openmp" "VC" CMAKE_C_FLAGS) + endif(USE_OPENMP) +elseif(BORLAND) + append_c_compiler_flags("-w" "BCC" CMAKE_C_FLAGS) + append_c_compiler_flags("-Oi;-Og;-Os;-Ov;-Ox" "BCC" CMAKE_C_FLAGS_RELEASE) +else(MSVC) + if(CMAKE_COMPILER_IS_GNUCC) + append_c_compiler_flags("-Wall" "GCC" CMAKE_C_FLAGS) + append_c_compiler_flags("-fomit-frame-pointer" "GCC" CMAKE_C_FLAGS_RELEASE) + if(USE_OPENMP) + append_c_compiler_flags("-fopenmp" "GCC" CMAKE_C_FLAGS) + endif(USE_OPENMP) + else(CMAKE_COMPILER_IS_GNUCC) + append_c_compiler_flags("-Wall" "UNKNOWN" CMAKE_C_FLAGS) + append_c_compiler_flags("-fomit-frame-pointer" "UNKNOWN" CMAKE_C_FLAGS_RELEASE) + if(USE_OPENMP) + append_c_compiler_flags("-fopenmp;-openmp;-omp" "UNKNOWN" CMAKE_C_FLAGS) + endif(USE_OPENMP) + endif(CMAKE_COMPILER_IS_GNUCC) +endif(MSVC) + +## Add definitions ## +add_definitions(-DHAVE_CONFIG_H=1 -D__STDC_LIMIT_MACROS -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS) + +## Add subdirectories ## +add_subdirectory(pkgconfig) +add_subdirectory(include) +add_subdirectory(lib) +if(BUILD_EXAMPLES) + add_subdirectory(examples) +endif(BUILD_EXAMPLES) + +## Add 'uninstall' target ## +CONFIGURE_FILE( + "${CMAKE_CURRENT_SOURCE_DIR}/CMakeModules/cmake_uninstall.cmake.in" + "${CMAKE_CURRENT_BINARY_DIR}/CMakeModules/cmake_uninstall.cmake" + IMMEDIATE @ONLY) +ADD_CUSTOM_TARGET(uninstall + "${CMAKE_COMMAND}" -P "${CMAKE_CURRENT_BINARY_DIR}/CMakeModules/cmake_uninstall.cmake") diff --git a/tools/apultra_src/src/libdivsufsort/CMakeModules/AppendCompilerFlags.cmake b/tools/apultra_src/src/libdivsufsort/CMakeModules/AppendCompilerFlags.cmake new file mode 100644 index 0000000..58d3f99 --- /dev/null +++ b/tools/apultra_src/src/libdivsufsort/CMakeModules/AppendCompilerFlags.cmake @@ -0,0 +1,38 @@ +include(CheckCSourceCompiles) +include(CheckCXXSourceCompiles) + +macro(append_c_compiler_flags _flags _name _result) + set(SAFE_CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS}) + string(REGEX REPLACE "[-+/ ]" "_" cname "${_name}") + string(TOUPPER "${cname}" cname) + foreach(flag ${_flags}) + string(REGEX REPLACE "^[-+/ ]+(.*)[-+/ ]*$" "\\1" flagname "${flag}") + string(REGEX REPLACE "[-+/ ]" "_" flagname "${flagname}") + string(TOUPPER "${flagname}" flagname) + set(have_flag "HAVE_${cname}_${flagname}") + set(CMAKE_REQUIRED_FLAGS "${flag}") + check_c_source_compiles("int main() { return 0; }" ${have_flag}) + if(${have_flag}) + set(${_result} "${${_result}} ${flag}") + endif(${have_flag}) + endforeach(flag) + set(CMAKE_REQUIRED_FLAGS ${SAFE_CMAKE_REQUIRED_FLAGS}) +endmacro(append_c_compiler_flags) + +macro(append_cxx_compiler_flags _flags _name _result) + set(SAFE_CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS}) + string(REGEX REPLACE "[-+/ ]" "_" cname "${_name}") + string(TOUPPER "${cname}" cname) + foreach(flag ${_flags}) + string(REGEX REPLACE "^[-+/ ]+(.*)[-+/ ]*$" "\\1" flagname "${flag}") + string(REGEX REPLACE "[-+/ ]" "_" flagname "${flagname}") + string(TOUPPER "${flagname}" flagname) + set(have_flag "HAVE_${cname}_${flagname}") + set(CMAKE_REQUIRED_FLAGS "${flag}") + check_cxx_source_compiles("int main() { return 0; }" ${have_flag}) + if(${have_flag}) + set(${_result} "${${_result}} ${flag}") + endif(${have_flag}) + endforeach(flag) + set(CMAKE_REQUIRED_FLAGS ${SAFE_CMAKE_REQUIRED_FLAGS}) +endmacro(append_cxx_compiler_flags) diff --git a/tools/apultra_src/src/libdivsufsort/CMakeModules/CheckFunctionKeywords.cmake b/tools/apultra_src/src/libdivsufsort/CMakeModules/CheckFunctionKeywords.cmake new file mode 100644 index 0000000..44601fd --- /dev/null +++ b/tools/apultra_src/src/libdivsufsort/CMakeModules/CheckFunctionKeywords.cmake @@ -0,0 +1,15 @@ +include(CheckCSourceCompiles) + +macro(check_function_keywords _wordlist) + set(${_result} "") + foreach(flag ${_wordlist}) + string(REGEX REPLACE "[-+/ ()]" "_" flagname "${flag}") + string(TOUPPER "${flagname}" flagname) + set(have_flag "HAVE_${flagname}") + check_c_source_compiles("${flag} void func(); void func() { } int main() { func(); return 0; }" ${have_flag}) + if(${have_flag} AND NOT ${_result}) + set(${_result} "${flag}") +# break() + endif(${have_flag} AND NOT ${_result}) + endforeach(flag) +endmacro(check_function_keywords) diff --git a/tools/apultra_src/src/libdivsufsort/CMakeModules/CheckLFS.cmake b/tools/apultra_src/src/libdivsufsort/CMakeModules/CheckLFS.cmake new file mode 100644 index 0000000..e2b0099 --- /dev/null +++ b/tools/apultra_src/src/libdivsufsort/CMakeModules/CheckLFS.cmake @@ -0,0 +1,109 @@ +## Checks for large file support ## +include(CheckIncludeFile) +include(CheckSymbolExists) +include(CheckTypeSize) + +macro(check_lfs _isenable) + set(LFS_OFF_T "") + set(LFS_FOPEN "") + set(LFS_FSEEK "") + set(LFS_FTELL "") + set(LFS_PRID "") + + if(${_isenable}) + set(SAFE_CMAKE_REQUIRED_DEFINITIONS "${CMAKE_REQUIRED_DEFINITIONS}") + set(CMAKE_REQUIRED_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS} + -D_LARGEFILE_SOURCE -D_LARGE_FILES -D_FILE_OFFSET_BITS=64 + -D__STDC_LIMIT_MACROS -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS) + + check_include_file("sys/types.h" HAVE_SYS_TYPES_H) + check_include_file("inttypes.h" HAVE_INTTYPES_H) + check_include_file("stddef.h" HAVE_STDDEF_H) + check_include_file("stdint.h" HAVE_STDINT_H) + + # LFS type1: 8 <= sizeof(off_t), fseeko, ftello + check_type_size("off_t" SIZEOF_OFF_T) + if(SIZEOF_OFF_T GREATER 7) + check_symbol_exists("fseeko" "stdio.h" HAVE_FSEEKO) + check_symbol_exists("ftello" "stdio.h" HAVE_FTELLO) + if(HAVE_FSEEKO AND HAVE_FTELLO) + set(LFS_OFF_T "off_t") + set(LFS_FOPEN "fopen") + set(LFS_FSEEK "fseeko") + set(LFS_FTELL "ftello") + check_symbol_exists("PRIdMAX" "inttypes.h" HAVE_PRIDMAX) + if(HAVE_PRIDMAX) + set(LFS_PRID "PRIdMAX") + else(HAVE_PRIDMAX) + check_type_size("long" SIZEOF_LONG) + check_type_size("int" SIZEOF_INT) + if(SIZEOF_OFF_T GREATER SIZEOF_LONG) + set(LFS_PRID "\"lld\"") + elseif(SIZEOF_LONG GREATER SIZEOF_INT) + set(LFS_PRID "\"ld\"") + else(SIZEOF_OFF_T GREATER SIZEOF_LONG) + set(LFS_PRID "\"d\"") + endif(SIZEOF_OFF_T GREATER SIZEOF_LONG) + endif(HAVE_PRIDMAX) + endif(HAVE_FSEEKO AND HAVE_FTELLO) + endif(SIZEOF_OFF_T GREATER 7) + + # LFS type2: 8 <= sizeof(off64_t), fopen64, fseeko64, ftello64 + if(NOT LFS_OFF_T) + check_type_size("off64_t" SIZEOF_OFF64_T) + if(SIZEOF_OFF64_T GREATER 7) + check_symbol_exists("fopen64" "stdio.h" HAVE_FOPEN64) + check_symbol_exists("fseeko64" "stdio.h" HAVE_FSEEKO64) + check_symbol_exists("ftello64" "stdio.h" HAVE_FTELLO64) + if(HAVE_FOPEN64 AND HAVE_FSEEKO64 AND HAVE_FTELLO64) + set(LFS_OFF_T "off64_t") + set(LFS_FOPEN "fopen64") + set(LFS_FSEEK "fseeko64") + set(LFS_FTELL "ftello64") + check_symbol_exists("PRIdMAX" "inttypes.h" HAVE_PRIDMAX) + if(HAVE_PRIDMAX) + set(LFS_PRID "PRIdMAX") + else(HAVE_PRIDMAX) + check_type_size("long" SIZEOF_LONG) + check_type_size("int" SIZEOF_INT) + if(SIZEOF_OFF64_T GREATER SIZEOF_LONG) + set(LFS_PRID "\"lld\"") + elseif(SIZEOF_LONG GREATER SIZEOF_INT) + set(LFS_PRID "\"ld\"") + else(SIZEOF_OFF64_T GREATER SIZEOF_LONG) + set(LFS_PRID "\"d\"") + endif(SIZEOF_OFF64_T GREATER SIZEOF_LONG) + endif(HAVE_PRIDMAX) + endif(HAVE_FOPEN64 AND HAVE_FSEEKO64 AND HAVE_FTELLO64) + endif(SIZEOF_OFF64_T GREATER 7) + endif(NOT LFS_OFF_T) + + # LFS type3: 8 <= sizeof(__int64), _fseeki64, _ftelli64 + if(NOT LFS_OFF_T) + check_type_size("__int64" SIZEOF___INT64) + if(SIZEOF___INT64 GREATER 7) + check_symbol_exists("_fseeki64" "stdio.h" HAVE__FSEEKI64) + check_symbol_exists("_ftelli64" "stdio.h" HAVE__FTELLI64) + if(HAVE__FSEEKI64 AND HAVE__FTELLI64) + set(LFS_OFF_T "__int64") + set(LFS_FOPEN "fopen") + set(LFS_FSEEK "_fseeki64") + set(LFS_FTELL "_ftelli64") + set(LFS_PRID "\"I64d\"") + endif(HAVE__FSEEKI64 AND HAVE__FTELLI64) + endif(SIZEOF___INT64 GREATER 7) + endif(NOT LFS_OFF_T) + + set(CMAKE_REQUIRED_DEFINITIONS "${SAFE_CMAKE_REQUIRED_DEFINITIONS}") + endif(${_isenable}) + + if(NOT LFS_OFF_T) + ## not found + set(LFS_OFF_T "long") + set(LFS_FOPEN "fopen") + set(LFS_FSEEK "fseek") + set(LFS_FTELL "ftell") + set(LFS_PRID "\"ld\"") + endif(NOT LFS_OFF_T) + +endmacro(check_lfs) diff --git a/tools/apultra_src/src/libdivsufsort/CMakeModules/ProjectCPack.cmake b/tools/apultra_src/src/libdivsufsort/CMakeModules/ProjectCPack.cmake new file mode 100644 index 0000000..7c105f9 --- /dev/null +++ b/tools/apultra_src/src/libdivsufsort/CMakeModules/ProjectCPack.cmake @@ -0,0 +1,38 @@ +# If the cmake version includes cpack, use it +IF(EXISTS "${CMAKE_ROOT}/Modules/CPack.cmake") + SET(CPACK_PACKAGE_DESCRIPTION_SUMMARY "${PROJECT_DESCRIPTION}") + SET(CPACK_PACKAGE_VENDOR "${PROJECT_VENDOR}") + SET(CPACK_PACKAGE_DESCRIPTION_FILE "${CMAKE_CURRENT_SOURCE_DIR}/README.md") + SET(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE") + SET(CPACK_PACKAGE_VERSION_MAJOR "${PROJECT_VERSION_MAJOR}") + SET(CPACK_PACKAGE_VERSION_MINOR "${PROJECT_VERSION_MINOR}") + SET(CPACK_PACKAGE_VERSION_PATCH "${PROJECT_VERSION_PATCH}") +# SET(CPACK_PACKAGE_INSTALL_DIRECTORY "${PROJECT_NAME} ${PROJECT_VERSION}") + SET(CPACK_SOURCE_PACKAGE_FILE_NAME "${PROJECT_NAME}-${PROJECT_VERSION_FULL}") + + IF(NOT DEFINED CPACK_SYSTEM_NAME) + SET(CPACK_SYSTEM_NAME "${CMAKE_SYSTEM_NAME}-${CMAKE_SYSTEM_PROCESSOR}") + ENDIF(NOT DEFINED CPACK_SYSTEM_NAME) + + IF(${CPACK_SYSTEM_NAME} MATCHES Windows) + IF(CMAKE_CL_64) + SET(CPACK_SYSTEM_NAME win64-${CMAKE_SYSTEM_PROCESSOR}) + ELSE(CMAKE_CL_64) + SET(CPACK_SYSTEM_NAME win32-${CMAKE_SYSTEM_PROCESSOR}) + ENDIF(CMAKE_CL_64) + ENDIF(${CPACK_SYSTEM_NAME} MATCHES Windows) + + IF(NOT DEFINED CPACK_PACKAGE_FILE_NAME) + SET(CPACK_PACKAGE_FILE_NAME "${CPACK_SOURCE_PACKAGE_FILE_NAME}-${CPACK_SYSTEM_NAME}") + ENDIF(NOT DEFINED CPACK_PACKAGE_FILE_NAME) + + SET(CPACK_PACKAGE_CONTACT "${PROJECT_CONTACT}") + IF(UNIX) + SET(CPACK_STRIP_FILES "") + SET(CPACK_SOURCE_STRIP_FILES "") +# SET(CPACK_PACKAGE_EXECUTABLES "ccmake" "CMake") + ENDIF(UNIX) + SET(CPACK_SOURCE_IGNORE_FILES "/CVS/" "/build/" "/\\\\.build/" "/\\\\.svn/" "~$") + # include CPack model once all variables are set + INCLUDE(CPack) +ENDIF(EXISTS "${CMAKE_ROOT}/Modules/CPack.cmake") diff --git a/tools/apultra_src/src/libdivsufsort/CMakeModules/cmake_uninstall.cmake.in b/tools/apultra_src/src/libdivsufsort/CMakeModules/cmake_uninstall.cmake.in new file mode 100644 index 0000000..8366a83 --- /dev/null +++ b/tools/apultra_src/src/libdivsufsort/CMakeModules/cmake_uninstall.cmake.in @@ -0,0 +1,36 @@ +IF(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") + MESSAGE(FATAL_ERROR "Cannot find install manifest: \"@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt\"") +ENDIF(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") + +FILE(READ "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt" files) +STRING(REGEX REPLACE "\n" ";" files "${files}") + +SET(NUM 0) +FOREACH(file ${files}) + IF(EXISTS "$ENV{DESTDIR}${file}") + MESSAGE(STATUS "Looking for \"$ENV{DESTDIR}${file}\" - found") + SET(UNINSTALL_CHECK_${NUM} 1) + ELSE(EXISTS "$ENV{DESTDIR}${file}") + MESSAGE(STATUS "Looking for \"$ENV{DESTDIR}${file}\" - not found") + SET(UNINSTALL_CHECK_${NUM} 0) + ENDIF(EXISTS "$ENV{DESTDIR}${file}") + MATH(EXPR NUM "1 + ${NUM}") +ENDFOREACH(file) + +SET(NUM 0) +FOREACH(file ${files}) + IF(${UNINSTALL_CHECK_${NUM}}) + MESSAGE(STATUS "Uninstalling \"$ENV{DESTDIR}${file}\"") + EXEC_PROGRAM( + "@CMAKE_COMMAND@" ARGS "-E remove \"$ENV{DESTDIR}${file}\"" + OUTPUT_VARIABLE rm_out + RETURN_VALUE rm_retval + ) + IF(NOT "${rm_retval}" STREQUAL 0) + MESSAGE(FATAL_ERROR "Problem when removing \"$ENV{DESTDIR}${file}\"") + ENDIF(NOT "${rm_retval}" STREQUAL 0) + ENDIF(${UNINSTALL_CHECK_${NUM}}) + MATH(EXPR NUM "1 + ${NUM}") +ENDFOREACH(file) + +FILE(REMOVE "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") diff --git a/tools/apultra_src/src/libdivsufsort/LICENSE b/tools/apultra_src/src/libdivsufsort/LICENSE new file mode 100644 index 0000000..249efa4 --- /dev/null +++ b/tools/apultra_src/src/libdivsufsort/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2003 Yuta Mori All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/tools/apultra_src/src/libdivsufsort/README.md b/tools/apultra_src/src/libdivsufsort/README.md new file mode 100644 index 0000000..381a188 --- /dev/null +++ b/tools/apultra_src/src/libdivsufsort/README.md @@ -0,0 +1,140 @@ +# libdivsufsort + +libdivsufsort is a software library that implements a lightweight suffix array construction algorithm. + +## News +* 2015-03-21: The project has moved from [Google Code](http://code.google.com/p/libdivsufsort/) to [GitHub](https://github.com/y-256/libdivsufsort) + +## Introduction +This library provides a simple and an efficient C API to construct a suffix array and a Burrows-Wheeler transformed string from a given string over a constant-size alphabet. +The algorithm runs in O(n log n) worst-case time using only 5n+O(1) bytes of memory space, where n is the length of +the string. + +## Build requirements +* An ANSI C Compiler (e.g. GNU GCC) +* [CMake](http://www.cmake.org/ "CMake") version 2.4.2 or newer +* CMake-supported build tool + +## Building on GNU/Linux +1. Get the source code from GitHub. You can either + * use git to clone the repository + ``` + git clone https://github.com/y-256/libdivsufsort.git + ``` + * or download a [zip file](../../archive/master.zip) directly +2. Create a `build` directory in the package source directory. +```shell +$ cd libdivsufsort +$ mkdir build +$ cd build +``` +3. Configure the package for your system. +If you want to install to a different location, change the -DCMAKE_INSTALL_PREFIX option. +```shell +$ cmake -DCMAKE_BUILD_TYPE="Release" \ +-DCMAKE_INSTALL_PREFIX="/usr/local" .. +``` +4. Compile the package. +```shell +$ make +``` +5. (Optional) Install the library and header files. +```shell +$ sudo make install +``` + +## API +```c +/* Data types */ +typedef int32_t saint_t; +typedef int32_t saidx_t; +typedef uint8_t sauchar_t; + +/* + * Constructs the suffix array of a given string. + * @param T[0..n-1] The input string. + * @param SA[0..n-1] The output array or suffixes. + * @param n The length of the given string. + * @return 0 if no error occurred, -1 or -2 otherwise. + */ +saint_t +divsufsort(const sauchar_t *T, saidx_t *SA, saidx_t n); + +/* + * Constructs the burrows-wheeler transformed string of a given string. + * @param T[0..n-1] The input string. + * @param U[0..n-1] The output string. (can be T) + * @param A[0..n-1] The temporary array. (can be NULL) + * @param n The length of the given string. + * @return The primary index if no error occurred, -1 or -2 otherwise. + */ +saidx_t +divbwt(const sauchar_t *T, sauchar_t *U, saidx_t *A, saidx_t n); +``` + +## Example Usage +```c +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <divsufsort.h> + +int main() { + // intput data + char *Text = "abracadabra"; + int n = strlen(Text); + int i, j; + + // allocate + int *SA = (int *)malloc(n * sizeof(int)); + + // sort + divsufsort((unsigned char *)Text, SA, n); + + // output + for(i = 0; i < n; ++i) { + printf("SA[%2d] = %2d: ", i, SA[i]); + for(j = SA[i]; j < n; ++j) { + printf("%c", Text[j]); + } + printf("$\n"); + } + + // deallocate + free(SA); + + return 0; +} +``` +See the [examples](examples) directory for a few other examples. + +## Benchmarks +See [Benchmarks](https://github.com/y-256/libdivsufsort/blob/wiki/SACA_Benchmarks.md) page for details. + +## License +libdivsufsort is released under the [MIT license](LICENSE "MIT license"). +> The MIT License (MIT) +> +> Copyright (c) 2003 Yuta Mori All rights reserved. +> +> Permission is hereby granted, free of charge, to any person obtaining a copy +> of this software and associated documentation files (the "Software"), to deal +> in the Software without restriction, including without limitation the rights +> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +> copies of the Software, and to permit persons to whom the Software is +> furnished to do so, subject to the following conditions: +> +> The above copyright notice and this permission notice shall be included in all +> copies or substantial portions of the Software. +> +> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +> SOFTWARE. + +## Author +* Yuta Mori diff --git a/tools/apultra_src/src/libdivsufsort/VERSION.cmake b/tools/apultra_src/src/libdivsufsort/VERSION.cmake new file mode 100644 index 0000000..3f11ac1 --- /dev/null +++ b/tools/apultra_src/src/libdivsufsort/VERSION.cmake @@ -0,0 +1,23 @@ +set(PROJECT_VERSION_MAJOR "2") +set(PROJECT_VERSION_MINOR "0") +set(PROJECT_VERSION_PATCH "2") +set(PROJECT_VERSION_EXTRA "-1") +set(PROJECT_VERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}") +set(PROJECT_VERSION_FULL "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}${PROJECT_VERSION_EXTRA}") + +set(LIBRARY_VERSION "3.0.1") +set(LIBRARY_SOVERSION "3") + +## Git revision number ## +if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/.git") + execute_process(COMMAND git describe --tags HEAD + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" + OUTPUT_VARIABLE GIT_DESCRIBE_TAGS ERROR_QUIET) + if(GIT_DESCRIBE_TAGS) + string(REGEX REPLACE "^v(.*)" "\\1" GIT_REVISION "${GIT_DESCRIBE_TAGS}") + string(STRIP "${GIT_REVISION}" GIT_REVISION) + if(GIT_REVISION) + set(PROJECT_VERSION_FULL "${GIT_REVISION}") + endif(GIT_REVISION) + endif(GIT_DESCRIBE_TAGS) +endif(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/.git") diff --git a/tools/apultra_src/src/libdivsufsort/examples/CMakeLists.txt b/tools/apultra_src/src/libdivsufsort/examples/CMakeLists.txt new file mode 100644 index 0000000..e801c81 --- /dev/null +++ b/tools/apultra_src/src/libdivsufsort/examples/CMakeLists.txt @@ -0,0 +1,11 @@ +## Add definitions ## +add_definitions(-D_LARGEFILE_SOURCE -D_LARGE_FILES -D_FILE_OFFSET_BITS=64) + +## Targets ## +include_directories("${CMAKE_CURRENT_SOURCE_DIR}/../include" + "${CMAKE_CURRENT_BINARY_DIR}/../include") +link_directories("${CMAKE_CURRENT_BINARY_DIR}/../lib") +foreach(src suftest mksary sasearch bwt unbwt) + add_executable(${src} ${src}.c) + target_link_libraries(${src} divsufsort) +endforeach(src) diff --git a/tools/apultra_src/src/libdivsufsort/examples/bwt.c b/tools/apultra_src/src/libdivsufsort/examples/bwt.c new file mode 100644 index 0000000..5a362d0 --- /dev/null +++ b/tools/apultra_src/src/libdivsufsort/examples/bwt.c @@ -0,0 +1,220 @@ +/* + * bwt.c for libdivsufsort + * Copyright (c) 2003-2008 Yuta Mori All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#if HAVE_CONFIG_H +# include "config.h" +#endif +#include <stdio.h> +#if HAVE_STRING_H +# include <string.h> +#endif +#if HAVE_STDLIB_H +# include <stdlib.h> +#endif +#if HAVE_MEMORY_H +# include <memory.h> +#endif +#if HAVE_STDDEF_H +# include <stddef.h> +#endif +#if HAVE_STRINGS_H +# include <strings.h> +#endif +#if HAVE_SYS_TYPES_H +# include <sys/types.h> +#endif +#if HAVE_IO_H && HAVE_FCNTL_H +# include <io.h> +# include <fcntl.h> +#endif +#include <time.h> +#include <divsufsort.h> +#include "lfs.h" + + +static +size_t +write_int(FILE *fp, saidx_t n) { + unsigned char c[4]; + c[0] = (unsigned char)((n >> 0) & 0xff), c[1] = (unsigned char)((n >> 8) & 0xff), + c[2] = (unsigned char)((n >> 16) & 0xff), c[3] = (unsigned char)((n >> 24) & 0xff); + return fwrite(c, sizeof(unsigned char), 4, fp); +} + +static +void +print_help(const char *progname, int status) { + fprintf(stderr, + "bwt, a burrows-wheeler transform program, version %s.\n", + divsufsort_version()); + fprintf(stderr, "usage: %s [-b num] INFILE OUTFILE\n", progname); + fprintf(stderr, " -b num set block size to num MiB [1..512] (default: 32)\n\n"); + exit(status); +} + +int +main(int argc, const char *argv[]) { + FILE *fp, *ofp; + const char *fname, *ofname; + sauchar_t *T; + saidx_t *SA; + LFS_OFF_T n; + size_t m; + saidx_t pidx; + clock_t start,finish; + saint_t i, blocksize = 32, needclose = 3; + + /* Check arguments. */ + if((argc == 1) || + (strcmp(argv[1], "-h") == 0) || + (strcmp(argv[1], "--help") == 0)) { print_help(argv[0], EXIT_SUCCESS); } + if((argc != 3) && (argc != 5)) { print_help(argv[0], EXIT_FAILURE); } + i = 1; + if(argc == 5) { + if(strcmp(argv[i], "-b") != 0) { print_help(argv[0], EXIT_FAILURE); } + blocksize = atoi(argv[i + 1]); + if(blocksize < 0) { blocksize = 1; } + else if(512 < blocksize) { blocksize = 512; } + i += 2; + } + blocksize <<= 20; + + /* Open a file for reading. */ + if(strcmp(argv[i], "-") != 0) { +#if HAVE_FOPEN_S + if(fopen_s(&fp, fname = argv[i], "rb") != 0) { +#else + if((fp = LFS_FOPEN(fname = argv[i], "rb")) == NULL) { +#endif + fprintf(stderr, "%s: Cannot open file `%s': ", argv[0], fname); + perror(NULL); + exit(EXIT_FAILURE); + } + } else { +#if HAVE__SETMODE && HAVE__FILENO + if(_setmode(_fileno(stdin), _O_BINARY) == -1) { + fprintf(stderr, "%s: Cannot set mode: ", argv[0]); + perror(NULL); + exit(EXIT_FAILURE); + } +#endif + fp = stdin; + fname = "stdin"; + needclose ^= 1; + } + i += 1; + + /* Open a file for writing. */ + if(strcmp(argv[i], "-") != 0) { +#if HAVE_FOPEN_S + if(fopen_s(&ofp, ofname = argv[i], "wb") != 0) { +#else + if((ofp = LFS_FOPEN(ofname = argv[i], "wb")) == NULL) { +#endif + fprintf(stderr, "%s: Cannot open file `%s': ", argv[0], ofname); + perror(NULL); + exit(EXIT_FAILURE); + } + } else { +#if HAVE__SETMODE && HAVE__FILENO + if(_setmode(_fileno(stdout), _O_BINARY) == -1) { + fprintf(stderr, "%s: Cannot set mode: ", argv[0]); + perror(NULL); + exit(EXIT_FAILURE); + } +#endif + ofp = stdout; + ofname = "stdout"; + needclose ^= 2; + } + + /* Get the file size. */ + if(LFS_FSEEK(fp, 0, SEEK_END) == 0) { + n = LFS_FTELL(fp); + rewind(fp); + if(n < 0) { + fprintf(stderr, "%s: Cannot ftell `%s': ", argv[0], fname); + perror(NULL); + exit(EXIT_FAILURE); + } + if(0x20000000L < n) { n = 0x20000000L; } + if((blocksize == 0) || (n < blocksize)) { blocksize = (saidx_t)n; } + } else if(blocksize == 0) { blocksize = 32 << 20; } + + /* Allocate 5blocksize bytes of memory. */ + T = (sauchar_t *)malloc(blocksize * sizeof(sauchar_t)); + SA = (saidx_t *)malloc(blocksize * sizeof(saidx_t)); + if((T == NULL) || (SA == NULL)) { + fprintf(stderr, "%s: Cannot allocate memory.\n", argv[0]); + exit(EXIT_FAILURE); + } + + /* Write the blocksize. */ + if(write_int(ofp, blocksize) != 4) { + fprintf(stderr, "%s: Cannot write to `%s': ", argv[0], ofname); + perror(NULL); + exit(EXIT_FAILURE); + } + + fprintf(stderr, " BWT (blocksize %" PRIdSAINT_T ") ... ", blocksize); + start = clock(); + for(n = 0; 0 < (m = fread(T, sizeof(sauchar_t), blocksize, fp)); n += m) { + /* Burrows-Wheeler Transform. */ + pidx = divbwt(T, T, SA, m); + if(pidx < 0) { + fprintf(stderr, "%s (bw_transform): %s.\n", + argv[0], + (pidx == -1) ? "Invalid arguments" : "Cannot allocate memory"); + exit(EXIT_FAILURE); + } + + /* Write the bwted data. */ + if((write_int(ofp, pidx) != 4) || + (fwrite(T, sizeof(sauchar_t), m, ofp) != m)) { + fprintf(stderr, "%s: Cannot write to `%s': ", argv[0], ofname); + perror(NULL); + exit(EXIT_FAILURE); + } + } + if(ferror(fp)) { + fprintf(stderr, "%s: Cannot read from `%s': ", argv[0], fname); + perror(NULL); + exit(EXIT_FAILURE); + } + finish = clock(); + fprintf(stderr, "%" PRIdOFF_T " bytes: %.4f sec\n", + n, (double)(finish - start) / (double)CLOCKS_PER_SEC); + + /* Close files */ + if(needclose & 1) { fclose(fp); } + if(needclose & 2) { fclose(ofp); } + + /* Deallocate memory. */ + free(SA); + free(T); + + return 0; +} diff --git a/tools/apultra_src/src/libdivsufsort/examples/mksary.c b/tools/apultra_src/src/libdivsufsort/examples/mksary.c new file mode 100644 index 0000000..b48177c --- /dev/null +++ b/tools/apultra_src/src/libdivsufsort/examples/mksary.c @@ -0,0 +1,193 @@ +/* + * mksary.c for libdivsufsort + * Copyright (c) 2003-2008 Yuta Mori All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#if HAVE_CONFIG_H +# include "config.h" +#endif +#include <stdio.h> +#if HAVE_STRING_H +# include <string.h> +#endif +#if HAVE_STDLIB_H +# include <stdlib.h> +#endif +#if HAVE_MEMORY_H +# include <memory.h> +#endif +#if HAVE_STDDEF_H +# include <stddef.h> +#endif +#if HAVE_STRINGS_H +# include <strings.h> +#endif +#if HAVE_SYS_TYPES_H +# include <sys/types.h> +#endif +#if HAVE_IO_H && HAVE_FCNTL_H +# include <io.h> +# include <fcntl.h> +#endif +#include <time.h> +#include <divsufsort.h> +#include "lfs.h" + + +static +void +print_help(const char *progname, int status) { + fprintf(stderr, + "mksary, a simple suffix array builder, version %s.\n", + divsufsort_version()); + fprintf(stderr, "usage: %s INFILE OUTFILE\n\n", progname); + exit(status); +} + +int +main(int argc, const char *argv[]) { + FILE *fp, *ofp; + const char *fname, *ofname; + sauchar_t *T; + saidx_t *SA; + LFS_OFF_T n; + clock_t start, finish; + saint_t needclose = 3; + + /* Check arguments. */ + if((argc == 1) || + (strcmp(argv[1], "-h") == 0) || + (strcmp(argv[1], "--help") == 0)) { print_help(argv[0], EXIT_SUCCESS); } + if(argc != 3) { print_help(argv[0], EXIT_FAILURE); } + + /* Open a file for reading. */ + if(strcmp(argv[1], "-") != 0) { +#if HAVE_FOPEN_S + if(fopen_s(&fp, fname = argv[1], "rb") != 0) { +#else + if((fp = LFS_FOPEN(fname = argv[1], "rb")) == NULL) { +#endif + fprintf(stderr, "%s: Cannot open file `%s': ", argv[0], fname); + perror(NULL); + exit(EXIT_FAILURE); + } + } else { +#if HAVE__SETMODE && HAVE__FILENO + if(_setmode(_fileno(stdin), _O_BINARY) == -1) { + fprintf(stderr, "%s: Cannot set mode: ", argv[0]); + perror(NULL); + exit(EXIT_FAILURE); + } +#endif + fp = stdin; + fname = "stdin"; + needclose ^= 1; + } + + /* Open a file for writing. */ + if(strcmp(argv[2], "-") != 0) { +#if HAVE_FOPEN_S + if(fopen_s(&ofp, ofname = argv[2], "wb") != 0) { +#else + if((ofp = LFS_FOPEN(ofname = argv[2], "wb")) == NULL) { +#endif + fprintf(stderr, "%s: Cannot open file `%s': ", argv[0], ofname); + perror(NULL); + exit(EXIT_FAILURE); + } + } else { +#if HAVE__SETMODE && HAVE__FILENO + if(_setmode(_fileno(stdout), _O_BINARY) == -1) { + fprintf(stderr, "%s: Cannot set mode: ", argv[0]); + perror(NULL); + exit(EXIT_FAILURE); + } +#endif + ofp = stdout; + ofname = "stdout"; + needclose ^= 2; + } + + /* Get the file size. */ + if(LFS_FSEEK(fp, 0, SEEK_END) == 0) { + n = LFS_FTELL(fp); + rewind(fp); + if(n < 0) { + fprintf(stderr, "%s: Cannot ftell `%s': ", argv[0], fname); + perror(NULL); + exit(EXIT_FAILURE); + } + if(0x7fffffff <= n) { + fprintf(stderr, "%s: Input file `%s' is too big.\n", argv[0], fname); + exit(EXIT_FAILURE); + } + } else { + fprintf(stderr, "%s: Cannot fseek `%s': ", argv[0], fname); + perror(NULL); + exit(EXIT_FAILURE); + } + + /* Allocate 5blocksize bytes of memory. */ + T = (sauchar_t *)malloc((size_t)n * sizeof(sauchar_t)); + SA = (saidx_t *)malloc((size_t)n * sizeof(saidx_t)); + if((T == NULL) || (SA == NULL)) { + fprintf(stderr, "%s: Cannot allocate memory.\n", argv[0]); + exit(EXIT_FAILURE); + } + + /* Read n bytes of data. */ + if(fread(T, sizeof(sauchar_t), (size_t)n, fp) != (size_t)n) { + fprintf(stderr, "%s: %s `%s': ", + argv[0], + (ferror(fp) || !feof(fp)) ? "Cannot read from" : "Unexpected EOF in", + fname); + perror(NULL); + exit(EXIT_FAILURE); + } + if(needclose & 1) { fclose(fp); } + + /* Construct the suffix array. */ + fprintf(stderr, "%s: %" PRIdOFF_T " bytes ... ", fname, n); + start = clock(); + if(divsufsort(T, SA, (saidx_t)n) != 0) { + fprintf(stderr, "%s: Cannot allocate memory.\n", argv[0]); + exit(EXIT_FAILURE); + } + finish = clock(); + fprintf(stderr, "%.4f sec\n", (double)(finish - start) / (double)CLOCKS_PER_SEC); + + /* Write the suffix array. */ + if(fwrite(SA, sizeof(saidx_t), (size_t)n, ofp) != (size_t)n) { + fprintf(stderr, "%s: Cannot write to `%s': ", argv[0], ofname); + perror(NULL); + exit(EXIT_FAILURE); + } + if(needclose & 2) { fclose(ofp); } + + /* Deallocate memory. */ + free(SA); + free(T); + + return 0; +} diff --git a/tools/apultra_src/src/libdivsufsort/examples/sasearch.c b/tools/apultra_src/src/libdivsufsort/examples/sasearch.c new file mode 100644 index 0000000..7e5ca4f --- /dev/null +++ b/tools/apultra_src/src/libdivsufsort/examples/sasearch.c @@ -0,0 +1,165 @@ +/* + * sasearch.c for libdivsufsort + * Copyright (c) 2003-2008 Yuta Mori All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#if HAVE_CONFIG_H +# include "config.h" +#endif +#include <stdio.h> +#if HAVE_STRING_H +# include <string.h> +#endif +#if HAVE_STDLIB_H +# include <stdlib.h> +#endif +#if HAVE_MEMORY_H +# include <memory.h> +#endif +#if HAVE_STDDEF_H +# include <stddef.h> +#endif +#if HAVE_STRINGS_H +# include <strings.h> +#endif +#if HAVE_SYS_TYPES_H +# include <sys/types.h> +#endif +#if HAVE_IO_H && HAVE_FCNTL_H +# include <io.h> +# include <fcntl.h> +#endif +#include <divsufsort.h> +#include "lfs.h" + + +static +void +print_help(const char *progname, int status) { + fprintf(stderr, + "sasearch, a simple SA-based full-text search tool, version %s\n", + divsufsort_version()); + fprintf(stderr, "usage: %s PATTERN FILE SAFILE\n\n", progname); + exit(status); +} + +int +main(int argc, const char *argv[]) { + FILE *fp; + const char *P; + sauchar_t *T; + saidx_t *SA; + LFS_OFF_T n; + size_t Psize; + saidx_t i, size, left; + + if((argc == 1) || + (strcmp(argv[1], "-h") == 0) || + (strcmp(argv[1], "--help") == 0)) { print_help(argv[0], EXIT_SUCCESS); } + if(argc != 4) { print_help(argv[0], EXIT_FAILURE); } + + P = argv[1]; + Psize = strlen(P); + + /* Open a file for reading. */ +#if HAVE_FOPEN_S + if(fopen_s(&fp, argv[2], "rb") != 0) { +#else + if((fp = LFS_FOPEN(argv[2], "rb")) == NULL) { +#endif + fprintf(stderr, "%s: Cannot open file `%s': ", argv[0], argv[2]); + perror(NULL); + exit(EXIT_FAILURE); + } + + /* Get the file size. */ + if(LFS_FSEEK(fp, 0, SEEK_END) == 0) { + n = LFS_FTELL(fp); + rewind(fp); + if(n < 0) { + fprintf(stderr, "%s: Cannot ftell `%s': ", argv[0], argv[2]); + perror(NULL); + exit(EXIT_FAILURE); + } + } else { + fprintf(stderr, "%s: Cannot fseek `%s': ", argv[0], argv[2]); + perror(NULL); + exit(EXIT_FAILURE); + } + + /* Allocate 5n bytes of memory. */ + T = (sauchar_t *)malloc((size_t)n * sizeof(sauchar_t)); + SA = (saidx_t *)malloc((size_t)n * sizeof(saidx_t)); + if((T == NULL) || (SA == NULL)) { + fprintf(stderr, "%s: Cannot allocate memory.\n", argv[0]); + exit(EXIT_FAILURE); + } + + /* Read n bytes of data. */ + if(fread(T, sizeof(sauchar_t), (size_t)n, fp) != (size_t)n) { + fprintf(stderr, "%s: %s `%s': ", + argv[0], + (ferror(fp) || !feof(fp)) ? "Cannot read from" : "Unexpected EOF in", + argv[2]); + perror(NULL); + exit(EXIT_FAILURE); + } + fclose(fp); + + /* Open the SA file for reading. */ +#if HAVE_FOPEN_S + if(fopen_s(&fp, argv[3], "rb") != 0) { +#else + if((fp = LFS_FOPEN(argv[3], "rb")) == NULL) { +#endif + fprintf(stderr, "%s: Cannot open file `%s': ", argv[0], argv[3]); + perror(NULL); + exit(EXIT_FAILURE); + } + + /* Read n * sizeof(saidx_t) bytes of data. */ + if(fread(SA, sizeof(saidx_t), (size_t)n, fp) != (size_t)n) { + fprintf(stderr, "%s: %s `%s': ", + argv[0], + (ferror(fp) || !feof(fp)) ? "Cannot read from" : "Unexpected EOF in", + argv[3]); + perror(NULL); + exit(EXIT_FAILURE); + } + fclose(fp); + + /* Search and print */ + size = sa_search(T, (saidx_t)n, + (const sauchar_t *)P, (saidx_t)Psize, + SA, (saidx_t)n, &left); + for(i = 0; i < size; ++i) { + fprintf(stdout, "%" PRIdSAIDX_T "\n", SA[left + i]); + } + + /* Deallocate memory. */ + free(SA); + free(T); + + return 0; +} diff --git a/tools/apultra_src/src/libdivsufsort/examples/suftest.c b/tools/apultra_src/src/libdivsufsort/examples/suftest.c new file mode 100644 index 0000000..71892ac --- /dev/null +++ b/tools/apultra_src/src/libdivsufsort/examples/suftest.c @@ -0,0 +1,164 @@ +/* + * suftest.c for libdivsufsort + * Copyright (c) 2003-2008 Yuta Mori All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#if HAVE_CONFIG_H +# include "config.h" +#endif +#include <stdio.h> +#if HAVE_STRING_H +# include <string.h> +#endif +#if HAVE_STDLIB_H +# include <stdlib.h> +#endif +#if HAVE_MEMORY_H +# include <memory.h> +#endif +#if HAVE_STDDEF_H +# include <stddef.h> +#endif +#if HAVE_STRINGS_H +# include <strings.h> +#endif +#if HAVE_SYS_TYPES_H +# include <sys/types.h> +#endif +#if HAVE_IO_H && HAVE_FCNTL_H +# include <io.h> +# include <fcntl.h> +#endif +#include <time.h> +#include <divsufsort.h> +#include "lfs.h" + + +static +void +print_help(const char *progname, int status) { + fprintf(stderr, + "suftest, a suffixsort tester, version %s.\n", + divsufsort_version()); + fprintf(stderr, "usage: %s FILE\n\n", progname); + exit(status); +} + +int +main(int argc, const char *argv[]) { + FILE *fp; + const char *fname; + sauchar_t *T; + saidx_t *SA; + LFS_OFF_T n; + clock_t start, finish; + saint_t needclose = 1; + + /* Check arguments. */ + if((argc == 1) || + (strcmp(argv[1], "-h") == 0) || + (strcmp(argv[1], "--help") == 0)) { print_help(argv[0], EXIT_SUCCESS); } + if(argc != 2) { print_help(argv[0], EXIT_FAILURE); } + + /* Open a file for reading. */ + if(strcmp(argv[1], "-") != 0) { +#if HAVE_FOPEN_S + if(fopen_s(&fp, fname = argv[1], "rb") != 0) { +#else + if((fp = LFS_FOPEN(fname = argv[1], "rb")) == NULL) { +#endif + fprintf(stderr, "%s: Cannot open file `%s': ", argv[0], fname); + perror(NULL); + exit(EXIT_FAILURE); + } + } else { +#if HAVE__SETMODE && HAVE__FILENO + if(_setmode(_fileno(stdin), _O_BINARY) == -1) { + fprintf(stderr, "%s: Cannot set mode: ", argv[0]); + perror(NULL); + exit(EXIT_FAILURE); + } +#endif + fp = stdin; + fname = "stdin"; + needclose = 0; + } + + /* Get the file size. */ + if(LFS_FSEEK(fp, 0, SEEK_END) == 0) { + n = LFS_FTELL(fp); + rewind(fp); + if(n < 0) { + fprintf(stderr, "%s: Cannot ftell `%s': ", argv[0], fname); + perror(NULL); + exit(EXIT_FAILURE); + } + if(0x7fffffff <= n) { + fprintf(stderr, "%s: Input file `%s' is too big.\n", argv[0], fname); + exit(EXIT_FAILURE); + } + } else { + fprintf(stderr, "%s: Cannot fseek `%s': ", argv[0], fname); + perror(NULL); + exit(EXIT_FAILURE); + } + + /* Allocate 5n bytes of memory. */ + T = (sauchar_t *)malloc((size_t)n * sizeof(sauchar_t)); + SA = (saidx_t *)malloc((size_t)n * sizeof(saidx_t)); + if((T == NULL) || (SA == NULL)) { + fprintf(stderr, "%s: Cannot allocate memory.\n", argv[0]); + exit(EXIT_FAILURE); + } + + /* Read n bytes of data. */ + if(fread(T, sizeof(sauchar_t), (size_t)n, fp) != (size_t)n) { + fprintf(stderr, "%s: %s `%s': ", + argv[0], + (ferror(fp) || !feof(fp)) ? "Cannot read from" : "Unexpected EOF in", + argv[1]); + perror(NULL); + exit(EXIT_FAILURE); + } + if(needclose & 1) { fclose(fp); } + + /* Construct the suffix array. */ + fprintf(stderr, "%s: %" PRIdOFF_T " bytes ... ", fname, n); + start = clock(); + if(divsufsort(T, SA, (saidx_t)n) != 0) { + fprintf(stderr, "%s: Cannot allocate memory.\n", argv[0]); + exit(EXIT_FAILURE); + } + finish = clock(); + fprintf(stderr, "%.4f sec\n", (double)(finish - start) / (double)CLOCKS_PER_SEC); + + /* Check the suffix array. */ + if(sufcheck(T, SA, (saidx_t)n, 1) != 0) { exit(EXIT_FAILURE); } + + /* Deallocate memory. */ + free(SA); + free(T); + + return 0; +} diff --git a/tools/apultra_src/src/libdivsufsort/examples/unbwt.c b/tools/apultra_src/src/libdivsufsort/examples/unbwt.c new file mode 100644 index 0000000..c0f19e9 --- /dev/null +++ b/tools/apultra_src/src/libdivsufsort/examples/unbwt.c @@ -0,0 +1,207 @@ +/* + * unbwt.c for libdivsufsort + * Copyright (c) 2003-2008 Yuta Mori All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#if HAVE_CONFIG_H +# include "config.h" +#endif +#include <stdio.h> +#if HAVE_STRING_H +# include <string.h> +#endif +#if HAVE_STDLIB_H +# include <stdlib.h> +#endif +#if HAVE_MEMORY_H +# include <memory.h> +#endif +#if HAVE_STDDEF_H +# include <stddef.h> +#endif +#if HAVE_STRINGS_H +# include <strings.h> +#endif +#if HAVE_SYS_TYPES_H +# include <sys/types.h> +#endif +#if HAVE_IO_H && HAVE_FCNTL_H +# include <io.h> +# include <fcntl.h> +#endif +#include <time.h> +#include <divsufsort.h> +#include "lfs.h" + + +static +size_t +read_int(FILE *fp, saidx_t *n) { + unsigned char c[4]; + size_t m = fread(c, sizeof(unsigned char), 4, fp); + if(m == 4) { + *n = (c[0] << 0) | (c[1] << 8) | + (c[2] << 16) | (c[3] << 24); + } + return m; +} + +static +void +print_help(const char *progname, int status) { + fprintf(stderr, + "unbwt, an inverse burrows-wheeler transform program, version %s.\n", + divsufsort_version()); + fprintf(stderr, "usage: %s INFILE OUTFILE\n\n", progname); + exit(status); +} + +int +main(int argc, const char *argv[]) { + FILE *fp, *ofp; + const char *fname, *ofname; + sauchar_t *T; + saidx_t *A; + LFS_OFF_T n; + size_t m; + saidx_t pidx; + clock_t start, finish; + saint_t err, blocksize, needclose = 3; + + /* Check arguments. */ + if((argc == 1) || + (strcmp(argv[1], "-h") == 0) || + (strcmp(argv[1], "--help") == 0)) { print_help(argv[0], EXIT_SUCCESS); } + if(argc != 3) { print_help(argv[0], EXIT_FAILURE); } + + /* Open a file for reading. */ + if(strcmp(argv[1], "-") != 0) { +#if HAVE_FOPEN_S + if(fopen_s(&fp, fname = argv[1], "rb") != 0) { +#else + if((fp = LFS_FOPEN(fname = argv[1], "rb")) == NULL) { +#endif + fprintf(stderr, "%s: Cannot open file `%s': ", argv[0], fname); + perror(NULL); + exit(EXIT_FAILURE); + } + } else { +#if HAVE__SETMODE && HAVE__FILENO + if(_setmode(_fileno(stdin), _O_BINARY) == -1) { + fprintf(stderr, "%s: Cannot set mode: ", argv[0]); + perror(NULL); + exit(EXIT_FAILURE); + } +#endif + fp = stdin; + fname = "stdin"; + needclose ^= 1; + } + + /* Open a file for writing. */ + if(strcmp(argv[2], "-") != 0) { +#if HAVE_FOPEN_S + if(fopen_s(&ofp, ofname = argv[2], "wb") != 0) { +#else + if((ofp = LFS_FOPEN(ofname = argv[2], "wb")) == NULL) { +#endif + fprintf(stderr, "%s: Cannot open file `%s': ", argv[0], ofname); + perror(NULL); + exit(EXIT_FAILURE); + } + } else { +#if HAVE__SETMODE && HAVE__FILENO + if(_setmode(_fileno(stdout), _O_BINARY) == -1) { + fprintf(stderr, "%s: Cannot set mode: ", argv[0]); + perror(NULL); + exit(EXIT_FAILURE); + } +#endif + ofp = stdout; + ofname = "stdout"; + needclose ^= 2; + } + + /* Read the blocksize. */ + if(read_int(fp, &blocksize) != 4) { + fprintf(stderr, "%s: Cannot read from `%s': ", argv[0], fname); + perror(NULL); + exit(EXIT_FAILURE); + } + + /* Allocate 5blocksize bytes of memory. */ + T = (sauchar_t *)malloc(blocksize * sizeof(sauchar_t)); + A = (saidx_t *)malloc(blocksize * sizeof(saidx_t)); + if((T == NULL) || (A == NULL)) { + fprintf(stderr, "%s: Cannot allocate memory.\n", argv[0]); + exit(EXIT_FAILURE); + } + + fprintf(stderr, "UnBWT (blocksize %" PRIdSAINT_T ") ... ", blocksize); + start = clock(); + for(n = 0; (m = read_int(fp, &pidx)) != 0; n += m) { + /* Read blocksize bytes of data. */ + if((m != 4) || ((m = fread(T, sizeof(sauchar_t), blocksize, fp)) == 0)) { + fprintf(stderr, "%s: %s `%s': ", + argv[0], + (ferror(fp) || !feof(fp)) ? "Cannot read from" : "Unexpected EOF in", + fname); + perror(NULL); + exit(EXIT_FAILURE); + } + + /* Inverse Burrows-Wheeler Transform. */ + if((err = inverse_bw_transform(T, T, A, m, pidx)) != 0) { + fprintf(stderr, "%s (reverseBWT): %s.\n", + argv[0], + (err == -1) ? "Invalid data" : "Cannot allocate memory"); + exit(EXIT_FAILURE); + } + + /* Write m bytes of data. */ + if(fwrite(T, sizeof(sauchar_t), m, ofp) != m) { + fprintf(stderr, "%s: Cannot write to `%s': ", argv[0], ofname); + perror(NULL); + exit(EXIT_FAILURE); + } + } + if(ferror(fp)) { + fprintf(stderr, "%s: Cannot read from `%s': ", argv[0], fname); + perror(NULL); + exit(EXIT_FAILURE); + } + finish = clock(); + fprintf(stderr, "%" PRIdOFF_T " bytes: %.4f sec\n", + n, (double)(finish - start) / (double)CLOCKS_PER_SEC); + + /* Close files */ + if(needclose & 1) { fclose(fp); } + if(needclose & 2) { fclose(ofp); } + + /* Deallocate memory. */ + free(A); + free(T); + + return 0; +} diff --git a/tools/apultra_src/src/libdivsufsort/include/CMakeLists.txt b/tools/apultra_src/src/libdivsufsort/include/CMakeLists.txt new file mode 100644 index 0000000..37781cc --- /dev/null +++ b/tools/apultra_src/src/libdivsufsort/include/CMakeLists.txt @@ -0,0 +1,162 @@ +include(CheckIncludeFiles) +include(CheckIncludeFile) +include(CheckSymbolExists) +include(CheckTypeSize) +include(CheckFunctionKeywords) +include(CheckLFS) + +## Checks for header files ## +check_include_file("inttypes.h" HAVE_INTTYPES_H) +check_include_file("memory.h" HAVE_MEMORY_H) +check_include_file("stddef.h" HAVE_STDDEF_H) +check_include_file("stdint.h" HAVE_STDINT_H) +check_include_file("stdlib.h" HAVE_STDLIB_H) +check_include_file("string.h" HAVE_STRING_H) +check_include_file("strings.h" HAVE_STRINGS_H) +check_include_file("sys/types.h" HAVE_SYS_TYPES_H) +if(HAVE_INTTYPES_H) + set(INCFILE "#include <inttypes.h>") +elseif(HAVE_STDINT_H) + set(INCFILE "#include <stdint.h>") +else(HAVE_INTTYPES_H) + set(INCFILE "") +endif(HAVE_INTTYPES_H) + +## create configuration files from .cmake file ## +if(BUILD_EXAMPLES) + ## Checks for WinIO ## + if(WIN32) + check_include_file("io.h" HAVE_IO_H) + check_include_file("fcntl.h" HAVE_FCNTL_H) + check_symbol_exists("_setmode" "io.h;fcntl.h" HAVE__SETMODE) + if(NOT HAVE__SETMODE) + check_symbol_exists("setmode" "io.h;fcntl.h" HAVE_SETMODE) + endif(NOT HAVE__SETMODE) + check_symbol_exists("_fileno" "stdio.h" HAVE__FILENO) + check_symbol_exists("fopen_s" "stdio.h" HAVE_FOPEN_S) + check_symbol_exists("_O_BINARY" "fcntl.h" HAVE__O_BINARY) + endif(WIN32) + + ## Checks for large file support ## + check_lfs(WITH_LFS) + configure_file("${CMAKE_CURRENT_SOURCE_DIR}/lfs.h.cmake" "${CMAKE_CURRENT_BINARY_DIR}/lfs.h" @ONLY) +endif(BUILD_EXAMPLES) + +## generate config.h ## +check_function_keywords("inline;__inline;__inline__;__declspec(dllexport);__declspec(dllimport)") +if(HAVE_INLINE) + set(INLINE "inline") +elseif(HAVE___INLINE) + set(INLINE "__inline") +elseif(HAVE___INLINE__) + set(INLINE "__inline__") +else(HAVE_INLINE) + set(INLINE "") +endif(HAVE_INLINE) +configure_file("${CMAKE_CURRENT_SOURCE_DIR}/config.h.cmake" "${CMAKE_CURRENT_BINARY_DIR}/config.h") + +## Checks for types ## +# sauchar_t (8bit) +check_type_size("uint8_t" UINT8_T) +if(HAVE_UINT8_T) + set(SAUCHAR_TYPE "uint8_t") +else(HAVE_UINT8_T) + check_type_size("unsigned char" SIZEOF_UNSIGNED_CHAR) + if("${SIZEOF_UNSIGNED_CHAR}" STREQUAL "1") + set(SAUCHAR_TYPE "unsigned char") + else("${SIZEOF_UNSIGNED_CHAR}" STREQUAL "1") + message(FATAL_ERROR "Cannot find unsigned 8-bit integer type") + endif("${SIZEOF_UNSIGNED_CHAR}" STREQUAL "1") +endif(HAVE_UINT8_T) +# saint_t (32bit) +check_type_size("int32_t" INT32_T) +if(HAVE_INT32_T) + set(SAINT32_TYPE "int32_t") + check_symbol_exists("PRId32" "inttypes.h" HAVE_PRID32) + if(HAVE_PRID32) + set(SAINT32_PRId "PRId32") + else(HAVE_PRID32) + set(SAINT32_PRId "\"d\"") + endif(HAVE_PRID32) +else(HAVE_INT32_T) + check_type_size("int" SIZEOF_INT) + check_type_size("long" SIZEOF_LONG) + check_type_size("short" SIZEOF_SHORT) + check_type_size("__int32" SIZEOF___INT32) + if("${SIZEOF_INT}" STREQUAL "4") + set(SAINT32_TYPE "int") + set(SAINT32_PRId "\"d\"") + elseif("${SIZEOF_LONG}" STREQUAL "4") + set(SAINT32_TYPE "long") + set(SAINT32_PRId "\"ld\"") + elseif("${SIZEOF_SHORT}" STREQUAL "4") + set(SAINT32_TYPE "short") + set(SAINT32_PRId "\"d\"") + elseif("${SIZEOF___INT32}" STREQUAL "4") + set(SAINT32_TYPE "__int32") + set(SAINT32_PRId "\"d\"") + else("${SIZEOF_INT}" STREQUAL "4") + message(FATAL_ERROR "Cannot find 32-bit integer type") + endif("${SIZEOF_INT}" STREQUAL "4") +endif(HAVE_INT32_T) +# saint64_t (64bit) +if(BUILD_DIVSUFSORT64) + check_type_size("int64_t" INT64_T) + if(HAVE_INT64_T) + set(SAINT64_TYPE "int64_t") + check_symbol_exists("PRId64" "inttypes.h" HAVE_PRID64) + if(HAVE_PRID64) + set(SAINT64_PRId "PRId64") + else(HAVE_PRID64) + set(SAINT64_PRId "\"lld\"") + endif(HAVE_PRID64) + else(HAVE_INT64_T) + check_type_size("int" SIZEOF_INT) + check_type_size("long" SIZEOF_LONG) + check_type_size("long long" SIZEOF_LONG_LONG) + check_type_size("__int64" SIZEOF___INT64) + if("${SIZEOF_INT}" STREQUAL "8") + set(SAINT64_TYPE "int") + set(SAINT64_PRId "\"d\"") + elseif("${SIZEOF_LONG}" STREQUAL "8") + set(SAINT64_TYPE "long") + set(SAINT64_PRId "\"ld\"") + elseif("${SIZEOF_LONG_LONG}" STREQUAL "8") + set(SAINT64_TYPE "long long") + set(SAINT64_PRId "\"lld\"") + elseif("${SIZEOF___INT64}" STREQUAL "8") + set(SAINT64_TYPE "__int64") + set(SAINT64_PRId "\"I64d\"") + else("${SIZEOF_INT}" STREQUAL "8") + message(SEND_ERROR "Cannot find 64-bit integer type") + set(BUILD_DIVSUFSORT64 OFF) + endif("${SIZEOF_INT}" STREQUAL "8") + endif(HAVE_INT64_T) +endif(BUILD_DIVSUFSORT64) + +## generate divsufsort.h ## +set(DIVSUFSORT_IMPORT "") +set(DIVSUFSORT_EXPORT "") +if(BUILD_SHARED_LIBS) + if(HAVE___DECLSPEC_DLLIMPORT_) + set(DIVSUFSORT_IMPORT "__declspec(dllimport)") + endif(HAVE___DECLSPEC_DLLIMPORT_) + if(HAVE___DECLSPEC_DLLEXPORT_) + set(DIVSUFSORT_EXPORT "__declspec(dllexport)") + endif(HAVE___DECLSPEC_DLLEXPORT_) +endif(BUILD_SHARED_LIBS) +set(W64BIT "") +set(SAINDEX_TYPE "${SAINT32_TYPE}") +set(SAINDEX_PRId "${SAINT32_PRId}") +set(SAINT_PRId "${SAINT32_PRId}") +configure_file("${CMAKE_CURRENT_SOURCE_DIR}/divsufsort.h.cmake" + "${CMAKE_CURRENT_BINARY_DIR}/divsufsort.h" @ONLY) +install(FILES "${CMAKE_CURRENT_BINARY_DIR}/divsufsort.h" DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) +if(BUILD_DIVSUFSORT64) + set(W64BIT "64") + set(SAINDEX_TYPE "${SAINT64_TYPE}") + set(SAINDEX_PRId "${SAINT64_PRId}") + configure_file("${CMAKE_CURRENT_SOURCE_DIR}/divsufsort.h.cmake" + "${CMAKE_CURRENT_BINARY_DIR}/divsufsort64.h" @ONLY) + install(FILES "${CMAKE_CURRENT_BINARY_DIR}/divsufsort64.h" DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) +endif(BUILD_DIVSUFSORT64) diff --git a/tools/apultra_src/src/libdivsufsort/include/config.h.cmake b/tools/apultra_src/src/libdivsufsort/include/config.h.cmake new file mode 100644 index 0000000..6a1cf47 --- /dev/null +++ b/tools/apultra_src/src/libdivsufsort/include/config.h.cmake @@ -0,0 +1,81 @@ +/* + * config.h for libdivsufsort + * Copyright (c) 2003-2008 Yuta Mori All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef _CONFIG_H +#define _CONFIG_H 1 + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** Define to the version of this package. **/ +#cmakedefine PROJECT_VERSION_FULL "${PROJECT_VERSION_FULL}" + +/** Define to 1 if you have the header files. **/ +#cmakedefine HAVE_INTTYPES_H 1 +#cmakedefine HAVE_STDDEF_H 1 +#cmakedefine HAVE_STDINT_H 1 +#cmakedefine HAVE_STDLIB_H 1 +#cmakedefine HAVE_STRING_H 1 +#cmakedefine HAVE_STRINGS_H 1 +#cmakedefine HAVE_MEMORY_H 1 +#cmakedefine HAVE_SYS_TYPES_H 1 + +/** for WinIO **/ +#cmakedefine HAVE_IO_H 1 +#cmakedefine HAVE_FCNTL_H 1 +#cmakedefine HAVE__SETMODE 1 +#cmakedefine HAVE_SETMODE 1 +#cmakedefine HAVE__FILENO 1 +#cmakedefine HAVE_FOPEN_S 1 +#cmakedefine HAVE__O_BINARY 1 +#ifndef HAVE__SETMODE +# if HAVE_SETMODE +# define _setmode setmode +# define HAVE__SETMODE 1 +# endif +# if HAVE__SETMODE && !HAVE__O_BINARY +# define _O_BINARY 0 +# define HAVE__O_BINARY 1 +# endif +#endif + +/** for inline **/ +#ifndef INLINE +# define INLINE @INLINE@ +#endif + +/** for VC++ warning **/ +#ifdef _MSC_VER +#pragma warning(disable: 4127) +#endif + + +#ifdef __cplusplus +} /* extern "C" */ +#endif /* __cplusplus */ + +#endif /* _CONFIG_H */ diff --git a/tools/apultra_src/src/libdivsufsort/include/divsufsort.h b/tools/apultra_src/src/libdivsufsort/include/divsufsort.h new file mode 100644 index 0000000..7ebb412 --- /dev/null +++ b/tools/apultra_src/src/libdivsufsort/include/divsufsort.h @@ -0,0 +1,189 @@ +/* + * divsufsort.h for libdivsufsort + * Copyright (c) 2003-2008 Yuta Mori All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef _DIVSUFSORT_H +#define _DIVSUFSORT_H 1 + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define DIVSUFSORT_API + +/*- Datatypes -*/ +#ifndef SAUCHAR_T +#define SAUCHAR_T +typedef unsigned char sauchar_t; +#endif /* SAUCHAR_T */ +#ifndef SAINT_T +#define SAINT_T +typedef int saint_t; +#endif /* SAINT_T */ +#ifndef SAIDX_T +#define SAIDX_T +typedef int saidx_t; +#endif /* SAIDX_T */ +#ifndef PRIdSAIDX_T +#define PRIdSAIDX_T "d" +#endif + +/*- divsufsort context */ +typedef struct _divsufsort_ctx_t { + saidx_t *bucket_A; + saidx_t *bucket_B; +} divsufsort_ctx_t; + +/*- Prototypes -*/ + +/** + * Initialize suffix array context + * + * @return 0 for success, or non-zero in case of an error + */ +int divsufsort_init(divsufsort_ctx_t *ctx); + +/** + * Destroy suffix array context + * + * @param ctx suffix array context to destroy + */ +void divsufsort_destroy(divsufsort_ctx_t *ctx); + +/** + * Constructs the suffix array of a given string. + * @param ctx suffix array context + * @param T[0..n-1] The input string. + * @param SA[0..n-1] The output array of suffixes. + * @param n The length of the given string. + * @return 0 if no error occurred, -1 or -2 otherwise. + */ +DIVSUFSORT_API +saint_t divsufsort_build_array(divsufsort_ctx_t *ctx, const sauchar_t *T, saidx_t *SA, saidx_t n); + +#if 0 +/** + * Constructs the burrows-wheeler transformed string of a given string. + * @param T[0..n-1] The input string. + * @param U[0..n-1] The output string. (can be T) + * @param A[0..n-1] The temporary array. (can be NULL) + * @param n The length of the given string. + * @return The primary index if no error occurred, -1 or -2 otherwise. + */ +DIVSUFSORT_API +saidx_t +divbwt(const sauchar_t *T, sauchar_t *U, saidx_t *A, saidx_t n); + +/** + * Returns the version of the divsufsort library. + * @return The version number string. + */ +DIVSUFSORT_API +const char * +divsufsort_version(void); + + +/** + * Constructs the burrows-wheeler transformed string of a given string and suffix array. + * @param T[0..n-1] The input string. + * @param U[0..n-1] The output string. (can be T) + * @param SA[0..n-1] The suffix array. (can be NULL) + * @param n The length of the given string. + * @param idx The output primary index. + * @return 0 if no error occurred, -1 or -2 otherwise. + */ +DIVSUFSORT_API +saint_t +bw_transform(const sauchar_t *T, sauchar_t *U, + saidx_t *SA /* can NULL */, + saidx_t n, saidx_t *idx); + +/** + * Inverse BW-transforms a given BWTed string. + * @param T[0..n-1] The input string. + * @param U[0..n-1] The output string. (can be T) + * @param A[0..n-1] The temporary array. (can be NULL) + * @param n The length of the given string. + * @param idx The primary index. + * @return 0 if no error occurred, -1 or -2 otherwise. + */ +DIVSUFSORT_API +saint_t +inverse_bw_transform(const sauchar_t *T, sauchar_t *U, + saidx_t *A /* can NULL */, + saidx_t n, saidx_t idx); + +/** + * Checks the correctness of a given suffix array. + * @param T[0..n-1] The input string. + * @param SA[0..n-1] The input suffix array. + * @param n The length of the given string. + * @param verbose The verbose mode. + * @return 0 if no error occurred. + */ +DIVSUFSORT_API +saint_t +sufcheck(const sauchar_t *T, const saidx_t *SA, saidx_t n, saint_t verbose); + +/** + * Search for the pattern P in the string T. + * @param T[0..Tsize-1] The input string. + * @param Tsize The length of the given string. + * @param P[0..Psize-1] The input pattern string. + * @param Psize The length of the given pattern string. + * @param SA[0..SAsize-1] The input suffix array. + * @param SAsize The length of the given suffix array. + * @param idx The output index. + * @return The count of matches if no error occurred, -1 otherwise. + */ +DIVSUFSORT_API +saidx_t +sa_search(const sauchar_t *T, saidx_t Tsize, + const sauchar_t *P, saidx_t Psize, + const saidx_t *SA, saidx_t SAsize, + saidx_t *left); + +/** + * Search for the character c in the string T. + * @param T[0..Tsize-1] The input string. + * @param Tsize The length of the given string. + * @param SA[0..SAsize-1] The input suffix array. + * @param SAsize The length of the given suffix array. + * @param c The input character. + * @param idx The output index. + * @return The count of matches if no error occurred, -1 otherwise. + */ +DIVSUFSORT_API +saidx_t +sa_simplesearch(const sauchar_t *T, saidx_t Tsize, + const saidx_t *SA, saidx_t SAsize, + saint_t c, saidx_t *left); +#endif + +#ifdef __cplusplus +} /* extern "C" */ +#endif /* __cplusplus */ + +#endif /* _DIVSUFSORT_H */ diff --git a/tools/apultra_src/src/libdivsufsort/include/divsufsort.h.cmake b/tools/apultra_src/src/libdivsufsort/include/divsufsort.h.cmake new file mode 100644 index 0000000..bcaba7c --- /dev/null +++ b/tools/apultra_src/src/libdivsufsort/include/divsufsort.h.cmake @@ -0,0 +1,180 @@ +/* + * divsufsort@W64BIT@.h for libdivsufsort@W64BIT@ + * Copyright (c) 2003-2008 Yuta Mori All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef _DIVSUFSORT@W64BIT@_H +#define _DIVSUFSORT@W64BIT@_H 1 + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +@INCFILE@ + +#ifndef DIVSUFSORT_API +# ifdef DIVSUFSORT_BUILD_DLL +# define DIVSUFSORT_API @DIVSUFSORT_EXPORT@ +# else +# define DIVSUFSORT_API @DIVSUFSORT_IMPORT@ +# endif +#endif + +/*- Datatypes -*/ +#ifndef SAUCHAR_T +#define SAUCHAR_T +typedef @SAUCHAR_TYPE@ sauchar_t; +#endif /* SAUCHAR_T */ +#ifndef SAINT_T +#define SAINT_T +typedef @SAINT32_TYPE@ saint_t; +#endif /* SAINT_T */ +#ifndef SAIDX@W64BIT@_T +#define SAIDX@W64BIT@_T +typedef @SAINDEX_TYPE@ saidx@W64BIT@_t; +#endif /* SAIDX@W64BIT@_T */ +#ifndef PRIdSAINT_T +#define PRIdSAINT_T @SAINT_PRId@ +#endif /* PRIdSAINT_T */ +#ifndef PRIdSAIDX@W64BIT@_T +#define PRIdSAIDX@W64BIT@_T @SAINDEX_PRId@ +#endif /* PRIdSAIDX@W64BIT@_T */ + + +/*- Prototypes -*/ + +/** + * Constructs the suffix array of a given string. + * @param T[0..n-1] The input string. + * @param SA[0..n-1] The output array of suffixes. + * @param n The length of the given string. + * @return 0 if no error occurred, -1 or -2 otherwise. + */ +DIVSUFSORT_API +saint_t +divsufsort@W64BIT@(const sauchar_t *T, saidx@W64BIT@_t *SA, saidx@W64BIT@_t n); + +/** + * Constructs the burrows-wheeler transformed string of a given string. + * @param T[0..n-1] The input string. + * @param U[0..n-1] The output string. (can be T) + * @param A[0..n-1] The temporary array. (can be NULL) + * @param n The length of the given string. + * @return The primary index if no error occurred, -1 or -2 otherwise. + */ +DIVSUFSORT_API +saidx@W64BIT@_t +divbwt@W64BIT@(const sauchar_t *T, sauchar_t *U, saidx@W64BIT@_t *A, saidx@W64BIT@_t n); + +/** + * Returns the version of the divsufsort library. + * @return The version number string. + */ +DIVSUFSORT_API +const char * +divsufsort@W64BIT@_version(void); + + +/** + * Constructs the burrows-wheeler transformed string of a given string and suffix array. + * @param T[0..n-1] The input string. + * @param U[0..n-1] The output string. (can be T) + * @param SA[0..n-1] The suffix array. (can be NULL) + * @param n The length of the given string. + * @param idx The output primary index. + * @return 0 if no error occurred, -1 or -2 otherwise. + */ +DIVSUFSORT_API +saint_t +bw_transform@W64BIT@(const sauchar_t *T, sauchar_t *U, + saidx@W64BIT@_t *SA /* can NULL */, + saidx@W64BIT@_t n, saidx@W64BIT@_t *idx); + +/** + * Inverse BW-transforms a given BWTed string. + * @param T[0..n-1] The input string. + * @param U[0..n-1] The output string. (can be T) + * @param A[0..n-1] The temporary array. (can be NULL) + * @param n The length of the given string. + * @param idx The primary index. + * @return 0 if no error occurred, -1 or -2 otherwise. + */ +DIVSUFSORT_API +saint_t +inverse_bw_transform@W64BIT@(const sauchar_t *T, sauchar_t *U, + saidx@W64BIT@_t *A /* can NULL */, + saidx@W64BIT@_t n, saidx@W64BIT@_t idx); + +/** + * Checks the correctness of a given suffix array. + * @param T[0..n-1] The input string. + * @param SA[0..n-1] The input suffix array. + * @param n The length of the given string. + * @param verbose The verbose mode. + * @return 0 if no error occurred. + */ +DIVSUFSORT_API +saint_t +sufcheck@W64BIT@(const sauchar_t *T, const saidx@W64BIT@_t *SA, saidx@W64BIT@_t n, saint_t verbose); + +/** + * Search for the pattern P in the string T. + * @param T[0..Tsize-1] The input string. + * @param Tsize The length of the given string. + * @param P[0..Psize-1] The input pattern string. + * @param Psize The length of the given pattern string. + * @param SA[0..SAsize-1] The input suffix array. + * @param SAsize The length of the given suffix array. + * @param idx The output index. + * @return The count of matches if no error occurred, -1 otherwise. + */ +DIVSUFSORT_API +saidx@W64BIT@_t +sa_search@W64BIT@(const sauchar_t *T, saidx@W64BIT@_t Tsize, + const sauchar_t *P, saidx@W64BIT@_t Psize, + const saidx@W64BIT@_t *SA, saidx@W64BIT@_t SAsize, + saidx@W64BIT@_t *left); + +/** + * Search for the character c in the string T. + * @param T[0..Tsize-1] The input string. + * @param Tsize The length of the given string. + * @param SA[0..SAsize-1] The input suffix array. + * @param SAsize The length of the given suffix array. + * @param c The input character. + * @param idx The output index. + * @return The count of matches if no error occurred, -1 otherwise. + */ +DIVSUFSORT_API +saidx@W64BIT@_t +sa_simplesearch@W64BIT@(const sauchar_t *T, saidx@W64BIT@_t Tsize, + const saidx@W64BIT@_t *SA, saidx@W64BIT@_t SAsize, + saint_t c, saidx@W64BIT@_t *left); + + +#ifdef __cplusplus +} /* extern "C" */ +#endif /* __cplusplus */ + +#endif /* _DIVSUFSORT@W64BIT@_H */ diff --git a/tools/apultra_src/src/libdivsufsort/include/divsufsort_config.h b/tools/apultra_src/src/libdivsufsort/include/divsufsort_config.h new file mode 100644 index 0000000..4054a8a --- /dev/null +++ b/tools/apultra_src/src/libdivsufsort/include/divsufsort_config.h @@ -0,0 +1,9 @@ +#define HAVE_STRING_H 1
+#define HAVE_STDLIB_H 1
+#define HAVE_MEMORY_H 1
+#define HAVE_STDINT_H 1
+#define INLINE inline
+
+#ifdef _MSC_VER
+#pragma warning( disable : 4244 )
+#endif /* _MSC_VER */
diff --git a/tools/apultra_src/src/libdivsufsort/include/divsufsort_private.h b/tools/apultra_src/src/libdivsufsort/include/divsufsort_private.h new file mode 100644 index 0000000..b4d97ad --- /dev/null +++ b/tools/apultra_src/src/libdivsufsort/include/divsufsort_private.h @@ -0,0 +1,205 @@ +/* + * divsufsort_private.h for libdivsufsort + * Copyright (c) 2003-2008 Yuta Mori All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef _DIVSUFSORT_PRIVATE_H +#define _DIVSUFSORT_PRIVATE_H 1 + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#include "divsufsort_config.h" +#include <assert.h> +#include <stdio.h> +#if HAVE_STRING_H +# include <string.h> +#endif +#if HAVE_STDLIB_H +# include <stdlib.h> +#endif +#if HAVE_MEMORY_H +# include <memory.h> +#endif +#if HAVE_STDDEF_H +# include <stddef.h> +#endif +#if HAVE_STRINGS_H +# include <strings.h> +#endif +#if HAVE_INTTYPES_H +# include <inttypes.h> +#else +# if HAVE_STDINT_H +# include <stdint.h> +# endif +#endif +#if defined(BUILD_DIVSUFSORT64) +# include "divsufsort64.h" +# ifndef SAIDX_T +# define SAIDX_T +# define saidx_t saidx64_t +# endif /* SAIDX_T */ +# ifndef PRIdSAIDX_T +# define PRIdSAIDX_T PRIdSAIDX64_T +# endif /* PRIdSAIDX_T */ +# define divsufsort divsufsort64 +# define divbwt divbwt64 +# define divsufsort_version divsufsort64_version +# define bw_transform bw_transform64 +# define inverse_bw_transform inverse_bw_transform64 +# define sufcheck sufcheck64 +# define sa_search sa_search64 +# define sa_simplesearch sa_simplesearch64 +# define sssort sssort64 +# define trsort trsort64 +#else +# include "divsufsort.h" +#endif + + +/*- Constants -*/ +#if !defined(UINT8_MAX) +# define UINT8_MAX (255) +#endif /* UINT8_MAX */ +#if defined(ALPHABET_SIZE) && (ALPHABET_SIZE < 1) +# undef ALPHABET_SIZE +#endif +#if !defined(ALPHABET_SIZE) +# define ALPHABET_SIZE (UINT8_MAX + 1) +#endif +/* for divsufsort.c */ +#define BUCKET_A_SIZE (ALPHABET_SIZE) +#define BUCKET_B_SIZE (ALPHABET_SIZE * ALPHABET_SIZE) +/* for sssort.c */ +#if defined(SS_INSERTIONSORT_THRESHOLD) +# if SS_INSERTIONSORT_THRESHOLD < 1 +# undef SS_INSERTIONSORT_THRESHOLD +# define SS_INSERTIONSORT_THRESHOLD (1) +# endif +#else +# define SS_INSERTIONSORT_THRESHOLD (8) +#endif +#if defined(SS_BLOCKSIZE) +# if SS_BLOCKSIZE < 0 +# undef SS_BLOCKSIZE +# define SS_BLOCKSIZE (0) +# elif 32768 <= SS_BLOCKSIZE +# undef SS_BLOCKSIZE +# define SS_BLOCKSIZE (32767) +# endif +#else +# define SS_BLOCKSIZE (1024) +#endif +/* minstacksize = log(SS_BLOCKSIZE) / log(3) * 2 */ +#if SS_BLOCKSIZE == 0 +# if defined(BUILD_DIVSUFSORT64) +# define SS_MISORT_STACKSIZE (96) +# else +# define SS_MISORT_STACKSIZE (64) +# endif +#elif SS_BLOCKSIZE <= 4096 +# define SS_MISORT_STACKSIZE (16) +#else +# define SS_MISORT_STACKSIZE (24) +#endif +#if defined(BUILD_DIVSUFSORT64) +# define SS_SMERGE_STACKSIZE (64) +#else +# define SS_SMERGE_STACKSIZE (32) +#endif +/* for trsort.c */ +#define TR_INSERTIONSORT_THRESHOLD (8) +#if defined(BUILD_DIVSUFSORT64) +# define TR_STACKSIZE (96) +#else +# define TR_STACKSIZE (64) +#endif + + +/*- Macros -*/ +#ifndef SWAP +# define SWAP(_a, _b) do { t = (_a); (_a) = (_b); (_b) = t; } while(0) +#endif /* SWAP */ +#ifndef MIN +# define MIN(_a, _b) (((_a) < (_b)) ? (_a) : (_b)) +#endif /* MIN */ +#ifndef MAX +# define MAX(_a, _b) (((_a) > (_b)) ? (_a) : (_b)) +#endif /* MAX */ +#define STACK_PUSH(_a, _b, _c, _d)\ + do {\ + assert(ssize < STACK_SIZE);\ + stack[ssize].a = (_a), stack[ssize].b = (_b),\ + stack[ssize].c = (_c), stack[ssize++].d = (_d);\ + } while(0) +#define STACK_PUSH5(_a, _b, _c, _d, _e)\ + do {\ + assert(ssize < STACK_SIZE);\ + stack[ssize].a = (_a), stack[ssize].b = (_b),\ + stack[ssize].c = (_c), stack[ssize].d = (_d), stack[ssize++].e = (_e);\ + } while(0) +#define STACK_POP(_a, _b, _c, _d)\ + do {\ + assert(0 <= ssize);\ + if(ssize == 0) { return; }\ + (_a) = stack[--ssize].a, (_b) = stack[ssize].b,\ + (_c) = stack[ssize].c, (_d) = stack[ssize].d;\ + } while(0) +#define STACK_POP5(_a, _b, _c, _d, _e)\ + do {\ + assert(0 <= ssize);\ + if(ssize == 0) { return; }\ + (_a) = stack[--ssize].a, (_b) = stack[ssize].b,\ + (_c) = stack[ssize].c, (_d) = stack[ssize].d, (_e) = stack[ssize].e;\ + } while(0) +/* for divsufsort.c */ +#define BUCKET_A(_c0) bucket_A[(_c0)] +#if ALPHABET_SIZE == 256 +#define BUCKET_B(_c0, _c1) (bucket_B[((_c1) << 8) | (_c0)]) +#define BUCKET_BSTAR(_c0, _c1) (bucket_B[((_c0) << 8) | (_c1)]) +#else +#define BUCKET_B(_c0, _c1) (bucket_B[(_c1) * ALPHABET_SIZE + (_c0)]) +#define BUCKET_BSTAR(_c0, _c1) (bucket_B[(_c0) * ALPHABET_SIZE + (_c1)]) +#endif + + +/*- Private Prototypes -*/ +/* sssort.c */ +void +sssort(const sauchar_t *Td, const saidx_t *PA, + saidx_t *first, saidx_t *last, + saidx_t *buf, saidx_t bufsize, + saidx_t depth, saidx_t n, saint_t lastsuffix); +/* trsort.c */ +void +trsort(saidx_t *ISA, saidx_t *SA, saidx_t n, saidx_t depth); + + +#ifdef __cplusplus +} /* extern "C" */ +#endif /* __cplusplus */ + +#endif /* _DIVSUFSORT_PRIVATE_H */ diff --git a/tools/apultra_src/src/libdivsufsort/include/lfs.h.cmake b/tools/apultra_src/src/libdivsufsort/include/lfs.h.cmake new file mode 100644 index 0000000..d5b84a8 --- /dev/null +++ b/tools/apultra_src/src/libdivsufsort/include/lfs.h.cmake @@ -0,0 +1,56 @@ +/* + * lfs.h for libdivsufsort + * Copyright (c) 2003-2008 Yuta Mori All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef _LFS_H +#define _LFS_H 1 + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#ifndef __STRICT_ANSI__ +# define LFS_OFF_T @LFS_OFF_T@ +# define LFS_FOPEN @LFS_FOPEN@ +# define LFS_FTELL @LFS_FTELL@ +# define LFS_FSEEK @LFS_FSEEK@ +# define LFS_PRId @LFS_PRID@ +#else +# define LFS_OFF_T long +# define LFS_FOPEN fopen +# define LFS_FTELL ftell +# define LFS_FSEEK fseek +# define LFS_PRId "ld" +#endif +#ifndef PRIdOFF_T +# define PRIdOFF_T LFS_PRId +#endif + + +#ifdef __cplusplus +} /* extern "C" */ +#endif /* __cplusplus */ + +#endif /* _LFS_H */ diff --git a/tools/apultra_src/src/libdivsufsort/lib/CMakeLists.txt b/tools/apultra_src/src/libdivsufsort/lib/CMakeLists.txt new file mode 100644 index 0000000..abc90e6 --- /dev/null +++ b/tools/apultra_src/src/libdivsufsort/lib/CMakeLists.txt @@ -0,0 +1,31 @@ +include_directories("${CMAKE_CURRENT_SOURCE_DIR}/../include" + "${CMAKE_CURRENT_BINARY_DIR}/../include") + +set(divsufsort_SRCS divsufsort.c sssort.c trsort.c utils.c) + +## libdivsufsort ## +add_library(divsufsort ${divsufsort_SRCS}) +install(TARGETS divsufsort + RUNTIME DESTINATION ${CMAKE_INSTALL_RUNTIMEDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) +set_target_properties(divsufsort PROPERTIES + VERSION "${LIBRARY_VERSION}" + SOVERSION "${LIBRARY_SOVERSION}" + DEFINE_SYMBOL DIVSUFSORT_BUILD_DLL + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/../examples") + +## libdivsufsort64 ## +if(BUILD_DIVSUFSORT64) + add_library(divsufsort64 ${divsufsort_SRCS}) + install(TARGETS divsufsort64 + RUNTIME DESTINATION ${CMAKE_INSTALL_RUNTIMEDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) + set_target_properties(divsufsort64 PROPERTIES + VERSION "${LIBRARY_VERSION}" + SOVERSION "${LIBRARY_SOVERSION}" + DEFINE_SYMBOL DIVSUFSORT_BUILD_DLL + COMPILE_FLAGS "-DBUILD_DIVSUFSORT64" + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/../examples") +endif(BUILD_DIVSUFSORT64) diff --git a/tools/apultra_src/src/libdivsufsort/lib/divsufsort.c b/tools/apultra_src/src/libdivsufsort/lib/divsufsort.c new file mode 100644 index 0000000..50631ac --- /dev/null +++ b/tools/apultra_src/src/libdivsufsort/lib/divsufsort.c @@ -0,0 +1,431 @@ +/* + * divsufsort.c for libdivsufsort + * Copyright (c) 2003-2008 Yuta Mori All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "divsufsort_private.h" +#ifdef _OPENMP +# include <omp.h> +#endif + + +/*- Private Functions -*/ + +/* Sorts suffixes of type B*. */ +static +saidx_t +sort_typeBstar(const sauchar_t *T, saidx_t *SA, + saidx_t *bucket_A, saidx_t *bucket_B, + saidx_t n) { + saidx_t *PAb, *ISAb, *buf; +#ifdef _OPENMP + saidx_t *curbuf; + saidx_t l; +#endif + saidx_t i, j, k, t, m, bufsize; + saint_t c0, c1; +#ifdef _OPENMP + saint_t d0, d1; + int tmp; +#endif + + /* Initialize bucket arrays. */ + for(i = 0; i < BUCKET_A_SIZE; ++i) { bucket_A[i] = 0; } + for(i = 0; i < BUCKET_B_SIZE; ++i) { bucket_B[i] = 0; } + + /* Count the number of occurrences of the first one or two characters of each + type A, B and B* suffix. Moreover, store the beginning position of all + type B* suffixes into the array SA. */ + for(i = n - 1, m = n, c0 = T[n - 1]; 0 <= i;) { + /* type A suffix. */ + do { ++BUCKET_A(c1 = c0); } while((0 <= --i) && ((c0 = T[i]) >= c1)); + if(0 <= i) { + /* type B* suffix. */ + ++BUCKET_BSTAR(c0, c1); + SA[--m] = i; + /* type B suffix. */ + for(--i, c1 = c0; (0 <= i) && ((c0 = T[i]) <= c1); --i, c1 = c0) { + ++BUCKET_B(c0, c1); + } + } + } + m = n - m; +/* +note: + A type B* suffix is lexicographically smaller than a type B suffix that + begins with the same first two characters. +*/ + + /* Calculate the index of start/end point of each bucket. */ + for(c0 = 0, i = 0, j = 0; c0 < ALPHABET_SIZE; ++c0) { + t = i + BUCKET_A(c0); + BUCKET_A(c0) = i + j; /* start point */ + i = t + BUCKET_B(c0, c0); + for(c1 = c0 + 1; c1 < ALPHABET_SIZE; ++c1) { + j += BUCKET_BSTAR(c0, c1); + BUCKET_BSTAR(c0, c1) = j; /* end point */ + i += BUCKET_B(c0, c1); + } + } + + if(0 < m) { + /* Sort the type B* suffixes by their first two characters. */ + PAb = SA + n - m; ISAb = SA + m; + for(i = m - 2; 0 <= i; --i) { + t = PAb[i], c0 = T[t], c1 = T[t + 1]; + SA[--BUCKET_BSTAR(c0, c1)] = i; + } + t = PAb[m - 1], c0 = T[t], c1 = T[t + 1]; + SA[--BUCKET_BSTAR(c0, c1)] = m - 1; + + /* Sort the type B* substrings using sssort. */ +#ifdef _OPENMP + tmp = omp_get_max_threads(); + buf = SA + m, bufsize = (n - (2 * m)) / tmp; + c0 = ALPHABET_SIZE - 2, c1 = ALPHABET_SIZE - 1, j = m; +#pragma omp parallel default(shared) private(curbuf, k, l, d0, d1, tmp) + { + tmp = omp_get_thread_num(); + curbuf = buf + tmp * bufsize; + k = 0; + for(;;) { + #pragma omp critical(sssort_lock) + { + if(0 < (l = j)) { + d0 = c0, d1 = c1; + do { + k = BUCKET_BSTAR(d0, d1); + if(--d1 <= d0) { + d1 = ALPHABET_SIZE - 1; + if(--d0 < 0) { break; } + } + } while(((l - k) <= 1) && (0 < (l = k))); + c0 = d0, c1 = d1, j = k; + } + } + if(l == 0) { break; } + sssort(T, PAb, SA + k, SA + l, + curbuf, bufsize, 2, n, *(SA + k) == (m - 1)); + } + } +#else + buf = SA + m, bufsize = n - (2 * m); + for(c0 = ALPHABET_SIZE - 2, j = m; 0 < j; --c0) { + for(c1 = ALPHABET_SIZE - 1; c0 < c1; j = i, --c1) { + i = BUCKET_BSTAR(c0, c1); + if(1 < (j - i)) { + sssort(T, PAb, SA + i, SA + j, + buf, bufsize, 2, n, *(SA + i) == (m - 1)); + } + } + } +#endif + + /* Compute ranks of type B* substrings. */ + for(i = m - 1; 0 <= i; --i) { + if(0 <= SA[i]) { + j = i; + do { ISAb[SA[i]] = i; } while((0 <= --i) && (0 <= SA[i])); + SA[i + 1] = i - j; + if(i <= 0) { break; } + } + j = i; + do { ISAb[SA[i] = ~SA[i]] = j; } while(SA[--i] < 0); + ISAb[SA[i]] = j; + } + + /* Construct the inverse suffix array of type B* suffixes using trsort. */ + trsort(ISAb, SA, m, 1); + + /* Set the sorted order of tyoe B* suffixes. */ + for(i = n - 1, j = m, c0 = T[n - 1]; 0 <= i;) { + for(--i, c1 = c0; (0 <= i) && ((c0 = T[i]) >= c1); --i, c1 = c0) { } + if(0 <= i) { + t = i; + for(--i, c1 = c0; (0 <= i) && ((c0 = T[i]) <= c1); --i, c1 = c0) { } + SA[ISAb[--j]] = ((t == 0) || (1 < (t - i))) ? t : ~t; + } + } + + /* Calculate the index of start/end point of each bucket. */ + BUCKET_B(ALPHABET_SIZE - 1, ALPHABET_SIZE - 1) = n; /* end point */ + for(c0 = ALPHABET_SIZE - 2, k = m - 1; 0 <= c0; --c0) { + i = BUCKET_A(c0 + 1) - 1; + for(c1 = ALPHABET_SIZE - 1; c0 < c1; --c1) { + t = i - BUCKET_B(c0, c1); + BUCKET_B(c0, c1) = i; /* end point */ + + /* Move all type B* suffixes to the correct position. */ + for(i = t, j = BUCKET_BSTAR(c0, c1); + j <= k; + --i, --k) { SA[i] = SA[k]; } + } + BUCKET_BSTAR(c0, c0 + 1) = i - BUCKET_B(c0, c0) + 1; /* start point */ + BUCKET_B(c0, c0) = i; /* end point */ + } + } + + return m; +} + +/* Constructs the suffix array by using the sorted order of type B* suffixes. */ +static +void +construct_SA(const sauchar_t *T, saidx_t *SA, + saidx_t *bucket_A, saidx_t *bucket_B, + saidx_t n, saidx_t m) { + saidx_t *i, *j, *k; + saidx_t s; + saint_t c0, c1, c2; + + if(0 < m) { + /* Construct the sorted order of type B suffixes by using + the sorted order of type B* suffixes. */ + for(c1 = ALPHABET_SIZE - 2; 0 <= c1; --c1) { + /* Scan the suffix array from right to left. */ + for(i = SA + BUCKET_BSTAR(c1, c1 + 1), + j = SA + BUCKET_A(c1 + 1) - 1, k = NULL, c2 = -1; + i <= j; + --j) { + if(0 < (s = *j)) { + assert(T[s] == c1); + assert(((s + 1) < n) && (T[s] <= T[s + 1])); + assert(T[s - 1] <= T[s]); + *j = ~s; + c0 = T[--s]; + if((0 < s) && (T[s - 1] > c0)) { s = ~s; } + if(c0 != c2) { + if(0 <= c2) { BUCKET_B(c2, c1) = k - SA; } + k = SA + BUCKET_B(c2 = c0, c1); + } + assert(k < j); + *k-- = s; + } else { + assert(((s == 0) && (T[s] == c1)) || (s < 0)); + *j = ~s; + } + } + } + } + + /* Construct the suffix array by using + the sorted order of type B suffixes. */ + k = SA + BUCKET_A(c2 = T[n - 1]); + *k++ = (T[n - 2] < c2) ? ~(n - 1) : (n - 1); + /* Scan the suffix array from left to right. */ + for(i = SA, j = SA + n; i < j; ++i) { + if(0 < (s = *i)) { + assert(T[s - 1] >= T[s]); + c0 = T[--s]; + if((s == 0) || (T[s - 1] < c0)) { s = ~s; } + if(c0 != c2) { + BUCKET_A(c2) = k - SA; + k = SA + BUCKET_A(c2 = c0); + } + assert(i < k); + *k++ = s; + } else { + assert(s < 0); + *i = ~s; + } + } +} + +#if 0 +/* Constructs the burrows-wheeler transformed string directly + by using the sorted order of type B* suffixes. */ +static +saidx_t +construct_BWT(const sauchar_t *T, saidx_t *SA, + saidx_t *bucket_A, saidx_t *bucket_B, + saidx_t n, saidx_t m) { + saidx_t *i, *j, *k, *orig; + saidx_t s; + saint_t c0, c1, c2; + + if(0 < m) { + /* Construct the sorted order of type B suffixes by using + the sorted order of type B* suffixes. */ + for(c1 = ALPHABET_SIZE - 2; 0 <= c1; --c1) { + /* Scan the suffix array from right to left. */ + for(i = SA + BUCKET_BSTAR(c1, c1 + 1), + j = SA + BUCKET_A(c1 + 1) - 1, k = NULL, c2 = -1; + i <= j; + --j) { + if(0 < (s = *j)) { + assert(T[s] == c1); + assert(((s + 1) < n) && (T[s] <= T[s + 1])); + assert(T[s - 1] <= T[s]); + c0 = T[--s]; + *j = ~((saidx_t)c0); + if((0 < s) && (T[s - 1] > c0)) { s = ~s; } + if(c0 != c2) { + if(0 <= c2) { BUCKET_B(c2, c1) = k - SA; } + k = SA + BUCKET_B(c2 = c0, c1); + } + assert(k < j); + *k-- = s; + } else if(s != 0) { + *j = ~s; +#ifndef NDEBUG + } else { + assert(T[s] == c1); +#endif + } + } + } + } + + /* Construct the BWTed string by using + the sorted order of type B suffixes. */ + k = SA + BUCKET_A(c2 = T[n - 1]); + *k++ = (T[n - 2] < c2) ? ~((saidx_t)T[n - 2]) : (n - 1); + /* Scan the suffix array from left to right. */ + for(i = SA, j = SA + n, orig = SA; i < j; ++i) { + if(0 < (s = *i)) { + assert(T[s - 1] >= T[s]); + c0 = T[--s]; + *i = c0; + if((0 < s) && (T[s - 1] < c0)) { s = ~((saidx_t)T[s - 1]); } + if(c0 != c2) { + BUCKET_A(c2) = k - SA; + k = SA + BUCKET_A(c2 = c0); + } + assert(i < k); + *k++ = s; + } else if(s != 0) { + *i = ~s; + } else { + orig = i; + } + } + + return orig - SA; +} +#endif + +/*---------------------------------------------------------------------------*/ + +/** + * Initialize suffix array context + * + * @return 0 for success, or non-zero in case of an error + */ +int divsufsort_init(divsufsort_ctx_t *ctx) { + ctx->bucket_A = (saidx_t *)malloc(BUCKET_A_SIZE * sizeof(saidx_t)); + ctx->bucket_B = NULL; + + if (ctx->bucket_A) { + ctx->bucket_B = (saidx_t *)malloc(BUCKET_B_SIZE * sizeof(saidx_t)); + + if (ctx->bucket_B) + return 0; + } + + divsufsort_destroy(ctx); + return -1; +} + +/** + * Destroy suffix array context + * + * @param ctx suffix array context to destroy + */ +void divsufsort_destroy(divsufsort_ctx_t *ctx) { + if (ctx->bucket_B) { + free(ctx->bucket_B); + ctx->bucket_B = NULL; + } + + if (ctx->bucket_A) { + free(ctx->bucket_A); + ctx->bucket_A = NULL; + } +} + +/*- Function -*/ + +saint_t +divsufsort_build_array(divsufsort_ctx_t *ctx, const sauchar_t *T, saidx_t *SA, saidx_t n) { + saidx_t m; + saint_t err = 0; + + /* Check arguments. */ + if((T == NULL) || (SA == NULL) || (n < 0)) { return -1; } + else if(n == 0) { return 0; } + else if(n == 1) { SA[0] = 0; return 0; } + else if(n == 2) { m = (T[0] < T[1]); SA[m ^ 1] = 0, SA[m] = 1; return 0; } + + /* Suffixsort. */ + if((ctx->bucket_A != NULL) && (ctx->bucket_B != NULL)) { + m = sort_typeBstar(T, SA, ctx->bucket_A, ctx->bucket_B, n); + construct_SA(T, SA, ctx->bucket_A, ctx->bucket_B, n, m); + } else { + err = -2; + } + + return err; +} + +#if 0 +saidx_t +divbwt(const sauchar_t *T, sauchar_t *U, saidx_t *A, saidx_t n) { + saidx_t *B; + saidx_t *bucket_A, *bucket_B; + saidx_t m, pidx, i; + + /* Check arguments. */ + if((T == NULL) || (U == NULL) || (n < 0)) { return -1; } + else if(n <= 1) { if(n == 1) { U[0] = T[0]; } return n; } + + if((B = A) == NULL) { B = (saidx_t *)malloc((size_t)(n + 1) * sizeof(saidx_t)); } + bucket_A = (saidx_t *)malloc(BUCKET_A_SIZE * sizeof(saidx_t)); + bucket_B = (saidx_t *)malloc(BUCKET_B_SIZE * sizeof(saidx_t)); + + /* Burrows-Wheeler Transform. */ + if((B != NULL) && (bucket_A != NULL) && (bucket_B != NULL)) { + m = sort_typeBstar(T, B, bucket_A, bucket_B, n); + pidx = construct_BWT(T, B, bucket_A, bucket_B, n, m); + + /* Copy to output string. */ + U[0] = T[n - 1]; + for(i = 0; i < pidx; ++i) { U[i + 1] = (sauchar_t)B[i]; } + for(i += 1; i < n; ++i) { U[i] = (sauchar_t)B[i]; } + pidx += 1; + } else { + pidx = -2; + } + + free(bucket_B); + free(bucket_A); + if(A == NULL) { free(B); } + + return pidx; +} + +const char * +divsufsort_version(void) { + return PROJECT_VERSION_FULL; +} +#endif diff --git a/tools/apultra_src/src/libdivsufsort/lib/divsufsort_utils.c b/tools/apultra_src/src/libdivsufsort/lib/divsufsort_utils.c new file mode 100644 index 0000000..f7cbc0d --- /dev/null +++ b/tools/apultra_src/src/libdivsufsort/lib/divsufsort_utils.c @@ -0,0 +1,383 @@ +/* + * utils.c for libdivsufsort + * Copyright (c) 2003-2008 Yuta Mori All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "divsufsort_private.h" + + +/*- Private Function -*/ + +#if 0 +/* Binary search for inverse bwt. */ +static +saidx_t +binarysearch_lower(const saidx_t *A, saidx_t size, saidx_t value) { + saidx_t half, i; + for(i = 0, half = size >> 1; + 0 < size; + size = half, half >>= 1) { + if(A[i + half] < value) { + i += half + 1; + half -= (size & 1) ^ 1; + } + } + return i; +} + + +/*- Functions -*/ + +/* Burrows-Wheeler transform. */ +saint_t +bw_transform(const sauchar_t *T, sauchar_t *U, saidx_t *SA, + saidx_t n, saidx_t *idx) { + saidx_t *A, i, j, p, t; + saint_t c; + + /* Check arguments. */ + if((T == NULL) || (U == NULL) || (n < 0) || (idx == NULL)) { return -1; } + if(n <= 1) { + if(n == 1) { U[0] = T[0]; } + *idx = n; + return 0; + } + + if((A = SA) == NULL) { + i = divbwt(T, U, NULL, n); + if(0 <= i) { *idx = i; i = 0; } + return (saint_t)i; + } + + /* BW transform. */ + if(T == U) { + t = n; + for(i = 0, j = 0; i < n; ++i) { + p = t - 1; + t = A[i]; + if(0 <= p) { + c = T[j]; + U[j] = (j <= p) ? T[p] : (sauchar_t)A[p]; + A[j] = c; + j++; + } else { + *idx = i; + } + } + p = t - 1; + if(0 <= p) { + c = T[j]; + U[j] = (j <= p) ? T[p] : (sauchar_t)A[p]; + A[j] = c; + } else { + *idx = i; + } + } else { + U[0] = T[n - 1]; + for(i = 0; A[i] != 0; ++i) { U[i + 1] = T[A[i] - 1]; } + *idx = i + 1; + for(++i; i < n; ++i) { U[i] = T[A[i] - 1]; } + } + + if(SA == NULL) { + /* Deallocate memory. */ + free(A); + } + + return 0; +} + +/* Inverse Burrows-Wheeler transform. */ +saint_t +inverse_bw_transform(const sauchar_t *T, sauchar_t *U, saidx_t *A, + saidx_t n, saidx_t idx) { + saidx_t C[ALPHABET_SIZE]; + sauchar_t D[ALPHABET_SIZE]; + saidx_t *B; + saidx_t i, p; + saint_t c, d; + + /* Check arguments. */ + if((T == NULL) || (U == NULL) || (n < 0) || (idx < 0) || + (n < idx) || ((0 < n) && (idx == 0))) { + return -1; + } + if(n <= 1) { return 0; } + + if((B = A) == NULL) { + /* Allocate n*sizeof(saidx_t) bytes of memory. */ + if((B = (saidx_t *)malloc((size_t)n * sizeof(saidx_t))) == NULL) { return -2; } + } + + /* Inverse BW transform. */ + for(c = 0; c < ALPHABET_SIZE; ++c) { C[c] = 0; } + for(i = 0; i < n; ++i) { ++C[T[i]]; } + for(c = 0, d = 0, i = 0; c < ALPHABET_SIZE; ++c) { + p = C[c]; + if(0 < p) { + C[c] = i; + D[d++] = (sauchar_t)c; + i += p; + } + } + for(i = 0; i < idx; ++i) { B[C[T[i]]++] = i; } + for( ; i < n; ++i) { B[C[T[i]]++] = i + 1; } + for(c = 0; c < d; ++c) { C[c] = C[D[c]]; } + for(i = 0, p = idx; i < n; ++i) { + U[i] = D[binarysearch_lower(C, d, p)]; + p = B[p - 1]; + } + + if(A == NULL) { + /* Deallocate memory. */ + free(B); + } + + return 0; +} + +/* Checks the suffix array SA of the string T. */ +saint_t +sufcheck(const sauchar_t *T, const saidx_t *SA, + saidx_t n, saint_t verbose) { + saidx_t C[ALPHABET_SIZE]; + saidx_t i, p, q, t; + saint_t c; + + if(verbose) { fprintf(stderr, "sufcheck: "); } + + /* Check arguments. */ + if((T == NULL) || (SA == NULL) || (n < 0)) { + if(verbose) { fprintf(stderr, "Invalid arguments.\n"); } + return -1; + } + if(n == 0) { + if(verbose) { fprintf(stderr, "Done.\n"); } + return 0; + } + + /* check range: [0..n-1] */ + for(i = 0; i < n; ++i) { + if((SA[i] < 0) || (n <= SA[i])) { + if(verbose) { + fprintf(stderr, "Out of the range [0,%" PRIdSAIDX_T "].\n" + " SA[%" PRIdSAIDX_T "]=%" PRIdSAIDX_T "\n", + n - 1, i, SA[i]); + } + return -2; + } + } + + /* check first characters. */ + for(i = 1; i < n; ++i) { + if(T[SA[i - 1]] > T[SA[i]]) { + if(verbose) { + fprintf(stderr, "Suffixes in wrong order.\n" + " T[SA[%" PRIdSAIDX_T "]=%" PRIdSAIDX_T "]=%d" + " > T[SA[%" PRIdSAIDX_T "]=%" PRIdSAIDX_T "]=%d\n", + i - 1, SA[i - 1], T[SA[i - 1]], i, SA[i], T[SA[i]]); + } + return -3; + } + } + + /* check suffixes. */ + for(i = 0; i < ALPHABET_SIZE; ++i) { C[i] = 0; } + for(i = 0; i < n; ++i) { ++C[T[i]]; } + for(i = 0, p = 0; i < ALPHABET_SIZE; ++i) { + t = C[i]; + C[i] = p; + p += t; + } + + q = C[T[n - 1]]; + C[T[n - 1]] += 1; + for(i = 0; i < n; ++i) { + p = SA[i]; + if(0 < p) { + c = T[--p]; + t = C[c]; + } else { + c = T[p = n - 1]; + t = q; + } + if((t < 0) || (p != SA[t])) { + if(verbose) { + fprintf(stderr, "Suffix in wrong position.\n" + " SA[%" PRIdSAIDX_T "]=%" PRIdSAIDX_T " or\n" + " SA[%" PRIdSAIDX_T "]=%" PRIdSAIDX_T "\n", + t, (0 <= t) ? SA[t] : -1, i, SA[i]); + } + return -4; + } + if(t != q) { + ++C[c]; + if((n <= C[c]) || (T[SA[C[c]]] != c)) { C[c] = -1; } + } + } + + if(1 <= verbose) { fprintf(stderr, "Done.\n"); } + return 0; +} + + +static +int +_compare(const sauchar_t *T, saidx_t Tsize, + const sauchar_t *P, saidx_t Psize, + saidx_t suf, saidx_t *match) { + saidx_t i, j; + saint_t r; + for(i = suf + *match, j = *match, r = 0; + (i < Tsize) && (j < Psize) && ((r = T[i] - P[j]) == 0); ++i, ++j) { } + *match = j; + return (r == 0) ? -(j != Psize) : r; +} + +/* Search for the pattern P in the string T. */ +saidx_t +sa_search(const sauchar_t *T, saidx_t Tsize, + const sauchar_t *P, saidx_t Psize, + const saidx_t *SA, saidx_t SAsize, + saidx_t *idx) { + saidx_t size, lsize, rsize, half; + saidx_t match, lmatch, rmatch; + saidx_t llmatch, lrmatch, rlmatch, rrmatch; + saidx_t i, j, k; + saint_t r; + + if(idx != NULL) { *idx = -1; } + if((T == NULL) || (P == NULL) || (SA == NULL) || + (Tsize < 0) || (Psize < 0) || (SAsize < 0)) { return -1; } + if((Tsize == 0) || (SAsize == 0)) { return 0; } + if(Psize == 0) { if(idx != NULL) { *idx = 0; } return SAsize; } + + for(i = j = k = 0, lmatch = rmatch = 0, size = SAsize, half = size >> 1; + 0 < size; + size = half, half >>= 1) { + match = MIN(lmatch, rmatch); + r = _compare(T, Tsize, P, Psize, SA[i + half], &match); + if(r < 0) { + i += half + 1; + half -= (size & 1) ^ 1; + lmatch = match; + } else if(r > 0) { + rmatch = match; + } else { + lsize = half, j = i, rsize = size - half - 1, k = i + half + 1; + + /* left part */ + for(llmatch = lmatch, lrmatch = match, half = lsize >> 1; + 0 < lsize; + lsize = half, half >>= 1) { + lmatch = MIN(llmatch, lrmatch); + r = _compare(T, Tsize, P, Psize, SA[j + half], &lmatch); + if(r < 0) { + j += half + 1; + half -= (lsize & 1) ^ 1; + llmatch = lmatch; + } else { + lrmatch = lmatch; + } + } + + /* right part */ + for(rlmatch = match, rrmatch = rmatch, half = rsize >> 1; + 0 < rsize; + rsize = half, half >>= 1) { + rmatch = MIN(rlmatch, rrmatch); + r = _compare(T, Tsize, P, Psize, SA[k + half], &rmatch); + if(r <= 0) { + k += half + 1; + half -= (rsize & 1) ^ 1; + rlmatch = rmatch; + } else { + rrmatch = rmatch; + } + } + + break; + } + } + + if(idx != NULL) { *idx = (0 < (k - j)) ? j : i; } + return k - j; +} + +/* Search for the character c in the string T. */ +saidx_t +sa_simplesearch(const sauchar_t *T, saidx_t Tsize, + const saidx_t *SA, saidx_t SAsize, + saint_t c, saidx_t *idx) { + saidx_t size, lsize, rsize, half; + saidx_t i, j, k, p; + saint_t r; + + if(idx != NULL) { *idx = -1; } + if((T == NULL) || (SA == NULL) || (Tsize < 0) || (SAsize < 0)) { return -1; } + if((Tsize == 0) || (SAsize == 0)) { return 0; } + + for(i = j = k = 0, size = SAsize, half = size >> 1; + 0 < size; + size = half, half >>= 1) { + p = SA[i + half]; + r = (p < Tsize) ? T[p] - c : -1; + if(r < 0) { + i += half + 1; + half -= (size & 1) ^ 1; + } else if(r == 0) { + lsize = half, j = i, rsize = size - half - 1, k = i + half + 1; + + /* left part */ + for(half = lsize >> 1; + 0 < lsize; + lsize = half, half >>= 1) { + p = SA[j + half]; + r = (p < Tsize) ? T[p] - c : -1; + if(r < 0) { + j += half + 1; + half -= (lsize & 1) ^ 1; + } + } + + /* right part */ + for(half = rsize >> 1; + 0 < rsize; + rsize = half, half >>= 1) { + p = SA[k + half]; + r = (p < Tsize) ? T[p] - c : -1; + if(r <= 0) { + k += half + 1; + half -= (rsize & 1) ^ 1; + } + } + + break; + } + } + + if(idx != NULL) { *idx = (0 < (k - j)) ? j : i; } + return k - j; +} +#endif diff --git a/tools/apultra_src/src/libdivsufsort/lib/sssort.c b/tools/apultra_src/src/libdivsufsort/lib/sssort.c new file mode 100644 index 0000000..4a18fd2 --- /dev/null +++ b/tools/apultra_src/src/libdivsufsort/lib/sssort.c @@ -0,0 +1,815 @@ +/* + * sssort.c for libdivsufsort + * Copyright (c) 2003-2008 Yuta Mori All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "divsufsort_private.h" + + +/*- Private Functions -*/ + +static const saint_t lg_table[256]= { + -1,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7 +}; + +#if (SS_BLOCKSIZE == 0) || (SS_INSERTIONSORT_THRESHOLD < SS_BLOCKSIZE) + +static INLINE +saint_t +ss_ilg(saidx_t n) { +#if SS_BLOCKSIZE == 0 +# if defined(BUILD_DIVSUFSORT64) + return (n >> 32) ? + ((n >> 48) ? + ((n >> 56) ? + 56 + lg_table[(n >> 56) & 0xff] : + 48 + lg_table[(n >> 48) & 0xff]) : + ((n >> 40) ? + 40 + lg_table[(n >> 40) & 0xff] : + 32 + lg_table[(n >> 32) & 0xff])) : + ((n & 0xffff0000) ? + ((n & 0xff000000) ? + 24 + lg_table[(n >> 24) & 0xff] : + 16 + lg_table[(n >> 16) & 0xff]) : + ((n & 0x0000ff00) ? + 8 + lg_table[(n >> 8) & 0xff] : + 0 + lg_table[(n >> 0) & 0xff])); +# else + return (n & 0xffff0000) ? + ((n & 0xff000000) ? + 24 + lg_table[(n >> 24) & 0xff] : + 16 + lg_table[(n >> 16) & 0xff]) : + ((n & 0x0000ff00) ? + 8 + lg_table[(n >> 8) & 0xff] : + 0 + lg_table[(n >> 0) & 0xff]); +# endif +#elif SS_BLOCKSIZE < 256 + return lg_table[n]; +#else + return (n & 0xff00) ? + 8 + lg_table[(n >> 8) & 0xff] : + 0 + lg_table[(n >> 0) & 0xff]; +#endif +} + +#endif /* (SS_BLOCKSIZE == 0) || (SS_INSERTIONSORT_THRESHOLD < SS_BLOCKSIZE) */ + +#if SS_BLOCKSIZE != 0 + +static const saint_t sqq_table[256] = { + 0, 16, 22, 27, 32, 35, 39, 42, 45, 48, 50, 53, 55, 57, 59, 61, + 64, 65, 67, 69, 71, 73, 75, 76, 78, 80, 81, 83, 84, 86, 87, 89, + 90, 91, 93, 94, 96, 97, 98, 99, 101, 102, 103, 104, 106, 107, 108, 109, +110, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, +128, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, +143, 144, 144, 145, 146, 147, 148, 149, 150, 150, 151, 152, 153, 154, 155, 155, +156, 157, 158, 159, 160, 160, 161, 162, 163, 163, 164, 165, 166, 167, 167, 168, +169, 170, 170, 171, 172, 173, 173, 174, 175, 176, 176, 177, 178, 178, 179, 180, +181, 181, 182, 183, 183, 184, 185, 185, 186, 187, 187, 188, 189, 189, 190, 191, +192, 192, 193, 193, 194, 195, 195, 196, 197, 197, 198, 199, 199, 200, 201, 201, +202, 203, 203, 204, 204, 205, 206, 206, 207, 208, 208, 209, 209, 210, 211, 211, +212, 212, 213, 214, 214, 215, 215, 216, 217, 217, 218, 218, 219, 219, 220, 221, +221, 222, 222, 223, 224, 224, 225, 225, 226, 226, 227, 227, 228, 229, 229, 230, +230, 231, 231, 232, 232, 233, 234, 234, 235, 235, 236, 236, 237, 237, 238, 238, +239, 240, 240, 241, 241, 242, 242, 243, 243, 244, 244, 245, 245, 246, 246, 247, +247, 248, 248, 249, 249, 250, 250, 251, 251, 252, 252, 253, 253, 254, 254, 255 +}; + +static INLINE +saidx_t +ss_isqrt(saidx_t x) { + saidx_t y, e; + + if(x >= (SS_BLOCKSIZE * SS_BLOCKSIZE)) { return SS_BLOCKSIZE; } + e = (x & 0xffff0000) ? + ((x & 0xff000000) ? + 24 + lg_table[(x >> 24) & 0xff] : + 16 + lg_table[(x >> 16) & 0xff]) : + ((x & 0x0000ff00) ? + 8 + lg_table[(x >> 8) & 0xff] : + 0 + lg_table[(x >> 0) & 0xff]); + + if(e >= 16) { + y = sqq_table[x >> ((e - 6) - (e & 1))] << ((e >> 1) - 7); + if(e >= 24) { y = (y + 1 + x / y) >> 1; } + y = (y + 1 + x / y) >> 1; + } else if(e >= 8) { + y = (sqq_table[x >> ((e - 6) - (e & 1))] >> (7 - (e >> 1))) + 1; + } else { + return sqq_table[x] >> 4; + } + + return (x < (y * y)) ? y - 1 : y; +} + +#endif /* SS_BLOCKSIZE != 0 */ + + +/*---------------------------------------------------------------------------*/ + +/* Compares two suffixes. */ +static INLINE +saint_t +ss_compare(const sauchar_t *T, + const saidx_t *p1, const saidx_t *p2, + saidx_t depth) { + const sauchar_t *U1, *U2, *U1n, *U2n; + + for(U1 = T + depth + *p1, + U2 = T + depth + *p2, + U1n = T + *(p1 + 1) + 2, + U2n = T + *(p2 + 1) + 2; + (U1 < U1n) && (U2 < U2n) && (*U1 == *U2); + ++U1, ++U2) { + } + + return U1 < U1n ? + (U2 < U2n ? *U1 - *U2 : 1) : + (U2 < U2n ? -1 : 0); +} + + +/*---------------------------------------------------------------------------*/ + +#if (SS_BLOCKSIZE != 1) && (SS_INSERTIONSORT_THRESHOLD != 1) + +/* Insertionsort for small size groups */ +static +void +ss_insertionsort(const sauchar_t *T, const saidx_t *PA, + saidx_t *first, saidx_t *last, saidx_t depth) { + saidx_t *i, *j; + saidx_t t; + saint_t r; + + for(i = last - 2; first <= i; --i) { + for(t = *i, j = i + 1; 0 < (r = ss_compare(T, PA + t, PA + *j, depth));) { + do { *(j - 1) = *j; } while((++j < last) && (*j < 0)); + if(last <= j) { break; } + } + if(r == 0) { *j = ~*j; } + *(j - 1) = t; + } +} + +#endif /* (SS_BLOCKSIZE != 1) && (SS_INSERTIONSORT_THRESHOLD != 1) */ + + +/*---------------------------------------------------------------------------*/ + +#if (SS_BLOCKSIZE == 0) || (SS_INSERTIONSORT_THRESHOLD < SS_BLOCKSIZE) + +static INLINE +void +ss_fixdown(const sauchar_t *Td, const saidx_t *PA, + saidx_t *SA, saidx_t i, saidx_t size) { + saidx_t j, k; + saidx_t v; + saint_t c, d, e; + + for(v = SA[i], c = Td[PA[v]]; (j = 2 * i + 1) < size; SA[i] = SA[k], i = k) { + d = Td[PA[SA[k = j++]]]; + if(d < (e = Td[PA[SA[j]]])) { k = j; d = e; } + if(d <= c) { break; } + } + SA[i] = v; +} + +/* Simple top-down heapsort. */ +static +void +ss_heapsort(const sauchar_t *Td, const saidx_t *PA, saidx_t *SA, saidx_t size) { + saidx_t i, m; + saidx_t t; + + m = size; + if((size % 2) == 0) { + m--; + if(Td[PA[SA[m / 2]]] < Td[PA[SA[m]]]) { SWAP(SA[m], SA[m / 2]); } + } + + for(i = m / 2 - 1; 0 <= i; --i) { ss_fixdown(Td, PA, SA, i, m); } + if((size % 2) == 0) { SWAP(SA[0], SA[m]); ss_fixdown(Td, PA, SA, 0, m); } + for(i = m - 1; 0 < i; --i) { + t = SA[0], SA[0] = SA[i]; + ss_fixdown(Td, PA, SA, 0, i); + SA[i] = t; + } +} + + +/*---------------------------------------------------------------------------*/ + +/* Returns the median of three elements. */ +static INLINE +saidx_t * +ss_median3(const sauchar_t *Td, const saidx_t *PA, + saidx_t *v1, saidx_t *v2, saidx_t *v3) { + saidx_t *t; + if(Td[PA[*v1]] > Td[PA[*v2]]) { SWAP(v1, v2); } + if(Td[PA[*v2]] > Td[PA[*v3]]) { + if(Td[PA[*v1]] > Td[PA[*v3]]) { return v1; } + else { return v3; } + } + return v2; +} + +/* Returns the median of five elements. */ +static INLINE +saidx_t * +ss_median5(const sauchar_t *Td, const saidx_t *PA, + saidx_t *v1, saidx_t *v2, saidx_t *v3, saidx_t *v4, saidx_t *v5) { + saidx_t *t; + if(Td[PA[*v2]] > Td[PA[*v3]]) { SWAP(v2, v3); } + if(Td[PA[*v4]] > Td[PA[*v5]]) { SWAP(v4, v5); } + if(Td[PA[*v2]] > Td[PA[*v4]]) { SWAP(v2, v4); SWAP(v3, v5); } + if(Td[PA[*v1]] > Td[PA[*v3]]) { SWAP(v1, v3); } + if(Td[PA[*v1]] > Td[PA[*v4]]) { SWAP(v1, v4); SWAP(v3, v5); } + if(Td[PA[*v3]] > Td[PA[*v4]]) { return v4; } + return v3; +} + +/* Returns the pivot element. */ +static INLINE +saidx_t * +ss_pivot(const sauchar_t *Td, const saidx_t *PA, saidx_t *first, saidx_t *last) { + saidx_t *middle; + saidx_t t; + + t = last - first; + middle = first + t / 2; + + if(t <= 512) { + if(t <= 32) { + return ss_median3(Td, PA, first, middle, last - 1); + } else { + t >>= 2; + return ss_median5(Td, PA, first, first + t, middle, last - 1 - t, last - 1); + } + } + t >>= 3; + first = ss_median3(Td, PA, first, first + t, first + (t << 1)); + middle = ss_median3(Td, PA, middle - t, middle, middle + t); + last = ss_median3(Td, PA, last - 1 - (t << 1), last - 1 - t, last - 1); + return ss_median3(Td, PA, first, middle, last); +} + + +/*---------------------------------------------------------------------------*/ + +/* Binary partition for substrings. */ +static INLINE +saidx_t * +ss_partition(const saidx_t *PA, + saidx_t *first, saidx_t *last, saidx_t depth) { + saidx_t *a, *b; + saidx_t t; + for(a = first - 1, b = last;;) { + for(; (++a < b) && ((PA[*a] + depth) >= (PA[*a + 1] + 1));) { *a = ~*a; } + for(; (a < --b) && ((PA[*b] + depth) < (PA[*b + 1] + 1));) { } + if(b <= a) { break; } + t = ~*b; + *b = *a; + *a = t; + } + if(first < a) { *first = ~*first; } + return a; +} + +/* Multikey introsort for medium size groups. */ +static +void +ss_mintrosort(const sauchar_t *T, const saidx_t *PA, + saidx_t *first, saidx_t *last, + saidx_t depth) { +#define STACK_SIZE SS_MISORT_STACKSIZE + struct { saidx_t *a, *b, c; saint_t d; } stack[STACK_SIZE]; + const sauchar_t *Td; + saidx_t *a, *b, *c, *d, *e, *f; + saidx_t s, t; + saint_t ssize; + saint_t limit; + saint_t v, x = 0; + + for(ssize = 0, limit = ss_ilg(last - first);;) { + + if((last - first) <= SS_INSERTIONSORT_THRESHOLD) { +#if 1 < SS_INSERTIONSORT_THRESHOLD + if(1 < (last - first)) { ss_insertionsort(T, PA, first, last, depth); } +#endif + STACK_POP(first, last, depth, limit); + continue; + } + + Td = T + depth; + if(limit-- == 0) { ss_heapsort(Td, PA, first, last - first); } + if(limit < 0) { + for(a = first + 1, v = Td[PA[*first]]; a < last; ++a) { + if((x = Td[PA[*a]]) != v) { + if(1 < (a - first)) { break; } + v = x; + first = a; + } + } + if(Td[PA[*first] - 1] < v) { + first = ss_partition(PA, first, a, depth); + } + if((a - first) <= (last - a)) { + if(1 < (a - first)) { + STACK_PUSH(a, last, depth, -1); + last = a, depth += 1, limit = ss_ilg(a - first); + } else { + first = a, limit = -1; + } + } else { + if(1 < (last - a)) { + STACK_PUSH(first, a, depth + 1, ss_ilg(a - first)); + first = a, limit = -1; + } else { + last = a, depth += 1, limit = ss_ilg(a - first); + } + } + continue; + } + + /* choose pivot */ + a = ss_pivot(Td, PA, first, last); + v = Td[PA[*a]]; + SWAP(*first, *a); + + /* partition */ + for(b = first; (++b < last) && ((x = Td[PA[*b]]) == v);) { } + if(((a = b) < last) && (x < v)) { + for(; (++b < last) && ((x = Td[PA[*b]]) <= v);) { + if(x == v) { SWAP(*b, *a); ++a; } + } + } + for(c = last; (b < --c) && ((x = Td[PA[*c]]) == v);) { } + if((b < (d = c)) && (x > v)) { + for(; (b < --c) && ((x = Td[PA[*c]]) >= v);) { + if(x == v) { SWAP(*c, *d); --d; } + } + } + for(; b < c;) { + SWAP(*b, *c); + for(; (++b < c) && ((x = Td[PA[*b]]) <= v);) { + if(x == v) { SWAP(*b, *a); ++a; } + } + for(; (b < --c) && ((x = Td[PA[*c]]) >= v);) { + if(x == v) { SWAP(*c, *d); --d; } + } + } + + if(a <= d) { + c = b - 1; + + if((s = a - first) > (t = b - a)) { s = t; } + for(e = first, f = b - s; 0 < s; --s, ++e, ++f) { SWAP(*e, *f); } + if((s = d - c) > (t = last - d - 1)) { s = t; } + for(e = b, f = last - s; 0 < s; --s, ++e, ++f) { SWAP(*e, *f); } + + a = first + (b - a), c = last - (d - c); + b = (v <= Td[PA[*a] - 1]) ? a : ss_partition(PA, a, c, depth); + + if((a - first) <= (last - c)) { + if((last - c) <= (c - b)) { + STACK_PUSH(b, c, depth + 1, ss_ilg(c - b)); + STACK_PUSH(c, last, depth, limit); + last = a; + } else if((a - first) <= (c - b)) { + STACK_PUSH(c, last, depth, limit); + STACK_PUSH(b, c, depth + 1, ss_ilg(c - b)); + last = a; + } else { + STACK_PUSH(c, last, depth, limit); + STACK_PUSH(first, a, depth, limit); + first = b, last = c, depth += 1, limit = ss_ilg(c - b); + } + } else { + if((a - first) <= (c - b)) { + STACK_PUSH(b, c, depth + 1, ss_ilg(c - b)); + STACK_PUSH(first, a, depth, limit); + first = c; + } else if((last - c) <= (c - b)) { + STACK_PUSH(first, a, depth, limit); + STACK_PUSH(b, c, depth + 1, ss_ilg(c - b)); + first = c; + } else { + STACK_PUSH(first, a, depth, limit); + STACK_PUSH(c, last, depth, limit); + first = b, last = c, depth += 1, limit = ss_ilg(c - b); + } + } + } else { + limit += 1; + if(Td[PA[*first] - 1] < v) { + first = ss_partition(PA, first, last, depth); + limit = ss_ilg(last - first); + } + depth += 1; + } + } +#undef STACK_SIZE +} + +#endif /* (SS_BLOCKSIZE == 0) || (SS_INSERTIONSORT_THRESHOLD < SS_BLOCKSIZE) */ + + +/*---------------------------------------------------------------------------*/ + +#if SS_BLOCKSIZE != 0 + +static INLINE +void +ss_blockswap(saidx_t *a, saidx_t *b, saidx_t n) { + saidx_t t; + for(; 0 < n; --n, ++a, ++b) { + t = *a, *a = *b, *b = t; + } +} + +static INLINE +void +ss_rotate(saidx_t *first, saidx_t *middle, saidx_t *last) { + saidx_t *a, *b, t; + saidx_t l, r; + l = middle - first, r = last - middle; + for(; (0 < l) && (0 < r);) { + if(l == r) { ss_blockswap(first, middle, l); break; } + if(l < r) { + a = last - 1, b = middle - 1; + t = *a; + do { + *a-- = *b, *b-- = *a; + if(b < first) { + *a = t; + last = a; + if((r -= l + 1) <= l) { break; } + a -= 1, b = middle - 1; + t = *a; + } + } while(1); + } else { + a = first, b = middle; + t = *a; + do { + *a++ = *b, *b++ = *a; + if(last <= b) { + *a = t; + first = a + 1; + if((l -= r + 1) <= r) { break; } + a += 1, b = middle; + t = *a; + } + } while(1); + } + } +} + + +/*---------------------------------------------------------------------------*/ + +static +void +ss_inplacemerge(const sauchar_t *T, const saidx_t *PA, + saidx_t *first, saidx_t *middle, saidx_t *last, + saidx_t depth) { + const saidx_t *p; + saidx_t *a, *b; + saidx_t len, half; + saint_t q, r; + saint_t x; + + for(;;) { + if(*(last - 1) < 0) { x = 1; p = PA + ~*(last - 1); } + else { x = 0; p = PA + *(last - 1); } + for(a = first, len = middle - first, half = len >> 1, r = -1; + 0 < len; + len = half, half >>= 1) { + b = a + half; + q = ss_compare(T, PA + ((0 <= *b) ? *b : ~*b), p, depth); + if(q < 0) { + a = b + 1; + half -= (len & 1) ^ 1; + } else { + r = q; + } + } + if(a < middle) { + if(r == 0) { *a = ~*a; } + ss_rotate(a, middle, last); + last -= middle - a; + middle = a; + if(first == middle) { break; } + } + --last; + if(x != 0) { while(*--last < 0) { } } + if(middle == last) { break; } + } +} + + +/*---------------------------------------------------------------------------*/ + +/* Merge-forward with internal buffer. */ +static +void +ss_mergeforward(const sauchar_t *T, const saidx_t *PA, + saidx_t *first, saidx_t *middle, saidx_t *last, + saidx_t *buf, saidx_t depth) { + saidx_t *a, *b, *c, *bufend; + saidx_t t; + saint_t r; + + bufend = buf + (middle - first) - 1; + ss_blockswap(buf, first, middle - first); + + for(t = *(a = first), b = buf, c = middle;;) { + r = ss_compare(T, PA + *b, PA + *c, depth); + if(r < 0) { + do { + *a++ = *b; + if(bufend <= b) { *bufend = t; return; } + *b++ = *a; + } while(*b < 0); + } else if(r > 0) { + do { + *a++ = *c, *c++ = *a; + if(last <= c) { + while(b < bufend) { *a++ = *b, *b++ = *a; } + *a = *b, *b = t; + return; + } + } while(*c < 0); + } else { + *c = ~*c; + do { + *a++ = *b; + if(bufend <= b) { *bufend = t; return; } + *b++ = *a; + } while(*b < 0); + + do { + *a++ = *c, *c++ = *a; + if(last <= c) { + while(b < bufend) { *a++ = *b, *b++ = *a; } + *a = *b, *b = t; + return; + } + } while(*c < 0); + } + } +} + +/* Merge-backward with internal buffer. */ +static +void +ss_mergebackward(const sauchar_t *T, const saidx_t *PA, + saidx_t *first, saidx_t *middle, saidx_t *last, + saidx_t *buf, saidx_t depth) { + const saidx_t *p1, *p2; + saidx_t *a, *b, *c, *bufend; + saidx_t t; + saint_t r; + saint_t x; + + bufend = buf + (last - middle) - 1; + ss_blockswap(buf, middle, last - middle); + + x = 0; + if(*bufend < 0) { p1 = PA + ~*bufend; x |= 1; } + else { p1 = PA + *bufend; } + if(*(middle - 1) < 0) { p2 = PA + ~*(middle - 1); x |= 2; } + else { p2 = PA + *(middle - 1); } + for(t = *(a = last - 1), b = bufend, c = middle - 1;;) { + r = ss_compare(T, p1, p2, depth); + if(0 < r) { + if(x & 1) { do { *a-- = *b, *b-- = *a; } while(*b < 0); x ^= 1; } + *a-- = *b; + if(b <= buf) { *buf = t; break; } + *b-- = *a; + if(*b < 0) { p1 = PA + ~*b; x |= 1; } + else { p1 = PA + *b; } + } else if(r < 0) { + if(x & 2) { do { *a-- = *c, *c-- = *a; } while(*c < 0); x ^= 2; } + *a-- = *c, *c-- = *a; + if(c < first) { + while(buf < b) { *a-- = *b, *b-- = *a; } + *a = *b, *b = t; + break; + } + if(*c < 0) { p2 = PA + ~*c; x |= 2; } + else { p2 = PA + *c; } + } else { + if(x & 1) { do { *a-- = *b, *b-- = *a; } while(*b < 0); x ^= 1; } + *a-- = ~*b; + if(b <= buf) { *buf = t; break; } + *b-- = *a; + if(x & 2) { do { *a-- = *c, *c-- = *a; } while(*c < 0); x ^= 2; } + *a-- = *c, *c-- = *a; + if(c < first) { + while(buf < b) { *a-- = *b, *b-- = *a; } + *a = *b, *b = t; + break; + } + if(*b < 0) { p1 = PA + ~*b; x |= 1; } + else { p1 = PA + *b; } + if(*c < 0) { p2 = PA + ~*c; x |= 2; } + else { p2 = PA + *c; } + } + } +} + +/* D&C based merge. */ +static +void +ss_swapmerge(const sauchar_t *T, const saidx_t *PA, + saidx_t *first, saidx_t *middle, saidx_t *last, + saidx_t *buf, saidx_t bufsize, saidx_t depth) { +#define STACK_SIZE SS_SMERGE_STACKSIZE +#define GETIDX(a) ((0 <= (a)) ? (a) : (~(a))) +#define MERGE_CHECK(a, b, c)\ + do {\ + if(((c) & 1) ||\ + (((c) & 2) && (ss_compare(T, PA + GETIDX(*((a) - 1)), PA + *(a), depth) == 0))) {\ + *(a) = ~*(a);\ + }\ + if(((c) & 4) && ((ss_compare(T, PA + GETIDX(*((b) - 1)), PA + *(b), depth) == 0))) {\ + *(b) = ~*(b);\ + }\ + } while(0) + struct { saidx_t *a, *b, *c; saint_t d; } stack[STACK_SIZE]; + saidx_t *l, *r, *lm, *rm; + saidx_t m, len, half; + saint_t ssize; + saint_t check, next; + + for(check = 0, ssize = 0;;) { + if((last - middle) <= bufsize) { + if((first < middle) && (middle < last)) { + ss_mergebackward(T, PA, first, middle, last, buf, depth); + } + MERGE_CHECK(first, last, check); + STACK_POP(first, middle, last, check); + continue; + } + + if((middle - first) <= bufsize) { + if(first < middle) { + ss_mergeforward(T, PA, first, middle, last, buf, depth); + } + MERGE_CHECK(first, last, check); + STACK_POP(first, middle, last, check); + continue; + } + + for(m = 0, len = MIN(middle - first, last - middle), half = len >> 1; + 0 < len; + len = half, half >>= 1) { + if(ss_compare(T, PA + GETIDX(*(middle + m + half)), + PA + GETIDX(*(middle - m - half - 1)), depth) < 0) { + m += half + 1; + half -= (len & 1) ^ 1; + } + } + + if(0 < m) { + lm = middle - m, rm = middle + m; + ss_blockswap(lm, middle, m); + l = r = middle, next = 0; + if(rm < last) { + if(*rm < 0) { + *rm = ~*rm; + if(first < lm) { for(; *--l < 0;) { } next |= 4; } + next |= 1; + } else if(first < lm) { + for(; *r < 0; ++r) { } + next |= 2; + } + } + + if((l - first) <= (last - r)) { + STACK_PUSH(r, rm, last, (next & 3) | (check & 4)); + middle = lm, last = l, check = (check & 3) | (next & 4); + } else { + if((next & 2) && (r == middle)) { next ^= 6; } + STACK_PUSH(first, lm, l, (check & 3) | (next & 4)); + first = r, middle = rm, check = (next & 3) | (check & 4); + } + } else { + if(ss_compare(T, PA + GETIDX(*(middle - 1)), PA + *middle, depth) == 0) { + *middle = ~*middle; + } + MERGE_CHECK(first, last, check); + STACK_POP(first, middle, last, check); + } + } +#undef STACK_SIZE +} + +#endif /* SS_BLOCKSIZE != 0 */ + + +/*---------------------------------------------------------------------------*/ + +/*- Function -*/ + +/* Substring sort */ +void +sssort(const sauchar_t *T, const saidx_t *PA, + saidx_t *first, saidx_t *last, + saidx_t *buf, saidx_t bufsize, + saidx_t depth, saidx_t n, saint_t lastsuffix) { + saidx_t *a; +#if SS_BLOCKSIZE != 0 + saidx_t *b, *middle, *curbuf; + saidx_t j, k, curbufsize, limit; +#endif + saidx_t i; + + if(lastsuffix != 0) { ++first; } + +#if SS_BLOCKSIZE == 0 + ss_mintrosort(T, PA, first, last, depth); +#else + if((bufsize < SS_BLOCKSIZE) && + (bufsize < (last - first)) && + (bufsize < (limit = ss_isqrt(last - first)))) { + if(SS_BLOCKSIZE < limit) { limit = SS_BLOCKSIZE; } + buf = middle = last - limit, bufsize = limit; + } else { + middle = last, limit = 0; + } + for(a = first, i = 0; SS_BLOCKSIZE < (middle - a); a += SS_BLOCKSIZE, ++i) { +#if SS_INSERTIONSORT_THRESHOLD < SS_BLOCKSIZE + ss_mintrosort(T, PA, a, a + SS_BLOCKSIZE, depth); +#elif 1 < SS_BLOCKSIZE + ss_insertionsort(T, PA, a, a + SS_BLOCKSIZE, depth); +#endif + curbufsize = last - (a + SS_BLOCKSIZE); + curbuf = a + SS_BLOCKSIZE; + if(curbufsize <= bufsize) { curbufsize = bufsize, curbuf = buf; } + for(b = a, k = SS_BLOCKSIZE, j = i; j & 1; b -= k, k <<= 1, j >>= 1) { + ss_swapmerge(T, PA, b - k, b, b + k, curbuf, curbufsize, depth); + } + } +#if SS_INSERTIONSORT_THRESHOLD < SS_BLOCKSIZE + ss_mintrosort(T, PA, a, middle, depth); +#elif 1 < SS_BLOCKSIZE + ss_insertionsort(T, PA, a, middle, depth); +#endif + for(k = SS_BLOCKSIZE; i != 0; k <<= 1, i >>= 1) { + if(i & 1) { + ss_swapmerge(T, PA, a - k, a, middle, buf, bufsize, depth); + a -= k; + } + } + if(limit != 0) { +#if SS_INSERTIONSORT_THRESHOLD < SS_BLOCKSIZE + ss_mintrosort(T, PA, middle, last, depth); +#elif 1 < SS_BLOCKSIZE + ss_insertionsort(T, PA, middle, last, depth); +#endif + ss_inplacemerge(T, PA, first, middle, last, depth); + } +#endif + + if(lastsuffix != 0) { + /* Insert last type B* suffix. */ + saidx_t PAi[2]; PAi[0] = PA[*(first - 1)], PAi[1] = n - 2; + for(a = first, i = *(first - 1); + (a < last) && ((*a < 0) || (0 < ss_compare(T, &(PAi[0]), PA + *a, depth))); + ++a) { + *(a - 1) = *a; + } + *(a - 1) = i; + } +} diff --git a/tools/apultra_src/src/libdivsufsort/lib/trsort.c b/tools/apultra_src/src/libdivsufsort/lib/trsort.c new file mode 100644 index 0000000..6fe3e67 --- /dev/null +++ b/tools/apultra_src/src/libdivsufsort/lib/trsort.c @@ -0,0 +1,586 @@ +/* + * trsort.c for libdivsufsort + * Copyright (c) 2003-2008 Yuta Mori All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "divsufsort_private.h" + + +/*- Private Functions -*/ + +static const saint_t lg_table[256]= { + -1,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7 +}; + +static INLINE +saint_t +tr_ilg(saidx_t n) { +#if defined(BUILD_DIVSUFSORT64) + return (n >> 32) ? + ((n >> 48) ? + ((n >> 56) ? + 56 + lg_table[(n >> 56) & 0xff] : + 48 + lg_table[(n >> 48) & 0xff]) : + ((n >> 40) ? + 40 + lg_table[(n >> 40) & 0xff] : + 32 + lg_table[(n >> 32) & 0xff])) : + ((n & 0xffff0000) ? + ((n & 0xff000000) ? + 24 + lg_table[(n >> 24) & 0xff] : + 16 + lg_table[(n >> 16) & 0xff]) : + ((n & 0x0000ff00) ? + 8 + lg_table[(n >> 8) & 0xff] : + 0 + lg_table[(n >> 0) & 0xff])); +#else + return (n & 0xffff0000) ? + ((n & 0xff000000) ? + 24 + lg_table[(n >> 24) & 0xff] : + 16 + lg_table[(n >> 16) & 0xff]) : + ((n & 0x0000ff00) ? + 8 + lg_table[(n >> 8) & 0xff] : + 0 + lg_table[(n >> 0) & 0xff]); +#endif +} + + +/*---------------------------------------------------------------------------*/ + +/* Simple insertionsort for small size groups. */ +static +void +tr_insertionsort(const saidx_t *ISAd, saidx_t *first, saidx_t *last) { + saidx_t *a, *b; + saidx_t t, r; + + for(a = first + 1; a < last; ++a) { + for(t = *a, b = a - 1; 0 > (r = ISAd[t] - ISAd[*b]);) { + do { *(b + 1) = *b; } while((first <= --b) && (*b < 0)); + if(b < first) { break; } + } + if(r == 0) { *b = ~*b; } + *(b + 1) = t; + } +} + + +/*---------------------------------------------------------------------------*/ + +static INLINE +void +tr_fixdown(const saidx_t *ISAd, saidx_t *SA, saidx_t i, saidx_t size) { + saidx_t j, k; + saidx_t v; + saidx_t c, d, e; + + for(v = SA[i], c = ISAd[v]; (j = 2 * i + 1) < size; SA[i] = SA[k], i = k) { + d = ISAd[SA[k = j++]]; + if(d < (e = ISAd[SA[j]])) { k = j; d = e; } + if(d <= c) { break; } + } + SA[i] = v; +} + +/* Simple top-down heapsort. */ +static +void +tr_heapsort(const saidx_t *ISAd, saidx_t *SA, saidx_t size) { + saidx_t i, m; + saidx_t t; + + m = size; + if((size % 2) == 0) { + m--; + if(ISAd[SA[m / 2]] < ISAd[SA[m]]) { SWAP(SA[m], SA[m / 2]); } + } + + for(i = m / 2 - 1; 0 <= i; --i) { tr_fixdown(ISAd, SA, i, m); } + if((size % 2) == 0) { SWAP(SA[0], SA[m]); tr_fixdown(ISAd, SA, 0, m); } + for(i = m - 1; 0 < i; --i) { + t = SA[0], SA[0] = SA[i]; + tr_fixdown(ISAd, SA, 0, i); + SA[i] = t; + } +} + + +/*---------------------------------------------------------------------------*/ + +/* Returns the median of three elements. */ +static INLINE +saidx_t * +tr_median3(const saidx_t *ISAd, saidx_t *v1, saidx_t *v2, saidx_t *v3) { + saidx_t *t; + if(ISAd[*v1] > ISAd[*v2]) { SWAP(v1, v2); } + if(ISAd[*v2] > ISAd[*v3]) { + if(ISAd[*v1] > ISAd[*v3]) { return v1; } + else { return v3; } + } + return v2; +} + +/* Returns the median of five elements. */ +static INLINE +saidx_t * +tr_median5(const saidx_t *ISAd, + saidx_t *v1, saidx_t *v2, saidx_t *v3, saidx_t *v4, saidx_t *v5) { + saidx_t *t; + if(ISAd[*v2] > ISAd[*v3]) { SWAP(v2, v3); } + if(ISAd[*v4] > ISAd[*v5]) { SWAP(v4, v5); } + if(ISAd[*v2] > ISAd[*v4]) { SWAP(v2, v4); SWAP(v3, v5); } + if(ISAd[*v1] > ISAd[*v3]) { SWAP(v1, v3); } + if(ISAd[*v1] > ISAd[*v4]) { SWAP(v1, v4); SWAP(v3, v5); } + if(ISAd[*v3] > ISAd[*v4]) { return v4; } + return v3; +} + +/* Returns the pivot element. */ +static INLINE +saidx_t * +tr_pivot(const saidx_t *ISAd, saidx_t *first, saidx_t *last) { + saidx_t *middle; + saidx_t t; + + t = last - first; + middle = first + t / 2; + + if(t <= 512) { + if(t <= 32) { + return tr_median3(ISAd, first, middle, last - 1); + } else { + t >>= 2; + return tr_median5(ISAd, first, first + t, middle, last - 1 - t, last - 1); + } + } + t >>= 3; + first = tr_median3(ISAd, first, first + t, first + (t << 1)); + middle = tr_median3(ISAd, middle - t, middle, middle + t); + last = tr_median3(ISAd, last - 1 - (t << 1), last - 1 - t, last - 1); + return tr_median3(ISAd, first, middle, last); +} + + +/*---------------------------------------------------------------------------*/ + +typedef struct _trbudget_t trbudget_t; +struct _trbudget_t { + saidx_t chance; + saidx_t remain; + saidx_t incval; + saidx_t count; +}; + +static INLINE +void +trbudget_init(trbudget_t *budget, saidx_t chance, saidx_t incval) { + budget->chance = chance; + budget->remain = budget->incval = incval; +} + +static INLINE +saint_t +trbudget_check(trbudget_t *budget, saidx_t size) { + if(size <= budget->remain) { budget->remain -= size; return 1; } + if(budget->chance == 0) { budget->count += size; return 0; } + budget->remain += budget->incval - size; + budget->chance -= 1; + return 1; +} + + +/*---------------------------------------------------------------------------*/ + +static INLINE +void +tr_partition(const saidx_t *ISAd, + saidx_t *first, saidx_t *middle, saidx_t *last, + saidx_t **pa, saidx_t **pb, saidx_t v) { + saidx_t *a, *b, *c, *d, *e, *f; + saidx_t t, s; + saidx_t x = 0; + + for(b = middle - 1; (++b < last) && ((x = ISAd[*b]) == v);) { } + if(((a = b) < last) && (x < v)) { + for(; (++b < last) && ((x = ISAd[*b]) <= v);) { + if(x == v) { SWAP(*b, *a); ++a; } + } + } + for(c = last; (b < --c) && ((x = ISAd[*c]) == v);) { } + if((b < (d = c)) && (x > v)) { + for(; (b < --c) && ((x = ISAd[*c]) >= v);) { + if(x == v) { SWAP(*c, *d); --d; } + } + } + for(; b < c;) { + SWAP(*b, *c); + for(; (++b < c) && ((x = ISAd[*b]) <= v);) { + if(x == v) { SWAP(*b, *a); ++a; } + } + for(; (b < --c) && ((x = ISAd[*c]) >= v);) { + if(x == v) { SWAP(*c, *d); --d; } + } + } + + if(a <= d) { + c = b - 1; + if((s = a - first) > (t = b - a)) { s = t; } + for(e = first, f = b - s; 0 < s; --s, ++e, ++f) { SWAP(*e, *f); } + if((s = d - c) > (t = last - d - 1)) { s = t; } + for(e = b, f = last - s; 0 < s; --s, ++e, ++f) { SWAP(*e, *f); } + first += (b - a), last -= (d - c); + } + *pa = first, *pb = last; +} + +static +void +tr_copy(saidx_t *ISA, const saidx_t *SA, + saidx_t *first, saidx_t *a, saidx_t *b, saidx_t *last, + saidx_t depth) { + /* sort suffixes of middle partition + by using sorted order of suffixes of left and right partition. */ + saidx_t *c, *d, *e; + saidx_t s, v; + + v = b - SA - 1; + for(c = first, d = a - 1; c <= d; ++c) { + if((0 <= (s = *c - depth)) && (ISA[s] == v)) { + *++d = s; + ISA[s] = d - SA; + } + } + for(c = last - 1, e = d + 1, d = b; e < d; --c) { + if((0 <= (s = *c - depth)) && (ISA[s] == v)) { + *--d = s; + ISA[s] = d - SA; + } + } +} + +static +void +tr_partialcopy(saidx_t *ISA, const saidx_t *SA, + saidx_t *first, saidx_t *a, saidx_t *b, saidx_t *last, + saidx_t depth) { + saidx_t *c, *d, *e; + saidx_t s, v; + saidx_t rank, lastrank, newrank = -1; + + v = b - SA - 1; + lastrank = -1; + for(c = first, d = a - 1; c <= d; ++c) { + if((0 <= (s = *c - depth)) && (ISA[s] == v)) { + *++d = s; + rank = ISA[s + depth]; + if(lastrank != rank) { lastrank = rank; newrank = d - SA; } + ISA[s] = newrank; + } + } + + lastrank = -1; + for(e = d; first <= e; --e) { + rank = ISA[*e]; + if(lastrank != rank) { lastrank = rank; newrank = e - SA; } + if(newrank != rank) { ISA[*e] = newrank; } + } + + lastrank = -1; + for(c = last - 1, e = d + 1, d = b; e < d; --c) { + if((0 <= (s = *c - depth)) && (ISA[s] == v)) { + *--d = s; + rank = ISA[s + depth]; + if(lastrank != rank) { lastrank = rank; newrank = d - SA; } + ISA[s] = newrank; + } + } +} + +static +void +tr_introsort(saidx_t *ISA, const saidx_t *ISAd, + saidx_t *SA, saidx_t *first, saidx_t *last, + trbudget_t *budget) { +#define STACK_SIZE TR_STACKSIZE + struct { const saidx_t *a; saidx_t *b, *c; saint_t d, e; }stack[STACK_SIZE]; + saidx_t *a, *b, *c; + saidx_t t; + saidx_t v, x = 0; + saidx_t incr = ISAd - ISA; + saint_t limit, next; + saint_t ssize, trlink = -1; + + for(ssize = 0, limit = tr_ilg(last - first);;) { + + if(limit < 0) { + if(limit == -1) { + /* tandem repeat partition */ + tr_partition(ISAd - incr, first, first, last, &a, &b, last - SA - 1); + + /* update ranks */ + if(a < last) { + for(c = first, v = a - SA - 1; c < a; ++c) { ISA[*c] = v; } + } + if(b < last) { + for(c = a, v = b - SA - 1; c < b; ++c) { ISA[*c] = v; } + } + + /* push */ + if(1 < (b - a)) { + STACK_PUSH5(NULL, a, b, 0, 0); + STACK_PUSH5(ISAd - incr, first, last, -2, trlink); + trlink = ssize - 2; + } + if((a - first) <= (last - b)) { + if(1 < (a - first)) { + STACK_PUSH5(ISAd, b, last, tr_ilg(last - b), trlink); + last = a, limit = tr_ilg(a - first); + } else if(1 < (last - b)) { + first = b, limit = tr_ilg(last - b); + } else { + STACK_POP5(ISAd, first, last, limit, trlink); + } + } else { + if(1 < (last - b)) { + STACK_PUSH5(ISAd, first, a, tr_ilg(a - first), trlink); + first = b, limit = tr_ilg(last - b); + } else if(1 < (a - first)) { + last = a, limit = tr_ilg(a - first); + } else { + STACK_POP5(ISAd, first, last, limit, trlink); + } + } + } else if(limit == -2) { + /* tandem repeat copy */ + a = stack[--ssize].b, b = stack[ssize].c; + if(stack[ssize].d == 0) { + tr_copy(ISA, SA, first, a, b, last, ISAd - ISA); + } else { + if(0 <= trlink) { stack[trlink].d = -1; } + tr_partialcopy(ISA, SA, first, a, b, last, ISAd - ISA); + } + STACK_POP5(ISAd, first, last, limit, trlink); + } else { + /* sorted partition */ + if(0 <= *first) { + a = first; + do { ISA[*a] = a - SA; } while((++a < last) && (0 <= *a)); + first = a; + } + if(first < last) { + a = first; do { *a = ~*a; } while(*++a < 0); + next = (ISA[*a] != ISAd[*a]) ? tr_ilg(a - first + 1) : -1; + if(++a < last) { for(b = first, v = a - SA - 1; b < a; ++b) { ISA[*b] = v; } } + + /* push */ + if(trbudget_check(budget, a - first)) { + if((a - first) <= (last - a)) { + STACK_PUSH5(ISAd, a, last, -3, trlink); + ISAd += incr, last = a, limit = next; + } else { + if(1 < (last - a)) { + STACK_PUSH5(ISAd + incr, first, a, next, trlink); + first = a, limit = -3; + } else { + ISAd += incr, last = a, limit = next; + } + } + } else { + if(0 <= trlink) { stack[trlink].d = -1; } + if(1 < (last - a)) { + first = a, limit = -3; + } else { + STACK_POP5(ISAd, first, last, limit, trlink); + } + } + } else { + STACK_POP5(ISAd, first, last, limit, trlink); + } + } + continue; + } + + if((last - first) <= TR_INSERTIONSORT_THRESHOLD) { + tr_insertionsort(ISAd, first, last); + limit = -3; + continue; + } + + if(limit-- == 0) { + tr_heapsort(ISAd, first, last - first); + for(a = last - 1; first < a; a = b) { + for(x = ISAd[*a], b = a - 1; (first <= b) && (ISAd[*b] == x); --b) { *b = ~*b; } + } + limit = -3; + continue; + } + + /* choose pivot */ + a = tr_pivot(ISAd, first, last); + SWAP(*first, *a); + v = ISAd[*first]; + + /* partition */ + tr_partition(ISAd, first, first + 1, last, &a, &b, v); + if((last - first) != (b - a)) { + next = (ISA[*a] != v) ? tr_ilg(b - a) : -1; + + /* update ranks */ + for(c = first, v = a - SA - 1; c < a; ++c) { ISA[*c] = v; } + if(b < last) { for(c = a, v = b - SA - 1; c < b; ++c) { ISA[*c] = v; } } + + /* push */ + if((1 < (b - a)) && (trbudget_check(budget, b - a))) { + if((a - first) <= (last - b)) { + if((last - b) <= (b - a)) { + if(1 < (a - first)) { + STACK_PUSH5(ISAd + incr, a, b, next, trlink); + STACK_PUSH5(ISAd, b, last, limit, trlink); + last = a; + } else if(1 < (last - b)) { + STACK_PUSH5(ISAd + incr, a, b, next, trlink); + first = b; + } else { + ISAd += incr, first = a, last = b, limit = next; + } + } else if((a - first) <= (b - a)) { + if(1 < (a - first)) { + STACK_PUSH5(ISAd, b, last, limit, trlink); + STACK_PUSH5(ISAd + incr, a, b, next, trlink); + last = a; + } else { + STACK_PUSH5(ISAd, b, last, limit, trlink); + ISAd += incr, first = a, last = b, limit = next; + } + } else { + STACK_PUSH5(ISAd, b, last, limit, trlink); + STACK_PUSH5(ISAd, first, a, limit, trlink); + ISAd += incr, first = a, last = b, limit = next; + } + } else { + if((a - first) <= (b - a)) { + if(1 < (last - b)) { + STACK_PUSH5(ISAd + incr, a, b, next, trlink); + STACK_PUSH5(ISAd, first, a, limit, trlink); + first = b; + } else if(1 < (a - first)) { + STACK_PUSH5(ISAd + incr, a, b, next, trlink); + last = a; + } else { + ISAd += incr, first = a, last = b, limit = next; + } + } else if((last - b) <= (b - a)) { + if(1 < (last - b)) { + STACK_PUSH5(ISAd, first, a, limit, trlink); + STACK_PUSH5(ISAd + incr, a, b, next, trlink); + first = b; + } else { + STACK_PUSH5(ISAd, first, a, limit, trlink); + ISAd += incr, first = a, last = b, limit = next; + } + } else { + STACK_PUSH5(ISAd, first, a, limit, trlink); + STACK_PUSH5(ISAd, b, last, limit, trlink); + ISAd += incr, first = a, last = b, limit = next; + } + } + } else { + if((1 < (b - a)) && (0 <= trlink)) { stack[trlink].d = -1; } + if((a - first) <= (last - b)) { + if(1 < (a - first)) { + STACK_PUSH5(ISAd, b, last, limit, trlink); + last = a; + } else if(1 < (last - b)) { + first = b; + } else { + STACK_POP5(ISAd, first, last, limit, trlink); + } + } else { + if(1 < (last - b)) { + STACK_PUSH5(ISAd, first, a, limit, trlink); + first = b; + } else if(1 < (a - first)) { + last = a; + } else { + STACK_POP5(ISAd, first, last, limit, trlink); + } + } + } + } else { + if(trbudget_check(budget, last - first)) { + limit = tr_ilg(last - first), ISAd += incr; + } else { + if(0 <= trlink) { stack[trlink].d = -1; } + STACK_POP5(ISAd, first, last, limit, trlink); + } + } + } +#undef STACK_SIZE +} + + + +/*---------------------------------------------------------------------------*/ + +/*- Function -*/ + +/* Tandem repeat sort */ +void +trsort(saidx_t *ISA, saidx_t *SA, saidx_t n, saidx_t depth) { + saidx_t *ISAd; + saidx_t *first, *last; + trbudget_t budget; + saidx_t t, skip, unsorted; + + trbudget_init(&budget, tr_ilg(n) * 2 / 3, n); +/* trbudget_init(&budget, tr_ilg(n) * 3 / 4, n); */ + for(ISAd = ISA + depth; -n < *SA; ISAd += ISAd - ISA) { + first = SA; + skip = 0; + unsorted = 0; + do { + if((t = *first) < 0) { first -= t; skip += t; } + else { + if(skip != 0) { *(first + skip) = skip; skip = 0; } + last = SA + ISA[t] + 1; + if(1 < (last - first)) { + budget.count = 0; + tr_introsort(ISA, ISAd, SA, first, last, &budget); + if(budget.count != 0) { unsorted += budget.count; } + else { skip = first - last; } + } else if((last - first) == 1) { + skip = -1; + } + first = last; + } + } while(first < (SA + n)); + if(skip != 0) { *(first + skip) = skip; } + if(unsorted == 0) { break; } + } +} diff --git a/tools/apultra_src/src/libdivsufsort/pkgconfig/CMakeLists.txt b/tools/apultra_src/src/libdivsufsort/pkgconfig/CMakeLists.txt new file mode 100644 index 0000000..ee7063c --- /dev/null +++ b/tools/apultra_src/src/libdivsufsort/pkgconfig/CMakeLists.txt @@ -0,0 +1,9 @@ +## generate libdivsufsort.pc ## +set(W64BIT "") +configure_file("${CMAKE_CURRENT_SOURCE_DIR}/libdivsufsort.pc.cmake" "${CMAKE_CURRENT_BINARY_DIR}/libdivsufsort.pc" @ONLY) +install(FILES "${CMAKE_CURRENT_BINARY_DIR}/libdivsufsort.pc" DESTINATION ${CMAKE_INSTALL_PKGCONFIGDIR}) +if(BUILD_DIVSUFSORT64) + set(W64BIT "64") + configure_file("${CMAKE_CURRENT_SOURCE_DIR}/libdivsufsort.pc.cmake" "${CMAKE_CURRENT_BINARY_DIR}/libdivsufsort64.pc" @ONLY) + install(FILES "${CMAKE_CURRENT_BINARY_DIR}/libdivsufsort64.pc" DESTINATION ${CMAKE_INSTALL_PKGCONFIGDIR}) +endif(BUILD_DIVSUFSORT64) diff --git a/tools/apultra_src/src/libdivsufsort/pkgconfig/libdivsufsort.pc.cmake b/tools/apultra_src/src/libdivsufsort/pkgconfig/libdivsufsort.pc.cmake new file mode 100644 index 0000000..6419d1e --- /dev/null +++ b/tools/apultra_src/src/libdivsufsort/pkgconfig/libdivsufsort.pc.cmake @@ -0,0 +1,11 @@ +prefix=@CMAKE_INSTALL_PREFIX@ +exec_prefix=${prefix} +libdir=@CMAKE_INSTALL_LIBDIR@ +includedir=@CMAKE_INSTALL_INCLUDEDIR@ + +Name: @PROJECT_NAME@@W64BIT@ +Description: @PROJECT_DESCRIPTION@ +Version: @PROJECT_VERSION_FULL@ +URL: @PROJECT_URL@ +Libs: -L${libdir} -ldivsufsort@W64BIT@ +Cflags: -I${includedir} diff --git a/tools/apultra_src/src/matchfinder.c b/tools/apultra_src/src/matchfinder.c new file mode 100644 index 0000000..dcf3464 --- /dev/null +++ b/tools/apultra_src/src/matchfinder.c @@ -0,0 +1,392 @@ +/*
+ * matchfinder.c - LZ match finder implementation
+ *
+ * The following copying information applies to this specific source code file:
+ *
+ * Written in 2019 by Emmanuel Marty <marty.emmanuel@gmail.com>
+ * Portions written in 2014-2015 by Eric Biggers <ebiggers3@gmail.com>
+ *
+ * To the extent possible under law, the author(s) have dedicated all copyright
+ * and related and neighboring rights to this software to the public domain
+ * worldwide via the Creative Commons Zero 1.0 Universal Public Domain
+ * Dedication (the "CC0").
+ *
+ * This software 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 CC0 for more details.
+ *
+ * You should have received a copy of the CC0 along with this software; if not
+ * see <http://creativecommons.org/publicdomain/zero/1.0/>.
+ */
+
+/*
+ * Uses the libdivsufsort library Copyright (c) 2003-2008 Yuta Mori
+ *
+ * Inspired by cap by Sven-Åke Dahl. https://github.com/svendahl/cap
+ * Also inspired by Charles Bloom's compression blog. http://cbloomrants.blogspot.com/
+ * With ideas from LZ4 by Yann Collet. https://github.com/lz4/lz4
+ * With help and support from spke <zxintrospec@gmail.com>
+ *
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include "matchfinder.h"
+#include "format.h"
+#include "libapultra.h"
+
+/**
+ * Hash index into TAG_BITS
+ *
+ * @param nIndex index value
+ *
+ * @return hash
+ */
+static inline int apultra_get_index_tag(unsigned int nIndex) {
+ return (int)(((unsigned long long)nIndex * 11400714819323198485ULL) >> (64ULL - TAG_BITS));
+}
+
+/**
+ * Parse input data, build suffix array and overlaid data structures to speed up match finding
+ *
+ * @param pCompressor compression context
+ * @param pInWindow pointer to input data window (previously compressed bytes + bytes to compress)
+ * @param nInWindowSize total input size in bytes (previously compressed bytes + bytes to compress)
+ *
+ * @return 0 for success, non-zero for failure
+ */
+int apultra_build_suffix_array(apultra_compressor *pCompressor, const unsigned char *pInWindow, const int nInWindowSize) {
+ unsigned long long *intervals = pCompressor->intervals;
+
+ /* Build suffix array from input data */
+ saidx_t *suffixArray = (saidx_t*)intervals;
+ if (divsufsort_build_array(&pCompressor->divsufsort_context, pInWindow, suffixArray, nInWindowSize) != 0) {
+ return 100;
+ }
+
+ int i;
+
+ for (i = nInWindowSize - 1; i >= 0; i--) {
+ intervals[i] = suffixArray[i];
+ }
+
+ int *PLCP = (int*)pCompressor->pos_data; /* Use temporarily */
+ int *Phi = PLCP;
+ int nCurLen = 0;
+
+ /* Compute the permuted LCP first (Kärkkäinen method) */
+ Phi[intervals[0]] = -1;
+ for (i = 1; i < nInWindowSize; i++)
+ Phi[intervals[i]] = (unsigned int)intervals[i - 1];
+ for (i = 0; i < nInWindowSize; i++) {
+ if (Phi[i] == -1) {
+ PLCP[i] = 0;
+ continue;
+ }
+ int nMaxLen = (i > Phi[i]) ? (nInWindowSize - i) : (nInWindowSize - Phi[i]);
+ while (nCurLen < nMaxLen && pInWindow[i + nCurLen] == pInWindow[Phi[i] + nCurLen]) nCurLen++;
+ PLCP[i] = nCurLen;
+ if (nCurLen > 0)
+ nCurLen--;
+ }
+
+ /* Rotate permuted LCP into the LCP. This has better cache locality than the direct Kasai LCP method. This also
+ * saves us from having to build the inverse suffix array index, as the LCP is calculated without it using this method,
+ * and the interval builder below doesn't need it either. */
+ intervals[0] &= POS_MASK;
+
+ for (i = 1; i < nInWindowSize; i++) {
+ int nIndex = (int)(intervals[i] & POS_MASK);
+ int nLen = PLCP[nIndex];
+ if (nLen < MIN_MATCH_SIZE)
+ nLen = 0;
+ if (nLen > LCP_MAX)
+ nLen = LCP_MAX;
+ int nTaggedLen = 0;
+ if (nLen)
+ nTaggedLen = (nLen << TAG_BITS) | (apultra_get_index_tag((unsigned int)nIndex) & ((1 << TAG_BITS) - 1));
+ intervals[i] = ((unsigned long long)nIndex) | (((unsigned long long)nTaggedLen) << LCP_SHIFT);
+ }
+
+ /**
+ * Build intervals for finding matches
+ *
+ * Methodology and code fragment taken from wimlib (CC0 license):
+ * https://wimlib.net/git/?p=wimlib;a=blob_plain;f=src/lcpit_matchfinder.c;h=a2d6a1e0cd95200d1f3a5464d8359d5736b14cbe;hb=HEAD
+ */
+ unsigned long long * const SA_and_LCP = intervals;
+ unsigned long long *pos_data = pCompressor->pos_data;
+ unsigned long long next_interval_idx;
+ unsigned long long *top = pCompressor->open_intervals;
+ unsigned long long prev_pos = SA_and_LCP[0] & POS_MASK;
+
+ *top = 0;
+ intervals[0] = 0;
+ next_interval_idx = 1;
+
+ for (int r = 1; r < nInWindowSize; r++) {
+ const unsigned long long next_pos = SA_and_LCP[r] & POS_MASK;
+ const unsigned long long next_lcp = SA_and_LCP[r] & LCP_MASK;
+ const unsigned long long top_lcp = *top & LCP_MASK;
+
+ if (next_lcp == top_lcp) {
+ /* Continuing the deepest open interval */
+ pos_data[prev_pos] = *top;
+ }
+ else if (next_lcp > top_lcp) {
+ /* Opening a new interval */
+ *++top = next_lcp | next_interval_idx++;
+ pos_data[prev_pos] = *top;
+ }
+ else {
+ /* Closing the deepest open interval */
+ pos_data[prev_pos] = *top;
+ for (;;) {
+ const unsigned long long closed_interval_idx = *top-- & POS_MASK;
+ const unsigned long long superinterval_lcp = *top & LCP_MASK;
+
+ if (next_lcp == superinterval_lcp) {
+ /* Continuing the superinterval */
+ intervals[closed_interval_idx] = *top;
+ break;
+ }
+ else if (next_lcp > superinterval_lcp) {
+ /* Creating a new interval that is a
+ * superinterval of the one being
+ * closed, but still a subinterval of
+ * its superinterval */
+ *++top = next_lcp | next_interval_idx++;
+ intervals[closed_interval_idx] = *top;
+ break;
+ }
+ else {
+ /* Also closing the superinterval */
+ intervals[closed_interval_idx] = *top;
+ }
+ }
+ }
+ prev_pos = next_pos;
+ }
+
+ /* Close any still-open intervals. */
+ pos_data[prev_pos] = *top;
+ for (; top > pCompressor->open_intervals; top--)
+ intervals[*top & POS_MASK] = *(top - 1);
+
+ /* Success */
+ return 0;
+}
+
+/**
+ * Find matches at the specified offset in the input window
+ *
+ * @param pCompressor compression context
+ * @param nOffset offset to find matches at, in the input window
+ * @param pMatches pointer to returned matches
+ * @param pMatchDepth pointer to returned match depths
+ * @param pMatch1 pointer to 1-byte length, 4 bit offset match
+ * @param nMaxMatches maximum number of matches to return (0 for none)
+ * @param nBlockFlags bit 0: 1 for first block, 0 otherwise; bit 1: 1 for last block, 0 otherwise
+ *
+ * @return number of matches
+ */
+int apultra_find_matches_at(apultra_compressor *pCompressor, const int nOffset, apultra_match *pMatches, unsigned short *pMatchDepth, unsigned char *pMatch1, const int nMaxMatches, const int nBlockFlags) {
+ unsigned long long *intervals = pCompressor->intervals;
+ unsigned long long *pos_data = pCompressor->pos_data;
+ unsigned long long ref;
+ unsigned long long super_ref;
+ unsigned long long match_pos;
+ apultra_match *matchptr;
+ unsigned short *depthptr;
+
+ *pMatch1 = 0;
+
+ /**
+ * Find matches using intervals
+ *
+ * Taken from wimlib (CC0 license):
+ * https://wimlib.net/git/?p=wimlib;a=blob_plain;f=src/lcpit_matchfinder.c;h=a2d6a1e0cd95200d1f3a5464d8359d5736b14cbe;hb=HEAD
+ */
+
+ /* Get the deepest lcp-interval containing the current suffix. */
+ ref = pos_data[nOffset];
+
+ pos_data[nOffset] = 0;
+
+ /* Ascend until we reach a visited interval, the root, or a child of the
+ * root. Link unvisited intervals to the current suffix as we go. */
+ while ((super_ref = intervals[ref & POS_MASK]) & LCP_MASK) {
+ intervals[ref & POS_MASK] = nOffset | VISITED_FLAG;
+ ref = super_ref;
+ }
+
+ if (super_ref == 0) {
+ /* In this case, the current interval may be any of:
+ * (1) the root;
+ * (2) an unvisited child of the root */
+
+ if (ref != 0) /* Not the root? */
+ intervals[ref & POS_MASK] = nOffset | VISITED_FLAG;
+ return 0;
+ }
+
+ /* Ascend indirectly via pos_data[] links. */
+ match_pos = super_ref & EXCL_VISITED_MASK;
+ matchptr = pMatches;
+ depthptr = pMatchDepth;
+ int nPrevOffset = 0;
+ int nPrevLen = 0;
+ int nCurDepth = 0;
+ unsigned short *cur_depth = NULL;
+
+ if (nOffset > match_pos && (nBlockFlags & 3) == 3) {
+ int nMatchOffset = (int)(nOffset - match_pos);
+ int nMatchLen = (int)(ref >> (LCP_SHIFT + TAG_BITS));
+
+ if ((matchptr - pMatches) < nMaxMatches) {
+ if (nMatchOffset <= MAX_OFFSET) {
+ if (nPrevOffset && nPrevLen > 2 && nMatchOffset == (nPrevOffset - 1) && nMatchLen == (nPrevLen - 1) && cur_depth && nCurDepth < LCP_MAX) {
+ nCurDepth++;
+ *cur_depth = nCurDepth;
+ }
+ else {
+ nCurDepth = 0;
+
+ cur_depth = depthptr;
+ matchptr->length = nMatchLen;
+ matchptr->offset = nMatchOffset;
+ *depthptr = 0;
+ matchptr++;
+ depthptr++;
+ }
+
+ nPrevLen = nMatchLen;
+ nPrevOffset = nMatchOffset;
+ }
+ }
+ }
+
+ for (;;) {
+ if ((super_ref = pos_data[match_pos]) > ref) {
+ match_pos = intervals[super_ref & POS_MASK] & EXCL_VISITED_MASK;
+
+ if (nOffset > match_pos && (nBlockFlags & 3) == 3) {
+ int nMatchOffset = (int)(nOffset - match_pos);
+ int nMatchLen = (int)(ref >> (LCP_SHIFT + TAG_BITS));
+
+ if ((matchptr - pMatches) < nMaxMatches) {
+ if (nMatchOffset <= MAX_OFFSET && abs(nMatchOffset - nPrevOffset) >= 288) {
+ if (nPrevOffset && nPrevLen > 2 && nMatchOffset == (nPrevOffset - 1) && nMatchLen == (nPrevLen - 1) && cur_depth && nCurDepth < LCP_MAX) {
+ nCurDepth++;
+ *cur_depth = nCurDepth | 0x8000;
+ }
+ else {
+ nCurDepth = 0;
+
+ cur_depth = depthptr;
+ matchptr->length = nMatchLen;
+ matchptr->offset = nMatchOffset;
+ *depthptr = 0x8000;
+ matchptr++;
+ depthptr++;
+ }
+
+ nPrevLen = nMatchLen;
+ nPrevOffset = nMatchOffset;
+ }
+ }
+ }
+ }
+
+ while ((super_ref = pos_data[match_pos]) > ref)
+ match_pos = intervals[super_ref & POS_MASK] & EXCL_VISITED_MASK;
+ intervals[ref & POS_MASK] = nOffset | VISITED_FLAG;
+ pos_data[match_pos] = (unsigned long long)ref;
+
+ int nMatchOffset = (int)(nOffset - match_pos);
+ int nMatchLen = (int)(ref >> (LCP_SHIFT + TAG_BITS));
+
+ if ((matchptr - pMatches) < nMaxMatches) {
+ if (nMatchOffset <= MAX_OFFSET && nMatchOffset != nPrevOffset) {
+ if (nPrevOffset && nPrevLen > 2 && nMatchOffset == (nPrevOffset - 1) && nMatchLen == (nPrevLen - 1) && cur_depth && nCurDepth < LCP_MAX) {
+ nCurDepth++;
+ *cur_depth = nCurDepth;
+ }
+ else {
+ nCurDepth = 0;
+
+ cur_depth = depthptr;
+ matchptr->length = nMatchLen;
+ matchptr->offset = nMatchOffset;
+ *depthptr = 0;
+ matchptr++;
+ depthptr++;
+ }
+
+ nPrevLen = nMatchLen;
+ nPrevOffset = nMatchOffset;
+ }
+ }
+
+ if (nMatchOffset && nMatchOffset < 16 && nMatchLen)
+ *pMatch1 = nMatchOffset;
+
+ if (super_ref == 0)
+ break;
+ ref = super_ref;
+ match_pos = intervals[ref & POS_MASK] & EXCL_VISITED_MASK;
+ }
+
+ return (int)(matchptr - pMatches);
+}
+
+/**
+ * Skip previously compressed bytes
+ *
+ * @param pCompressor compression context
+ * @param nStartOffset current offset in input window (typically 0)
+ * @param nEndOffset offset to skip to in input window (typically the number of previously compressed bytes)
+ */
+void apultra_skip_matches(apultra_compressor *pCompressor, const int nStartOffset, const int nEndOffset) {
+ apultra_match match;
+ unsigned short depth;
+ unsigned char match1;
+ int i;
+
+ /* Skipping still requires scanning for matches, as this also performs a lazy update of the intervals. However,
+ * we don't store the matches. */
+ for (i = nStartOffset; i < nEndOffset; i++) {
+ apultra_find_matches_at(pCompressor, i, &match, &depth, &match1, 0, 0);
+ }
+}
+
+/**
+ * Find all matches for the data to be compressed
+ *
+ * @param pCompressor compression context
+ * @param nMatchesPerOffset maximum number of matches to store for each offset
+ * @param nStartOffset current offset in input window (typically the number of previously compressed bytes)
+ * @param nEndOffset offset to end finding matches at (typically the size of the total input window in bytes
+ * @param nBlockFlags bit 0: 1 for first block, 0 otherwise; bit 1: 1 for last block, 0 otherwise
+ */
+void apultra_find_all_matches(apultra_compressor *pCompressor, const int nMatchesPerOffset, const int nStartOffset, const int nEndOffset, const int nBlockFlags) {
+ apultra_match *pMatch = pCompressor->match;
+ unsigned short *pMatchDepth = pCompressor->match_depth;
+ unsigned char *pMatch1 = pCompressor->match1;
+ int i;
+
+ for (i = nStartOffset; i < nEndOffset; i++) {
+ int nMatches = apultra_find_matches_at(pCompressor, i, pMatch, pMatchDepth, pMatch1, nMatchesPerOffset, nBlockFlags);
+
+ while (nMatches < nMatchesPerOffset) {
+ pMatch[nMatches].length = 0;
+ pMatch[nMatches].offset = 0;
+ pMatchDepth[nMatches] = 0;
+ nMatches++;
+ }
+
+ pMatch += nMatchesPerOffset;
+ pMatchDepth += nMatchesPerOffset;
+ pMatch1++;
+ }
+}
diff --git a/tools/apultra_src/src/matchfinder.h b/tools/apultra_src/src/matchfinder.h new file mode 100644 index 0000000..7d68eaf --- /dev/null +++ b/tools/apultra_src/src/matchfinder.h @@ -0,0 +1,94 @@ +/*
+ * matchfinder.h - LZ match finder definitions
+ *
+ * Copyright (C) 2019 Emmanuel Marty
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty. In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ */
+
+/*
+ * Uses the libdivsufsort library Copyright (c) 2003-2008 Yuta Mori
+ *
+ * Inspired by cap by Sven-Åke Dahl. https://github.com/svendahl/cap
+ * Also inspired by Charles Bloom's compression blog. http://cbloomrants.blogspot.com/
+ * With ideas from LZ4 by Yann Collet. https://github.com/lz4/lz4
+ * With help and support from spke <zxintrospec@gmail.com>
+ *
+ */
+
+#ifndef _MATCHFINDER_H
+#define _MATCHFINDER_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Forward declarations */
+typedef struct _apultra_match apultra_match;
+typedef struct _apultra_compressor apultra_compressor;
+
+/**
+ * Parse input data, build suffix array and overlaid data structures to speed up match finding
+ *
+ * @param pCompressor compression context
+ * @param pInWindow pointer to input data window (previously compressed bytes + bytes to compress)
+ * @param nInWindowSize total input size in bytes (previously compressed bytes + bytes to compress)
+ *
+ * @return 0 for success, non-zero for failure
+ */
+int apultra_build_suffix_array(apultra_compressor *pCompressor, const unsigned char *pInWindow, const int nInWindowSize);
+
+/**
+ * Find matches at the specified offset in the input window
+ *
+ * @param pCompressor compression context
+ * @param nOffset offset to find matches at, in the input window
+ * @param pMatches pointer to returned matches
+ * @param pMatchDepth pointer to returned match depths
+ * @param pMatch1 pointer to 1-byte length, 4 bit offset match
+ * @param nMaxMatches maximum number of matches to return (0 for none)
+ * @param nBlockFlags bit 0: 1 for first block, 0 otherwise; bit 1: 1 for last block, 0 otherwise
+ *
+ * @return number of matches
+ */
+int apultra_find_matches_at(apultra_compressor *pCompressor, const int nOffset, apultra_match *pMatches, unsigned short *pMatchDepth, unsigned char *pMatch1, const int nMaxMatches, const int nBlockFlags);
+
+/**
+ * Skip previously compressed bytes
+ *
+ * @param pCompressor compression context
+ * @param nStartOffset current offset in input window (typically 0)
+ * @param nEndOffset offset to skip to in input window (typically the number of previously compressed bytes)
+ */
+void apultra_skip_matches(apultra_compressor *pCompressor, const int nStartOffset, const int nEndOffset);
+
+/**
+ * Find all matches for the data to be compressed
+ *
+ * @param pCompressor compression context
+ * @param nMatchesPerOffset maximum number of matches to store for each offset
+ * @param nStartOffset current offset in input window (typically the number of previously compressed bytes)
+ * @param nEndOffset offset to end finding matches at (typically the size of the total input window in bytes
+ * @param nBlockFlags bit 0: 1 for first block, 0 otherwise; bit 1: 1 for last block, 0 otherwise
+ */
+void apultra_find_all_matches(apultra_compressor *pCompressor, const int nMatchesPerOffset, const int nStartOffset, const int nEndOffset, const int nBlockFlags);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _MATCHFINDER_H */
diff --git a/tools/apultra_src/src/shrink.c b/tools/apultra_src/src/shrink.c new file mode 100644 index 0000000..127c6fb --- /dev/null +++ b/tools/apultra_src/src/shrink.c @@ -0,0 +1,1615 @@ +/*
+ * shrink.c - compressor implementation
+ *
+ * Copyright (C) 2019 Emmanuel Marty
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty. In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ */
+
+/*
+ * Uses the libdivsufsort library Copyright (c) 2003-2008 Yuta Mori
+ *
+ * Inspired by cap by Sven-Åke Dahl. https://github.com/svendahl/cap
+ * Also inspired by Charles Bloom's compression blog. http://cbloomrants.blogspot.com/
+ * With ideas from LZ4 by Yann Collet. https://github.com/lz4/lz4
+ * With help and support from spke <zxintrospec@gmail.com>
+ *
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#include "libapultra.h"
+#include "matchfinder.h"
+#include "shrink.h"
+#include "format.h"
+
+#define TOKEN_PREFIX_SIZE 1 /* literal/ match bit */
+
+#define TOKEN_CODE_LARGE_MATCH 0 /* 0 */
+#define TOKEN_SIZE_LARGE_MATCH 1
+
+#define TOKEN_CODE_7BIT_MATCH 2 /* 10 */
+#define TOKEN_SIZE_7BIT_MATCH 2
+
+#define TOKEN_CODE_4BIT_MATCH 3 /* 11 */
+#define TOKEN_SIZE_4BIT_MATCH 2
+
+#define CountShift(N,bits) if ((N)>>(bits)) { (N)>>=(bits); (n) += (bits); }
+
+/** Code values for variable 8+gamma2 bits offset + gamma2 len match; 7 bits offset + 1 bit len match; and 4 bits offset + fixed 1 byte len match */
+static const int _token_code[3] = { TOKEN_CODE_LARGE_MATCH, TOKEN_CODE_7BIT_MATCH, TOKEN_CODE_4BIT_MATCH };
+
+/** Code sizes for variable 8+gamma2 bits offset + gamma2 len match; 7 bits offset + 1 bit len match; and 4 bits offset + fixed 1 byte len match */
+static const int _token_size[3] = { TOKEN_SIZE_LARGE_MATCH, TOKEN_SIZE_7BIT_MATCH, TOKEN_SIZE_4BIT_MATCH };
+
+/** Gamma2 bit counts for common values, up to 255 */
+static char _gamma2_size[256] = {
+ 0, 0, 2, 2, 4, 4, 4, 4, 6, 6, 6, 6, 6, 6, 6, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
+};
+
+/**
+ * Write bit to output (compressed) buffer
+ *
+ * @param pOutData pointer to output buffer
+ * @param nOutOffset current write index into output buffer
+ * @param nMaxOutDataSize maximum size of output buffer, in bytes
+ * @param nBit value of bit to write (0 or 1)
+ * @param nCurBitsOffset write index into output buffer, of current byte being filled with bits
+ * @param nCurBitMask bit shifter
+ * @param nBitBufferIdx index of bit buffer to write to
+ *
+ * @return updated write index into output buffer, or -1 in case of an error
+ */
+static int apultra_write_bit(unsigned char *pOutData, int nOutOffset, const int nMaxOutDataSize, const int nBit, int *nCurBitsOffset, int *nCurBitMask, const int nBitBufferIdx) {
+ if (nOutOffset < 0) return -1;
+
+ if (nCurBitsOffset[nBitBufferIdx] == INT_MIN) {
+ /* Allocate a new byte in the stream to pack bits in */
+ if (nOutOffset >= nMaxOutDataSize) return -1;
+ nCurBitsOffset[nBitBufferIdx] = nOutOffset;
+ nCurBitMask[nBitBufferIdx] = 1 << 7;
+ pOutData[nOutOffset++] = 0;
+ }
+
+ if (nBit) {
+ pOutData[nCurBitsOffset[nBitBufferIdx]] |= nCurBitMask[nBitBufferIdx];
+ }
+
+ nCurBitMask[nBitBufferIdx] >>= 1;
+ if (nCurBitMask[nBitBufferIdx] == 0) {
+ /* Current byte is full */
+ nCurBitsOffset[nBitBufferIdx] = INT_MIN;
+ }
+
+ return nOutOffset;
+}
+
+/**
+ * Get size of gamma2 encoded value
+ *
+ * @param nValue value of evaluate (2..n)
+ *
+ * @return number of bits required
+ */
+static int apultra_get_gamma2_size(int nValue) {
+ if (nValue >= 0 && nValue < 256)
+ return _gamma2_size[nValue];
+ else {
+ unsigned int n = 0;
+ CountShift(nValue, 16);
+ CountShift(nValue, 8);
+ CountShift(nValue, 4);
+ CountShift(nValue, 2);
+ CountShift(nValue, 1);
+
+ return n << 1;
+ }
+}
+
+/**
+ * Write gamma2 encoded value to output (compressed) buffer
+ *
+ * @param pOutData pointer to output buffer
+ * @param nOutOffset current write index into output buffer
+ * @param nMaxOutDataSize maximum size of output buffer, in bytes
+ * @param nValue value of write (2..n)
+ * @param nCurBitsOffset write index into output buffer, of current byte being filled with bits
+ * @param nCurBitMask bit shifter
+ * @param nBitBufferIdx index of bit buffer to write to
+ *
+ * @return updated write index into output buffer, or -1 in case of an error
+ */
+static int apultra_write_gamma2_value(unsigned char *pOutData, int nOutOffset, const int nMaxOutDataSize, int nValue, int *nCurBitsOffset, int *nCurBitMask, const int nBitBufferIdx) {
+ int msb = 30;
+ while ((nValue >> msb--) == 0);
+
+ if (nBitBufferIdx == 0) {
+ /* Standard aPLib encoding. */
+ while (msb >= 0) {
+ int bit = (nValue >> msb) & 1;
+
+ nOutOffset = apultra_write_bit(pOutData, nOutOffset, nMaxOutDataSize, bit ? 1 : 0, nCurBitsOffset, nCurBitMask, nBitBufferIdx);
+ msb--;
+ nOutOffset = apultra_write_bit(pOutData, nOutOffset, nMaxOutDataSize, msb >= 0 ? 1 : 0, nCurBitsOffset, nCurBitMask, nBitBufferIdx);
+ }
+ }
+ else {
+ /* Enhanced encoding for 8-bit microprocessors ...
+ * 1) Write out values of 256 and higher lo-byte first (so the gamma2 decoder only needs to rotate a byte).
+ * 2) Swap meaning of continue/stop bits (saves a byte on the 6502, with no effect on other platforms).
+ */
+ if (msb >= 8) {
+ int btm = 7;
+ /* Write lo-byte of nValue first. */
+ while (btm >= 0) {
+ int bit = (nValue >> btm) & 1;
+
+ nOutOffset = apultra_write_bit(pOutData, nOutOffset, nMaxOutDataSize, bit ? 1 : 0, nCurBitsOffset, nCurBitMask, nBitBufferIdx);
+ btm--;
+ nOutOffset = apultra_write_bit(pOutData, nOutOffset, nMaxOutDataSize, 0, nCurBitsOffset, nCurBitMask, nBitBufferIdx);
+ }
+ /* Then write the remaining bits. */
+ nValue >>= 8;
+ msb -= 8;
+ }
+
+ while (msb >= 0) {
+ int bit = (nValue >> msb) & 1;
+
+ nOutOffset = apultra_write_bit(pOutData, nOutOffset, nMaxOutDataSize, bit ? 1 : 0, nCurBitsOffset, nCurBitMask, nBitBufferIdx);
+ msb--;
+ nOutOffset = apultra_write_bit(pOutData, nOutOffset, nMaxOutDataSize, msb >= 0 ? 0 : 1, nCurBitsOffset, nCurBitMask, nBitBufferIdx);
+ }
+ }
+
+ return nOutOffset;
+}
+
+/**
+ * Get the number of extra bits required to represent a literals length
+ *
+ * @param nLength literals length
+ *
+ * @return number of extra bits required
+ */
+static inline int apultra_get_literals_varlen_size(const int nLength) {
+ return nLength;
+}
+
+/**
+ * Get the number of extra bits required to represent a match offset
+ *
+ * @param nLength match length
+ * @param nMatchOffset match offset
+ * @param nFollowsLiteral non-zero if the match follows a literal, zero if it immediately follows another match
+ *
+ * @return number of extra bits required
+ */
+static inline int apultra_get_offset_varlen_size(const int nLength, const int nMatchOffset, const int nFollowsLiteral) {
+ if (nLength == 1 && nMatchOffset < 16)
+ return 4 + TOKEN_SIZE_4BIT_MATCH;
+ else {
+ if (nLength <= 3 && nMatchOffset < 128)
+ return 8 + TOKEN_SIZE_7BIT_MATCH;
+ else {
+ if (nFollowsLiteral)
+ return 8 + TOKEN_SIZE_LARGE_MATCH + apultra_get_gamma2_size((nMatchOffset >> 8) + 3);
+ else
+ return 8 + TOKEN_SIZE_LARGE_MATCH + apultra_get_gamma2_size((nMatchOffset >> 8) + 2);
+ }
+ }
+}
+
+/**
+ * Get the number of extra bits required to represent a rep-match
+ *
+ * @return number of extra bits required
+ */
+static inline int apultra_get_rep_offset_varlen_size(void) {
+ return TOKEN_SIZE_LARGE_MATCH + 2 /* apultra_get_gamma2_size(2) */;
+}
+
+/**
+ * Get the number of extra bits required to represent a match length
+ *
+ * @param nLength match length
+ * @param nMatchOffset match offset
+ * @param nIsRepMatch non-zero if this is a rep-match, zero if it is a regular match
+ *
+ * @return number of extra bits required
+ */
+static inline int apultra_get_match_varlen_size(int nLength, const int nMatchOffset, const int nIsRepMatch) {
+ if (nLength == 1 && nMatchOffset < 16)
+ return 0;
+ else {
+ if (nLength <= 3 && nMatchOffset < 128 && !nIsRepMatch)
+ return 0;
+ else {
+ if (nMatchOffset < 128 && !nIsRepMatch)
+ nLength -= 2;
+ if (nMatchOffset < MINMATCH3_OFFSET || nIsRepMatch)
+ return apultra_get_gamma2_size(nLength);
+ else if (nMatchOffset < MINMATCH4_OFFSET)
+ return apultra_get_gamma2_size(nLength - 1);
+ else
+ return apultra_get_gamma2_size(nLength - 2);
+ }
+ }
+}
+
+/**
+ * Write extra encoded match length bytes to output (compressed) buffer. The caller must first check that there is enough
+ * room to write the bytes.
+ *
+ * @param pOutData pointer to output buffer
+ * @param nOutOffset current write index into output buffer
+ * @param nMaxOutDataSize maximum size of output buffer, in bytes
+ * @param nCurBitsOffset write index into output buffer, of current byte being filled with bits
+ * @param nCurBitMask bit shifter
+ * @param nLength match length
+ * @param nMatchOffset match offset
+ * @param nIsRepMatch non-zero if this is a rep-match, zero if it is a regular match
+ * @param nBitBufferIdx index of bit buffer to write to
+ *
+ * @return updated write index into output buffer, or -1 in case of an error
+ */
+static inline int apultra_write_match_varlen(unsigned char *pOutData, int nOutOffset, const int nMaxOutDataSize, int *nCurBitsOffset, int *nCurBitMask, const int nBitBufferIdx, int nLength, const int nMatchOffset, const int nIsRepMatch) {
+ if (nLength < 2)
+ return -1;
+
+ if (nMatchOffset < 128 && !nIsRepMatch)
+ nLength -= 2;
+ if (nMatchOffset < MINMATCH3_OFFSET || nIsRepMatch)
+ return apultra_write_gamma2_value(pOutData, nOutOffset, nMaxOutDataSize, nLength, nCurBitsOffset, nCurBitMask, nBitBufferIdx);
+ else if (nMatchOffset < MINMATCH4_OFFSET)
+ return apultra_write_gamma2_value(pOutData, nOutOffset, nMaxOutDataSize, nLength - 1, nCurBitsOffset, nCurBitMask, nBitBufferIdx);
+ else
+ return apultra_write_gamma2_value(pOutData, nOutOffset, nMaxOutDataSize, nLength - 2, nCurBitsOffset, nCurBitMask, nBitBufferIdx);
+}
+
+/**
+ * Insert forward rep candidate
+ *
+ * @param pCompressor compression context
+ * @param pInWindow pointer to input data window (previously compressed bytes + bytes to compress)
+ * @param i input data window position whose matches are being considered
+ * @param nMatchOffset match offset to use as rep candidate
+ * @param nStartOffset current offset in input window (typically the number of previously compressed bytes)
+ * @param nEndOffset offset to end finding matches at (typically the size of the total input window in bytes
+ * @param nDepth current insertion depth
+ */
+static void apultra_insert_forward_match(apultra_compressor *pCompressor, const unsigned char *pInWindow, const int i, const int nMatchOffset, const int nStartOffset, const int nEndOffset, const int nMatchesPerArrival, int nDepth) {
+ apultra_arrival *arrival = pCompressor->arrival - (nStartOffset * NMATCHES_PER_ARRIVAL);
+ int j;
+
+ if (nDepth >= 10) return;
+
+ for (j = 0; j < nMatchesPerArrival && arrival[(i * NMATCHES_PER_ARRIVAL) + j].from_slot; j++) {
+ int nRepOffset = arrival[(i * NMATCHES_PER_ARRIVAL) + j].rep_offset;
+
+ if (nMatchOffset != nRepOffset && nRepOffset) {
+ int nRepPos = arrival[(i * NMATCHES_PER_ARRIVAL) + j].rep_pos;
+
+ if (arrival[(i * NMATCHES_PER_ARRIVAL) + j].follows_literal &&
+ nRepPos &&
+ nRepPos > nMatchOffset &&
+ nRepPos < nEndOffset) {
+ int nCurRepLen = 0;
+
+ int nMaxRepLen = nEndOffset - nRepPos;
+ if (nMaxRepLen > LCP_MAX)
+ nMaxRepLen = LCP_MAX;
+ while ((nCurRepLen + 8) < nMaxRepLen && !memcmp(pInWindow + nRepPos + nCurRepLen, pInWindow + nRepPos - nMatchOffset + nCurRepLen, 8))
+ nCurRepLen += 8;
+ while ((nCurRepLen + 4) < nMaxRepLen && !memcmp(pInWindow + nRepPos + nCurRepLen, pInWindow + nRepPos - nMatchOffset + nCurRepLen, 4))
+ nCurRepLen += 4;
+ while (nCurRepLen < nMaxRepLen && pInWindow[nRepPos + nCurRepLen] == pInWindow[nRepPos - nMatchOffset + nCurRepLen])
+ nCurRepLen++;
+
+ if (nCurRepLen >= 2) {
+ apultra_match *fwd_match = pCompressor->match + ((nRepPos - nStartOffset) << MATCHES_PER_INDEX_SHIFT);
+ unsigned short *fwd_depth = pCompressor->match_depth + ((nRepPos - nStartOffset) << MATCHES_PER_INDEX_SHIFT);
+ int exists = 0;
+ int r;
+
+ for (r = 0; r < NMATCHES_PER_INDEX && fwd_match[r].length >= MIN_MATCH_SIZE; r++) {
+ if (fwd_match[r].offset == nMatchOffset && (fwd_depth[r] & 0x7fff) == 0) {
+ exists = 1;
+
+ if ((int)fwd_match[r].length < nCurRepLen) {
+ fwd_match[r].length = nCurRepLen;
+ fwd_depth[r] = 0;
+ apultra_insert_forward_match(pCompressor, pInWindow, nRepPos, nMatchOffset, nStartOffset, nEndOffset, nMatchesPerArrival, nDepth + 1);
+ }
+ break;
+ }
+ }
+
+ if (!exists && r < NMATCHES_PER_INDEX) {
+ fwd_match[r].offset = nMatchOffset;
+ fwd_match[r].length = nCurRepLen;
+ fwd_depth[r] = 0;
+
+ apultra_insert_forward_match(pCompressor, pInWindow, nRepPos, nMatchOffset, nStartOffset, nEndOffset, nMatchesPerArrival, nDepth + 1);
+ }
+ }
+ }
+ }
+ }
+}
+
+/**
+ * Attempt to pick optimal matches, so as to produce the smallest possible output that decompresses to the same input
+ *
+ * @param pCompressor compression context
+ * @param pInWindow pointer to input data window (previously compressed bytes + bytes to compress)
+ * @param nStartOffset current offset in input window (typically the number of previously compressed bytes)
+ * @param nEndOffset offset to end finding matches at (typically the size of the total input window in bytes
+ * @param nInsertForwardReps non-zero to insert forward repmatch candidates, zero to use the previously inserted candidates
+ * @param nCurRepMatchOffset starting rep offset for this block
+ * @param nBlockFlags bit 0: 1 for first block, 0 otherwise; bit 1: 1 for last block, 0 otherwise
+ */
+static void apultra_optimize_forward(apultra_compressor *pCompressor, const unsigned char *pInWindow, const int nStartOffset, const int nEndOffset, const int nInsertForwardReps, const int *nCurRepMatchOffset, const int nBlockFlags, const int nMatchesPerArrival) {
+ apultra_arrival *arrival = pCompressor->arrival - (nStartOffset * NMATCHES_PER_ARRIVAL);
+ int i, j, n;
+
+ if ((nEndOffset - nStartOffset) > pCompressor->block_size) return;
+
+ memset(arrival + (nStartOffset * NMATCHES_PER_ARRIVAL), 0, sizeof(apultra_arrival) * ((nEndOffset - nStartOffset + 1) * NMATCHES_PER_ARRIVAL));
+
+ arrival[nStartOffset * NMATCHES_PER_ARRIVAL].from_slot = -1;
+ arrival[nStartOffset * NMATCHES_PER_ARRIVAL].rep_offset = *nCurRepMatchOffset;
+
+ for (i = (nStartOffset * NMATCHES_PER_ARRIVAL); i != ((nEndOffset+1) * NMATCHES_PER_ARRIVAL); i++) {
+ arrival[i].cost = 0x40000000;
+ }
+
+ for (i = nStartOffset; i != nEndOffset; i++) {
+ int m;
+
+ unsigned char *match1 = pCompressor->match1 + (i - nStartOffset);
+
+ if ((pInWindow[i] != 0 && (*match1) == 0) || (i == nStartOffset && (nBlockFlags & 1))) {
+ for (j = 0; j < nMatchesPerArrival && arrival[(i * NMATCHES_PER_ARRIVAL) + j].from_slot; j++) {
+ int nPrevCost = arrival[(i * NMATCHES_PER_ARRIVAL) + j].cost & 0x3fffffff;
+ int nCodingChoiceCost = nPrevCost + 8 /* literal */;
+
+ nCodingChoiceCost ++ /* Literal bit */;
+
+ apultra_arrival *pDestSlots = &arrival[(i + 1) * NMATCHES_PER_ARRIVAL];
+ if (nCodingChoiceCost <= pDestSlots[nMatchesPerArrival - 1].cost) {
+ int nScore = arrival[(i * NMATCHES_PER_ARRIVAL) + j].score + 1;
+ int exists = 0;
+
+ for (n = 0;
+ n < nMatchesPerArrival && pDestSlots[n].cost <= nCodingChoiceCost;
+ n++) {
+ if (pDestSlots[n].rep_offset == arrival[(i * NMATCHES_PER_ARRIVAL) + j].rep_offset) {
+ exists = 1;
+ break;
+ }
+ }
+
+ if (!exists) {
+ for (n = 0; n < nMatchesPerArrival; n++) {
+ apultra_arrival *pDestArrival = &pDestSlots[n];
+ if (nCodingChoiceCost < pDestArrival->cost ||
+ (nCodingChoiceCost == pDestArrival->cost && nScore < pDestArrival->score)) {
+ int z;
+
+ for (z = n; z < nMatchesPerArrival - 1; z++) {
+ if (pDestSlots[z].rep_offset == arrival[(i * NMATCHES_PER_ARRIVAL) + j].rep_offset)
+ break;
+ }
+
+ memmove(&pDestSlots[n + 1],
+ &pDestSlots[n],
+ sizeof(apultra_arrival) * (z - n));
+
+ pDestArrival->cost = nCodingChoiceCost;
+ pDestArrival->from_pos = i;
+ pDestArrival->from_slot = j + 1;
+ pDestArrival->follows_literal = 1;
+ pDestArrival->match_offset = 0;
+ pDestArrival->match_len = 0;
+ pDestArrival->score = nScore;
+ pDestArrival->rep_offset = arrival[(i * NMATCHES_PER_ARRIVAL) + j].rep_offset;
+ pDestArrival->rep_pos = arrival[(i * NMATCHES_PER_ARRIVAL) + j].rep_pos;
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ else {
+ int nShortOffset = (pInWindow[i] == 0) ? 0 : (*match1);
+
+ for (j = 0; j < nMatchesPerArrival && arrival[(i * NMATCHES_PER_ARRIVAL) + j].from_slot; j++) {
+ int nPrevCost = arrival[(i * NMATCHES_PER_ARRIVAL) + j].cost & 0x3fffffff;
+ int nCodingChoiceCost = nPrevCost + TOKEN_PREFIX_SIZE /* token */ /* the actual cost of the literals themselves accumulates up the chain */ + (4 + TOKEN_SIZE_4BIT_MATCH) /* command and offset cost; no length cost */;
+
+ apultra_arrival *pDestSlots = &arrival[(i + 1) * NMATCHES_PER_ARRIVAL];
+ if (nCodingChoiceCost <= pDestSlots[nMatchesPerArrival - 1].cost) {
+ int nScore = arrival[(i * NMATCHES_PER_ARRIVAL) + j].score + (nShortOffset ? 3 : 1);
+ int exists = 0;
+
+ for (n = 0;
+ n < nMatchesPerArrival && pDestSlots[n].cost <= nCodingChoiceCost;
+ n++) {
+ if (pDestSlots[n].rep_offset == arrival[(i * NMATCHES_PER_ARRIVAL) + j].rep_offset) {
+ exists = 1;
+ break;
+ }
+ }
+
+ if (!exists) {
+ for (n = 0; n < nMatchesPerArrival; n++) {
+ apultra_arrival *pDestArrival = &pDestSlots[n];
+
+ if (nCodingChoiceCost < pDestArrival->cost ||
+ (nCodingChoiceCost == pDestArrival->cost && nScore < pDestArrival->score)) {
+ int z;
+
+ for (z = n; z < nMatchesPerArrival - 1; z++) {
+ if (pDestSlots[z].rep_offset == arrival[(i * NMATCHES_PER_ARRIVAL) + j].rep_offset)
+ break;
+ }
+
+ memmove(&pDestSlots[n + 1],
+ &pDestSlots[n],
+ sizeof(apultra_arrival) * (z - n));
+
+ pDestArrival->cost = nCodingChoiceCost;
+ pDestArrival->from_pos = i;
+ pDestArrival->from_slot = j + 1;
+ pDestArrival->match_offset = nShortOffset;
+ pDestArrival->match_len = 1;
+ pDestArrival->follows_literal = 1;
+ pDestArrival->score = nScore;
+ pDestArrival->rep_offset = arrival[(i * NMATCHES_PER_ARRIVAL) + j].rep_offset;
+ pDestArrival->rep_pos = arrival[(i * NMATCHES_PER_ARRIVAL) + j].rep_pos;
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (i == nStartOffset && (nBlockFlags & 1)) continue;
+
+ apultra_match *match = pCompressor->match + ((i - nStartOffset) << MATCHES_PER_INDEX_SHIFT);
+ unsigned short *match_depth = pCompressor->match_depth + ((i - nStartOffset) << MATCHES_PER_INDEX_SHIFT);
+ const int nRepMatchOffsetCost = TOKEN_PREFIX_SIZE /* token */ + apultra_get_rep_offset_varlen_size();
+
+ int nMinRepLen[NMATCHES_PER_ARRIVAL];
+ memset(nMinRepLen, 0, NMATCHES_PER_ARRIVAL * sizeof(int));
+
+ for (m = 0; m < NMATCHES_PER_INDEX && match[m].length; m++) {
+ const int nOrigMatchLen = match[m].length;
+ const int nOrigMatchOffset = match[m].offset;
+ const unsigned int nOrigMatchDepth = match_depth[m] & 0x7fff;
+ const int nScorePenalty = 3 + ((match_depth[m] & 0x8000) >> 15);
+
+ for (unsigned int d = 0; d <= nOrigMatchDepth; d += (nOrigMatchDepth ? nOrigMatchDepth : 1)) {
+ int nStartingMatchLen, k;
+ int nMaxRepLen[NMATCHES_PER_ARRIVAL];
+ int nMinMatchLen[NMATCHES_PER_ARRIVAL];
+
+ const int nMatchOffset = nOrigMatchOffset - d;
+ int nMatchLen = nOrigMatchLen - d;
+
+ if ((i + nMatchLen) > nEndOffset)
+ nMatchLen = nEndOffset - i;
+
+ for (j = 0; j < nMatchesPerArrival && arrival[(i * NMATCHES_PER_ARRIVAL) + j].from_slot; j++) {
+ int nRepOffset = arrival[(i * NMATCHES_PER_ARRIVAL) + j].rep_offset;
+ int nCurMaxLen = 0;
+
+ if (arrival[(i * NMATCHES_PER_ARRIVAL) + j].follows_literal &&
+ nRepOffset) {
+
+ if (nMatchOffset == nRepOffset)
+ nCurMaxLen = nMatchLen;
+ else {
+ if (i > nRepOffset &&
+ (i - nRepOffset + nMatchLen) <= nEndOffset) {
+ nCurMaxLen = nMinRepLen[j];
+ while ((nCurMaxLen + 8) < nMatchLen && !memcmp(pInWindow + i - nRepOffset + nCurMaxLen, pInWindow + i - nMatchOffset + nCurMaxLen, 8))
+ nCurMaxLen += 8;
+ while ((nCurMaxLen + 4) < nMatchLen && !memcmp(pInWindow + i - nRepOffset + nCurMaxLen, pInWindow + i - nMatchOffset + nCurMaxLen, 4))
+ nCurMaxLen += 4;
+ while (nCurMaxLen < nMatchLen && pInWindow[i - nRepOffset + nCurMaxLen] == pInWindow[i - nMatchOffset + nCurMaxLen])
+ nCurMaxLen++;
+ nMinRepLen[j] = nCurMaxLen;
+ }
+ }
+ }
+
+ nMaxRepLen[j] = nCurMaxLen;
+
+ int nIsRepMatch = (nMatchOffset == nRepOffset &&
+ arrival[(i * NMATCHES_PER_ARRIVAL) + j].follows_literal) ? 1 : 0;
+
+ if (nIsRepMatch == 0) {
+ if (nMatchOffset < MINMATCH3_OFFSET)
+ nMinMatchLen[j] = 2;
+ else {
+ if (nMatchOffset < MINMATCH4_OFFSET)
+ nMinMatchLen[j] = 3;
+ else
+ nMinMatchLen[j] = 4;
+ }
+ }
+ else {
+ nMinMatchLen[j] = nMatchLen + 1;
+ }
+ }
+ while (j < NMATCHES_PER_ARRIVAL) {
+ nMaxRepLen[j] = 0;
+ nMinMatchLen[j++] = 0;
+ }
+
+ if (nInsertForwardReps)
+ apultra_insert_forward_match(pCompressor, pInWindow, i, nMatchOffset, nStartOffset, nEndOffset, nMatchesPerArrival, 0);
+
+ if (nMatchLen >= LEAVE_ALONE_MATCH_SIZE && i >= nMatchLen)
+ nStartingMatchLen = nMatchLen;
+ else
+ nStartingMatchLen = 2;
+
+ if (nStartingMatchLen <= nMatchLen) {
+ int nNoRepMatchOffsetCostForLit[2];
+
+ if (nStartingMatchLen <= 3) {
+ nNoRepMatchOffsetCostForLit[0] = TOKEN_PREFIX_SIZE /* token */ + apultra_get_offset_varlen_size(2, nMatchOffset, 0);
+ nNoRepMatchOffsetCostForLit[1] = TOKEN_PREFIX_SIZE /* token */ + apultra_get_offset_varlen_size(2, nMatchOffset, 1);
+ }
+ else {
+ nNoRepMatchOffsetCostForLit[0] = TOKEN_PREFIX_SIZE /* token */ + apultra_get_offset_varlen_size(4, nMatchOffset, 0);
+ nNoRepMatchOffsetCostForLit[1] = TOKEN_PREFIX_SIZE /* token */ + apultra_get_offset_varlen_size(4, nMatchOffset, 1);
+ }
+
+ for (k = nStartingMatchLen; k <= nMatchLen; k++) {
+ int nNoRepMatchMatchLenCost;
+ int nRepMatchMatchLenCost = apultra_get_gamma2_size(k);
+
+ if (k <= 3 && nMatchOffset < 128)
+ nNoRepMatchMatchLenCost = 0;
+ else {
+ if (nMatchOffset < 128 || nMatchOffset >= MINMATCH4_OFFSET)
+ nNoRepMatchMatchLenCost = apultra_get_gamma2_size(k - 2);
+ else if (nMatchOffset < MINMATCH3_OFFSET)
+ nNoRepMatchMatchLenCost = nRepMatchMatchLenCost;
+ else
+ nNoRepMatchMatchLenCost = apultra_get_gamma2_size(k - 1);
+ }
+
+ int nRepMatchCmdCost = nRepMatchOffsetCost + nRepMatchMatchLenCost;
+ apultra_arrival *pDestSlots = &arrival[(i + k) * NMATCHES_PER_ARRIVAL];
+
+ for (j = 0; j < nMatchesPerArrival && arrival[(i * NMATCHES_PER_ARRIVAL) + j].from_slot; j++) {
+ int nPrevCost = arrival[(i * NMATCHES_PER_ARRIVAL) + j].cost & 0x3fffffff;
+
+ int nRepCodingChoiceCost = nPrevCost /* the actual cost of the literals themselves accumulates up the chain */ + nRepMatchCmdCost;
+
+ if (nRepCodingChoiceCost <= pDestSlots[nMatchesPerArrival - 1].cost) {
+ if (k >= nMinMatchLen[j]) {
+ int nMatchCmdCost = nNoRepMatchMatchLenCost + nNoRepMatchOffsetCostForLit[arrival[(i * NMATCHES_PER_ARRIVAL) + j].follows_literal];
+ int nCodingChoiceCost = nPrevCost /* the actual cost of the literals themselves accumulates up the chain */ + nMatchCmdCost;
+
+ if (nCodingChoiceCost <= pDestSlots[nMatchesPerArrival - 1].cost) {
+ int exists = 0;
+
+ for (n = 0;
+ n < nMatchesPerArrival && pDestSlots[n].cost <= nCodingChoiceCost;
+ n++) {
+ if (pDestSlots[n].rep_offset == nMatchOffset) {
+ exists = 1;
+ break;
+ }
+ }
+
+ if (!exists) {
+ int nScore = arrival[(i * NMATCHES_PER_ARRIVAL) + j].score + nScorePenalty;
+
+ if (nMatchLen >= LCP_MAX)
+ nCodingChoiceCost -= 1;
+
+ for (n = 0; n < nMatchesPerArrival - 1; n++) {
+ apultra_arrival *pDestArrival = &pDestSlots[n];
+
+ if (nCodingChoiceCost < pDestArrival->cost ||
+ (nCodingChoiceCost == pDestArrival->cost && nScore < pDestArrival->score)) {
+ int z;
+
+ for (z = n; z < nMatchesPerArrival - 1; z++) {
+ if (pDestSlots[z].rep_offset == nMatchOffset)
+ break;
+ }
+
+ if (z == (nMatchesPerArrival - 1) && pDestSlots[z].from_slot && pDestSlots[z].match_len < 2)
+ z--;
+
+ memmove(&pDestSlots[n + 1],
+ &pDestSlots[n],
+ sizeof(apultra_arrival) * (z - n));
+
+ pDestArrival->cost = nCodingChoiceCost;
+ pDestArrival->from_pos = i;
+ pDestArrival->from_slot = j + 1;
+ pDestArrival->match_offset = nMatchOffset;
+ pDestArrival->match_len = k;
+ pDestArrival->follows_literal = 0;
+ pDestArrival->score = nScore;
+ pDestArrival->rep_offset = nMatchOffset;
+ pDestArrival->rep_pos = i;
+ nMinMatchLen[j] = k + 1;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ /* If this coding choice doesn't rep-match, see if we still get a match by using the current repmatch offset for this arrival. This can occur (and not have the
+ * matchfinder offer the offset in the first place, or have too many choices with the same cost to retain the repmatchable offset) when compressing regions
+ * of identical bytes, for instance. Checking for this provides a big compression win on some files. */
+
+ if (nMaxRepLen[j] >= k) {
+ int nRepOffset = arrival[(i * NMATCHES_PER_ARRIVAL) + j].rep_offset;
+
+ /* A match is possible at the rep offset; insert the extra coding choice. */
+
+ int exists = 0;
+
+ for (n = 0;
+ n < nMatchesPerArrival && pDestSlots[n].cost <= nRepCodingChoiceCost;
+ n++) {
+ if (pDestSlots[n].rep_offset == nRepOffset) {
+ exists = 1;
+ break;
+ }
+ }
+
+ if (!exists) {
+ int nScore = arrival[(i * NMATCHES_PER_ARRIVAL) + j].score + 2;
+
+ for (n = 0; n < nMatchesPerArrival; n++) {
+ apultra_arrival *pDestArrival = &pDestSlots[n];
+
+ if (nRepCodingChoiceCost < pDestArrival->cost ||
+ (nRepCodingChoiceCost == pDestArrival->cost && nScore < pDestArrival->score)) {
+ int z;
+
+ for (z = n; z < nMatchesPerArrival - 1; z++) {
+ if (pDestSlots[z].rep_offset == nRepOffset)
+ break;
+ }
+
+ memmove(&pDestSlots[n + 1],
+ &pDestSlots[n],
+ sizeof(apultra_arrival) * (z - n));
+
+ pDestArrival->cost = nRepCodingChoiceCost;
+ pDestArrival->from_pos = i;
+ pDestArrival->from_slot = j + 1;
+ pDestArrival->match_offset = nRepOffset;
+ pDestArrival->match_len = k;
+ pDestArrival->follows_literal = 0;
+ pDestArrival->score = nScore;
+ pDestArrival->rep_offset = nRepOffset;
+ pDestArrival->rep_pos = i;
+ break;
+ }
+ }
+ }
+ }
+ }
+ else {
+ break;
+ }
+ }
+
+ if (k == 3) {
+ nNoRepMatchOffsetCostForLit[0] = TOKEN_PREFIX_SIZE /* token */ + apultra_get_offset_varlen_size(4, nMatchOffset, 0);
+ nNoRepMatchOffsetCostForLit[1] = TOKEN_PREFIX_SIZE /* token */ + apultra_get_offset_varlen_size(4, nMatchOffset, 1);
+ }
+ }
+ }
+
+ if (nOrigMatchLen >= 512)
+ break;
+ }
+ }
+ }
+
+ apultra_arrival *end_arrival = &arrival[(i * NMATCHES_PER_ARRIVAL) + 0];
+ apultra_final_match *pBestMatch = pCompressor->best_match - nStartOffset;
+
+ int nEndCost = end_arrival->cost;
+
+ while (end_arrival->from_slot > 0 && end_arrival->from_pos >= 0 && (int)end_arrival->from_pos < nEndOffset) {
+ pBestMatch[end_arrival->from_pos].length = end_arrival->match_len;
+ pBestMatch[end_arrival->from_pos].offset = end_arrival->match_offset;
+
+ end_arrival = &arrival[(end_arrival->from_pos * NMATCHES_PER_ARRIVAL) + (end_arrival->from_slot-1)];
+ }
+}
+
+/**
+ * Attempt to replace matches by literals when it makes the final bitstream smaller, and merge large matches
+ *
+ * @param pCompressor compression context
+ * @param pInWindow pointer to input data window (previously compressed bytes + bytes to compress)
+ * @param pBestMatch optimal matches to evaluate and update
+ * @param nStartOffset current offset in input window (typically the number of previously compressed bytes)
+ * @param nEndOffset offset to end finding matches at (typically the size of the total input window in bytes
+ * @param nCurRepMatchOffset starting rep offset for this block
+ *
+ * @return non-zero if the number of tokens was reduced, 0 if it wasn't
+ */
+static int apultra_reduce_commands(apultra_compressor *pCompressor, const unsigned char *pInWindow, apultra_final_match *pBestMatch, const int nStartOffset, const int nEndOffset, const int *nCurRepMatchOffset) {
+ int i;
+ int nNumLiterals = 0;
+ int nRepMatchOffset = *nCurRepMatchOffset;
+ int nFollowsLiteral = 0;
+ int nDidReduce = 0;
+ int nLastMatchLen = 0;
+ const unsigned char *match1 = pCompressor->match1 - nStartOffset;
+
+ for (i = nStartOffset; i < nEndOffset; ) {
+ apultra_final_match *pMatch = pBestMatch + i;
+
+ if (pMatch->length <= 1 &&
+ (i + 1) < nEndOffset &&
+ pBestMatch[i + 1].length >= 2 &&
+ pBestMatch[i + 1].length < MAX_VARLEN &&
+ pBestMatch[i + 1].offset &&
+ i >= pBestMatch[i + 1].offset &&
+ (i + pBestMatch[i + 1].length + 1) <= nEndOffset &&
+ !memcmp(pInWindow + i - (pBestMatch[i + 1].offset), pInWindow + i, pBestMatch[i + 1].length + 1)) {
+ if ((pBestMatch[i + 1].offset < MINMATCH3_OFFSET || (pBestMatch[i + 1].length + 1) >= 3 || (pBestMatch[i + 1].offset == nRepMatchOffset && nFollowsLiteral)) &&
+ (pBestMatch[i + 1].offset < MINMATCH4_OFFSET || (pBestMatch[i + 1].length + 1) >= 4 || (pBestMatch[i + 1].offset == nRepMatchOffset && nFollowsLiteral))) {
+
+ int nCurPartialCommandSize = TOKEN_PREFIX_SIZE /* literal bit */ + ((pMatch->length == 1) ? (TOKEN_SIZE_4BIT_MATCH + 4) : 8 /* literal size */);
+ if (pBestMatch[i + 1].offset == nRepMatchOffset /* always follows a literal, the one at the current position */) {
+ nCurPartialCommandSize += apultra_get_rep_offset_varlen_size() + apultra_get_match_varlen_size(pBestMatch[i + 1].length, pBestMatch[i + 1].offset, 1);
+ }
+ else {
+ nCurPartialCommandSize += apultra_get_offset_varlen_size(pBestMatch[i + 1].length, pBestMatch[i + 1].offset, 1) + apultra_get_match_varlen_size(pBestMatch[i + 1].length, pBestMatch[i + 1].offset, 0);
+ }
+
+ int nReducedPartialCommandSize;
+ if (pBestMatch[i + 1].offset == nRepMatchOffset && nFollowsLiteral) {
+ nReducedPartialCommandSize = apultra_get_rep_offset_varlen_size() + apultra_get_match_varlen_size(pBestMatch[i + 1].length, pBestMatch[i + 1].offset, 1);
+ }
+ else {
+ nReducedPartialCommandSize = apultra_get_offset_varlen_size(pBestMatch[i + 1].length, pBestMatch[i + 1].offset, nFollowsLiteral) + apultra_get_match_varlen_size(pBestMatch[i + 1].length, pBestMatch[i + 1].offset, 0);
+ }
+
+ if (nReducedPartialCommandSize < nCurPartialCommandSize || (nFollowsLiteral == 0 && nLastMatchLen >= LCP_MAX)) {
+ /* Merge */
+ pBestMatch[i].length = pBestMatch[i + 1].length + 1;
+ pBestMatch[i].offset = pBestMatch[i + 1].offset;
+ pBestMatch[i + 1].length = 0;
+ pBestMatch[i + 1].offset = 0;
+ nDidReduce = 1;
+ continue;
+ }
+ }
+ }
+
+ if (pMatch->length >= MIN_MATCH_SIZE) {
+ if (pMatch->length < 8 && /* Don't waste time considering large matches, they will always win over literals */
+ (i + pMatch->length) < nEndOffset /* Don't consider the last match in the block, we can only reduce a match inbetween other tokens */) {
+ int nNextIndex = i + pMatch->length;
+ int nNextLiterals = 0;
+ int nNextFollowsLiteral = (pMatch->length >= 2) ? 0 : 1;
+ int nCannotEncode = 0;
+
+ while (nNextIndex < nEndOffset && pBestMatch[nNextIndex].length < 2) {
+ nNextLiterals++;
+ nNextIndex++;
+ nNextFollowsLiteral = 1;
+ }
+
+ if (nNextIndex < nEndOffset && pBestMatch[nNextIndex].length >= 2) {
+ if (pMatch->length >= 2 && nRepMatchOffset && nRepMatchOffset != pMatch->offset && pBestMatch[nNextIndex].offset && pMatch->offset != pBestMatch[nNextIndex].offset &&
+ nNextFollowsLiteral) {
+ /* Try to gain a match forward */
+ if (i >= pBestMatch[nNextIndex].offset && (i - pBestMatch[nNextIndex].offset + pMatch->length) <= nEndOffset) {
+ if ((pBestMatch[nNextIndex].offset < MINMATCH3_OFFSET || pMatch->length >= 3) &&
+ (pBestMatch[nNextIndex].offset < MINMATCH4_OFFSET || pMatch->length >= 4)) {
+ int nMaxLen = 0;
+ while (nMaxLen < pMatch->length && pInWindow[i - pBestMatch[nNextIndex].offset + nMaxLen] == pInWindow[i - pMatch->offset + nMaxLen])
+ nMaxLen++;
+
+ if (nMaxLen >= pMatch->length) {
+ /* Replace */
+ pMatch->offset = pBestMatch[nNextIndex].offset;
+ nDidReduce = 1;
+ }
+ else if (nMaxLen >= 2) {
+ if ((nFollowsLiteral && nRepMatchOffset == pBestMatch[nNextIndex].offset) ||
+ ((pBestMatch[nNextIndex].offset < MINMATCH3_OFFSET || nMaxLen >= 3) &&
+ (pBestMatch[nNextIndex].offset < MINMATCH4_OFFSET || nMaxLen >= 4))) {
+
+ int nPartialSizeBefore, nPartialSizeAfter;
+
+ nPartialSizeBefore = apultra_get_offset_varlen_size(pMatch->length, pMatch->offset, nFollowsLiteral);
+ nPartialSizeBefore += apultra_get_match_varlen_size(pMatch->length, pMatch->offset, 0);
+
+ nPartialSizeBefore += apultra_get_offset_varlen_size(pBestMatch[nNextIndex].length, pBestMatch[nNextIndex].offset, 1);
+ nPartialSizeBefore += apultra_get_match_varlen_size(pBestMatch[nNextIndex].length, pBestMatch[nNextIndex].offset, 0);
+
+ nPartialSizeAfter = apultra_get_offset_varlen_size(nMaxLen, pBestMatch[nNextIndex].offset, nFollowsLiteral);
+ nPartialSizeAfter += apultra_get_match_varlen_size(nMaxLen, pBestMatch[nNextIndex].offset, (nFollowsLiteral && nRepMatchOffset == pBestMatch[nNextIndex].offset) ? 1 : 0);
+
+ nPartialSizeAfter += apultra_get_rep_offset_varlen_size();
+ nPartialSizeAfter += apultra_get_match_varlen_size(pBestMatch[nNextIndex].length, pBestMatch[nNextIndex].offset, 1);
+ nPartialSizeAfter += apultra_get_literals_varlen_size(pMatch->length - nMaxLen);
+
+ for (int j = nMaxLen; j < pMatch->length; j++) {
+ if (pInWindow[i + j] == 0 || match1[i + j])
+ nPartialSizeAfter += TOKEN_SIZE_4BIT_MATCH + 4;
+ else
+ nPartialSizeAfter += 8;
+ }
+
+ if (nPartialSizeAfter < nPartialSizeBefore) {
+ /* We gain a repmatch that is shorter than the original match as this is the best we can do, so it is followed by extra literals, but
+ * we have calculated that this is shorter */
+
+ int nOrigLen = pMatch->length;
+ pMatch->offset = pBestMatch[nNextIndex].offset;
+ pMatch->length = nMaxLen;
+
+ for (int j = nMaxLen; j < nOrigLen; j++) {
+ pBestMatch[i + j].offset = match1[i + j];
+ pBestMatch[i + j].length = (pInWindow[i + j] && match1[i+j] == 0) ? 0 : 1;
+ }
+
+ nDidReduce = 1;
+ continue;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /* This command is a match, is followed by 'nNextLiterals' literals and then by another match. Calculate this command's current cost (excluding 'nNumLiterals' bytes) */
+
+ int nCurCommandSize = TOKEN_PREFIX_SIZE /* token */ + apultra_get_literals_varlen_size(nNumLiterals);
+ if (pMatch->offset == nRepMatchOffset && nFollowsLiteral && pMatch->length >= 2) {
+ nCurCommandSize += apultra_get_rep_offset_varlen_size() + apultra_get_match_varlen_size(pMatch->length, pMatch->offset, 1);
+ }
+ else {
+ nCurCommandSize += apultra_get_offset_varlen_size(pMatch->length, pMatch->offset, nFollowsLiteral) + apultra_get_match_varlen_size(pMatch->length, pMatch->offset, 0);
+ }
+
+ /* Calculate the next command's current cost */
+ int nNextCommandSize = TOKEN_PREFIX_SIZE /* token */ + apultra_get_literals_varlen_size(nNextLiterals) + (nNextLiterals << 3);
+ int nCurRepOffset = (pMatch->length >= 2) ? pMatch->offset : nRepMatchOffset;
+ if (pBestMatch[nNextIndex].offset == nCurRepOffset && nNextFollowsLiteral && pBestMatch[nNextIndex].length >= 2) {
+ nNextCommandSize += apultra_get_rep_offset_varlen_size() + apultra_get_match_varlen_size(pBestMatch[nNextIndex].length, pBestMatch[nNextIndex].offset, 1);
+ }
+ else {
+ nNextCommandSize += apultra_get_offset_varlen_size(pBestMatch[nNextIndex].length, pBestMatch[nNextIndex].offset, nNextFollowsLiteral) + apultra_get_match_varlen_size(pBestMatch[nNextIndex].length, pBestMatch[nNextIndex].offset, 0);
+ }
+
+ int nOriginalCombinedCommandSize = nCurCommandSize + nNextCommandSize;
+
+ /* Calculate the cost of replacing this match command by literals + the next command with the cost of encoding these literals (excluding 'nNumLiterals' bytes) */
+ int nReducedFollowsLiteral = (nNumLiterals + pMatch->length) ? 1 : 0;
+ int nReducedCommandSize = TOKEN_PREFIX_SIZE /* token */ + apultra_get_literals_varlen_size(nNumLiterals + pMatch->length + nNextLiterals) + (nNextLiterals << 3);
+
+ for (int j = 0; j < pMatch->length; j++) {
+ if (pInWindow[i + j] == 0 || match1[i + j])
+ nReducedCommandSize += TOKEN_SIZE_4BIT_MATCH + 4;
+ else
+ nReducedCommandSize += 8;
+ }
+
+ if (pBestMatch[nNextIndex].offset == nRepMatchOffset && nReducedFollowsLiteral && pBestMatch[nNextIndex].length >= 2) {
+ nReducedCommandSize += apultra_get_rep_offset_varlen_size() + apultra_get_match_varlen_size(pBestMatch[nNextIndex].length, pBestMatch[nNextIndex].offset, 1);
+ }
+ else {
+ if ((pBestMatch[nNextIndex].length < 3 && pBestMatch[nNextIndex].offset >= MINMATCH3_OFFSET) ||
+ (pBestMatch[nNextIndex].length < 4 && pBestMatch[nNextIndex].offset >= MINMATCH4_OFFSET)) {
+ /* This match length can only be encoded with a rep-match */
+ nCannotEncode = 1;
+ }
+ else {
+ nReducedCommandSize += apultra_get_offset_varlen_size(pBestMatch[nNextIndex].length, pBestMatch[nNextIndex].offset, nReducedFollowsLiteral) + apultra_get_match_varlen_size(pBestMatch[nNextIndex].length, pBestMatch[nNextIndex].offset, 0);
+ }
+ }
+
+ if (!nCannotEncode && nOriginalCombinedCommandSize > nReducedCommandSize) {
+ /* Reduce */
+ int nMatchLen = pMatch->length;
+ int j;
+
+ for (j = 0; j < nMatchLen; j++) {
+ pBestMatch[i + j].offset = match1[i + j];
+ pBestMatch[i + j].length = (pInWindow[i + j] && match1[i + j] == 0) ? 0 : 1;
+ }
+
+ nDidReduce = 1;
+ continue;
+ }
+ }
+ }
+
+ if ((i + pMatch->length) < nEndOffset && pMatch->offset > 0 && pMatch->length >= 2 &&
+ pBestMatch[i + pMatch->length].offset > 0 &&
+ pBestMatch[i + pMatch->length].length >= 2 &&
+ (pMatch->length + pBestMatch[i + pMatch->length].length) >= LEAVE_ALONE_MATCH_SIZE &&
+ (pMatch->length + pBestMatch[i + pMatch->length].length) <= MAX_VARLEN &&
+ (i + pMatch->length) > pMatch->offset &&
+ (i + pMatch->length) > pBestMatch[i + pMatch->length].offset &&
+ (i + pMatch->length + pBestMatch[i + pMatch->length].length) <= nEndOffset &&
+ !memcmp(pInWindow + i + pMatch->length - pMatch->offset,
+ pInWindow + i + pMatch->length - pBestMatch[i + pMatch->length].offset,
+ pBestMatch[i + pMatch->length].length)) {
+ int nMatchLen = pMatch->length;
+
+ /* Join large matches */
+
+ int nNextIndex = i + pMatch->length + pBestMatch[i + pMatch->length].length;
+ int nNextFollowsLiteral = 0;
+ int nCannotEncode = 0;
+
+ while (nNextIndex < nEndOffset && pBestMatch[nNextIndex].length < 2) {
+ nNextIndex++;
+ nNextFollowsLiteral = 1;
+ }
+
+ if (nNextIndex < nEndOffset && nNextFollowsLiteral && pBestMatch[nNextIndex].length >= 2 &&
+ pBestMatch[nNextIndex].offset == pBestMatch[i + pMatch->length].offset) {
+ if ((pBestMatch[nNextIndex].offset >= MINMATCH3_OFFSET && pBestMatch[nNextIndex].length < 3) ||
+ (pBestMatch[nNextIndex].offset >= MINMATCH4_OFFSET && pBestMatch[nNextIndex].length < 4)) {
+ nCannotEncode = 1;
+ }
+ }
+
+ if (!nCannotEncode) {
+ pMatch->length += pBestMatch[i + nMatchLen].length;
+ pBestMatch[i + nMatchLen].offset = 0;
+ pBestMatch[i + nMatchLen].length = -1;
+ nDidReduce = 1;
+ continue;
+ }
+ }
+
+ if (pMatch->offset == nRepMatchOffset && nFollowsLiteral && pMatch->length >= 2) {
+ /* Rep-match */
+ nRepMatchOffset = pMatch->offset;
+ nFollowsLiteral = 0;
+ nLastMatchLen = pMatch->length;
+ }
+ else {
+ if (pMatch->length == 1 && pMatch->offset < 16) {
+ /* 4 bits offset */
+ nFollowsLiteral = 1;
+ nLastMatchLen = 0;
+ }
+ else if (pMatch->length <= 3 && pMatch->offset < 128) {
+ /* 7 bits offset + 1 bit length */
+ nRepMatchOffset = pMatch->offset;
+ nFollowsLiteral = 0;
+ nLastMatchLen = pMatch->length;
+ }
+ else {
+ /* 8+n bits offset */
+ nRepMatchOffset = pMatch->offset;
+ nFollowsLiteral = 0;
+ nLastMatchLen = pMatch->length;
+ }
+ }
+
+ i += pMatch->length;
+ nNumLiterals = 0;
+ }
+ else {
+ nNumLiterals++;
+ i++;
+ nFollowsLiteral = 1;
+ nLastMatchLen = 0;
+ }
+ }
+
+ return nDidReduce;
+}
+
+/**
+ * Emit a block of compressed data
+ *
+ * @param pCompressor compression context
+ * @param pBestMatch optimal matches to emit
+ * @param pInWindow pointer to input data window (previously compressed bytes + bytes to compress)
+ * @param nStartOffset current offset in input window (typically the number of previously compressed bytes)
+ * @param nEndOffset offset to end finding matches at (typically the size of the total input window in bytes
+ * @param pOutData pointer to output buffer
+ * @param nMaxOutDataSize maximum size of output buffer, in bytes
+ * @param nCurBitsOffset write index into output buffer, of current byte being filled with bits
+ * @param nCurBitMask bit shifter
+ * @param nFollowsLiteral non-zero if the next command to be issued follows a literal, 0 if not
+ * @param nCurRepMatchOffset starting rep offset for this block, updated after the block is compressed successfully
+ * @param nBlockFlags bit 0: 1 for first block, 0 otherwise; bit 1: 1 for last block, 0 otherwise
+ *
+ * @return size of compressed data in output buffer, or -1 if the data is uncompressible
+ */
+static int apultra_write_block(apultra_compressor *pCompressor, apultra_final_match *pBestMatch, const unsigned char *pInWindow, const int nStartOffset, const int nEndOffset, unsigned char *pOutData, int nOutOffset, const int nMaxOutDataSize, int *nCurBitsOffset, int *nCurBitMask, int *nFollowsLiteral, int *nCurRepMatchOffset, const int nBlockFlags) {
+ int i, j;
+ int nInFirstLiteralOffset = 0;
+ int nRepMatchOffset = *nCurRepMatchOffset;
+ int nSingleBitBufferIdx = 0;
+ int nGammaBitBufferIdx = (pCompressor->flags & APULTRA_FLAG_ENHANCED) ? 1 : 0;
+ int nNibblesBitBufferIdx = (pCompressor->flags & APULTRA_FLAG_ENHANCED) ? 2 : 0;
+
+ if (nBlockFlags & 1) {
+ if (nOutOffset < 0 || nOutOffset >= nMaxOutDataSize)
+ return -1;
+ pOutData[nOutOffset++] = pInWindow[nStartOffset];
+ *nFollowsLiteral = 1;
+ }
+
+ for (i = nStartOffset + ((nBlockFlags & 1) ? 1 : 0); i < nEndOffset; ) {
+ const apultra_final_match *pMatch = pBestMatch + i;
+
+ if (pMatch->length >= MIN_MATCH_SIZE) {
+ int nMatchOffset = pMatch->offset;
+ int nMatchLen = pMatch->length;
+ int nTokenOffsetMode;
+ int nOffsetSize;
+
+ if (nMatchOffset == nRepMatchOffset && *nFollowsLiteral && nMatchLen >= 2) {
+ /* Rep-match */
+ nTokenOffsetMode = 3;
+ nOffsetSize = TOKEN_SIZE_LARGE_MATCH + apultra_get_gamma2_size(2);
+ }
+ else {
+ if (nMatchLen == 1 && nMatchOffset < 16) {
+ /* 4 bits offset */
+ nTokenOffsetMode = 2;
+ nOffsetSize = 4 + TOKEN_SIZE_4BIT_MATCH;
+ }
+ else if (nMatchLen <= 3 && nMatchOffset < 128) {
+ /* 7 bits offset + 1 bit length */
+ nTokenOffsetMode = 1;
+ nOffsetSize = 8 + TOKEN_SIZE_7BIT_MATCH;
+ }
+ else {
+ /* 8+n bits offset */
+ nTokenOffsetMode = 0;
+ if (*nFollowsLiteral)
+ nOffsetSize = 8 + TOKEN_SIZE_LARGE_MATCH + apultra_get_gamma2_size((nMatchOffset >> 8) + 3);
+ else
+ nOffsetSize = 8 + TOKEN_SIZE_LARGE_MATCH + apultra_get_gamma2_size((nMatchOffset >> 8) + 2);
+ }
+ }
+
+ int nCommandSize = TOKEN_PREFIX_SIZE /* token */ + nOffsetSize /* match offset */ + apultra_get_match_varlen_size(nMatchLen, nMatchOffset, (nTokenOffsetMode == 3) ? 1 : 0);
+
+ if ((nOutOffset + ((nCommandSize + 7) >> 3)) > nMaxOutDataSize)
+ return -1;
+ if (nMatchOffset < ((nMatchLen == 1) ? 0 : MIN_OFFSET) || nMatchOffset > MAX_OFFSET)
+ return -1;
+
+ int nActualTokenOffsetMode = nTokenOffsetMode;
+ if (nActualTokenOffsetMode == 3)
+ nActualTokenOffsetMode = 0;
+ nOutOffset = apultra_write_bit(pOutData, nOutOffset, nMaxOutDataSize, 1 /* match */, nCurBitsOffset, nCurBitMask, nSingleBitBufferIdx);
+ for (j = _token_size[nActualTokenOffsetMode] - 1; j >= 0; j--)
+ nOutOffset = apultra_write_bit(pOutData, nOutOffset, nMaxOutDataSize, (_token_code[nActualTokenOffsetMode] & (1 << j)) ? 1 : 0, nCurBitsOffset, nCurBitMask, nSingleBitBufferIdx);
+
+ int nEmitMatchLength = 0;
+
+ if (nTokenOffsetMode == 0) {
+ /* 8+n bits offset */
+
+ if (nOutOffset < 0 || nOutOffset >= nMaxOutDataSize)
+ return -1;
+ if (*nFollowsLiteral)
+ nOutOffset = apultra_write_gamma2_value(pOutData, nOutOffset, nMaxOutDataSize, (nMatchOffset >> 8) + 3, nCurBitsOffset, nCurBitMask, nGammaBitBufferIdx);
+ else
+ nOutOffset = apultra_write_gamma2_value(pOutData, nOutOffset, nMaxOutDataSize, (nMatchOffset >> 8) + 2, nCurBitsOffset, nCurBitMask, nGammaBitBufferIdx);
+ pOutData[nOutOffset++] = nMatchOffset & 0xff;
+
+ if (nMatchOffset < 128 && nMatchLen <= 3) {
+ /* A shorter match must have been encoded as a 4 bits offset or a 7 bits offset + 1 bit match length command */
+ return -1;
+ }
+
+ nEmitMatchLength = 1;
+ *nFollowsLiteral = 0;
+ nRepMatchOffset = nMatchOffset;
+
+ pCompressor->stats.num_variable_matches++;
+ }
+ else if (nTokenOffsetMode == 1) {
+ /* 7 bits offset + 1 bit length */
+
+ if (nOutOffset < 0 || nOutOffset >= nMaxOutDataSize)
+ return -1;
+ pOutData[nOutOffset++] = ((nMatchOffset) & 0x7f) << 1 | (nMatchLen - 2);
+
+ *nFollowsLiteral = 0;
+ nRepMatchOffset = nMatchOffset;
+
+ pCompressor->stats.num_7bit_matches++;
+ }
+ else if (nTokenOffsetMode == 2) {
+ /* 4 bits offset */
+
+ nOutOffset = apultra_write_bit(pOutData, nOutOffset, nMaxOutDataSize, (nMatchOffset & 0x08) ? 1 : 0, nCurBitsOffset, nCurBitMask, nNibblesBitBufferIdx);
+ nOutOffset = apultra_write_bit(pOutData, nOutOffset, nMaxOutDataSize, (nMatchOffset & 0x04) ? 1 : 0, nCurBitsOffset, nCurBitMask, nNibblesBitBufferIdx);
+ nOutOffset = apultra_write_bit(pOutData, nOutOffset, nMaxOutDataSize, (nMatchOffset & 0x02) ? 1 : 0, nCurBitsOffset, nCurBitMask, nNibblesBitBufferIdx);
+ nOutOffset = apultra_write_bit(pOutData, nOutOffset, nMaxOutDataSize, (nMatchOffset & 0x01) ? 1 : 0, nCurBitsOffset, nCurBitMask, nNibblesBitBufferIdx);
+ if (nOutOffset < 0) return -1;
+
+ *nFollowsLiteral = 1;
+
+ pCompressor->stats.num_4bit_matches++;
+ }
+ else {
+ /* rep match */
+ nOutOffset = apultra_write_gamma2_value(pOutData, nOutOffset, nMaxOutDataSize, 2, nCurBitsOffset, nCurBitMask, nGammaBitBufferIdx);
+
+ nEmitMatchLength = 1;
+ *nFollowsLiteral = 0;
+
+ pCompressor->stats.num_rep_matches++;
+ }
+
+ if (nEmitMatchLength) {
+ /* The match length isn't encoded in the command, emit elias gamma value */
+ nOutOffset = apultra_write_match_varlen(pOutData, nOutOffset, nMaxOutDataSize, nCurBitsOffset, nCurBitMask, nGammaBitBufferIdx, nMatchLen, nMatchOffset, (nTokenOffsetMode == 3) ? 1 : 0);
+ if (nOutOffset < 0) return -1;
+ }
+
+ if (nMatchOffset < pCompressor->stats.min_offset || pCompressor->stats.min_offset == -1)
+ pCompressor->stats.min_offset = nMatchOffset;
+ if (nMatchOffset > pCompressor->stats.max_offset)
+ pCompressor->stats.max_offset = nMatchOffset;
+ pCompressor->stats.total_offsets += (long long)nMatchOffset;
+
+ if (nMatchLen < pCompressor->stats.min_match_len || pCompressor->stats.min_match_len == -1)
+ pCompressor->stats.min_match_len = nMatchLen;
+ if (nMatchLen > pCompressor->stats.max_match_len)
+ pCompressor->stats.max_match_len = nMatchLen;
+ pCompressor->stats.total_match_lens += nMatchLen;
+ pCompressor->stats.match_divisor++;
+
+ if (nMatchOffset == 1) {
+ if (nMatchLen < pCompressor->stats.min_rle1_len || pCompressor->stats.min_rle1_len == -1)
+ pCompressor->stats.min_rle1_len = nMatchLen;
+ if (nMatchLen > pCompressor->stats.max_rle1_len)
+ pCompressor->stats.max_rle1_len = nMatchLen;
+ pCompressor->stats.total_rle1_lens += nMatchLen;
+ pCompressor->stats.rle1_divisor++;
+ }
+ else if (nMatchOffset == 2) {
+ if (nMatchLen < pCompressor->stats.min_rle2_len || pCompressor->stats.min_rle2_len == -1)
+ pCompressor->stats.min_rle2_len = nMatchLen;
+ if (nMatchLen > pCompressor->stats.max_rle2_len)
+ pCompressor->stats.max_rle2_len = nMatchLen;
+ pCompressor->stats.total_rle2_lens += nMatchLen;
+ pCompressor->stats.rle2_divisor++;
+ }
+
+ i += nMatchLen;
+
+ pCompressor->stats.commands_divisor++;
+ }
+ else {
+ nOutOffset = apultra_write_bit(pOutData, nOutOffset, nMaxOutDataSize, 0 /* literal */, nCurBitsOffset, nCurBitMask, nSingleBitBufferIdx);
+
+ if (nOutOffset < 0 || nOutOffset >= nMaxOutDataSize)
+ return -1;
+ pOutData[nOutOffset++] = pInWindow[i];
+
+ pCompressor->stats.num_literals++;
+ pCompressor->stats.commands_divisor++;
+ i++;
+ *nFollowsLiteral = 1;
+ }
+ }
+
+ if (nBlockFlags & 2) {
+ nOutOffset = apultra_write_bit(pOutData, nOutOffset, nMaxOutDataSize, 1 /* match */, nCurBitsOffset, nCurBitMask, nSingleBitBufferIdx);
+
+ /* 8 bits offset */
+
+ for (j = TOKEN_SIZE_7BIT_MATCH - 1; j >= 0; j--)
+ nOutOffset = apultra_write_bit(pOutData, nOutOffset, nMaxOutDataSize, (_token_code[1] & (1 << j)) ? 1 : 0, nCurBitsOffset, nCurBitMask, nSingleBitBufferIdx);
+
+ if (nOutOffset < 0 || nOutOffset >= nMaxOutDataSize)
+ return -1;
+ pOutData[nOutOffset++] = 0x00; /* Offset: EOD */
+ pCompressor->stats.commands_divisor++;
+ }
+
+ *nCurRepMatchOffset = nRepMatchOffset;
+ return nOutOffset;
+}
+
+/**
+ * Emit raw block of uncompressible data
+ *
+ * @param pCompressor compression context
+ * @param pInWindow pointer to input data window (previously compressed bytes + bytes to compress)
+ * @param nStartOffset current offset in input window (typically the number of previously compressed bytes)
+ * @param nEndOffset offset to end finding matches at (typically the size of the total input window in bytes
+ * @param pOutData pointer to output buffer
+ * @param nMaxOutDataSize maximum size of output buffer, in bytes
+ * @param nBlockFlags bit 0: 1 for first block, 0 otherwise; bit 1: 1 for last block, 0 otherwise
+ * @param nCurBitsOffset write index into output buffer, of current byte being filled with bits
+ * @param nCurBitMask bit shifter
+ * @param nFollowsLiteral non-zero if the next command to be issued follows a literal, 0 if not
+ *
+ * @return size of compressed data in output buffer, or -1 if the data is uncompressible
+ */
+static int apultra_write_raw_uncompressed_block_v3(apultra_compressor *pCompressor, const unsigned char *pInWindow, const int nStartOffset, const int nEndOffset, unsigned char *pOutData, int nOutOffset, const int nMaxOutDataSize, const int nBlockFlags, int *nCurBitsOffset, int *nCurBitMask, int *nFollowsLiteral) {
+ int nNumLiterals = nEndOffset - nStartOffset;
+ int j;
+ int nInOffset = nStartOffset;
+ int nSingleBitBufferIdx = 0;
+
+ int nCommandSize = apultra_get_literals_varlen_size(nNumLiterals) + (nNumLiterals << 3) + TOKEN_PREFIX_SIZE + TOKEN_SIZE_7BIT_MATCH /* token */ + 8 /* match offset */;
+ if ((nOutOffset + ((nCommandSize + 7) >> 3)) > nMaxOutDataSize)
+ return -1;
+
+ pCompressor->stats.commands_divisor = 0;
+ *nFollowsLiteral = 1;
+
+ for (j = 0; j < nNumLiterals; j++) {
+ nOutOffset = apultra_write_bit(pOutData, nOutOffset, nMaxOutDataSize, 0 /* literal */, nCurBitsOffset, nCurBitMask, nSingleBitBufferIdx);
+ pOutData[nOutOffset++] = pInWindow[nInOffset + j];
+ }
+
+ nNumLiterals = 0;
+
+ nOutOffset = apultra_write_bit(pOutData, nOutOffset, nMaxOutDataSize, 1 /* match */, nCurBitsOffset, nCurBitMask, nSingleBitBufferIdx);
+
+ /* 8 bits offset */
+ for (j = TOKEN_SIZE_7BIT_MATCH - 1; j >= 0; j--)
+ nOutOffset = apultra_write_bit(pOutData, nOutOffset, nMaxOutDataSize, (_token_code[1] & (1 << j)) ? 1 : 0, nCurBitsOffset, nCurBitMask, nSingleBitBufferIdx);
+
+ if (nOutOffset < 0 || nOutOffset >= nMaxOutDataSize)
+ return -1;
+ pOutData[nOutOffset++] = 0x00; /* Offset: EOD */
+
+ pCompressor->stats.commands_divisor++;
+
+ return nOutOffset;
+}
+
+/**
+ * Select the most optimal matches, reduce the token count if possible, and then emit a block of compressed data
+ *
+ * @param pCompressor compression context
+ * @param pInWindow pointer to input data window (previously compressed bytes + bytes to compress)
+ * @param nPreviousBlockSize number of previously compressed bytes (or 0 for none)
+ * @param nInDataSize number of input bytes to compress
+ * @param pOutData pointer to output buffer
+ * @param nMaxOutDataSize maximum size of output buffer, in bytes
+ * @param nCurBitsOffset write index into output buffer, of current byte being filled with bits
+ * @param nCurBitMask bit shifter
+ * @param nCurFollowsLiteral non-zero if the next command to be issued follows a literal, 0 if not
+ * @param nCurRepMatchOffset starting rep offset for this block, updated after the block is compressed successfully
+ * @param nBlockFlags bit 0: 1 for first block, 0 otherwise; bit 1: 1 for last block, 0 otherwise
+ *
+ * @return size of compressed data in output buffer, or -1 if the data is uncompressible
+ */
+static int apultra_optimize_and_write_block(apultra_compressor *pCompressor, const unsigned char *pInWindow, const int nPreviousBlockSize, const int nInDataSize, unsigned char *pOutData, const int nMaxOutDataSize, int *nCurBitsOffset, int *nCurBitMask, int *nCurFollowsLiteral, int *nCurRepMatchOffset, const int nBlockFlags) {
+ int nResult;
+ int nOutOffset = 0;
+ const int nMatchesPerArrival = ((nBlockFlags & 3) == 3) ? NMATCHES_PER_ARRIVAL : NMATCHES_PER_ARRIVAL_SMALL;
+
+ memset(pCompressor->best_match, 0, pCompressor->block_size * sizeof(apultra_final_match));
+ apultra_optimize_forward(pCompressor, pInWindow, nPreviousBlockSize, nPreviousBlockSize + nInDataSize, 1 /* nInsertForwardReps */, nCurRepMatchOffset, nBlockFlags, nMatchesPerArrival);
+
+ /* Pick optimal matches */
+ apultra_optimize_forward(pCompressor, pInWindow, nPreviousBlockSize, nPreviousBlockSize + nInDataSize, 0 /* nInsertForwardReps */, nCurRepMatchOffset, nBlockFlags, nMatchesPerArrival);
+
+ /* Apply reduction and merge pass */
+ int nDidReduce;
+ int nPasses = 0;
+ do {
+ nDidReduce = apultra_reduce_commands(pCompressor, pInWindow, pCompressor->best_match - nPreviousBlockSize, nPreviousBlockSize, nPreviousBlockSize + nInDataSize, nCurRepMatchOffset);
+ nPasses++;
+ } while (nDidReduce && nPasses < 20);
+
+ /* Write compressed block */
+
+ nResult = apultra_write_block(pCompressor, pCompressor->best_match - nPreviousBlockSize, pInWindow, nPreviousBlockSize, nPreviousBlockSize + nInDataSize, pOutData, nOutOffset, nMaxOutDataSize, nCurBitsOffset, nCurBitMask, nCurFollowsLiteral, nCurRepMatchOffset, nBlockFlags);
+ if (nResult < 0) {
+ /* Try to write block as all literals */
+ *nCurRepMatchOffset = 0;
+ nResult = apultra_write_raw_uncompressed_block_v3(pCompressor, pInWindow, nPreviousBlockSize, nPreviousBlockSize + nInDataSize, pOutData, nOutOffset, nMaxOutDataSize, nBlockFlags, nCurBitsOffset, nCurBitMask, nCurFollowsLiteral);
+ }
+
+ return nResult;
+}
+
+/* Forward declaration */
+static void apultra_compressor_destroy(apultra_compressor *pCompressor);
+
+/**
+ * Initialize compression context
+ *
+ * @param pCompressor compression context to initialize
+ * @param nBlockSize maximum size of input data (bytes to compress only)
+ * @param nMaxWindowSize maximum size of input data window (previously compressed bytes + bytes to compress)
+ * @param nFlags compression flags
+ *
+ * @return 0 for success, non-zero for failure
+ */
+static int apultra_compressor_init(apultra_compressor *pCompressor, const int nBlockSize, const int nMaxWindowSize, const int nFlags) {
+ int nResult;
+
+ nResult = divsufsort_init(&pCompressor->divsufsort_context);
+ pCompressor->intervals = NULL;
+ pCompressor->pos_data = NULL;
+ pCompressor->open_intervals = NULL;
+ pCompressor->match = NULL;
+ pCompressor->match_depth = NULL;
+ pCompressor->match1 = NULL;
+ pCompressor->best_match = NULL;
+ pCompressor->arrival = NULL;
+ pCompressor->flags = nFlags;
+ pCompressor->block_size = nBlockSize;
+
+ memset(&pCompressor->stats, 0, sizeof(pCompressor->stats));
+ pCompressor->stats.min_match_len = -1;
+ pCompressor->stats.min_offset = -1;
+ pCompressor->stats.min_rle1_len = -1;
+ pCompressor->stats.min_rle2_len = -1;
+
+ if (!nResult) {
+ pCompressor->intervals = (unsigned long long *)malloc(nMaxWindowSize * sizeof(unsigned long long));
+
+ if (pCompressor->intervals) {
+ pCompressor->pos_data = (unsigned long long *)malloc(nMaxWindowSize * sizeof(unsigned long long));
+
+ if (pCompressor->pos_data) {
+ pCompressor->open_intervals = (unsigned long long *)malloc((LCP_AND_TAG_MAX + 1) * sizeof(unsigned long long));
+
+ if (pCompressor->open_intervals) {
+ pCompressor->arrival = (apultra_arrival *)malloc((nBlockSize + 1) * NMATCHES_PER_ARRIVAL * sizeof(apultra_arrival));
+
+ if (pCompressor->arrival) {
+ pCompressor->best_match = (apultra_final_match *)malloc(nBlockSize * sizeof(apultra_final_match));
+
+ if (pCompressor->best_match) {
+ pCompressor->match = (apultra_match *)malloc(nBlockSize * NMATCHES_PER_INDEX * sizeof(apultra_match));
+ if (pCompressor->match) {
+ pCompressor->match_depth = (unsigned short *)malloc(nBlockSize * NMATCHES_PER_INDEX * sizeof(unsigned short));
+ if (pCompressor->match_depth) {
+ pCompressor->match1 = (unsigned char *)malloc(nBlockSize * sizeof(unsigned char));
+ if (pCompressor->match1)
+ return 0;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ apultra_compressor_destroy(pCompressor);
+ return 100;
+}
+
+/**
+ * Clean up compression context and free up any associated resources
+ *
+ * @param pCompressor compression context to clean up
+ */
+static void apultra_compressor_destroy(apultra_compressor *pCompressor) {
+ divsufsort_destroy(&pCompressor->divsufsort_context);
+
+ if (pCompressor->match1) {
+ free(pCompressor->match1);
+ pCompressor->match1 = NULL;
+ }
+
+ if (pCompressor->match_depth) {
+ free(pCompressor->match_depth);
+ pCompressor->match_depth = NULL;
+ }
+
+ if (pCompressor->match) {
+ free(pCompressor->match);
+ pCompressor->match = NULL;
+ }
+
+ if (pCompressor->arrival) {
+ free(pCompressor->arrival);
+ pCompressor->arrival = NULL;
+ }
+
+ if (pCompressor->best_match) {
+ free(pCompressor->best_match);
+ pCompressor->best_match = NULL;
+ }
+
+ if (pCompressor->open_intervals) {
+ free(pCompressor->open_intervals);
+ pCompressor->open_intervals = NULL;
+ }
+
+ if (pCompressor->pos_data) {
+ free(pCompressor->pos_data);
+ pCompressor->pos_data = NULL;
+ }
+
+ if (pCompressor->intervals) {
+ free(pCompressor->intervals);
+ pCompressor->intervals = NULL;
+ }
+}
+
+/**
+ * Compress one block of data
+ *
+ * @param pCompressor compression context
+ * @param pInWindow pointer to input data window (previously compressed bytes + bytes to compress)
+ * @param nPreviousBlockSize number of previously compressed bytes (or 0 for none)
+ * @param nInDataSize number of input bytes to compress
+ * @param pOutData pointer to output buffer
+ * @param nMaxOutDataSize maximum size of output buffer, in bytes
+ * @param nCurBitsOffset write index into output buffer, of current byte being filled with bits
+ * @param nCurBitMask bit shifter
+ * @param nCurFollowsLiteral non-zero if the next command to be issued follows a literal, 0 if not
+ * @param nCurRepMatchOffset starting rep offset for this block, updated after the block is compressed successfully
+ * @param nBlockFlags bit 0: 1 for first block, 0 otherwise; bit 1: 1 for last block, 0 otherwise
+ *
+ * @return size of compressed data in output buffer, or -1 if the data is uncompressible
+ */
+static int apultra_compressor_shrink_block(apultra_compressor *pCompressor, const unsigned char *pInWindow, const int nPreviousBlockSize, const int nInDataSize, unsigned char *pOutData, const int nMaxOutDataSize, int *nCurBitsOffset, int *nCurBitMask, int *nCurFollowsLiteral, int *nCurRepMatchOffset, const int nBlockFlags) {
+ int nCompressedSize;
+
+ if (apultra_build_suffix_array(pCompressor, pInWindow, nPreviousBlockSize + nInDataSize))
+ nCompressedSize = -1;
+ else {
+ if (nPreviousBlockSize) {
+ apultra_skip_matches(pCompressor, 0, nPreviousBlockSize);
+ }
+ apultra_find_all_matches(pCompressor, NMATCHES_PER_INDEX, nPreviousBlockSize, nPreviousBlockSize + nInDataSize, nBlockFlags);
+
+ nCompressedSize = apultra_optimize_and_write_block(pCompressor, pInWindow, nPreviousBlockSize, nInDataSize, pOutData, nMaxOutDataSize, nCurBitsOffset, nCurBitMask, nCurFollowsLiteral, nCurRepMatchOffset, nBlockFlags);
+ }
+
+ return nCompressedSize;
+}
+
+/**
+ * Get maximum compressed size of input(source) data
+ *
+ * @param nInputSize input(source) size in bytes
+ *
+ * @return maximum compressed size
+ */
+size_t apultra_get_max_compressed_size(size_t nInputSize) {
+ return ((nInputSize * 9 /* literals + literal bits */ + 1 /* match bit */ + 2 /* 7+1 command bits */ + 8 /* EOD offset bits */) + 7) >> 3;
+}
+
+/**
+ * Compress memory
+ *
+ * @param pInputData pointer to input(source) data to compress
+ * @param pOutBuffer buffer for compressed data
+ * @param nInputSize input(source) size in bytes
+ * @param nMaxOutBufferSize maximum capacity of compression buffer
+ * @param nFlags compression flags (a bitmask of APULTRA_FLAG_xxx, or 0)
+ * @param progress progress function, called after compressing each block, or NULL for none
+ * @param pStats pointer to compression stats that are filled if this function is successful, or NULL
+ * @param nMaxWindowSize maximum window size to use (0 for default)
+ *
+ * @return actual compressed size, or -1 for error
+ */
+size_t apultra_compress(const unsigned char *pInputData, unsigned char *pOutBuffer, size_t nInputSize, size_t nMaxOutBufferSize,
+ const unsigned int nFlags, size_t nMaxWindowSize, void(*progress)(long long nOriginalSize, long long nCompressedSize), apultra_stats *pStats) {
+ apultra_compressor compressor;
+ size_t nOriginalSize = 0;
+ size_t nCompressedSize = 0L;
+ int nResult;
+ int nError = 0;
+ const int nDefaultBlockSize = (nInputSize < BLOCK_SIZE) ? ((nInputSize < 1024) ? 1024 : (int)nInputSize) : BLOCK_SIZE;
+ const int nBlockSize = nMaxWindowSize ? ((nDefaultBlockSize < nMaxWindowSize / 2) ? nDefaultBlockSize : (int)nMaxWindowSize / 2) : nDefaultBlockSize;
+ const int nMaxOutBlockSize = (int)apultra_get_max_compressed_size(nBlockSize);
+
+ nResult = apultra_compressor_init(&compressor, nBlockSize, nBlockSize * 2, nFlags);
+ if (nResult != 0) {
+ return -1;
+ }
+
+ int nPreviousBlockSize = 0;
+ int nNumBlocks = 0;
+ int nCurBitsOffset[3] = { INT_MIN, INT_MIN, INT_MIN }, nCurBitMask[3] = { 0, 0, 0 }, nCurFollowsLiteral = 0;
+ int nBlockFlags = 1;
+ int nCurRepMatchOffset = 0;
+
+ while (nOriginalSize < nInputSize && !nError) {
+ int nInDataSize;
+
+ nInDataSize = (int)(nInputSize - nOriginalSize);
+ if (nInDataSize > nBlockSize)
+ nInDataSize = nBlockSize;
+
+ if (nInDataSize > 0) {
+ int nOutDataSize;
+ int nOutDataEnd = (int)(nMaxOutBufferSize - nCompressedSize);
+
+ if (nOutDataEnd > nMaxOutBlockSize)
+ nOutDataEnd = nMaxOutBlockSize;
+
+ if ((nOriginalSize + nInDataSize) >= nInputSize)
+ nBlockFlags |= 2;
+ nOutDataSize = apultra_compressor_shrink_block(&compressor, pInputData + nOriginalSize - nPreviousBlockSize, nPreviousBlockSize, nInDataSize, pOutBuffer + nCompressedSize, nOutDataEnd,
+ nCurBitsOffset, nCurBitMask, &nCurFollowsLiteral, &nCurRepMatchOffset, nBlockFlags);
+ nBlockFlags &= (~1);
+
+ if (nOutDataSize >= 0) {
+ /* Write compressed block */
+
+ if (!nError) {
+ nOriginalSize += nInDataSize;
+ nCompressedSize += nOutDataSize;
+ if (nCurBitsOffset[0] != INT_MIN)
+ nCurBitsOffset[0] -= nOutDataSize;
+ if (nCurBitsOffset[1] != INT_MIN)
+ nCurBitsOffset[1] -= nOutDataSize;
+ if (nCurBitsOffset[2] != INT_MIN)
+ nCurBitsOffset[2] -= nOutDataSize;
+ }
+ }
+ else {
+ nError = -1;
+ }
+
+ nPreviousBlockSize = nInDataSize;
+ nNumBlocks++;
+ }
+
+ if (!nError && nOriginalSize < nInputSize) {
+ if (progress)
+ progress(nOriginalSize, nCompressedSize);
+ }
+ }
+
+ if (progress)
+ progress(nOriginalSize, nCompressedSize);
+ if (pStats)
+ *pStats = compressor.stats;
+
+ apultra_compressor_destroy(&compressor);
+
+ if (nError) {
+ return -1;
+ }
+ else {
+ return nCompressedSize;
+ }
+}
diff --git a/tools/apultra_src/src/shrink.h b/tools/apultra_src/src/shrink.h new file mode 100644 index 0000000..0ed8a8f --- /dev/null +++ b/tools/apultra_src/src/shrink.h @@ -0,0 +1,166 @@ +/*
+ * shrink.h - compressor definitions
+ *
+ * Copyright (C) 2019 Emmanuel Marty
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty. In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ */
+
+/*
+ * Uses the libdivsufsort library Copyright (c) 2003-2008 Yuta Mori
+ *
+ * Inspired by cap by Sven-Åke Dahl. https://github.com/svendahl/cap
+ * Also inspired by Charles Bloom's compression blog. http://cbloomrants.blogspot.com/
+ * With ideas from LZ4 by Yann Collet. https://github.com/lz4/lz4
+ * With help and support from spke <zxintrospec@gmail.com>
+ *
+ */
+
+#ifndef _SHRINK_H
+#define _SHRINK_H
+
+#include "divsufsort.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define LCP_BITS 15
+#define TAG_BITS 4
+#define LCP_MAX ((1U<<(LCP_BITS - TAG_BITS)) - 1)
+#define LCP_AND_TAG_MAX ((1U<<LCP_BITS) - 1)
+#define LCP_SHIFT (63-LCP_BITS)
+#define LCP_MASK (((1ULL<<LCP_BITS) - 1) << LCP_SHIFT)
+#define POS_MASK ((1ULL<<LCP_SHIFT) - 1)
+#define VISITED_FLAG 0x8000000000000000ULL
+#define EXCL_VISITED_MASK 0x7fffffffffffffffULL
+
+#define NMATCHES_PER_ARRIVAL 24
+#define NMATCHES_PER_ARRIVAL_SMALL 9
+
+#define NMATCHES_PER_INDEX 64
+#define MATCHES_PER_INDEX_SHIFT 6
+
+#define LEAVE_ALONE_MATCH_SIZE 120
+
+/** One match option */
+typedef struct _apultra_match {
+ unsigned int length:11;
+ unsigned int offset:21;
+} apultra_match;
+
+/** One finalized match */
+typedef struct _apultra_final_match {
+ int length;
+ int offset;
+} apultra_final_match;
+
+/** Forward arrival slot */
+typedef struct {
+ int cost;
+ unsigned int from_pos:21;
+ int from_slot:8;
+ unsigned int follows_literal:1;
+
+ unsigned int rep_offset;
+ unsigned int rep_pos;
+ int score;
+
+ unsigned int match_offset:21;
+ unsigned int match_len:11;
+} apultra_arrival;
+
+/** Compression statistics */
+typedef struct _apultra_stats {
+ int num_literals;
+ int num_4bit_matches;
+ int num_7bit_matches;
+ int num_variable_matches;
+ int num_rep_matches;
+
+ int min_offset;
+ int max_offset;
+ long long total_offsets;
+
+ int min_match_len;
+ int max_match_len;
+ int total_match_lens;
+
+ int min_rle1_len;
+ int max_rle1_len;
+ int total_rle1_lens;
+
+ int min_rle2_len;
+ int max_rle2_len;
+ int total_rle2_lens;
+
+ int commands_divisor;
+ int match_divisor;
+ int rle1_divisor;
+ int rle2_divisor;
+} apultra_stats;
+
+/** Compression context */
+typedef struct _apultra_compressor {
+ divsufsort_ctx_t divsufsort_context;
+ unsigned long long *intervals;
+ unsigned long long *pos_data;
+ unsigned long long *open_intervals;
+ apultra_match *match;
+ unsigned short *match_depth;
+ unsigned char *match1;
+ apultra_final_match *best_match;
+ apultra_arrival *arrival;
+ int flags;
+ int block_size;
+ apultra_stats stats;
+} apultra_compressor;
+
+/** Compression flags */
+#define APULTRA_FLAG_ENHANCED 1 /**< Use enhanced (incompatible) format */
+
+/**
+ * Get maximum compressed size of input(source) data
+ *
+ * @param nInputSize input(source) size in bytes
+ *
+ * @return maximum compressed size
+ */
+size_t apultra_get_max_compressed_size(size_t nInputSize);
+
+/**
+ * Compress memory
+ *
+ * @param pInputData pointer to input(source) data to compress
+ * @param pOutBuffer buffer for compressed data
+ * @param nInputSize input(source) size in bytes
+ * @param nMaxOutBufferSize maximum capacity of compression buffer
+ * @param nFlags compression flags (a bitmask of APULTRA_FLAG_xxx, or 0)
+ * @param progress progress function, called after compressing each block, or NULL for none
+ * @param pStats pointer to compression stats that are filled if this function is successful, or NULL
+ * @param nMaxWindowSize maximum window size to use (0 for default)
+ *
+ * @return actual compressed size, or -1 for error
+ */
+size_t apultra_compress(const unsigned char *pInputData, unsigned char *pOutBuffer, size_t nInputSize, size_t nMaxOutBufferSize,
+ const unsigned int nFlags, size_t nMaxWindowSize, void(*progress)(long long nOriginalSize, long long nCompressedSize), apultra_stats *pStats);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SHRINK_H */
diff --git a/tools/apultra_src/src/shrink_context.c b/tools/apultra_src/src/shrink_context.c new file mode 100644 index 0000000..29e64fa --- /dev/null +++ b/tools/apultra_src/src/shrink_context.c @@ -0,0 +1,39 @@ +/*
+ * shrink_context.c - compression context implementation
+ *
+ * Copyright (C) 2019 Emmanuel Marty
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty. In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ */
+
+/*
+ * Uses the libdivsufsort library Copyright (c) 2003-2008 Yuta Mori
+ *
+ * Inspired by LZ4 by Yann Collet. https://github.com/lz4/lz4
+ * With help, ideas, optimizations and speed measurements by spke <zxintrospec@gmail.com>
+ * With ideas from Lizard by Przemyslaw Skibinski and Yann Collet. https://github.com/inikep/lizard
+ * Also with ideas from smallz4 by Stephan Brumme. https://create.stephan-brumme.com/smallz4/
+ *
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include "shrink_context.h"
+#include "shrink_block.h"
+#include "format.h"
+#include "matchfinder.h"
+#include "lib.h"
diff --git a/tools/bin2h.py b/tools/bin2h.py new file mode 100755 index 0000000..4629fd4 --- /dev/null +++ b/tools/bin2h.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python3 + +from argparse import ArgumentParser + +__version__ = "1.0" + + +def main(): + + parser = ArgumentParser( + description="Bin to H converter", + epilog="Copyright (C) 2014-2023 Juan J Martinez <jjm@usebox.net>", + ) + + parser.add_argument( + "--version", action="version", version="%(prog)s " + __version__ + ) + parser.add_argument("file", help="file to convert") + parser.add_argument("id", help="variable to use") + + args = parser.parse_args() + + with open(args.file, "rb") as fd: + data = bytearray(fd.read()) + + data_out = "" + for part in range(0, len(data), 8): + if data_out: + data_out += ",\n" + data_out += ", ".join(["0x%02x" % b for b in data[part : part + 8]]) + + print("/* file: %s */" % args.file) + print("#define %s_LEN %d\n" % (args.id.upper(), len(data))) + print("#ifdef LOCAL") + print("const unsigned char %s[] = {\n%s\n};\n" % (args.id, data_out)) + print("#else") + print("extern const unsigned char %s[];\n" % args.id) + print("#endif") + + +if __name__ == "__main__": + main() diff --git a/tools/chksize b/tools/chksize new file mode 100755 index 0000000..147f4af --- /dev/null +++ b/tools/chksize @@ -0,0 +1,40 @@ +#!/usr/bin/env python3 + +import sys + +# video memory - stack +TOP_MEM = 0xc000 - 256 + + +def main(): + if len(sys.argv) != 3: + sys.exit("usage: %s load_addr filename.map" % sys.argv[0]) + + load_addr = int(sys.argv[1]) + + with open(sys.argv[2], "r") as fd: + lines = fd.readlines() + # warnings + for line in lines: + if "l__INITIALIZED" in line: + init_size = int(line.split()[0], base=16) + if init_size != 0: + print("*WARNING* Initialized data found") + # search for HEAP + for line in lines: + if "s__HEAP" in line: + heap_addr = int(line.split()[0], base=16) + print("""\ +*** + Max: {} bytes + Current: {} bytes ({} bytes left) +*** +""".format(TOP_MEM - load_addr, heap_addr - load_addr, + TOP_MEM - heap_addr - load_addr)) + sys.exit(0) + + sys.exit("HEAP size not found") + + +if __name__ == "__main__": + main() diff --git a/tools/dump-pal.py b/tools/dump-pal.py new file mode 100755 index 0000000..84c52e3 --- /dev/null +++ b/tools/dump-pal.py @@ -0,0 +1,132 @@ +#!/usr/bin/env python3 + +__version__ = "1.0" + +from argparse import ArgumentParser +from PIL import Image + +# firmware +CPC_PAL = ( + [0, 0, 0], + [0, 0, 128], + [0, 0, 255], + [128, 0, 0], + [128, 0, 128], + [128, 0, 255], + [255, 0, 0], + [255, 0, 128], + [255, 0, 255], + [0, 128, 0], + [0, 128, 128], + [0, 128, 255], + [128, 128, 0], + [128, 128, 128], + [128, 128, 255], + [255, 128, 0], + [255, 128, 128], + [255, 128, 255], + [0, 255, 0], + [0, 255, 128], + [0, 255, 255], + [128, 255, 0], + [128, 255, 128], + [128, 255, 255], + [255, 255, 0], + [255, 255, 128], + [255, 255, 255], +) + +SW_TO_HW = ( + 0x54, + 0x44, + 0x55, + 0x5C, + 0x58, + 0x5D, + 0x4C, + 0x45, + 0x4D, + 0x56, + 0x46, + 0x57, + 0x5E, + 0x40, + 0x5F, + 0x4E, + 0x47, + 0x4F, + 0x52, + 0x42, + 0x53, + 0x5A, + 0x59, + 0x5B, + 0x4A, + 0x43, + 0x4B, +) + + +def main(): + + parser = ArgumentParser( + description="PNG to CPC palette (firmware)", + epilog="Copyright (C) 2015-2023 Juan J Martinez <jjm@usebox.net>", + ) + + parser.add_argument( + "--version", action="version", version="%(prog)s " + __version__ + ) + parser.add_argument("--header", dest="header", action="store_true") + parser.add_argument("--hardware", dest="hardware", action="store_true") + parser.add_argument("image", help="image to convert") + parser.add_argument( + "pal_dump", + help="filename for the palette dump (variable if header flag is set)", + ) + + args = parser.parse_args() + + try: + image = Image.open(args.image) + except IOError: + parser.error("failed to open the image") + + if image.mode != "P": + parser.error("not an indexed image (no palette)") + + palette = image.getpalette() + if not palette: + parser.error("failed to extract the palette (is this an indexed image?)") + + rgb = [] + for i in range(0, 16 * 3, 3): + c = palette[i : i + 3] + if c not in CPC_PAL: + parser.error("%r not in the CPC palette" % c) + rgb.append(c) + + out = [CPC_PAL.index(c) for c in rgb] + if len(out) < 16: + out.extend([0 for _ in range(16 - len(out))]) + + if args.hardware: + out = [SW_TO_HW[c] for c in out[:]] + + if args.header: + print("/* %s palette */" % ("hardware" if args.hardware else "firmware")) + print("#ifdef LOCAL") + print( + "const unsigned char %s[] = { %s };\n" + % (args.pal_dump, ", ".join([hex(c) for c in out])) + ) + print("#else") + print("extern const unsigned char %s[];\n" % (args.pal_dump)) + print("#endif") + else: + with open(args.pal_dump, "wb") as fd: + fd.write(bytearray(out)) + + +if __name__ == "__main__": + main() diff --git a/tools/gfx2crtc/AUTHORS b/tools/gfx2crtc/AUTHORS new file mode 100644 index 0000000..4cb7787 --- /dev/null +++ b/tools/gfx2crtc/AUTHORS @@ -0,0 +1,5 @@ +Logiciel Initial : +CloudStrife/Shinra (Quentin Carlier) <cloudstrife@cpcscene.com> + +Contributors: +PulkoMandy/Shinra (Adrien Destugues) <pulkomandy@gmail.com> diff --git a/tools/gfx2crtc/LICENCE b/tools/gfx2crtc/LICENCE new file mode 100644 index 0000000..1613fca --- /dev/null +++ b/tools/gfx2crtc/LICENCE @@ -0,0 +1,512 @@ + +CONTRAT DE LICENCE DE LOGICIEL LIBRE CeCILL + + + Avertissement + +Ce contrat est une licence de logiciel libre issue d'une concertation +entre ses auteurs afin que le respect de deux grands principes préside à +sa rédaction: + + * d'une part, le respect des principes de diffusion des logiciels + libres: accès au code source, droits étendus conférés aux + utilisateurs, + * d'autre part, la désignation d'un droit applicable, le droit + français, auquel elle est conforme, tant au regard du droit de la + responsabilité civile que du droit de la propriété intellectuelle + et de la protection qu'il offre aux auteurs et titulaires des + droits patrimoniaux sur un logiciel. + +Les auteurs de la licence CeCILL (pour Ce[a] C[nrs] I[nria] L[ogiciel] +L[ibre]) sont: + +Commissariat à l'Energie Atomique - CEA, établissement public de +recherche à caractère scientifique, technique et industriel, dont le +siège est situé 25 rue Leblanc, immeuble Le Ponant D, 75015 Paris. + +Centre National de la Recherche Scientifique - CNRS, établissement +public à caractère scientifique et technologique, dont le siège est +situé 3 rue Michel-Ange, 75794 Paris cedex 16. + +Institut National de Recherche en Informatique et en Automatique - +INRIA, établissement public à caractère scientifique et technologique, +dont le siège est situé Domaine de Voluceau, Rocquencourt, BP 105, 78153 +Le Chesnay cedex. + + + Préambule + +Ce contrat est une licence de logiciel libre dont l'objectif est de +conférer aux utilisateurs la liberté de modification et de +redistribution du logiciel régi par cette licence dans le cadre d'un +modèle de diffusion en logiciel libre. + +L'exercice de ces libertés est assorti de certains devoirs à la charge +des utilisateurs afin de préserver ce statut au cours des +redistributions ultérieures. + +L'accessibilité au code source et les droits de copie, de modification +et de redistribution qui en découlent ont pour contrepartie de n'offrir +aux utilisateurs qu'une garantie limitée et de ne faire peser sur +l'auteur du logiciel, le titulaire des droits patrimoniaux et les +concédants successifs qu'une responsabilité restreinte. + +A cet égard l'attention de l'utilisateur est attirée sur les risques +associés au chargement, à l'utilisation, à la modification et/ou au +développement et à la reproduction du logiciel par l'utilisateur étant +donné sa spécificité de logiciel libre, qui peut le rendre complexe à +manipuler et qui le réserve donc à des développeurs ou des +professionnels avertis possédant des connaissances informatiques +approfondies. Les utilisateurs sont donc invités à charger et tester +l'adéquation du logiciel à leurs besoins dans des conditions permettant +d'assurer la sécurité de leurs systèmes et/ou de leurs données et, plus +généralement, à l'utiliser et l'exploiter dans les mêmes conditions de +sécurité. Ce contrat peut être reproduit et diffusé librement, sous +réserve de le conserver en l'état, sans ajout ni suppression de clauses. + +Ce contrat est susceptible de s'appliquer à tout logiciel dont le +titulaire des droits patrimoniaux décide de soumettre l'exploitation aux +dispositions qu'il contient. + + + Article 1 - DEFINITIONS + +Dans ce contrat, les termes suivants, lorsqu'ils seront écrits avec une +lettre capitale, auront la signification suivante: + +Contrat: désigne le présent contrat de licence, ses éventuelles versions +postérieures et annexes. + +Logiciel: désigne le logiciel sous sa forme de Code Objet et/ou de Code +Source et le cas échéant sa documentation, dans leur état au moment de +l'acceptation du Contrat par le Licencié. + +Logiciel Initial: désigne le Logiciel sous sa forme de Code Source et +éventuellement de Code Objet et le cas échéant sa documentation, dans +leur état au moment de leur première diffusion sous les termes du Contrat. + +Logiciel Modifié: désigne le Logiciel modifié par au moins une +Contribution. + +Code Source: désigne l'ensemble des instructions et des lignes de +programme du Logiciel et auquel l'accès est nécessaire en vue de +modifier le Logiciel. + +Code Objet: désigne les fichiers binaires issus de la compilation du +Code Source. + +Titulaire: désigne le ou les détenteurs des droits patrimoniaux d'auteur +sur le Logiciel Initial. + +Licencié: désigne le ou les utilisateurs du Logiciel ayant accepté le +Contrat. + +Contributeur: désigne le Licencié auteur d'au moins une Contribution. + +Concédant: désigne le Titulaire ou toute personne physique ou morale +distribuant le Logiciel sous le Contrat. + +Contribution: désigne l'ensemble des modifications, corrections, +traductions, adaptations et/ou nouvelles fonctionnalités intégrées dans +le Logiciel par tout Contributeur, ainsi que tout Module Interne. + +Module: désigne un ensemble de fichiers sources y compris leur +documentation qui permet de réaliser des fonctionnalités ou services +supplémentaires à ceux fournis par le Logiciel. + +Module Externe: désigne tout Module, non dérivé du Logiciel, tel que ce +Module et le Logiciel s'exécutent dans des espaces d'adressage +différents, l'un appelant l'autre au moment de leur exécution. + +Module Interne: désigne tout Module lié au Logiciel de telle sorte +qu'ils s'exécutent dans le même espace d'adressage. + +GNU GPL: désigne la GNU General Public License dans sa version 2 ou +toute version ultérieure, telle que publiée par Free Software Foundation +Inc. + +Parties: désigne collectivement le Licencié et le Concédant. + +Ces termes s'entendent au singulier comme au pluriel. + + + Article 2 - OBJET + +Le Contrat a pour objet la concession par le Concédant au Licencié d'une +licence non exclusive, cessible et mondiale du Logiciel telle que +définie ci-après à l'article 5 pour toute la durée de protection des droits +portant sur ce Logiciel. + + + Article 3 - ACCEPTATION + +3.1 L'acceptation par le Licencié des termes du Contrat est réputée +acquise du fait du premier des faits suivants: + + * (i) le chargement du Logiciel par tout moyen notamment par + téléchargement à partir d'un serveur distant ou par chargement à + partir d'un support physique; + * (ii) le premier exercice par le Licencié de l'un quelconque des + droits concédés par le Contrat. + +3.2 Un exemplaire du Contrat, contenant notamment un avertissement +relatif aux spécificités du Logiciel, à la restriction de garantie et à +la limitation à un usage par des utilisateurs expérimentés a été mis à +disposition du Licencié préalablement à son acceptation telle que +définie à l'article 3.1 ci dessus et le Licencié reconnaît en avoir pris +connaissance. + + + Article 4 - ENTREE EN VIGUEUR ET DUREE + + + 4.1 ENTREE EN VIGUEUR + +Le Contrat entre en vigueur à la date de son acceptation par le Licencié +telle que définie en 3.1. + + + 4.2 DUREE + +Le Contrat produira ses effets pendant toute la durée légale de +protection des droits patrimoniaux portant sur le Logiciel. + + + Article 5 - ETENDUE DES DROITS CONCEDES + +Le Concédant concède au Licencié, qui accepte, les droits suivants sur +le Logiciel pour toutes destinations et pour la durée du Contrat dans +les conditions ci-après détaillées. + +Par ailleurs, si le Concédant détient ou venait à détenir un ou +plusieurs brevets d'invention protégeant tout ou partie des +fonctionnalités du Logiciel ou de ses composants, il s'engage à ne pas +opposer les éventuels droits conférés par ces brevets aux Licenciés +successifs qui utiliseraient, exploiteraient ou modifieraient le +Logiciel. En cas de cession de ces brevets, le Concédant s'engage à +faire reprendre les obligations du présent alinéa aux cessionnaires. + + + 5.1 DROIT D'UTILISATION + +Le Licencié est autorisé à utiliser le Logiciel, sans restriction quant +aux domaines d'application, étant ci-après précisé que cela comporte: + + 1. la reproduction permanente ou provisoire du Logiciel en tout ou + partie par tout moyen et sous toute forme. + + 2. le chargement, l'affichage, l'exécution, ou le stockage du + Logiciel sur tout support. + + 3. la possibilité d'en observer, d'en étudier, ou d'en tester le + fonctionnement afin de déterminer les idées et principes qui sont + à la base de n'importe quel élément de ce Logiciel; et ceci, + lorsque le Licencié effectue toute opération de chargement, + d'affichage, d'exécution, de transmission ou de stockage du + Logiciel qu'il est en droit d'effectuer en vertu du Contrat. + + + 5.2 DROIT D'APPORTER DES CONTRIBUTIONS + +Le droit d'apporter des Contributions comporte le droit de traduire, +d'adapter, d'arranger ou d'apporter toute autre modification au Logiciel +et le droit de reproduire le logiciel en résultant. + +Le Licencié est autorisé à apporter toute Contribution au Logiciel sous +réserve de mentionner, de façon explicite, son nom en tant qu'auteur de +cette Contribution et la date de création de celle-ci. + + + 5.3 DROIT DE DISTRIBUTION + +Le droit de distribution comporte notamment le droit de diffuser, de +transmettre et de communiquer le Logiciel au public sur tout support et +par tout moyen ainsi que le droit de mettre sur le marché à titre +onéreux ou gratuit, un ou des exemplaires du Logiciel par tout procédé. + +Le Licencié est autorisé à distribuer des copies du Logiciel, modifié ou +non, à des tiers dans les conditions ci-après détaillées. + + + 5.3.1 DISTRIBUTION DU LOGICIEL SANS MODIFICATION + +Le Licencié est autorisé à distribuer des copies conformes du Logiciel, +sous forme de Code Source ou de Code Objet, à condition que cette +distribution respecte les dispositions du Contrat dans leur totalité et +soit accompagnée: + + 1. d'un exemplaire du Contrat, + + 2. d'un avertissement relatif à la restriction de garantie et de + responsabilité du Concédant telle que prévue aux articles 8 + et 9, + +et que, dans le cas où seul le Code Objet du Logiciel est redistribué, +le Licencié permette aux futurs Licenciés d'accéder facilement au Code +Source complet du Logiciel en indiquant les modalités d'accès, étant +entendu que le coût additionnel d'acquisition du Code Source ne devra +pas excéder le simple coût de transfert des données. + + + 5.3.2 DISTRIBUTION DU LOGICIEL MODIFIE + +Lorsque le Licencié apporte une Contribution au Logiciel, les conditions +de distribution du Logiciel Modifié en résultant sont alors soumises à +l'intégralité des dispositions du Contrat. + +Le Licencié est autorisé à distribuer le Logiciel Modifié, sous forme de +code source ou de code objet, à condition que cette distribution +respecte les dispositions du Contrat dans leur totalité et soit +accompagnée: + + 1. d'un exemplaire du Contrat, + + 2. d'un avertissement relatif à la restriction de garantie et de + responsabilité du Concédant telle que prévue aux articles 8 + et 9, + +et que, dans le cas où seul le code objet du Logiciel Modifié est +redistribué, le Licencié permette aux futurs Licenciés d'accéder +facilement au code source complet du Logiciel Modifié en indiquant les +modalités d'accès, étant entendu que le coût additionnel d'acquisition +du code source ne devra pas excéder le simple coût de transfert des données. + + + 5.3.3 DISTRIBUTION DES MODULES EXTERNES + +Lorsque le Licencié a développé un Module Externe les conditions du +Contrat ne s'appliquent pas à ce Module Externe, qui peut être distribué +sous un contrat de licence différent. + + + 5.3.4 COMPATIBILITE AVEC LA LICENCE GNU GPL + +Le Licencié peut inclure un code soumis aux dispositions d'une des +versions de la licence GNU GPL dans le Logiciel modifié ou non et +distribuer l'ensemble sous les conditions de la même version de la +licence GNU GPL. + +Le Licencié peut inclure le Logiciel modifié ou non dans un code soumis +aux dispositions d'une des versions de la licence GNU GPL et distribuer +l'ensemble sous les conditions de la même version de la licence GNU GPL. + + + Article 6 - PROPRIETE INTELLECTUELLE + + + 6.1 SUR LE LOGICIEL INITIAL + +Le Titulaire est détenteur des droits patrimoniaux sur le Logiciel +Initial. Toute utilisation du Logiciel Initial est soumise au respect +des conditions dans lesquelles le Titulaire a choisi de diffuser son +oeuvre et nul autre n'a la faculté de modifier les conditions de +diffusion de ce Logiciel Initial. + +Le Titulaire s'engage à ce que le Logiciel Initial reste au moins régi +par le Contrat et ce, pour la durée visée à l'article 4.2. + + + 6.2 SUR LES CONTRIBUTIONS + +Le Licencié qui a développé une Contribution est titulaire sur celle-ci +des droits de propriété intellectuelle dans les conditions définies par +la législation applicable. + + + 6.3 SUR LES MODULES EXTERNES + +Le Licencié qui a développé un Module Externe est titulaire sur celui-ci +des droits de propriété intellectuelle dans les conditions définies par +la législation applicable et reste libre du choix du contrat régissant +sa diffusion. + + + 6.4 DISPOSITIONS COMMUNES + +Le Licencié s'engage expressément: + + 1. à ne pas supprimer ou modifier de quelque manière que ce soit les + mentions de propriété intellectuelle apposées sur le Logiciel; + + 2. à reproduire à l'identique lesdites mentions de propriété + intellectuelle sur les copies du Logiciel modifié ou non. + +Le Licencié s'engage à ne pas porter atteinte, directement ou +indirectement, aux droits de propriété intellectuelle du Titulaire et/ou +des Contributeurs sur le Logiciel et à prendre, le cas échéant, à +l'égard de son personnel toutes les mesures nécessaires pour assurer le +respect des dits droits de propriété intellectuelle du Titulaire et/ou +des Contributeurs. + + + Article 7 - SERVICES ASSOCIES + +7.1 Le Contrat n'oblige en aucun cas le Concédant à la réalisation de +prestations d'assistance technique ou de maintenance du Logiciel. + +Cependant le Concédant reste libre de proposer ce type de services. Les +termes et conditions d'une telle assistance technique et/ou d'une telle +maintenance seront alors déterminés dans un acte séparé. Ces actes de +maintenance et/ou assistance technique n'engageront que la seule +responsabilité du Concédant qui les propose. + +7.2 De même, tout Concédant est libre de proposer, sous sa seule +responsabilité, à ses licenciés une garantie, qui n'engagera que lui, +lors de la redistribution du Logiciel et/ou du Logiciel Modifié et ce, +dans les conditions qu'il souhaite. Cette garantie et les modalités +financières de son application feront l'objet d'un acte séparé entre le +Concédant et le Licencié. + + + Article 8 - RESPONSABILITE + +8.1 Sous réserve des dispositions de l'article 8.2, le Licencié a la +faculté, sous réserve de prouver la faute du Concédant concerné, de +solliciter la réparation du préjudice direct qu'il subirait du fait du +Logiciel et dont il apportera la preuve. + +8.2 La responsabilité du Concédant est limitée aux engagements pris en +application du Contrat et ne saurait être engagée en raison notamment: +(i) des dommages dus à l'inexécution, totale ou partielle, de ses +obligations par le Licencié, (ii) des dommages directs ou indirects +découlant de l'utilisation ou des performances du Logiciel subis par le +Licencié et (iii) plus généralement d'un quelconque dommage indirect. En +particulier, les Parties conviennent expressément que tout préjudice +financier ou commercial (par exemple perte de données, perte de +bénéfices, perte d'exploitation, perte de clientèle ou de commandes, +manque à gagner, trouble commercial quelconque) ou toute action dirigée +contre le Licencié par un tiers, constitue un dommage indirect et +n'ouvre pas droit à réparation par le Concédant. + + + Article 9 - GARANTIE + +9.1 Le Licencié reconnaît que l'état actuel des connaissances +scientifiques et techniques au moment de la mise en circulation du +Logiciel ne permet pas d'en tester et d'en vérifier toutes les +utilisations ni de détecter l'existence d'éventuels défauts. L'attention +du Licencié a été attirée sur ce point sur les risques associés au +chargement, à l'utilisation, la modification et/ou au développement et à +la reproduction du Logiciel qui sont réservés à des utilisateurs avertis. + +Il relève de la responsabilité du Licencié de contrôler, par tous +moyens, l'adéquation du produit à ses besoins, son bon fonctionnement et +de s'assurer qu'il ne causera pas de dommages aux personnes et aux biens. + +9.2 Le Concédant déclare de bonne foi être en droit de concéder +l'ensemble des droits attachés au Logiciel (comprenant notamment les +droits visés à l'article 5). + +9.3 Le Licencié reconnaît que le Logiciel est fourni "en l'état" par le +Concédant sans autre garantie, expresse ou tacite, que celle prévue à +l'article 9.2 et notamment sans aucune garantie sur sa valeur commerciale, +son caractère sécurisé, innovant ou pertinent. + +En particulier, le Concédant ne garantit pas que le Logiciel est exempt +d'erreur, qu'il fonctionnera sans interruption, qu'il sera compatible +avec l'équipement du Licencié et sa configuration logicielle ni qu'il +remplira les besoins du Licencié. + +9.4 Le Concédant ne garantit pas, de manière expresse ou tacite, que le +Logiciel ne porte pas atteinte à un quelconque droit de propriété +intellectuelle d'un tiers portant sur un brevet, un logiciel ou sur tout +autre droit de propriété. Ainsi, le Concédant exclut toute garantie au +profit du Licencié contre les actions en contrefaçon qui pourraient être +diligentées au titre de l'utilisation, de la modification, et de la +redistribution du Logiciel. Néanmoins, si de telles actions sont +exercées contre le Licencié, le Concédant lui apportera son aide +technique et juridique pour sa défense. Cette aide technique et +juridique est déterminée au cas par cas entre le Concédant concerné et +le Licencié dans le cadre d'un protocole d'accord. Le Concédant dégage +toute responsabilité quant à l'utilisation de la dénomination du +Logiciel par le Licencié. Aucune garantie n'est apportée quant à +l'existence de droits antérieurs sur le nom du Logiciel et sur +l'existence d'une marque. + + + Article 10 - RESILIATION + +10.1 En cas de manquement par le Licencié aux obligations mises à sa +charge par le Contrat, le Concédant pourra résilier de plein droit le +Contrat trente (30) jours après notification adressée au Licencié et +restée sans effet. + +10.2 Le Licencié dont le Contrat est résilié n'est plus autorisé à +utiliser, modifier ou distribuer le Logiciel. Cependant, toutes les +licences qu'il aura concédées antérieurement à la résiliation du Contrat +resteront valides sous réserve qu'elles aient été effectuées en +conformité avec le Contrat. + + + Article 11 - DISPOSITIONS DIVERSES + + + 11.1 CAUSE EXTERIEURE + +Aucune des Parties ne sera responsable d'un retard ou d'une défaillance +d'exécution du Contrat qui serait dû à un cas de force majeure, un cas +fortuit ou une cause extérieure, telle que, notamment, le mauvais +fonctionnement ou les interruptions du réseau électrique ou de +télécommunication, la paralysie du réseau liée à une attaque +informatique, l'intervention des autorités gouvernementales, les +catastrophes naturelles, les dégâts des eaux, les tremblements de terre, +le feu, les explosions, les grèves et les conflits sociaux, l'état de +guerre... + +11.2 Le fait, par l'une ou l'autre des Parties, d'omettre en une ou +plusieurs occasions de se prévaloir d'une ou plusieurs dispositions du +Contrat, ne pourra en aucun cas impliquer renonciation par la Partie +intéressée à s'en prévaloir ultérieurement. + +11.3 Le Contrat annule et remplace toute convention antérieure, écrite +ou orale, entre les Parties sur le même objet et constitue l'accord +entier entre les Parties sur cet objet. Aucune addition ou modification +aux termes du Contrat n'aura d'effet à l'égard des Parties à moins +d'être faite par écrit et signée par leurs représentants dûment habilités. + +11.4 Dans l'hypothèse où une ou plusieurs des dispositions du Contrat +s'avèrerait contraire à une loi ou à un texte applicable, existants ou +futurs, cette loi ou ce texte prévaudrait, et les Parties feraient les +amendements nécessaires pour se conformer à cette loi ou à ce texte. +Toutes les autres dispositions resteront en vigueur. De même, la +nullité, pour quelque raison que ce soit, d'une des dispositions du +Contrat ne saurait entraîner la nullité de l'ensemble du Contrat. + + + 11.5 LANGUE + +Le Contrat est rédigé en langue française et en langue anglaise, ces +deux versions faisant également foi. + + + Article 12 - NOUVELLES VERSIONS DU CONTRAT + +12.1 Toute personne est autorisée à copier et distribuer des copies de +ce Contrat. + +12.2 Afin d'en préserver la cohérence, le texte du Contrat est protégé +et ne peut être modifié que par les auteurs de la licence, lesquels se +réservent le droit de publier périodiquement des mises à jour ou de +nouvelles versions du Contrat, qui posséderont chacune un numéro +distinct. Ces versions ultérieures seront susceptibles de prendre en +compte de nouvelles problématiques rencontrées par les logiciels libres. + +12.3 Tout Logiciel diffusé sous une version donnée du Contrat ne pourra +faire l'objet d'une diffusion ultérieure que sous la même version du +Contrat ou une version postérieure, sous réserve des dispositions de +l'article 5.3.4. + + + Article 13 - LOI APPLICABLE ET COMPETENCE TERRITORIALE + +13.1 Le Contrat est régi par la loi française. Les Parties conviennent +de tenter de régler à l'amiable les différends ou litiges qui +viendraient à se produire par suite ou à l'occasion du Contrat. + +13.2 A défaut d'accord amiable dans un délai de deux (2) mois à compter +de leur survenance et sauf situation relevant d'une procédure d'urgence, +les différends ou litiges seront portés par la Partie la plus diligente +devant les Tribunaux compétents de Paris. + + +Version 2.0 du 2006-09-05. diff --git a/tools/gfx2crtc/LICENSE b/tools/gfx2crtc/LICENSE new file mode 100644 index 0000000..fcc8df2 --- /dev/null +++ b/tools/gfx2crtc/LICENSE @@ -0,0 +1,506 @@ + +CeCILL FREE SOFTWARE LICENSE AGREEMENT + + + Notice + +This Agreement is a Free Software license agreement that is the result +of discussions between its authors in order to ensure compliance with +the two main principles guiding its drafting: + + * firstly, compliance with the principles governing the distribution + of Free Software: access to source code, broad rights granted to + users, + * secondly, the election of a governing law, French law, with which + it is conformant, both as regards the law of torts and + intellectual property law, and the protection that it offers to + both authors and holders of the economic rights over software. + +The authors of the CeCILL (for Ce[a] C[nrs] I[nria] L[ogiciel] L[ibre]) +license are: + +Commissariat à l'Energie Atomique - CEA, a public scientific, technical +and industrial research establishment, having its principal place of +business at 25 rue Leblanc, immeuble Le Ponant D, 75015 Paris, France. + +Centre National de la Recherche Scientifique - CNRS, a public scientific +and technological establishment, having its principal place of business +at 3 rue Michel-Ange, 75794 Paris cedex 16, France. + +Institut National de Recherche en Informatique et en Automatique - +INRIA, a public scientific and technological establishment, having its +principal place of business at Domaine de Voluceau, Rocquencourt, BP +105, 78153 Le Chesnay cedex, France. + + + Preamble + +The purpose of this Free Software license agreement is to grant users +the right to modify and redistribute the software governed by this +license within the framework of an open source distribution model. + +The exercising of these rights is conditional upon certain obligations +for users so as to preserve this status for all subsequent redistributions. + +In consideration of access to the source code and the rights to copy, +modify and redistribute granted by the license, users are provided only +with a limited warranty and the software's author, the holder of the +economic rights, and the successive licensors only have limited liability. + +In this respect, the risks associated with loading, using, modifying +and/or developing or reproducing the software by the user are brought to +the user's attention, given its Free Software status, which may make it +complicated to use, with the result that its use is reserved for +developers and experienced professionals having in-depth computer +knowledge. Users are therefore encouraged to load and test the +suitability of the software as regards their requirements in conditions +enabling the security of their systems and/or data to be ensured and, +more generally, to use and operate it in the same conditions of +security. This Agreement may be freely reproduced and published, +provided it is not altered, and that no provisions are either added or +removed herefrom. + +This Agreement may apply to any or all software for which the holder of +the economic rights decides to submit the use thereof to its provisions. + + + Article 1 - DEFINITIONS + +For the purpose of this Agreement, when the following expressions +commence with a capital letter, they shall have the following meaning: + +Agreement: means this license agreement, and its possible subsequent +versions and annexes. + +Software: means the software in its Object Code and/or Source Code form +and, where applicable, its documentation, "as is" when the Licensee +accepts the Agreement. + +Initial Software: means the Software in its Source Code and possibly its +Object Code form and, where applicable, its documentation, "as is" when +it is first distributed under the terms and conditions of the Agreement. + +Modified Software: means the Software modified by at least one +Contribution. + +Source Code: means all the Software's instructions and program lines to +which access is required so as to modify the Software. + +Object Code: means the binary files originating from the compilation of +the Source Code. + +Holder: means the holder(s) of the economic rights over the Initial +Software. + +Licensee: means the Software user(s) having accepted the Agreement. + +Contributor: means a Licensee having made at least one Contribution. + +Licensor: means the Holder, or any other individual or legal entity, who +distributes the Software under the Agreement. + +Contribution: means any or all modifications, corrections, translations, +adaptations and/or new functions integrated into the Software by any or +all Contributors, as well as any or all Internal Modules. + +Module: means a set of sources files including their documentation that +enables supplementary functions or services in addition to those offered +by the Software. + +External Module: means any or all Modules, not derived from the +Software, so that this Module and the Software run in separate address +spaces, with one calling the other when they are run. + +Internal Module: means any or all Module, connected to the Software so +that they both execute in the same address space. + +GNU GPL: means the GNU General Public License version 2 or any +subsequent version, as published by the Free Software Foundation Inc. + +Parties: mean both the Licensee and the Licensor. + +These expressions may be used both in singular and plural form. + + + Article 2 - PURPOSE + +The purpose of the Agreement is the grant by the Licensor to the +Licensee of a non-exclusive, transferable and worldwide license for the +Software as set forth in Article 5 hereinafter for the whole term of the +protection granted by the rights over said Software. + + + Article 3 - ACCEPTANCE + +3.1 The Licensee shall be deemed as having accepted the terms and +conditions of this Agreement upon the occurrence of the first of the +following events: + + * (i) loading the Software by any or all means, notably, by + downloading from a remote server, or by loading from a physical + medium; + * (ii) the first time the Licensee exercises any of the rights + granted hereunder. + +3.2 One copy of the Agreement, containing a notice relating to the +characteristics of the Software, to the limited warranty, and to the +fact that its use is restricted to experienced users has been provided +to the Licensee prior to its acceptance as set forth in Article 3.1 +hereinabove, and the Licensee hereby acknowledges that it has read and +understood it. + + + Article 4 - EFFECTIVE DATE AND TERM + + + 4.1 EFFECTIVE DATE + +The Agreement shall become effective on the date when it is accepted by +the Licensee as set forth in Article 3.1. + + + 4.2 TERM + +The Agreement shall remain in force for the entire legal term of +protection of the economic rights over the Software. + + + Article 5 - SCOPE OF RIGHTS GRANTED + +The Licensor hereby grants to the Licensee, who accepts, the following +rights over the Software for any or all use, and for the term of the +Agreement, on the basis of the terms and conditions set forth hereinafter. + +Besides, if the Licensor owns or comes to own one or more patents +protecting all or part of the functions of the Software or of its +components, the Licensor undertakes not to enforce the rights granted by +these patents against successive Licensees using, exploiting or +modifying the Software. If these patents are transferred, the Licensor +undertakes to have the transferees subscribe to the obligations set +forth in this paragraph. + + + 5.1 RIGHT OF USE + +The Licensee is authorized to use the Software, without any limitation +as to its fields of application, with it being hereinafter specified +that this comprises: + + 1. permanent or temporary reproduction of all or part of the Software + by any or all means and in any or all form. + + 2. loading, displaying, running, or storing the Software on any or + all medium. + + 3. entitlement to observe, study or test its operation so as to + determine the ideas and principles behind any or all constituent + elements of said Software. This shall apply when the Licensee + carries out any or all loading, displaying, running, transmission + or storage operation as regards the Software, that it is entitled + to carry out hereunder. + + + 5.2 ENTITLEMENT TO MAKE CONTRIBUTIONS + +The right to make Contributions includes the right to translate, adapt, +arrange, or make any or all modifications to the Software, and the right +to reproduce the resulting software. + +The Licensee is authorized to make any or all Contributions to the +Software provided that it includes an explicit notice that it is the +author of said Contribution and indicates the date of the creation thereof. + + + 5.3 RIGHT OF DISTRIBUTION + +In particular, the right of distribution includes the right to publish, +transmit and communicate the Software to the general public on any or +all medium, and by any or all means, and the right to market, either in +consideration of a fee, or free of charge, one or more copies of the +Software by any means. + +The Licensee is further authorized to distribute copies of the modified +or unmodified Software to third parties according to the terms and +conditions set forth hereinafter. + + + 5.3.1 DISTRIBUTION OF SOFTWARE WITHOUT MODIFICATION + +The Licensee is authorized to distribute true copies of the Software in +Source Code or Object Code form, provided that said distribution +complies with all the provisions of the Agreement and is accompanied by: + + 1. a copy of the Agreement, + + 2. a notice relating to the limitation of both the Licensor's + warranty and liability as set forth in Articles 8 and 9, + +and that, in the event that only the Object Code of the Software is +redistributed, the Licensee allows future Licensees unhindered access to +the full Source Code of the Software by indicating how to access it, it +being understood that the additional cost of acquiring the Source Code +shall not exceed the cost of transferring the data. + + + 5.3.2 DISTRIBUTION OF MODIFIED SOFTWARE + +When the Licensee makes a Contribution to the Software, the terms and +conditions for the distribution of the resulting Modified Software +become subject to all the provisions of this Agreement. + +The Licensee is authorized to distribute the Modified Software, in +source code or object code form, provided that said distribution +complies with all the provisions of the Agreement and is accompanied by: + + 1. a copy of the Agreement, + + 2. a notice relating to the limitation of both the Licensor's + warranty and liability as set forth in Articles 8 and 9, + +and that, in the event that only the object code of the Modified +Software is redistributed, the Licensee allows future Licensees +unhindered access to the full source code of the Modified Software by +indicating how to access it, it being understood that the additional +cost of acquiring the source code shall not exceed the cost of +transferring the data. + + + 5.3.3 DISTRIBUTION OF EXTERNAL MODULES + +When the Licensee has developed an External Module, the terms and +conditions of this Agreement do not apply to said External Module, that +may be distributed under a separate license agreement. + + + 5.3.4 COMPATIBILITY WITH THE GNU GPL + +The Licensee can include a code that is subject to the provisions of one +of the versions of the GNU GPL in the Modified or unmodified Software, +and distribute that entire code under the terms of the same version of +the GNU GPL. + +The Licensee can include the Modified or unmodified Software in a code +that is subject to the provisions of one of the versions of the GNU GPL, +and distribute that entire code under the terms of the same version of +the GNU GPL. + + + Article 6 - INTELLECTUAL PROPERTY + + + 6.1 OVER THE INITIAL SOFTWARE + +The Holder owns the economic rights over the Initial Software. Any or +all use of the Initial Software is subject to compliance with the terms +and conditions under which the Holder has elected to distribute its work +and no one shall be entitled to modify the terms and conditions for the +distribution of said Initial Software. + +The Holder undertakes that the Initial Software will remain ruled at +least by this Agreement, for the duration set forth in Article 4.2. + + + 6.2 OVER THE CONTRIBUTIONS + +The Licensee who develops a Contribution is the owner of the +intellectual property rights over this Contribution as defined by +applicable law. + + + 6.3 OVER THE EXTERNAL MODULES + +The Licensee who develops an External Module is the owner of the +intellectual property rights over this External Module as defined by +applicable law and is free to choose the type of agreement that shall +govern its distribution. + + + 6.4 JOINT PROVISIONS + +The Licensee expressly undertakes: + + 1. not to remove, or modify, in any manner, the intellectual property + notices attached to the Software; + + 2. to reproduce said notices, in an identical manner, in the copies + of the Software modified or not. + +The Licensee undertakes not to directly or indirectly infringe the +intellectual property rights of the Holder and/or Contributors on the +Software and to take, where applicable, vis-à-vis its staff, any and all +measures required to ensure respect of said intellectual property rights +of the Holder and/or Contributors. + + + Article 7 - RELATED SERVICES + +7.1 Under no circumstances shall the Agreement oblige the Licensor to +provide technical assistance or maintenance services for the Software. + +However, the Licensor is entitled to offer this type of services. The +terms and conditions of such technical assistance, and/or such +maintenance, shall be set forth in a separate instrument. Only the +Licensor offering said maintenance and/or technical assistance services +shall incur liability therefor. + +7.2 Similarly, any Licensor is entitled to offer to its licensees, under +its sole responsibility, a warranty, that shall only be binding upon +itself, for the redistribution of the Software and/or the Modified +Software, under terms and conditions that it is free to decide. Said +warranty, and the financial terms and conditions of its application, +shall be subject of a separate instrument executed between the Licensor +and the Licensee. + + + Article 8 - LIABILITY + +8.1 Subject to the provisions of Article 8.2, the Licensee shall be +entitled to claim compensation for any direct loss it may have suffered +from the Software as a result of a fault on the part of the relevant +Licensor, subject to providing evidence thereof. + +8.2 The Licensor's liability is limited to the commitments made under +this Agreement and shall not be incurred as a result of in particular: +(i) loss due the Licensee's total or partial failure to fulfill its +obligations, (ii) direct or consequential loss that is suffered by the +Licensee due to the use or performance of the Software, and (iii) more +generally, any consequential loss. In particular the Parties expressly +agree that any or all pecuniary or business loss (i.e. loss of data, +loss of profits, operating loss, loss of customers or orders, +opportunity cost, any disturbance to business activities) or any or all +legal proceedings instituted against the Licensee by a third party, +shall constitute consequential loss and shall not provide entitlement to +any or all compensation from the Licensor. + + + Article 9 - WARRANTY + +9.1 The Licensee acknowledges that the scientific and technical +state-of-the-art when the Software was distributed did not enable all +possible uses to be tested and verified, nor for the presence of +possible defects to be detected. In this respect, the Licensee's +attention has been drawn to the risks associated with loading, using, +modifying and/or developing and reproducing the Software which are +reserved for experienced users. + +The Licensee shall be responsible for verifying, by any or all means, +the suitability of the product for its requirements, its good working +order, and for ensuring that it shall not cause damage to either persons +or properties. + +9.2 The Licensor hereby represents, in good faith, that it is entitled +to grant all the rights over the Software (including in particular the +rights set forth in Article 5). + +9.3 The Licensee acknowledges that the Software is supplied "as is" by +the Licensor without any other express or tacit warranty, other than +that provided for in Article 9.2 and, in particular, without any warranty +as to its commercial value, its secured, safe, innovative or relevant +nature. + +Specifically, the Licensor does not warrant that the Software is free +from any error, that it will operate without interruption, that it will +be compatible with the Licensee's own equipment and software +configuration, nor that it will meet the Licensee's requirements. + +9.4 The Licensor does not either expressly or tacitly warrant that the +Software does not infringe any third party intellectual property right +relating to a patent, software or any other property right. Therefore, +the Licensor disclaims any and all liability towards the Licensee +arising out of any or all proceedings for infringement that may be +instituted in respect of the use, modification and redistribution of the +Software. Nevertheless, should such proceedings be instituted against +the Licensee, the Licensor shall provide it with technical and legal +assistance for its defense. Such technical and legal assistance shall be +decided on a case-by-case basis between the relevant Licensor and the +Licensee pursuant to a memorandum of understanding. The Licensor +disclaims any and all liability as regards the Licensee's use of the +name of the Software. No warranty is given as regards the existence of +prior rights over the name of the Software or as regards the existence +of a trademark. + + + Article 10 - TERMINATION + +10.1 In the event of a breach by the Licensee of its obligations +hereunder, the Licensor may automatically terminate this Agreement +thirty (30) days after notice has been sent to the Licensee and has +remained ineffective. + +10.2 A Licensee whose Agreement is terminated shall no longer be +authorized to use, modify or distribute the Software. However, any +licenses that it may have granted prior to termination of the Agreement +shall remain valid subject to their having been granted in compliance +with the terms and conditions hereof. + + + Article 11 - MISCELLANEOUS + + + 11.1 EXCUSABLE EVENTS + +Neither Party shall be liable for any or all delay, or failure to +perform the Agreement, that may be attributable to an event of force +majeure, an act of God or an outside cause, such as defective +functioning or interruptions of the electricity or telecommunications +networks, network paralysis following a virus attack, intervention by +government authorities, natural disasters, water damage, earthquakes, +fire, explosions, strikes and labor unrest, war, etc. + +11.2 Any failure by either Party, on one or more occasions, to invoke +one or more of the provisions hereof, shall under no circumstances be +interpreted as being a waiver by the interested Party of its right to +invoke said provision(s) subsequently. + +11.3 The Agreement cancels and replaces any or all previous agreements, +whether written or oral, between the Parties and having the same +purpose, and constitutes the entirety of the agreement between said +Parties concerning said purpose. No supplement or modification to the +terms and conditions hereof shall be effective as between the Parties +unless it is made in writing and signed by their duly authorized +representatives. + +11.4 In the event that one or more of the provisions hereof were to +conflict with a current or future applicable act or legislative text, +said act or legislative text shall prevail, and the Parties shall make +the necessary amendments so as to comply with said act or legislative +text. All other provisions shall remain effective. Similarly, invalidity +of a provision of the Agreement, for any reason whatsoever, shall not +cause the Agreement as a whole to be invalid. + + + 11.5 LANGUAGE + +The Agreement is drafted in both French and English and both versions +are deemed authentic. + + + Article 12 - NEW VERSIONS OF THE AGREEMENT + +12.1 Any person is authorized to duplicate and distribute copies of this +Agreement. + +12.2 So as to ensure coherence, the wording of this Agreement is +protected and may only be modified by the authors of the License, who +reserve the right to periodically publish updates or new versions of the +Agreement, each with a separate number. These subsequent versions may +address new issues encountered by Free Software. + +12.3 Any Software distributed under a given version of the Agreement may +only be subsequently distributed under the same version of the Agreement +or a subsequent version, subject to the provisions of Article 5.3.4. + + + Article 13 - GOVERNING LAW AND JURISDICTION + +13.1 The Agreement is governed by French law. The Parties agree to +endeavor to seek an amicable solution to any disagreements or disputes +that may arise during the performance of the Agreement. + +13.2 Failing an amicable solution within two (2) months as from their +occurrence, and unless emergency proceedings are necessary, the +disagreements or disputes shall be referred to the Paris Courts having +jurisdiction, by the more diligent Party. + + +Version 2.0 dated 2006-09-05. diff --git a/tools/gfx2crtc/README b/tools/gfx2crtc/README new file mode 100644 index 0000000..596349d --- /dev/null +++ b/tools/gfx2crtc/README @@ -0,0 +1,36 @@ +English Version at the end. + +Version Française (French Version) +---------------------------------------------------------------------- + gfx2crtc - utilitaire de convertion d'image vers image au format cpc +---------------------------------------------------------------------- + +gfx2crtc est un utilitaire qui permet la convertion d'image (format brut +lineaire 8bits par pixel ou PNG 1,2 ou 4 bits) dans un format compatible avec +l'Amstrad CPC et Plus. + +Logiciel Initial écrit par : +CloudStrife/Shinra (Quentin Carlier) <cloudstrife@cpcscene.com>. +Il est diffusé sur la Licence de Logiciel Libre CeCILL version 2, +Voire LICENCE pour plus d'information. +Voire le fichier AUTHORS pour voire la liste des differents Contributeur. + +Utilisation : +------------- + +English Version +---------------------------------------------------- + gfx2crtc - gfx convert tools to amstrad cpc format +---------------------------------------------------- + +gfx2crtc is a tools program for convert gfx (linear raw 8bits per pixel or +PNG 1,2 ou 4 bits) in a compatible format for Amstrad CPC and Plus. + +Initial Software write by : +CloudStrife/Shinra (Quentin Carlier) <cloudstrife@cpcscene.com>. +It is diffused on the CeCILL Free Software License v2, +See LICENSE for more information. +See AUTHORS for the list of Contributor. + +Usage : +-------
\ No newline at end of file diff --git a/tools/gfx2crtc/TODO b/tools/gfx2crtc/TODO new file mode 100644 index 0000000..a394ead --- /dev/null +++ b/tools/gfx2crtc/TODO @@ -0,0 +1,9 @@ +- Ajouter un mode de convertion lineaire (équivalent à R9=0) +- Extraire la palette d'un png si palette CPC +- Gestion du registre 13 // Fait r47 +- Possibilité de ne garder qu'un pixel sur 2 (exemple pour Mode 0) +- Possibilité de ne garder qu'une ligne sur 2 (exemple pour Mode 2) +- Sprite plus (4bits/pixel, 1pixel/octets, 16x16, gauche a droite, haut en bas) +- Gestion mode entrelacé +- Visualisation ? +- Version Drag'n'drop pour windows ? diff --git a/tools/gfx2crtc/libraw2crtc.c b/tools/gfx2crtc/libraw2crtc.c new file mode 100644 index 0000000..b7d2f97 --- /dev/null +++ b/tools/gfx2crtc/libraw2crtc.c @@ -0,0 +1,186 @@ +/* GFX2CRTC - libraw2crtc.c + * CloudStrife - 20080921 + * Diffusé sous licence libre CeCILL v2 + * Voire LICENCE + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +unsigned short addrCalc(unsigned char vcc, unsigned char rcc, unsigned char hcc, unsigned char cclk, unsigned char r1, unsigned char r12, unsigned char r13) +{ + unsigned short MA; + unsigned short addr; + + //MA = vcc*r1 + hcc + (0x0C)*256; + MA = vcc*r1 + hcc + r12*256 + r13; + addr = cclk | ((MA & 0x03FF) << 1); + addr = addr | ((rcc & 0x07) << 11); + addr = addr | ((MA & 0x3000) << 2); + + return addr; +} + +unsigned char mode0interlace(unsigned char *x) +{ + unsigned char mode0pixel[] = {0, 64, 4, 68, 16, 80, 20, 84, 1, 65, 5, 69, 17, 81, 21, 85}; + return mode0pixel[x[0]] << 1 | mode0pixel[x[1]]; +} + +unsigned char mode1interlace(unsigned char *x) +{ + unsigned char mode1pixel[] = {0, 16, 1, 17}; + return mode1pixel[x[0]] << 3 | mode1pixel[x[1]] << 2 | mode1pixel[x[2]] << 1 | mode1pixel[x[3]]; +} + +unsigned char mode2interlace(unsigned char *x) +{ + unsigned char out = 0; + int i; + for(i = 0; i < 8; i++) out += ((x[7-i]&1) << i); + return out; +} + +unsigned char mode3interlace(unsigned char *x) +{ + unsigned char mode3pixel[] = {0, 16, 1, 17}; + return mode3pixel[x[0]] << 3 | mode3pixel[x[1]] << 2; +} + +unsigned char (*ptrMode)(unsigned char *x); + +unsigned char *raw2crtc(unsigned char *input, unsigned short width, unsigned short height, unsigned char mode, unsigned char r9, unsigned long *outSize, unsigned char *r1, unsigned char r12, unsigned char r13, unsigned char* reg6) +{ + unsigned char *outBuffer; + unsigned char *tmpBuffer; + unsigned char *allocationBuffer; + unsigned short minAddr = 0; + unsigned char minAddrIsDefined = 0; + unsigned short maxAddr; + + unsigned char nbPixPerByte; + int y,x; + + switch(mode) + { + case 0: + { + *r1 = (width+3)/4; + nbPixPerByte = 2; + ptrMode = mode0interlace; + break; + } + case 1: + { + *r1 = (width+7)/8; + nbPixPerByte = 4; + ptrMode = mode1interlace; + break; + } + case 2: + { + *r1 = (width+15)/16; + nbPixPerByte = 8; + ptrMode = mode2interlace; + break; + } + case 3: + { + *r1 = (width+3)/4; + nbPixPerByte = 2; + ptrMode = mode3interlace; + break; + } + default: + { + exit(4); + } + } + + tmpBuffer = (unsigned char*)malloc(0xFFFF); + if (tmpBuffer == NULL) + { + printf("Allocation tmpBuffer raté\n"); + exit(4); + } + + allocationBuffer = (unsigned char*)calloc(0xFFFF, 1); + if(allocationBuffer == NULL) + { + printf("Allocation allocationBuffer raté\n"); + exit(4); + } + + { + unsigned char r6; + unsigned char vcc,rcc,hcc,cclk; + r6 = (height+r9)/(r9+1); + *reg6 = r6; + + for(vcc = 0; vcc < r6; vcc++) + { + for(rcc = 0; rcc < (r9+1); rcc++) + { + for(hcc = 0; hcc < *r1; hcc++) + { + for(cclk = 0; cclk < 2; cclk++) + { + x = (hcc << 1 | cclk); + y = vcc*(r9+1) + rcc; + *(tmpBuffer + addrCalc(vcc, rcc, hcc, cclk, *r1, r12, r13)) = (*ptrMode)(input + y*width + x*nbPixPerByte); + *(allocationBuffer + addrCalc(vcc, rcc, hcc, cclk, *r1, r12, r13)) += 1; + } + } + } + } + } + + { + unsigned short i; + for(i = 0; i < 0xFFFF; i++) + { + if(*(allocationBuffer + i) > 1) + { + printf("Attention : Ecriture multiple a l'adresse mémoire %d\n",i); + } + if(*(allocationBuffer + i) > 0) + { + maxAddr = i; + } + if((*(allocationBuffer + i) == 1) && (minAddrIsDefined == 0)) + { + minAddr = i; + minAddrIsDefined = 1; + } + } + } + + *outSize = (maxAddr + 1) - minAddr; + + outBuffer = (unsigned char*)malloc((*outSize)); + if (outBuffer == NULL) + { + printf("Allocation outBuffer raté\n"); + exit(4); + } + + { + unsigned char *ptrTmp; + unsigned char *ptrOut; + unsigned short i; + ptrTmp = tmpBuffer + minAddr; + ptrOut = outBuffer; + + for(i = minAddr; i <= maxAddr; i++) + { + *(ptrOut++) = *(ptrTmp++); + } + } + free(tmpBuffer); + tmpBuffer = NULL; + free(allocationBuffer); + allocationBuffer = NULL; + + return outBuffer; +} diff --git a/tools/gfx2crtc/libraw2crtc.h b/tools/gfx2crtc/libraw2crtc.h new file mode 100644 index 0000000..8bab0e5 --- /dev/null +++ b/tools/gfx2crtc/libraw2crtc.h @@ -0,0 +1,12 @@ +/* GFX2CRTC - libraw2crtc.h + * CloudStrife - 20080921 + * Diffusé sous licence libre CeCILL v2 + * Voire LICENCE + */ + +#ifndef LIBRAW2CRTC_H +#define LIBRAW2CRTC_H 1 + +unsigned char * raw2crtc(unsigned char *input, unsigned short width, unsigned short height, unsigned char mode, unsigned char r9, unsigned long *outSize, unsigned char *r1, unsigned char r12, unsigned char r13, unsigned char* reg6); + +#endif diff --git a/tools/gfx2crtc/makefile b/tools/gfx2crtc/makefile new file mode 100644 index 0000000..8e4b2e2 --- /dev/null +++ b/tools/gfx2crtc/makefile @@ -0,0 +1,29 @@ +ALL= raw2crtc png2crtc + + +CC=gcc + +# Detect gcc2, if we're running it, use gnu9x standard instead of c99... +GCC_MAJOR = $(shell $(CC) -v 2>&1 |grep version |cut -d' ' -f3 |cut -d'.' -f1) + +ifeq ($(GCC_MAJOR),2) + CCFLAGS=-Os -W -Wall -std=gnu9x -g +else + CCFLAGS=-Os -W -Wall -std=c99 -g +endif + +#CCFLAGS=-O3 -W -Wall -pedantic -ansi + +all: $(ALL) + +clean : + rm -f a.out *.o core $(ALL) + +raw2crtc : raw2crtc.o libraw2crtc.o + $(CC) $(CCFLAGS) raw2crtc.o libraw2crtc.o -o raw2crtc + +png2crtc : png2crtc.o libraw2crtc.o + $(CC) $(CCFLAGS) png2crtc.o libraw2crtc.o -o png2crtc -lpng -lz + +.c.o : + $(CC) $(CCFLAGS) -c $< diff --git a/tools/gfx2crtc/png2crtc.c b/tools/gfx2crtc/png2crtc.c new file mode 100644 index 0000000..9742960 --- /dev/null +++ b/tools/gfx2crtc/png2crtc.c @@ -0,0 +1,204 @@ +/* GFX2CRTC - png2crtc.c + * CloudStrife - 20080921 + * Diffusé sous licence libre CeCILL v2 + * Voire LICENCE + */ + +#include <stdio.h> +#include <stdlib.h> +#include <png.h> +#include "libraw2crtc.h" + +#define ERROR 1 + +int main(int argc, char **argv) +{ + FILE *inFile, *outFile; + unsigned char *inBuffer, *outBuffer; + unsigned long outSize; + + unsigned char r1, r6, r9, r12, r13; + png_uint_32 width; + png_uint_32 height; + int bitdepth; + int colorType; + unsigned char mode; + unsigned char forcemode = 0; + unsigned char forcer12 = 0, forcer13 = 0; + + unsigned char header[8]; + unsigned char is_png; + + unsigned int y; + png_structp png_ptr; + png_infop info_ptr; + png_infop end_info; + + png_bytep * ptrRow; + + // We have to use this temporary variable insted of scanning directly into + // 8 bit integers using %hhu because mingW, relying on MSVCRT, is not C99 + // compliant and can't handle this "modern" stuff. + unsigned int stupid_mingw_isnt_c99; + + if((argc != 3) && (argc != 4) && (argc != 5) && (argc != 6) && (argc != 7)) + { + printf("Utilisation : %s input_filename output_filename [registre9] [mode] " + "[r12] [r13]\n",argv[0]); + exit(0); + } + + inFile = fopen(argv[1],"rb"); + + if(argc >= 4) + /*{sscanf(argv[3],"%hhu",&r9);}*/ + {sscanf(argv[3],"%u",&stupid_mingw_isnt_c99); r9=stupid_mingw_isnt_c99;} + else + {r9 = 7;} + if(argc >= 5) + { + // sscanf(argv[4],"%hhu",&mode); + sscanf(argv[4],"%u",&stupid_mingw_isnt_c99); mode = stupid_mingw_isnt_c99; + forcemode = 1; + if(mode > 3) puts("mode doit être compris entre 0 et 3"); + mode = mode & 3; + } + + // Registre r12 et r13 par défaut : + r12 = 0x0C; + r13 = 0x00; + + if(argc >= 6) + { + // sscanf(argv[5],"%hhu",&r12); + sscanf(argv[5],"%u",&stupid_mingw_isnt_c99); r12 = stupid_mingw_isnt_c99; + forcer12 = 1; + } + if(argc >= 7) + { + // sscanf(argv[6],"%hhu",&r13); + sscanf(argv[6],"%u",&stupid_mingw_isnt_c99); r13=stupid_mingw_isnt_c99; + forcer13 = 1; + } + + if (inFile == NULL) + { + printf("Fichier Inexistant\n"); + exit(1); + } + + fread(header, 1, 8, inFile); + is_png = !png_sig_cmp(header, 0, 8); + if (!is_png) + { + printf("Ce n'est pas un png\n"); + exit(2); + } + + png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, (png_voidp)NULL, NULL, + NULL); + if (!png_ptr) return (ERROR); + + info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) + { + png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL); + return (ERROR); + } + + end_info = png_create_info_struct(png_ptr); + if (!end_info) + { + png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL); + return (ERROR); + } + + if (setjmp(png_jmpbuf(png_ptr))) + { + png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); + fclose(inFile); + return (ERROR); + } + + png_init_io(png_ptr, inFile); + png_set_sig_bytes(png_ptr, 8); + + png_read_info(png_ptr, info_ptr); + + png_get_IHDR(png_ptr, info_ptr, &width, &height, &bitdepth, &colorType, NULL, + NULL, NULL); + + if(!((colorType == PNG_COLOR_TYPE_GRAY) + || (colorType == PNG_COLOR_TYPE_PALETTE))) + { + puts("Ce PNG n'est pas dans un format exploitable " + "(niveaux de gris ou palette)"); + return (ERROR); + } + + if(forcemode == 0) + { + switch(bitdepth) + { + case 1: + { + mode = 2; + break; + } + case 2: + { + mode = 1; + break; + } + case 4: + { + mode = 0; + break; + } + default: + { + puts("Ce PNG n'est pas dans un format exploitable" + "(bitdepth = 1, 2 ou 4)"); + return (ERROR); + } + } + } + + png_set_packing(png_ptr); /* Convertir en mode 1 pixel par octets */ + png_read_update_info(png_ptr, info_ptr); + + inBuffer = (unsigned char*)malloc(width*height); + if (inBuffer == NULL) + { + printf("Allocation inBuffer raté\n"); + exit(3); + } + + ptrRow = (png_bytep*)malloc(sizeof(png_bytep)*height); + for(y = 0; y < height; y++) + { + ptrRow[y] = (inBuffer + width*y); + } + + png_read_image(png_ptr, ptrRow); + + outBuffer = raw2crtc(inBuffer, width, height, mode, r9, &outSize, &r1, r12, r13, &r6); + + printf("Taille de l'écran de sortie : %lu\n",outSize); + printf("Mode = %d Largeur = %d Hauteur = %d R1 = %d R9 = %d R6 = %d\n",mode,(int)width,(int)height,r1,r9,r6); + + outFile = fopen(argv[2], "wb"); + fwrite(outBuffer, 1, outSize, outFile); + fclose(outFile); + + png_read_end(png_ptr, end_info); + png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); + + free(inBuffer); + inBuffer = NULL; + + free(outBuffer); + outBuffer = NULL; + + return 0; +} diff --git a/tools/gfx2crtc/raw2crtc.c b/tools/gfx2crtc/raw2crtc.c new file mode 100644 index 0000000..c8738dc --- /dev/null +++ b/tools/gfx2crtc/raw2crtc.c @@ -0,0 +1,86 @@ +/* GFX2CRTC - raw2crtc.c + * CloudStrife - 20080921 + * Diffusé sous licence libre CeCILL v2 + * Voire LICENCE + */ + +#include <stdio.h> +#include <stdlib.h> +#include "libraw2crtc.h" + +int main(int argc, char **argv) +{ + FILE *inFile, *outFile; + unsigned char *inBuffer, *outBuffer; + unsigned long inSize; + unsigned long outSize; + + unsigned char r1, r6, r9; + unsigned short width; + unsigned short height; + unsigned char mode; + + if((argc != 6) && (argc != 7)) + { + printf("Utilisation : %s input_filename output_filename width height mode [registre9]\n",argv[0]); + printf("Exemple : Convertir une image en 176 sur 224 en mode 0 :\n"); + printf("%s image.raw image.scr 176 224 0\n",argv[0]); + printf("Exemple : Convertir une image en 256 sur 128 en mode 1 avec R9 = 3 :\n"); + printf("%s image.raw image.scr 256 128 1 3\n",argv[0]); + exit(0); + } + + inFile = fopen(argv[1],"rb"); + + sscanf(argv[3],"%hud",&width); + sscanf(argv[4],"%hud",&height); + sscanf(argv[5],"%hhud",&mode); + if(mode > 3) printf("mode doit être compris entre 0 et 3"); + mode = mode & 3; + if(argc == 7) + {sscanf(argv[6],"%hhud",&r9);} + else + {r9 = 7;} + + if (inFile == NULL) + { + printf("Fichier Inexistant\n"); + exit(1); + } + + fseek(inFile, 0, SEEK_END); + inSize = ftell(inFile); + rewind(inFile); + + if (inSize != (unsigned long)(width*height)) + { + printf("Attention ! Mauvaise taille du fichier d'entré\n"); + } + + inBuffer = (unsigned char*)malloc(inSize); + if (inBuffer == NULL) + { + printf("Allocation inBuffer raté\n"); + exit(3); + } + + fread(inBuffer, 1, inSize, inFile); + fclose(inFile); + + outBuffer = raw2crtc(inBuffer, width, height, mode, r9, &outSize, &r1, 0, 0, &r6); + + printf("Taille de l'écran de sortie : %lu\n",outSize); + printf("Mode = %d Largeur = %d Hauteur = %d R1 = %d R9 = %d R6 = %d\n",mode,width,height,r1,r9,r6); + + outFile = fopen(argv[2], "wb"); + fwrite(outBuffer, 1, outSize, outFile); + fclose(outFile); + + free(inBuffer); + inBuffer = NULL; + + free(outBuffer); + outBuffer = NULL; + + return 0; +} diff --git a/tools/hex2bin-2.0/Makefile b/tools/hex2bin-2.0/Makefile new file mode 100755 index 0000000..bc5e71a --- /dev/null +++ b/tools/hex2bin-2.0/Makefile @@ -0,0 +1,42 @@ +# Makefile hex2bin/mot2bin +SRCDIR = src +BINDIR = bin +OBJDIR = obj +TGTDIR = $(BINDIR) +B_SRCFILES= $(foreach F, hex2bin.c common.c libcrc.c binary.c, $(SRCDIR)/$(F)) +B_OBJFILES= $(foreach F, hex2bin.o common.o libcrc.o binary.o, $(OBJDIR)/$(F)) +M_SRCFILES= $(foreach F, mot2bin.c common.c libcrc.c binary.c, $(SRCDIR)/$(F)) +M_OBJFILES= $(foreach F, mot2bin.o common.o libcrc.o binary.o, $(OBJDIR)/$(F)) + +# For generating documentation (hex2bin.1, select the second line) +# -- You will require pod2man installed for this to work +TGT_FILES = $(foreach F, hex2bin mot2bin, $(TGTDIR)/$(F)) +#TGT_FILES = $(foreach F, hex2bin mot2bin hex2bin.1, $(TGTDIR)/$(F)) + +CPFLAGS = -std=gnu99 -O3 -fsigned-char -Wall -pedantic -fcommon +# Compile +all: objectdir $(TGT_FILES) + +$(OBJDIR)/%.o: $(SRCDIR)/%.c + gcc -c $(CPFLAGS) $< -o $@ + +objectdir: + @echo "Creating directory $(OBJDIR)..." + mkdir -p $(OBJDIR) + +$(TGTDIR)/hex2bin.1: $(SRCDIR)/hex2bin.pod + pod2man $(SRCDIR)/hex2bin.pod > $(TGTDIR)/hex2bin.1 + +$(TGTDIR)/hex2bin: $(B_OBJFILES) + gcc $(CPFLAGS) -o $(TGTDIR)/hex2bin $(B_OBJFILES) + +$(TGTDIR)/mot2bin: $(M_OBJFILES) + gcc $(CPFLAGS) -o $(TGTDIR)/mot2bin $(M_OBJFILES) + +clean: + @echo "Removing objects directory $(OBJDIR)/ ..." + @rm -rf $(OBJDIR) + +cleanall: clean + @echo "Removing binary files in $(BINDIR)/ ..." + @rm -f $(BINDIR)/* diff --git a/tools/hex2bin-2.0/bin/.deleteme b/tools/hex2bin-2.0/bin/.deleteme new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/tools/hex2bin-2.0/bin/.deleteme diff --git a/tools/hex2bin-2.0/doc/CRC list.txt b/tools/hex2bin-2.0/doc/CRC list.txt new file mode 100644 index 0000000..8549799 --- /dev/null +++ b/tools/hex2bin-2.0/doc/CRC list.txt @@ -0,0 +1,542 @@ +http://regregex.bbcmicro.net/crc-catalogue.htm + +CRC-8 +07 +<crc-8-atm> + Name : "CRC-8" + Width : 8 + Poly : 07 + Init : 00 + RefIn : False + RefOut : False + XorOut : 00 + Check : F4 + +<crc-8-itu> + Name : "CRC-8/ITU" + Width : 8 + Poly : 07 + Init : 00 + RefIn : False + RefOut : False + XorOut : 55 + Check : A1 + +<crc-8-rohc> + Name : "CRC-8/ROHC" + Width : 8 + Poly : 07 + Init : FF + RefIn : True + RefOut : True + XorOut : 0 + Check : D0 + +39 +<crc-8-darc> + Name : "CRC-8/DARC" + Width : 8 + Poly : 39 + Init : 00 + RefIn : True + RefOut : True + XorOut : 00 + Check : 15 + +1D +<crc-8-icode> + Name : "CRC-8/I-CODE" + Width : 8 + Poly : 1D + Init : FD + RefIn : False + RefOut : False + XorOut : 00 + Check : 7E + +<crc-8-j1850> + Name : "CRC-8/J1850" (new entry) + Width : 8 + Poly : 1D + Init : FF + RefIn : False + RefOut : False + XorOut : FF + Check : 4B + +31 +<crc-8-maxim> + Name : "CRC-8/MAXIM" + Alias : "DOW-CRC" + Width : 8 + Poly : 31 + Init : 00 + RefIn : True + RefOut : True + XorOut : 00 + Check : A1 + +9B +<crc-8-wcdma> + Name : "CRC-8/WCDMA" + Width : 8 + Poly : 9B + Init : 00 + RefIn : True + RefOut : True + XorOut : 00 + Check : 25 + +8D +<crc-8-ccitt> + Name : "CRC-8/CCITT" (new entry) 1-Wire? + Width : 8 + Poly : 8D + Init : 00? + RefIn : False? + RefOut : False? + XorOut : 00? + Check : D2 + +D5 +<crc-8> + Name : "CRC-8" (new entry) + Width : 8 + Poly : D5 + Init : 00? + RefIn : False? + RefOut : False? + XorOut : 00? + Check : BC + +CRC-16 +8005 +<crc-16> + Name : "ARC" + Alias : "CRC-16" + Alias : "CRC-IBM" + Alias : "CRC-16/ARC" + Alias : "CRC-16/LHA" + Width : 16 + Poly : 8005 + Init : 0000 + RefIn : True + RefOut : True + XorOut : 0000 + Check : BB3D + +<crc-16-buypass> + Name : "CRC-16/BUYPASS" + Alias : "CRC-16/VERIFONE" + Width : 16 + Poly : 8005 + Init : 0000 + RefIn : False + RefOut : False + XorOut : 0000 + Check : FEE8 + +<crc-dds-110> + Name : "CRC-16/DDS-110" + Width : 16 + Poly : 8005 + Init : 800D + RefIn : False + RefOut : False + XorOut : 0000 + Check : 9ECF + XCheck : CFE9 + +<crc-16-maxim> + Name : "CRC-16/MAXIM" + Width : 16 + Poly : 8005 + Init : 0000 + RefIn : True + RefOut : True + XorOut : FFFF + Check : 44C2 + +<crc-usb> + Name : "CRC-16/USB" + Width : 16 + Poly : 8005 + Init : FFFF + RefIn : True + RefOut : True + XorOut : FFFF + Check : B4C8 + +<crc-modbus> + Name : "MODBUS" + Width : 16 + Poly : 8005 + Init : FFFF + RefIn : True + RefOut : True + XorOut : 0000 + Check : 4B37 + +1021 +<crc-ccitt-1d0f> + Name : "CRC-16/AUG-CCITT" + Alias : "CRC-16/SPI-FUJITSU" + Width : 16 + Poly : 1021 + Init : 1D0F + RefIn : False + RefOut : False + XorOut : 0000 + Check : E5CC + +<crc-ccitt-ffff> + Name : "CRC-16/CCITT-FALSE" + Width : 16 + Poly : 1021 + Init : FFFF + RefIn : False + RefOut : False + XorOut : 0000 + Check : 29B1 + +<crc-genibus> + Name : "CRC-16/GENIBUS" + Alias : "CRC-16/I-CODE" + Alias : "CRC-16/DARC" + Width : 16 + Poly : 1021 + Init : FFFF + RefIn : False + RefOut : False + XorOut : FFFF + Check : D64E + +<crc-ccitt-xmodem> + Name : "XMODEM" + Alias : "ZMODEM" + Alias : "CRC-16/ACORN" + Width : 16 + Poly : 1021 + Init : 0000 + RefIn : False + RefOut : False + XorOut : 0000 + Check : 31C3 + +<crc-mcrf4xx> + Name : "CRC-16/MCRF4XX" + Width : 16 + Poly : 1021 + Init : FFFF + RefIn : True + RefOut : True + XorOut : 0000 + Check : 6F91 + +<crc-riello> + Name : "CRC-16/RIELLO" + Width : 16 + Poly : 1021 + Init : B2AA + RefIn : True + RefOut : True + XorOut : 0000 + Check : 63D0 + + <crc-ccitt-kermit> + Name : "KERMIT" + Alias : "CRC-16/CCITT" + Alias : "CRC-16/CCITT-TRUE" + Alias : "CRC-CCITT" + Width : 16 + Poly : 1021 + Init : 0000 + RefIn : True + RefOut : True + XorOut : 0000 + Check : 2189 + XCheck : 8921 + +<crc-x25> + Name : "X-25" + Alias : "CRC-16/IBM-SDLC" + Alias : "CRC-16/ISO-HDLC" + Width : 16 + Poly : 1021 + Init : FFFF + RefIn : True + RefOut : True + XorOut : FFFF + Check : 906E + XCheck : 6E90 + +0589 +<crc-dect-r> + Name : "CRC-16/DECT-R" + Alias : "R-CRC-16" + Width : 16 + Poly : 0589 + Init : 0000 + RefIn : False + RefOut : False + XorOut : 0001 + Check : 007E + +<crc-dect-x> + Name : "CRC-16/DECT-X" + Alias : "X-CRC-16" + Width : 16 + Poly : 0589 + Init : 0000 + RefIn : False + RefOut : False + XorOut : 0000 + Check : 007F + +3D65 +<crc-dnp> + Name : "CRC-16/DNP" + Width : 16 + Poly : 3D65 + Init : 0000 + RefIn : True + RefOut : True + XorOut : FFFF + Check : EA82 + XCheck : 82EA + +<crc-en-13757> + Name : "CRC-16/EN-13757" + Width : 16 + Poly : 3D65 + Init : 0000 + RefIn : False + RefOut : False + XorOut : FFFF + Check : C2B7 + +8BB7 +<crc-t10-dif> + Name : "CRC-16/T10-DIF" + Width : 16 + Poly : 8BB7 + Init : 0000 + RefIn : False + RefOut : False + XorOut : 0000 + Check : D0DB + +A097 +<crc-teledisk> + Name : "CRC-16/TELEDISK" + Width : 16 + Poly : A097 + Init : 0000 + RefIn : False + RefOut : False + XorOut : 0000 + Check : 0FB3 + +CRC-24 +864CFB +<crc-24> + Name : "CRC-24" + Alias : "CRC-24/OPENPGP" + Width : 24 + Poly : 864CFB + Init : B704CE + RefIn : False + RefOut : False + XorOut : 000000 + Check : 21CF02 + +5D6DCB +<crc-24-flexray-a> + Name : "CRC-24/FLEXRAY-A" + Width : 24 + Poly : 5D6DCB + Init : FEDCBA + RefIn : False + RefOut : False + XorOut : 000000 + Check : 7979BD + +<crc-24-flexray-b> + Name : "CRC-24/FLEXRAY-B" + Width : 24 + Poly : 5D6DCB + Init : ABCDEF + RefIn : False + RefOut : False + XorOut : 000000 + Check : 1F23B8 + +CRC-32 +04C11DB7 +<crc-32> + Name : "CRC-32" + Alias : "CRC-32/ADCCP" + Alias : "PKZIP" + Width : 32 + Poly : 04C11DB7 + Init : FFFFFFFF + RefIn : True + RefOut : True + XorOut : FFFFFFFF + Check : CBF43926 + +<crc-32-bzip2> + Name : "CRC-32/BZIP2" + Alias : "B-CRC-32" + Width : 32 + Poly : 04C11DB7 + Init : FFFFFFFF + RefIn : False + RefOut : False + XorOut : FFFFFFFF + Check : FC891918 + +<crc-32-mpeg-2> + Name : "CRC-32/MPEG-2" + Width : 32 + Poly : 04C11DB7 + Init : FFFFFFFF + RefIn : False + RefOut : False + XorOut : 00000000 + Check : 0376E6E7 + +<crc-32-posix> + Name : "CRC-32/POSIX" + Alias : "CKSUM" + Width : 32 + Poly : 04C11DB7 + Init : 00000000 + RefIn : False + RefOut : False + XorOut : FFFFFFFF + Check : 765E7680 + LCheck : 377A6011 + +<crc-32-jamcrc> + Name : "JAMCRC" + Width : 32 + Poly : 04C11DB7 + Init : FFFFFFFF + RefIn : True + RefOut : True + XorOut : 00000000 + Check : 340BC6D9 + +1EDC6F41 +<crc-32-C> + Name : "CRC-32C" + Alias : "CRC-32/ISCSI" + Alias : "CRC-32/CASTAGNOLI" + Width : 32 + Poly : 1EDC6F41 + Init : FFFFFFFF + RefIn : True + RefOut : True + XorOut : FFFFFFFF + Check : E3069283 + +A833982B +<crc-32-D> + Name : "CRC-32D" + Width : 32 + Poly : A833982B + Init : FFFFFFFF + RefIn : True + RefOut : True + XorOut : FFFFFFFF + Check : 87315576 + +741B8CD7 +<crc-32-K> (new entry) + Name : "CRC-32K" + Alias : "CRC-32/KOOPMAN" + Width : 32 + Poly : 741B8CD7 + Init : 00000000? + RefIn : False? + RefOut : False? + XorOut : 00000000? + Check : 085A3197 ? + +814141AB +<crc-32-Q> + Name : "CRC-32Q" + Width : 32 + Poly : 814141AB + Init : 00000000 + RefIn : False + RefOut : False + XorOut : 00000000 + Check : 3010BF7F + +000000AF +<crc-32-xfer> + Name : "XFER" + Width : 32 + Poly : 000000AF + Init : 00000000 + RefIn : False + RefOut : False + XorOut : 00000000 + Check : BD0BE338 + +CRC-40 +<crc-40-gsm> + Name : "CRC-40/GSM" + Width : 40 + Poly : 0004820009 + Init : 0000000000 + RefIn : False + RefOut : False + XorOut : 0000000000 + Check : 2BE9B039B9 + +CRC-64 +42F0E1EBA9EA3693 +<crc-64> + Name : "CRC-64" + Width : 64 + Poly : 42F0E1EBA9EA3693 + Init : 0000000000000000 + RefIn : False + RefOut : False + XorOut : 0000000000000000 + Check : 6C40DF5F0B497347 + +<crc-64-we> + Name : "CRC-64/WE" + Width : 64 + Poly : 42F0E1EBA9EA3693 + Init : FFFFFFFFFFFFFFFF + RefIn : False + RefOut : False + XorOut : FFFFFFFFFFFFFFFF + Check : 62EC59E3F1A4F00A + +000000000000001B +<crc-64-1b> + Name : "CRC-64/1B" (New entry) + Width : 64 + Poly : 000000000000001B + Init : 0000000000000000 + RefIn : True + RefOut : True + XorOut : 0000000000000000 + Check : 46A5A9388A5BEFFE + +AD93D23594C935A9 +<crc-64-jones> + Name : "CRC-64/Jones" (New entry) + Width : 64 + Poly : AD93D23594C935A9 + Init : FFFFFFFFFFFFFFFF + RefIn : True + RefOut : True + XorOut : 0000000000000000 + Check : CAA717168609F281 diff --git a/tools/hex2bin-2.0/doc/ChangeLog_hex2bin b/tools/hex2bin-2.0/doc/ChangeLog_hex2bin new file mode 100644 index 0000000..3339250 --- /dev/null +++ b/tools/hex2bin-2.0/doc/ChangeLog_hex2bin @@ -0,0 +1,57 @@ +(UTF8 encoding) + +- hex2bin 1.0.12 - 20141122 Simone Fratini + small feature added + 20141121 Slucx + added line for removing extra CR when entering file name at run time. + 20141008 JP + removed junk code + +- hex2bin 1.0.11 - 20141005 Jacques Pelletier + added option to support byte-swapped hex used by Microchip's MPLAB IDE + corrected bug caused by extra LF at end or within file + +- hex2bin 1.0.10 - 20120509 Yoshimasa Nakane + modified error checking (also for output file, JP) + modified option parser (JP) + +- hex2bin 1.0.9 - 20120125 - Danny Schneider + Added code for filling a binary file to a given Max_Length relative to + Starting Address if Max-Address is larger than Highest-Address + +- hex2bin 1.0.8 - 20100402 - Jacques Pelletier + Fixed a bug with physical address calculation with extended linear address records + ADDRESS_MASK is now calculated from MEMORY_SIZE + +- hex2bin 1.0.7 - 20091212 - Jacques Pelletier + Fixed the crash on 0 byte length data records + +- hex2bin 1.0.6 - 20080103 - Jacques Pelletier + Fixed a bug when generating binary files near the end of the buffer + +- hex2bin 1.0.5 - 20071005 - PaweÅ‚ Grabski - + Improved parsing of options. + +- hex2bin 1.0.4 - 20050126 - Jacques Pelletier - + Corrected the conversion LF -> CR+LF bug + applied patch for correcting the incorrect handling of + extended segment address record + added the Rockwell checksum extensions, and modified them a bit to allow + other types later. + +- hex2bin 1.0.3 - 20040617 - Alf Lacis - + Added pad byte (may not always want FF). + Added 'break;' to remove GNU compiler warning about label at + end of compound statement + Added PROGRAM & VERSION strings. + +- hex2bin 1.0.2 - + Corrected Bug in checksum verification + +- hex2bin 1.0.1 - + Added checking for memory indexing out of bound. + Added segmented and linear extended addressing in hex2bin. + Corrected an error: & were interverted with && (and bitwise, logical and). + +- hex2bin 1.0.0 - + Initial release diff --git a/tools/hex2bin-2.0/doc/ChangeLog_mot2bin b/tools/hex2bin-2.0/doc/ChangeLog_mot2bin new file mode 100644 index 0000000..4e3c7b3 --- /dev/null +++ b/tools/hex2bin-2.0/doc/ChangeLog_mot2bin @@ -0,0 +1,49 @@ +(UTF8 encoding tab = 4) +- mot2bin 1.0.12 - 20141122 Simone Fratini + small feature added + 20141121 Slucx + added line for removing extra CR when entering file name at run time. + +- mot2bin 1.0.11 - 20141005 Jacques Pelletier + added option to support byte-swapped hex used by Microchip's MPLAB IDE + corrected bug caused by extra LF at end or within file + +- mot2bin 1.0.10 - 20120509 Yoshimasa Nakane + modified error checking (also for output file, JP) + +- mot2bin 1.0.9 - 20120125 - Danny Schneider + Added code for filling a binary file to a given Max_Length relative to + Starting Address if Max-Address is larger than Highest-Address + (JP) corrected a bug in the checksum checking + (JP) added code for record types 0,5,7,8,9 + +- mot2bin 1.0.8 - 20100402 - Jacques Pelletier + ADDRESS_MASK is now calculated from MEMORY_SIZE + +- mot2bin 1.0.7 - 20091212 - Jacques Pelletier + Fixed the crash on 0 byte length data records + +- mot2bin 1.0.6 - 20080103 - Jacques Pelletier + Corrected a bug when generating a binary file near the end of the buffer. + +- mot2bin 1.0.5 - 20071005 - PaweÅ‚ Grabski - + Improved parsing of options (same code as hex2bin). + +- mot2bin 1.0.4 - 20050128 - Jacques Pelletier - + Modified the checksum code to be able to generate other checksum types + later (ex. CRC). + +- mot2bin 1.0.3 - 20041026 - Scott A. Mintz - + Modified the MOT2BIN file to compute a checksum over a range using + 8bit, 16bit little endian, or 16bit big endian and optionally forcing + the checksum to a specific value by modifying a memory location. + +- mot2bin 1.0.2 - 20040617 - Alf Lacis - + Added pad byte (may not always want FF). + Added initialisation to Checksum to remove GNU + compiler warning about possible uninitialised usage + Added 2x'break;' to remove GNU compiler warning about label at + end of compound statement + Added PROGRAM & VERSION strings. + +- no previous ChangeLog - diff --git a/tools/hex2bin-2.0/doc/README b/tools/hex2bin-2.0/doc/README new file mode 100644 index 0000000..78ee7e7 --- /dev/null +++ b/tools/hex2bin-2.0/doc/README @@ -0,0 +1,225 @@ +Yet Another Hex to bin converter + +It can handle the extended Intel hex format in segmented and linear address +modes. Records need not be sorted and there can be gaps between records. + +Some hex files are produced by compilers. They generate objects files for each +module in a project, and when the linker generates the final hex file, the +object files are stored within the hex files, but modules can appear not +necessary in order of address. + +How does it work? + +Hex2bin/mot2bin allocates a 4 MBytes buffer and just place the converted bytes +in its buffer. At the end, the buffer is written to disk. Using a buffer elimi- +nates the need to sort records. If the option l is used (3), the buffer will be +allocated with the maximum size specified if over 4Mbytes. + +Before reading the hex file, the buffer is filled with a default value. These +padding bytes are all FF by default so an EPROM programmer can skip these bytes +when programming. The padding value can be changed with the -p option. + +1. Compiling on Linux or other unix platforms + + make + + then + + make install + + This will install the program to /usr/local/bin. + +1a. Compiling for Windows on Msys, Cygwin or DOS prompt + + The programs can be compiled as follows: + gcc -O2 -Wall -o hex2bin.exe hex2bin.c common.c libcrc.c binary.c + gcc -O2 -Wall -o mot2bin.exe mot2bin.c common.c libcrc.c binary.c + +2. Using hex2bin + + hex2bin example.hex + + hex2bin will generate a binary file example.bin starting at the + lowest address in the hex file. + +3. Binary file starting address and length + + If the lowest address isn't 0000, + ex: 0100: (the first record begins with :nn010000xxx ) + + there will be problems when using the binary file to program a EPROM + since the first byte supposed to be at 0100 is stored in the binary file + at 0000. + + you can specify a starting address for the binary file on the command line: + + hex2bin -s 0000 start_at_0100.hex + + This start address is not the same thing as the start address record in + the hex file. The start address record is used to specify the starting + address for execution of the binary code. + + The bytes will be stored in the binary file with a padding from 0000 + to the lowest address minus 1 (00FF in this case). + + Similarly, the binary file can be padded up to Length -1 with FF or another byte. + + Here, the space between the last byte and 07FF will be filled with FF. + hex2bin -l 0800 ends_before_07FF.hex + + EPROM, EEPROM and Flash memories contain all FF when erased. + + This program does minimal error checking since many hex files are + generated by known good assemblers. + + When the source file name is + for-example.test.hex + the binary created will have the name + for-example.bin + the ".test" part will be dropped. + + Hex2bin/mot2bin assume the source file doesn't contain overlapping records, + if so, overlaps will be reported. + +4. Checksum of source file + + By default, it ignores record checksum errors, so that someone can change + by hand some bytes allowing quick and dirty changes. + If you want checksum error reporting, specify the option -c. + + hex2bin -c example.hex + + If there is a record checksum error somewhere, the program will continue the + conversion anyway. + + The example file example.hex contains some records with checksum errors. + +5. Check value inserted inside binary file + + A check value can be inserted in the resulting binary file. + + hex2bin -k [0-4] -r [start] [end] -f [address] -C [Poly] [Init] [RefIn] [RefOut] [XorOut] + + -k Select the check method: + 0: Checksum 8-bit + 1: Checksum 16-bit + 2: CRC8 + 3: CRC16 + 4: CRC32 + + -r Range to compute checksum over (default is min and max addresses) + + -f Address of the result to write + + -C Parameters for CRC + Parameters for common CRCs are listed in doc/CRC list.txt. They appear in + the same order. Feed them as is and use t for TRUE, f for FALSE. + + See also the test/Makefile for these common CRCs; since they're tested, + you'll have the command line figured out. + + -E Endian for storing the check result or forcing it + 0: little + 1: big + + Change from previous versions of hex2bin/mot2bin: + Replace former options to this version + -k 1 -> -k 1 -E 0 + -k 2 -> -k 1 -E 1 + +6. Value inserted directly inside binary file + Instead of calculating a value, it can be inserted directly into the file at a specified address. + + hex2bin -k [0|1|2] -F [address] [value] + + -k Select the value format: + + 0 = 8-bit + 1 = 16-bit + 2 = 32-bit + + -F Address and value of checksum to force + + -E Endian for storing the check result or forcing it + 0: little + 1: big + +7. Motorola S files + + mot2bin example.s19 + + Options for mot2bin are the same as hex2bin. Executing the program + without argument will display available options. Some are specific to + Motorola files. + + This program will handle S19 files generated for Motorola micropro- + cessors. Since I use this program for an EPROM programmer, I will + rarely need to have more than 4M, I limited the source program for + 24 bits or 16 bits address records. + + 32 bits records are now supported, but obviously I can't allocate all + the memory for the binary target. What I did is simply assume that the + binary file will occupy less than 4M. For binary files greater than 4M, + see length option (section 3). + +8. Support for byte-swapped hex/S19 files + + -w Wordwise swap: for each pair of bytes, exchange the low and high part. + If a checksum needs to be generated to insert in the binary file, select + one of the 16-bit checksums. + + hex2bin -w test-byte-swap.hex + +9. Goodies + + Description of the file formats is included. + Added examples files for extended addressing. + + Check for overlapping records. The check is rather basic: supposing + that the buffer is filled with pad bytes, when a record overlaps a + previous one, value in the buffer will be different from the pad bytes. + This will not detect the case when the previous value equals the pad byte, + but it's more likely that more than one byte will be overlapped. + + +10. Error messages + + "Can't allocate memory." + + Can't do anything in this case, so the program simply exits. + + "Error occurred while reading from file" + + Problem with fgets. + + "Input/Output file %s cannot be opened. Enter new filename: " + + The user may not have permissions to open the file. + + "0 byte length data record ignored" + + This means that an empty data record was read. Since it's empty, it's simply + ignored and should have no impact on the binary file. + + "Data record skipped at ..." + + This means that the records are falling outside the memory buffer. + + "Overlapped record detected" + + A record is overwritten by a subsequent record. If you're using SDCC, check + if more than one area is specified with a starting address. Checking the map + file generated by the linker can help. + + "Some error occurred when parsing options." + + + +11. History + + See ChangeLog + +12. Other hex tool + + There is a program that supports more formats and has more features. + See SRecord at http://srecord.sourceforge.net/ diff --git a/tools/hex2bin-2.0/doc/S-record.txt b/tools/hex2bin-2.0/doc/S-record.txt new file mode 100644 index 0000000..ba4abc1 --- /dev/null +++ b/tools/hex2bin-2.0/doc/S-record.txt @@ -0,0 +1,361 @@ +S-Record Format
+
+ A file in Motorola S-record format is an ASCII file. There are three different
+ formats:
+
+ S19 for 16-bit address
+ S2 for 24-bit address
+ S3 for 32-bit address
+
+
+ The files consist of optional symbol table information, data specifications
+ for loading memory, and a terminator record.
+
+ [ $$ {module_record}
+ symbol records
+ $$ [ module_record ]
+ symbol_records
+ $$]
+ header_record
+ data_records
+ record_count_record
+ terminator_record
+
+
+Module Record (Optional)
+
+ Each object file contains one record for each module that is a component of it. This
+ record contains the name of the module. There is one module record for each relocatable
+ object created by the assembler. The name of the relocatable object module
+ contained in the record comes from the IDNT directive. For absolute objects created
+ by the linker, there is one module record for each relocatable object file linked,
+ plus an additional record whose name comes from the NAME command for the
+ linker.
+
+ Example:
+
+ $$ MODNAME
+
+
+Symbol Record (Optional)
+
+ As many symbol records as needed can be contained in the object module. Up to 4
+ symbols per line can be used, but it is not mandatory that each line contain 4
+ symbols. A module can contain only symbol records.
+
+ Example:
+
+ APPLE $00000 LABEL1 $ODOC3
+ MEM $OFFFF ZEEK $01947
+
+ The module name associated with the symbols can be specified in the
+ module_record preceding the symbol records.
+
+ Example:
+
+ $$MAIN
+
+ Symbols are assumed to be in the module named in the preceding module_record
+ until another module is specified with another module_record. Symbols defined by
+ the linker's PUBLIC command appear following the first module record, which
+ indicates the name of the output object module specified by the linker's NAME
+ command.
+
+
+*****************************************************************************************
+
+Header Record (SO)
+
+ Each object module has exactly one header record with the following format:
+
+ S00600004844521B
+
+ Description:
+
+ S0 Identifies the record as a header record
+ 06 The number of bytes following this one
+ 0000 The address field, which is ignored
+ 484452 The string HDR in ASCII
+ 1B The checksum
+
+
+
+*****************************************************************************************
+
+Data Record (S1)
+
+ A data record specifies data bytes that are to be loaded into memory. Figure 1
+ shows the format for such a record. The columns shown in the figure represent half
+ of a byte (4 bits).
+
+ ---------------------------------------------
+ | 1 2 3 4 5 6 7 8 9 ... 40 41 42 |
+ | |
+ | S ID byte load data...data checksum |
+ | count address 1 n |
+ ---------------------------------------------
+ Figure 1: Data Record Formatter 16-Bit Load Address
+
+
+ Column Description
+
+ 1 Contains the ASCII character S, which indicates the start of
+ a record in Motorola S-record format.
+
+ 2 Contains the ASCII character identifying the record type.
+ For data records, this character is 1.
+
+ 3 to 4 Contain the count of the number of bytes following this one
+ within the record. The count includes the checksum and the
+ load address bytes but not the byte count itself.
+
+ 5 to 8 Contain the load address. The first data byte is to be loaded
+ into this address and subsequent bytes into the next sequential
+ address. Columns 5 and 6 contain the high-order address
+ byte, and columns 7 and 8 contain the low-order address byte.
+
+ 9 to 40 Contain the specifications for up to 16 bytes of data.
+
+ 41 to 42 Contain a checksum for the record. To calculate this, take the
+ sum of the values of all bytes from the byte count up to the
+ last data byte, inclusive, modulo 256. Subtract this result
+ from 255.
+
+
+*****************************************************************************************
+
+Data Record (S2)
+
+
+ A data record specifies data bytes that are to be loaded into memory. Figure 2
+ shows the format for such a record. The columns shown in the figure represent half
+ of a byte (4 bits).
+
+
+ ----------------------------------------------------
+ | 1 2 3 4 5 6 7 8 9 10 11 ... 42 43 44 |
+ | |
+ | S ID byte load data...data checksum |
+ | count address 1 n |
+ ----------------------------------------------------
+ Figure 2: Data Record Format for 24-Bit Load Address
+
+ Column Description
+
+ 1 Contains the ASCII character S, which indicates the start of
+ a record in Motorola S-record format.
+
+ 2 Contains the ASCII character identifying the record type.
+ For data records, this character is 2.
+
+ 3 to 4 Contain the count of the number of bytes following this one
+ within the record. The count includes the checksum and the
+ load address bytes but not the byte count itself.
+
+ 5 to 10 Contain the load address. The first data byte is to be loaded
+ into this address and subsequent bytes into the next sequential
+ address. Columns 5 and 6 contain the high-order address
+ byte, and columns 9 and 10 contain the low-order address byte.
+
+ 11 to 42 Contain the specifications for up to 16 bytes of data.
+
+ 43 to 44 Contain a checksum for the record. To calculate this, take the
+ sum of the values of all bytes from the byte count up to the
+ last data byte, inclusive, modulo 256. Subtract this result
+ from 255.
+
+
+*****************************************************************************************
+
+Data Record (S3)
+
+
+ A data record specifies data bytes that are to be loaded into memory. Figure 3
+ shows the format for such a record. The columns shown in the figure represent half
+ of a byte (4 bits).
+
+ ----------------------------------------------------------
+ | 1 2 3 4 5 6 7 8 9 10 11 12 13 ... 44 45 46 |
+ | |
+ | S ID byte load data...data checksum |
+ | count address 1 n |
+ ----------------------------------------------------------
+ Figure 3: Data Record Format for 32-Bit Load Address
+
+ Column Description
+
+ 1 Contains the ASCII character S, which indicates the start of
+ a record in Motorola S-record format.
+
+ 2 Contains the ASCII character identifying the record type.
+ For data records, this digit is 3 for 32-bit addresses.
+
+ 3 to 4 Contain the count of the number of bytes following this one
+ within the record. The count includes the checksum and the
+ load address bytes but not the byte count itself.
+
+ 5 to 12 Contain the load address. The first data byte is to be loaded
+ into this address and subsequent bytes into the next sequential
+ address. Columns 5 and 6 contain the high-order address
+ byte, and columns 11 and 12 contain the low-order address byte.
+
+ 13 to 44 Contain the specifications for up to 15 bytes of data.
+
+ 45 to 46 Contain a checksum for the record. To calculate this, take the
+ sum of the values of all bytes from the byte count up to the
+ last data byte, inclusive, modulo 256. Subtract this result
+ from 255.
+
+
+*****************************************************************************************
+
+Record Count Record (S5)
+
+
+ The record count record verifies the number of data records preceding it. Figure 4
+ shows the format for such a record. The columns shown in the figure represent half
+ of a byte (4 bits).
+
+ --------------------------------------
+ | 1 2 3 4 5 6 7 8 9 10 |
+ | |
+ | S ID byte # of data checksum |
+ | count records |
+ --------------------------------------
+ Figure 4: Record Count Record Format
+
+ Column Description
+
+ 1 Contains the ASCII character S, which indicates the start of
+ a record in Motorola S-record format.
+
+ 2 Contains the ASCII character 5, which indicates a record
+ count record.
+
+ 3 to 4 Contain the byte count, ASCII string 03.
+
+ 5 to 8 Contain the number of data records in this file. The high-
+ order byte is in columns 5 and 6.
+
+ 9 to 10 Contain the checksum for the record.
+
+ Example:
+
+ S503010DEE
+
+ The example above shows a record count record indicating a total of 269 records
+ (0x010D) and a checksum of 0xEE.
+
+
+
+*****************************************************************************************
+
+Terminator Record for 32-bit address (S7)
+
+ A terminator record specifies the end of the data records. Figure 5 shows the
+ format for such a record. The columns shown in the figure represent half of a byte
+ (4 bits).
+
+ -------------------------------------
+ | 1 2 3 4 5...12 13 14 |
+ | |
+ | S ID byte load checksum |
+ | count address |
+ -------------------------------------
+ Figure5: Terminator Record Format for 32-Bit Load Address
+
+ Column Description
+
+ 1 Contains the ASCII character S, which indicates the start of
+ a record in Motorola S-record format.
+
+ 2 Contains the ASCII character 7, which indicates a 32-bit
+ load address.
+
+ 3 to 4 Contain the byte count, ASCII string 04.
+
+ 5 to 12 Contain the load address that is either set to zero or to the
+ starting address specified in the END directive or START
+ command (there are no data bytes).
+
+ 13 to 14 Contain the checksum for the record.
+
+*****************************************************************************************
+
+Terminator Record for 24-bit address (S8)
+
+
+ A terminator record specifies the end of the data records. Figure 6 shows the
+ format for such a record. The columns shown in the figure represent half of a byte
+ (4 bits).
+
+ ----------------------------------------
+ | 1 2 3 4 5 6 7 8 9 10 11 12 |
+ | |
+ | S ID byte load checksum |
+ | count address |
+ ----------------------------------------
+ Figure 6: Terminator Record Format for 24-Bit Load Address
+
+ Column Description
+
+ 1 Contains the ASCII character S, which indicates the start of
+ a record in Motorola S-record format.
+
+ 2 Contains the ASCII character 8, which indicates a 24-bit
+ load address.
+
+ 3 to 4 Contain the byte count, ASCII string 04.
+
+ 5 to 10 Contain the load address, which is either set to zero or to the
+ starting address specified in the END directive or START
+ command. There are no data bytes.
+
+ 11 to 12 Contain the checksum for the record.
+
+ Example:
+
+ S804000AF0001
+
+ The previous example shows a terminator record with a 24-bit load address of
+ 0x000AF0 and a checksum of 0x01.
+
+
+*****************************************************************************************
+
+Terminator Record for 16-bit address (S9)
+
+
+ A terminator record specifies the end of the data records. Figure 7 shows the
+ format for such a record. The columns shown in the figure represent half of a byte
+ (4 bits).
+
+ -------------------------------------
+ | 1 2 3 4 5 6 7 8 9 10 |
+ | |
+ | S ID byte load checksum |
+ | count address |
+ -------------------------------------
+ Figure 7: Terminator Record Format for 16-Bit Load Address
+
+
+ Column Description
+
+ 1 Contains the ASCII character S, which indicates the start of
+ a record in Motorola S-record format.
+
+ 2 Contains the ASCII character 9, which indicates a 16-bit
+ load address.
+
+ 3 to 4 Contain the byte count, ASCII string 04.
+
+ 5 to 8 Contain the load address, which is either set to zero or to the
+ starting address specified in the END directive or START
+ command (there are no data bytes).
+
+ 9 to 10 Contain the checksum for the record.
+
+
+
+*****************************************************************************************
+ hagen.v.tronje@on-line.de
diff --git a/tools/hex2bin-2.0/doc/formats.txt b/tools/hex2bin-2.0/doc/formats.txt new file mode 100644 index 0000000..25e5e37 --- /dev/null +++ b/tools/hex2bin-2.0/doc/formats.txt @@ -0,0 +1,72 @@ +Hex formats + +Intel +===== + +Hexadecimal values are always in uppercase. Each line is a record. +The sum of all the bytes in each record should be 00 (modulo 256). + +Record types: + +00: data records +01: end-of-file record +02: extended address record + +Data record +----------- + + :0D011C0000000000C3E0FF0000000000C30F + +: 0D 011C 00 00000000C3E0FF0000000000C3 0F +| | | | -------------+------------ | +| | | | | +--- Checksum +| | | | +------------------ Data bytes +| | | +--------------------------------- Record type +| | +------------------------------------- Address +| +----------------------------------------- Number of data bytes ++-------------------------------------------- Start of record + + +End of file record +------------------ + + :00000001FE + +: 00 0000 01 FE +| | | | | +| | | | +--- Checksum +| | | +------ Record type +| | +---------- Address +| +-------------- Number of data bytes ++----------------- Start of record + + + +Extended address record +----------------------- + + :02010002E0001B + +: 02 0100 02 E000 1B +| | | | | | +| | | | | +--- Checksum +| | | | +-------- Segment address +| | | +----------- Record type +| | +--------------- Address +| +------------------- Number of data bytes ++---------------------- Start of record + +Following data records will start at E000:0100 or E0100 + + + + + + + + + + + + + diff --git a/tools/hex2bin-2.0/doc/intelhex.spc b/tools/hex2bin-2.0/doc/intelhex.spc new file mode 100644 index 0000000..946d586 --- /dev/null +++ b/tools/hex2bin-2.0/doc/intelhex.spc @@ -0,0 +1,409 @@ + +====================================================================== + +Intel +Hexadecimal Object File +Format Specification +Revision A, 1/6/88 + + + +DISCLAIMER + +Intel makes no representation or warranties with respect to the contents +hereof and specifically disclaims any implied warranties of +merchantability or fitness for any particular purpose. Further, Intel +reserves the right to revise this publication from time to time in the +content hereof without obligation of Intel to notify any person of such +revision or changes. The publication of this specification should not +be construed as a commitment on Intel's part to implement any product. + + +1. Introduction +This document describes the hexadecimal object file format for the Intel +8- bit, 16-bit, and 32-bit microprocessors. The hexadecimal format is +suitable as input to PROM programmers or hardware emulators. +Hexadecimal object file format is a way of representing an absolute +binary object file in ASCII. Because the file is in ASCII instead of +binary, it is possible to store the file is non-binary medium such as +paper-tape, punch cards, etc.; and the file can also be displayed on CRT +terminals, line printers, etc.. The 8-bit hexadecimal object file +format allows for the placement of code and data within the 16-bit +linear address space of the Intel 8-bit processors. The 16-bit +hexadecimal format allows for the 20-bit segmented address space of the +Intel 16-bit processors. And the 32-bit format allows for the 32-bit +linear address space of the Intel 32-bit processors. +The hexadecimal representation of binary is coded in ASCII alphanumeric +characters. For example, the 8-bit binary value 0011-1111 is 3F in +hexadecimal. To code this in ASCII, one 8-bit byte containing the ASCII +code for the character '3' (0011-0011 or 033H) and one 8-bit byte +containing the ASCII code for the character 'F' (0100-0110 or 046H) are +required. For each byte value, the high-order hexadecimal digit is +always the first digit of the pair of hexadecimal digits. This +representation (ASCII hexadecimal) requires twice as ma ny bytes as the +binary representation. +A hexadecimal object file is blocked into records, each of which +contains the record type, length, memory load address and checksum in +addition to the data. There are currently six (6) different types of +records that are defined, not all combinations of these records are +meaningful, however. The records are: + +Data Record (8-, 16-, or 32-bit formats) +End of File Record (8-, 16-, or 32-bit formats) +Extended Segment Address Record (16- or 32-bit formats) +Start Segment Address Record (16- or 32-bit formats) +Extended Linear Address Record (32-bit format only) +Start Linear Address Record (32-bit format only) + + +2. General Record Format +| RECORD | LOAD | | | INFO | | +| MARK | RECLEN | OFFSET | RECTYP | or | CHKSUM | +| ':' | | | | DATA | | + 1-byte 1-byte 2-bytes 1-byte n-bytes 1-byte + +Each record begins with a RECORD MARK field containing 03AH, the ASCII +code for the colon (':') character. +Each record has a RECLEN field which specifies the number of bytes of +information or data which follows the RECTYP field of the record. Note +that one data byte is represented by two ASCII characters. The maximum +value of the RECLEN field is hexadecimal 'FF' or 255. +Each record has a LOAD OFFSET field which specifies the 16-bit starting +load offset of the data bytes, therefore this field is only used for +Data Records. In other records where this field is not used, it should +be coded as four ASCII zero characters ('0000' or 030303030H). +Each record has a RECTYP field which specifies the record type of this +record. The RECTYP field is used to interpret the remaining information +within the record. The encoding for all the current record types are: + +'00' Data Record +'01' End of File Record +'02' Extended Segment Address Record +'03' Start Segment Address Record +'04' Extended Linear Address Record +'05' Start Linear Address Record + +Each record has a variable length INFO/DATA field, it consists of zero +or more bytes encoded as pairs of hexadecimal digits. The +interpretation of this field depends on the RECTYP field. +Each record ends with a CHKSUM field that contains the ASCII hexadecimal +representation of the two's complement of the 8-bit bytes that result +from converting each pair of ASCII hexadecimal digits to one byte of +binary, from and including the RECLEN field to and including the last +byte of the INFO/DATA field. Therefore, the sum of all the ASCII pairs +in a record after converting to binary, from the RECLEN field to and +including the CHKSUM field, is zero. + + +3. Extended Linear Address Record (32-bit format only) +| RECORD | LOAD | | | | | +| MARK | RECLEN | OFFSET | RECTYP | ULBA | CHKSUM | +| ':' | '02' | '0000' | '04' | | | + 1-byte 1-byte 2-bytes 1-byte 2-bytes 1-byte + +The 32-bit Extended Linear Address Record is used to specify bits 16-31 +of the Linear Base Address (LBA), where bits 0-15 of the LBA are zero. +Bits 16-31 of the LBA are referred to as the Upper Linear Base Address +(ULBA). The absolute memory address of a content byte in a subsequent +Data Record is obtained by adding the LBA to an offset calculated by +adding the LOAD OFFSET field of the containing Data Record to the index +of the byte in the Data Record (0, 1, 2, ... n). This offset addition +is done modulo 4G (i.e., 32-bits), ignoring any carry, so that offset +wrap-around loading (from OFFFFFFFFH to OOOOOOOOOH) results in wrapping +around from the end to the beginning of the 4G linear address defined by +the LBA. The linear address at which a particular byte is loaded is +calculated as: +(LBA + DRLO + DRI) MOD 4G +where: +DRLO is the LOAD OFFSET field of a Data Record. +DRI is the data byte index within the Data Record. + +When an Extended Linear Address Record defines the value of LBA, it may +appear anywhere within a 32-bit hexadecimal object file. This value +remains in effect until another Extended Linear Address Record is +encountered. The LBA defaults to zero until an Extended Linear Address +Record is encountered. +The contents of the individual fields within the record are: + +RECORD MARK +This field contains 03AH, the hexadecimal encoding of the ASCII colon +(':') character. + +RECLEN +The field contains 03032H, the hexadecimal encoding of the ASCII +characters '02', which is the length, in bytes, of the ULBA data +information within this record. + +LOAD OFFSET +This field contains 030303030H, the hexadecimal encoding of the ASCII +characters '0000', since this field is not used for this record. + +RECTYP +This field contains 03034H, the hexadecimal encoding of the ASCII +character '04', which specifies the record type to be an Extended Linear +Address Record. + +ULBA +This field contains four ASCII hexadecimal digits that specify the +16-bit Upper Linear Base Address value. The high-order byte is the +10th/llth character pair of the record. The low-order byte is the +12th/13th character pair of the record. + +CHKSUM +This field contains the check sum on the RECLEN, LOAD OFFSET, RECTYP, +and ULBA fields. + + +4. Extended Segment Address Record (16- or 32-bit formats) +| RECORD | LOAD | | | | | +| MARK | RECLEN | OFFSET | RECTYP | USBA | CHKSUM | +| ':' | '02' | '0000' | '02' | | | + 1-byte 1-byte 2-bytes 1-byte 2-bytes 1-byte + +The 16-bit Extended Segment Address Record is used to specify bits 4-19 +of the Segment Base Address (SBA), where bits 0-3 of the SBA are zero. +Bits 4-19 of the SBA are referred to as the Upper Segment Base Address +(USBA). The absolute memory address of a content byte in a subsequent +Data Record is obtained by adding the SBA to an offset calculated by +adding the LOAD OFFSET field of the containing Data Record to the index +of the byte in the Data Record (0, 1, 2, ... n). This offset addition +is done modulo 64K (i.e., 16-bits), ignoring any carry, so that offset +wraparound loading (from OFFFFH to OOOOOH) results in wrapping around +from the end to the beginning of the 64K segment defined by the SBA. +The address at which a particular byte is loaded is calculated as: + + SBA + ([DRLO + DRI] MOD 64K) + +where: + DRLO is the LOAD OFFSET field of a Data Record. + DRI is the data byte index within the Data Record. + +When an Extended Segment Address Record defines the value of SBA, it +may appear anywhere within a 16-bit hexadecimal object file. This +value remains in effect until another Extended Segment Address +Record is encountered. The SBA defaults to zero until an Extended +Segment Address Record is encountered. + +The contents of the individual fields within the record are: + +RECORD MARK +This field contains 03AH, the hexadecimal encoding of the ASCII +colon (':') character. + +RECLEN +The field contains 03032H, the hexadecimal encoding of the ASCII +characters '02', which is the length, in bytes, of the USBA data +information within this record. + +LOAD OFFSET +This field contains 030303030H, the hexadecimal encoding of the +ASCII characters '0000', since this field is not used for this +record. + +RECTYP +This field contains 03032H, the hexadecimal encoding of the ASCII +character '02', which specifies the record type to be an Extended +Segment Address Record. + +USBA +This field contains four ASCII hexadecimal digits that specify the +16-bit Upper Segment Base Address value. The high-order byte is the +10th/1lth character pair of the record. The low-order byte is the +12th/13th character pair of the record. + +CHKSUM +This field contains the check sum on the RECLEN, LOAD OFFSET, +RECTYP, and USBA fields. + +5. Data Record (8-, 16-, or 32-bit formats) + +| RECORD | LOAD | | | | | +| MARK | RECLEN | OFFSET | RECTYP | DATA | CHKSUM | +| ':' | | | '00' | | | + 1-byte 1-byte 2-bytes 1-byte n-bytes 1-byte + + +The Data Record provides a set of hexadecimal digits that represent +the ASCII code for data bytes that make up a portion of a memory +image. The method for calculating the absolute address (linear in +the 8-bit and 32-bit case and segmented in the 16-bit case) for each +byte of data is described in the discussions of the Extended Linear +Address Record and the Extended Segment Address Record. + +The contents of the individual fields within the record are: + +RECORD MARK +This field contains 03AH, the hexadecimal encoding of the ASCII +colon (':') character. + +RECLEN +The field contains two ASCII hexadecimal digits that specify the +number of data bytes in the record. The maximum value is 'FF' or +04646H (255 decimal). + +LOAD OFFSET +This field contains four ASCII hexadecimal digits representing the +offset from the LBA (see Extended Linear Address Record) or SBA (see +Extended Segment Address Record) defining the address which the +first byte of the data is to be placed. + +RECTYP +This field contains 03030H, the hexadecimal encoding of the ASCII +character '00', which specifies the record type to be a Data Record. + +DATA +This field contains pairs of ASCII hexadecimal digits, one pair for +each data byte. + +CHKSUM +This field contains the check sum on the RECLEN, LOAD OFFSET, +RECTYP, and DATA fields. + + +6. Start Linear Address Record (32-bit format only) + +| RECORD | LOAD | | | | | +| MARK | RECLEN | OFFSET | RECTYP | EIP | CHKSUM | +| ':' | '04' | '0000' | '05' | | | + 1-byte 1-byte 2-bytes 1-byte 4-bytes 1-byte + + +The Start Linear Address Record is used to specify the execution +start address for the object file. The value given is the 32-bit +linear address for the EIP register. Note that this record only +specifies the code address within the 32-bit linear address space of +the 80386. If the code is to start execution in the real mode of +the 80386, then the Start Segment Address Record should be used +instead, since that record specifies both the CS and IP register +contents necessary for real mode. + +The Start Linear Address Record can appear anywhere in a 32-bit +hexadecimal object file. If such a record is not present in a +hexadecimal object file, a loader is free to assign a default start +address. + +The contents of the individual fields within the record are: + +RECORD MARK +This field contains 03AH, the hexadecimal encoding of the ASCII +colon (':') character. + +RECLEN +The field contains 03034H, the hexadecimal encoding of the ASCII +characters '04', which is the length, in bytes, of the EIP register +content within this record. + +LOAD OFFSET +This field contains 030303030H, the hexadecimal encoding of the +ASCII characters '0000', since this field is not used for this +record. + +RECTYP +This field contains 03035H, the hexadecimal encoding of the ASCII +character '05', which specifies the record type to be a Start Linear +Address Record. + +EIP +This field contains eight ASCII hexadecimal digits that specify the +32-bit EIP register contents. The high-order byte is the 10th/1lth +character pair. + +CHKSUM +This field contains the check sum on the RECLEN, LOAD OFFSET, +RECTYP, and EIP fields. + + +7. Start Segment Address Record (16- or 32-bit formats) + +| RECORD | LOAD | | | | | +| MARK | RECLEN | OFFSET | RECTYP | CS/IP | CHKSUM | +| ':' | '04' | '0000' | '03' | | | + 1-byte 1-byte 2-bytes 1-byte 4-bytes 1-byte + + +The Start Segment Address Record is used to specify the execution +start address for the object file. The value given is the 20-bit +segment address for the CS and IP registers. Note that this record +only specifies the code address within the 20-bit segmented address +space of the 8086/80186. + +The Start Segment Address Record can appear anywhere in a 16-bit +hexadecimal object file. If such a record is not present in a +hexadecimal object file, a loader is free to assign a default start +address. + +The contents of the individual fields within the record are: + +RECORD MARK +This field contains 03AH, the hexadecimal encoding of the ASCII +colon (':') character. + +RECLEN +The field contains 03034H, the hexadecimal encoding of the ASCII +characters '04', which is the length, in bytes, of the CS/IP +register contents within this record. + +LOAD OFFSET +This field contains 030303030H, the hexadecimal encoding of the +ASCII characters '0000', since this field is not used for this +record. + +RECTYP +This field contains 03033H, the hexadecimal encoding of the ASCII +character '03', which specifies the record type to be a Start +Segment Address Record. + +CS/IP +This field contains eight ASCII hexadecimal digits that specify the +16-bit CS register and 16-bit IP register contents. The high-order +byte of the CS register content is the 10th/llth character pair, the +low-order byte is the 12th/13th character pair of the record. The +high-order byte of the IP register content is the 14th/15th +character pair, the low-order byte is the 16th/17th character pair +of the record. + +CHKSUM +This field contains the check sum on the RECLEN, LOAD OFFSET, +RECTYP, and CS/IP fields. + + + +8. End of File Record (8-, 16-, or 32-bit formats) + +| RECORD | LOAD | | | | +| MARK | RECLEN | OFFSET | RECTYP | CHKSUM | +| ':' | '00' | '0000' | '01' | | + 1-byte 1-byte 2-bytes 1-byte 1-byte + +The End of File Record specifies the end of the hexadecimal object +file. + +The contents of the individual fields within the record are: + +RECORD MARK +This field contains 03AH, the hexadecimal encoding of the ASCII +colon (':') character. + +RECLEN +The field contains 03030H, the hexadecimal encoding of the ASCII +characters '00'. Since this record does not contain any INFO/DATA +bytes, the length is zero. + +LOAD OFFSET +This field contains 030303030H, the hexadecimal encoding of the +ASCII characters '0000', since this field is not used for this +record. + +RECTYP +This field contains 03031H, the hexadecimal encoding of the ASCII +character '01', which specifies the record type to be an End of File +Record. + +CHKSUM +This field contains the check sum an the RECLEN, LOAD OFFSET, and +RECTYP fields. Since all the fields are static, the check sum can +also be calculated statically, and the value is 04646H, the +hexadecimal encoding of the ASCII characters 'FF'. + +======================================================================== +END diff --git a/tools/hex2bin-2.0/doc/srec.txt b/tools/hex2bin-2.0/doc/srec.txt new file mode 100644 index 0000000..ea63d31 --- /dev/null +++ b/tools/hex2bin-2.0/doc/srec.txt @@ -0,0 +1,447 @@ +S-Records
+
+
+ -S-Record Format-
+
+ Chaplin@keinstr.uucp (Roger Chaplin) reposted an article written
+ by mcdchg!motmpl!ron (Ron Widell) that explained how Motorola
+ S-Records are formatted. This comes from a unix man page. No
+ mention of which version of Unix is specified. This section
+ of the FAQ is a bit long. An anonymous ftp archive is currently
+ being sought. When one is found, the section will be placed in
+ the archive.
+
+
+ SREC(4) UNIX 5.0 (03/21/84) SREC(4)
+
+
+ An S-record file consists of a sequence of specially formatted
+ ASCII character strings. An S-record will be less than or equal to
+ 78 bytes in length.
+
+ The order of S-records within a file is of no significance and no
+ particular order may be assumed.
+
+ The general format of an S-record follow:
+
+ +------------------//-------------------//-----------------------+
+ | type | count | address | data | checksum |
+ +------------------//-------------------//-----------------------+
+
+ type A char-2- field. These characters describe the
+ type of record (S0, S1, S2, S3, S5, S7, S8, or
+ S9).
+ count A char-2- field. These characters when paired and
+ interpreted as a hexadecimal value, display the
+ count of remaining character pairs in the record.
+
+ address A char-4,6, or 8- field. These characters grouped
+ and interpreted as a hexadecimal value, display
+ the address at which the data field is to be
+ loaded into memory. The length of the field
+ depends on the number of bytes necessary to hold
+ the address. A 2-byte address uses 4 characters,
+ a 3-byte address uses 6 characters, and a 4-byte
+ address uses 8 characters.
+ data A char -0-64- field. These characters when paired
+ and interpreted as hexadecimal values represent
+ the memory loadable data or descriptive
+ information.
+
+ checksum A char-2- field. These characters when paired and
+ interpreted as a hexadecimal value display the
+ least significant byte of the ones complement of
+ the sum of the byte values represented by the
+ pairs of characters making up the count, the
+ address, and the data fields.
+
+ Each record is terminated with a line feed. If any
+ additional or different record terminator(s) or delay
+ characters are needed during transmission to the target
+ system it is the responsibility of the transmitting program
+ to provide them.
+
+ S0 Record The type of record is 'S0' (0x5330). The address
+
+
+ field is unused and will be filled with zeros
+ (0x0000). The header information within the data
+ field is divided into the following subfields.
+
+ mname is char-20- and is the
+ module name.
+ ver is char-2- and is the
+ version number.
+
+ rev is char-2- and is the
+ revision number.
+ description is char-0-36- and is a
+ text comment.
+
+ Each of the subfields is composed of ASCII bytes
+ whose associated characters, when paired,
+ represent one byte hexadecimal values in the case
+ of the version and revision numbers, or represent
+ the hexadecimal values of the ASCII characters
+ comprising the module name and description.
+
+ S1 Record The type of record field is 'S1' (0x5331). The
+ address field is interpreted as a 2-byte address.
+ The data field is composed of memory loadable
+ data.
+ S2 Record The type of record field is 'S2' (0x5332). The
+ address field is interpreted as a 3-byte address.
+ The data field is composed of memory loadable
+ data.
+
+ S3 Record The type of record field is 'S3' (0x5333). The
+ address field is interpreted as a 4-byte address.
+ The data field is composed of memory loadable
+ data.
+ S5 Record The type of record field is 'S5' (0x5335). The
+ address field is interpreted as a 2-byte value
+ and contains the count of S1, S2, and S3 records
+ previously transmitted. There is no data field.
+
+ S7 Record The type of record field is 'S7' (0x5337). The
+ address field contains the starting execution
+ address and is interpreted as 4-byte address.
+ There is no data field.
+ S8 Record The type of record field is 'S8' (0x5338). The
+ address field contains the starting execution
+ address and is interpreted as 3-byte address.
+ There is no data field.
+
+ S9 Record The type of record field is 'S9' (0x5339). The
+ address field contains the starting execution
+ address and is interpreted as 2-byte address.
+ There is no data field.
+
+ EXAMPLE
+
+ Shown below is a typical S-record format file.
+
+ S00600004844521B
+ S1130000285F245F2212226A000424290008237C2A
+ S11300100002000800082629001853812341001813
+ S113002041E900084E42234300182342000824A952
+ S107003000144ED492
+ S5030004F8
+ S9030000FC
+
+ The file consists of one S0 record, four S1 records, one S5
+ record and an S9 record.
+
+ The S0 record is comprised as follows:
+
+ S0 S-record type S0, indicating it is a header
+ record.
+ 06 Hexadecimal 06 (decimal 6), indicating that six
+ character pairs (or ASCII bytes) follow.
+
+ 00 00 Four character 2-byte address field, zeroes in
+ this example.
+ 48 ASCII H, D, and R - "HDR".
+
+ 1B The checksum.
+
+ The first S1 record is comprised as follows:
+ S1 S-record type S1, indicating it is a data record
+ to be loaded at a 2-byte address.
+
+ 13 Hexadecimal 13 (decimal 19), indicating that
+ nineteen character pairs, representing a 2 byte
+ address, 16 bytes of binary data, and a 1 byte
+ checksum, follow.
+ 00 00 Four character 2-byte address field; hexadecimal
+ address 0x0000, where the data which follows is to
+ be loaded.
+
+ 28 5F 24 5F 22 12 22 6A 00 04 24 29 00 08 23 7C Sixteen
+ character pairs representing the actual binary
+ data.
+ 2A The checksum.
+
+ The second and third S1 records each contain 0x13 (19)
+ character pairs and are ended with checksums of 13 and 52,
+ respectively. The fourth S1 record contains 07 character
+ pairs and has a checksum of 92.
+
+ The S5 record is comprised as follows:
+
+ S5 S-record type S5, indicating it is a count record
+ indicating the number of S1 records.
+
+
+
+ 03 Hexadecimal 03 (decimal 3), indicating that three
+ character pairs follow.
+
+ 00 04 Hexadecimal 0004 (decimal 4), indicating that
+ there are four data records previous to this
+ record.
+ F8 The checksum.
+
+ The S9 record is comprised as follows:
+
+ S9 S-record type S9, indicating it is a termination
+ record.
+ 03 Hexadecimal 03 (decimal 3), indicating that three
+ character pairs follow.
+
+ 00 00 The address field, hexadecimal 0 (decimal 0)
+ indicating the starting execution address.
+ FC The checksum.
+
+
+ -Intel Hex ASCII Format-
+
+ Intel HEX-ASCII format takes the form:
+
+ +----------------------------------- Start Character
+ |
+ | +-------------------------------- Byte Count
+ | | (# of data bytes)
+ | |
+ | | +-------------------------- Address of first data.
+ | | |
+ | | | +-------------------- Record Type (00 data,
+ | | | | 01 end of record)
+ | | | |
+ | | | | +------------ Data Bytes
+ | | | | |
+ | | | | | +---- Checksum
+ | | | | | |
+ | / \ / \ / \ / \ / \
+ : B C A A A A T T H H ... H H C C
+
+ An examples:
+
+ :10000000DB00E60F5F1600211100197ED300C3004C
+ :1000100000000101030307070F0F1F1F3F3F7F7FF2
+ :01002000FFE0
+ :00000001FF
+
+ This information comes from _Microprocessors and Programmed
+ Logic_, Second Edition, Kenneth L. Short, 1987, Prentice-Hall,
+ ISBN 0-13-580606-2.
+
+ Provisions have been made for data spaces larger than 64 kBytes.
+ The above reference does not discuss them. I suspect there is
+ a start of segment type record, but I do not know how it is
+ implemented.
+
+----------------------------------------------------------------------------
+
+/* This file contains source code to read a Motorola S-record file into
+** a memory image. The size of the file cannot exceed BUFSIZE of data.
+** The image is then written to disk either as binary data starting at
+** address 0 with no data gaps, or as a C array of unsigned longs.
+** Input lines must be no longer than MAXLINE. No check is made!
+**
+** Author: Eric McRae, Electro-Logic Machines, Inc.
+** Date: Copyright 1994
+**
+** This source code is made available to the public "as is". No
+** warranty is given or implied for it's proper operation. This source
+** code may be used in whole or in part as long as this copyright is
+** included.
+*/
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* Comment the following line for non PC applications */
+#define PCDOS
+
+/* Uncomment the following line if you want a binary output instead of
+** a structure
+*/
+/* #define BINARY */
+
+#ifdef PCDOS /* Intel x86 architecture */
+#define BUFSIZE 49152 /* 48K to avoid segment hopping */
+#else /* Any reasonable (non-segmented) arch... */
+#define BUFSIZE 65536 /* As big as you want */
+#endif
+
+#define MAXLINE 256 /* Length of longest input line + 1 */
+/* Globals */
+FILE *infilePH, *outfilePH; /* Handles for input and output files */
+unsigned char *bufAC, /* Allocated image buffer */
+ *highestPC = NULL; /* Highest buffer address written */
+
+/* Change this string to reflect the name of the output array */
+char headerAC[] = "unsigned long sRec[] =\n{\n";
+
+/* Predeclarations */
+int parsebufN( char * ); /* Does the actual parsing */
+
+void main(int argc, const char * argv[])
+{
+ int c, /* Temp char storage */
+ resN; /* result status */
+ char *lbufPC, lbufAC[MAXLINE];
+ int linectrN = 0; /* Used to correlate parse fail to input line */
+
+#ifndef BINARY
+ int i;
+ unsigned long *codePL;
+ unsigned char *codePC;
+#endif
+
+ /* Check the argument count */
+ if( argc != 3 ) /* If didn't specify input and output files */
+ {
+ printf("Usage: %s: infile outfile\n", argv[0] );
+ exit(1);
+ }
+
+ /* OK, let's open some files */
+ if( ( infilePH = fopen( argv[1], "r" ) )== NULL )
+ {
+ printf("%s: Couldn't open input file %s\n", argv[0], argv[1] );
+ exit(2);
+ }
+
+ if( ( outfilePH = fopen( argv[2], "w" ) ) == NULL )
+ {
+ printf("%s: Couldn't open output file %s\n", argv[0], argv[3] );
+ exit(3);
+ }
+
+ /* OK, get a buffer and clear it. */
+ if( (bufAC = calloc( (size_t)BUFSIZE, (size_t)1 )) == NULL )
+ {
+ printf("%s: Couldn't malloc memory for buffer\n", argv[0] );
+ exit(4);
+ }
+
+ lbufPC = lbufAC; /* Point at beginning of line buffer */
+ while( c = fgetc( infilePH ))
+ {
+ if( (c == '\n') || (c == EOF) ) /* If found end of line or file */
+ { /* Parse the Line */
+ if( ( c == EOF ) && ( ferror( infilePH ) ) )
+ {
+ printf("%s: Error reading input file\n", argv[0] );
+ exit(5);
+ }
+ else
+ { /* OK, have a complete line in buffer */
+ linectrN++; /* Increment line counter */
+ if( lbufPC == lbufAC )
+ break; /* ignore blank lines */
+ *lbufPC = 0; /* Terminate the line string */
+ if( resN = parsebufN( lbufAC ) ) /* Parse data record to mem */
+ {
+ printf("%s: Error reading input file at line %d, return code = %d\n",
+ argv[0], linectrN, resN );
+ exit( resN );
+ }
+ lbufPC = lbufAC; /* Repoint line buffer pointer */
+ } /* End of have a complete line */
+ }
+ else
+ *lbufPC++ = c; /* Place char into line buffer */
+ }
+
+ /* At this point, the input file has been emptied. Now dispose of the
+ ** output data according to compilation mode.
+ */
+
+#ifdef BINARY
+
+ /* Write the buffer back to disk as a binary image */
+ resN = fwrite( bufAC, 1, (size_t)((highestPC - bufAC) + 1), outfilePH );
+ if( resN != (int)( (highestPC - bufAC) + 1) )
+ {
+ printf("%s: Error writing output file\n", argv[0] );
+ exit( 6 );
+ }
+
+#else
+ /* Produce a file that can be included in a C program. Data is read
+ ** from buffer as bytes to avoid portability/endian problems with
+ ** this program.
+ */
+ /* Output header first, then 1 long per line */
+ fwrite( (void *)headerAC, 1, (size_t)(sizeof( headerAC )-1), outfilePH );
+
+ codePL = (unsigned long *)bufAC;
+ for( i = (highestPC - bufAC + 1) / 4; i; i-- ) /* for each long */
+ {
+ codePC = (unsigned char *)codePL++;
+ sprintf(lbufAC, "0x%02x%02x%02x%02x%s",
+ *codePC, *(codePC + 1), *(codePC + 2), *(codePC + 3),
+ i == 1 ? "\n" : ",\n" ); /* No comma after final long */
+ fwrite( lbufAC, 1, (size_t)(strlen( lbufAC )), outfilePH );
+ }
+ /* OK, data has been written out, close end of array */
+ fwrite( "};\n", 1, (size_t)3, outfilePH );
+#endif
+}
+
+/* Function: parsebufV
+** Parses an S-record in the buffer and writes it into the buffer
+** if it is has a valid checksum.
+**
+** Args: pointer to character buffer for null terminated line
+** Returns: int result code: 0 = success, else failure
+*/
+int parsebufN( char *lbufPC )
+{
+ unsigned long addrL;
+ unsigned char cksmB, /* checksum of addr, count, & data length */
+ *bufPC; /* Pointer into memory array */
+ int i, countN, /* Number of bytes represented in record */
+ oheadN, /* Number of overhead (addr + chksum) bytes */
+ tvalN; /* Temp for check checksum */
+
+ switch( *(lbufPC+1) ) /* examine 2nd character on the line */
+ {
+ case '1': /* 16 bit address field */
+ if( sscanf(lbufPC, "S1%2x%4lx", &countN, &addrL ) != 2 )
+ return( 10 ); /* Flag error in S1 record */
+ oheadN = 3; /* 2 address + 1 checksum */
+ break;
+
+ case '2': /* 24 bit address field */
+ if( sscanf(lbufPC, "S2%2x%6lx", &countN, &addrL ) != 2 )
+ return( 11 ); /* Flag error in S2 record */
+ oheadN = 4; /* 3 address + 1 checksum */
+ break;
+
+ case '3': /* 32 bit address field */
+ if( sscanf(lbufPC, "S3%2x%8lx", &countN, &addrL ) != 2 )
+ return( 12 ); /* Flag error in S3 record */
+ oheadN = 5; /* 4 address + 1 checksum */
+ break;
+
+ default: /* ignore all but S1,2,3 records. */
+ return( 0 );
+ }
+
+ if( addrL > BUFSIZE ) return( 13 ); /* if address exceeds buffer size */
+ bufPC = bufAC + addrL; /* otherwise, point to right spot in buffer */
+
+ /* OK now see if checksum is OK, while reading data to buffer */
+ cksmB = 0;
+ countN++; /* Bump counter to read final checksum too */
+ for( i = 1; i <= countN; i++ )
+ {
+ sscanf( lbufPC + i*2, "%2x", &tvalN ); /* Scan a 2 hex digit byte */
+ cksmB += (unsigned char)tvalN;
+ if( ( i > oheadN ) && ( i < countN ) ) /* If scanned a data byte */
+ *bufPC++ = (unsigned char) tvalN; /* write it to the buffer */
+ }
+ if( cksmB += 1 ) return( 14 ); /* flag checksum error */
+
+ if( (bufPC - 1) > highestPC )
+ highestPC = bufPC - 1; /* track highest address loaded */
+
+ return( 0 ); /* Successful return */
+}
+
+
+
diff --git a/tools/hex2bin-2.0/src/binary.c b/tools/hex2bin-2.0/src/binary.c new file mode 100644 index 0000000..63ead0d --- /dev/null +++ b/tools/hex2bin-2.0/src/binary.c @@ -0,0 +1,196 @@ +/*---------------------------------------------------------------------------* + * binary.c * + * Copyright (C) 2014 Jacques Pelletier * + * * + * 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. * + *---------------------------------------------------------------------------*/ + +#include <stdint.h> + +#include "binary.h" + +const uint8_t Reflect8[256] = { + 0x00,0x80,0x40,0xC0,0x20,0xA0,0x60,0xE0,0x10,0x90,0x50,0xD0,0x30,0xB0,0x70,0xF0, + 0x08,0x88,0x48,0xC8,0x28,0xA8,0x68,0xE8,0x18,0x98,0x58,0xD8,0x38,0xB8,0x78,0xF8, + 0x04,0x84,0x44,0xC4,0x24,0xA4,0x64,0xE4,0x14,0x94,0x54,0xD4,0x34,0xB4,0x74,0xF4, + 0x0C,0x8C,0x4C,0xCC,0x2C,0xAC,0x6C,0xEC,0x1C,0x9C,0x5C,0xDC,0x3C,0xBC,0x7C,0xFC, + 0x02,0x82,0x42,0xC2,0x22,0xA2,0x62,0xE2,0x12,0x92,0x52,0xD2,0x32,0xB2,0x72,0xF2, + 0x0A,0x8A,0x4A,0xCA,0x2A,0xAA,0x6A,0xEA,0x1A,0x9A,0x5A,0xDA,0x3A,0xBA,0x7A,0xFA, + 0x06,0x86,0x46,0xC6,0x26,0xA6,0x66,0xE6,0x16,0x96,0x56,0xD6,0x36,0xB6,0x76,0xF6, + 0x0E,0x8E,0x4E,0xCE,0x2E,0xAE,0x6E,0xEE,0x1E,0x9E,0x5E,0xDE,0x3E,0xBE,0x7E,0xFE, + 0x01,0x81,0x41,0xC1,0x21,0xA1,0x61,0xE1,0x11,0x91,0x51,0xD1,0x31,0xB1,0x71,0xF1, + 0x09,0x89,0x49,0xC9,0x29,0xA9,0x69,0xE9,0x19,0x99,0x59,0xD9,0x39,0xB9,0x79,0xF9, + 0x05,0x85,0x45,0xC5,0x25,0xA5,0x65,0xE5,0x15,0x95,0x55,0xD5,0x35,0xB5,0x75,0xF5, + 0x0D,0x8D,0x4D,0xCD,0x2D,0xAD,0x6D,0xED,0x1D,0x9D,0x5D,0xDD,0x3D,0xBD,0x7D,0xFD, + 0x03,0x83,0x43,0xC3,0x23,0xA3,0x63,0xE3,0x13,0x93,0x53,0xD3,0x33,0xB3,0x73,0xF3, + 0x0B,0x8B,0x4B,0xCB,0x2B,0xAB,0x6B,0xEB,0x1B,0x9B,0x5B,0xDB,0x3B,0xBB,0x7B,0xFB, + 0x07,0x87,0x47,0xC7,0x27,0xA7,0x67,0xE7,0x17,0x97,0x57,0xD7,0x37,0xB7,0x77,0xF7, + 0x0F,0x8F,0x4F,0xCF,0x2F,0xAF,0x6F,0xEF,0x1F,0x9F,0x5F,0xDF,0x3F,0xBF,0x7F,0xFF, +}; + +uint16_t Reflect16(uint16_t Value16) +{ + return (((uint16_t) Reflect8[u16_lo(Value16)]) << 8) | ((uint16_t) Reflect8[u16_hi(Value16)]); +} + +uint32_t Reflect24(uint32_t Value24) +{ + return ( + (((uint32_t) Reflect8[u32_b0(Value24)]) << 16) | + (((uint32_t) Reflect8[u32_b1(Value24)]) << 8) | + ((uint32_t) Reflect8[u32_b2(Value24)]) + ); +} + +uint32_t Reflect32(uint32_t Value32) +{ + return ( + (((uint32_t) Reflect8[u32_b0(Value32)]) << 24) | + (((uint32_t) Reflect8[u32_b1(Value32)]) << 16) | + (((uint32_t) Reflect8[u32_b2(Value32)]) << 8) | + ((uint32_t) Reflect8[u32_b3(Value32)]) + ); +} + +uint64_t Reflect40(uint64_t Value40) +{ + return ( + (((uint64_t) Reflect8[u64_b0(Value40)]) << 32) | + (((uint64_t) Reflect8[u64_b1(Value40)]) << 24) | + (((uint64_t) Reflect8[u64_b2(Value40)]) << 16) | + (((uint64_t) Reflect8[u64_b3(Value40)]) << 8) | + ((uint64_t) Reflect8[u64_b4(Value40)]) + ); +} + +uint64_t Reflect64(uint64_t Value64) +{ + return ( + (((uint64_t) Reflect8[u64_b0(Value64)]) << 56) | + (((uint64_t) Reflect8[u64_b1(Value64)]) << 48) | + (((uint64_t) Reflect8[u64_b2(Value64)]) << 40) | + (((uint64_t) Reflect8[u64_b3(Value64)]) << 32) | + (((uint64_t) Reflect8[u64_b4(Value64)]) << 24) | + (((uint64_t) Reflect8[u64_b5(Value64)]) << 16) | + (((uint64_t) Reflect8[u64_b6(Value64)]) << 8) | + ((uint64_t) Reflect8[u64_b7(Value64)]) + ); +} + +uint8_t u16_hi(uint16_t value) +{ + return (uint8_t)((value & 0xFF00) >> 8); +} + +uint8_t u16_lo(uint16_t value) +{ + return (uint8_t)(value & 0x00FF); +} + +uint8_t u32_b3(uint32_t value) +{ + return (uint8_t)((value & 0xFF000000) >> 24); +} + +uint8_t u32_b2(uint32_t value) +{ + return (uint8_t)((value & 0x00FF0000) >> 16); +} + +uint8_t u32_b1(uint32_t value) +{ + return (uint8_t)((value & 0x0000FF00) >> 8); +} + +uint8_t u32_b0(uint32_t value) +{ + return (uint8_t)(value & 0x000000FF); +} + +uint8_t u64_b7(uint64_t value) +{ + return (uint8_t)((value & 0xFF00000000000000) >> 56); +} + +uint8_t u64_b6(uint64_t value) +{ + return (uint8_t)((value & 0x00FF000000000000) >> 48); +} + +uint8_t u64_b5(uint64_t value) +{ + return (uint8_t)((value & 0x0000FF0000000000) >> 40); +} + +uint8_t u64_b4(uint64_t value) +{ + return (uint8_t)((value & 0x000000FF00000000) >> 32); +} + +uint8_t u64_b3(uint64_t value) +{ + return (uint8_t)((value & 0x00000000FF000000) >> 24); +} + +uint8_t u64_b2(uint64_t value) +{ + return (uint8_t)((value & 0x0000000000FF0000) >> 16); +} + +uint8_t u64_b1(uint64_t value) +{ + return (uint8_t)((value & 0x000000000000FF00) >> 8); +} + +uint8_t u64_b0(uint64_t value) +{ + return (uint8_t)(value & 0x00000000000000FF); +} + +/* Checksum/CRC conversion to ASCII */ +uint8_t nibble2ascii(uint8_t value) +{ + uint8_t result = value & 0x0f; + + if (result > 9) return result + 0x41-0x0A; + else return result + 0x30; +} + +bool cs_isdecdigit(char c) +{ + return (c >= 0x30) && (c < 0x3A); +} + +unsigned char tohex(unsigned char c) +{ + if ((c >= '0') && (c < '9'+1)) + return (c - '0'); + if ((c >= 'A') && (c < 'F'+1)) + return (c - 'A' + 0x0A); + if ((c >= 'a') && (c < 'f'+1)) + return (c - 'a' + 0x0A); + + return 0; +} + +unsigned char todecimal(unsigned char c) +{ + if ((c >= '0') && (c < '9'+1)) + return (c - '0'); + + return 0; +} + + diff --git a/tools/hex2bin-2.0/src/binary.h b/tools/hex2bin-2.0/src/binary.h new file mode 100644 index 0000000..198589b --- /dev/null +++ b/tools/hex2bin-2.0/src/binary.h @@ -0,0 +1,36 @@ +#ifndef _BINARY_H_ +#define _BINARY_H_ + +typedef enum {false, true} bool; + +extern const unsigned char Reflect8[256]; + +uint16_t Reflect16(uint16_t Value16); +uint32_t Reflect24(uint32_t Value24); +uint32_t Reflect32(uint32_t Value32); +uint64_t Reflect40(uint64_t Value40); +uint64_t Reflect64(uint64_t Value64); + +uint8_t u16_hi(uint16_t value); +uint8_t u16_lo(uint16_t value); + +uint8_t u32_b3(uint32_t value); +uint8_t u32_b2(uint32_t value); +uint8_t u32_b1(uint32_t value); +uint8_t u32_b0(uint32_t value); + +uint8_t u64_b7(uint64_t value); +uint8_t u64_b6(uint64_t value); +uint8_t u64_b5(uint64_t value); +uint8_t u64_b4(uint64_t value); +uint8_t u64_b3(uint64_t value); +uint8_t u64_b2(uint64_t value); +uint8_t u64_b1(uint64_t value); +uint8_t u64_b0(uint64_t value); + +uint8_t nibble2ascii(uint8_t value); +bool cs_isdecdigit(char c); +unsigned char tohex(unsigned char c); +unsigned char todecimal(unsigned char c); + +#endif /* _BINARY_H_ */ diff --git a/tools/hex2bin-2.0/src/common.c b/tools/hex2bin-2.0/src/common.c new file mode 100644 index 0000000..f453dc4 --- /dev/null +++ b/tools/hex2bin-2.0/src/common.c @@ -0,0 +1,527 @@ +#include "common.h" + +filetype Filename; /* string for opening files */ +char Extension[MAX_EXTENSION_SIZE]; /* filename extension for output files */ + +FILE *Filin, /* input files */ + *Filout; /* output files */ + +#ifdef USE_FILE_BUFFERS +char *FilinBuf, /* text buffer for file input */ + *FiloutBuf; /* text buffer for file output */ +#endif + +int Pad_Byte = 0xFF; +bool Enable_Checksum_Error = false; +bool Status_Checksum_Error = false; +byte Checksum; +unsigned int Record_Nb; + +/* This will hold binary codes translated from hex file. */ +byte *Memory_Block; +unsigned int Lowest_Address, Highest_Address; +unsigned int Starting_Address; +unsigned int Max_Length = 0; +unsigned int Minimum_Block_Size = 0x1000; // 4096 byte +int Module; +bool Minimum_Block_Size_Setted = false; +bool Starting_Address_Setted = false; +bool Max_Length_Setted = false; +bool Swap_Wordwise = false; + +int Endian = 0; + +t_CRC Cks_Type = CHK8_SUM; +unsigned int Cks_Start = 0, Cks_End = 0, Cks_Addr = 0, Cks_Value = 0; +bool Cks_range_set = false; +bool Cks_Addr_set = false; +bool Force_Value = false; + +unsigned int Crc_Poly = 0x07, Crc_Init = 0, Crc_XorOut = 0; +bool Crc_RefIn = false; +bool Crc_RefOut = false; + +void usage(void) +{ + fprintf (stderr, + "\n" + "usage: %s [OPTIONS] filename\n" + "Options:\n" + " -c Enable record checksum verification\n" + " -C [Poly][Init][RefIn][RefOut][XorOut]\n CRC parameters" + " -e [ext] Output filename extension (without the dot)\n" + " -E [0|1] Endian for checksum/CRC, 0: little, 1: big\n" + " -f [address] Address of check result to write\n" + " -F [address] [value]\n Address and value to force\n" + " -k [0-4] Select check method (checksum or CRC) and size\n" + " -d display list of check methods/value size\n" + " -l [length] Maximal Length (Starting address + Length -1 is Max Address)\n" + " File will be filled with Pattern until Max Address is reached\n" + " -m [size] Minimum Block Size\n" + " File Size Dimension will be a multiple of Minimum block size\n" + " File will be filled with Pattern\n" + " Length must be a power of 2 in hexadecimal [see -l option]\n" + " Attention this option is STRONGER than Maximal Length \n" + " -p [value] Pad-byte value in hex (default: %x)\n" + " -r [start] [end]\n" + " Range to compute checksum over (default is min and max addresses)\n" + " -s [address] Starting address in hex (default: 0)\n" + " -w Swap wordwise (low <-> high)\n\n", + Pgm_Name,Pad_Byte); + exit(1); +} /* procedure USAGE */ + +void DisplayCheckMethods(void) +{ + fprintf (stderr, + "Check methods/value size:\n" + "0: Checksum 8-bit\n" + "1: Checksum 16-bit\n" + "2: CRC8\n" + "3: CRC16\n" + "4: CRC32\n"); + exit(1); +} + +#define LAST_CHECK_METHOD 4 + +void *NoFailMalloc (size_t size) +{ + void *result; + + if ((result = malloc (size)) == NULL) + { + fprintf (stderr,"Can't allocate memory.\n"); + exit(1); + } + return (result); +} + +/* Open the input file, with error checking */ +void NoFailOpenInputFile (char *Flnm) +{ + while ((Filin = fopen(Flnm,"r")) == NULL) + { + fprintf (stderr,"Input file %s cannot be opened. Enter new filename: ",Flnm); + if (fgets (Flnm,MAX_FILE_NAME_SIZE,stdin) == NULL) exit(1); /* modified error checking */ + + if (Flnm[strlen(Flnm) - 1] == '\n') Flnm[strlen(Flnm) - 1] = '\0'; + } + +#ifdef USE_FILE_BUFFERS + FilinBuf = (char *) NoFailMalloc (BUFFSZ); + setvbuf(Filin, FilinBuf, _IOFBF, BUFFSZ); +#endif +} /* procedure OPENFILIN */ + +/* Open the output file, with error checking */ +void NoFailOpenOutputFile (char *Flnm) +{ + while ((Filout = fopen(Flnm,"wb")) == NULL) + { + /* Failure to open the output file may be + simply due to an insufficiant permission setting. */ + fprintf(stderr,"Output file %s cannot be opened. Enter new file name: ", Flnm); + if (fgets(Flnm,MAX_FILE_NAME_SIZE,stdin) == NULL) exit(1); + + if (Flnm[strlen(Flnm) - 1] == '\n') Flnm[strlen(Flnm) - 1] = '\0'; + } + +#ifdef USE_FILE_BUFFERS + FiloutBuf = (char *) NoFailMalloc (BUFFSZ); + setvbuf(Filout, FiloutBuf, _IOFBF, BUFFSZ); +#endif + +} /* procedure OPENFILOUT */ + +void GetLine(char* str,FILE *in) +{ + char *result; + + result = fgets(str,MAX_LINE_SIZE,in); + if ((result == NULL) && !feof (in)) fprintf(stderr,"Error occurred while reading from file\n"); +} + +// 0 or 1 +int GetBin(const char *str) +{ + int result; + unsigned int value; + + result = sscanf(str,"%u",&value); + + if (result == 1) return value & 1; + else + { + fprintf(stderr,"GetBin: some error occurred when parsing options.\n"); + exit (1); + } +} + +int GetDec(const char *str) +{ + int result; + unsigned int value; + + result = sscanf(str,"%u",&value); + + if (result == 1) return value; + else + { + fprintf(stderr,"GetDec: some error occurred when parsing options.\n"); + exit (1); + } +} + +int GetHex(const char *str) +{ + int result; + unsigned int value; + + result = sscanf(str,"%x",&value); + + if (result == 1) return value; + else + { + fprintf(stderr,"GetHex: some error occurred when parsing options.\n"); + exit (1); + } +} + +// Char t/T: true f/F: false +bool GetBoolean(const char *str) +{ + int result; + unsigned char value, temp; + + result = sscanf(str,"%c",&value); + temp = tolower(value); + + if ((result == 1) && ((temp == 't') || (temp == 'f'))) + { + return (temp == 't'); + } + else + { + fprintf(stderr,"GetBoolean: some error occurred when parsing options.\n"); + exit (1); + } +} + +void GetExtension(const char *str,char *ext) +{ + if (strlen(str) > MAX_EXTENSION_SIZE) + usage(); + + strcpy(ext, str); +} + +/* Adds an extension to a file name */ +void PutExtension(char *Flnm, char *Extension) +{ + char *Period; /* location of period in file name */ + bool Samename; + + /* This assumes DOS like file names */ + /* Don't use strchr(): consider the following filename: + ../my.dir/file.hex + */ + if ((Period = strrchr(Flnm,'.')) != NULL) + *(Period) = '\0'; + + Samename = false; + if (strcmp(Extension, Period+1) == 0) + Samename = true; + + strcat(Flnm,"."); + strcat(Flnm, Extension); + if (Samename) + { + fprintf (stderr,"Input and output filenames (%s) are the same.", Flnm); + exit(1); + } +} + +void VerifyChecksumValue(void) +{ + if ((Checksum != 0) && Enable_Checksum_Error) + { + fprintf(stderr,"Checksum error in record %d: should be %02X\n", + Record_Nb, (256 - Checksum) & 0xFF); + Status_Checksum_Error = true; + } +} + +void CrcParamsCheck(void) +{ + switch (Cks_Type) + { + case CRC8: + Crc_Poly &= 0xFF; + Crc_Init &= 0xFF; + Crc_XorOut &= 0xFF; + break; + case CRC16: + Crc_Poly &= 0xFFFF; + Crc_Init &= 0xFFFF; + Crc_XorOut &= 0xFFFF; + break; + case CRC32: + break; + default: + fprintf (stderr,"See file CRC list.txt for parameters\n"); + exit(1); + } +} + +void WriteMemBlock16(uint16_t Value) +{ + if (Endian == 1) + { + Memory_Block[Cks_Addr - Lowest_Address] = u16_hi(Value); + Memory_Block[Cks_Addr - Lowest_Address +1] = u16_lo(Value); + } + else + { + Memory_Block[Cks_Addr - Lowest_Address +1] = u16_hi(Value); + Memory_Block[Cks_Addr - Lowest_Address] = u16_lo(Value); + } +} + +void WriteMemBlock32(uint32_t Value) +{ + if (Endian == 1) + { + Memory_Block[Cks_Addr - Lowest_Address] = u32_b3(Value); + Memory_Block[Cks_Addr - Lowest_Address +1] = u32_b2(Value); + Memory_Block[Cks_Addr - Lowest_Address +2] = u32_b1(Value); + Memory_Block[Cks_Addr - Lowest_Address +3] = u32_b0(Value); + } + else + { + Memory_Block[Cks_Addr - Lowest_Address +3] = u32_b3(Value); + Memory_Block[Cks_Addr - Lowest_Address +2] = u32_b2(Value); + Memory_Block[Cks_Addr - Lowest_Address +1] = u32_b1(Value); + Memory_Block[Cks_Addr - Lowest_Address] = u32_b0(Value); + } +} + +void WriteMemory(void) +{ + if ((Cks_Addr >= Lowest_Address) || (Cks_Addr < Highest_Address)) + { + if(Force_Value) + { + switch (Cks_Type) + { + case 0: + Memory_Block[Cks_Addr - Lowest_Address] = Cks_Value; + fprintf(stdout,"Addr %08X set to %02X\n",Cks_Addr, Cks_Value); + break; + case 1: + WriteMemBlock16(Cks_Value); + fprintf(stdout,"Addr %08X set to %04X\n",Cks_Addr, Cks_Value); + break; + case 2: + WriteMemBlock32(Cks_Value); + fprintf(stdout,"Addr %08X set to %08X\n",Cks_Addr, Cks_Value); + break; + default:; + } + } + else if (Cks_Addr_set) + { + /* Add a checksum to the binary file */ + if (!Cks_range_set) + { + Cks_Start = Lowest_Address; + Cks_End = Highest_Address; + } + /* checksum range MUST BE in the array bounds */ + + if (Cks_Start < Lowest_Address) + { + fprintf(stdout,"Modifying range start from %X to %X\n",Cks_Start,Lowest_Address); + Cks_Start = Lowest_Address; + } + if (Cks_End > Highest_Address) + { + fprintf(stdout,"Modifying range end from %X to %X\n",Cks_End,Highest_Address); + Cks_End = Highest_Address; + } + + switch (Cks_Type) + { + case CHK8_SUM: + { + uint8_t wCKS = 0; + + for (unsigned int i=Cks_Start; i<=Cks_End; i++) + { + wCKS += Memory_Block[i - Lowest_Address]; + } + + fprintf(stdout,"8-bit Checksum = %02X\n",wCKS & 0xff); + Memory_Block[Cks_Addr - Lowest_Address] = wCKS; + fprintf(stdout,"Addr %08X set to %02X\n",Cks_Addr, wCKS); + } + break; + + case CHK16: + { + uint16_t wCKS, w; + + wCKS = 0; + + if (Endian == 1) + { + for (unsigned int i=Cks_Start; i<=Cks_End; i+=2) + { + w = Memory_Block[i - Lowest_Address +1] | ((word)Memory_Block[i - Lowest_Address] << 8); + wCKS += w; + } + } + else + { + for (unsigned int i=Cks_Start; i<=Cks_End; i+=2) + { + w = Memory_Block[i - Lowest_Address] | ((word)Memory_Block[i - Lowest_Address +1] << 8); + wCKS += w; + } + } + fprintf(stdout,"16-bit Checksum = %04X\n",wCKS); + WriteMemBlock16(wCKS); + fprintf(stdout,"Addr %08X set to %04X\n",Cks_Addr, wCKS); + } + break; + + case CRC8: + { + uint8_t CRC8; + crc_table = NoFailMalloc(256); + + if (Crc_RefIn) + { + init_crc8_reflected_tab(Reflect8[Crc_Poly]); + CRC8 = Reflect8[Crc_Init]; + } + else + { + init_crc8_normal_tab(Crc_Poly); + CRC8 = Crc_Init; + } + + for (unsigned int i=Cks_Start; i<=Cks_End; i++) + { + CRC8 = update_crc8(CRC8,Memory_Block[i - Lowest_Address]); + } + + CRC8 = (CRC8 ^ Crc_XorOut) & 0xff; + Memory_Block[Cks_Addr - Lowest_Address] = CRC8; + fprintf(stdout,"Addr %08X set to %02X\n",Cks_Addr, CRC8); + } + break; + + case CRC16: + { + uint16_t CRC16; + crc_table = NoFailMalloc(256 * 2); + + if (Crc_RefIn) + { + init_crc16_reflected_tab(Reflect16(Crc_Poly)); + CRC16 = Reflect16(Crc_Init); + + for (unsigned int i=Cks_Start; i<=Cks_End; i++) + { + CRC16 = update_crc16_reflected(CRC16,Memory_Block[i - Lowest_Address]); + } + } + else + { + init_crc16_normal_tab(Crc_Poly); + CRC16 = Crc_Init; + + + for (unsigned int i=Cks_Start; i<=Cks_End; i++) + { + CRC16 = update_crc16_normal(CRC16,Memory_Block[i - Lowest_Address]); + } + } + + CRC16 = (CRC16 ^ Crc_XorOut) & 0xffff; + WriteMemBlock16(CRC16); + fprintf(stdout,"Addr %08X set to %04X\n",Cks_Addr, CRC16); + } + break; + + case CRC32: + { + uint32_t CRC32; + + crc_table = NoFailMalloc(256 * 4); + if (Crc_RefIn) + { + init_crc32_reflected_tab(Reflect32(Crc_Poly)); + CRC32 = Reflect32(Crc_Init); + + for (unsigned int i=Cks_Start; i<=Cks_End; i++) + { + CRC32 = update_crc32_reflected(CRC32,Memory_Block[i - Lowest_Address]); + } + } + else + { + init_crc32_normal_tab(Crc_Poly); + CRC32 = Crc_Init; + + for (unsigned int i=Cks_Start; i<=Cks_End; i++) + { + CRC32 = update_crc32_normal(CRC32,Memory_Block[i - Lowest_Address]); + } + } + + CRC32 ^= Crc_XorOut; + WriteMemBlock32(CRC32); + fprintf(stdout,"Addr %08X set to %08X\n",Cks_Addr, CRC32); + } + break; + + default: + ; + } + + free(crc_table); + } + } + else + { + fprintf (stderr,"Force/Check address outside of memory range\n"); + } + + /* write binary file */ + fwrite (Memory_Block, + Max_Length, + 1, + Filout); + + free (Memory_Block); + + // Minimum_Block_Size is set; the memory buffer is multiple of this? + if (Minimum_Block_Size_Setted==true) + { + Module = Max_Length % Minimum_Block_Size; + if (Module) + { + Memory_Block = (byte *) NoFailMalloc(Module); + memset (Memory_Block,Pad_Byte,Module); + fwrite (Memory_Block, + Module, + 1, + Filout); + free (Memory_Block); + if (Max_Length_Setted==true) + fprintf(stdout,"Attention Max Length changed by Minimum Block Size\n"); + } + } +} diff --git a/tools/hex2bin-2.0/src/common.h b/tools/hex2bin-2.0/src/common.h new file mode 100644 index 0000000..64caff5 --- /dev/null +++ b/tools/hex2bin-2.0/src/common.h @@ -0,0 +1,116 @@ +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> + +#include "binary.h" +#include "libcrc.h" + +/* To compile with Microsoft Visual Studio */ +#ifdef _MSC_VER +#define _CRT_SECURE_NO_WARNINGS +#endif + +#if defined(MSDOS) || defined(__DOS__) || defined(__MSDOS__) || defined(_MSDOS) +#define _IS_OPTION_(x) (((x) == '-') || ((x) == '/')) +#else +/* Assume unix and similar */ +/* We don't accept an option beginning with a '/' because it could be a file name. */ +#define _IS_OPTION_(x) ((x) == '-') +#endif + +/* We use buffer to speed disk access. */ +#ifdef USE_FILE_BUFFERS +#define BUFFSZ 4096 +#endif + +/* FIXME how to get it from the system/OS? */ +#define MAX_FILE_NAME_SIZE 81 + +#ifdef DOS +#define MAX_EXTENSION_SIZE 4 +#else +#define MAX_EXTENSION_SIZE 16 +#endif + +/* The data records can contain 255 bytes: this means 512 characters. */ +#define MAX_LINE_SIZE 1024 + +typedef char filetype[MAX_FILE_NAME_SIZE]; +typedef unsigned char byte; +typedef unsigned short word; + +#define LAST_CHECK_METHOD 4 + +typedef enum Crc +{ + CHK8_SUM =0, + CHK16, + CRC8, + CRC16, + CRC32 +} t_CRC; + +extern const char *Pgm_Name; +void usage(void); +void DisplayCheckMethods(void); + +void *NoFailMalloc (size_t size); +void NoFailOpenInputFile (char *Flnm); +void NoFailOpenOutputFile (char *Flnm); +void GetLine(char* str,FILE *in); +int GetBin(const char *str); +int GetDec(const char *str); +int GetHex(const char *str); +bool GetBoolean(const char *str); +void GetExtension(const char *str,char *ext); +void PutExtension(char *Flnm, char *Extension); + +filetype Filename; /* string for opening files */ +char Extension[MAX_EXTENSION_SIZE]; /* filename extension for output files */ + +FILE *Filin, /* input files */ + *Filout; /* output files */ + +#ifdef USE_FILE_BUFFERS +char *FilinBuf, /* text buffer for file input */ + *FiloutBuf; /* text buffer for file output */ +#endif + +int Pad_Byte; +bool Enable_Checksum_Error; +bool Status_Checksum_Error; +byte Checksum; +unsigned int Record_Nb; + +/* This will hold binary codes translated from hex file. */ +byte *Memory_Block; +unsigned int Lowest_Address, Highest_Address; +unsigned int Starting_Address; +unsigned int Max_Length; +unsigned int Minimum_Block_Size; +int Module; +bool Minimum_Block_Size_Setted; +bool Starting_Address_Setted; +bool Max_Length_Setted; +bool Swap_Wordwise; + +int Endian; + +t_CRC Cks_Type; +unsigned int Cks_Start, Cks_End, Cks_Addr, Cks_Value; +bool Cks_range_set; +bool Cks_Addr_set; +bool Force_Value; + +unsigned int Crc_Poly, Crc_Init, Crc_XorOut; +bool Crc_RefIn; +bool Crc_RefOut; + +void VerifyChecksumValue(void); +void CrcParamsCheck(void); +void WriteMemBlock16(uint16_t Value); +void WriteMemBlock32(uint32_t Value); +void WriteMemory(void); + diff --git a/tools/hex2bin-2.0/src/hex2bin.1 b/tools/hex2bin-2.0/src/hex2bin.1 new file mode 100644 index 0000000..c2e5e89 --- /dev/null +++ b/tools/hex2bin-2.0/src/hex2bin.1 @@ -0,0 +1,294 @@ +.\" Automatically generated by Pod::Man 2.27 (Pod::Simple 3.28) +.\" +.\" Standard preamble: +.\" ======================================================================== +.de Sp \" Vertical space (when we can't use .PP) +.if t .sp .5v +.if n .sp +.. +.de Vb \" Begin verbatim text +.ft CW +.nf +.ne \\$1 +.. +.de Ve \" End verbatim text +.ft R +.fi +.. +.\" Set up some character translations and predefined strings. \*(-- will +.\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left +.\" double quote, and \*(R" will give a right double quote. \*(C+ will +.\" give a nicer C++. Capital omega is used to do unbreakable dashes and +.\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, +.\" nothing in troff, for use with C<>. +.tr \(*W- +.ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' +.ie n \{\ +. ds -- \(*W- +. ds PI pi +. if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch +. if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch +. ds L" "" +. ds R" "" +. ds C` "" +. ds C' "" +'br\} +.el\{\ +. ds -- \|\(em\| +. ds PI \(*p +. ds L" `` +. ds R" '' +. ds C` +. ds C' +'br\} +.\" +.\" Escape single quotes in literal strings from groff's Unicode transform. +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" +.\" If the F register is turned on, we'll generate index entries on stderr for +.\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index +.\" entries marked with X<> in POD. Of course, you'll have to process the +.\" output yourself in some meaningful fashion. +.\" +.\" Avoid warning from groff about undefined register 'F'. +.de IX +.. +.nr rF 0 +.if \n(.g .if rF .nr rF 1 +.if (\n(rF:(\n(.g==0)) \{ +. if \nF \{ +. de IX +. tm Index:\\$1\t\\n%\t"\\$2" +.. +. if !\nF==2 \{ +. nr % 0 +. nr F 2 +. \} +. \} +.\} +.rr rF +.\" +.\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). +.\" Fear. Run. Save yourself. No user-serviceable parts. +. \" fudge factors for nroff and troff +.if n \{\ +. ds #H 0 +. ds #V .8m +. ds #F .3m +. ds #[ \f1 +. ds #] \fP +.\} +.if t \{\ +. ds #H ((1u-(\\\\n(.fu%2u))*.13m) +. ds #V .6m +. ds #F 0 +. ds #[ \& +. ds #] \& +.\} +. \" simple accents for nroff and troff +.if n \{\ +. ds ' \& +. ds ` \& +. ds ^ \& +. ds , \& +. ds ~ ~ +. ds / +.\} +.if t \{\ +. ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" +. ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' +. ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' +. ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' +. ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' +. ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' +.\} +. \" troff and (daisy-wheel) nroff accents +.ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' +.ds 8 \h'\*(#H'\(*b\h'-\*(#H' +.ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] +.ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' +.ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' +.ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] +.ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] +.ds ae a\h'-(\w'a'u*4/10)'e +.ds Ae A\h'-(\w'A'u*4/10)'E +. \" corrections for vroff +.if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' +.if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' +. \" for low resolution devices (crt and lpr) +.if \n(.H>23 .if \n(.V>19 \ +\{\ +. ds : e +. ds 8 ss +. ds o a +. ds d- d\h'-1'\(ga +. ds D- D\h'-1'\(hy +. ds th \o'bp' +. ds Th \o'LP' +. ds ae ae +. ds Ae AE +.\} +.rm #[ #] #H #V #F C +.\" ======================================================================== +.\" +.IX Title "HEX2BIN 1" +.TH HEX2BIN 1 "2015-02-28" "perl v5.18.2" "User Contributed Perl Documentation" +.\" For nroff, turn off justification. Always turn off hyphenation; it makes +.\" way too many mistakes in technical documents. +.if n .ad l +.nh +.SH "NAME" +hex2bin/mot2bin \e\- converts Intel/Motorola hex files into binary +.SH "SYNOPSIS" +.IX Header "SYNOPSIS" +hex2bin [options] file +.PP +Option list: + [\-c] + [\-C Poly Init RefIn RefOut XorOut] + [\-e extension] + [\-E 0|1] + [\-f address] + [\-F address value] + [\-k checksum type] + [\-l length] + [\-m minimum block size] + [\-p pad byte] + [\-r start end] + [\-s address] + [\-w] +.SH "DESCRIPTION" +.IX Header "DESCRIPTION" +\&\fBHex2bin\fR +is a program that converts an Intel hex format into binary. +It can handle the extended Intel hex format. Both the segmented +and linear address records are supported. +Records need not be sorted and there can be gaps between records. +Records are assumed to be non-overlapping. +Padding bytes may be specified and a checksum may be inserted in the +binary file. +.PP +\&\fBMot2bin\fR +does the same with Motorola hex files. It has the same features and command line +options. 24 bit and 32 bit records are supported. +.SH "OPTIONS" +.IX Header "OPTIONS" +Options can be specified in any order, with the file name at the end. Options are +now case sensitive. All option values are specified in hexadecimal. +.PP +\&\fB\-c\fR +.PP +Enables checksum verification. +.PP +By default, it ignores checksum errors in the hex file, so that someone can change +by hand some bytes with a text editor, allowing quick fixes without recompiling a source +code all over again. This is useful when tweaking constants directly in the code or +something similar. If you want checksum error reporting, specify the option \-c. +.PP +\&\fBEx.: hex2bin \-c example.hex\fR +.PP +If there is a checksum error somewhere, the program will continue the +conversion anyway. +.PP +\&\fB\-C Poly Init RefIn RefOut XorOut\fR +.PP +\&\s-1CRC\s0 parameters. See the doc/CRC list.txt file for a description of common CRCs. See also +the test/Makefile for examples of command lines. Needs \fB\-k\fR and \fB\-f\fR option. +RefIn and RefOut parameters are specified by \fBt\fR or \fBf\fR for true or false. +.PP +\&\fB\-d\fR +.PP +Display the list of available check methods and sizes. +.PP +\&\fB\-e extension\fR +.PP +By default, the output file will have an extension \fBfilename.bin\fR. +Another extension may be specified with this command: +.PP +\&\fBEx.: hex2bin \-e com example.hex\fR +.PP +The output file will be example.com +.PP +\&\fB\-E 0|1\fR +.PP +Endianness for writing the check result or forcing a 16\-bit value. + \fB0\fR: little, \fB1\fR: big. +.PP +By default, little endian is used. +.PP +\&\fB\-f address\fR +.PP +Address in hexadecimal for inserting the check value in the binary file. Needs \fB\-k\fR +option to specify the check method. A range can be specified with option \fB\-r\fR. +.PP +\&\fB\-F address value\fR +.PP +Address and value of checksum to insert (force) in the binary file. Needs \fB\-k\fR +option to specify the size. The value is written as is. +.PP +\&\fB\-k 0\-4\fR +.PP +In many cases, someone needs to insert a check value in the binary file. For example, +a boot rom is programmed with a checksum which is verified at power-up. This feature +uses also options \fB\-r\fR, \fB\-C\fR and \fB\-f\fR. Display the list with \fB\-d\fR. +.PP +Select the checksum type to insert into the binary file + 0: Checksum 8\-bit + 1: Checksum 16\-bit + 2: \s-1CRC8 + 3: CRC16 + 4: CRC32\s0 +.PP +\&\fB\-l length\fR +.PP +The binary file will be padded with \s-1FF\s0 or pad bytes as specified by the option +below, up to a maximal Length (Starting address + Length \-1 is Max Address) +.PP +\&\fB\-m minimum_block_size\fR +.PP +File Size Dimension will be a multiple of Minimum block size. +File will be filled with Pattern. +Length must be a power of 2 in hexadecimal [see \fB\-l\fR option] +Attention this option is \s-1STRONGER\s0 than Maximal Length +.PP +\&\fB\-p pad_byte\fR +.PP +Pads unused locations with the specified byte. +.PP +By default, this byte is \s-1FF,\s0 which is the unprogrammed value for most EPROM/EEPROM/Flash. +.PP +\&\fBEx.: hex2bin \-p 3E example.hex\fR +.PP +\&\fB\-r [start] [end]\fR +.PP +Range to compute binary checksum over (default is min and max addresses) +.PP +\&\fB\-s address\fR +.PP +Specify the starting address of the binary file. +.PP +Normally, hex2bin will generate a binary file starting at the lowest address in +the hex file. If the lowest address isn't 0000, ex: 0100, the first byte that +should be at 0100 will be stored at address 0000 in the binary file. This may +cause problems when using the binary file to program an \s-1EPROM.\s0 +.PP +If you can't specify the starting address (or offset) to your \s-1EPROM\s0 programmer, +you can specify a starting address on the command line: +.PP +\&\fBEx.: hex2bin \-s 0000 records_start_at_0100.hex\fR +.PP +The bytes will be stored in the binary file with a padding from 0000 to the +lowest address (00FF in this case). Padding bytes are all \s-1FF\s0 by default so an \s-1EPROM\s0 +programmer can skip these bytes when programming. The padding value can be changed +with the \-p option. +.PP +\&\fB\-w\fR +.PP +Swap wordwise (low <\-> high). Used by Microchip's \s-1MPLAB IDE\s0 +.SH "NOTES" +.IX Header "NOTES" +This program does minimal error checking since many hex files are +generated by known good assemblers. +.SH "AUTHOR Jacques Pelletier (jpelletier@ieee.org) \- version 2.0" +.IX Header "AUTHOR Jacques Pelletier (jpelletier@ieee.org) - version 2.0" diff --git a/tools/hex2bin-2.0/src/hex2bin.c b/tools/hex2bin-2.0/src/hex2bin.c new file mode 100644 index 0000000..7537d8c --- /dev/null +++ b/tools/hex2bin-2.0/src/hex2bin.c @@ -0,0 +1,587 @@ +/* + hex2bin converts an Intel hex file to binary. + + Copyright (C) 2015, Jacques Pelletier + checksum extensions Copyright (C) 2004 Rockwell Automation + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + 20040617 Alf Lacis: Added pad byte (may not always want FF). + Added 'break;' to remove GNU compiler warning about label at + end of compound statement + Added PROGRAM & VERSION strings. + + 20071005 PG: Improvements on options parsing + 20091212 JP: Corrected crash on 0 byte length data records + 20100402 JP: Corrected bug on physical address calculation for extended + linear address record. + ADDRESS_MASK is now calculated from MEMORY_SIZE + + 20120125 Danny Schneider: + Added code for filling a binary file to a given Max_Length relative to + Starting Address if Max-Address is larger than Highest-Address + 20120509 Yoshimasa Nakane: + modified error checking (also for output file, JP) + 20141005 JP: added support for byte swapped hex files + corrected bug caused by extra LF at end or within file + 20141008 JP: removed junk code + 20141121 Slucx: added line for removing extra CR when entering file name at run time. + 20141122 Simone Fratini: small feature added + 20150116 Richard Genoud (Paratronic): correct buffer overflows/wrong results with the -l flag + 20150122 JP: added support for different check methods + 20150221 JP: rewrite of the checksum write/force value +*/ + +#define PROGRAM "hex2bin" +#define VERSION "2.0" + +#include "common.h" + +#define NO_ADDRESS_TYPE_SELECTED 0 +#define LINEAR_ADDRESS 1 +#define SEGMENTED_ADDRESS 2 + +const char *Pgm_Name = PROGRAM; + +int main (int argc, char *argv[]) +{ + /* line inputted from file */ + char Line[MAX_LINE_SIZE]; + + /* flag that a file was read */ + bool Fileread; + + /* cmd-line parameter # */ + char *p; + + int Param,result; + + /* Application specific */ + + unsigned int Nb_Bytes; + unsigned int First_Word, Address, Segment, Upper_Address; + unsigned int Phys_Addr, Type; + unsigned int temp; + unsigned int Records_Start; // Lowest address of the records + + /* We will assume that when one type of addressing is selected, it will be valid for all the + current file. Records for the other type will be ignored. */ + unsigned int Seg_Lin_Select = NO_ADDRESS_TYPE_SELECTED; + + unsigned int temp2; + + byte Data_Str[MAX_LINE_SIZE]; + + fprintf (stdout,PROGRAM" v"VERSION", Copyright (C) 2015 Jacques Pelletier & contributors\n\n"); + + if (argc == 1) + usage(); + + strcpy(Extension, "bin"); /* default is for binary file extension */ + + /* read file */ + Starting_Address = 0; + + /* + use p for parsing arguments + use i for number of parameters to skip + use c for the current option + */ + for (Param = 1; Param < argc; Param++) + { + int i = 0; + char c; + + p = argv[Param]; + c = *(p+1); /* Get option character */ + + if ( _IS_OPTION_(*p) ) + { + // test for no space between option and parameter + if (strlen(p) != 2) usage(); + + switch(c) + { + /* file extension */ + case 'c': + Enable_Checksum_Error = true; + i = 0; + break; + case 'd': + DisplayCheckMethods(); + case 'e': + GetExtension(argv[Param + 1],Extension); + i = 1; /* add 1 to Param */ + break; + case 'E': + Endian = GetBin(argv[Param + 1]); + i = 1; /* add 1 to Param */ + break; + case 'f': + Cks_Addr = GetHex(argv[Param + 1]); + Cks_Addr_set = true; + i = 1; /* add 1 to Param */ + break; + case 'F': + Cks_Addr = GetHex(argv[Param + 1]); + Cks_Value = GetHex(argv[Param + 2]); + Force_Value = true; + i = 2; /* add 2 to Param */ + break; + case 'k': + Cks_Type = GetHex(argv[Param + 1]); + { + if (Cks_Type > LAST_CHECK_METHOD) usage(); + } + i = 1; /* add 1 to Param */ + break; + case 'l': + Max_Length = GetHex(argv[Param + 1]); + if (Max_Length > 0x800000) + { + fprintf(stderr,"Max_Length = %u\n", Max_Length); + exit(1); + } + Max_Length_Setted = true; + i = 1; /* add 1 to Param */ + break; + case 'm': + Minimum_Block_Size = GetHex(argv[Param + 1]); + Minimum_Block_Size_Setted = true; + i = 1; /* add 1 to Param */ + break; + case 'p': + Pad_Byte = GetHex(argv[Param + 1]); + i = 1; /* add 1 to Param */ + break; + case 'r': + Cks_Start = GetHex(argv[Param + 1]); + Cks_End = GetHex(argv[Param + 2]); + Cks_range_set = true; + i = 2; /* add 2 to Param */ + break; + case 's': + Starting_Address = GetHex(argv[Param + 1]); + Starting_Address_Setted = true; + i = 1; /* add 1 to Param */ + break; + case 'w': + Swap_Wordwise = true; + i = 0; + break; + case 'C': + Crc_Poly = GetHex(argv[Param + 1]); + Crc_Init = GetHex(argv[Param + 2]); + Crc_RefIn = GetBoolean(argv[Param + 3]); + Crc_RefOut = GetBoolean(argv[Param + 4]); + Crc_XorOut = GetHex(argv[Param + 5]); + CrcParamsCheck(); + i = 5; /* add 5 to Param */ + break; + + case '?': + case 'h': + default: + usage(); + } /* switch */ + + /* Last parameter is not a filename */ + if (Param == argc-1) usage(); + + /* if (Param + i) < (argc -1) */ + if (Param < argc -1 -i) Param += i; + else usage(); + + } + else + break; + /* if option */ + } /* for Param */ + + /* when user enters input file name */ + + /* Assume last parameter is filename */ + strcpy(Filename,argv[argc -1]); + + /* Just a normal file name */ + NoFailOpenInputFile (Filename); + PutExtension(Filename, Extension); + NoFailOpenOutputFile(Filename); + Fileread = true; + + /* To begin, assume the lowest address is at the end of the memory. + While reading each records, subsequent addresses will lower this number. + At the end of the input file, this value will be the lowest address. + + A similar assumption is made for highest address. It starts at the + beginning of memory. While reading each records, subsequent addresses will raise this number. + At the end of the input file, this value will be the highest address. */ + Lowest_Address = (unsigned int)-1; + Highest_Address = 0; + Records_Start = 0; + Segment = 0; + Upper_Address = 0; + Record_Nb = 0; // Used for reporting errors + + /* get highest and lowest addresses so that we can allocate the right size */ + do + { + unsigned int i; + + /* Read a line from input file. */ + GetLine(Line,Filin); + Record_Nb++; + + /* Remove carriage return/line feed at the end of line. */ + i = strlen(Line); + + if (--i != 0) + { + if (Line[i] == '\n') Line[i] = '\0'; + + /* Scan the first two bytes and nb of bytes. + The two bytes are read in First_Word since its use depend on the + record type: if it's an extended address record or a data record. + */ + result = sscanf (Line, ":%2x%4x%2x%s",&Nb_Bytes,&First_Word,&Type,Data_Str); + if (result != 4) fprintf(stderr,"Error in line %d of hex file\n", Record_Nb); + + p = (char *) Data_Str; + + /* If we're reading the last record, ignore it. */ + switch (Type) + { + /* Data record */ + case 0: + if (Nb_Bytes == 0) + break; + + Address = First_Word; + + if (Seg_Lin_Select == SEGMENTED_ADDRESS) + { + Phys_Addr = (Segment << 4) + Address; + } + else + { + /* LINEAR_ADDRESS or NO_ADDRESS_TYPE_SELECTED + Upper_Address = 0 as specified in the Intel spec. until an extended address + record is read. */ + Phys_Addr = ((Upper_Address << 16) + Address); + } + + /* Set the lowest address as base pointer. */ + if (Phys_Addr < Lowest_Address) + Lowest_Address = Phys_Addr; + + /* Same for the top address. */ + temp = Phys_Addr + Nb_Bytes -1; + + if (temp > Highest_Address) + Highest_Address = temp; + + break; + + case 2: + /* First_Word contains the offset. It's supposed to be 0000 so + we ignore it. */ + + /* First extended segment address record ? */ + if (Seg_Lin_Select == NO_ADDRESS_TYPE_SELECTED) + Seg_Lin_Select = SEGMENTED_ADDRESS; + + /* Then ignore subsequent extended linear address records */ + if (Seg_Lin_Select == SEGMENTED_ADDRESS) + { + result = sscanf (p, "%4x%2x",&Segment,&temp2); + if (result != 2) fprintf(stderr,"Error in line %d of hex file\n", Record_Nb); + + /* Update the current address. */ + Phys_Addr = (Segment << 4); + } + else + { + fprintf(stderr,"Ignored extended linear address record %d\n", Record_Nb); + } + break; + + case 4: + /* First_Word contains the offset. It's supposed to be 0000 so + we ignore it. */ + + /* First extended linear address record ? */ + if (Seg_Lin_Select == NO_ADDRESS_TYPE_SELECTED) + Seg_Lin_Select = LINEAR_ADDRESS; + + /* Then ignore subsequent extended segment address records */ + if (Seg_Lin_Select == LINEAR_ADDRESS) + { + result = sscanf (p, "%4x%2x",&Upper_Address,&temp2); + if (result != 2) fprintf(stderr,"Error in line %d of hex file\n", Record_Nb); + + /* Update the current address. */ + Phys_Addr = (Upper_Address << 16); + } + else + { + fprintf(stderr,"Ignored extended segment address record %d\n", Record_Nb); + } + break; + + default: + break; + } + } + } + while (!feof (Filin)); + + rewind(Filin); + Segment = 0; + Upper_Address = 0; + Record_Nb = 0; + + if (Starting_Address_Setted == true) + { + Records_Start = Lowest_Address; + Lowest_Address = Starting_Address; + } + else + { + Records_Start = Lowest_Address; + Starting_Address = Lowest_Address; + } + + if (Max_Length_Setted == false) + Max_Length = Highest_Address - Lowest_Address + 1; + else + Highest_Address = Lowest_Address + Max_Length - 1; + + /* Now, that we know the buffer size, we can allocate it. */ + /* allocate a buffer */ + Memory_Block = (byte *) NoFailMalloc(Max_Length); + + /* For EPROM or FLASH memory types, fill unused bytes with FF or the value specified by the p option */ + memset (Memory_Block,Pad_Byte,Max_Length); + + /* Read the file & process the lines. */ + do /* repeat until EOF(Filin) */ + { + unsigned int i; + + /* Read a line from input file. */ + GetLine(Line,Filin); + Record_Nb++; + + /* Remove carriage return/line feed at the end of line. */ + i = strlen(Line); + + //fprintf(stderr,"Record: %d; length: %d\n", Record_Nb, i); + + if (--i != 0) + { + if (Line[i] == '\n') Line[i] = '\0'; + + /* Scan the first two bytes and nb of bytes. + The two bytes are read in First_Word since its use depend on the + record type: if it's an extended address record or a data record. + */ + result = sscanf (Line, ":%2x%4x%2x%s",&Nb_Bytes,&First_Word,&Type,Data_Str); + if (result != 4) fprintf(stderr,"Error in line %d of hex file\n", Record_Nb); + + Checksum = Nb_Bytes + (First_Word >> 8) + (First_Word & 0xFF) + Type; + + p = (char *) Data_Str; + + /* If we're reading the last record, ignore it. */ + switch (Type) + { + /* Data record */ + case 0: + if (Nb_Bytes == 0) + { + fprintf(stderr,"0 byte length Data record ignored\n"); + break; + } + + Address = First_Word; + + if (Seg_Lin_Select == SEGMENTED_ADDRESS) + Phys_Addr = (Segment << 4) + Address; + else + /* LINEAR_ADDRESS or NO_ADDRESS_TYPE_SELECTED + Upper_Address = 0 as specified in the Intel spec. until an extended address + record is read. */ + Phys_Addr = ((Upper_Address << 16) + Address); + + /* Check that the physical address stays in the buffer's range. */ + if ((Phys_Addr >= Lowest_Address) && (Phys_Addr <= Highest_Address)) + { + /* The memory block begins at Lowest_Address */ + Phys_Addr -= Lowest_Address; + + /* Read the Data bytes. */ + /* Bytes are written in the Memory block even if checksum is wrong. */ + i = Nb_Bytes; + + do + { + result = sscanf (p, "%2x",&temp2); + if (result != 1) fprintf(stderr,"Error in line %d of hex file\n", Record_Nb); + p += 2; + + /* Check that the physical address stays in the buffer's range. */ + if (Phys_Addr < Max_Length) + { + /* Overlapping record will erase the pad bytes */ + if (Swap_Wordwise) + { + if (Memory_Block[Phys_Addr ^ 1] != Pad_Byte) fprintf(stderr,"Overlapped record detected\n"); + Memory_Block[Phys_Addr++ ^ 1] = temp2; + } + else + { + if (Memory_Block[Phys_Addr] != Pad_Byte) fprintf(stderr,"Overlapped record detected\n"); + Memory_Block[Phys_Addr++] = temp2; + } + + Checksum = (Checksum + temp2) & 0xFF; + } + } + while (--i != 0); + + /* Read the Checksum value. */ + result = sscanf (p, "%2x",&temp2); + if (result != 1) fprintf(stderr,"Error in line %d of hex file\n", Record_Nb); + + /* Verify Checksum value. */ + Checksum = (Checksum + temp2) & 0xFF; + VerifyChecksumValue(); + } + else + { + if (Seg_Lin_Select == SEGMENTED_ADDRESS) + fprintf(stderr,"Data record skipped at %4X:%4X\n",Segment,Address); + else + fprintf(stderr,"Data record skipped at %8X\n",Phys_Addr); + } + + break; + + /* End of file record */ + case 1: + /* Simply ignore checksum errors in this line. */ + break; + + /* Extended segment address record */ + case 2: + /* First_Word contains the offset. It's supposed to be 0000 so + we ignore it. */ + + /* First extended segment address record ? */ + if (Seg_Lin_Select == NO_ADDRESS_TYPE_SELECTED) + Seg_Lin_Select = SEGMENTED_ADDRESS; + + /* Then ignore subsequent extended linear address records */ + if (Seg_Lin_Select == SEGMENTED_ADDRESS) + { + result = sscanf (p, "%4x%2x",&Segment,&temp2); + if (result != 2) fprintf(stderr,"Error in line %d of hex file\n", Record_Nb); + + /* Update the current address. */ + Phys_Addr = (Segment << 4); + + /* Verify Checksum value. */ + Checksum = (Checksum + (Segment >> 8) + (Segment & 0xFF) + temp2) & 0xFF; + VerifyChecksumValue(); + } + break; + + /* Start segment address record */ + case 3: + /* Nothing to be done since it's for specifying the starting address for + execution of the binary code */ + break; + + /* Extended linear address record */ + case 4: + /* First_Word contains the offset. It's supposed to be 0000 so + we ignore it. */ + + /* First extended linear address record ? */ + if (Seg_Lin_Select == NO_ADDRESS_TYPE_SELECTED) + Seg_Lin_Select = LINEAR_ADDRESS; + + /* Then ignore subsequent extended segment address records */ + if (Seg_Lin_Select == LINEAR_ADDRESS) + { + result = sscanf (p, "%4x%2x",&Upper_Address,&temp2); + if (result != 2) fprintf(stderr,"Error in line %d of hex file\n", Record_Nb); + + /* Update the current address. */ + Phys_Addr = (Upper_Address << 16); + + /* Verify Checksum value. */ + Checksum = (Checksum + (Upper_Address >> 8) + (Upper_Address & 0xFF) + temp2) + & 0xFF; + VerifyChecksumValue(); + } + break; + + /* Start linear address record */ + case 5: + /* Nothing to be done since it's for specifying the starting address for + execution of the binary code */ + break; + default: + fprintf(stderr,"Unknown record type\n"); + break; + } + } + } + while (!feof (Filin)); + /*-----------------------------------------------------------------------------*/ + + fprintf(stdout,"Binary file start = %08X\n",Lowest_Address); + fprintf(stdout,"Records start = %08X\n",Records_Start); + fprintf(stdout,"Highest address = %08X\n",Highest_Address); + fprintf(stdout,"Pad Byte = %X\n", Pad_Byte); + + WriteMemory(); + +#ifdef USE_FILE_BUFFERS + free (FilinBuf); + free (FiloutBuf); +#endif + + fclose (Filin); + fclose (Filout); + + if (Status_Checksum_Error && Enable_Checksum_Error) + { + fprintf(stderr,"Checksum error detected.\n"); + return 1; + } + + if (!Fileread) + usage(); + return 0; +} diff --git a/tools/hex2bin-2.0/src/hex2bin.pod b/tools/hex2bin-2.0/src/hex2bin.pod new file mode 100644 index 0000000..a8eb238 --- /dev/null +++ b/tools/hex2bin-2.0/src/hex2bin.pod @@ -0,0 +1,161 @@ +HEX2BIN 1 "2015 february 22nd" "Hex2bin Version 2.0" +=head1 NAME + +hex2bin/mot2bin \- converts Intel/Motorola hex files into binary + +=head1 SYNOPSIS + +hex2bin [options] file + +Option list: + [-c] + [-C Poly Init RefIn RefOut XorOut] + [-e extension] + [-E 0|1] + [-f address] + [-F address value] + [-k checksum type] + [-l length] + [-m minimum block size] + [-p pad byte] + [-r start end] + [-s address] + [-w] + +=head1 DESCRIPTION + +B<Hex2bin> +is a program that converts an Intel hex format into binary. +It can handle the extended Intel hex format. Both the segmented +and linear address records are supported. +Records need not be sorted and there can be gaps between records. +Records are assumed to be non-overlapping. +Padding bytes may be specified and a checksum may be inserted in the +binary file. + +B<Mot2bin> +does the same with Motorola hex files. It has the same features and command line +options. 24 bit and 32 bit records are supported. + +=head1 OPTIONS + +Options can be specified in any order, with the file name at the end. Options are +now case sensitive. All option values are specified in hexadecimal. + +B<-c> + +Enables checksum verification. + +By default, it ignores checksum errors in the hex file, so that someone can change +by hand some bytes with a text editor, allowing quick fixes without recompiling a source +code all over again. This is useful when tweaking constants directly in the code or +something similar. If you want checksum error reporting, specify the option -c. + +B<Ex.: hex2bin -c example.hex> + +If there is a checksum error somewhere, the program will continue the +conversion anyway. + +B<-C Poly Init RefIn RefOut XorOut> + +CRC parameters. See the doc/CRC list.txt file for a description of common CRCs. See also +the test/Makefile for examples of command lines. Needs B<-k> and B<-f> option. +RefIn and RefOut parameters are specified by B<t> or B<f> for true or false. + +B<-d> + +Display the list of available check methods and sizes. + +B<-e extension> + +By default, the output file will have an extension B<filename.bin>. +Another extension may be specified with this command: + +B<Ex.: hex2bin -e com example.hex> + +The output file will be example.com + +B<-E 0|1> + +Endianness for writing the check result or forcing a 16-bit value. + B<0>: little, B<1>: big. + +By default, little endian is used. + +B<-f address> + +Address in hexadecimal for inserting the check value in the binary file. Needs B<-k> +option to specify the check method. A range can be specified with option B<-r>. + +B<-F address value> + +Address and value of checksum to insert (force) in the binary file. Needs B<-k> +option to specify the size. The value is written as is. + +B<-k 0-4> + +In many cases, someone needs to insert a check value in the binary file. For example, +a boot rom is programmed with a checksum which is verified at power-up. This feature +uses also options B<-r>, B<-C> and B<-f>. Display the list with B<-d>. + +Select the checksum type to insert into the binary file + 0: Checksum 8-bit + 1: Checksum 16-bit + 2: CRC8 + 3: CRC16 + 4: CRC32 + +B<-l length> + +The binary file will be padded with FF or pad bytes as specified by the option +below, up to a maximal Length (Starting address + Length -1 is Max Address) + +B<-m minimum_block_size> + +File Size Dimension will be a multiple of Minimum block size. +File will be filled with Pattern. +Length must be a power of 2 in hexadecimal [see B<-l> option] +Attention this option is STRONGER than Maximal Length + +B<-p pad_byte> + +Pads unused locations with the specified byte. + +By default, this byte is FF, which is the unprogrammed value for most EPROM/EEPROM/Flash. + +B<Ex.: hex2bin -p 3E example.hex> + +B<-r [start] [end]> + +Range to compute binary checksum over (default is min and max addresses) + +B<-s address> + +Specify the starting address of the binary file. + +Normally, hex2bin will generate a binary file starting at the lowest address in +the hex file. If the lowest address isn't 0000, ex: 0100, the first byte that +should be at 0100 will be stored at address 0000 in the binary file. This may +cause problems when using the binary file to program an EPROM. + +If you can't specify the starting address (or offset) to your EPROM programmer, +you can specify a starting address on the command line: + +B<Ex.: hex2bin -s 0000 records_start_at_0100.hex> + +The bytes will be stored in the binary file with a padding from 0000 to the +lowest address (00FF in this case). Padding bytes are all FF by default so an EPROM +programmer can skip these bytes when programming. The padding value can be changed +with the -p option. + +B<-w> + +Swap wordwise (low <-> high). Used by Microchip's MPLAB IDE + +=head1 NOTES + +This program does minimal error checking since many hex files are +generated by known good assemblers. + +=head1 AUTHOR +Jacques Pelletier (jpelletier@ieee.org) - version 2.0 diff --git a/tools/hex2bin-2.0/src/libcrc.c b/tools/hex2bin-2.0/src/libcrc.c new file mode 100644 index 0000000..02bcd4b --- /dev/null +++ b/tools/hex2bin-2.0/src/libcrc.c @@ -0,0 +1,204 @@ +/********************************************************************* + * * + * Library : lib_crc * + * File : lib_crc.c * + * Author : Lammert Bies 1999-2008 * + * E-mail : info@lammertbies.nl * + * Language : ANSI C * + * * + * * + * Description * + * =========== * + * * + * The file lib_crc.c contains the private and public func- * + * tions used for the calculation of CRC-16, CRC-CCITT and * + * CRC-32 cyclic redundancy values. * + * * + * * + * Dependencies * + * ============ * + * * + * libcrc.h CRC definitions and prototypes * + * * + ********************************************************************/ +#include <stdint.h> + +#ifndef G_GUINT64_CONSTANT +#define G_GUINT64_CONSTANT(val) (val##UL) +#endif + +void *crc_table; + +/* private */ + +void init_crc8_normal_tab(uint8_t polynom) +{ + int i, j; + uint8_t crc; + uint8_t *p; + + p = (uint8_t *) crc_table; + + for (i=0; i<256; i++) + { + crc = (uint8_t) i; + + for (j=0; j<8; j++) + { + if (crc & 0x80) crc = (crc << 1) ^ polynom; + else crc <<= 1; + } + *p++ = crc; + } +} + +void init_crc8_reflected_tab(uint8_t polynom) +{ + int i, j; + uint8_t crc; + uint8_t *p; + + p = (uint8_t *) crc_table; + + for (i=0; i<256; i++) + { + crc = (uint8_t) i; + + for (j=0; j<8; j++) + { + if (crc & 0x01) crc = (crc >> 1) ^ polynom; + else crc >>= 1; + } + *p++ = crc; + } +} + +/* Common routines for calculations */ +void init_crc16_normal_tab(uint16_t polynom) +{ + int i, j; + uint16_t crc; + uint16_t *p; + + p = (uint16_t *) crc_table; + + for (i=0; i<256; i++) + { + crc = ((uint16_t) i) << 8; + + for (j=0; j<8; j++) + { + if ( crc & 0x8000 ) crc = ( crc << 1 ) ^ polynom; + else crc <<= 1; + } + *p++ = crc; + } +} + +void init_crc16_reflected_tab(uint16_t polynom) +{ + int i, j; + uint16_t crc; + uint16_t *p; + + p = (uint16_t *) crc_table; + + for (i=0; i<256; i++) + { + crc = (uint16_t) i; + + for (j=0; j<8; j++) + { + if ( crc & 0x0001 ) crc = ( crc >> 1 ) ^ polynom; + else crc >>= 1; + } + *p++ = crc; + } +} + +void init_crc32_normal_tab(uint32_t polynom) +{ + int i, j; + uint32_t crc; + uint32_t *p; + + p = (uint32_t *) crc_table; + + for (i=0; i<256; i++) + { + crc = ((uint32_t) i) << 24; + + for (j=0; j<8; j++) + { + if ( crc & 0x80000000L ) crc = ( crc << 1 ) ^ polynom; + else crc <<= 1; + } + *p++ = crc; + } +} + +void init_crc32_reflected_tab(uint32_t polynom) +{ + int i, j; + uint32_t crc; + uint32_t *p; + + p = (uint32_t *) crc_table; + + for (i=0; i<256; i++) + { + crc = (uint32_t) i; + + for (j=0; j<8; j++) + { + if ( crc & 0x00000001L ) crc = ( crc >> 1 ) ^ polynom; + else crc >>= 1; + } + *p++ = crc; + } +} + +/* Common routines for calculations */ + +uint8_t update_crc8(uint8_t crc, uint8_t c) +{ + return (((uint8_t *) crc_table)[crc ^ c]); +} + +uint16_t update_crc16_normal(uint16_t crc, char c ) +{ + uint16_t short_c; + + short_c = 0x00ff & (uint16_t) c; + + /* Normal form */ + return (crc << 8) ^ ((uint16_t *) crc_table)[(crc >> 8) ^ short_c]; +} + +uint16_t update_crc16_reflected(uint16_t crc, char c ) +{ + uint16_t short_c; + + short_c = 0x00ff & (uint16_t) c; + + /* Reflected form */ + return (crc >> 8) ^ ((uint16_t *) crc_table)[(crc ^ short_c) & 0xff]; +} + +uint32_t update_crc32_normal(uint32_t crc, char c ) +{ + uint32_t long_c; + + long_c = 0x000000ffL & (uint32_t) c; + + return (crc << 8) ^ ((uint32_t *) crc_table)[((crc >> 24) ^ long_c) & 0xff]; +} + +uint32_t update_crc32_reflected(uint32_t crc, char c ) +{ + uint32_t long_c; + + long_c = 0x000000ffL & (uint32_t) c; + + return (crc >> 8) ^ ((uint32_t *) crc_table)[(crc ^ long_c) & 0xff]; +} diff --git a/tools/hex2bin-2.0/src/libcrc.h b/tools/hex2bin-2.0/src/libcrc.h new file mode 100644 index 0000000..39f20d0 --- /dev/null +++ b/tools/hex2bin-2.0/src/libcrc.h @@ -0,0 +1,44 @@ + /******************************************************************** + * * + * Library : lib_crc * + * File : lib_crc.h * + * Author : Lammert Bies 1999-2008 * + * E-mail : info@lammertbies.nl * + * Language : ANSI C * + * * + * * + * Description * + * =========== * + * * + * The file lib_crc.h contains public definitions and proto- * + * types for the CRC functions present in lib_crc.c. * + * * + * * + * Dependencies * + * ============ * + * * + * none * + * * + * * + ********************************************************************/ +#ifndef _LIBCRC_H_ +#define _LIBCRC_H_ + +void *crc_table; + +void init_crc8_normal_tab(uint8_t polynom); +void init_crc8_reflected_tab(uint8_t polynom); + +void init_crc16_normal_tab(uint16_t polynom); +void init_crc16_reflected_tab(uint16_t polynom); +void init_crc32_normal_tab(uint32_t polynom); +void init_crc32_reflected_tab(uint32_t polynom); + +uint8_t update_crc8(uint8_t crc, uint8_t c); + +uint16_t update_crc16_normal(uint16_t crc, char c ); +uint16_t update_crc16_reflected(uint16_t crc, char c ); +uint32_t update_crc32_normal(uint32_t crc, char c ); +uint32_t update_crc32_reflected(uint32_t crc, char c ); + +#endif /* _LIBCRC_H_ */ diff --git a/tools/hex2bin-2.0/src/mot2bin.c b/tools/hex2bin-2.0/src/mot2bin.c new file mode 100644 index 0000000..9e0e23c --- /dev/null +++ b/tools/hex2bin-2.0/src/mot2bin.c @@ -0,0 +1,518 @@ +/* +mot2bin converts a Motorola hex file to binary. + +Copyright (C) 2015, Jacques Pelletier +checksum extensions Copyright (C) 2004 Rockwell Automation +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: +Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimer. +Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +20040617 Alf Lacis: Added pad byte (may not always want FF). + Added initialisation to Checksum to remove GNU + compiler warning about possible uninitialised usage + Added 2x'break;' to remove GNU compiler warning about label at + end of compound statement + Added PROGRAM & VERSION strings. + +20071005 PG: Improvements on options parsing +20091212 JP: Corrected crash on 0 byte length data records +20100402 JP: ADDRESS_MASK is now calculated from MEMORY_SIZE + +20120125 Danny Schneider: + Added code for filling a binary file to a given Max_Length relative to + Starting Address if Max-Address is larger than Highest-Address +20120509 Yoshimasa Nakane: + modified error checking (also for output file, JP) +20141005 JP: added support for byte swapped hex files + corrected bug caused by extra LF at end or within file +20141121 Slucx: added line for removing extra CR when entering file name at run time. +20150116 Richard Genoud (Paratronic): correct buffer overflows/wrong results with the -l flag +20150122 JP: added support for different check methods +20150221 JP: rewrite of the checksum write/force value +*/ + +#define PROGRAM "mot2bin" +#define VERSION "2.0" + +#include "common.h" + +const char *Pgm_Name = PROGRAM; + +int main (int argc, char *argv[]) +{ + /* line inputted from file */ + char Line[MAX_LINE_SIZE]; + + /* flag that a file was read */ + bool Fileread; + + /* cmd-line parameter # */ + char *p; + + int Param, result; + + /* Application specific */ + + unsigned int Nb_Bytes; + unsigned int First_Word, Address; + + unsigned int Phys_Addr, Type; + unsigned int Exec_Address; + unsigned int temp; + unsigned int Record_Count, Record_Checksum; + unsigned int Records_Start; // Lowest address of the records + + unsigned int temp2; + + byte Data_Str[MAX_LINE_SIZE]; + + fprintf (stdout,PROGRAM" v"VERSION", Copyright (C) 2015 Jacques Pelletier & contributors\n\n"); + + if (argc == 1) + usage(); + + strcpy(Extension, "bin"); /* default is for binary file extension */ + + /* read file */ + Starting_Address = 0; + + /* + use p for parsing arguments + use i for number of parameters to skip + use c for the current option + */ + for (Param = 1; Param < argc; Param++) + { + int i = 0; + char c; + + p = argv[Param]; + c = *(p+1); /* Get option character */ + + if ( _IS_OPTION_(*p) ) + { + // test for no space between option and parameter + if (strlen(p) != 2) usage(); + + switch(c) + { + /* file extension */ + case 'c': + Enable_Checksum_Error = true; + i = 0; + break; + case 'd': + DisplayCheckMethods(); + case 'e': + GetExtension(argv[Param + 1],Extension); + i = 1; /* add 1 to Param */ + break; + case 'f': + Cks_Addr = GetHex(argv[Param + 1]); + Cks_Addr_set = true; + i = 1; /* add 1 to Param */ + break; + case 'F': + Cks_Addr = GetHex(argv[Param + 1]); + Cks_Value = GetHex(argv[Param + 2]); + Force_Value = true; + i = 2; /* add 2 to Param */ + break; + case 'k': + Cks_Type = GetHex(argv[Param + 1]); + { + if (Cks_Type > LAST_CHECK_METHOD) usage(); + } + i = 1; /* add 1 to Param */ + break; + case 'l': + Max_Length = GetHex(argv[Param + 1]); + Max_Length_Setted = true; + i = 1; /* add 1 to Param */ + break; + case 'm': + Minimum_Block_Size = GetHex(argv[Param + 1]); + Minimum_Block_Size_Setted = true; + i = 1; /* add 1 to Param */ + break; + case 'p': + Pad_Byte = GetHex(argv[Param + 1]); + i = 1; /* add 1 to Param */ + break; + case 'r': + Cks_Start = GetHex(argv[Param + 1]); + Cks_End = GetHex(argv[Param + 2]); + Cks_range_set = true; + i = 2; /* add 2 to Param */ + break; + case 's': + Starting_Address = GetHex(argv[Param + 1]); + Starting_Address_Setted = true; + i = 1; /* add 1 to Param */ + break; + case 'w': + Swap_Wordwise = true; + i = 0; + break; + case 'C': + Crc_Poly = GetHex(argv[Param + 1]); + Crc_Init = GetHex(argv[Param + 2]); + Crc_RefIn = GetBoolean(argv[Param + 3]); + Crc_RefOut = GetBoolean(argv[Param + 4]); + Crc_XorOut = GetHex(argv[Param + 5]); + CrcParamsCheck(); + i = 5; /* add 5 to Param */ + break; + + case '?': + case 'h': + default: + usage(); + } /* switch */ + + /* Last parameter is not a filename */ + if (Param == argc-1) usage(); + + // fprintf(stderr,"Param: %d, option: %c\n",Param,c); + + /* if (Param + i) < (argc -1) */ + if (Param < argc -1 -i) Param += i; + else usage(); + + } + else + break; + /* if option */ + } /* for Param */ + + /* when user enters input file name */ + + /* Assume last parameter is filename */ + strcpy(Filename,argv[argc -1]); + + /* Just a normal file name */ + NoFailOpenInputFile (Filename); + PutExtension(Filename, Extension); + NoFailOpenOutputFile(Filename); + Fileread = true; + + /* To begin, assume the lowest address is at the end of the memory. + While reading each records, subsequent addresses will lower this number. + At the end of the input file, this value will be the lowest address. + + A similar assumption is made for highest address. It starts at the + beginning of memory. While reading each records, subsequent addresses will raise this number. + At the end of the input file, this value will be the highest address. */ + Lowest_Address = (unsigned int)-1; + Highest_Address = 0; + Records_Start = 0; + Record_Nb = 0; + + /* get highest and lowest addresses so that we can allocate the right size */ + do + { + unsigned int i; + + /* Read a line from input file. */ + GetLine(Line,Filin); + Record_Nb++; + + /* Remove carriage return/line feed at the end of line. */ + i = strlen(Line); + + if (--i != 0) + { + if (Line[i] == '\n') Line[i] = '\0'; + + p = (char *) Data_Str; + + switch(Line[1]) + { + case '0': + break; + + /* 16 bits address */ + case '1': + result = sscanf (Line,"S%1x%2x%4x",&Type,&Nb_Bytes,&First_Word); + if (result != 3) fprintf(stderr,"Error in line %d of hex file\n", Record_Nb); + + /* Adjust Nb_Bytes for the number of data bytes */ + Nb_Bytes = Nb_Bytes - 3; + break; + + /* 24 bits address */ + case '2': + result = sscanf (Line,"S%1x%2x%6x",&Type,&Nb_Bytes,&First_Word); + if (result != 3) fprintf(stderr,"Error in line %d of hex file\n", Record_Nb); + + /* Adjust Nb_Bytes for the number of data bytes */ + Nb_Bytes = Nb_Bytes - 4; + break; + + /* 32 bits address */ + case '3': + result = sscanf (Line,"S%1x%2x%8x",&Type,&Nb_Bytes,&First_Word); + if (result != 3) fprintf(stderr,"Error in line %d of hex file\n", Record_Nb); + + /* Adjust Nb_Bytes for the number of data bytes */ + Nb_Bytes = Nb_Bytes - 5; + break; + } + + Phys_Addr = First_Word; + + /* Set the lowest address as base pointer. */ + if (Phys_Addr < Lowest_Address) + Lowest_Address = Phys_Addr; + + /* Same for the top address. */ + temp = Phys_Addr + Nb_Bytes -1; + + if (temp > Highest_Address) + Highest_Address = temp; + } + } + while (!feof (Filin)); + + if (Starting_Address_Setted == true) + { + Records_Start = Lowest_Address; + Lowest_Address = Starting_Address; + } + else + { + Records_Start = Lowest_Address; + Starting_Address = Lowest_Address; + } + + if (Max_Length_Setted == false) + Max_Length = Highest_Address - Lowest_Address + 1; + else + Highest_Address = Lowest_Address + Max_Length - 1; + + /* Now, that we know the buffer size, we can allocate it. */ + /* allocate a buffer */ + Memory_Block = (byte *) NoFailMalloc(Max_Length); + + /* For EPROM or FLASH memory types, fill unused bytes with FF or the value specified by the p option */ + memset (Memory_Block,Pad_Byte,Max_Length); + + rewind(Filin); + Record_Nb = 0; + + /* Read the file & process the lines. */ + do /* repeat until EOF(Filin) */ + { + int i; + + Checksum = 0; + + /* Read a line from input file. */ + GetLine(Line,Filin); + Record_Nb++; + + /* Remove carriage return/line feed at the end of line. */ + i = strlen(Line); + + if (--i != 0) + { + if (Line[i] == '\n') Line[i] = '\0'; + + /* Scan starting address and nb of bytes. */ + /* Look at the record type after the 'S' */ + Type = 0; + + switch(Line[1]) + { + case '0': + result = sscanf (Line,"S0%2x0000484452%2x",&Nb_Bytes,&Record_Checksum); + if (result != 2) fprintf(stderr,"Error in line %d of hex file\n", Record_Nb); + Checksum = Nb_Bytes + 0x48 + 0x44 + 0x52; + + /* Adjust Nb_Bytes for the number of data bytes */ + Nb_Bytes = 0; + break; + + /* 16 bits address */ + case '1': + result = sscanf (Line,"S%1x%2x%4x%s",&Type,&Nb_Bytes,&Address,Data_Str); + if (result != 4) fprintf(stderr,"Error in line %d of hex file\n", Record_Nb); + Checksum = Nb_Bytes + (Address >> 8) + (Address & 0xFF); + + /* Adjust Nb_Bytes for the number of data bytes */ + Nb_Bytes = Nb_Bytes - 3; + break; + + /* 24 bits address */ + case '2': + result = sscanf (Line,"S%1x%2x%6x%s",&Type,&Nb_Bytes,&Address,Data_Str); + if (result != 4) fprintf(stderr,"Error in line %d of hex file\n", Record_Nb); + Checksum = Nb_Bytes + (Address >> 16) + (Address >> 8) + (Address & 0xFF); + + /* Adjust Nb_Bytes for the number of data bytes */ + Nb_Bytes = Nb_Bytes - 4; + break; + + /* 32 bits address */ + case '3': + result = sscanf (Line,"S%1x%2x%8x%s",&Type,&Nb_Bytes,&Address,Data_Str); + if (result != 4) fprintf(stderr,"Error in line %d of hex file\n", Record_Nb); + Checksum = Nb_Bytes + (Address >> 24) + (Address >> 16) + (Address >> 8) + (Address & 0xFF); + + /* Adjust Nb_Bytes for the number of data bytes */ + Nb_Bytes = Nb_Bytes - 5; + break; + + case '5': + result = sscanf (Line,"S%1x%2x%4x%2x",&Type,&Nb_Bytes,&Record_Count,&Record_Checksum); + if (result != 4) fprintf(stderr,"Error in line %d of hex file\n", Record_Nb); + Checksum = Nb_Bytes + (Record_Count >> 8) + (Record_Count & 0xFF); + + /* Adjust Nb_Bytes for the number of data bytes */ + Nb_Bytes = 0; + break; + + case '7': + result = sscanf (Line,"S%1x%2x%8x%2x",&Type,&Nb_Bytes,&Exec_Address,&Record_Checksum); + if (result != 4) fprintf(stderr,"Error in line %d of hex file\n", Record_Nb); + Checksum = Nb_Bytes + (Exec_Address >> 24) + (Exec_Address >> 16) + (Exec_Address >> 8) + (Exec_Address & 0xFF); + Nb_Bytes = 0; + break; + + case '8': + result = sscanf (Line,"S%1x%2x%6x%2x",&Type,&Nb_Bytes,&Exec_Address,&Record_Checksum); + if (result != 4) fprintf(stderr,"Error in line %d of hex file\n", Record_Nb); + Checksum = Nb_Bytes + (Exec_Address >> 16) + (Exec_Address >> 8) + (Exec_Address & 0xFF); + Nb_Bytes = 0; + break; + case '9': + result = sscanf (Line,"S%1x%2x%4x%2x",&Type,&Nb_Bytes,&Exec_Address,&Record_Checksum); + if (result != 4) fprintf(stderr,"Error in line %d of hex file\n", Record_Nb); + Checksum = Nb_Bytes + (Exec_Address >> 8) + (Exec_Address & 0xFF); + Nb_Bytes = 0; + break; + } + + p = (char *) Data_Str; + + /* If we're reading the last record, ignore it. */ + switch (Type) + { + /* Data record */ + case 1: + case 2: + case 3: + if (Nb_Bytes == 0) + { + fprintf(stderr,"0 byte length Data record ignored\n"); + break; + } + + Phys_Addr = Address; + + /* Read the Data bytes. */ + i = Nb_Bytes; + + do + { + result = sscanf (p, "%2x",&temp2); + if (result != 1) fprintf(stderr,"Error in line %d of hex file\n", Record_Nb); + p += 2; + + /* Overlapping record will erase the pad bytes */ + if (Swap_Wordwise) + { + if (Memory_Block[Phys_Addr ^ 1] != Pad_Byte) fprintf(stderr,"Overlapped record detected\n"); + Memory_Block[Phys_Addr++ ^ 1] = temp2; + } + else + { + if (Memory_Block[Phys_Addr] != Pad_Byte) fprintf(stderr,"Overlapped record detected\n"); + Memory_Block[Phys_Addr++] = temp2; + } + + Checksum = (Checksum + temp2) & 0xFF; + } + while (--i != 0); + + /* Read the Checksum value. */ + result = sscanf (p, "%2x",&Record_Checksum); + if (result != 1) fprintf(stderr,"Error in line %d of hex file\n", Record_Nb); + break; + + case 5: + fprintf(stderr,"Record total: %d\n",Record_Count); + break; + + case 7: + fprintf(stderr,"Execution Address (unused): %08X\n",Exec_Address); + break; + + case 8: + fprintf(stderr,"Execution Address (unused): %06X\n",Exec_Address); + break; + + case 9: + fprintf(stderr,"Execution Address (unused): %04X\n",Exec_Address); + break; + + /* Ignore all other records */ + default:; + } + + Record_Checksum &= 0xFF; + + /* Verify Checksum value. */ + if (((Record_Checksum + Checksum) != 0xFF) && Enable_Checksum_Error) + { + fprintf(stderr,"Checksum error in record %d: should be %02X\n",Record_Nb, 255-Checksum); + Status_Checksum_Error = true; + } + } + } + while (!feof (Filin)); + /*-----------------------------------------------------------------------------*/ + + fprintf(stdout,"Binary file start = %08X\n",Lowest_Address); + fprintf(stdout,"Records start = %08X\n",Records_Start); + fprintf(stdout,"Highest address = %08X\n",Highest_Address); + fprintf(stdout,"Pad Byte = %X\n", Pad_Byte); + + WriteMemory(); + +#ifdef USE_FILE_BUFFERS + free (FilinBuf); + free (FiloutBuf); +#endif + + fclose (Filin); + fclose (Filout); + + if (Status_Checksum_Error && Enable_Checksum_Error) + { + fprintf(stderr,"Checksum error detected.\n"); + return 1; + } + + if (!Fileread) + usage(); + return 0; +} diff --git a/tools/iDSK/AUTHORS b/tools/iDSK/AUTHORS new file mode 100644 index 0000000..0c18ff6 --- /dev/null +++ b/tools/iDSK/AUTHORS @@ -0,0 +1,3 @@ +cpcemu - Marco Vieth +manageDSK - Ludovic Deplanque +iDSK - Sid from IMPACT / PulkoMandy from the Shinra Team diff --git a/tools/iDSK/CMakeLists.txt b/tools/iDSK/CMakeLists.txt new file mode 100644 index 0000000..9e8319e --- /dev/null +++ b/tools/iDSK/CMakeLists.txt @@ -0,0 +1,15 @@ +PROJECT(iDSK) + +add_executable(iDSK + src/Basic.cpp + src/BitmapCPC.cpp + src/Dams.cpp + src/Desass.cpp + src/endianPPC.cpp + src/GestDsk.cpp + src/getopt_pp.cpp + src/Main.cpp + src/Outils.cpp + src/ViewFile.cpp + src/Ascii.cpp +) diff --git a/tools/iDSK/COPYING b/tools/iDSK/COPYING new file mode 100644 index 0000000..d200743 --- /dev/null +++ b/tools/iDSK/COPYING @@ -0,0 +1,23 @@ +Portions parts of CPCemu, (C) 1991-2002 Marco Vieth & Rainer Loritz +Portions parts of ManageDSK, (c) 2005-2015 Ludovic Deplanque (Demoniak) + +iDSK is copyright (c) ????-2019 Jérôme Le Saux (Sid), Colin Pitrat, Thomas Bernard (miniupnp), Romain Giot (Krusty) and Adrien Destugues (PulkoMandy) + +Distributed under the terms of the MIT License + +Permission is hereby granted, free of charge, to any person obtaining a copy of this +software and associated documentation files (the "Software"), to deal in the Software +without restriction, including without limitation the rights to use, copy, modify, +merge, publish, distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be included in all copies +or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF +CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE +OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/tools/iDSK/README.md b/tools/iDSK/README.md new file mode 100644 index 0000000..54a773c --- /dev/null +++ b/tools/iDSK/README.md @@ -0,0 +1,7 @@ +[![Build Status](https://travis-ci.org/cpcsdk/idsk.svg?branch=master)](https://travis-ci.org/cpcsdk/idsk) + +iDSK is a tool to edit DSK (Amstrad CPC disk images) files from the command +line. You can add and remove files from your image, but also list BASIC and +DAMS source files (which are usually in a tokenized format, not plain ASCII). + +iDSK can add and remove AMSDOS headers as needed. diff --git a/tools/iDSK/src/Ascii.cpp b/tools/iDSK/src/Ascii.cpp new file mode 100644 index 0000000..19fced5 --- /dev/null +++ b/tools/iDSK/src/Ascii.cpp @@ -0,0 +1,16 @@ +#include <iostream> +using namespace std; +#include <cmath> +#include <cstring> +#include <cstdio> +#include <ctype.h> + +#include "MyType.h" +#include "Ascii.h" + +void Ascii(unsigned char *Prg, char *Listing, int Longueur) +{ + int Adr = 0; + * Listing = 0; + strncpy(Listing,(const char*)Prg,Longueur); +}
\ No newline at end of file diff --git a/tools/iDSK/src/Ascii.h b/tools/iDSK/src/Ascii.h new file mode 100644 index 0000000..688f8a4 --- /dev/null +++ b/tools/iDSK/src/Ascii.h @@ -0,0 +1,6 @@ +#ifndef ASCII_H +#define ASCII_H + +void Ascii(unsigned char *Prg, char *Listing, int Longueur); + +#endif
\ No newline at end of file diff --git a/tools/iDSK/src/Basic.cpp b/tools/iDSK/src/Basic.cpp new file mode 100644 index 0000000..6fe044d --- /dev/null +++ b/tools/iDSK/src/Basic.cpp @@ -0,0 +1,359 @@ +#include <iostream> +using namespace std; +#include <cmath> +#include <cstring> +#include <cstdio> +#include <ctype.h> + +#include "MyType.h" +#include "Basic.h" + + +//static char ConvCpcFr[ 128 ] = " !\"#$%&'()*+,-./0123456789:;<=>?à ABCDEFGHIJKLMNOPQRSTUVWXYZ[ç]^_`abcdefghijklmnopqrstuvwxyzéùè~"; + + +// +// Tableau de décryptage d'un programme en basic protégé +// +static BYTE DproBasic[ 128 ] = + { + 0xAB, 0x2C, 0xED, 0xEA, 0x6C, 0x37, 0x3F, 0xEC, + 0x9B, 0xDF, 0x7A, 0x0C, 0x3B, 0xD4, 0x6D, 0xF5, + 0x04, 0x44, 0x03, 0x11, 0xDF, 0x59, 0x8F, 0x21, + 0x73, 0x7A, 0xCC, 0x83, 0xDD, 0x30, 0x6A, 0x30, + 0xD3, 0x8F, 0x02, 0xF0, 0x60, 0x6B, 0x94, 0xE4, + 0xB7, 0xF3, 0x03, 0xA8, 0x60, 0x88, 0xF0, 0x43, + 0xE8, 0x8E, 0x43, 0xA0, 0xCA, 0x84, 0x31, 0x53, + 0xF3, 0x1F, 0xC9, 0xE8, 0xAD, 0xC0, 0xBA, 0x6D, + 0x93, 0x08, 0xD4, 0x6A, 0x2C, 0xB2, 0x07, 0x27, + 0xC0, 0x99, 0xEE, 0x89, 0xAF, 0xC3, 0x53, 0xAB, + 0x2B, 0x34, 0x5C, 0x2F, 0x13, 0xEE, 0xAA, 0x2C, + 0xD9, 0xF4, 0xBC, 0x12, 0xB3, 0xC5, 0x1C, 0x68, + 0x01, 0x20, 0x2C, 0xFA, 0x77, 0xA6, 0xB5, 0xA4, + 0xFC, 0x9B, 0xF1, 0x32, 0x5B, 0xC3, 0x70, 0x77, + 0x85, 0x36, 0xBE, 0x5B, 0x8C, 0xC8, 0xB5, 0xC2, + 0xF0, 0x0B, 0x98, 0x0F, 0x36, 0x9D, 0xD8, 0x96 + }; + + +BYTE GetByte( BYTE * BufFile, int Pos, int Deprotect ) +{ + //BYTE b = ( BYTE )( BufFile[ Pos ] ^ ( DproBasic[ Pos & 0x7F ] * Deprotect ) ); + //cout << "GetByte:"<<hex<<b<<endl; + return( BYTE )( BufFile[ Pos ] ^ ( DproBasic[ Pos & 0x7F ] * Deprotect ) ); +} + + +int GetWord( BYTE * BufFile, int Pos, int Deprotect ) +{ + int Ret = BufFile[ Pos ] ^ ( DproBasic[ Pos & 0x7F ] * Deprotect ); + Pos++; + Ret += ( ( BufFile[ Pos ] ^ ( DproBasic[ Pos & 0x7F ] * Deprotect ) ) << 8 ); + return( Ret ); +} + + +// +// Ajoute un "mot" (nom d'une variable, RSX...) dans la chaine "Listing" +// +int AddWord( BYTE * BufFile, int Pos, char * Listing, int Deprotect ) +{ + int LenVar = 0, l = strlen( Listing ); + BYTE b; + + do + { + b = GetByte( BufFile, Pos++, Deprotect ); + Listing[ l++ ] = ( char )( b & 0x7F ); + } + while( ! ( b & 0x80 ) && LenVar++ < 0xFF ); + Listing[ l ] = 0; + return( Pos ); +} + + +// +// Convertir le buffer en listing basic +// +void Basic( BYTE * BufFile, char * Listing, bool IsBasic, bool CrLf ) +{ + static char Tmp[ 32 ]; + int Pos = 0, Token = 0; + int StartLigne = 0, EndLigne; + char * p; + double f; + int exp; + int Deprotect=0; + //cout << BufFile <<endl; + static const char * Nbre[ 11 ] = + { + "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10" + }; + static const char * MotsClefs[ 0x80 ] = + { + "AFTER", "AUTO", "BORDER", "CALL", "CAT", "CHAIN", "CLEAR", "CLG", + "CLOSEIN", "CLOSEOUT", "CLS", "CONT", "DATA", "DEF", "DEFINT", + "DEFREAL", "DEFSTR", "DEG", "DELETE", "DIM", "DRAW", "DRAWR", "EDIT", + "ELSE", "END", "ENT", "ENV", "ERASE", "ERROR", "EVERY", "FOR", + "GOSUB", "GOTO", "IF", "INK", "INPUT", "KEY", "LET", "LINE", "LIST", + "LOAD", "LOCATE", "MEMORY", "MERGE", "MID$", "MODE", "MOVE", "MOVER", + "NEXT", "NEW", "ON", "ON BREAK", "ON ERROR GOTO", "SQ", "OPENIN", + "OPENOUT", "ORIGIN", "OUT", "PAPER", "PEN", "PLOT", "PLOTR", "POKE", + "PRINT", "'", "RAD", "RANDOMIZE", "READ", "RELEASE", "REM", "RENUM", + "RESTORE", "RESUME", "RETURN", "RUN", "SAVE", "SOUND", "SPEED", "STOP", + "SYMBOL", "TAG", "TAGOFF", "TROFF", "TRON", "WAIT", "WEND", "WHILE", + "WIDTH", "WINDOW", "WRITE", "ZONE", "DI", "EI", "FILL", "GRAPHICS", + "MASK", "FRAME", "CURSOR", "#E2", "ERL", "FN", "SPC", "STEP", "SWAP", + "#E8", "#E9", "TAB", "THEN", "TO", "USING", ">", "=", ">=", "<", "<>", + "<=", "+", "-", "*", "/", "^", "\\ ", "AND", "MOD", "OR", "XOR", "NOT", + "#FF" + }; + + static const char * Fcts[ 0x80 ] = + { + "ABS", "ASC", "ATN", "CHR$", "CINT", "COS", "CREAL", "EXP", "FIX", + "FRE", "INKEY", "INP", "INT", "JOY", "LEN", "LOG", "LOG10", "LOWER$", + "PEEK", "REMAIN", "SGN", "SIN", "SPACE$", "SQ", "SQR", "STR$", "TAN", + "UNT", "UPPER$", "VAL", "", "", "", "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "EOF", "ERR", "HIMEM", "INKEY$", "PI", "RND", + "TIME", "XPOS", "YPOS", "DERR", "", "", "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", "", "", "", "BIN$", "DEC$", "HEX$", + "INSTR", "LEFT$", "MAX", "MIN", "POS", "RIGHT$", "ROUND", "STRING$", + "TEST", "TESTR", "COPYCHR$", "VPOS" + }; + + + * Listing = 0; + Token = GetByte( BufFile, 0, Deprotect ); + for ( ;; ) + { + //cout << "Listing : " <<Listing << endl; + if ( IsBasic ) + { + int lg = GetWord( BufFile, Pos, Deprotect ); + Pos += 2; + if ( ! lg ) + break; + + int NumLigne = GetWord( BufFile, Pos, Deprotect ); + Pos += 2; + sprintf( Tmp, "%d ", NumLigne ); + strcat( Listing, Tmp ); + } + else + if ( ! Token || Token == 0x1A ) + break; + + int DansChaine = 0; // #### Hum, plus compliqué que ça je pense... + do + { + //cout << "Tmp:"<<Tmp<<endl; + Token = GetByte( BufFile, Pos++, Deprotect ); + if ( ! IsBasic && Token == 0x1A ) + break; + + if ( DansChaine || ! IsBasic ) + { + Tmp[ 0 ] = ( char )Token; + Tmp[ 1 ] = 0; + strcat( Listing, Tmp ); + if ( Token == '"' ) + DansChaine ^= 1; + // cout << " DansChaine Tmp:"<<Tmp<<endl; + } + else + if ( Token > 0x7F && Token < 0xFF ) + { + // #### Traitement particulier du ':' avant le ELSE + if ( Listing[ strlen( Listing ) - 1 ] == ':' + && Token == 0x97 + ) + Listing[ strlen( Listing ) - 1 ] = 0; + + strcat( Listing + , MotsClefs[ Token & 0x7F ] + ); + } + else + if ( Token >= 0x0E && Token <= 0x18 ) + strcat( Listing + , Nbre[ Token - 0x0E ] + ); + else + if ( Token >= 0x20 && Token < 0x7C ) + { + Tmp[ 0 ] = ( char )Token; + Tmp[ 1 ] = 0; + strcat( Listing, Tmp ); + if ( Token == '"' ) + DansChaine ^= 1; + } + else + { + //cout << "Token:" << Token <<endl; + switch( Token ) + { + case 0x01 : + Tmp[ 0 ] = ':'; + Tmp[ 1 ] = 0; + strcat( Listing, Tmp ); + break; + + case 0x02 : // Variable entière (type %) + Pos = AddWord( BufFile + , 2 + Pos + , Listing + , Deprotect + ); + strcat( Listing, "%" ); + break; + + + case 0x03 : // Variable chaine (type $) + Pos = AddWord( BufFile + , 2 + Pos + , Listing + , Deprotect + ); + strcat( Listing, "$" ); + break; + + case 0x04 : // Variable float (type !) + Pos = AddWord( BufFile + , 2 + Pos + , Listing + , Deprotect + ); + strcat( Listing, "!" ); + break; + + case 0x0B : + case 0x0C : + case 0x0D : // Variable "standard" + Pos = AddWord( BufFile + , 2 + Pos + , Listing + , Deprotect + ); + break; + + case 0x19 : // Constante entière 8 bits + sprintf(Listing+strlen(Listing),"%d",(BYTE)GetByte( BufFile, Pos, Deprotect)); + Pos++; + break; + + case 0x1A : + case 0x1E : // Constante entière 16 bits + sprintf(Listing+strlen(Listing),"%d",GetWord( BufFile, Pos, Deprotect)); + Pos += 2; + break; + + case 0x1B : + sprintf( Tmp + , "&X%X" + , GetWord( BufFile, Pos, Deprotect ) + ); + strcat( Listing, Tmp ); + Pos += 2; + break; + + case 0x1C : + sprintf( Tmp + , "&%X" + , GetWord( BufFile, Pos, Deprotect ) + ); + strcat( Listing, Tmp ); + Pos += 2; + break; + + case 0x1F : // Constante flottante + f = ( GetByte( BufFile, Pos + 2, Deprotect ) << 16 ) + + ( GetByte( BufFile, Pos + 1, Deprotect ) << 8 ) + + GetByte( BufFile, Pos, Deprotect ) + + ( ( GetByte( BufFile, Pos + 3, Deprotect ) & 0x7F ) << 24 ); + f = 1 + ( f / 0x80000000 ); + + if ( GetByte( BufFile, Pos + 3, Deprotect ) & 0x80 ) + f = -f; + + exp = GetByte( BufFile, Pos + 4, Deprotect ) - 129; + Pos += 5; + sprintf( Tmp, "%f", f * pow( (double) 2, exp ) ); + // Suppression des '0' inutiles + p = &Tmp[ strlen( Tmp ) - 1 ]; + while( * p == '0' ) + * p-- = 0; + + if ( * p == '.' ) + * p = 0; + + strcat( Listing, Tmp ); + break; + + case 0x7C : + strcat( Listing, "|" ); + Pos = AddWord( BufFile + , 1 + Pos + , Listing + , Deprotect + ); + break; + + case 0xFF : + if ( GetByte( BufFile, Pos, Deprotect ) < 0x80 ) + strcat( Listing + , Fcts[ GetByte( BufFile + , Pos++ + , Deprotect + ) + ] + ); + else + { + Tmp[ 1 ] = 0; + Tmp[ 0 ] = ( char )( GetByte( BufFile + , Pos++ + , Deprotect + ) & 0x7F + ); + strcat( Listing, Tmp ); + } + break; + + default : + Token = Token; + } + } + } + while( Token ); + if ( CrLf ) + { + // + // Retour à la ligne si > 80 caractères + // + EndLigne = strlen( &Listing[ StartLigne ] ); + while( EndLigne > 80 ) + { + memmove( &Listing[ StartLigne + 82 ] + , &Listing[ StartLigne + 80 ] + , EndLigne + ); + memcpy( &Listing[ StartLigne + 80 ], "\r\n", 2 ); + StartLigne += 82; + EndLigne -= 80; + } + } + strcat( Listing, "\r\n" ); + StartLigne = strlen( Listing ); + } + // Conversion des caractères accentués si nécessaire + + for ( int i = strlen( Listing); i--; ) + { + //cout << i << " "; + + if ( ! isprint(Listing[ i ]) && Listing[ i ] != '\n' && Listing[ i ] != '\r' ) Listing[ i ] = '?'; + } +} diff --git a/tools/iDSK/src/Basic.h b/tools/iDSK/src/Basic.h new file mode 100644 index 0000000..4f9cd38 --- /dev/null +++ b/tools/iDSK/src/Basic.h @@ -0,0 +1,8 @@ +#ifndef BASIC_H +#define BASIC_H + + +void Basic( unsigned char * BufFile, char * Listing, bool IsBasic, bool CrLf ); + + +#endif diff --git a/tools/iDSK/src/BitmapCPC.cpp b/tools/iDSK/src/BitmapCPC.cpp new file mode 100644 index 0000000..8eef0d1 --- /dev/null +++ b/tools/iDSK/src/BitmapCPC.cpp @@ -0,0 +1,264 @@ +#include <iostream> +#include <cstdio> +#include "MyType.h" +#include "BitmapCPC.h" +#include "GestDsk.h" +#include <string> +#include <cstring> +using namespace std; + + +// +// Couleurs du CPC converties en composantes r, v, b +// +static StRVB RgbCPC[ 27 ] = + { + { 0x00, 0x00, 0x00, 0x00 }, + { 0x7F, 0x00, 0x00, 0x00 }, + { 0xFF, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x7F, 0x00 }, + { 0x7F, 0x00, 0x7F, 0x00 }, + { 0xFF, 0x00, 0x7F, 0x00 }, + { 0x00, 0x00, 0xFF, 0x00 }, + { 0x7F, 0x00, 0xFF, 0x00 }, + { 0xFF, 0x00, 0xFF, 0x00 }, + { 0x00, 0x7F, 0x00, 0x00 }, + { 0x7F, 0x7F, 0x00, 0x00 }, + { 0xFF, 0x7F, 0x00, 0x00 }, + { 0x00, 0x7F, 0x7F, 0x00 }, + { 0x7F, 0x7F, 0x7F, 0x00 }, + { 0xFF, 0x7F, 0x7F, 0x00 }, + { 0x00, 0x7F, 0xFF, 0x00 }, + { 0x7F, 0x7F, 0xFF, 0x00 }, + { 0xFF, 0x7F, 0xFF, 0x00 }, + { 0x00, 0xFF, 0x00, 0x00 }, + { 0x7F, 0xFF, 0x00, 0x00 }, + { 0xFF, 0xFF, 0x00, 0x00 }, + { 0x00, 0xFF, 0x7F, 0x00 }, + { 0x7F, 0xFF, 0x7F, 0x00 }, + { 0xFF, 0xFF, 0x7F, 0x00 }, + { 0x00, 0xFF, 0xFF, 0x00 }, + { 0x7F, 0xFF, 0xFF, 0x00 }, + { 0xFF, 0xFF, 0xFF, 0x00 } + }; + + +int GetRgbCPC( int Coul ) +{ + if ( Coul >= 0 && Coul < 27 ) + { + StRVB i = RgbCPC[ Coul ]; + return( ( i.b << 16 ) + ( i.v << 8 ) + i.r ); + } + return( -1 ); +} + + +StRVB GetPalCPC( int Coul ) +{ + if ( Coul >= 0 && Coul < 27 ) + return( RgbCPC[ Coul ] ); + + return( RgbCPC[ 0 ] ); +} + + +void InitPalette( unsigned char NewPal[ 16 ], bool SetNewPal ) +{ + /* + Si sauvegard� avec ConvImgCpc, alors la palette se trouve + dans l'image... + */ + int i; + if ( BitmapCPC[ 0x7D0 ] == 0x3A + && BitmapCPC[ 0x7D1 ] == 0xD0 + && BitmapCPC[ 0x7D2 ] == 0xD7 + && BitmapCPC[ 0x7D3 ] == 0xCD + ) + { + Mode = BitmapCPC[ 0x17D0 ]; + for ( i = 0; i < 16; i++ ) + Palette[ i ] = BitmapCPC[ 0x17D1 + i ]; + } + if ( SetNewPal ) + for ( i = 0; i < 16; i++ ) + Palette[ i ] = NewPal[ i ]; +} + + +// +// D�compacter une image au format OCP +// +void DepactOCP( void ) +{ + static unsigned char BufTmp[ 0x4000 ]; + int PosIn = 0, PosOut = 0; + int LgOut, CntBlock = 0; + int c,i; + unsigned char a; + memcpy( BufTmp, BitmapCPC, sizeof( BufTmp ) ); + memset( BitmapCPC, 0, 0x4000 ); + while( PosOut < 0x4000 ) + { + if ( ! strncmp( ( char * )&BufTmp[ PosIn ], "MJH", 3 ) ) + { + PosIn += 3; + LgOut = BufTmp[ PosIn++ ]; + LgOut += ( BufTmp[ PosIn++ ] << 8 ); + CntBlock = 0; + while( CntBlock < LgOut ) + { + if ( ! strncmp( ( char * )&BufTmp[ PosIn ], "MJH", 3 ) ) + break; + + a = BufTmp[ PosIn++ ]; + if ( a == MARKER_OCP ) + { + c = BufTmp[ PosIn++ ]; + a = BufTmp[ PosIn++ ]; + if ( ! c ) + c = 0x100; + + for ( i = 0; i < c && CntBlock < LgOut; i++ ) + { + BitmapCPC[ PosOut++ ] = a; + CntBlock++; + } + } + else + { + BitmapCPC[ PosOut++ ] = a; + CntBlock++; + } + } + } + else + PosOut = 0x4000; + } +} + + +bool LireImage( char * Nom, StRVB * Bitmap ) +{ + static unsigned char Entete[ 0x80 ]; + bool Ret = FALSE; + //DWORD Nb; + FILE* hFile; + + + if ( (hFile=fopen(Nom,"rb"))!=NULL ) + { + fread(Entete,sizeof(Entete),1,hFile); + // ReadFile( hFile, Entete, sizeof( Entete ), &Nb, NULL ); + if ( CheckAmsdos( Entete ) ) + { + fread(BitmapCPC,sizeof( BitmapCPC ),1,hFile); + // ReadFile( hFile, BitmapCPC, sizeof( BitmapCPC ), &Nb, NULL ); + if ( ! strncmp( ( char * )BitmapCPC, "MJH", 3 ) ) + DepactOCP(); + + InitPalette( NULL, FALSE ); + Ret = TRUE; + } + // CloseHandle( hFile ); + fclose(hFile); + if ( Ret ) + Render( Bitmap, 1 ); + } + return( Ret ); + +} + + +// +// Affiche l'image � l'�cran +// +void Render( StRVB * Bitmap, bool Flat ) +{ + int AdrCPC = 0, i, p0, p1, p2, p3; + int y,x,AdrBitmap; + unsigned char Octet; + for ( y = 0; y < NbLignes; y++ ) + { + AdrBitmap = TAILLE_CPC_X * ( y + ( ( 200 - NbLignes ) >> 1 ) ) + + ( ( ( 80 - NbCol ) >> 1 ) << 3 ); + for ( x = 0; x < NbCol; x++ ) + { + Octet = BitmapCPC[ AdrCPC + x ]; + switch( Mode ) + { + case 0 : + case 3 : // Mode 3 = Mode 0 + p0 = ( Octet >> 7 ) + + ( ( Octet & 0x20 ) >> 3 ) + + ( ( Octet & 0x08 ) >> 2 ) + + ( ( Octet & 0x02 ) << 2 ); + p1 = ( ( Octet & 0x40 ) >> 6 ) + + ( ( Octet & 0x10 ) >> 2 ) + + ( ( Octet & 0x04 ) >> 1 ) + + ( ( Octet & 0x01 ) << 3 ); + Bitmap[ AdrBitmap++ ] = GetPalCPC( Palette[ p0 ] ); + Bitmap[ AdrBitmap++ ] = GetPalCPC( Palette[ p0 ] ); + Bitmap[ AdrBitmap++ ] = GetPalCPC( Palette[ p0 ] ); + Bitmap[ AdrBitmap++ ] = GetPalCPC( Palette[ p0 ] ); + Bitmap[ AdrBitmap++ ] = GetPalCPC( Palette[ p1 ] ); + Bitmap[ AdrBitmap++ ] = GetPalCPC( Palette[ p1 ] ); + Bitmap[ AdrBitmap++ ] = GetPalCPC( Palette[ p1 ] ); + Bitmap[ AdrBitmap++ ] = GetPalCPC( Palette[ p1 ] ); + break; + + case 1 : + p0 = ( ( Octet >> 7 ) & 1 ) + ( ( Octet >> 2 ) & 2 ); + p1 = ( ( Octet >> 6 ) & 1 ) + ( ( Octet >> 1 ) & 2 ); + p2 = ( ( Octet >> 5 ) & 1 ) + ( ( Octet >> 0 ) & 2 ); + p3 = ( ( Octet >> 4 ) & 1 ) + ( ( Octet << 1 ) & 2 ); + Bitmap[ AdrBitmap++ ] = GetPalCPC( Palette[ p0 ] ); + Bitmap[ AdrBitmap++ ] = GetPalCPC( Palette[ p0 ] ); + Bitmap[ AdrBitmap++ ] = GetPalCPC( Palette[ p1 ] ); + Bitmap[ AdrBitmap++ ] = GetPalCPC( Palette[ p1 ] ); + Bitmap[ AdrBitmap++ ] = GetPalCPC( Palette[ p2 ] ); + Bitmap[ AdrBitmap++ ] = GetPalCPC( Palette[ p2 ] ); + Bitmap[ AdrBitmap++ ] = GetPalCPC( Palette[ p3 ] ); + Bitmap[ AdrBitmap++ ] = GetPalCPC( Palette[ p3 ] ); + break; + + case 2 : + for ( i = 8; i--; ) + Bitmap[ AdrBitmap++ ] = GetPalCPC( Palette[ ( Octet >> i ) & 1 ] ); + break; + } + } + if ( Flat ) + AdrCPC += NbCol; + else + { + AdrCPC += 0x800; + if ( AdrCPC > 0x3FFF ) + AdrCPC -= 0x3FB0; + } + } +} + + +void SetBitmapCPC( unsigned char * BitmapSource ) +{ + memcpy( BitmapCPC, BitmapSource, 0x4000 ); + if ( ! strncmp( ( char * )BitmapCPC, "MJH", 3 ) ) + DepactOCP(); + + InitPalette( NULL, FALSE ); +} + + +void SetNbCol( int n ) +{ + if ( n > 0 && n <= 80 ) + NbCol = n; +} + + +void SetNbLignes( int n ) +{ + if ( n > 0 && n <= 200 ) + NbLignes = n; +} diff --git a/tools/iDSK/src/BitmapCPC.h b/tools/iDSK/src/BitmapCPC.h new file mode 100644 index 0000000..d18d808 --- /dev/null +++ b/tools/iDSK/src/BitmapCPC.h @@ -0,0 +1,46 @@ +#ifndef BITMAPCPC_H +#define BITMAPCPC_H + + +#define TAILLE_CPC_X 640 + +#define TAILLE_CPC_Y 200 + +#define MARKER_OCP 1 // Marker pour compression RLE +int Mode, NbCol, NbLignes; +unsigned char BitmapCPC[ 0x4000 ]; +unsigned char Palette[ 16 ]; +typedef struct + { + unsigned char b, v, r, a; + } StRVB; + + + +void CBitmapCPC( void ) { NbCol = 80; NbLignes = 200; } +bool LireImage( char * Nom, StRVB * Bitmap ); +void Render( StRVB * Bitmap, bool Flat ); +void SetBitmapCPC( unsigned char * BitmapSource ); +unsigned char * GetBitmapCPC( void ) { return( BitmapCPC ); } +void SetMode( int m ) { Mode = m; } +void InitPalette( unsigned char Pal[ 16 ], bool SetPal ); +unsigned char * GetPalette( void ) { return( Palette ); } +int GetMode( void ) { return( Mode ); } +void SetNbCol( int n ); +void SetNbLignes( int n ); + + +void DepactOCP( void ); +void LisseBitmap( StRVB * Bitmap ); + + + + + + +StRVB GetPalCPC( int Coul ); + +int GetRgbCPC( int Coul ); + + +#endif diff --git a/tools/iDSK/src/Dams.cpp b/tools/iDSK/src/Dams.cpp new file mode 100644 index 0000000..52cad9b --- /dev/null +++ b/tools/iDSK/src/Dams.cpp @@ -0,0 +1,184 @@ +#include <iostream> +#include <cstdio> +#include <cstring> +using namespace std; + + +// +// Convertir le buffer en listing au format Dams +// Adaptation des sources de Thierry JOUIN ( Ramlaid ) +// +void Dams( unsigned char * BufFile, int TailleFic, char * Listing ) +{ + const char * MotCleDams[ 0x80 ] = + { + "LD","INC","DEC","ADD","ADC","SUB","SBC","AND","XOR","OR","CP", + "PUSH","POP","BIT","RES","SET","RLC","RRC","RL","RR","SLA","SRA", + "SRL","IN","OUT","RST","DJNZ","EX","IM","JR","CALL","RET","JP", + "NOP","RLCA","RRCA","RLA","RRA","DAA","CPL","SCF","CCF","HALT", + "EXX","DI","EI","NEG","RETN","RETI","RRD","RLD","LDI","CPI","INI", + "OUTI","LDD","CPD","IND","OUTD","LDIR","CPIR","INIR","OTIR","LDDR", + "CPDR","INDR","OTDR","DB","DW","DM","DS","EQU","ORG","ENT", + "IF","ELSE","END" + }; + char Tmp[ 32 ]; + int PosFile = 0; + int PosDest = 0; + unsigned char c; + + * Listing = 0; + c = BufFile[ PosFile++ ]; + while( c ) + { + if ( c == 0xFF ) + { + // Commentaire ligne + Listing[ PosDest++ ] = ';'; + c = BufFile[ PosFile++ ]; + while( c != 0x0D && PosFile < TailleFic ) + { + Listing[ PosDest++ ] = c; + c = BufFile[ PosFile++ ]; + } + Listing[ PosDest++ ] = '\r'; + Listing[ PosDest++ ] = '\n'; + } + else + { + if ( c >= 0x80 && c != 0x0D ) + { + // Mnemonique sans label + // ENT + if ( c == 0xC9 ) + Listing[ PosDest++ ] = ';'; + + sprintf( Tmp, "\t%s\t", MotCleDams[ c & 0x7F ] ); + int l = strlen( Tmp ); + memcpy( &Listing[ PosDest ], Tmp, l ); + PosDest += l; + // DS ?,? + if ( c == 0xC6 ) + { + c = BufFile[ PosFile++ ]; + // Fin de ligne + while( c != 0x0D && PosFile < TailleFic ) + { + if ( c == ',' ) + { + while( c != 0x0D && c != 0xFF && PosFile < TailleFic ) + c = BufFile[ PosFile++ ]; + } + if ( c != 0x0D ) + { + if ( c == 0xFF ) + Listing[ PosDest++ ] = '\t'; + else + Listing[ PosDest++ ] = c; + + c = BufFile[ PosFile++ ]; + } + } + } + else + { + c = BufFile[ PosFile++ ]; + // Fin de ligne + while( c != 0x0D && PosFile < TailleFic ) + { + if ( c == 0xFF ) + Listing[ PosDest++ ] = '\t'; + else + Listing[ PosDest++ ] = c; + + c = BufFile[ PosFile++ ]; + } + } + Listing[ PosDest++ ] = '\r'; + Listing[ PosDest++ ] = '\n'; + } + else + { + // Label + while( c < 0x80 && c != 0x0D && PosFile < TailleFic ) + { + Listing[ PosDest++ ] = c; + c = BufFile[ PosFile++ ]; + } + if ( c != 0x0D ) + { + // Mnemonique apres label + // ENT + if ( c == 0xC9 ) + Listing[ PosDest++ ] = ';'; + + if ( c != 0xFF ) + { + sprintf( Tmp, "\t%s\t", MotCleDams[ c & 0x7F ] ); + int l = strlen( Tmp ); + memcpy( &Listing[ PosDest ], Tmp, l ); + PosDest += l; + } + else + { + Listing[ PosDest++ ] = '\t'; + Listing[ PosDest++ ] = '\t'; + Listing[ PosDest++ ] = '\t'; + } + // DS ?,? + if ( c == 0xC6 ) + { + c = BufFile[ PosFile++ ]; + // Fin de ligne + while( c != 0x0D && PosFile < TailleFic ) + { + if ( c == ',' ) + { + while( c != 0x0D && c != 0xFF && PosFile < TailleFic ) + c = BufFile[ PosFile++ ]; + } + if ( c != 0x0D ) + { + if ( c == 0xFF ) + { + Listing[ PosDest++ ] = '\t'; + Listing[ PosDest++ ] = ';'; + } + else + Listing[ PosDest++ ] = c; + + c = BufFile[ PosFile++ ]; + } + } + } + else + { + c = BufFile[ PosFile++ ]; + // Fin de ligne + while( c != 0x0D && PosFile < TailleFic ) + { + if ( c == 0xFF ) + { + Listing[ PosDest++ ] = '\t'; + Listing[ PosDest++ ] = ';'; + } + else + Listing[ PosDest++ ] = c; + + c = BufFile[ PosFile++ ]; + } + } + Listing[ PosDest++ ] = '\r'; + Listing[ PosDest++ ] = '\n'; + } + else + { + Listing[ PosDest++ ] = '\r'; + Listing[ PosDest++ ] = '\n'; + } + } + } + c = BufFile[ PosFile++ ]; + if ( PosFile > TailleFic ) + break; + } +} diff --git a/tools/iDSK/src/Dams.h b/tools/iDSK/src/Dams.h new file mode 100644 index 0000000..65dfa9b --- /dev/null +++ b/tools/iDSK/src/Dams.h @@ -0,0 +1,8 @@ +#ifndef DAMS_H +#define DAMS_H + + +void Dams( unsigned char * BufFile, int TailleFic, char * Listing ); + + +#endif diff --git a/tools/iDSK/src/Desass.cpp b/tools/iDSK/src/Desass.cpp new file mode 100644 index 0000000..171c636 --- /dev/null +++ b/tools/iDSK/src/Desass.cpp @@ -0,0 +1,553 @@ +#include <iostream> +#include <cstdio> +#include <cstring> + +#include "Outils.h" +using namespace std; + +// Vecteurs du Firmware CPC +#include "firmware.h" + +// +// Tableau des OP-Codes Z80... +// +const char * const TabInstrCB[ 256 ] = + { + "RLC B","RLC C","RLC D","RLC E", + "RLC H","RLC L","RLC (HL)","RLC A", + "RRC B","RRC C","RRC D","RRC E", + "RRC H","RRC L","RRC (HL)","RRC A", + "RL B","RL C","RL D","RL E", + "RL H","RL L","RL (HL)","RL A", + "RR B","RR C","RR D","RR E", + "RR H","RR L","RR (HL)","RR A", + "SLA B","SLA C","SLA D","SLA E", + "SLA H","SLA L","SLA (HL)","SLA A", + "SRA B","SRA C","SRA D","SRA E", + "SRA H","SRA L","SRA (HL)","SRA A", + "SLL B","SLL C","SLL D","SLL E", + "SLL H","SLL L","SLL (HL)","SLL A", + "SRL B","SRL C","SRL D","SRL E", + "SRL H","SRL L","SRL (HL)","SRL A", + "BIT 0,B","BIT 0,C","BIT 0,D","BIT 0,E", + "BIT 0,H","BIT 0,L","BIT 0,(HL)","BIT 0,A", + "BIT 1,B","BIT 1,C","BIT 1,D","BIT 1,E", + "BIT 1,H","BIT 1,L","BIT 1,(HL)","BIT 1,A", + "BIT 2,B","BIT 2,C","BIT 2,D","BIT 2,E", + "BIT 2,H","BIT 2,L","BIT 2,(HL)","BIT 2,A", + "BIT 3,B","BIT 3,C","BIT 3,D","BIT 3,E", + "BIT 3,H","BIT 3,L","BIT 3,(HL)","BIT 3,A", + "BIT 4,B","BIT 4,C","BIT 4,D","BIT 4,E", + "BIT 4,H","BIT 4,L","BIT 4,(HL)","BIT 4,A", + "BIT 5,B","BIT 5,C","BIT 5,D","BIT 5,E", + "BIT 5,H","BIT 5,L","BIT 5,(HL)","BIT 5,A", + "BIT 6,B","BIT 6,C","BIT 6,D","BIT 6,E", + "BIT 6,H","BIT 6,L","BIT 6,(HL)","BIT 6,A", + "BIT 7,B","BIT 7,C","BIT 7,D","BIT 7,E", + "BIT 7,H","BIT 7,L","BIT 7,(HL)","BIT 7,A", + "RES 0,B","RES 0,C","RES 0,D","RES 0,E", + "RES 0,H","RES 0,L","RES 0,(HL)","RES 0,A", + "RES 1,B","RES 1,C","RES 1,D","RES 1,E", + "RES 1,H","RES 1,L","RES 1,(HL)","RES 1,A", + "RES 2,B","RES 2,C","RES 2,D","RES 2,E", + "RES 2,H","RES 2,L","RES 2,(HL)","RES 2,A", + "RES 3,B","RES 3,C","RES 3,D","RES 3,E", + "RES 3,H","RES 3,L","RES 3,(HL)","RES 3,A", + "RES 4,B","RES 4,C","RES 4,D","RES 4,E", + "RES 4,H","RES 4,L","RES 4,(HL)","RES 4,A", + "RES 5,B","RES 5,C","RES 5,D","RES 5,E", + "RES 5,H","RES 5,L","RES 5,(HL)","RES 5,A", + "RES 6,B","RES 6,C","RES 6,D","RES 6,E", + "RES 6,H","RES 6,L","RES 6,(HL)","RES 6,A", + "RES 7,B","RES 7,C","RES 7,D","RES 7,E", + "RES 7,H","RES 7,L","RES 7,(HL)","RES 7,A", + "SET 0,B","SET 0,C","SET 0,D","SET 0,E", + "SET 0,H","SET 0,L","SET 0,(HL)","SET 0,A", + "SET 1,B","SET 1,C","SET 1,D","SET 1,E", + "SET 1,H","SET 1,L","SET 1,(HL)","SET 1,A", + "SET 2,B","SET 2,C","SET 2,D","SET 2,E", + "SET 2,H","SET 2,L","SET 2,(HL)","SET 2,A", + "SET 3,B","SET 3,C","SET 3,D","SET 3,E", + "SET 3,H","SET 3,L","SET 3,(HL)","SET 3,A", + "SET 4,B","SET 4,C","SET 4,D","SET 4,E", + "SET 4,H","SET 4,L","SET 4,(HL)","SET 4,A", + "SET 5,B","SET 5,C","SET 5,D","SET 5,E", + "SET 5,H","SET 5,L","SET 5,(HL)","SET 5,A", + "SET 6,B","SET 6,C","SET 6,D","SET 6,E", + "SET 6,H","SET 6,L","SET 6,(HL)","SET 6,A", + "SET 7,B","SET 7,C","SET 7,D","SET 7,E", + "SET 7,H","SET 7,L","SET 7,(HL)","SET 7,A" + }; + + +const char * const TabInstrED[ 256 ] = + { + 0,0,0,0, + 0,0,0,0, + 0,0,0,0, + 0,0,0,0, + 0,0,0,0, + 0,0,0,0, + 0,0,0,0, + 0,0,0,0, + 0,0,0,0, + 0,0,0,0, + 0,0,0,0, + 0,0,0,0, + 0,0,0,0, + 0,0,0,0, + 0,0,0,0, + 0,0,0,0, + "IN B,(C)","OUT (C),B","SBC HL,BC","LD (nnnn),BC", + "NEG","RETN","IM 0","LD I,A", + "IN C,(C)","OUT (C),C","ADC HL,BC","LD BC,(nnnn)", + 0,"RETI",0,"LD R,A", + "IN D,(C)","OUT (C),D","SBC HL,DE","LD (nnnn),DE", + 0,0,"IM 1","LD A,I", + "IN E,(C)","OUT (C),E","ADC HL,DE","LD DE,(nnnn)", + 0,0,"IM 2","LD A,R", + "IN H,(C)","OUT (C),H","SBC HL,HL",0, + 0,0,0,"RRD", + "IN L,(C)","OUT (C),L","ADC HL,HL",0, + 0,0,0,"RLD", + 0,"OUT (C),0","SBC HL,SP","LD (nnnn),SP", + 0,0,0,0, + "IN A,(C)","OUT (C),A","ADC HL,SP","LD SP,(nnnn)", + 0,0,0,0, + 0,0,0,0, + 0,0,0,0, + 0,0,0,0, + 0,0,0,0, + 0,0,0,0, + 0,0,0,0, + 0,0,0,0, + 0,0,0,0, + "LDI","CPI","INI","OUTI", + 0,0,0,0, + "LDD","CPD","IND","OUTD", + 0,0,0,0, + "LDIR","CPIR","INIR","OTIR", + 0,0,0,0, + "LDDR","CPDR","INDR","OTDR", + 0,0,0,0, + 0,0,0,0, + 0,0,0,0, + 0,0,0,0, + 0,0,0,0, + 0,0,0,0, + 0,0,0,0, + 0,0,0,0, + 0,0,0,0, + 0,0,0,0, + 0,0,0,0, + 0,0,0,0, + 0,0,0,0, + 0,0,0,0, + 0,0,0,0, + 0,0,0,0, + 0,0,0,0 + }; + + +const char * const TabInstrDD[ 256 ] = + { + 0,0,0,0, + 0,0,0,0, + 0,"ADD IX,BC",0,0, + 0,0,0,0, + 0,0,0,0, + 0,0,0,0, + 0,"ADD IX,DE",0,0, + 0,0,0,0, + 0,"LD IX,nnnn","LD (nnnn),IX","INC IX", + "INC IXh","DEC IXh","LD IXh,nn",0, + 0,"ADD IX,HL","LD IX,(nnnn)","DEC IX", + "INC IXl","DEC IXl","LD IXl,nn",0, + 0,0,0,0, + "INC (IX+nn)","DEC (IX+nn)","LD (IX+nn),nn",0, + 0,"ADD IX,SP",0,0, + 0,0,0,0, + 0,0,0,0, + "LD B,IXh","LD B,IXl","LD B,(IX+nn)",0, + 0,0,0,0, + "LD C,IXh","LD C,IXl","LD C,(IX+nn)",0, + 0,0,0,0, + "LD D,IXh","LD D,IXl","LD D,(IX+nn)",0, + 0,0,0,0, + "LD E,IXh","LD E,IXl","LD E,(IX+nn)",0, + "LD IXh,B","LD IXh,C","LD IXh,D","LD IXh,E", + "LD IXh,IXh","LD IXh,IXl","LD H,(IX+nn)","LD IXh,A", + "LD IXl,B","LD IXl,C","LD IXl,D","LD IXl,E", + "LD IXl,IXh","LD IXl,IXl","LD L,(IX+nn)","LD IXl,A", + "LD (IX+nn),B","LD (IX+nn),C","LD (IX+nn),D","LD (IX+nn),E", + "LD (IX+nn),H","LD (IX+nn),L",0,"LD (IX+nn),A", + 0,0,0,0, + "LD A,IXh","LD A,IXl","LD A,(IX+nn)",0, + 0,0,0,0, + "ADD A,IXh","ADD A,IXl","ADD A,(IX+nn)",0, + 0,0,0,0, + "ADC A,IXh","ADC A,IXl","ADC A,(IX+nn)",0, + 0,0,0,0, + "SUB IXh","SUB IXl","SUB (IX+nn)",0, + 0,0,0,0, + "SBC A,IXh","SBC A,IXl","SBC A,(IX+nn)",0, + 0,0,0,0, + "AND IXh","AND IXl","AND (IX+nn)",0, + 0,0,0,0, + "XOR IXh","XOR IXl","XOR (IX+nn)",0, + 0,0,0,0, + "OR IXh","OR IXl","OR (IX+nn)",0, + 0,0,0,0, + "CP IXh","CP IXl","CP (IX+nn)",0, + 0,0,0,0, + 0,0,0,0, + 0,0,0,0, + 0,0,0,0, + 0,0,0,0, + 0,0,0,0, + 0,0,0,0, + 0,0,0,0, + 0,"POP IX",0,"EX (SP),IX", + 0,"PUSH IX",0,0, + 0,"JP (IX)",0,0, + 0,0,0,0, + 0,0,0,0, + 0,0,0,0, + 0,"LD SP,IX",0,0, + 0,0,0,0 + }; + + +const char * const TabInstrDDCB[ 256 ] = + { + 0,0,0,0,0,0,"RLC (IX+nn)",0, + 0,0,0,0,0,0,"RRC (IX+nn)",0, + 0,0,0,0,0,0,"RL (IX+nn)",0, + 0,0,0,0,0,0,"RR (IX+nn)",0, + 0,0,0,0,0,0,"SLA (IX+nn)",0, + 0,0,0,0,0,0,"SRA (IX+nn)",0, + 0,0,0,0,0,0,"SLL (IX+nn)",0, + 0,0,0,0,0,0,"SRL (IX+nn)",0, + 0,0,0,0,0,0,"BIT 0,(IX+nn)",0, + 0,0,0,0,0,0,"BIT 1,(IX+nn)",0, + 0,0,0,0,0,0,"BIT 2,(IX+nn)",0, + 0,0,0,0,0,0,"BIT 3,(IX+nn)",0, + 0,0,0,0,0,0,"BIT 4,(IX+nn)",0, + 0,0,0,0,0,0,"BIT 5,(IX+nn)",0, + 0,0,0,0,0,0,"BIT 6,(IX+nn)",0, + 0,0,0,0,0,0,"BIT 7,(IX+nn)",0, + 0,0,0,0,0,0,"RES 0,(IX+nn)",0, + 0,0,0,0,0,0,"RES 1,(IX+nn)",0, + 0,0,0,0,0,0,"RES 2,(IX+nn)",0, + 0,0,0,0,0,0,"RES 3,(IX+nn)",0, + 0,0,0,0,0,0,"RES 4,(IX+nn)",0, + 0,0,0,0,0,0,"RES 5,(IX+nn)",0, + 0,0,0,0,0,0,"RES 6,(IX+nn)",0, + 0,0,0,0,0,0,"RES 7,(IX+nn)",0, + 0,0,0,0,0,0,"SET 0,(IX+nn)",0, + 0,0,0,0,0,0,"SET 1,(IX+nn)",0, + 0,0,0,0,0,0,"SET 2,(IX+nn)",0, + 0,0,0,0,0,0,"SET 3,(IX+nn)",0, + 0,0,0,0,0,0,"SET 4,(IX+nn)",0, + 0,0,0,0,0,0,"SET 5,(IX+nn)",0, + 0,0,0,0,0,0,"SET 6,(IX+nn)",0, + 0,0,0,0,0,0,"SET 7,(IX+nn)",0 + }; + + +const char * const TabInstrFD[ 256 ] = + { + 0,0,0,0,0,0,0,0, + 0,"ADD IY,BC",0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,"ADD IY,DE",0,0,0,0,0,0, + 0,"LD IY,nnnn","LD (nnnn),IY","INC IY","INC IYh","DEC IYh","LD IYh,nn",0, + 0,"ADD IY,HL","LD IY,(nnnn)","DEC IY","INC IYl","DEC IYl","LD IYl,nn",0, + 0,0,0,0,"INC (IY+nn)","DEC (IY+nn)","LD (IY+nn),nn",0, + 0,"ADD IY,SP",0,0,0,0,0,0, + 0,0,0,0,"LD B,IYh","LD B,IYl","LD B,(IY+nn)",0, + 0,0,0,0,"LD C,IYh","LD C,IYl","LD C,(IY+nn)",0, + 0,0,0,0,"LD D,IYh","LD D,IYl","LD D,(IY+nn)",0, + 0,0,0,0,"LD E,IYh","LD E,IYl","LD E,(IY+nn)",0, + "LD IYh,B","LD IYh,C","LD IYh,D","LD IYh,E","LD IYh,IYh","LD IYh,IYl","LD H,(IY+nn)","LD IYh,A", + "LD IYl,B","LD IYl,C","LD IYl,D","LD IYl,E","LD IYl,IYh","LD IYl,IYl","LD L,(IY+nn)","LD IYl,A", + "LD (IY+nn),B","LD (IY+nn),C","LD (IY+nn),D","LD (IY+nn),E","LD (IY+nn),H","LD (IY+nn),L",0,"LD (IY+nn),A", + 0,0,0,0,"LD A,IYh","LD A,IYl","LD A,(IY+nn)",0, + 0,0,0,0,"ADD A,IYh","ADD A,IYl","ADD A,(IY+nn)",0, + 0,0,0,0,"ADC A,IYh","ADC A,IYl","ADC A,(IY+nn)",0, + 0,0,0,0,"SUB IYh","SUB IYl","SUB (IY+nn)",0, + 0,0,0,0,"SBC A,IYh","SBC A,IYl","SBC A,(IY+nn)",0, + 0,0,0,0,"AND IYh","AND IYl","AND (IY+nn)",0, + 0,0,0,0,"XOR IYh","XOR IYl","XOR (IY+nn)",0, + 0,0,0,0,"OR IYh","OR IYl","OR (IY+nn)",0, + 0,0,0,0,"CP IYh","CP IYl","CP (IY+nn)",0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,"POP IY",0,"EX (SP),IY",0,"PUSH IY",0,0, + 0,"JP (IY)",0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,"LD SP,IY",0,0,0,0,0,0 + }; + + +const char * const TabInstrFDCB[ 256 ] = + { + 0,0,0,0,0,0,"RLC (IY+nn)",0, + 0,0,0,0,0,0,"RRC (IY+nn)",0, + 0,0,0,0,0,0,"RL (IY+nn)",0, + 0,0,0,0,0,0,"RR (IY+nn)",0, + 0,0,0,0,0,0,"SLA (IY+nn)",0, + 0,0,0,0,0,0,"SRA (IY+nn)",0, + 0,0,0,0,0,0,"SLL (IY+nn)",0, + 0,0,0,0,0,0,"SRL (IY+nn)",0, + 0,0,0,0,0,0,"BIT 0,(IY+nn)",0, + 0,0,0,0,0,0,"BIT 1,(IY+nn)",0, + 0,0,0,0,0,0,"BIT 2,(IY+nn)",0, + 0,0,0,0,0,0,"BIT 3,(IY+nn)",0, + 0,0,0,0,0,0,"BIT 4,(IY+nn)",0, + 0,0,0,0,0,0,"BIT 5,(IY+nn)",0, + 0,0,0,0,0,0,"BIT 6,(IY+nn)",0, + 0,0,0,0,0,0,"BIT 7,(IY+nn)",0, + 0,0,0,0,0,0,"RES 0,(IY+nn)",0, + 0,0,0,0,0,0,"RES 1,(IY+nn)",0, + 0,0,0,0,0,0,"RES 2,(IY+nn)",0, + 0,0,0,0,0,0,"RES 3,(IY+nn)",0, + 0,0,0,0,0,0,"RES 4,(IY+nn)",0, + 0,0,0,0,0,0,"RES 5,(IY+nn)",0, + 0,0,0,0,0,0,"RES 6,(IY+nn)",0, + 0,0,0,0,0,0,"RES 7,(IY+nn)",0, + 0,0,0,0,0,0,"SET 0,(IY+nn)",0, + 0,0,0,0,0,0,"SET 1,(IY+nn)",0, + 0,0,0,0,0,0,"SET 2,(IY+nn)",0, + 0,0,0,0,0,0,"SET 3,(IY+nn)",0, + 0,0,0,0,0,0,"SET 4,(IY+nn)",0, + 0,0,0,0,0,0,"SET 5,(IY+nn)",0, + 0,0,0,0,0,0,"SET 6,(IY+nn)",0, + 0,0,0,0,0,0,"SET 7,(IY+nn)",0 + }; + + +const char * const TabInstr[ 256 ] = + { + "NOP","LD BC,nnnn","LD (BC),A","INC BC", + "INC B","DEC B","LD B,nn","RLCA", + "EX AF,AF","ADD HL,BC","LD A,(BC)","DEC BC", + "INC C","DEC C","LD C,nn","RRCA", + "DJNZ eeee","LD DE,nnnn","LD (DE),A","INC DE", + "INC D","DEC D","LD D,nn","RLA", + "JR eeee","ADD HL,DE","LD A,(DE)","DEC DE", + "INC E","DEC E","LD E,nn","RRA", + "JR NZ,eeee","LD HL,nnnn","LD (nnnn),HL","INC HL", + "INC H","DEC H","LD H,nn","DAA", + "JR Z,eeee","ADD HL,HL","LD HL,(nnnn)","DEC HL", + "INC L","DEC L","LD L,nn","CPL", + "JR NC,eeee","LD SP,nnnn","LD (nnnn),A","INC SP", + "INC (HL)","DEC (HL)","LD (HL),nn","SCF", + "JR C,eeee","ADD HL,SP","LD A,(nnnn)","DEC SP", + "INC A","DEC A","LD A,nn","CCF", + "LD B,B","LD B,C","LD B,D","LD B,E", + "LD B,H","LD B,L","LD B,(HL)","LD B,A", + "LD C,B","LD C,C","LD C,D","LD C,E", + "LD C,H","LD C,L","LD C,(HL)","LD C,A", + "LD D,B","LD D,C","LD D,D","LD D,E", + "LD D,H","LD D,L","LD D,(HL)","LD D,A", + "LD E,B","LD E,C","LD E,D","LD E,E", + "LD E,H","LD E,L","LD E,(HL)","LD E,A", + "LD H,B","LD H,C","LD H,D","LD H,E", + "LD H,H","LD H,L","LD H,(HL)","LD H,A", + "LD L,B","LD L,C","LD L,D","LD L,E", + "LD L,H","LD L,L","LD L,(HL)","LD L,A", + "LD (HL),B","LD (HL),C","LD (HL),D","LD (HL),E", + "LD (HL),H","LD (HL),L","HALT","LD (HL),A", + "LD A,B","LD A,C","LD A,D","LD A,E", + "LD A,H","LD A,L","LD A,(HL)","LD A,A", + "ADD A,B","ADD A,C","ADD A,D","ADD A,E", + "ADD A,H","ADD A,L","ADD A,(HL)","ADD A,A", + "ADC A,B","ADC A,C","ADC A,D","ADC A,E", + "ADC A,H","ADC A,L","ADC A,(HL)","ADC A,A", + "SUB B","SUB C","SUB D","SUB E", + "SUB H","SUB L","SUB (HL)","SUB A", + "SBC A,B","SBC A,C","SBC A,D","SBC A,E", + "SBC A,H","SBC A,L","SBC A,(HL)","SBC A,A", + "AND B","AND C","AND D","AND E", + "AND H","AND L","AND (HL)","AND A", + "XOR B","XOR C","XOR D","XOR E", + "XOR H","XOR L","XOR (HL)","XOR A", + "OR B","OR C","OR D","OR E", + "OR H","OR L","OR (HL)","OR A", + "CP B","CP C","CP D","CP E", + "CP H","CP L","CP (HL)","CP A", + "RET NZ","POP BC","JP NZ,nnnn","JP nnnn", + "CALL NZ,nnnn","PUSH BC","ADD A,nn","RST 00", + "RET Z","RET","JP Z,nnnn",0, + "CALL Z,nnnn","CALL nnnn","ADC A,nn","RST 08", + "RET NC","POP DE","JP NC,nnnn","OUT (nn),A", + "CALL NC,nnnn","PUSH DE","SUB nn","RST 10", + "RET C","EXX","JP C,nnnn","IN A,(nn)", + "CALL C,nnnn",0,"SBC A,nn","RST 18", + "RET PE","POP HL","JP PE,nnnn","EX (SP),HL", + "CALL PE,nnnn","PUSH HL","AND nn","RST 20", + "RET PO","JP (HL)","JP PO,nnnn","EX DE,HL", + "CALL PO,nnnn",0,"XOR nn","RST 28", + "RET P","POP AF","JP P,nnnn","DI", + "CALL P,nnnn","PUSH AF","OR nn","RST 30", + "RET M","LD SP,HL","JP M,nnnn","EI", + "CALL M,nnnn",0,"CP nn","RST 38" + }; + +const char * GetFirmWareVectorName(unsigned short address) +{ + for (int i = 0; FirmWareVectors[i].label; i++) { + if (FirmWareVectors[i].address == address) + return FirmWareVectors[i].label; + } + return NULL; +} + +// +// Convertir le buffer en listing désassemblé +// +void Desass( unsigned char * Prg, char * Listing, int Longueur, int Offset ) +{ + int i, Instr, Inst2 = 0, Inst3 = 0, Inst4 = 0, Ad16; + const char * Chaine; + char *p; + char Inst[ 1024 ]; + + int Adr, OldAdr, PosD = 0; + char Ad8; + + * Listing = 0; + for ( Adr = 0; Adr < Longueur; ) + { + OldAdr = Adr; + Instr = Prg[ Adr++ ]; + Chaine = TabInstr[ Instr ]; + if ( Instr == 0xCB ) + { + Inst2 = Prg[ Adr++ ]; + Chaine = TabInstrCB[ Inst2 ]; + } + else + if ( Instr == 0xDD ) + { + Inst2 = Prg[ Adr++ ]; + if ( Inst2 == 0xCB ) + { + Inst3 = Prg[ Adr++ ]; + Inst4 = Prg[ Adr++ ]; + Chaine = TabInstrDDCB[ Inst4 ]; + strcpy( Inst, Chaine ); + p = strstr( Inst, "nn" ); + if ( p ) + { + if ( Inst3 < 0x80 ) + Hex( p, Inst3, 2 ); + else + { + Hex( p, -Inst3, 2 ); + p[ -1 ] = '-'; + } + } + Chaine = Inst; + } + else + Chaine = TabInstrDD[ Inst2 ]; + } + else + if ( Instr == 0xED ) + { + Inst2 = Prg[ Adr++ ]; + Chaine = TabInstrED[ Inst2 ]; + } + else + if ( Instr == 0xFD ) + { + Inst2 = Prg[ Adr++ ]; + if ( Inst2 == 0xCB ) + { + Ad8 = Prg[ Adr++ ]; + Inst3 = Prg[ Adr++ ]; + Chaine = TabInstrFDCB[ Inst3 ]; + if ( Chaine ) + { + strcpy( Inst, Chaine ); + Chaine = Inst; + p = strstr( Inst, "nn" ); + if ( p ) + Hex( p, Ad8, 2 ); + } + } + else + Chaine = TabInstrFD[ Inst2 ]; + } + if ( Chaine ) + { + strcpy( Inst, Chaine ); + p = strstr( Inst, "nnnn" ); + Ad16 = Prg[ Adr++ ]; + Ad16 += Prg[ Adr ] << 8; + Ad8 = ( char ) Ad16; + if ( p ) + { + Hex( p, Ad16, 4 ); + Adr++; + const char * label = GetFirmWareVectorName( Ad16 ); + if (label && 0 == memcmp( Inst, "CALL", 4)) + { + p[4] = ' '; + p[5] = ' '; + p[6] = ' '; + p[7] = ' '; + p[8] = ';'; + p[9] = ' '; + strcpy(p + 10, label); + } + } + else + { + p = strstr( Inst, "nn" ); + if ( p ) + { + Hex( p, Ad16, 2 ); + p = strstr( Inst, "nn" ); + if ( p ) + Hex( p, Ad16 >> 8, 2 ); + } + else + { + p = strstr( Inst, "eeee" ); + if ( p ) + Hex( p, Adr + Ad8 + Offset, 4 ); + else + Adr--; + } + } + } + else + sprintf( Inst, "%02X %02X %02X ????", Instr, Inst2, Inst3 ); + + Hex( &Listing[ PosD ], OldAdr + Offset, 4 ); // current address + Listing[ PosD + 4 ] = ' '; + PosD += 5; + for ( i = OldAdr; i < Adr; i++ ) + { + Hex( &Listing[ PosD ], Prg[ i ], 2 ); + Listing[ PosD + 2 ] = ' '; + PosD += 3; + } + for ( i = 0; i < 5 - Adr + OldAdr; i++ ) + { + Listing[ PosD ] = Listing[ PosD + 1 ] = Listing[ PosD + 2 ] = ' '; + PosD += 3; + } + const char * p = Inst; + while( * p ) + Listing[ PosD++ ] = * p++; + + Listing[ PosD++ ] = '\r'; + Listing[ PosD++ ] = '\n'; + } +} diff --git a/tools/iDSK/src/Desass.h b/tools/iDSK/src/Desass.h new file mode 100644 index 0000000..4df1b82 --- /dev/null +++ b/tools/iDSK/src/Desass.h @@ -0,0 +1,8 @@ +#ifndef DESASS_H +#define DESASS_H + + +void Desass( unsigned char * Prg, char * Desass, int Longueur, int Offset ); + + +#endif diff --git a/tools/iDSK/src/GestDsk.cpp b/tools/iDSK/src/GestDsk.cpp new file mode 100644 index 0000000..99b69d8 --- /dev/null +++ b/tools/iDSK/src/GestDsk.cpp @@ -0,0 +1,1173 @@ +#include <iostream> +#include <string.h> +#include <cstdlib> +#include <cstdio> +#include <algorithm> +#include <sstream> + +#include "MyType.h" +#include "GestDsk.h" +#include "endianPPC.h" +#include "Outils.h" +#include <cerrno> + +#ifdef _MSC_VER +#define snprintf _snprintf +#endif + +using namespace std; + +char Listing[ 0x280000 ]; +unsigned char BufFile[ 0x10000 ]; +int TailleFic, CurLigne, AdresseCharg, AdresseExec; + + +// +// Verifie si en-tete AMSDOS est valide +// +bool CheckAmsdos( unsigned char * Buf ) { + int i, Checksum = 0; + bool ModeAmsdos = false; + unsigned short CheckSumFile; + CheckSumFile = Buf[ 0x43 ] + Buf[ 0x43 +1 ] *256; + for ( i = 0; i < 67; i++ ) + Checksum += Buf[ i ]; + + if ( ( CheckSumFile == ( unsigned short )Checksum ) && Checksum ) + ModeAmsdos = true; + + return( ModeAmsdos ); +} + + + +// +// Cr�e une en-t�te AMSDOS par d�faut +// +StAmsdos * CreeEnteteAmsdos( char * NomFic, unsigned short Longueur ) { + static char NomReel[ 256 ]; + static StAmsdos Entete; + static char Nom[ 12 ]; + int i; + + strcpy( NomReel, NomFic ); + memset( &Entete, 0, sizeof( Entete ) ); + memset( Nom, ' ', sizeof( Nom ) ); + char * p = NULL; + do { + p = strchr( NomReel, '/' ); //Sous linux c'est le / qu'il faut enlever ... + if ( p ) + strcpy( NomReel, ++p ); + } while( p ); + p = strchr( NomReel, '.' ); + if ( p ) + * p++ = 0; + + int l = strlen( NomReel ); + if ( l > 8 ) + l = 8; + + for ( int i = 0; i < l; i++ ) + Nom[ i ] = ( char )toupper( NomReel[ i ] ); + + if ( p ) + for ( i = 0; i < 3; i++ ) + Nom[ i + 8 ] = ( char )toupper( p[ i ] ); + + memcpy( Entete.FileName, Nom, 11 ); + Entete.Length = 0; //Non renseign� par AMSDos !! + Entete.RealLength = Entete.LogicalLength = Longueur; + Entete.FileType = 2; //Fichier binaire + + SetChecksum(&Entete); + + return( &Entete ); +} + + +// +// Calcule et positionne le checksum AMSDOS +// +void SetChecksum( StAmsdos * pEntete ) { + int i, Checksum = 0; + unsigned char * p = ( unsigned char * )pEntete; + for ( i = 0; i < 67; i++ ) + Checksum += * (p+i); + + pEntete->CheckSum = ( unsigned short )Checksum; +} + + +// +// Effectue un "nettoyage" de l'en-tete Amsdos : +// remet a zero les octets inutilises +// +void ClearAmsdos( unsigned char * Buf ) { + if ( CheckAmsdos( Buf ) ) { + int i, Checksum = 0; + StAmsdos * pEntete = ( StAmsdos * )Buf; + memset( pEntete->Unused, 0, sizeof( pEntete->Unused ) ); + memset( pEntete->Unused2, 0, sizeof( pEntete->Unused2 ) ); + for ( i = 0; i < 67; i++ ) + Checksum += Buf[ i ]; + + Buf[ 0x43 ] = ( unsigned short )Checksum; + } +} + +// +// Recherche le plus petit secteur d'une piste +// +int DSK::GetMinSect( void ) { + int Sect = 0x100; + CPCEMUTrack * tr = ( CPCEMUTrack * )&ImgDsk[ sizeof( CPCEMUEnt ) ]; + for ( int s = 0; s < tr->NbSect; s++ ) + if ( Sect > tr->Sect[ s ].R ) + Sect = tr->Sect[ s ].R; + + return( Sect ); +} + + +// +// Retourne la position d'un secteur dans le fichier DSK +// +int DSK::GetPosData( int track, int sect, bool SectPhysique ) { + // Recherche position secteur + int Pos = sizeof( CPCEMUEnt ); + CPCEMUTrack * tr = ( CPCEMUTrack * )&ImgDsk[ Pos ]; + short SizeByte; + for ( int t = 0; t <= track; t++ ) { + Pos += sizeof( CPCEMUTrack ); + for ( int s = 0; s < tr->NbSect; s++ ) { + if ( t == track ) { + if ( ( ( tr->Sect[ s ].R == sect ) && SectPhysique ) + || ( ( s == sect ) && ! SectPhysique ) + ) + break; + } + SizeByte = tr->Sect[ s ].SizeByte ; + if (SizeByte) + Pos += SizeByte; + else + Pos += ( 128 << tr->Sect[ s ].N ); + } + } + return( Pos ); +} + + +// +// Recherche un bloc libre et le remplit +// +int DSK::RechercheBlocLibre( int MaxBloc ) { + for ( int i = 2; i < MaxBloc; i++ ) + if ( ! Bitmap[ i ] ) { + Bitmap[ i ] = 1; + return( i ); + } + return( 0 ); +} + + +// +// Recherche une entr�e de r�pertoire libre +// +int DSK::RechercheDirLibre( void ) { + for ( int i = 0; i < 64; i++ ) { + StDirEntry * Dir = GetInfoDirEntry( i ); + if ( Dir->User == USER_DELETED ) + return( i ); + } + return( -1 ); +} + + +// +// Retourne les donn�es "brutes" de l'image disquette +// +unsigned char * DSK::GetRawData( int Pos ) { + return( &ImgDsk[ Pos ] ); +} + + +// +// Ecriture de donn�es "brutes" dans l'image disquette +// +void DSK::WriteRawData( int Pos, unsigned char * Data, int Longueur ) { + memcpy( &ImgDsk[ Pos ], Data, Longueur ); +} + + +// +// Retourne la taille du fichier image +// +int DSK::GetTailleDsk( void ) { + CPCEMUEnt * Infos = ( CPCEMUEnt * )ImgDsk; + int NbTracks = Infos->NbTracks; + int Pos = sizeof( CPCEMUEnt ); + CPCEMUTrack * tr = ( CPCEMUTrack * )&ImgDsk[ Pos ]; + for ( int t = 0; t < NbTracks; t++ ) { + Pos += sizeof( CPCEMUTrack ); + for ( int s = 0; s < tr->NbSect; s++ ) { + if ( tr->Sect[ s ].SizeByte ) + Pos += tr->Sect[ s ].SizeByte; + else + Pos += ( 128 << tr->Sect[ s ].N ); + } + } + return( Pos ); +} + + +// +// Retourne le nombre de pistes de la disquette +// +int DSK::GetNbTracks( void ) { + CPCEMUEnt * Infos = ( CPCEMUEnt * )ImgDsk; + return( Infos->NbTracks ); +} + + +// +// Lecture d'un bloc AMSDOS (1 block = 2 secteurs) +// +unsigned char * DSK::ReadBloc( int bloc ) { + static unsigned char BufBloc[ SECTSIZE * 2 ]; + int track = ( bloc << 1 ) / 9; + int sect = ( bloc << 1 ) % 9; + int MinSect = GetMinSect(); + if ( MinSect == 0x41 ) + track += 2; + else + if ( MinSect == 0x01 ) + track++; + + int Pos = GetPosData( track, sect + MinSect, true ); + memcpy( BufBloc, &ImgDsk[ Pos ], SECTSIZE ); + if ( ++sect > 8 ) { + track++; + sect = 0; + } + + Pos = GetPosData( track, sect + MinSect, true ); + memcpy( &BufBloc[ SECTSIZE ], &ImgDsk[ Pos ], SECTSIZE ); + return( BufBloc ); +} + + +// +// Formatter une piste +// +void DSK::FormatTrack( CPCEMUEnt * Infos, int t, int MinSect, int NbSect ) { + CPCEMUTrack * tr = ( CPCEMUTrack * )&ImgDsk[ sizeof( CPCEMUEnt ) + t * Infos->DataSize ]; + memset( &ImgDsk[ sizeof( CPCEMUEnt ) + + sizeof( CPCEMUTrack ) + + ( t * Infos->DataSize ) + ] + , 0xE5 + , 0x200 * NbSect + ); + strcpy( tr->ID, "Track-Info\r\n" ); + tr->Track = ( unsigned char )t; + tr->Head = 0; + tr->SectSize = 2; + tr->NbSect = ( unsigned char )NbSect; + tr->Gap3 = 0x4E; + tr->OctRemp = 0xE5; + int ss = 0; + // + // Gestion "entrelacement" des secteurs + // + for ( int s = 0; s < NbSect; ) { + tr->Sect[ s ].C = ( unsigned char )t; + tr->Sect[ s ].H = 0; + tr->Sect[ s ].R = ( unsigned char )( ss + MinSect ); + tr->Sect[ s ].N = 2; + tr->Sect[ s ].SizeByte = 0x200; + ss++; + if ( ++s < NbSect ) { + tr->Sect[ s ].C = ( unsigned char )t; + tr->Sect[ s ].H = 0; + tr->Sect[ s ].R = ( unsigned char )( ss + MinSect + 4 ); + tr->Sect[ s ].N = 2; + tr->Sect[ s ].SizeByte = 0x200; + s++; + } + } +} + + +// +// Ecriture d'un bloc AMSDOS (1 block = 2 secteurs) +// +void DSK::WriteBloc( int bloc, unsigned char BufBloc[ SECTSIZE * 2 ] ) { + int track = ( bloc << 1 ) / 9; + int sect = ( bloc << 1 ) % 9; + int MinSect = GetMinSect(); + if ( MinSect == 0x41 ) + track += 2; + else + if ( MinSect == 0x01 ) + track++; + + // + // Ajuste le nombre de pistes si d�passement capacit� + // + CPCEMUEnt * Entete = ( CPCEMUEnt * )ImgDsk; + if ( track > Entete->NbTracks - 1 ) { + Entete->NbTracks = ( unsigned char )( track + 1 ); + FormatTrack( Entete, track, MinSect, 9 ); + } + + int Pos = GetPosData( track, sect + MinSect, true ); + memcpy( &ImgDsk[ Pos ], BufBloc, SECTSIZE ); + if ( ++sect > 8 ) { + track++; + sect = 0; + } + Pos = GetPosData( track, sect + MinSect, true ); + memcpy( &ImgDsk[ Pos ], &BufBloc[ SECTSIZE ], SECTSIZE ); +} + + +// +// Ecriture d'un secteur +// +void DSK::WriteSect( int Track, int Sect, unsigned char * Buff, int AmsdosMode ) { + int MinSect = AmsdosMode ? GetMinSect() : 0; + if ( ( MinSect == 0x41 ) && AmsdosMode ) + Track += 2; + else + if ( ( MinSect == 0x01 ) && AmsdosMode ) + Track++; + + int Pos = GetPosData( Track, Sect + MinSect, AmsdosMode ); + memcpy( &ImgDsk[ Pos ], Buff, SECTSIZE ); +} + + +// +// Lecture d'un secteur +// +unsigned char * DSK::ReadSect( int Track, int Sect, int AmsdosMode ) { + int MinSect = AmsdosMode ? GetMinSect() : 0; + if ( ( MinSect == 0x41 ) && AmsdosMode ) + Track += 2; + else + if ( ( MinSect == 0x01 ) && AmsdosMode ) + Track++; + + int Pos = GetPosData( Track, Sect + MinSect, AmsdosMode ); + return( &ImgDsk[ Pos ] ); +} + + +// +// Retourne les informations d'une piste +// +CPCEMUTrack * DSK::GetInfoTrack( int Track ) { + int Pos = sizeof( CPCEMUEnt ); + CPCEMUTrack * tr = ( CPCEMUTrack * )&ImgDsk[ Pos ]; + for ( int t = 0; t < Track; t++ ) { + Pos += sizeof( CPCEMUTrack ); + + for ( int s = 0; s < tr->NbSect; s++ ) { + if ( tr->Sect[ s ].SizeByte ) + Pos += tr->Sect[ s ].SizeByte; + else + Pos += ( 128 << tr->Sect[ s ].N ); + } + } + return( ( CPCEMUTrack * )&ImgDsk[ Pos ] ); +} + +// +// Remplit un "bitmap" pour savoir o� il y a des fichiers sur la disquette +// Retourne �galement le nombre de Ko utilis�s sur la disquette +// +int DSK::FillBitmap( void ) { + int NbKo = 0; + + memset( Bitmap, 0, sizeof( Bitmap ) ); + Bitmap[ 0 ] = Bitmap[ 1 ] = 1; + for ( int i = 0; i < 64; i++ ) { + StDirEntry * Dir = GetInfoDirEntry( i ); + if ( Dir->User != USER_DELETED ) { + for ( int j = 0; j < 16; j++ ) { + int b = Dir->Blocks[ j ]; + if ( b > 1 && ( ! Bitmap[ b ] ) ) { + Bitmap[ b ] = 1; + NbKo++; + } + } + } + } + return( NbKo ); +} + + +// +// Positionne une entr�e dans le r�pertoire +// +void DSK::SetInfoDirEntry( int NumDir, StDirEntry * Dir ) { + int MinSect = GetMinSect(); + int s = ( NumDir >> 4 ) + MinSect; + int t = ( MinSect == 0x41 ? 2 : 0 ); + if ( MinSect == 1 ) + t = 1; + + for (int i =0; i<16; i++) + memcpy( &ImgDsk[ ( ( NumDir & 15 ) << 5 ) + GetPosData( t, s, true ) ] + , Dir + , sizeof( StDirEntry ) + ); +} + + +// +// V�rifie l'existente d'un fichier, retourne l'indice du fichier si existe, +// -1 sinon +// +int DSK::FileExist( char * Nom ) { + int i; + for ( i = 0; i < 64; i++ ) { + StDirEntry * Dir = GetInfoDirEntry( i ); + for(int q=0;q<12;q++) + Dir->Nom[q]=Dir->Nom[q]&127; // Avoid missing hidden files + if ( Dir->User != USER_DELETED + && ! strncmp( Nom, ( char * )Dir->Nom, 11 ) // 11 = 8+3 car le point est enlev� + ) + return( i ); + } + return( -1 ); +} + + +StDirEntry * DSK::GetNomDir( string NomFic ) { + static StDirEntry DirLoc; + int i; + + memset( &DirLoc, 0, sizeof( DirLoc ) ); + memset( DirLoc.Nom, ' ', 8 ); + memset( DirLoc.Ext, ' ', 3 ); + size_t p = NomFic.find('.'); + if ( p!=std::string::npos ) + { + NomFic.copy( DirLoc.Nom, std::min((int)p,8), 0); + p++; + NomFic.copy( DirLoc.Ext, std::min( (int)(NomFic.size()-p), 3 ), p ); + } + else + NomFic.copy( DirLoc.Nom, std::min((int)NomFic.size(), 8 ),0); + + for ( i = 0; i < 11; i++ ) + DirLoc.Nom[ i ] = ( unsigned char )toupper( DirLoc.Nom[ i ] ); + + return( &DirLoc ); +} + + +int DSK::FileIsIn( string FileName ) { + StDirEntry * DirLoc = GetNomDir( FileName ); + return FileExist( ( char*) DirLoc->Nom ); +} + +// +// Copie un fichier sur le DSK +// +// la taille est determine par le nombre de NbPages +// regarder pourquoi different d'une autre DSK +int DSK::CopieFichier( unsigned char * BufFile, char * NomFic, int TailleFic, int MaxBloc, int UserNumber, bool System_file, bool Read_only ) { + int j, l, Bloc, PosFile, NbPages = 0, PosDir, TaillePage; + FillBitmap(); + StDirEntry * DirLoc = GetNomDir( NomFic ); //Construit l'entr�e pour mettre dans le catalogue + for ( PosFile = 0; PosFile < TailleFic; ) { //Pour chaque bloc du fichier + PosDir = RechercheDirLibre(); //Trouve une entr�e libre dans le CAT + if ( PosDir != -1 ) { + DirLoc->User = UserNumber; //Remplit l'entr�e : User 0 + // http://www.cpm8680.com/cpmtools/cpm.htm + if(System_file) DirLoc->Ext[1]|=0x80; + if(Read_only) DirLoc->Ext[0]|=0x80; + DirLoc->NumPage = ( unsigned char )NbPages++; // Num�ro de l'entr�e dans le fichier + TaillePage = (TailleFic - PosFile + 127) >> 7 ; // Taille de la page (on arrondit par le haut) + if ( TaillePage > 128 ) // Si y'a plus de 16k il faut plusieurs pages + TaillePage = 128; + + DirLoc->NbPages = ( unsigned char )TaillePage; + l = ( DirLoc->NbPages + 7 ) >> 3; //Nombre de blocs=TaillePage/8 arrondi par le haut + memset( DirLoc->Blocks, 0, 16 ); + for ( j = 0; j < l; j++ ) { //Pour chaque bloc de la page + Bloc = RechercheBlocLibre( MaxBloc ); //Met le fichier sur la disquette + if ( Bloc ) { + DirLoc->Blocks[ j ] = ( unsigned char )Bloc; + WriteBloc( Bloc, &BufFile[ PosFile ] ); + PosFile += 1024; // Passe au bloc suivant + } + else + return( ERR_NO_BLOCK ); + + } + SetInfoDirEntry( PosDir, DirLoc ); + } + else + return( ERR_NO_DIRENTRY ); + } + return( ERR_NO_ERR ); +} + + +// +// Retourne une entr�e du r�pertoire +// +StDirEntry * DSK::GetInfoDirEntry( int NumDir ) { + static StDirEntry Dir; + int MinSect = GetMinSect(); + int s = ( NumDir >> 4 ) + MinSect; + int t = ( MinSect == 0x41 ? 2 : 0 ); + if ( MinSect == 1 ) + t = 1; + + memcpy( &Dir + , &ImgDsk[ ( ( NumDir & 15 ) << 5 ) + GetPosData( t, s, true ) ] + , sizeof( StDirEntry ) + ); + return( &Dir ); +} + + +// +// V�rifier si DSK est "standard" (DATA ou VENDOR) +// +bool DSK::CheckDsk( void ) { + CPCEMUEnt * Infos = ( CPCEMUEnt * )ImgDsk; + if ( Infos->NbHeads == 1 ) { + int MinSectFirst = GetMinSect(); + if ( MinSectFirst != 0x41 && MinSectFirst != 0xC1 && MinSectFirst != 0x01 ) + { + cout << "DSK has wrong sector number!" << endl; + return( false ); + } + + + if ( Infos->NbTracks > 42 ) + Infos->NbTracks = 42; + + for ( int track = 0; track < Infos->NbTracks; track++ ) { + // Recherche position secteur + int Pos = sizeof( CPCEMUEnt ) + ( 0x1200 + sizeof( CPCEMUTrack ) ) * track; + CPCEMUTrack * tr = ( CPCEMUTrack * )&ImgDsk[ Pos ]; + + int MinSect = 0xFF, MaxSect = 0; + if ( tr->NbSect != 9 ) + { + cout << "Warning : track " << track <<" has "<<(int)tr->NbSect<<" sectors ! (wanted 9)" << endl; + // return( false ); + } + for ( int s = 0; s < (int)tr->NbSect; s++ ) { + if ( MinSect > tr->Sect[ s ].R ) + MinSect = tr->Sect[ s ].R; + + if ( MaxSect < tr->Sect[ s ].R ) + MaxSect = tr->Sect[ s ].R; + } + if ( MaxSect - MinSect != 8 ) + { + cout << "Warning : strange sector numbering in track "<<track<<"!" << endl; + // return( false ); + } + if ( MinSect != MinSectFirst ) + { + cout << "Warning : track "<<track<<" start at sector"<<MinSect<<" while track 0 starts at "<<MinSectFirst << endl; + //return( false ); + } + } + return( true ); + } + cout << "Multi-side dsk ! Expected 1 head, got " << (int)Infos->NbHeads << endl; + return( false ); +} + + +// +// Lire un fichier DSK +// +bool DSK::ReadDsk( std::string NomFic ) { + bool Ret = false; + CPCEMUEnt * Infos; + if(sizeof(CPCEMUEnt) != 0x100) cout << "INVALID DSK BUILD" << endl; + FILE* fp ; + + if ( (fp=fopen(NomFic.c_str(),"rb"))!=NULL ) { + fread(ImgDsk,sizeof(ImgDsk),1,fp); + Infos = ( CPCEMUEnt * )ImgDsk; + if ( isBigEndian( ) ) FixEndianDsk( false ); // fix endian for Big endianness machines (PPC) + if ( ! strncmp( Infos->debut, "MV -", 4 ) + || ! strncmp( Infos->debut, "EXTENDED CPC DSK", 16 ) + ) + Ret = true; + fclose(fp); + } + return( Ret ); +} + + +// +// Formatter une disquette +// +void DSK::FormatDsk( int NbSect, int NbTrack ) { + CPCEMUEnt * Infos = ( CPCEMUEnt * )ImgDsk; + + strcpy( Infos->debut, "MV - CPCEMU Disk-File\r\nDisk-Info\r\n" ); + Infos->DataSize = ( short )( sizeof( CPCEMUTrack ) + (0x200 * NbSect) ); + Infos->NbTracks = ( unsigned char ) NbTrack; + Infos->NbHeads = 1; + for ( int t = 0; t < NbTrack; t++ ) + FormatTrack( Infos, t, 0xC1, NbSect ); + + + FillBitmap(); +} + + + +// +// Modifie le endianness de la disquette +// +void DSK::FixEndianDsk( bool littleToBig) { + CPCEMUEnt * Infos = ( CPCEMUEnt * )ImgDsk; + //std::cerr<< "FixEndianDsk() Infos->DataSize : " << Infos->DataSize <<std::endl; + + if ( ! littleToBig ) + Infos->DataSize = FIX_SHORT( Infos->DataSize ); + for ( int t = 0; t < Infos->NbTracks; t++ ) + FixEndianTrack( Infos, t, 9 ); + if ( littleToBig ) + Infos->DataSize = FIX_SHORT( Infos->DataSize ); + FillBitmap(); +} + +// +// Modifie le endianness de la piste +// +void DSK::FixEndianTrack( CPCEMUEnt * Infos, int t, int NbSect ) { + CPCEMUTrack *tr; + if ( Infos->DataSize != 0 ) + tr = ( CPCEMUTrack * )&ImgDsk[ sizeof( CPCEMUEnt ) + t * Infos->DataSize ]; + else { + int ExtendedDataSize = ImgDsk[ 0x34 + t ] *256; //case of a extended dsk image + tr = ( CPCEMUTrack * )&ImgDsk[ sizeof( CPCEMUEnt ) + t * ExtendedDataSize ]; + } + int ss = 0; + + // + // Gestion "entrelacement" des secteurs + // + for ( int s = 0; s < NbSect; ) { + tr->Sect[ s ].SizeByte = FIX_SHORT( tr->Sect[ s ].SizeByte ); + tr->Sect[ s ].Un1 = FIX_SHORT( tr->Sect[ s ].Un1 ); + ss++; + if ( ++s < NbSect ) { + tr->Sect[ s ].SizeByte = FIX_SHORT( tr->Sect[ s ].SizeByte ); + tr->Sect[ s ].Un1 = FIX_SHORT( tr->Sect[ s ].Un1 ); + s++; + } + } + tr->Unused = FIX_SHORT( tr->Unused ); +} + + +// +// Ecriture du fichier DSK +// +bool DSK::WriteDsk( string NomDsk ) { + CPCEMUEnt * Infos = ( CPCEMUEnt * )ImgDsk; + FILE* fp; + int Taille,Copie; + + + if ( (fp=fopen(NomDsk.c_str(),"wb+")) != NULL) { + if ( ! Infos->DataSize ) Infos->DataSize = 0x100 + SECTSIZE * 9; + Taille = Infos->NbTracks * Infos->DataSize + sizeof( * Infos ); + if ( isBigEndian() ) FixEndianDsk( true ) ; // Fix endianness for Big endian machines (PPC) + + if ( (Copie=(fwrite(ImgDsk,1,Taille,fp))) !=Taille ) + cerr << Copie << "!=" << Taille; + fclose(fp); + // in case of the same DSK image stay in memory + if ( isBigEndian() ) FixEndianDsk( false ) ; // unFix endianness for Big endian machines (PPC) + + return( true ); + } + return( false ); +} + + +void DSK::DskEndian() { + CPCEMUEnt * Infos = ( CPCEMUEnt * )ImgDsk; + for ( int i=1 ; i<(int)Infos->NbTracks ; i++) { + CPCEMUTrack * TrackData = GetInfoTrack( i ); + TrackData = CPCEMUTrackEndian ( TrackData ) ; + } + Infos = CPCEMUEntEndian ( Infos ) ; +} + + +StAmsdos* DSK::StAmsdosEndian ( StAmsdos * pEntete ){ + pEntete->Length = FIX_SHORT( pEntete->Length ); + pEntete->Adress = FIX_SHORT( pEntete->Adress ); + pEntete->LogicalLength = FIX_SHORT( pEntete->LogicalLength); + pEntete->EntryAdress = FIX_SHORT( pEntete->EntryAdress ); + pEntete->RealLength = FIX_SHORT( pEntete->RealLength ); + pEntete->CheckSum = FIX_SHORT( pEntete->CheckSum ) ; + return ( pEntete ); +} + + +CPCEMUEnt* DSK::CPCEMUEntEndian ( CPCEMUEnt* Infos ) { + Infos->DataSize = FIX_SHORT( Infos->DataSize ); + return (Infos); +} + + +CPCEMUTrack* DSK::CPCEMUTrackEndian ( CPCEMUTrack* tr ) { + for ( int i=0;i < (int)tr->NbSect ; i++) { + tr->Sect[i] = CPCEMUSectEndian( tr->Sect[i] ); + } + + return ( tr); + +} + + +CPCEMUSect DSK::CPCEMUSectEndian ( CPCEMUSect Sect) { + Sect.Un1 = FIX_SHORT( Sect.Un1 ); + Sect.SizeByte = FIX_SHORT( Sect.SizeByte ); + return (Sect); +} + +// Retourne le type de fichier sous forme de chaine +// +const char * DSK::GetType( int Langue, StAmsdos * Ams ) { + if ( CheckAmsdos( ( unsigned char * )Ams ) ) { + switch( Ams->FileType ) { + case 0 : // BASIC + return( "BASIC"); //GetTexteLoc( 22, Langue ) ); + + case 1 : // BASIC (P) + return( "BASIC(P)"); // GetTexteLoc( 23, Langue ) ); + + case 2 : // BINAIRE + return("BINAIRE"); // GetTexteLoc( 24, Langue ) ); + + case 3 : // BINAIRE (P) + return( "BINAIRE(P)"); //GetTexteLoc( 25, Langue ) ); + + default : + return( "INCONNU"); // GetTexteLoc( 26, Langue ) ); + } + } + return("ASCII"); // GetTexteLoc( 27, Langue ) ); +} + +char * DSK::GetEntryNameInCatalogue ( int num , char* Nom ) { + int PosItem[ 64 ]; + StDirEntry TabDir[ 64 ]; + + memset( PosItem, 0, sizeof( PosItem ) ); + + for ( int i = 0; i < 64; i++ ) + memcpy( &TabDir[ i ], GetInfoDirEntry( i ), sizeof( StDirEntry )); + + for ( int i = 0; i < 64; i++ ) { + SetInfoDirEntry( i, &TabDir[ i ] ); + + if ( TabDir[ i ].User != USER_DELETED && ! TabDir[ i ].NumPage && num == i) { + memcpy( Nom, TabDir[ i ].Nom, 8 ); + memcpy( &Nom[ 9 ], TabDir[ i ].Ext, 3 ); + Nom[ 8 ] = '.'; + Nom[ 12 ] = 0; + for ( int j = 0; j < 12; j++ ) + Nom[ j ] &= 0x7F; + for ( int j = 0; j < 12; j++ ) + if ( ! isprint( Nom[ j ] ) ) + Nom[ j ] = '?' ; + return Nom; + } + } + return Nom; +} + +char * DSK::GetEntrySizeInCatalogue ( int num , char* Size ) { + int PosItem[ 64 ]; + StDirEntry TabDir[ 64 ]; + + + memset( PosItem, 0, sizeof( PosItem ) ); + + for ( int i = 0; i < 64; i++ ) + memcpy( &TabDir[ i ], GetInfoDirEntry( i ), sizeof( StDirEntry )); + + for ( int i = 0; i < 64; i++ ) { + SetInfoDirEntry( i, &TabDir[ i ] ); + + if ( TabDir[ i ].User != USER_DELETED && ! TabDir[ i ].NumPage && num == i) { + int p = 0, t = 0; + do { + if ( TabDir[ p + i ].User == TabDir[ i ].User ) { + t += TabDir[ p + i ].NbPages; + } + p++; + } + while( TabDir[ p + i ].NumPage && ( p + i ) < 64 ); + sprintf( Size, "%d Ko", ( t + 7 ) >>3 ); + return Size; + } + } + return Size; +} + + +bool DSK::GetFileInDsk( char* path, int Indice ){ + int i = Indice; + char current[ 16 ]; + char NomIndice[ 16 ]; + int lMax = 0x1000000; + int cumul=0; + FILE* f; + StDirEntry TabDir[ 64 ]; + + if ( (f=fopen(path,"wb"))==NULL ) + return false; + + for ( int i = 0; i < 64; i++ ) + memcpy( &TabDir[ i ], GetInfoDirEntry( i ), sizeof( StDirEntry )); + + + memset( NomIndice, 0 , sizeof( NomIndice ) ); + strncpy( NomIndice, GetNomAmsdos( TabDir[ i ].Nom ), 16); + strncat( NomIndice, GetNomAmsdos( TabDir[ i ].Ext), 3); + + do + { + // Longueur du fichier + int l = ( TabDir[ i ].NbPages + 7 ) >> 3; + for ( int j = 0; j < l; j++ ) { + int TailleBloc = 1024; + unsigned char * p = ReadBloc( TabDir[ i ].Blocks[ j ] ); + int NbOctets = min( lMax, TailleBloc ); + if ( NbOctets > 0 ) { + fwrite(p,1,NbOctets,f); + cumul+=NbOctets; + } + lMax -= 1024; + } + memset( current , 0, sizeof( current ) ); + i++; + strncpy(current, GetNomAmsdos( TabDir[ i ].Nom ), 16 ); + strncat(current, GetNomAmsdos( TabDir[ i ].Ext ), 3); + + if ( i > 64 ) return false; + }while (! strncmp( NomIndice, current , max( strlen( NomIndice ), strlen( current ) ))); + + fclose (f); + return true; +} + + +bool DSK::PutFileInDsk(string Masque, int TypeModeImport, + int loadAddress, int exeAddress, int UserNumber, + bool System_file, bool Read_only ) +{ + static unsigned char Buff[0x20000]; + static char *cFileName; + unsigned long Lg; + bool ret; + FILE* Hfile; + + // Get AMSDOS-formatted name + cFileName = GetNomAmsdos(Masque.c_str()); + + // Open and read the input file + if ((Hfile = fopen(Masque.c_str(),"rb")) == NULL) + return false; + Lg=fread(Buff,1, 0x20000 ,Hfile); + fclose( Hfile ); + + // Check if file already has an header + bool IsAmsdos = (TypeModeImport != MODE_RAW) && CheckAmsdos(Buff); + + // Force binary mode if a load or execution address is specified + if (loadAddress != 0 || exeAddress != 0) + TypeModeImport = MODE_BINAIRE; + + switch(TypeModeImport) { + case MODE_ASCII: + // In ASCII mode, delete the header if there is one + if (IsAmsdos) { + memmove(Buff, Buff + sizeof(StAmsdos), Lg - sizeof(StAmsdos)); + Lg -= sizeof(StAmsdos); + } + break; + + case MODE_BINAIRE : + // In binary mode, add an header if there is none + if ( ! IsAmsdos ) { + // Sanity check on file size (we cast to unsigned short) + if ( Lg >= 0x10000 ) { + cerr << "Creating an header for files larger than 64K is not supported yet\n"; + return false; + } + + // Create and fill AMSDOS header + cout << "Automatically generating header for file\n"; + StAmsdos * e; + e = CreeEnteteAmsdos(cFileName, (unsigned short)Lg); + if (loadAddress != 0) + { + e->Adress = (unsigned short)loadAddress; + } + if (exeAddress != 0) + { + e->EntryAdress = (unsigned short)exeAddress; + } + // After changing addresses, recompute header checksum + SetChecksum(e); + // Fix endianness if run on big-endian machine + if (isBigEndian()) e = StAmsdosEndian(e); + + // Insert the header before the file + memmove(&Buff[sizeof(StAmsdos)], Buff, Lg); + memcpy(Buff, e, sizeof(StAmsdos)); + Lg += sizeof(StAmsdos); + } + else + cout << "File already has an header\n"; + break; + case MODE_RAW : + cout << "Using raw mode, no header\n"; + + } + + // Copy the file inside the DSK image. + if (CopieFichier(Buff, cFileName, Lg, 256, UserNumber, System_file, Read_only) != ERR_NO_ERR) + ret = false; + else + ret = true; + + return ret; +} + + +bool DSK::OnViewFic(int nItem) { + int LongFic = 0; + memset( BufFile, 0, sizeof( BufFile ) ); + memset( Listing, 0, sizeof( Listing ) ); + char NomFic[ 16 ]; + char current[ 16 ]; + int i = nItem; + bool FirstBlock = true; + StDirEntry TabDir[ 64 ]; + + for ( int j = 0; j < 64; j++ ) + memcpy( &TabDir[ j ], GetInfoDirEntry( j ), sizeof( StDirEntry )); + + memset( NomFic, 0 , sizeof( NomFic ) ); + strncpy( NomFic, GetNomAmsdos( TabDir[ i ].Nom ), 16); + strncat( NomFic, GetNomAmsdos( TabDir[ i ].Ext), 3); + + int lMax = sizeof( BufFile ); + + TailleFic = 0; + + + do + { + // Longueur du fichier + int l = ( TabDir[ i ].NbPages + 7 ) >> 3; + for ( int j = 0; j < l; j++ ) { + int TailleBloc = 1024; + unsigned char * p = ReadBloc( TabDir[ i ].Blocks[ j ] ); + if ( FirstBlock ) { + if ( CheckAmsdos( p ) ) { + TailleFic = p[ 0x18 +1 ] *256 + p[ 0x18 ]; + AdresseCharg = p[ 0x15 +1 ] *256 + p[ 0x15 ]; + AdresseExec = p[ 0x1a +1 ] *256 + p[ 0x1a ]; + TailleBloc -= sizeof( StAmsdos ); + memcpy( p , &p[ 0x80 ] , TailleBloc ); + } + FirstBlock = false; + + } + int NbOctets = min( lMax, TailleBloc ); + if ( NbOctets > 0 ) { + memcpy( &BufFile[ LongFic ], p, NbOctets ); + LongFic += NbOctets; + } + lMax -= 1024; + } + memset( current , 0, sizeof( current ) ); + i++; + strncpy(current, GetNomAmsdos( TabDir[ i ].Nom ), 16 ); + strncat(current, GetNomAmsdos( TabDir[ i ].Ext ), 3); + if ( i > 64 ) return false; + }while( ! strncmp( NomFic, current, max( strlen( current ), strlen( NomFic ) ) ) ); + + if ( TailleFic == 0 ) + TailleFic = LongFic; + return true; +} + + +bool DSK::Hexdecimal() { + + int TailleCourante=0; + char OffSet[ 7 ]; + const char * CodeHexa = "0123456789ABCDEF"; + + while (TailleCourante <= TailleFic ) { + // display the offset + memset( OffSet, 0 , 7 ); + snprintf( OffSet,6,"#%.4X:", TailleCourante ); + strcat( Listing, OffSet ); + strcat( Listing, " "); + char Ascii[ 18 ]; + char Hex[ 16 *3 +1 ]; + memset( Ascii, 0 , 18 ); + memset( Hex , 0 , ( 16*3 +1) ); + for ( int i=0; i<16 ; ++i ) { + unsigned char cur = BufFile[ TailleCourante + i ]; + // manage the ascii display + if ( cur > 32 && cur < 125 ) + Ascii[ i ] = cur; + else + Ascii[ i ] = '.'; + char Val[ 4 ]; + // manage the hexadeciaml display + Val[ 0 ] = CodeHexa[ cur >> 4 ]; + Val[ 1 ] = CodeHexa[ cur & 0x0F ]; + Val[ 2 ] = ' '; + Val[ 3 ] ='\0'; + strcat( Hex, Val ); + } + Ascii[ 16 ] = '\n'; + strcat( Listing, Hex ); + strcat( Listing, "| "); + strcat( Listing, Ascii ); + TailleCourante += 16; + } + + return true; +} + + +void DSK::RemoveFile ( int item ) { + char NomFic[ 16 ]; + int i = item; + StDirEntry TabDir[ 64 ]; + + for ( int j = 0; j < 64; j++ ) + memcpy( &TabDir[ j ], GetInfoDirEntry( j ), sizeof( StDirEntry )); + + strcpy( NomFic, GetNomAmsdos( TabDir[ i ].Nom ) ); + char *p ; + + do { + TabDir[ i ].User = USER_DELETED; + SetInfoDirEntry( i, &TabDir[ i ]); + p = GetNomAmsdos( TabDir[ ++i ].Nom) ; + } while ( ! strncmp( NomFic, p , max(strlen( p ), strlen( NomFic ) )) ); + + + return ; +} + + + +void DSK::RenameFile( int item , char *NewName) { + char NomFic[ 16 ]; + StDirEntry TabDir[ 64 ]; + StDirEntry DirLoc; + int c = item; + for ( int j = 0; j < 64; j++ ) + memcpy( &TabDir[ j ], GetInfoDirEntry( j ), sizeof( StDirEntry )); + + memset( DirLoc.Nom, ' ', 8); + memset( DirLoc.Ext, ' ', 3); + for ( int i=0; i<(int) strlen( NewName ) ; ++i) + NewName[ i ] = toupper( NewName[ i ] ); + + char *p = strchr( NewName, '.'); + + if ( p ) { + p++; + memcpy( DirLoc.Nom, NewName, p - NewName -1); + memcpy( DirLoc.Ext, p, std::min((int)strlen(p),3) ); + } + else { + memcpy( DirLoc.Nom, NewName, min( (int)strlen( NewName) , 8 ) ); + } + strcpy( NomFic, GetNomAmsdos( TabDir[ c ].Nom )); + + do { + memcpy( TabDir[ c ].Nom , DirLoc.Nom , 8 ); + memcpy( TabDir[ c ].Ext , DirLoc.Ext, 3 ); + SetInfoDirEntry( c, &TabDir[ c ]); + p = GetNomAmsdos( TabDir[ ++c ].Nom ); + }while (!strncmp( NomFic, p , max(strlen(p),strlen(NomFic)))); +} + + +std::string DSK::ReadDskDir( void ) { + StDirEntry TabDir[ 64 ]; + string catalogue; + for ( int i = 0; i < 64; i++ ) { + memcpy( &TabDir[ i ] + , GetInfoDirEntry( i ) + , sizeof( StDirEntry ) + ); + } + // Trier les fichiers + for ( int i = 0; i < 64; i++ ) { + // + // Afficher les fichiers non effac�s + // + if ( TabDir[ i ].User != USER_DELETED && ! TabDir[ i ].NumPage ) { + char Nom[ 13 ]; + memcpy( Nom, TabDir[ i ].Nom, 8 ); + memcpy( &Nom[ 9 ], TabDir[ i ].Ext, 3 ); + Nom[ 8 ] = '.'; + Nom[ 12 ] = 0; + // + // Masquer les bits d'attributs + // + for ( int j = 0; j < 12; j++ ) + { + Nom[ j ] &= 0x7F; + + if ( ! isprint( Nom[ j ] ) ) + Nom[ j ] = '?' ; + } + + catalogue += Nom; + catalogue += " "; + ostringstream c; + c << (int)TabDir[i].User; + catalogue += c.str(); + // + // Calcule la taille du fichier en fonction du nombre de blocs + // + int p = 0, t = 0; + do { + if ( TabDir[ p + i ].User == TabDir[ i ].User ) + t += TabDir[ p + i ].NbPages; + p++; + } while( TabDir[ p + i ].NumPage && ( p + i ) < 64 ); + //string size = GetTaille( ( t + 7 ) >> 3 ); + //catalogue+= " : " + size + "\n"; + catalogue += "\n"; + + } + } + return catalogue; +} diff --git a/tools/iDSK/src/GestDsk.h b/tools/iDSK/src/GestDsk.h new file mode 100644 index 0000000..116e080 --- /dev/null +++ b/tools/iDSK/src/GestDsk.h @@ -0,0 +1,176 @@ +#ifndef GESTDSK_H +#define GESTDSK_H + +#include <string> + +#define USER_DELETED 0xE5 + + +extern char Listing[ 0x280000 ]; +extern unsigned char BufFile[ 0x10000 ]; + + +extern int TailleFic, CurLigne, AdresseCharg, AdresseExec; + +#pragma pack(1) //evite le padding des structures qui sont utilisées dans des memcpy par la suite + +// +// Structure d'une entree AMSDOS +// +typedef struct +{ + unsigned char UserNumber; // 00 User + unsigned char FileName[15]; // 01-0F Nom + extension + unsigned char BlockNum; // 10 Numéro du bloc (disquette) + unsigned char LastBlock; // 11 Flag "dernier bloc" bloc (disquette) + unsigned char FileType; // 12 Type de fichier + unsigned short Length; // 13-14 Longueur + unsigned short Adress; // 15-16 Adresse + unsigned char FirstBlock; // 17 Flag premier bloc de fichier (disquette) + unsigned short LogicalLength; // 18-19 Longueur logique + unsigned short EntryAdress; // 1A-1B Point d'entree + unsigned char Unused[0x24]; + unsigned short RealLength; // 40-42 Longueur reelle + unsigned char BigLength; // Longueur reelle (3 octets) + unsigned short CheckSum; // 43-44 CheckSum Amsdos + unsigned char Unused2[0x3B]; +} StAmsdos; + + +#define SECTSIZE 512 + + +typedef struct +{ + char debut[0x30]; // "MV - CPCEMU Disk-File\r\nDisk-Info\r\n" + unsigned char NbTracks; + unsigned char NbHeads; + unsigned short DataSize; // 0x1300 = 256 + ( 512 * nbsecteurs ) + unsigned char Unused[0xCC]; +} CPCEMUEnt; + + +typedef struct +{ + unsigned char C; // track + unsigned char H; // head + unsigned char R; // sect + unsigned char N; // size + short Un1; + short SizeByte; // Taille secteur en octets +} CPCEMUSect; + + +typedef struct +{ + char ID[0x10]; // "Track-Info\r\n" + unsigned char Track; + unsigned char Head; + short Unused; + unsigned char SectSize; // 2 + unsigned char NbSect; // 9 + unsigned char Gap3; // 0x4E + unsigned char OctRemp; // 0xE5 + CPCEMUSect Sect[29]; +} CPCEMUTrack; + +#ifdef __GNUC__ +#define PACK( __Declaration__ ) __Declaration__ __attribute__((__packed__)) +#else +#define PACK( __Declaration__ ) __pragma( pack(push, 1) ) __Declaration__ __pragma( pack(pop) ) +#endif + +// see http://www.cpcwiki.eu/index.php/Disk_structure#Directory_entries +PACK(typedef struct +{ + unsigned char User; // 00 + char Nom[8]; // 01-08 + char Ext[3]; // 09-0B + unsigned char NumPage; // 0C + unsigned char Unused[2]; // 0D-0E + unsigned char NbPages; // 0F + unsigned char Blocks[16]; // 10-1F +}) StDirEntry; + +#pragma pack() + +enum { ERR_NO_ERR = 0, ERR_NO_DIRENTRY, ERR_NO_BLOCK, ERR_FILE_EXIST }; + +bool CheckAmsdos( unsigned char * Buf ); +StAmsdos * CreeEnteteAmsdos( char * NomFic, unsigned short Length ); +void ClearAmsdos( unsigned char * Buf ); +void SetChecksum( StAmsdos * pEntete ); +bool CheckAmsdos( unsigned char * Buf ); + + +class DSK +{ + unsigned char ImgDsk[ 0x80000 ]; + unsigned char Bitmap[ 256 ]; + + unsigned char * GetRawData( int Pos ); + void WriteRawData( int Pos, unsigned char * Data, int Longueur ); + int GetNbTracks( void ); + void WriteBloc( int bloc, unsigned char * BufBloc ); + void WriteSect( int Track, int Sect, unsigned char * Buff, int AmsdosMode ); + unsigned char * ReadSect( int Track, int Sect, int AmsdosMode ); + CPCEMUTrack * GetInfoTrack( int Track ); + int FillBitmap( void ); + void DskEndian(); + CPCEMUEnt* CPCEMUEntEndian ( CPCEMUEnt* Infos ); + CPCEMUTrack* CPCEMUTrackEndian ( CPCEMUTrack* tr ); + CPCEMUSect CPCEMUSectEndian ( CPCEMUSect Sect); + const char * GetType( int Langue, StAmsdos * Ams ); + int GetMinSect( void ); + int GetPosData( int track, int sect, bool SectPhysique ); + int RechercheBlocLibre( int MaxBloc ); + void FormatTrack( CPCEMUEnt * Infos, int t, int MinSect, int NbSect ); + +public: + DSK() + { + for (int i=0; i< 0x80000; i++) + ImgDsk[i]=0; + for (int j=0; j< 256 ; j++ ) + Bitmap[j]=0; + } + + DSK(const DSK& d) + { + for (int i=0; i< 0x80000; i++) + ImgDsk[i]=d.ImgDsk[i]; + for (int j=0; j< 256 ; j++ ) + Bitmap[j]=d.Bitmap[j]; + } + + ~DSK(){} + + int GetTailleDsk(); + StDirEntry * GetNomDir(std::string Nom ); + int CopieFichier( unsigned char * BufFile, char * NomFic, int TailleFic, int MaxBloc, int, bool,bool ); + bool WriteDsk( std::string NomDsk ); + unsigned char * ReadBloc( int bloc ); + bool ReadDsk( std::string NomFic ); + bool CheckDsk( void ); + int FileExist( char * Nom ); + StDirEntry * GetInfoDirEntry( int NumDir ); + int FileIsIn( std::string FileName ); + int RechercheDirLibre( void ); + void FormatDsk( int NbSect, int NbTrack ); + StAmsdos* StAmsdosEndian ( StAmsdos * pEntete ); + void SetInfoDirEntry( int NumDir, StDirEntry * Dir ); + char * GetEntryNameInCatalogue ( int num , char* Nom ); + char * GetEntrySizeInCatalogue ( int num , char* Size ); + bool GetFileInDsk( char* path, int Indice ); + bool PutFileInDsk( std::string Masque ,int TypeModeImport ,int loadAdress, int exeAdress, int,bool,bool ); + bool OnViewFic(int nItem); + bool Hexdecimal(); + void RemoveFile ( int item ); + void FixEndianDsk( bool LittleToBig ); + void FixEndianTrack( CPCEMUEnt * Infos, int t, int NbSect ); + void RenameFile( int item , char *NewName); + std::string ReadDskDir(void); + +}; + +#endif diff --git a/tools/iDSK/src/Main.cpp b/tools/iDSK/src/Main.cpp new file mode 100644 index 0000000..1bbe6d4 --- /dev/null +++ b/tools/iDSK/src/Main.cpp @@ -0,0 +1,309 @@ +#include <iostream> +#include <cstdlib> +#include <cstring> +#include <algorithm> // pour contourner un bug de std::vector ... + +#include "getopt_pp.h" /* Command line handling */ + +using namespace std; + +#include "MyType.h" +#include "GestDsk.h" +#include "Outils.h" +#include "Main.h" +#include "endianPPC.h" +#include "ViewFile.h" + +int main(int argc, char **argv) +{ + bool IsDskLoc, IsDskSet, + ModeListDsk, ModeImportFile, + ModeRemoveFile, + ModeDisaFile, ModeListBasic, + ModeListDams, ModeListHex, + ModeGetFile, ModeNewDsk, Force_Overwrite, + Read_only, System_file, Split_lines, ModeListAscii, NoOptionSet; + + ModeListDsk = ModeImportFile = ModeListAscii = + ModeRemoveFile = ModeDisaFile = + ModeListBasic = ModeListDams = ModeListHex = ModeNewDsk = + ModeGetFile = IsDskLoc = IsDskSet = Force_Overwrite = Read_only = System_file = false; + NoOptionSet = true; + + string DskFile, AmsdosFile; + vector<string> AmsdosFileList; + + int exeAdress = 0, loadAdress = 0, AmsdosType = 1, UserNumber = 0; + + DSK MyDsk; + + IsDsk = IsDskValid = false; + IsDskSaved = true; + + // Récupération des arguments avec getopt_pp + { + using namespace GetOpt; + GetOpt_pp opts(argc, argv); + + opts >> GlobalOption(DskFile); + if (DskFile != "") + IsDskSet = true; + + opts >> OptionPresent('l', "list", ModeListDsk) + + >> OptionPresent('i', "import", ModeImportFile) >> Option('i', "import", AmsdosFileList) + + >> OptionPresent('r', "remove", ModeRemoveFile) >> Option('r', "remove", AmsdosFileList) + + >> OptionPresent('n', "new", ModeNewDsk) + + >> OptionPresent('z', "disassemble", ModeDisaFile) >> Option('z', "disassemble", AmsdosFileList) + + >> OptionPresent('a', "ascii", ModeListAscii) >> Option('a', "ascii", AmsdosFileList) + + >> OptionPresent('b', "basic", ModeListBasic) >> Option('b', "basic", AmsdosFileList) + + >> OptionPresent('d', "dams", ModeListDams) >> Option('d', "dams", AmsdosFileList) + + >> OptionPresent('h', "hex", ModeListHex) >> Option('h', "hex", AmsdosFileList) + + >> std::hex >> Option('e', "exec", exeAdress) >> Option('c', "load", loadAdress) >> std::dec >> Option('t', "type", AmsdosType) + + >> OptionPresent('g', "get", ModeGetFile) >> Option('g', "get", AmsdosFileList) + + >> OptionPresent('f', "force", Force_Overwrite) >> OptionPresent('o', "write-protect", Read_only) >> OptionPresent('s', "system", System_file) >> OptionPresent('p', "split-lines", Split_lines) >> Option('u', "user", UserNumber); + + if (opts.options_remain()) + { + cout << "Unhandled option ! Check the syntax." << endl; + exit(EXIT_FAILURE); + } + + } //namespace getopt + + if (!IsDskSet) + { + cerr << "You did not select a DSK file to work with !" << endl; + help(); + } + else + cerr << "DSK : " << DskFile << endl; + + if (ModeListBasic || ModeListHex || ModeListDams || ModeDisaFile || ModeListAscii) + { + NoOptionSet = false; + if (!MyDsk.ReadDsk(DskFile)) + { + cerr << "Error reading file (" << DskFile << ")." << endl; + exit(EXIT_FAILURE); + } + if (!MyDsk.CheckDsk()) + { + cerr << "Fichier image non supporté (" << DskFile << ")." << endl; + exit(EXIT_FAILURE); + } + int Indice; + for (vector<string>::iterator iter = AmsdosFileList.begin(); iter != AmsdosFileList.end(); iter++) + { + char *amsdosF = GetNomAmsdos(iter->c_str()); + cerr << "Amsdos file : " << amsdosF << endl; + if ((Indice = MyDsk.FileIsIn(amsdosF)) < 0) + { + cerr << "Error: File " << amsdosF << " not found." << endl; + exit(EXIT_FAILURE); + } + MyDsk.OnViewFic(Indice); + + if (ModeListBasic) + cout << ViewBasic(Split_lines) << endl; + else if (ModeListDams) + cout << "Not yet coded ! Please try a newer version of iDSK ! Sorry !" << endl; + else if (ModeListHex) + { + MyDsk.Hexdecimal(); + cout << Listing << endl; + } + else if (ModeDisaFile) + cout << ViewDesass() << endl; + else if (ModeListAscii) + cout << ViewAscii() << endl; + } + } + + if (ModeNewDsk) + { + NoOptionSet = false; + MyDsk.FormatDsk(9, 42); + if (!MyDsk.WriteDsk(DskFile)) + { + cerr << "Error writing file " << DskFile << endl; + exit(EXIT_FAILURE); + } + } + + + + if (ModeImportFile) + { // Ajouter fichiers sur dsk + NoOptionSet = false; + if (!MyDsk.ReadDsk(DskFile)) + { + cerr << "Error reading file (" << DskFile << ")." << endl; + exit(EXIT_FAILURE); + } + + if (!MyDsk.CheckDsk()) + { + cerr << "Unsupported dsk file (" << DskFile << ")." << endl; + exit(EXIT_FAILURE); + } + + for (vector<string>::iterator iter = AmsdosFileList.begin(); iter != AmsdosFileList.end(); iter++) + { + string amsdosfile = GetNomAmsdos(iter->c_str()); + int Indice; + // Ensure Indice is valid (the file is in the disk already) before atempting to remove it + if ((Indice = MyDsk.FileIsIn(amsdosfile)) != -1) + { + if (!Force_Overwrite) + { + cerr << "(" << amsdosfile << ") File exists, replace ? (Y/N) (try -f switch for autoreplace...):"; + string answer; + cin >> answer; + if (toupper(answer[0]) == 'Y') + MyDsk.RemoveFile(Indice); + else + { + cerr << "Import cancelled, dsk unchanged." << endl; + cout << MyDsk.ReadDskDir(); + exit(EXIT_SUCCESS); + } + } + else + MyDsk.RemoveFile(Indice); + } + cerr << "Amsdos file : " << *iter << endl; + + MyDsk.PutFileInDsk(*iter, AmsdosType, loadAdress, exeAdress, UserNumber, System_file, Read_only); + } + if (MyDsk.WriteDsk(DskFile)) + cout << MyDsk.ReadDskDir(); + else + cerr << "Error writing file : " << DskFile << endl; + } + + if (ModeRemoveFile) + { + NoOptionSet = false; + if (!MyDsk.ReadDsk((char *)DskFile.c_str())) + { + cerr << "Error reading file (" << DskFile << ")." << endl; + exit(EXIT_FAILURE); + } + if (!MyDsk.CheckDsk()) + { + cerr << "unsupported DSK file (" << DskFile << ")." << endl; + exit(EXIT_FAILURE); + } + int Indice; + for (vector<string>::iterator iter = AmsdosFileList.begin(); iter != AmsdosFileList.end(); iter++) + { + char *amsdosF = GetNomAmsdos(iter->c_str()); + cerr << "Amsdos file : " << amsdosF << endl; + if ((Indice = MyDsk.FileIsIn(amsdosF)) < 0) + { + cerr << "Error : file " << amsdosF << " not found." << endl; + exit(EXIT_FAILURE); + } + MyDsk.RemoveFile(Indice); + if (MyDsk.WriteDsk((char *)DskFile.c_str())) + cout << MyDsk.ReadDskDir(); + else + cerr << "Error writing file " << (*iter) << endl; + } + } + + if (ModeGetFile) + { + NoOptionSet = false; + if (!MyDsk.ReadDsk((char *)DskFile.c_str())) + { + cerr << "Error reading dskfile (" << DskFile << ")." << endl; + exit(EXIT_FAILURE); + } + if (!MyDsk.CheckDsk()) + { + cerr << "Unsupported dsk (" << DskFile << ")." << endl; + exit(EXIT_FAILURE); + } + int Indice; + + for (vector<string>::iterator iter = AmsdosFileList.begin(); iter != AmsdosFileList.end(); iter++) + { + char *amsdosF = GetNomAmsdos(iter->c_str()); + cerr << "Fichier Amsdos : " << amsdosF << endl; + if ((Indice = MyDsk.FileIsIn(amsdosF)) < 0) + { + cerr << "Error : file " << amsdosF << " not found." << endl; + exit(EXIT_FAILURE); + } + if (!MyDsk.GetFileInDsk((char *)(*iter).c_str(), Indice)) + { + cerr << "System error : unable to copy (" << AmsdosFile << ")." << endl; + exit(EXIT_FAILURE); + } + } + } + if (ModeListDsk || NoOptionSet) + { // lire Dsk + if (!MyDsk.ReadDsk(DskFile)) + { + cerr << "Error reading file (" << DskFile << ")." << endl; + exit(EXIT_FAILURE); + } + if (!MyDsk.CheckDsk()) + { + cerr << "Unsupported dsk file (" << DskFile << ")." << endl; + exit(EXIT_FAILURE); + } + cout << MyDsk.ReadDskDir(); + } + + cerr << "------------------------------------" << endl; + + return (EXIT_SUCCESS); +} + +void help(void) +{ + cout << endl; + cout << "--------------------------------------------------------------------------------" << endl; + cout << "################################################################################" << endl; + cout << VERSION << " (by Demoniak, Sid, PulkoMandy), http://github.com/cpcsdk " << endl; + cout << "################################################################################" << endl; + cout << endl; + cout << "Usage : " << endl; + cout << "\t" << PROGNAME << " <DSKfile> [OPTIONS] [files to process]" << endl; + cout << "OPTIONS : EXAMPLE" << endl; + cout << "-l : List disk catalog iDSK floppy.dsk -l (default option is no option is set)" << endl; + cout << "-g : export ('Get') file iDSK floppy.dsk -g myprog.bas" << endl; + cout << "-r : Remove file iDSK floppy.dsk -r myprog.bas" << endl; + cout << "-n : create New dsk file iDSK floppy2.dsk -n" << endl; + cout << "-z : disassemble a binary file iDSK floppy.dsk -z myprog.bin" << endl; + cout << "-b : list a Basic file iDSK floppy.dsk -b myprog.bas" << endl + << "-p : split lines after 80 char ... -p" << endl; + cout << "-a : list a Ascii file iDSK floppy.dsk -a myprog.txt" << endl; + cout << "-d : list a Dams file iDSK floppy.dsk -d myprog.dms" << endl; + cout << "-h : list a binary file as Hexadecimal iDSK floppy.dsk -h myprog.bin" << endl; + cout << "-i : Import file iDSK floppy.dsk -i myprog.bas" << endl + << " -t : fileType (0=ASCII/1=BINARY/2=raw) ... -t 1" << endl; + cout << " -e : hex Execute address of file ... -e C000 -t 1" << endl; + cout << " -c : hex loading address of file ... -e C000 -c 4000 -t 1" << endl; + cout << " -f : Force overwriting if file exists ... -f" << endl + << " -o : insert a read-Only file ... -o" << endl + << " -s : insert a System file ... -s" << endl + << " -u : insert file with User number ... -u 3" << endl; + cout << "--------------------------------------------------------------------------------" << endl; + cout << "Please report bugs ! - Demoniak/Sid/PulkoMandy" << endl; + exit(0); +} diff --git a/tools/iDSK/src/Main.h b/tools/iDSK/src/Main.h new file mode 100644 index 0000000..325eda1 --- /dev/null +++ b/tools/iDSK/src/Main.h @@ -0,0 +1,16 @@ +#ifndef __MAIN_CPP__ +#define __MAIN_CPP__ +#define VERSION "iDSK version 0.20" +#define PROGNAME "iDSK" +char Nom[256]; +char Msg[128]; +StDirEntry TabDir[64]; +int PosItem[64]; +int Langue; +bool IsDsk, IsDskValid, IsDskSaved; +int TypeModeImport, TypeModeExport; + +void help(void); +void DecomposeArg(char **argv, int argc); + +#endif diff --git a/tools/iDSK/src/MyType.h b/tools/iDSK/src/MyType.h new file mode 100644 index 0000000..0112428 --- /dev/null +++ b/tools/iDSK/src/MyType.h @@ -0,0 +1,20 @@ +#ifndef MYTYPE_H_ +#define MYTYPE_H_ + +/* types definitions to be coherent with Visual Basic C++ types */ + +typedef unsigned char BYTE; /* unsigned 8-bit type */ +typedef unsigned short WORD; /* unsigned 16-bit type */ +typedef unsigned long DWORD; /* unsigned 32-bit type */ + + +/* constant defintion usefull in the code */ + +#define TRUE 1 +#define FALSE 0 +#define MODE_ASCII 0 +#define MODE_BINAIRE 1 +#define MODE_RAW 2 + + +#endif /*MYTYPE_H_*/ diff --git a/tools/iDSK/src/Outils.cpp b/tools/iDSK/src/Outils.cpp new file mode 100644 index 0000000..54e3c22 --- /dev/null +++ b/tools/iDSK/src/Outils.cpp @@ -0,0 +1,165 @@ +#include <iostream> +#include <cstring> +#include <cstdio> +using namespace std; +#include "Outils.h" + +// +// Initialise une chaine au format hexad�cimal en fonction de la valeur d'entr�e +// +void Hex( char Chaine[], int Valeur, int Digit ) +{ + static char TabDigit[ 17 ] = "0123456789ABCDEF"; + + while( Digit ) + * Chaine++ = TabDigit[ ( Valeur >> ( 4 * ( --Digit ) ) ) & 0x0F ]; +} + + +// +// Conversion hexa->d�cimal +// +int HexToDec( char * Valeur ) +{ + char * p = strchr( Valeur, 'X' ); + if ( p ) + Valeur = ++p; + + p = strchr( Valeur, 'x' ); + if ( p ) + Valeur = ++p; + + p = strchr( Valeur, '#' ); + if ( p ) + Valeur = ++p; + + p = strchr( Valeur, '$' ); + if ( p ) + Valeur = ++p; + + p = strchr( Valeur, '&' ); + if ( p ) + Valeur = ++p; + + int Ret = 0, i = 0; + while( Valeur[ i ] ) + { + Ret <<= 4; + char c = Valeur[ i++ ]; + if ( c >= '0' && c <= '9' ) + Ret += c - '0'; + else + Ret += ( c & 0x5F ) - 0x37; + } + return( Ret ); +} + + +// +// Conversion d'un secteur (512 octets) en affichage Hexa et ASCII +// +void SetBuffViewHexa( unsigned char * src, char * Hex, char * Ascii, unsigned short Offset, int AddOffset) +{ + const char * CodeHexa = "0123456789ABCDEF"; + int q = 0,i; + + // + // Parcourir les 512 octets de la source et remplir les buffers + // + for ( i = 0; i < 512; i++ ) + { + unsigned char b = * src++; + if ( b > 32 && b < 127 ) + { + Ascii[ i ] = b; + // cout << "b32:" << (int)b <<" Ascii["<<i<<"]:"<< Ascii[ i ] << endl; + } + else + { + Ascii[ i ] = '.'; + } + if ( AddOffset && ( ! ( i & 0x0F ) ) ) + { + Hex[ q++ ] = '#'; + Hex[ q++ ] = CodeHexa[ Offset >> 12 ]; + Hex[ q++ ] = CodeHexa[ ( Offset >> 8 ) & 0x0F ]; + Hex[ q++ ] = CodeHexa[ ( Offset >> 4 ) & 0x0F ]; + Hex[ q++ ] = CodeHexa[ Offset & 0x0F ]; + Hex[ q++ ] = ':'; + } + Offset++; + Hex[ q++ ] = CodeHexa[ b >> 4 ]; + Hex[ q++ ] = CodeHexa[ b & 0x0F ]; + Hex[ q++ ] = ' '; + } + Hex[ q ] = 0; + Ascii[ i ] = 0; +} + + +// +// Retourne le num�ro d'user sous forme de chaine +// +char * GetUser( int u ) +{ + static char User[ 8 ]; + sprintf(User, "%d", u); + return( User); +} + + + +// +// Retourne la taille du fichier sous forme de chaine +// +char * GetTaille( int t ) +{ + static char Taille[ 16 ]; + + sprintf( Taille, "%d Ko", t ); + return( Taille ); +} + + +// +// Retourne le nom du fichier formatt� amsdos (8+3) +// +char * GetNomAmsdos(const char * AmsName ) +{ + // Extract the name (without directory components) + const char* lastSlash = strrchr(AmsName, '/'); + const char* lastBackslash = strrchr(AmsName, '\\'); + if (lastSlash > lastBackslash) + AmsName = lastSlash + 1; + else if (lastSlash < lastBackslash) + AmsName = lastBackslash + 1; + + static char NomAmsdos[ 16 ]; + int i; + + char * p = NomAmsdos; + for ( i = 0; i < 8; i++ ) { + if ( * AmsName != ' ' && *AmsName != '.' ) + * p++ = * AmsName++; + /*if ( * AmsName == '-' ) + * p++ = * AmsName++;*/ + } + + while( * AmsName != '.' && * AmsName ) + AmsName++; + + AmsName++; + + * p = 0; + strcat( NomAmsdos, "." ); + + for ( i = 0; * AmsName && i < 3; i++ ) + *++p = * AmsName++; + + * ++p = 0; + i = 0; + while( NomAmsdos[ i ] ) + NomAmsdos[ i++ ] &= 0x7F; + + return( NomAmsdos ); +} diff --git a/tools/iDSK/src/Outils.h b/tools/iDSK/src/Outils.h new file mode 100644 index 0000000..911346f --- /dev/null +++ b/tools/iDSK/src/Outils.h @@ -0,0 +1,19 @@ +#ifndef __OUTILS_H__ +#define __OUTILS_H__ + + +void Hex( char Chaine[], int Valeur, int Digit ); + +int HexToDec( char * Valeur ); + +void SetBuffViewHexa( unsigned char * src, char * Hex, char * Ascii,unsigned short Offset, int AddOffset); + +char * GetNomAmsdos( const char * AmsName ); + +char * GetUser( int u ); + +char * GetTaille( int t ); + + + +#endif diff --git a/tools/iDSK/src/ViewFile.cpp b/tools/iDSK/src/ViewFile.cpp new file mode 100644 index 0000000..733bba6 --- /dev/null +++ b/tools/iDSK/src/ViewFile.cpp @@ -0,0 +1,44 @@ +#include <iostream> +using namespace std; +#include "GestDsk.h" +#include "Outils.h" +#include "Basic.h" +#include "Desass.h" +#include "Dams.h" +#include "endianPPC.h" +#include "ViewFile.h" +#include "Ascii.h" + +string ViewDams() +{ + cerr << "Taille du fichier : " << TailleFic << endl; + Dams(BufFile, TailleFic, Listing); + return Listing; + //cout << Listing << endl; +} + +string ViewDesass() +{ + cerr << "Taille du fichier : " << TailleFic << endl; + Desass(BufFile, Listing, TailleFic, AdresseCharg); + return Listing; + //cout << Listing << endl; +} + +string ViewBasic(bool AddCrLf) +{ + bool IsBasic = true; + //cout << "Entre Ici\n"; + cerr << "Taille du fichier : " << TailleFic << endl; + Basic(BufFile, Listing, IsBasic, AddCrLf); + //cout << Listing << endl; + return Listing; +} + +string ViewAscii() +{ + cerr << "Taille du fichier : " << TailleFic << endl; + Ascii(BufFile, Listing, TailleFic); + //cout << Listing << endl; + return Listing; +} diff --git a/tools/iDSK/src/ViewFile.h b/tools/iDSK/src/ViewFile.h new file mode 100644 index 0000000..1f5ed1f --- /dev/null +++ b/tools/iDSK/src/ViewFile.h @@ -0,0 +1,10 @@ +#ifndef __VIEWFILE_H__ +#define __VIEWFILE_H__ + +string ViewDams(); +string ViewLine(); +string ViewDesass(); +string ViewAscii(); +string ViewBasic(bool AddCrLf); + +#endif diff --git a/tools/iDSK/src/endianPPC.cpp b/tools/iDSK/src/endianPPC.cpp new file mode 100644 index 0000000..f3146d6 --- /dev/null +++ b/tools/iDSK/src/endianPPC.cpp @@ -0,0 +1,24 @@ +#include <iostream> +#include <cstdlib> +using namespace std; +#include "endianPPC.h" + +#ifndef _MSC_VER +#include <sys/param.h> +#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__MORPHOS__) +#include <machine/endian.h> +#else +#include <endian.h> +#endif +#endif + +bool isBigEndian(void) +{ +#ifdef BYTE_ORDER + return BYTE_ORDER == BIG_ENDIAN; +#elif defined _MSC_VER + return false; // Modern VC++ only runs on x86, x64 and ARM, doesn't require big/little endian conversion +#else + return __BYTE_ORDER == __BIG_ENDIAN; +#endif +} diff --git a/tools/iDSK/src/endianPPC.h b/tools/iDSK/src/endianPPC.h new file mode 100644 index 0000000..9def637 --- /dev/null +++ b/tools/iDSK/src/endianPPC.h @@ -0,0 +1,22 @@ +#ifndef __ENDIANPPC_H__ +#define __ENDIANPPC_H__ + +/* macros convertion little endian convertion to big endian convertion */ + + +#define SWAP_2(x) ( (((x) & 0xff) << 8) | ((unsigned short)(x) >> 8) ) +#define SWAP_4(x) ( ((x) << 24) | \ + (((x) << 8) & 0x00ff0000) | \ + (((x) >> 8) & 0x0000ff00) | \ + ((x) >> 24) ) +#define FIX_SHORT(x) (*(unsigned short *)&(x) = SWAP_2(*(unsigned short *)&(x))) +#define FIX_INT(x) (*(unsigned int *)&(x) = SWAP_4(*(unsigned int *)&(x))) +#define FIX_FLOAT(x) FIX_INT(x) + + + +/* endianness test function */ + +bool isBigEndian(void); + +#endif diff --git a/tools/iDSK/src/firmware.h b/tools/iDSK/src/firmware.h new file mode 100644 index 0000000..13ecc7d --- /dev/null +++ b/tools/iDSK/src/firmware.h @@ -0,0 +1,308 @@ +struct { + unsigned short address; + const char * label; +} FirmWareVectors[] = { +{0x0000, "RESET_ENTRY"}, +{0x0008, "LOW_JUMP"}, +{0x000B, "KL_LOW_PCHL"}, +{0x000E, "PCBC"}, +{0x0010, "SIDE_CALL"}, +{0x0013, "KL_SIDE_PCHL"}, +{0x0016, "PCDE"}, +{0x0018, "FAR_CALL"}, +{0x001B, "KL_FAR_PCHL"}, +{0x001E, "PCHL"}, +{0x0020, "RAM_LAM"}, +{0x0023, "KL_FAR_CALL"}, +{0x0028, "FIRM_JUMP"}, +{0x0030, "USER_RESTART"}, +{0x0038, "INTERRUPT_ENTRY"}, +{0x003B, "EXT_INTERRUPT"}, +{0xB900, "KL_U_ROM_ENABLE"}, +{0xB903, "KL_U_ROM_DISABLE"}, +{0xB906, "KL_L_ROM_ENABLE"}, +{0xB909, "KL_L_ROM_DISABLE"}, +{0xB90C, "KL_ROM_RESTORE"}, +{0xB90F, "KL_ROM_SELECT"}, +{0xB912, "KL_CURR_SELECTION"}, +{0xB915, "KL_PROBE_ROM"}, +{0xB918, "KL_ROM_DESELECT"}, +{0xB91B, "KL_LDIR"}, +{0xB91E, "KL_LDDR"}, +{0xB921, "KL_POLL_SYNCHRONOUS"}, +{0xB92A, "KL_SCAN_NEEDED"}, +{0xBB00, "KM_INITIALISE"}, +{0xBB03, "KM_RESET"}, +{0xBB06, "KM_WAIT_CHAR"}, +{0xBB09, "KM_READ_CHAR"}, +{0xBB0C, "KM_CHAR_RETURN"}, +{0xBB0F, "KM_SET_EXPAND"}, +{0xBB12, "KM_GET_EXPAND"}, +{0xBB15, "KM_EXP_BUFFER"}, +{0xBB18, "KM_WAIT_KEY"}, +{0xBB1B, "KM_READ_KEY"}, +{0xBB1E, "KM_TEST_KEY"}, +{0xBB21, "KM_GET_STATE"}, +{0xBB24, "KM_GET_JOYSTICK"}, +{0xBB27, "KM_SET_TRANSLATE"}, +{0xBB2A, "KM_GET_TRANSLATE"}, +{0xBB2D, "KM_SET_SHIFT"}, +{0xBB30, "KM_GET_SHIFT"}, +{0xBB33, "KM_SET_CONTROL"}, +{0xBB36, "KM_GET_CONTROL"}, +{0xBB39, "KM_SET_REPEAT"}, +{0xBB3C, "KM_GET_REPEAT"}, +{0xBB3F, "KM_SET_DELAY"}, +{0xBB42, "KM_GET_DELAY"}, +{0xBB45, "KM_ARM_BREAKS"}, +{0xBB48, "KM_DISARM_BREAK"}, +{0xBB4B, "KM_BREAK_EVENT"}, +{0xBB4E, "TXT_INITIALISE"}, +{0xBB51, "TXT_RESET"}, +{0xBB54, "TXT_VDU_ENABLE"}, +{0xBB57, "TXT_VDU_DISABLE"}, +{0xBB5A, "TXT_OUTPUT"}, +{0xBB5D, "TXT_WR_CHAR"}, +{0xBB60, "TXT_RD_CHAR"}, +{0xBB63, "TXT_SET_GRAPHIC"}, +{0xBB66, "TXT_WIN_ENABLE"}, +{0xBB69, "TXT_GET_WINDOW"}, +{0xBB6C, "TXT_CLEAR_WINDOW"}, +{0xBB6F, "TXT_SET_COLUMN"}, +{0xBB72, "TXT_SET_ROW"}, +{0xBB75, "TXT_SET_CURSOR"}, +{0xBB78, "TXT_GET_CURSOR"}, +{0xBB7B, "TXT_CUR_ENABLE"}, +{0xBB7E, "TXT_CUR_DISABLE"}, +{0xBB81, "TXT_CUR_ON"}, +{0xBB84, "TXT_CUR_OFF"}, +{0xBB87, "TXT_VALIDATE"}, +{0xBB8A, "TXT_PLACE_CURSOR"}, +{0xBB8D, "TXT_REMOVE_CURSOR"}, +{0xBB90, "TXT_SET_PEN"}, +{0xBB93, "TXT_GET_PEN"}, +{0xBB96, "TXT_SET_PAPER"}, +{0xBB99, "TXT_GET_PAPER"}, +{0xBB9C, "TXT_INVERSE"}, +{0xBB9F, "TXT_SET_BACK"}, +{0xBBA2, "TXT_GET_BACK"}, +{0xBBA5, "TXT_GET_MATRIX"}, +{0xBBA8, "TXT_SET_MATRIX"}, +{0xBBAB, "TXT_SET_M_TABLE"}, +{0xBBAE, "TXT_GET_M_TABLE"}, +{0xBBB1, "TXT_GET_CONTROLS"}, +{0xBBB4, "TXT_STR_SELECT"}, +{0xBBB7, "TXT_SWAP_STREAMS"}, +{0xBBBA, "GRA_INITIALISE"}, +{0xBBBD, "GRA_RESET"}, +{0xBBC0, "GRA_MOVE_ABSOLUTE"}, +{0xBBC3, "GRA_MOVE_RELATIVE"}, +{0xBBC6, "GRA_ASK_CURSOR"}, +{0xBBC9, "GRA_SET_ORIGIN"}, +{0xBBCC, "GRA_GET_ORIGIN"}, +{0xBBCF, "GRA_WIN_WIDTH"}, +{0xBBD2, "GRA_WIN_HEIGHT"}, +{0xBBD5, "GRA_GET_W_WIDTH"}, +{0xBBD8, "GRA_GET_W_HEIGHT"}, +{0xBBDB, "GRA_CLEAR_WINDOW"}, +{0xBBDE, "GRA_SET_PEN"}, +{0xBBE1, "GRA_GET_PEN"}, +{0xBBE4, "GRA_SET_PAPER"}, +{0xBBE7, "GRA_GET_PAPER"}, +{0xBBEA, "GRA_PLOT_ABSOLUTE"}, +{0xBBED, "GRA_PLOT_RELATIVE"}, +{0xBBF0, "GRA_TEST_ABSOLUTE"}, +{0xBBF3, "GRA_TEST_RELATIVE"}, +{0xBBF6, "GRA_LINE_ABSOLUTE"}, +{0xBBF9, "GRA_LINE_RELATIVE"}, +{0xBBFC, "GRA_WR_CHAR"}, +{0xBBFF, "SCR_INITIALISE"}, +{0xBC02, "SCR_RESET"}, +{0xBC05, "SCR_SET_OFFSET"}, +{0xBC08, "SCR_SET_BASE"}, +{0xBC0B, "SCR_GET_LOCATION"}, +{0xBC0E, "SCR_SET_MODE"}, +{0xBC11, "SCR_GET_MODE"}, +{0xBC14, "SCR_CLEAR"}, +{0xBC17, "SCR_CHAR_LIMITS"}, +{0xBC1A, "SCR_CHAR_POSITION"}, +{0xBC1D, "SCR_DOT_POSITION"}, +{0xBC20, "SCR_NEXT_BYTE"}, +{0xBC23, "SCR_PREV_BYTE"}, +{0xBC26, "SCR_NEXT_LINE"}, +{0xBC29, "SCR_PREV_LINE"}, +{0xBC2C, "SCR_INK_ENCODE"}, +{0xBC2F, "SCR_INK_DECODE"}, +{0xBC32, "SCR_SET_INK"}, +{0xBC35, "SCR_GET_INK"}, +{0xBC38, "SCR_SET_BORDER"}, +{0xBC3B, "SCR_GET_BORDER"}, +{0xBC3E, "SCR_SET_FLASHING"}, +{0xBC41, "SCR_GET_FLASHING"}, +{0xBC44, "SCR_FILL_BOX"}, +{0xBC47, "SCR_FLOOD_BOX"}, +{0xBC4A, "SCR_CHAR_INVERT"}, +{0xBC4D, "SCR_HW_ROLL"}, +{0xBC50, "SCR_SW_ROLL"}, +{0xBC53, "SCR_UNPACK"}, +{0xBC56, "SCR_REPACK"}, +{0xBC59, "SCR_ACCESS"}, +{0xBC5C, "SCR_PIXELS"}, +{0xBC5F, "SCR_HORIZONTAL"}, +{0xBC62, "SCR_VERTICAL"}, +{0xBC65, "CAS_INITIALISE"}, +{0xBC68, "CAS_SET_SPEED"}, +{0xBC6B, "CAS_NOISY"}, +{0xBC6E, "CAS_START_MOTOR"}, +{0xBC71, "CAS_STOP_MOTOR"}, +{0xBC74, "CAS_RESTORE_MOTOR"}, +{0xBC77, "CAS_IN_OPEN"}, +{0xBC7A, "CAS_IN_CLOSE"}, +{0xBC7D, "CAS_IN_ABANDON"}, +{0xBC80, "CAS_IN_CHAR"}, +{0xBC83, "CAS_IN_DIRECT"}, +{0xBC86, "CAS_RETURN"}, +{0xBC89, "CAS_TEST_EOF"}, +{0xBC8C, "CAS_OUT_OPEN"}, +{0xBC8F, "CAS_OUT_CLOSE"}, +{0xBC92, "CAS_OUT_ABANDON"}, +{0xBC95, "CAS_OUT_CHAR"}, +{0xBC98, "CAS_OUT_DIRECT"}, +{0xBC9B, "CAS_CATALOG"}, +{0xBC9E, "CAS_WRITE"}, +{0xBCA1, "CAS_READ"}, +{0xBCA4, "CAS_CHECK"}, +{0xBCA7, "SOUND_RESET"}, +{0xBCAA, "SOUND_QUEUE"}, +{0xBCAD, "SOUND_CHECK"}, +{0xBCB0, "SOUND_ARM_EVENT"}, +{0xBCB3, "SOUND_RELEASE"}, +{0xBCB6, "SOUND_HOLD"}, +{0xBCB9, "SOUND_CONTINUE"}, +{0xBCBC, "SOUND_AMPL_ENVELOPE"}, +{0xBCBF, "SOUND_TONE_ENVELOPE"}, +{0xBCC2, "SOUND_A_ADDRESS"}, +{0xBCC5, "SOUND_T_ADDRESS"}, +{0xBCC8, "KL_CHOKE_OFF"}, +{0xBCCB, "KL_ROM_WALK"}, +{0xBCCE, "KL_INIT_BACK"}, +{0xBCD1, "KL_LOG_EXT"}, +{0xBCD4, "KL_FIND_COMMAND"}, +{0xBCD7, "KL_NEW_FRAME_FLY"}, +{0xBCDA, "KL_ADD_FRAME_FLY"}, +{0xBCDD, "KL_DEL_FRAME_FLY"}, +{0xBCE0, "KL_NEW_FAST_TICKER"}, +{0xBCE3, "KL_ADD_FAST_TICKER"}, +{0xBCE6, "KL_DEL_FAST_TICKER"}, +{0xBCE9, "KL_ADD_TICKER"}, +{0xBCEC, "KL_DEL_TICKER"}, +{0xBCEF, "KL_INIT_EVENT"}, +{0xBCF2, "KL_EVENT"}, +{0xBCF5, "KL_SYNC_RESET"}, +{0xBCF8, "KL_DEL_SYNCHRONOUS"}, +{0xBCFB, "KL_NEXT_SYNCH"}, +{0xBCFE, "KL_DO_SYNCH"}, +{0xBD01, "KL_DONE_SYNCH"}, +{0xBD04, "KL_EVENT_DISABLE"}, +{0xBD07, "KL_EVENT_ENABLE"}, +{0xBD0A, "KL_DISARM_EVENT"}, +{0xBD0D, "KL_TIME_PLEASE"}, +{0xBD10, "KL_TIME_SET"}, +{0xBD13, "MC_BOOT_PROGRAM"}, +{0xBD16, "MC_START_PROGRAM"}, +{0xBD19, "MC_WAIT_FLYBACK"}, +{0xBD1C, "MC_SET_MODE"}, +{0xBD1F, "MC_SCREEN_OFFSET"}, +{0xBD22, "MC_CLEAR_INKS"}, +{0xBD25, "MC_SET_INKS"}, +{0xBD28, "MC_RESET_PRINTER"}, +{0xBD2B, "MC_PRINT_CHAR"}, +{0xBD2E, "MC_PRINTER_BUSY"}, +{0xBD31, "MC_SEND_PRINTER"}, +{0xBD34, "MC_SOUND_REGISTER"}, +{0xBD37, "JUMP_RESTORE"}, +{0xBD3A, "KM_SET_LOCKS"}, +{0xBD40, "TXT_ASK_STATE"}, +{0xBD43, "GRA_DEFAULT"}, +{0xBD46, "GRA_SET_BACK"}, +{0xBD49, "GRA_SET_FIRST"}, +{0xBD4C, "GRA_SET_LINE_MASK"}, +{0xBD4F, "GRA_FROM_USER"}, +{0xBD52, "GRA_FILL"}, +{0xBD55, "SCR_SET_POSITION"}, +{0xBD58, "MC_PRINT_TRANSLATION"}, +{0xBD5B, "KL_BANK_SWITCH"}, +{0xBDCD, "IND_TXT_DRAW_CURSOR"}, +{0xBDD0, "IND_TXT_UNDRAW_CURSOR"}, +{0xBDD3, "IND_TXT_WRITE_CHAR"}, +{0xBDD6, "IND_TXT_UNWRITE"}, +{0xBDD9, "IND_TXT_OUT_ACTION"}, +{0xBDDC, "IND_GRA_PLOT"}, +{0xBDDF, "IND_GRA_TEST"}, +{0xBDE2, "IND_GRA_LINE"}, +{0xBDE5, "IND_SCR_READ"}, +{0xBDE8, "IND_SCR_WRITE"}, +{0xBDEB, "IND_SCR_MODE_CLEAR"}, +{0xBDEE, "IND_KM_TEST_BREAK"}, +{0xBDF1, "IND_MC_WAIT_PRINTER"}, +{0xBDF4, "IND_KM_SCAN_KEYS"}, +{0xBD61, "MOVE_REAL"}, +{0xBD64, "INTEGER_TO_REAL"}, +{0xBD67, "BINARY_TO_REAL"}, +{0xBD6A, "REAL_TO_INTEGER"}, +{0xBD6D, "REAL_TO_BINARY"}, +{0xBD70, "REAL_FIX"}, +{0xBD73, "REAL_INT"}, +{0xBD76, "INTERNAL_SUBROUTINE"}, +{0xBD79, "REAL_x10POWA"}, +{0xBD7C, "REAL_ADDITION"}, +{0xBD82, "REAL_REVERSE_SUBTRACTION"}, +{0xBD85, "REAL_MULTIPLICATION"}, +{0xBD88, "REAL_DIVISION"}, +{0xBD8E, "REAL_COMPARISON"}, +{0xBD91, "REAL_UNARY_MINUS"}, +{0xBD94, "REAL_SIGNUM_SGN"}, +{0xBD97, "SET_ANGLE_MODE"}, +{0xBD9A, "REAL_PI"}, +{0xBD9D, "REAL_SQR"}, +{0xBDA0, "REAL_POWER"}, +{0xBDA3, "REAL_LOG"}, +{0xBDA6, "REAL_LOG_10"}, +{0xBDA9, "REAL_EXP"}, +{0xBDAC, "REAL_SINE"}, +{0xBDAF, "REAL_COSINE"}, +{0xBDB2, "REAL_TANGENT"}, +{0xBDB5, "REAL_ARCTANGENT"}, +{0xBD5B, "REAL_SUBTRACTION"}, +{0xBD67, "REAL_EXPONENT_ADDITION"}, +{0xBDAC, "INTEGER_ADDITION"}, +{0xBDAF, "INTEGER_SUBTRACTION"}, +{0xBDB2, "INTEGER_REVERSE_SUBTRACTION"}, +{0xBDB5, "INTEGER_MULTIPLICATION"}, +{0xBDB8, "INTEGER_DIVISION"}, +{0xBDBB, "INTEGER_DIVISION_2"}, +{0xBDC4, "INTEGER_COMPARISON"}, +{0xBDC7, "INTEGER_UNARY_MINUS"}, +{0xBDCA, "INTEGER_SIGNUM_SGN"}, +{0xBD5E, "TEXT_INPUT"}, +{0xBD7F, "REAL_RND"}, +{0xBD8B, "REAL_RND0"}, +{0xC033, "BIOS_SET_MESSAGE"}, +{0xC036, "BIOS_SETUP_DISC"}, +{0xC039, "BIOS_SELECT_FORMAT"}, +{0xC03C, "BIOS_READ_SECTOR"}, +{0xC03F, "BIOS_WRITE_SECTOR"}, +{0xC042, "BIOS_FORMAT_TRACK"}, +{0xC045, "BIOS_MOVE_TRACK"}, +{0xC048, "BIOS_GET_STATUS"}, +{0xC04B, "BIOS_SET_RETRY_COUNT"}, +{0xC56C, "GET_SECTOR_DATA"}, +{0xBE53, "AMSDOS_DRIVE_HSUS"}, +{0xBE54, "AMSDOS_DRIVE_TRACK"}, +{0xBE5E, "AMSDOS_FLAG_RW_SECTOR"}, +{0xBE5F, "AMSDOS_FLAG_MOTOR"}, +{0xBE7D, "AMSDOS_RESERVED_AREA"}, +{0xBE7D, "AMSDOS_MEMORY_POOL"}, +{0xBE7F, "AMSDOS_HOOK"}, +{0, 0} +}; diff --git a/tools/iDSK/src/getopt_pp.cpp b/tools/iDSK/src/getopt_pp.cpp new file mode 100644 index 0000000..a497c14 --- /dev/null +++ b/tools/iDSK/src/getopt_pp.cpp @@ -0,0 +1,269 @@ +/* +GetOpt_pp: Yet another C++ version of getopt. + This file is part of GetOpt_pp. + + Copyright (C) Daniel Gutson, FuDePAN 2007-2010 + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt in the root directory or + copy at http://www.boost.org/LICENSE_1_0.txt) + + GetOpt_pp IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT + SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE + FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, + ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +#include <fstream> + +#if __APPLE__ +#include <crt_externs.h> +#elif _WIN32 +#include <Stdio.h> +#else +#include <unistd.h> +#endif + +#include "getopt_pp.h" + +namespace GetOpt +{ + +GETOPT_INLINE Token* GetOpt_pp::_add_token(const std::string& value, Token::Type type) +{ + Token* const ret = new Token(value, type); + if (_first_token == NULL) + _first_token = ret; + else + _last_token->link_to(ret); + _last_token = ret; + return ret; +} + +GETOPT_INLINE void GetOpt_pp::_init_flags() +{ + std::stringstream ss; + _flags = ss.flags(); +} + +GETOPT_INLINE void GetOpt_pp::_parse_sub_file(const std::string& file) +{ + std::ifstream ifile(file.c_str()); + if (!ifile) + throw OptionsFileNotFoundEx(file); + + std::vector<std::string> args; + std::string arg; + + while (ifile >> arg) + args.push_back(arg); + + _parse(args); +} + +GETOPT_INLINE void GetOpt_pp::_parse(const std::vector<std::string>& args) +{ + bool any_option_processed = false; + const size_t argc = args.size(); + + size_t start = 0; + if ( _app_name.empty() ) + { + _app_name = args[0]; + start = 1; + } + + // parse arguments by their '-' or '--': + // (this will be a state machine soon) + for (size_t i = start; i < argc; i++) + { + const std::string& currentArg = args[i]; + + if (currentArg[0] == '-' && currentArg.size() > 1) + { + // see what's next, differentiate whether it's short or long: + if (currentArg[1] == '-') + { + if ( currentArg.size() > 2 ) + { + // long option + _longOps[currentArg.substr(2)].token = _add_token(currentArg.substr(2), Token::LongOption); + } + else + { + // it's the -- option alone + _longOps[currentArg].token = _add_token(currentArg, Token::GlobalArgument); + } + + any_option_processed = true; + } + else + { + // check if it is a negative number: rules + // * floating point negative numbers are straight classified as 'arguments' + // * integer negative numbers of more than 1 digit length are also 'arguments' + // * integer negatives of 1 digit length can be either arguments or short options. + // * anything else: short options. + int anInt; + float aFloat; + std::stringstream dummy; + if ( convert(currentArg, anInt, dummy.flags()) == _Option::OK ) + { + if ( currentArg.size() > 2 ) // if it's larger than -d (d=digit), then assume it's a negative number: + _add_token(currentArg, any_option_processed ? Token::UnknownYet : Token::GlobalArgument); + else // size == 2: it's a 1 digit negative number + _shortOps[currentArg[1]].token = _add_token(currentArg, Token::PossibleNegativeArgument); + } + else if ( convert(currentArg, aFloat, dummy.flags()) == _Option::OK ) + _add_token(currentArg, any_option_processed ? Token::UnknownYet : Token::GlobalArgument); + else + { + // short option + // iterate over all of them, keeping the last one in currentData + // (so the intermediates will generate 'existent' arguments, as of '-abc') + for( size_t j = 1; j < currentArg.size(); j++ ) + _shortOps[currentArg[j]].token = _add_token(std::string(currentArg, j, 1), Token::ShortOption); + } + + any_option_processed = true; + } + } + else if ( currentArg[0] == '@' && currentArg.size() > 1 ) + { + // suboptions file + _parse_sub_file(currentArg.substr(1)); + } + else + { + _add_token(currentArg, any_option_processed ? Token::UnknownYet : Token::GlobalArgument); + } + } + + _last = _Option::OK; // TODO: IMPROVE!! +} + +GETOPT_INLINE void GetOpt_pp::_argc_argv_to_vector(int argc, const char* const* const argv, std::vector<std::string>& args) +{ + for (int i = 0; i < argc; i++) + args.push_back(argv[i]); +} + +GETOPT_INLINE GetOpt_pp::TokensDeleter::~TokensDeleter() +{ + Token* next; + Token* current(_first); + while (current != NULL) + { + next = current->next; + delete current; + current = next; + } +} + +GETOPT_INLINE GetOpt_pp::GetOpt_pp(int argc, const char* const* const argv) + : _exc(std::ios_base::goodbit), _first_token(NULL), _last_token(NULL), _tokens_deleter(_first_token) +{ + _init_flags(); + std::vector<std::string> args; + _argc_argv_to_vector(argc, argv, args); + _parse(args); +} + +GETOPT_INLINE GetOpt_pp::GetOpt_pp(int argc, const char* const* const argv, _EnvTag) + : _first_token(NULL), _last_token(NULL), _tokens_deleter(_first_token) +{ + _init_flags(); + std::vector<std::string> args; + _argc_argv_to_vector(argc, argv, args); + _parse(args); +} + +GETOPT_INLINE GetOpt_pp& GetOpt_pp::operator >> (const _Option& opt) +{ + if (_last != _Option::ParsingError) + { + _last = opt(_shortOps, _longOps, _first_token, _flags); + + switch (_last) + { + case _Option::OK: + break; + + case _Option::OptionNotFound: + if (_exc & std::ios_base::eofbit) + throw OptionNotFoundEx(); + break; + + case _Option::BadType: + if (_exc & std::ios_base::failbit) + throw InvalidFormatEx(); + break; + + case _Option::NoArgs: + if (_exc & std::ios_base::eofbit) + throw ArgumentNotFoundEx(); + break; + + case _Option::TooManyArgs: + if (_exc & std::ios_base::failbit) + throw TooManyArgumentsEx(); + break; + + case _Option::OptionNotFound_NoEx: + break; // Ok, it will be read by casting to bool + + case _Option::ParsingError: + break; // just to disable warning + } + } + else if (_exc & std::ios_base::failbit) + throw ParsingErrorEx(); + + return *this; +} + +GETOPT_INLINE GetOpt_pp& GetOpt_pp::operator >> (std::ios_base & (*iomanip)(std::ios_base&)) +{ + std::stringstream ss; + ss.flags(_flags); + _flags = (ss << iomanip).flags(); + return *this; +} + +GETOPT_INLINE bool GetOpt_pp::options_remain() const +{ + bool remain = false; + ShortOptions::const_iterator it = _shortOps.begin(); + while (it != _shortOps.end() && !remain) + { + remain = (it->second.flags == OptionData::CmdLine_NotExtracted); + ++it; + } + + if (!remain) + { + LongOptions::const_iterator it = _longOps.begin(); + while (it != _longOps.end() && !remain) + { + remain = (it->second.flags == OptionData::CmdLine_NotExtracted); + ++it; + } + } + + if (!remain) + { + // check for the global arguments: + Token* token = _first_token; + while (!remain && token != NULL) + { + remain = (token->type == Token::GlobalArgument || token->type == Token::UnknownYet); + token = token->next; + } + } + + return remain; +} + +} diff --git a/tools/iDSK/src/getopt_pp.h b/tools/iDSK/src/getopt_pp.h new file mode 100644 index 0000000..117575f --- /dev/null +++ b/tools/iDSK/src/getopt_pp.h @@ -0,0 +1,745 @@ +/* +GetOpt_pp: Yet another C++ version of getopt. + This file is part of GetOpt_pp. + + Copyright (C) Daniel Gutson, FuDePAN 2007-2010 + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt in the root directory or + copy at http://www.boost.org/LICENSE_1_0.txt) + + GetOpt_pp IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT + SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE + FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, + ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +#ifndef GETOPT_PP_H +#define GETOPT_PP_H + +#include <string> +#include <vector> // candidate to be removed +#include <map> +#include <sstream> +#include <list> + +/* + DESIGN GOALS: + - EASY to use + - EASY to learn + - mimc STL's streams + - EASY to extend +*/ + +#ifndef GETOPT_INLINE +# define GETOPT_INLINE +#endif + +namespace GetOpt +{ + +struct Token +{ + enum Type + { + ShortOption, + LongOption, + GlobalArgument, + GlobalArgumentUsed, // already read, skip in the next read + OptionArgument, + PossibleNegativeArgument, + UnknownYet // can be a global option, or an option of the previous one + }; + + Type type; + std::string value; + Token* next; + + Token(const std::string& value, Type type = UnknownYet) + : type(type), value(value), next(NULL) + {} + + bool is_last() const + { + return next == NULL; + } + + void link_to(Token* new_next) + { + next = new_next; + } + + Token* get_next_option_argument() const + { + if (is_last()) + return NULL; + else + { + if (next->type == UnknownYet || next->type == OptionArgument || next->type == PossibleNegativeArgument) + return next; + else + return NULL; + } + } +}; + +struct OptionData +{ + enum _Flags + { + CmdLine_NotExtracted, + CmdLine_Extracted, + Envir + }; + + _Flags flags; + Token* token; + OptionData() : flags(CmdLine_NotExtracted) {} +}; + +typedef std::map<std::string, OptionData> LongOptions; +typedef std::map<char, OptionData> ShortOptions; + + +struct _Option +{ + enum Result + { + OK, + ParsingError, + OptionNotFound, + BadType, + NoArgs, + TooManyArgs, + OptionNotFound_NoEx + }; + + virtual Result operator()(ShortOptions& short_ops, LongOptions& long_ops, Token* first, std::ios::fmtflags flags) const = 0; + virtual ~_Option() {} + + static const char NO_SHORT_OPT = 0; +protected: + static void setTokenAsUsed(Token* token, ShortOptions& short_ops, Token::Type usedAs) + { + if (token->type == Token::PossibleNegativeArgument) + short_ops.erase(token->value[1]); + + token->type = usedAs; + } +}; + +template <class T> inline _Option::Result convert(const std::string& s, T& result, std::ios::fmtflags flags) +{ + std::stringstream ss; + ss.clear(); + ss.flags(flags); + ss << s; + ss >> result; + if (ss.fail() || !ss.eof()) + return _Option::BadType; + else + return _Option::OK; +} + +template <> inline _Option::Result convert<std::string>(const std::string& s, std::string& result, std::ios::fmtflags /*flags*/) +{ + result = s; + return _Option::OK; +} + + +template <class T> class _OptionTBase : public _Option +{ + const char short_opt; + const std::string long_opt; +protected: + T& target; + virtual Result _assign(Token* token, std::ios::fmtflags flags, ShortOptions& short_ops) const = 0; + +public: + _OptionTBase(const _OptionTBase<T>& other) + : _Option(), short_opt(other.short_opt), long_opt(other.long_opt), target(other.target) + {} + + _OptionTBase(char short_opt, const std::string& long_opt, T& target) + : short_opt(short_opt), long_opt(long_opt), target(target) + {} + + virtual Result operator()(ShortOptions& short_ops, LongOptions& long_ops, Token* /*first*/, std::ios::fmtflags flags) const + { + Result ret = OptionNotFound; + ShortOptions::iterator it; + if (short_opt == _Option::NO_SHORT_OPT) + it = short_ops.end(); + else + it = short_ops.find(short_opt); + + if (it != short_ops.end()) + { + it->second.flags = OptionData::CmdLine_Extracted; + ret = _assign(it->second.token, flags, short_ops); + } + else if (!long_opt.empty()) + { + LongOptions::iterator it = long_ops.find(long_opt); + if (it != long_ops.end()) + { + it->second.flags = OptionData::CmdLine_Extracted; + ret = _assign(it->second.token, flags, short_ops); + } + } + + return ret; + } +}; + + +template <class T> class _OptionT : public _OptionTBase<T> +{ +protected: + virtual _Option::Result _assign(Token* token, std::ios::fmtflags flags, ShortOptions& short_ops) const + { + Token* const option_token = token->get_next_option_argument(); + if (option_token == NULL) + return _Option::NoArgs; + else + { + this->setTokenAsUsed(option_token, short_ops, Token::OptionArgument); + return convert<T>(option_token->value, this->target, flags); + } + } +public: + _OptionT(const _OptionT<T>& other) + : _OptionTBase<T>(other) + {} + + _OptionT(char short_opt, const std::string& long_opt, T& target) + : _OptionTBase<T>(short_opt, long_opt, target) + {} + +}; + +template <class T> class _OptionT<std::vector<T> > : public _OptionTBase<std::vector<T> > +{ +protected: + virtual _Option::Result _assign(Token* token, std::ios::fmtflags flags, ShortOptions& short_ops) const + { + Token* option_token = token->get_next_option_argument(); + if (option_token != NULL) + { + _Option::Result result; + //OptionArgs::const_iterator it = args.begin(); + T temp; + + do + { + this->setTokenAsUsed(option_token, short_ops, Token::OptionArgument); + result = convert<T>(option_token->value, temp, flags); + if (result == _Option::OK) + this->target.push_back(temp); + + option_token = option_token->get_next_option_argument(); + } + while (option_token != NULL && result == _Option::OK); + + return result; + } + else + return _Option::NoArgs; + } + +public: + _OptionT(const _OptionT<std::vector<T> >& other) + : _OptionTBase<std::vector<T> >(other) + {} + + _OptionT(char short_opt, const std::string& long_opt, std::vector<T>& target) + : _OptionTBase<std::vector<T> >(short_opt, long_opt, target) + {} +}; + + +template <class T, class BaseOption> +class _DefValOption : public BaseOption +{ + const T default_value; +public: + + _DefValOption(const _DefValOption<T, BaseOption>& other) + : BaseOption(other), default_value(other.default_value) + {} + + _DefValOption(char short_opt, const std::string& long_opt, T& target, const T& default_value) + : BaseOption(short_opt, long_opt, target), default_value(default_value) + {} + + virtual _Option::Result operator()(ShortOptions& short_ops, LongOptions& long_ops, Token* first, std::ios::fmtflags flags) const + { + _Option::Result ret = BaseOption::operator()(short_ops, long_ops, first, flags); + + if (ret == _Option::OptionNotFound) + { + this->target = default_value; + ret = _Option::OK; + } + + return ret; + } +}; + +template <class T> +class _GlobalOption : public _Option +{ + T& target; + virtual Result operator()(ShortOptions& short_ops, LongOptions& long_ops, Token* first, std::ios::fmtflags flags) const + { + // find first token GlobalArgument or UnknownYet (candidate) or PossibleNegativeArgument (candidate too) + Token* token(first); + bool found(false); + while (token != NULL && !found) + { + found = (token->type == Token::GlobalArgument || token->type == Token::UnknownYet || token->type == Token::PossibleNegativeArgument); + if (!found) + token = token->next; + } + if (found) + { + this->setTokenAsUsed(token, short_ops, Token::GlobalArgumentUsed); + return convert<T>(token->value, target, flags); + } + else + return OptionNotFound; + } +public: + _GlobalOption(const _GlobalOption<T>& other) + : target(other.target) + {} + + _GlobalOption(T& target) + : target(target) + {} +}; + +template <class T> +class _GlobalOption<std::vector<T> > : public _Option +{ + std::vector<T>& target; + virtual Result operator()(ShortOptions& short_ops, LongOptions& /*long_ops*/, Token* first, std::ios::fmtflags flags) const + { + // find first token GlobalArgument or UnknownYet (candidate) or PossibleNegativeArgument (candidate too) + Token* token(first); + bool found_any(false); + T tmp; + Result res(OK); + + while (token != NULL && res == OK) + { + if (token->type == Token::GlobalArgument || token->type == Token::UnknownYet || token->type == Token::PossibleNegativeArgument) + { + res = convert<T>(token->value, tmp, flags); + if (res == OK) + { + this->setTokenAsUsed(token, short_ops, Token::GlobalArgumentUsed); + found_any = true; + target.push_back(tmp); + } + } + token = token->next; + } + if (res == OK) + { + if (found_any) + return res; + else + return OptionNotFound; + } + else + return res; + } +public: + _GlobalOption(const _GlobalOption<std::vector<T> >& other) + : target(other.target) + {} + + _GlobalOption(std::vector<T>& target) + : target(target) + {} +}; + +template <class T> +inline _OptionT<T> Option(char short_opt, const std::string& long_opt, T& target) +{ + return _OptionT<T>(short_opt, long_opt, target); +} + +template <class T> +inline _OptionT<T> Option(char short_opt, T& target) +{ + return _OptionT<T>(short_opt, std::string(), target); +} + +// LongOpt only +template <class T> +inline _OptionT<T> Option(const std::string& long_opt, T& target) +{ + return _OptionT<T>(_Option::NO_SHORT_OPT, long_opt, target); +} + +// Defaulted version +template <class T> +inline _DefValOption<T, _OptionT<T> > +Option(char short_opt, const std::string& long_opt, T& target, const T& def) +{ + return _DefValOption<T, _OptionT<T> >(short_opt, long_opt, target, def); +} + +template <class T> +inline _DefValOption<T, _OptionT<T> > Option(char short_opt, T& target, const T& def) +{ + return _DefValOption<T, _OptionT<T> >(short_opt, std::string(), target, def); +} + +// no short opt. +template <class T> +inline _DefValOption<T, _OptionT<T> > +Option(const std::string& long_opt, T& target, const T& def) +{ + return _DefValOption<T, _OptionT<T> >(_Option::NO_SHORT_OPT, long_opt, target, def); +} + +// Defaults for strings: +inline _DefValOption<std::string, _OptionT<std::string> > +Option(char short_opt, const std::string& long_opt, std::string& target, const char* def) +{ + return _DefValOption<std::string, _OptionT<std::string> >(short_opt, long_opt, target, def); +} + +inline _OptionT<std::string> Option(char short_opt, std::string& target, const char* def) +{ + return _DefValOption<std::string, _OptionT<std::string> >(short_opt, std::string(), target, def); +} + +// no short opt. +inline _DefValOption<std::string, _OptionT<std::string> > +Option(const std::string& long_opt, std::string& target, const char* def) +{ + return _DefValOption<std::string, _OptionT<std::string> >(_Option::NO_SHORT_OPT, long_opt, target, def); +} + +// Global Option: +template <class T> +inline _GlobalOption<T> GlobalOption(T& target) +{ + return _GlobalOption<T>(target); +} + +class OptionPresent : public _Option +{ + const char short_opt; + const std::string long_opt; + bool* const present; +public: + // two combinations: with/without target, and with/without long opt. + + // WITH long_opt: + OptionPresent(char short_opt, const std::string& long_opt, bool& present) + : short_opt(short_opt), long_opt(long_opt), present(&present) + {} + + OptionPresent(char short_opt, const std::string& long_opt) + : short_opt(short_opt), long_opt(long_opt), present(NULL) + {} + + // WITHOUT long_opt: + OptionPresent(char short_opt, bool& present) + : short_opt(short_opt), present(&present) + {} + + OptionPresent(char short_opt) + : short_opt(short_opt), present(NULL) + {} + + // WITHOUT short_opt + OptionPresent(const std::string& long_opt, bool& present) + : short_opt(_Option::NO_SHORT_OPT), long_opt(long_opt), present(&present) + {} + + OptionPresent(const std::string& long_opt) + : short_opt(_Option::NO_SHORT_OPT), long_opt(long_opt), present(NULL) + {} +protected: + virtual Result operator()(ShortOptions& short_ops, LongOptions& long_ops, Token* /*first*/, std::ios::fmtflags /*flags*/) const + { + bool found; + ShortOptions::iterator it = short_ops.find(short_opt); + + found = (it != short_ops.end()); + if (found) + { + it->second.flags = OptionData::CmdLine_Extracted; + } + else if (!long_opt.empty()) + { + LongOptions::iterator it = long_ops.find(long_opt); + found = (it != long_ops.end()); + if (found) + { + it->second.flags = OptionData::CmdLine_Extracted; + } + } + + if (present != NULL) + { + *present = found; + return OK; + } + else + { + return found ? OK : OptionNotFound_NoEx; + } + } +}; + +class GetOptEx : public std::exception {}; +struct ParsingErrorEx : GetOptEx {}; +struct InvalidFormatEx : GetOptEx {}; +struct ArgumentNotFoundEx : GetOptEx {}; +struct TooManyArgumentsEx : GetOptEx {}; +struct OptionNotFoundEx : GetOptEx {}; +struct TooManyOptionsEx : GetOptEx {}; +struct OptionsFileNotFoundEx : GetOptEx +{ + const std::string targetFile; + OptionsFileNotFoundEx(const std::string& file) : targetFile(file) {} + ~OptionsFileNotFoundEx() throw() {} +}; + +enum _EnvTag +{ + Include_Environment +}; + +class GetOpt_pp +{ + ShortOptions _shortOps; + LongOptions _longOps; + std::ios_base::iostate _exc; + _Option::Result _last; + std::ios::fmtflags _flags; + std::string _app_name; + Token* _first_token; + Token* _last_token; + + class TokensDeleter + { + Token*& _first; + public: + TokensDeleter(Token*& first) : _first(first) {} + + GETOPT_INLINE ~TokensDeleter(); + }; + + TokensDeleter _tokens_deleter; + + GETOPT_INLINE Token* _add_token(const std::string& value, Token::Type type); + GETOPT_INLINE void _init_flags(); + GETOPT_INLINE void _parse(const std::vector<std::string>& args); + GETOPT_INLINE void _parse_env(); + static GETOPT_INLINE void _argc_argv_to_vector(int argc, const char* const* const argv, std::vector<std::string>& args); + GETOPT_INLINE void _parse_sub_file(const std::string& file); +public: + GETOPT_INLINE GetOpt_pp(int argc, const char* const* const argv); + GETOPT_INLINE GetOpt_pp(int argc, const char* const* const argv, _EnvTag); + + std::ios_base::iostate exceptions() const + { + return _exc; + } + void exceptions(std::ios_base::iostate except) + { + _exc = except; + } + void exceptions_all() + { + _exc = std::ios_base::failbit | std::ios_base::eofbit; + } + + operator bool() const + { + return _last == _Option::OK; + } + + GETOPT_INLINE bool options_remain() const; + + void end_of_options() const + { + if (options_remain() && (_exc & std::ios_base::eofbit)) + throw TooManyOptionsEx(); + } + + std::ios::fmtflags flags() const + { + return _flags; + } + void flags(std::ios::fmtflags flags) + { + _flags = flags; + } + + const std::string& app_name() const + { + return _app_name; + } + + GETOPT_INLINE GetOpt_pp& operator >> (const _Option& opt); + + GETOPT_INLINE GetOpt_pp& operator >> (std::ios_base& (*iomanip)(std::ios_base&)); + + // Alternative to manipulators, for those who don't like them: the 'getopt' method :) + // Combination 1: with long option: + template <class T> inline T getopt(char short_opt, const std::string& long_opt) + { + T result; + operator >> (Option(short_opt, long_opt, result)); + return result; + } + + template <class T> inline T getopt(char short_opt, const std::string& long_opt, const T& def_value) + { + T result; + operator >> (Option(short_opt, long_opt, result, def_value)); + return result; + } + + // Combination 2: without long option: + template <class T> inline T getopt(char short_opt) + { + T result; + operator >> (Option(short_opt, result)); + return result; + } + + template <class T> inline T getopt(char short_opt, const T& def_value) + { + T result; + operator >> (Option(short_opt, result, def_value)); + return result; + } + + struct ItCtorData + { + ShortOptions::const_iterator short_iter; + LongOptions::const_iterator long_iter; + GetOpt_pp* getopt_pp; + }; + + template <class Container, class Adapter, class OptionType> + class _iterator + { + typename Container::const_iterator _it; + GetOpt_pp* _getopt_pp; + public: + _iterator(const ItCtorData& ctor_data) + { + _it = Adapter::adapt(ctor_data); + _getopt_pp = ctor_data.getopt_pp; + } + + _iterator() : _getopt_pp(NULL) + {} + + _iterator<Container, Adapter, OptionType>& operator = (const _iterator<Container, Adapter, OptionType>& other) + { + _it = other._it; + _getopt_pp = other._getopt_pp; + return *this; + } + + bool operator != (const _iterator<Container, Adapter, OptionType>& other) const + { + return _it != other._it; + } + + OptionType option() const + { + return _it->first; + } + OptionType operator*() const + { + return option(); + } + + _iterator<Container, Adapter, OptionType>& operator ++() + { + ++_it; + return *this; + } + + template <class T> + GetOpt_pp& operator >> (T& t) + { + Adapter::extract(t, *_getopt_pp, option()); + return *_getopt_pp; + } + }; + + ItCtorData begin() + { + ItCtorData ret; + ret.short_iter = _shortOps.begin(); + ret.long_iter = _longOps.begin(); + ret.getopt_pp = this; + return ret; + } + + ItCtorData end() + { + ItCtorData ret; + ret.short_iter = _shortOps.end(); + ret.long_iter = _longOps.end(); + ret.getopt_pp = this; + return ret; + } + + struct ShortAdapter + { + static ShortOptions::const_iterator adapt(const ItCtorData& data) + { + return data.short_iter; + } + + template <class T> + static void extract(T& t, GetOpt_pp& getopt_pp, char option) + { + getopt_pp >> Option(option, t); + } + }; + + struct LongAdapter + { + static LongOptions::const_iterator adapt(const ItCtorData& data) + { + return data.long_iter; + } + + template <class T> + static void extract(T& t, GetOpt_pp& getopt_pp, const std::string& option) + { + getopt_pp >> Option('\0', option, t); + } + }; + + typedef _iterator<ShortOptions, ShortAdapter, char> short_iterator; + typedef _iterator<LongOptions, LongAdapter, const std::string&> long_iterator; +}; + +class Environment +{ + // Coming soon! +}; + +} + +#endif diff --git a/tools/img2sprite.py b/tools/img2sprite.py new file mode 100755 index 0000000..daa1a22 --- /dev/null +++ b/tools/img2sprite.py @@ -0,0 +1,199 @@ +#!/usr/bin/env python3 + +__version__ = "1.0" + +import sys +from argparse import ArgumentParser +from PIL import Image + +DEF_W = 8 +DEF_H = 16 + + +def encode_byte(a, b): + return ( + (a & 1) << 7 + | (b & 1) << 6 + | (a & 4) << 3 + | (b & 4) << 2 + | (a & 2) << 2 + | (b & 2) << 1 + | (a & 8) >> 2 + | (b & 8) >> 3 + ) + + +def main(): + + parser = ArgumentParser( + description="Image to CPC tile/image", + epilog="Copyright (C) 2020-2023 Juan J Martinez <jjm@usebox.net>", + ) + + parser.add_argument( + "--version", action="version", version="%(prog)s " + __version__ + ) + parser.add_argument( + "-i", + "--id", + dest="id", + default="sprite", + type=str, + help="variable name (default: sprite)", + ) + parser.add_argument( + "--width", + dest="w", + default=DEF_W, + type=int, + help="sprite width (default: %s)" % DEF_W, + ) + parser.add_argument( + "--height", + dest="h", + default=DEF_H, + type=int, + help="sprite height (default: %s)" % DEF_H, + ) + parser.add_argument( + "--transparent-color", + dest="tc", + default=None, + type=int, + help="palette index for the transparent color (default: None)", + ) + parser.add_argument( + "-b", + "--binary", + dest="binary", + action="store_true", + help="output binary instead of C code", + ) + parser.add_argument( + "-f", + "--frames", + dest="frames", + type=str, + default=None, + help="colon separated frame list to generate a frame map (default: no frame map)", + ) + + parser.add_argument("image", help="image to convert", nargs="?") + + args = parser.parse_args() + + if args.frames: + try: + frame_map = list(map(int, args.frames.split(":"))) + except Exception as ex: + parser.error("Failed to parse frame map list: %s" % ex) + else: + frame_map = None + + if not args.image: + parser.error("required parameter: image") + + if args.tc: + try: + args.tc = int(args.tc) + if args.tc < 0 or args.tc > 15: + raise ValueError() + except ValueError: + parser.error("--transparent-color expects an integer in [0, 15]") + + try: + image = Image.open(args.image) + except IOError: + parser.error("failed to open the image") + + if image.mode != "P": + parser.error("not an indexed image (no palette)") + + (w, h) = image.size + + if w % args.w or h % args.h: + parser.error("%s size is not multiple of the image size" % args.image) + + data = image.getdata() + + out = [] + for base_y in range(0, h, args.h): + for x in range(0, w, args.w): + frame = [] + for y in range(base_y, base_y + args.h): + for i in range(0, args.w, 2): + a = data[x + i + (y * w)] + b = data[x + i + 1 + (y * w)] + + if args.tc is not None: + mask_a = mask_b = 0 + if a == args.tc: + mask_a = 0xF + a = 0 + if b == args.tc: + mask_b = 0xF + b = 0 + frame.append(encode_byte(mask_a, mask_b)) + + frame.append(encode_byte(a, b)) + out.append(frame) + + if args.binary: + for frame in out: + sys.stdout.buffer.write(bytearray(frame)) + return + + frames = len(out) + + print( + "/* %sx%s (frames: %s, mask: %s, frame_map: %s) */" + % (args.w, args.h, frames, args.tc is not None, frame_map) + ) + print("#ifdef LOCAL") + + if frame_map: + for i, block in enumerate(out): + print( + "const unsigned char %s_frame%d[%d] = { %s };" + % (args.id, i, len(block), ", ".join(["0x%02x" % b for b in block])) + ) + + print( + "const unsigned char * const %s_frames[%d] = { %s };" + % ( + args.id, + len(frame_map), + ", ".join(["%s_frame%d" % (args.id, f) for f in frame_map]), + ) + ) + else: + data_out = "" + for block in out: + if data_out: + data_out += ",\n" + data_out += "{" + for part in range(0, len(block), 4): + if data_out and data_out[-1] != "{": + data_out += ",\n" + data_out += ", ".join(["0x%02x" % b for b in block[part : part + 4]]) + data_out += "}" + print( + "const unsigned char %s[%d][%d] = {\n%s\n};\n" + % (args.id, len(out), len(out[0]), data_out) + ) + print("#else") + if frame_map: + print( + "extern const unsigned char * const %s_frames[%d];" + % (args.id, len(frame_map)) + ) + else: + print( + "extern const unsigned char %s[%d][%d];\n" + % (args.id, len(out), len(out[0])) + ) + print("#endif") + + +if __name__ == "__main__": + main() diff --git a/tools/map.py b/tools/map.py new file mode 100755 index 0000000..5e51330 --- /dev/null +++ b/tools/map.py @@ -0,0 +1,460 @@ +#!/usr/bin/env python3 + +import os +import sys +from argparse import ArgumentParser +import json +import subprocess +import tempfile +from collections import defaultdict +from math import ceil +import traceback + +__version__ = "1.0" + +DEF_ROOM_WIDTH = 20 +DEF_ROOM_HEIGHT = 20 +DEF_TS_SIZE = 16 +DEF_BITS = 4 + +# entity types +# unused shouldn't be used :) +TYPES = ("unused", "fill", "link", "door", "platform", "spirit", + "flame", "vampire", "oni", "ninja", "spider", "demon", "cloud", + "torch", "switch", "gem", "key", "potion", "gtail", "start", + "blocked", "sdoor",) + +# entity weight; for cases where the number of sprites is different than 1 (e.g. no sprite, or 2) +TYPE_W = {"unused": None, "fill": None, "link": None, "sdoor": None, + "demon": 2, "ninja": 2} +# persistent types +TYPES_P = ("door", "potion", "key", "gtail", "gem",) + +NO_ID = 255 + + +def aplib_compress(data): + with tempfile.NamedTemporaryFile() as fd: + fd.write(bytearray(data)) + fd.flush() + + ap_name = fd.name + ".ap" + subprocess.call(["apultra", fd.name, ap_name], stdout=sys.stderr) + + with open(ap_name, "rb") as fd: + out = fd.read() + os.unlink(ap_name) + return [int(byte) for byte in out] + + +def zx7_compress(data): + with tempfile.NamedTemporaryFile() as fd: + fd.write(bytearray(data)) + fd.flush() + + zx7_name = fd.name + ".zx7" + subprocess.call(["zx7", "-f", fd.name], stdout=sys.stderr) + + with open(zx7_name, "rb") as fd: + out = fd.read() + os.unlink(zx7_name) + return [int(byte) for byte in out] + + +def ucl_compress(data): + p = subprocess.Popen( + ["ucl", ], stdin=subprocess.PIPE, stdout=subprocess.PIPE) + output, err = p.communicate(bytearray(data)) + return [int(byte) for byte in output] + + +def find_name(data, name): + for item in data: + if item.get("name").lower() == name.lower(): + return item + raise ValueError("%r not found" % name) + + +def find_id(data, id): + for item in data: + if item.get("id") == id: + return item + raise ValueError("id %r not found", id) + + +def get_property(obj, name, default): + props = obj.get("properties", {}) + + if isinstance(props, dict): + return props.get(name, default) + + for p in props: + if p["name"] == name: + return p["value"] + + return default + + +def main(): + + parser = ArgumentParser(description="Map importer", + epilog="Copyright (C) 2020 Juan J Martinez <jjm@usebox.net>", + ) + + parser.add_argument( + "--version", action="version", version="%(prog)s " + __version__) + parser.add_argument( + "--room-width", dest="rw", default=DEF_ROOM_WIDTH, type=int, + help="room width (default: %s)" % DEF_ROOM_WIDTH) + parser.add_argument( + "--room-height", dest="rh", default=DEF_ROOM_HEIGHT, type=int, + help="room height (default: %s)" % DEF_ROOM_HEIGHT) + parser.add_argument("--ts-size", dest="tsz", default=DEF_TS_SIZE, type=int, + help="tileset size (default: %s)" % DEF_TS_SIZE) + parser.add_argument("--max-ents", dest="max_ents", default=0, type=int, + help="max entities per room (default: unlimited)") + parser.add_argument("-b", dest="bin", action="store_true", + help="output binary data (default: C code)") + parser.add_argument("--ucl", dest="ucl", action="store_true", + help="UCL compressed") + parser.add_argument("--zx7", dest="zx7", action="store_true", + help="ZX7 compressed") + parser.add_argument("--aplib", dest="aplib", action="store_true", + help="APLIB compressed") + parser.add_argument("-q", dest="quiet", action="store_true", + help="Don't output stats on stderr") + parser.add_argument("map_json", help="Map to import") + parser.add_argument("id", help="variable name") + + args = parser.parse_args() + + if [args.ucl, args.zx7, args.aplib].count(True) > 1: + parser.error( + "Can't use more than one compression type at the same time") + + if args.ucl: + compress = ucl_compress + compressor = "UCL" + elif args.zx7: + compress = zx7_compress + compressor = "ZX7" + elif args.aplib: + compress = aplib_compress + compressor = "APLIB" + else: + compress = None + compressor = "none" + + with open(args.map_json, "rt") as fd: + data = json.load(fd) + + mh = data.get("height", 0) + mw = data.get("width", 0) + + if mh < args.rh or mh % args.rh: + parser.error("Map size height not multiple of the room size") + if mw < args.rw or mw % args.rw: + parser.error("Map size witdh not multiple of the room size") + + tilewidth = data["tilewidth"] + tileheight = data["tileheight"] + + tile_layer = find_name(data["layers"], "Map")["data"] + + def_tileset = find_name(data["tilesets"], "default") + firstgid = def_tileset.get("firstgid") + + tilesets = [] + out = [] + for y in range(0, mh, args.rh): + for x in range(0, mw, args.rw): + block = [] + ts = None + for j in range(args.rh): + for i in range(args.rw): + block.append(tile_layer[x + i + (y + j) * mw] - firstgid) + + # pack + current = [] + for i in range(0, args.rh * args.rw, 8 // DEF_BITS): + tiles = [] + for k in range(8 // DEF_BITS): + tiles.append(block[i + k]) + + # store the tileset + if ts is None: + ts = tiles[0] // args.tsz + if ts > 15: + parser.error("Too many tilesets (%d)" % (ts + 1)) + + # don't allow mixed tilesets + for k in range(8 // DEF_BITS): + if tiles[k] // args.tsz != ts: + parser.error("Mixed tilesets in map %d" % len(out)) + + # correct the tile as it was 1st tileset + if tiles[0] > args.tsz - 1: + for k in range(8 // DEF_BITS): + tiles[k] %= args.tsz + + b = 0 + pos = 8 + for k in range(8 // DEF_BITS): + pos -= DEF_BITS + b |= (tiles[k] & ((2**DEF_BITS) - 1)) << pos + + current.append(b) + + tilesets.append(ts) + out.append(current) + + # track empty maps + empty = [] + for i, block in enumerate(out): + if all([byte == 0xff for byte in block]): + empty.append(i) + + if compress: + compressed = [] + for i, block in enumerate(out): + if i in empty: + compressed.append(None) + continue + compressed.append(compress(block)) + out = compressed + + # add the map header + for i in range(len(out)): + if out[i] is None: + continue + size = len(out[i]) + if size > 255: + # unlikely + parser.error("map %i size is larger than 255" % i) + + out[i] = [size, tilesets[i], ] + out[i] + + entities_layer = find_name(data["layers"], "Entities") + if len(entities_layer): + start = None + map_ents = defaultdict(list) + map_ents_w = defaultdict(int) + pid_cnt = 0 + + gems = 0 + + try: + objs = sorted( + entities_layer["objects"], key=lambda o: TYPES.index(o["name"].lower()), reverse=True) + except ValueError: + parser.error("map has an unnamed object") + for obj in objs: + name = obj["name"].lower() + m = ((obj["x"] // tilewidth) // args.rw) \ + + (((obj["y"] // tileheight) // args.rh) * (mw // args.rw)) + x = obj["x"] % (args.rw * tilewidth) + y = obj["y"] % (args.rh * tileheight) + + if name == "start": + start = (m, x, y) + continue + + if name == "gem": + gems += 1 + + if name == "blocked": + if obj["width"] > obj["height"]: + if y == 0: + # up blocked + out[m][1] |= (1 << 4) + else: + # down blocked + out[m][1] |= (1 << 5) + else: + if x == 0: + # left blocked + out[m][1] |= (1 << 6) + else: + # tight blocked + out[m][1] |= (1 << 7) + continue + + t = TYPES.index(name) + + if name in TYPES_P: + pid = pid_cnt + pid_cnt += 1 + else: + pid = NO_ID + + # MSB is direction + + param = int(get_property(obj, "param", 0)) + if param == 1: + t |= 128 + + if args.max_ents: + # update the entity count per map + try: + if TYPE_W[name] is not None: + map_ents_w[m] += TYPE_W[name] + except KeyError: + # no entry, assume 1 + map_ents_w[m] += 1 + + special = None + + # specials + if name == "fill": + # tile to fill with + pid = get_property(obj, "tile", 0) + # fill length + special = obj["width"] // tilewidth + + if get_property(obj, "fixed", None) is not None: + if obj["width"] >= obj["height"]: + special = obj["width"] - tilewidth + if not param: + x += special + else: + special = obj["height"] - tileheight + if not param: + y += special + + if name == "link": + target_id = get_property(obj, "target", None) + if target_id is None: + parser.error("link object (%s) with no target" % obj["id"]) + + target = find_id(objs, target_id) + back = get_property(target, "target", None) + if back != obj["id"]: + parser.error( + "link object (%s) link back is missing" % obj["id"]) + + # if not in first line, must be down + if y != 0: + t |= 128 + else: + t &= 127 + + # pid will target the map + pid = ((target["x"] // tilewidth) // args.rw) \ + + (((target["y"] // tileheight) // args.rh) + * (mw // args.rw)) + + target_x = target["x"] % (args.rw * tilewidth) + + # only up/down supported + # x will have any offset, y unused + y = 0 + x = (target_x - x) & 0xff + + if name == "switch": + target_id = get_property(obj, "target", None) + if target_id is None: + parser.error("link object (%s) with no target" % obj["id"]) + + target = find_id(objs, target_id) + + # not persistent + pid = 0xff + target_x = target["x"] % (args.rw * tilewidth) + target_y = target["y"] % (args.rh * tileheight) + special = (target_x, target_y) + + # managed by the switch + if name == "sdoor": + continue + + map_ents[m].extend([t, pid, x, y]) + if special is not None: + if isinstance(special, (tuple, list)): + map_ents[m].extend(special) + else: + map_ents[m].append(special) + + if args.max_ents: + for i, weight in map_ents_w.items(): + if weight > args.max_ents: + parser.error("map %i has %d entities, max is %d" % + (i, weight, args.max_ents)) + + # append the entities to the map data + for i in range(len(out)): + if not out[i]: + continue + elif map_ents[i]: + out[i].extend(map_ents[i]) + # terminator + out[i].append(0xff) + + if start is None: + parser.error("No start found") + + if args.bin: + # untested; to be used loading from disc + sys.stdout.write(bytearray(out)) + return + + print("#ifndef _%s_H" % args.id.upper()) + print("#define _%s_H" % args.id.upper()) + print("/* compressed: %s */" % compressor) + print("#define PERSISTENCE_LEN %d\n" % max(ceil(pid_cnt / 8.), 1)) + print("#define WMAPS %d\n" % (mw // args.rw)) + print("#define MAPS %d\n" % len(out)) + + if start is not None: + m, x, y = start + print("#define START_MAP %d" % m) + print("#define START_X %d" % x) + print("#define START_Y %d" % y) + + print("#ifdef LOCAL") + + # includes a map table for fast access + data_out = "" + for i, block in enumerate(out): + if not isinstance(block, list): + continue + data_out_part = "" + for part in range(0, len(block), args.rw // 2): + if data_out_part: + data_out_part += ",\n" + data_out_part += ', '.join( + ["0x%02x" % byte for byte in block[part: part + args.rw // 2]]) + data_out += "const unsigned char %s_%d[%d] = {\n" % ( + args.id, i, len(block)) + data_out += data_out_part + "\n};\n" + + data_out += "const unsigned char * const %s[%d] = { " % (args.id, len(out)) + data_out += ', '.join( + ["%s_%d" % (args.id, + i) if i not in empty else "(unsigned char *)0" for i in range(len(out))]) + data_out += " };\n" + print(data_out) + print("unsigned char persistence[PERSISTENCE_LEN];\n") + + print("#else") + print("extern const unsigned char * const %s[%d];\n" % (args.id, len(out))) + print("extern unsigned char persistence[PERSISTENCE_LEN];\n") + + print("#endif // LOCAL") + print("#endif // _%s_H" % args.id.upper()) + + if not args.quiet: + screen_with_data = len(out) - len(empty) + total_bytes = sum(len(b) if b else 0 for b in out) + print("%s: %s (%d screens, %d bytes, %.2f bytes avg) - %d gems" % ( + os.path.basename(sys.argv[0]), args.id, + screen_with_data, total_bytes, total_bytes / screen_with_data, + gems), file=sys.stderr) + + +if __name__ == "__main__": + try: + main() + except Exception as ex: + print("FATAL: %s\n***" % ex, file=sys.stderr) + traceback.print_exc(ex) + sys.exit(1) diff --git a/tools/rasm_src/Makefile b/tools/rasm_src/Makefile new file mode 100644 index 0000000..5d3ee4e --- /dev/null +++ b/tools/rasm_src/Makefile @@ -0,0 +1,11 @@ +all: rasm + +rasm: rasm_v0119.c + gcc -s -O2 $< -o $@ -lm -lrt -march=native + cp rasm .. + +clean: + rm -f rasm + +.PHONY: all clean + diff --git a/tools/rasm_src/decrunch/aplib_z80_todo.asm b/tools/rasm_src/decrunch/aplib_z80_todo.asm new file mode 100644 index 0000000..6843a14 --- /dev/null +++ b/tools/rasm_src/decrunch/aplib_z80_todo.asm @@ -0,0 +1,190 @@ +;Z80 Version by Dan Weiss +;Call depack. +;hl = source +;de = dest + +ap_bits: .db 0 +ap_byte: .db 0 +lwm: .db 0 +r0: .dw 0 + +ap_getbit: + push bc + ld bc,(ap_bits) + rrc c + jr nc,ap_getbit_continue + ld b,(hl) + inc hl +ap_getbit_continue: + ld a,c + and b + ld (ap_bits),bc + pop bc + ret + +ap_getbitbc: ;doubles BC and adds the read bit + sla c + rl b + call ap_getbit + ret z + inc bc + ret + +ap_getgamma: + ld bc,1 +ap_getgammaloop: + call ap_getbitbc + call ap_getbit + jr nz,ap_getgammaloop + ret + + +depack: + ;hl = source + ;de = dest + ldi + xor a + ld (lwm),a + inc a + ld (ap_bits),a + +aploop: + call ap_getbit + jp z, apbranch1 + call ap_getbit + jr z, apbranch2 + call ap_getbit + jr z, apbranch3 + ;LWM = 0 + xor a + ld (lwm),a + ;get an offset + ld bc,0 + call ap_getbitbc + call ap_getbitbc + call ap_getbitbc + call ap_getbitbc + ld a,b + or c + jr nz,apbranch4 + xor a ;write a 0 + ld (de),a + inc de + jr aploop +apbranch4: + ex de,hl ;write a previous bit (1-15 away from dest) + push hl + sbc hl,bc + ld a,(hl) + pop hl + ld (hl),a + inc hl + ex de,hl + jr aploop +apbranch3: + ;use 7 bit offset, length = 2 or 3 + ;if a zero is encountered here, it's EOF + ld c,(hl) + inc hl + rr c + ret z + ld b,2 + jr nc,ap_dont_inc_b + inc b +ap_dont_inc_b: + ;LWM = 1 + ld a,1 + ld (lwm),a + + push hl + ld a,b + ld b,0 + ;R0 = c + ld (r0),bc + ld h,d + ld l,e + or a + sbc hl,bc + ld c,a + ldir + pop hl + jr aploop +apbranch2: + ;use a gamma code * 256 for offset, another gamma code for length + call ap_getgamma + dec bc + dec bc + ld a,(lwm) + or a + jr nz,ap_not_lwm + ;bc = 2? + ld a,b + or c + jr nz,ap_not_zero_gamma + ;if gamma code is 2, use old r0 offset, and a new gamma code for length + call ap_getgamma + push hl + ld h,d + ld l,e + push bc + ld bc,(r0) + sbc hl,bc + pop bc + ldir + pop hl + jr ap_finishup + +ap_not_zero_gamma: + dec bc +ap_not_lwm: + ;do I even need this code? + ;bc=bc*256+(hl), lazy 16bit way + ld b,c + ld c,(hl) + inc hl + ld (r0),bc + push bc + call ap_getgamma + ex (sp),hl + ;bc = len, hl=offs + push de + ex de,hl + ;some comparison junk for some reason + ld hl,31999 + or a + sbc hl,de + jr nc,skip1 + inc bc +skip1: + ld hl,1279 + or a + sbc hl,de + jr nc,skip2 + inc bc +skip2: + ld hl,127 + or a + sbc hl,de + jr c,skip3 + inc bc + inc bc +skip3: + ;bc = len, de = offs, hl=junk + pop hl + push hl + or a + sbc hl,de + pop de + ;hl=dest-offs, bc=len, de = dest + ldir + pop hl +ap_finishup: + ld a,1 + ld (lwm),a + jp aploop + +apbranch1: + ldi + xor a + ld (lwm),a + jp aploop diff --git a/tools/rasm_src/decrunch/deexo.asm b/tools/rasm_src/decrunch/deexo.asm new file mode 100644 index 0000000..6a4a7ac --- /dev/null +++ b/tools/rasm_src/decrunch/deexo.asm @@ -0,0 +1,118 @@ +;Exomizer 2 Z80 decoder
+; by Metalbrain
+;
+; optimized by Antonio Villena and Urusergi (169 bytes)
+;
+; compression algorithm by Magnus Lind
+
+;input: hl=compressed data start
+; de=uncompressed destination start
+;
+; you may change exo_mapbasebits to point to any free buffer
+;
+;ATTENTION!
+;A huge speed boost (around 14%) can be gained at the cost of only 5 bytes.
+;If you want this, replace all instances of "call exo_getbit" with "srl a" followed by
+;"call z,exo_getbit", and remove the first two instructions in exo_getbit routine.
+
+Macro Mizoumizeur
+
+@deexo: ld iy, @exo_mapbasebits+11
+ ld a, (hl)
+ inc hl
+ ld b, 52
+ push de
+ cp a
+@exo_initbits: ld c, 16
+ jr nz, @exo_get4bits
+ ld ixl, c
+ ld de, 1 ;DE=b2
+@exo_get4bits: srl a: call z, @exo_getbit ;get one bit
+ rl c
+ jr nc, @exo_get4bits
+ inc c
+ push hl
+ ld hl, 1
+ ld (iy+41), c ;bits[i]=b1 (and opcode 41 == add hl,hl)
+@exo_setbit: dec c
+ jr nz, @exo_setbit-1 ;jump to add hl,hl instruction
+ ld (iy-11), e
+ ld (iy+93), d ;base[i]=b2
+ add hl, de
+ ex de, hl
+ inc iy
+ pop hl
+ dec ixl
+ djnz @exo_initbits
+ pop de
+ jr @exo_mainloop
+@exo_literalrun: ld e, c ;DE=1
+@exo_getbits: dec b
+ ret z
+@exo_getbits1: srl a : call z,@exo_getbit
+ rl e
+ rl d
+ jr nc, @exo_getbits
+ ld b, d
+ ld c, e
+ pop de
+@exo_literalcopy:ldir
+@exo_mainloop: inc c
+ srl a : call z,@exo_getbit ;literal?
+ jr c, @exo_literalcopy
+ ld c, 239
+@exo_getindex: srl a : call z,@exo_getbit
+ inc c
+ jr nc,@exo_getindex
+ ret z
+ push de
+ ld d, b
+ jp p, @exo_literalrun
+ ld iy, @exo_mapbasebits-229
+ call @exo_getpair
+ push de
+ rlc d
+ jr nz, @exo_dontgo
+ dec e
+ ld bc, 512+32 ;2 bits, 48 offset
+ jr z, @exo_goforit
+ dec e ;2?
+@exo_dontgo: ld bc, 1024+16 ;4 bits, 32 offset
+ jr z, @exo_goforit
+ ld de, 0
+ ld c, d ;16 offset
+@exo_goforit: call @exo_getbits1
+ ld iy, @exo_mapbasebits+27
+ add iy, de
+ call @exo_getpair
+ pop bc
+ ex (sp), hl
+ push hl
+ sbc hl, de
+ pop de
+ ldir
+ pop hl
+ jr @exo_mainloop ;Next!
+
+@exo_getpair: add iy, bc
+ ld e, d
+ ld b, (iy+41)
+ call @exo_getbits
+ ex de, hl
+ ld c, (iy-11)
+ ld b, (iy+93)
+ add hl, bc ;Always clear C flag
+ ex de, hl
+ ret
+
+@exo_getbit: ; srl a
+ ;ret nz
+ ld a, (hl)
+ inc hl
+ rra
+ ret
+
+@exo_mapbasebits: defs 156 ;tables for bits, baseL, baseH
+
+Mend
+
diff --git a/tools/rasm_src/decrunch/dzx7_turbo.asm b/tools/rasm_src/decrunch/dzx7_turbo.asm new file mode 100644 index 0000000..779ced5 --- /dev/null +++ b/tools/rasm_src/decrunch/dzx7_turbo.asm @@ -0,0 +1,80 @@ +; -----------------------------------------------------------------------------
+; ZX7 decoder by Einar Saukas & Urusergi
+; "Turbo" version (88 bytes, 25% faster)
+; -----------------------------------------------------------------------------
+; Parameters:
+; HL: source address (compressed data)
+; DE: destination address (decompressing)
+; -----------------------------------------------------------------------------
+
+dzx7_turbo:
+ ld a, $80
+dzx7t_copy_byte_loop:
+ ldi ; copy literal byte
+dzx7t_main_loop:
+ add a, a ; check next bit
+ call z, dzx7t_load_bits ; no more bits left?
+ jr nc, dzx7t_copy_byte_loop ; next bit indicates either literal or sequence
+
+; determine number of bits used for length (Elias gamma coding)
+ push de
+ ld bc, 1
+ ld d, b
+dzx7t_len_size_loop:
+ inc d
+ add a, a ; check next bit
+ call z, dzx7t_load_bits ; no more bits left?
+ jr nc, dzx7t_len_size_loop
+ jp dzx7t_len_value_start
+
+; determine length
+dzx7t_len_value_loop:
+ add a, a ; check next bit
+ call z, dzx7t_load_bits ; no more bits left?
+ rl c
+ rl b
+ jr c, dzx7t_exit ; check end marker
+dzx7t_len_value_start:
+ dec d
+ jr nz, dzx7t_len_value_loop
+ inc bc ; adjust length
+
+; determine offset
+ ld e, (hl) ; load offset flag (1 bit) + offset value (7 bits)
+ inc hl
+ defb $cb, $33 ; opcode for undocumented instruction "SLL E" aka "SLS E"
+ jr nc, dzx7t_offset_end ; if offset flag is set, load 4 extra bits
+ add a, a ; check next bit
+ call z, dzx7t_load_bits ; no more bits left?
+ rl d ; insert first bit into D
+ add a, a ; check next bit
+ call z, dzx7t_load_bits ; no more bits left?
+ rl d ; insert second bit into D
+ add a, a ; check next bit
+ call z, dzx7t_load_bits ; no more bits left?
+ rl d ; insert third bit into D
+ add a, a ; check next bit
+ call z, dzx7t_load_bits ; no more bits left?
+ ccf
+ jr c, dzx7t_offset_end
+ inc d ; equivalent to adding 128 to DE
+dzx7t_offset_end:
+ rr e ; insert inverted fourth bit into E
+
+; copy previous sequence
+ ex (sp), hl ; store source, restore destination
+ push hl ; store destination
+ sbc hl, de ; HL = destination - offset - 1
+ pop de ; DE = destination
+ ldir
+dzx7t_exit:
+ pop hl ; restore source address (compressed data)
+ jp nc, dzx7t_main_loop
+
+dzx7t_load_bits:
+ ld a, (hl) ; load another group of 8 bits
+ inc hl
+ rla
+ ret
+
+; -----------------------------------------------------------------------------
diff --git a/tools/rasm_src/decrunch/exomizer3megachur.asm b/tools/rasm_src/decrunch/exomizer3megachur.asm new file mode 100644 index 0000000..ea1973e --- /dev/null +++ b/tools/rasm_src/decrunch/exomizer3megachur.asm @@ -0,0 +1,210 @@ +;Exomizer 2 Z80 decoder
+;Copyright (C) 2008-2016 by Jaime Tejedor Gomez (Metalbrain)
+;
+;Optimized by Antonio Villena and Urusergi (169 bytes)
+;
+;Compression algorithm by Magnus Lind
+;
+; This depacker is free software; you can redistribute it and/or
+; modify it under the terms of the GNU Lesser General Public
+; License as published by the Free Software Foundation; either
+; version 2.1 of the License, or (at your option) any later version.
+;
+; This library 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
+; Lesser General Public License for more details.
+;
+; You should have received a copy of the GNU Lesser General Public
+; License along with this library; if not, write to the Free Software
+; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+;
+;
+;input- hl=compressed data start
+; de=uncompressed destination start
+;
+; you may change exo_mapbasebits to point to any free buffer
+;
+;ATTENTION!
+;A huge speed boost (around 14%) can be gained at the cost of only 5 bytes.
+;If you want this, replace all instances of "call exo_getbit" with "srl a" followed by
+;"call z,exo_getbit", and remove the first two instructions in exo_getbit routine.
+; ---------------------------
+; modified by Megachur in 2018
+; ---------------------------
+; hl -> compressed data start
+; de -> uncompressed destination start
+; ---------------------------
+
+;EXO_BACKWARD equ 1
+ENABLE_MEXO_GETBIT equ 1
+
+list:EXOMIZER_ADDRESS:nolist
+; ---------------------------
+MACRO MEXO_GETBIT
+ srl a
+ jr nz,@1
+ ld a,(hl)
+ IFDEF EXO_BACKWARD
+ dec hl
+ ELSE
+ inc hl
+ ENDIF
+ rra
+@1
+ENDM
+
+deexo:
+ ld iy,exo_mapbasebits+11
+ ld a,(hl)
+
+ IFDEF EXO_BACKWARD
+ dec hl
+ ELSE
+ inc hl
+ ENDIF
+
+ ld b,52
+ push de
+ cp a
+
+exo_initbits:
+ ld c,16
+ jr nz,exo_get4bits
+ ld ixl,c
+ ld de,1 ;DE=b2
+
+exo_get4bits:
+ IFDEF ENABLE_MEXO_GETBIT
+ MEXO_GETBIT
+ ELSE
+ srl a:call z,exo_getbit ;call exo_getbit ;get one bit
+ ENDIF
+ rl c
+ jr nc,exo_get4bits
+ inc c
+ push hl
+ ld hl,1
+ ld (iy+41),c ;bits[i]=b1 (and opcode 41 == add hl,hl)
+
+exo_setbit:
+ dec c
+ jr nz,exo_setbit-1 ;jump to add hl,hl instruction
+ ld (iy-11),e
+ ld (iy+93),d ;base[i]=b2
+ add hl,de
+ ex de,hl
+ inc iy
+ pop hl
+ dec ixl
+ djnz exo_initbits
+ pop de
+ jr exo_mainloop
+
+exo_literalrun:
+ ld e,c ;DE=1
+
+exo_getbits:
+ dec b
+ ret z
+
+exo_getbits1:
+ IFDEF ENABLE_MEXO_GETBIT
+ MEXO_GETBIT
+ ELSE
+ srl a:call z,exo_getbit ;call exo_getbit
+ ENDIF
+ rl e
+ rl d
+ jr nc,exo_getbits
+ ld b,d
+ ld c,e
+ pop de
+
+exo_literalcopy:
+ IFDEF EXO_BACKWARD
+ lddr
+ ELSE
+ ldir
+ ENDIF
+exo_mainloop:
+ inc c
+ IFDEF ENABLE_MEXO_GETBIT
+ MEXO_GETBIT
+ ELSE
+ srl a:call z,exo_getbit ;call exo_getbit ;literal?
+ ENDIF
+ jr c,exo_literalcopy
+ ld c,239
+exo_getindex:
+ IFDEF ENABLE_MEXO_GETBIT
+ MEXO_GETBIT
+ ELSE
+ srl a:call z,exo_getbit ;call exo_getbit
+ ENDIF
+ inc c
+ jr nc,exo_getindex
+ ret z
+ push de
+ ld d,b
+ jp p,exo_literalrun
+ ld iy,exo_mapbasebits-229
+ call exo_getpair
+ push de
+ rlc d
+ jr nz,exo_dontgo
+ dec e
+ ld bc,512+32 ;2 bits,48 offset
+ jr z,exo_goforit
+ dec e ;2?
+exo_dontgo:
+ ld bc,1024+16 ;4 bits,32 offset
+ jr z,exo_goforit
+ ld de,0
+ ld c,d ;16 offset
+exo_goforit:
+ call exo_getbits1
+ ld iy,exo_mapbasebits+27
+ add iy,de
+ call exo_getpair
+ pop bc
+ ex (sp),hl
+ IFDEF EXO_BACKWARD
+ ex de,hl
+ add hl,de
+ lddr
+ ELSE
+ push hl
+ sbc hl,de
+ pop de
+ ldir
+ ENDIF
+ pop hl
+ jr exo_mainloop ;Next!
+exo_getpair:
+ add iy,bc
+ ld e,d
+ ld b,(iy+41)
+ call exo_getbits
+ ex de,hl
+ ld c,(iy-11)
+ ld b,(iy+93)
+ add hl,bc ;Always clear C flag
+ ex de,hl
+ ret
+
+ IFDEF ENABLE_MEXO_GETBIT
+ ELSE
+exo_getbit:
+; srl a
+; ret nz
+ ld a,(hl)
+ inc hl
+ rra
+ ret
+ ENDIF
+
+exo_mapbasebits:
+ ds 156,#00 ;tables for bits,baseL,baseH
+; ---------------------------
+list:EXOMIZER_ADDRESS_LENGTH equ $-EXOMIZER_ADDRESS:nolist
\ No newline at end of file diff --git a/tools/rasm_src/decrunch/lz48decrunch_v006.asm b/tools/rasm_src/decrunch/lz48decrunch_v006.asm new file mode 100644 index 0000000..750b571 --- /dev/null +++ b/tools/rasm_src/decrunch/lz48decrunch_v006.asm @@ -0,0 +1,113 @@ +;
+; LZ48 decrunch
+;
+; hl compressed data adress
+; de output adress of data
+;
+
+
+org #8000
+
+; CALL #8000,source,destination
+di
+
+; parameters
+ld h,(ix+3)
+ld l,(ix+2)
+ld d,(ix+1)
+ld e,(ix+0)
+
+call LZ48_decrunch
+
+ei
+ret
+
+
+
+
+
+LZ48_decrunch
+ldi
+ld b,0
+
+nextsequence
+ld a,(hl)
+inc hl
+ld lx,a
+and #F0
+jr z,lzunpack ; no litteral bytes
+rrca
+rrca
+rrca
+rrca
+
+ld c,a
+cp 15 ; more bytes for length?
+jr nz,copyliteral
+
+getadditionallength
+ld a,(hl)
+inc hl
+inc a
+jr nz,lengthnext
+inc b
+dec bc
+jr getadditionallength
+lengthnext
+dec a
+add a,c
+ld c,a
+ld a,b
+adc a,0
+ld b,a ; bc=length
+
+copyliteral
+ldir
+
+lzunpack
+ld a,lx
+and #F
+add 3
+ld c,a
+cp 18 ; more bytes for length?
+jr nz,readoffset
+
+getadditionallengthbis
+ld a,(hl)
+inc hl
+inc a
+jr nz,lengthnextbis
+inc b
+dec bc
+jr getadditionallengthbis
+lengthnextbis
+dec a
+add a,c
+ld c,a
+ld a,b
+adc a,0
+ld b,a ; bc=length
+
+readoffset
+; read encoded offset
+ld a,(hl)
+inc a
+ret z ; LZ48 end with zero offset
+inc hl
+push hl
+ld l,a
+ld a,e
+sub l
+ld l,a
+ld a,d
+sbc a,0
+ld h,a
+; source=dest-copyoffset
+
+copykey
+ldir
+
+pop hl
+jr nextsequence
+
+
diff --git a/tools/rasm_src/decrunch/lz49decrunch_v001.asm b/tools/rasm_src/decrunch/lz49decrunch_v001.asm new file mode 100644 index 0000000..7f811b5 --- /dev/null +++ b/tools/rasm_src/decrunch/lz49decrunch_v001.asm @@ -0,0 +1,138 @@ +;
+; LZ48 decrunch
+; input
+; hl compressed data adress
+; de output adress of data
+;
+; output
+; hl last adress of compressed data read (you must inc once for LZ48 stream)
+; de last adress of decrunched data write +1
+; bc always 3
+; a always zero
+; lx undetermined
+; flags (inc a -> 0)
+
+org #8000
+
+; CALL #8000,source,destination
+di
+
+; parameters
+ld h,(ix+3)
+ld l,(ix+2)
+ld d,(ix+1)
+ld e,(ix+0)
+
+call LZ49_decrunch
+
+ei
+ret
+
+
+
+
+
+LZ49_decrunch
+ldi
+ld b,0
+
+nextsequence
+ld a,(hl)
+inc hl
+ld lx,a
+and #70
+jr z,lzunpack ; no litteral bytes
+rrca
+rrca
+rrca
+rrca
+
+ld c,a
+cp 7 ; more bytes for length?
+jr nz,copyliteral
+
+getadditionallength
+ld a,(hl)
+inc hl
+inc a
+jr nz,lengthnext
+inc b
+dec bc
+jr getadditionallength
+lengthnext
+dec a
+add a,c
+ld c,a
+ld a,b
+adc a,0
+ld b,a ; bc=length
+
+copyliteral
+ldir
+
+lzunpack
+ld a,lx
+and #F
+add 3
+ld c,a
+cp 18 ; more bytes for length?
+jr nz,readoffset
+
+getadditionallengthbis
+ld a,(hl)
+inc hl
+inc a
+jr nz,lengthnextbis
+inc b
+dec bc
+jr getadditionallengthbis
+lengthnextbis
+dec a
+add a,c
+ld c,a
+ld a,b
+adc a,0
+ld b,a ; bc=length
+
+readoffset
+ld a,lx
+add a
+jr c,extendedoffset
+; read encoded offset
+ld a,(hl)
+inc a
+ret z ; LZ48 end with zero offset
+inc hl
+push hl
+ld l,a
+ld a,e
+sub l
+ld l,a
+ld a,d
+sbc a,0
+ld h,a
+; source=dest-copyoffset
+ldir
+pop hl
+jr nextsequence
+
+extendedoffset
+ld a,(hl)
+inc hl
+push hl
+inc a
+ld l,a
+ld a,e
+sub l
+ld l,a
+ld a,d
+sbc a,1
+ld h,a
+; source=dest-copyoffset
+ldir
+pop hl
+jr nextsequence
+
+
+
+
diff --git a/tools/rasm_src/decrunch/lz4_docent.asm b/tools/rasm_src/decrunch/lz4_docent.asm new file mode 100644 index 0000000..a0b2188 --- /dev/null +++ b/tools/rasm_src/decrunch/lz4_docent.asm @@ -0,0 +1,118 @@ +; decompress raw lz4 data packet +; on entry hl - start of packed buffer, de - destination buffer, bc - size of packed data +LZ4_decompress_raw: + push de ; store original destination pointer + push hl ; store start of compressed data source + add hl,bc ; calculate end address of compressed block + ld b,h ; move end address of compressed data to bc + ld c,l + pop hl ; restore start of compressed data source + push bc ; store end address of compessed data +; now hl - start of packed buffer, de - destination, bc - end of packed buffer + ld b,0 ; clear b, c is set later + +; get decompression token +LZ4_GetToken: + xor a ; reset c flag for sbc later + ld a,(hl) ; read token + inc hl + push af ; store token +; unpack 4 high bits to get the length of literal + rlca + rlca + rlca + rlca +; copy literals + and #f ; token can be max 15 - mask out unimportant bits + jr z,LZ4_skipcalc ; there is no literals, skip calculation of literal size + ld c,a ; set the count for calculation + cp #f ; if literal size <15 + jr nz, LZ4_copyliterals ; copy literal, else +; calculate total literal size by adding contents of following bytes + push de ; store destination + ex de,hl +; a = size of literal to copy, de=pointer to data to be added + ld h,0 ; set hl with size of literal to copy + ld l,a + +LZ4_calcloop: + ld a,(de) ; get additional literal size to add + inc de + ld c,a ; set bc to the length of literal + add hl,bc ; add it to the total literal length + cp #ff ; if literal=255 + jr z,LZ4_calcloop ; continue calculating the total literal size + ld b,h ; store total literal size to copy in bc + ld c,l + ex de,hl ; hl now contains current compressed data pointer + pop de ; restore destination to de + +LZ4_copyliterals: + ldir ; copy literal to destination + +LZ4_skipcalc: +; check for end of compressed data + pop af ; restore token, carry is cleared because of xor a at the beginning of GetToken + pop bc ; restore end address of compressed data + push hl ; store current compressed data pointer + sbc hl, bc ; check if we reached the end of compressed data buffer + pop hl ; restore current compressed data pointer + jr z,LZ4_decompress_success ; decompression finished + push bc ; store end address of compressed data + +; Copy Matches + and #f ; token can be max 15 - mask out unimportant bits. resets also c flag for sbc later +; get the offset + ld c,(hl) + inc hl + ld b,(hl) ; bc now contains the offset + inc hl + push hl ; store current compressed data pointer + push de ; store destination pointer + + ex de,hl + sbc hl,bc ; calculate from the offset the new decompressed data source to copy from +; hl contains new copy source, de source ptr + + ld b,0 ; load bc with the token + ld c,a + cp #f ; if matchlength <15 + jr nz, LZ4_copymatches ; copy matches. else + +; calculate total matchlength by adding additional bytes + push hl ; store current decompressed data source +; a = size of match to copy, de= pointer to data to be added + ld h,0 ; set hl with initial matchlength to copy + ld l,a +LZ4_calcloop2: + ld a,(de) ; get additional matchlength to add + inc de + ld c,a ; set bc to the matchlength + add hl,bc ; add it to the total match length + cp #ff ; if matchlength=255 + jr z,LZ4_calcloop2 ; continue calculating the total match length + ld b,h ; store total matchlength to copy in bc + ld c,l + pop hl ; restore current decompressed data source + pop af ; set stack to proper position by restoring destination pointer temporarily into af + ex de,hl + ex (sp),hl ; update current compressed data pointer on the stack to the new value from de + ex de,hl + push af ; restore stack + +LZ4_copymatches: + pop de ; restore destination pointer + inc bc ; add base length of 4 to get the correct size of matchlength + inc bc + inc bc + inc bc + ldir ; copy match + pop hl ; restore current compressed data source + jr LZ4_GetToken ; continue decompression +LZ4_decompress_success: + pop hl ; store destination pointer + sbc hl,de ; calculate the number of decompressed bytes + xor a ; clear exit code + ret + + diff --git a/tools/rasm_src/exomizer.h b/tools/rasm_src/exomizer.h new file mode 100644 index 0000000..1f3c7ca --- /dev/null +++ b/tools/rasm_src/exomizer.h @@ -0,0 +1,4942 @@ +/*
+
+Warning! This is a modified version of original sources!
+
+To sum up:
+- all include files and C sources were merged in a single file
+- existing logs were removed (except error logs)
+- main were removed and wrapper added
+
+
+*/
+
+#ifndef ALREADY_INCLUDED_CALLBACK
+#define ALREADY_INCLUDED_CALLBACK
+
+/*
+ * Copyright (c) 2005 Magnus Lind.
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from
+ * the use of this software.
+ *
+ * Permission is granted to anyone to use this software, alter it and re-
+ * distribute it freely for any non-commercial, non-profit purpose subject to
+ * the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software in a
+ * product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ *
+ * 2. Altered source versions must be plainly marked as such, and must not
+ * be misrepresented as being the original software.
+ *
+ * 3. This notice may not be removed or altered from any distribution.
+ *
+ * 4. The names of this software and/or it's copyright holders may not be
+ * used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ */
+
+typedef int cb_cmp(const void *a, const void *b);
+typedef void cb_free(void *a);
+typedef void cb_fprint(FILE *f, const void *a);
+
+#endif
+#ifndef INCLUDED_INT
+#define INCLUDED_INT
+
+/*
+ * Copyright (c) 2005 Magnus Lind.
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from
+ * the use of this software.
+ *
+ * Permission is granted to anyone to use this software, alter it and re-
+ * distribute it freely for any non-commercial, non-profit purpose subject to
+ * the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software in a
+ * product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ *
+ * 2. Altered source versions must be plainly marked as such, and must not
+ * be misrepresented as being the original software.
+ *
+ * 3. This notice may not be removed or altered from any distribution.
+ *
+ * 4. The names of this software and/or it's copyright holders may not be
+ * used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ */
+
+typedef signed char i8;
+typedef signed short int i16;
+typedef signed int i32;
+
+typedef unsigned char u8;
+typedef unsigned short int u16;
+typedef unsigned int u32;
+
+#endif
+#ifndef ALREADY_INCLUDED_PROGRESS
+#define ALREADY_INCLUDED_PROGRESS
+
+/*
+ * Copyright (c) 2005 Magnus Lind.
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from
+ * the use of this software.
+ *
+ * Permission is granted to anyone to use this software, alter it and re-
+ * distribute it freely for any non-commercial, non-profit purpose subject to
+ * the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software in a
+ * product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ *
+ * 2. Altered source versions must be plainly marked as such, and must not
+ * be misrepresented as being the original software.
+ *
+ * 3. This notice may not be removed or altered from any distribution.
+ *
+ * 4. The names of this software and/or it's copyright holders may not be
+ * used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ */
+
+struct progress
+{
+ char *msg;
+ float factor;
+ int offset;
+ int last;
+};
+
+void progress_init(struct progress p[1], char *msg, int start, int end);
+
+void progress_bump(struct progress p[1], int pos);
+
+void progress_free(struct progress p[1]);
+
+#endif
+#ifndef ALREADY_INCLUDED_CHUNKPOOL
+#define ALREADY_INCLUDED_CHUNKPOOL
+
+/*
+ * Copyright (c) 2003 -2005 Magnus Lind.
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from
+ * the use of this software.
+ *
+ * Permission is granted to anyone to use this software, alter it and re-
+ * distribute it freely for any non-commercial, non-profit purpose subject to
+ * the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software in a
+ * product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ *
+ * 2. Altered source versions must be plainly marked as such, and must not
+ * be misrepresented as being the original software.
+ *
+ * 3. This notice may not be removed or altered from any distribution.
+ *
+ * 4. The names of this software and/or it's copyright holders may not be
+ * used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ */
+
+#define CHUNKPOOL_CHUNKS_MAX 64
+
+struct chunkpool {
+ int chunk_size;
+ int chunk;
+ int chunk_pos;
+ int chunk_max;
+ void *chunks[64];
+};
+
+void
+chunkpool_init(struct chunkpool *ctx, int size);
+
+void
+chunkpool_free(struct chunkpool *ctx);
+
+void chunkpool_free2(struct chunkpool *ctx, cb_free *f);
+
+void *
+chunkpool_malloc(struct chunkpool *ctx);
+
+void *
+chunkpool_calloc(struct chunkpool *ctx);
+
+#endif
+#ifndef ALREADY_INCLUDED_MATCH
+#define ALREADY_INCLUDED_MATCH
+/*
+ * Copyright (c) 2002 - 2005, 2013 Magnus Lind.
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from
+ * the use of this software.
+ *
+ * Permission is granted to anyone to use this software, alter it and re-
+ * distribute it freely for any non-commercial, non-profit purpose subject to
+ * the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software in a
+ * product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ *
+ * 2. Altered source versions must be plainly marked as such, and must not
+ * be misrepresented as being the original software.
+ *
+ * 3. This notice may not be removed or altered from any distribution.
+ *
+ * 4. The names of this software and/or it's copyright holders may not be
+ * used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ */
+
+struct match {
+ unsigned short int offset;
+ unsigned short int len;
+ struct match *next;
+};
+
+typedef struct match match[1];
+typedef struct match *matchp;
+typedef const struct match *const_matchp;
+
+struct pre_calc {
+ struct match_node *single;
+ const struct match *cache;
+};
+
+struct match_ctx {
+ struct chunkpool m_pool[1];
+ struct pre_calc (*info)[1];
+ unsigned short int *rle;
+ unsigned short int *rle_r;
+ const unsigned char *buf;
+ int len;
+ int max_offset;
+ int max_len;
+};
+
+typedef struct match_ctx match_ctx[1];
+typedef struct match_ctx *match_ctxp;
+
+//void match_ctx_init(match_ctx ctx, /* IN/OUT */ struct membuf *inbuf, /* IN */ int max_len, /* IN */ int max_offset, /* IN */ int use_imprecise_rle); /* IN */
+
+void match_ctx_free(match_ctx ctx); /* IN/OUT */
+
+/* this needs to be called with the indexes in
+ * reverse order */
+const_matchp matches_get(match_ctx ctx, /* IN/OUT */
+ int index); /* IN */
+
+void match_delete(match_ctx ctx, /* IN/OUT */
+ matchp mp); /* IN */
+
+struct matchp_cache_enum {
+ match_ctxp ctx;
+ const_matchp next;
+ match tmp1;
+ match tmp2;
+ int pos;
+};
+
+typedef struct matchp_cache_enum matchp_cache_enum[1];
+typedef struct matchp_cache_enum *matchp_cache_enump;
+
+void matchp_cache_get_enum(match_ctx ctx, /* IN */
+ matchp_cache_enum mpce); /* IN/OUT */
+
+typedef const_matchp matchp_enum_get_next_f(void *matchp_enum); /* IN/OUT */
+
+const_matchp matchp_cache_enum_get_next(void *matchp_cache_enum); /* IN */
+
+#endif
+#ifndef ALREADY_INCLUDED_OUTPUT
+#define ALREADY_INCLUDED_OUTPUT
+
+/*
+ * Copyright (c) 2002 - 2005 Magnus Lind.
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from
+ * the use of this software.
+ *
+ * Permission is granted to anyone to use this software, alter it and re-
+ * distribute it freely for any non-commercial, non-profit purpose subject to
+ * the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software in a
+ * product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ *
+ * 2. Altered source versions must be plainly marked as such, and must not
+ * be misrepresented as being the original software.
+ *
+ * 3. This notice may not be removed or altered from any distribution.
+ *
+ * 4. The names of this software and/or it's copyright holders may not be
+ * used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ */
+
+struct _output_ctx {
+ unsigned int bitbuf;
+ int pos;
+ int start;
+ struct membuf *buf;
+};
+
+typedef struct _output_ctx output_ctx[1];
+typedef struct _output_ctx *output_ctxp;
+
+void output_ctx_init(output_ctx ctx, struct membuf *out); /* IN/OUT */
+
+unsigned int output_get_pos(output_ctx ctx); /* IN */
+
+void output_byte(output_ctx ctx, /* IN/OUT */
+ unsigned char byte); /* IN */
+
+void output_word(output_ctx ctx, /* IN/OUT */
+ unsigned short int word); /* IN */
+
+void output_bits_flush(output_ctx ctx); /* IN/OUT */
+
+void output_bits(output_ctx ctx, /* IN/OUT */
+ int count, /* IN */
+ int val); /* IN */
+
+void output_gamma_code(output_ctx ctx, /* IN/OUT */
+ int code); /* IN */
+#endif
+#ifndef ALREADY_INCLUDED_SEARCH
+#define ALREADY_INCLUDED_SEARCH
+
+/*
+ * Copyright (c) 2002 - 2005 Magnus Lind.
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from
+ * the use of this software.
+ *
+ * Permission is granted to anyone to use this software, alter it and re-
+ * distribute it freely for any non-commercial, non-profit purpose subject to
+ * the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software in a
+ * product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ *
+ * 2. Altered source versions must be plainly marked as such, and must not
+ * be misrepresented as being the original software.
+ *
+ * 3. This notice may not be removed or altered from any distribution.
+ *
+ * 4. The names of this software and/or it's copyright holders may not be
+ * used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ */
+
+struct _search_node {
+ int index;
+ match match;
+ unsigned int total_offset;
+ float total_score;
+ struct _search_node *prev;
+};
+
+typedef struct _search_node search_node[1];
+typedef struct _search_node *search_nodep;
+typedef const struct _search_node *const_search_nodep;
+
+struct _encode_match_data {
+ output_ctxp out;
+ void *priv;
+};
+
+typedef struct _encode_match_data encode_match_data[1];
+typedef struct _encode_match_data *encode_match_datap;
+
+/* example of what may be used for priv data
+ * field in the encode_match_data struct */
+typedef
+float encode_int_f(int val, void *priv, output_ctxp out); /* IN */
+
+struct _encode_match_priv {
+ int lit_num;
+ int seq_num;
+ int rle_num;
+ float lit_bits;
+ float seq_bits;
+ float rle_bits;
+
+ encode_int_f *offset_f;
+ encode_int_f *len_f;
+ void *offset_f_priv;
+ void *len_f_priv;
+
+ output_ctxp out;
+};
+
+typedef struct _encode_match_priv encode_match_priv[1];
+typedef struct _encode_match_priv *encode_match_privp;
+/* end of example */
+
+typedef
+float encode_match_f(const_matchp mp, encode_match_data emd); /* IN */
+
+void search_node_dump(search_nodep snp); /* IN */
+
+void search_node_free(search_nodep snp); /* IN/OUT */
+
+search_nodep search_buffer(match_ctx ctx, /* IN */
+ encode_match_f * f, /* IN */
+ encode_match_data emd,
+ int use_literal_sequences); /* IN */
+
+struct _matchp_snp_enum {
+ const_search_nodep startp;
+ const_search_nodep currp;
+};
+
+typedef struct _matchp_snp_enum matchp_snp_enum[1];
+typedef struct _matchp_snp_enum *matchp_snp_enump;
+
+void matchp_snp_get_enum(const_search_nodep snp, /* IN */
+ matchp_snp_enum snpe); /* IN/OUT */
+
+const_matchp matchp_snp_enum_get_next(void *matchp_snp_enum);
+
+#endif
+#ifndef ALREADY_INCLUDED_RADIX
+#define ALREADY_INCLUDED_RADIX
+/*
+ * Copyright (c) 2002, 2003 Magnus Lind.
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from
+ * the use of this software.
+ *
+ * Permission is granted to anyone to use this software, alter it and re-
+ * distribute it freely for any non-commercial, non-profit purpose subject to
+ * the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software in a
+ * product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ *
+ * 2. Altered source versions must be plainly marked as such, and must not
+ * be misrepresented as being the original software.
+ *
+ * 3. This notice may not be removed or altered from any distribution.
+ *
+ * 4. The names of this software and/or it's copyright holders may not be
+ * used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ */
+
+typedef struct _radix_node *radix_nodep;
+
+struct _radix_root {
+ int depth;
+ radix_nodep root;
+ struct chunkpool mem[1];
+};
+
+typedef struct _radix_root radix_root[1];
+typedef struct _radix_root *radix_rootp;
+
+
+typedef void free_callback(void *data, void *priv);
+
+/* *f will be called even for null pointers */
+void radix_tree_free(radix_root rr, /* IN */
+ free_callback * f, /* IN */
+ void *priv); /* IN */
+
+void radix_tree_init(radix_root rr); /* IN */
+
+void radix_node_set(radix_root rr, /* IN */
+ unsigned int index, /* IN */
+ void *data); /* IN */
+
+void *radix_node_get(radix_root rr, /* IN */
+ unsigned int index); /* IN */
+
+#endif
+#ifndef MEMBUF_IO_ALREADY_INCLUDED
+#define MEMBUF_IO_ALREADY_INCLUDED
+
+/*
+ * Copyright (c) 2005 Magnus Lind.
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from
+ * the use of this software.
+ *
+ * Permission is granted to anyone to use this software, alter it and re-
+ * distribute it freely for any non-commercial, non-profit purpose subject to
+ * the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software in a
+ * product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ *
+ * 2. Altered source versions must be plainly marked as such, and must not
+ * be misrepresented as being the original software.
+ *
+ * 3. This notice may not be removed or altered from any distribution.
+ *
+ * 4. The names of this software and/or it's copyright holders may not be
+ * used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ */
+
+void read_file(const char *name, struct membuf *buf);
+void write_file(const char *name, struct membuf *buf);
+
+#endif
+#ifndef ALREADY_INCLUDED_MEMBUF
+#define ALREADY_INCLUDED_MEMBUF
+
+/*
+ * Copyright (c) 2002 - 2005 Magnus Lind.
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from
+ * the use of this software.
+ *
+ * Permission is granted to anyone to use this software, alter it and re-
+ * distribute it freely for any non-commercial, non-profit purpose subject to
+ * the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software in a
+ * product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ *
+ * 2. Altered source versions must be plainly marked as such, and must not
+ * be misrepresented as being the original software.
+ *
+ * 3. This notice may not be removed or altered from any distribution.
+ *
+ * 4. The names of this software and/or it's copyright holders may not be
+ * used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ */
+
+#define STATIC_MEMBUF_INIT {0, 0, 0}
+
+struct membuf {
+ void *buf;
+ int len;
+ int size;
+};
+
+void membuf_init(struct membuf *sb);
+void membuf_clear(struct membuf *sb);
+void membuf_free(struct membuf *sb);
+void membuf_new(struct membuf **sbp);
+void membuf_delete(struct membuf **sbp);
+/* Gets the length of data put into the membuf */
+int membuf_memlen(const struct membuf *sb);
+void membuf_truncate(struct membuf *sb, int len);
+
+/* returns the new len or < 0 if failure */
+int membuf_trim(struct membuf *sb, int pos);
+
+void *membuf_memcpy(struct membuf *sb, int offset, const void *mem, int len);
+void *membuf_append(struct membuf *sb, const void *mem, int len);
+void *membuf_append_char(struct membuf *sb, char c);
+void *membuf_insert(struct membuf *sb, int offset, const void *mem, int len);
+void membuf_remove(struct membuf *sb, int offset, int len);
+/* Grows the capacity if it's less than the given size */
+void membuf_atleast(struct membuf *sb, int size);
+/* Skrinks the capacity if it's greater than the given size */
+void membuf_atmost(struct membuf *sb, int size);
+/* Gets the current capacity of the membuf */
+int membuf_get_size(const struct membuf *sb);
+/* Gets a pointer to the internal buffer. Don't dereferece it beyond
+ * its size. */
+void *membuf_get(const struct membuf *sb);
+
+#endif
+#ifndef INCLUDED_PARSE
+#define INCLUDED_PARSE
+
+/*
+ * Copyright (c) 2005 Magnus Lind.
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from
+ * the use of this software.
+ *
+ * Permission is granted to anyone to use this software, alter it and re-
+ * distribute it freely for any non-commercial, non-profit purpose subject to
+ * the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software in a
+ * product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ *
+ * 2. Altered source versions must be plainly marked as such, and must not
+ * be misrepresented as being the original software.
+ *
+ * 3. This notice may not be removed or altered from any distribution.
+ *
+ * 4. The names of this software and/or it's copyright holders may not be
+ * used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ */
+
+#define ATOM_TYPE_OP_ARG_NONE 0 /* uses u.op */
+#define ATOM_TYPE_OP_ARG_U8 1 /* uses u.op */
+#define ATOM_TYPE_OP_ARG_U16 2 /* uses u.op */
+#define ATOM_TYPE_OP_ARG_I8 3 /* uses u.op */
+#define ATOM_TYPE_OP_ARG_UI8 4 /* uses u.op */
+#define ATOM_TYPE_EXPRS 12 /* uses u.exprs */
+#define ATOM_TYPE_WORD_EXPRS 10 /* uses u.exprs */
+#define ATOM_TYPE_BYTE_EXPRS 11 /* uses u.exprs */
+#define ATOM_TYPE_RES 13 /* uses u.res */
+#define ATOM_TYPE_BUFFER 14 /* uses u.buffer */
+
+struct op
+{
+ struct expr *arg;
+ u8 code;
+};
+
+struct res
+{
+ struct expr *length;
+ struct expr *value;
+};
+
+struct buffer
+{
+ const char *name;
+ i32 length;
+ i32 skip;
+};
+
+struct atom
+{
+ u8 type;
+ union
+ {
+ struct op op;
+ struct vec *exprs;
+ struct buffer buffer;
+ struct res res;
+ } u;
+};
+
+extern int push_state_skip;
+extern int push_state_macro;
+extern int push_state_init;
+extern int num_lines;
+
+void parse_init(void);
+void parse_free(void);
+
+void set_initial_symbol(const char *symbol, i32 value);
+void initial_symbol_dump(int level, const char *symbol);
+
+struct membuf *new_initial_named_buffer(const char *name);
+
+int assemble(struct membuf *source, struct membuf *dest);
+
+/* start of internal functions */
+
+struct atom *new_op(u8 op_code, u8 op_size, struct expr *arg);
+struct atom *new_op0(u8 op_code);
+
+struct atom *new_exprs(struct expr *arg);
+struct atom *exprs_add(struct atom *atom, struct expr *arg);
+struct atom *exprs_to_byte_exprs(struct atom *atom);
+struct atom *exprs_to_word_exprs(struct atom *atom);
+
+struct atom *new_res(struct expr *len, struct expr *value);
+struct atom *new_incbin(const char *name,
+ struct expr *skip, struct expr *len);
+
+struct expr *new_is_defined(const char *symbol);
+struct expr *new_expr_inclen(const char *name);
+struct expr *new_expr_incword(const char *name,
+ struct expr *skip);
+
+void new_symbol_expr(const char *symbol, struct expr *arg);
+void new_symbol_expr_guess(const char *symbol, struct expr *arg);
+
+/* returns NULL if found, not otherwise, expp may be NULL. */
+const char *find_symref(const char *symbol,
+ struct expr **expp);
+
+int resolve_symbol(const char *symbol, int *has_valuep, i32 *valuep);
+void symbol_dump_resolved(int level, const char *symbol);
+
+void new_label(const char *label);
+void set_org(struct expr *arg);
+void push_if_state(struct expr *arg);
+void push_macro_state(const char *name);
+void macro_append(const char *text);
+void asm_error(const char *msg);
+void asm_echo(const char *msg, struct atom *atom);
+void asm_include(const char *msg);
+
+void output_atoms(struct membuf *out, struct vec *mem);
+void asm_src_buffer_push(struct membuf *buf);
+
+int assembleSinglePass(struct membuf *source, struct membuf *dest);
+
+#endif
+#ifndef ALREADY_INCLUDED_OPTIMAL
+#define ALREADY_INCLUDED_OPTIMAL
+
+/*
+ * Copyright (c) 2002 - 2005 Magnus Lind.
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from
+ * the use of this software.
+ *
+ * Permission is granted to anyone to use this software, alter it and re-
+ * distribute it freely for any non-commercial, non-profit purpose subject to
+ * the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software in a
+ * product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ *
+ * 2. Altered source versions must be plainly marked as such, and must not
+ * be misrepresented as being the original software.
+ *
+ * 3. This notice may not be removed or altered from any distribution.
+ *
+ * 4. The names of this software and/or it's copyright holders may not be
+ * used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ */
+
+float optimal_encode(const_matchp mp, /* IN */
+ encode_match_data emp); /* IN */
+
+void optimal_init(encode_match_data emp); /* IN/OUT */
+
+void optimal_free(encode_match_data emd); /* IN */
+
+void optimal_optimize(encode_match_data emd, /* IN/OUT */
+ matchp_enum_get_next_f * f, /* IN */
+ void *priv); /* IN */
+
+void optimal_encoding_import(encode_match_data emd, /* IN/OUT */
+ const char *encoding); /* IN */
+
+const char *
+optimal_encoding_export(encode_match_data emd); /* IN */
+
+void optimal_dump(int level, encode_match_data emp); /* IN */
+
+void optimal_out(output_ctx out, /* IN/OUT */
+ encode_match_data emd); /* IN */
+
+#endif
+#ifndef ALREADY_INCLUDED_VEC
+#define ALREADY_INCLUDED_VEC
+
+/*
+ * Copyright (c) 2003 - 2005 Magnus Lind.
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from
+ * the use of this software.
+ *
+ * Permission is granted to anyone to use this software, alter it and re-
+ * distribute it freely for any non-commercial, non-profit purpose subject to
+ * the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software in a
+ * product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ *
+ * 2. Altered source versions must be plainly marked as such, and must not
+ * be misrepresented as being the original software.
+ *
+ * 3. This notice may not be removed or altered from any distribution.
+ *
+ * 4. The names of this software and/or it's copyright holders may not be
+ * used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ */
+#define STATIC_VEC_INIT(EL_SIZE) {(EL_SIZE), STATIC_MEMBUF_INIT, 1}
+
+struct vec {
+ size_t elsize;
+ struct membuf buf;
+ int flags;
+};
+
+struct vec_iterator {
+ const struct vec *vec;
+ int pos;
+};
+
+void vec_init(struct vec *p, size_t elsize);
+void vec_clear(struct vec *p, cb_free * f);
+void vec_free(struct vec *p, cb_free * f);
+
+int vec_count(const struct vec *p);
+void *vec_get(const struct vec *p, int index);
+
+/**
+ * Returns a pointer to the set area or null if the index is out of
+ * bounds.
+ **/
+void *vec_set(struct vec *p, int index, const void *in);
+void *vec_insert(struct vec *p, int index, const void *in);
+void vec_remove(struct vec *p, int index);
+
+void *vec_push(struct vec *p, const void *in);
+
+/**
+ * Gets the position where the key is stored in the vector. The vector
+ * needs to be sorted for this function to work. Returns the position,
+ * -1 on error or a negative number that can be converted to where
+ * it should have been if it had been inserted. insert_pos = -(val + 2)
+ **/
+int vec_find(const struct vec *p, cb_cmp * f, const void *key);
+
+/**
+ * Gets a pointer to the element that the key points to.
+ * Returns a pointer that may be null if not found.
+ **/
+void *vec_find2(const struct vec *p, cb_cmp * f, const void *key);
+
+/**
+ * Inserts the in element in its correct position in a sorted vector.
+ * returns 1 if insertion is successful, 0 if element is already
+ * present or -1 on error. If out is not NULL it will be
+ * dereferenced and set to the inserted or present element.
+ **/
+int vec_insert_uniq(struct vec *p, cb_cmp * f, const void *in, void **out);
+void vec_sort(struct vec *p, cb_cmp * f);
+
+void vec_get_iterator(const struct vec *p, struct vec_iterator *i);
+void *vec_iterator_next(struct vec_iterator *i);
+
+int vec_equals(const struct vec *a, const struct vec *b, cb_cmp *equals);
+void vec_fprint(FILE *, const struct vec *a, cb_fprint *fprint);
+
+#endif
+#ifndef ALREADY_INCLUDED_MAP
+#define ALREADY_INCLUDED_MAP
+
+/*
+ * Copyright (c) 2006 Magnus Lind.
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from
+ * the use of this software.
+ *
+ * Permission is granted to anyone to use this software, alter it and re-
+ * distribute it freely for any non-commercial, non-profit purpose subject to
+ * the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software in a
+ * product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ *
+ * 2. Altered source versions must be plainly marked as such, and must not
+ * be misrepresented as being the original software.
+ *
+ * 3. This notice may not be removed or altered from any distribution.
+ *
+ * 4. The names of this software and/or it's copyright holders may not be
+ * used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ */
+
+struct map_entry {
+ const char *key;
+ void *value;
+};
+
+#define STATIC_MAP_INIT {STATIC_VEC_INIT(sizeof(struct map_entry))}
+
+struct map {
+ struct vec vec;
+};
+
+struct map_iterator {
+ struct vec_iterator vec;
+};
+
+void map_init(struct map *m);
+void map_clear(struct map *m);
+void map_free(struct map *m);
+
+void *map_put(struct map *m, const char *key, void *value);
+void *map_get(const struct map *m, const char *key);
+int map_contains_key(const struct map *m, const char *key);
+void map_put_all(struct map *m, const struct map *source);
+
+int map_contains(const struct map *m1, const struct map *m2, cb_cmp *f);
+
+/**
+ * If f is NULL, only the keys will be compared.
+ * returns -1 on error, 1 on equality and 0 otherwise,
+ **/
+int map_equals(const struct map *m1, const struct map *m2, cb_cmp *f);
+
+void map_get_iterator(const struct map *p, struct map_iterator *i);
+const struct map_entry *map_iterator_next(struct map_iterator *i);
+
+#endif
+#ifndef NAMED_BUFFER_INCLUDED
+#define NAMED_BUFFER_INCLUDED
+
+/*
+ * Copyright (c) 2005 Magnus Lind.
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from
+ * the use of this software.
+ *
+ * Permission is granted to anyone to use this software, alter it and re-
+ * distribute it freely for any non-commercial, non-profit purpose subject to
+ * the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software in a
+ * product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ *
+ * 2. Altered source versions must be plainly marked as such, and must not
+ * be misrepresented as being the original software.
+ *
+ * 3. This notice may not be removed or altered from any distribution.
+ *
+ * 4. The names of this software and/or it's copyright holders may not be
+ * used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ */
+
+struct named_buffer {
+ struct map map;
+ struct chunkpool buf;
+};
+
+void named_buffer_init(struct named_buffer *nb);
+void named_buffer_free(struct named_buffer *nb);
+void named_buffer_clear(struct named_buffer *nb);
+
+void named_buffer_copy(struct named_buffer *nb,
+ const struct named_buffer *source);
+
+struct membuf *new_named_buffer(struct named_buffer *nb, const char *name);
+struct membuf *get_named_buffer(struct named_buffer *nb, const char *name);
+
+#endif
+#ifndef ALREADY_INCLUDED_LOG
+#define ALREADY_INCLUDED_LOG
+/*
+ * Copyright (c) 2002, 2003 Magnus Lind.
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from
+ * the use of this software.
+ *
+ * Permission is granted to anyone to use this software, alter it and re-
+ * distribute it freely for any non-commercial, non-profit purpose subject to
+ * the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software in a
+ * product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ *
+ * 2. Altered source versions must be plainly marked as such, and must not
+ * be misrepresented as being the original software.
+ *
+ * 3. This notice may not be removed or altered from any distribution.
+ *
+ * 4. The names of this software and/or it's copyright holders may not be
+ * used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ */
+
+
+#ifndef __GNUC__
+#define __attribute__(x) /*NOTHING*/
+#endif
+
+enum log_level {
+ LOG_MIN = -99,
+ LOG_FATAL = -40,
+ LOG_ERROR = -30,
+ LOG_WARNING = -20,
+ LOG_BRIEF = -10,
+ LOG_NORMAL = 0,
+ LOG_VERBOSE = 10,
+ LOG_TRACE = 20,
+ LOG_DEBUG = 30,
+ LOG_DUMP = 40,
+ LOG_MAX = 99
+};
+
+typedef
+void log_formatter_f(FILE * out, /* IN */
+ enum log_level level, /* IN */
+ const char *context, /* IN */
+ const char *); /* IN */
+
+/*
+ * this log output function adds nothing
+ */
+void raw_log_formatter(FILE * out, /* IN */
+ enum log_level level, /* IN */
+ const char *context, /* IN */
+ const char *log); /* IN */
+
+
+struct log_output;
+
+struct log_ctx;
+
+struct log_ctx *log_new(void);
+
+/* log_delete closes all added output streams
+ * and files except for stdout and stderr
+ */
+void log_delete(struct log_ctx *ctx);
+
+void log_set_level(struct log_ctx *ctx, /* IN/OUT */
+ enum log_level level); /* IN */
+
+void log_add_output_stream(struct log_ctx *ctx, /* IN/OUT */
+ enum log_level min, /* IN */
+ enum log_level max, /* IN */
+ log_formatter_f * default_f, /* IN */
+ FILE * out_stream); /* IN */
+
+void log_vlog(struct log_ctx *ctx, /* IN */
+ enum log_level level, /* IN */
+ const char *context, /* IN */
+ log_formatter_f * f, /* IN */
+ const char *printf_str, /* IN */
+ va_list argp);
+
+
+void log_log_default(const char *printf_str, /* IN */
+ ...)
+ __attribute__((format(printf,1,2)));
+
+/* some helper macros */
+
+extern struct log_ctx *G_log_ctx;
+extern enum log_level G_log_level;
+extern enum log_level G_log_log_level;
+
+#define LOG_SET_LEVEL(L) \
+do { \
+ log_set_level(G_log_ctx, (L)); \
+ G_log_level = (L); \
+} while(0)
+
+#define LOG_INIT(L) \
+do { \
+ G_log_ctx = log_new(); \
+ log_set_level(G_log_ctx, (L)); \
+ G_log_level = (L); \
+} while(0)
+
+#define LOG_INIT_CONSOLE(X) \
+do { \
+ G_log_ctx = log_new(); \
+ log_set_level(G_log_ctx, (X)); \
+ G_log_level = (X); \
+ log_add_output_stream(G_log_ctx, LOG_WARNING, LOG_MAX, NULL, stderr); \
+ log_add_output_stream(G_log_ctx, LOG_MIN, LOG_WARNING - 1, NULL, stderr); \
+} while(0)
+
+#define LOG_FREE log_delete(G_log_ctx)
+
+#define IS_LOGGABLE(L) (G_log_level >= (L))
+
+#define LOG(L, M) \
+do { \
+ if(IS_LOGGABLE(L)) { \
+ G_log_log_level = (L); \
+ log_log_default M; \
+ } \
+} while(0)
+
+
+void hex_dump(int level, unsigned char *p, int len);
+
+#endif
+#ifndef ALREADY_INCLUDED_GETFLAG
+#define ALREADY_INCLUDED_GETFLAG
+/*
+ * Copyright (c) 2002, 2003 Magnus Lind.
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from
+ * the use of this software.
+ *
+ * Permission is granted to anyone to use this software, alter it and re-
+ * distribute it freely for any non-commercial, non-profit purpose subject to
+ * the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software in a
+ * product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ *
+ * 2. Altered source versions must be plainly marked as such, and must not
+ * be misrepresented as being the original software.
+ *
+ * 3. This notice may not be removed or altered from any distribution.
+ *
+ * 4. The names of this software and/or it's copyright holders may not be
+ * used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ */
+
+extern int flagind;
+extern int flagflag;
+extern const char *flagarg;
+
+int getflag(int argc, char *argv[], const char *flags);
+
+#endif
+#ifndef ALREADY_INCLUDED_EXODEC
+#define ALREADY_INCLUDED_EXODEC
+
+/*
+ * Copyright (c) 2005 Magnus Lind.
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from
+ * the use of this software.
+ *
+ * Permission is granted to anyone to use this software, alter it and re-
+ * distribute it freely for any non-commercial, non-profit purpose subject to
+ * the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software in a
+ * product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ *
+ * 2. Altered source versions must be plainly marked as such, and must not
+ * be misrepresented as being the original software.
+ *
+ * 3. This notice may not be removed or altered from any distribution.
+ *
+ * 4. The names of this software and/or it's copyright holders may not be
+ * used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ */
+
+
+struct dec_table
+{
+ unsigned char table_bit[3];
+ unsigned char table_off[3];
+ unsigned char table_bi[100];
+ unsigned char table_lo[100];
+ unsigned char table_hi[100];
+};
+
+struct dec_ctx
+{
+ int inpos;
+ int inend;
+ unsigned char *inbuf;
+ struct membuf *outbuf;
+ unsigned int bitbuf;
+ /* dep_table */
+ struct dec_table t[1];
+ int bits_read;
+};
+
+/* returns the encoding */
+char *
+dec_ctx_init(struct dec_ctx ctx[1],
+ struct membuf *inbuf, struct membuf *outbuf);
+
+void
+dec_ctx_free(struct dec_ctx ctx[1]);
+
+void dec_ctx_decrunch(struct dec_ctx ctx[1]);
+
+#endif
+#ifndef EXO_UTIL_ALREADY_INCLUDED
+#define EXO_UTIL_ALREADY_INCLUDED
+
+/*
+ * Copyright (c) 2008 Magnus Lind.
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from
+ * the use of this software.
+ *
+ * Permission is granted to anyone to use this software, alter it and re-
+ * distribute it freely for any non-commercial, non-profit purpose subject to
+ * the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software in a
+ * product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ *
+ * 2. Altered source versions must be plainly marked as such, and must not
+ * be misrepresented as being the original software.
+ *
+ * 3. This notice may not be removed or altered from any distribution.
+ *
+ * 4. The names of this software and/or it's copyright holders may not be
+ * used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ */
+
+
+/*
+ * target is the basic token for the sys/call basic command
+ * it may be -1 for hardcoded detection of a few targets.
+ */
+int find_sys(const unsigned char *buf, int target);
+
+struct load_info
+{
+ int basic_txt_start; /* in */
+ int basic_var_start; /* out */
+ int run; /* out */
+ int start; /* out */
+ int end; /* out */
+};
+
+void load_located(char *filename, unsigned char mem[65536],
+ struct load_info *info);
+
+int str_to_int(const char *str, int *value);
+
+const char *fixup_appl(char *appl);
+
+#endif
+#ifndef EXO_HELPER_ALREADY_INCLUDED
+#define EXO_HELPER_ALREADY_INCLUDED
+
+/*
+ * Copyright (c) 2005, 2013 Magnus Lind.
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from
+ * the use of this software.
+ *
+ * Permission is granted to anyone to use this software, alter it and re-
+ * distribute it freely for any non-commercial, non-profit purpose subject to
+ * the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software in a
+ * product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ *
+ * 2. Altered source versions must be plainly marked as such, and must not
+ * be misrepresented as being the original software.
+ *
+ * 3. This notice may not be removed or altered from any distribution.
+ *
+ * 4. The names of this software and/or it's copyright holders may not be
+ * used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ */
+
+
+#define CRUNCH_OPTIONS_DEFAULT {NULL, 65535, 65535, 65535, 1, 0}
+
+struct common_flags
+{
+ struct crunch_options *options;
+ const char *outfile;
+};
+
+#define CRUNCH_FLAGS "cCe:m:M:p:o:qv"
+#define BASE_FLAGS "o:qv"
+
+void print_crunch_flags(enum log_level level, const char *default_outfile);
+
+void print_base_flags(enum log_level level, const char *default_outfile);
+
+typedef void print_usage_f(const char *appl, enum log_level level,
+ const char *default_outfile);
+
+void handle_crunch_flags(int flag_char, /* IN */
+ const char *flag_arg, /* IN */
+ print_usage_f *print_usage, /* IN */
+ const char *appl, /* IN */
+ struct common_flags *options); /* OUT */
+
+void handle_base_flags(int flag_char, /* IN */
+ const char *flag_arg, /* IN */
+ print_usage_f *print_usage, /* IN */
+ const char *appl, /* IN */
+ const char **default_outfilep); /* OUT */
+
+struct crunch_options
+{
+ const char *exported_encoding;
+ int max_passes;
+ int max_len;
+ int max_offset;
+ int use_literal_sequences;
+ int use_imprecise_rle;
+};
+
+struct crunch_info
+{
+ int literal_sequences_used;
+ int needed_safety_offset;
+};
+
+void print_license(void);
+
+void crunch_backwards(struct membuf *inbuf,
+ struct membuf *outbuf,
+ struct crunch_options *options, /* IN */
+ struct crunch_info *info); /* OUT */
+
+void exocrunch(struct membuf *inbuf,
+ struct membuf *outbuf,
+ struct crunch_options *options, /* IN */
+ struct crunch_info *info); /* OUT */
+
+void exodecrunch(int level,
+ struct membuf *inbuf,
+ struct membuf *outbuf);
+
+void decrunch_backwards(int level,
+ struct membuf *inbuf,
+ struct membuf *outbuf);
+
+void reverse_buffer(char *start, int len);
+#endif
+#ifndef ALREADY_INCLUDED_DESFX
+#define ALREADY_INCLUDED_DESFX
+
+/*
+ * Copyright (c) 2007 Magnus Lind.
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from
+ * the use of this software.
+ *
+ * Permission is granted to anyone to use this software, alter it and re-
+ * distribute it freely for any non-commercial, non-profit purpose subject to
+ * the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software in a
+ * product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ *
+ * 2. Altered source versions must be plainly marked as such, and must not
+ * be misrepresented as being the original software.
+ *
+ * 3. This notice may not be removed or altered from any distribution.
+ *
+ * 4. The names of this software and/or it's copyright holders may not be
+ * used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ */
+
+u16 decrunch_sfx(u8 mem[65536], u16 run, u16 *start, u16 *end);
+
+#endif
+
+/*
+ * Copyright (c) 2002 - 2007 Magnus Lind.
+ *
+ * This software is provided 'as-is', without any express or implied warranty.
+ * In no event will the authors be held liable for any damages arising from
+ * the use of this software.
+ *
+ * Permission is granted to anyone to use this software, alter it and re-
+ * distribute it freely for any non-commercial, non-profit purpose subject to
+ * the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software in a
+ * product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ *
+ * 2. Altered source versions must be plainly marked as such, and must not
+ * be misrepresented as being the original software.
+ *
+ * 3. This notice may not be removed or altered from any distribution.
+ *
+ * 4. The names of this software and/or it's copyright holders may not be
+ * used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ */
+#define DEFAULT_OUTFILE "a.out"
+#define OUTPUT_FLAG_REVERSE 1
+
+/*
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <ctype.h>
+#include <math.h>
+*/
+
+#ifdef WIN32
+#define vsnprintf _vsnprintf
+#endif
+#ifdef DJGPP
+#define vsnprintf(A, B, C, D) vsprintf((A),(C),(D))
+#endif
+
+#define BAR_LENGTH 64
+
+//extern struct membuf sfxdecr[];
+
+
+#define RADIX_TREE_NODE_RADIX 11U
+#define RADIX_TREE_NODE_MASK ((1U << RADIX_TREE_NODE_RADIX) - 1U)
+
+struct _radix_node {
+ struct _radix_node *rn;
+};
+
+void radix_tree_init(radix_root rr) /* IN */
+{
+ rr->depth = 0;
+ rr->root = NULL;
+
+ chunkpool_init(rr->mem, (1 << RADIX_TREE_NODE_RADIX) * sizeof(void *));
+}
+
+static
+void radix_tree_free_helper(int depth, radix_nodep rnp, free_callback * f, /* IN */
+ void *priv) /* IN */
+{
+ int i;
+ do
+ {
+ if (depth == 0)
+ {
+ /* do something to the data pointer? */
+ if (f != NULL)
+ {
+ f(rnp, priv);
+ }
+ break;
+ }
+ if (rnp == NULL)
+ {
+ /* tree not grown here */
+ break;
+ }
+
+ for (i = RADIX_TREE_NODE_MASK; i >= 0; --i)
+ {
+ radix_tree_free_helper(depth - 1, rnp[i].rn, f, priv);
+ rnp[i].rn = NULL;
+ }
+ }
+ while (0);
+}
+
+void radix_tree_free(radix_root rr, /* IN */
+ free_callback * f, /* IN */
+ void *priv) /* IN */
+{
+ radix_tree_free_helper(rr->depth, rr->root, f, priv);
+ rr->depth = 0;
+ rr->root = NULL;
+ chunkpool_free(rr->mem);
+}
+
+void radix_node_set(radix_rootp rrp, /* IN */
+ unsigned int index, /* IN */
+ void *data) /* IN */
+{
+ radix_nodep rnp;
+ radix_nodep *rnpp;
+ unsigned int mask;
+ int depth;
+
+ mask = ~0U << (RADIX_TREE_NODE_RADIX * rrp->depth);
+ while (index & mask)
+ {
+ /*LOG(LOG_DUMP, ("calloc called\n")); */
+ /* not deep enough, let's deepen the tree */
+ rnp = chunkpool_calloc(rrp->mem);
+
+ rnp[0].rn = rrp->root;
+ rrp->root = rnp;
+ rrp->depth += 1;
+
+ mask = ~0U << (RADIX_TREE_NODE_RADIX * rrp->depth);
+ }
+
+ /* go down */
+ rnpp = &rrp->root;
+ for (depth = rrp->depth - 1; depth >= 0; --depth)
+ {
+ unsigned int node_index;
+
+ if (*rnpp == NULL)
+ {
+ /*LOG(LOG_DUMP, ("calloc called\n")); */
+ /* tree is not grown in this interval */
+ *rnpp = chunkpool_calloc(rrp->mem);
+ }
+ node_index = ((index >> (RADIX_TREE_NODE_RADIX * depth)) &
+ RADIX_TREE_NODE_MASK);
+
+ rnpp = &((*rnpp)[node_index].rn);
+ }
+ *rnpp = data;
+}
+
+void *radix_node_get(radix_root rr, /* IN */
+ unsigned int index) /* IN */
+{
+ radix_nodep rnp;
+ unsigned short int depth;
+
+ /* go down */
+ rnp = rr->root;
+ for (depth = rr->depth - 1; depth < 0xffff; --depth)
+ {
+ unsigned short int node_index;
+
+ if (rnp == NULL)
+ {
+ /* tree is not grown in this interval */
+ break;
+ }
+ node_index = ((index >> (RADIX_TREE_NODE_RADIX * depth)) &
+ RADIX_TREE_NODE_MASK);
+
+ rnp = rnp[node_index].rn;
+ }
+ return rnp;
+}
+
+void search_node_free(search_nodep snp) /* IN */
+{
+ /* emty now since snp:s are stored in an array */
+}
+
+search_nodep search_buffer(match_ctx ctx, /* IN */
+ encode_match_f * f, /* IN */
+ encode_match_data emd, /* IN */
+ int use_literal_sequences)
+{
+ static struct membuf backing[1] = { STATIC_MEMBUF_INIT };
+ static search_node *snp_arr;
+ const_matchp mp = NULL;
+ search_nodep snp;
+ search_nodep best_copy_snp;
+ int best_copy_len;
+
+ search_nodep best_rle_snp;
+
+ int len = ctx->len + 1;
+
+
+ membuf_atleast(backing, len * sizeof(search_node));
+ snp_arr = membuf_get(backing);
+ memset(snp_arr, 0, len * sizeof(search_node));
+
+ --len;
+ snp = snp_arr[len];
+ snp->index = len;
+ snp->match->offset = 0;
+ snp->match->len = 0;
+ snp->total_offset = 0;
+ snp->total_score = 0;
+ snp->prev = NULL;
+
+ best_copy_snp = snp;
+ best_copy_len = 0.0;
+
+ best_rle_snp = NULL;
+
+ /* think twice about changing this code,
+ * it works the way it is. The last time
+ * I examined this code I was certain it was
+ * broken and broke it myself, trying to fix it. */
+ while (len > 0 && (mp = matches_get(ctx, len - 1)) != NULL)
+ {
+ float prev_score;
+ float prev_offset_sum;
+
+ if(use_literal_sequences)
+ {
+ /* check if we can do even better with copy */
+ snp = snp_arr[len];
+ if(best_copy_snp->total_score+best_copy_len * 8.0 -
+ snp->total_score > 0.0 || best_copy_len > 65535)
+ {
+ /* found a better copy endpoint */
+ best_copy_snp = snp;
+ best_copy_len = 0.0;
+ } else
+ {
+ float copy_score = best_copy_len * 8.0 + (1.0 + 17.0 + 17.0);
+ float total_copy_score = best_copy_snp->total_score +
+ copy_score;
+
+
+ if(snp->total_score > total_copy_score )
+ {
+ match local_mp;
+ /* here it is good to just copy instead of crunch */
+
+
+ local_mp->len = best_copy_len;
+ local_mp->offset = 0;
+ local_mp->next = NULL;
+ snp->total_score = total_copy_score;
+ snp->total_offset = best_copy_snp->total_offset;
+ snp->prev = best_copy_snp;
+ *snp->match = *local_mp;
+ }
+ }
+ /* end of copy optimization */
+ }
+
+ /* check if we can do rle */
+ snp = snp_arr[len];
+ if(best_rle_snp == NULL ||
+ snp->index + 65535 < best_rle_snp->index ||
+ snp->index + ctx->rle_r[snp->index] < best_rle_snp->index)
+ {
+ /* best_rle_snp can't be reached by rle from snp, reset it*/
+ if(ctx->rle[snp->index] > 0)
+ {
+ best_rle_snp = snp;
+ }
+ else
+ {
+ best_rle_snp = NULL;
+ }
+ }
+ else if(ctx->rle[snp->index] > 0 &&
+ snp->index + ctx->rle_r[snp->index] >= best_rle_snp->index)
+ {
+ float best_rle_score;
+ float total_best_rle_score;
+ float snp_rle_score;
+ float total_snp_rle_score;
+ match rle_mp;
+
+
+ /* snp and best_rle_snp is the same rle area,
+ * let's see which is best */
+#undef NEW_STYLE
+#ifdef NEW_STYLE
+ rle_mp->len = best_rle_snp->index - snp->index;
+#else
+ rle_mp->len = ctx->rle[best_rle_snp->index];
+#endif
+ rle_mp->offset = 1;
+ best_rle_score = f(rle_mp, emd);
+ total_best_rle_score = best_rle_snp->total_score +
+ best_rle_score;
+
+#ifdef NEW_STYLE
+ snp_rle_score = 0.0;
+#else
+ rle_mp->len = ctx->rle[snp->index];
+ rle_mp->offset = 1;
+ snp_rle_score = f(rle_mp, emd);
+#endif
+ total_snp_rle_score = snp->total_score + snp_rle_score;
+
+ if(total_snp_rle_score <= total_best_rle_score)
+ {
+ /* yes, the snp is a better rle than best_rle_snp */
+ best_rle_snp = snp;
+ }
+ }
+ if(best_rle_snp != NULL && best_rle_snp != snp)
+ {
+ float rle_score;
+ float total_rle_score;
+ /* check if rle is better */
+ match local_mp;
+ local_mp->len = best_rle_snp->index - snp->index;
+ local_mp->offset = 1;
+
+ rle_score = f(local_mp, emd);
+ total_rle_score = best_rle_snp->total_score + rle_score;
+
+ if(snp->total_score > total_rle_score)
+ {
+ /*here it is good to do rle instead of crunch */
+ snp->total_score = total_rle_score;
+ snp->total_offset = best_rle_snp->total_offset + 1;
+ snp->prev = best_rle_snp;
+
+ *snp->match = *local_mp;
+ }
+ }
+ /* end of rle optimization */
+
+
+ prev_score = snp_arr[len]->total_score;
+ prev_offset_sum = snp_arr[len]->total_offset;
+ while (mp != NULL)
+ {
+ matchp next;
+ int end_len;
+
+ match tmp;
+
+ next = mp->next;
+ end_len = 1;
+#if 0
+ if(next != NULL)
+ {
+ end_len = next->len + (next->offset > 0);
+ }
+#endif
+ *tmp = *mp;
+#if 1
+ tmp->next = NULL;
+#endif
+ for(tmp->len = mp->len; tmp->len >= end_len; --(tmp->len))
+ {
+ float score;
+ float total_score;
+ unsigned int total_offset;
+
+ score = f(tmp, emd);
+ total_score = prev_score + score;
+ total_offset = prev_offset_sum + tmp->offset;
+ snp = snp_arr[len - tmp->len];
+
+ if ((total_score < 100000000.0) &&
+ (snp->match->len == 0 ||
+ total_score < snp->total_score ||
+ (total_score == snp->total_score &&
+#if 1
+ (tmp->offset == 0 ||
+ (snp->match->len == tmp->len &&
+ (total_offset <= snp->total_offset))))))
+#endif
+ {
+ snp->index = len - tmp->len;
+
+ *snp->match = *tmp;
+ snp->total_offset = total_offset;
+ snp->total_score = total_score;
+ snp->prev = snp_arr[len];
+ }
+ //LOG(LOG_DUMP, ("\n"));
+ }
+ mp = next;
+ }
+
+ /* slow way to get to the next node for cur */
+ --len;
+ ++best_copy_len;
+ }
+ if(len > 0 && mp == NULL)
+ {
+ LOG(LOG_ERROR, ("No matches at len %d.\n", len));
+ }
+
+
+ return snp_arr[0];
+}
+
+void matchp_snp_get_enum(const_search_nodep snp, /* IN */
+ matchp_snp_enum snpe) /* IN/OUT */
+{
+ snpe->startp = snp;
+ snpe->currp = snp;
+}
+
+const_matchp matchp_snp_enum_get_next(void *matchp_snp_enum)
+{
+ matchp_snp_enump snpe;
+ const_matchp val;
+
+ snpe = matchp_snp_enum;
+
+ val = NULL;
+ while (snpe->currp != NULL && val == NULL)
+ {
+ val = snpe->currp->match;
+ snpe->currp = snpe->currp->prev;
+ }
+
+ if (snpe->currp == NULL)
+ {
+ snpe->currp = snpe->startp;
+ }
+ return val;
+}
+
+void
+chunkpool_init(struct chunkpool *ctx, int size)
+{
+ ctx->chunk_size = size;
+ ctx->chunk = -1;
+ ctx->chunk_max = (0x1fffff / size) * size;
+ ctx->chunk_pos = ctx->chunk_max;
+}
+
+void
+chunkpool_free2(struct chunkpool *ctx, cb_free *f)
+{
+ while(ctx->chunk >= 0)
+ {
+ if(f != NULL)
+ {
+ do
+ {
+ ctx->chunk_pos -= ctx->chunk_size;
+ f((char*)ctx->chunks[ctx->chunk] + ctx->chunk_pos);
+ }
+ while(ctx->chunk_pos > 0);
+ ctx->chunk_pos = ctx->chunk_max;
+ }
+ free(ctx->chunks[ctx->chunk]);
+ ctx->chunk -= 1;
+ }
+ ctx->chunk_size = -1;
+ ctx->chunk_max = -1;
+ ctx->chunk_pos = -1;
+}
+
+void
+chunkpool_free(struct chunkpool *ctx)
+{
+ chunkpool_free2(ctx, NULL);
+}
+
+void *
+chunkpool_malloc(struct chunkpool *ctx)
+{
+ void *p;
+ if(ctx->chunk_pos == ctx->chunk_max)
+ {
+ void *m;
+ if(ctx->chunk == CHUNKPOOL_CHUNKS_MAX - 1)
+ {
+ LOG(LOG_ERROR, ("out of chunks in file %s, line %d\n",
+ __FILE__, __LINE__));
+ LOG(LOG_BRIEF, ("chunk_size %d\n", ctx->chunk_size));
+ LOG(LOG_BRIEF, ("chunk_max %d\n", ctx->chunk_max));
+ LOG(LOG_BRIEF, ("chunk %d\n", ctx->chunk));
+ exit(-1);
+ }
+ m = malloc(ctx->chunk_max);
+ if (m == NULL)
+ {
+ LOG(LOG_ERROR, ("out of memory error in file %s, line %d\n",
+ __FILE__, __LINE__));
+ exit(-1);
+ }
+ ctx->chunk += 1;
+ ctx->chunks[ctx->chunk] = m;
+ ctx->chunk_pos = 0;
+ }
+ p = (char*)ctx->chunks[ctx->chunk] + ctx->chunk_pos;
+ ctx->chunk_pos += ctx->chunk_size;
+ return p;
+}
+
+void *
+chunkpool_calloc(struct chunkpool *ctx)
+{
+ void *p = chunkpool_malloc(ctx);
+ memset(p, 0, ctx->chunk_size);
+ return p;
+}
+
+static struct crunch_options default_options[1] = { CRUNCH_OPTIONS_DEFAULT };
+
+int do_output(match_ctx ctx,
+ search_nodep snp,
+ encode_match_data emd,
+ encode_match_f * f,
+ struct membuf *outbuf,
+ int *literal_sequences_used)
+{
+ int pos;
+ int pos_diff;
+ int max_diff;
+ int diff;
+ int copy_used = 0;
+ output_ctxp old;
+ output_ctx out;
+
+ output_ctx_init(out, outbuf);
+ old = emd->out;
+ emd->out = out;
+
+ pos = output_get_pos(out);
+
+ pos_diff = pos;
+ max_diff = 0;
+
+ output_gamma_code(out, 16);
+ output_bits(out, 1, 0); /* 1 bit out */
+
+ diff = output_get_pos(out) - pos_diff;
+ if(diff > max_diff)
+ {
+ max_diff = diff;
+ }
+
+ while (snp != NULL)
+ {
+ const_matchp mp;
+
+ mp = snp->match;
+ if (mp != NULL && mp->len > 0)
+ {
+ if (mp->offset == 0)
+ {
+ if(mp->len == 1)
+ {
+ /* literal */
+ output_byte(out, ctx->buf[snp->index]);
+ output_bits(out, 1, 1);
+ } else
+ {
+ int i;
+ for(i = 0; i < mp->len; ++i)
+ {
+ output_byte(out, ctx->buf[snp->index + i]);
+ }
+ output_bits(out, 16, mp->len);
+ output_gamma_code(out, 17);
+ output_bits(out, 1, 0);
+ copy_used = 1;
+ }
+ } else
+ {
+ f(mp, emd);
+ output_bits(out, 1, 0);
+ }
+
+ pos_diff += mp->len;
+ diff = output_get_pos(out) - pos_diff;
+ if(diff > max_diff)
+ {
+ max_diff = diff;
+ }
+ }
+ snp = snp->prev;
+ }
+
+ /* output header here */
+ optimal_out(out, emd);
+
+ output_bits_flush(out);
+
+ emd->out = old;
+
+ if(literal_sequences_used != NULL)
+ {
+ *literal_sequences_used = copy_used;
+ }
+
+ return max_diff;
+}
+
+search_nodep
+do_compress(match_ctx ctx, encode_match_data emd,
+ const char *exported_encoding,
+ int max_passes,
+ int use_literal_sequences)
+{
+ matchp_cache_enum mpce;
+ matchp_snp_enum snpe;
+ search_nodep snp;
+ search_nodep best_snp;
+ int pass;
+ float size;
+ float old_size;
+ char prev_enc[100];
+ const char *curr_enc;
+
+ pass = 1;
+ prev_enc[0] = '\0';
+
+ if(exported_encoding != NULL)
+ {
+ optimal_encoding_import(emd, exported_encoding);
+ }
+ else
+ {
+ matchp_cache_get_enum(ctx, mpce);
+ optimal_optimize(emd, matchp_cache_enum_get_next, mpce);
+ }
+
+ best_snp = NULL;
+ old_size = 100000000.0;
+
+ for (;;)
+ {
+ snp = search_buffer(ctx, optimal_encode, emd,
+ use_literal_sequences);
+ if (snp == NULL)
+ {
+ LOG(LOG_ERROR, ("error: search_buffer() returned NULL\n"));
+ exit(-1);
+ }
+
+ size = snp->total_score;
+
+ if (size >= old_size)
+ {
+ search_node_free(snp);
+ break;
+ }
+
+ if (best_snp != NULL)
+ {
+ search_node_free(best_snp);
+ }
+ best_snp = snp;
+ old_size = size;
+ ++pass;
+
+ if(pass > max_passes)
+ {
+ break;
+ }
+
+ optimal_free(emd);
+ optimal_init(emd);
+
+ matchp_snp_get_enum(snp, snpe);
+ optimal_optimize(emd, matchp_snp_enum_get_next, snpe);
+
+ curr_enc = optimal_encoding_export(emd);
+ if (strcmp(curr_enc, prev_enc) == 0)
+ {
+ break;
+ }
+ strcpy(prev_enc, curr_enc);
+ }
+
+ return best_snp;
+}
+
+void match_ctx_init(match_ctx ctx, /* IN/OUT */
+ struct membuf *inbuf, /* IN */
+ int max_len, /* IN */
+ int max_offset, /* IN */
+ int use_imprecise_rle); /* IN */
+void crunch_backwards(struct membuf *inbuf,
+ struct membuf *outbuf,
+ struct crunch_options *options, /* IN */
+ struct crunch_info *info) /* OUT */
+{
+ static match_ctx ctx;
+ encode_match_data emd;
+ search_nodep snp;
+ int outlen;
+ int safety;
+ int copy_used;
+
+ if(options == NULL)
+ {
+ options = default_options;
+ }
+
+ outlen = membuf_memlen(outbuf);
+ emd->out = NULL;
+ optimal_init(emd);
+
+ //LOG(LOG_NORMAL, (" Length of indata: %d bytes.\n", membuf_memlen(inbuf)));
+
+ match_ctx_init(ctx, inbuf, options->max_len, options->max_offset,
+ options->use_imprecise_rle);
+
+
+ emd->out = NULL;
+ optimal_init(emd);
+
+ snp = do_compress(ctx, emd, options->exported_encoding,
+ options->max_passes, options->use_literal_sequences);
+
+ safety = do_output(ctx, snp, emd, optimal_encode, outbuf, ©_used);
+ //LOG(LOG_NORMAL, (" Length of crunched data: %d bytes.\n",membuf_memlen(outbuf) - outlen));
+
+ optimal_free(emd);
+ search_node_free(snp);
+ match_ctx_free(ctx);
+
+ if(info != NULL)
+ {
+ info->literal_sequences_used = copy_used;
+ info->needed_safety_offset = safety;
+ }
+}
+
+void reverse_buffer(char *start, int len)
+{
+ char *end = start + len - 1;
+ char tmp;
+
+ while (start < end)
+ {
+ tmp = *start;
+ *start = *end;
+ *end = tmp;
+
+ ++start;
+ --end;
+ }
+}
+
+void exocrunch(struct membuf *inbuf,
+ struct membuf *outbuf,
+ struct crunch_options *options, /* IN */
+ struct crunch_info *info) /* OUT */
+{
+ int outpos;
+ reverse_buffer(membuf_get(inbuf), membuf_memlen(inbuf));
+ outpos = membuf_memlen(outbuf);
+
+ crunch_backwards(inbuf, outbuf, options, info);
+
+ reverse_buffer(membuf_get(inbuf), membuf_memlen(inbuf));
+ reverse_buffer((char*)membuf_get(outbuf) + outpos,
+ membuf_memlen(outbuf) - outpos);
+}
+
+void exodecrunch(int level,
+ struct membuf *inbuf,
+ struct membuf *outbuf)
+{
+ struct dec_ctx ctx[1];
+ char *enc;
+ enc = dec_ctx_init(ctx, inbuf, outbuf);
+
+ LOG(level, (" Encoding: %s\n", enc));
+
+ dec_ctx_decrunch(ctx);
+ dec_ctx_free(ctx);
+}
+
+void decrunch_backwards(int level,
+ struct membuf *inbuf,
+ struct membuf *outbuf)
+{
+ int outpos;
+ reverse_buffer(membuf_get(inbuf), membuf_memlen(inbuf));
+ outpos = membuf_memlen(outbuf);
+
+ exodecrunch(level, inbuf, outbuf);
+
+ reverse_buffer(membuf_get(inbuf), membuf_memlen(inbuf));
+ reverse_buffer((char*)membuf_get(outbuf) + outpos,
+ membuf_memlen(outbuf) - outpos);
+}
+
+void print_license(void)
+{
+}
+
+void print_base_flags(enum log_level level, const char *default_outfile)
+{
+ LOG(level,
+ (" -o <outfile> sets the outfile name, default is \"%s\"\n",
+ default_outfile));
+ LOG(level,
+ (" -q quiet mode, disables display output\n"
+ " -v displays version and the usage license\n"
+ " -- treats all following arguments as non-options\n"
+ " -? displays this help screen\n"));
+}
+
+void print_crunch_flags(enum log_level level, const char *default_outfile)
+{
+ LOG(level,
+ (" -c compatibility mode, disables the use of literal sequences\n"
+ " -C enable imprecise rle matching, trades result for speed\n"
+ " -e <encoding> uses the given encoding for crunching\n"
+ " -m <offset> sets the maximum sequence offset, default is 65535\n"
+ " -M <length> sets the maximum sequence length, default is 65535\n"
+ " -p <passes> limits the number of optimization passes, default is 65535\n"));
+ print_base_flags(level, default_outfile);
+}
+
+void handle_base_flags(int flag_char, /* IN */
+ const char *flag_arg, /* IN */
+ print_usage_f *print_usage, /* IN */
+ const char *appl, /* IN */
+ const char **default_outfilep) /* IN */
+{
+ switch(flag_char)
+ {
+ case 'o':
+ *default_outfilep = flag_arg;
+ break;
+ case 'q':
+ LOG_SET_LEVEL(LOG_BRIEF);
+ break;
+ case 'v':
+ print_license();
+ exit(0);
+ default:
+ if (flagflag != '?')
+ {
+ LOG(LOG_ERROR,
+ ("error, invalid option \"-%c\"", flagflag));
+ if (flagarg != NULL)
+ {
+ LOG(LOG_ERROR, (" with argument \"%s\"", flagarg));
+ }
+ LOG(LOG_ERROR, ("\n"));
+ }
+ print_usage(appl, LOG_BRIEF, *default_outfilep);
+ exit(0);
+ }
+}
+
+void handle_crunch_flags(int flag_char, /* IN */
+ const char *flag_arg, /* IN */
+ print_usage_f *print_usage, /* IN */
+ const char *appl, /* IN */
+ struct common_flags *flags) /* OUT */
+{
+ struct crunch_options *options = flags->options;
+ switch(flag_char)
+ {
+ case 'c':
+ options->use_literal_sequences = 0;
+ break;
+ case 'C':
+ options->use_imprecise_rle = 1;
+ break;
+ case 'e':
+ options->exported_encoding = flag_arg;
+ break;
+ case 'm':
+ if (str_to_int(flag_arg, &options->max_offset) != 0 ||
+ options->max_offset < 0 || options->max_offset >= 65536)
+ {
+ LOG(LOG_ERROR,
+ ("Error: invalid offset for -m option, "
+ "must be in the range of [0 - 65535]\n"));
+ print_usage(appl, LOG_NORMAL, flags->outfile);
+ exit(-1);
+ }
+ break;
+ case 'M':
+ if (str_to_int(flag_arg, &options->max_len) != 0 ||
+ options->max_len < 0 || options->max_len >= 65536)
+ {
+ LOG(LOG_ERROR,
+ ("Error: invalid offset for -n option, "
+ "must be in the range of [0 - 65535]\n"));
+ print_usage(appl, LOG_NORMAL, flags->outfile);
+ exit(-1);
+ }
+ break;
+ case 'p':
+ if (str_to_int(flag_arg, &options->max_passes) != 0 ||
+ options->max_passes < 1 || options->max_passes >= 65536)
+ {
+ LOG(LOG_ERROR,
+ ("Error: invalid value for -p option, "
+ "must be in the range of [1 - 65535]\n"));
+ print_usage(appl, LOG_NORMAL, flags->outfile);
+ exit(-1);
+ }
+ break;
+ default:
+ handle_base_flags(flag_char, flag_arg, print_usage,
+ appl, &flags->outfile);
+ }
+}
+
+int find_sys(const unsigned char *buf, int target)
+{
+ int outstart = -1;
+ int state = 1;
+ int i = 0;
+ /* skip link and line number */
+ buf += 4;
+ /* exit loop at line end */
+ while(i < 1000 && buf[i] != '\0')
+ {
+ unsigned char *sys_end;
+ int c = buf[i];
+ switch(state)
+ {
+ /* look for and consume sys token */
+ case 1:
+ if((target == -1 &&
+ (c == 0x9e /* cbm */ ||
+ c == 0x8c /* apple 2*/ ||
+ c == 0xbf /* oric 1*/)) ||
+ c == target)
+ {
+ state = 2;
+ }
+ break;
+ /* skip spaces and left parenthesis, if any */
+ case 2:
+ if(strchr(" (", c) != NULL) break;
+ state = 3;
+ /* convert string number to int */
+ case 3:
+ outstart = strtol((char*)(buf + i), (void*)&sys_end, 10);
+ if((buf + i) == sys_end)
+ {
+ /* we got nothing */
+ outstart = -1;
+ }
+ state = 4;
+ break;
+ case 4:
+ break;
+ }
+ ++i;
+ }
+
+ return outstart;
+}
+
+static int ExoUtil_get_byte(FILE *in)
+{
+ int byte = fgetc(in);
+ if(byte == EOF)
+ {
+ LOG(LOG_ERROR, ("Error: unexpected end of xex-file."));
+ fclose(in);
+ exit(-1);
+ }
+ return byte;
+}
+
+static int get_le_word(FILE *in)
+{
+ int word = ExoUtil_get_byte(in);
+ word |= ExoUtil_get_byte(in) << 8;
+ return word;
+}
+
+static int get_be_word(FILE *in)
+{
+ int word = ExoUtil_get_byte(in) << 8;
+ word |= ExoUtil_get_byte(in);
+ return word;
+}
+
+static
+FILE *
+open_file(char *name, int *load_addr)
+{
+ FILE * in;
+ int is_plain = 0;
+ int is_relocated = 0;
+ int load = -3;
+
+ do
+ {
+ char *load_str;
+ char *at_str;
+
+ in = fopen(name, "rb");
+ if (in != NULL)
+ {
+ /* We have succeded in opening the file.
+ * There's no address suffix. */
+ break;
+ }
+
+ /* hmm, let's see if the user is trying to relocate it */
+ load_str = strrchr(name, ',');
+ at_str = strrchr(name, '@');
+ if(at_str != NULL && (load_str == NULL || at_str > load_str))
+ {
+ is_plain = 1;
+ load_str = at_str;
+ }
+
+ if (load_str == NULL)
+ {
+ /* nope, */
+ break;
+ }
+
+ *load_str = '\0';
+ ++load_str;
+ is_relocated = 1;
+
+ /* relocation was requested */
+ if (str_to_int(load_str, &load) != 0)
+ {
+ /* we fail */
+ LOG(LOG_ERROR,
+ (" can't parse load address from \"%s\"\n", load_str));
+ exit(-1);
+ }
+
+ in = fopen(name, "rb");
+
+ } while (0);
+ if (in == NULL)
+ {
+ LOG(LOG_ERROR,
+ (" can't open file \"%s\" for input\n", name));
+ exit(-1);
+ }
+
+ if(!is_plain)
+ {
+ /* read the prg load address */
+ int prg_load = get_le_word(in);
+ if(!is_relocated)
+ {
+ load = prg_load;
+ /* unrelocated prg loading to $ffff is xex */
+ if(prg_load == 0xffff)
+ {
+ /* differentiate this from relocated $ffff files so it is
+ * possible to override the xex auto-detection. */
+ load = -1;
+ }
+ /* unrelocated prg loading to $1616 is Oric tap */
+ else if(prg_load == 0x1616)
+ {
+ load = -2;
+ }
+ }
+ }
+
+ if(load_addr != NULL)
+ {
+ *load_addr = load;
+ }
+ return in;
+}
+
+static void load_xex(unsigned char mem[65536], FILE *in,
+ struct load_info *info)
+{
+ int run = -1;
+ int jsr = -1;
+ int min = 65536, max = 0;
+
+ goto initial_state;
+ for(;;)
+ {
+ int start, end, len;
+
+ start = fgetc(in);
+ if(start == EOF) break;
+ ungetc(start, in);
+
+ start = get_le_word(in);
+ if(start == 0xffff)
+ {
+ /* allowed optional header */
+ initial_state:
+ start = get_le_word(in);
+ }
+ end = get_le_word(in);
+ if(start > 0xffff || end > 0xffff || end < start)
+ {
+ LOG(LOG_ERROR, ("Error: corrupt data in xex-file."));
+ fclose(in);
+ exit(-1);
+ }
+ if(start == 0x2e2 && end == 0x2e3)
+ {
+ /* init vector */
+ jsr = get_le_word(in);
+ LOG(LOG_VERBOSE, ("Found xex initad $%04X.\n", jsr));
+ continue;
+ }
+ if(start == 0x2e0 && end == 0x2e1)
+ {
+ /* run vector */
+ run = get_le_word(in);
+ LOG(LOG_VERBOSE, ("Found xex runad $%04X.\n", run));
+ continue;
+ }
+ ++end;
+ jsr = -1;
+ if(start < min) min = start;
+ if(end > max) max = end;
+
+ len = fread(mem + start, 1, end - start, in);
+ if(len != end - start)
+ {
+ LOG(LOG_ERROR, ("Error: unexpected end of xex-file.\n"));
+ fclose(in);
+ exit(-1);
+ }
+ LOG(LOG_VERBOSE, (" xex chunk loading from $%04X to $%04X\n",
+ start, end));
+ }
+
+ if(run == -1 && jsr != -1) run = jsr;
+
+ info->start = min;
+ info->end = max;
+ info->basic_var_start = -1;
+ info->run = -1;
+ if(run != -1)
+ {
+ info->run = run;
+ }
+}
+
+static void load_oric_tap(unsigned char mem[65536], FILE *in,
+ struct load_info *info)
+{
+ int c;
+ int autostart;
+ int start, end, len;
+
+ /* read oric tap header */
+
+ /* next byte must be 0x16 as we have already read two and must
+ * have at least three */
+ if(ExoUtil_get_byte(in) != 0x16)
+ {
+ LOG(LOG_ERROR, ("Error: fewer than three lead-in bytes ($16) "
+ "in Oric tap-file header.\n"));
+ fclose(in);
+ exit(-1);
+ }
+ /* optionally more 0x16 bytes */
+ while((c = ExoUtil_get_byte(in)) == 0x16);
+ /* next byte must be 0x24 */
+ if(c != 0x24)
+ {
+ LOG(LOG_ERROR, ("Error: bad sync byte after lead-in in Oric tap-file "
+ "header, got $%02X but expected $24\n", c));
+ fclose(in);
+ exit(-1);
+ }
+
+ /* now we are in sync, lets be lenient */
+ ExoUtil_get_byte(in); /* should be 0x0 */
+ ExoUtil_get_byte(in); /* should be 0x0 */
+ ExoUtil_get_byte(in); /* should be 0x0 or 0x80 */
+ autostart = (ExoUtil_get_byte(in) != 0); /* should be 0x0, 0x80 or 0xc7 */
+ end = get_be_word(in) + 1; /* the header end address is inclusive */
+ start = get_be_word(in);
+ ExoUtil_get_byte(in); /* should be 0x0 */
+ /* read optional file name */
+ while(ExoUtil_get_byte(in) != 0x0);
+
+ /* read the data */
+ len = fread(mem + start, 1, end - start, in);
+ if(len != end - start)
+ {
+ LOG(LOG_BRIEF, ("Warning: Oric tap-file contains %d byte(s) data "
+ "less than expected.\n", end - start - len));
+ end = start + len;
+ }
+ LOG(LOG_VERBOSE, (" Oric tap-file loading from $%04X to $%04X\n",
+ start, end));
+
+ /* fill in the fields */
+ info->start = start;
+ info->end = end;
+ info->run = -1;
+ info->basic_var_start = -1;
+ if(autostart)
+ {
+ info->run = start;
+ }
+ if(info->basic_txt_start >= start &&
+ info->basic_txt_start < end)
+ {
+ info->basic_var_start = end - 1;
+ }
+}
+
+static void load_prg(unsigned char mem[65536], FILE *in,
+ struct load_info *info)
+{
+ int len;
+ len = fread(mem + info->start, 1, 65536 - info->start, in);
+
+ info->end = info->start + len;
+ info->basic_var_start = -1;
+ info->run = -1;
+ if(info->basic_txt_start >= info->start &&
+ info->basic_txt_start < info->end)
+ {
+ info->basic_var_start = info->end;
+ }
+}
+
+void load_located(char *filename, unsigned char mem[65536],
+ struct load_info *info)
+{
+ int load;
+ FILE *in;
+
+ in = open_file(filename, &load);
+ if(load == -1)
+ {
+ /* file is an xex file */
+ load_xex(mem, in, info);
+ }
+ else if(load == -2)
+ {
+ /* file is an oric tap file */
+ load_oric_tap(mem, in, info);
+ }
+ else
+ {
+ /* file is a located plain file or a prg file */
+ info->start = load;
+ load_prg(mem, in, info);
+ }
+ fclose(in);
+
+ LOG(LOG_NORMAL,
+ (" filename: \"%s\", loading from $%04X to $%04X\n",
+ filename, info->start, info->end));
+}
+
+/* returns 0 if ok, 1 otherwise */
+int str_to_int(const char *str, int *value)
+{
+ int status = 0;
+ do {
+ char *str_end;
+ long lval;
+
+ /* base 0 is auto detect */
+ int base = 0;
+
+ if (*str == '\0')
+ {
+ /* no string to parse */
+ status = 1;
+ break;
+ }
+
+ if (*str == '$')
+ {
+ /* a $ prefix specifies base 16 */
+ ++str;
+ base = 16;
+ }
+
+ lval = strtol(str, &str_end, base);
+
+ if(*str_end != '\0')
+ {
+ /* there is garbage in the string */
+ status = 1;
+ break;
+ }
+
+ if(value != NULL)
+ {
+ /* all is well, set the out parameter */
+ *value = (int)lval;
+ }
+ } while(0);
+
+ return status;
+}
+
+const char *fixup_appl(char *appl)
+{
+ char *applp;
+
+ /* strip pathprefix from appl */
+ applp = strrchr(appl, '\\');
+ if (applp != NULL)
+ {
+ appl = applp + 1;
+ }
+ applp = strrchr(appl, '/');
+ if (applp != NULL)
+ {
+ appl = applp + 1;
+ }
+ /* strip possible exe suffix */
+ applp = appl + strlen(appl) - 4;
+ if(strcmp(applp, ".exe") == 0 || strcmp(applp, ".EXE") == 0)
+ {
+ *applp = '\0';
+ }
+ return appl;
+}
+
+char *get(struct membuf *buf)
+{
+ return membuf_get(buf);
+}
+
+int get_byte(struct dec_ctx *ctx)
+{
+ int c;
+ if(ctx->inpos == ctx->inend)
+ {
+ LOG(LOG_ERROR, ("unexpected end of input data\n"));
+ exit(-1);
+ }
+ c = ctx->inbuf[ctx->inpos++];
+
+ return c;
+}
+
+int
+get_bits(struct dec_ctx *ctx, int count)
+{
+ int val;
+
+ val = 0;
+
+ /*printf("get_bits: count = %d", count);*/
+ while(count-- > 0) {
+ if((ctx->bitbuf & 0x1FF) == 1) {
+ ctx->bitbuf = get_byte(ctx) | 0x100;
+ }
+ val <<= 1;
+ val |= ctx->bitbuf & 0x1;
+ ctx->bitbuf >>= 1;
+ /*printf("bit read %d\n", val &1);*/
+ ctx->bits_read++;
+ }
+ /*printf(" val = %d\n", val);*/
+ return val;
+}
+
+int
+get_gamma_code(struct dec_ctx *ctx)
+{
+ int gamma_code;
+ /* get bitnum index */
+ gamma_code = 0;
+ while(get_bits(ctx, 1) == 0)
+ {
+ ++gamma_code;
+ }
+ return gamma_code;
+}
+
+int
+get_cooked_code_phase2(struct dec_ctx *ctx, int index)
+{
+ int base;
+ struct dec_table *tp;
+ tp = ctx->t;
+
+ base = tp->table_lo[index] | (tp->table_hi[index] << 8);
+ return base + get_bits(ctx, tp->table_bi[index]);
+}
+
+static
+void
+table_init(struct dec_ctx *ctx, struct dec_table *tp) /* IN/OUT */
+{
+ int i;
+ unsigned int a = 0;
+ unsigned int b = 0;
+
+ tp->table_bit[0] = 2;
+ tp->table_bit[1] = 4;
+ tp->table_bit[2] = 4;
+
+ tp->table_off[0] = 48;
+ tp->table_off[1] = 32;
+ tp->table_off[2] = 16;
+
+ for(i = 0; i < 52; ++i)
+ {
+ if(i & 0xF)
+ {
+ a += 1 << b;
+ } else
+ {
+ a = 1;
+ }
+
+ tp->table_lo[i] = a & 0xFF;
+ tp->table_hi[i] = a >> 8;
+
+ b = get_bits(ctx, 4);
+
+ tp->table_bi[i] = b;
+
+ }
+}
+
+char *
+table_dump(struct dec_table *tp)
+{
+ int i, j;
+ static char buf[100];
+ char *p = buf;
+
+ for(i = 0; i < 16; ++i)
+ {
+ p += sprintf(p, "%X", tp->table_bi[i]);
+ }
+ for(j = 0; j < 3; ++j)
+ {
+ int start;
+ int end;
+ p += sprintf(p, ",");
+ start = tp->table_off[j];
+ end = start + (1 << tp->table_bit[j]);
+ for(i = start; i < end; ++i)
+ {
+ p += sprintf(p, "%X", tp->table_bi[i]);
+ }
+ }
+ return buf;
+}
+
+char *
+dec_ctx_init(struct dec_ctx *ctx, struct membuf *inbuf, struct membuf *outbuf)
+{
+ char *encoding;
+ ctx->bits_read = 0;
+
+ ctx->inbuf = membuf_get(inbuf);
+ ctx->inend = membuf_memlen(inbuf);
+ ctx->inpos = 0;
+
+ ctx->outbuf = outbuf;
+
+ /* init bitbuf */
+ ctx->bitbuf = get_byte(ctx);
+
+ /* init tables */
+ table_init(ctx, ctx->t);
+ encoding = table_dump(ctx->t);
+ return encoding;
+}
+
+void dec_ctx_free(struct dec_ctx *ctx)
+{
+}
+
+void dec_ctx_decrunch(struct dec_ctx ctx[1])
+{
+ int bits;
+ int val;
+ int i;
+ int len;
+ int offset;
+ int src = 0;
+
+ for(;;)
+ {
+ int literal = 0;
+ bits = ctx->bits_read;
+ if(get_bits(ctx, 1))
+ {
+ /* literal */
+ len = 1;
+
+ literal = 1;
+ goto literal;
+ }
+
+ val = get_gamma_code(ctx);
+ if(val == 16)
+ {
+ /* done */
+ break;
+ }
+ if(val == 17)
+ {
+ len = get_bits(ctx, 16);
+ literal = 1;
+
+ goto literal;
+ }
+
+ len = get_cooked_code_phase2(ctx, val);
+
+ i = (len > 3 ? 3 : len) - 1;
+
+ val = ctx->t->table_off[i] + get_bits(ctx, ctx->t->table_bit[i]);
+ offset = get_cooked_code_phase2(ctx, val);
+
+ src = membuf_memlen(ctx->outbuf) - offset;
+
+ literal:
+ do {
+ if(literal)
+ {
+ val = get_byte(ctx);
+ }
+ else
+ {
+ val = get(ctx->outbuf)[src++];
+ }
+ membuf_append_char(ctx->outbuf, val);
+ } while (--len > 0);
+
+ }
+}
+
+int flagind = 1;
+int flagflag = '?';
+const char *flagarg = NULL;
+
+static void reverse(char **buf, int pos1, int pos2)
+{
+ char **buf1;
+ char **buf2;
+ char *tmp;
+
+ buf1 = buf + pos1;
+ buf2 = buf + pos2 - 1;
+
+ while (buf1 < buf2)
+ {
+ tmp = *buf1;
+ *buf1 = *buf2;
+ *buf2 = tmp;
+
+ ++buf1;
+ --buf2;
+ }
+}
+
+
+int getflag(int argc, char **argv, const char *flags)
+{
+ int argstart, flagstart, c;
+ const char *flagp;
+
+ c = -1;
+ flagarg = NULL;
+ argstart = flagind;
+ flagstart = argc;
+
+ /* skip over non-flags */
+ while (flagind < argc && argv[flagind][0] != '-')
+ {
+ ++flagind;
+ }
+ if (flagind == argc)
+ {
+ /* no more args */
+ flagind = argstart;
+ return c;
+ }
+ /* we have an arg to work with */
+ do
+ {
+ flagstart = flagind;
+ if (argv[flagind][1] == '-' && argv[flagind][2] == '\0')
+ {
+ /* stop parsing at '--' flag */
+ break;
+ }
+ c = flagflag = argv[flagind][1];
+ if (c == ':' || c == '\0')
+ {
+ /* this is an illegal flag */
+ c = '?';
+ break;
+ }
+ /* flag with arg */
+ if (argv[flagind][2] != '\0')
+ {
+ /* flag-arg in same argv[] */
+ flagarg = argv[flagind] + 2;
+ }
+ flagp = strchr(flags, c);
+ if (flagp == NULL)
+ {
+ /* this is an unknown flag */
+ c = '?';
+ break;
+ }
+ if (flagarg != NULL || flagp[1] != ':')
+ {
+ if (flagarg != NULL && flagp[1] != ':')
+ {
+ /* error, a simple flag with an argument */
+ c = '?';
+ }
+ break;
+ }
+
+ /* flag-arg is in the next argv[] */
+ if (flagind + 1 == argc)
+ {
+ /* auahh, no flag-arg */
+ flagstart = argstart;
+ c = '?';
+ break;
+ }
+ flagarg = argv[++flagind];
+ } while (0);
+ /* skip to next arg */
+ ++flagind;
+
+ if (flagstart < flagind && argstart < flagstart)
+ {
+ /* we have found some args
+ * we have also skipped over some non-args
+ * shuffle the non-flag arg to the end of argv */
+ reverse(argv, argstart, flagstart);
+ reverse(argv, flagstart, flagind);
+ reverse(argv, argstart, flagind);
+ }
+ flagind = argstart + flagind - flagstart;
+
+ return c;
+}
+
+struct log_output {
+ enum log_level min;
+ enum log_level max;
+ FILE *stream;
+ log_formatter_f *f;
+};
+
+struct log_ctx {
+ enum log_level level;
+ int out_len;
+ struct log_output *out;
+ int buf_len;
+ char *buf;
+};
+
+struct log_ctx *G_log_ctx = NULL;
+enum log_level G_log_level = LOG_MIN;
+enum log_level G_log_log_level = 0;
+
+struct log_ctx *log_new(void)
+{
+ struct log_ctx *ctx;
+
+ ctx = malloc(sizeof(*ctx));
+ if (ctx == NULL)
+ {
+ fprintf(stderr,
+ "fatal error, can't allocate memory for log context\n");
+ exit(1);
+ }
+ ctx->level = LOG_NORMAL;
+ ctx->out_len = 0;
+ ctx->out = NULL;
+ ctx->buf_len = 0;
+ ctx->buf = NULL;
+
+ return ctx;
+}
+
+/* log_delete closes all added output streams
+ * and files except for stdout and stderr
+ */
+void log_delete(struct log_ctx *ctx)
+{
+ int i;
+
+ for (i = 0; i < ctx->out_len; ++i)
+ {
+ FILE *file = ctx->out[i].stream;
+ if (file != stderr && file != stdout)
+ {
+ fclose(file);
+ }
+ }
+ free(ctx->out);
+ free(ctx->buf);
+ free(ctx);
+}
+
+void log_set_level(struct log_ctx *ctx, /* IN/OUT */
+ enum log_level level) /* IN */
+{
+ ctx->level = level;
+}
+
+void log_add_output_stream(struct log_ctx *ctx, /* IN/OUT */
+ enum log_level min, /* IN */
+ enum log_level max, /* IN */
+ log_formatter_f * default_f, /* IN */
+ FILE * out_stream) /* IN */
+{
+ struct log_output *out;
+
+ ctx->out_len += 1;
+ ctx->out = realloc(ctx->out, ctx->out_len * sizeof(*(ctx->out)));
+ if (ctx->out == NULL)
+ {
+ fprintf(stderr,
+ "fatal error, can't allocate memory for log output\n");
+ exit(1);
+ }
+ out = &(ctx->out[ctx->out_len - 1]);
+ out->min = min;
+ out->max = max;
+ out->stream = out_stream;
+ out->f = default_f;
+}
+
+void raw_log_formatter(FILE * out, /* IN */
+ enum log_level level, /* IN */
+ const char *context, /* IN */
+ const char *log) /* IN */
+{
+ fprintf(out, "%s", log);
+ fflush(out);
+}
+
+void log_vlog(struct log_ctx *ctx, /* IN */
+ enum log_level level, /* IN */
+ const char *context, /* IN */
+ log_formatter_f * f, /* IN */
+ const char *printf_str, /* IN */
+ va_list argp)
+{
+ int len;
+ int i;
+
+ if (ctx->level < level)
+ {
+ /* don't log this */
+ return;
+ }
+
+ len = 0;
+ do
+ {
+ if (len >= ctx->buf_len)
+ {
+ ctx->buf_len = len + 1024;
+ ctx->buf = realloc(ctx->buf, ctx->buf_len);
+ if (ctx->buf == NULL)
+ {
+ fprintf(stderr,
+ "fatal error, can't allocate memory for log log\n");
+ exit(1);
+ }
+ }
+ len = vsnprintf(ctx->buf, ctx->buf_len, printf_str, argp);
+ }
+
+ while (len >= ctx->buf_len);
+
+ for (i = 0; i < ctx->out_len; ++i)
+ {
+ struct log_output *o = &ctx->out[i];
+ log_formatter_f *of = f;
+
+ if (level >= o->min && level <= o->max)
+ {
+ /* generate log for this output */
+ if (of == NULL)
+ {
+ of = o->f;
+ }
+ if (of != NULL)
+ {
+ of(o->stream, level, context, ctx->buf);
+ } else
+ {
+ fprintf(o->stream, "%s\n", ctx->buf);
+ fflush(o->stream);
+ }
+ }
+ }
+}
+
+void log_log_default(const char *printf_str, /* IN */
+ ...)
+{
+ va_list argp;
+ va_start(argp, printf_str);
+ log_vlog(G_log_ctx, G_log_log_level,
+ NULL, raw_log_formatter, printf_str, argp);
+}
+
+void log_log(struct log_ctx *ctx, /* IN */
+ enum log_level level, /* IN */
+ const char *context, /* IN */
+ log_formatter_f * f, /* IN */
+ const char *printf_str, /* IN */
+ ...)
+{
+ va_list argp;
+ va_start(argp, printf_str);
+ log_vlog(ctx, level, context, f, printf_str, argp);
+}
+
+void hex_dump(int level, unsigned char *p, int len)
+{
+#if 0
+ int i;
+ int j;
+ for(i = 0; i < len;)
+ {
+ LOG(level, ("%02x", p[i]));
+ ++i;
+ if(i == len || (i & 15) == 0)
+ {
+ LOG(level, ("\t\""));
+ for(j = (i - 1) & ~15; j < i; ++j)
+ {
+ unsigned char c = p[j];
+ if(!isprint(c))
+ {
+ c = '.';
+ }
+ LOG(level, ("%c", c));
+ }
+ LOG(level, ("\"\n"));
+ }
+ else
+ {
+ LOG(level, (","));
+ }
+ }
+#endif
+}
+
+struct match_node {
+ int index;
+ struct match_node *next;
+};
+
+static
+const_matchp matches_calc(match_ctx ctx, /* IN/OUT */
+ int index); /* IN */
+
+matchp match_new(match_ctx ctx, /* IN/OUT */
+ matchp *mpp,
+ int len,
+ int offset)
+{
+ matchp m = chunkpool_malloc(ctx->m_pool);
+
+ if(len == 0)
+ {
+ LOG(LOG_ERROR, ("tried to allocate len0 match.\n"));
+ *(volatile int*)0;
+ }
+ if(len > 65535)
+ {
+ len = 65535;
+ }
+
+ m->len = len;
+ m->offset = offset;
+
+ /* insert new node in list */
+ m->next = *mpp;
+ *mpp = m;
+
+ return m;
+}
+
+
+void match_ctx_init(match_ctx ctx, /* IN/OUT */
+ struct membuf *inbuf, /* IN */
+ int max_len, /* IN */
+ int max_offset, /* IN */
+ int use_imprecise_rle) /* IN */
+{
+ struct match_node *np;
+
+ int buf_len = membuf_memlen(inbuf);
+ const unsigned char *buf = membuf_get(inbuf);
+
+ int c, i;
+ int val;
+
+ ctx->info = calloc(buf_len + 1, sizeof(*ctx->info));
+ ctx->rle = calloc(buf_len + 1, sizeof(*ctx->rle));
+ ctx->rle_r = calloc(buf_len + 1, sizeof(*ctx->rle_r));
+
+ chunkpool_init(ctx->m_pool, sizeof(match));
+
+ ctx->max_offset = max_offset;
+ ctx->max_len = max_len;
+
+ ctx->buf = buf;
+ ctx->len = buf_len;
+
+ val = buf[0];
+ for (i = 1; i < buf_len; ++i)
+ {
+ if (buf[i] == val)
+ {
+ int len = ctx->rle[i - 1] + 1;
+ if(len > 65535)
+ {
+ len = 0;
+ }
+ ctx->rle[i] = len;
+ } else
+ {
+ ctx->rle[i] = 0;
+ }
+ val = buf[i];
+ }
+
+ for (i = buf_len - 2; i >= 0; --i)
+ {
+ if (ctx->rle[i] < ctx->rle[i + 1])
+ {
+ ctx->rle_r[i] = ctx->rle_r[i + 1] + 1;
+ } else
+ {
+ ctx->rle_r[i] = 0;
+ }
+ }
+
+ /* add extra nodes to rle sequences */
+ for(c = 0; c < 256; ++c)
+ {
+ static char rle_map[65536];
+ struct match_node *prev_np;
+ unsigned short int rle_len;
+
+ /* for each possible rle char */
+ memset(rle_map, 0, sizeof(rle_map));
+ prev_np = NULL;
+ for (i = 0; i < buf_len; ++i)
+ {
+ /* must be the correct char */
+ if(buf[i] != c)
+ {
+ continue;
+ }
+
+ rle_len = ctx->rle[i];
+ if(!rle_map[rle_len] && ctx->rle_r[i] > 16)
+ {
+ /* no previous lengths and not our primary length*/
+ continue;
+ }
+
+ if (use_imprecise_rle &&
+ ctx->rle_r[i] != 0 && ctx->rle[i] != 0)
+ {
+ continue;
+ }
+
+ np = chunkpool_malloc(ctx->m_pool);
+ np->index = i;
+ np->next = NULL;
+ rle_map[rle_len] = 1;
+
+ LOG(LOG_DUMP, ("0) c = %d, added np idx %d -> %d\n", c, i, 0));
+
+ /* if we have a previous entry, let's chain it together */
+ if(prev_np != NULL)
+ {
+ LOG(LOG_DUMP, ("1) c = %d, pointed np idx %d -> %d\n",
+ c, prev_np->index, i));
+ prev_np->next = np;
+ }
+
+ ctx->info[i]->single = np;
+ prev_np = np;
+ }
+
+ memset(rle_map, 0, sizeof(rle_map));
+ prev_np = NULL;
+ for (i = buf_len - 1; i >= 0; --i)
+ {
+ /* must be the correct char */
+ if(buf[i] != c)
+ {
+ continue;
+ }
+
+ rle_len = ctx->rle_r[i];
+ np = ctx->info[i]->single;
+ if(np == NULL)
+ {
+ if(rle_map[rle_len] && prev_np != NULL && rle_len > 0)
+ {
+ np = chunkpool_malloc(ctx->m_pool);
+ np->index = i;
+ np->next = prev_np;
+ ctx->info[i]->single = np;
+
+ LOG(LOG_DEBUG, ("2) c = %d, added np idx %d -> %d\n",
+ c, i, prev_np->index));
+ }
+ }
+ else
+ {
+ prev_np = np;
+ }
+
+ if(ctx->rle_r[i] > 0)
+ {
+ continue;
+ }
+ rle_len = ctx->rle[i] + 1;
+ rle_map[rle_len] = 1;
+ }
+ }
+
+
+ for (i = buf_len - 1; i >= 0; --i)
+ {
+ const_matchp matches;
+
+ /* let's populate the cache */
+ matches = matches_calc(ctx, i);
+
+ /* add to cache */
+ ctx->info[i]->cache = matches;
+
+ }
+
+}
+
+void match_ctx_free(match_ctx ctx) /* IN/OUT */
+{
+ chunkpool_free(ctx->m_pool);
+ free(ctx->info);
+ free(ctx->rle);
+ free(ctx->rle_r);
+}
+
+void dump_matches(int level, matchp mp)
+{
+ if (mp == NULL)
+ {
+ LOG(level, (" (NULL)\n"));
+ } else
+ {
+ if(mp->offset > 0)
+ {
+ LOG(level, (" offset %d, len %d\n", mp->offset, mp->len));
+ }
+ if (mp->next != NULL)
+ {
+ dump_matches(level, mp->next);
+ }
+ }
+}
+
+const_matchp matches_get(match_ctx ctx, /* IN/OUT */
+ int index) /* IN */
+{
+ return ctx->info[index]->cache;
+}
+
+/* this needs to be called with the indexes in
+ * reverse order */
+const_matchp matches_calc(match_ctx ctx, /* IN/OUT */
+ int index) /* IN */
+{
+ const unsigned char *buf;
+
+ matchp matches;
+ matchp mp;
+ struct match_node *np;
+
+ buf = ctx->buf;
+ matches = NULL;
+
+ LOG(LOG_DUMP, ("index %d, char '%c', rle %d, rle_r %d\n",
+ index, buf[index], ctx->rle[index],
+ ctx->rle_r[index]));
+
+ /* proces the literal match and add it to matches */
+ mp = match_new(ctx, &matches, 1, 0);
+
+ /* get possible match */
+ np = ctx->info[index]->single;
+ if(np != NULL)
+ {
+ np = np->next;
+ }
+ for (; np != NULL; np = np->next)
+ {
+ int mp_len;
+ int len;
+ int pos;
+ int offset;
+
+ /* limit according to max offset */
+ if(np->index > index + ctx->max_offset)
+ {
+ break;
+ }
+
+ LOG(LOG_DUMP, ("find lengths for index %d to index %d\n",
+ index, np->index));
+
+ /* get match len */
+ mp_len = mp->offset > 0 ? mp->len : 0;
+ LOG(LOG_DUMP, ("0) comparing with current best [%d] off %d len %d\n",
+ index, mp->offset, mp_len));
+
+ offset = np->index - index;
+ len = mp_len;
+ pos = index + 1 - len;
+ /* Compare the first <previous len> bytes backwards. We can
+ * skip some comparisons by increasing by the rle count. We
+ * don't need to compare the first byte, hence > 1 instead of
+ * > 0 */
+ while(len > 1 && buf[pos] == buf[pos + offset])
+ {
+#if 1
+ int offset1 = ctx->rle_r[pos];
+ int offset2 = ctx->rle_r[pos + offset];
+ int offset = offset1 < offset2 ? offset1 : offset2;
+
+ LOG(LOG_DUMP, ("1) compared sucesssfully [%d] %d %d\n",
+ index, pos, pos + offset));
+
+ len -= 1 + offset;
+ pos += 1 + offset;
+#else
+ --len;
+ ++pos;
+#endif
+ }
+ if(len > 1)
+ {
+ /* sequence length too short, skip this match */
+ continue;
+ }
+
+ if(offset < 17)
+ {
+ /* allocate match struct and add it to matches */
+ mp = match_new(ctx, &matches, 1, offset);
+ }
+
+ /* Here we know that the current match is atleast as long as
+ * the previuos one. let's compare further. */
+ len = mp_len;
+ pos = index - len;
+ while(len <= ctx->max_len &&
+ pos >= 0 && buf[pos] == buf[pos + offset])
+ {
+ LOG(LOG_DUMP, ("2) compared sucesssfully [%d] %d %d\n",
+ index, pos, pos + offset));
+ ++len;
+ --pos;
+ }
+ if(len > mp_len)
+ {
+ /* allocate match struct and add it to matches */
+ mp = match_new(ctx, &matches, index - pos, offset);
+ }
+ if (len > ctx->max_len)
+ {
+ break;
+ }
+ if(pos < 0)
+ {
+ /* we have reached the eof, no better matches can be found */
+ break;
+ }
+ }
+ LOG(LOG_DEBUG, ("adding matches for index %d to cache\n", index));
+ dump_matches(LOG_DEBUG, matches);
+
+ return matches;
+}
+
+static
+int
+matchp_keep_this(const_matchp mp)
+{
+ int val = 1;
+ /* if we want to ignore this matchp then return true else false */
+ if(mp->len == 1)
+ {
+ if(mp->offset > 32)
+ {
+ val = 0;
+ }
+ }
+ return val;
+}
+
+static
+void
+matchp_cache_peek(struct match_ctx *ctx, int pos,
+ const_matchp *litpp, const_matchp *seqpp,
+ matchp lit_tmp, matchp val_tmp)
+{
+ const_matchp litp, seqp, val;
+
+ seqp = NULL;
+ litp = NULL;
+ if(pos >= 0)
+ {
+ val = matches_get(ctx, pos);
+ litp = val;
+ while(litp->offset != 0)
+ {
+ litp = litp->next;
+ }
+
+ /* inject extra rle match */
+ if(ctx->rle_r[pos] > 0)
+ {
+ val_tmp->offset = 1;
+ val_tmp->len = ctx->rle[pos] + 1;
+ val_tmp->next = (matchp)val;
+ val = val_tmp;
+ LOG(LOG_DEBUG, ("injecting rle val(%d,%d)\n",
+ val->len, val->offset));
+ }
+
+ while(val != NULL)
+ {
+ if(val->offset != 0)
+ {
+ if(matchp_keep_this(val))
+ {
+ if(seqp == NULL || val->len > seqp->len ||
+ (val->len == seqp->len && val->offset < seqp->offset))
+ {
+ seqp = val;
+ }
+ }
+ if(litp->offset == 0 || litp->offset > val->offset)
+ {
+ LOG(LOG_DEBUG, ("val(%d,%d)", val->len, val->offset));
+ if(lit_tmp != NULL)
+ {
+ int diff;
+ match tmp2;
+ *tmp2 = *val;
+ tmp2->len = 1;
+ diff = ctx->rle[pos + val->offset];
+ if(tmp2->offset > diff)
+ {
+ tmp2->offset -= diff;
+ }
+ else
+ {
+ tmp2->offset = 1;
+ }
+ LOG(LOG_DEBUG, ("=> litp(%d,%d)",
+ tmp2->len, tmp2->offset));
+ if(matchp_keep_this(tmp2))
+ {
+ LOG(LOG_DEBUG, (", keeping"));
+ *lit_tmp = *tmp2;
+ litp = lit_tmp;
+ }
+ }
+ LOG(LOG_DEBUG, ("\n"));
+ }
+ }
+ val = val->next;
+ }
+ }
+#if 0
+ LOG(LOG_NORMAL, ("[%05d]: ", pos));
+ if(litp == NULL)
+ LOG(LOG_NORMAL, ("litp(NULL)"));
+ else
+ LOG(LOG_NORMAL, ("litp(%d,%d)", litp->len, litp->offset));
+
+ if(seqp == NULL)
+ LOG(LOG_NORMAL, ("seqp(NULL)"));
+ else
+ LOG(LOG_NORMAL, ("seqp(%d,%d)", seqp->len, seqp->offset));
+
+ LOG(LOG_NORMAL, ("\n"));
+#endif
+
+ if(litpp != NULL) *litpp = litp;
+ if(seqpp != NULL) *seqpp = seqp;
+}
+
+void matchp_cache_get_enum(match_ctx ctx, /* IN */
+ matchp_cache_enum mpce) /* IN/OUT */
+{
+ mpce->ctx = ctx;
+ mpce->pos = ctx->len - 1;
+ /*mpce->next = NULL;*/ /* two iterations */
+ mpce->next = (void*)mpce; /* just one */
+}
+
+const_matchp matchp_cache_enum_get_next(void *matchp_cache_enum)
+{
+ const_matchp val, lit, seq;
+ matchp_cache_enump mpce;
+
+ mpce = matchp_cache_enum;
+
+ restart:
+ matchp_cache_peek(mpce->ctx, mpce->pos, &lit, &seq,
+ mpce->tmp1, mpce->tmp2);
+
+ val = lit;
+ if(lit == NULL)
+ {
+ /* the end, reset enum and return NULL */
+ mpce->pos = mpce->ctx->len - 1;
+ if(mpce->next == NULL)
+ {
+ mpce->next = (void*)mpce;
+ goto restart;
+ }
+ else
+ {
+ mpce->next = NULL;
+ }
+ }
+ else
+ {
+ if(seq != NULL)
+ {
+ match t1;
+ match t2;
+ const_matchp next;
+ matchp_cache_peek(mpce->ctx, mpce->pos - 1, NULL, &next, t1 ,t2);
+ if(next == NULL ||
+ (next->len + (mpce->next != NULL && next->len < 3) <= seq->len))
+ {
+ /* nope, next is not better, use this sequence */
+ val = seq;
+ }
+ }
+ }
+ if(val != NULL)
+ {
+ LOG(LOG_DEBUG, ("Using len %05d, offset, %05d\n", val->len, val->offset));
+ mpce->pos -= val->len;
+ }
+ return val;
+}
+
+
+
+void read_file(const char *name, struct membuf *buf)
+{
+ char block[1024];
+ FILE *in;
+ int len;
+
+ in = fopen(name, "rb");
+ if(in == NULL)
+ {
+ LOG(LOG_ERROR, ("Can't open file \"%s\" for input.\n", name));
+ exit(-1);
+ }
+ do
+ {
+ len = fread(block, 1, 1024, in);
+ membuf_append(buf, block, len);
+ }
+ while(len == 1024);
+ LOG(LOG_DEBUG, ("read %d bytes from file\n", len));
+ fclose(in);
+}
+
+void write_file(const char *name, struct membuf *buf)
+{
+ FILE *out;
+ out = fopen(name, "wb");
+ if(out == NULL)
+ {
+ LOG(LOG_ERROR, ("Can't open file \"%s\" for output.\n", name));
+ exit(-1);
+ }
+ fwrite(membuf_get(buf), 1, membuf_memlen(buf), out);
+ fclose(out);
+}
+void membuf_init(struct membuf *sb)
+{
+ sb->buf = NULL;
+ sb->len = 0;
+ sb->size = 0;
+}
+void membuf_clear(struct membuf *sb)
+{
+ sb->len = 0;
+}
+void membuf_free(struct membuf *sb)
+{
+ if (sb->buf != NULL)
+ {
+ free(sb->buf);
+ sb->buf = NULL;
+ }
+ sb->len = 0;
+ sb->size = 0;
+}
+
+void membuf_new(struct membuf **sbp)
+{
+ struct membuf *sb;
+
+ sb = malloc(sizeof(struct membuf));
+ if (sb == NULL)
+ {
+ fprintf(stderr, "error, can't allocate memory\n");
+ exit(1);
+ }
+ sb->buf = NULL;
+ sb->len = 0;
+ sb->size = 0;
+
+ *sbp = sb;
+}
+
+void membuf_delete(struct membuf **sbp)
+{
+ struct membuf *sb;
+
+ sb = *sbp;
+ membuf_free(sb);
+ free(sb);
+ sb = NULL;
+ *sbp = sb;
+}
+
+int membuf_memlen(const struct membuf *sb)
+{
+ return sb->len;
+}
+
+void membuf_truncate(struct membuf *sb, int len)
+{
+ sb->len = len;
+}
+
+int membuf_trim(struct membuf *sb, int pos)
+{
+ if(pos < 0 || pos > sb->len)
+ {
+ return -1;
+ }
+ if(pos == 0)
+ {
+ return sb->len;
+ }
+ if(pos != sb->len)
+ {
+ memmove(sb->buf, (char*)sb->buf + pos, sb->len - pos);
+ }
+ sb->len -= pos;
+ return sb->len;
+}
+
+void *membuf_memcpy(struct membuf *sb, int offset, const void *mem, int len)
+{
+ char *buf;
+ membuf_atleast(sb, offset + len);
+ buf = (char*)sb->buf + offset;
+ memcpy(buf, mem, len);
+ return buf;
+}
+
+void *membuf_append(struct membuf *sb, const void *mem, int len)
+{
+ int newlen;
+ void *p;
+ newlen = sb->len + len;
+ membuf_atleast(sb, newlen);
+ p = (char *) sb->buf + sb->len;
+ if(mem == NULL)
+ {
+ memset(p, 0, len);
+ }
+ else
+ {
+ memcpy(p, mem, len);
+ }
+ sb->len = newlen;
+ return p;
+}
+
+void *membuf_append_char(struct membuf *sb, char c)
+{
+ int newlen;
+ char *p;
+ newlen = sb->len + 1;
+ membuf_atleast(sb, newlen);
+ p = (char *) sb->buf + sb->len;
+ *p = c;
+ sb->len = newlen;
+ return p;
+}
+
+void *membuf_insert(struct membuf *sb, int offset, const void *mem, int len)
+{
+ int newlen;
+ void *from;
+ void *to;
+ newlen = sb->len + len;
+ membuf_atleast(sb, newlen);
+ from = (char *)sb->buf + offset;
+ to = (char *)from + len;
+ memmove(to, from, sb->len - offset);
+ if(mem == NULL)
+ {
+ memset(from, 0, len);
+ }
+ else
+ {
+ memcpy(from, mem, len);
+ }
+ sb->len = newlen;
+ return from;
+}
+
+void membuf_remove(struct membuf *sb, int offset, int len)
+{
+ void *from;
+ void *to;
+ to = (char *)sb->buf + offset;
+ from = (char *)to + len;
+ sb->len -= len;
+ memmove(to, from, sb->len - offset);
+
+}
+
+void membuf_atleast(struct membuf *sb, int len)
+{
+ int size;
+
+ size = sb->size;
+ if (size == 0)
+ size = 1;
+ while (size < len)
+ {
+ size <<= 1;
+ }
+ if (size > sb->size)
+ {
+ sb->buf = realloc(sb->buf, size);
+ if (sb->buf == NULL)
+ {
+ fprintf(stderr, "error, can't reallocate memory\n");
+ exit(1);
+ }
+ sb->size = size;
+ }
+}
+
+void membuf_atmost(struct membuf *sb, int len)
+{
+ int size;
+
+ size = sb->size;
+ while (size > len)
+ {
+ size >>= 1;
+ }
+ if (size < sb->size)
+ {
+ sb->buf = realloc(sb->buf, size);
+ if (sb->buf == NULL)
+ {
+ fprintf(stderr, "error, can't reallocate memory\n");
+ exit(1);
+ }
+ sb->size = size;
+ sb->len = size;
+ }
+}
+
+int membuf_get_size(const struct membuf *sb)
+{
+ return sb->size;
+}
+void *membuf_get(const struct membuf *sb)
+{
+ return sb->buf;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+struct _interval_node {
+ int start;
+ int score;
+ struct _interval_node *next;
+ signed char prefix;
+ signed char bits;
+ signed char depth;
+ signed char flags;
+};
+
+typedef struct _interval_node interval_node[1];
+typedef struct _interval_node *interval_nodep;
+
+static
+void
+interval_node_init(interval_nodep inp, int start, int depth, int flags)
+{
+ inp->start = start;
+ inp->flags = flags;
+ inp->depth = depth;
+ inp->bits = 0;
+ inp->prefix = flags >= 0 ? flags : depth + 1;
+ inp->score = -1;
+ inp->next = NULL;
+}
+
+static
+interval_nodep interval_node_clone(interval_nodep inp)
+{
+ interval_nodep inp2 = NULL;
+
+ if(inp != NULL)
+ {
+ inp2 = malloc(sizeof(interval_node));
+ if (inp2 == NULL)
+ {
+ LOG(LOG_ERROR, ("out of memory error in file %s, line %d\n",
+ __FILE__, __LINE__));
+ exit(0);
+ }
+ /* copy contents */
+ *inp2 = *inp;
+ inp2->next = interval_node_clone(inp->next);
+ }
+
+ return inp2;
+}
+
+static
+void interval_node_delete(interval_nodep inp)
+{
+ interval_nodep inp2;
+ while (inp != NULL)
+ {
+ inp2 = inp;
+ inp = inp->next;
+ free(inp2);
+ }
+}
+
+static
+void interval_node_dump(int level, interval_nodep inp)
+{
+ int end;
+
+ end = 0;
+ while (inp != NULL)
+ {
+ end = inp->start + (1 << inp->bits);
+ LOG(level, ("%X", inp->bits));
+ inp = inp->next;
+ }
+ LOG(level, ("[eol@%d]\n", end));
+}
+
+float optimal_encode_int(int arg, void *priv, output_ctxp out)
+{
+ interval_nodep inp;
+ int end;
+
+ float val;
+
+ inp = (interval_nodep) priv;
+ val = 100000000.0;
+ end = 0;
+ while (inp != NULL)
+ {
+ end = inp->start + (1 << inp->bits);
+ if (arg >= inp->start && arg < end)
+ {
+ break;
+ }
+ inp = inp->next;
+ }
+ if (inp != NULL)
+ {
+ val = (float) (inp->prefix + inp->bits);
+ } else
+ {
+ val += (float) (arg - end);
+ }
+ LOG(LOG_DUMP, ("encoding %d to %0.1f bits\n", arg, val));
+
+ if (out != NULL)
+ {
+ output_bits(out, inp->bits, arg - inp->start);
+ if (inp->flags < 0)
+ {
+ LOG(LOG_DUMP, ("gamma prefix code = %d\n", inp->depth));
+ output_gamma_code(out, inp->depth);
+ } else
+ {
+ LOG(LOG_DUMP, ("flat prefix %d bits\n", inp->depth));
+ output_bits(out, inp->prefix, inp->depth);
+ }
+ }
+
+ return val;
+}
+
+float optimal_encode(const_matchp mp, encode_match_data emd)
+{
+ interval_nodep *offset;
+ float bits;
+ encode_match_privp data;
+
+ data = emd->priv;
+ offset = data->offset_f_priv;
+
+ bits = 0.0;
+ if (mp->offset == 0)
+ {
+ bits += 9.0f * mp->len;
+ data->lit_num += mp->len;
+ data->lit_bits += bits;
+ } else
+ {
+ bits += 1.0;
+ switch (mp->len)
+ {
+ case 0:
+ LOG(LOG_ERROR, ("bad len\n"));
+ exit(1);
+ break;
+ case 1:
+ bits += data->offset_f(mp->offset, offset[0], emd->out);
+ break;
+ case 2:
+ bits += data->offset_f(mp->offset, offset[1], emd->out);
+ break;
+ default:
+ bits += data->offset_f(mp->offset, offset[7], emd->out);
+ break;
+ }
+ bits += data->len_f(mp->len, data->len_f_priv, emd->out);
+ if (bits > (9.0 * mp->len))
+ {
+ /* lets make literals out of it */
+ data->lit_num += 1;
+ data->lit_bits += bits;
+ } else
+ {
+ if (mp->offset == 1)
+ {
+ data->rle_num += 1;
+ data->rle_bits += bits;
+ } else
+ {
+ data->seq_num += 1;
+ data->seq_bits += bits;
+ }
+ }
+ }
+ return bits;
+}
+
+struct _optimize_arg {
+ radix_root cache;
+ int *stats;
+ int *stats2;
+ int max_depth;
+ int flags;
+ struct chunkpool in_pool[1];
+};
+
+#define CACHE_KEY(START, DEPTH, MAXDEPTH) ((int)((START)*(MAXDEPTH)|DEPTH))
+
+typedef struct _optimize_arg optimize_arg[1];
+typedef struct _optimize_arg optimize_argp;
+
+static interval_nodep
+optimize1(optimize_arg arg, int start, int depth, int init)
+{
+ interval_node inp;
+ interval_nodep best_inp;
+ int key;
+ int end, i;
+ int start_count, end_count;
+
+ LOG(LOG_DUMP, ("IN start %d, depth %d\n", start, depth));
+
+ do
+ {
+ best_inp = NULL;
+ if (arg->stats[start] == 0)
+ {
+ break;
+ }
+ key = CACHE_KEY(start, depth, arg->max_depth);
+ best_inp = radix_node_get(arg->cache, key);
+ if (best_inp != NULL)
+ {
+ break;
+ }
+
+ interval_node_init(inp, start, depth, arg->flags);
+
+ for (i = 0; i < 16; ++i)
+ {
+ inp->next = NULL;
+ inp->bits = i;
+ end = start + (1 << i);
+
+ start_count = end_count = 0;
+ if (start < 65536)
+ {
+ start_count = arg->stats[start];
+ if (end < 65536)
+ {
+ end_count = arg->stats[end];
+ }
+ }
+
+ inp->score = (start_count - end_count) *
+ (inp->prefix + inp->bits);
+
+ /* one index below */
+ LOG(LOG_DUMP, ("interval score: [%d«%d[%d\n",
+ start, i, inp->score));
+ if (end_count > 0)
+ {
+ int penalty;
+ /* we're not done, now choose between using
+ * more bits, go deeper or skip the rest */
+ if (depth + 1 < arg->max_depth)
+ {
+ /* we can go deeper, let's try that */
+ inp->next = optimize1(arg, end, depth + 1, i);
+ }
+ /* get the penalty for skipping */
+ penalty = 100000000;
+ if (arg->stats2 != NULL)
+ {
+ penalty = arg->stats2[end];
+ }
+ if (inp->next != NULL && inp->next->score < penalty)
+ {
+ penalty = inp->next->score;
+ }
+ inp->score += penalty;
+ }
+ if (best_inp == NULL || inp->score < best_inp->score)
+ {
+ /* it's the new best in town, use it */
+ if (best_inp == NULL)
+ {
+ /* allocate if null */
+ best_inp = chunkpool_malloc(arg->in_pool);
+ }
+ *best_inp = *inp;
+ }
+ }
+ if (best_inp != NULL)
+ {
+ radix_node_set(arg->cache, key, best_inp);
+ }
+ }
+ while (0);
+
+ if(IS_LOGGABLE(LOG_DUMP))
+ {
+ LOG(LOG_DUMP, ("OUT depth %d: ", depth));
+ interval_node_dump(LOG_DUMP, best_inp);
+ }
+ return best_inp;
+}
+
+static interval_nodep
+exo_optimize(int stats[65536], int stats2[65536], int max_depth, int flags)
+{
+ optimize_arg arg;
+
+ interval_nodep inp;
+
+ arg->stats = stats;
+ arg->stats2 = stats2;
+
+ arg->max_depth = max_depth;
+ arg->flags = flags;
+
+ chunkpool_init(arg->in_pool, sizeof(interval_node));
+
+ radix_tree_init(arg->cache);
+
+ inp = optimize1(arg, 1, 0, 0);
+
+ /* use normal malloc for the winner */
+ inp = interval_node_clone(inp);
+
+ /* cleanup */
+ radix_tree_free(arg->cache, NULL, NULL);
+ chunkpool_free(arg->in_pool);
+
+ return inp;
+}
+
+static const char *export_helper(interval_nodep np, int depth)
+{
+ static char buf[20];
+ char *p = buf;
+ while(np != NULL)
+ {
+ p += sprintf(p, "%X", np->bits);
+ np = np->next;
+ --depth;
+ }
+ while(depth-- > 0)
+ {
+ p += sprintf(p, "0");
+ }
+ return buf;
+}
+
+const char *optimal_encoding_export(encode_match_data emd)
+{
+ interval_nodep *offsets;
+ static char buf[100];
+ char *p = buf;
+ encode_match_privp data;
+ data = emd->priv;
+ offsets = (interval_nodep*)data->offset_f_priv;
+ p += sprintf(p, "%s", export_helper((interval_nodep)data->len_f_priv, 16));
+ p += sprintf(p, ",%s", export_helper(offsets[0], 4));
+ p += sprintf(p, ",%s", export_helper(offsets[1], 16));
+ p += sprintf(p, ",%s", export_helper(offsets[7], 16));
+ return buf;
+}
+
+static void import_helper(interval_nodep *npp,
+ const char **encodingp,
+ int flags)
+{
+ int c;
+ int start = 1;
+ int depth = 0;
+ const char *encoding;
+
+ encoding = *encodingp;
+ while((c = *(encoding++)) != '\0')
+ {
+ char buf[2] = {0, 0};
+ char *dummy;
+ int bits;
+ interval_nodep np;
+
+ if(c == ',')
+ {
+ break;
+ }
+
+ buf[0] = c;
+ bits = strtol(buf, &dummy, 16);
+
+ LOG(LOG_DUMP, ("got bits %d\n", bits));
+
+ np = malloc(sizeof(interval_node));
+ interval_node_init(np, start, depth, flags);
+ np->bits = bits;
+
+ ++depth;
+ start += 1 << bits;
+
+ *npp = np;
+ npp = &(np->next);
+ }
+ *encodingp = encoding;
+}
+
+void optimal_encoding_import(encode_match_data emd,
+ const char *encoding)
+{
+ encode_match_privp data;
+ interval_nodep *npp, *offsets;
+
+ LOG(LOG_DEBUG, ("importing encoding: %s\n", encoding));
+
+ optimal_free(emd);
+ optimal_init(emd);
+
+ data = emd->priv;
+ offsets = (interval_nodep*)data->offset_f_priv;
+
+ /* lengths */
+ npp = (void*)&data->len_f_priv;
+ import_helper(npp, &encoding, -1);
+
+ /* offsets, len = 1 */
+ npp = &offsets[0];
+ import_helper(npp, &encoding, 2);
+
+ /* offsets, len = 2 */
+ npp = &offsets[1];
+ import_helper(npp, &encoding, 4);
+
+ /* offsets, len >= 3 */
+ npp = &offsets[7];
+ import_helper(npp, &encoding, 4);
+
+ LOG(LOG_DEBUG, ("imported encoding: "));
+ optimal_dump(LOG_DEBUG, emd);
+}
+
+void optimal_init(encode_match_data emd) /* IN/OUT */
+{
+ encode_match_privp data;
+ interval_nodep *inpp;
+
+ emd->priv = malloc(sizeof(encode_match_priv));
+ data = emd->priv;
+
+ memset(data, 0, sizeof(encode_match_priv));
+
+ data->offset_f = optimal_encode_int;
+ data->len_f = optimal_encode_int;
+ inpp = malloc(sizeof(interval_nodep[8]));
+ inpp[0] = NULL;
+ inpp[1] = NULL;
+ inpp[2] = NULL;
+ inpp[3] = NULL;
+ inpp[4] = NULL;
+ inpp[5] = NULL;
+ inpp[6] = NULL;
+ inpp[7] = NULL;
+ data->offset_f_priv = inpp;
+ data->len_f_priv = NULL;
+}
+
+void optimal_free(encode_match_data emd) /* IN */
+{
+ encode_match_privp data;
+ interval_nodep *inpp;
+ interval_nodep inp;
+
+ data = emd->priv;
+
+ inpp = data->offset_f_priv;
+ if (inpp != NULL)
+ {
+ interval_node_delete(inpp[0]);
+ interval_node_delete(inpp[1]);
+ interval_node_delete(inpp[2]);
+ interval_node_delete(inpp[3]);
+ interval_node_delete(inpp[4]);
+ interval_node_delete(inpp[5]);
+ interval_node_delete(inpp[6]);
+ interval_node_delete(inpp[7]);
+ }
+ free(inpp);
+
+ inp = data->len_f_priv;
+ interval_node_delete(inp);
+
+ data->offset_f_priv = NULL;
+ data->len_f_priv = NULL;
+}
+
+void freq_stats_dump(int level, int arr[65536])
+{
+ int i;
+ for (i = 0; i < 32; ++i)
+ {
+ LOG(level, ("%d, ", arr[i] - arr[i + 1]));
+ }
+ LOG(level, ("\n"));
+}
+
+void freq_stats_dump_raw(int level, int arr[65536])
+{
+ int i;
+ for (i = 0; i < 32; ++i)
+ {
+ LOG(level, ("%d, ", arr[i]));
+ }
+ LOG(level, ("\n"));
+}
+
+void optimal_optimize(encode_match_data emd, /* IN/OUT */
+ matchp_enum_get_next_f * f, /* IN */
+ void *matchp_enum) /* IN */
+{
+ encode_match_privp data;
+ const_matchp mp;
+ interval_nodep *offset;
+ static int offset_arr[8][65536];
+ static int offset_parr[8][65536];
+ static int len_arr[65536];
+ int treshold;
+
+ int i, j;
+ void *priv1;
+
+ data = emd->priv;
+
+ memset(offset_arr, 0, sizeof(offset_arr));
+ memset(offset_parr, 0, sizeof(offset_parr));
+ memset(len_arr, 0, sizeof(len_arr));
+
+ offset = data->offset_f_priv;
+
+ /* first the lens */
+ priv1 = matchp_enum;
+#if 0
+ while ((mp = f(priv1)) != NULL)
+ {
+ LOG(LOG_DEBUG, ("%p len %d offset %d\n", mp, mp->len, mp->offset));
+ }
+ if(mp->len < 0)
+ {
+ LOG(LOG_ERROR, ("the horror, negative len!\n"));
+ }
+#endif
+ while ((mp = f(priv1)) != NULL && mp->len > 0)
+ {
+ if (mp->offset > 0)
+ {
+ len_arr[mp->len] += 1;
+ if(len_arr[mp->len] < 0)
+ {
+ LOG(LOG_ERROR, ("len counter wrapped!\n"));
+ }
+ }
+ }
+
+ for (i = 65534; i >= 0; --i)
+ {
+ len_arr[i] += len_arr[i + 1];
+ if(len_arr[i] < 0)
+ {
+ LOG(LOG_ERROR, ("len counter wrapped!\n"));
+ }
+ }
+
+ data->len_f_priv = exo_optimize(len_arr, NULL, 16, -1);
+
+ /* then the offsets */
+ priv1 = matchp_enum;
+ while ((mp = f(priv1)) != NULL && mp->len > 0)
+ {
+ if (mp->offset > 0)
+ {
+ treshold = mp->len * 9;
+ treshold -= 1 + (int) optimal_encode_int(mp->len,
+ data->len_f_priv,
+ NULL);
+ switch (mp->len)
+ {
+ case 0:
+ LOG(LOG_ERROR, ("bad len\n"));
+ exit(0);
+ break;
+ case 1:
+ offset_parr[0][mp->offset] += treshold;
+ offset_arr[0][mp->offset] += 1;
+ if(offset_arr[0][mp->offset] < 0)
+ {
+ LOG(LOG_ERROR, ("offset0 counter wrapped!\n"));
+ }
+ break;
+ case 2:
+ offset_parr[1][mp->offset] += treshold;
+ offset_arr[1][mp->offset] += 1;
+ if(offset_arr[1][mp->offset] < 0)
+ {
+ LOG(LOG_ERROR, ("offset1 counter wrapped!\n"));
+ }
+ break;
+ default:
+ offset_parr[7][mp->offset] += treshold;
+ offset_arr[7][mp->offset] += 1;
+ if(offset_arr[7][mp->offset] < 0)
+ {
+ LOG(LOG_ERROR, ("offset7 counter wrapped!\n"));
+ }
+ break;
+ }
+ }
+ }
+
+ for (i = 65534; i >= 0; --i)
+ {
+ for (j = 0; j < 8; ++j)
+ {
+ offset_arr[j][i] += offset_arr[j][i + 1];
+ offset_parr[j][i] += offset_parr[j][i + 1];
+ }
+ }
+
+ offset[0] = exo_optimize(offset_arr[0], offset_parr[0], 1 << 2, 2);
+ offset[1] = exo_optimize(offset_arr[1], offset_parr[1], 1 << 4, 4);
+ offset[2] = exo_optimize(offset_arr[2], offset_parr[2], 1 << 4, 4);
+ offset[3] = exo_optimize(offset_arr[3], offset_parr[3], 1 << 4, 4);
+ offset[4] = exo_optimize(offset_arr[4], offset_parr[4], 1 << 4, 4);
+ offset[5] = exo_optimize(offset_arr[5], offset_parr[5], 1 << 4, 4);
+ offset[6] = exo_optimize(offset_arr[6], offset_parr[6], 1 << 4, 4);
+ offset[7] = exo_optimize(offset_arr[7], offset_parr[7], 1 << 4, 4);
+
+ if(IS_LOGGABLE(LOG_DEBUG))
+ {
+ optimal_dump(LOG_DEBUG, emd);
+ }
+}
+
+void optimal_dump(int level, encode_match_data emd)
+{
+ encode_match_privp data;
+ interval_nodep *offset;
+ interval_nodep len;
+
+ data = emd->priv;
+
+ offset = data->offset_f_priv;
+ len = data->len_f_priv;
+
+ LOG(level, ("lens: "));
+ interval_node_dump(level, len);
+
+ LOG(level, ("offsets (len =1): "));
+ interval_node_dump(level, offset[0]);
+
+ LOG(level, ("offsets (len =2): "));
+ interval_node_dump(level, offset[1]);
+
+ LOG(level, ("offsets (len =8): "));
+ interval_node_dump(level, offset[7]);
+}
+
+static
+void interval_out(output_ctx out, interval_nodep inp1, int size)
+{
+ unsigned char buffer[256];
+ unsigned char count;
+ interval_nodep inp;
+
+ count = 0;
+
+ memset(buffer, 0, sizeof(buffer));
+ inp = inp1;
+ while (inp != NULL)
+ {
+ ++count;
+ LOG(LOG_DUMP, ("bits %d, lo %d, hi %d\n",
+ inp->bits, inp->start & 0xFF, inp->start >> 8));
+ buffer[sizeof(buffer) - count] = inp->bits;
+ inp = inp->next;
+ }
+
+ while (size > 0)
+ {
+ int b;
+ b = buffer[sizeof(buffer) - size];
+ LOG(LOG_DUMP, ("outputting nibble %d\n", b));
+ output_bits(out, 4, b);
+ size--;
+ }
+}
+
+void optimal_out(output_ctx out, /* IN/OUT */
+ encode_match_data emd) /* IN */
+{
+ encode_match_privp data;
+ interval_nodep *offset;
+ interval_nodep len;
+
+ data = emd->priv;
+
+ offset = data->offset_f_priv;
+ len = data->len_f_priv;
+
+ interval_out(out, offset[0], 4);
+ interval_out(out, offset[1], 16);
+ interval_out(out, offset[7], 16);
+ interval_out(out, len, 16);
+}
+
+void output_ctx_init(output_ctx ctx, struct membuf *out) /* IN/OUT */
+{
+ ctx->bitbuf = 1;
+ ctx->pos = membuf_memlen(out);
+ ctx->buf = out;
+}
+
+unsigned int output_get_pos(output_ctx ctx) /* IN */
+{
+ return ctx->pos;
+}
+
+void output_byte(output_ctx ctx, /* IN/OUT */
+ unsigned char byte) /* IN */
+{
+ /*LOG(LOG_DUMP, ("output_byte: $%02X\n", byte)); */
+ if(ctx->pos < membuf_memlen(ctx->buf))
+ {
+ char *p;
+ p = membuf_get(ctx->buf);
+ p[ctx->pos] = byte;
+ }
+ else
+ {
+ while(ctx->pos > membuf_memlen(ctx->buf))
+ {
+ membuf_append_char(ctx->buf, '\0');
+ }
+ membuf_append_char(ctx->buf, byte);
+ }
+ ++(ctx->pos);
+}
+
+void output_word(output_ctx ctx, /* IN/OUT */
+ unsigned short int word) /* IN */
+{
+ output_byte(ctx, (unsigned char) (word & 0xff));
+ output_byte(ctx, (unsigned char) (word >> 8));
+}
+
+
+void output_bits_flush(output_ctx ctx) /* IN/OUT */
+{
+ /* flush the bitbuf including
+ * the extra 1 bit acting as eob flag */
+ output_byte(ctx, (unsigned char) (ctx->bitbuf & 0xFF));
+ if (ctx->bitbuf & 0x100)
+ {
+ output_byte(ctx, 1);
+ }
+ LOG(LOG_DUMP, ("bitstream flushed 0x%02X\n", ctx->bitbuf & 0xFF));
+
+ /* reset it */
+ ctx->bitbuf = 1;
+}
+
+void bits_dump(int count, int val)
+{
+ static char buf[1024];
+ char *pek;
+ pek = buf;
+ if (count > 0)
+ {
+ pek += sprintf(pek, "0x%04X, % 2d: ", val, count);
+ }
+ while (count-- > 0)
+ {
+ *(pek++) = val & (1 << count) ? '1' : '0';
+ }
+ *(pek++) = '\0';
+ LOG(LOG_NORMAL, ("%s\n", buf));
+}
+
+static void output_bits_int(output_ctx ctx, /* IN/OUT */
+ int count, /* IN */
+ int val) /* IN */
+{
+ /* this makes the bits appear in reversed
+ * big endian order in the output stream */
+ while (count-- > 0)
+ {
+ ctx->bitbuf <<= 1;
+ ctx->bitbuf |= val & 0x1;
+ val >>= 1;
+ if (ctx->bitbuf & 0x100)
+ {
+ /* full byte, flush it */
+ output_byte(ctx, (unsigned char) (ctx->bitbuf & 0xFF));
+ LOG(LOG_DUMP,
+ ("bitstream byte 0x%02X\n", ctx->bitbuf & 0xFF));
+ ctx->bitbuf = 1;
+ }
+ }
+}
+
+void output_bits(output_ctx ctx, /* IN/OUT */
+ int count, /* IN */
+ int val) /* IN */
+{
+ LOG(LOG_DUMP, ("output bits: count = %d, val = %d\n", count, val));
+ output_bits_int(ctx, count, val);
+}
+
+void output_gamma_code(output_ctx ctx, /* IN/OUT */
+ int code) /* IN */
+{
+ LOG(LOG_DUMP, ("output gamma: code = %d\n", code));
+ output_bits_int(ctx, 1, 1);
+ while (code-- > 0)
+ {
+ output_bits_int(ctx, 1, 0);
+ }
+}
+
+static
+void print_usage(const char *appl, enum log_level level,
+ const char *default_out_name)
+{
+ LOG(level, ("usage: %s [option]... infile\n", appl));
+ LOG(level,
+ (" -b crunch/decrunch backwards\n"
+ " -r write outfile in reverse order\n"
+ " -d decrunch (instead of crunch)\n"));
+ print_crunch_flags(level, default_out_name);
+}
+
+#define DEFAULT_OUTFILE "a.out"
+
+unsigned char *Exomizer_crunch(unsigned char *input_data, int input_len, int *retlen)
+{
+ int argc=1;
+ char **argv=NULL;
+ char flags_arr[32];
+ int decrunch_mode = 0;
+ int backwards_mode = 0;
+ int reverse_mode = 0;
+ int c, infilec;
+ char **infilev;
+ /* output buffer */
+ unsigned char *output_data;
+
+ static struct crunch_options options[1] = { CRUNCH_OPTIONS_DEFAULT };
+ struct common_flags flags[1] = { {options, DEFAULT_OUTFILE} };
+
+ struct membuf inbuf[1];
+ struct membuf outbuf[1];
+
+ const char *appl;;
+
+
+ argv=malloc(sizeof(char *));
+ argv[0]=strdup("mem_exomizer");
+ /* init args */
+ appl = fixup_appl(argv[0]);
+
+ sprintf(flags_arr, "bdr%s", CRUNCH_FLAGS);
+ while ((c = getflag(argc, argv, flags_arr)) != -1)
+ {
+ switch (c)
+ {
+ case 'b':
+ backwards_mode = 1;
+ break;
+ case 'r':
+ reverse_mode = 1;
+ break;
+ default:
+ handle_crunch_flags(c, flagarg, print_usage, appl, flags);
+ }
+ }
+
+ infilev = argv + flagind;
+ infilec = argc - flagind;
+
+
+printf("crunching with exomizer (the art of patience...)\n");
+
+/* only memory */
+
+ membuf_init(inbuf);
+ membuf_init(outbuf);
+
+ /* rustine */
+ membuf_append(inbuf, input_data, input_len);
+
+
+ {
+ struct crunch_info info[1];
+ if(backwards_mode)
+ {
+ crunch_backwards(inbuf, outbuf, options, info);
+ }
+ else
+ {
+ exocrunch(inbuf, outbuf, options, info);
+ }
+ }
+
+ if(reverse_mode)
+ {
+ reverse_buffer(membuf_get(outbuf), membuf_memlen(outbuf));
+ }
+
+ output_data=MemMalloc(membuf_memlen(outbuf));
+ memcpy(output_data,membuf_get(outbuf),membuf_memlen(outbuf));
+ *retlen=membuf_memlen(outbuf);
+
+ membuf_free(outbuf);
+ membuf_free(inbuf);
+
+ return output_data;
+}
diff --git a/tools/rasm_src/lz4.h b/tools/rasm_src/lz4.h new file mode 100644 index 0000000..f34bae4 --- /dev/null +++ b/tools/rasm_src/lz4.h @@ -0,0 +1,3329 @@ +/* + +Warning! This is a modified version of original sources! + +To sum up: +- all include files and C sources were merged in a single file +- existing logs were removed (except error logs) +- main were removed and wrapper added + + + * LZ4 - Fast LZ compression algorithm + * Header File + * Copyright (C) 2011-2017, Yann Collet. + + BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following disclaimer + in the documentation and/or other materials provided with the + distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + You can contact the author at : + - LZ4 homepage : http://www.lz4.org + - LZ4 source repository : https://github.com/lz4/lz4 +*/ +#if defined (__cplusplus) +extern "C" { +#endif + +#ifndef LZ4_H_2983827168210 +#define LZ4_H_2983827168210 + +/* --- Dependency --- */ + + +/** + Introduction + + LZ4 is lossless compression algorithm, providing compression speed at 400 MB/s per core, + scalable with multi-cores CPU. It features an extremely fast decoder, with speed in + multiple GB/s per core, typically reaching RAM speed limits on multi-core systems. + + The LZ4 compression library provides in-memory compression and decompression functions. + Compression can be done in: + - a single step (described as Simple Functions) + - a single step, reusing a context (described in Advanced Functions) + - unbounded multiple steps (described as Streaming compression) + + lz4.h provides block compression functions. It gives full buffer control to user. + Decompressing an lz4-compressed block also requires metadata (such as compressed size). + Each application is free to encode such metadata in whichever way it wants. + + An additional format, called LZ4 frame specification (doc/lz4_Frame_format.md), + take care of encoding standard metadata alongside LZ4-compressed blocks. + If your application requires interoperability, it's recommended to use it. + A library is provided to take care of it, see lz4frame.h. +*/ + +/*^*************************************************************** +* Export parameters +*****************************************************************/ +/* +* LZ4_DLL_EXPORT : +* Enable exporting of functions when building a Windows DLL +* LZ4LIB_API : +* Control library symbols visibility. +*/ +#if defined(LZ4_DLL_EXPORT) && (LZ4_DLL_EXPORT==1) +# define LZ4LIB_API __declspec(dllexport) +#elif defined(LZ4_DLL_IMPORT) && (LZ4_DLL_IMPORT==1) +# define LZ4LIB_API __declspec(dllimport) /* It isn't required but allows to generate better code, saving a function pointer load from the IAT and an indirect jump.*/ +#elif defined(__GNUC__) && (__GNUC__ >= 4) +# define LZ4LIB_API __attribute__ ((__visibility__ ("default"))) +#else +# define LZ4LIB_API +#endif + + +/*------ Version ------*/ +#define LZ4_VERSION_MAJOR 1 /* for breaking interface changes */ +#define LZ4_VERSION_MINOR 7 /* for new (non-breaking) interface capabilities */ +#define LZ4_VERSION_RELEASE 6 /* for tweaks, bug-fixes, or development */ + +#define LZ4_VERSION_NUMBER (LZ4_VERSION_MAJOR *100*100 + LZ4_VERSION_MINOR *100 + LZ4_VERSION_RELEASE) + +#define LZ4_LIB_VERSION LZ4_VERSION_MAJOR.LZ4_VERSION_MINOR.LZ4_VERSION_RELEASE +#define LZ4_QUOTE(str) #str +#define LZ4_EXPAND_AND_QUOTE(str) LZ4_QUOTE(str) +#define LZ4_VERSION_STRING LZ4_EXPAND_AND_QUOTE(LZ4_LIB_VERSION) + +LZ4LIB_API int LZ4_versionNumber (void); /**< library version number; to be used when checking dll version */ +LZ4LIB_API const char* LZ4_versionString (void); /**< library version string; to be used when checking dll version */ + + +/*-************************************ +* Tuning parameter +**************************************/ +/*! + * LZ4_MEMORY_USAGE : + * Memory usage formula : N->2^N Bytes (examples : 10 -> 1KB; 12 -> 4KB ; 16 -> 64KB; 20 -> 1MB; etc.) + * Increasing memory usage improves compression ratio + * Reduced memory usage can improve speed, due to cache effect + * Default value is 14, for 16KB, which nicely fits into Intel x86 L1 cache + */ +#ifndef LZ4_MEMORY_USAGE +# define LZ4_MEMORY_USAGE 14 +#endif + +/*-************************************ +* Simple Functions +**************************************/ +/*! LZ4_compress_default() : + Compresses 'sourceSize' bytes from buffer 'source' + into already allocated 'dest' buffer of size 'maxDestSize'. + Compression is guaranteed to succeed if 'maxDestSize' >= LZ4_compressBound(sourceSize). + It also runs faster, so it's a recommended setting. + If the function cannot compress 'source' into a more limited 'dest' budget, + compression stops *immediately*, and the function result is zero. + As a consequence, 'dest' content is not valid. + This function never writes outside 'dest' buffer, nor read outside 'source' buffer. + sourceSize : Max supported value is LZ4_MAX_INPUT_VALUE + maxDestSize : full or partial size of buffer 'dest' (which must be already allocated) + return : the number of bytes written into buffer 'dest' (necessarily <= maxOutputSize) + or 0 if compression fails */ +LZ4LIB_API int LZ4_compress_default(const char* source, char* dest, int sourceSize, int maxDestSize); + +/*! LZ4_decompress_safe() : + compressedSize : is the precise full size of the compressed block. + maxDecompressedSize : is the size of destination buffer, which must be already allocated. + return : the number of bytes decompressed into destination buffer (necessarily <= maxDecompressedSize) + If destination buffer is not large enough, decoding will stop and output an error code (<0). + If the source stream is detected malformed, the function will stop decoding and return a negative result. + This function is protected against buffer overflow exploits, including malicious data packets. + It never writes outside output buffer, nor reads outside input buffer. +*/ +LZ4LIB_API int LZ4_decompress_safe (const char* source, char* dest, int compressedSize, int maxDecompressedSize); + + +/*-************************************ +* Advanced Functions +**************************************/ +#define LZ4_MAX_INPUT_SIZE 0x7E000000 /* 2 113 929 216 bytes */ +#define LZ4_COMPRESSBOUND(isize) ((unsigned)(isize) > (unsigned)LZ4_MAX_INPUT_SIZE ? 0 : (isize) + ((isize)/255) + 16) + +/*! +LZ4_compressBound() : + Provides the maximum size that LZ4 compression may output in a "worst case" scenario (input data not compressible) + This function is primarily useful for memory allocation purposes (destination buffer size). + Macro LZ4_COMPRESSBOUND() is also provided for compilation-time evaluation (stack memory allocation for example). + Note that LZ4_compress_default() compress faster when dest buffer size is >= LZ4_compressBound(srcSize) + inputSize : max supported value is LZ4_MAX_INPUT_SIZE + return : maximum output size in a "worst case" scenario + or 0, if input size is too large ( > LZ4_MAX_INPUT_SIZE) +*/ +LZ4LIB_API int LZ4_compressBound(int inputSize); + +/*! +LZ4_compress_fast() : + Same as LZ4_compress_default(), but allows to select an "acceleration" factor. + The larger the acceleration value, the faster the algorithm, but also the lesser the compression. + It's a trade-off. It can be fine tuned, with each successive value providing roughly +~3% to speed. + An acceleration value of "1" is the same as regular LZ4_compress_default() + Values <= 0 will be replaced by ACCELERATION_DEFAULT (see lz4.c), which is 1. +*/ +LZ4LIB_API int LZ4_compress_fast (const char* source, char* dest, int sourceSize, int maxDestSize, int acceleration); + + +/*! +LZ4_compress_fast_extState() : + Same compression function, just using an externally allocated memory space to store compression state. + Use LZ4_sizeofState() to know how much memory must be allocated, + and allocate it on 8-bytes boundaries (using malloc() typically). + Then, provide it as 'void* state' to compression function. +*/ +LZ4LIB_API int LZ4_sizeofState(void); +LZ4LIB_API int LZ4_compress_fast_extState (void* state, const char* source, char* dest, int inputSize, int maxDestSize, int acceleration); + + +/*! +LZ4_compress_destSize() : + Reverse the logic, by compressing as much data as possible from 'source' buffer + into already allocated buffer 'dest' of size 'targetDestSize'. + This function either compresses the entire 'source' content into 'dest' if it's large enough, + or fill 'dest' buffer completely with as much data as possible from 'source'. + *sourceSizePtr : will be modified to indicate how many bytes where read from 'source' to fill 'dest'. + New value is necessarily <= old value. + return : Nb bytes written into 'dest' (necessarily <= targetDestSize) + or 0 if compression fails +*/ +LZ4LIB_API int LZ4_compress_destSize (const char* source, char* dest, int* sourceSizePtr, int targetDestSize); + + +/*! +LZ4_decompress_fast() : + originalSize : is the original and therefore uncompressed size + return : the number of bytes read from the source buffer (in other words, the compressed size) + If the source stream is detected malformed, the function will stop decoding and return a negative result. + Destination buffer must be already allocated. Its size must be a minimum of 'originalSize' bytes. + note : This function fully respect memory boundaries for properly formed compressed data. + It is a bit faster than LZ4_decompress_safe(). + However, it does not provide any protection against intentionally modified data stream (malicious input). + Use this function in trusted environment only (data to decode comes from a trusted source). +*/ +LZ4LIB_API int LZ4_decompress_fast (const char* source, char* dest, int originalSize); + +/*! +LZ4_decompress_safe_partial() : + This function decompress a compressed block of size 'compressedSize' at position 'source' + into destination buffer 'dest' of size 'maxDecompressedSize'. + The function tries to stop decompressing operation as soon as 'targetOutputSize' has been reached, + reducing decompression time. + return : the number of bytes decoded in the destination buffer (necessarily <= maxDecompressedSize) + Note : this number can be < 'targetOutputSize' should the compressed block to decode be smaller. + Always control how many bytes were decoded. + If the source stream is detected malformed, the function will stop decoding and return a negative result. + This function never writes outside of output buffer, and never reads outside of input buffer. It is therefore protected against malicious data packets +*/ +LZ4LIB_API int LZ4_decompress_safe_partial (const char* source, char* dest, int compressedSize, int targetOutputSize, int maxDecompressedSize); + + +/*-********************************************* +* Streaming Compression Functions +***********************************************/ +typedef union LZ4_stream_u LZ4_stream_t; /* incomplete type (defined later) */ + +/*! LZ4_createStream() and LZ4_freeStream() : + * LZ4_createStream() will allocate and initialize an `LZ4_stream_t` structure. + * LZ4_freeStream() releases its memory. + */ +LZ4LIB_API LZ4_stream_t* LZ4_createStream(void); +LZ4LIB_API int LZ4_freeStream (LZ4_stream_t* streamPtr); + +/*! LZ4_resetStream() : + * An LZ4_stream_t structure can be allocated once and re-used multiple times. + * Use this function to init an allocated `LZ4_stream_t` structure and start a new compression. + */ +LZ4LIB_API void LZ4_resetStream (LZ4_stream_t* streamPtr); + +/*! LZ4_loadDict() : + * Use this function to load a static dictionary into LZ4_stream. + * Any previous data will be forgotten, only 'dictionary' will remain in memory. + * Loading a size of 0 is allowed. + * Return : dictionary size, in bytes (necessarily <= 64 KB) + */ +LZ4LIB_API int LZ4_loadDict (LZ4_stream_t* streamPtr, const char* dictionary, int dictSize); + +/*! LZ4_compress_fast_continue() : + * Compress buffer content 'src', using data from previously compressed blocks as dictionary to improve compression ratio. + * Important : Previous data blocks are assumed to remain present and unmodified ! + * 'dst' buffer must be already allocated. + * If dstCapacity >= LZ4_compressBound(srcSize), compression is guaranteed to succeed, and runs faster. + * If not, and if compressed data cannot fit into 'dst' buffer size, compression stops, and function returns a zero. + * After an error, the stream status is invalid, and it can only be reset or freed. + */ +LZ4LIB_API int LZ4_compress_fast_continue (LZ4_stream_t* streamPtr, const char* src, char* dst, int srcSize, int dstCapacity, int acceleration); + +/*! LZ4_saveDict() : + * If previously compressed data block is not guaranteed to remain available at its current memory location, + * save it into a safer place (char* safeBuffer). + * Note : it's not necessary to call LZ4_loadDict() after LZ4_saveDict(), dictionary is immediately usable. + * @return : saved dictionary size in bytes (necessarily <= dictSize), or 0 if error. + */ +LZ4LIB_API int LZ4_saveDict (LZ4_stream_t* streamPtr, char* safeBuffer, int dictSize); + + +/*-********************************************** +* Streaming Decompression Functions +* Bufferless synchronous API +************************************************/ +typedef union LZ4_streamDecode_u LZ4_streamDecode_t; /* incomplete type (defined later) */ + +/*! LZ4_createStreamDecode() and LZ4_freeStreamDecode() : + * creation / destruction of streaming decompression tracking structure */ +LZ4LIB_API LZ4_streamDecode_t* LZ4_createStreamDecode(void); +LZ4LIB_API int LZ4_freeStreamDecode (LZ4_streamDecode_t* LZ4_stream); + +/*! LZ4_setStreamDecode() : + * Use this function to instruct where to find the dictionary. + * Setting a size of 0 is allowed (same effect as reset). + * @return : 1 if OK, 0 if error + */ +LZ4LIB_API int LZ4_setStreamDecode (LZ4_streamDecode_t* LZ4_streamDecode, const char* dictionary, int dictSize); + +/*! +LZ4_decompress_*_continue() : + These decoding functions allow decompression of multiple blocks in "streaming" mode. + Previously decoded blocks *must* remain available at the memory position where they were decoded (up to 64 KB) + In the case of a ring buffers, decoding buffer must be either : + - Exactly same size as encoding buffer, with same update rule (block boundaries at same positions) + In which case, the decoding & encoding ring buffer can have any size, including very small ones ( < 64 KB). + - Larger than encoding buffer, by a minimum of maxBlockSize more bytes. + maxBlockSize is implementation dependent. It's the maximum size you intend to compress into a single block. + In which case, encoding and decoding buffers do not need to be synchronized, + and encoding ring buffer can have any size, including small ones ( < 64 KB). + - _At least_ 64 KB + 8 bytes + maxBlockSize. + In which case, encoding and decoding buffers do not need to be synchronized, + and encoding ring buffer can have any size, including larger than decoding buffer. + Whenever these conditions are not possible, save the last 64KB of decoded data into a safe buffer, + and indicate where it is saved using LZ4_setStreamDecode() +*/ +LZ4LIB_API int LZ4_decompress_safe_continue (LZ4_streamDecode_t* LZ4_streamDecode, const char* source, char* dest, int compressedSize, int maxDecompressedSize); +LZ4LIB_API int LZ4_decompress_fast_continue (LZ4_streamDecode_t* LZ4_streamDecode, const char* source, char* dest, int originalSize); + + +/*! LZ4_decompress_*_usingDict() : + * These decoding functions work the same as + * a combination of LZ4_setStreamDecode() followed by LZ4_decompress_*_continue() + * They are stand-alone, and don't need an LZ4_streamDecode_t structure. + */ +LZ4LIB_API int LZ4_decompress_safe_usingDict (const char* source, char* dest, int compressedSize, int maxDecompressedSize, const char* dictStart, int dictSize); +LZ4LIB_API int LZ4_decompress_fast_usingDict (const char* source, char* dest, int originalSize, const char* dictStart, int dictSize); + + +/*^********************************************** + * !!!!!! STATIC LINKING ONLY !!!!!! + ***********************************************/ +/*-************************************ + * Private definitions + ************************************** + * Do not use these definitions. + * They are exposed to allow static allocation of `LZ4_stream_t` and `LZ4_streamDecode_t`. + * Using these definitions will expose code to API and/or ABI break in future versions of the library. + **************************************/ +#define LZ4_HASHLOG (LZ4_MEMORY_USAGE-2) +#define LZ4_HASHTABLESIZE (1 << LZ4_MEMORY_USAGE) +#define LZ4_HASH_SIZE_U32 (1 << LZ4_HASHLOG) /* required as macro for static allocation */ + +#if defined(__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) + +typedef struct { + uint32_t hashTable[LZ4_HASH_SIZE_U32]; + uint32_t currentOffset; + uint32_t initCheck; + const uint8_t* dictionary; + uint8_t* bufferStart; /* obsolete, used for slideInputBuffer */ + uint32_t dictSize; +} LZ4_stream_t_internal; + +typedef struct { + const uint8_t* externalDict; + size_t extDictSize; + const uint8_t* prefixEnd; + size_t prefixSize; +} LZ4_streamDecode_t_internal; + +#else + +typedef struct { + unsigned int hashTable[LZ4_HASH_SIZE_U32]; + unsigned int currentOffset; + unsigned int initCheck; + const unsigned char* dictionary; + unsigned char* bufferStart; /* obsolete, used for slideInputBuffer */ + unsigned int dictSize; +} LZ4_stream_t_internal; + +typedef struct { + const unsigned char* externalDict; + size_t extDictSize; + const unsigned char* prefixEnd; + size_t prefixSize; +} LZ4_streamDecode_t_internal; + +#endif + +/*! + * LZ4_stream_t : + * information structure to track an LZ4 stream. + * init this structure before first use. + * note : only use in association with static linking ! + * this definition is not API/ABI safe, + * and may change in a future version ! + */ +#define LZ4_STREAMSIZE_U64 ((1 << (LZ4_MEMORY_USAGE-3)) + 4) +#define LZ4_STREAMSIZE (LZ4_STREAMSIZE_U64 * sizeof(unsigned long long)) +union LZ4_stream_u { + unsigned long long table[LZ4_STREAMSIZE_U64]; + LZ4_stream_t_internal internal_donotuse; +} ; /* previously typedef'd to LZ4_stream_t */ + + +/*! + * LZ4_streamDecode_t : + * information structure to track an LZ4 stream during decompression. + * init this structure using LZ4_setStreamDecode (or memset()) before first use + * note : only use in association with static linking ! + * this definition is not API/ABI safe, + * and may change in a future version ! + */ +#define LZ4_STREAMDECODESIZE_U64 4 +#define LZ4_STREAMDECODESIZE (LZ4_STREAMDECODESIZE_U64 * sizeof(unsigned long long)) +union LZ4_streamDecode_u { + unsigned long long table[LZ4_STREAMDECODESIZE_U64]; + LZ4_streamDecode_t_internal internal_donotuse; +} ; /* previously typedef'd to LZ4_streamDecode_t */ + + +/*-************************************ +* Obsolete Functions +**************************************/ + +/*! Deprecation warnings + Should deprecation warnings be a problem, + it is generally possible to disable them, + typically with -Wno-deprecated-declarations for gcc + or _CRT_SECURE_NO_WARNINGS in Visual. + Otherwise, it's also possible to define LZ4_DISABLE_DEPRECATE_WARNINGS */ +#ifdef LZ4_DISABLE_DEPRECATE_WARNINGS +# define LZ4_DEPRECATED(message) /* disable deprecation warnings */ +#else +# define LZ4_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__) +# if defined (__cplusplus) && (__cplusplus >= 201402) /* C++14 or greater */ +# define LZ4_DEPRECATED(message) [[deprecated(message)]] +# elif (LZ4_GCC_VERSION >= 405) || defined(__clang__) +# define LZ4_DEPRECATED(message) __attribute__((deprecated(message))) +# elif (LZ4_GCC_VERSION >= 301) +# define LZ4_DEPRECATED(message) __attribute__((deprecated)) +# elif defined(_MSC_VER) +# define LZ4_DEPRECATED(message) __declspec(deprecated(message)) +# else +# pragma message("WARNING: You need to implement LZ4_DEPRECATED for this compiler") +# define LZ4_DEPRECATED(message) +# endif +#endif /* LZ4_DISABLE_DEPRECATE_WARNINGS */ + +/* Obsolete compression functions */ +LZ4LIB_API LZ4_DEPRECATED("use LZ4_compress_default() instead") int LZ4_compress (const char* source, char* dest, int sourceSize); +LZ4LIB_API LZ4_DEPRECATED("use LZ4_compress_default() instead") int LZ4_compress_limitedOutput (const char* source, char* dest, int sourceSize, int maxOutputSize); +LZ4LIB_API LZ4_DEPRECATED("use LZ4_compress_fast_extState() instead") int LZ4_compress_withState (void* state, const char* source, char* dest, int inputSize); +LZ4LIB_API LZ4_DEPRECATED("use LZ4_compress_fast_extState() instead") int LZ4_compress_limitedOutput_withState (void* state, const char* source, char* dest, int inputSize, int maxOutputSize); +LZ4LIB_API LZ4_DEPRECATED("use LZ4_compress_fast_continue() instead") int LZ4_compress_continue (LZ4_stream_t* LZ4_streamPtr, const char* source, char* dest, int inputSize); +LZ4LIB_API LZ4_DEPRECATED("use LZ4_compress_fast_continue() instead") int LZ4_compress_limitedOutput_continue (LZ4_stream_t* LZ4_streamPtr, const char* source, char* dest, int inputSize, int maxOutputSize); + +/* Obsolete decompression functions */ +LZ4LIB_API LZ4_DEPRECATED("use LZ4_decompress_fast() instead") int LZ4_uncompress (const char* source, char* dest, int outputSize); +LZ4LIB_API LZ4_DEPRECATED("use LZ4_decompress_safe() instead") int LZ4_uncompress_unknownOutputSize (const char* source, char* dest, int isize, int maxOutputSize); + +/* Obsolete streaming functions; use new streaming interface whenever possible */ +LZ4LIB_API LZ4_DEPRECATED("use LZ4_createStream() instead") void* LZ4_create (char* inputBuffer); +LZ4LIB_API LZ4_DEPRECATED("use LZ4_createStream() instead") int LZ4_sizeofStreamState(void); +LZ4LIB_API LZ4_DEPRECATED("use LZ4_resetStream() instead") int LZ4_resetStreamState(void* state, char* inputBuffer); +LZ4LIB_API LZ4_DEPRECATED("use LZ4_saveDict() instead") char* LZ4_slideInputBuffer (void* state); + +/* Obsolete streaming decoding functions */ +LZ4LIB_API LZ4_DEPRECATED("use LZ4_decompress_safe_usingDict() instead") int LZ4_decompress_safe_withPrefix64k (const char* src, char* dst, int compressedSize, int maxDstSize); +LZ4LIB_API LZ4_DEPRECATED("use LZ4_decompress_fast_usingDict() instead") int LZ4_decompress_fast_withPrefix64k (const char* src, char* dst, int originalSize); + +#endif /* LZ4_H_2983827168210 */ + +#if defined (__cplusplus) +} +#endif +/* + LZ4 - Fast LZ compression algorithm + Copyright (C) 2011-2017, Yann Collet. + + BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following disclaimer + in the documentation and/or other materials provided with the + distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + You can contact the author at : + - LZ4 homepage : http://www.lz4.org + - LZ4 source repository : https://github.com/lz4/lz4 +*/ + + +/*-************************************ +* Tuning parameters +**************************************/ +/* + * HEAPMODE : + * Select how default compression functions will allocate memory for their hash table, + * in memory stack (0:default, fastest), or in memory heap (1:requires malloc()). + */ +#ifndef HEAPMODE +# define HEAPMODE 0 +#endif + +/* + * ACCELERATION_DEFAULT : + * Select "acceleration" for LZ4_compress_fast() when parameter value <= 0 + */ +#define ACCELERATION_DEFAULT 1 + + +/*-************************************ +* CPU Feature Detection +**************************************/ +/* LZ4_FORCE_MEMORY_ACCESS + * By default, access to unaligned memory is controlled by `memcpy()`, which is safe and portable. + * Unfortunately, on some target/compiler combinations, the generated assembly is sub-optimal. + * The below switch allow to select different access method for improved performance. + * Method 0 (default) : use `memcpy()`. Safe and portable. + * Method 1 : `__packed` statement. It depends on compiler extension (ie, not portable). + * This method is safe if your compiler supports it, and *generally* as fast or faster than `memcpy`. + * Method 2 : direct access. This method is portable but violate C standard. + * It can generate buggy code on targets which assembly generation depends on alignment. + * But in some circumstances, it's the only known way to get the most performance (ie GCC + ARMv6) + * See https://fastcompression.blogspot.fr/2015/08/accessing-unaligned-memory.html for details. + * Prefer these methods in priority order (0 > 1 > 2) + */ +#ifndef LZ4_FORCE_MEMORY_ACCESS /* can be defined externally */ +# if defined(__GNUC__) && ( defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_6J__) || defined(__ARM_ARCH_6K__) || defined(__ARM_ARCH_6Z__) || defined(__ARM_ARCH_6ZK__) || defined(__ARM_ARCH_6T2__) ) +# define LZ4_FORCE_MEMORY_ACCESS 2 +# elif defined(__INTEL_COMPILER) || defined(__GNUC__) +# define LZ4_FORCE_MEMORY_ACCESS 1 +# endif +#endif + +/* + * LZ4_FORCE_SW_BITCOUNT + * Define this parameter if your target system or compiler does not support hardware bit count + */ +#if defined(_MSC_VER) && defined(_WIN32_WCE) /* Visual Studio for Windows CE does not support Hardware bit count */ +# define LZ4_FORCE_SW_BITCOUNT +#endif + + +/*-************************************ +* Dependency +**************************************/ +/* see also "memory routines" below */ + + +/*-************************************ +* Compiler Options +**************************************/ +#ifdef _MSC_VER /* Visual Studio */ +# define FORCE_INLINE static __forceinline +# pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */ +# pragma warning(disable : 4293) /* disable: C4293: too large shift (32-bits) */ +#else +# if defined(__GNUC__) || defined(__clang__) +# define FORCE_INLINE static inline __attribute__((always_inline)) +# elif defined(__cplusplus) || (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) +# define FORCE_INLINE static inline +# else +# define FORCE_INLINE static +# endif +#endif /* _MSC_VER */ + +#if (defined(__GNUC__) && (__GNUC__ >= 3)) || (defined(__INTEL_COMPILER) && (__INTEL_COMPILER >= 800)) || defined(__clang__) +# define expect(expr,value) (__builtin_expect ((expr),(value)) ) +#else +# define expect(expr,value) (expr) +#endif + +#define likely(expr) expect((expr) != 0, 1) +#define unlikely(expr) expect((expr) != 0, 0) + + +/*-************************************ +* Memory routines +**************************************/ +#define ALLOCATOR(n,s) calloc(n,s) +#define FREEMEM free +#define MEM_INIT memset + + +/*-************************************ +* Basic Types +**************************************/ +#if defined(__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) + typedef uint8_t BYTE; + typedef uint16_t U16; + typedef uint32_t U32; + typedef int32_t S32; + typedef uint64_t U64; + typedef uintptr_t uptrval; +#else + typedef unsigned char BYTE; + typedef unsigned short U16; + typedef unsigned int U32; + typedef signed int S32; + typedef unsigned long long U64; + typedef size_t uptrval; /* generally true, except OpenVMS-64 */ +#endif + +#if defined(__x86_64__) + typedef U64 reg_t; /* 64-bits in x32 mode */ +#else + typedef size_t reg_t; /* 32-bits in x32 mode */ +#endif + +/*-************************************ +* Reading and writing into memory +**************************************/ +static unsigned LZ4_isLittleEndian(void) +{ + const union { U32 u; BYTE c[4]; } one = { 1 }; /* don't use static : performance detrimental */ + return one.c[0]; +} + + +#if defined(LZ4_FORCE_MEMORY_ACCESS) && (LZ4_FORCE_MEMORY_ACCESS==2) +/* lie to the compiler about data alignment; use with caution */ + +static U16 LZ4_read16(const void* memPtr) { return *(const U16*) memPtr; } +static U32 LZ4_read32(const void* memPtr) { return *(const U32*) memPtr; } +static reg_t LZ4_read_ARCH(const void* memPtr) { return *(const reg_t*) memPtr; } + +static void LZ4_write16(void* memPtr, U16 value) { *(U16*)memPtr = value; } +static void LZ4_write32(void* memPtr, U32 value) { *(U32*)memPtr = value; } + +#elif defined(LZ4_FORCE_MEMORY_ACCESS) && (LZ4_FORCE_MEMORY_ACCESS==1) + +/* __pack instructions are safer, but compiler specific, hence potentially problematic for some compilers */ +/* currently only defined for gcc and icc */ +typedef union { U16 u16; U32 u32; reg_t uArch; } __attribute__((packed)) unalign; + +static U16 LZ4_read16(const void* ptr) { return ((const unalign*)ptr)->u16; } +static U32 LZ4_read32(const void* ptr) { return ((const unalign*)ptr)->u32; } +static reg_t LZ4_read_ARCH(const void* ptr) { return ((const unalign*)ptr)->uArch; } + +static void LZ4_write16(void* memPtr, U16 value) { ((unalign*)memPtr)->u16 = value; } +static void LZ4_write32(void* memPtr, U32 value) { ((unalign*)memPtr)->u32 = value; } + +#else /* safe and portable access through memcpy() */ + +static U16 LZ4_read16(const void* memPtr) +{ + U16 val; memcpy(&val, memPtr, sizeof(val)); return val; +} + +static U32 LZ4_read32(const void* memPtr) +{ + U32 val; memcpy(&val, memPtr, sizeof(val)); return val; +} + +static reg_t LZ4_read_ARCH(const void* memPtr) +{ + reg_t val; memcpy(&val, memPtr, sizeof(val)); return val; +} + +static void LZ4_write16(void* memPtr, U16 value) +{ + memcpy(memPtr, &value, sizeof(value)); +} + +static void LZ4_write32(void* memPtr, U32 value) +{ + memcpy(memPtr, &value, sizeof(value)); +} + +#endif /* LZ4_FORCE_MEMORY_ACCESS */ + + +static U16 LZ4_readLE16(const void* memPtr) +{ + if (LZ4_isLittleEndian()) { + return LZ4_read16(memPtr); + } else { + const BYTE* p = (const BYTE*)memPtr; + return (U16)((U16)p[0] + (p[1]<<8)); + } +} + +static void LZ4_writeLE16(void* memPtr, U16 value) +{ + if (LZ4_isLittleEndian()) { + LZ4_write16(memPtr, value); + } else { + BYTE* p = (BYTE*)memPtr; + p[0] = (BYTE) value; + p[1] = (BYTE)(value>>8); + } +} + +static void LZ4_copy8(void* dst, const void* src) +{ + memcpy(dst,src,8); +} + +/* customized variant of memcpy, which can overwrite up to 8 bytes beyond dstEnd */ +static void LZ4_wildCopy(void* dstPtr, const void* srcPtr, void* dstEnd) +{ + BYTE* d = (BYTE*)dstPtr; + const BYTE* s = (const BYTE*)srcPtr; + BYTE* const e = (BYTE*)dstEnd; + + do { LZ4_copy8(d,s); d+=8; s+=8; } while (d<e); +} + + +/*-************************************ +* Common Constants +**************************************/ +#define MINMATCH 4 + +#define WILDCOPYLENGTH 8 +#define LASTLITERALS 5 +#define MFLIMIT (WILDCOPYLENGTH+MINMATCH) +static const int LZ4_minLength = (MFLIMIT+1); + +#define KB *(1 <<10) +#define MB *(1 <<20) +#define GB *(1U<<30) + +#define MAXD_LOG 16 +#define MAX_DISTANCE ((1 << MAXD_LOG) - 1) + +#define ML_BITS 4 +#define ML_MASK ((1U<<ML_BITS)-1) +#define RUN_BITS (8-ML_BITS) +#define RUN_MASK ((1U<<RUN_BITS)-1) + + +/*-************************************ +* Common Utils +**************************************/ +#define LZ4_STATIC_ASSERT(c) { enum { LZ4_static_assert = 1/(int)(!!(c)) }; } /* use only *after* variable declarations */ + + +/*-************************************ +* Common functions +**************************************/ +static unsigned LZ4_NbCommonBytes (register reg_t val) +{ + if (LZ4_isLittleEndian()) { + if (sizeof(val)==8) { +# if defined(_MSC_VER) && defined(_WIN64) && !defined(LZ4_FORCE_SW_BITCOUNT) + unsigned long r = 0; + _BitScanForward64( &r, (U64)val ); + return (int)(r>>3); +# elif (defined(__clang__) || (defined(__GNUC__) && (__GNUC__>=3))) && !defined(LZ4_FORCE_SW_BITCOUNT) + return (__builtin_ctzll((U64)val) >> 3); +# else + static const int DeBruijnBytePos[64] = { 0, 0, 0, 0, 0, 1, 1, 2, 0, 3, 1, 3, 1, 4, 2, 7, 0, 2, 3, 6, 1, 5, 3, 5, 1, 3, 4, 4, 2, 5, 6, 7, 7, 0, 1, 2, 3, 3, 4, 6, 2, 6, 5, 5, 3, 4, 5, 6, 7, 1, 2, 4, 6, 4, 4, 5, 7, 2, 6, 5, 7, 6, 7, 7 }; + return DeBruijnBytePos[((U64)((val & -(long long)val) * 0x0218A392CDABBD3FULL)) >> 58]; +# endif + } else /* 32 bits */ { +# if defined(_MSC_VER) && !defined(LZ4_FORCE_SW_BITCOUNT) + unsigned long r; + _BitScanForward( &r, (U32)val ); + return (int)(r>>3); +# elif (defined(__clang__) || (defined(__GNUC__) && (__GNUC__>=3))) && !defined(LZ4_FORCE_SW_BITCOUNT) + return (__builtin_ctz((U32)val) >> 3); +# else + static const int DeBruijnBytePos[32] = { 0, 0, 3, 0, 3, 1, 3, 0, 3, 2, 2, 1, 3, 2, 0, 1, 3, 3, 1, 2, 2, 2, 2, 0, 3, 1, 2, 0, 1, 0, 1, 1 }; + return DeBruijnBytePos[((U32)((val & -(S32)val) * 0x077CB531U)) >> 27]; +# endif + } + } else /* Big Endian CPU */ { + if (sizeof(val)==8) { +# if defined(_MSC_VER) && defined(_WIN64) && !defined(LZ4_FORCE_SW_BITCOUNT) + unsigned long r = 0; + _BitScanReverse64( &r, val ); + return (unsigned)(r>>3); +# elif (defined(__clang__) || (defined(__GNUC__) && (__GNUC__>=3))) && !defined(LZ4_FORCE_SW_BITCOUNT) + return (__builtin_clzll((U64)val) >> 3); +# else + unsigned r; + if (!(val>>32)) { r=4; } else { r=0; val>>=32; } + if (!(val>>16)) { r+=2; val>>=8; } else { val>>=24; } + r += (!val); + return r; +# endif + } else /* 32 bits */ { +# if defined(_MSC_VER) && !defined(LZ4_FORCE_SW_BITCOUNT) + unsigned long r = 0; + _BitScanReverse( &r, (unsigned long)val ); + return (unsigned)(r>>3); +# elif (defined(__clang__) || (defined(__GNUC__) && (__GNUC__>=3))) && !defined(LZ4_FORCE_SW_BITCOUNT) + return (__builtin_clz((U32)val) >> 3); +# else + unsigned r; + if (!(val>>16)) { r=2; val>>=8; } else { r=0; val>>=24; } + r += (!val); + return r; +# endif + } + } +} + +#define STEPSIZE sizeof(reg_t) +static unsigned LZ4_count(const BYTE* pIn, const BYTE* pMatch, const BYTE* pInLimit) +{ + const BYTE* const pStart = pIn; + + while (likely(pIn<pInLimit-(STEPSIZE-1))) { + reg_t const diff = LZ4_read_ARCH(pMatch) ^ LZ4_read_ARCH(pIn); + if (!diff) { pIn+=STEPSIZE; pMatch+=STEPSIZE; continue; } + pIn += LZ4_NbCommonBytes(diff); + return (unsigned)(pIn - pStart); + } + + if ((STEPSIZE==8) && (pIn<(pInLimit-3)) && (LZ4_read32(pMatch) == LZ4_read32(pIn))) { pIn+=4; pMatch+=4; } + if ((pIn<(pInLimit-1)) && (LZ4_read16(pMatch) == LZ4_read16(pIn))) { pIn+=2; pMatch+=2; } + if ((pIn<pInLimit) && (*pMatch == *pIn)) pIn++; + return (unsigned)(pIn - pStart); +} + + +#ifndef LZ4_COMMONDEFS_ONLY +/*-************************************ +* Local Constants +**************************************/ +static const int LZ4_64Klimit = ((64 KB) + (MFLIMIT-1)); +static const U32 LZ4_skipTrigger = 6; /* Increase this value ==> compression run slower on incompressible data */ + + +/*-************************************ +* Local Structures and types +**************************************/ +typedef enum { + noLimit = 0,notLimited = 0, + limitedOutput = 1, + limitedDestSize = 2, +} limitedOutput_directive; +typedef enum { byPtr, byU32, byU16 } tableType_t; + +typedef enum { noDict = 0, withPrefix64k, usingExtDict } dict_directive; +typedef enum { noDictIssue = 0, dictSmall } dictIssue_directive; + +typedef enum { endOnOutputSize = 0, endOnInputSize = 1 } endCondition_directive; +typedef enum { full = 0, partial = 1 } earlyEnd_directive; + + +/*-************************************ +* Local Utils +**************************************/ +int LZ4_versionNumber (void) { return LZ4_VERSION_NUMBER; } +const char* LZ4_versionString(void) { return LZ4_VERSION_STRING; } +int LZ4_compressBound(int isize) { return LZ4_COMPRESSBOUND(isize); } +int LZ4_sizeofState() { return LZ4_STREAMSIZE; } + + +/*-****************************** +* Compression functions +********************************/ +static U32 LZ4_hash4(U32 sequence, tableType_t const tableType) +{ + if (tableType == byU16) + return ((sequence * 2654435761U) >> ((MINMATCH*8)-(LZ4_HASHLOG+1))); + else + return ((sequence * 2654435761U) >> ((MINMATCH*8)-LZ4_HASHLOG)); +} + +static U32 LZ4_hash5(U64 sequence, tableType_t const tableType) +{ + static const U64 prime5bytes = 889523592379ULL; + static const U64 prime8bytes = 11400714785074694791ULL; + const U32 hashLog = (tableType == byU16) ? LZ4_HASHLOG+1 : LZ4_HASHLOG; + if (LZ4_isLittleEndian()) + return (U32)(((sequence << 24) * prime5bytes) >> (64 - hashLog)); + else + return (U32)(((sequence >> 24) * prime8bytes) >> (64 - hashLog)); +} + +FORCE_INLINE U32 LZ4_hashPosition(const void* const p, tableType_t const tableType) +{ + if ((sizeof(reg_t)==8) && (tableType != byU16)) return LZ4_hash5(LZ4_read_ARCH(p), tableType); + return LZ4_hash4(LZ4_read32(p), tableType); +} + +static void LZ4_putPositionOnHash(const BYTE* p, U32 h, void* tableBase, tableType_t const tableType, const BYTE* srcBase) +{ + switch (tableType) + { + case byPtr: { const BYTE** hashTable = (const BYTE**)tableBase; hashTable[h] = p; return; } + case byU32: { U32* hashTable = (U32*) tableBase; hashTable[h] = (U32)(p-srcBase); return; } + case byU16: { U16* hashTable = (U16*) tableBase; hashTable[h] = (U16)(p-srcBase); return; } + } +} + +FORCE_INLINE void LZ4_putPosition(const BYTE* p, void* tableBase, tableType_t tableType, const BYTE* srcBase) +{ + U32 const h = LZ4_hashPosition(p, tableType); + LZ4_putPositionOnHash(p, h, tableBase, tableType, srcBase); +} + +static const BYTE* LZ4_getPositionOnHash(U32 h, void* tableBase, tableType_t tableType, const BYTE* srcBase) +{ + if (tableType == byPtr) { const BYTE** hashTable = (const BYTE**) tableBase; return hashTable[h]; } + if (tableType == byU32) { const U32* const hashTable = (U32*) tableBase; return hashTable[h] + srcBase; } + { const U16* const hashTable = (U16*) tableBase; return hashTable[h] + srcBase; } /* default, to ensure a return */ +} + +FORCE_INLINE const BYTE* LZ4_getPosition(const BYTE* p, void* tableBase, tableType_t tableType, const BYTE* srcBase) +{ + U32 const h = LZ4_hashPosition(p, tableType); + return LZ4_getPositionOnHash(h, tableBase, tableType, srcBase); +} + + +/** LZ4_compress_generic() : + inlined, to ensure branches are decided at compilation time */ +FORCE_INLINE int LZ4_compress_generic( + LZ4_stream_t_internal* const cctx, + const char* const source, + char* const dest, + const int inputSize, + const int maxOutputSize, + const limitedOutput_directive outputLimited, + const tableType_t tableType, + const dict_directive dict, + const dictIssue_directive dictIssue, + const U32 acceleration) +{ + const BYTE* ip = (const BYTE*) source; + const BYTE* base; + const BYTE* lowLimit; + const BYTE* const lowRefLimit = ip - cctx->dictSize; + const BYTE* const dictionary = cctx->dictionary; + const BYTE* const dictEnd = dictionary + cctx->dictSize; + const ptrdiff_t dictDelta = dictEnd - (const BYTE*)source; + const BYTE* anchor = (const BYTE*) source; + const BYTE* const iend = ip + inputSize; + const BYTE* const mflimit = iend - MFLIMIT; + const BYTE* const matchlimit = iend - LASTLITERALS; + + BYTE* op = (BYTE*) dest; + BYTE* const olimit = op + maxOutputSize; + + U32 forwardH; + + /* Init conditions */ + if ((U32)inputSize > (U32)LZ4_MAX_INPUT_SIZE) return 0; /* Unsupported inputSize, too large (or negative) */ + switch(dict) + { + case noDict: + default: + base = (const BYTE*)source; + lowLimit = (const BYTE*)source; + break; + case withPrefix64k: + base = (const BYTE*)source - cctx->currentOffset; + lowLimit = (const BYTE*)source - cctx->dictSize; + break; + case usingExtDict: + base = (const BYTE*)source - cctx->currentOffset; + lowLimit = (const BYTE*)source; + break; + } + if ((tableType == byU16) && (inputSize>=LZ4_64Klimit)) return 0; /* Size too large (not within 64K limit) */ + if (inputSize<LZ4_minLength) goto _last_literals; /* Input too small, no compression (all literals) */ + + /* First Byte */ + LZ4_putPosition(ip, cctx->hashTable, tableType, base); + ip++; forwardH = LZ4_hashPosition(ip, tableType); + + /* Main Loop */ + for ( ; ; ) { + ptrdiff_t refDelta = 0; + const BYTE* match; + BYTE* token; + + /* Find a match */ + { const BYTE* forwardIp = ip; + unsigned step = 1; + unsigned searchMatchNb = acceleration << LZ4_skipTrigger; + do { + U32 const h = forwardH; + ip = forwardIp; + forwardIp += step; + step = (searchMatchNb++ >> LZ4_skipTrigger); + + if (unlikely(forwardIp > mflimit)) goto _last_literals; + + match = LZ4_getPositionOnHash(h, cctx->hashTable, tableType, base); + if (dict==usingExtDict) { + if (match < (const BYTE*)source) { + refDelta = dictDelta; + lowLimit = dictionary; + } else { + refDelta = 0; + lowLimit = (const BYTE*)source; + } } + forwardH = LZ4_hashPosition(forwardIp, tableType); + LZ4_putPositionOnHash(ip, h, cctx->hashTable, tableType, base); + + } while ( ((dictIssue==dictSmall) ? (match < lowRefLimit) : 0) + || ((tableType==byU16) ? 0 : (match + MAX_DISTANCE < ip)) + || (LZ4_read32(match+refDelta) != LZ4_read32(ip)) ); + } + + /* Catch up */ + while (((ip>anchor) & (match+refDelta > lowLimit)) && (unlikely(ip[-1]==match[refDelta-1]))) { ip--; match--; } + + /* Encode Literals */ + { unsigned const litLength = (unsigned)(ip - anchor); + token = op++; + if ((outputLimited) && /* Check output buffer overflow */ + (unlikely(op + litLength + (2 + 1 + LASTLITERALS) + (litLength/255) > olimit))) + return 0; + if (litLength >= RUN_MASK) { + int len = (int)litLength-RUN_MASK; + *token = (RUN_MASK<<ML_BITS); + for(; len >= 255 ; len-=255) *op++ = 255; + *op++ = (BYTE)len; + } + else *token = (BYTE)(litLength<<ML_BITS); + + /* Copy Literals */ + LZ4_wildCopy(op, anchor, op+litLength); + op+=litLength; + } + +_next_match: + /* Encode Offset */ + LZ4_writeLE16(op, (U16)(ip-match)); op+=2; + + /* Encode MatchLength */ + { unsigned matchCode; + + if ((dict==usingExtDict) && (lowLimit==dictionary)) { + const BYTE* limit; + match += refDelta; + limit = ip + (dictEnd-match); + if (limit > matchlimit) limit = matchlimit; + matchCode = LZ4_count(ip+MINMATCH, match+MINMATCH, limit); + ip += MINMATCH + matchCode; + if (ip==limit) { + unsigned const more = LZ4_count(ip, (const BYTE*)source, matchlimit); + matchCode += more; + ip += more; + } + } else { + matchCode = LZ4_count(ip+MINMATCH, match+MINMATCH, matchlimit); + ip += MINMATCH + matchCode; + } + + if ( outputLimited && /* Check output buffer overflow */ + (unlikely(op + (1 + LASTLITERALS) + (matchCode>>8) > olimit)) ) + return 0; + if (matchCode >= ML_MASK) { + *token += ML_MASK; + matchCode -= ML_MASK; + LZ4_write32(op, 0xFFFFFFFF); + while (matchCode >= 4*255) op+=4, LZ4_write32(op, 0xFFFFFFFF), matchCode -= 4*255; + op += matchCode / 255; + *op++ = (BYTE)(matchCode % 255); + } else + *token += (BYTE)(matchCode); + } + + anchor = ip; + + /* Test end of chunk */ + if (ip > mflimit) break; + + /* Fill table */ + LZ4_putPosition(ip-2, cctx->hashTable, tableType, base); + + /* Test next position */ + match = LZ4_getPosition(ip, cctx->hashTable, tableType, base); + if (dict==usingExtDict) { + if (match < (const BYTE*)source) { + refDelta = dictDelta; + lowLimit = dictionary; + } else { + refDelta = 0; + lowLimit = (const BYTE*)source; + } } + LZ4_putPosition(ip, cctx->hashTable, tableType, base); + if ( ((dictIssue==dictSmall) ? (match>=lowRefLimit) : 1) + && (match+MAX_DISTANCE>=ip) + && (LZ4_read32(match+refDelta)==LZ4_read32(ip)) ) + { token=op++; *token=0; goto _next_match; } + + /* Prepare next loop */ + forwardH = LZ4_hashPosition(++ip, tableType); + } + +_last_literals: + /* Encode Last Literals */ + { size_t const lastRun = (size_t)(iend - anchor); + if ( (outputLimited) && /* Check output buffer overflow */ + ((op - (BYTE*)dest) + lastRun + 1 + ((lastRun+255-RUN_MASK)/255) > (U32)maxOutputSize) ) + return 0; + if (lastRun >= RUN_MASK) { + size_t accumulator = lastRun - RUN_MASK; + *op++ = RUN_MASK << ML_BITS; + for(; accumulator >= 255 ; accumulator-=255) *op++ = 255; + *op++ = (BYTE) accumulator; + } else { + *op++ = (BYTE)(lastRun<<ML_BITS); + } + memcpy(op, anchor, lastRun); + op += lastRun; + } + + /* End */ + return (int) (((char*)op)-dest); +} + + +int LZ4_compress_fast_extState(void* state, const char* source, char* dest, int inputSize, int maxOutputSize, int acceleration) +{ + LZ4_stream_t_internal* ctx = &((LZ4_stream_t*)state)->internal_donotuse; + LZ4_resetStream((LZ4_stream_t*)state); + if (acceleration < 1) acceleration = ACCELERATION_DEFAULT; + + if (maxOutputSize >= LZ4_compressBound(inputSize)) { + if (inputSize < LZ4_64Klimit) + return LZ4_compress_generic(ctx, source, dest, inputSize, 0, notLimited, byU16, noDict, noDictIssue, acceleration); + else + return LZ4_compress_generic(ctx, source, dest, inputSize, 0, notLimited, (sizeof(void*)==8) ? byU32 : byPtr, noDict, noDictIssue, acceleration); + } else { + if (inputSize < LZ4_64Klimit) + return LZ4_compress_generic(ctx, source, dest, inputSize, maxOutputSize, limitedOutput, byU16, noDict, noDictIssue, acceleration); + else + return LZ4_compress_generic(ctx, source, dest, inputSize, maxOutputSize, limitedOutput, (sizeof(void*)==8) ? byU32 : byPtr, noDict, noDictIssue, acceleration); + } +} + + +int LZ4_compress_fast(const char* source, char* dest, int inputSize, int maxOutputSize, int acceleration) +{ +#if (HEAPMODE) + void* ctxPtr = ALLOCATOR(1, sizeof(LZ4_stream_t)); /* malloc-calloc always properly aligned */ +#else + LZ4_stream_t ctx; + void* const ctxPtr = &ctx; +#endif + + int const result = LZ4_compress_fast_extState(ctxPtr, source, dest, inputSize, maxOutputSize, acceleration); + +#if (HEAPMODE) + FREEMEM(ctxPtr); +#endif + return result; +} + + +int LZ4_compress_default(const char* source, char* dest, int inputSize, int maxOutputSize) +{ + return LZ4_compress_fast(source, dest, inputSize, maxOutputSize, 1); +} + + +/* hidden debug function */ +/* strangely enough, gcc generates faster code when this function is uncommented, even if unused */ +int LZ4_compress_fast_force(const char* source, char* dest, int inputSize, int maxOutputSize, int acceleration) +{ + LZ4_stream_t ctx; + LZ4_resetStream(&ctx); + + if (inputSize < LZ4_64Klimit) + return LZ4_compress_generic(&ctx.internal_donotuse, source, dest, inputSize, maxOutputSize, limitedOutput, byU16, noDict, noDictIssue, acceleration); + else + return LZ4_compress_generic(&ctx.internal_donotuse, source, dest, inputSize, maxOutputSize, limitedOutput, sizeof(void*)==8 ? byU32 : byPtr, noDict, noDictIssue, acceleration); +} + + +/*-****************************** +* *_destSize() variant +********************************/ + +static int LZ4_compress_destSize_generic( + LZ4_stream_t_internal* const ctx, + const char* const src, + char* const dst, + int* const srcSizePtr, + const int targetDstSize, + const tableType_t tableType) +{ + const BYTE* ip = (const BYTE*) src; + const BYTE* base = (const BYTE*) src; + const BYTE* lowLimit = (const BYTE*) src; + const BYTE* anchor = ip; + const BYTE* const iend = ip + *srcSizePtr; + const BYTE* const mflimit = iend - MFLIMIT; + const BYTE* const matchlimit = iend - LASTLITERALS; + + BYTE* op = (BYTE*) dst; + BYTE* const oend = op + targetDstSize; + BYTE* const oMaxLit = op + targetDstSize - 2 /* offset */ - 8 /* because 8+MINMATCH==MFLIMIT */ - 1 /* token */; + BYTE* const oMaxMatch = op + targetDstSize - (LASTLITERALS + 1 /* token */); + BYTE* const oMaxSeq = oMaxLit - 1 /* token */; + + U32 forwardH; + + + /* Init conditions */ + if (targetDstSize < 1) return 0; /* Impossible to store anything */ + if ((U32)*srcSizePtr > (U32)LZ4_MAX_INPUT_SIZE) return 0; /* Unsupported input size, too large (or negative) */ + if ((tableType == byU16) && (*srcSizePtr>=LZ4_64Klimit)) return 0; /* Size too large (not within 64K limit) */ + if (*srcSizePtr<LZ4_minLength) goto _last_literals; /* Input too small, no compression (all literals) */ + + /* First Byte */ + *srcSizePtr = 0; + LZ4_putPosition(ip, ctx->hashTable, tableType, base); + ip++; forwardH = LZ4_hashPosition(ip, tableType); + + /* Main Loop */ + for ( ; ; ) { + const BYTE* match; + BYTE* token; + + /* Find a match */ + { const BYTE* forwardIp = ip; + unsigned step = 1; + unsigned searchMatchNb = 1 << LZ4_skipTrigger; + + do { + U32 h = forwardH; + ip = forwardIp; + forwardIp += step; + step = (searchMatchNb++ >> LZ4_skipTrigger); + + if (unlikely(forwardIp > mflimit)) goto _last_literals; + + match = LZ4_getPositionOnHash(h, ctx->hashTable, tableType, base); + forwardH = LZ4_hashPosition(forwardIp, tableType); + LZ4_putPositionOnHash(ip, h, ctx->hashTable, tableType, base); + + } while ( ((tableType==byU16) ? 0 : (match + MAX_DISTANCE < ip)) + || (LZ4_read32(match) != LZ4_read32(ip)) ); + } + + /* Catch up */ + while ((ip>anchor) && (match > lowLimit) && (unlikely(ip[-1]==match[-1]))) { ip--; match--; } + + /* Encode Literal length */ + { unsigned litLength = (unsigned)(ip - anchor); + token = op++; + if (op + ((litLength+240)/255) + litLength > oMaxLit) { + /* Not enough space for a last match */ + op--; + goto _last_literals; + } + if (litLength>=RUN_MASK) { + unsigned len = litLength - RUN_MASK; + *token=(RUN_MASK<<ML_BITS); + for(; len >= 255 ; len-=255) *op++ = 255; + *op++ = (BYTE)len; + } + else *token = (BYTE)(litLength<<ML_BITS); + + /* Copy Literals */ + LZ4_wildCopy(op, anchor, op+litLength); + op += litLength; + } + +_next_match: + /* Encode Offset */ + LZ4_writeLE16(op, (U16)(ip-match)); op+=2; + + /* Encode MatchLength */ + { size_t matchLength = LZ4_count(ip+MINMATCH, match+MINMATCH, matchlimit); + + if (op + ((matchLength+240)/255) > oMaxMatch) { + /* Match description too long : reduce it */ + matchLength = (15-1) + (oMaxMatch-op) * 255; + } + ip += MINMATCH + matchLength; + + if (matchLength>=ML_MASK) { + *token += ML_MASK; + matchLength -= ML_MASK; + while (matchLength >= 255) { matchLength-=255; *op++ = 255; } + *op++ = (BYTE)matchLength; + } + else *token += (BYTE)(matchLength); + } + + anchor = ip; + + /* Test end of block */ + if (ip > mflimit) break; + if (op > oMaxSeq) break; + + /* Fill table */ + LZ4_putPosition(ip-2, ctx->hashTable, tableType, base); + + /* Test next position */ + match = LZ4_getPosition(ip, ctx->hashTable, tableType, base); + LZ4_putPosition(ip, ctx->hashTable, tableType, base); + if ( (match+MAX_DISTANCE>=ip) + && (LZ4_read32(match)==LZ4_read32(ip)) ) + { token=op++; *token=0; goto _next_match; } + + /* Prepare next loop */ + forwardH = LZ4_hashPosition(++ip, tableType); + } + +_last_literals: + /* Encode Last Literals */ + { size_t lastRunSize = (size_t)(iend - anchor); + if (op + 1 /* token */ + ((lastRunSize+240)/255) /* litLength */ + lastRunSize /* literals */ > oend) { + /* adapt lastRunSize to fill 'dst' */ + lastRunSize = (oend-op) - 1; + lastRunSize -= (lastRunSize+240)/255; + } + ip = anchor + lastRunSize; + + if (lastRunSize >= RUN_MASK) { + size_t accumulator = lastRunSize - RUN_MASK; + *op++ = RUN_MASK << ML_BITS; + for(; accumulator >= 255 ; accumulator-=255) *op++ = 255; + *op++ = (BYTE) accumulator; + } else { + *op++ = (BYTE)(lastRunSize<<ML_BITS); + } + memcpy(op, anchor, lastRunSize); + op += lastRunSize; + } + + /* End */ + *srcSizePtr = (int) (((const char*)ip)-src); + return (int) (((char*)op)-dst); +} + + +static int LZ4_compress_destSize_extState (LZ4_stream_t* state, const char* src, char* dst, int* srcSizePtr, int targetDstSize) +{ + LZ4_resetStream(state); + + if (targetDstSize >= LZ4_compressBound(*srcSizePtr)) { /* compression success is guaranteed */ + return LZ4_compress_fast_extState(state, src, dst, *srcSizePtr, targetDstSize, 1); + } else { + if (*srcSizePtr < LZ4_64Klimit) + return LZ4_compress_destSize_generic(&state->internal_donotuse, src, dst, srcSizePtr, targetDstSize, byU16); + else + return LZ4_compress_destSize_generic(&state->internal_donotuse, src, dst, srcSizePtr, targetDstSize, sizeof(void*)==8 ? byU32 : byPtr); + } +} + + +int LZ4_compress_destSize(const char* src, char* dst, int* srcSizePtr, int targetDstSize) +{ +#if (HEAPMODE) + LZ4_stream_t* ctx = (LZ4_stream_t*)ALLOCATOR(1, sizeof(LZ4_stream_t)); /* malloc-calloc always properly aligned */ +#else + LZ4_stream_t ctxBody; + LZ4_stream_t* ctx = &ctxBody; +#endif + + int result = LZ4_compress_destSize_extState(ctx, src, dst, srcSizePtr, targetDstSize); + +#if (HEAPMODE) + FREEMEM(ctx); +#endif + return result; +} + + + +/*-****************************** +* Streaming functions +********************************/ + +LZ4_stream_t* LZ4_createStream(void) +{ + LZ4_stream_t* lz4s = (LZ4_stream_t*)ALLOCATOR(8, LZ4_STREAMSIZE_U64); + LZ4_STATIC_ASSERT(LZ4_STREAMSIZE >= sizeof(LZ4_stream_t_internal)); /* A compilation error here means LZ4_STREAMSIZE is not large enough */ + LZ4_resetStream(lz4s); + return lz4s; +} + +void LZ4_resetStream (LZ4_stream_t* LZ4_stream) +{ + MEM_INIT(LZ4_stream, 0, sizeof(LZ4_stream_t)); +} + +int LZ4_freeStream (LZ4_stream_t* LZ4_stream) +{ + FREEMEM(LZ4_stream); + return (0); +} + + +#define HASH_UNIT sizeof(reg_t) +int LZ4_loadDict (LZ4_stream_t* LZ4_dict, const char* dictionary, int dictSize) +{ + LZ4_stream_t_internal* dict = &LZ4_dict->internal_donotuse; + const BYTE* p = (const BYTE*)dictionary; + const BYTE* const dictEnd = p + dictSize; + const BYTE* base; + + if ((dict->initCheck) || (dict->currentOffset > 1 GB)) /* Uninitialized structure, or reuse overflow */ + LZ4_resetStream(LZ4_dict); + + if (dictSize < (int)HASH_UNIT) { + dict->dictionary = NULL; + dict->dictSize = 0; + return 0; + } + + if ((dictEnd - p) > 64 KB) p = dictEnd - 64 KB; + dict->currentOffset += 64 KB; + base = p - dict->currentOffset; + dict->dictionary = p; + dict->dictSize = (U32)(dictEnd - p); + dict->currentOffset += dict->dictSize; + + while (p <= dictEnd-HASH_UNIT) { + LZ4_putPosition(p, dict->hashTable, byU32, base); + p+=3; + } + + return dict->dictSize; +} + + +static void LZ4_renormDictT(LZ4_stream_t_internal* LZ4_dict, const BYTE* src) +{ + if ((LZ4_dict->currentOffset > 0x80000000) || + ((uptrval)LZ4_dict->currentOffset > (uptrval)src)) { /* address space overflow */ + /* rescale hash table */ + U32 const delta = LZ4_dict->currentOffset - 64 KB; + const BYTE* dictEnd = LZ4_dict->dictionary + LZ4_dict->dictSize; + int i; + for (i=0; i<LZ4_HASH_SIZE_U32; i++) { + if (LZ4_dict->hashTable[i] < delta) LZ4_dict->hashTable[i]=0; + else LZ4_dict->hashTable[i] -= delta; + } + LZ4_dict->currentOffset = 64 KB; + if (LZ4_dict->dictSize > 64 KB) LZ4_dict->dictSize = 64 KB; + LZ4_dict->dictionary = dictEnd - LZ4_dict->dictSize; + } +} + + +int LZ4_compress_fast_continue (LZ4_stream_t* LZ4_stream, const char* source, char* dest, int inputSize, int maxOutputSize, int acceleration) +{ + LZ4_stream_t_internal* streamPtr = &LZ4_stream->internal_donotuse; + const BYTE* const dictEnd = streamPtr->dictionary + streamPtr->dictSize; + + const BYTE* smallest = (const BYTE*) source; + if (streamPtr->initCheck) return 0; /* Uninitialized structure detected */ + if ((streamPtr->dictSize>0) && (smallest>dictEnd)) smallest = dictEnd; + LZ4_renormDictT(streamPtr, smallest); + if (acceleration < 1) acceleration = ACCELERATION_DEFAULT; + + /* Check overlapping input/dictionary space */ + { const BYTE* sourceEnd = (const BYTE*) source + inputSize; + if ((sourceEnd > streamPtr->dictionary) && (sourceEnd < dictEnd)) { + streamPtr->dictSize = (U32)(dictEnd - sourceEnd); + if (streamPtr->dictSize > 64 KB) streamPtr->dictSize = 64 KB; + if (streamPtr->dictSize < 4) streamPtr->dictSize = 0; + streamPtr->dictionary = dictEnd - streamPtr->dictSize; + } + } + + /* prefix mode : source data follows dictionary */ + if (dictEnd == (const BYTE*)source) { + int result; + if ((streamPtr->dictSize < 64 KB) && (streamPtr->dictSize < streamPtr->currentOffset)) + result = LZ4_compress_generic(streamPtr, source, dest, inputSize, maxOutputSize, limitedOutput, byU32, withPrefix64k, dictSmall, acceleration); + else + result = LZ4_compress_generic(streamPtr, source, dest, inputSize, maxOutputSize, limitedOutput, byU32, withPrefix64k, noDictIssue, acceleration); + streamPtr->dictSize += (U32)inputSize; + streamPtr->currentOffset += (U32)inputSize; + return result; + } + + /* external dictionary mode */ + { int result; + if ((streamPtr->dictSize < 64 KB) && (streamPtr->dictSize < streamPtr->currentOffset)) + result = LZ4_compress_generic(streamPtr, source, dest, inputSize, maxOutputSize, limitedOutput, byU32, usingExtDict, dictSmall, acceleration); + else + result = LZ4_compress_generic(streamPtr, source, dest, inputSize, maxOutputSize, limitedOutput, byU32, usingExtDict, noDictIssue, acceleration); + streamPtr->dictionary = (const BYTE*)source; + streamPtr->dictSize = (U32)inputSize; + streamPtr->currentOffset += (U32)inputSize; + return result; + } +} + + +/* Hidden debug function, to force external dictionary mode */ +int LZ4_compress_forceExtDict (LZ4_stream_t* LZ4_dict, const char* source, char* dest, int inputSize) +{ + LZ4_stream_t_internal* streamPtr = &LZ4_dict->internal_donotuse; + int result; + const BYTE* const dictEnd = streamPtr->dictionary + streamPtr->dictSize; + + const BYTE* smallest = dictEnd; + if (smallest > (const BYTE*) source) smallest = (const BYTE*) source; + LZ4_renormDictT(streamPtr, smallest); + + result = LZ4_compress_generic(streamPtr, source, dest, inputSize, 0, notLimited, byU32, usingExtDict, noDictIssue, 1); + + streamPtr->dictionary = (const BYTE*)source; + streamPtr->dictSize = (U32)inputSize; + streamPtr->currentOffset += (U32)inputSize; + + return result; +} + + +/*! LZ4_saveDict() : + * If previously compressed data block is not guaranteed to remain available at its memory location, + * save it into a safer place (char* safeBuffer). + * Note : you don't need to call LZ4_loadDict() afterwards, + * dictionary is immediately usable, you can therefore call LZ4_compress_fast_continue(). + * Return : saved dictionary size in bytes (necessarily <= dictSize), or 0 if error. + */ +int LZ4_saveDict (LZ4_stream_t* LZ4_dict, char* safeBuffer, int dictSize) +{ + LZ4_stream_t_internal* const dict = &LZ4_dict->internal_donotuse; + const BYTE* const previousDictEnd = dict->dictionary + dict->dictSize; + + if ((U32)dictSize > 64 KB) dictSize = 64 KB; /* useless to define a dictionary > 64 KB */ + if ((U32)dictSize > dict->dictSize) dictSize = dict->dictSize; + + memmove(safeBuffer, previousDictEnd - dictSize, dictSize); + + dict->dictionary = (const BYTE*)safeBuffer; + dict->dictSize = (U32)dictSize; + + return dictSize; +} + + + +/*-***************************** +* Decompression functions +*******************************/ +/*! LZ4_decompress_generic() : + * This generic decompression function cover all use cases. + * It shall be instantiated several times, using different sets of directives + * Note that it is important this generic function is really inlined, + * in order to remove useless branches during compilation optimization. + */ +FORCE_INLINE int LZ4_decompress_generic( + const char* const source, + char* const dest, + int inputSize, + int outputSize, /* If endOnInput==endOnInputSize, this value is the max size of Output Buffer. */ + + int endOnInput, /* endOnOutputSize, endOnInputSize */ + int partialDecoding, /* full, partial */ + int targetOutputSize, /* only used if partialDecoding==partial */ + int dict, /* noDict, withPrefix64k, usingExtDict */ + const BYTE* const lowPrefix, /* == dest when no prefix */ + const BYTE* const dictStart, /* only if dict==usingExtDict */ + const size_t dictSize /* note : = 0 if noDict */ + ) +{ + /* Local Variables */ + const BYTE* ip = (const BYTE*) source; + const BYTE* const iend = ip + inputSize; + + BYTE* op = (BYTE*) dest; + BYTE* const oend = op + outputSize; + BYTE* cpy; + BYTE* oexit = op + targetOutputSize; + const BYTE* const lowLimit = lowPrefix - dictSize; + + const BYTE* const dictEnd = (const BYTE*)dictStart + dictSize; + const unsigned dec32table[] = {0, 1, 2, 1, 4, 4, 4, 4}; + const int dec64table[] = {0, 0, 0, -1, 0, 1, 2, 3}; + + const int safeDecode = (endOnInput==endOnInputSize); + const int checkOffset = ((safeDecode) && (dictSize < (int)(64 KB))); + + + /* Special cases */ + if ((partialDecoding) && (oexit > oend-MFLIMIT)) oexit = oend-MFLIMIT; /* targetOutputSize too high => decode everything */ + if ((endOnInput) && (unlikely(outputSize==0))) return ((inputSize==1) && (*ip==0)) ? 0 : -1; /* Empty output buffer */ + if ((!endOnInput) && (unlikely(outputSize==0))) return (*ip==0?1:-1); + + /* Main Loop : decode sequences */ + while (1) { + size_t length; + const BYTE* match; + size_t offset; + + /* get literal length */ + unsigned const token = *ip++; + if ((length=(token>>ML_BITS)) == RUN_MASK) { + unsigned s; + do { + s = *ip++; + length += s; + } while ( likely(endOnInput ? ip<iend-RUN_MASK : 1) & (s==255) ); + if ((safeDecode) && unlikely((uptrval)(op)+length<(uptrval)(op))) goto _output_error; /* overflow detection */ + if ((safeDecode) && unlikely((uptrval)(ip)+length<(uptrval)(ip))) goto _output_error; /* overflow detection */ + } + + /* copy literals */ + cpy = op+length; + if ( ((endOnInput) && ((cpy>(partialDecoding?oexit:oend-MFLIMIT)) || (ip+length>iend-(2+1+LASTLITERALS))) ) + || ((!endOnInput) && (cpy>oend-WILDCOPYLENGTH)) ) + { + if (partialDecoding) { + if (cpy > oend) goto _output_error; /* Error : write attempt beyond end of output buffer */ + if ((endOnInput) && (ip+length > iend)) goto _output_error; /* Error : read attempt beyond end of input buffer */ + } else { + if ((!endOnInput) && (cpy != oend)) goto _output_error; /* Error : block decoding must stop exactly there */ + if ((endOnInput) && ((ip+length != iend) || (cpy > oend))) goto _output_error; /* Error : input must be consumed */ + } + memcpy(op, ip, length); + ip += length; + op += length; + break; /* Necessarily EOF, due to parsing restrictions */ + } + LZ4_wildCopy(op, ip, cpy); + ip += length; op = cpy; + + /* get offset */ + offset = LZ4_readLE16(ip); ip+=2; + match = op - offset; + if ((checkOffset) && (unlikely(match < lowLimit))) goto _output_error; /* Error : offset outside buffers */ + LZ4_write32(op, (U32)offset); /* costs ~1%; silence an msan warning when offset==0 */ + + /* get matchlength */ + length = token & ML_MASK; + if (length == ML_MASK) { + unsigned s; + do { + s = *ip++; + if ((endOnInput) && (ip > iend-LASTLITERALS)) goto _output_error; + length += s; + } while (s==255); + if ((safeDecode) && unlikely((uptrval)(op)+length<(uptrval)op)) goto _output_error; /* overflow detection */ + } + length += MINMATCH; + + /* check external dictionary */ + if ((dict==usingExtDict) && (match < lowPrefix)) { + if (unlikely(op+length > oend-LASTLITERALS)) goto _output_error; /* doesn't respect parsing restriction */ + + if (length <= (size_t)(lowPrefix-match)) { + /* match can be copied as a single segment from external dictionary */ + memmove(op, dictEnd - (lowPrefix-match), length); + op += length; + } else { + /* match encompass external dictionary and current block */ + size_t const copySize = (size_t)(lowPrefix-match); + size_t const restSize = length - copySize; + memcpy(op, dictEnd - copySize, copySize); + op += copySize; + if (restSize > (size_t)(op-lowPrefix)) { /* overlap copy */ + BYTE* const endOfMatch = op + restSize; + const BYTE* copyFrom = lowPrefix; + while (op < endOfMatch) *op++ = *copyFrom++; + } else { + memcpy(op, lowPrefix, restSize); + op += restSize; + } } + continue; + } + + /* copy match within block */ + cpy = op + length; + if (unlikely(offset<8)) { + const int dec64 = dec64table[offset]; + op[0] = match[0]; + op[1] = match[1]; + op[2] = match[2]; + op[3] = match[3]; + match += dec32table[offset]; + memcpy(op+4, match, 4); + match -= dec64; + } else { LZ4_copy8(op, match); match+=8; } + op += 8; + + if (unlikely(cpy>oend-12)) { + BYTE* const oCopyLimit = oend-(WILDCOPYLENGTH-1); + if (cpy > oend-LASTLITERALS) goto _output_error; /* Error : last LASTLITERALS bytes must be literals (uncompressed) */ + if (op < oCopyLimit) { + LZ4_wildCopy(op, match, oCopyLimit); + match += oCopyLimit - op; + op = oCopyLimit; + } + while (op<cpy) *op++ = *match++; + } else { + LZ4_copy8(op, match); + if (length>16) LZ4_wildCopy(op+8, match+8, cpy); + } + op=cpy; /* correction */ + } + + /* end of decoding */ + if (endOnInput) + return (int) (((char*)op)-dest); /* Nb of output bytes decoded */ + else + return (int) (((const char*)ip)-source); /* Nb of input bytes read */ + + /* Overflow error detected */ +_output_error: + return (int) (-(((const char*)ip)-source))-1; +} + + +int LZ4_decompress_safe(const char* source, char* dest, int compressedSize, int maxDecompressedSize) +{ + return LZ4_decompress_generic(source, dest, compressedSize, maxDecompressedSize, endOnInputSize, full, 0, noDict, (BYTE*)dest, NULL, 0); +} + +int LZ4_decompress_safe_partial(const char* source, char* dest, int compressedSize, int targetOutputSize, int maxDecompressedSize) +{ + return LZ4_decompress_generic(source, dest, compressedSize, maxDecompressedSize, endOnInputSize, partial, targetOutputSize, noDict, (BYTE*)dest, NULL, 0); +} + +int LZ4_decompress_fast(const char* source, char* dest, int originalSize) +{ + return LZ4_decompress_generic(source, dest, 0, originalSize, endOnOutputSize, full, 0, withPrefix64k, (BYTE*)(dest - 64 KB), NULL, 64 KB); +} + + +/*===== streaming decompression functions =====*/ + +/* + * If you prefer dynamic allocation methods, + * LZ4_createStreamDecode() + * provides a pointer (void*) towards an initialized LZ4_streamDecode_t structure. + */ +LZ4_streamDecode_t* LZ4_createStreamDecode(void) +{ + LZ4_streamDecode_t* lz4s = (LZ4_streamDecode_t*) ALLOCATOR(1, sizeof(LZ4_streamDecode_t)); + return lz4s; +} + +int LZ4_freeStreamDecode (LZ4_streamDecode_t* LZ4_stream) +{ + FREEMEM(LZ4_stream); + return 0; +} + +/*! + * LZ4_setStreamDecode() : + * Use this function to instruct where to find the dictionary. + * This function is not necessary if previous data is still available where it was decoded. + * Loading a size of 0 is allowed (same effect as no dictionary). + * Return : 1 if OK, 0 if error + */ +int LZ4_setStreamDecode (LZ4_streamDecode_t* LZ4_streamDecode, const char* dictionary, int dictSize) +{ + LZ4_streamDecode_t_internal* lz4sd = &LZ4_streamDecode->internal_donotuse; + lz4sd->prefixSize = (size_t) dictSize; + lz4sd->prefixEnd = (const BYTE*) dictionary + dictSize; + lz4sd->externalDict = NULL; + lz4sd->extDictSize = 0; + return 1; +} + +/* +*_continue() : + These decoding functions allow decompression of multiple blocks in "streaming" mode. + Previously decoded blocks must still be available at the memory position where they were decoded. + If it's not possible, save the relevant part of decoded data into a safe buffer, + and indicate where it stands using LZ4_setStreamDecode() +*/ +int LZ4_decompress_safe_continue (LZ4_streamDecode_t* LZ4_streamDecode, const char* source, char* dest, int compressedSize, int maxOutputSize) +{ + LZ4_streamDecode_t_internal* lz4sd = &LZ4_streamDecode->internal_donotuse; + int result; + + if (lz4sd->prefixEnd == (BYTE*)dest) { + result = LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, + endOnInputSize, full, 0, + usingExtDict, lz4sd->prefixEnd - lz4sd->prefixSize, lz4sd->externalDict, lz4sd->extDictSize); + if (result <= 0) return result; + lz4sd->prefixSize += result; + lz4sd->prefixEnd += result; + } else { + lz4sd->extDictSize = lz4sd->prefixSize; + lz4sd->externalDict = lz4sd->prefixEnd - lz4sd->extDictSize; + result = LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, + endOnInputSize, full, 0, + usingExtDict, (BYTE*)dest, lz4sd->externalDict, lz4sd->extDictSize); + if (result <= 0) return result; + lz4sd->prefixSize = result; + lz4sd->prefixEnd = (BYTE*)dest + result; + } + + return result; +} + +int LZ4_decompress_fast_continue (LZ4_streamDecode_t* LZ4_streamDecode, const char* source, char* dest, int originalSize) +{ + LZ4_streamDecode_t_internal* lz4sd = &LZ4_streamDecode->internal_donotuse; + int result; + + if (lz4sd->prefixEnd == (BYTE*)dest) { + result = LZ4_decompress_generic(source, dest, 0, originalSize, + endOnOutputSize, full, 0, + usingExtDict, lz4sd->prefixEnd - lz4sd->prefixSize, lz4sd->externalDict, lz4sd->extDictSize); + if (result <= 0) return result; + lz4sd->prefixSize += originalSize; + lz4sd->prefixEnd += originalSize; + } else { + lz4sd->extDictSize = lz4sd->prefixSize; + lz4sd->externalDict = lz4sd->prefixEnd - lz4sd->extDictSize; + result = LZ4_decompress_generic(source, dest, 0, originalSize, + endOnOutputSize, full, 0, + usingExtDict, (BYTE*)dest, lz4sd->externalDict, lz4sd->extDictSize); + if (result <= 0) return result; + lz4sd->prefixSize = originalSize; + lz4sd->prefixEnd = (BYTE*)dest + originalSize; + } + + return result; +} + + +/* +Advanced decoding functions : +*_usingDict() : + These decoding functions work the same as "_continue" ones, + the dictionary must be explicitly provided within parameters +*/ + +FORCE_INLINE int LZ4_decompress_usingDict_generic(const char* source, char* dest, int compressedSize, int maxOutputSize, int safe, const char* dictStart, int dictSize) +{ + if (dictSize==0) + return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, safe, full, 0, noDict, (BYTE*)dest, NULL, 0); + if (dictStart+dictSize == dest) { + if (dictSize >= (int)(64 KB - 1)) + return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, safe, full, 0, withPrefix64k, (BYTE*)dest-64 KB, NULL, 0); + return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, safe, full, 0, noDict, (BYTE*)dest-dictSize, NULL, 0); + } + return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, safe, full, 0, usingExtDict, (BYTE*)dest, (const BYTE*)dictStart, dictSize); +} + +int LZ4_decompress_safe_usingDict(const char* source, char* dest, int compressedSize, int maxOutputSize, const char* dictStart, int dictSize) +{ + return LZ4_decompress_usingDict_generic(source, dest, compressedSize, maxOutputSize, 1, dictStart, dictSize); +} + +int LZ4_decompress_fast_usingDict(const char* source, char* dest, int originalSize, const char* dictStart, int dictSize) +{ + return LZ4_decompress_usingDict_generic(source, dest, 0, originalSize, 0, dictStart, dictSize); +} + +/* debug function */ +int LZ4_decompress_safe_forceExtDict(const char* source, char* dest, int compressedSize, int maxOutputSize, const char* dictStart, int dictSize) +{ + return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, endOnInputSize, full, 0, usingExtDict, (BYTE*)dest, (const BYTE*)dictStart, dictSize); +} + + +/*=************************************************* +* Obsolete Functions +***************************************************/ +/* obsolete compression functions */ +int LZ4_compress_limitedOutput(const char* source, char* dest, int inputSize, int maxOutputSize) { return LZ4_compress_default(source, dest, inputSize, maxOutputSize); } +int LZ4_compress(const char* source, char* dest, int inputSize) { return LZ4_compress_default(source, dest, inputSize, LZ4_compressBound(inputSize)); } +int LZ4_compress_limitedOutput_withState (void* state, const char* src, char* dst, int srcSize, int dstSize) { return LZ4_compress_fast_extState(state, src, dst, srcSize, dstSize, 1); } +int LZ4_compress_withState (void* state, const char* src, char* dst, int srcSize) { return LZ4_compress_fast_extState(state, src, dst, srcSize, LZ4_compressBound(srcSize), 1); } +int LZ4_compress_limitedOutput_continue (LZ4_stream_t* LZ4_stream, const char* src, char* dst, int srcSize, int maxDstSize) { return LZ4_compress_fast_continue(LZ4_stream, src, dst, srcSize, maxDstSize, 1); } +int LZ4_compress_continue (LZ4_stream_t* LZ4_stream, const char* source, char* dest, int inputSize) { return LZ4_compress_fast_continue(LZ4_stream, source, dest, inputSize, LZ4_compressBound(inputSize), 1); } + +/* +These function names are deprecated and should no longer be used. +They are only provided here for compatibility with older user programs. +- LZ4_uncompress is totally equivalent to LZ4_decompress_fast +- LZ4_uncompress_unknownOutputSize is totally equivalent to LZ4_decompress_safe +*/ +int LZ4_uncompress (const char* source, char* dest, int outputSize) { return LZ4_decompress_fast(source, dest, outputSize); } +int LZ4_uncompress_unknownOutputSize (const char* source, char* dest, int isize, int maxOutputSize) { return LZ4_decompress_safe(source, dest, isize, maxOutputSize); } + + +/* Obsolete Streaming functions */ + +int LZ4_sizeofStreamState() { return LZ4_STREAMSIZE; } + +static void LZ4_init(LZ4_stream_t* lz4ds, BYTE* base) +{ + MEM_INIT(lz4ds, 0, sizeof(LZ4_stream_t)); + lz4ds->internal_donotuse.bufferStart = base; +} + +int LZ4_resetStreamState(void* state, char* inputBuffer) +{ + if ((((uptrval)state) & 3) != 0) return 1; /* Error : pointer is not aligned on 4-bytes boundary */ + LZ4_init((LZ4_stream_t*)state, (BYTE*)inputBuffer); + return 0; +} + +void* LZ4_create (char* inputBuffer) +{ + LZ4_stream_t* lz4ds = (LZ4_stream_t*)ALLOCATOR(8, sizeof(LZ4_stream_t)); + LZ4_init (lz4ds, (BYTE*)inputBuffer); + return lz4ds; +} + +char* LZ4_slideInputBuffer (void* LZ4_Data) +{ + LZ4_stream_t_internal* ctx = &((LZ4_stream_t*)LZ4_Data)->internal_donotuse; + int dictSize = LZ4_saveDict((LZ4_stream_t*)LZ4_Data, (char*)ctx->bufferStart, 64 KB); + return (char*)(ctx->bufferStart + dictSize); +} + +/* Obsolete streaming decompression functions */ + +int LZ4_decompress_safe_withPrefix64k(const char* source, char* dest, int compressedSize, int maxOutputSize) +{ + return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, endOnInputSize, full, 0, withPrefix64k, (BYTE*)dest - 64 KB, NULL, 64 KB); +} + +int LZ4_decompress_fast_withPrefix64k(const char* source, char* dest, int originalSize) +{ + return LZ4_decompress_generic(source, dest, 0, originalSize, endOnOutputSize, full, 0, withPrefix64k, (BYTE*)dest - 64 KB, NULL, 64 KB); +} + +#endif /* LZ4_COMMONDEFS_ONLY */ +/* + LZ4 HC - High Compression Mode of LZ4 + Header File + Copyright (C) 2011-2017, Yann Collet. + BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following disclaimer + in the documentation and/or other materials provided with the + distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + You can contact the author at : + - LZ4 source repository : https://github.com/lz4/lz4 + - LZ4 public forum : https://groups.google.com/forum/#!forum/lz4c +*/ +#ifndef LZ4_HC_H_19834876238432 +#define LZ4_HC_H_19834876238432 + +#if defined (__cplusplus) +extern "C" { +#endif + +/* --- Dependency --- */ +/* note : lz4hc is not an independent module, it requires lz4.h/lz4.c for proper compilation */ + + +/* --- Useful constants --- */ +#define LZ4HC_CLEVEL_MIN 3 +#define LZ4HC_CLEVEL_DEFAULT 9 +#define LZ4HC_CLEVEL_OPT_MIN 11 +#define LZ4HC_CLEVEL_MAX 12 + + +/*-************************************ + * Block Compression + **************************************/ +/*! LZ4_compress_HC() : + * Compress data from `src` into `dst`, using the more powerful but slower "HC" algorithm. + * `dst` must be already allocated. + * Compression is guaranteed to succeed if `dstCapacity >= LZ4_compressBound(srcSize)` (see "lz4.h") + * Max supported `srcSize` value is LZ4_MAX_INPUT_SIZE (see "lz4.h") + * `compressionLevel` : Recommended values are between 4 and 9, although any value between 1 and LZ4HC_MAX_CLEVEL will work. + * Values >LZ4HC_MAX_CLEVEL behave the same as LZ4HC_MAX_CLEVEL. + * @return : the number of bytes written into 'dst' + * or 0 if compression fails. + */ +LZ4LIB_API int LZ4_compress_HC (const char* src, char* dst, int srcSize, int dstCapacity, int compressionLevel); + + +/* Note : + * Decompression functions are provided within "lz4.h" (BSD license) + */ + + +/*! LZ4_compress_HC_extStateHC() : + * Same as LZ4_compress_HC(), but using an externally allocated memory segment for `state`. + * `state` size is provided by LZ4_sizeofStateHC(). + * Memory segment must be aligned on 8-bytes boundaries (which a normal malloc() will do properly). + */ +LZ4LIB_API int LZ4_compress_HC_extStateHC(void* state, const char* src, char* dst, int srcSize, int maxDstSize, int compressionLevel); +LZ4LIB_API int LZ4_sizeofStateHC(void); + + +/*-************************************ + * Streaming Compression + * Bufferless synchronous API + **************************************/ + typedef union LZ4_streamHC_u LZ4_streamHC_t; /* incomplete type (defined later) */ + +/*! LZ4_createStreamHC() and LZ4_freeStreamHC() : + * These functions create and release memory for LZ4 HC streaming state. + * Newly created states are automatically initialized. + * Existing states can be re-used several times, using LZ4_resetStreamHC(). + * These methods are API and ABI stable, they can be used in combination with a DLL. + */ +LZ4LIB_API LZ4_streamHC_t* LZ4_createStreamHC(void); +LZ4LIB_API int LZ4_freeStreamHC (LZ4_streamHC_t* streamHCPtr); + +LZ4LIB_API void LZ4_resetStreamHC (LZ4_streamHC_t* streamHCPtr, int compressionLevel); +LZ4LIB_API int LZ4_loadDictHC (LZ4_streamHC_t* streamHCPtr, const char* dictionary, int dictSize); + +LZ4LIB_API int LZ4_compress_HC_continue (LZ4_streamHC_t* streamHCPtr, const char* src, char* dst, int srcSize, int maxDstSize); + +LZ4LIB_API int LZ4_saveDictHC (LZ4_streamHC_t* streamHCPtr, char* safeBuffer, int maxDictSize); + +/* + These functions compress data in successive blocks of any size, using previous blocks as dictionary. + One key assumption is that previous blocks (up to 64 KB) remain read-accessible while compressing next blocks. + There is an exception for ring buffers, which can be smaller than 64 KB. + Ring buffers scenario is automatically detected and handled by LZ4_compress_HC_continue(). + + Before starting compression, state must be properly initialized, using LZ4_resetStreamHC(). + A first "fictional block" can then be designated as initial dictionary, using LZ4_loadDictHC() (Optional). + + Then, use LZ4_compress_HC_continue() to compress each successive block. + Previous memory blocks (including initial dictionary when present) must remain accessible and unmodified during compression. + 'dst' buffer should be sized to handle worst case scenarios (see LZ4_compressBound()), to ensure operation success. + Because in case of failure, the API does not guarantee context recovery, and context will have to be reset. + If `dst` buffer budget cannot be >= LZ4_compressBound(), consider using LZ4_compress_HC_continue_destSize() instead. + + If, for any reason, previous data block can't be preserved unmodified in memory for next compression block, + you can save it to a more stable memory space, using LZ4_saveDictHC(). + Return value of LZ4_saveDictHC() is the size of dictionary effectively saved into 'safeBuffer'. +*/ + + + /*-************************************* + * PRIVATE DEFINITIONS : + * Do not use these definitions. + * They are exposed to allow static allocation of `LZ4_streamHC_t`. + * Using these definitions makes the code vulnerable to potential API break when upgrading LZ4 + **************************************/ +#define LZ4HC_DICTIONARY_LOGSIZE 17 /* because of btopt, hc would only need 16 */ +#define LZ4HC_MAXD (1<<LZ4HC_DICTIONARY_LOGSIZE) +#define LZ4HC_MAXD_MASK (LZ4HC_MAXD - 1) + +#define LZ4HC_HASH_LOG 15 +#define LZ4HC_HASHTABLESIZE (1 << LZ4HC_HASH_LOG) +#define LZ4HC_HASH_MASK (LZ4HC_HASHTABLESIZE - 1) + + +#if defined(__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) + +typedef struct +{ + uint32_t hashTable[LZ4HC_HASHTABLESIZE]; + uint16_t chainTable[LZ4HC_MAXD]; + const uint8_t* end; /* next block here to continue on current prefix */ + const uint8_t* base; /* All index relative to this position */ + const uint8_t* dictBase; /* alternate base for extDict */ + uint8_t* inputBuffer; /* deprecated */ + uint32_t dictLimit; /* below that point, need extDict */ + uint32_t lowLimit; /* below that point, no more dict */ + uint32_t nextToUpdate; /* index from which to continue dictionary update */ + uint32_t searchNum; /* only for optimal parser */ + uint32_t compressionLevel; +} LZ4HC_CCtx_internal; + +#else + +typedef struct +{ + unsigned int hashTable[LZ4HC_HASHTABLESIZE]; + unsigned short chainTable[LZ4HC_MAXD]; + const unsigned char* end; /* next block here to continue on current prefix */ + const unsigned char* base; /* All index relative to this position */ + const unsigned char* dictBase; /* alternate base for extDict */ + unsigned char* inputBuffer; /* deprecated */ + unsigned int dictLimit; /* below that point, need extDict */ + unsigned int lowLimit; /* below that point, no more dict */ + unsigned int nextToUpdate; /* index from which to continue dictionary update */ + unsigned int searchNum; /* only for optimal parser */ + int compressionLevel; +} LZ4HC_CCtx_internal; + +#endif + +#define LZ4_STREAMHCSIZE (4*LZ4HC_HASHTABLESIZE + 2*LZ4HC_MAXD + 56) /* 393268 */ +#define LZ4_STREAMHCSIZE_SIZET (LZ4_STREAMHCSIZE / sizeof(size_t)) +union LZ4_streamHC_u { + size_t table[LZ4_STREAMHCSIZE_SIZET]; + LZ4HC_CCtx_internal internal_donotuse; +}; /* previously typedef'd to LZ4_streamHC_t */ +/* + LZ4_streamHC_t : + This structure allows static allocation of LZ4 HC streaming state. + State must be initialized using LZ4_resetStreamHC() before first use. + + Static allocation shall only be used in combination with static linking. + When invoking LZ4 from a DLL, use create/free functions instead, which are API and ABI stable. +*/ + + +/*-************************************ +* Deprecated Functions +**************************************/ +/* see lz4.h LZ4_DISABLE_DEPRECATE_WARNINGS to turn off deprecation warnings */ + +/* deprecated compression functions */ +/* these functions will trigger warning messages in future releases */ +LZ4LIB_API LZ4_DEPRECATED("use LZ4_compress_HC() instead") int LZ4_compressHC (const char* source, char* dest, int inputSize); +LZ4LIB_API LZ4_DEPRECATED("use LZ4_compress_HC() instead") int LZ4_compressHC_limitedOutput (const char* source, char* dest, int inputSize, int maxOutputSize); +LZ4LIB_API LZ4_DEPRECATED("use LZ4_compress_HC() instead") int LZ4_compressHC2 (const char* source, char* dest, int inputSize, int compressionLevel); +LZ4LIB_API LZ4_DEPRECATED("use LZ4_compress_HC() instead") int LZ4_compressHC2_limitedOutput (const char* source, char* dest, int inputSize, int maxOutputSize, int compressionLevel); +LZ4LIB_API LZ4_DEPRECATED("use LZ4_compress_HC_extStateHC() instead") int LZ4_compressHC_withStateHC (void* state, const char* source, char* dest, int inputSize); +LZ4LIB_API LZ4_DEPRECATED("use LZ4_compress_HC_extStateHC() instead") int LZ4_compressHC_limitedOutput_withStateHC (void* state, const char* source, char* dest, int inputSize, int maxOutputSize); +LZ4LIB_API LZ4_DEPRECATED("use LZ4_compress_HC_extStateHC() instead") int LZ4_compressHC2_withStateHC (void* state, const char* source, char* dest, int inputSize, int compressionLevel); +LZ4LIB_API LZ4_DEPRECATED("use LZ4_compress_HC_extStateHC() instead") int LZ4_compressHC2_limitedOutput_withStateHC(void* state, const char* source, char* dest, int inputSize, int maxOutputSize, int compressionLevel); +LZ4LIB_API LZ4_DEPRECATED("use LZ4_compress_HC_continue() instead") int LZ4_compressHC_continue (LZ4_streamHC_t* LZ4_streamHCPtr, const char* source, char* dest, int inputSize); +LZ4LIB_API LZ4_DEPRECATED("use LZ4_compress_HC_continue() instead") int LZ4_compressHC_limitedOutput_continue (LZ4_streamHC_t* LZ4_streamHCPtr, const char* source, char* dest, int inputSize, int maxOutputSize); + +/* Deprecated Streaming functions using older model; should no longer be used */ +LZ4LIB_API LZ4_DEPRECATED("use LZ4_createStreamHC() instead") void* LZ4_createHC (char* inputBuffer); +LZ4LIB_API LZ4_DEPRECATED("use LZ4_saveDictHC() instead") char* LZ4_slideInputBufferHC (void* LZ4HC_Data); +LZ4LIB_API LZ4_DEPRECATED("use LZ4_freeStreamHC() instead") int LZ4_freeHC (void* LZ4HC_Data); +LZ4LIB_API LZ4_DEPRECATED("use LZ4_compress_HC_continue() instead") int LZ4_compressHC2_continue (void* LZ4HC_Data, const char* source, char* dest, int inputSize, int compressionLevel); +LZ4LIB_API LZ4_DEPRECATED("use LZ4_compress_HC_continue() instead") int LZ4_compressHC2_limitedOutput_continue (void* LZ4HC_Data, const char* source, char* dest, int inputSize, int maxOutputSize, int compressionLevel); +LZ4LIB_API LZ4_DEPRECATED("use LZ4_createStreamHC() instead") int LZ4_sizeofStreamStateHC(void); +LZ4LIB_API LZ4_DEPRECATED("use LZ4_resetStreamHC() instead") int LZ4_resetStreamStateHC(void* state, char* inputBuffer); + + +#if defined (__cplusplus) +} +#endif + +#endif /* LZ4_HC_H_19834876238432 */ + +/*-************************************************ + * !!!!! STATIC LINKING ONLY !!!!! + * Following definitions are considered experimental. + * They should not be linked from DLL, + * as there is no guarantee of API stability yet. + * Prototypes will be promoted to "stable" status + * after successfull usage in real-life scenarios. + *************************************************/ +#ifdef LZ4_HC_STATIC_LINKING_ONLY /* protection macro */ +#ifndef LZ4_HC_SLO_098092834 +#define LZ4_HC_SLO_098092834 + +/*! LZ4_compress_HC_destSize() : + * Will try to compress as much data from `src` as possible + * that can fit in `targetDstSize` budget. + * Result is provided in 2 parts : + * @return : the number of bytes written into 'dst' + * or 0 if compression fails. + * `srcSizePtr` : value will be updated to indicate how much bytes were read from `src` + */ +LZ4LIB_API int LZ4_compress_HC_destSize(void* LZ4HC_Data, + const char* src, char* dst, + int* srcSizePtr, int targetDstSize, + int compressionLevel); + +/*! LZ4_compress_HC_continue_destSize() : + * Similar as LZ4_compress_HC_continue(), + * but will read a variable nb of bytes from `src` + * to fit into `targetDstSize` budget. + * Result is provided in 2 parts : + * @return : the number of bytes written into 'dst' + * or 0 if compression fails. + * `srcSizePtr` : value will be updated to indicate how much bytes were read from `src` + * Important : due to limitations, this prototype only works well up to cLevel < LZ4HC_CLEVEL_OPT_MIN + * beyond that level, compression performance will be much reduced due to internal incompatibilities + */ +LZ4LIB_API int LZ4_compress_HC_continue_destSize(LZ4_streamHC_t* LZ4_streamHCPtr, + const char* src, char* dst, + int* srcSizePtr, int targetDstSize); + +#endif /* LZ4_HC_SLO_098092834 */ +#endif /* LZ4_HC_STATIC_LINKING_ONLY */ +/* + lz4opt.h - Optimal Mode of LZ4 + Copyright (C) 2015-2017, Przemyslaw Skibinski <inikep@gmail.com> + + BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following disclaimer + in the documentation and/or other materials provided with the + distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + You can contact the author at : + - LZ4 source repository : https://github.com/lz4/lz4 + - LZ4 public forum : https://groups.google.com/forum/#!forum/lz4c +*/ + +#define LZ4_OPT_NUM (1<<12) + + +typedef struct { + int off; + int len; +} LZ4HC_match_t; + +typedef struct { + int price; + int off; + int mlen; + int litlen; +} LZ4HC_optimal_t; + + +/* price in bits */ +FORCE_INLINE size_t LZ4HC_literalsPrice(size_t litlen) +{ + size_t price = litlen; + if (litlen >= (size_t)RUN_MASK) price += 1 + (litlen-RUN_MASK)/255; + return price; +} + + +/* requires mlen >= MINMATCH */ +FORCE_INLINE size_t LZ4HC_sequencePrice(size_t litlen, size_t mlen) +{ + size_t price = 2 + 1; /* 16-bit offset + token */ + + price += LZ4HC_literalsPrice(litlen); + + if (mlen >= (size_t)(ML_MASK+MINMATCH)) + price+= 1+(mlen-(ML_MASK+MINMATCH))/255; + + return price; +} + + +/*=== Common LZ4 definitions ===*/ +#if defined(__GNUC__) +# pragma GCC diagnostic ignored "-Wunused-function" +#endif +#if defined (__clang__) +# pragma clang diagnostic ignored "-Wunused-function" +#endif + +//#define LZ4_COMMONDEFS_ONLY + + +/*=== Constants ===*/ +#define OPTIMAL_ML (int)((ML_MASK-1)+MINMATCH) + + +/*=== Macros ===*/ +#define HASH_FUNCTION(i) (((i) * 2654435761U) >> ((MINMATCH*8)-LZ4HC_HASH_LOG)) +#define DELTANEXTMAXD(p) chainTable[(p) & LZ4HC_MAXD_MASK] /* flexible, LZ4HC_MAXD dependent */ +#define DELTANEXTU16(p) chainTable[(U16)(p)] /* faster */ +static U32 LZ4HC_hashPtr(const void* ptr) { return HASH_FUNCTION(LZ4_read32(ptr)); } +/*-************************************* +* Binary Tree search +***************************************/ +FORCE_INLINE int LZ4HC_BinTree_InsertAndGetAllMatches ( + LZ4HC_CCtx_internal* ctx, + const BYTE* const ip, + const BYTE* const iHighLimit, + size_t best_mlen, + LZ4HC_match_t* matches, + int* matchNum) +{ + U16* const chainTable = ctx->chainTable; + U32* const HashTable = ctx->hashTable; + const BYTE* const base = ctx->base; + const U32 dictLimit = ctx->dictLimit; + const U32 current = (U32)(ip - base); + const U32 lowLimit = (ctx->lowLimit + MAX_DISTANCE > current) ? ctx->lowLimit : current - (MAX_DISTANCE - 1); + const BYTE* const dictBase = ctx->dictBase; + const BYTE* match; + int nbAttempts = ctx->searchNum; + int mnum = 0; + U16 *ptr0, *ptr1, delta0, delta1; + U32 matchIndex; + size_t matchLength = 0; + U32* HashPos; + + if (ip + MINMATCH > iHighLimit) return 1; + + /* HC4 match finder */ + HashPos = &HashTable[LZ4HC_hashPtr(ip)]; + matchIndex = *HashPos; + *HashPos = current; + + ptr0 = &DELTANEXTMAXD(current*2+1); + ptr1 = &DELTANEXTMAXD(current*2); + delta0 = delta1 = (U16)(current - matchIndex); + + while ((matchIndex < current) && (matchIndex>=lowLimit) && (nbAttempts)) { + nbAttempts--; + if (matchIndex >= dictLimit) { + match = base + matchIndex; + matchLength = LZ4_count(ip, match, iHighLimit); + } else { + const BYTE* vLimit = ip + (dictLimit - matchIndex); + match = dictBase + matchIndex; + if (vLimit > iHighLimit) vLimit = iHighLimit; + matchLength = LZ4_count(ip, match, vLimit); + if ((ip+matchLength == vLimit) && (vLimit < iHighLimit)) + matchLength += LZ4_count(ip+matchLength, base+dictLimit, iHighLimit); + } + + if (matchLength > best_mlen) { + best_mlen = matchLength; + if (matches) { + if (matchIndex >= dictLimit) + matches[mnum].off = (int)(ip - match); + else + matches[mnum].off = (int)(ip - (base + matchIndex)); /* virtual matchpos */ + matches[mnum].len = (int)matchLength; + mnum++; + } + if (best_mlen > LZ4_OPT_NUM) break; + } + + if (ip+matchLength >= iHighLimit) /* equal : no way to know if inf or sup */ + break; /* drop , to guarantee consistency ; miss a bit of compression, but other solutions can corrupt the tree */ + + if (*(ip+matchLength) < *(match+matchLength)) { + *ptr0 = delta0; + ptr0 = &DELTANEXTMAXD(matchIndex*2); + if (*ptr0 == (U16)-1) break; + delta0 = *ptr0; + delta1 += delta0; + matchIndex -= delta0; + } else { + *ptr1 = delta1; + ptr1 = &DELTANEXTMAXD(matchIndex*2+1); + if (*ptr1 == (U16)-1) break; + delta1 = *ptr1; + delta0 += delta1; + matchIndex -= delta1; + } + } + + *ptr0 = (U16)-1; + *ptr1 = (U16)-1; + if (matchNum) *matchNum = mnum; + /* if (best_mlen > 8) return best_mlen-8; */ + if (!matchNum) return 1; + return 1; +} + + +FORCE_INLINE void LZ4HC_updateBinTree(LZ4HC_CCtx_internal* ctx, const BYTE* const ip, const BYTE* const iHighLimit) +{ + const BYTE* const base = ctx->base; + const U32 target = (U32)(ip - base); + U32 idx = ctx->nextToUpdate; + while(idx < target) + idx += LZ4HC_BinTree_InsertAndGetAllMatches(ctx, base+idx, iHighLimit, 8, NULL, NULL); +} + + +/** Tree updater, providing best match */ +FORCE_INLINE int LZ4HC_BinTree_GetAllMatches ( + LZ4HC_CCtx_internal* ctx, + const BYTE* const ip, const BYTE* const iHighLimit, + size_t best_mlen, LZ4HC_match_t* matches, const int fullUpdate) +{ + int mnum = 0; + if (ip < ctx->base + ctx->nextToUpdate) return 0; /* skipped area */ + if (fullUpdate) LZ4HC_updateBinTree(ctx, ip, iHighLimit); + best_mlen = LZ4HC_BinTree_InsertAndGetAllMatches(ctx, ip, iHighLimit, best_mlen, matches, &mnum); + ctx->nextToUpdate = (U32)(ip - ctx->base + best_mlen); + return mnum; +} + + +#define SET_PRICE(pos, ml, offset, ll, cost) \ +{ \ + while (last_pos < pos) { opt[last_pos+1].price = 1<<30; last_pos++; } \ + opt[pos].mlen = (int)ml; \ + opt[pos].off = (int)offset; \ + opt[pos].litlen = (int)ll; \ + opt[pos].price = (int)cost; \ +} + +FORCE_INLINE int LZ4HC_encodeSequence ( const BYTE** ip, BYTE** op, const BYTE** anchor, int matchLength, const BYTE* const match, limitedOutput_directive limit, BYTE* oend); + +static int LZ4HC_compress_optimal ( + LZ4HC_CCtx_internal* ctx, + const char* const source, + char* dest, + int inputSize, + int maxOutputSize, + limitedOutput_directive limit, + size_t sufficient_len, + const int fullUpdate + ) +{ + LZ4HC_optimal_t opt[LZ4_OPT_NUM + 1]; /* this uses a bit too much stack memory to my taste ... */ + LZ4HC_match_t matches[LZ4_OPT_NUM + 1]; + + const BYTE* ip = (const BYTE*) source; + const BYTE* anchor = ip; + const BYTE* const iend = ip + inputSize; + const BYTE* const mflimit = iend - MFLIMIT; + const BYTE* const matchlimit = (iend - LASTLITERALS); + BYTE* op = (BYTE*) dest; + BYTE* const oend = op + maxOutputSize; + + /* init */ + if (sufficient_len >= LZ4_OPT_NUM) sufficient_len = LZ4_OPT_NUM-1; + ctx->end += inputSize; + ip++; + + /* Main Loop */ + while (ip < mflimit) { + size_t const llen = ip - anchor; + size_t last_pos = 0; + size_t match_num, cur, best_mlen, best_off; + memset(opt, 0, sizeof(LZ4HC_optimal_t)); + + match_num = LZ4HC_BinTree_GetAllMatches(ctx, ip, matchlimit, MINMATCH-1, matches, fullUpdate); + if (!match_num) { ip++; continue; } + + if ((size_t)matches[match_num-1].len > sufficient_len) { + /* good enough solution : immediate encoding */ + best_mlen = matches[match_num-1].len; + best_off = matches[match_num-1].off; + cur = 0; + last_pos = 1; + goto encode; + } + + /* set prices using matches at position = 0 */ + { size_t matchNb; + for (matchNb = 0; matchNb < match_num; matchNb++) { + size_t mlen = (matchNb>0) ? (size_t)matches[matchNb-1].len+1 : MINMATCH; + best_mlen = matches[matchNb].len; /* necessarily < sufficient_len < LZ4_OPT_NUM */ + for ( ; mlen <= best_mlen ; mlen++) { + size_t const cost = LZ4HC_sequencePrice(llen, mlen) - LZ4HC_literalsPrice(llen); + SET_PRICE(mlen, mlen, matches[matchNb].off, 0, cost); /* updates last_pos and opt[pos] */ + } } } + + if (last_pos < MINMATCH) { ip++; continue; } /* note : on clang at least, this test improves performance */ + + /* check further positions */ + opt[0].mlen = opt[1].mlen = 1; + for (cur = 1; cur <= last_pos; cur++) { + const BYTE* const curPtr = ip + cur; + + /* establish baseline price if cur is literal */ + { size_t price, litlen; + if (opt[cur-1].mlen == 1) { + /* no match at previous position */ + litlen = opt[cur-1].litlen + 1; + if (cur > litlen) { + price = opt[cur - litlen].price + LZ4HC_literalsPrice(litlen); + } else { + price = LZ4HC_literalsPrice(llen + litlen) - LZ4HC_literalsPrice(llen); + } + } else { + litlen = 1; + price = opt[cur - 1].price + LZ4HC_literalsPrice(1); + } + + if (price < (size_t)opt[cur].price) + SET_PRICE(cur, 1, 0, litlen, price); /* note : increases last_pos */ + } + + if (cur == last_pos || curPtr >= mflimit) break; + + match_num = LZ4HC_BinTree_GetAllMatches(ctx, curPtr, matchlimit, MINMATCH-1, matches, fullUpdate); + if ((match_num > 0) && (size_t)matches[match_num-1].len > sufficient_len) { + /* immediate encoding */ + best_mlen = matches[match_num-1].len; + best_off = matches[match_num-1].off; + last_pos = cur + 1; + goto encode; + } + + /* set prices using matches at position = cur */ + { size_t matchNb; + for (matchNb = 0; matchNb < match_num; matchNb++) { + size_t ml = (matchNb>0) ? (size_t)matches[matchNb-1].len+1 : MINMATCH; + best_mlen = (cur + matches[matchNb].len < LZ4_OPT_NUM) ? + (size_t)matches[matchNb].len : LZ4_OPT_NUM - cur; + + for ( ; ml <= best_mlen ; ml++) { + size_t ll, price; + if (opt[cur].mlen == 1) { + ll = opt[cur].litlen; + if (cur > ll) + price = opt[cur - ll].price + LZ4HC_sequencePrice(ll, ml); + else + price = LZ4HC_sequencePrice(llen + ll, ml) - LZ4HC_literalsPrice(llen); + } else { + ll = 0; + price = opt[cur].price + LZ4HC_sequencePrice(0, ml); + } + + if (cur + ml > last_pos || price < (size_t)opt[cur + ml].price) { + SET_PRICE(cur + ml, ml, matches[matchNb].off, ll, price); + } } } } + } /* for (cur = 1; cur <= last_pos; cur++) */ + + best_mlen = opt[last_pos].mlen; + best_off = opt[last_pos].off; + cur = last_pos - best_mlen; + +encode: /* cur, last_pos, best_mlen, best_off must be set */ + opt[0].mlen = 1; + while (1) { /* from end to beginning */ + size_t const ml = opt[cur].mlen; + int const offset = opt[cur].off; + opt[cur].mlen = (int)best_mlen; + opt[cur].off = (int)best_off; + best_mlen = ml; + best_off = offset; + if (ml > cur) break; + cur -= ml; + } + + /* encode all recorded sequences */ + cur = 0; + while (cur < last_pos) { + int const ml = opt[cur].mlen; + int const offset = opt[cur].off; + if (ml == 1) { ip++; cur++; continue; } + cur += ml; + if ( LZ4HC_encodeSequence(&ip, &op, &anchor, ml, ip - offset, limit, oend) ) return 0; + } + } /* while (ip < mflimit) */ + + /* Encode Last Literals */ + { int lastRun = (int)(iend - anchor); + if ((limit) && (((char*)op - dest) + lastRun + 1 + ((lastRun+255-RUN_MASK)/255) > (U32)maxOutputSize)) return 0; /* Check output limit */ + if (lastRun>=(int)RUN_MASK) { *op++=(RUN_MASK<<ML_BITS); lastRun-=RUN_MASK; for(; lastRun > 254 ; lastRun-=255) *op++ = 255; *op++ = (BYTE) lastRun; } + else *op++ = (BYTE)(lastRun<<ML_BITS); + memcpy(op, anchor, iend - anchor); + op += iend-anchor; + } + + /* End */ + return (int) ((char*)op-dest); +} +/* + LZ4 HC - High Compression Mode of LZ4 + Copyright (C) 2011-2017, Yann Collet. + + BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following disclaimer + in the documentation and/or other materials provided with the + distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + You can contact the author at : + - LZ4 source repository : https://github.com/lz4/lz4 + - LZ4 public forum : https://groups.google.com/forum/#!forum/lz4c +*/ +/* note : lz4hc is not an independent module, it requires lz4.h/lz4.c for proper compilation */ + + +/* ************************************* +* Tuning Parameter +***************************************/ + +/*! HEAPMODE : + * Select how default compression function will allocate workplace memory, + * in stack (0:fastest), or in heap (1:requires malloc()). + * Since workplace is rather large, heap mode is recommended. + */ +#ifndef LZ4HC_HEAPMODE +# define LZ4HC_HEAPMODE 1 +#endif + + +/*=== Dependency ===*/ + + + + + + +/************************************** +* HC Compression +**************************************/ +static void LZ4HC_init (LZ4HC_CCtx_internal* hc4, const BYTE* start) +{ + MEM_INIT((void*)hc4->hashTable, 0, sizeof(hc4->hashTable)); + MEM_INIT(hc4->chainTable, 0xFF, sizeof(hc4->chainTable)); + hc4->nextToUpdate = 64 KB; + hc4->base = start - 64 KB; + hc4->end = start; + hc4->dictBase = start - 64 KB; + hc4->dictLimit = 64 KB; + hc4->lowLimit = 64 KB; +} + + +/* Update chains up to ip (excluded) */ +FORCE_INLINE void LZ4HC_Insert (LZ4HC_CCtx_internal* hc4, const BYTE* ip) +{ + U16* const chainTable = hc4->chainTable; + U32* const hashTable = hc4->hashTable; + const BYTE* const base = hc4->base; + U32 const target = (U32)(ip - base); + U32 idx = hc4->nextToUpdate; + + while (idx < target) { + U32 const h = LZ4HC_hashPtr(base+idx); + size_t delta = idx - hashTable[h]; + if (delta>MAX_DISTANCE) delta = MAX_DISTANCE; + DELTANEXTU16(idx) = (U16)delta; + hashTable[h] = idx; + idx++; + } + + hc4->nextToUpdate = target; +} + + +FORCE_INLINE int LZ4HC_InsertAndFindBestMatch (LZ4HC_CCtx_internal* hc4, /* Index table will be updated */ + const BYTE* ip, const BYTE* const iLimit, + const BYTE** matchpos, + const int maxNbAttempts) +{ + U16* const chainTable = hc4->chainTable; + U32* const HashTable = hc4->hashTable; + const BYTE* const base = hc4->base; + const BYTE* const dictBase = hc4->dictBase; + const U32 dictLimit = hc4->dictLimit; + const U32 lowLimit = (hc4->lowLimit + 64 KB > (U32)(ip-base)) ? hc4->lowLimit : (U32)(ip - base) - (64 KB - 1); + U32 matchIndex; + int nbAttempts = maxNbAttempts; + size_t ml = 0; + + /* HC4 match finder */ + LZ4HC_Insert(hc4, ip); + matchIndex = HashTable[LZ4HC_hashPtr(ip)]; + + while ((matchIndex>=lowLimit) && (nbAttempts)) { + nbAttempts--; + if (matchIndex >= dictLimit) { + const BYTE* const match = base + matchIndex; + if (*(match+ml) == *(ip+ml) + && (LZ4_read32(match) == LZ4_read32(ip))) + { + size_t const mlt = LZ4_count(ip+MINMATCH, match+MINMATCH, iLimit) + MINMATCH; + if (mlt > ml) { ml = mlt; *matchpos = match; } + } + } else { + const BYTE* const match = dictBase + matchIndex; + if (LZ4_read32(match) == LZ4_read32(ip)) { + size_t mlt; + const BYTE* vLimit = ip + (dictLimit - matchIndex); + if (vLimit > iLimit) vLimit = iLimit; + mlt = LZ4_count(ip+MINMATCH, match+MINMATCH, vLimit) + MINMATCH; + if ((ip+mlt == vLimit) && (vLimit < iLimit)) + mlt += LZ4_count(ip+mlt, base+dictLimit, iLimit); + if (mlt > ml) { ml = mlt; *matchpos = base + matchIndex; } /* virtual matchpos */ + } + } + matchIndex -= DELTANEXTU16(matchIndex); + } + + return (int)ml; +} + + +FORCE_INLINE int LZ4HC_InsertAndGetWiderMatch ( + LZ4HC_CCtx_internal* hc4, + const BYTE* const ip, + const BYTE* const iLowLimit, + const BYTE* const iHighLimit, + int longest, + const BYTE** matchpos, + const BYTE** startpos, + const int maxNbAttempts) +{ + U16* const chainTable = hc4->chainTable; + U32* const HashTable = hc4->hashTable; + const BYTE* const base = hc4->base; + const U32 dictLimit = hc4->dictLimit; + const BYTE* const lowPrefixPtr = base + dictLimit; + const U32 lowLimit = (hc4->lowLimit + 64 KB > (U32)(ip-base)) ? hc4->lowLimit : (U32)(ip - base) - (64 KB - 1); + const BYTE* const dictBase = hc4->dictBase; + U32 matchIndex; + int nbAttempts = maxNbAttempts; + int delta = (int)(ip-iLowLimit); + + + /* First Match */ + LZ4HC_Insert(hc4, ip); + matchIndex = HashTable[LZ4HC_hashPtr(ip)]; + + while ((matchIndex>=lowLimit) && (nbAttempts)) { + nbAttempts--; + if (matchIndex >= dictLimit) { + const BYTE* matchPtr = base + matchIndex; + if (*(iLowLimit + longest) == *(matchPtr - delta + longest)) { + if (LZ4_read32(matchPtr) == LZ4_read32(ip)) { + int mlt = MINMATCH + LZ4_count(ip+MINMATCH, matchPtr+MINMATCH, iHighLimit); + int back = 0; + + while ((ip+back > iLowLimit) + && (matchPtr+back > lowPrefixPtr) + && (ip[back-1] == matchPtr[back-1])) + back--; + + mlt -= back; + + if (mlt > longest) { + longest = (int)mlt; + *matchpos = matchPtr+back; + *startpos = ip+back; + } } } + } else { + const BYTE* const matchPtr = dictBase + matchIndex; + if (LZ4_read32(matchPtr) == LZ4_read32(ip)) { + size_t mlt; + int back=0; + const BYTE* vLimit = ip + (dictLimit - matchIndex); + if (vLimit > iHighLimit) vLimit = iHighLimit; + mlt = LZ4_count(ip+MINMATCH, matchPtr+MINMATCH, vLimit) + MINMATCH; + if ((ip+mlt == vLimit) && (vLimit < iHighLimit)) + mlt += LZ4_count(ip+mlt, base+dictLimit, iHighLimit); + while ((ip+back > iLowLimit) && (matchIndex+back > lowLimit) && (ip[back-1] == matchPtr[back-1])) back--; + mlt -= back; + if ((int)mlt > longest) { longest = (int)mlt; *matchpos = base + matchIndex + back; *startpos = ip+back; } + } + } + matchIndex -= DELTANEXTU16(matchIndex); + } + + return longest; +} + + + +#define LZ4HC_DEBUG 0 +#if LZ4HC_DEBUG +static unsigned debug = 0; +#endif + + +/* LZ4HC_encodeSequence() : + * @return : 0 if ok, + * 1 if buffer issue detected */ +FORCE_INLINE int LZ4HC_encodeSequence ( const BYTE** ip, BYTE** op, const BYTE** anchor, int matchLength, const BYTE* const match, limitedOutput_directive limit, BYTE* oend) +{ + size_t length; + BYTE* token; + +#if LZ4HC_DEBUG + if (debug) printf("literal : %u -- match : %u -- offset : %u\n", (U32)(*ip - *anchor), (U32)matchLength, (U32)(*ip-match)); +#endif + + /* Encode Literal length */ + length = (size_t)(*ip - *anchor); + token = (*op)++; + if ((limit) && ((*op + (length >> 8) + length + (2 + 1 + LASTLITERALS)) > oend)) return 1; /* Check output limit */ + if (length >= RUN_MASK) { + size_t len = length - RUN_MASK; + *token = (RUN_MASK << ML_BITS); + for(; len >= 255 ; len -= 255) *(*op)++ = 255; + *(*op)++ = (BYTE)len; + } else { + *token = (BYTE)(length << ML_BITS); + } + + /* Copy Literals */ + LZ4_wildCopy(*op, *anchor, (*op) + length); + *op += length; + + /* Encode Offset */ + LZ4_writeLE16(*op, (U16)(*ip-match)); *op += 2; + + /* Encode MatchLength */ + length = (size_t)(matchLength - MINMATCH); + if ((limit) && (*op + (length >> 8) + (1 + LASTLITERALS) > oend)) return 1; /* Check output limit */ + if (length >= ML_MASK) { + *token += ML_MASK; + length -= ML_MASK; + for(; length >= 510 ; length -= 510) { *(*op)++ = 255; *(*op)++ = 255; } + if (length >= 255) { length -= 255; *(*op)++ = 255; } + *(*op)++ = (BYTE)length; + } else { + *token += (BYTE)(length); + } + + /* Prepare next loop */ + *ip += matchLength; + *anchor = *ip; + + return 0; +} + +/* btopt */ + + +static int LZ4HC_compress_hashChain ( + LZ4HC_CCtx_internal* const ctx, + const char* const source, + char* const dest, + int* srcSizePtr, + int const maxOutputSize, + unsigned maxNbAttempts, + limitedOutput_directive limit + ) +{ + const int inputSize = *srcSizePtr; + + const BYTE* ip = (const BYTE*) source; + const BYTE* anchor = ip; + const BYTE* const iend = ip + inputSize; + const BYTE* const mflimit = iend - MFLIMIT; + const BYTE* const matchlimit = (iend - LASTLITERALS); + + BYTE* optr = (BYTE*) dest; + BYTE* op = (BYTE*) dest; + BYTE* oend = op + maxOutputSize; + + int ml, ml2, ml3, ml0; + const BYTE* ref = NULL; + const BYTE* start2 = NULL; + const BYTE* ref2 = NULL; + const BYTE* start3 = NULL; + const BYTE* ref3 = NULL; + const BYTE* start0; + const BYTE* ref0; + + /* init */ + *srcSizePtr = 0; + if (limit == limitedDestSize && maxOutputSize < 1) return 0; /* Impossible to store anything */ + if ((U32)inputSize > (U32)LZ4_MAX_INPUT_SIZE) return 0; /* Unsupported input size, too large (or negative) */ + + ctx->end += inputSize; + if (limit == limitedDestSize) oend -= LASTLITERALS; /* Hack for support limitations LZ4 decompressor */ + if (inputSize < LZ4_minLength) goto _last_literals; /* Input too small, no compression (all literals) */ + + ip++; + + /* Main Loop */ + while (ip < mflimit) { + ml = LZ4HC_InsertAndFindBestMatch (ctx, ip, matchlimit, (&ref), maxNbAttempts); + if (!ml) { ip++; continue; } + + /* saved, in case we would skip too much */ + start0 = ip; + ref0 = ref; + ml0 = ml; + +_Search2: + if (ip+ml < mflimit) + ml2 = LZ4HC_InsertAndGetWiderMatch(ctx, ip + ml - 2, ip + 0, matchlimit, ml, &ref2, &start2, maxNbAttempts); + else + ml2 = ml; + + if (ml2 == ml) { /* No better match */ + optr = op; + if (LZ4HC_encodeSequence(&ip, &op, &anchor, ml, ref, limit, oend)) goto _dest_overflow; + continue; + } + + if (start0 < ip) { + if (start2 < ip + ml0) { /* empirical */ + ip = start0; + ref = ref0; + ml = ml0; + } + } + + /* Here, start0==ip */ + if ((start2 - ip) < 3) { /* First Match too small : removed */ + ml = ml2; + ip = start2; + ref =ref2; + goto _Search2; + } + +_Search3: + /* At this stage, we have : + * ml2 > ml1, and + * ip1+3 <= ip2 (usually < ip1+ml1) */ + if ((start2 - ip) < OPTIMAL_ML) { + int correction; + int new_ml = ml; + if (new_ml > OPTIMAL_ML) new_ml = OPTIMAL_ML; + if (ip+new_ml > start2 + ml2 - MINMATCH) new_ml = (int)(start2 - ip) + ml2 - MINMATCH; + correction = new_ml - (int)(start2 - ip); + if (correction > 0) { + start2 += correction; + ref2 += correction; + ml2 -= correction; + } + } + /* Now, we have start2 = ip+new_ml, with new_ml = min(ml, OPTIMAL_ML=18) */ + + if (start2 + ml2 < mflimit) + ml3 = LZ4HC_InsertAndGetWiderMatch(ctx, start2 + ml2 - 3, start2, matchlimit, ml2, &ref3, &start3, maxNbAttempts); + else + ml3 = ml2; + + if (ml3 == ml2) { /* No better match : 2 sequences to encode */ + /* ip & ref are known; Now for ml */ + if (start2 < ip+ml) ml = (int)(start2 - ip); + /* Now, encode 2 sequences */ + optr = op; + if (LZ4HC_encodeSequence(&ip, &op, &anchor, ml, ref, limit, oend)) goto _dest_overflow; + ip = start2; + optr = op; + if (LZ4HC_encodeSequence(&ip, &op, &anchor, ml2, ref2, limit, oend)) goto _dest_overflow; + continue; + } + + if (start3 < ip+ml+3) { /* Not enough space for match 2 : remove it */ + if (start3 >= (ip+ml)) { /* can write Seq1 immediately ==> Seq2 is removed, so Seq3 becomes Seq1 */ + if (start2 < ip+ml) { + int correction = (int)(ip+ml - start2); + start2 += correction; + ref2 += correction; + ml2 -= correction; + if (ml2 < MINMATCH) { + start2 = start3; + ref2 = ref3; + ml2 = ml3; + } + } + + optr = op; + if (LZ4HC_encodeSequence(&ip, &op, &anchor, ml, ref, limit, oend)) goto _dest_overflow; + ip = start3; + ref = ref3; + ml = ml3; + + start0 = start2; + ref0 = ref2; + ml0 = ml2; + goto _Search2; + } + + start2 = start3; + ref2 = ref3; + ml2 = ml3; + goto _Search3; + } + + /* + * OK, now we have 3 ascending matches; let's write at least the first one + * ip & ref are known; Now for ml + */ + if (start2 < ip+ml) { + if ((start2 - ip) < (int)ML_MASK) { + int correction; + if (ml > OPTIMAL_ML) ml = OPTIMAL_ML; + if (ip + ml > start2 + ml2 - MINMATCH) ml = (int)(start2 - ip) + ml2 - MINMATCH; + correction = ml - (int)(start2 - ip); + if (correction > 0) { + start2 += correction; + ref2 += correction; + ml2 -= correction; + } + } else { + ml = (int)(start2 - ip); + } + } + optr = op; + if (LZ4HC_encodeSequence(&ip, &op, &anchor, ml, ref, limit, oend)) goto _dest_overflow; + + ip = start2; + ref = ref2; + ml = ml2; + + start2 = start3; + ref2 = ref3; + ml2 = ml3; + + goto _Search3; + } + +_last_literals: + /* Encode Last Literals */ + { size_t lastRunSize = (size_t)(iend - anchor); /* literals */ + size_t litLength = (lastRunSize + 255 - RUN_MASK) / 255; + size_t const totalSize = 1 + litLength + lastRunSize; + if (limit == limitedDestSize) oend += LASTLITERALS; /* restore correct value */ + if (limit && (op + totalSize > oend)) { + if (limit == limitedOutput) return 0; /* Check output limit */ + /* adapt lastRunSize to fill 'dest' */ + lastRunSize = (size_t)(oend - op) - 1; + litLength = (lastRunSize + 255 - RUN_MASK) / 255; + lastRunSize -= litLength; + } + ip = anchor + lastRunSize; + + if (lastRunSize >= RUN_MASK) { + size_t accumulator = lastRunSize - RUN_MASK; + *op++ = (RUN_MASK << ML_BITS); + for(; accumulator >= 255 ; accumulator -= 255) *op++ = 255; + *op++ = (BYTE) accumulator; + } else { + *op++ = (BYTE)(lastRunSize << ML_BITS); + } + memcpy(op, anchor, lastRunSize); + op += lastRunSize; + } + + /* End */ + *srcSizePtr = (int) (((const char*)ip) - source); + return (int) (((char*)op)-dest); + +_dest_overflow: + if (limit == limitedDestSize) { + op = optr; /* restore correct out pointer */ + goto _last_literals; + } + return 0; +} + +static int LZ4HC_getSearchNum(int compressionLevel) +{ + switch (compressionLevel) { + default: return 0; /* unused */ + case 11: return 128; + case 12: return 1<<10; + } +} + +static int LZ4HC_compress_generic ( + LZ4HC_CCtx_internal* const ctx, + const char* const src, + char* const dst, + int* const srcSizePtr, + int const dstCapacity, + int cLevel, + limitedOutput_directive limit + ) +{ + if (cLevel < 1) cLevel = LZ4HC_CLEVEL_DEFAULT; /* note : convention is different from lz4frame, maybe to reconsider */ + if (cLevel > 9) { + if (limit == limitedDestSize) cLevel = 10; + switch (cLevel) { + case 10: + return LZ4HC_compress_hashChain(ctx, src, dst, srcSizePtr, dstCapacity, 1 << (15-1), limit); + case 11: + ctx->searchNum = LZ4HC_getSearchNum(cLevel); + return LZ4HC_compress_optimal(ctx, src, dst, *srcSizePtr, dstCapacity, limit, 128, 0); + default: + case 12: + ctx->searchNum = LZ4HC_getSearchNum(cLevel); + return LZ4HC_compress_optimal(ctx, src, dst, *srcSizePtr, dstCapacity, limit, LZ4_OPT_NUM, 1); + } + } + return LZ4HC_compress_hashChain(ctx, src, dst, srcSizePtr, dstCapacity, 1 << (cLevel-1), limit); /* levels 1-9 */ +} + + +int LZ4_sizeofStateHC(void) { return sizeof(LZ4_streamHC_t); } + +int LZ4_compress_HC_extStateHC (void* state, const char* src, char* dst, int srcSize, int dstCapacity, int compressionLevel) +{ + LZ4HC_CCtx_internal* const ctx = &((LZ4_streamHC_t*)state)->internal_donotuse; + if (((size_t)(state)&(sizeof(void*)-1)) != 0) return 0; /* Error : state is not aligned for pointers (32 or 64 bits) */ + LZ4HC_init (ctx, (const BYTE*)src); + if (dstCapacity < LZ4_compressBound(srcSize)) + return LZ4HC_compress_generic (ctx, src, dst, &srcSize, dstCapacity, compressionLevel, limitedOutput); + else + return LZ4HC_compress_generic (ctx, src, dst, &srcSize, dstCapacity, compressionLevel, noLimit); +} + +int LZ4_compress_HC(const char* src, char* dst, int srcSize, int dstCapacity, int compressionLevel) +{ +#if defined(LZ4HC_HEAPMODE) && LZ4HC_HEAPMODE==1 + LZ4_streamHC_t* const statePtr = (LZ4_streamHC_t*)malloc(sizeof(LZ4_streamHC_t)); +#else + LZ4_streamHC_t state; + LZ4_streamHC_t* const statePtr = &state; +#endif + int const cSize = LZ4_compress_HC_extStateHC(statePtr, src, dst, srcSize, dstCapacity, compressionLevel); +#if defined(LZ4HC_HEAPMODE) && LZ4HC_HEAPMODE==1 + free(statePtr); +#endif + return cSize; +} + +/* LZ4_compress_HC_destSize() : + * currently, only compatible with Hash Chain implementation, + * hence limit compression level to LZ4HC_CLEVEL_OPT_MIN-1*/ +int LZ4_compress_HC_destSize(void* LZ4HC_Data, const char* source, char* dest, int* sourceSizePtr, int targetDestSize, int cLevel) +{ + LZ4HC_CCtx_internal* const ctx = &((LZ4_streamHC_t*)LZ4HC_Data)->internal_donotuse; + LZ4HC_init(ctx, (const BYTE*) source); + return LZ4HC_compress_generic(ctx, source, dest, sourceSizePtr, targetDestSize, cLevel, limitedDestSize); +} + + + +/************************************** +* Streaming Functions +**************************************/ +/* allocation */ +LZ4_streamHC_t* LZ4_createStreamHC(void) { return (LZ4_streamHC_t*)malloc(sizeof(LZ4_streamHC_t)); } +int LZ4_freeStreamHC (LZ4_streamHC_t* LZ4_streamHCPtr) { free(LZ4_streamHCPtr); return 0; } + + +/* initialization */ +void LZ4_resetStreamHC (LZ4_streamHC_t* LZ4_streamHCPtr, int compressionLevel) +{ + LZ4_STATIC_ASSERT(sizeof(LZ4HC_CCtx_internal) <= sizeof(size_t) * LZ4_STREAMHCSIZE_SIZET); /* if compilation fails here, LZ4_STREAMHCSIZE must be increased */ + LZ4_streamHCPtr->internal_donotuse.base = NULL; + if (compressionLevel > LZ4HC_CLEVEL_MAX) compressionLevel = LZ4HC_CLEVEL_MAX; /* cap compression level */ + LZ4_streamHCPtr->internal_donotuse.compressionLevel = compressionLevel; + LZ4_streamHCPtr->internal_donotuse.searchNum = LZ4HC_getSearchNum(compressionLevel); +} + +int LZ4_loadDictHC (LZ4_streamHC_t* LZ4_streamHCPtr, const char* dictionary, int dictSize) +{ + LZ4HC_CCtx_internal* const ctxPtr = &LZ4_streamHCPtr->internal_donotuse; + if (dictSize > 64 KB) { + dictionary += dictSize - 64 KB; + dictSize = 64 KB; + } + LZ4HC_init (ctxPtr, (const BYTE*)dictionary); + ctxPtr->end = (const BYTE*)dictionary + dictSize; + if (ctxPtr->compressionLevel >= LZ4HC_CLEVEL_OPT_MIN) + LZ4HC_updateBinTree(ctxPtr, ctxPtr->end - MFLIMIT, ctxPtr->end - LASTLITERALS); + else + if (dictSize >= 4) LZ4HC_Insert (ctxPtr, ctxPtr->end-3); + return dictSize; +} + + +/* compression */ + +static void LZ4HC_setExternalDict(LZ4HC_CCtx_internal* ctxPtr, const BYTE* newBlock) +{ + if (ctxPtr->compressionLevel >= LZ4HC_CLEVEL_OPT_MIN) + LZ4HC_updateBinTree(ctxPtr, ctxPtr->end - MFLIMIT, ctxPtr->end - LASTLITERALS); + else + if (ctxPtr->end >= ctxPtr->base + 4) LZ4HC_Insert (ctxPtr, ctxPtr->end-3); /* Referencing remaining dictionary content */ + + /* Only one memory segment for extDict, so any previous extDict is lost at this stage */ + ctxPtr->lowLimit = ctxPtr->dictLimit; + ctxPtr->dictLimit = (U32)(ctxPtr->end - ctxPtr->base); + ctxPtr->dictBase = ctxPtr->base; + ctxPtr->base = newBlock - ctxPtr->dictLimit; + ctxPtr->end = newBlock; + ctxPtr->nextToUpdate = ctxPtr->dictLimit; /* match referencing will resume from there */ +} + +static int LZ4_compressHC_continue_generic (LZ4_streamHC_t* LZ4_streamHCPtr, + const char* src, char* dst, + int* srcSizePtr, int dstCapacity, + limitedOutput_directive limit) +{ + LZ4HC_CCtx_internal* const ctxPtr = &LZ4_streamHCPtr->internal_donotuse; + /* auto-init if forgotten */ + if (ctxPtr->base == NULL) LZ4HC_init (ctxPtr, (const BYTE*) src); + + /* Check overflow */ + if ((size_t)(ctxPtr->end - ctxPtr->base) > 2 GB) { + size_t dictSize = (size_t)(ctxPtr->end - ctxPtr->base) - ctxPtr->dictLimit; + if (dictSize > 64 KB) dictSize = 64 KB; + LZ4_loadDictHC(LZ4_streamHCPtr, (const char*)(ctxPtr->end) - dictSize, (int)dictSize); + } + + /* Check if blocks follow each other */ + if ((const BYTE*)src != ctxPtr->end) LZ4HC_setExternalDict(ctxPtr, (const BYTE*)src); + + /* Check overlapping input/dictionary space */ + { const BYTE* sourceEnd = (const BYTE*) src + *srcSizePtr; + const BYTE* const dictBegin = ctxPtr->dictBase + ctxPtr->lowLimit; + const BYTE* const dictEnd = ctxPtr->dictBase + ctxPtr->dictLimit; + if ((sourceEnd > dictBegin) && ((const BYTE*)src < dictEnd)) { + if (sourceEnd > dictEnd) sourceEnd = dictEnd; + ctxPtr->lowLimit = (U32)(sourceEnd - ctxPtr->dictBase); + if (ctxPtr->dictLimit - ctxPtr->lowLimit < 4) ctxPtr->lowLimit = ctxPtr->dictLimit; + } + } + + return LZ4HC_compress_generic (ctxPtr, src, dst, srcSizePtr, dstCapacity, ctxPtr->compressionLevel, limit); +} + +int LZ4_compress_HC_continue (LZ4_streamHC_t* LZ4_streamHCPtr, const char* src, char* dst, int srcSize, int dstCapacity) +{ + if (dstCapacity < LZ4_compressBound(srcSize)) + return LZ4_compressHC_continue_generic (LZ4_streamHCPtr, src, dst, &srcSize, dstCapacity, limitedOutput); + else + return LZ4_compressHC_continue_generic (LZ4_streamHCPtr, src, dst, &srcSize, dstCapacity, noLimit); +} + +int LZ4_compress_HC_continue_destSize (LZ4_streamHC_t* LZ4_streamHCPtr, const char* src, char* dst, int* srcSizePtr, int targetDestSize) +{ + LZ4HC_CCtx_internal* const ctxPtr = &LZ4_streamHCPtr->internal_donotuse; + if (ctxPtr->compressionLevel >= LZ4HC_CLEVEL_OPT_MIN) LZ4HC_init(ctxPtr, (const BYTE*)src); /* not compatible with btopt implementation */ + return LZ4_compressHC_continue_generic(LZ4_streamHCPtr, src, dst, srcSizePtr, targetDestSize, limitedDestSize); +} + + + +/* dictionary saving */ + +int LZ4_saveDictHC (LZ4_streamHC_t* LZ4_streamHCPtr, char* safeBuffer, int dictSize) +{ + LZ4HC_CCtx_internal* const streamPtr = &LZ4_streamHCPtr->internal_donotuse; + int const prefixSize = (int)(streamPtr->end - (streamPtr->base + streamPtr->dictLimit)); + if (dictSize > 64 KB) dictSize = 64 KB; + if (dictSize < 4) dictSize = 0; + if (dictSize > prefixSize) dictSize = prefixSize; + memmove(safeBuffer, streamPtr->end - dictSize, dictSize); + { U32 const endIndex = (U32)(streamPtr->end - streamPtr->base); + streamPtr->end = (const BYTE*)safeBuffer + dictSize; + streamPtr->base = streamPtr->end - endIndex; + streamPtr->dictLimit = endIndex - dictSize; + streamPtr->lowLimit = endIndex - dictSize; + if (streamPtr->nextToUpdate < streamPtr->dictLimit) streamPtr->nextToUpdate = streamPtr->dictLimit; + } + return dictSize; +} + + +/*********************************** +* Deprecated Functions +***********************************/ +/* These functions currently generate deprecation warnings */ +/* Deprecated compression functions */ +int LZ4_compressHC(const char* src, char* dst, int srcSize) { return LZ4_compress_HC (src, dst, srcSize, LZ4_compressBound(srcSize), 0); } +int LZ4_compressHC_limitedOutput(const char* src, char* dst, int srcSize, int maxDstSize) { return LZ4_compress_HC(src, dst, srcSize, maxDstSize, 0); } +int LZ4_compressHC2(const char* src, char* dst, int srcSize, int cLevel) { return LZ4_compress_HC (src, dst, srcSize, LZ4_compressBound(srcSize), cLevel); } +int LZ4_compressHC2_limitedOutput(const char* src, char* dst, int srcSize, int maxDstSize, int cLevel) { return LZ4_compress_HC(src, dst, srcSize, maxDstSize, cLevel); } +int LZ4_compressHC_withStateHC (void* state, const char* src, char* dst, int srcSize) { return LZ4_compress_HC_extStateHC (state, src, dst, srcSize, LZ4_compressBound(srcSize), 0); } +int LZ4_compressHC_limitedOutput_withStateHC (void* state, const char* src, char* dst, int srcSize, int maxDstSize) { return LZ4_compress_HC_extStateHC (state, src, dst, srcSize, maxDstSize, 0); } +int LZ4_compressHC2_withStateHC (void* state, const char* src, char* dst, int srcSize, int cLevel) { return LZ4_compress_HC_extStateHC(state, src, dst, srcSize, LZ4_compressBound(srcSize), cLevel); } +int LZ4_compressHC2_limitedOutput_withStateHC (void* state, const char* src, char* dst, int srcSize, int maxDstSize, int cLevel) { return LZ4_compress_HC_extStateHC(state, src, dst, srcSize, maxDstSize, cLevel); } +int LZ4_compressHC_continue (LZ4_streamHC_t* ctx, const char* src, char* dst, int srcSize) { return LZ4_compress_HC_continue (ctx, src, dst, srcSize, LZ4_compressBound(srcSize)); } +int LZ4_compressHC_limitedOutput_continue (LZ4_streamHC_t* ctx, const char* src, char* dst, int srcSize, int maxDstSize) { return LZ4_compress_HC_continue (ctx, src, dst, srcSize, maxDstSize); } + + +/* Deprecated streaming functions */ +int LZ4_sizeofStreamStateHC(void) { return LZ4_STREAMHCSIZE; } + +int LZ4_resetStreamStateHC(void* state, char* inputBuffer) +{ + LZ4HC_CCtx_internal *ctx = &((LZ4_streamHC_t*)state)->internal_donotuse; + if ((((size_t)state) & (sizeof(void*)-1)) != 0) return 1; /* Error : pointer is not aligned for pointer (32 or 64 bits) */ + LZ4HC_init(ctx, (const BYTE*)inputBuffer); + ctx->inputBuffer = (BYTE*)inputBuffer; + return 0; +} + +void* LZ4_createHC (char* inputBuffer) +{ + LZ4_streamHC_t* hc4 = (LZ4_streamHC_t*)ALLOCATOR(1, sizeof(LZ4_streamHC_t)); + if (hc4 == NULL) return NULL; /* not enough memory */ + LZ4HC_init (&hc4->internal_donotuse, (const BYTE*)inputBuffer); + hc4->internal_donotuse.inputBuffer = (BYTE*)inputBuffer; + return hc4; +} + +int LZ4_freeHC (void* LZ4HC_Data) { FREEMEM(LZ4HC_Data); return 0; } + +int LZ4_compressHC2_continue (void* LZ4HC_Data, const char* src, char* dst, int srcSize, int cLevel) +{ + return LZ4HC_compress_generic (&((LZ4_streamHC_t*)LZ4HC_Data)->internal_donotuse, src, dst, &srcSize, 0, cLevel, noLimit); +} + +int LZ4_compressHC2_limitedOutput_continue (void* LZ4HC_Data, const char* src, char* dst, int srcSize, int dstCapacity, int cLevel) +{ + return LZ4HC_compress_generic (&((LZ4_streamHC_t*)LZ4HC_Data)->internal_donotuse, src, dst, &srcSize, dstCapacity, cLevel, limitedOutput); +} + +char* LZ4_slideInputBufferHC(void* LZ4HC_Data) +{ + LZ4HC_CCtx_internal* const hc4 = &((LZ4_streamHC_t*)LZ4HC_Data)->internal_donotuse; + int const dictSize = LZ4_saveDictHC((LZ4_streamHC_t*)LZ4HC_Data, (char*)(hc4->inputBuffer), 64 KB); + return (char*)(hc4->inputBuffer + dictSize); +} diff --git a/tools/rasm_src/minilib.h b/tools/rasm_src/minilib.h new file mode 100644 index 0000000..99d55eb --- /dev/null +++ b/tools/rasm_src/minilib.h @@ -0,0 +1,1151 @@ +#define __FILENAME__ "minilib.h" + +#include<string.h> +#include<stdlib.h> +#include<stddef.h> +#include<stdint.h> +#include<stdarg.h> +#include<stdio.h> +#include<errno.h> +#include<ctype.h> +#include<time.h> +#include<math.h> +#include<sys/types.h> +#include<sys/stat.h> +#include<fcntl.h> +#include<errno.h> +#ifdef OS_WIN +#include<io.h> +#include<limits.h> +#include<direct.h> +#define snprintf _snprintf +#endif +#include<ctype.h> +#include<stdio.h> +#include<stdlib.h> +#ifndef OS_WIN +#include<unistd.h> +#endif +#include<string.h> +#include<setjmp.h> +#include<errno.h> +#include<sys/timeb.h> + +#ifndef ARG_MAX +#define ARG_MAX 8191 +#endif + + + +#define MemFree free +#define MemRealloc realloc +#define MemMalloc malloc +#define MemMove memmove +#ifdef OS_WIN +#define TxtStrDup _strdup +#else +#define TxtStrDup strdup +#endif + +#define loginfo(...); {printf(__VA_ARGS__);printf("\n");} +#define logdebug(...); {printf(__VA_ARGS__);printf("\n");} +#define logwarn(...); {printf(__VA_ARGS__);printf("\n");} +#define logerr(...); {printf(__VA_ARGS__);printf("\n");} + +#define INTERNAL_ERROR 1 +#define ABORT_ERROR 7 + +#define MAX_LINE_BUFFER 16384 + +#ifndef PATH_MAX +#define PATH_MAX 4096 +#endif + +static int _static_library_nbfile_opened=0; +static int _static_library_nbfile_opened_max=0; + +void _internal_ObjectArrayAddDynamicValue(void **zearray, void *zeobject, int object_size,int curline, char *curfunc, char *cursource); +void _internal_ObjectArrayAddDynamicValueConcat(void **zearray, int *nbfields, int *maxfields, void *zeobject, int object_size, int curline, char *curfunc, char *cursource); +#define ObjectArrayAddDynamicValue(zearray,zeobject,objsize) _internal_ObjectArrayAddDynamicValue(zearray,zeobject,objsize,__LINE__,FUNC,__FILENAME__) +#define ObjectArrayAddDynamicValueConcat(zearray,nbv,maxv,zeobject,objsize) _internal_ObjectArrayAddDynamicValueConcat(zearray,nbv,maxv,zeobject,objsize,__LINE__,FUNC,__FILENAME__) +void _internal_IntArrayAddDynamicValueConcat(int **zearray, int *nbval, int *maxval, int zevalue, int curline, char *curfunc, char *cursource); +#define IntArrayAddDynamicValueConcat(zearray,nbv,maxv,zevalue) _internal_IntArrayAddDynamicValueConcat(zearray,nbv,maxv,zevalue,__LINE__,FUNC,__FILENAME__) +void _internal_FieldArrayAddDynamicValue(char ***zearray, char *zevalue, int curline, char *curfunc, char *cursource); +#define FieldArrayAddDynamicValue(zearray,zevalue) _internal_FieldArrayAddDynamicValue(zearray,zevalue,__LINE__,FUNC,__FILENAME__) +void _internal_FieldArrayAddDynamicValueConcat(char ***zearray, int *nbfields, int *maxfields, char *zevalue, int curline, char *curfunc, char *cursource); +#define FieldArrayAddDynamicValueConcat(zearray,nb,maxf,zevalue) _internal_FieldArrayAddDynamicValueConcat(zearray,nb,maxf,zevalue,__LINE__,FUNC,__FILENAME__) + + +long long FileGetSize(char *filename); + +int MinMaxInt(int zeval, int zemin, int zemax) +{ + #undef FUNC + #define FUNC "MinMaxInt" + + if (zeval<zemin) return zemin; + if (zeval>zemax) return zemax; + return zeval; +} + +/* (c) FSF */ +#ifdef __WATCOMC__ +size_t strnlen (s, maxlen) + register const char *s; + size_t maxlen; +{ + register const char *e; + size_t n; + + for (e = s, n = 0; *e && n < maxlen; e++, n++) + ; + return n; +} +#endif + + +char *TxtStrDupLen(char *str, int *len) +{ + #undef FUNC + #define FUNC "TxtStrDupLen" + + char *newstr; + *len=strlen(str)+1; + newstr=MemMalloc(*len); + strcpy(newstr,str); + return newstr; +} + +void _internal_ObjectArrayAddDynamicValueConcat(void **zearray, int *nbfields, int *maxfields, void *zeobject, int object_size, int curline, char *curfunc, char *cursource) +{ + #undef FUNC + #define FUNC "ObjectArrayAddDynamicValueConcat" + + char *dst; + + if ((*zearray)==NULL) { + *nbfields=1; + *maxfields=3; + (*zearray)=malloc((*maxfields)*object_size); + } else { + *nbfields=(*nbfields)+1; + if (*nbfields>=*maxfields) { + *maxfields=(*maxfields)*2; + (*zearray)=realloc((*zearray),(*maxfields)*object_size); + } + } + /* using direct calls because it is more interresting to know which is the caller */ + dst=((char *)(*zearray))+((*nbfields)-1)*object_size; + /* control of memory for overflow */ + memcpy(dst,zeobject,object_size); +} + +/*** + Add a value to a list of integers + Reallocate memory on the fly + + zearray: pointer to the array of integers + nbval: pointer to the current index + maxval: pointer to the current allocated memory + zevalue: integer value to concat to + + output: the pointer to the array is modified because the + function reallocate memory to store the new string at + the end of the array. +*/ +void _internal_IntArrayAddDynamicValueConcat(int **zearray, int *nbval, int *maxval, int zevalue, int curline, char *curfunc, char *cursource) +{ + #undef FUNC + #define FUNC "IntArrayAddDynamicValue" + + if ((*zearray)==NULL) { + *nbval=1; + *maxval=4; + (*zearray)=MemMalloc(sizeof(int)*(*maxval)); + } else { + *nbval=*nbval+1; + if (*nbval>*maxval) { + *maxval=(*maxval)*2; + (*zearray)=realloc((*zearray),sizeof(int)*(*maxval)); + } + } + (*zearray)[(*nbval)-1]=zevalue; +} + +/*** + CSVGetFieldArrayNumber + + count array elements +*/ +int CSVGetFieldArrayNumber(char **myfield) +{ + #undef FUNC + #define FUNC "CSVGetFieldArrayNumber" + + int n=0; + + if (myfield==NULL) + return 0; + + while (myfield[n]!=NULL) n++; + + return n; + +} +/*** + * TxtSplit + * + * split the parameter string into an array of strings + */ +char **TxtSplitWithChar(char *in_str, char split_char) +{ + #undef FUNC + #define FUNC "TxtSplitWithChar" + + char **tab=NULL; + char *match_str; + int redo=1; + int idx,idmax; + + match_str=in_str; + + while (redo && *in_str) { + while (*match_str && *match_str!=split_char) match_str++; + redo=*match_str; + *match_str=0; + FieldArrayAddDynamicValueConcat(&tab,&idx,&idmax,in_str); + in_str=++match_str; + } + + return tab; +} + +/*** + Add a value to a list of strings + Reallocate memory on the fly + + input: pointer to an array of string + string value + + output: the pointer to the array is modified because the + function reallocate memory to store the new string at + the end of the array. +*/ +void _internal_FieldArrayAddDynamicValue(char ***zearray, char *zevalue, int curline, char *curfunc, char *cursource) +{ + #undef FUNC + #define FUNC "FieldArrayAddDynamicValue" + int nbfield; + if ((*zearray)==NULL) nbfield=2; else nbfield=CSVGetFieldArrayNumber((*zearray))+2; + /* using direct calls because it is more interresting to know which is the caller */ + (*zearray)=realloc((*zearray),nbfield*sizeof(char *)); + (*zearray)[nbfield-2]=TxtStrDup(zevalue); + (*zearray)[nbfield-1]=NULL; +} +void _internal_FieldArrayAddDynamicValueConcat(char ***zearray, int *nbfields, int *maxfields, char *zevalue, int curline, char *curfunc, char *cursource) +{ + #undef FUNC + #define FUNC "FieldArrayAddDynamicValueConcat" + + if ((*zearray)==NULL) { + *nbfields=1; + *maxfields=10; + (*zearray)=realloc(NULL,(*maxfields)*sizeof(char *)); + } else { + *nbfields=(*nbfields)+1; + if (*nbfields>=*maxfields) { + *maxfields=(*maxfields)*2; + (*zearray)=MemRealloc((*zearray),(*maxfields)*sizeof(char *)); + } + } + /* using direct calls because it is more interresting to know which is the caller */ + (*zearray)[(*nbfields)-1]=TxtStrDup(zevalue); + (*zearray)[(*nbfields)]=NULL; +} + + + +/*** + CSVFreeFields + + free allocated memory for fields +*/ +void CSVFreeFields(char **fields) +{ + #undef FUNC + #define FUNC "CSVFreeFields" + + int i=0; + if (fields!=NULL) + { + /*loginfo("%8X",fields); + if (fields[i]==NULL) + loginfo("NULL i=%d",i); */ + + while (fields[i]!=NULL) + { + MemFree(fields[i]); + i++; + } + MemFree(fields); + } +} +void FreeArrayDynamicValue(char ***zearray) +{ + CSVFreeFields(*zearray); + *zearray=NULL; +} + + +void CSVFreeFields(char **fields); +#define FreeFields(fields) CSVFreeFields(fields) + + +/************************** File operation ******************************/ + + +/*** + s_fileid + structure used by FileReadLine and FileWriteLine to manage multiple files at a time +*/ +struct s_fileid +{ + FILE *file_id; + char *filename; + char opening_type[4]; + int cpt; + struct s_fileid *next; + /* v2 */ + int closed; + unsigned long curpos; +}; + +static struct s_fileid *fileidROOT=NULL; + +/*** + FileGetStructFromName + + input: filename + output: file structure + NULL if not found +*/ +struct s_fileid *FileGetStructFromName(char *filename) +{ + #undef FUNC + #define FUNC "FileGetStructFromName" + struct s_fileid *curfile; + struct s_fileid *prevfile; + + if (!filename) + { + logerr("filename must not be NULL"); + exit(ABORT_ERROR); + } + + if (strnlen(filename,PATH_MAX)==PATH_MAX) + { + logerr("cannot open this file because the argument size is bigger than PATH_MAX (%d)",PATH_MAX); + exit(ABORT_ERROR); + } + + /* try to find the file in the list */ + curfile=fileidROOT; + prevfile=NULL; + while (curfile!=NULL) + { + if (!strcmp(curfile->filename,filename)) { + break; + } else { + prevfile=curfile; + curfile=curfile->next; + } + } + /* put the struct to the top of the list */ + if (curfile && prevfile) { + prevfile->next=curfile->next; + curfile->next=fileidROOT; + fileidROOT=curfile; + } + + return curfile; +} + +/*** + FileOpen function + + open a file in any mode (r,w,a,r+,w+,a+) + check if the file is already open + check for conflicts +*/ +FILE *FileOpen(char *filename, char *opening_type) +{ + #undef FUNC + #define FUNC "FileOpen" + struct s_fileid *curfile; + struct s_fileid *oldfile; + + /* check parameters coherency */ + if (strlen(opening_type)>3) + { + logerr("illegal opening type (too long)"); + exit(ABORT_ERROR); + } + if (strcmp(opening_type,"a") && strcmp(opening_type,"w") && strcmp(opening_type,"r") + && strcmp(opening_type,"a+") && strcmp(opening_type,"w+") && strcmp(opening_type,"r+") && strcmp(opening_type,"rb")) + { + logerr("illegal opening type [%s]\nallowed options are: r,w,a,r+,w+,a+",opening_type); + exit(ABORT_ERROR); + } + + curfile=FileGetStructFromName(filename); + + /* if curfile is NULL then the file is not opened yet */ + if (!curfile) + { + /* insert a new record */ + _static_library_nbfile_opened++; + if (_static_library_nbfile_opened>_static_library_nbfile_opened_max) + _static_library_nbfile_opened_max=_static_library_nbfile_opened; + curfile=MemMalloc(sizeof(struct s_fileid)); + memset(curfile,0,sizeof(struct s_fileid)); + curfile->filename=MemMalloc(strlen(filename)+1); + strcpy(curfile->filename,filename); + strcpy(curfile->opening_type,opening_type); + curfile->next=fileidROOT; + curfile->cpt=0; + fileidROOT=curfile; + } + else + { + if (strcmp(curfile->opening_type,opening_type)) + { + logerr("You can't open the file [%s] in [%s] mode cause it's already open in [%s] mode",filename,opening_type,curfile->opening_type); + exit(ABORT_ERROR); + } + if (!curfile->closed) { + /* already opened, just return the id */ + return curfile->file_id; + } + } + + curfile->file_id=fopen(filename,opening_type); + if (!curfile->file_id) + { + if (errno==EMFILE) { + /* too many files opened, close the latest of the list */ + oldfile=fileidROOT; + while (oldfile->next && !oldfile->next->closed) oldfile=oldfile->next; + if (oldfile==curfile) { + logerr("cannot open a single file!"); + exit(INTERNAL_ERROR); + } + /* save position and close the file */ + oldfile->curpos=ftell(oldfile->file_id); + fclose(oldfile->file_id); + oldfile->file_id=0; + oldfile->closed=1; + /* then try again */ + curfile->file_id=fopen(filename,opening_type); + if (curfile->file_id) { + if (curfile->curpos) { + fseek(curfile->file_id,curfile->curpos,SEEK_SET); + curfile->curpos=0; + } + + logdebug("opening file [%s] in %s mode (too many opened files limit reached)",filename,opening_type); + return curfile->file_id; + } + } + + logerr("failed to open file [%s] with mode [%s]",filename,opening_type); + logerr("check empty space and permissions"); + exit(ABORT_ERROR); + } + + /* go on previous position */ + if (curfile->curpos) { + fseek(curfile->file_id,curfile->curpos,SEEK_SET); + curfile->curpos=0; + } + + //logdebug("opening file [%s] in %s mode",filename,opening_type); + return curfile->file_id; +} + +struct s_fileid *FileGetStructFromID(FILE *file_id); + +void FileSeekBinary(char *filename,int pos, int st) +{ + #undef FUNC + #define FUNC "FileSeekBinary" + FILE *last_id=NULL; + struct s_fileid *curfile=NULL; + int err; + + last_id=FileOpen(filename,"r"); + if ((err=fseek(last_id,pos,st))!=0) { + logerr("error seeking %s (%d)",filename,ferror(last_id)); + exit(ABORT_ERROR); + } + + curfile=FileGetStructFromID(last_id); + switch (st) { + case SEEK_SET: curfile->curpos=pos; break; + case SEEK_CUR: curfile->curpos+=pos; break; + case SEEK_END: logerr("TODO§§§"); break; + default:logerr("unknown SEEK mode!");exit(INTERNAL_ERROR); + } +} + +/*** + FileGetStructFromID + + retrieve the file structure from the tree, with his ID +*/ +struct s_fileid *FileGetStructFromID(FILE *file_id) +{ + #undef FUNC + #define FUNC "FileGetStructFromID" + struct s_fileid *curfile; + struct s_fileid *prevfile; + + curfile=fileidROOT; + prevfile=NULL; + while (curfile!=NULL) + { + if (curfile->file_id==file_id) { + break; + } else { + prevfile=curfile; + curfile=curfile->next; + } + } + if (!curfile) + { + logerr("ID requested for an unknown file! (was supposed to be opened)"); + exit(INTERNAL_ERROR); + } + /* put the struct to the top of the list */ + if (prevfile) { + prevfile->next=curfile->next; + curfile->next=fileidROOT; + fileidROOT=curfile; + } + + return curfile; +} + +/*** + FileClose function + + check for closing return code + free the memory file structure +*/ +void FileClose(FILE *file_id) +{ + #undef FUNC + #define FUNC "FileClose" + struct s_fileid *curfile; + struct s_fileid *unlinkcell; + + curfile=FileGetStructFromID(file_id); + + if (!curfile->closed) { + if (fclose(curfile->file_id)) + { + logerr("error while closing file [%s]",curfile->filename); + } + } else { + /* already closed */ + } + + /* unlink the cell from ROOT */ + if (curfile==fileidROOT) + { + fileidROOT=curfile->next; + } + else + { + unlinkcell=fileidROOT; + while (unlinkcell->next!=curfile) + unlinkcell=unlinkcell->next; + unlinkcell->next=curfile->next; + } + MemFree(curfile->filename); + MemFree(curfile); + _static_library_nbfile_opened--; +} + +/*** + FileAddCPT + + add n to counter when reading or writing in a file +*/ +void FileAddCPT(FILE *file_id, int n) +{ + #undef FUNC + #define FUNC "FileAddCPT" + struct s_fileid *curfile; + + curfile=FileGetStructFromID(file_id); + curfile->cpt+=n; +} +#define FileIncCPT(file_id) FileAddCPT(file_id,1) + +/*** + FileGetCPT + + Get file counter information + input: file_id +*/ +int FileGetCPT(FILE *file_id) +{ + #undef FUNC + #define FUNC "FileGetCPT" + struct s_fileid *curfile; + + curfile=FileGetStructFromID(file_id); + return curfile->cpt; +} +/*** + FileGetCPTFromName + + Get file counter information + input: filename +*/ +int FileGetCPTFromName(char *filename) +{ + #undef FUNC + #define FUNC "FileGetCPTFromName" + struct s_fileid *curfile; + + curfile=FileGetStructFromName(filename); + if (!curfile) + { + logerr("You requested a counter for a file that is not opened! [%s]",filename); + exit(INTERNAL_ERROR); + } + return curfile->cpt; +} + + + +char *_internal_fgetsClose(char *buffer, int maxlen, FILE *f) +{ + #undef FUNC + #define FUNC "_internal_fgetsClose" + return NULL; +} + +/*** + FileReadLine/FileReadLineXML function + + input: + - filename + + output: + - a static buffer with the line read + + this function can handle many file simultaneously + just use different filenames without regarding to opened/closed pointer + the opened handles are automatically closed when the end of the file is reached + you are only limited by the system, not by the code + + the XML version ass carriage returns after closing tag + this can be usefull for parsing when reading XML file in a single line +*/ +enum e_reading_mode { +RAW_READING, +CLOSE_READING +}; + +char *_internal_fgetsmulti(char *filename, int read_mode) +{ + #undef FUNC + #define FUNC "_internal_fgetsmulti" + static char buffer[MAX_LINE_BUFFER+1]={0}; + FILE *last_id=NULL; + char * (*_file_get_string)(char *, int, FILE *); + + last_id=FileOpen(filename,"r"); + + switch (read_mode) + { + case RAW_READING:_file_get_string=fgets;break; + case CLOSE_READING:_file_get_string=_internal_fgetsClose;break; + default:logerr("Unknown read mode! (%d)",read_mode); + } + + if (_file_get_string(buffer,MAX_LINE_BUFFER,last_id)!=NULL) + { + FileIncCPT(last_id); + if (strnlen(buffer,MAX_LINE_BUFFER)==MAX_LINE_BUFFER) + { + logerr("line %d is too long! More than %d characters\n",FileGetCPT(last_id),MAX_LINE_BUFFER); + exit(INTERNAL_ERROR); + } + return buffer; + } + else + { + /* End of file, we close the handle */ + logdebug("%d line(s) read from %s",FileGetCPT(last_id),filename); + FileClose(last_id); + return NULL; + } +} + +char **_internal_fgetsmultilines(char *filename, int read_mode) +{ + #undef FUNC + #define FUNC "_internal_fgetsmultilines" + static char buffer[MAX_LINE_BUFFER+1]={0}; + FILE *last_id=NULL; + char * (*_file_get_string)(char *, int, FILE *); + char **lines_buffer=NULL; + int cur_line=0,max_line=0; + + last_id=FileOpen(filename,"r"); + + switch (read_mode) + { + case RAW_READING:_file_get_string=fgets;break; + case CLOSE_READING:_file_get_string=_internal_fgetsClose;break; + default:logerr("Unknown read mode! (%d)",read_mode); + } + + + while (_file_get_string(buffer,MAX_LINE_BUFFER,last_id)!=NULL) + { + FileIncCPT(last_id); + if (strnlen(buffer,MAX_LINE_BUFFER)==MAX_LINE_BUFFER) + { + logerr("line %d is too long! More than %d characters\n",FileGetCPT(last_id),MAX_LINE_BUFFER); + exit(INTERNAL_ERROR); + } + FieldArrayAddDynamicValueConcat(&lines_buffer,&cur_line,&max_line,buffer); + } + /* if file is empty, we allocate an empty struct */ + if (!lines_buffer) { + /* create the end of the list */ + lines_buffer=MemMalloc(sizeof(char*)); + lines_buffer[0]=NULL; + } + + /* End of file, we close the handle */ + logdebug("%d line(s) read from %s",FileGetCPT(last_id),filename); + FileClose(last_id); + + return lines_buffer; +} + +#define FileReadClose(filename) _internal_fgetsmulti(filename,CLOSE_READING) +#define FileReadLine(filename) _internal_fgetsmulti(filename,RAW_READING) + +long FileReadPos(char *filename) +{ + #undef FUNC + #define FUNC "FileReadPos" + + FILE *last_id; + + last_id=FileOpen(filename,"r"); + return ftell(last_id); +} + +/*** + + FileWriteLine function + + input: + - filename + - a buffer with the line to write + + the function do not close the file until a NULL buffer is sent. + It prevents from too much write flushes + + you can use it with many files simultaneously but do not forget + to close your files if you use this function in a loop of filenames + or the system will warn you +*/ + +void FileWriteLine(char *filename, char *buffer) +{ + #undef FUNC + #define FUNC "FileWriteLine" + FILE *last_id=NULL; + + last_id=FileOpen(filename,"a+"); + + if (buffer!=NULL) + { + fputs(buffer,last_id); + FileIncCPT(last_id); + } + else + { + /* NULL buffer sent, this means End of file, we close the handle */ + //logdebug("%d line(s) written to %s",FileGetCPT(last_id),filename); + FileClose(last_id); + } +} +void FileWriteLines(char *filename, char **buffer) +{ + #undef FUNC + #define FUNC "FileWriteLines" + FILE *last_id=NULL; + int i; + + last_id=FileOpen(filename,"a+"); + for (i=0;buffer[i];i++) { + fputs(buffer[i],last_id); + FileIncCPT(last_id); + } + //logdebug("%d line(s) written to %s",FileGetCPT(last_id),filename); + FileClose(last_id); +} + +/*** + FileReadBinary function + + read n bytes into buffer + return number of byte really read + + as other File functions, you can use it with many files simultaneously +*/ +int FileReadBinary(char *filename,char *data,int n) +{ + #undef FUNC + #define FUNC "FileReadBinary" + FILE *last_id=NULL; + int nn; + + last_id=FileOpen(filename,"rb"); + + if (data==NULL) + { + FileClose(last_id); + return 0; + } + + nn=fread(data,1,n,last_id); + //printf("%d bytes lus\n",nn); + FileAddCPT(last_id,nn); + if (nn!=n) + { + /* if eof is set, this is not an error */ + if (feof(last_id)) + { + //logdebug("%d byte(s) read from %s",FileGetCPT(last_id),filename); + } + else + if (ferror(last_id)) + { + if (!nn) { + logerr("cannot read %s",filename); + } + else { + logerr("error %d bytes were read during reading of %d bytes of %s",nn,n,filename); + } + exit(ABORT_ERROR); + } + else + { + logerr("error during read of %s (but no error neither eof flag set)",filename); + exit(INTERNAL_ERROR); + } + FileClose(last_id); + } + return nn; +} + +/*** + FileWriteBinary function + + write n bytes from buffer to file + return number of byte really written + + as other File functions, you can use it with many files simultaneously +*/ +int FileWriteBinary(char *filename,char *data,int n) +{ + #undef FUNC + #define FUNC "FileWriteBinary" + FILE *last_id=NULL; + int nn; + + #ifdef OS_WIN + last_id=FileOpen(filename,"w"); + #else + last_id=FileOpen(filename,"a+"); + #endif + if (data!=NULL) + { + #ifdef OS_WIN + int sr; + sr=_setmode(_fileno(last_id), _O_BINARY ); + if (sr==-1) { + logerr("FATAL: cannot set binary mode for writing"); + exit(ABORT_ERROR); + } + #endif + nn=fwrite(data,1,n,last_id); + FileAddCPT(last_id,nn); + /* we must always write the same amount of data */ + if (n!=nn) + { + if (ferror(last_id)) + { + if (!nn) { + logerr("cannot write %s",filename); + logerr("%s",strerror(errno)); + } + else{ + logerr("error during write of %s (%d byte(s))",filename,FileGetCPT(last_id)); + } + exit(ABORT_ERROR); + } + else + { + logerr("error during write of %s (but no error neither eof flag set) %d byte(s) written",filename,FileGetCPT(last_id)); + exit(INTERNAL_ERROR); + } + } + } + else + { + /* NULL buffer sent, this means End of file, we close the handle */ + //logdebug("%d byte(s) written to %s",FileGetCPT(last_id),filename); + FileClose(last_id); + } + return nn; +} +/*** macro to close binary files */ +#define FileWriteLineClose(filename) FileWriteLine(filename,NULL) +#define FileReadBinaryClose(filename) FileReadBinary(filename,NULL,0) +#define FileWriteBinaryClose(filename) FileWriteBinary(filename,NULL,0) + +/*** + FileGetStat + + input: filename + return the stat structure of a given file +*/ +struct stat *FileGetStat(char *filename) +{ + #undef FUNC + #define FUNC "FileGetStat" + struct stat *filestat; +#ifdef OS_WIN + struct _stat winstat; +#endif + + if (!filename) + { + logerr("filename must not be NULL"); + exit(ABORT_ERROR); + } + /* check after by the system but... */ + if (strnlen(filename,PATH_MAX)==PATH_MAX) + { + logerr("cannot open this file because the argument size is bigger than PATH_MAX (%d)",PATH_MAX); + logerr("[%s]",filename); + exit(ABORT_ERROR); + } + + filestat=MemMalloc(sizeof(struct stat)); + memset(filestat,0,sizeof(struct stat)); +#ifdef OS_WIN + if (_stat(filename,&winstat)!=0) +#else + if (stat(filename,filestat) && errno!=ENOENT) +#endif + { + logerr("stat %s failed",filename); + switch (errno) + { + case EACCES:logerr("Search permission is denied for one of the directories in the path prefix of path.");break; + case EBADF:logerr("filedes is bad.");break; + case EFAULT:logerr("Bad address.");break; + //case ELOOP:logerr("Too many symbolic links encountered while traversing the path.");break; + case ENAMETOOLONG:logerr("File name too long.");break; + case ENOMEM:logerr("Out of memory (i.e. kernel memory).");break; + case ENOTDIR:logerr("A component of the path is not a directory.");break; + default:logerr("Unknown error %d during stat: %s",errno,strerror(errno)); + } + exit(ABORT_ERROR); + } +#ifdef OS_WIN + filestat->st_size=winstat.st_size; + filestat->st_mode=winstat.st_mode; + filestat->st_atime=winstat.st_atime; /* last access */ + filestat->st_mtime=winstat.st_mtime; /* last modification */ + filestat->st_ctime=winstat.st_ctime; /* time of creation */ +#endif + return filestat; +} + +/*** + FileGetSize + + input: filename + output: the size in bytes of the file +*/ +long long FileGetSize(char *filename) +{ + #undef FUNC + #define FUNC "FileGetSize" + struct stat *filestat; + long long nn; + + filestat=FileGetStat(filename); + nn=filestat->st_size; + MemFree(filestat); + //logdebug("size of %s = %d (%dkb)",filename,nn,nn/1024); + return nn; +} + +/*** + * append a string + * return the number of char appened + */ +int strappend(char *Adst, char *Asrc) +{ + int Lcpt=0; + + /* must be at the end to concat */ + while (*Adst) { + Adst++; + } + + /* concat and count */ + while (*Asrc) { + *Adst++=*Asrc++; + Lcpt++; + } + *Adst=0; + + return Lcpt; +} + +char * TxtTrimEnd(char *in_str) +{ + #undef FUNC + #define FUNC "TxtTrimEnd" + + char *curs; + char *space=NULL; + + curs=in_str; + /* read the string and save the first space position, unfollowed by another character */ + while (*curs) { + //if (*curs==' ' || *curs==0x0D || *curs==0x0A || *curs=='\t') { + /* this include tab, space, line feed, carriage return, ...*/ + if (*curs<=' ') { + if (!space) { + space=curs; + } + } else { + space=NULL; + } + curs++; + } + if (space) { + *space=0; + } + + return in_str; +} + +int LookForFile(char *filename, char *dirname) +{ + #undef FUNC + #define FUNC "LookForFile" + + char fullpath[PATH_MAX+1]; + int pathlen; + + /* checked by DirReadEntry but it is more convenient to have an error inside this function */ + if (!filename) + { + logerr("You must specify at least a filename with the path, or a filename and a dirname. Filename cannot be NULL!"); + exit(INTERNAL_ERROR); + } + + pathlen=strnlen(filename,PATH_MAX); + if (pathlen==PATH_MAX) + { + logerr("cannot look for this file or directory because the argument size is bigger than PATH_MAX (%d)",PATH_MAX); + exit(ABORT_ERROR); + } + + if (!dirname) { +#ifdef OS_WIN + if (_access(filename,0)==0) return 1; else return 0; +#else + if (access(filename,F_OK)!=-1) return 1; else return 0; +#endif + } else { + pathlen+=strnlen(dirname,PATH_MAX-pathlen); + if (pathlen>=PATH_MAX-1) + { + logerr("cannot look for this file or directory because the full path size is bigger than PATH_MAX (%d)",PATH_MAX); + exit(ABORT_ERROR); + } + sprintf(fullpath,"%s/%s",dirname,filename); +#ifdef OS_WIN + if (_access(fullpath,0)==0) return 1; else return 0; +#else + if (access(fullpath,F_OK)!=-1) return 1; else return 0; +#endif + } +} + +#define FileExists(zefile) LookForFile(zefile,NULL) + +int _internal_do_remove(char *zename, char *zetype) +{ + #undef FUNC + #define FUNC "_do_remove" + + if (zename==NULL) + { + logerr("the argument cannot be NULL"); + exit(INTERNAL_ERROR); + } + if (strnlen(zename,PATH_MAX)==PATH_MAX) + { + logerr("cannot remove this file or directory because the argument size is bigger than PATH_MAX (%d)",PATH_MAX); + exit(ABORT_ERROR); + } + + if (remove(zename)) + { + switch (errno) + { + case EACCES:logerr("Write permission is denied for the directory from which the %s [%s] is to be removed, or the directory has the sticky bit set and you do not own the file.",zetype,zename);break; + case EBUSY:logerr("This error indicates that the %s [%s] is being used by the system in such a way that it can't be unlinked. For example, you might see this error if the file name specifies the root directory or a mount point for a file system.",zetype,zename);break; + case ENOENT:logerr("The %s named [%s] to be deleted doesn't exist.",zetype,zename);break; + case EPERM:logerr("On some systems unlink cannot be used to delete the name of a directory [%s], or at least can only be used this way by a privileged user. To avoid such problems, use rmdir to delete directories.",zename);break; + case EROFS:logerr("The directory containing the %s named [%s] to be deleted is on a read-only file system and can't be modified.",zetype,zename);break; + case ENOTEMPTY:logerr("The directory [%s] to be deleted is not empty.",zename);break; + default:logerr("Unknown error %d during remove [%s]: %s",errno,zename,strerror(errno)); + } + exit(ABORT_ERROR); + } + //logdebug("%s deleted",zename); + return 0; +} + +#define FileRemove(filename) _internal_do_remove(filename,"file") + +void FileRemoveIfExists(char *filename) +{ + #undef FUNC + #define FUNC "FileRemoveIfExists" + if (FileExists(filename)) + FileRemove(filename); +} + + +#undef __FILENAME__ diff --git a/tools/rasm_src/rasm.h b/tools/rasm_src/rasm.h new file mode 100644 index 0000000..bfac332 --- /dev/null +++ b/tools/rasm_src/rasm.h @@ -0,0 +1,26 @@ + +struct s_debug_error { + char *filename; + int line; + char *msg; + int lenmsg,lenfilename; +}; +struct s_debug_symbol { + char *name; + int v; +}; +struct s_rasm_info { + struct s_debug_error *error; + int nberror,maxerror; + struct s_debug_symbol *symbol; + int nbsymbol,maxsymbol; +}; + + +extern "C" { +int RasmAssemble(const char *datain, int lenin, unsigned char **dataout, int *lenout); +int RasmAssembleInfo(const char *datain, int lenin, unsigned char **dataout, int *lenout, struct s_rasm_info **debug); +void RasmFreeInfoStruct(struct s_rasm_info *debug); +}; + + diff --git a/tools/rasm_src/rasm_v0119.c b/tools/rasm_src/rasm_v0119.c new file mode 100644 index 0000000..6593392 --- /dev/null +++ b/tools/rasm_src/rasm_v0119.c @@ -0,0 +1,17316 @@ +#define PROGRAM_NAME "RASM" +#define PROGRAM_VERSION "0.119" +#define PROGRAM_DATE "xx/12/2019" +#define PROGRAM_COPYRIGHT "© 2017 BERGE Edouard / roudoudou from Resistance" + +#define RASM_VERSION PROGRAM_NAME" v"PROGRAM_VERSION" (build "PROGRAM_DATE")" +#define RASM_SNAP_VERSION PROGRAM_NAME" v"PROGRAM_VERSION + +#define TRACE_GENERALE 0 +#define TRACE_PREPRO 0 +#define TRACE_ASSEMBLE 0 +#define TRACE_COMPUTE_EXPRESSION 0 +#define TRACE_HEXBIN 0 +#define TRACE_MAKEAMSDOSREAL 0 +#define TRACE_STRUCT 0 +#define TRACE_EDSK 0 + + + +/*** +Rasm (roudoudou assembler) Z80 assembler + +doc & latest official release at: http://www.cpcwiki.eu/forum/programming/rasm-z80-assembler-in-beta/ + +You may send requests/bugs in the same topic + +----------------------------------------------------------------------------------------------------- +This software is using MIT "expat" license + +« Copyright © BERGE Edouard (roudoudou) + +Permission is hereby granted, free of charge,to any person obtaining a copy of this software +and associated documentation/source files of RASM, to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, publish, distribute, +sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or +substantial portions of the Software. +The Software is provided "as is", without warranty of any kind, express or implied, +including but not limited to the warranties of merchantability, fitness for a particular +purpose and noninfringement. In no event shall the authors or copyright holders be liable for +any claim, damages or other liability, whether in an action of contract, tort or otherwise, +arising from, out of or in connection with the software or the use or other dealings in the +Software. » +----------------------------------------------------------------------------------------------------- +Linux compilation with GCC or Clang: +cc rasm_v0116.c -O2 -lm -lrt -march=native -o rasm +strip rasm + +Windows compilation with Visual studio: +cl.exe rasm_v0116.c -O2 -Ob3 + +pure MS-DOS 32 bits compilation with Watcom: +wcl386 rasm_v0116.c -6r -6s -fp6 -d0 -k4000000 -ox /bt=DOS /l=dos4g + +MorphOS compilation (ixemul): +ppc-morphos-gcc-5 -O2 -c -o rasm rasm_v0116.c +strip rasm + +MacOS compilation: +cc rasm_v0116.c -O2 -lm -march=native -o rasm + +*/ + +#ifdef __WATCOMC__ +#define OS_WIN 1 +#endif + +#ifdef _WIN32 +#define OS_WIN 1 +#endif + +#ifdef _WIN64 +#define OS_WIN 1 +#endif + +#ifndef RDD + /* public lib */ + #include"minilib.h" +#else + /* private dev lib wont be published */ + #include"../tools/library.h" + #define TxtSplitWithChar _internal_TxtSplitWithChar +#endif + +#ifndef NO_3RD_PARTIES +#define __FILENAME__ "3rd parties" +/* 3rd parties compression */ +#include"zx7.h" +#include"lz4.h" +#include"exomizer.h" +#endif + +#ifdef __MORPHOS__ +/* Add standard version string to executable */ +const char __attribute__((section(".text"))) ver_version[]={ "\0$VER: "PROGRAM_NAME" "PROGRAM_VERSION" ("PROGRAM_DATE") "PROGRAM_COPYRIGHT"" }; +#endif + +#undef __FILENAME__ +#define __FILENAME__ "rasm.c" + +#ifndef OS_WIN +#define KNORMAL "\x1B[0m" +#define KERROR "\x1B[31m" +#define KAYGREEN "\x1B[32m" +#define KWARNING "\x1B[33m" +#define KBLUE "\x1B[34m" +#define KVERBOSE "\x1B[36m" +#define KIO "\x1B[97m" +#else +#define KNORMAL "" +#define KERROR "Error: " +#define KAYGREEN "" +#define KWARNING "Warning: " +#define KBLUE "" +#define KVERBOSE "" +#define KIO "" +#endif + +/******************************************************************* + c o m m a n d l i n e p a r a m e t e r s +*******************************************************************/ +enum e_dependencies_type { +E_DEPENDENCIES_NO=0, +E_DEPENDENCIES_LIST, +E_DEPENDENCIES_MAKE +}; + +struct s_parameter { + char **labelfilename; + char *filename; + char *outputfilename; + int automatic_radix; + int export_local; + int export_var; + int export_equ; + int export_sym; + int export_multisym; + int export_tape; + char *flexible_export; + int export_sna; + int export_snabrk; + int export_brk; + int nowarning; + int checkmode; + int dependencies; + int maxerr; + int macrovoid; + int extended_error; + int edskoverwrite; + float rough; + int as80,dams; + int v2; + int warn_unused; + char *symbol_name; + char *binary_name; + char *cartridge_name; + char *snapshot_name; + char *tape_name; + char *breakpoint_name; + char **symboldef; + int nsymb,msymb; + char **pathdef; + int npath,mpath; +}; + + + +/******************************************************************* + c o m p u t e o p e r a t i o n s f o r c a l c u l a t o r +*******************************************************************/ + +enum e_compute_operation_type { +E_COMPUTE_OPERATION_PUSH_DATASTC=0, +E_COMPUTE_OPERATION_OPEN=1, +E_COMPUTE_OPERATION_CLOSE=2, +E_COMPUTE_OPERATION_ADD=3, +E_COMPUTE_OPERATION_SUB=4, +E_COMPUTE_OPERATION_DIV=5, +E_COMPUTE_OPERATION_MUL=6, +E_COMPUTE_OPERATION_AND=7, +E_COMPUTE_OPERATION_OR=8, +E_COMPUTE_OPERATION_MOD=9, +E_COMPUTE_OPERATION_XOR=10, +E_COMPUTE_OPERATION_NOT=11, +E_COMPUTE_OPERATION_SHL=12, +E_COMPUTE_OPERATION_SHR=13, +E_COMPUTE_OPERATION_BAND=14, +E_COMPUTE_OPERATION_BOR=15, +E_COMPUTE_OPERATION_LOWER=16, +E_COMPUTE_OPERATION_GREATER=17, +E_COMPUTE_OPERATION_EQUAL=18, +E_COMPUTE_OPERATION_NOTEQUAL=19, +E_COMPUTE_OPERATION_LOWEREQ=20, +E_COMPUTE_OPERATION_GREATEREQ=21, +/* math functions */ +E_COMPUTE_OPERATION_SIN=22, +E_COMPUTE_OPERATION_COS=23, +E_COMPUTE_OPERATION_INT=24, +E_COMPUTE_OPERATION_FLOOR=25, +E_COMPUTE_OPERATION_ABS=26, +E_COMPUTE_OPERATION_LN=27, +E_COMPUTE_OPERATION_LOG10=28, +E_COMPUTE_OPERATION_SQRT=29, +E_COMPUTE_OPERATION_ASIN=30, +E_COMPUTE_OPERATION_ACOS=31, +E_COMPUTE_OPERATION_ATAN=32, +E_COMPUTE_OPERATION_EXP=33, +E_COMPUTE_OPERATION_LOW=34, +E_COMPUTE_OPERATION_HIGH=35, +E_COMPUTE_OPERATION_PSG=36, +E_COMPUTE_OPERATION_RND=37, +E_COMPUTE_OPERATION_FRAC=38, +E_COMPUTE_OPERATION_CEIL=39, +E_COMPUTE_OPERATION_GET_R=40, +E_COMPUTE_OPERATION_GET_V=41, +E_COMPUTE_OPERATION_GET_B=42, +E_COMPUTE_OPERATION_SET_R=43, +E_COMPUTE_OPERATION_SET_V=44, +E_COMPUTE_OPERATION_SET_B=45, +E_COMPUTE_OPERATION_END=46 +}; + +struct s_compute_element { +enum e_compute_operation_type operator; +double value; +int priority; +}; + +struct s_compute_core_data { + /* evaluator v3 may be recursive */ + char *varbuffer; + int maxivar; + struct s_compute_element *tokenstack; + int maxtokenstack; + struct s_compute_element *operatorstack; + int maxoperatorstack; +}; + +/*********************************************************** + w a v h e a d e r f o r a u d i o i m p o r t +***********************************************************/ +struct s_wav_header { +unsigned char ChunkID[4]; +unsigned char ChunkSize[4]; +unsigned char Format[4]; +unsigned char SubChunk1ID[4]; +unsigned char SubChunk1Size[4]; +unsigned char AudioFormat[2]; +unsigned char NumChannels[2]; +unsigned char SampleRate[4]; +unsigned char ByteRate[4]; +unsigned char BlockAlign[2]; +unsigned char BitsPerSample[2]; +unsigned char SubChunk2ID[4]; +unsigned char SubChunk2Size[4]; +}; + +enum e_audio_sample_type { +AUDIOSAMPLE_SMP, +AUDIOSAMPLE_SM2, +AUDIOSAMPLE_SM4, +AUDIOSAMPLE_DMA, +AUDIOSAMPLE_END +}; + +/*********************************************************************** + e x p r e s s i o n t y p e s f o r d e l a y e d w r i t e +***********************************************************************/ +enum e_expression { + E_EXPRESSION_J8, /* relative 8bits jump */ + E_EXPRESSION_0V8, /* 8 bits value to current adress */ + E_EXPRESSION_V8, /* 8 bits value to current adress+1 */ + E_EXPRESSION_V16, /* 16 bits value to current adress+1 */ + E_EXPRESSION_V16C, /* 16 bits value to current adress+1 */ + E_EXPRESSION_0V16, /* 16 bits value to current adress */ + E_EXPRESSION_0V32, /* 32 bits value to current adress */ + E_EXPRESSION_0VR, /* AMSDOS real value (5 bytes) to current adress */ + E_EXPRESSION_IV8, /* 8 bits value to current adress+2 */ + E_EXPRESSION_IV81, /* 8 bits value+1 to current adress+2 */ + E_EXPRESSION_3V8, /* 8 bits value to current adress+3 used with LD (IX+n),n */ + E_EXPRESSION_IV16, /* 16 bits value to current adress+2 */ + E_EXPRESSION_RST, /* the offset of RST is translated to the opcode */ + E_EXPRESSION_IM, /* the interrupt mode is translated to the opcode */ + E_EXPRESSION_RUN, /* delayed RUN value */ + E_EXPRESSION_ZXRUN, /* delayed RUN value for ZX snapshot */ + E_EXPRESSION_ZXSTACK /* delayed STACK value for ZX snapshot */ +}; + +struct s_expression { + char *reference; /* backup when used inside loop (or macro?) */ + int iw; /* word index in the main wordlist */ + int o; /* offset de depart 0, 1 ou 3 selon l'opcode */ + int ptr; /* offset courant pour calculs relatifs */ + int wptr; /* where to write the result */ + enum e_expression zetype; /* type of delayed write */ + int lz; /* lz zone */ + int ibank; /* ibank of expression */ + int iorgzone; /* org of expression */ +}; + +struct s_expr_dico { + char *name; + int crc; + int autorise_export; + double v; + int used; + int iw; +}; + +struct s_label { + char *name; /* is alloced for local repeat or struct OR generated global -> in this case iw=-1 */ + int iw; /* index of the word of label name */ + int crc; /* crc of the label name */ + int ptr; /* "physical" adress */ + int lz; /* is the label in a crunched section (or after)? */ + int iorgzone; /* org of label */ + int ibank; /* current CPR bank / always zero in classic mode */ + int local; + /* errmsg */ + int fileidx; + int fileline; + int autorise_export,backidx; + int used; +}; + +struct s_alias { + char *alias; + char *translation; + int crc,len,autorise_export; + int iw; + int used; +}; + +struct s_ticker { + char *varname; + int crc; + long nopstart; + long tickerstart; +}; + +/*********************************************************************** + m e r k e l t r e e s f o r l a b e l, v a r, a l i a s +***********************************************************************/ +struct s_crclabel_tree { + struct s_crclabel_tree *radix[256]; + struct s_label *label; + int nlabel,mlabel; +}; +struct s_crcdico_tree { + struct s_crcdico_tree *radix[256]; + struct s_expr_dico *dico; + int ndico,mdico; +}; +struct s_crcused_tree { + struct s_crcused_tree *radix[256]; + char **used; + int nused,mused; +}; +struct s_crcstring_tree { + struct s_crcstring_tree *radix[256]; + char **text; + int ntext,mtext; + char **replace; + int nreplace,mreplace; +}; +/************************************************* + m e m o r y s e c t i o n +*************************************************/ +struct s_lz_section { + int iw; + int memstart,memend; + int lzversion; /* 4 -> LZ4 / 7 -> ZX7 / 48 -> LZ48 / 49 -> LZ49 / 8 -> Exomizer */ + int iorgzone; + int ibank; + /* idx backup */ + int iexpr; + int ilabel; +}; + +struct s_orgzone { + int ibank,protect; + int memstart,memend; + int ifile,iline; + int nocode; +}; + +/************************************************** + i n c b i n s t o r a g e +**************************************************/ +struct s_hexbin { + unsigned char *data; + int datalen,rawlen; + char *filename; + int crunch; +}; + +/************************************************** + e d s k m a n a g e m e n t +**************************************************/ +struct s_edsk_sector_global_struct { +unsigned char track; +unsigned char side; +unsigned char id; +unsigned char size; +unsigned char st1; +unsigned char st2; +unsigned short int length; +unsigned char *data; +}; + +struct s_edsk_track_global_struct { +int sectornumber; +/* information purpose */ +int sectorsize; +int gap3length; +int fillerbyte; +int datarate; +int recordingmode; +struct s_edsk_sector_global_struct *sector; +}; + +struct s_edsk_global_struct { +int tracknumber; +int sidenumber; +int tracksize; /* DSK legacy */ +struct s_edsk_track_global_struct *track; +}; + +struct s_edsk_wrapper_entry { +unsigned char user; +unsigned char filename[11]; +unsigned char subcpt; +unsigned char extendcounter; +unsigned char reserved; +unsigned char rc; +unsigned char blocks[16]; +}; + +struct s_edsk_wrapper { +char *edsk_filename; +struct s_edsk_wrapper_entry entry[64]; +int nbentry; +unsigned char blocks[178][1024]; /* DATA format */ +int face; +}; + +struct s_save { + int ibank; + int ioffset; + int isize; + int iw,irun; + int amsdos,hobeta; + int tape,dsk,face,iwdskname; +}; + + +/******************** + L O O P S +********************/ + +enum e_loop_style { +E_LOOPSTYLE_REPEATN, +E_LOOPSTYLE_REPEATUNTIL, +E_LOOPSTYLE_WHILE +}; + +struct s_repeat { + int start; + int cpt; + int value; + int maxim; + int repeat_counter; + char *repeatvar; + int repeatcrc; +}; + +struct s_whilewend { + int start; + int cpt; + int value; + int maxim; + int while_counter; +}; + +struct s_switchcase { + int refval; + int execute; + int casematch; +}; + +struct s_repeat_index { + int ifile; + int ol,oidx; + int cl,cidx; +}; + + +enum e_ifthen_type { +E_IFTHEN_TYPE_IF=0, +E_IFTHEN_TYPE_IFNOT=1, +E_IFTHEN_TYPE_IFDEF=2, +E_IFTHEN_TYPE_IFNDEF=3, +E_IFTHEN_TYPE_ELSE=4, +E_IFTHEN_TYPE_ELSEIF=5, +E_IFTHEN_TYPE_IFUSED=6, +E_IFTHEN_TYPE_IFNUSED=7, +E_IFTHEN_TYPE_END +}; + +struct s_ifthen { + char *filename; + int line,v; + enum e_ifthen_type type; +}; + +/************************************************** + w o r d p r o c e s s i n g +**************************************************/ +struct s_wordlist { + char *w; + int l,t,e; /* e=1 si egalite dans le mot */ + int ifile; +}; + +struct s_macro { + char *mnemo; + int crc; + /* une macro concatene des chaines et des parametres */ + struct s_wordlist *wc; + int nbword,maxword; + /**/ + char **param; + int nbparam; +}; + +struct s_macro_position { + int start,end,value; +}; + +/* preprocessing only */ +struct s_macro_fast { + char *mnemo; + int crc; +}; + +struct s_math_keyword { + char *mnemo; + int crc; + enum e_compute_operation_type operation; +}; + +struct s_expr_word { + char *w; + int aw; + int op; + int comma; + int fct; + double v; +}; + +struct s_listing { + char *listing; + int ifile; + int iline; +}; + +enum e_tagtranslateoption { +E_TAGOPTION_NONE=0, +E_TAGOPTION_REMOVESPACE=1, +E_TAGOPTION_PRESERVE=2 +}; + +#ifdef RASM_THREAD +struct s_rasm_thread { + pthread_t thread; + int lz; + unsigned char *datain; + int datalen; + unsigned char *dataout; + int lenout; + int status; +}; +#endif + + +/********************************************************* + S N A P S H O T E X P O R T +*********************************************************/ +/* extension 4Mo = 256 slots + 4 slots 64K de RAM par défaut => 260 */ + +#define BANK_MAX_NUMBER 260 + +struct s_snapshot_symbol { + unsigned char size; + unsigned char name[256]; + unsigned char reserved[6]; + unsigned char bigendian_adress[2]; +}; + + +struct s_zxsnapshot { + + unsigned int run; + unsigned int stack; +}; + +struct s_snapshot { + char idmark[8]; + char unused1[8]; + unsigned char version; /* 3 */ + struct { + struct { + unsigned char F; + unsigned char A; + unsigned char C; + unsigned char B; + unsigned char E; + unsigned char D; + unsigned char L; + unsigned char H; + }general; + unsigned char R; + unsigned char regI; /* I incompatible with tgmath.h */ + unsigned char IFF0; + unsigned char IFF1; + unsigned char LX; + unsigned char HX; + unsigned char LY; + unsigned char HY; + unsigned char LSP; + unsigned char HSP; + unsigned char LPC; + unsigned char HPC; + unsigned char IM; /* 0,1,2 */ + struct { + unsigned char F; + unsigned char A; + unsigned char C; + unsigned char B; + unsigned char E; + unsigned char D; + unsigned char L; + unsigned char H; + }alternate; + }registers; + + struct { + unsigned char selectedpen; + unsigned char palette[17]; + unsigned char multiconfiguration; + }gatearray; + unsigned char ramconfiguration; + struct { + unsigned char selectedregister; + unsigned char registervalue[18]; + }crtc; + unsigned char romselect; + struct { + unsigned char portA; + unsigned char portB; + unsigned char portC; + unsigned char control; + }ppi; + struct { + unsigned char selectedregister; + unsigned char registervalue[16]; + }psg; + unsigned char dumpsize[2]; /* 64 then use extended memory chunks */ + + unsigned char CPCType; /* 0=464 / 1=664 / 2=6128 / 4=6128+ / 5=464+ / 6=GX4000 */ + unsigned char interruptnumber; + unsigned char multimodebytes[6]; + unsigned char unused2[0x9C-0x75]; + + /* offset #9C */ + struct { + unsigned char motorstate; + unsigned char physicaltrack; + }fdd; + unsigned char unused3[3]; + unsigned char printerstrobe; + unsigned char unused4[2]; + struct { + unsigned char model; /* 0->4 */ + unsigned char unused5[4]; + unsigned char HCC; + unsigned char unused; + unsigned char CLC; + unsigned char RLC; + unsigned char VTC; + unsigned char HSC; + unsigned char VSC; + unsigned short int flags; + }crtcstate; + unsigned char vsyncdelay; + unsigned char interruptscanlinecounter; + unsigned char interruptrequestflag; + unsigned char unused6[0xFF-0xB5+1]; +}; + +struct s_snapshot_chunks { + unsigned char chunkname[4]; /* MEM1 -> MEM8 */ + unsigned int chunksize; +}; + +struct s_breakpoint { + int address; + int bank; +}; + + +/********************************* + S T R U C T U R E S +*********************************/ +enum e_rasmstructfieldtype { +E_RASMSTRUCTFIELD_BYTE, +E_RASMSTRUCTFIELD_WORD, +E_RASMSTRUCTFIELD_LONG, +E_RASMSTRUCTFIELD_REAL, +E_RASMSTRUCTFIELD_END +}; +struct s_rasmstructfield { + char *fullname; + char *name; + int offset; + int size; + int crc; + /* filler */ + unsigned char *data; + int idata,mdata; + enum e_rasmstructfieldtype zetype; +}; + +struct s_rasmstruct { + char *name; + int crc; + int size; + int ptr; + int nbelem; + /* fields */ + struct s_rasmstructfield *rasmstructfield; + int irasmstructfield,mrasmstructfield; +}; + +/********************************* + D E B U G +*********************************/ +struct s_debug_error { + char *filename; + int line; + char *msg; + int lenmsg,lenfilename; +}; +struct s_debug_symbol { + char *name; + int v; +}; +struct s_rasm_info { + struct s_debug_error *error; + int nberror,maxerror; + struct s_debug_symbol *symbol; + int nbsymbol,maxsymbol; +}; + +/******************************************* + G L O B A L S T R U C T +*******************************************/ +struct s_assenv { + /* current memory */ + int maxptr; + /* CPR memory */ + unsigned char **mem; + int iwnamebank[BANK_MAX_NUMBER]; + int nbbank,maxbank; + int forcetape,forcezx,forcecpr,bankmode,activebank,amsdos,forcesnapshot,packedbank; + struct s_snapshot snapshot; + struct s_zxsnapshot zxsnapshot; + int bankset[BANK_MAX_NUMBER>>2]; /* 64K selected flag */ + int bankused[BANK_MAX_NUMBER]; /* 16K selected flag */ + int bankgate[BANK_MAX_NUMBER+1]; + int setgate[BANK_MAX_NUMBER+1]; + int rundefined; + /* parsing */ + struct s_wordlist *wl; + int nbword; + int idx,stage; + char *label_filename; + int label_line; + char **filename; + int ifile,maxfile; + int nberr,flux; + int fastmatch[256]; + unsigned char charset[256]; + int maxerr,extended_error,nowarning; + /* ORG tracking */ + int codeadr,outputadr,nocode; + int codeadrbackup,outputadrbackup; + int minadr,maxadr; + struct s_orgzone *orgzone; + int io,mo; + /* Struct */ + struct s_rasmstruct *rasmstruct; + int irasmstruct,mrasmstruct; + int getstruct; + int backup_outputadr,backup_codeadr; + char *backup_filename; + int backup_line; + struct s_rasmstruct *rasmstructalias; + int irasmstructalias,mrasmstructalias; + /* expressions */ + struct s_expression *expression; + int ie,me; + int maxam,as80,dams; + float rough; + struct s_compute_core_data *computectx,ctx1,ctx2; + struct s_crcstring_tree stringtree; + /* label */ + struct s_label *label; + int il,ml; + struct s_crclabel_tree labeltree; /* fast label access */ + char *module; + int modulen; + struct s_breakpoint *breakpoint; + int ibreakpoint,maxbreakpoint; + char *lastgloballabel; + char *lastsuperglobal; + int lastgloballabellen, lastglobalalloc; + /* repeat */ + struct s_repeat *repeat; + int ir,mr; + /* while/wend */ + struct s_whilewend *whilewend; + int iw,mw; + /* if/then/else */ + //int *ifthen; + struct s_ifthen *ifthen; + int ii,mi; + /* switch/case */ + struct s_switchcase *switchcase; + int isw,msw; + /* expression dictionnary */ + struct s_expr_dico *dico; + int idic,mdic; + struct s_crcdico_tree dicotree; /* fast dico access */ + struct s_crcused_tree usedtree; /* fast used access */ + /* ticker */ + struct s_ticker *ticker; + int iticker,mticker; + long tick,nop; + /* crunch section flag */ + struct s_lz_section *lzsection; + int ilz,mlz; + int lz,curlz; + /* macro */ + struct s_macro *macro; + int imacro,mmacro; + int macrovoid; + /* labels locaux */ + int repeatcounter,whilecounter,macrocounter; + struct s_macro_position *macropos; + int imacropos,mmacropos; + /* alias */ + struct s_alias *alias; + int ialias,malias; + /* hexbin */ + struct s_rasm_thread **rasm_thread; + int irt,mrt; + struct s_hexbin *hexbin; + int ih,mh; + char **includepath; + int ipath,mpath; + /* automates */ + char AutomateExpressionValidCharExtended[256]; + char AutomateExpressionValidCharFirst[256]; + char AutomateExpressionValidChar[256]; + char AutomateExpressionDecision[256]; + char AutomateValidLabelFirst[256]; + char AutomateValidLabel[256]; + char AutomateDigit[256]; + char AutomateHexa[256]; + struct s_compute_element AutomateElement[256]; + unsigned char psgtab[256]; + unsigned char psgfine[256]; + /* output */ + char *outputfilename; + int export_sym,export_local,export_multisym; + int export_var,export_equ; + int export_sna,export_snabrk; + int export_brk,export_tape; + int autorise_export; + char *flexible_export; + char *breakpoint_name; + char *symbol_name; + char *binary_name; + char *cartridge_name; + char *snapshot_name; + struct s_save *save; + int nbsave,maxsave; + int current_run_idx; + struct s_edsk_wrapper *edsk_wrapper; + int nbedskwrapper,maxedskwrapper; + int edskoverwrite; + int checkmode,dependencies; + int stop; + int warn_unused; + /* debug */ + struct s_rasm_info debug; + struct s_rasm_info **retdebug; + int debug_total_len; +}; + +/************************************* + D I R E C T I V E S +*************************************/ +struct s_asm_keyword { + char *mnemo; + int crc; + void (*makemnemo)(struct s_assenv *ae); +}; + +struct s_math_keyword math_keyword[]={ +{"SIN",0,E_COMPUTE_OPERATION_SIN}, +{"COS",0,E_COMPUTE_OPERATION_COS}, +{"INT",0,E_COMPUTE_OPERATION_INT}, +{"ABS",0,E_COMPUTE_OPERATION_ABS}, +{"LN",0,E_COMPUTE_OPERATION_LN}, +{"LOG10",0,E_COMPUTE_OPERATION_LOG10}, +{"SQRT",0,E_COMPUTE_OPERATION_SQRT}, +{"FLOOR",0,E_COMPUTE_OPERATION_FLOOR}, +{"ASIN",0,E_COMPUTE_OPERATION_ASIN}, +{"ACOS",0,E_COMPUTE_OPERATION_ACOS}, +{"ATAN",0,E_COMPUTE_OPERATION_ATAN}, +{"EXP",0,E_COMPUTE_OPERATION_EXP}, +{"LO",0,E_COMPUTE_OPERATION_LOW}, +{"HI",0,E_COMPUTE_OPERATION_HIGH}, +{"PSGVALUE",0,E_COMPUTE_OPERATION_PSG}, +{"RND",0,E_COMPUTE_OPERATION_RND}, +{"FRAC",0,E_COMPUTE_OPERATION_FRAC}, +{"CEIL",0,E_COMPUTE_OPERATION_CEIL}, +{"GETR",0,E_COMPUTE_OPERATION_GET_R}, +{"GETV",0,E_COMPUTE_OPERATION_GET_V}, +{"GETG",0,E_COMPUTE_OPERATION_GET_V}, +{"GETB",0,E_COMPUTE_OPERATION_GET_B}, +{"SETR",0,E_COMPUTE_OPERATION_SET_R}, +{"SETV",0,E_COMPUTE_OPERATION_SET_V}, +{"SETG",0,E_COMPUTE_OPERATION_SET_V}, +{"SETB",0,E_COMPUTE_OPERATION_SET_B}, +{"",0,-1} +}; + +#define CRC_SWITCH 0x01AEDE4A +#define CRC_CASE 0x0826B794 +#define CRC_DEFAULT 0x9A0DAC7D +#define CRC_BREAK 0xCD364DDD +#define CRC_ENDSWITCH 0x18E9FB21 + +#define CRC_ELSEIF 0xE175E230 +#define CRC_ELSE 0x3FF177A1 +#define CRC_ENDIF 0xCD5265DE +#define CRC_IF 0x4BD52507 +#define CRC_IFDEF 0x4CB29DD6 +#define CRC_UNDEF 0xCCD2FDEA +#define CRC_IFNDEF 0xD9AD0824 +#define CRC_IFNOT 0x4CCAC9F8 +#define CRC_WHILE 0xBC268FF1 +#define CRC_UNTIL 0xCC12A604 +#define CRC_MEND 0xFFFD899C +#define CRC_ENDM 0x3FF9559C +#define CRC_MACRO 0x64AA85EA +#define CRC_IFUSED 0x91752638 +#define CRC_IFNUSED 0x1B39A886 + +#define CRC_SIN 0xE1B71962 +#define CRC_COS 0xE077C55D + +#define CRC_0 0x7A98A6A8 +#define CRC_1 0x7A98A6A9 +#define CRC_2 0x7A98A6AA + + +#define CRC_NC 0x4BD52B09 +#define CRC_Z 0x7A98A6D2 +#define CRC_NZ 0x4BD52B20 +#define CRC_P 0x7A98A6C8 +#define CRC_PO 0x4BD53717 +#define CRC_PE 0x4BD5370D +#define CRC_M 0x7A98A6C5 + +/* 8 bits registers */ +#define CRC_F 0x7A98A6BE +#define CRC_I 0x7A98A6C1 +#define CRC_R 0x7A98A6CA +#define CRC_A 0x7A98A6B9 +#define CRC_B 0x7A98A6BA +#define CRC_C 0x7A98A6BB +#define CRC_D 0x7A98A6BC +#define CRC_E 0x7A98A6BD +#define CRC_H 0x7A98A6C0 +#define CRC_L 0x7A98A6C4 +/* dual naming */ +#define CRC_XH 0x4BD50718 +#define CRC_XL 0x4BD5071C +#define CRC_YH 0x4BD50519 +#define CRC_YL 0x4BD5051D +#define CRC_HX 0x4BD52718 +#define CRC_LX 0x4BD52F1C +#define CRC_HY 0x4BD52719 +#define CRC_LY 0x4BD52F1D +#define CRC_IXL 0xE19F1765 +#define CRC_IXH 0xE19F1761 +#define CRC_IYL 0xE19F1166 +#define CRC_IYH 0xE19F1162 + +/* 16 bits registers */ +#define CRC_BC 0x4BD5D2FD +#define CRC_DE 0x4BD5DF01 +#define CRC_HL 0x4BD5270C +#define CRC_IX 0x4BD52519 +#define CRC_IY 0x4BD5251A +#define CRC_SP 0x4BD5311B +#define CRC_AF 0x4BD5D4FF +/* memory convention */ +#define CRC_MHL 0xD0765F5D +#define CRC_MDE 0xD0467D52 +#define CRC_MBC 0xD05E694E +#define CRC_MIX 0xD072B76A +#define CRC_MIY 0xD072B16B +#define CRC_MSP 0xD01A876C +#define CRC_MC 0xE018210C +/* struct parsing */ +#define CRC_DEFB 0x37D15389 +#define CRC_DB 0x4BD5DEFE +#define CRC_DEFW 0x37D1539E +#define CRC_DW 0x4BD5DF13 +#define CRC_DEFI 0x37D15390 +#define CRC_DEFS 0x37D1539A +#define CRC_DS 0x4BD5DF0F +#define CRC_DEFR 0x37D15399 +#define CRC_DR 0x4BD5DF0E + +/* struct declaration use special instructions for defines */ +int ICRC_DEFB,ICRC_DEFW,ICRC_DEFI,ICRC_DEFR,ICRC_DEFS,ICRC_DB,ICRC_DW,ICRC_DR,ICRC_DS; +/* need to pre-declare var */ +extern struct s_asm_keyword instruction[]; + +/* +# base=16 +% base=2 +0-9 base=10 +A-Z variable ou fonction (cos, sin, tan, sqr, pow, mod, and, xor, mod, ...) ++*-/&^m| operateur +*/ + +#define AutomateExpressionValidCharExtendedDefinition "0123456789.ABCDEFGHIJKLMNOPQRSTUVWXYZ_{}@+-*/~^$#%§<=>|&" +#define AutomateExpressionValidCharFirstDefinition "#%0123456789.ABCDEFGHIJKLMNOPQRSTUVWXYZ_@${" +#define AutomateExpressionValidCharDefinition "0123456789.ABCDEFGHIJKLMNOPQRSTUVWXYZ_{}@$" +#define AutomateValidLabelFirstDefinition ".ABCDEFGHIJKLMNOPQRSTUVWXYZ_@" +#define AutomateValidLabelDefinition "0123456789.ABCDEFGHIJKLMNOPQRSTUVWXYZ_@{}" +#define AutomateDigitDefinition ".0123456789" +#define AutomateHexaDefinition "0123456789ABCDEF" + +#ifndef NO_3RD_PARTIES +unsigned char *LZ4_crunch(unsigned char *data, int zelen, int *retlen){ + unsigned char *lzdest=NULL; + lzdest=MemMalloc(65536); + *retlen=LZ4_compress_HC((char*)data,(char*)lzdest,zelen,65536,9); + return lzdest; +} +#endif +unsigned char *LZ48_encode_legacy(unsigned char *data, int length, int *retlength); +#define LZ48_crunch LZ48_encode_legacy +unsigned char *LZ49_encode_legacy(unsigned char *data, int length, int *retlength); +#define LZ49_crunch LZ49_encode_legacy + + +/* + * optimised reading of text file in one shot + */ +unsigned char *_internal_readbinaryfile(char *filename, int *filelength) +{ + #undef FUNC + #define FUNC "_internal_readbinaryfile" + + unsigned char *binary_data=NULL; + + *filelength=FileGetSize(filename); + binary_data=MemMalloc((*filelength)+1); + /* we try to read one byte more to close the file just after the read func */ + if (FileReadBinary(filename,(char*)binary_data,(*filelength)+1)!=*filelength) { + logerr("Cannot fully read %s",filename); + exit(INTERNAL_ERROR); + } + return binary_data; +} +char **_internal_readtextfile(char *filename, char replacechar) +{ + #undef FUNC + #define FUNC "_internal_readtextfile" + + char **lines_buffer=NULL; + unsigned char *bigbuffer; + int nb_lines=0,max_lines=0,i=0,e=0; + int file_size; + + bigbuffer=_internal_readbinaryfile(filename,&file_size); + + while (i<file_size) { + while (e<file_size && bigbuffer[e]!=0x0A) { + /* Windows de meeeeeeeerrrdde... */ + if (bigbuffer[e]==0x0D) bigbuffer[e]=replacechar; + e++; + } + if (e<file_size) e++; + if (nb_lines>=max_lines) { + max_lines=max_lines*2+10; + lines_buffer=MemRealloc(lines_buffer,(max_lines+1)*sizeof(char **)); + } + lines_buffer[nb_lines]=MemMalloc(e-i+1); + memcpy(lines_buffer[nb_lines],bigbuffer+i,e-i); + lines_buffer[nb_lines][e-i]=0; + if (0) + { + int yy; + for (yy=0;lines_buffer[nb_lines][yy];yy++) { + if (lines_buffer[nb_lines][yy]>31) printf("%c",lines_buffer[nb_lines][yy]); else printf("(0x%X)",lines_buffer[nb_lines][yy]); + } + printf("\n"); + } + nb_lines++; + i=e; + } + if (!max_lines) { + lines_buffer=MemMalloc(sizeof(char**)); + lines_buffer[0]=NULL; + } else { + lines_buffer[nb_lines]=NULL; + } + MemFree(bigbuffer); + return lines_buffer; +} + +#define FileReadLines(filename) _internal_readtextfile(filename,':') +#define FileReadLinesRAW(filename) _internal_readtextfile(filename,0x0D) +#define FileReadContent(filename,filesize) _internal_readbinaryfile(filename,filesize) + + +/*** + TxtReplace + + input: + in_str: string where replace will occur + in_substr: substring to look for + out_substr: replace substring + recurse: loop until no in_substr is found + + note: in_str MUST BE previously mallocated if out_substr is bigger than in_substr +*/ +#ifndef RDD +char *TxtReplace(char *in_str, char *in_substr, char *out_substr, int recurse) +{ + #undef FUNC + #define FUNC "TxtReplace" + + char *str_look,*m1,*m2; + char *out_str; + int sl,l1,l2,dif,cpt; + + if (in_str==NULL) + return NULL; + + sl=strlen(in_str); + l1=strlen(in_substr); + /* empty string, nothing to do except return empty string */ + if (!sl || !l1) + return in_str; + + l2=strlen(out_substr); + dif=l2-l1; + + /* replace string is small or equal in size, we dont realloc */ + if (dif<=0) + { + /* we loop while there is a replace to do */ + str_look=strstr(in_str,in_substr); + while (str_look!=NULL) + { + /* we copy the new string if his len is not null */ + if (l2) + memcpy(str_look,out_substr,l2); + /* only if len are different */ + if (l1!=l2) + { + /* we move the end of the string byte per byte + because memory locations overlap. This is + faster than memmove */ + m1=str_look+l1; + m2=str_look+l2; + while (*m1!=0) + { + *m2=*m1; + m1++;m2++; + } + /* we must copy the EOL */ + *m2=*m1; + } + /* look for next replace */ + if (!recurse) + str_look=strstr(str_look+l2,in_substr); + else + str_look=strstr(in_str,in_substr); + } + out_str=in_str; + } + else + { + /* we need to count each replace */ + cpt=0; + str_look=strstr(in_str,in_substr); + while (str_look!=NULL) + { + cpt++; + str_look=strstr(str_look+l1,in_substr); + } + /* is there anything to do? */ + if (cpt) + { + /* we realloc to a size that will fit all replaces */ + out_str=MemRealloc(in_str,sl+1+dif*cpt); + str_look=strstr(out_str,in_substr); + while (str_look!=NULL && cpt) + { + /* as the replace string is bigger we + have to move memory first from the end */ + m1=out_str+sl; + m2=m1+dif; + sl+=dif; + while (m1!=str_look+l1-dif) + { + *m2=*m1; + m1--;m2--; + } + /* then we copy the replace string (can't be NULL in this case) */ + memcpy(str_look,out_substr,l2); + + /* look for next replace */ + if (!recurse) + str_look=strstr(str_look+l2,in_substr); + else + str_look=strstr(in_str,in_substr); + + /* to prevent from naughty overlap */ + cpt--; + } + if (str_look!=NULL) + { + printf("INTERNAL ERROR - overlapping replace string (%s/%s), you can't use this one!\n",in_substr,out_substr); + exit(ABORT_ERROR); + } + } + else + out_str=in_str; + } + return out_str; +} +#endif + +#ifndef min +#define min(a,b) \ + ({ __typeof__ (a) _a = (a); \ + __typeof__ (b) _b = (b); \ + _a < _b ? _a : _b; }) +#endif + +/* Levenshtein implementation by TheRayTracer https://gist.github.com/TheRayTracer/2644387 */ +int _internal_LevenshteinDistance(char *s, char *t) +{ + int i,j,n,m,*d; + int im,jn; + int r; + + n=strlen(s)+1; + m=strlen(t)+1; + d=malloc(n*m*sizeof(int)); + memset(d, 0, sizeof(int) * n * m); + + for (i = 1, im = 0; i < m; i++, im++) + { + for (j = 1, jn = 0; j < n; j++, jn++) + { + if (s[jn] == t[im]) + { + d[(i * n) + j] = d[((i - 1) * n) + (j - 1)]; + } + else + { + d[(i * n) + j] = min(d[(i - 1) * n + j] + 1, /* A deletion. */ + min(d[i * n + (j - 1)] + 1, /* An insertion. */ + d[(i - 1) * n + (j - 1)] + 1)); /* A substitution. */ + } + } + } + r = d[n * m - 1]; + free(d); + return r; +} + +#ifdef RASM_THREAD +/* + threads used for crunching +*/ +void _internal_ExecuteThreads(struct s_assenv *ae,struct s_rasm_thread *rasm_thread, void *(*fct)(void *)) +{ + #undef FUNC + #define FUNC "_internal_ExecuteThreads" + + pthread_attr_t attr; + void *status; + int rc; + /* launch threads */ + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_JOINABLE); + pthread_attr_setstacksize(&attr,65536); + + if ((rc=pthread_create(&image_threads[i].thread,&attr,fct,(void *)rasm_thread))) { + rasm_printf(ae,"FATAL ERROR - Cannot create thread!\n"); + exit(INTERNAL_ERROR); + } +} +void _internal_WaitForThreads(struct s_assenv *ae,struct s_rasm_thread *rasm_thread) +{ + #undef FUNC + #define FUNC "_internal_WaitForThreads" + int rc; + + if ((rc=pthread_join(rasm_thread->thread,&status))) { + rasm_printf(ae,"FATAL ERROR - Cannot wait for thread\n"); + exit(INTERNAL_ERROR); + } +} +void PushCrunchedFile(struct s_assenv *ae, unsigned char *datain, int datalen, int lz) +{ + #undef FUNC + #define FUNC "PushCrunchedFile" + + struct s_rasm_thread *rasm_thread; + + rasm_thread=MemMalloc(sizeof(struct s_rasm_thread)); + memset(rasm_thread,0,sizeof(struct s_rasm_thread)); + rasm_thread->datain=datain; + rasm_thread->datalen=datalen; + rasm_thread->lz=lz; + _internal_ExecuteThreads(ae,rasm_thread, void *(*fct)(void *)); + ObjectArrayAddDynamicValueConcat((void**)&ae->rasm_thread,&ae->irt,&ae->mrt,&rasm_thread,sizeof(struct s_rasm_thread *)); +} +void PopAllCrunchedFiles(struct s_assenv *ae) +{ + #undef FUNC + #define FUNC "PopAllCrunchedFiles" + + int i; + for (i=0;i<ae->irt;i++) { + _internal_WaitForThreads(ae,ae->rasm_thread[i]); + } +} +#endif + +void MaxError(struct s_assenv *ae); + +void rasm_printf(struct s_assenv *ae, ...) { + #undef FUNC + #define FUNC "(internal) rasm_printf" + + char *format; + va_list argptr; + + if (!ae->flux && !ae->dependencies) { + va_start(argptr,ae); + format=va_arg(argptr,char *); + vfprintf(stdout,format,argptr); + va_end(argptr); + fprintf(stdout,KNORMAL); + } +} +/*** + build the string of current line for error messages +*/ +char *rasm_getline(struct s_assenv *ae, int offset) { + #undef FUNC + #define FUNC "rasm_getline" + + static char myline[40]={0}; + int idx=0,icopy,first=1; + + while (!ae->wl[ae->idx+offset].t && idx<32) { + for (icopy=0;idx<32 && ae->wl[ae->idx+offset].w[icopy];icopy++) { + myline[idx++]=ae->wl[ae->idx+offset].w[icopy]; + } + if (!first) myline[idx++]=','; else first=0; + offset++; + } + if (idx>=32) { + strcpy(myline+29,"..."); + } else { + myline[idx++]=0; + } + + return myline; +} + +char *SimplifyPath(char *filename) { + #undef FUNC + #define FUNC "SimplifyPath" + + return filename; +#if 0 + char *pos,*repos; + int i,len; + + char *rpath; + + rpath=realpath(filename,NULL); + if (!rpath) { + printf("rpath error!\n"); + switch (errno) { + case EACCES:printf("read permission failure\n");break; + case EINVAL:printf("wrong argument\n");break; + case EIO:printf("I/O error\n");break; + case ELOOP:printf("too many symbolic links\n");break; + case ENAMETOOLONG:printf("names too long\n");break; + case ENOENT:printf("names does not exists\n");break; + case ENOMEM:printf("out of memory\n");break; + case ENOTDIR:printf("a component of the path is not a directory\n");break; + default:printf("unknown error\n");break; + } + exit(1); + } + if (strlen(rpath)<strlen(filename)) { + strcpy(filename,rpath); + } + free(rpath); + return filename; + +#ifdef OS_WIN + while ((pos=strstr(filename,"\\..\\"))!=NULL) { + repos=pos-1; + /* sequence found, looking back for '\' */ + while (repos>=filename) { + if (*repos=='\\') { + break; + } + repos--; + } + repos++; + if (repos>=filename && repos!=pos) { + len=strlen(pos)-4+1; + pos+=4; + for (i=0;i<len;i++) { + *repos=*pos; + repos++; + pos++; + } + } + if (strncmp(filename,".\\..\\",5)==0) { + repos=filename; + pos=repos+2; + for (;*repos;pos++,repos++) { + *repos=*pos; + } + *repos=0; + } + } +#else +printf("*************\nfilename=[%s]\n",filename); + while ((pos=strstr(filename,"/../"))!=NULL) { + repos=pos-1; + while (repos>=filename) { + if (*repos=='/') { + break; + } + repos--; + } + repos++; + if (repos>=filename && repos!=pos) { + len=strlen(pos)-4+1; + pos+=4; + for (i=0;i<len;i++) { + *repos=*pos; + repos++; + pos++; + } + } +printf("filename=[%s]\n",filename); + if (strncmp(filename,"./../",5)==0) { + repos=filename; + pos=repos+2; + for (;*repos;pos++,repos++) { + *repos=*pos; + } + *repos=0; + } +printf("filename=[%s]\n",filename); + } +#endif + return NULL; +#endif + +} + +char *GetPath(char *filename) { + #undef FUNC + #define FUNC "GetPath" + + static char curpath[PATH_MAX]; + int zelen,idx; + + zelen=strlen(filename); + +#ifdef OS_WIN + #define CURRENT_DIR ".\\" + + TxtReplace(filename,"/","\\",1); + idx=zelen-1; + while (idx>=0 && filename[idx]!='\\') idx--; + if (idx<0) { + /* pas de chemin */ + strcpy(curpath,".\\"); + } else { + /* chemin trouve */ + strcpy(curpath,filename); + curpath[idx+1]=0; + } +#else +#ifdef __MORPHOS__ + #define CURRENT_DIR "" +#else + #define CURRENT_DIR "./" +#endif + idx=zelen-1; + while (idx>=0 && filename[idx]!='/') idx--; + if (idx<0) { + /* pas de chemin */ + strcpy(curpath,CURRENT_DIR); + } else { + /* chemin trouve */ + strcpy(curpath,filename); + curpath[idx+1]=0; + } +#endif + + return curpath; +} +char *MergePath(struct s_assenv *ae,char *dadfilename, char *filename) { + #undef FUNC + #define FUNC "MergePath" + + static char curpath[PATH_MAX]; + int zelen; + +#ifdef OS_WIN + TxtReplace(filename,"/","\\",1); + + if (filename[0] && filename[1]==':' && filename[2]=='\\') { + /* chemin absolu */ + strcpy(curpath,filename); + } else if (filename[0] && filename[1]==':') { + rasm_printf(ae,KERROR"unsupported path style [%s]\n",filename); + exit(-111); + } else { + if (filename[0]=='.' && filename[1]=='\\') { + strcpy(curpath,GetPath(dadfilename)); + strcat(curpath,filename+2); + } else { + strcpy(curpath,GetPath(dadfilename)); + strcat(curpath,filename); + } + } +#else + if (filename[0]=='/') { + /* chemin absolu */ + strcpy(curpath,filename); + } else if (filename[0]=='.' && filename[1]=='/') { + strcpy(curpath,GetPath(dadfilename)); + strcat(curpath,filename+2); + } else { + strcpy(curpath,GetPath(dadfilename)); + strcat(curpath,filename); + } +#endif + + return curpath; +} + + +void InitAutomate(char *autotab, const unsigned char *def) +{ + #undef FUNC + #define FUNC "InitAutomate" + + int i; + + memset(autotab,0,256); + for (i=0;def[i];i++) { + autotab[(int)def[i]]=1; + } +} +void StateMachineResizeBuffer(char **ABuf, int idx, int *ASize) { + #undef FUNC + #define FUNC "StateMachineResizeBuffer" + + if (idx>=*ASize) { + if (*ASize<16384) { + *ASize=(*ASize)*2; + } else { + *ASize=(*ASize)+16384; + } + *ABuf=MemRealloc(*ABuf,(*ASize)+2); + } +} + +int GetCRC(char *label) +{ + #undef FUNC + #define FUNC "GetCRC" + int crc=0x12345678; + int i=0; + + while (label[i]!=0) { + crc=(crc<<9)^(crc+label[i++]); + } + return crc; +} + +int IsRegister(char *zeexpression) +{ + #undef FUNC + #define FUNC "IsRegister" + + switch (GetCRC(zeexpression)) { + case CRC_F:if (strcmp(zeexpression,"F")==0) return 1; else return 0; + case CRC_I:if (strcmp(zeexpression,"I")==0) return 1; else return 0; + case CRC_R:if (strcmp(zeexpression,"R")==0) return 1; else return 0; + case CRC_A:if (strcmp(zeexpression,"A")==0) return 1; else return 0; + case CRC_B:if (strcmp(zeexpression,"B")==0) return 1; else return 0; + case CRC_C:if (strcmp(zeexpression,"C")==0) return 1; else return 0; + case CRC_D:if (strcmp(zeexpression,"D")==0) return 1; else return 0; + case CRC_E:if (strcmp(zeexpression,"E")==0) return 1; else return 0; + case CRC_H:if (strcmp(zeexpression,"H")==0) return 1; else return 0; + case CRC_L:if (strcmp(zeexpression,"L")==0) return 1; else return 0; + case CRC_BC:if (strcmp(zeexpression,"BC")==0) return 1; else return 0; + case CRC_DE:if (strcmp(zeexpression,"DE")==0) return 1; else return 0; + case CRC_HL:if (strcmp(zeexpression,"HL")==0) return 1; else return 0; + case CRC_IX:if (strcmp(zeexpression,"IX")==0) return 1; else return 0; + case CRC_IY:if (strcmp(zeexpression,"IY")==0) return 1; else return 0; + case CRC_SP:if (strcmp(zeexpression,"SP")==0) return 1; else return 0; + case CRC_AF:if (strcmp(zeexpression,"AF")==0) return 1; else return 0; + case CRC_XH:if (strcmp(zeexpression,"XH")==0) return 1; else return 0; + case CRC_XL:if (strcmp(zeexpression,"XL")==0) return 1; else return 0; + case CRC_YH:if (strcmp(zeexpression,"YH")==0) return 1; else return 0; + case CRC_YL:if (strcmp(zeexpression,"YL")==0) return 1; else return 0; + case CRC_HX:if (strcmp(zeexpression,"HX")==0) return 1; else return 0; + case CRC_LX:if (strcmp(zeexpression,"LX")==0) return 1; else return 0; + case CRC_HY:if (strcmp(zeexpression,"HY")==0) return 1; else return 0; + case CRC_LY:if (strcmp(zeexpression,"LY")==0) return 1; else return 0; + case CRC_IXL:if (strcmp(zeexpression,"IXL")==0) return 1; else return 0; + case CRC_IXH:if (strcmp(zeexpression,"IXH")==0) return 1; else return 0; + case CRC_IYL:if (strcmp(zeexpression,"IYL")==0) return 1; else return 0; + case CRC_IYH:if (strcmp(zeexpression,"IYH")==0) return 1; else return 0; + default:break; + } + return 0; +} + +int StringIsMem(char *w) +{ + #undef FUNC + #define FUNC "StringIsMem" + + int p=1,idx=1; + + if (w[0]=='(') { + while (w[idx]) { + switch (w[idx]) { + case '\\':if (w[idx+1]) idx++; + break; + case '\'':if (w[idx+1] && w[idx+1]!='\\') idx++; + break; + case '(':p++;break; + case ')':p--; + if (!p && w[idx+1]) return 0; + break; + default:break; + } + idx++; + } + if (w[idx-1]!=')') return 0; + } else { + return 0; + } + return 1; + +} +int StringIsQuote(char *w) +{ + #undef FUNC + #define FUNC "StringIsQuote" + + int i,tquote,lens; + + if (w[0]=='\'' || w[0]=='"') { + tquote=w[0]; + lens=strlen(w); + + /* est-ce bien une chaine et uniquement une chaine? */ + i=1; + while (w[i] && w[i]!=tquote) { + if (w[i]=='\\') i++; + i++; + } + if (i==lens-1) { + return tquote; + } + } + return 0; +} +char *StringLooksLikeDicoRecurse(struct s_crcdico_tree *lt, int *score, char *str) +{ + #undef FUNC + #define FUNC "StringLooksLikeDicoRecurse" + + char *retstr=NULL,*tmpstr; + int i,curs; + + for (i=0;i<256;i++) { + if (lt->radix[i]) { + tmpstr=StringLooksLikeDicoRecurse(lt->radix[i],score,str); + if (tmpstr!=NULL) retstr=tmpstr; + } + } + if (lt->mdico) { + for (i=0;i<lt->ndico;i++) { + if (strlen(lt->dico[i].name)>4) { + curs=_internal_LevenshteinDistance(str,lt->dico[i].name); + if (curs<*score) { + *score=curs; + retstr=lt->dico[i].name; + } + } + } + } + return retstr; +} +char *StringLooksLikeDico(struct s_assenv *ae, int *score, char *str) +{ + #undef FUNC + #define FUNC "StringLooksLikeDico" + + char *retstr=NULL,*tmpstr; + int i; + + for (i=0;i<256;i++) { + if (ae->dicotree.radix[i]) { + tmpstr=StringLooksLikeDicoRecurse(ae->dicotree.radix[i],score,str); + if (tmpstr!=NULL) retstr=tmpstr; + } + } + return retstr; +} +char *StringLooksLikeMacro(struct s_assenv *ae, char *str, int *retscore) +{ + #undef FUNC + #define FUNC "StringLooksLikeMacro" + + char *ret=NULL; + int i,curs,score=3; + /* search in macros */ + for (i=0;i<ae->imacro;i++) { + curs=_internal_LevenshteinDistance(ae->macro[i].mnemo,str); + if (curs<score) { + score=curs; + ret=ae->macro[i].mnemo; + } + } + if (retscore) *retscore=score; + return ret; +} + +char *StringLooksLike(struct s_assenv *ae, char *str) +{ + #undef FUNC + #define FUNC "StringLooksLike" + + char *ret=NULL,*tmpret; + int i,curs,score=4; + + /* search in variables */ + ret=StringLooksLikeDico(ae,&score,str); + + /* search in labels */ + for (i=0;i<ae->il;i++) { + if (!ae->label[i].name && strlen(ae->wl[ae->label[i].iw].w)>4) { + curs=_internal_LevenshteinDistance(ae->wl[ae->label[i].iw].w,str); + if (curs<score) { + score=curs; + ret=ae->wl[ae->label[i].iw].w; + } + } + } + + /* search in alias */ + for (i=0;i<ae->ialias;i++) { + if (strlen(ae->alias[i].alias)>4) { + curs=_internal_LevenshteinDistance(ae->alias[i].alias,str); + if (curs<score) { + score=curs; + ret=ae->alias[i].alias; + } + } + } + + tmpret=StringLooksLikeMacro(ae,str,&curs); + if (curs<score) { + score=curs; + ret=tmpret; + } + return ret; +} + +int RoundComputeExpression(struct s_assenv *ae,char *expr, int ptr, int didx, int expression_expected); +int RoundComputeExpressionCore(struct s_assenv *ae,char *zeexpression,int ptr,int didx); +double ComputeExpressionCore(struct s_assenv *ae,char *original_zeexpression,int ptr, int didx); +char *GetExpFile(struct s_assenv *ae,int didx); +void __STOP(struct s_assenv *ae); + + +void MakeError(struct s_assenv *ae, char *filename, int line, char *format, ...) +{ + #undef FUNC + #define FUNC "MakeError" + + va_list argptr; + + MaxError(ae); + if (ae->flux) { + /* in embedded Rasm all errors are stored in a debug struct */ + struct s_debug_error curerror; + char toosmalltotakeitall[2]={0}; + int myalloc; + char *errstr; + + va_start(argptr,format); + myalloc=vsnprintf(toosmalltotakeitall,1,format,argptr); + va_end(argptr); + + #if defined(_MSC_VER) && _MSC_VER < 1900 + /* visual studio before 2015 does not fully support C99 */ + if (myalloc<1 && strlen(format)) { + va_start(argptr,format); + myalloc=_vscprintf(format,argptr); + va_end(argptr); + } + #endif + if (myalloc<1) { + /* do not crash */ + return; + } + + va_start(argptr,format); + errstr=MemMalloc(myalloc+1); + vsnprintf(errstr,myalloc,format,argptr); + curerror.msg=errstr; + curerror.lenmsg=myalloc; + curerror.line=line; + if (filename) curerror.filename=TxtStrDupLen(filename,&curerror.lenfilename); else curerror.filename=TxtStrDupLen("<internal>",&curerror.lenfilename); + ObjectArrayAddDynamicValueConcat((void **)&ae->debug.error,&ae->debug.nberror,&ae->debug.maxerror,&curerror,sizeof(struct s_debug_error)); + va_end(argptr); + } else { + fprintf(stdout,KERROR); + va_start(argptr,format); + if (filename && line) { + printf("[%s:%d] ",filename,line); + } else if (filename) { + printf("[%s] ",filename); + } + vfprintf(stdout,format,argptr); + va_end(argptr); + fprintf(stdout,KNORMAL); + } +} + + +/* convert v double value to Amstrad REAL */ +unsigned char *__internal_MakeAmsdosREAL(struct s_assenv *ae, double v, int iexpression) +{ + #undef FUNC + #define FUNC "__internal_MakeAmsdosREAL" + + static unsigned char rc[5]; + + double tmpval; + int j,ib,ibb,exp=0; + unsigned int deci; + int fracmax=0; + double frac; + int mesbits[32]; + int ibit=0; + unsigned int mask; + + memset(rc,0,sizeof(rc)); + + deci=fabs(floor(v)); + frac=fabs(v)-deci; + + if (deci) { + mask=0x80000000; + while (!(deci & mask)) mask=mask/2; + while (mask) { + mesbits[ibit]=!!(deci & mask); +#if TRACE_MAKEAMSDOSREAL +printf("%d",mesbits[ibit]); +#endif + ibit++; + mask=mask/2; + } +#if TRACE_MAKEAMSDOSREAL +printf("\nexposant positif: %d\n",ibit); +#endif + exp=ibit; +#if TRACE_MAKEAMSDOSREAL +printf("."); +#endif + while (ibit<32 && frac!=0) { + frac=frac*2; + if (frac>=1.0) { + mesbits[ibit++]=1; +#if TRACE_MAKEAMSDOSREAL +printf("1"); +#endif + frac-=1.0; + } else { + mesbits[ibit++]=0; +#if TRACE_MAKEAMSDOSREAL +printf("0"); +#endif + } + fracmax++; + } + } else { +#if TRACE_MAKEAMSDOSREAL +printf("\nexposant negatif a definir:\n"); +printf("x."); +#endif + + /* handling zero */ + if (frac==0.0) { + exp=0; + ibit=0; + } else { + /* looking for first significant bit */ + while (1) { + frac=frac*2; + if (frac>=1.0) { + mesbits[ibit++]=1; +#if TRACE_MAKEAMSDOSREAL +printf("1"); +#endif + frac-=1.0; + break; /* first significant bit found, now looking for limit */ + } else { +#if TRACE_MAKEAMSDOSREAL +printf("o"); +#endif + } + fracmax++; + exp--; + } + while (ibit<32 && frac!=0) { + frac=frac*2; + if (frac>=1.0) { + mesbits[ibit++]=1; +#if TRACE_MAKEAMSDOSREAL +printf("1"); +#endif + frac-=1.0; + } else { + mesbits[ibit++]=0; +#if TRACE_MAKEAMSDOSREAL +printf("0"); +#endif + } + fracmax++; + } + } + } + +#if TRACE_MAKEAMSDOSREAL +printf("\n%d bits utilises en mantisse\n",ibit); +#endif + /* pack bits */ + ib=3;ibb=0x80; + for (j=0;j<ibit;j++) { + if (mesbits[j]) rc[ib]|=ibb; + ibb/=2; + if (ibb==0) { + ibb=0x80; + ib--; + } + } + /* exponent */ + exp+=128; + if (exp<0 || exp>255) { + if (iexpression) MakeError(ae,GetExpFile(ae,iexpression),ae->wl[ae->expression[iexpression].iw].l,"Exponent overflow\n"); + else MakeError(ae,GetExpFile(ae,0),ae->wl[ae->idx].l,"Exponent overflow\n"); + exp=128; + } + rc[4]=exp; + + /* REAL sign */ + if (v>=0) { + rc[3]&=0x7F; + } else { + rc[3]|=0x80; + } + +#if TRACE_MAKEAMSDOSREAL + for (j=0;j<5;j++) printf("%02X ",rc[j]); + printf("\n"); +#endif + + return rc; +} + + + + +struct s_label *SearchLabel(struct s_assenv *ae, char *label, int crc); +char *GetExpFile(struct s_assenv *ae,int didx){ + #undef FUNC + #define FUNC "GetExpFile" + + if (ae->label_filename) { + return ae->label_filename; + } + if (didx<0) { + return ae->filename[ae->wl[-didx].ifile]; + } else if (!didx) { + return ae->filename[ae->wl[ae->idx].ifile]; + } else if (ae->expression && didx<ae->ie) { + return ae->filename[ae->wl[ae->expression[didx].iw].ifile]; + } else { + //return ae->filename[ae->wl[ae->idx].ifile]; + return 0; + } +} + +int GetExpLine(struct s_assenv *ae,int didx){ + #undef FUNC + #define FUNC "GetExpLine" + + if (ae->label_line) return ae->label_line; + + if (didx<0) { + return ae->wl[-didx].l; + } else if (!didx) { + return ae->wl[ae->idx].l; + } else if (didx<ae->ie) { + return ae->wl[ae->expression[didx].iw].l; + } else return 0; +} + +char *GetCurrentFile(struct s_assenv *ae) +{ + return GetExpFile(ae,0); +} + + +/******************************************************************************************* + M E M O R Y C L E A N U P +*******************************************************************************************/ +void FreeLabelTree(struct s_assenv *ae); +void FreeDicoTree(struct s_assenv *ae); +void FreeUsedTree(struct s_assenv *ae); +void ExpressionFastTranslate(struct s_assenv *ae, char **ptr_expr, int fullreplace); +char *TradExpression(char *zexp); + + +void _internal_RasmFreeInfoStruct(struct s_rasm_info *debug) +{ + #undef FUNC + #define FUNC "RasmFreeInfoStruct" + + int i; + if (debug->maxerror) { + for (i=0;i<debug->nberror;i++) { + MemFree(debug->error[i].filename); + MemFree(debug->error[i].msg); + } + MemFree(debug->error); + } + if (debug->maxsymbol) { + for (i=0;i<debug->nbsymbol;i++) { + MemFree(debug->symbol[i].name); + } + MemFree(debug->symbol); + } +} + +void RasmFreeInfoStruct(struct s_rasm_info *debug) +{ + _internal_RasmFreeInfoStruct(debug); + MemFree(debug); +} + +void FreeAssenv(struct s_assenv *ae) +{ + #undef FUNC + #define FUNC "FreeAssenv" + int i,j; + +#ifndef RDD + /* let the system free the memory in command line except when debug/dev */ + if (!ae->flux) return; +#endif + /*** debug info ***/ + if (!ae->retdebug) { + _internal_RasmFreeInfoStruct(&ae->debug); + } else { + /* symbols */ + struct s_debug_symbol debug_symbol={0}; + + for (i=0;i<ae->il;i++) { + /* on exporte tout */ + if (!ae->label[i].name) { + /* les labels entiers */ + debug_symbol.name=TxtStrDup(ae->wl[ae->label[i].iw].w); + debug_symbol.v=ae->label[i].ptr; + ObjectArrayAddDynamicValueConcat((void**)&ae->debug.symbol,&ae->debug.nbsymbol,&ae->debug.maxsymbol,&debug_symbol,sizeof(struct s_debug_symbol)); + } else { + /* les labels locaux et générés */ + debug_symbol.name=TxtStrDup(ae->label[i].name); + debug_symbol.v=ae->label[i].ptr; + ObjectArrayAddDynamicValueConcat((void**)&ae->debug.symbol,&ae->debug.nbsymbol,&ae->debug.maxsymbol,&debug_symbol,sizeof(struct s_debug_symbol)); + } + } + for (i=0;i<ae->ialias;i++) { + if (strcmp(ae->alias[i].alias,"IX") && strcmp(ae->alias[i].alias,"IY")) { + debug_symbol.name=TxtStrDup(ae->alias[i].alias); + debug_symbol.v=RoundComputeExpression(ae,ae->alias[i].translation,0,0,0); + ObjectArrayAddDynamicValueConcat((void**)&ae->debug.symbol,&ae->debug.nbsymbol,&ae->debug.maxsymbol,&debug_symbol,sizeof(struct s_debug_symbol)); + } + } + + /* export struct */ + *ae->retdebug=MemMalloc(sizeof(struct s_rasm_info)); + memcpy(*ae->retdebug,&ae->debug,sizeof(struct s_rasm_info)); + } + /*** end debug ***/ + + for (i=0;i<ae->nbbank;i++) { + MemFree(ae->mem[i]); + } + MemFree(ae->mem); + + /* expression core buffer free */ + ComputeExpressionCore(NULL,NULL,0,0); + ExpressionFastTranslate(NULL,NULL,0); + /* free labels, expression, orgzone, repeat, ... */ + if (ae->mo) MemFree(ae->orgzone); + if (ae->me) { + for (i=0;i<ae->ie;i++) if (ae->expression[i].reference) MemFree(ae->expression[i].reference); + MemFree(ae->expression); + } + if (ae->mh) { + for (i=0;i<ae->ih;i++) { + MemFree(ae->hexbin[i].data); + MemFree(ae->hexbin[i].filename); + } + MemFree(ae->hexbin); + } + for (i=0;i<ae->il;i++) { + if (ae->label[i].name && ae->label[i].iw==-1) MemFree(ae->label[i].name); + } + /* structures */ + for (i=0;i<ae->irasmstructalias;i++) { + MemFree(ae->rasmstructalias[i].name); + } + if (ae->mrasmstructalias) MemFree(ae->rasmstructalias); + + for (i=0;i<ae->irasmstruct;i++) { + for (j=0;j<ae->rasmstruct[i].irasmstructfield;j++) { + MemFree(ae->rasmstruct[i].rasmstructfield[j].fullname); + MemFree(ae->rasmstruct[i].rasmstructfield[j].name); + } + if (ae->rasmstruct[i].mrasmstructfield) MemFree(ae->rasmstruct[i].rasmstructfield); + MemFree(ae->rasmstruct[i].name); + } + if (ae->mrasmstruct) MemFree(ae->rasmstruct); + + /* other */ + if (ae->maxbreakpoint) MemFree(ae->breakpoint); + if (ae->ml) MemFree(ae->label); + if (ae->mr) MemFree(ae->repeat); + if (ae->mi) MemFree(ae->ifthen); + if (ae->msw) MemFree(ae->switchcase); + if (ae->mw) MemFree(ae->whilewend); + if (ae->modulen || ae->module) { + MemFree(ae->module); + } + /* deprecated + for (i=0;i<ae->idic;i++) { + MemFree(ae->dico[i].name); + } + if (ae->mdic) MemFree(ae->dico); + */ + if (ae->mlz) MemFree(ae->lzsection); + + for (i=0;i<ae->ifile;i++) { + MemFree(ae->filename[i]); + } + MemFree(ae->filename); + + for (i=0;i<ae->imacro;i++) { + if (ae->macro[i].maxword) MemFree(ae->macro[i].wc); + for (j=0;j<ae->macro[i].nbparam;j++) MemFree(ae->macro[i].param[j]); + if (ae->macro[i].nbparam) MemFree(ae->macro[i].param); + } + + + if (ae->mmacro) MemFree(ae->macro); + + for (i=0;i<ae->ialias;i++) { + MemFree(ae->alias[i].alias); + MemFree(ae->alias[i].translation); + } + if (ae->malias) MemFree(ae->alias); + + for (i=0;ae->wl[i].t!=2;i++) { + MemFree(ae->wl[i].w); + } + MemFree(ae->wl); + + if (ae->ctx1.varbuffer) { + MemFree(ae->ctx1.varbuffer); + } + if (ae->ctx1.maxtokenstack) { + MemFree(ae->ctx1.tokenstack); + } + if (ae->ctx1.maxoperatorstack) { + MemFree(ae->ctx1.operatorstack); + } + if (ae->ctx2.varbuffer) { + MemFree(ae->ctx2.varbuffer); + } + if (ae->ctx2.maxtokenstack) { + MemFree(ae->ctx2.tokenstack); + } + if (ae->ctx2.maxoperatorstack) { + MemFree(ae->ctx2.operatorstack); + } + + for (i=0;i<ae->iticker;i++) { + MemFree(ae->ticker[i].varname); + } + if (ae->mticker) MemFree(ae->ticker); + + MemFree(ae->outputfilename); + FreeLabelTree(ae); + FreeDicoTree(ae); + FreeUsedTree(ae); + if (ae->mmacropos) MemFree(ae->macropos); + TradExpression(NULL); + MemFree(ae); +} + + + +void MaxError(struct s_assenv *ae) +{ + #undef FUNC + #define FUNC "MaxError" + + char **source_lines=NULL; + int idx,crc,zeline; + char c; + + /* extended error is useful with generated code we do not want to edit */ + if (ae->extended_error && ae->wl) { + /* super dupper slow but anyway, there is an error... */ + if (ae->wl[ae->idx].l) { + source_lines=FileReadLinesRAW(GetCurrentFile(ae)); + zeline=0; + while (zeline<ae->wl[ae->idx].l-1 && source_lines[zeline]) zeline++; + if (zeline==ae->wl[ae->idx].l-1 && source_lines[zeline]) { + rasm_printf(ae,KAYGREEN"-> %s",source_lines[zeline]); + } else { + rasm_printf(ae,KERROR"cannot read line %d of file [%s]\n",ae->wl[ae->idx].l,GetCurrentFile(ae)); + } + FreeArrayDynamicValue(&source_lines); + } + } + + ae->nberr++; + if (ae->nberr==ae->maxerr) { + rasm_printf(ae,KERROR"Too many errors!\n"); + FreeAssenv(ae); + exit(ae->nberr); + } +} + +void (*___output)(struct s_assenv *ae, unsigned char v); + +void ___internal_output_disabled(struct s_assenv *ae,unsigned char v) +{ + #undef FUNC + #define FUNC "fake ___output" +} +void ___internal_output(struct s_assenv *ae,unsigned char v) +{ + #undef FUNC + #define FUNC "___output" + + if (ae->outputadr<ae->maxptr) { + ae->mem[ae->activebank][ae->outputadr++]=v; + ae->codeadr++; + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"output exceed limit %d\n",ae->maxptr); + ae->stop=1; + ___output=___internal_output_disabled; + } +} +void ___internal_output_nocode(struct s_assenv *ae,unsigned char v) +{ + #undef FUNC + #define FUNC "___output (nocode)" + + if (ae->outputadr<ae->maxptr) { + /* struct definition always in NOCODE */ + if (ae->getstruct) { + int irs,irsf; + irs=ae->irasmstruct-1; + irsf=ae->rasmstruct[irs].irasmstructfield-1; + + /* ajouter les data du flux au champ de la structure */ + ObjectArrayAddDynamicValueConcat((void**)&ae->rasmstruct[irs].rasmstructfield[irsf].data, + &ae->rasmstruct[irs].rasmstructfield[irsf].idata, + &ae->rasmstruct[irs].rasmstructfield[irsf].mdata, + &v,sizeof(unsigned char)); + } + + ae->outputadr++; + ae->codeadr++; + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"output exceed limit %d\n",ae->maxptr); + ae->stop=1; + ___output=___internal_output_disabled; + } +} + + +void ___output_set_limit(struct s_assenv *ae,int zelimit) +{ + #undef FUNC + #define FUNC "___output_set_limit" + + int limit=65535; + + if (zelimit<=limit) { + /* apply limit */ + limit=zelimit; + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"limit exceed hardware limitation!"); + ae->stop=1; + } + if (ae->outputadr>=0 && ae->outputadr>limit) { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"limit too high for current output!"); + ae->stop=1; + } + ae->maxptr=limit; +} + +unsigned char *MakeAMSDOSHeader(int run, int minmem, int maxmem, char *amsdos_name) { + #undef FUNC + #define FUNC "MakeAMSDOSHeader" + + static unsigned char AmsdosHeader[128]; + int checksum,i=0; + /*** cpcwiki + Byte 00: User number + Byte 01 to 08: filename + Byte 09 bis 11: Extension + Byte 18: type-byte + Byte 21 and 22: loading address + Byte 24 and 25: file length + Byte 26 and 27: execution address for machine code programs + Byte 64 and 65: (file length) + Byte 67 and 68: checksum for byte 00 to byte 66 + To calculate the checksum, just add byte 00 to byte 66 to each other. + */ + memset(AmsdosHeader,0,sizeof(AmsdosHeader)); + AmsdosHeader[0]=0; + memcpy(AmsdosHeader+1,amsdos_name,11); + + AmsdosHeader[18]=2; /* 0 basic 1 basic protege 2 binaire */ + AmsdosHeader[19]=(maxmem-minmem)&0xFF; + AmsdosHeader[20]=(maxmem-minmem)>>8; + AmsdosHeader[21]=minmem&0xFF; + AmsdosHeader[22]=minmem>>8; + AmsdosHeader[24]=AmsdosHeader[19]; + AmsdosHeader[25]=AmsdosHeader[20]; + AmsdosHeader[26]=run&0xFF; + AmsdosHeader[27]=run>>8; + AmsdosHeader[64]=AmsdosHeader[19]; + AmsdosHeader[65]=AmsdosHeader[20]; + AmsdosHeader[66]=0; + + for (i=checksum=0;i<=66;i++) { + checksum+=AmsdosHeader[i]; + } + AmsdosHeader[67]=checksum&0xFF; + AmsdosHeader[68]=checksum>>8; + + /* garbage / shadow values from sector buffer? */ + memcpy(AmsdosHeader+0x47,amsdos_name,8); + AmsdosHeader[0x4F]=0x24; + AmsdosHeader[0x50]=0x24; + AmsdosHeader[0x51]=0x24; + AmsdosHeader[0x52]=0xFF; + AmsdosHeader[0x54]=0xFF; + AmsdosHeader[0x57]=0x02; + AmsdosHeader[0x5A]=AmsdosHeader[21]; + AmsdosHeader[0x5B]=AmsdosHeader[22]; + AmsdosHeader[0x5D]=AmsdosHeader[24]; + AmsdosHeader[0x5E]=AmsdosHeader[25]; + + sprintf((char *)AmsdosHeader+0x47+17," generated by %s ",RASM_SNAP_VERSION); + + return AmsdosHeader; +} + +unsigned char *MakeHobetaHeader(int minmem, int maxmem, char *trdos_name) { + #undef FUNC + #define FUNC "MakeHobetaHeader" + + static unsigned char HobetaHeader[17]; + int i,checksum=0; + /*** http://rk.nvg.ntnu.no/sinclair/faq/fileform.html#HOBETA + 0x00 FileName 0x08 TR-DOS file name + 0x08 FileType 0x01 TR-DOS file type + 0x09 StartAdr 0x02 start address of file + 0x0A FlLength 0x02 length of file (in bytes) -> /!\ wrong offset!!! + 0x0C FileSize 0x02 size of file (in sectors) + 0x0E HdrCRC16 0x02 Control checksum of the 15 byte + header (not sector data!) + */ + memset(HobetaHeader,0,sizeof(HobetaHeader)); + + strncpy(HobetaHeader,trdos_name,8); + HobetaHeader[8]='C'; + HobetaHeader[0x9]=(maxmem-minmem)&0xFF; + HobetaHeader[0xA]=(maxmem-minmem)>>8; + + HobetaHeader[0xB]=(maxmem-minmem)&0xFF; + HobetaHeader[0xC]=(maxmem-minmem)>>8; + + HobetaHeader[0xD]=((maxmem-minmem)+255)>>8; + HobetaHeader[0xE]=0; + + for (i=0;i<0xF;i++) checksum+=HobetaHeader[i]*257+i; + + HobetaHeader[0xF]=checksum&0xFF; + HobetaHeader[0x10]=(checksum>>8)&0xFF; + + return HobetaHeader; +} + + +int cmpAmsdosentry(const void * a, const void * b) +{ + return memcmp(a,b,32); +} + +int cmpmacros(const void * a, const void * b) +{ + struct s_macro *sa,*sb; + sa=(struct s_macro *)a; + sb=(struct s_macro *)b; + if (sa->crc<sb->crc) return -1; else return 1; +} +int SearchAlias(struct s_assenv *ae, int crc, char *zemot) +{ + int dw,dm,du,i; +//printf("SearchAlias [%s] ",zemot); + /* inutile de tourner autour du pot pour un si petit nombre */ + if (ae->ialias<5) { + for (i=0;i<ae->ialias;i++) { + if (ae->alias[i].crc==crc && strcmp(ae->alias[i].alias,zemot)==0) { + ae->alias[i].used=1; +//printf("found\n"); + return i; + } + } +//printf("not found\n"); + return -1; + } + + dw=0; + du=ae->ialias-1; + while (dw<=du) { + dm=(dw+du)/2; + if (ae->alias[dm].crc==crc) { + /* chercher le premier de la liste */ + while (dm>0 && ae->alias[dm-1].crc==crc) dm--; + /* controle sur le texte entier */ + while (ae->alias[dm].crc==crc && strcmp(ae->alias[dm].alias,zemot)) dm++; + if (ae->alias[dm].crc==crc && strcmp(ae->alias[dm].alias,zemot)==0) { + ae->alias[dm].used=1; +//printf("found\n"); + return dm; + } else return -1; + } else if (ae->alias[dm].crc>crc) { + du=dm-1; + } else if (ae->alias[dm].crc<crc) { + dw=dm+1; + } + } +//printf("not found\n"); + return -1; +} +int SearchMacro(struct s_assenv *ae, int crc, char *zemot) +{ + int dw,dm,du,i; + + /* inutile de tourner autour du pot pour un si petit nombre */ + if (ae->imacro<5) { + for (i=0;i<ae->imacro;i++) { + if (ae->macro[i].crc==crc && strcmp(ae->macro[i].mnemo,zemot)==0) { + return i; + } + } + return -1; + } + + dw=0; + du=ae->imacro-1; + while (dw<=du) { + dm=(dw+du)/2; + if (ae->macro[dm].crc==crc) { + /* chercher le premier de la liste */ + while (dm>0 && ae->macro[dm-1].crc==crc) dm--; + /* controle sur le texte entier */ + while (ae->macro[dm].crc==crc && strcmp(ae->macro[dm].mnemo,zemot)) dm++; + if (ae->macro[dm].crc==crc && strcmp(ae->macro[dm].mnemo,zemot)==0) return dm; else return -1; + } else if (ae->macro[dm].crc>crc) { + du=dm-1; + } else if (ae->macro[dm].crc<crc) { + dw=dm+1; + } + } + return -1; +} + +void CheckAndSortAliases(struct s_assenv *ae) +{ + #undef FUNC + #define FUNC "CheckAndSortAliases" + + struct s_alias tmpalias; + int i,dw,dm,du,crc; + for (i=0;i<ae->ialias-1;i++) { + /* is there previous aliases in the new alias? */ + if (strstr(ae->alias[ae->ialias-1].translation,ae->alias[i].alias)) { + /* there is a match, apply alias translation */ + ExpressionFastTranslate(ae,&ae->alias[ae->ialias-1].translation,2); + /* need to compute again len */ + ae->alias[ae->ialias-1].len=strlen(ae->alias[ae->ialias-1].translation); + break; + } + } + + /* cas particuliers pour insertion en début ou fin de liste */ + if (ae->ialias-1) { + if (ae->alias[ae->ialias-1].crc>ae->alias[ae->ialias-2].crc) { + /* pas de tri il est déjà au bon endroit */ + } else if (ae->alias[ae->ialias-1].crc<ae->alias[0].crc) { + /* insertion tout en bas de liste */ + tmpalias=ae->alias[ae->ialias-1]; + MemMove(&ae->alias[1],&ae->alias[0],sizeof(struct s_alias)*(ae->ialias-1)); + ae->alias[0]=tmpalias; + } else { + /* on cherche ou inserer */ + crc=ae->alias[ae->ialias-1].crc; + dw=0; + du=ae->ialias-1; + while (dw<=du) { + dm=(dw+du)/2; + if (ae->alias[dm].crc==crc) { + break; + } else if (ae->alias[dm].crc>crc) { + du=dm-1; + } else if (ae->alias[dm].crc<crc) { + dw=dm+1; + } + } + /* ajustement */ + if (ae->alias[dm].crc<crc) dm++; + /* insertion */ + tmpalias=ae->alias[ae->ialias-1]; + MemMove(&ae->alias[dm+1],&ae->alias[dm],sizeof(struct s_alias)*(ae->ialias-1-dm)); + ae->alias[dm]=tmpalias; + } + } else { + /* one alias need no sort */ + } +} + +void InsertDicoToTree(struct s_assenv *ae, struct s_expr_dico *dico) +{ + #undef FUNC + #define FUNC "InsertDicoToTree" + + struct s_crcdico_tree *curdicotree; + int radix,dek=32; + + curdicotree=&ae->dicotree; + while (dek) { + dek=dek-8; + radix=(dico->crc>>dek)&0xFF; + if (curdicotree->radix[radix]) { + curdicotree=curdicotree->radix[radix]; + } else { + curdicotree->radix[radix]=MemMalloc(sizeof(struct s_crcdico_tree)); + curdicotree=curdicotree->radix[radix]; + memset(curdicotree,0,sizeof(struct s_crcdico_tree)); + } + } + ObjectArrayAddDynamicValueConcat((void**)&curdicotree->dico,&curdicotree->ndico,&curdicotree->mdico,dico,sizeof(struct s_expr_dico)); +} + +unsigned char *SnapshotDicoInsert(char *symbol_name, int ptr, int *retidx) +{ + static unsigned char *subchunk=NULL; + static int subchunksize=0; + static int idx=0; + int symbol_len; + + if (retidx) { + if (symbol_name && strcmp(symbol_name,"FREE")==0) { + subchunksize=0; + idx=0; + MemFree(subchunk); + subchunk=NULL; + } + *retidx=idx; + return subchunk; + } + + if (idx+65536>subchunksize) { + subchunksize=subchunksize+65536; + subchunk=MemRealloc(subchunk,subchunksize); + } + + symbol_len=strlen(symbol_name); + if (symbol_len>255) symbol_len=255; + subchunk[idx++]=symbol_len; + memcpy(subchunk+idx,symbol_name,symbol_len); + idx+=symbol_len; + memset(subchunk+idx,0,6); + idx+=6; + subchunk[idx++]=(ptr&0xFF00)/256; + subchunk[idx++]=ptr&0xFF; + return NULL; +} + +void SnapshotDicoTreeRecurse(struct s_crcdico_tree *lt) +{ + #undef FUNC + #define FUNC "SnapshottDicoTreeRecurse" + + int i; + + for (i=0;i<256;i++) { + if (lt->radix[i]) { + SnapshotDicoTreeRecurse(lt->radix[i]); + } + } + if (lt->mdico) { + for (i=0;i<lt->ndico;i++) { + if (strcmp(lt->dico[i].name,"IX") && strcmp(lt->dico[i].name,"IY") && strcmp(lt->dico[i].name,"PI") && strcmp(lt->dico[i].name,"ASSEMBLER_RASM")) { + SnapshotDicoInsert(lt->dico[i].name,(int)floor(lt->dico[i].v+0.5),NULL); + } + } + } +} +unsigned char *SnapshotDicoTree(struct s_assenv *ae, int *retidx) +{ + #undef FUNC + #define FUNC "SnapshotDicoTree" + + unsigned char *sc; + int idx; + int i; + + for (i=0;i<256;i++) { + if (ae->dicotree.radix[i]) { + SnapshotDicoTreeRecurse(ae->dicotree.radix[i]); + } + } + + sc=SnapshotDicoInsert(NULL,0,&idx); + *retidx=idx; + return sc; +} + +void WarnLabelTreeRecurse(struct s_assenv *ae, struct s_crclabel_tree *lt) +{ + #undef FUNC + #define FUNC "WarnLabelTreeRecurse" + + int i; + + for (i=0;i<256;i++) { + if (lt->radix[i]) { + WarnLabelTreeRecurse(ae,lt->radix[i]); + } + } + for (i=0;i<lt->nlabel;i++) { + if (!lt->label[i].used) { + if (!lt->label[i].name) { + rasm_printf(ae,KWARNING"[%s:%d] Warning: label %s declared but not used\n",ae->filename[lt->label[i].fileidx],lt->label[i].fileline,ae->wl[lt->label[i].iw].w); + } else { + rasm_printf(ae,KWARNING"[%s:%d] Warning: label %s declared but not used\n",ae->filename[lt->label[i].fileidx],lt->label[i].fileline,lt->label[i].name); + } + } + } +} +void WarnLabelTree(struct s_assenv *ae) +{ + #undef FUNC + #define FUNC "WarnLabelTree" + + int i; + + for (i=0;i<256;i++) { + if (ae->labeltree.radix[i]) { + WarnLabelTreeRecurse(ae,ae->labeltree.radix[i]); + } + } +} +void WarnDicoTreeRecurse(struct s_assenv *ae, struct s_crcdico_tree *lt) +{ + #undef FUNC + #define FUNC "WarnDicoTreeRecurse" + + char symbol_line[1024]; + int i; + + for (i=0;i<256;i++) { + if (lt->radix[i]) { + WarnDicoTreeRecurse(ae,lt->radix[i]); + } + } + for (i=0;i<lt->ndico;i++) { + if (strcmp(lt->dico[i].name,"IX") && strcmp(lt->dico[i].name,"IY") && strcmp(lt->dico[i].name,"PI") && strcmp(lt->dico[i].name,"ASSEMBLER_RASM") && lt->dico[i].autorise_export) { + rasm_printf(ae,KWARNING"[%s:%d] Warning: variable %s declared but not used\n",ae->filename[ae->wl[lt->dico[i].iw].ifile],ae->wl[lt->dico[i].iw].l,lt->dico[i].name); + } + } +} +void WarnDicoTree(struct s_assenv *ae) +{ + #undef FUNC + #define FUNC "ExportDicoTree" + + int i; + + for (i=0;i<256;i++) { + if (ae->dicotree.radix[i]) { + WarnDicoTreeRecurse(ae,ae->dicotree.radix[i]); + } + } +} +void ExportDicoTreeRecurse(struct s_crcdico_tree *lt, char *zefile, char *zeformat) +{ + #undef FUNC + #define FUNC "ExportDicoTreeRecurse" + + char symbol_line[1024]; + int i; + + for (i=0;i<256;i++) { + if (lt->radix[i]) { + ExportDicoTreeRecurse(lt->radix[i],zefile,zeformat); + } + } + if (lt->mdico) { + for (i=0;i<lt->ndico;i++) { + if (strcmp(lt->dico[i].name,"IX") && strcmp(lt->dico[i].name,"IY") && strcmp(lt->dico[i].name,"PI") && strcmp(lt->dico[i].name,"ASSEMBLER_RASM") && lt->dico[i].autorise_export) { + snprintf(symbol_line,sizeof(symbol_line)-1,zeformat,lt->dico[i].name,(int)floor(lt->dico[i].v+0.5)); + symbol_line[sizeof(symbol_line)-1]=0xD; + FileWriteLine(zefile,symbol_line); + } + } + } +} +void ExportDicoTree(struct s_assenv *ae, char *zefile, char *zeformat) +{ + #undef FUNC + #define FUNC "ExportDicoTree" + + int i; + + for (i=0;i<256;i++) { + if (ae->dicotree.radix[i]) { + ExportDicoTreeRecurse(ae->dicotree.radix[i],zefile,zeformat); + } + } +} +void FreeDicoTreeRecurse(struct s_crcdico_tree *lt) +{ + #undef FUNC + #define FUNC "FreeDicoTreeRecurse" + + int i; + + for (i=0;i<256;i++) { + if (lt->radix[i]) { + FreeDicoTreeRecurse(lt->radix[i]); + } + } + if (lt->mdico) { + for (i=0;i<lt->ndico;i++) { + MemFree(lt->dico[i].name); + } + MemFree(lt->dico); + } + MemFree(lt); +} +void FreeDicoTree(struct s_assenv *ae) +{ + #undef FUNC + #define FUNC "FreeDicoTree" + + int i; + + for (i=0;i<256;i++) { + if (ae->dicotree.radix[i]) { + FreeDicoTreeRecurse(ae->dicotree.radix[i]); + } + } + if (ae->dicotree.mdico) { + for (i=0;i<ae->dicotree.ndico;i++) MemFree(ae->dicotree.dico[i].name); + MemFree(ae->dicotree.dico); + } +} +struct s_expr_dico *SearchDico(struct s_assenv *ae, char *dico, int crc) +{ + #undef FUNC + #define FUNC "SearchDico" + + struct s_crcdico_tree *curdicotree; + struct s_expr_dico *retdico=NULL; + int i,radix,dek=32; + + curdicotree=&ae->dicotree; + + while (dek) { + dek=dek-8; + radix=(crc>>dek)&0xFF; + if (curdicotree->radix[radix]) { + curdicotree=curdicotree->radix[radix]; + } else { + /* radix not found, dico is not in index */ + return NULL; + } + } + for (i=0;i<curdicotree->ndico;i++) { + if (strcmp(curdicotree->dico[i].name,dico)==0) { + curdicotree->dico[i].used=1; + return &curdicotree->dico[i]; + } + } + return NULL; +} +int DelDico(struct s_assenv *ae, char *dico, int crc) +{ + #undef FUNC + #define FUNC "DelDico" + + struct s_crcdico_tree *curdicotree; + struct s_expr_dico *retdico=NULL; + int i,radix,dek=32; + + curdicotree=&ae->dicotree; + + while (dek) { + dek=dek-8; + radix=(crc>>dek)&0xFF; + if (curdicotree->radix[radix]) { + curdicotree=curdicotree->radix[radix]; + } else { + /* radix not found, dico is not in index */ + return 0; + } + } + for (i=0;i<curdicotree->ndico;i++) { + if (strcmp(curdicotree->dico[i].name,dico)==0) { + /* must free memory */ + MemFree(curdicotree->dico[i].name); + if (i<curdicotree->ndico-1) { + MemMove(&curdicotree->dico[i],&curdicotree->dico[i+1],(curdicotree->ndico-i-1)*sizeof(struct s_expr_dico)); + } + curdicotree->ndico--; + return 1; + } + } + return 0; +} + + +void InsertUsedToTree(struct s_assenv *ae, char *used, int crc) +{ + #undef FUNC + #define FUNC "InsertUsedToTree" + + struct s_crcused_tree *curusedtree; + int radix,dek=32,i; + + curusedtree=&ae->usedtree; + while (dek) { + dek=dek-8; + radix=(crc>>dek)&0xFF; + if (curusedtree->radix[radix]) { + curusedtree=curusedtree->radix[radix]; + } else { + curusedtree->radix[radix]=MemMalloc(sizeof(struct s_crcused_tree)); + curusedtree=curusedtree->radix[radix]; + memset(curusedtree,0,sizeof(struct s_crcused_tree)); + } + } + for (i=0;i<curusedtree->nused;i++) if (strcmp(used,curusedtree->used[i])==0) break; + /* no double */ + if (i==curusedtree->nused) { + FieldArrayAddDynamicValueConcat(&curusedtree->used,&curusedtree->nused,&curusedtree->mused,used); + } +} + +void FreeUsedTreeRecurse(struct s_crcused_tree *lt) +{ + #undef FUNC + #define FUNC "FreeUsedTreeRecurse" + + int i; + + for (i=0;i<256;i++) { + if (lt->radix[i]) { + FreeUsedTreeRecurse(lt->radix[i]); + } + } + if (lt->mused) { + for (i=0;i<lt->nused;i++) MemFree(lt->used[i]); + MemFree(lt->used); + } + MemFree(lt); +} +void FreeUsedTree(struct s_assenv *ae) +{ + #undef FUNC + #define FUNC "FreeUsedTree" + + int i; + + for (i=0;i<256;i++) { + if (ae->usedtree.radix[i]) { + FreeUsedTreeRecurse(ae->usedtree.radix[i]); + } + } +} +int SearchUsed(struct s_assenv *ae, char *used, int crc) +{ + #undef FUNC + #define FUNC "SearchUsed" + + struct s_crcused_tree *curusedtree; + int i,radix,dek=32; + + curusedtree=&ae->usedtree; + while (dek) { + dek=dek-8; + radix=(crc>>dek)&0xFF; + if (curusedtree->radix[radix]) { + curusedtree=curusedtree->radix[radix]; + } else { + /* radix not found, used is not in index */ + return 0; + } + } + for (i=0;i<curusedtree->nused;i++) { + if (strcmp(curusedtree->used[i],used)==0) { + return 1; + } + } + return 0; +} + + + +void InsertTextToTree(struct s_assenv *ae, char *text, char *replace, int crc) +{ + #undef FUNC + #define FUNC "InsertTextToTree" + + struct s_crcstring_tree *curstringtree; + int radix,dek=32,i; + + curstringtree=&ae->stringtree; + while (dek) { + dek=dek-8; + radix=(crc>>dek)&0xFF; + if (curstringtree->radix[radix]) { + curstringtree=curstringtree->radix[radix]; + } else { + curstringtree->radix[radix]=MemMalloc(sizeof(struct s_crcused_tree)); + curstringtree=curstringtree->radix[radix]; + memset(curstringtree,0,sizeof(struct s_crcused_tree)); + } + } + for (i=0;i<curstringtree->ntext;i++) if (strcmp(text,curstringtree->text[i])==0) break; + /* no double */ + if (i==curstringtree->ntext) { + text=TxtStrDup(text); + replace=TxtStrDup(replace); + FieldArrayAddDynamicValueConcat(&curstringtree->text,&curstringtree->ntext,&curstringtree->mtext,text); + FieldArrayAddDynamicValueConcat(&curstringtree->replace,&curstringtree->nreplace,&curstringtree->mreplace,replace); + } +} + +void FreeTextTreeRecurse(struct s_crcstring_tree *lt) +{ + #undef FUNC + #define FUNC "FreeTextTreeRecurse" + + int i; + + for (i=0;i<256;i++) { + if (lt->radix[i]) { + FreeTextTreeRecurse(lt->radix[i]); + } + } + if (lt->mtext) { + for (i=0;i<lt->ntext;i++) MemFree(lt->text[i]); + MemFree(lt->text); + } + MemFree(lt); +} +void FreeTextTree(struct s_assenv *ae) +{ + #undef FUNC + #define FUNC "FreeTextTree" + + int i; + + for (i=0;i<256;i++) { + if (ae->stringtree.radix[i]) { + FreeTextTreeRecurse(ae->stringtree.radix[i]); + } + } + if (ae->stringtree.mtext) MemFree(ae->stringtree.text); +} +int SearchText(struct s_assenv *ae, char *text, int crc) +{ + #undef FUNC + #define FUNC "SearchText" + + struct s_crcstring_tree *curstringtree; + int i,radix,dek=32; + + curstringtree=&ae->stringtree; + while (dek) { + dek=dek-8; + radix=(crc>>dek)&0xFF; + if (curstringtree->radix[radix]) { + curstringtree=curstringtree->radix[radix]; + } else { + /* radix not found, used is not in index */ + return 0; + } + } + for (i=0;i<curstringtree->ntext;i++) { + if (strcmp(curstringtree->text[i],text)==0) { + return 1; + } + } + return 0; +} + + + + + + + +/* +struct s_crclabel_tree { + + + + +/* +struct s_crclabel_tree { + struct s_crclabel_tree *radix[256]; + struct s_label *label; + int nlabel,mlabel; +}; +*/ +void FreeLabelTreeRecurse(struct s_crclabel_tree *lt) +{ + #undef FUNC + #define FUNC "FreeLabelTreeRecurse" + + int i; + + for (i=0;i<256;i++) { + if (lt->radix[i]) { + FreeLabelTreeRecurse(lt->radix[i]); + } + } + /* label.name already freed elsewhere as this one is a copy */ + if (lt->mlabel) MemFree(lt->label); + MemFree(lt); +} +void FreeLabelTree(struct s_assenv *ae) +{ + #undef FUNC + #define FUNC "FreeLabelTree" + + int i; + + for (i=0;i<256;i++) { + if (ae->labeltree.radix[i]) { + FreeLabelTreeRecurse(ae->labeltree.radix[i]); + } + } + if (ae->labeltree.mlabel) MemFree(ae->labeltree.label); +} + +struct s_label *SearchLabel(struct s_assenv *ae, char *label, int crc) +{ + #undef FUNC + #define FUNC "SearchLabel" + + struct s_crclabel_tree *curlabeltree; + struct s_label *retlabel=NULL; + int i,radix,dek=32; +//printf("searchLabel [%s]",label); + curlabeltree=&ae->labeltree; + while (dek) { + dek=dek-8; + radix=(crc>>dek)&0xFF; + if (curlabeltree->radix[radix]) { + curlabeltree=curlabeltree->radix[radix]; + } else { + /* radix not found, label is not in index */ +//printf(" not found\n"); + return NULL; + } + } + for (i=0;i<curlabeltree->nlabel;i++) { + if (!curlabeltree->label[i].name && strcmp(ae->wl[curlabeltree->label[i].iw].w,label)==0) { + curlabeltree->label[i].used=1; +//printf(" found (global)\n"); + return &curlabeltree->label[i]; + } else if (curlabeltree->label[i].name && strcmp(curlabeltree->label[i].name,label)==0) { + curlabeltree->label[i].used=1; +//printf(" found (local or proximity)\n"); + return &curlabeltree->label[i]; + } + } + return NULL; +} + +char *MakeLocalLabel(struct s_assenv *ae,char *varbuffer, int *retdek) +{ + #undef FUNC + #define FUNC "MakeLocalLabel" + + char *locallabel; + char hexdigit[32]; + int lenbuf=0,dek,i,im; + char *zepoint; + + lenbuf=strlen(varbuffer); + + /* not so local labels */ + if (varbuffer[0]=='.') { + /* create reference */ + if (ae->lastgloballabel) { + locallabel=MemMalloc(strlen(varbuffer)+1+ae->lastgloballabellen); + sprintf(locallabel,"%s%s",ae->lastgloballabel,varbuffer); + if (retdek) *retdek=0; + return locallabel; + } else { + if (retdek) *retdek=0; + return TxtStrDup(varbuffer); + } + } + + /*************************************************** + without retdek -> build a local label + with retdek -> build the hash string + ***************************************************/ + if (!retdek) { + locallabel=MemMalloc(lenbuf+(ae->ir+ae->iw+3)*8+8); + zepoint=strchr(varbuffer,'.'); + if (zepoint) { + *zepoint=0; + } + strcpy(locallabel,varbuffer); + } else { + locallabel=MemMalloc((ae->ir+ae->iw+3)*8+4); + locallabel[0]=0; + } +//printf("locallabel=[%s] (draft)\n",locallabel); + + dek=0; + dek+=strappend(locallabel,"R"); + for (i=0;i<ae->ir;i++) { + sprintf(hexdigit,"%04X",ae->repeat[i].cpt); + dek+=strappend(locallabel,hexdigit); + } + if (ae->ir) { + sprintf(hexdigit,"%04X",ae->repeat[ae->ir-1].value); + dek+=strappend(locallabel+dek,hexdigit); + } + + dek+=strappend(locallabel,"W"); + for (i=0;i<ae->iw;i++) { + sprintf(hexdigit,"%04X",ae->whilewend[i].cpt); + dek+=strappend(locallabel+dek,hexdigit); + } + if (ae->iw) { + sprintf(hexdigit,"%04X",ae->whilewend[ae->iw-1].value); + dek+=strappend(locallabel+dek,hexdigit); + } + /* where are we? */ + if (ae->imacropos) { + for (im=ae->imacropos-1;im>=0;im--) { + if (ae->idx>=ae->macropos[im].start && ae->idx<ae->macropos[im].end) break; + } + if (im>=0) { + /* si on n'est pas dans une macro, on n'indique rien */ + sprintf(hexdigit,"M%04X",ae->macropos[im].value); + dek+=strappend(locallabel+dek,hexdigit); + } + } + if (!retdek) { + if (zepoint) { + *zepoint='.'; + strcat(locallabel+dek,zepoint); + } + } else { + *retdek=dek; + } +//printf("locallabel=[%s] (end)\n",locallabel); + return locallabel; +} + +char *TradExpression(char *zexp) +{ + #undef FUNC + #define FUNC "TradExpression" + + static char *last_expression=NULL; + char *wstr; + + if (last_expression) {MemFree(last_expression);last_expression=NULL;} + if (!zexp) return NULL; + + wstr=TxtStrDup(zexp); + wstr=TxtReplace(wstr,"[","<<",0); + wstr=TxtReplace(wstr,"]",">>",0); + wstr=TxtReplace(wstr,"m","%",0); + + last_expression=wstr; + return wstr; +} + +int TrimFloatingPointString(char *fps) { + int i=0,pflag,zflag=0; + + while (fps[i]) { + if (fps[i]=='.') { + pflag=i; + zflag=1; + } else if (fps[i]!='0') { + zflag=0; + } + i++; + } + /* truncate floating fract */ + if (zflag) { + fps[pflag]=0; + } else { + pflag=i; + } + return pflag; +} + + + +/* + translate tag or formula between curly brackets + used in label declaration + used in print directive +*/ +char *TranslateTag(struct s_assenv *ae, char *varbuffer, int *touched, int enablefast, int tagoption) { + /******************************************************* + v a r i a b l e s i n s t r i n g s + *******************************************************/ + char *starttag,*endtag,*tagcheck,*expr; + int newlen,lenw,taglen,tagidx,tagcount,validx; + char curvalstr[256]={0}; + char *equpos=NULL,*equback; + +//printf("TranslateTag [%s]\n",varbuffer); + + if (tagoption & E_TAGOPTION_PRESERVE) { + if (ae->iw || ae->ir) { + /* inside a loop we must care about variables */ +//printf("TranslateTag [%s] with PRESERVE inside a loop!\n",varbuffer); + return varbuffer; + } + } + + *touched=0; + while ((starttag=strchr(varbuffer+1,'{'))!=NULL) { + if ((endtag=strchr(starttag,'}'))==NULL) { + MakeError(ae,GetCurrentFile(ae),GetExpLine(ae,0),"invalid tag in string [%s]\n",varbuffer); + return NULL; + } + /* allow inception */ + tagcount=1; + tagcheck=starttag+1; + while (*tagcheck && tagcount) { + if (*tagcheck=='}') tagcount--; else if (*tagcheck=='{') tagcount++; + tagcheck++; + } + if (tagcount) { + MakeError(ae,GetCurrentFile(ae),GetExpLine(ae,0),"invalid brackets combination in string [%s]\n",varbuffer); + return NULL; + } else { + endtag=tagcheck-1; + } + *touched=1; + taglen=endtag-starttag+1; + tagidx=starttag-varbuffer; + lenw=strlen(varbuffer); // before the EOF write + *endtag=0; + /*** c o m p u t e e x p r e s s i o n ***/ + expr=TxtStrDup(starttag+1); + if (tagoption & E_TAGOPTION_REMOVESPACE) expr=TxtReplace(expr," ","",0); + if (enablefast) ExpressionFastTranslate(ae,&expr,0); + validx=(int)RoundComputeExpressionCore(ae,expr,ae->codeadr,0); + if (validx<0) { + strcpy(curvalstr,""); + MakeError(ae,GetCurrentFile(ae),GetExpLine(ae,0),"indexed tag must NOT be a negative value [%s]\n",varbuffer); + MemFree(expr); + return NULL; + } else { + #ifdef OS_WIN + snprintf(curvalstr,sizeof(curvalstr)-1,"%d",validx); + newlen=strlen(curvalstr); + #else + newlen=snprintf(curvalstr,sizeof(curvalstr)-1,"%d",validx); + #endif + } + MemFree(expr); + if (newlen>taglen) { + /* realloc */ + varbuffer=MemRealloc(varbuffer,lenw+newlen-taglen+1); + } + if (newlen!=taglen ) { + MemMove(varbuffer+tagidx+newlen,varbuffer+tagidx+taglen,lenw-taglen-tagidx+1); + } + strncpy(varbuffer+tagidx,curvalstr,newlen); /* copy without zero terminator */ + } + + return varbuffer; +} + +double ComputeExpressionCore(struct s_assenv *ae,char *original_zeexpression,int ptr, int didx) +{ + #undef FUNC + #define FUNC "ComputeExpressionCore" + + /* static execution buffers */ + static double *accu=NULL; + static int maccu=0; + static struct s_compute_element *computestack=NULL; + static int maxcomputestack=0; + int i,j,paccu=0; + int nbtokenstack=0; + int nbcomputestack=0; + int nboperatorstack=0; + + struct s_compute_element stackelement; + int o2,okclose,itoken; + + int idx=0,crc,icheck,is_binary,ivar=0; + char asciivalue[11]; + unsigned char c; + int accu_err=0; + /* backup alias replace */ + char *zeexpression,*expr; + int original=1; + int ialias,startvar; + int newlen,lenw; + /* dictionnary */ + struct s_expr_dico *curdic; + struct s_label *curlabel; + char *localname; + int minusptr,imkey,bank,page,idxmacro; + double curval; + /* negative value */ + int allow_minus_as_sign=0; + /* extended replace in labels */ + int curly=0,curlyflag=0; + char *Automate; + double dummint; + + /* memory cleanup */ + if (!ae) { + if (maccu) MemFree(accu); + accu=NULL;maccu=0; + if (maxcomputestack) MemFree(computestack); + computestack=NULL;maxcomputestack=0; +#if 0 + if (maxivar) MemFree(varbuffer); + if (maxtokenstack) MemFree(tokenstack); + if (maxoperatorstack) MemFree(operatorstack); + maxtokenstack=maxoperatorstack=0; + maxivar=1; + varbuffer=NULL; + tokenstack=NULL; + operatorstack=NULL; +#endif + return 0.0; + } + + /* be sure to have at least some bytes allocated */ + StateMachineResizeBuffer(&ae->computectx->varbuffer,128,&ae->computectx->maxivar); + + +#if TRACE_COMPUTE_EXPRESSION + printf("expression=[%s]\n",zeexpression); +#endif + zeexpression=original_zeexpression; + if (!zeexpression[0]) { + return 0; + } + /* double hack if the first value is negative */ + if (zeexpression[0]=='-') { + if (ae->AutomateExpressionValidCharFirst[(int)zeexpression[1]&0xFF]) { + allow_minus_as_sign=1; + } else { + memset(&stackelement,0,sizeof(stackelement)); + ObjectArrayAddDynamicValueConcat((void **)&ae->computectx->tokenstack,&nbtokenstack,&ae->computectx->maxtokenstack,&stackelement,sizeof(stackelement)); + } + } + + /* is there ascii char? */ + while ((c=zeexpression[idx])!=0) { + if (c=='\'' || c=='"') { + /* echappement */ + if (zeexpression[idx+1]=='\\') { + if (zeexpression[idx+2] && zeexpression[idx+3]==c) { + sprintf(asciivalue,"#%03X",zeexpression[idx+2]); + memcpy(zeexpression+idx,asciivalue,4); + idx+=3; + } else { + MakeError(ae,GetExpFile(ae,didx),GetExpLine(ae,didx),"Only single escaped char may be quoted [%s]\n",TradExpression(zeexpression)); + zeexpression[0]=0; + return 0; + } + } else if (zeexpression[idx+1] && zeexpression[idx+2]==c) { + sprintf(asciivalue,"#%02X",zeexpression[idx+1]); + memcpy(zeexpression+idx,asciivalue,3); + idx+=2; + } else { + MakeError(ae,GetExpFile(ae,didx),GetExpLine(ae,didx),"Only single char may be quoted [%s]\n",TradExpression(zeexpression)); + zeexpression[0]=0; + return 0; + } + } + + idx++; + } +#if TRACE_COMPUTE_EXPRESSION + printf("apres conversion des chars [%s]\n",zeexpression); +#endif + /*********************************************************** + P A T C H F O R P O S I T I V E V A L U E + ***********************************************************/ + if (zeexpression[0]=='+') idx=1; else idx=0; + /*********************************************************** + C O M P U T E E X P R E S S I O N M A I N L O O P + ***********************************************************/ + while ((c=zeexpression[idx])!=0) { + switch (c) { + /* parenthesis */ + case ')': + /* next to a closing parenthesis, a minus is an operator */ + allow_minus_as_sign=0; + break; + case '(': + /* operator detection */ + case '*': + case '/': + case '^': + case '[': + case 'm': + case '+': + case ']': + allow_minus_as_sign=1; + break; + case '&': + allow_minus_as_sign=1; + if (c=='&' && zeexpression[idx+1]=='&') { + idx++; + c='a'; // boolean AND + } + break; + case '|': + allow_minus_as_sign=1; + if (c=='|' && zeexpression[idx+1]=='|') { + idx++; + c='o'; // boolean OR + } + break; + /* testing */ + case '<': + allow_minus_as_sign=1; + if (zeexpression[idx+1]=='=') { + idx++; + c='k'; // boolean LOWEREQ + } else if (zeexpression[idx+1]=='>') { + idx++; + c='n'; // boolean NOTEQUAL + } else { + c='l'; + } + break; + case '>': + allow_minus_as_sign=1; + if (zeexpression[idx+1]=='=') { + idx++; + c='h'; // boolean GREATEREQ + } else { + c='g'; + } + break; + case '!': + allow_minus_as_sign=1; + if (zeexpression[idx+1]=='=') { + idx++; + c='n'; // boolean NOTEQUAL + } else { + c='b'; + } + break; + case '=': + allow_minus_as_sign=1; + /* expecting == */ + if (zeexpression[idx+1]=='=') { + idx++; + c='e'; // boolean EQUAL + /* except in maxam mode with a single = */ + } else if (ae->maxam) { + c='e'; // boolean EQUAL + /* cannot affect data inside an expression */ + } else { + MakeError(ae,GetExpFile(ae,didx),GetExpLine(ae,didx),"expression [%s] cannot set variable inside an expression\n",TradExpression(zeexpression)); + return 0; + } + break; + case '-': + if (allow_minus_as_sign) { + /* previous char was an opening parenthesis or an operator */ + ivar=0; + ae->computectx->varbuffer[ivar++]='-'; + StateMachineResizeBuffer(&ae->computectx->varbuffer,ivar,&ae->computectx->maxivar); + c=zeexpression[++idx]; + if (ae->AutomateExpressionValidCharFirst[(int)c&0xFF]) { + ae->computectx->varbuffer[ivar++]=c; + StateMachineResizeBuffer(&ae->computectx->varbuffer,ivar,&ae->computectx->maxivar); + c=zeexpression[++idx]; + while (ae->AutomateExpressionValidChar[(int)c&0xFF]) { + ae->computectx->varbuffer[ivar++]=c; + StateMachineResizeBuffer(&ae->computectx->varbuffer,ivar,&ae->computectx->maxivar); + c=zeexpression[++idx]; + } + } + ae->computectx->varbuffer[ivar]=0; + if (ivar<2) { + MakeError(ae,GetExpFile(ae,didx),GetExpLine(ae,didx),"expression [%s] invalid minus sign\n",TradExpression(zeexpression)); + if (!original) { + MemFree(zeexpression); + } + return 0; + } + break; + } + allow_minus_as_sign=1; + break; + + /* operator OR binary value */ + case '%': + /* % symbol may be a modulo or a binary literal value */ + is_binary=0; + for (icheck=1;zeexpression[idx+icheck];icheck++) { + switch (zeexpression[idx+icheck]) { + case '1': + case '0':/* still binary */ + is_binary=1; + break; + case '+': + case '-': + case '/': + case '*': + case '|': + case 'm': + case '%': + case '^': + case '&': + case '(': + case ')': + case '=': + case '<': + case '>': + case '!': + case '[': + case ']': + if (is_binary) is_binary=2; else is_binary=-1; + break; + default: + is_binary=-1; + } + if (is_binary==2) { + break; + } + if (is_binary==-1) { + is_binary=0; + break; + } + } + if (!is_binary) { + allow_minus_as_sign=1; + c='m'; + break; + } + default: + allow_minus_as_sign=0; + /* semantic analysis */ + startvar=idx; + ivar=0; + /* first char does not allow same chars as next chars */ + if (ae->AutomateExpressionValidCharFirst[((int)c)&0xFF]) { + ae->computectx->varbuffer[ivar++]=c; + if (c=='{') { + /* not a formula but only a prefix tag */ + curly++; + } + StateMachineResizeBuffer(&ae->computectx->varbuffer,ivar,&ae->computectx->maxivar); + idx++; + c=zeexpression[idx]; + Automate=ae->AutomateExpressionValidChar; + while (Automate[((int)c)&0xFF]) { + if (c=='{') { + curly++; + curlyflag=1; + Automate=ae->AutomateExpressionValidCharExtended; + } else if (c=='}') { + curly--; + if (!curly) { + Automate=ae->AutomateExpressionValidChar; + } + } + ae->computectx->varbuffer[ivar++]=c; + StateMachineResizeBuffer(&ae->computectx->varbuffer,ivar,&ae->computectx->maxivar); + idx++; + c=zeexpression[idx]; + } + } + ae->computectx->varbuffer[ivar]=0; + if (!ivar) { + MakeError(ae,GetExpFile(ae,didx),GetExpLine(ae,didx),"invalid char (%d=%c) expression [%s]\n",c,c>31?c:' ',TradExpression(zeexpression)); + if (!original) { + MemFree(zeexpression); + } + return 0; + } else if (curly) { + MakeError(ae,GetExpFile(ae,didx),GetExpLine(ae,didx),"wrong curly brackets in expression [%s]\n",TradExpression(zeexpression)); + if (!original) { + MemFree(zeexpression); + } + return 0; + } + } + if (c && !ivar) idx++; + + /************************************ + S T A C K D I S P A T C H E R + ************************************/ + /* push operator or stack value */ + if (!ivar) { + /************************************ + O P E R A T O R + ************************************/ + stackelement=ae->AutomateElement[c]; + if (stackelement.operator>E_COMPUTE_OPERATION_GREATEREQ) { + MakeError(ae,GetExpFile(ae,didx),GetExpLine(ae,didx),"expression [%s] has unknown operator %c (%d)\n",TradExpression(zeexpression),c>31?c:'.',c); + } + /* stackelement.value isn't used */ + } else { + /************************************ + V A L U E + ************************************/ +#if TRACE_COMPUTE_EXPRESSION + printf("value [%s]\n",ae->computectx->varbuffer); +#endif + if (ae->computectx->varbuffer[0]=='-') minusptr=1; else minusptr=0; + /* constantes ou variables/labels */ + switch (ae->computectx->varbuffer[minusptr]) { + case '0': + /* 0x hexa value hack */ + if (ae->computectx->varbuffer[minusptr+1]=='X' && ae->AutomateHexa[ae->computectx->varbuffer[minusptr+2]]) { + for (icheck=minusptr+3;ae->computectx->varbuffer[icheck];icheck++) { + if (ae->AutomateHexa[ae->computectx->varbuffer[icheck]]) continue; + MakeError(ae,GetExpFile(ae,didx),GetExpLine(ae,didx),"expression [%s] - %s is not a valid hex number\n",TradExpression(zeexpression),ae->computectx->varbuffer); + break; + } + curval=strtol(ae->computectx->varbuffer+minusptr+2,NULL,16); + break; + } else + /* 0b binary value hack */ + if (ae->computectx->varbuffer[minusptr+1]=='B' && (ae->computectx->varbuffer[minusptr+2]>='0' && ae->computectx->varbuffer[minusptr+2]<='1')) { + for (icheck=minusptr+3;ae->computectx->varbuffer[icheck];icheck++) { + if (ae->computectx->varbuffer[icheck]>='0' && ae->computectx->varbuffer[icheck]<='1') continue; + MakeError(ae,GetExpFile(ae,didx),GetExpLine(ae,didx),"expression [%s] - %s is not a valid binary number\n",TradExpression(zeexpression),ae->computectx->varbuffer); + break; + } + curval=strtol(ae->computectx->varbuffer+minusptr+2,NULL,2); + break; + } + /* 0o octal value hack */ + if (ae->computectx->varbuffer[minusptr+1]=='O' && (ae->computectx->varbuffer[minusptr+2]>='0' && ae->computectx->varbuffer[minusptr+2]<='5')) { + for (icheck=minusptr+3;ae->computectx->varbuffer[icheck];icheck++) { + if (ae->computectx->varbuffer[icheck]>='0' && ae->computectx->varbuffer[icheck]<='5') continue; + MakeError(ae,GetExpFile(ae,didx),GetExpLine(ae,didx),"expression [%s] - %s is not a valid octal number\n",TradExpression(zeexpression),ae->computectx->varbuffer); + break; + } + curval=strtol(ae->computectx->varbuffer+minusptr+2,NULL,2); + break; + } + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + /* check number */ + for (icheck=minusptr;ae->computectx->varbuffer[icheck];icheck++) { + if (ae->AutomateDigit[ae->computectx->varbuffer[icheck]]) continue; + /* Intel hexa & binary style */ + switch (ae->computectx->varbuffer[strlen(ae->computectx->varbuffer)-1]) { + case 'H': + for (icheck=minusptr;ae->computectx->varbuffer[icheck+1];icheck++) { + if (ae->AutomateHexa[ae->computectx->varbuffer[icheck]]) continue; + MakeError(ae,GetExpFile(ae,didx),GetExpLine(ae,didx),"expression [%s] - %s is not a valid hex number\n",TradExpression(zeexpression),ae->computectx->varbuffer); + } + curval=strtol(ae->computectx->varbuffer+minusptr,NULL,16); + break; + case 'B': + for (icheck=minusptr;ae->computectx->varbuffer[icheck+1];icheck++) { + if (ae->computectx->varbuffer[icheck]=='0' || ae->computectx->varbuffer[icheck]=='1') continue; + MakeError(ae,GetExpFile(ae,didx),GetExpLine(ae,didx),"expression [%s] - %s is not a valid binary number\n",TradExpression(zeexpression),ae->computectx->varbuffer); + } + curval=strtol(ae->computectx->varbuffer+minusptr,NULL,2); + break; + default: + MakeError(ae,GetExpFile(ae,didx),GetExpLine(ae,didx),"expression [%s] - %s is not a valid number\n",TradExpression(zeexpression),ae->computectx->varbuffer); + } + icheck=0; + break; + } + if (!ae->computectx->varbuffer[icheck]) curval=atof(ae->computectx->varbuffer+minusptr); + break; + case '%': + /* check number */ + if (!ae->computectx->varbuffer[minusptr+1]) { + MakeError(ae,GetExpFile(ae,didx),GetExpLine(ae,didx),"expression [%s] - %s is an empty binary number\n",TradExpression(zeexpression),ae->computectx->varbuffer); + } + for (icheck=minusptr+1;ae->computectx->varbuffer[icheck];icheck++) { + if (ae->computectx->varbuffer[icheck]=='0' || ae->computectx->varbuffer[icheck]=='1') continue; + MakeError(ae,GetExpFile(ae,didx),GetExpLine(ae,didx),"expression [%s] - %s is not a valid binary number\n",TradExpression(zeexpression),ae->computectx->varbuffer); + break; + } + curval=strtol(ae->computectx->varbuffer+minusptr+1,NULL,2); + break; + case '#': + /* check number */ + if (!ae->computectx->varbuffer[minusptr+1]) { + MakeError(ae,GetExpFile(ae,didx),GetExpLine(ae,didx),"expression [%s] - %s is an empty hex number\n",TradExpression(zeexpression),ae->computectx->varbuffer); + } + for (icheck=minusptr+1;ae->computectx->varbuffer[icheck];icheck++) { + if (ae->AutomateHexa[ae->computectx->varbuffer[icheck]]) continue; + MakeError(ae,GetExpFile(ae,didx),GetExpLine(ae,didx),"expression [%s] - %s is not a valid hex number\n",TradExpression(zeexpression),ae->computectx->varbuffer); + break; + } + curval=strtol(ae->computectx->varbuffer+minusptr+1,NULL,16); + break; + default: + if (1 || !curlyflag) { + /* $ hex value hack */ + if (ae->computectx->varbuffer[minusptr+0]=='$' && ae->AutomateHexa[ae->computectx->varbuffer[minusptr+1]]) { + for (icheck=minusptr+2;ae->computectx->varbuffer[icheck];icheck++) { + if (ae->AutomateHexa[ae->computectx->varbuffer[icheck]]) continue; + MakeError(ae,GetExpFile(ae,didx),GetExpLine(ae,didx),"expression [%s] - %s is not a valid hex number\n",TradExpression(zeexpression),ae->computectx->varbuffer); + break; + } + curval=strtol(ae->computectx->varbuffer+minusptr+1,NULL,16); + break; + } + /* @ octal value hack */ + if (ae->computectx->varbuffer[minusptr+0]=='@' && ((ae->computectx->varbuffer[minusptr+1]>='0' && ae->computectx->varbuffer[minusptr+1]<='7'))) { + for (icheck=minusptr+2;ae->computectx->varbuffer[icheck];icheck++) { + if (ae->computectx->varbuffer[icheck]>='0' && ae->computectx->varbuffer[icheck]<='7') continue; + MakeError(ae,GetExpFile(ae,didx),GetExpLine(ae,didx),"expression [%s] - %s is not a valid octal number\n",TradExpression(zeexpression),ae->computectx->varbuffer); + break; + } + curval=strtol(ae->computectx->varbuffer+minusptr+1,NULL,8); + break; + } + /* Intel hexa value hack */ + if (ae->AutomateHexa[ae->computectx->varbuffer[minusptr+0]]) { + if (ae->computectx->varbuffer[strlen(ae->computectx->varbuffer)-1]=='H') { + for (icheck=minusptr;ae->computectx->varbuffer[icheck+1];icheck++) { + if (!ae->AutomateHexa[ae->computectx->varbuffer[icheck]]) break; + } + if (!ae->computectx->varbuffer[icheck+1]) { + curval=strtol(ae->computectx->varbuffer+minusptr,NULL,16); + break; + } + } + } + } + + + if (curlyflag) { + char *minivarbuffer; + int touched; + + /* besoin d'un sous-contexte */ + minivarbuffer=TxtStrDup(ae->computectx->varbuffer+minusptr); + ae->computectx=&ae->ctx2; +#if TRACE_COMPUTE_EXPRESSION + printf("curly [%s]\n",minivarbuffer); +#endif + minivarbuffer=TranslateTag(ae,minivarbuffer, &touched,0,E_TAGOPTION_NONE); +#if TRACE_COMPUTE_EXPRESSION + printf("après curly [%s]\n",minivarbuffer); +#endif + ae->computectx=&ae->ctx1; + if (!touched) { + strcpy(ae->computectx->varbuffer+minusptr,minivarbuffer); + } else { + StateMachineResizeBuffer(&ae->computectx->varbuffer,strlen(minivarbuffer)+2,&ae->computectx->maxivar); + strcpy(ae->computectx->varbuffer+minusptr,minivarbuffer); + } + MemFree(minivarbuffer); + curlyflag=0; + } + + crc=GetCRC(ae->computectx->varbuffer+minusptr); + /*************************************************** + L O O K I N G F O R A F U N C T I O N + ***************************************************/ + for (imkey=0;math_keyword[imkey].mnemo[0];imkey++) { + if (crc==math_keyword[imkey].crc && strcmp(ae->computectx->varbuffer+minusptr,math_keyword[imkey].mnemo)==0) { + if (c=='(') { + /* push function as operator! */ + stackelement.operator=math_keyword[imkey].operation; + //stackelement.priority=0; + /************************************************ + C R E A T E E X T R A T O K E N + ************************************************/ + ObjectArrayAddDynamicValueConcat((void **)&ae->computectx->tokenstack,&nbtokenstack,&ae->computectx->maxtokenstack,&stackelement,sizeof(stackelement)); + stackelement.operator=E_COMPUTE_OPERATION_OPEN; + ObjectArrayAddDynamicValueConcat((void **)&ae->computectx->tokenstack,&nbtokenstack,&ae->computectx->maxtokenstack,&stackelement,sizeof(stackelement)); + allow_minus_as_sign=1; + idx++; + } else { + MakeError(ae,GetExpFile(ae,didx),GetExpLine(ae,didx),"expression [%s] - %s is a reserved keyword!\n",TradExpression(zeexpression),math_keyword[imkey].mnemo); + curval=0; + idx++; + } + ivar=0; + break; + } + } + if (math_keyword[imkey].mnemo[0]) continue; + + if (ae->computectx->varbuffer[minusptr+0]=='$' && ae->computectx->varbuffer[minusptr+1]==0) { + curval=ptr; + } else { +#if TRACE_COMPUTE_EXPRESSION + printf("search dico [%s]\n",ae->computectx->varbuffer+minusptr); +#endif + curdic=SearchDico(ae,ae->computectx->varbuffer+minusptr,crc); + if (curdic) { +#if TRACE_COMPUTE_EXPRESSION + printf("trouvé valeur=%.2lf\n",curdic->v); +#endif + curval=curdic->v; + break; + } else { + /* getbank hack */ + if (ae->computectx->varbuffer[minusptr]!='{') { + bank=0; + page=0; + } else if (strncmp(ae->computectx->varbuffer+minusptr,"{BANK}",6)==0) { + bank=6; + page=0; + /* obligé de recalculer le CRC */ + crc=GetCRC(ae->computectx->varbuffer+minusptr+bank); + } else if (strncmp(ae->computectx->varbuffer+minusptr,"{PAGE}",6)==0) { + bank=6; + page=1; + /* obligé de recalculer le CRC */ + crc=GetCRC(ae->computectx->varbuffer+minusptr+bank); + } else if (strncmp(ae->computectx->varbuffer+minusptr,"{PAGESET}",9)==0) { + bank=9; + page=2; + /* obligé de recalculer le CRC */ + crc=GetCRC(ae->computectx->varbuffer+minusptr+bank); + } else if (strncmp(ae->computectx->varbuffer+minusptr,"{SIZEOF}",8)==0) { + bank=8; + page=3; + /* obligé de recalculer le CRC */ + crc=GetCRC(ae->computectx->varbuffer+minusptr+bank); + /* search in structures prototypes and subfields */ + for (i=0;i<ae->irasmstruct;i++) { + if (ae->rasmstruct[i].crc==crc && strcmp(ae->rasmstruct[i].name,ae->computectx->varbuffer+minusptr+bank)==0) { + curval=ae->rasmstruct[i].size; + break; + } + + for (j=0;j<ae->rasmstruct[i].irasmstructfield;j++) { + if (ae->rasmstruct[i].rasmstructfield[j].crc==crc && strcmp(ae->rasmstruct[i].rasmstructfield[j].fullname,ae->computectx->varbuffer+minusptr+bank)==0) { + curval=ae->rasmstruct[i].rasmstructfield[j].size; + i=ae->irasmstruct+1; + break; + } + } + } + + if (i==ae->irasmstruct) { + /* search in structures aliases */ + for (i=0;i<ae->irasmstructalias;i++) { + if (ae->rasmstructalias[i].crc==crc && strcmp(ae->rasmstructalias[i].name,ae->computectx->varbuffer+minusptr+bank)==0) { + curval=ae->rasmstructalias[i].size+ae->rasmstructalias[i].ptr; + break; + } + } + if (i==ae->irasmstructalias) { + MakeError(ae,GetExpFile(ae,didx),GetExpLine(ae,didx),"cannot SIZEOF unknown structure [%s]!\n",ae->computectx->varbuffer+minusptr+bank); + curval=0; + } + } + } else { + MakeError(ae,GetExpFile(ae,didx),GetExpLine(ae,didx),"expression [%s] - %s is an unknown prefix!\n",TradExpression(zeexpression),ae->computectx->varbuffer); + } + /* limited label translation while processing crunched blocks + ae->curlz == current crunched block processed + expression->crunch_block=0 -> oui + expression->crunch_block=1 -> oui si même block + expression->crunch_block=2 -> non car sera relogée + */ + if (page!=3) { + +#if TRACE_COMPUTE_EXPRESSION +printf("search label [%s]\n",ae->computectx->varbuffer+minusptr+bank); +#endif + curlabel=SearchLabel(ae,ae->computectx->varbuffer+minusptr+bank,crc); + if (curlabel) { + if (ae->stage<2) { + if (curlabel->lz==-1) { + if (!bank) { + curval=curlabel->ptr; + } else { +#if TRACE_COMPUTE_EXPRESSION +printf("page=%d | ptr=%X ibank=%d\n",page,curlabel->ptr,curlabel->ibank); +#endif + switch (page) { + case 2: /* PAGESET */ + if (curlabel->ibank<BANK_MAX_NUMBER) { + curval=ae->setgate[curlabel->ibank]; + } else { + MakeError(ae,GetExpFile(ae,didx),GetExpLine(ae,didx),"expression [%s] cannot use PAGESET - label [%s] is in a temporary space!\n",TradExpression(zeexpression),ae->computectx->varbuffer); + curval=curlabel->ibank; + } + break; + case 1:/* PAGE */ + if (curlabel->ibank<BANK_MAX_NUMBER) { + /* 4M expansion compliant */ + if (ae->bankset[curlabel->ibank>>2]) { + curval=ae->bankgate[(curlabel->ibank&0x1FC)+(curlabel->ptr>>14)]; + } else { + curval=ae->bankgate[curlabel->ibank]; + } + } else { + MakeError(ae,GetExpFile(ae,didx),GetExpLine(ae,didx),"expression [%s] cannot use PAGE - label [%s] is in a temporary space!\n",TradExpression(zeexpression),ae->computectx->varbuffer); + curval=curlabel->ibank; + } + break; + case 0: + curval=curlabel->ibank; + break; + default:MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"INTERNAL ERROR (unknown paging)\n",GetExpFile(ae,didx),GetExpLine(ae,didx));FreeAssenv(ae);exit(-664); + } + } + } else { + /* label MUST be in the crunched block */ + if (curlabel->iorgzone==ae->expression[didx].iorgzone && curlabel->ibank==ae->expression[didx].ibank && curlabel->lz<=ae->expression[didx].lz) { + if (!bank) { + curval=curlabel->ptr; + } else { + if (page) { + switch (page) { + case 2: /* PAGESET */ + if (curlabel->ibank<BANK_MAX_NUMBER) { + /* 4M expansion compliant */ + curval=ae->setgate[curlabel->ibank]; + } else { + MakeError(ae,GetExpFile(ae,didx),GetExpLine(ae,didx),"expression [%s] cannot use PAGESET - label [%s] is in a temporary space!\n",TradExpression(zeexpression),ae->computectx->varbuffer); + curval=curlabel->ibank; + } + break; + case 1: /* PAGE */ + if (curlabel->ibank<BANK_MAX_NUMBER) { + /* 4M expansion compliant */ + if (ae->bankset[curlabel->ibank>>2]) { + curval=ae->bankgate[(curlabel->ibank&0x1FC)+(curlabel->ptr>>14)]; + } else { + curval=ae->bankgate[curlabel->ibank]; + } + } else { + MakeError(ae,GetExpFile(ae,didx),GetExpLine(ae,didx),"expression [%s] cannot use PAGE - label [%s] is in a temporary space!\n",TradExpression(zeexpression),ae->computectx->varbuffer); + curval=curlabel->ibank; + } + break; + case 0:curval=curlabel->ibank;break; + default:MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"INTERNAL ERROR (unknown paging)\n",GetExpFile(ae,didx),GetExpLine(ae,didx));FreeAssenv(ae);exit(-664); + } + } + } + } else { + MakeError(ae,GetExpFile(ae,didx),GetExpLine(ae,didx),"Label [%s](%d) cannot be computed because it is located after the crunched zone %d\n",ae->computectx->varbuffer,curlabel->lz,ae->expression[didx].lz); + curval=0; + } + } + } else { +#if TRACE_COMPUTE_EXPRESSION +printf("stage 2 | page=%d | ptr=%X ibank=%d\n",page,curlabel->ptr,curlabel->ibank); +#endif + if (bank) { + //curval=curlabel->ibank; + switch (page) { + case 2: /* PAGESET */ + if (curlabel->ibank<BANK_MAX_NUMBER) { + curval=ae->setgate[curlabel->ibank]; + } else { + MakeError(ae,GetExpFile(ae,didx),GetExpLine(ae,didx),"expression [%s] cannot use PAGESET - label [%s] is in a temporary space!\n",TradExpression(zeexpression),ae->computectx->varbuffer); + curval=curlabel->ibank; + } + break; + case 1:/* PAGE */ + if (curlabel->ibank<BANK_MAX_NUMBER) { + /* 4M expansion compliant */ + if (ae->bankset[curlabel->ibank>>2]) { + curval=ae->bankgate[(curlabel->ibank&0x1FC)+(curlabel->ptr>>14)]; + } else { + curval=ae->bankgate[curlabel->ibank]; + } + } else { + MakeError(ae,GetExpFile(ae,didx),GetExpLine(ae,didx),"expression [%s] cannot use PAGE - label [%s] is in a temporary space!\n",TradExpression(zeexpression),ae->computectx->varbuffer); + curval=curlabel->ibank; + } + break; + case 0: + curval=curlabel->ibank; + break; + default:MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"INTERNAL ERROR (unknown paging)\n",GetExpFile(ae,didx),GetExpLine(ae,didx));FreeAssenv(ae);exit(-664); + } + } else { + curval=curlabel->ptr; + } + } + } else { + /*********** + to allow aliases declared after use + ***********/ + if ((ialias=SearchAlias(ae,crc,ae->computectx->varbuffer+minusptr))>=0) { + newlen=ae->alias[ialias].len; + lenw=strlen(zeexpression); + if (newlen>ivar) { + /* realloc bigger */ + if (original) { + expr=MemMalloc(lenw+newlen-ivar+1); + memcpy(expr,zeexpression,lenw+1); + zeexpression=expr; + original=0; + } else { + zeexpression=MemRealloc(zeexpression,lenw+newlen-ivar+1); + } + } + /* startvar? */ + if (newlen!=ivar) { + MemMove(zeexpression+startvar+newlen,zeexpression+startvar+ivar,lenw-startvar-ivar+1); + } + strncpy(zeexpression+startvar,ae->alias[ialias].translation,newlen); /* copy without zero terminator */ + idx=startvar; + ivar=0; + continue; + } else { + /* index possible sur une struct? */ + int reverse_idx,validx=-1; + char *structlabel; + + reverse_idx=strlen(ae->computectx->varbuffer)-1; + if (ae->computectx->varbuffer[reverse_idx]>='0' && ae->computectx->varbuffer[reverse_idx]<='9') { + /* vu que ça ne PEUT PAS être une valeur litérale, on ne fait pas de test de débordement */ + reverse_idx--; + while (ae->computectx->varbuffer[reverse_idx]>='0' && ae->computectx->varbuffer[reverse_idx]<='9') { + reverse_idx--; + } + reverse_idx++; + validx=atoi(ae->computectx->varbuffer+reverse_idx); + structlabel=TxtStrDup(ae->computectx->varbuffer+minusptr); + structlabel[reverse_idx-minusptr]=0; +#ifdef TRACE_STRUCT + printf("EVOL 119 -> looking for struct %s IDX=%d\n",structlabel,validx); +#endif + /* unoptimized search in structures aliases */ + crc=GetCRC(structlabel); + for (i=0;i<ae->irasmstructalias;i++) { + if (ae->rasmstructalias[i].crc==crc && strcmp(ae->rasmstructalias[i].name,structlabel)==0) { +#ifdef TRACE_STRUCT + printf("EVOL 119 -> found! ptr=%d size=%d\n",ae->rasmstructalias[i].ptr,ae->rasmstructalias[i].size); +#endif + curval=ae->rasmstructalias[i].size*validx+ae->rasmstructalias[i].ptr; + if (validx>=ae->rasmstructalias[i].nbelem) { + if (!ae->nowarning) rasm_printf(ae,KWARNING"[%s:%d] Warning: index out of array size!\n",GetExpFile(ae,didx),GetExpLine(ae,didx)); + } + break; + } + } + if (i==ae->irasmstructalias) { + /* not found */ + validx=-1; + } + MemFree(structlabel); + } + if (validx<0) { + /* last chance to get a keyword */ + if (strcmp(ae->computectx->varbuffer+minusptr,"REPEAT_COUNTER")==0) { + if (ae->ir) { + curval=ae->repeat[ae->ir-1].repeat_counter; + } else { + MakeError(ae,GetExpFile(ae,didx),GetExpLine(ae,didx),"cannot use REPEAT_COUNTER keyword outside a repeat loop\n"); + curval=0; + } + } else if (strcmp(ae->computectx->varbuffer+minusptr,"WHILE_COUNTER")==0) { + if (ae->iw) { + curval=ae->whilewend[ae->iw-1].while_counter; + } else { + MakeError(ae,GetExpFile(ae,didx),GetExpLine(ae,didx),"cannot use WHILE_COUNTER keyword outside a while loop\n"); + curval=0; + } + } else { + /* in case the expression is a register */ + if (IsRegister(ae->computectx->varbuffer+minusptr)) { + MakeError(ae,GetExpFile(ae,didx),GetExpLine(ae,didx),"cannot use register %s in this context\n",TradExpression(zeexpression)); + } else { + MakeError(ae,GetExpFile(ae,didx),GetExpLine(ae,didx),"expression [%s] keyword [%s] not found in variables, labels or aliases\n",TradExpression(zeexpression),ae->computectx->varbuffer+minusptr); + if (ae->extended_error) { + char *lookstr; + lookstr=StringLooksLike(ae,ae->computectx->varbuffer+minusptr); + if (lookstr) { + rasm_printf(ae,KERROR" did you mean [%s] ?\n",lookstr); + } + } + } + + curval=0; + } + } + } + } + } + } + } + } + if (minusptr) curval=-curval; + stackelement.operator=E_COMPUTE_OPERATION_PUSH_DATASTC; + stackelement.value=curval; + /* priority isn't used */ + + allow_minus_as_sign=0; + ivar=0; + } + /************************************ + C R E A T E T O K E N + ************************************/ + ObjectArrayAddDynamicValueConcat((void **)&ae->computectx->tokenstack,&nbtokenstack,&ae->computectx->maxtokenstack,&stackelement,sizeof(stackelement)); + } + /******************************************************* + C R E A T E E X E C U T I O N S T A C K + *******************************************************/ +#define DEBUG_STACK 0 +#if DEBUG_STACK + for (itoken=0;itoken<nbtokenstack;itoken++) { + switch (ae->computectx->tokenstack[itoken].operator) { + case E_COMPUTE_OPERATION_PUSH_DATASTC:printf("%lf ",ae->computectx->tokenstack[itoken].value);break; + case E_COMPUTE_OPERATION_OPEN:printf("(");break; + case E_COMPUTE_OPERATION_CLOSE:printf(")");break; + case E_COMPUTE_OPERATION_ADD:printf("+ ");break; + case E_COMPUTE_OPERATION_SUB:printf("- ");break; + case E_COMPUTE_OPERATION_DIV:printf("/ ");break; + case E_COMPUTE_OPERATION_MUL:printf("* ");break; + case E_COMPUTE_OPERATION_AND:printf("and ");break; + case E_COMPUTE_OPERATION_OR:printf("or ");break; + case E_COMPUTE_OPERATION_MOD:printf("mod ");break; + case E_COMPUTE_OPERATION_XOR:printf("xor ");break; + case E_COMPUTE_OPERATION_NOT:printf("! ");break; + case E_COMPUTE_OPERATION_SHL:printf("<< ");break; + case E_COMPUTE_OPERATION_SHR:printf(">> ");break; + case E_COMPUTE_OPERATION_BAND:printf("&& ");break; + case E_COMPUTE_OPERATION_BOR:printf("|| ");break; + case E_COMPUTE_OPERATION_LOWER:printf("< ");break; + case E_COMPUTE_OPERATION_GREATER:printf("> ");break; + case E_COMPUTE_OPERATION_EQUAL:printf("== ");break; + case E_COMPUTE_OPERATION_NOTEQUAL:printf("!= ");break; + case E_COMPUTE_OPERATION_LOWEREQ:printf("<= ");break; + case E_COMPUTE_OPERATION_GREATEREQ:printf(">= ");break; + case E_COMPUTE_OPERATION_SIN:printf("sin ");break; + case E_COMPUTE_OPERATION_COS:printf("cos ");break; + case E_COMPUTE_OPERATION_INT:printf("int ");break; + case E_COMPUTE_OPERATION_FLOOR:printf("floor ");break; + case E_COMPUTE_OPERATION_ABS:printf("abs ");break; + case E_COMPUTE_OPERATION_LN:printf("ln ");break; + case E_COMPUTE_OPERATION_LOG10:printf("log10 ");break; + case E_COMPUTE_OPERATION_SQRT:printf("sqrt ");break; + case E_COMPUTE_OPERATION_ASIN:printf("asin ");break; + case E_COMPUTE_OPERATION_ACOS:printf("acos ");break; + case E_COMPUTE_OPERATION_ATAN:printf("atan ");break; + case E_COMPUTE_OPERATION_EXP:printf("exp ");break; + case E_COMPUTE_OPERATION_LOW:printf("low ");break; + case E_COMPUTE_OPERATION_HIGH:printf("high ");break; + case E_COMPUTE_OPERATION_PSG:printf("psg ");break; + case E_COMPUTE_OPERATION_RND:printf("rnd ");break; + case E_COMPUTE_OPERATION_FRAC:printf("frac ");break; + case E_COMPUTE_OPERATION_CEIL:printf("ceil ");break; + case E_COMPUTE_OPERATION_GET_R:printf("get_r ");break; + case E_COMPUTE_OPERATION_GET_V:printf("get_v ");break; + case E_COMPUTE_OPERATION_GET_B:printf("get_b ");break; + case E_COMPUTE_OPERATION_SET_R:printf("set_r ");break; + case E_COMPUTE_OPERATION_SET_V:printf("set_v ");break; + case E_COMPUTE_OPERATION_SET_B:printf("set_b ");break; + default:printf("bug\n");break; + } + + } + printf("\n"); +#endif + + for (itoken=0;itoken<nbtokenstack;itoken++) { + switch (ae->computectx->tokenstack[itoken].operator) { + case E_COMPUTE_OPERATION_PUSH_DATASTC: +#if DEBUG_STACK +printf("data\n"); +#endif + ObjectArrayAddDynamicValueConcat((void **)&computestack,&nbcomputestack,&maxcomputestack,&ae->computectx->tokenstack[itoken],sizeof(stackelement)); + break; + case E_COMPUTE_OPERATION_OPEN: + ObjectArrayAddDynamicValueConcat((void **)&ae->computectx->operatorstack,&nboperatorstack,&ae->computectx->maxoperatorstack,&ae->computectx->tokenstack[itoken],sizeof(stackelement)); +#if DEBUG_STACK +printf("ajout (\n"); +#endif + break; + case E_COMPUTE_OPERATION_CLOSE: +#if DEBUG_STACK +printf("close\n"); +#endif + /* pop out token until the opened parenthesis is reached */ + o2=nboperatorstack-1; + okclose=0; + while (o2>=0) { + if (ae->computectx->operatorstack[o2].operator!=E_COMPUTE_OPERATION_OPEN) { + ObjectArrayAddDynamicValueConcat((void **)&computestack,&nbcomputestack,&maxcomputestack,&ae->computectx->operatorstack[o2],sizeof(stackelement)); + nboperatorstack--; +#if DEBUG_STACK +printf("op--\n"); +#endif + o2--; + } else { + /* discard opening parenthesis as operator */ +#if DEBUG_STACK +printf("discard )\n"); +#endif + nboperatorstack--; + okclose=1; + o2--; + break; + } + } + if (!okclose) { + MakeError(ae,GetExpFile(ae,didx),GetExpLine(ae,didx),"missing parenthesis [%s]\n",TradExpression(zeexpression)); + if (!original) { + MemFree(zeexpression); + } + return 0; + } + /* if upper token is a function then pop from the stack */ + if (o2>=0 && ae->computectx->operatorstack[o2].operator>=E_COMPUTE_OPERATION_SIN) { + ObjectArrayAddDynamicValueConcat((void **)&computestack,&nbcomputestack,&maxcomputestack,&ae->computectx->operatorstack[o2],sizeof(stackelement)); + nboperatorstack--; +#if DEBUG_STACK +printf("pop function\n"); +#endif + } + break; + case E_COMPUTE_OPERATION_ADD: + case E_COMPUTE_OPERATION_SUB: + case E_COMPUTE_OPERATION_DIV: + case E_COMPUTE_OPERATION_MUL: + case E_COMPUTE_OPERATION_AND: + case E_COMPUTE_OPERATION_OR: + case E_COMPUTE_OPERATION_MOD: + case E_COMPUTE_OPERATION_XOR: + case E_COMPUTE_OPERATION_NOT: + case E_COMPUTE_OPERATION_SHL: + case E_COMPUTE_OPERATION_SHR: + case E_COMPUTE_OPERATION_BAND: + case E_COMPUTE_OPERATION_BOR: + case E_COMPUTE_OPERATION_LOWER: + case E_COMPUTE_OPERATION_GREATER: + case E_COMPUTE_OPERATION_EQUAL: + case E_COMPUTE_OPERATION_NOTEQUAL: + case E_COMPUTE_OPERATION_LOWEREQ: + case E_COMPUTE_OPERATION_GREATEREQ: +#if DEBUG_STACK +printf("operator\n"); +#endif + o2=nboperatorstack-1; + while (o2>=0 && ae->computectx->operatorstack[o2].operator!=E_COMPUTE_OPERATION_OPEN) { + if (ae->computectx->tokenstack[itoken].priority>=ae->computectx->operatorstack[o2].priority || ae->computectx->operatorstack[o2].operator>=E_COMPUTE_OPERATION_SIN) { + ObjectArrayAddDynamicValueConcat((void **)&computestack,&nbcomputestack,&maxcomputestack,&ae->computectx->operatorstack[o2],sizeof(stackelement)); + nboperatorstack--; + o2--; + } else { + break; + } + } + ObjectArrayAddDynamicValueConcat((void **)&ae->computectx->operatorstack,&nboperatorstack,&ae->computectx->maxoperatorstack,&ae->computectx->tokenstack[itoken],sizeof(stackelement)); + break; + case E_COMPUTE_OPERATION_SIN: + case E_COMPUTE_OPERATION_COS: + case E_COMPUTE_OPERATION_INT: + case E_COMPUTE_OPERATION_FLOOR: + case E_COMPUTE_OPERATION_ABS: + case E_COMPUTE_OPERATION_LN: + case E_COMPUTE_OPERATION_LOG10: + case E_COMPUTE_OPERATION_SQRT: + case E_COMPUTE_OPERATION_ASIN: + case E_COMPUTE_OPERATION_ACOS: + case E_COMPUTE_OPERATION_ATAN: + case E_COMPUTE_OPERATION_EXP: + case E_COMPUTE_OPERATION_LOW: + case E_COMPUTE_OPERATION_HIGH: + case E_COMPUTE_OPERATION_PSG: + case E_COMPUTE_OPERATION_RND: + case E_COMPUTE_OPERATION_FRAC: + case E_COMPUTE_OPERATION_CEIL: + case E_COMPUTE_OPERATION_GET_R: + case E_COMPUTE_OPERATION_GET_V: + case E_COMPUTE_OPERATION_GET_B: + case E_COMPUTE_OPERATION_SET_R: + case E_COMPUTE_OPERATION_SET_V: + case E_COMPUTE_OPERATION_SET_B: +#if DEBUG_STACK +printf("ajout de la fonction\n"); +#endif + ObjectArrayAddDynamicValueConcat((void **)&ae->computectx->operatorstack,&nboperatorstack,&ae->computectx->maxoperatorstack,&ae->computectx->tokenstack[itoken],sizeof(stackelement)); + break; + default:break; + } + } + /* pop remaining operators */ + while (nboperatorstack>0) { + ObjectArrayAddDynamicValueConcat((void **)&computestack,&nbcomputestack,&maxcomputestack,&ae->computectx->operatorstack[--nboperatorstack],sizeof(stackelement)); + } + + /******************************************** + E X E C U T E S T A C K + ********************************************/ + if (ae->maxam || ae->as80) { + int workinterval; + if (ae->as80) workinterval=0xFFFFFFFF; else workinterval=0xFFFF; + for (i=0;i<nbcomputestack;i++) { + switch (computestack[i].operator) { + /************************************************ + c a s e s s h o u l d b e s o r t e d + ************************************************/ + case E_COMPUTE_OPERATION_PUSH_DATASTC: + if (maccu<=paccu) { + maccu=16+paccu; + accu=MemRealloc(accu,sizeof(double)*maccu); + } + accu[paccu]=computestack[i].value;paccu++; + break; + case E_COMPUTE_OPERATION_OPEN: + case E_COMPUTE_OPERATION_CLOSE:/* cannot happend */ break; + case E_COMPUTE_OPERATION_ADD:if (paccu>1) accu[paccu-2]=((int)accu[paccu-2]+(int)accu[paccu-1])&workinterval;paccu--;break; + case E_COMPUTE_OPERATION_SUB:if (paccu>1) accu[paccu-2]=((int)accu[paccu-2]-(int)accu[paccu-1])&workinterval;paccu--;break; + case E_COMPUTE_OPERATION_MUL:if (paccu>1) accu[paccu-2]=((int)accu[paccu-2]*(int)accu[paccu-1])&workinterval;paccu--;break; + case E_COMPUTE_OPERATION_DIV:if (paccu>1) accu[paccu-2]=((int)accu[paccu-2]/(int)accu[paccu-1])&workinterval;paccu--;break; + case E_COMPUTE_OPERATION_AND:if (paccu>1) accu[paccu-2]=((int)accu[paccu-2]&(int)accu[paccu-1])&workinterval;paccu--;break; + case E_COMPUTE_OPERATION_OR:if (paccu>1) accu[paccu-2]=((int)accu[paccu-2]|(int)accu[paccu-1])&workinterval;paccu--;break; + case E_COMPUTE_OPERATION_XOR:if (paccu>1) accu[paccu-2]=((int)accu[paccu-2]^(int)accu[paccu-1])&workinterval;paccu--;break; + case E_COMPUTE_OPERATION_MOD:if (paccu>1) accu[paccu-2]=((int)accu[paccu-2]%(int)accu[paccu-1])&workinterval;paccu--;break; + case E_COMPUTE_OPERATION_SHL:if (paccu>1) accu[paccu-2]=((int)accu[paccu-2])<<((int)accu[paccu-1]);paccu--;break; + case E_COMPUTE_OPERATION_SHR:if (paccu>1) accu[paccu-2]=((int)accu[paccu-2])>>((int)accu[paccu-1]);paccu--;break; + case E_COMPUTE_OPERATION_BAND:if (paccu>1) accu[paccu-2]=((int)accu[paccu-2]&&(int)accu[paccu-1])&workinterval;paccu--;break; + case E_COMPUTE_OPERATION_BOR:if (paccu>1) accu[paccu-2]=((int)accu[paccu-2]||(int)accu[paccu-1])&workinterval;paccu--;break; + /* comparison */ + case E_COMPUTE_OPERATION_LOWER:if (paccu>1) accu[paccu-2]=((int)accu[paccu-2]&workinterval)<((int)accu[paccu-1]&workinterval);paccu--;break; + case E_COMPUTE_OPERATION_LOWEREQ:if (paccu>1) accu[paccu-2]=((int)accu[paccu-2]&workinterval)<=((int)accu[paccu-1]&workinterval);paccu--;break; + case E_COMPUTE_OPERATION_EQUAL:if (paccu>1) accu[paccu-2]=((int)accu[paccu-2]&workinterval)==((int)accu[paccu-1]&workinterval);paccu--;break; + case E_COMPUTE_OPERATION_NOTEQUAL:if (paccu>1) accu[paccu-2]=((int)accu[paccu-2]&workinterval)!=((int)accu[paccu-1]&workinterval);paccu--;break; + case E_COMPUTE_OPERATION_GREATER:if (paccu>1) accu[paccu-2]=((int)accu[paccu-2]&workinterval)>((int)accu[paccu-1]&workinterval);paccu--;break; + case E_COMPUTE_OPERATION_GREATEREQ:if (paccu>1) accu[paccu-2]=((int)accu[paccu-2]&workinterval)>=((int)accu[paccu-1]&workinterval);paccu--;break; + /* functions */ + case E_COMPUTE_OPERATION_SIN:if (paccu>0) accu[paccu-1]=(int)sin(accu[paccu-1]*3.1415926545/180.0);break; + case E_COMPUTE_OPERATION_COS:if (paccu>0) accu[paccu-1]=(int)cos(accu[paccu-1]*3.1415926545/180.0);break; + case E_COMPUTE_OPERATION_ASIN:if (paccu>0) accu[paccu-1]=(int)asin(accu[paccu-1])*180.0/3.1415926545;break; + case E_COMPUTE_OPERATION_ACOS:if (paccu>0) accu[paccu-1]=(int)acos(accu[paccu-1])*180.0/3.1415926545;break; + case E_COMPUTE_OPERATION_ATAN:if (paccu>0) accu[paccu-1]=(int)atan(accu[paccu-1])*180.0/3.1415926545;break; + case E_COMPUTE_OPERATION_INT:break; + case E_COMPUTE_OPERATION_FLOOR:if (paccu>0) accu[paccu-1]=(int)floor(accu[paccu-1])&workinterval;break; + case E_COMPUTE_OPERATION_ABS:if (paccu>0) accu[paccu-1]=(int)fabs(accu[paccu-1])&workinterval;break; + case E_COMPUTE_OPERATION_EXP:if (paccu>0) accu[paccu-1]=(int)exp(accu[paccu-1])&workinterval;break; + case E_COMPUTE_OPERATION_LN:if (paccu>0) accu[paccu-1]=(int)log(accu[paccu-1])&workinterval;break; + case E_COMPUTE_OPERATION_LOG10:if (paccu>0) accu[paccu-1]=(int)log10(accu[paccu-1])&workinterval;break; + case E_COMPUTE_OPERATION_SQRT:if (paccu>0) accu[paccu-1]=(int)sqrt(accu[paccu-1])&workinterval;break; + case E_COMPUTE_OPERATION_LOW:if (paccu>0) accu[paccu-1]=((int)accu[paccu-1])&0xFF;break; + case E_COMPUTE_OPERATION_HIGH:if (paccu>0) accu[paccu-1]=(((int)accu[paccu-1])&0xFF00)>>8;break; + case E_COMPUTE_OPERATION_PSG:if (paccu>0) accu[paccu-1]=ae->psgfine[((int)accu[paccu-1])&0xFF];break; + case E_COMPUTE_OPERATION_RND:if (paccu>0) accu[paccu-1]=rand()%((int)accu[paccu-1]);break; + case E_COMPUTE_OPERATION_FRAC:if (paccu>0) accu[paccu-1]=((int)(accu[paccu-1]-(int)accu[paccu-1]));break; + case E_COMPUTE_OPERATION_CEIL:if (paccu>0) accu[paccu-1]=(int)ceil(accu[paccu-1])&workinterval;break; + case E_COMPUTE_OPERATION_GET_R:if (paccu>0) accu[paccu-1]=((((int)accu[paccu-1])&0xF0)>>4);break; + case E_COMPUTE_OPERATION_GET_V:if (paccu>0) accu[paccu-1]=((((int)accu[paccu-1])&0xF00)>>8);break; + case E_COMPUTE_OPERATION_GET_B:if (paccu>0) accu[paccu-1]=(((int)accu[paccu-1])&0xF);break; + case E_COMPUTE_OPERATION_SET_R:if (paccu>0) accu[paccu-1]=MinMaxInt(accu[paccu-1],0,15)<<4;break; + case E_COMPUTE_OPERATION_SET_V:if (paccu>0) accu[paccu-1]=MinMaxInt(accu[paccu-1],0,15)<<8;break; + case E_COMPUTE_OPERATION_SET_B:if (paccu>0) accu[paccu-1]=MinMaxInt(accu[paccu-1],0,15);break; + default:MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"invalid computing state! (%d)\n",GetExpFile(ae,didx),GetExpLine(ae,didx),computestack[i].operator);paccu=0; + } + if (!paccu) { + if (zeexpression[0]=='&') { + MakeError(ae,GetExpFile(ae,didx),GetExpLine(ae,didx),"Missing operand for calculation [%s] Did you use & for an hexadecimal value?\n",TradExpression(zeexpression)); + } else { + MakeError(ae,GetExpFile(ae,didx),GetExpLine(ae,didx),"Missing operand for calculation [%s]\n",TradExpression(zeexpression)); + } + accu_err=1; + break; + } + } + } else { + for (i=0;i<nbcomputestack;i++) { +#if 0 + int kk; + for (kk=0;kk<paccu;kk++) printf("stack[%d]=%lf\n",kk,accu[kk]); + if (computestack[i].operator==E_COMPUTE_OPERATION_PUSH_DATASTC) { + printf("pacc=%d push %.1lf\n",paccu,computestack[i].value); + } else { + printf("pacc=%d operation %s p=%d\n",paccu,computestack[i].operator==E_COMPUTE_OPERATION_MUL?"*": + computestack[i].operator==E_COMPUTE_OPERATION_ADD?"+": + computestack[i].operator==E_COMPUTE_OPERATION_DIV?"/": + computestack[i].operator==E_COMPUTE_OPERATION_SUB?"-": + computestack[i].operator==E_COMPUTE_OPERATION_BAND?"&&": + computestack[i].operator==E_COMPUTE_OPERATION_BOR?"||": + computestack[i].operator==E_COMPUTE_OPERATION_SHL?"<<": + computestack[i].operator==E_COMPUTE_OPERATION_SHR?">>": + computestack[i].operator==E_COMPUTE_OPERATION_LOWER?"<": + computestack[i].operator==E_COMPUTE_OPERATION_GREATER?">": + computestack[i].operator==E_COMPUTE_OPERATION_EQUAL?"==": + computestack[i].operator==E_COMPUTE_OPERATION_INT?"INT": + computestack[i].operator==E_COMPUTE_OPERATION_LOWEREQ?"<=": + computestack[i].operator==E_COMPUTE_OPERATION_GREATEREQ?">=": + computestack[i].operator==E_COMPUTE_OPERATION_OPEN?"(": + computestack[i].operator==E_COMPUTE_OPERATION_CLOSE?")": + "<autre>",computestack[i].priority); + } +#endif + switch (computestack[i].operator) { + case E_COMPUTE_OPERATION_PUSH_DATASTC: + if (maccu<=paccu) { + maccu=16+paccu; + accu=MemRealloc(accu,sizeof(double)*maccu); + } + accu[paccu]=computestack[i].value;paccu++; + break; + case E_COMPUTE_OPERATION_OPEN: + case E_COMPUTE_OPERATION_CLOSE: /* cannot happend */ break; + case E_COMPUTE_OPERATION_ADD:if (paccu>1) accu[paccu-2]+=accu[paccu-1];paccu--;break; + case E_COMPUTE_OPERATION_SUB:if (paccu>1) accu[paccu-2]-=accu[paccu-1];paccu--;break; + case E_COMPUTE_OPERATION_MUL:if (paccu>1) accu[paccu-2]*=accu[paccu-1];paccu--;break; + case E_COMPUTE_OPERATION_DIV:if (paccu>1) accu[paccu-2]/=accu[paccu-1];paccu--;break; + case E_COMPUTE_OPERATION_AND:if (paccu>1) accu[paccu-2]=((int)floor(accu[paccu-2]+0.5))&((int)floor(accu[paccu-1]+0.5));paccu--;break; + case E_COMPUTE_OPERATION_OR:if (paccu>1) accu[paccu-2]=((int)floor(accu[paccu-2]+0.5))|((int)floor(accu[paccu-1]+0.5));paccu--;break; + case E_COMPUTE_OPERATION_XOR:if (paccu>1) accu[paccu-2]=((int)floor(accu[paccu-2]+0.5))^((int)floor(accu[paccu-1]+0.5));paccu--;break; + case E_COMPUTE_OPERATION_NOT:/* half operator, half function */ if (paccu>0) accu[paccu-1]=!((int)floor(accu[paccu-1]+0.5));break; + case E_COMPUTE_OPERATION_MOD:if (paccu>1) accu[paccu-2]=((int)floor(accu[paccu-2]+0.5))%((int)floor(accu[paccu-1]+0.5));paccu--;break; + case E_COMPUTE_OPERATION_SHL:if (paccu>1) accu[paccu-2]=((int)floor(accu[paccu-2]+0.5))<<((int)floor(accu[paccu-1]+0.5));paccu--;break; + case E_COMPUTE_OPERATION_SHR:if (paccu>1) accu[paccu-2]=((int)floor(accu[paccu-2]+0.5))>>((int)floor(accu[paccu-1]+0.5));paccu--;break; + case E_COMPUTE_OPERATION_BAND:if (paccu>1) accu[paccu-2]=((int)floor(accu[paccu-2]+0.5))&&((int)floor(accu[paccu-1]+0.5));paccu--;break; + case E_COMPUTE_OPERATION_BOR:if (paccu>1) accu[paccu-2]=((int)floor(accu[paccu-2]+0.5))||((int)floor(accu[paccu-1]+0.5));paccu--;break; + /* comparison */ + case E_COMPUTE_OPERATION_LOWER:if (paccu>1) accu[paccu-2]=accu[paccu-2]<accu[paccu-1];paccu--;break; + case E_COMPUTE_OPERATION_LOWEREQ:if (paccu>1) accu[paccu-2]=accu[paccu-2]<=accu[paccu-1];paccu--;break; + case E_COMPUTE_OPERATION_EQUAL:if (paccu>1) accu[paccu-2]=fabs(accu[paccu-2]-accu[paccu-1])<0.000001;paccu--;break; + case E_COMPUTE_OPERATION_NOTEQUAL:if (paccu>1) accu[paccu-2]=accu[paccu-2]!=accu[paccu-1];paccu--;break; + case E_COMPUTE_OPERATION_GREATER:if (paccu>1) accu[paccu-2]=accu[paccu-2]>accu[paccu-1];paccu--;break; + case E_COMPUTE_OPERATION_GREATEREQ:if (paccu>1) accu[paccu-2]=accu[paccu-2]>=accu[paccu-1];paccu--;break; + /* functions */ + case E_COMPUTE_OPERATION_SIN:if (paccu>0) accu[paccu-1]=sin(accu[paccu-1]*3.1415926545/180.0);break; + case E_COMPUTE_OPERATION_COS:if (paccu>0) accu[paccu-1]=cos(accu[paccu-1]*3.1415926545/180.0);break; + case E_COMPUTE_OPERATION_ASIN:if (paccu>0) accu[paccu-1]=asin(accu[paccu-1])*180.0/3.1415926545;break; + case E_COMPUTE_OPERATION_ACOS:if (paccu>0) accu[paccu-1]=acos(accu[paccu-1])*180.0/3.1415926545;break; + case E_COMPUTE_OPERATION_ATAN:if (paccu>0) accu[paccu-1]=atan(accu[paccu-1])*180.0/3.1415926545;break; + case E_COMPUTE_OPERATION_INT:if (paccu>0) accu[paccu-1]=floor(accu[paccu-1]+0.5);break; + case E_COMPUTE_OPERATION_FLOOR:if (paccu>0) accu[paccu-1]=floor(accu[paccu-1]);break; + case E_COMPUTE_OPERATION_ABS:if (paccu>0) accu[paccu-1]=fabs(accu[paccu-1]);break; + case E_COMPUTE_OPERATION_EXP:if (paccu>0) accu[paccu-1]=exp(accu[paccu-1]);break; + case E_COMPUTE_OPERATION_LN:if (paccu>0) accu[paccu-1]=log(accu[paccu-1]);break; + case E_COMPUTE_OPERATION_LOG10:if (paccu>0) accu[paccu-1]=log10(accu[paccu-1]);break; + case E_COMPUTE_OPERATION_SQRT:if (paccu>0) accu[paccu-1]=sqrt(accu[paccu-1]);break; + case E_COMPUTE_OPERATION_LOW:if (paccu>0) accu[paccu-1]=((int)floor(accu[paccu-1]+0.5))&0xFF;break; + case E_COMPUTE_OPERATION_HIGH:if (paccu>0) accu[paccu-1]=(((int)floor(accu[paccu-1]+0.5))&0xFF00)>>8;break; + case E_COMPUTE_OPERATION_PSG:if (paccu>0) accu[paccu-1]=ae->psgfine[((int)floor(accu[paccu-1]+0.5))&0xFF];break; + case E_COMPUTE_OPERATION_RND:if (paccu>0) accu[paccu-1]=rand()%((int)accu[paccu-1]);break; + case E_COMPUTE_OPERATION_FRAC:if (paccu>0) accu[paccu-1]=modf(accu[paccu-1],&dummint);break; + case E_COMPUTE_OPERATION_CEIL:if (paccu>0) accu[paccu-1]=ceil(accu[paccu-1]);break; + case E_COMPUTE_OPERATION_GET_R:if (paccu>0) accu[paccu-1]=((((int)accu[paccu-1])&0xF0)>>4);break; + case E_COMPUTE_OPERATION_GET_V:if (paccu>0) accu[paccu-1]=((((int)accu[paccu-1])&0xF00)>>8);break; + case E_COMPUTE_OPERATION_GET_B:if (paccu>0) accu[paccu-1]=(((int)accu[paccu-1])&0xF);break; + case E_COMPUTE_OPERATION_SET_R:if (paccu>0) accu[paccu-1]=MinMaxInt(accu[paccu-1],0,15)<<4;break; + case E_COMPUTE_OPERATION_SET_V:if (paccu>0) accu[paccu-1]=MinMaxInt(accu[paccu-1],0,15)<<8;break; + case E_COMPUTE_OPERATION_SET_B:if (paccu>0) accu[paccu-1]=MinMaxInt(accu[paccu-1],0,15);break; + default:MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"invalid computing state! (%d)\n",GetExpFile(ae,didx),GetExpLine(ae,didx),computestack[i].operator);paccu=0; + } + if (!paccu) { + if (zeexpression[0]=='&') { + MakeError(ae,GetExpFile(ae,didx),GetExpLine(ae,didx),"Missing operand for calculation [%s] Did you use & for an hexadecimal value?\n",TradExpression(zeexpression)); + } else { + MakeError(ae,GetExpFile(ae,didx),GetExpLine(ae,didx),"Missing operand for calculation [%s]\n",TradExpression(zeexpression)); + } + accu_err=1; + break; + } + } + } + if (!original) { + MemFree(zeexpression); + } + if (paccu==1) { + return accu[0]; + } else if (!accu_err) { + if (paccu) { + MakeError(ae,GetExpFile(ae,didx),GetExpLine(ae,didx),"Missing operator\n"); + } else { + MakeError(ae,GetExpFile(ae,didx),GetExpLine(ae,didx),"Missing operand for calculation\n"); + } + return 0; + } else { + return 0; + } +} +int RoundComputeExpressionCore(struct s_assenv *ae,char *zeexpression,int ptr,int didx) { + return floor(ComputeExpressionCore(ae,zeexpression,ptr,didx)+ae->rough); +} + +void ExpressionSetDicoVar(struct s_assenv *ae,char *name, double v) +{ + #undef FUNC + #define FUNC "ExpressionSetDicoVar" + + struct s_expr_dico curdic; + curdic.name=TxtStrDup(name); + curdic.crc=GetCRC(name); + curdic.v=v; + curdic.iw=ae->idx; + curdic.autorise_export=ae->autorise_export; + //ObjectArrayAddDynamicValueConcat((void**)&ae->dico,&ae->idic,&ae->mdic,&curdic,sizeof(curdic)); + if (SearchLabel(ae,curdic.name,curdic.crc)) { + MakeError(ae,GetCurrentFile(ae),GetExpLine(ae,0),"cannot create variable [%s] as there is already a label with the same name\n",name); + MemFree(curdic.name); + return; + } + InsertDicoToTree(ae,&curdic); +} + +double ComputeExpression(struct s_assenv *ae,char *expr, int ptr, int didx, int expected_eval) +{ + #undef FUNC + #define FUNC "ComputeExpression" + + char *ptr_exp,*ptr_exp2,backupeval; + int crc,idic,idx=0,ialias,touched,hasformula=0; + double v,vl; + struct s_alias curalias; + struct s_expr_dico *curdic; + char *minibuffer; + + while (!ae->AutomateExpressionDecision[((int)expr[idx])&0xFF]) idx++; + + switch (ae->AutomateExpressionDecision[((int)expr[idx])&0xFF]) { + /***************************************** + M A K E A L I A S + *****************************************/ + case '~': + memset(&curalias,0,sizeof(curalias)); + ptr_exp=expr+idx; + *ptr_exp=0; // on scinde l'alias de son texte + ptr_exp2=ptr_exp+1; +#if TRACE_COMPUTE_EXPRESSION +printf("MakeAlias (1) EXPR=[%s EQU %s]\n",expr,ptr_exp2); +#endif + + /* alias locaux ou de proximité */ + if (strchr("@.",expr[0])) { +#if TRACE_COMPUTE_EXPRESSION +printf("WARNING! alias is local! [%s]\n",expr); +#endif + /* local label creation does not handle formula in tags */ + curalias.alias=TranslateTag(ae,TxtStrDup(expr),&touched,0,E_TAGOPTION_NONE); + curalias.alias=MakeLocalLabel(ae,curalias.alias,NULL); + hasformula=1; + } else if (strchr(expr,'{')) { +#if TRACE_COMPUTE_EXPRESSION +printf("WARNING! alias has tag! [%s]\n",expr); +#endif + /* alias name contains formula */ + curalias.alias=TranslateTag(ae,TxtStrDup(expr),&touched,0,E_TAGOPTION_NONE); +#if TRACE_COMPUTE_EXPRESSION +printf("MakeAlias (2) EXPR=[%s EQU %s]\n",expr,ptr_exp2); +#endif + hasformula=1; + } else { + curalias.alias=TxtStrDup(expr); + } + curalias.crc=GetCRC(curalias.alias); + if ((ialias=SearchAlias(ae,curalias.crc,curalias.alias))>=0) { + MakeError(ae,GetCurrentFile(ae),GetExpLine(ae,0),"Duplicate alias [%s]\n",expr); + MemFree(curalias.alias); + } else if (SearchDico(ae,curalias.alias,curalias.crc)) { + MakeError(ae,GetCurrentFile(ae),GetExpLine(ae,0),"Alias cannot override existing variable [%s]\n",expr); + MemFree(curalias.alias); + } else { + curalias.translation=MemMalloc(strlen(ptr_exp2)+1+2); + sprintf(curalias.translation,"(%s)",ptr_exp2); +#if TRACE_COMPUTE_EXPRESSION +printf("MakeAlias (3) EXPR=[%s EQU %s]\n",expr,ptr_exp2); +printf("alias translation [%s] -> ",curalias.translation);fflush(stdout); +#endif + ExpressionFastTranslate(ae,&curalias.translation,2); // FAST type 2 +#if TRACE_COMPUTE_EXPRESSION +printf("%s\n",curalias.translation); +#endif + curalias.len=strlen(curalias.translation); + curalias.autorise_export=ae->autorise_export; + curalias.iw=ae->idx; + ObjectArrayAddDynamicValueConcat((void**)&ae->alias,&ae->ialias,&ae->malias,&curalias,sizeof(curalias)); + CheckAndSortAliases(ae); + } + *ptr_exp='~'; // on remet l'alias en place +#if TRACE_COMPUTE_EXPRESSION +printf("MakeAlias end with alias=[%s]=[%s]\n",curalias.alias,curalias.translation); +printf("***********\n"); +#endif + return 0; + /***************************************** + S E T V A R + *****************************************/ + case '=': + /* patch NOT */ + if (ae->AutomateExpressionDecision[((int)expr[idx+1])&0xFF]==0 || expr[idx+1]=='!') { + if (expected_eval) { + if (ae->maxam) { + /* maxam mode AND expected a value -> force comparison */ + } else { + MakeError(ae,GetCurrentFile(ae),GetExpLine(ae,0),"meaningless use of an expression [%s]\n",expr); + return 0; + } + } else { + /* affectation */ + if ((expr[0]<'A' || expr[0]>'Z') && expr[0]!='_') { + MakeError(ae,GetCurrentFile(ae),GetExpLine(ae,0),"variable name must begin by a letter or '_' [%s]\n",expr); + return 0; + } else { + ptr_exp=expr+idx; + v=ComputeExpressionCore(ae,ptr_exp+1,ptr,didx); + *ptr_exp=0; + crc=GetCRC(expr); + if ((ialias=SearchAlias(ae,crc,expr))>=0) { + MakeError(ae,GetCurrentFile(ae),GetExpLine(ae,0),"Variable cannot override existing alias [%s]\n",expr); + return 0; + } + curdic=SearchDico(ae,expr,crc); + if (curdic) { + /* on affecte */ + curdic->v=v; + } else { + /* on cree une nouvelle variable */ + ExpressionSetDicoVar(ae,expr,v); + } + *ptr_exp='='; + return v; + } + } + } + break; + /***************************************** + P U R E E X P R E S S I O N + *****************************************/ + default:break; + } + return ComputeExpressionCore(ae,expr,ptr,didx); +} +int RoundComputeExpression(struct s_assenv *ae,char *expr, int ptr, int didx, int expression_expected) { + return floor(ComputeExpression(ae,expr,ptr,didx,expression_expected)+ae->rough); +} + +/* + ExpressionFastTranslate + + purpose: translate all known symbols in an expression (especially variables acting like counters) +*/ +void ExpressionFastTranslate(struct s_assenv *ae, char **ptr_expr, int fullreplace) +{ + #undef FUNC + #define FUNC "ExpressionFastTranslate" + + struct s_label *curlabel; + struct s_expr_dico *curdic; + static char *varbuffer=NULL; + static int ivar=0,maxivar=1; + char curval[256]={0}; + int c,lenw=0,idx=0,crc,startvar,newlen,ialias,found_replace,yves,dek,reidx,lenbuf,rlen,tagoffset; + double v; + char tmpuchar[16]; + char *expr,*locallabel; + int curly=0,curlyflag=0; + char *Automate; + int recurse=-1,recursecount=0; + + if (!ae || !ptr_expr) { + if (varbuffer) MemFree(varbuffer); + varbuffer=NULL; + maxivar=1; + ivar=0; + return; + } + /* be sure to have at least some bytes allocated */ + StateMachineResizeBuffer(&varbuffer,128,&maxivar); + expr=*ptr_expr; + +//printf("fast [%s]\n",expr); + + while (!ae->AutomateExpressionDecision[((int)expr[idx])&0xFF]) idx++; + + switch (ae->maxam) { + default: + case 0: /* full check */ + if (expr[idx]=='~' || (expr[idx]=='=' && expr[idx+1]!='=')) {reidx=idx+1;break;} + reidx=0; + break; + case 1: /* partial check with maxam */ + if (expr[idx]=='~') {reidx=idx+1;break;} + reidx=0; + break; + } + + idx=0; + /* is there ascii char? */ + while ((c=expr[idx])!=0) { + if (c=='\'' || c=='"') { + /* echappement */ + if (expr[idx+1]=='\\') { + if (expr[idx+2] && expr[idx+3]==c) { + /* no charset conversion for escaped chars */ + c=expr[idx+2]; + switch (c) { + case 'b':c='\b';break; + case 'v':c='\v';break; + case 'f':c='\f';break; + case '0':c='\0';break; + case 'r':c='\r';break; + case 'n':c='\n';break; + case 't':c='\t';break; + default:break; + } + sprintf(tmpuchar,"#%03X",c); + memcpy(expr+idx,tmpuchar,4); + idx+=3; + } else { + MakeError(ae,GetCurrentFile(ae),GetExpLine(ae,0),"expression [%s] - Only single escaped char may be quoted\n",expr); + expr[0]=0; + return; + } + } else if (expr[idx+1] && expr[idx+2]==c) { + sprintf(tmpuchar,"#%02X",ae->charset[(int)expr[idx+1]]); + memcpy(expr+idx,tmpuchar,3); + idx+=2; + } else { + MakeError(ae,GetCurrentFile(ae),GetExpLine(ae,0),"expression [%s] - Only single char may be quoted\n",expr); + expr[0]=0; + return; + } + } + idx++; + } + + idx=reidx; + while ((c=expr[idx])!=0) { + switch (c) { + /* operator / parenthesis */ + case '!': + case '=': + case '>': + case '<': + case '(': + case ')': + case ']': + case '[': + case '*': + case '/': + case '+': + case '~': + case '-': + case '^': + case 'm': + case '|': + case '&': + idx++; + break; + default: + startvar=idx; + if (ae->AutomateExpressionValidCharFirst[((int)c)&0xFF]) { + varbuffer[ivar++]=c; + if (c=='{') { + /* this is only tag and not a formula */ + curly++; + } + StateMachineResizeBuffer(&varbuffer,ivar,&maxivar); + idx++; + c=expr[idx]; + + Automate=ae->AutomateExpressionValidChar; + while (Automate[((int)c)&0xFF]) { + if (c=='{') { + curly++; + curlyflag=1; + Automate=ae->AutomateExpressionValidCharExtended; + } else if (c=='}') { + curly--; + if (!curly) { + Automate=ae->AutomateExpressionValidChar; + } + } + varbuffer[ivar++]=c; + StateMachineResizeBuffer(&varbuffer,ivar,&maxivar); + idx++; + c=expr[idx]; + } + } + varbuffer[ivar]=0; + if (!ivar) { + MakeError(ae,GetCurrentFile(ae),GetExpLine(ae,0),"invalid expression [%s] c=[%c] idx=%d\n",expr,c,idx); + return; + } else if (curly) { + MakeError(ae,GetCurrentFile(ae),GetExpLine(ae,0),"wrong curly brackets in expression [%s]\n",expr); + return; + } + } + if (ivar && (varbuffer[0]<'0' || varbuffer[0]>'9')) { + /* numbering var or label */ + if (curlyflag) { + char *minivarbuffer; + int touched; +//printf("ExpressionFastTranslate curly\n"); + minivarbuffer=TranslateTag(ae,TxtStrDup(varbuffer), &touched,0,E_TAGOPTION_NONE|(fullreplace?0:E_TAGOPTION_PRESERVE)); + StateMachineResizeBuffer(&varbuffer,strlen(minivarbuffer)+1,&maxivar); + strcpy(varbuffer,minivarbuffer); + newlen=strlen(varbuffer); + lenw=strlen(expr); + /* must update source */ + if (newlen>ivar) { + /* realloc bigger */ + expr=*ptr_expr=MemRealloc(expr,lenw+newlen-ivar+1); + } + if (newlen!=ivar ) { + lenw=strlen(expr); + MemMove(expr+startvar+newlen,expr+startvar+ivar,lenw-startvar-ivar+1); + } + strncpy(expr+startvar,minivarbuffer,newlen); /* copy without zero terminator */ + idx=startvar+newlen; + /***/ + MemFree(minivarbuffer); + curlyflag=0; + /******* ivar must be updated in case of label or alias following ***********/ + ivar=newlen; + } + + /* recherche dans dictionnaire et remplacement */ + crc=GetCRC(varbuffer); + found_replace=0; + /* pour les affectations ou les tests conditionnels on ne remplace pas le dico (pour le Push oui par contre!) */ + if (fullreplace) { + if (varbuffer[0]=='$' && !varbuffer[1]) { + #ifdef OS_WIN + snprintf(curval,sizeof(curval)-1,"%d",ae->codeadr); + newlen=strlen(curval); + #else + newlen=snprintf(curval,sizeof(curval)-1,"%d",ae->codeadr); + #endif + lenw=strlen(expr); + if (newlen>ivar) { + /* realloc bigger */ + expr=*ptr_expr=MemRealloc(expr,lenw+newlen-ivar+1); + } + if (newlen!=ivar ) { + MemMove(expr+startvar+newlen,expr+startvar+ivar,lenw-startvar-ivar+1); + found_replace=1; + } + strncpy(expr+startvar,curval,newlen); /* copy without zero terminator */ + idx=startvar+newlen; + ivar=0; + found_replace=1; + } else { + curdic=SearchDico(ae,varbuffer,crc); + if (curdic) { + v=curdic->v; +//printf("ExpressionFastTranslate (full) -> replace var (%s=%0.1lf)\n",varbuffer,v); + + #ifdef OS_WIN + snprintf(curval,sizeof(curval)-1,"%lf",v); + newlen=TrimFloatingPointString(curval); + #else + snprintf(curval,sizeof(curval)-1,"%lf",v); + newlen=TrimFloatingPointString(curval); + #endif + lenw=strlen(expr); + if (newlen>ivar) { + /* realloc bigger */ + expr=*ptr_expr=MemRealloc(expr,lenw+newlen-ivar+1); + } + if (newlen!=ivar ) { + MemMove(expr+startvar+newlen,expr+startvar+ivar,lenw-startvar-ivar+1); + } + strncpy(expr+startvar,curval,newlen); /* copy without zero terminator */ + idx=startvar+newlen; + ivar=0; + found_replace=1; + } + } + } + /* on cherche aussi dans les labels existants */ + if (!found_replace) { + curlabel=SearchLabel(ae,varbuffer,crc); + if (curlabel) { + if (!curlabel->lz || ae->stage>1) { + yves=curlabel->ptr; + + #ifdef OS_WIN + snprintf(curval,sizeof(curval)-1,"%d",yves); + newlen=strlen(curval); + #else + newlen=snprintf(curval,sizeof(curval)-1,"%d",yves); + #endif + lenw=strlen(expr); + if (newlen>ivar) { + /* realloc bigger */ + expr=*ptr_expr=MemRealloc(expr,lenw+newlen-ivar+1); + } + if (newlen!=ivar ) { + MemMove(expr+startvar+newlen,expr+startvar+ivar,lenw-startvar-ivar+1); + } + strncpy(expr+startvar,curval,newlen); /* copy without zero terminator */ + found_replace=1; + idx=startvar+newlen; + ivar=0; + } + } + } + /* non trouve on cherche dans les alias */ + if (!found_replace) { + if ((ialias=SearchAlias(ae,crc,varbuffer))>=0) { + newlen=ae->alias[ialias].len; + lenw=strlen(expr); + /* infinite replacement check */ + if (recurse<=startvar) { + /* recurse maximum count is a mix of alias len and alias number */ + if (recursecount>ae->ialias+ae->alias[ialias].len) { + if (strchr(expr,'~')!=NULL) *strchr(expr,'~')=0; + MakeError(ae,GetCurrentFile(ae),GetExpLine(ae,0),"alias definition of %s has infinite recursivity\n",expr); + expr[0]=0; /* avoid some errors due to shitty definition */ + return; + } else { + recursecount++; + } + } + if (newlen>ivar) { + /* realloc bigger */ + expr=*ptr_expr=MemRealloc(expr,lenw+newlen-ivar+1); + } + if (newlen!=ivar) { + MemMove(expr+startvar+newlen,expr+startvar+ivar,lenw-startvar-ivar+1); + } + strncpy(expr+startvar,ae->alias[ialias].translation,newlen); /* copy without zero terminator */ + found_replace=1; + /* need to parse again alias because of delayed declarations */ + recurse=startvar; + idx=startvar; + ivar=0; + } else { + } + } + if (!found_replace) { + //printf("fasttranslate test local label\n"); + /* non trouve c'est peut-etre un label local - mais pas de l'octal */ + if (varbuffer[0]=='@' && (varbuffer[1]<'0' || varbuffer[1]>'9')) { + char *zepoint; + lenbuf=strlen(varbuffer); +//printf("MakeLocalLabel(ae,varbuffer,&dek); (1)\n"); + locallabel=MakeLocalLabel(ae,varbuffer,&dek); +//printf("exprin =[%s] rlen=%d dek-lenbuf=%d\n",expr,rlen,dek-lenbuf); + /*** le grand remplacement ***/ + /* local to macro or loop */ + rlen=strlen(expr+startvar+lenbuf)+1; + expr=*ptr_expr=MemRealloc(expr,strlen(expr)+dek+1); + /* move end of expression in order to insert local ID */ + zepoint=strchr(varbuffer,'.'); + if (zepoint) { + /* far proximity access */ + int suffixlen,dotpos; + dotpos=(zepoint-varbuffer); + suffixlen=lenbuf-dotpos; + + MemMove(expr+startvar+dotpos+dek,expr+startvar+dotpos,rlen+suffixlen); + strncpy(expr+startvar+dotpos,locallabel,dek); + } else { + /* legacy */ + MemMove(expr+startvar+lenbuf+dek,expr+startvar+lenbuf,rlen); + strncpy(expr+startvar+lenbuf,locallabel,dek); + } + idx+=dek; + MemFree(locallabel); + found_replace=1; +//printf("exprout=[%s]\n",expr); + } else if (varbuffer[0]=='.' && (varbuffer[1]<'0' || varbuffer[1]>'9')) { + /* proximity label */ + lenbuf=strlen(varbuffer); +//printf("MakeLocalLabel(ae,varbuffer,&dek); (2)\n"); + locallabel=MakeLocalLabel(ae,varbuffer,&dek); + /*** le grand remplacement ***/ + rlen=strlen(expr+startvar+lenbuf)+1; + dek=strlen(locallabel); +//printf("exprin =[%s] rlen=%d dek-lenbuf=%d\n",expr,rlen,dek-lenbuf); + expr=*ptr_expr=MemRealloc(expr,strlen(expr)+dek-lenbuf+1); + MemMove(expr+startvar+dek,expr+startvar+lenbuf,rlen); + strncpy(expr+startvar,locallabel,dek); + idx+=dek-lenbuf; + MemFree(locallabel); +//printf("exprout=[%s]\n",expr); + +//@@TODO ajouter une recherche d'alias? + + } else if (varbuffer[0]=='{') { + if (strncmp(varbuffer,"{BANK}",6)==0 || strncmp(varbuffer,"{PAGE}",6)==0) tagoffset=6; else + if (strncmp(varbuffer,"{PAGESET}",9)==0) tagoffset=9; else + if (strncmp(varbuffer,"{SIZEOF}",8)==0) tagoffset=8; else + { + tagoffset=0; + MakeError(ae,GetCurrentFile(ae),GetExpLine(ae,0),"Unknown prefix tag\n"); + } + + if (varbuffer[tagoffset]=='@') { + char *zepoint; + startvar+=tagoffset; + lenbuf=strlen(varbuffer+tagoffset); +//printf("MakeLocalLabel(ae,varbuffer,&dek); (3)\n"); + locallabel=MakeLocalLabel(ae,varbuffer+tagoffset,&dek); + /*** le grand remplacement ***/ + rlen=strlen(expr+startvar+lenbuf)+1; + expr=*ptr_expr=MemRealloc(expr,strlen(expr)+dek+1); + /* move end of expression in order to insert local ID */ + zepoint=strchr(varbuffer,'.'); + if (zepoint) { + /* far proximity access */ + int suffixlen,dotpos; + dotpos=(zepoint-varbuffer); + suffixlen=lenbuf-dotpos; + + MemMove(expr+startvar+dotpos+dek,expr+startvar+dotpos,rlen+suffixlen); + strncpy(expr+startvar+dotpos,locallabel,dek); + } else { + /* legacy */ + MemMove(expr+startvar+lenbuf+dek,expr+startvar+lenbuf,rlen); + strncpy(expr+startvar+lenbuf,locallabel,dek); + } + idx+=dek; + MemFree(locallabel); + found_replace=1; + } else if (varbuffer[tagoffset]=='$') { + int tagvalue=-1; + if (strcmp(varbuffer,"{BANK}$")==0) { + if (ae->forcecpr) { + if (ae->activebank<32) { + tagvalue=ae->activebank; + } else { + MakeError(ae,GetCurrentFile(ae),GetExpLine(ae,0),"expression [%s] cannot use BANK $ in a temporary space!\n",TradExpression(expr)); + tagvalue=0; + } + } else if (ae->forcesnapshot) { + if (ae->activebank<BANK_MAX_NUMBER) { + /* on autorise le préfixe BANK en snapshot avec une subtilité */ + if (ae->bankset[ae->activebank>>2]) { + tagvalue=ae->activebank+(ae->codeadr>>14); /* dans un bankset on tient compte de l'adresse */ + } else { + tagvalue=ae->activebank; + } + + } else { + MakeError(ae,GetCurrentFile(ae),GetExpLine(ae,0),"expression [%s] cannot use BANK $ in a temporary space!\n",TradExpression(expr)); + tagvalue=0; + } + } + } else if (strcmp(varbuffer,"{PAGE}$")==0) { + if (ae->activebank<BANK_MAX_NUMBER) { + if (ae->bankset[ae->activebank>>2]) { + tagvalue=ae->bankgate[(ae->activebank&0x1FC)+(ae->codeadr>>14)]; + } else { + tagvalue=ae->bankgate[ae->activebank]; + } + } else { + MakeError(ae,GetCurrentFile(ae),GetExpLine(ae,0),"expression [%s] cannot use PAGE $ in a temporary space!\n",TradExpression(expr)); + tagvalue=ae->activebank; + } + } else if (strcmp(varbuffer,"{PAGESET}$")==0) { + if (ae->activebank<BANK_MAX_NUMBER) { + tagvalue=ae->setgate[ae->activebank]; + //if (ae->activebank>3) tagvalue=((ae->activebank>>2)-1)*8+0xC2; else tagvalue=0xC0; + } else { + MakeError(ae,GetCurrentFile(ae),GetExpLine(ae,0),"expression [%s] cannot use PAGESET $ in a temporary space!\n",TradExpression(expr)); + tagvalue=ae->activebank; + } + } + /* replace */ + #ifdef OS_WIN + snprintf(curval,sizeof(curval)-1,"%d",tagvalue); + newlen=strlen(curval); + #else + newlen=snprintf(curval,sizeof(curval)-1,"%d",tagvalue); + #endif + lenw=strlen(expr); + if (newlen>ivar) { + /* realloc bigger */ + expr=*ptr_expr=MemRealloc(expr,lenw+newlen-ivar+1); + } + if (newlen!=ivar ) { + MemMove(expr+startvar+newlen,expr+startvar+ivar,lenw-startvar-ivar+1); + found_replace=1; + } + strncpy(expr+startvar,curval,newlen); /* copy without zero terminator */ + idx=startvar+newlen; + ivar=0; + found_replace=1; + } + } + } + + + + + + + if (!found_replace && strcmp(varbuffer,"REPEAT_COUNTER")==0) { + if (ae->ir) { + yves=ae->repeat[ae->ir-1].repeat_counter; + #ifdef OS_WIN + snprintf(curval,sizeof(curval)-1,"%d",yves); + newlen=strlen(curval); + #else + newlen=snprintf(curval,sizeof(curval)-1,"%d",yves); + #endif + lenw=strlen(expr); + if (newlen>ivar) { + /* realloc bigger */ + expr=*ptr_expr=MemRealloc(expr,lenw+newlen-ivar+1); + } + if (newlen!=ivar ) { + MemMove(expr+startvar+newlen,expr+startvar+ivar,lenw-startvar-ivar+1); + found_replace=1; + } + strncpy(expr+startvar,curval,newlen); /* copy without zero terminator */ + found_replace=1; + idx=startvar+newlen; + ivar=0; + } else { + MakeError(ae,GetCurrentFile(ae),GetExpLine(ae,0),"cannot use REPEAT_COUNTER outside repeat loop\n"); + } + } + if (!found_replace && strcmp(varbuffer,"WHILE_COUNTER")==0) { + if (ae->iw) { + yves=ae->whilewend[ae->iw-1].while_counter; + #ifdef OS_WIN + snprintf(curval,sizeof(curval)-1,"%d",yves); + newlen=strlen(curval); + #else + newlen=snprintf(curval,sizeof(curval)-1,"%d",yves); + #endif + lenw=strlen(expr); + if (newlen>ivar) { + /* realloc bigger */ + expr=*ptr_expr=MemRealloc(expr,lenw+newlen-ivar+1); + } + if (newlen!=ivar ) { + MemMove(expr+startvar+newlen,expr+startvar+ivar,lenw-startvar-ivar+1); + found_replace=1; + } + strncpy(expr+startvar,curval,newlen); /* copy without zero terminator */ + found_replace=1; + idx=startvar+newlen; + ivar=0; + } else { + MakeError(ae,GetCurrentFile(ae),GetExpLine(ae,0),"cannot use WHILE_COUNTER outside repeat loop\n"); + } + } + /* unknown symbol -> add to used symbol pool */ + if (!found_replace) { + InsertUsedToTree(ae,varbuffer,crc); + } + } + ivar=0; + } +} + +void PushExpression(struct s_assenv *ae,int iw,enum e_expression zetype) +{ + #undef FUNC + #define FUNC "PushExpression" + + struct s_expression curexp={0}; + int startptr=0; + + if (!ae->nocode) { + curexp.iw=iw; + curexp.wptr=ae->outputadr; + curexp.zetype=zetype; + curexp.ibank=ae->activebank; + curexp.iorgzone=ae->io-1; + curexp.lz=ae->lz; + /* on traduit de suite les variables du dictionnaire pour les boucles et increments + SAUF si c'est une affectation */ + if (!ae->wl[iw].e) { + switch (zetype) { + case E_EXPRESSION_V16C: + /* check non register usage */ + switch (GetCRC(ae->wl[iw].w)) { + case CRC_IX: + case CRC_IY: + case CRC_MIX: + case CRC_MIY: + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"invalid register usage\n",ae->maxptr); + default:break; + } + case E_EXPRESSION_J8: + case E_EXPRESSION_V8: + case E_EXPRESSION_V16: + case E_EXPRESSION_IM:startptr=-1; + break; + case E_EXPRESSION_IV8: + case E_EXPRESSION_IV81: + case E_EXPRESSION_IV16:startptr=-2; + break; + case E_EXPRESSION_3V8:startptr=-3; + break; + case E_EXPRESSION_RUN: + case E_EXPRESSION_ZXRUN: + case E_EXPRESSION_ZXSTACK: + default:break; + } + /* hack pourri pour gérer le $ */ + ae->codeadr+=startptr; + /* ok mais les labels locaux des macros? */ + if (ae->ir || ae->iw || ae->imacro) { + curexp.reference=TxtStrDup(ae->wl[iw].w); + ExpressionFastTranslate(ae,&curexp.reference,1); + } else { + ExpressionFastTranslate(ae,&ae->wl[iw].w,1); + } + ae->codeadr-=startptr; + } + /* calcul adresse de reference et post-incrementation pour sauter les data */ + switch (zetype) { + case E_EXPRESSION_J8:curexp.ptr=ae->codeadr-1;ae->outputadr++;ae->codeadr++;break; + case E_EXPRESSION_0V8:curexp.ptr=ae->codeadr;ae->outputadr++;ae->codeadr++;break; + case E_EXPRESSION_V8:curexp.ptr=ae->codeadr-1;ae->outputadr++;ae->codeadr++;break; + case E_EXPRESSION_0V16:curexp.ptr=ae->codeadr;ae->outputadr+=2;ae->codeadr+=2;break; + case E_EXPRESSION_0V32:curexp.ptr=ae->codeadr;ae->outputadr+=4;ae->codeadr+=4;break; + case E_EXPRESSION_0VR:curexp.ptr=ae->codeadr;ae->outputadr+=5;ae->codeadr+=5;break; + case E_EXPRESSION_V16C: + case E_EXPRESSION_V16:curexp.ptr=ae->codeadr-1;ae->outputadr+=2;ae->codeadr+=2;break; + case E_EXPRESSION_IV81:curexp.ptr=ae->codeadr-2;ae->outputadr++;ae->codeadr++;break; + case E_EXPRESSION_IV8:curexp.ptr=ae->codeadr-2;ae->outputadr++;ae->codeadr++;break; + case E_EXPRESSION_3V8:curexp.ptr=ae->codeadr-3;ae->outputadr++;ae->codeadr++;break; + case E_EXPRESSION_IV16:curexp.ptr=ae->codeadr-2;ae->outputadr+=2;ae->codeadr+=2;break; + case E_EXPRESSION_RST:curexp.ptr=ae->codeadr;ae->outputadr++;ae->codeadr++;break; + case E_EXPRESSION_IM:curexp.ptr=ae->codeadr-1;ae->outputadr++;ae->codeadr++;break; + case E_EXPRESSION_RUN:break; + case E_EXPRESSION_ZXRUN:break; + case E_EXPRESSION_ZXSTACK:break; + } + /* le contrôle n'est pas bon avec les DEFB, DEFW, ... -> @@TODO */ + if (ae->outputadr<=ae->maxptr) { + ObjectArrayAddDynamicValueConcat((void **)&ae->expression,&ae->ie,&ae->me,&curexp,sizeof(curexp)); + } else { + /* to avoid double error message */ + if (!ae->stop) MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"output exceed limit %d\n",ae->maxptr); else MaxError(ae); + ae->stop=1; + return; + } + } else { + switch (zetype) { + case E_EXPRESSION_J8:ae->outputadr++;ae->codeadr++;break; + case E_EXPRESSION_0V8:ae->outputadr++;ae->codeadr++;break; + case E_EXPRESSION_V8:ae->outputadr++;ae->codeadr++;break; + case E_EXPRESSION_0V16:ae->outputadr+=2;ae->codeadr+=2;break; + case E_EXPRESSION_0V32:ae->outputadr+=4;ae->codeadr+=4;break; + case E_EXPRESSION_0VR:ae->outputadr+=5;ae->codeadr+=5;break; + case E_EXPRESSION_V16C: + case E_EXPRESSION_V16:ae->outputadr+=2;ae->codeadr+=2;break; + case E_EXPRESSION_IV81:ae->outputadr++;ae->codeadr++;break; + case E_EXPRESSION_IV8:ae->outputadr++;ae->codeadr++;break; + case E_EXPRESSION_3V8:ae->outputadr++;ae->codeadr++;break; + case E_EXPRESSION_IV16:ae->outputadr+=2;ae->codeadr+=2;break; + case E_EXPRESSION_RST:ae->outputadr++;ae->codeadr++;break; + case E_EXPRESSION_IM:ae->outputadr++;ae->codeadr++;break; + case E_EXPRESSION_RUN:break; + case E_EXPRESSION_ZXRUN:break; + case E_EXPRESSION_ZXSTACK:break; + } + if (ae->outputadr<=ae->maxptr) { + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"NOCODE output exceed limit %d\n",GetCurrentFile(ae),ae->wl[ae->idx].l,ae->maxptr); + FreeAssenv(ae);exit(3); + } + } +} + +/* +The CP/M 2.2 directory has only one type of entry: + +UU F1 F2 F3 F4 F5 F6 F7 F8 T1 T2 T3 EX S1 S2 RC .FILENAMETYP.... +AL AL AL AL AL AL AL AL AL AL AL AL AL AL AL AL ................ + +UU = User number. 0-15 (on some systems, 0-31). The user number allows multiple + files of the same name to coexist on the disc. + User number = 0E5h => File deleted +Fn - filename +Tn - filetype. The characters used for these are 7-bit ASCII. + The top bit of T1 (often referred to as T1') is set if the file is + read-only. + T2' is set if the file is a system file (this corresponds to "hidden" on + other systems). +EX = Extent counter, low byte - takes values from 0-31 +S2 = Extent counter, high byte. + + An extent is the portion of a file controlled by one directory entry. + If a file takes up more blocks than can be listed in one directory entry, + it is given multiple entries, distinguished by their EX and S2 bytes. The + formula is: Entry number = ((32*S2)+EX) / (exm+1) where exm is the + extent mask value from the Disc Parameter Block. + +S1 - reserved, set to 0. +RC - Number of records (1 record=128 bytes) used in this extent, low byte. + The total number of records used in this extent is + + (EX & exm) * 128 + RC + + If RC is 80h, this extent is full and there may be another one on the disc. + File lengths are only saved to the nearest 128 bytes. + +AL - Allocation. Each AL is the number of a block on the disc. If an AL + number is zero, that section of the file has no storage allocated to it + (ie it does not exist). For example, a 3k file might have allocation + 5,6,8,0,0.... - the first 1k is in block 5, the second in block 6, the + third in block 8. + AL numbers can either be 8-bit (if there are fewer than 256 blocks on the + disc) or 16-bit (stored low byte first). +*/ +int EDSK_getblockid(int *fb) { + #undef FUNC + #define FUNC "EDSK_getblockid" + + int i; + for (i=0;i<180;i++) { + if (fb[i]) { + return i; + } + } + return -1; +} +int EDSK_getdirid(struct s_edsk_wrapper *curwrap) { + #undef FUNC + #define FUNC "EDSK_getdirid" + + int ie; + for (ie=0;ie<64;ie++) { + if (curwrap->entry[ie].user==0xE5) { +#if TRACE_EDSK + printf("getdirid returns %d\n",ie); +#endif + return ie; + } + } + return -1; +} +char *MakeAMSDOS_name(struct s_assenv *ae, char *filename) +{ + #undef FUNC + #define FUNC "MakeAMSDOS_name" + + static char amsdos_name[12]; + int i,ia; + char *pp; + /* warning */ + if (strlen(filename)>12) { + if (!ae->nowarning) rasm_printf(ae,KWARNING"Warning - filename [%s] too long for AMSDOS, will be truncated\n",filename); + } else if ((pp=strchr(filename,'.'))!=NULL) { + if (pp-filename>8) { + if (!ae->nowarning) rasm_printf(ae,KWARNING"Warning - filename [%s] too long for AMSDOS, will be truncated\n",filename); + } + } + /* copy filename */ + for (i=0;filename[i]!=0 && filename[i]!='.' && i<8;i++) { + amsdos_name[i]=toupper(filename[i]); + } + /* fill with spaces */ + for (ia=i;ia<8;ia++) { + amsdos_name[ia]=0x20; + } + /* looking for extension */ + for (;filename[i]!=0 && filename[i]!='.';i++); + /* then copy it if any */ + if (filename[i]=='.') { + i++; + for (ia=0;filename[i]!=0 && ia<3;ia++) { + amsdos_name[8+ia]=toupper(filename[i++]); + } + } + amsdos_name[11]=0; +#if TRACE_EDSK + printf("MakeAMSDOS_name [%s] -> [%s]\n",filename,amsdos_name); +#endif + return amsdos_name; +} + + +void EDSK_load(struct s_assenv *ae,struct s_edsk_wrapper *curwrap, char *edskfilename, int face) +{ + #undef FUNC + #define FUNC "EDSK_load" + + unsigned char header[256]; + unsigned char *data; + int tracknumber,sidenumber,tracksize,disksize; + int i,b,s,f,t,curtrack,sectornumber,sectorsize,sectorid,reallength; + int currenttrackposition=0,currentsectorposition,tmpcurrentsectorposition; + unsigned char checksectorid[9]; + int curblock=0,curoffset=0; +#if TRACE_EDSK + printf("EDSK_Load('%s',%d);",edskfilename,face); +#endif + if (FileReadBinary(edskfilename,(char*)&header,0x100)!=0x100) { + rasm_printf(ae,KERROR"Cannot read EDSK header of [%s]!\n",edskfilename); + FreeAssenv(ae);exit(ABORT_ERROR); + } + if (strncmp((char *)header,"MV - CPC",8)==0) { + rasm_printf(ae,KAYGREEN"updating DSK to EDSK [%s] / creator: %s",edskfilename,header+34); + + tracknumber=header[34+14]; + sidenumber=header[34+14+1]; + tracksize=header[34+14+1+1]+header[34+14+1+1+1]*256; + rasm_printf(ae,"tracks: %d sides:%d track size:%d",tracknumber,sidenumber,tracksize); + if (tracknumber>40 || sidenumber>2) { + rasm_printf(ae,KERROR"[%s] DSK format is not supported in update mode (ntrack=%d nside=%d)\n",edskfilename,tracknumber,sidenumber); + FreeAssenv(ae);exit(ABORT_ERROR); + } + if (face>=sidenumber) { + rasm_printf(ae,KWARNING"[%s] Warning - DSK has no face %d - DSK updated\n",edskfilename,face); + return; + } + + data=MemMalloc(tracksize*tracknumber*sidenumber); + memset(data,0,tracksize*tracknumber*sidenumber); + if (FileReadBinary(edskfilename,(char *)data,tracksize*tracknumber*sidenumber)!=tracksize*tracknumber*sidenumber) { + rasm_printf(ae,"Cannot read DSK tracks!"); + FreeAssenv(ae);exit(ABORT_ERROR); + } + //loginfo("track data read (%dkb)",tracksize*tracknumber*sidenumber/1024); + f=face; + for (t=0;t<tracknumber;t++) { + curtrack=t*sidenumber+f; + + i=(t*sidenumber+f)*tracksize; + if (strncmp((char *)data+i,"Track-Info\r\n",12)) { + rasm_printf(ae,"Invalid track information block side %d track %d",f,t); + FreeAssenv(ae);exit(ABORT_ERROR); + } + sectornumber=data[i+21]; + sectorsize=data[i+20]; + if (sectornumber!=9 || sectorsize!=2) { + rasm_printf(ae,"Cannot read [%s] Invalid DATA format",edskfilename); + FreeAssenv(ae);exit(ABORT_ERROR); + } + memset(checksectorid,0,sizeof(checksectorid)); + /* we want DATA format */ + for (s=0;s<sectornumber;s++) { + if (t!=data[i+24+8*s]) { + rasm_printf(ae,"Invalid track number in sector %02X track %d",data[i+24+8*s+2],t); + FreeAssenv(ae);exit(ABORT_ERROR); + } + if (f!=data[i+24+8*s+1]) { + rasm_printf(ae,"Invalid side number in sector %02X track %d",data[i+24+8*s+2],t); + FreeAssenv(ae);exit(ABORT_ERROR); + } + if (data[i+24+8*s+2]<0xC1 || data[i+24+8*s+2]>0xC9) { + rasm_printf(ae,"Invalid sector ID in sector %02X track %d",data[i+24+8*s+2],t); + FreeAssenv(ae);exit(ABORT_ERROR); + } else { + checksectorid[data[i+24+8*s+2]-0xC1]=1; + } + if (data[i+24+8*s+3]!=2) { + rasm_printf(ae,"Invalid sector size in sector %02X track %d",data[i+24+8*s+2],t); + FreeAssenv(ae);exit(ABORT_ERROR); + } + } + for (s=0;s<sectornumber;s++) { + if (!checksectorid[s]) { + rasm_printf(ae,"Missing sector %02X track %d",s+0xC1,t); + FreeAssenv(ae);exit(ABORT_ERROR); + } + } + /* piste à piste on lit les blocs DANS L'ORDRE LOGIQUE!!! */ + for (b=0xC1;b<=0xC9;b++) + for (s=0;s<sectornumber;s++) { + if (data[i+24+8*s+2]==b) { + memcpy(&curwrap->blocks[curblock][curoffset],&data[i+0x100+s*512],512); + curoffset+=512; + if (curoffset>=1024) { + curoffset=0; + curblock++; + } + } + } + } + } else if (strncmp((char *)header,"EXTENDED",8)==0) { + rasm_printf(ae,KAYGREEN"updating EDSK [%s] / creator: %-14.14s\n",edskfilename,header+34); + tracknumber=header[34+14]; + sidenumber=header[34+14+1]; + // not in EDSK tracksize=header[34+14+1+1]+header[34+14+1+1+1]*256; +#if TRACE_EDSK + loginfo("tracks: %d sides:%d",tracknumber,sidenumber); +#endif + + if (sidenumber>2) { + rasm_printf(ae,KERROR"[%s] EDSK format is not supported in update mode (ntrack=%d nside=%d)\n",edskfilename,tracknumber,sidenumber); + FreeAssenv(ae);exit(ABORT_ERROR); + } + if (face>=sidenumber) { + rasm_printf(ae,KWARNING"[%s] EDSK has no face %d - DSK updated\n",edskfilename,face); + return; + } + + for (i=disksize=0;i<tracknumber*sidenumber;i++) disksize+=header[0x34+i]*256; +#if TRACE_EDSK + loginfo("total track size: %dkb",disksize/1024); +#endif + + data=MemMalloc(disksize); + memset(data,0,disksize); + if (FileReadBinary(edskfilename,(char *)data,disksize)!=disksize) { + rasm_printf(ae,KERROR"Cannot read DSK tracks!\n"); + FreeAssenv(ae);exit(ABORT_ERROR); + } + + f=face; + for (t=0;t<tracknumber && t<40;t++) { + int track_sectorsize; + + curtrack=t*sidenumber+f; + i=currenttrackposition; + currentsectorposition=i+0x100; + + if (!header[0x34+curtrack] && t<40) { + rasm_printf(ae,KERROR"Unexpected unformated track Side %d Track %02d\n",f,t); + } else { + currenttrackposition+=header[0x34+curtrack]*256; + + if (strncmp((char *)data+i,"Track-Info\r\n",12)) { + rasm_printf(ae,KERROR"Invalid track information block side %d track %d\n",f,t); + FreeAssenv(ae);exit(ABORT_ERROR); + } + sectornumber=data[i+21]; + track_sectorsize=data[i+20]; + if (sectornumber!=9) { + rasm_printf(ae,KERROR"Unsupported track %d (sectornumber=%d sectorsize=%d)\n",t,sectornumber,sectorsize); + FreeAssenv(ae);exit(ABORT_ERROR); + } + memset(checksectorid,0,sizeof(checksectorid)); + /* we want DATA format */ + for (s=0;s<sectornumber;s++) { + sectorid=data[i+24+8*s+2]; + if (sectorid>=0xC1 && sectorid<=0xC9) checksectorid[sectorid-0xC1]=1; else { + rasm_printf(ae,KERROR"invalid sector id %02X for DATA track %d\n",sectorid,t); + return; + } + sectorsize=data[i+24+8*s+3]; + if (sectorsize!=2) { + rasm_printf(ae,KERROR"invalid sector size track %d\n",t); + return; + } + reallength=data[i+24+8*s+6]+data[i+24+8*s+7]*256; /* real length stored */ + if (reallength!=512) { + rasm_printf(ae,KERROR"invalid sector length %d for track %d\n",reallength,t); + return; + } +#if TRACE_EDSK + printf("%02X ",sectorid); +#endif + } + if (track_sectorsize!=2) { + rasm_printf(ae,KWARNING"track %02d has invalid sector size but sectors are OK\n",t); + } +#if TRACE_EDSK + printf("\n"); +#endif + + /* piste à piste on lit les blocs DANS L'ORDRE LOGIQUE!!! */ + for (b=0xC1;b<=0xC9;b++) { + tmpcurrentsectorposition=currentsectorposition; + for (s=0;s<sectornumber;s++) { + if (b==data[i+24+8*s+2]) { + memcpy(&curwrap->blocks[curblock][curoffset],&data[tmpcurrentsectorposition],512); + curoffset+=512; + if (curoffset>=1024) { + curoffset=0; + curblock++; + } + } + reallength=data[i+24+8*s+6]+data[i+24+8*s+7]*256; + tmpcurrentsectorposition+=reallength; + } + } + } + } + + + } else { + rasm_printf(ae,KERROR"file [%s] is not a valid (E)DSK floppy image\n",edskfilename); + FreeAssenv(ae);exit(-923); + } + FileReadBinaryClose(edskfilename); + + /* Rasm management of (e)DSK files is AMSDOS compatible, just need to copy CATalog blocks but sort them... */ + memcpy(&curwrap->entry[0],curwrap->blocks[0],1024); + memcpy(&curwrap->entry[32],curwrap->blocks[1],1024); + /* tri des entrées selon le user */ + qsort(curwrap->entry,64,sizeof(struct s_edsk_wrapper_entry),cmpAmsdosentry); + curwrap->nbentry=64; + for (i=0;i<64;i++) { + if (curwrap->entry[i].user==0xE5) { + curwrap->nbentry=i; + break; + } + } +#if TRACE_EDSK + printf("%d entr%s found\n",curwrap->nbentry,curwrap->nbentry>1?"ies":"y"); + for (i=0;i<curwrap->nbentry;i++) { + printf("[%02d] - ",i); + if (curwrap->entry[i].user<16) { + printf("U%02d [%-8.8s.%c%c%c] %c%c subcpt=#%02X rc=#%02X blocks=",curwrap->entry[i].user,curwrap->entry[i].filename, + curwrap->entry[i].filename[8]&0x7F,curwrap->entry[i].filename[9]&0x7F,curwrap->entry[i].filename[10], + curwrap->entry[i].filename[8]&0x80?'P':'-',curwrap->entry[i].filename[9]&0x80?'H':'-', + curwrap->entry[i].subcpt,curwrap->entry[i].rc); + for (b=0;b<16;b++) if (curwrap->entry[i].blocks[b]) printf("%s%02X",b>0?" ":"",curwrap->entry[i].blocks[b]); else printf("%s ",b>0?" ":""); + if (i&1) printf("\n"); else printf(" | "); + } else { + printf("free entry = rc= blocks= "); + if (i&1) printf("\n"); else printf(" | "); + } + } + if (i&1) printf("\n"); +#endif +} + +struct s_edsk_wrapper *EDSK_select(struct s_assenv *ae,char *edskfilename, int facenumber) +{ + #undef FUNC + #define FUNC "EDSK_select" + + struct s_edsk_wrapper newwrap={0},*curwrap=NULL; + int i; +#if TRACE_EDSK + printf("EDSK_select('%s',%d);\n",edskfilename,facenumber); +#endif + /* check if there is a DSK in memory */ + for (i=0;i<ae->nbedskwrapper;i++) { + if (!strcmp(ae->edsk_wrapper[i].edsk_filename,edskfilename)) { +#if TRACE_EDSK + printf("Found! return %d\n",i); +#endif + return &ae->edsk_wrapper[i]; + } + } + /* not in memory, create an empty struct */ + newwrap.edsk_filename=TxtStrDup(edskfilename); + memset(newwrap.entry,0xE5,sizeof(struct s_edsk_wrapper_entry)*64); + memset(newwrap.blocks[0],0xE5,1024); + memset(newwrap.blocks[1],0xE5,1024); +#if TRACE_EDSK + printf("Not found! create empty struct\n"); +#endif + newwrap.face=facenumber; + ObjectArrayAddDynamicValueConcat((void**)&ae->edsk_wrapper,&ae->nbedskwrapper,&ae->maxedskwrapper,&newwrap,sizeof(struct s_edsk_wrapper)); + /* and load files if the DSK exists on disk */ + curwrap=&ae->edsk_wrapper[ae->nbedskwrapper-1]; + if (FileExists(edskfilename)) { + EDSK_load(ae,curwrap,edskfilename,facenumber); + } + return curwrap; +} + +int EDSK_addfile(struct s_assenv *ae,char *edskfilename,int facenumber, char *filename,unsigned char *indata,int insize, int offset, int run) +{ + #undef FUNC + #define FUNC "EDSK_addfile" + + struct s_edsk_wrapper *curwrap=NULL; + char amsdos_name[12]={0}; + int j,i,ia,ib,ie,filesize,idxdata; + int fb[180],rc,idxb; + unsigned char *data=NULL; + int size=0; + int firstblock; + + curwrap=EDSK_select(ae,edskfilename,facenumber); + /* update struct */ + size=insize+128; + data=MemMalloc(size); + strcpy(amsdos_name,MakeAMSDOS_name(ae,filename)); + memcpy(data,MakeAMSDOSHeader(run,offset,offset+insize,amsdos_name),128); + memcpy(data+128,indata,insize); + /* overwrite check */ +#if TRACE_EDSK + printf("EDSK_addfile will checks %d entr%s for [%s]\n",curwrap->nbentry,curwrap->nbentry>1?"ies":"y",amsdos_name); +#endif + for (i=0;i<curwrap->nbentry;i++) { + if (!strncmp((char *)curwrap->entry[i].filename,amsdos_name,11)) { + if (!ae->edskoverwrite) { + MakeError(ae,NULL,0,"Error - Cannot save [%s] in edsk [%s] with overwrite disabled as the file already exists\n",amsdos_name,edskfilename); + MemFree(data); + return 0; + } else { + /* overwriting previous file */ +#if TRACE_EDSK + printf(" -> reset previous entry %d with 0xE5\n",i); +#endif + memset(&curwrap->entry[i],0xE5,sizeof(struct s_edsk_wrapper_entry)); + } + } + } + /* find free blocks */ +#if TRACE_EDSK + printf("EDSK_addfile find free blocks\n"); +#endif + fb[0]=fb[1]=0; + for (i=2;i<180;i++) fb[i]=1; + for (i=0;i<curwrap->nbentry;i++) { + if (curwrap->entry[i].rc!=0xE5 && curwrap->entry[i].rc!=0) { + /* entry found, compute number of blocks to read */ + rc=curwrap->entry[i].rc/8; + if (curwrap->entry[i].rc%8) rc++; /* adjust value */ + /* mark as used */ + for (j=0;j<rc;j++) { + fb[curwrap->entry[i].blocks[j]]=0; + } + } + } + /* set directory, blocks and data in blocks */ + firstblock=-1; + filesize=size; + idxdata=0; + ia=0; + +#if TRACE_EDSK + printf("Writing [%s] size=%d\n",amsdos_name,size); +#endif + + while (filesize>0) { + if (filesize>16384) { + /* extended entry */ +#if TRACE_EDSK + printf("extended entry for file (filesize=%d)\nblocklist: ",filesize); +#endif + if ((ie=EDSK_getdirid(curwrap))==-1) { + MakeError(ae,NULL,0,"Error - edsk [%s] DIRECTORY FULL\n",edskfilename); + MemFree(data); + return 0; + } + if (curwrap->nbentry<=ie) curwrap->nbentry=ie+1; + idxb=0; + for (i=0;i<16;i++) { + if ((ib=EDSK_getblockid(fb))==-1) { + MakeError(ae,NULL,0,"Error - edsk [%s] DISK FULL\n",edskfilename); + MemFree(data); + return 0; + } else { + if (firstblock==-1) firstblock=ib; + +#if TRACE_EDSK + printf("%02X ",ib); +#endif + memcpy(curwrap->blocks[ib],data+idxdata,1024); + idxdata+=1024; + filesize-=1024; + fb[ib]=0; + curwrap->entry[ie].blocks[idxb++]=ib; + } + } +#if TRACE_EDSK + printf("\n"); +#endif + memcpy(curwrap->entry[ie].filename,amsdos_name,11); + curwrap->entry[ie].subcpt=ia; + curwrap->entry[ie].rc=0x80; + curwrap->entry[ie].user=0; + ia++; + idxb=0; + } else { + /* last entry */ +#if TRACE_EDSK + printf("last entry for file (filesize=%d)\nblocklist: ",filesize); +#endif + if ((ie=EDSK_getdirid(curwrap))==-1) { + MakeError(ae,NULL,0,"Error - edsk [%s] DIRECTORY FULL\n",edskfilename); + MemFree(data); + return 0; + } + if (curwrap->nbentry<=ie) curwrap->nbentry=ie+1; + /* calcul du nombre de sous blocs de 128 octets */ + curwrap->entry[ie].rc=filesize/128; + if (filesize%128) { + curwrap->entry[ie].rc+=1; + } + idxb=0; + for (i=0;i<16 && filesize>0;i++) { + if ((ib=EDSK_getblockid(fb))==-1) { + MakeError(ae,NULL,0,"Error - edsk [%s] DISK FULL\n",edskfilename); + MemFree(data); + return 0; + } else { + if (firstblock==-1) firstblock=ib; +#if TRACE_EDSK + printf("%02X ",ib); +#endif + + memcpy(curwrap->blocks[ib],&data[idxdata],filesize>1024?1024:filesize); + idxdata+=1024; + filesize-=1024; + fb[ib]=0; + curwrap->entry[ie].blocks[idxb++]=ib; + } + } +#if TRACE_EDSK + printf("\n"); +#endif + filesize=0; + memcpy(curwrap->entry[ie].filename,amsdos_name,11); + curwrap->entry[ie].subcpt=ia; + curwrap->entry[ie].user=0; + } + } + + MemFree(data); + return 1; +} + +void EDSK_build_amsdos_directory(struct s_edsk_wrapper *face) +{ + #undef FUNC + #define FUNC "EDSK_build_amsdos_directory" + + unsigned char amsdosdir[2048]={0}; + int i,idx=0,b; + + if (!face) return; + +#if TRACE_EDSK +printf("build amsdos dir with %d entries\n",face->nbentry); +#endif + for (i=0;i<face->nbentry;i++) { + if (face->entry[i].rc && face->entry[i].rc!=0xE5) { + amsdosdir[idx]=face->entry[i].user; + memcpy(amsdosdir+idx+1,face->entry[i].filename,11); + amsdosdir[idx+12]=face->entry[i].subcpt; + amsdosdir[idx+13]=0; + amsdosdir[idx+14]=0; + amsdosdir[idx+15]=face->entry[i].rc; +#if TRACE_EDSK +printf("%-11.11s [%02X.%02X] blocks:",amsdosdir+idx+1,amsdosdir[idx+12],amsdosdir[idx+15]); +#endif + for (b=0;b<16;b++) { + if (face->entry[i].blocks[b]!=0xE5) { + amsdosdir[idx+16+b]=face->entry[i].blocks[b]; +#if TRACE_EDSK + printf("%s%02X",b>0?".":"",amsdosdir[idx+16+b]); +#endif + } else { + amsdosdir[idx+16+b]=0; + } + } +#if TRACE_EDSK +printf("\n"); +#endif + } + idx+=32; + } +#if TRACE_EDSK +printf("filling amsdos remaining entries (%d) with #E5\n",64-face->nbentry); +#endif + memset(amsdosdir+idx,0xE5,32*(64-face->nbentry)); + + /* AMSDOS directory copy to blocks! */ + memcpy(face->blocks[0],amsdosdir,1024); + memcpy(face->blocks[1],amsdosdir+1024,1024); +} +void EDSK_write_file(struct s_assenv *ae,struct s_edsk_wrapper *faceA,struct s_edsk_wrapper *faceB) +{ + #undef FUNC + #define FUNC "EDSK_write_file" + + struct s_edsk_wrapper emptyface={0}; + unsigned char header[256]={0}; + unsigned char trackblock[256]={0}; + int idblock,blockoffset; + int i,t; + + if (!faceA && !faceB) return; + + /* création des deux blocs du directory par face */ + EDSK_build_amsdos_directory(faceA); + EDSK_build_amsdos_directory(faceB); + /* écriture header */ + strcpy((char *)header,"EXTENDED CPC DSK File\r\nDisk-Info\r\n"); + strcpy((char *)header+0x22,RASM_SNAP_VERSION); + header[0x30]=40; + if (!faceA) { + faceA=&emptyface; + faceA->edsk_filename=TxtStrDup(faceB->edsk_filename); + } +#if TRACE_EDSK + printf("deleting [%s]\n",faceA->edsk_filename); +#endif + FileRemoveIfExists(faceA->edsk_filename); + + if (faceB!=NULL) header[0x31]=2; else header[0x31]=1; + for (i=0;i<header[0x30]*header[0x31];i++) header[0x34+i]=19; /* tracksize=(9*512+256)/256 */ +#if TRACE_EDSK + printf("writing EDSK header (256b)\n"); +#endif + FileWriteBinary(faceA->edsk_filename,(char *)header,256); + + /* écriture des pistes */ + for (t=0;t<40;t++) { + strcpy((char *)trackblock,"Track-Info\r\n"); + trackblock[0x10]=t; + trackblock[0x11]=0; + trackblock[0x14]=2; + trackblock[0x15]=9; + trackblock[0x16]=0x4E; + trackblock[0x17]=0xE5; + i=0; + while (1) { + trackblock[0x18+i*8+0]=trackblock[0x10]; + trackblock[0x18+i*8+1]=trackblock[0x11]; + trackblock[0x18+i*8+2]=(i>>1)+0xC1; +#if TRACE_EDSK + if (t<3) printf("%02X ",trackblock[0x18+i*8+2]); +#endif + trackblock[0x18+i*8+3]=2; + trackblock[0x18+i*8+4]=0; + trackblock[0x18+i*8+5]=0; + trackblock[0x18+i*8+6]=0; + trackblock[0x18+i*8+7]=2; + i++; + if (i==9) break; + /* interleave */ + trackblock[0x18+i*8+0]=trackblock[0x10]; + trackblock[0x18+i*8+1]=trackblock[0x11]; + trackblock[0x18+i*8+2]=(i>>1)+0xC6; /* start at C6 */ +#if TRACE_EDSK + if (t<3) printf("%02X ",trackblock[0x18+i*8+2]); +#endif + trackblock[0x18+i*8+3]=2; + trackblock[0x18+i*8+4]=0; + trackblock[0x18+i*8+5]=0; + trackblock[0x18+i*8+6]=0; + trackblock[0x18+i*8+7]=2; + i++; + } +#if TRACE_EDSK + if (t<3) printf("\n"); else if (t==3) printf("...\n"); +#endif + /* écriture du track info */ + FileWriteBinary(faceA->edsk_filename,(char *)trackblock,256); + + + /* il faut convertir les blocs logiques en secteurs physiques ET entrelacés */ + idblock=t*9/2; + blockoffset=((t*9)%2)*512; + + /* le premier secteur de la piste est à cheval sur le bloc logique une fois sur deux */ + FileWriteBinary(faceA->edsk_filename,(char *)&faceA->blocks[idblock][0]+blockoffset,512); /* C1 */ + if (!blockoffset) { + FileWriteBinary(faceA->edsk_filename,(char *)&faceA->blocks[idblock+2][0]+512,512); /* C6 */ + FileWriteBinary(faceA->edsk_filename,(char *)&faceA->blocks[idblock+0][0]+512,512); /* C2 */ + FileWriteBinary(faceA->edsk_filename,(char *)&faceA->blocks[idblock+3][0]+0,512); /* C7 */ + FileWriteBinary(faceA->edsk_filename,(char *)&faceA->blocks[idblock+1][0]+0,512); /* C3 */ + FileWriteBinary(faceA->edsk_filename,(char *)&faceA->blocks[idblock+3][0]+512,512); /* C8 */ + FileWriteBinary(faceA->edsk_filename,(char *)&faceA->blocks[idblock+1][0]+512,512); /* C4 */ + FileWriteBinary(faceA->edsk_filename,(char *)&faceA->blocks[idblock+4][0]+0,512); /* C9 */ + FileWriteBinary(faceA->edsk_filename,(char *)&faceA->blocks[idblock+2][0]+0,512); /* C5 */ + } else { + FileWriteBinary(faceA->edsk_filename,(char *)&faceA->blocks[idblock+3][0]+0,512); /* C6 */ + FileWriteBinary(faceA->edsk_filename,(char *)&faceA->blocks[idblock+1][0]+0,512); /* C2 */ + FileWriteBinary(faceA->edsk_filename,(char *)&faceA->blocks[idblock+3][0]+512,512); /* C7 */ + FileWriteBinary(faceA->edsk_filename,(char *)&faceA->blocks[idblock+1][0]+512,512); /* C3 */ + FileWriteBinary(faceA->edsk_filename,(char *)&faceA->blocks[idblock+4][0]+0,512); /* C8 */ + FileWriteBinary(faceA->edsk_filename,(char *)&faceA->blocks[idblock+2][0]+0,512); /* C4 */ + FileWriteBinary(faceA->edsk_filename,(char *)&faceA->blocks[idblock+4][0]+512,512); /* C9 */ + FileWriteBinary(faceA->edsk_filename,(char *)&faceA->blocks[idblock+2][0]+512,512); /* C5 */ + } + + /* @@TODO ça semble un peu foireux comme procédé */ + if (faceB) { +#if TRACE_EDSK + printf("writing EDSK face B /!\\ probably NOT WORKING !!!\n"); +#endif + trackblock[0x11]=1; + for (i=0;i<9;i++) { + trackblock[0x18+i*8+0]=trackblock[0x10]; + trackblock[0x18+i*8+1]=trackblock[0x11]; + } + /* écriture du track info */ + FileWriteBinary(faceB->edsk_filename,(char *)trackblock,256); + /* écriture des secteurs */ + idblock=t*9/2; + blockoffset=((t*9)%2)*512; + FileWriteBinary(faceB->edsk_filename,(char *)&faceB->blocks[idblock][0]+blockoffset,512); + if (!blockoffset) { + FileWriteBinary(faceB->edsk_filename,(char *)&faceB->blocks[idblock+2][0]+512,512); /* C6 */ + FileWriteBinary(faceB->edsk_filename,(char *)&faceB->blocks[idblock+0][0]+512,512); /* C2 */ + FileWriteBinary(faceB->edsk_filename,(char *)&faceB->blocks[idblock+3][0]+0,512); /* C7 */ + FileWriteBinary(faceB->edsk_filename,(char *)&faceB->blocks[idblock+1][0]+0,512); /* C3 */ + FileWriteBinary(faceB->edsk_filename,(char *)&faceB->blocks[idblock+3][0]+512,512); /* C8 */ + FileWriteBinary(faceB->edsk_filename,(char *)&faceB->blocks[idblock+1][0]+512,512); /* C4 */ + FileWriteBinary(faceB->edsk_filename,(char *)&faceB->blocks[idblock+4][0]+0,512); /* C9 */ + FileWriteBinary(faceB->edsk_filename,(char *)&faceB->blocks[idblock+2][0]+0,512); /* C5 */ + } else { + FileWriteBinary(faceB->edsk_filename,(char *)&faceB->blocks[idblock+3][0]+0,512); /* C6 */ + FileWriteBinary(faceB->edsk_filename,(char *)&faceB->blocks[idblock+1][0]+0,512); /* C2 */ + FileWriteBinary(faceB->edsk_filename,(char *)&faceB->blocks[idblock+3][0]+512,512); /* C7 */ + FileWriteBinary(faceB->edsk_filename,(char *)&faceB->blocks[idblock+1][0]+512,512); /* C3 */ + FileWriteBinary(faceB->edsk_filename,(char *)&faceB->blocks[idblock+4][0]+0,512); /* C8 */ + FileWriteBinary(faceB->edsk_filename,(char *)&faceB->blocks[idblock+2][0]+0,512); /* C4 */ + FileWriteBinary(faceB->edsk_filename,(char *)&faceB->blocks[idblock+4][0]+512,512); /* C9 */ + FileWriteBinary(faceB->edsk_filename,(char *)&faceB->blocks[idblock+2][0]+512,512); /* C5 */ + } + } + } + FileWriteBinaryClose(faceA->edsk_filename); + rasm_printf(ae,KIO"Write edsk file %s\n",faceA->edsk_filename); +} +void EDSK_write(struct s_assenv *ae) +{ + #undef FUNC + #define FUNC "EDSK_write" + + struct s_edsk_wrapper *faceA,*faceB; + char *edskfilename; + int i,j; + + /* on passe en revue toutes les structs */ + for (i=0;i<ae->nbedskwrapper;i++) { + /* already done */ + if (ae->edsk_wrapper[i].face==-1) continue; + + switch (ae->edsk_wrapper[i].face) { + default: + case 0:faceA=&ae->edsk_wrapper[i];faceB=NULL;break; + case 1:faceA=NULL;faceB=&ae->edsk_wrapper[i];break; + } + /* doit-on fusionner avec une autre face? */ + for (j=i+1;j<ae->nbedskwrapper;j++) { + if (!strcmp(ae->edsk_wrapper[i].edsk_filename,ae->edsk_wrapper[j].edsk_filename)) { + /* found another face for the floppy */ + switch (ae->edsk_wrapper[j].face) { + default: + case 0:faceA=&ae->edsk_wrapper[j];break; + case 1:faceB=&ae->edsk_wrapper[j];break; + } + } + } + EDSK_write_file(ae,faceA,faceB); + } +} +void PopAllSave(struct s_assenv *ae) +{ + #undef FUNC + #define FUNC "PopAllSave" + + unsigned char *AmsdosHeader; + char *dskfilename; + char *filename; + int offset,size,run; + int i,is,erreur=0; + + for (is=0;is<ae->nbsave;is++) { + /* avoid quotes */ + filename=ae->wl[ae->save[is].iw].w; + filename[strlen(filename)-1]=0; + filename++; + /**/ + +#if TRACE_EDSK + printf("woff=[%s](%d) wsize=[%s](%d)\n",ae->wl[ae->save[is].ioffset].w,ae->save[is].ioffset,ae->wl[ae->save[is].isize].w,ae->save[is].isize); +#endif + + ae->idx=ae->save[is].ioffset; /* exp hack */ + ExpressionFastTranslate(ae,&ae->wl[ae->idx].w,0); + offset=RoundComputeExpression(ae,ae->wl[ae->idx].w,0,0,0); + + ae->idx=ae->save[is].isize; /* exp hack */ + ExpressionFastTranslate(ae,&ae->wl[ae->idx].w,0); + size=RoundComputeExpression(ae,ae->wl[ae->idx].w,0,0,0); + + ae->idx=ae->save[is].irun; /* exp hack */ + if (ae->idx) { + ExpressionFastTranslate(ae,&ae->wl[ae->idx].w,0); + run=RoundComputeExpression(ae,ae->wl[ae->idx].w,0,0,0); + } else { + run=offset; + } + + if (size<1 || size>65536) { + MakeError(ae,NULL,0,"cannot save [%s] as the size is invalid!\n",filename); + continue; + } + if (offset<0 || offset>65535) { + MakeError(ae,NULL,0,"cannot save [%s] as the offset is invalid!\n",filename); + continue; + } + if (offset+size>65536) { + MakeError(ae,NULL,0,"cannot save [%s] as the offset+size will be out of bounds!\n",filename); + continue; + } + /* DSK management */ + if (ae->save[is].dsk) { + if (ae->save[is].iwdskname!=-1) { + /* obligé de dupliquer à cause du reuse */ + dskfilename=TxtStrDup(ae->wl[ae->save[is].iwdskname].w); + dskfilename[strlen(dskfilename)-1]=0; + if (!EDSK_addfile(ae,dskfilename+1,ae->save[is].face,filename,ae->mem[ae->save[is].ibank]+offset,size,offset,run)) { + erreur++; + break; + } + MemFree(dskfilename); + } + } else if (ae->save[is].tape) { + char TZX_header[10]; + unsigned char IDval[2]; + int wrksize,nbblock; + + /* output file on filesystem */ + FileRemoveIfExists(filename); + + strcpy(TZX_header,"ZXTape!"); + TZX_header[7]=0x1A; + TZX_header[8]=1; + TZX_header[9]=20; + FileWriteBinary(filename,(char *)TZX_header,10); + + IDval[0]=0x20; + FileWriteBinary(filename,(char *)IDval,1); + IDval[0]=0x03; + IDval[1]=0x03; + FileWriteBinary(filename,(char *)IDval,2); // first silence + + IDval[0]=0x10; + FileWriteBinary(filename,(char *)IDval,1); + IDval[0]=0x03; + IDval[1]=0x03; + FileWriteBinary(filename,(char *)IDval,2); // little silence + if (size+128<=2048) wrksize=size+128; else wrksize=2048; + IDval[0]=(wrksize+128) & 0xFF; + IDval[1]=((wrksize+128)>>8) & 0xFF; + FileWriteBinary(filename,(char *)IDval,2); // block len + nbblock=1; + AmsdosHeader=MakeAMSDOSHeader(run,offset,offset+size,MakeAMSDOS_name(ae,filename)); + FileWriteBinary(filename,(char *)AmsdosHeader,128); + if (size<=2048-128) { + FileWriteBinary(filename,(char*)ae->mem[ae->save[is].ibank]+offset,size); + } else { + FileWriteBinary(filename,(char*)ae->mem[ae->save[is].ibank]+offset,2048-128); + size=size-2048+128; + while (size>0) { + nbblock++; + /* additionnal block */ + IDval[0]=0x10; + FileWriteBinary(filename,(char *)IDval,1); + IDval[0]=0x04; + IDval[1]=0x04; + FileWriteBinary(filename,(char *)IDval,2); // silence 1s delay + if (size<=2048) wrksize=size; else wrksize=2048; + IDval[0]=(wrksize+128) & 0xFF; + IDval[1]=((wrksize+128)>>8) & 0xFF; + FileWriteBinary(filename,(char *)IDval,2); // block len + FileWriteBinary(filename,(char*)ae->mem[ae->save[is].ibank]+offset,wrksize); + /* adjust */ + size=size-2048; + } + } + FileWriteBinaryClose(filename); + rasm_printf(ae,KIO"Write tape file %s (%d block%s)\n",filename,nbblock,nbblock>1?"s":""); + } else { + /* output file on filesystem */ + rasm_printf(ae,KIO"Write binary file %s (%d byte%s)\n",filename,size,size>1?"s":""); + FileRemoveIfExists(filename); + if (ae->save[is].amsdos) { + AmsdosHeader=MakeAMSDOSHeader(run,offset,offset+size,MakeAMSDOS_name(ae,filename)); + FileWriteBinary(filename,(char *)AmsdosHeader,128); + } + FileWriteBinary(filename,(char*)ae->mem[ae->save[is].ibank]+offset,size); + FileWriteBinaryClose(filename); + } + } + if (!erreur) EDSK_write(ae); + + for (i=0;i<ae->nbedskwrapper;i++) { + MemFree(ae->edsk_wrapper[i].edsk_filename); + } + if (ae->maxedskwrapper) MemFree(ae->edsk_wrapper); + + if (ae->nbsave) { + MemFree(ae->save); + } +} + +void PopAllExpression(struct s_assenv *ae, int crunched_zone) +{ + #undef FUNC + #define FUNC "PopAllExpression" + + static int first=1; + double v; + long r; + int i; + unsigned char *mem; + char *expr; + + /* pop all expressions BUT thoses who where already computed (in crunched blocks) */ + + /* calcul des labels et expressions en zone crunch (et locale?) + les labels doivent pointer: + - une valeur absolue (numerique ou variable calculee) -> completement transparent + - un label dans la meme zone de crunch -> label->lz=1 && verif de la zone crunch + - un label hors zone crunch MAIS avant toute zone de crunch de la bank destination (!label->lz) + + idealement on doit tolerer les adresses situees apres le crunch dans une autre ORG zone! + + on utilise ae->stage pour créer un état intermédiaire dans le ComputeExpressionCore + */ + if (crunched_zone>=0) { + ae->stage=1; + } else { + /* on rescanne tout pour combler les trous */ + ae->stage=2; + first=1; + } + + for (i=first;i<ae->ie;i++) { + /* first compute only crunched expression (0,1,2,3,...) then (-1) at the end */ + if (crunched_zone>=0) { + /* calcul des expressions en zone crunch */ + if (ae->expression[i].lz<crunched_zone) continue; + if (ae->expression[i].lz>crunched_zone) { + first=i; + break; + } + } else { + if (ae->expression[i].lz>=0) continue; + } + + mem=ae->mem[ae->expression[i].ibank]; + + if (ae->expression[i].reference) { + expr=ae->expression[i].reference; + } else { + expr=ae->wl[ae->expression[i].iw].w; + } + v=ComputeExpressionCore(ae,expr,ae->expression[i].ptr,i); + r=(long)floor(v+ae->rough); + switch (ae->expression[i].zetype) { + case E_EXPRESSION_J8: + r=r-ae->expression[i].ptr-2; + if (r<-128 || r>127) { + MakeError(ae,GetExpFile(ae,i),ae->wl[ae->expression[i].iw].l,"relative offset %d too far [%s]\n",r,ae->wl[ae->expression[i].iw].w); + } + mem[ae->expression[i].wptr]=(unsigned char)r; + break; + case E_EXPRESSION_IV81: + /* for enhanced 16bits instructions */ + r++; + case E_EXPRESSION_0V8: + case E_EXPRESSION_IV8: + case E_EXPRESSION_3V8: + case E_EXPRESSION_V8: + if (r>255 || r<-128) { + if (!ae->nowarning) rasm_printf(ae,KWARNING"[%s:%d] Warning: truncating value #%X to #%X\n",GetExpFile(ae,i),ae->wl[ae->expression[i].iw].l,r,r&0xFF); + } + mem[ae->expression[i].wptr]=(unsigned char)r; + break; + case E_EXPRESSION_IV16: + case E_EXPRESSION_V16: + case E_EXPRESSION_V16C: + case E_EXPRESSION_0V16: + if (r>65535 || r<-32768) { + if (!ae->nowarning) rasm_printf(ae,KWARNING"[%s:%d] Warning: truncating value #%X to #%X\n",GetExpFile(ae,i),ae->wl[ae->expression[i].iw].l,r,r&0xFFFF); + } + mem[ae->expression[i].wptr]=(unsigned char)r&0xFF; + mem[ae->expression[i].wptr+1]=(unsigned char)((r&0xFF00)>>8); + break; + case E_EXPRESSION_0V32: + /* meaningless in 32 bits architecture... */ + if (v>4294967295 || v<-2147483648) { + if (!ae->nowarning) rasm_printf(ae,KWARNING"[%s:%d] Warning: truncating value\n",GetExpFile(ae,i),ae->wl[ae->expression[i].iw].l); + } + mem[ae->expression[i].wptr]=(unsigned char)r&0xFF; + mem[ae->expression[i].wptr+1]=(unsigned char)((r>>8)&0xFF); + mem[ae->expression[i].wptr+2]=(unsigned char)((r>>16)&0xFF); + mem[ae->expression[i].wptr+3]=(unsigned char)((r>>24)&0xFF); + break; + case E_EXPRESSION_0VR: + /* convert v double value to Amstrad REAL */ + memcpy(&mem[ae->expression[i].wptr],__internal_MakeAmsdosREAL(ae,v,i),5); + break; + case E_EXPRESSION_IM: + switch (r) { + case 0x00:mem[ae->expression[i].wptr]=0x46;break; + case 0x01:mem[ae->expression[i].wptr]=0x56;break; + case 0x02:mem[ae->expression[i].wptr]=0x5E;break; + default: + MakeError(ae,GetExpFile(ae,i),ae->wl[ae->expression[i].iw].l,"IM 0,1 or 2 only\n"); + mem[ae->expression[i].wptr]=0; + } + break; + case E_EXPRESSION_RST: + switch (r) { + case 0x00:mem[ae->expression[i].wptr]=0xC7;break; + case 0x08:mem[ae->expression[i].wptr]=0xCF;break; + case 0x10:mem[ae->expression[i].wptr]=0xD7;break; + case 0x18:mem[ae->expression[i].wptr]=0xDF;break; + case 0x20:mem[ae->expression[i].wptr]=0xE7;break; + case 0x28:mem[ae->expression[i].wptr]=0xEF;break; + case 0x30:mem[ae->expression[i].wptr]=0xF7;break; + case 0x38:mem[ae->expression[i].wptr]=0xFF;break; + default: + MakeError(ae,GetExpFile(ae,i),ae->wl[ae->expression[i].iw].l,"RST #0,#8,#10,#18,#20,#28,#30,#38 only\n"); + mem[ae->expression[i].wptr]=0; + } + break; + case E_EXPRESSION_RUN: + if (r<0 || r>65535) { + if (!ae->nowarning) rasm_printf(ae,KWARNING"[%s:%d] Warning: run adress truncated from %X to %X\n",GetExpFile(ae,i),ae->wl[ae->expression[i].iw].l,r,r&0xFFFF); + } + ae->snapshot.registers.LPC=r&0xFF; + ae->snapshot.registers.HPC=(r>>8)&0xFF; + break; + case E_EXPRESSION_ZXRUN: + if (r<0 || r>65535) { + if (!ae->nowarning) rasm_printf(ae,KWARNING"[%s:%d] Warning: run adress truncated from %X to %X\n",GetExpFile(ae,i),ae->wl[ae->expression[i].iw].l,r,r&0xFFFF); + } + ae->zxsnapshot.run=r&0xFFFF; + break; + case E_EXPRESSION_ZXSTACK: + if (r<0 || r>65535) { + if (!ae->nowarning) rasm_printf(ae,KWARNING"[%s:%d] Warning: stack adress truncated from %X to %X\n",GetExpFile(ae,i),ae->wl[ae->expression[i].iw].l,r,r&0xFFFF); + } + ae->zxsnapshot.stack=r&0xFFFF; + break; + default: + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"FATAL - unknown expression type\n",GetExpFile(ae,i),ae->wl[ae->expression[i].iw].l); + FreeAssenv(ae);exit(-8); + } + } +} + +void InsertLabelToTree(struct s_assenv *ae, struct s_label *label) +{ + #undef FUNC + #define FUNC "InsertLabelToTree" + + struct s_crclabel_tree *curlabeltree; + int radix,dek=32; + + curlabeltree=&ae->labeltree; + while (dek) { + dek=dek-8; + radix=(label->crc>>dek)&0xFF; + if (curlabeltree->radix[radix]) { + curlabeltree=curlabeltree->radix[radix]; + } else { + curlabeltree->radix[radix]=MemMalloc(sizeof(struct s_crclabel_tree)); + curlabeltree=curlabeltree->radix[radix]; + memset(curlabeltree,0,sizeof(struct s_crclabel_tree)); + } + } + ObjectArrayAddDynamicValueConcat((void**)&curlabeltree->label,&curlabeltree->nlabel,&curlabeltree->mlabel,&label[0],sizeof(struct s_label)); +} + +/* use by structure mechanism and label import to add fake labels */ +void PushLabelLight(struct s_assenv *ae, struct s_label *curlabel) { + #undef FUNC + #define FUNC "PushLabelLight" + + struct s_label *searched_label; + + /* PushLabel light */ + if ((searched_label=SearchLabel(ae,curlabel->name,curlabel->crc))!=NULL) { + MakeError(ae,GetCurrentFile(ae),GetExpLine(ae,0),"%s caused duplicate label [%s]\n",ae->idx?"Structure insertion":"Label import",curlabel->name); + MemFree(curlabel->name); + } else { + curlabel->backidx=ae->il; + ObjectArrayAddDynamicValueConcat((void **)&ae->label,&ae->il,&ae->ml,curlabel,sizeof(struct s_label)); + InsertLabelToTree(ae,curlabel); + } +} +void PushLabel(struct s_assenv *ae) +{ + #undef FUNC + #define FUNC "PushLabel" + + struct s_label curlabel={0},*searched_label; + char *curlabelname; + int i; + /* label with counters */ + struct s_expr_dico *curdic; + char curval[32]; + char *varbuffer,*expr; + char *starttag,*endtag,*tagcheck; + int taglen,tagidx,lenw,tagcount=0; + int crc,newlen,touched; + + if (ae->AutomateValidLabelFirst[ae->wl[ae->idx].w[0]]) { + for (i=1;ae->wl[ae->idx].w[i];i++) { + if (ae->wl[ae->idx].w[i]=='{') tagcount++; else if (ae->wl[ae->idx].w[i]=='}') tagcount--; + if (!tagcount) { + if (!ae->AutomateValidLabel[ae->wl[ae->idx].w[i]]) { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Invalid char in label declaration (%c)\n",ae->wl[ae->idx].w[i]); + return; + } + } + } + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Invalid first char in label declaration (%c)\n",ae->wl[ae->idx].w[0]); + return; + } + + switch (i) { + case 1: + switch (ae->wl[ae->idx].w[0]) { + case 'A': + case 'B': + case 'C': + case 'D': + case 'E': + case 'F': + case 'H': + case 'L': + case 'I': + case 'R': + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Cannot use reserved word [%s] for label\n",ae->wl[ae->idx].w); + return; + default:break; + } + break; + case 2: + if (strcmp(ae->wl[ae->idx].w,"AF")==0 || strcmp(ae->wl[ae->idx].w,"BC")==0 || strcmp(ae->wl[ae->idx].w,"DE")==0 || strcmp(ae->wl[ae->idx].w,"HL")==0 || + strcmp(ae->wl[ae->idx].w,"IX")==0 || strcmp(ae->wl[ae->idx].w,"IY")==0 || strcmp(ae->wl[ae->idx].w,"SP")==0 || + strcmp(ae->wl[ae->idx].w,"LX")==0 || strcmp(ae->wl[ae->idx].w,"HX")==0 || strcmp(ae->wl[ae->idx].w,"XL")==0 || strcmp(ae->wl[ae->idx].w,"XH")==0 || + strcmp(ae->wl[ae->idx].w,"LY")==0 || strcmp(ae->wl[ae->idx].w,"HY")==0 || strcmp(ae->wl[ae->idx].w,"YL")==0 || strcmp(ae->wl[ae->idx].w,"YH")==0) { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Cannot use reserved word [%s] for label\n",ae->wl[ae->idx].w); + return; + } + break; + case 3: + if (strcmp(ae->wl[ae->idx].w,"IXL")==0 || strcmp(ae->wl[ae->idx].w,"IYL")==0 || strcmp(ae->wl[ae->idx].w,"IXH")==0 || strcmp(ae->wl[ae->idx].w,"IYH")==0) { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Cannot use reserved word [%s] for label\n",ae->wl[ae->idx].w); + return; + } + break; + case 4: + if (strcmp(ae->wl[ae->idx].w,"VOID")==0) { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Cannot use reserved word [%s] for label\n",ae->wl[ae->idx].w); + return; + } + default:break; + } + + /******************************************************* + v a r i a b l e s i n l a b e l n a m e + *******************************************************/ + varbuffer=TranslateTag(ae,TxtStrDup(ae->wl[ae->idx].w),&touched,1,E_TAGOPTION_NONE); + + /************************************************** + s t r u c t u r e d e c l a r a t i o n + **************************************************/ + if (ae->getstruct) { + struct s_rasmstructfield rasmstructfield={0}; + if (varbuffer[0]=='@') { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Please no local label in a struct [%s]\n",ae->wl[ae->idx].w); + return; + } + /* copy label+offset in the structure */ + rasmstructfield.name=TxtStrDup(varbuffer); + rasmstructfield.offset=ae->codeadr; + ObjectArrayAddDynamicValueConcat((void **)&ae->rasmstruct[ae->irasmstruct-1].rasmstructfield, + &ae->rasmstruct[ae->irasmstruct-1].irasmstructfield,&ae->rasmstruct[ae->irasmstruct-1].mrasmstructfield, + &rasmstructfield,sizeof(rasmstructfield)); + /* label is structname+field */ + curlabelname=curlabel.name=MemMalloc(strlen(ae->rasmstruct[ae->irasmstruct-1].name)+strlen(varbuffer)+2); + sprintf(curlabel.name,"%s.%s",ae->rasmstruct[ae->irasmstruct-1].name,varbuffer); + curlabel.iw=-1; + /* legacy */ + curlabel.crc=GetCRC(curlabel.name); + curlabel.ptr=ae->codeadr; +#if TRACE_STRUCT + printf("pushLabel (struct) [%X] [%s]\n",curlabel.ptr,curlabel.name); +#endif + } else { + /************************************************** + l a b e l s + **************************************************/ + /* labels locaux */ + if (varbuffer[0]=='@' && (ae->ir || ae->iw || ae->imacro)) { + curlabel.iw=-1; + curlabel.local=1; + curlabelname=curlabel.name=MakeLocalLabel(ae,varbuffer,NULL); + curlabel.crc=GetCRC(curlabel.name); + + /* local labels ALSO set new reference */ + if (ae->lastglobalalloc) { +//printf("push LOCAL is freeing lastgloballabel\n"); + MemFree(ae->lastgloballabel); + } + ae->lastgloballabel=TxtStrDup(curlabelname); +//printf("push LOCAL as reference for proximity label -> [%s]\n",ae->lastgloballabel); + ae->lastgloballabellen=strlen(ae->lastgloballabel); + ae->lastglobalalloc=1; + } else { + switch (varbuffer[0]) { + case '.': + if (ae->dams) { + /* old Dams style declaration (remove the dot) */ + i=0; + do { + varbuffer[i]=varbuffer[i+1]; + ae->wl[ae->idx].w[i]=ae->wl[ae->idx].w[i+1]; + i++; + } while (varbuffer[i]!=0); + if (!touched) { + curlabel.iw=ae->idx; + } else { + curlabel.iw=-1; + curlabel.name=varbuffer; + } + curlabel.crc=GetCRC(varbuffer); + curlabelname=varbuffer; + } else { + /* proximity labels */ + if (ae->lastgloballabel) { + curlabelname=MemMalloc(strlen(varbuffer)+1+ae->lastgloballabellen); + sprintf(curlabelname,"%s%s",ae->lastgloballabel,varbuffer); + MemFree(varbuffer); + touched=1; // cause realloc! + curlabel.iw=-1; + curlabel.name=varbuffer=curlabelname; + curlabel.crc=GetCRC(varbuffer); +//printf("push proximity label that may be exported [%s]->[%s]\n",ae->wl[ae->idx].w,varbuffer); + } else { + /* MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"cannot create proximity label [%s] as there is no previous global label\n",varbuffer); + return; */ + + // not optimal but! + curlabelname=TxtStrDup(varbuffer); + MemFree(varbuffer); + touched=1; // cause realloc! + curlabel.iw=-1; + curlabel.name=varbuffer=curlabelname; + curlabel.crc=GetCRC(varbuffer); + } + } + break; + default: + if (!touched) { + curlabel.iw=ae->idx; + } else { + curlabel.iw=-1; + curlabel.name=varbuffer; + } + curlabel.crc=GetCRC(varbuffer); + curlabelname=varbuffer; + /* global labels set new reference */ + if (ae->lastglobalalloc) MemFree(ae->lastgloballabel); + ae->lastgloballabel=ae->wl[ae->idx].w; + ae->lastsuperglobal=ae->wl[ae->idx].w; + ae->lastgloballabellen=strlen(ae->wl[ae->idx].w); + ae->lastglobalalloc=0; +//printf("SET global label [%s] l=%d\n",ae->lastgloballabel,ae->lastgloballabellen); + break; + } + + /* contrôle dico uniquement avec des labels non locaux */ + if (SearchDico(ae,curlabelname,curlabel.crc)) { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"cannot create label [%s] as there is already a variable with the same name\n",curlabelname); + return; + } + if(SearchAlias(ae,curlabel.crc,curlabelname)!=-1) { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"cannot create label [%s] as there is already an alias with the same name\n",curlabelname); + return; + } + } + curlabel.ptr=ae->codeadr; + curlabel.ibank=ae->activebank; + curlabel.iorgzone=ae->io-1; + curlabel.lz=ae->lz; + } + + if ((searched_label=SearchLabel(ae,curlabelname,curlabel.crc))!=NULL) { + MakeError(ae,GetCurrentFile(ae),GetExpLine(ae,0),"Duplicate label [%s] - previously defined in [%s:%d]\n",curlabelname,ae->filename[searched_label->fileidx],searched_label->fileline); + if (curlabel.iw==-1) MemFree(curlabelname); + } else { +//printf("PushLabel(%s) name=%s crc=%X\n",curlabelname,curlabel.name?curlabel.name:"null",curlabel.crc); + curlabel.fileidx=ae->wl[ae->idx].ifile; + curlabel.fileline=ae->wl[ae->idx].l; + curlabel.autorise_export=ae->autorise_export; + curlabel.backidx=ae->il; + ObjectArrayAddDynamicValueConcat((void **)&ae->label,&ae->il,&ae->ml,&curlabel,sizeof(curlabel)); + InsertLabelToTree(ae,&curlabel); + } + + if (!touched) MemFree(varbuffer); +} + + +unsigned char *EncodeSnapshotRLE(unsigned char *memin, int *lenout) { + #undef FUNC + #define FUNC "EncodeSnapshotRLE" + + int i,cpt,idx=0; + unsigned char *memout=NULL; + + memout=MemMalloc(65540); + + for (i=0;i<65536;) { + for (cpt=1;cpt<255;cpt++) if (memin[i]!=memin[i+cpt]) break; + if (cpt>=3 || memin[i]==0xE5) { + memout[idx++]=0xE5; + memout[idx++]=cpt; + memout[idx++]=memin[i]; + i+=cpt; + } else { + memout[idx++]=memin[i++]; + } + } + if (lenout) *lenout=idx; + if (idx<65536) return memout; + + MemFree(memout); + return NULL; +} + + + +#undef FUNC +#define FUNC "Instruction CORE" + +void _IN(struct s_assenv *ae) { + if (!ae->wl[ae->idx].t && !ae->wl[ae->idx+1].t && ae->wl[ae->idx+2].t==1) { + if (strcmp(ae->wl[ae->idx+2].w,"(C)")==0) { + switch (GetCRC(ae->wl[ae->idx+1].w)) { + case CRC_0: + case CRC_F:___output(ae,0xED);___output(ae,0x70);ae->nop+=4;break; + case CRC_A:___output(ae,0xED);___output(ae,0x78);ae->nop+=3;break; + case CRC_B:___output(ae,0xED);___output(ae,0x40);ae->nop+=4;break; + case CRC_C:___output(ae,0xED);___output(ae,0x48);ae->nop+=4;break; + case CRC_D:___output(ae,0xED);___output(ae,0x50);ae->nop+=4;break; + case CRC_E:___output(ae,0xED);___output(ae,0x58);ae->nop+=4;break; + case CRC_H:___output(ae,0xED);___output(ae,0x60);ae->nop+=4;break; + case CRC_L:___output(ae,0xED);___output(ae,0x68);ae->nop+=4;break; + default: + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is IN [0,F,A,B,C,D,E,H,L],(C)\n"); + } + } else if (strcmp(ae->wl[ae->idx+1].w,"A")==0 && StringIsMem(ae->wl[ae->idx+2].w)) { + ___output(ae,0xDB); + PushExpression(ae,ae->idx+2,E_EXPRESSION_V8); + ae->nop+=3; + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"IN [0,F,A,B,C,D,E,H,L],(C) or IN A,(n) only\n"); + } + ae->idx+=2; + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"IN [0,F,A,B,C,D,E,H,L],(C) or IN A,(n) only\n"); + } +} + +void _OUT(struct s_assenv *ae) { + if (!ae->wl[ae->idx].t && !ae->wl[ae->idx+1].t && ae->wl[ae->idx+2].t==1) { + if (strcmp(ae->wl[ae->idx+1].w,"(C)")==0) { + switch (GetCRC(ae->wl[ae->idx+2].w)) { + case CRC_0:___output(ae,0xED);___output(ae,0x71);ae->nop+=4;break; + case CRC_A:___output(ae,0xED);___output(ae,0x79);ae->nop+=4;break; + case CRC_B:___output(ae,0xED);___output(ae,0x41);ae->nop+=4;break; + case CRC_C:___output(ae,0xED);___output(ae,0x49);ae->nop+=4;break; + case CRC_D:___output(ae,0xED);___output(ae,0x51);ae->nop+=4;break; + case CRC_E:___output(ae,0xED);___output(ae,0x59);ae->nop+=4;break; + case CRC_H:___output(ae,0xED);___output(ae,0x61);ae->nop+=4;break; + case CRC_L:___output(ae,0xED);___output(ae,0x69);ae->nop+=4;break; + default: + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is OUT (C),[0,A,B,C,D,E,H,L]\n"); + } + } else if (strcmp(ae->wl[ae->idx+2].w,"A")==0 && StringIsMem(ae->wl[ae->idx+1].w)) { + ___output(ae,0xD3); + PushExpression(ae,ae->idx+1,E_EXPRESSION_V8); + ae->nop+=3; + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"OUT (C),[0,A,B,C,D,E,H,L] or OUT (n),A only\n"); + } + ae->idx+=2; + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"OUT (C),[0,A,B,C,D,E,H,L] or OUT (n),A only\n"); + } +} + +void _EX(struct s_assenv *ae) { + if (!ae->wl[ae->idx].t && !ae->wl[ae->idx+1].t && ae->wl[ae->idx+2].t==1) { + switch (GetCRC(ae->wl[ae->idx+1].w)) { + case CRC_HL: + switch (GetCRC(ae->wl[ae->idx+2].w)) { + case CRC_DE:___output(ae,0xEB);ae->nop+=1;break; + case CRC_MSP:___output(ae,0xE3);ae->nop+=6;break; + default: + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is EX HL,[(SP),DE]\n"); + } + break; + case CRC_AF: + if (strcmp(ae->wl[ae->idx+2].w,"AF'")==0) { + ___output(ae,0x08);ae->nop+=1; + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is EX AF,AF'\n"); + } + break; + case CRC_MSP: + switch (GetCRC(ae->wl[ae->idx+2].w)) { + case CRC_HL:___output(ae,0xE3);ae->nop+=6;break; + case CRC_IX:___output(ae,0xDD);___output(ae,0xE3);ae->nop+=7;break; + case CRC_IY:___output(ae,0xFD);___output(ae,0xE3);ae->nop+=7;break; + default: + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is EX (SP),[HL,IX,IY]\n"); + } + break; + case CRC_DE: + switch (GetCRC(ae->wl[ae->idx+2].w)) { + case CRC_HL:___output(ae,0xEB);ae->nop+=1;break; + default: + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is EX DE,HL\n"); + } + break; + case CRC_IX: + switch (GetCRC(ae->wl[ae->idx+2].w)) { + case CRC_MSP:___output(ae,0xDD);___output(ae,0xE3);ae->nop+=7;break; + default: + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is EX IX,(SP)\n"); + } + break; + case CRC_IY: + switch (GetCRC(ae->wl[ae->idx+2].w)) { + case CRC_MSP:___output(ae,0xFD);___output(ae,0xE3);ae->nop+=7;break; + default: + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is EX IY,(SP)\n"); + } + break; + default: + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is EX [AF,DE,HL,(SP),IX,IY],reg16\n"); + } + ae->idx+=2; + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Use EX reg16,reg16\n"); + } +} + +void _SBC(struct s_assenv *ae) { + if ((!ae->wl[ae->idx].t && ae->wl[ae->idx+1].t==1) || ((!ae->wl[ae->idx].t && !ae->wl[ae->idx+1].t && ae->wl[ae->idx+2].t==1) && strcmp(ae->wl[ae->idx+1].w,"A")==0)) { + if (!ae->wl[ae->idx+1].t) ae->idx++; + /* do implicit A */ + switch (GetCRC(ae->wl[ae->idx+1].w)) { + case CRC_A:___output(ae,0x9F);ae->nop+=1;break; + case CRC_MHL:___output(ae,0x9E);ae->nop+=2;break; + case CRC_B:___output(ae,0x98);ae->nop+=1;break; + case CRC_C:___output(ae,0x99);ae->nop+=1;break; + case CRC_D:___output(ae,0x9A);ae->nop+=1;break; + case CRC_E:___output(ae,0x9B);ae->nop+=1;break; + case CRC_H:___output(ae,0x9C);ae->nop+=1;break; + case CRC_L:___output(ae,0x9D);ae->nop+=1;break; + case CRC_IXH:case CRC_HX:case CRC_XH:___output(ae,0xDD);___output(ae,0x9C);ae->nop+=2;break; + case CRC_IXL:case CRC_LX:case CRC_XL:___output(ae,0xDD);___output(ae,0x9D);ae->nop+=2;break; + case CRC_IYH:case CRC_HY:case CRC_YH:___output(ae,0xFD);___output(ae,0x9C);ae->nop+=2;break; + case CRC_IYL:case CRC_LY:case CRC_YL:___output(ae,0xFD);___output(ae,0x9D);ae->nop+=2;break; + case CRC_IX:case CRC_IY: + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Use SBC with A,B,C,D,E,H,L,XH,XL,YH,YL,(HL),(IX),(IY)\n"); + ae->idx++; + return; + default: + if (strncmp(ae->wl[ae->idx+1].w,"(IX",3)==0) { + ___output(ae,0xDD);___output(ae,0x9E); + PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8); + ae->nop+=3; + } else if (strncmp(ae->wl[ae->idx+1].w,"(IY",3)==0) { + ___output(ae,0xFD);___output(ae,0x9E); + PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8); + ae->nop+=3; + } else { + ___output(ae,0xDE); + PushExpression(ae,ae->idx+1,E_EXPRESSION_V8); + ae->nop+=2; + } + } + ae->idx++; + } else if (!ae->wl[ae->idx].t && !ae->wl[ae->idx+1].t && ae->wl[ae->idx+2].t==1) { + switch (GetCRC(ae->wl[ae->idx+1].w)) { + case CRC_HL: + switch (GetCRC(ae->wl[ae->idx+2].w)) { + case CRC_BC:___output(ae,0xED);___output(ae,0x42);ae->nop+=4;break; + case CRC_DE:___output(ae,0xED);___output(ae,0x52);ae->nop+=4;break; + case CRC_HL:___output(ae,0xED);___output(ae,0x62);ae->nop+=4;break; + case CRC_SP:___output(ae,0xED);___output(ae,0x72);ae->nop+=4;break; + default: + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is SBC HL,[BC,DE,HL,SP]\n"); + } + break; + default: + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is SBC HL,[BC,DE,HL,SP]\n"); + } + ae->idx+=2; + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Invalid syntax for SBC\n"); + } +} + +void _ADC(struct s_assenv *ae) { + if ((!ae->wl[ae->idx].t && ae->wl[ae->idx+1].t==1) || ((!ae->wl[ae->idx].t && !ae->wl[ae->idx+1].t && ae->wl[ae->idx+2].t==1) && strcmp(ae->wl[ae->idx+1].w,"A")==0)) { + if (!ae->wl[ae->idx+1].t) ae->idx++; + /* also implicit A */ + switch (GetCRC(ae->wl[ae->idx+1].w)) { + case CRC_A:___output(ae,0x8F);ae->nop+=1;break; + case CRC_MHL:___output(ae,0x8E);ae->nop+=2;break; + case CRC_B:___output(ae,0x88);ae->nop+=1;break; + case CRC_C:___output(ae,0x89);ae->nop+=1;break; + case CRC_D:___output(ae,0x8A);ae->nop+=1;break; + case CRC_E:___output(ae,0x8B);ae->nop+=1;break; + case CRC_H:___output(ae,0x8C);ae->nop+=1;break; + case CRC_L:___output(ae,0x8D);ae->nop+=1;break; + case CRC_IXH:case CRC_HX:case CRC_XH:___output(ae,0xDD);___output(ae,0x8C);ae->nop+=2;break; + case CRC_IXL:case CRC_LX:case CRC_XL:___output(ae,0xDD);___output(ae,0x8D);ae->nop+=2;break; + case CRC_IYH:case CRC_HY:case CRC_YH:___output(ae,0xFD);___output(ae,0x8C);ae->nop+=2;break; + case CRC_IYL:case CRC_LY:case CRC_YL:___output(ae,0xFD);___output(ae,0x8D);ae->nop+=2;break; + case CRC_IX:case CRC_IY: + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Use ADC with A,B,C,D,E,H,L,XH,XL,YH,YL,(HL),(IX),(IY)\n"); + ae->idx++; + return; + default: + if (strncmp(ae->wl[ae->idx+1].w,"(IX",3)==0) { + ___output(ae,0xDD);___output(ae,0x8E); + PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8); + ae->nop+=3; + } else if (strncmp(ae->wl[ae->idx+1].w,"(IY",3)==0) { + ___output(ae,0xFD);___output(ae,0x8E); + PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8); + ae->nop+=3; + } else { + ___output(ae,0xCE); + PushExpression(ae,ae->idx+1,E_EXPRESSION_V8); + ae->nop+=2; + } + } + ae->idx++; + } else if (!ae->wl[ae->idx].t && !ae->wl[ae->idx+1].t && ae->wl[ae->idx+2].t==1) { + switch (GetCRC(ae->wl[ae->idx+1].w)) { + case CRC_HL: + switch (GetCRC(ae->wl[ae->idx+2].w)) { + case CRC_BC:___output(ae,0xED);___output(ae,0x4A);ae->nop+=4;break; + case CRC_DE:___output(ae,0xED);___output(ae,0x5A);ae->nop+=4;break; + case CRC_HL:___output(ae,0xED);___output(ae,0x6A);ae->nop+=4;break; + case CRC_SP:___output(ae,0xED);___output(ae,0x7A);ae->nop+=4;break; + default: + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is ADC HL,[BC,DE,HL,SP]\n"); + } + break; + default: + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is ADC HL,[BC,DE,HL,SP]\n"); + } + ae->idx+=2; + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Invalid syntax for ADC\n"); + } +} + +void _ADD(struct s_assenv *ae) { + if ((!ae->wl[ae->idx].t && ae->wl[ae->idx+1].t==1) || ((!ae->wl[ae->idx].t && !ae->wl[ae->idx+1].t && ae->wl[ae->idx+2].t==1) && strcmp(ae->wl[ae->idx+1].w,"A")==0)) { + if (!ae->wl[ae->idx+1].t) ae->idx++; + /* also implicit A */ + switch (GetCRC(ae->wl[ae->idx+1].w)) { + case CRC_A:___output(ae,0x87);ae->nop+=1;break; + case CRC_MHL:___output(ae,0x86);ae->nop+=2;break; + case CRC_B:___output(ae,0x80);ae->nop+=1;break; + case CRC_C:___output(ae,0x81);ae->nop+=1;break; + case CRC_D:___output(ae,0x82);ae->nop+=1;break; + case CRC_E:___output(ae,0x83);ae->nop+=1;break; + case CRC_H:___output(ae,0x84);ae->nop+=1;break; + case CRC_L:___output(ae,0x85);ae->nop+=1;break; + case CRC_IXH:case CRC_HX:case CRC_XH:___output(ae,0xDD);___output(ae,0x84);ae->nop+=2;break; + case CRC_IXL:case CRC_LX:case CRC_XL:___output(ae,0xDD);___output(ae,0x85);ae->nop+=2;break; + case CRC_IYH:case CRC_HY:case CRC_YH:___output(ae,0xFD);___output(ae,0x84);ae->nop+=2;break; + case CRC_IYL:case CRC_LY:case CRC_YL:___output(ae,0xFD);___output(ae,0x85);ae->nop+=2;break; + case CRC_IX:case CRC_IY: + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Use ADD with A,B,C,D,E,H,L,XH,XL,YH,YL,(HL),(IX),(IY)\n"); + ae->idx++; + return; + default: + if (strncmp(ae->wl[ae->idx+1].w,"(IX",3)==0) { + ___output(ae,0xDD);___output(ae,0x86); + PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8); + ae->nop+=5; + } else if (strncmp(ae->wl[ae->idx+1].w,"(IY",3)==0) { + ___output(ae,0xFD);___output(ae,0x86); + PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8); + ae->nop+=5; + } else { + ___output(ae,0xC6); + PushExpression(ae,ae->idx+1,E_EXPRESSION_V8); + ae->nop+=2; + } + } + ae->idx++; + } else if (!ae->wl[ae->idx].t && !ae->wl[ae->idx+1].t && ae->wl[ae->idx+2].t==1) { + switch (GetCRC(ae->wl[ae->idx+1].w)) { + case CRC_HL: + switch (GetCRC(ae->wl[ae->idx+2].w)) { + case CRC_BC:___output(ae,0x09);ae->nop+=3;break; + case CRC_DE:___output(ae,0x19);ae->nop+=3;break; + case CRC_HL:___output(ae,0x29);ae->nop+=3;break; + case CRC_SP:___output(ae,0x39);ae->nop+=3;break; + default: + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is ADD HL,[BC,DE,HL,SP]\n"); + } + break; + case CRC_IX: + switch (GetCRC(ae->wl[ae->idx+2].w)) { + case CRC_BC:___output(ae,0xDD);___output(ae,0x09);ae->nop+=4;break; + case CRC_DE:___output(ae,0xDD);___output(ae,0x19);ae->nop+=4;break; + case CRC_IX:___output(ae,0xDD);___output(ae,0x29);ae->nop+=4;break; + case CRC_SP:___output(ae,0xDD);___output(ae,0x39);ae->nop+=4;break; + default: + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is ADD IX,[BC,DE,IX,SP]\n"); + } + break; + case CRC_IY: + switch (GetCRC(ae->wl[ae->idx+2].w)) { + case CRC_BC:___output(ae,0xFD);___output(ae,0x09);ae->nop+=4;break; + case CRC_DE:___output(ae,0xFD);___output(ae,0x19);ae->nop+=4;break; + case CRC_IY:___output(ae,0xFD);___output(ae,0x29);ae->nop+=4;break; + case CRC_SP:___output(ae,0xFD);___output(ae,0x39);ae->nop+=4;break; + default: + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is ADD IY,[BC,DE,IY,SP]\n"); + } + break; + default: + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is ADD [HL,IX,IY],reg16\n"); + } + ae->idx+=2; + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Invalid syntax for ADD\n"); + } +} + +void _CP(struct s_assenv *ae) { + if ((!ae->wl[ae->idx].t && ae->wl[ae->idx+1].t==1) || ((!ae->wl[ae->idx].t && !ae->wl[ae->idx+1].t && ae->wl[ae->idx+2].t==1) && strcmp(ae->wl[ae->idx+1].w,"A")==0)) { + if (!ae->wl[ae->idx+1].t) ae->idx++; + /* also implicit A */ + switch (GetCRC(ae->wl[ae->idx+1].w)) { + case CRC_A:___output(ae,0xBF);ae->nop+=1;break; + case CRC_MHL:___output(ae,0xBE);ae->nop+=2;break; + case CRC_B:___output(ae,0xB8);ae->nop+=1;break; + case CRC_C:___output(ae,0xB9);ae->nop+=1;break; + case CRC_D:___output(ae,0xBA);ae->nop+=1;break; + case CRC_E:___output(ae,0xBB);ae->nop+=1;break; + case CRC_H:___output(ae,0xBC);ae->nop+=1;break; + case CRC_L:___output(ae,0xBD);ae->nop+=1;break; + case CRC_IXH:case CRC_HX:case CRC_XH:___output(ae,0xDD);___output(ae,0xBC);ae->nop+=2;break; + case CRC_IXL:case CRC_LX:case CRC_XL:___output(ae,0xDD);___output(ae,0xBD);ae->nop+=2;break; + case CRC_IYH:case CRC_HY:case CRC_YH:___output(ae,0xFD);___output(ae,0xBC);ae->nop+=2;break; + case CRC_IYL:case CRC_LY:case CRC_YL:___output(ae,0xFD);___output(ae,0xBD);ae->nop+=2;break; + default: + if (strncmp(ae->wl[ae->idx+1].w,"(IX",3)==0) { + ___output(ae,0xDD);___output(ae,0xBE); + PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8); + ae->nop+=5; + } else if (strncmp(ae->wl[ae->idx+1].w,"(IY",3)==0) { + ___output(ae,0xFD);___output(ae,0xBE); + PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8); + ae->nop+=5; + } else { + ___output(ae,0xFE); + PushExpression(ae,ae->idx+1,E_EXPRESSION_V8); + ae->nop+=2; + } + } + ae->idx++; + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Syntax is CP reg8/(reg16)\n"); + } +} + +void _RET(struct s_assenv *ae) { + if (!ae->wl[ae->idx].t && ae->wl[ae->idx+1].t==1) { + switch (GetCRC(ae->wl[ae->idx+1].w)) { + case CRC_NZ:___output(ae,0xC0);ae->nop+=2;break; + case CRC_Z:___output(ae,0xC8);ae->nop+=2;break; + case CRC_C:___output(ae,0xD8);ae->nop+=2;break; + case CRC_NC:___output(ae,0xD0);ae->nop+=2;break; + case CRC_PE:___output(ae,0xE8);ae->nop+=2;break; + case CRC_PO:___output(ae,0xE0);ae->nop+=2;break; + case CRC_P:___output(ae,0xF0);ae->nop+=2;break; + case CRC_M:___output(ae,0xF8);ae->nop+=2;break; + default: + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Available flags for RET are C,NC,Z,NZ,PE,PO,P,M\n"); + } + ae->idx++; + } else if (ae->wl[ae->idx].t==1) { + ___output(ae,0xC9); + ae->nop+=3; + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Invalid RET syntax\n"); + } +} + +void _CALL(struct s_assenv *ae) { + if (!ae->wl[ae->idx].t && ae->wl[ae->idx+1].t==0 && ae->wl[ae->idx+2].t==1) { + switch (GetCRC(ae->wl[ae->idx+1].w)) { + case CRC_C:___output(ae,0xDC);ae->nop+=3;break; + case CRC_Z:___output(ae,0xCC);ae->nop+=3;break; + case CRC_NZ:___output(ae,0xC4);ae->nop+=3;break; + case CRC_NC:___output(ae,0xD4);ae->nop+=3;break; + case CRC_PE:___output(ae,0xEC);ae->nop+=3;break; + case CRC_PO:___output(ae,0xE4);ae->nop+=3;break; + case CRC_P:___output(ae,0xF4);ae->nop+=3;break; + case CRC_M:___output(ae,0xFC);ae->nop+=3;break; + default: + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Available flags for CALL are C,NC,Z,NZ,PE,PO,P,M\n"); + } + PushExpression(ae,ae->idx+2,E_EXPRESSION_V16C); + ae->idx+=2; + } else if (!ae->wl[ae->idx].t && ae->wl[ae->idx+1].t==1) { + ___output(ae,0xCD); + PushExpression(ae,ae->idx+1,E_EXPRESSION_V16C); + ae->idx++; + ae->nop+=5; + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Invalid CALL syntax\n"); + } +} + +void _JR(struct s_assenv *ae) { + if (!ae->wl[ae->idx].t && ae->wl[ae->idx+1].t==0 && ae->wl[ae->idx+2].t==1) { + switch (GetCRC(ae->wl[ae->idx+1].w)) { + case CRC_NZ:___output(ae,0x20);ae->nop+=2;break; + case CRC_C:___output(ae,0x38);ae->nop+=2;break; + case CRC_Z:___output(ae,0x28);ae->nop+=2;break; + case CRC_NC:___output(ae,0x30);ae->nop+=2;break; + default: + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Available flags for JR are C,NC,Z,NZ\n"); + } + PushExpression(ae,ae->idx+2,E_EXPRESSION_J8); + ae->idx+=2; + } else if (!ae->wl[ae->idx].t && ae->wl[ae->idx+1].t==1) { + ___output(ae,0x18); + PushExpression(ae,ae->idx+1,E_EXPRESSION_J8); + ae->idx++; + ae->nop+=3; + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Invalid JR syntax\n"); + } +} + +void _JP(struct s_assenv *ae) { + if (!ae->wl[ae->idx].t && ae->wl[ae->idx+1].t==0 && ae->wl[ae->idx+2].t==1) { + switch (GetCRC(ae->wl[ae->idx+1].w)) { + case CRC_C:___output(ae,0xDA);ae->nop+=3;break; + case CRC_Z:___output(ae,0xCA);ae->nop+=3;break; + case CRC_NZ:___output(ae,0xC2);ae->nop+=3;break; + case CRC_NC:___output(ae,0xD2);ae->nop+=3;break; + case CRC_PE:___output(ae,0xEA);ae->nop+=3;break; + case CRC_PO:___output(ae,0xE2);ae->nop+=3;break; + case CRC_P:___output(ae,0xF2);ae->nop+=3;break; + case CRC_M:___output(ae,0xFA);ae->nop+=3;break; + default: + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Available flags for JP are C,NC,Z,NZ,PE,PO,P,M\n"); + } + if (!strcmp(ae->wl[ae->idx+2].w,"(IX)") || !strcmp(ae->wl[ae->idx+2].w,"(IY)")) { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"conditionnal JP cannot use register adressing\n"); + } else { + PushExpression(ae,ae->idx+2,E_EXPRESSION_V16); + } + ae->idx+=2; + } else if (!ae->wl[ae->idx].t && ae->wl[ae->idx+1].t==1) { + switch (GetCRC(ae->wl[ae->idx+1].w)) { + case CRC_HL:case CRC_MHL:___output(ae,0xE9);ae->nop+=1;break; + case CRC_IX:case CRC_MIX:___output(ae,0xDD);___output(ae,0xE9);ae->nop+=2;break; + case CRC_IY:case CRC_MIY:___output(ae,0xFD);___output(ae,0xE9);ae->nop+=2;break; + default: + ___output(ae,0xC3); + PushExpression(ae,ae->idx+1,E_EXPRESSION_V16); + } + ae->idx++; + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Invalid JP syntax\n"); + } +} + + +void _DEC(struct s_assenv *ae) { + if (!ae->wl[ae->idx].t) { + do { + switch (GetCRC(ae->wl[ae->idx+1].w)) { + case CRC_A:___output(ae,0x3D);ae->nop+=1;break; + case CRC_B:___output(ae,0x05);ae->nop+=1;break; + case CRC_C:___output(ae,0x0D);ae->nop+=1;break; + case CRC_D:___output(ae,0x15);ae->nop+=1;break; + case CRC_E:___output(ae,0x1D);ae->nop+=1;break; + case CRC_H:___output(ae,0x25);ae->nop+=1;break; + case CRC_L:___output(ae,0x2D);ae->nop+=1;break; + case CRC_IXH:case CRC_HX:case CRC_XH:___output(ae,0xDD);___output(ae,0x25);ae->nop+=2;break; + case CRC_IXL:case CRC_LX:case CRC_XL:___output(ae,0xDD);___output(ae,0x2D);ae->nop+=2;break; + case CRC_IYH:case CRC_HY:case CRC_YH:___output(ae,0xFD);___output(ae,0x25);ae->nop+=2;break; + case CRC_IYL:case CRC_LY:case CRC_YL:___output(ae,0xFD);___output(ae,0x2D);ae->nop+=2;break; + case CRC_BC:___output(ae,0x0B);ae->nop+=2;break; + case CRC_DE:___output(ae,0x1B);ae->nop+=2;break; + case CRC_HL:___output(ae,0x2B);ae->nop+=2;break; + case CRC_IX:___output(ae,0xDD);___output(ae,0x2B);ae->nop+=3;break; + case CRC_IY:___output(ae,0xFD);___output(ae,0x2B);ae->nop+=3;break; + case CRC_SP:___output(ae,0x3B);ae->nop+=2;break; + case CRC_MHL:___output(ae,0x35);ae->nop+=3;break; + default: + if (strncmp(ae->wl[ae->idx+1].w,"(IX",3)==0) { + ___output(ae,0xDD);___output(ae,0x35); + PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8); + ae->nop+=6; + } else if (strncmp(ae->wl[ae->idx+1].w,"(IY",3)==0) { + ___output(ae,0xFD);___output(ae,0x35); + PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8); + ae->nop+=6; + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Use DEC with A,B,C,D,E,H,L,XH,XL,YH,YL,BC,DE,HL,SP,(HL),(IX),(IY)\n"); + } + } + ae->idx++; + } while (ae->wl[ae->idx].t==0); + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Use DEC with A,B,C,D,E,H,L,XH,XL,YH,YL,BC,DE,HL,SP,(HL),(IX),(IY)\n"); + } +} +void _INC(struct s_assenv *ae) { + if (!ae->wl[ae->idx].t) { + do { + switch (GetCRC(ae->wl[ae->idx+1].w)) { + case CRC_A:___output(ae,0x3C);ae->nop+=1;break; + case CRC_B:___output(ae,0x04);ae->nop+=1;break; + case CRC_C:___output(ae,0x0C);ae->nop+=1;break; + case CRC_D:___output(ae,0x14);ae->nop+=1;break; + case CRC_E:___output(ae,0x1C);ae->nop+=1;break; + case CRC_H:___output(ae,0x24);ae->nop+=1;break; + case CRC_L:___output(ae,0x2C);ae->nop+=1;break; + case CRC_IXH:case CRC_HX:case CRC_XH:___output(ae,0xDD);___output(ae,0x24);ae->nop+=2;break; + case CRC_IXL:case CRC_LX:case CRC_XL:___output(ae,0xDD);___output(ae,0x2C);ae->nop+=2;break; + case CRC_IYH:case CRC_HY:case CRC_YH:___output(ae,0xFD);___output(ae,0x24);ae->nop+=2;break; + case CRC_IYL:case CRC_LY:case CRC_YL:___output(ae,0xFD);___output(ae,0x2C);ae->nop+=2;break; + case CRC_BC:___output(ae,0x03);ae->nop+=2;break; + case CRC_DE:___output(ae,0x13);ae->nop+=2;break; + case CRC_HL:___output(ae,0x23);ae->nop+=2;break; + case CRC_IX:___output(ae,0xDD);___output(ae,0x23);ae->nop+=3;break; + case CRC_IY:___output(ae,0xFD);___output(ae,0x23);ae->nop+=3;break; + case CRC_SP:___output(ae,0x33);ae->nop+=2;break; + case CRC_MHL:___output(ae,0x34);ae->nop+=3;break; + default: + if (strncmp(ae->wl[ae->idx+1].w,"(IX",3)==0) { + ___output(ae,0xDD);___output(ae,0x34); + PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8); + ae->nop+=6; + } else if (strncmp(ae->wl[ae->idx+1].w,"(IY",3)==0) { + ___output(ae,0xFD);___output(ae,0x34); + PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8); + ae->nop+=6; + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Use INC with A,B,C,D,E,H,L,XH,XL,YH,YL,BC,DE,HL,SP,(HL),(IX),(IY)\n"); + } + } + ae->idx++; + } while (ae->wl[ae->idx].t==0); + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Use INC with A,B,C,D,E,H,L,XH,XL,YH,YL,BC,DE,HL,SP,(HL),(IX),(IY)\n"); + } +} + +void _SUB(struct s_assenv *ae) { + #ifdef OPCODE + #undef OPCODE + #endif + #define OPCODE 0x90 + + if ((!ae->wl[ae->idx].t && ae->wl[ae->idx+1].t==1) || ((!ae->wl[ae->idx].t && !ae->wl[ae->idx+1].t && ae->wl[ae->idx+2].t==1) && strcmp(ae->wl[ae->idx+1].w,"A")==0)) { + if (!ae->wl[ae->idx+1].t) ae->idx++; + switch (GetCRC(ae->wl[ae->idx+1].w)) { + case CRC_A:___output(ae,OPCODE+7);ae->nop+=1;break; + case CRC_MHL:___output(ae,OPCODE+6);ae->nop+=2;break; + case CRC_B:___output(ae,OPCODE);ae->nop+=1;break; + case CRC_C:___output(ae,OPCODE+1);ae->nop+=1;break; + case CRC_D:___output(ae,OPCODE+2);ae->nop+=1;break; + case CRC_E:___output(ae,OPCODE+3);ae->nop+=1;break; + case CRC_H:___output(ae,OPCODE+4);ae->nop+=1;break; + case CRC_L:___output(ae,OPCODE+5);ae->nop+=1;break; + case CRC_IXH:case CRC_HX:case CRC_XH:___output(ae,0xDD);___output(ae,OPCODE+4);ae->nop+=2;break; + case CRC_IXL:case CRC_LX:case CRC_XL:___output(ae,0xDD);___output(ae,OPCODE+5);ae->nop+=2;break; + case CRC_IYH:case CRC_HY:case CRC_YH:___output(ae,0xFD);___output(ae,OPCODE+4);ae->nop+=2;break; + case CRC_IYL:case CRC_LY:case CRC_YL:___output(ae,0xFD);___output(ae,OPCODE+5);ae->nop+=2;break; + case CRC_IX:case CRC_IY: + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Use SUB with A,B,C,D,E,H,L,XH,XL,YH,YL,(HL),(IX),(IY)\n"); + ae->idx++; + return; + default: + if (strncmp(ae->wl[ae->idx+1].w,"(IX",3)==0) { + ___output(ae,0xDD);___output(ae,OPCODE+6); + PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8); + ae->nop+=5; + } else if (strncmp(ae->wl[ae->idx+1].w,"(IY",3)==0) { + ___output(ae,0xFD);___output(ae,OPCODE+6); + PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8); + ae->nop+=5; + } else { + ___output(ae,0xD6); + PushExpression(ae,ae->idx+1,E_EXPRESSION_V8); + ae->nop+=2; + } + } + ae->idx++; + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Use SUB with A,B,C,D,E,H,L,XH,XL,YH,YL,(HL),(IX),(IY)\n"); + } +} +void _AND(struct s_assenv *ae) { + #ifdef OPCODE + #undef OPCODE + #endif + #define OPCODE 0xA0 + + if (!ae->wl[ae->idx].t && ae->wl[ae->idx+1].t==1) { + switch (GetCRC(ae->wl[ae->idx+1].w)) { + case CRC_A:___output(ae,OPCODE+7);ae->nop+=1;break; + case CRC_MHL:___output(ae,OPCODE+6);ae->nop+=2;break; + case CRC_B:___output(ae,OPCODE);ae->nop+=1;break; + case CRC_C:___output(ae,OPCODE+1);ae->nop+=1;break; + case CRC_D:___output(ae,OPCODE+2);ae->nop+=1;break; + case CRC_E:___output(ae,OPCODE+3);ae->nop+=1;break; + case CRC_H:___output(ae,OPCODE+4);ae->nop+=1;break; + case CRC_L:___output(ae,OPCODE+5);ae->nop+=1;break; + case CRC_IXH:case CRC_HX:case CRC_XH:___output(ae,0xDD);___output(ae,OPCODE+4);ae->nop+=2;break; + case CRC_IXL:case CRC_LX:case CRC_XL:___output(ae,0xDD);___output(ae,OPCODE+5);ae->nop+=2;break; + case CRC_IYH:case CRC_HY:case CRC_YH:___output(ae,0xFD);___output(ae,OPCODE+4);ae->nop+=2;break; + case CRC_IYL:case CRC_LY:case CRC_YL:___output(ae,0xFD);___output(ae,OPCODE+5);ae->nop+=2;break; + default: + if (strncmp(ae->wl[ae->idx+1].w,"(IX",3)==0) { + ___output(ae,0xDD);___output(ae,OPCODE+6); + PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8); + ae->nop+=5; + } else if (strncmp(ae->wl[ae->idx+1].w,"(IY",3)==0) { + ___output(ae,0xFD);___output(ae,OPCODE+6); + PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8); + ae->nop+=5; + } else { + ___output(ae,0xE6); + PushExpression(ae,ae->idx+1,E_EXPRESSION_V8); + ae->nop+=2; + } + } + ae->idx++; + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Use AND with A,B,C,D,E,H,L,XH,XL,YH,YL,(HL),(IX),(IY)\n"); + } +} +void _OR(struct s_assenv *ae) { + #ifdef OPCODE + #undef OPCODE + #endif + #define OPCODE 0xB0 + + if (!ae->wl[ae->idx].t && ae->wl[ae->idx+1].t==1) { + switch (GetCRC(ae->wl[ae->idx+1].w)) { + case CRC_A:___output(ae,OPCODE+7);ae->nop+=1;break; + case CRC_MHL:___output(ae,OPCODE+6);ae->nop+=2;break; + case CRC_B:___output(ae,OPCODE);ae->nop+=1;break; + case CRC_C:___output(ae,OPCODE+1);ae->nop+=1;break; + case CRC_D:___output(ae,OPCODE+2);ae->nop+=1;break; + case CRC_E:___output(ae,OPCODE+3);ae->nop+=1;break; + case CRC_H:___output(ae,OPCODE+4);ae->nop+=1;break; + case CRC_L:___output(ae,OPCODE+5);ae->nop+=1;break; + case CRC_IXH:case CRC_HX:case CRC_XH:___output(ae,0xDD);___output(ae,OPCODE+4);ae->nop+=2;break; + case CRC_IXL:case CRC_LX:case CRC_XL:___output(ae,0xDD);___output(ae,OPCODE+5);ae->nop+=2;break; + case CRC_IYH:case CRC_HY:case CRC_YH:___output(ae,0xFD);___output(ae,OPCODE+4);ae->nop+=2;break; + case CRC_IYL:case CRC_LY:case CRC_YL:___output(ae,0xFD);___output(ae,OPCODE+5);ae->nop+=2;break; + default: + if (strncmp(ae->wl[ae->idx+1].w,"(IX",3)==0) { + ___output(ae,0xDD);___output(ae,OPCODE+6); + PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8); + ae->nop+=5; + } else if (strncmp(ae->wl[ae->idx+1].w,"(IY",3)==0) { + ___output(ae,0xFD);___output(ae,OPCODE+6); + PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8); + ae->nop+=5; + } else { + ___output(ae,0xF6); + PushExpression(ae,ae->idx+1,E_EXPRESSION_V8); + ae->nop+=2; + } + } + ae->idx++; + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Use OR with A,B,C,D,E,H,L,XH,XL,YH,YL,(HL),(IX),(IY)\n"); + } +} +void _XOR(struct s_assenv *ae) { + #ifdef OPCODE + #undef OPCODE + #endif + #define OPCODE 0xA8 + + if (!ae->wl[ae->idx].t && ae->wl[ae->idx+1].t==1) { + switch (GetCRC(ae->wl[ae->idx+1].w)) { + case CRC_A:___output(ae,OPCODE+7);ae->nop+=1;break; + case CRC_MHL:___output(ae,OPCODE+6);ae->nop+=2;break; + case CRC_B:___output(ae,OPCODE);ae->nop+=1;break; + case CRC_C:___output(ae,OPCODE+1);ae->nop+=1;break; + case CRC_D:___output(ae,OPCODE+2);ae->nop+=1;break; + case CRC_E:___output(ae,OPCODE+3);ae->nop+=1;break; + case CRC_H:___output(ae,OPCODE+4);ae->nop+=1;break; + case CRC_L:___output(ae,OPCODE+5);ae->nop+=1;break; + case CRC_IXH:case CRC_HX:case CRC_XH:___output(ae,0xDD);___output(ae,OPCODE+4);ae->nop+=2;break; + case CRC_IXL:case CRC_LX:case CRC_XL:___output(ae,0xDD);___output(ae,OPCODE+5);ae->nop+=2;break; + case CRC_IYH:case CRC_HY:case CRC_YH:___output(ae,0xFD);___output(ae,OPCODE+4);ae->nop+=2;break; + case CRC_IYL:case CRC_LY:case CRC_YL:___output(ae,0xFD);___output(ae,OPCODE+5);ae->nop+=2;break; + default: + if (strncmp(ae->wl[ae->idx+1].w,"(IX",3)==0) { + ___output(ae,0xDD);___output(ae,OPCODE+6); + PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8); + ae->nop+=5; + } else if (strncmp(ae->wl[ae->idx+1].w,"(IY",3)==0) { + ___output(ae,0xFD);___output(ae,OPCODE+6); + PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8); + ae->nop+=5; + } else { + ___output(ae,0xEE); + PushExpression(ae,ae->idx+1,E_EXPRESSION_V8); + ae->nop+=2; + } + } + ae->idx++; + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Use XOR with A,B,C,D,E,H,L,XH,XL,YH,YL,(HL),(IX),(IY)\n"); + } +} + + +void _POP(struct s_assenv *ae) { + if (!ae->wl[ae->idx].t) { + do { + ae->idx++; + switch (GetCRC(ae->wl[ae->idx].w)) { + case CRC_AF:___output(ae,0xF1);ae->nop+=3;break; + case CRC_BC:___output(ae,0xC1);ae->nop+=3;break; + case CRC_DE:___output(ae,0xD1);ae->nop+=3;break; + case CRC_HL:___output(ae,0xE1);ae->nop+=3;break; + case CRC_IX:___output(ae,0xDD);___output(ae,0xE1);ae->nop+=4;break; + case CRC_IY:___output(ae,0xFD);___output(ae,0xE1);ae->nop+=4;break; + default: + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Use POP with AF,BC,DE,HL,IX,IY\n"); + } + } while (ae->wl[ae->idx].t!=1); + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"POP need at least one parameter\n"); + } +} +void _PUSH(struct s_assenv *ae) { + if (!ae->wl[ae->idx].t) { + do { + ae->idx++; + switch (GetCRC(ae->wl[ae->idx].w)) { + case CRC_AF:___output(ae,0xF5);ae->nop+=4;break; + case CRC_BC:___output(ae,0xC5);ae->nop+=4;break; + case CRC_DE:___output(ae,0xD5);ae->nop+=4;break; + case CRC_HL:___output(ae,0xE5);ae->nop+=4;break; + case CRC_IX:___output(ae,0xDD);___output(ae,0xE5);ae->nop+=5;break; + case CRC_IY:___output(ae,0xFD);___output(ae,0xE5);ae->nop+=5;break; + default: + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Use PUSH with AF,BC,DE,HL,IX,IY\n"); + } + } while (ae->wl[ae->idx].t!=1); + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"PUSH need at least one parameter\n"); + } +} + +void _IM(struct s_assenv *ae) { + if (!ae->wl[ae->idx].t && ae->wl[ae->idx+1].t==1) { + /* la valeur du parametre va definir l'opcode du IM */ + ___output(ae,0xED); + PushExpression(ae,ae->idx+1,E_EXPRESSION_IM); + ae->idx++; + ae->nop+=2; + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"IM need one parameter\n"); + } +} + +void _RLCA(struct s_assenv *ae) { + if (ae->wl[ae->idx].t) { + ___output(ae,0x7); + ae->nop+=1; + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"RLCA does not need parameter\n"); + } +} +void _RRCA(struct s_assenv *ae) { + if (ae->wl[ae->idx].t) { + ___output(ae,0xF); + ae->nop+=1; + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"RRCA does not need parameter\n"); + } +} +void _NEG(struct s_assenv *ae) { + if (ae->wl[ae->idx].t) { + ___output(ae,0xED); + ___output(ae,0x44); + ae->nop+=2; + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"NEG does not need parameter\n"); + } +} +void _DAA(struct s_assenv *ae) { + if (ae->wl[ae->idx].t) { + ___output(ae,0x27); + ae->nop+=1; + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"DAA does not need parameter\n"); + } +} +void _CPL(struct s_assenv *ae) { + if (ae->wl[ae->idx].t) { + ___output(ae,0x2F); + ae->nop+=1; + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"CPL does not need parameter\n"); + } +} +void _RETI(struct s_assenv *ae) { + if (ae->wl[ae->idx].t) { + ___output(ae,0xED); + ___output(ae,0x4D); + ae->nop+=4; + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"RETI does not need parameter\n"); + } +} +void _SCF(struct s_assenv *ae) { + if (ae->wl[ae->idx].t) { + ___output(ae,0x37); + ae->nop+=1; + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"SCF does not need parameter\n"); + } +} +void _LDD(struct s_assenv *ae) { + if (ae->wl[ae->idx].t) { + ___output(ae,0xED); + ___output(ae,0xA8); + ae->nop+=5; + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"LDD does not need parameter\n"); + } +} +void _LDDR(struct s_assenv *ae) { + if (ae->wl[ae->idx].t) { + ___output(ae,0xED); + ___output(ae,0xB8); + ae->nop+=5; + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"LDDR does not need parameter\n"); + } +} +void _LDI(struct s_assenv *ae) { + if (ae->wl[ae->idx].t) { + ___output(ae,0xED); + ___output(ae,0xA0); + ae->nop+=5; + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"LDI does not need parameter\n"); + } +} +void _LDIR(struct s_assenv *ae) { + if (ae->wl[ae->idx].t) { + ___output(ae,0xED); + ___output(ae,0xB0); + ae->nop+=5; + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"LDIR does not need parameter\n"); + } +} +void _CCF(struct s_assenv *ae) { + if (ae->wl[ae->idx].t) { + ___output(ae,0x3F); + ae->nop+=5; + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"CCF does not need parameter\n"); + } +} +void _CPD(struct s_assenv *ae) { + if (ae->wl[ae->idx].t) { + ___output(ae,0xED); + ___output(ae,0xA9); + ae->nop+=4; + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"CPD does not need parameter\n"); + } +} +void _CPDR(struct s_assenv *ae) { + if (ae->wl[ae->idx].t) { + ___output(ae,0xED); + ___output(ae,0xB9); + ae->nop+=4; + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"CPDR does not need parameter\n"); + } +} +void _CPI(struct s_assenv *ae) { + if (ae->wl[ae->idx].t) { + ___output(ae,0xED); + ___output(ae,0xA1); + ae->nop+=4; + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"CPI does not need parameter\n"); + } +} +void _CPIR(struct s_assenv *ae) { + if (ae->wl[ae->idx].t) { + ___output(ae,0xED); + ___output(ae,0xB1); + ae->nop+=4; + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"CPIR does not need parameter\n"); + } +} +void _OUTD(struct s_assenv *ae) { + if (ae->wl[ae->idx].t) { + ___output(ae,0xED); + ___output(ae,0xAB); + ae->nop+=5; + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"OUTD does not need parameter\n"); + } +} +void _OTDR(struct s_assenv *ae) { + if (ae->wl[ae->idx].t) { + ___output(ae,0xED); + ___output(ae,0xBB); + ae->nop+=5; + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"OTDR does not need parameter\n"); + } +} +void _OUTI(struct s_assenv *ae) { + if (ae->wl[ae->idx].t) { + ___output(ae,0xED); + ___output(ae,0xA3); + ae->nop+=5; + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"OUTI does not need parameter\n"); + } +} +void _OTIR(struct s_assenv *ae) { + if (ae->wl[ae->idx].t) { + ___output(ae,0xED); + ___output(ae,0xB3); + ae->nop+=5; + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"OTIR does not need parameter\n"); + } +} +void _RETN(struct s_assenv *ae) { + if (ae->wl[ae->idx].t) { + ___output(ae,0xED); + ___output(ae,0x45); + ae->nop+=4; + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"RETN does not need parameter\n"); + } +} +void _IND(struct s_assenv *ae) { + if (ae->wl[ae->idx].t) { + ___output(ae,0xED); + ___output(ae,0xAA); + ae->nop+=5; + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"IND does not need parameter\n"); + } +} +void _INDR(struct s_assenv *ae) { + if (ae->wl[ae->idx].t) { + ___output(ae,0xED); + ___output(ae,0xBA); + ae->nop+=5; + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"INDR does not need parameter\n"); + } +} +void _INI(struct s_assenv *ae) { + if (ae->wl[ae->idx].t) { + ___output(ae,0xED); + ___output(ae,0xA2); + ae->nop+=5; + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"INI does not need parameter\n"); + } +} +void _INIR(struct s_assenv *ae) { + if (ae->wl[ae->idx].t==1) { + ___output(ae,0xED); + ___output(ae,0xB2); + ae->nop+=5; + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"INIR does not need parameter\n"); + } +} +void _EXX(struct s_assenv *ae) { + if (ae->wl[ae->idx].t==1) { + ___output(ae,0xD9); + ae->nop+=1; + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"EXX does not need parameter\n"); + } +} +void _HALT(struct s_assenv *ae) { + if (ae->wl[ae->idx].t==1) { + ___output(ae,0x76); + ae->nop+=1; + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"HALT does not need parameter\n"); + } +} + +void _RLA(struct s_assenv *ae) { + if (ae->wl[ae->idx].t==1) { + ___output(ae,0x17); + ae->nop+=1; + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"RLA does not need parameter\n"); + } +} +void _RRA(struct s_assenv *ae) { + if (ae->wl[ae->idx].t==1) { + ___output(ae,0x1F); + ae->nop+=1; + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"RRA does not need parameter\n"); + } +} +void _RLD(struct s_assenv *ae) { + if (ae->wl[ae->idx].t==1) { + ___output(ae,0xED); + ___output(ae,0x6F); + ae->nop+=5; + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"RLD does not need parameter\n"); + } +} +void _RRD(struct s_assenv *ae) { + if (ae->wl[ae->idx].t==1) { + ___output(ae,0xED); + ___output(ae,0x67); + ae->nop+=5; + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"RRD does not need parameter\n"); + } +} + + +void _EXA(struct s_assenv *ae) { + if (ae->wl[ae->idx].t==1) { + ___output(ae,0x08);ae->nop+=1; + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"EXA alias does not need parameter\n"); + } +} + +void _NOP(struct s_assenv *ae) { + int o; + + if (ae->wl[ae->idx].t) { + ___output(ae,0x00); + ae->nop+=1; + } else if (!ae->wl[ae->idx].t && ae->wl[ae->idx+1].t==1) { + ExpressionFastTranslate(ae,&ae->wl[ae->idx+1].w,0); + o=RoundComputeExpressionCore(ae,ae->wl[ae->idx+1].w,ae->codeadr,0); + if (o>=0) { + while (o>0) { + ___output(ae,0x00); + ae->nop+=1; + o--; + } + } + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"NOP is supposed to be used without parameter or with one optional parameter\n"); + } +} +void _DI(struct s_assenv *ae) { + if (ae->wl[ae->idx].t) { + ___output(ae,0xF3); + ae->nop+=1; + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"DI does not need parameter\n"); + } +} +void _EI(struct s_assenv *ae) { + if (ae->wl[ae->idx].t) { + ___output(ae,0xFB); + ae->nop+=1; + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"EI does not need parameter\n"); + } +} + +void _RST(struct s_assenv *ae) { + if (!ae->wl[ae->idx].t && ae->wl[ae->idx+1].t!=2) { + if (!strcmp(ae->wl[ae->idx+1].w,"(IY)") || !strcmp(ae->wl[ae->idx+1].w,"(IX)")) { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"RST cannot use IX or IY\n"); + } else { + /* la valeur du parametre va definir l'opcode du RST */ + PushExpression(ae,ae->idx+1,E_EXPRESSION_RST); + } + ae->idx++; + ae->nop+=4; + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"RST need one parameter\n"); + } +} + +void _DJNZ(struct s_assenv *ae) { + if (!ae->wl[ae->idx].t && ae->wl[ae->idx+1].t==1) { + if (IsRegister(ae->wl[ae->idx+1].w)) { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"DJNZ cannot use register\n"); + } else if (strcmp("(IX)",ae->wl[ae->idx+1].w)==0 || strcmp("(IY)",ae->wl[ae->idx+1].w)==0) { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"DJNZ cannot use register\n"); + } else { + ___output(ae,0x10); + PushExpression(ae,ae->idx+1,E_EXPRESSION_J8); + ae->nop+=3; + } + ae->idx++; + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"DJNZ need one parameter\n"); + } +} + +void _LD(struct s_assenv *ae) { + /* on check qu'il y a au moins deux parametres */ + if (!ae->wl[ae->idx+1].t && ae->wl[ae->idx+2].t==1) { + switch (GetCRC(ae->wl[ae->idx+1].w)) { + case CRC_A: + switch (GetCRC(ae->wl[ae->idx+2].w)) { + case CRC_I:___output(ae,0xED);___output(ae,0x57);ae->nop+=3;break; + case CRC_R:___output(ae,0xED);___output(ae,0x5F);ae->nop+=3;break; + case CRC_B:___output(ae,0x78);ae->nop+=1;break; + case CRC_C:___output(ae,0x79);ae->nop+=1;break; + case CRC_D:___output(ae,0x7A);ae->nop+=1;break; + case CRC_E:___output(ae,0x7B);ae->nop+=1;break; + case CRC_H:___output(ae,0x7C);ae->nop+=1;break; + case CRC_L:___output(ae,0x7D);ae->nop+=1;break; + case CRC_IXH:case CRC_HX:case CRC_XH:___output(ae,0xDD);___output(ae,0x7C);ae->nop+=2;break; + case CRC_IXL:case CRC_LX:case CRC_XL:___output(ae,0xDD);___output(ae,0x7D);ae->nop+=2;break; + case CRC_IYH:case CRC_HY:case CRC_YH:___output(ae,0xFD);___output(ae,0x7C);ae->nop+=2;break; + case CRC_IYL:case CRC_LY:case CRC_YL:___output(ae,0xFD);___output(ae,0x7D);ae->nop+=2;break; + case CRC_MHL:___output(ae,0x7E);ae->nop+=2;break; + case CRC_A:___output(ae,0x7F);ae->nop+=1;break; + case CRC_MBC:___output(ae,0x0A);ae->nop+=2;break; + case CRC_MDE:___output(ae,0x1A);ae->nop+=2;break; + default: + /* (ix+expression) (iy+expression) (expression) expression */ + if (strncmp(ae->wl[ae->idx+2].w,"(IX",3)==0) { + ___output(ae,0xDD);___output(ae,0x7E); + PushExpression(ae,ae->idx+2,E_EXPRESSION_IV8); + ae->nop+=5; + } else if (strncmp(ae->wl[ae->idx+2].w,"(IY",3)==0) { + ___output(ae,0xFD);___output(ae,0x7E); + PushExpression(ae,ae->idx+2,E_EXPRESSION_IV8); + ae->nop+=5; + } else if (StringIsMem(ae->wl[ae->idx+2].w)) { + ___output(ae,0x3A); + PushExpression(ae,ae->idx+2,E_EXPRESSION_V16); + ae->nop+=4; + } else { + ___output(ae,0x3E); + PushExpression(ae,ae->idx+2,E_EXPRESSION_V8); + ae->nop+=2; + } + } + break; + case CRC_I: + if (GetCRC(ae->wl[ae->idx+2].w)==CRC_A) { + ___output(ae,0xED);___output(ae,0x47); + ae->nop+=3; + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"LD I,A only\n"); + } + break; + case CRC_R: + if (GetCRC(ae->wl[ae->idx+2].w)==CRC_A) { + ___output(ae,0xED);___output(ae,0x4F); + ae->nop+=3; + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"LD R,A only\n"); + } + break; + case CRC_B: + switch (GetCRC(ae->wl[ae->idx+2].w)) { + case CRC_B:___output(ae,0x40);ae->nop+=1;break; + case CRC_C:___output(ae,0x41);ae->nop+=1;break; + case CRC_D:___output(ae,0x42);ae->nop+=1;break; + case CRC_E:___output(ae,0x43);ae->nop+=1;break; + case CRC_H:___output(ae,0x44);ae->nop+=1;break; + case CRC_L:___output(ae,0x45);ae->nop+=1;break; + case CRC_IXH:case CRC_HX:case CRC_XH:___output(ae,0xDD);___output(ae,0x44);ae->nop+=2;break; + case CRC_IXL:case CRC_LX:case CRC_XL:___output(ae,0xDD);___output(ae,0x45);ae->nop+=2;break; + case CRC_IYH:case CRC_HY:case CRC_YH:___output(ae,0xFD);___output(ae,0x44);ae->nop+=2;break; + case CRC_IYL:case CRC_LY:case CRC_YL:___output(ae,0xFD);___output(ae,0x45);ae->nop+=2;break; + case CRC_MHL:___output(ae,0x46);ae->nop+=2;break; + case CRC_A:___output(ae,0x47);ae->nop+=1;break; + default: + /* (ix+expression) (iy+expression) expression */ + if (strncmp(ae->wl[ae->idx+2].w,"(IX",3)==0) { + ___output(ae,0xDD);___output(ae,0x46); + PushExpression(ae,ae->idx+2,E_EXPRESSION_IV8); + ae->nop+=5; + } else if (strncmp(ae->wl[ae->idx+2].w,"(IY",3)==0) { + ___output(ae,0xFD);___output(ae,0x46); + PushExpression(ae,ae->idx+2,E_EXPRESSION_IV8); + ae->nop+=5; + } else { + ___output(ae,0x06); + PushExpression(ae,ae->idx+2,E_EXPRESSION_V8); + ae->nop+=2; + } + } + break; + case CRC_C: + switch (GetCRC(ae->wl[ae->idx+2].w)) { + case CRC_B:___output(ae,0x48);ae->nop+=1;break; + case CRC_C:___output(ae,0x49);ae->nop+=1;break; + case CRC_D:___output(ae,0x4A);ae->nop+=1;break; + case CRC_E:___output(ae,0x4B);ae->nop+=1;break; + case CRC_H:___output(ae,0x4C);ae->nop+=1;break; + case CRC_L:___output(ae,0x4D);ae->nop+=1;break; + case CRC_IXH:case CRC_HX:case CRC_XH:___output(ae,0xDD);___output(ae,0x4C);ae->nop+=2;break; + case CRC_IXL:case CRC_LX:case CRC_XL:___output(ae,0xDD);___output(ae,0x4D);ae->nop+=2;break; + case CRC_IYH:case CRC_HY:case CRC_YH:___output(ae,0xFD);___output(ae,0x4C);ae->nop+=2;break; + case CRC_IYL:case CRC_LY:case CRC_YL:___output(ae,0xFD);___output(ae,0x4D);ae->nop+=2;break; + case CRC_MHL:___output(ae,0x4E);ae->nop+=2;break; + case CRC_A:___output(ae,0x4F);ae->nop+=1;break; + default: + /* (ix+expression) (iy+expression) expression */ + if (strncmp(ae->wl[ae->idx+2].w,"(IX",3)==0) { + ___output(ae,0xDD);___output(ae,0x4E); + PushExpression(ae,ae->idx+2,E_EXPRESSION_IV8); + ae->nop+=5; + } else if (strncmp(ae->wl[ae->idx+2].w,"(IY",3)==0) { + ___output(ae,0xFD);___output(ae,0x4E); + PushExpression(ae,ae->idx+2,E_EXPRESSION_IV8); + ae->nop+=5; + } else { + ___output(ae,0x0E); + PushExpression(ae,ae->idx+2,E_EXPRESSION_V8); + ae->nop+=2; + } + } + break; + case CRC_D: + switch (GetCRC(ae->wl[ae->idx+2].w)) { + case CRC_B:___output(ae,0x50);ae->nop+=1;break; + case CRC_C:___output(ae,0x51);ae->nop+=1;break; + case CRC_D:___output(ae,0x52);ae->nop+=1;break; + case CRC_E:___output(ae,0x53);ae->nop+=1;break; + case CRC_H:___output(ae,0x54);ae->nop+=1;break; + case CRC_L:___output(ae,0x55);ae->nop+=1;break; + case CRC_IXH:case CRC_HX:case CRC_XH:___output(ae,0xDD);___output(ae,0x54);ae->nop+=2;break; + case CRC_IXL:case CRC_LX:case CRC_XL:___output(ae,0xDD);___output(ae,0x55);ae->nop+=2;break; + case CRC_IYH:case CRC_HY:case CRC_YH:___output(ae,0xFD);___output(ae,0x54);ae->nop+=2;break; + case CRC_IYL:case CRC_LY:case CRC_YL:___output(ae,0xFD);___output(ae,0x55);ae->nop+=2;break; + case CRC_MHL:___output(ae,0x56);ae->nop+=2;break; + case CRC_A:___output(ae,0x57);ae->nop+=1;break; + default: + /* (ix+expression) (iy+expression) expression */ + if (strncmp(ae->wl[ae->idx+2].w,"(IX",3)==0) { + ___output(ae,0xDD);___output(ae,0x56); + PushExpression(ae,ae->idx+2,E_EXPRESSION_IV8); + ae->nop+=5; + } else if (strncmp(ae->wl[ae->idx+2].w,"(IY",3)==0) { + ___output(ae,0xFD);___output(ae,0x56); + PushExpression(ae,ae->idx+2,E_EXPRESSION_IV8); + ae->nop+=5; + } else { + ___output(ae,0x16); + PushExpression(ae,ae->idx+2,E_EXPRESSION_V8); + ae->nop+=2; + } + } + break; + case CRC_E: + switch (GetCRC(ae->wl[ae->idx+2].w)) { + case CRC_B:___output(ae,0x58);ae->nop+=1;break; + case CRC_C:___output(ae,0x59);ae->nop+=1;break; + case CRC_D:___output(ae,0x5A);ae->nop+=1;break; + case CRC_E:___output(ae,0x5B);ae->nop+=1;break; + case CRC_H:___output(ae,0x5C);ae->nop+=1;break; + case CRC_L:___output(ae,0x5D);ae->nop+=1;break; + case CRC_IXH:case CRC_HX:case CRC_XH:___output(ae,0xDD);___output(ae,0x5C);ae->nop+=2;break; + case CRC_IXL:case CRC_LX:case CRC_XL:___output(ae,0xDD);___output(ae,0x5D);ae->nop+=2;break; + case CRC_IYH:case CRC_HY:case CRC_YH:___output(ae,0xFD);___output(ae,0x5C);ae->nop+=2;break; + case CRC_IYL:case CRC_LY:case CRC_YL:___output(ae,0xFD);___output(ae,0x5D);ae->nop+=2;break; + case CRC_MHL:___output(ae,0x5E);ae->nop+=2;break; + case CRC_A:___output(ae,0x5F);ae->nop+=1;break; + default: + /* (ix+expression) (iy+expression) expression */ + if (strncmp(ae->wl[ae->idx+2].w,"(IX",3)==0) { + ___output(ae,0xDD);___output(ae,0x5E); + PushExpression(ae,ae->idx+2,E_EXPRESSION_IV8); + ae->nop+=5; + } else if (strncmp(ae->wl[ae->idx+2].w,"(IY",3)==0) { + ___output(ae,0xFD);___output(ae,0x5E); + PushExpression(ae,ae->idx+2,E_EXPRESSION_IV8); + ae->nop+=5; + } else { + ___output(ae,0x1E); + PushExpression(ae,ae->idx+2,E_EXPRESSION_V8); + ae->nop+=2; + } + } + break; + case CRC_IYH:case CRC_HY:case CRC_YH: + switch (GetCRC(ae->wl[ae->idx+2].w)) { + case CRC_B:___output(ae,0xFD);___output(ae,0x60);ae->nop+=2;break; + case CRC_C:___output(ae,0xFD);___output(ae,0x61);ae->nop+=2;break; + case CRC_D:___output(ae,0xFD);___output(ae,0x62);ae->nop+=2;break; + case CRC_E:___output(ae,0xFD);___output(ae,0x63);ae->nop+=2;break; + case CRC_IYH:case CRC_HY:case CRC_YH:___output(ae,0xFD);___output(ae,0x64);ae->nop+=2;break; + case CRC_IYL:case CRC_LY:case CRC_YL:___output(ae,0xFD);___output(ae,0x65);ae->nop+=2;break; + case CRC_A:___output(ae,0xFD);___output(ae,0x67);ae->nop+=2;break; + default: + ___output(ae,0xFD);___output(ae,0x26); + PushExpression(ae,ae->idx+2,E_EXPRESSION_V8); + ae->nop+=3; + } + break; + case CRC_IYL:case CRC_LY:case CRC_YL: + switch (GetCRC(ae->wl[ae->idx+2].w)) { + case CRC_B:___output(ae,0xFD);___output(ae,0x68);ae->nop+=2;break; + case CRC_C:___output(ae,0xFD);___output(ae,0x69);ae->nop+=2;break; + case CRC_D:___output(ae,0xFD);___output(ae,0x6A);ae->nop+=2;break; + case CRC_E:___output(ae,0xFD);___output(ae,0x6B);ae->nop+=2;break; + case CRC_IYH:case CRC_HY:case CRC_YH:___output(ae,0xFD);___output(ae,0x6C);ae->nop+=2;break; + case CRC_IYL:case CRC_LY:case CRC_YL:___output(ae,0xFD);___output(ae,0x6D);ae->nop+=2;break; + case CRC_A:___output(ae,0xFD);___output(ae,0x6F);ae->nop+=2;break; + default: + ___output(ae,0xFD);___output(ae,0x2E); + PushExpression(ae,ae->idx+2,E_EXPRESSION_V8); + ae->nop+=3; + } + break; + case CRC_IXH:case CRC_HX:case CRC_XH: + switch (GetCRC(ae->wl[ae->idx+2].w)) { + case CRC_B:___output(ae,0xDD);___output(ae,0x60);ae->nop+=2;break; + case CRC_C:___output(ae,0xDD);___output(ae,0x61);ae->nop+=2;break; + case CRC_D:___output(ae,0xDD);___output(ae,0x62);ae->nop+=2;break; + case CRC_E:___output(ae,0xDD);___output(ae,0x63);ae->nop+=2;break; + case CRC_IXH:case CRC_HX:case CRC_XH:___output(ae,0xDD);___output(ae,0x64);ae->nop+=2;break; + case CRC_IXL:case CRC_LX:case CRC_XL:___output(ae,0xDD);___output(ae,0x65);ae->nop+=2;break; + case CRC_A:___output(ae,0xDD);___output(ae,0x67);ae->nop+=2;break; + default: + ___output(ae,0xDD);___output(ae,0x26); + PushExpression(ae,ae->idx+2,E_EXPRESSION_V8); + ae->nop+=3; + } + break; + case CRC_IXL:case CRC_LX:case CRC_XL: + switch (GetCRC(ae->wl[ae->idx+2].w)) { + case CRC_B:___output(ae,0xDD);___output(ae,0x68);ae->nop+=2;break; + case CRC_C:___output(ae,0xDD);___output(ae,0x69);ae->nop+=2;break; + case CRC_D:___output(ae,0xDD);___output(ae,0x6A);ae->nop+=2;break; + case CRC_E:___output(ae,0xDD);___output(ae,0x6B);ae->nop+=2;break; + case CRC_IXH:case CRC_HX:case CRC_XH:___output(ae,0xDD);___output(ae,0x6C);ae->nop+=2;break; + case CRC_IXL:case CRC_LX:case CRC_XL:___output(ae,0xDD);___output(ae,0x6D);ae->nop+=2;break; + case CRC_A:___output(ae,0xDD);___output(ae,0x6F);ae->nop+=2;break; + default: + ___output(ae,0xDD);___output(ae,0x2E); + PushExpression(ae,ae->idx+2,E_EXPRESSION_V8); + ae->nop+=3; + } + break; + case CRC_H: + switch (GetCRC(ae->wl[ae->idx+2].w)) { + case CRC_B:___output(ae,0x60);ae->nop+=1;break; + case CRC_C:___output(ae,0x61);ae->nop+=1;break; + case CRC_D:___output(ae,0x62);ae->nop+=1;break; + case CRC_E:___output(ae,0x63);ae->nop+=1;break; + case CRC_H:___output(ae,0x64);ae->nop+=1;break; + case CRC_L:___output(ae,0x65);ae->nop+=1;break; + case CRC_MHL:___output(ae,0x66);ae->nop+=2;break; + case CRC_A:___output(ae,0x67);ae->nop+=1;break; + default: + /* (ix+expression) (iy+expression) expression */ + if (strncmp(ae->wl[ae->idx+2].w,"(IX",3)==0) { + ___output(ae,0xDD);___output(ae,0x66); + PushExpression(ae,ae->idx+2,E_EXPRESSION_IV8); + ae->nop+=5; + } else if (strncmp(ae->wl[ae->idx+2].w,"(IY",3)==0) { + ___output(ae,0xFD);___output(ae,0x66); + PushExpression(ae,ae->idx+2,E_EXPRESSION_IV8); + ae->nop+=5; + } else { + ___output(ae,0x26); + PushExpression(ae,ae->idx+2,E_EXPRESSION_V8); + ae->nop+=2; + } + } + break; + case CRC_L: + switch (GetCRC(ae->wl[ae->idx+2].w)) { + case CRC_B:___output(ae,0x68);ae->nop+=1;break; + case CRC_C:___output(ae,0x69);ae->nop+=1;break; + case CRC_D:___output(ae,0x6A);ae->nop+=1;break; + case CRC_E:___output(ae,0x6B);ae->nop+=1;break; + case CRC_H:___output(ae,0x6C);ae->nop+=1;break; + case CRC_L:___output(ae,0x6D);ae->nop+=1;break; + case CRC_MHL:___output(ae,0x6E);ae->nop+=2;break; + case CRC_A:___output(ae,0x6F);ae->nop+=1;break; + default: + /* (ix+expression) (iy+expression) expression */ + if (strncmp(ae->wl[ae->idx+2].w,"(IX",3)==0) { + ___output(ae,0xDD);___output(ae,0x6E); + PushExpression(ae,ae->idx+2,E_EXPRESSION_IV8); + ae->nop+=5; + } else if (strncmp(ae->wl[ae->idx+2].w,"(IY",3)==0) { + ___output(ae,0xFD);___output(ae,0x6E); + PushExpression(ae,ae->idx+2,E_EXPRESSION_IV8); + ae->nop+=5; + } else { + ___output(ae,0x2E); + PushExpression(ae,ae->idx+2,E_EXPRESSION_V8); + ae->nop+=2; + } + } + break; + case CRC_MHL: + switch (GetCRC(ae->wl[ae->idx+2].w)) { + case CRC_B:___output(ae,0x70);ae->nop+=2;break; + case CRC_C:___output(ae,0x71);ae->nop+=2;break; + case CRC_D:___output(ae,0x72);ae->nop+=2;break; + case CRC_E:___output(ae,0x73);ae->nop+=2;break; + case CRC_H:___output(ae,0x74);ae->nop+=2;break; + case CRC_L:___output(ae,0x75);ae->nop+=2;break; + case CRC_A:___output(ae,0x77);ae->nop+=2;break; + default: + /* expression */ + ___output(ae,0x36); + PushExpression(ae,ae->idx+2,E_EXPRESSION_V8); + ae->nop+=3; + } + break; + case CRC_MBC: + if (GetCRC(ae->wl[ae->idx+2].w)==CRC_A) { + ___output(ae,0x02); + ae->nop+=2; + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"LD (BC),A only\n"); + } + break; + case CRC_MDE: + if (GetCRC(ae->wl[ae->idx+2].w)==CRC_A) { + ___output(ae,0x12); + ae->nop+=2; + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"LD (DE),A only\n"); + } + break; + case CRC_HL: + switch (GetCRC(ae->wl[ae->idx+2].w)) { + case CRC_BC:___output(ae,0x60);___output(ae,0x69);ae->nop+=2;break; + case CRC_DE:___output(ae,0x62);___output(ae,0x6B);ae->nop+=2;break; + case CRC_HL:___output(ae,0x64);___output(ae,0x6D);ae->nop+=2;break; + default: + if (strncmp(ae->wl[ae->idx+2].w,"(IX+",4)==0) { + /* enhanced LD HL,(IX+nn) */ + ___output(ae,0xDD);___output(ae,0x66); + PushExpression(ae,ae->idx+2,E_EXPRESSION_IV81); + ___output(ae,0xDD);___output(ae,0x6E); + PushExpression(ae,ae->idx+2,E_EXPRESSION_IV8); + ae->nop+=10; + } else if (strncmp(ae->wl[ae->idx+2].w,"(IY+",4)==0) { + /* enhanced LD HL,(IY+nn) */ + ___output(ae,0xFD);___output(ae,0x66); + PushExpression(ae,ae->idx+2,E_EXPRESSION_IV81); + ___output(ae,0xFD);___output(ae,0x6E); + PushExpression(ae,ae->idx+2,E_EXPRESSION_IV8); + ae->nop+=10; + } else if (StringIsMem(ae->wl[ae->idx+2].w)) { + ___output(ae,0x2A); + PushExpression(ae,ae->idx+2,E_EXPRESSION_V16); + ae->nop+=5; + } else { + ___output(ae,0x21); + PushExpression(ae,ae->idx+2,E_EXPRESSION_V16); + ae->nop+=3; + } + } + break; + case CRC_BC: + switch (GetCRC(ae->wl[ae->idx+2].w)) { + case CRC_BC:___output(ae,0x40);___output(ae,0x49);ae->nop+=2;break; + case CRC_DE:___output(ae,0x42);___output(ae,0x4B);ae->nop+=2;break; + case CRC_HL:___output(ae,0x44);___output(ae,0x4D);ae->nop+=2;break; + default: + if (strncmp(ae->wl[ae->idx+2].w,"(IX+",4)==0) { + /* enhanced LD BC,(IX+nn) */ + ___output(ae,0xDD);___output(ae,0x46); + PushExpression(ae,ae->idx+2,E_EXPRESSION_IV81); + ___output(ae,0xDD);___output(ae,0x4E); + PushExpression(ae,ae->idx+2,E_EXPRESSION_IV8); + ae->nop+=10; + } else if (strncmp(ae->wl[ae->idx+2].w,"(IY+",4)==0) { + /* enhanced LD BC,(IY+nn) */ + ___output(ae,0xFD);___output(ae,0x46); + PushExpression(ae,ae->idx+2,E_EXPRESSION_IV81); + ___output(ae,0xFD);___output(ae,0x4E); + PushExpression(ae,ae->idx+2,E_EXPRESSION_IV8); + ae->nop+=10; + } else if (StringIsMem(ae->wl[ae->idx+2].w)) { + ___output(ae,0xED);___output(ae,0x4B); + PushExpression(ae,ae->idx+2,E_EXPRESSION_IV16); + ae->nop+=6; + } else { + ___output(ae,0x01); + PushExpression(ae,ae->idx+2,E_EXPRESSION_V16); + ae->nop+=3; + } + } + break; + case CRC_DE: + switch (GetCRC(ae->wl[ae->idx+2].w)) { + case CRC_BC:___output(ae,0x50);___output(ae,0x59);ae->nop+=2;break; + case CRC_DE:___output(ae,0x52);___output(ae,0x5B);ae->nop+=2;break; + case CRC_HL:___output(ae,0x54);___output(ae,0x5D);ae->nop+=2;break; + default: + if (strncmp(ae->wl[ae->idx+2].w,"(IX+",4)==0) { + /* enhanced LD DE,(IX+nn) */ + ___output(ae,0xDD);___output(ae,0x56); + PushExpression(ae,ae->idx+2,E_EXPRESSION_IV81); + ___output(ae,0xDD);___output(ae,0x5E); + PushExpression(ae,ae->idx+2,E_EXPRESSION_IV8); + ae->nop+=10; + } else if (strncmp(ae->wl[ae->idx+2].w,"(IY+",4)==0) { + /* enhanced LD DE,(IY+nn) */ + ___output(ae,0xFD);___output(ae,0x56); + PushExpression(ae,ae->idx+2,E_EXPRESSION_IV81); + ___output(ae,0xFD);___output(ae,0x5E); + PushExpression(ae,ae->idx+2,E_EXPRESSION_IV8); + ae->nop+=10; + } else if (StringIsMem(ae->wl[ae->idx+2].w)) { + ___output(ae,0xED);___output(ae,0x5B); + PushExpression(ae,ae->idx+2,E_EXPRESSION_IV16); + ae->nop+=6; + } else { + ___output(ae,0x11); + PushExpression(ae,ae->idx+2,E_EXPRESSION_V16); + ae->nop+=3; + } + } + break; + case CRC_IX: + if (StringIsMem(ae->wl[ae->idx+2].w)) { + ___output(ae,0xDD);___output(ae,0x2A); + PushExpression(ae,ae->idx+2,E_EXPRESSION_IV16); + ae->nop+=6; + } else { + ___output(ae,0xDD);___output(ae,0x21); + PushExpression(ae,ae->idx+2,E_EXPRESSION_IV16); + ae->nop+=4; + } + break; + case CRC_IY: + if (StringIsMem(ae->wl[ae->idx+2].w)) { + ___output(ae,0xFD);___output(ae,0x2A); + PushExpression(ae,ae->idx+2,E_EXPRESSION_IV16); + ae->nop+=6; + } else { + ___output(ae,0xFD);___output(ae,0x21); + PushExpression(ae,ae->idx+2,E_EXPRESSION_IV16); + ae->nop+=4; + } + break; + case CRC_SP: + switch (GetCRC(ae->wl[ae->idx+2].w)) { + case CRC_HL:___output(ae,0xF9);ae->nop+=2;break; + case CRC_IX:___output(ae,0xDD);___output(ae,0xF9);ae->nop+=3;break; + case CRC_IY:___output(ae,0xFD);___output(ae,0xF9);ae->nop+=3;break; + default: + if (StringIsMem(ae->wl[ae->idx+2].w)) { + ___output(ae,0xED);___output(ae,0x7B); + PushExpression(ae,ae->idx+2,E_EXPRESSION_IV16); + ae->nop+=6; + } else { + ___output(ae,0x31); + PushExpression(ae,ae->idx+2,E_EXPRESSION_V16); + ae->nop+=3; + } + } + break; + default: + /* (ix+expression) (iy+expression) (expression) expression */ + if (strncmp(ae->wl[ae->idx+1].w,"(IX",3)==0) { + switch (GetCRC(ae->wl[ae->idx+2].w)) { + case CRC_B:___output(ae,0xDD);___output(ae,0x70);PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);ae->nop+=5;break; + case CRC_C:___output(ae,0xDD);___output(ae,0x71);PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);ae->nop+=5;break; + case CRC_D:___output(ae,0xDD);___output(ae,0x72);PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);ae->nop+=5;break; + case CRC_E:___output(ae,0xDD);___output(ae,0x73);PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);ae->nop+=5;break; + case CRC_H:___output(ae,0xDD);___output(ae,0x74);PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);ae->nop+=5;break; + case CRC_L:___output(ae,0xDD);___output(ae,0x75);PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);ae->nop+=5;break; + case CRC_A:___output(ae,0xDD);___output(ae,0x77);PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);ae->nop+=5;break; + case CRC_HL:___output(ae,0xDD);___output(ae,0x74);PushExpression(ae,ae->idx+1,E_EXPRESSION_IV81);___output(ae,0xDD);___output(ae,0x75);PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);ae->nop+=10;break; + case CRC_DE:___output(ae,0xDD);___output(ae,0x72);PushExpression(ae,ae->idx+1,E_EXPRESSION_IV81);___output(ae,0xDD);___output(ae,0x73);PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);ae->nop+=10;break; + case CRC_BC:___output(ae,0xDD);___output(ae,0x70);PushExpression(ae,ae->idx+1,E_EXPRESSION_IV81);___output(ae,0xDD);___output(ae,0x71);PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);ae->nop+=10;break; + default:___output(ae,0xDD);___output(ae,0x36); + PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8); + PushExpression(ae,ae->idx+2,E_EXPRESSION_3V8); + ae->nop+=6; + } + } else if (strncmp(ae->wl[ae->idx+1].w,"(IY",3)==0) { + switch (GetCRC(ae->wl[ae->idx+2].w)) { + case CRC_B:___output(ae,0xFD);___output(ae,0x70);PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);ae->nop+=5;break; + case CRC_C:___output(ae,0xFD);___output(ae,0x71);PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);ae->nop+=5;break; + case CRC_D:___output(ae,0xFD);___output(ae,0x72);PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);ae->nop+=5;break; + case CRC_E:___output(ae,0xFD);___output(ae,0x73);PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);ae->nop+=5;break; + case CRC_H:___output(ae,0xFD);___output(ae,0x74);PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);ae->nop+=5;break; + case CRC_L:___output(ae,0xFD);___output(ae,0x75);PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);ae->nop+=5;break; + case CRC_A:___output(ae,0xFD);___output(ae,0x77);PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);ae->nop+=5;break; + case CRC_HL:___output(ae,0xFD);___output(ae,0x74);PushExpression(ae,ae->idx+1,E_EXPRESSION_IV81);___output(ae,0xFD);___output(ae,0x75);PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);ae->nop+=10;break; + case CRC_DE:___output(ae,0xFD);___output(ae,0x72);PushExpression(ae,ae->idx+1,E_EXPRESSION_IV81);___output(ae,0xFD);___output(ae,0x73);PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);ae->nop+=10;break; + case CRC_BC:___output(ae,0xFD);___output(ae,0x70);PushExpression(ae,ae->idx+1,E_EXPRESSION_IV81);___output(ae,0xFD);___output(ae,0x71);PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);ae->nop+=10;break; + default:___output(ae,0xFD);___output(ae,0x36); + PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8); + PushExpression(ae,ae->idx+2,E_EXPRESSION_3V8); + ae->nop+=6; + } + } else if (StringIsMem(ae->wl[ae->idx+1].w)) { + switch (GetCRC(ae->wl[ae->idx+2].w)) { + case CRC_A:___output(ae,0x32);PushExpression(ae,ae->idx+1,E_EXPRESSION_V16);ae->nop+=4;break; + case CRC_BC:___output(ae,0xED);___output(ae,0x43);PushExpression(ae,ae->idx+1,E_EXPRESSION_IV16);ae->nop+=6;break; + case CRC_DE:___output(ae,0xED);___output(ae,0x53);PushExpression(ae,ae->idx+1,E_EXPRESSION_IV16);ae->nop+=6;break; + case CRC_HL:___output(ae,0x22);PushExpression(ae,ae->idx+1,E_EXPRESSION_V16);ae->nop+=5;break; + case CRC_IX:___output(ae,0xDD);___output(ae,0x22);PushExpression(ae,ae->idx+1,E_EXPRESSION_IV16);ae->nop+=6;break; + case CRC_IY:___output(ae,0xFD);___output(ae,0x22);PushExpression(ae,ae->idx+1,E_EXPRESSION_IV16);ae->nop+=6;break; + case CRC_SP:___output(ae,0xED);___output(ae,0x73);PushExpression(ae,ae->idx+1,E_EXPRESSION_IV16);ae->nop+=6;break; + default: + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"LD (#nnnn),[A,BC,DE,HL,SP,IX,IY] only\n"); + } + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Unknown LD format\n"); + } + break; + } + ae->idx+=2; + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"LD needs two parameters\n"); + } +} + + +void _RLC(struct s_assenv *ae) { + /* on check qu'il y a un ou deux parametres */ + if (ae->wl[ae->idx+1].t==1) { + switch (GetCRC(ae->wl[ae->idx+1].w)) { + case CRC_B:___output(ae,0xCB);___output(ae,0x0);ae->nop+=2;break; + case CRC_C:___output(ae,0xCB);___output(ae,0x1);ae->nop+=2;break; + case CRC_D:___output(ae,0xCB);___output(ae,0x2);ae->nop+=2;break; + case CRC_E:___output(ae,0xCB);___output(ae,0x3);ae->nop+=2;break; + case CRC_H:___output(ae,0xCB);___output(ae,0x4);ae->nop+=2;break; + case CRC_L:___output(ae,0xCB);___output(ae,0x5);ae->nop+=2;break; + case CRC_MHL:___output(ae,0xCB);___output(ae,0x6);ae->nop+=4;break; + case CRC_A:___output(ae,0xCB);___output(ae,0x7);ae->nop+=2;break; + default: + if (strncmp(ae->wl[ae->idx+1].w,"(IX",3)==0) { + ___output(ae,0xDD);___output(ae,0xCB); + PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8); + ___output(ae,0x6); + ae->nop+=7; + } else if (strncmp(ae->wl[ae->idx+1].w,"(IY",3)==0) { + ___output(ae,0xFD);___output(ae,0xCB); + PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8); + ___output(ae,0x6); + ae->nop+=7; + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is RLC reg8/(HL)/(IX+n)/(IY+n)\n"); + } + } + ae->idx++; + } else if (!ae->wl[ae->idx+1].t && ae->wl[ae->idx+2].t!=2) { + if (strncmp(ae->wl[ae->idx+1].w,"(IX",3)==0) { + ___output(ae,0xDD); + } else if (strncmp(ae->wl[ae->idx+1].w,"(IY",3)==0) { + ___output(ae,0xFD); + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is RLC (IX+n),reg8\n"); + } + ___output(ae,0xCB); + switch (GetCRC(ae->wl[ae->idx+2].w)) { + case CRC_B:PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);___output(ae,0x0);ae->nop+=7;break; + case CRC_C:PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);___output(ae,0x1);ae->nop+=7;break; + case CRC_D:PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);___output(ae,0x2);ae->nop+=7;break; + case CRC_E:PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);___output(ae,0x3);ae->nop+=7;break; + case CRC_H:PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);___output(ae,0x4);ae->nop+=7;break; + case CRC_L:PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);___output(ae,0x5);ae->nop+=7;break; + case CRC_A:PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);___output(ae,0x7);ae->nop+=7;break; + default: + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is RLC (IX+n),reg8\n"); + } + ae->idx++; + ae->idx++; + } +} + +void _RRC(struct s_assenv *ae) { + /* on check qu'il y a un ou deux parametres */ + if (ae->wl[ae->idx+1].t==1) { + switch (GetCRC(ae->wl[ae->idx+1].w)) { + case CRC_B:___output(ae,0xCB);___output(ae,0x8);ae->nop+=2;break; + case CRC_C:___output(ae,0xCB);___output(ae,0x9);ae->nop+=2;break; + case CRC_D:___output(ae,0xCB);___output(ae,0xA);ae->nop+=2;break; + case CRC_E:___output(ae,0xCB);___output(ae,0xB);ae->nop+=2;break; + case CRC_H:___output(ae,0xCB);___output(ae,0xC);ae->nop+=2;break; + case CRC_L:___output(ae,0xCB);___output(ae,0xD);ae->nop+=2;break; + case CRC_MHL:___output(ae,0xCB);___output(ae,0xE);ae->nop+=4;break; + case CRC_A:___output(ae,0xCB);___output(ae,0xF);ae->nop+=2;break; + default: + if (strncmp(ae->wl[ae->idx+1].w,"(IX",3)==0) { + ___output(ae,0xDD);___output(ae,0xCB); + PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8); + ___output(ae,0xE); + ae->nop+=7; + } else if (strncmp(ae->wl[ae->idx+1].w,"(IY",3)==0) { + ___output(ae,0xFD);___output(ae,0xCB); + PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8); + ___output(ae,0xE); + ae->nop+=7; + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is RRC reg8/(HL)/(IX+n)/(IY+n)\n"); + } + } + ae->idx++; + } else if (!ae->wl[ae->idx+1].t && ae->wl[ae->idx+2].t!=2) { + if (strncmp(ae->wl[ae->idx+1].w,"(IX",3)==0) { + ___output(ae,0xDD); + } else if (strncmp(ae->wl[ae->idx+1].w,"(IY",3)==0) { + ___output(ae,0xFD); + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is RRC (IX+n),reg8\n"); + } + ___output(ae,0xCB); + switch (GetCRC(ae->wl[ae->idx+2].w)) { + case CRC_B:PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);___output(ae,0x8);ae->nop+=7;break; + case CRC_C:PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);___output(ae,0x9);ae->nop+=7;break; + case CRC_D:PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);___output(ae,0xA);ae->nop+=7;break; + case CRC_E:PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);___output(ae,0xB);ae->nop+=7;break; + case CRC_H:PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);___output(ae,0xC);ae->nop+=7;break; + case CRC_L:PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);___output(ae,0xD);ae->nop+=7;break; + case CRC_A:PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);___output(ae,0xF);ae->nop+=7;break; + default: + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is RRC (IX+n),reg8\n"); + } + ae->idx++; + ae->idx++; + } +} + + +void _RL(struct s_assenv *ae) { + /* on check qu'il y a un ou deux parametres */ + if (ae->wl[ae->idx+1].t==1) { + switch (GetCRC(ae->wl[ae->idx+1].w)) { + case CRC_BC:___output(ae,0xCB);___output(ae,0x10);___output(ae,0xCB);___output(ae,0x11);ae->nop+=4;break; + case CRC_B:___output(ae,0xCB);___output(ae,0x10);ae->nop+=2;break; + case CRC_C:___output(ae,0xCB);___output(ae,0x11);ae->nop+=2;break; + case CRC_DE:___output(ae,0xCB);___output(ae,0x12);___output(ae,0xCB);___output(ae,0x13);ae->nop+=4;break; + case CRC_D:___output(ae,0xCB);___output(ae,0x12);ae->nop+=2;break; + case CRC_E:___output(ae,0xCB);___output(ae,0x13);ae->nop+=2;break; + case CRC_HL:___output(ae,0xCB);___output(ae,0x14);___output(ae,0xCB);___output(ae,0x15);ae->nop+=4;break; + case CRC_H:___output(ae,0xCB);___output(ae,0x14);ae->nop+=2;break; + case CRC_L:___output(ae,0xCB);___output(ae,0x15);ae->nop+=2;break; + case CRC_MHL:___output(ae,0xCB);___output(ae,0x16);ae->nop+=4;break; + case CRC_A:___output(ae,0xCB);___output(ae,0x17);ae->nop+=2;break; + default: + if (strncmp(ae->wl[ae->idx+1].w,"(IX",3)==0) { + ___output(ae,0xDD);___output(ae,0xCB); + PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8); + ___output(ae,0x16); + ae->nop+=7; + } else if (strncmp(ae->wl[ae->idx+1].w,"(IY",3)==0) { + ___output(ae,0xFD);___output(ae,0xCB); + PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8); + ___output(ae,0x16); + ae->nop+=7; + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is RL reg8/(HL)/(IX+n)/(IY+n)\n"); + } + } + ae->idx++; + } else if (!ae->wl[ae->idx+1].t && ae->wl[ae->idx+2].t!=2) { + if (strncmp(ae->wl[ae->idx+1].w,"(IX",3)==0) { + ___output(ae,0xDD); + } else if (strncmp(ae->wl[ae->idx+1].w,"(IY",3)==0) { + ___output(ae,0xFD); + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is RL (IX+n),reg8\n"); + } + ___output(ae,0xCB); + switch (GetCRC(ae->wl[ae->idx+2].w)) { + case CRC_B:PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);___output(ae,0x10);ae->nop+=7;break; + case CRC_C:PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);___output(ae,0x11);ae->nop+=7;break; + case CRC_D:PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);___output(ae,0x12);ae->nop+=7;break; + case CRC_E:PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);___output(ae,0x13);ae->nop+=7;break; + case CRC_H:PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);___output(ae,0x14);ae->nop+=7;break; + case CRC_L:PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);___output(ae,0x15);ae->nop+=7;break; + case CRC_A:PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);___output(ae,0x17);ae->nop+=7;break; + default: + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is RL (IX+n),reg8\n"); + } + ae->idx++; + ae->idx++; + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is RL (IX+n),reg8 or RL reg8/(HL)/(IX+n)/(IY+n)\n"); + } +} + +void _RR(struct s_assenv *ae) { + /* on check qu'il y a un ou deux parametres */ + if (ae->wl[ae->idx+1].t==1) { + switch (GetCRC(ae->wl[ae->idx+1].w)) { + case CRC_BC:___output(ae,0xCB);___output(ae,0x18);___output(ae,0xCB);___output(ae,0x19);ae->nop+=4;break; + case CRC_B:___output(ae,0xCB);___output(ae,0x18);ae->nop+=2;break; + case CRC_C:___output(ae,0xCB);___output(ae,0x19);ae->nop+=2;break; + case CRC_DE:___output(ae,0xCB);___output(ae,0x1A);___output(ae,0xCB);___output(ae,0x1B);ae->nop+=4;break; + case CRC_D:___output(ae,0xCB);___output(ae,0x1A);ae->nop+=2;break; + case CRC_E:___output(ae,0xCB);___output(ae,0x1B);ae->nop+=2;break; + case CRC_HL:___output(ae,0xCB);___output(ae,0x1C);___output(ae,0xCB);___output(ae,0x1D);ae->nop+=4;break; + case CRC_H:___output(ae,0xCB);___output(ae,0x1C);ae->nop+=2;break; + case CRC_L:___output(ae,0xCB);___output(ae,0x1D);ae->nop+=2;break; + case CRC_MHL:___output(ae,0xCB);___output(ae,0x1E);ae->nop+=4;break; + case CRC_A:___output(ae,0xCB);___output(ae,0x1F);ae->nop+=2;break; + default: + if (strncmp(ae->wl[ae->idx+1].w,"(IX",3)==0) { + ___output(ae,0xDD);___output(ae,0xCB); + PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8); + ___output(ae,0x1E); + ae->nop+=7; + } else if (strncmp(ae->wl[ae->idx+1].w,"(IY",3)==0) { + ___output(ae,0xFD);___output(ae,0xCB); + PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8); + ___output(ae,0x1E); + ae->nop+=7; + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is RR reg8/(HL)/(IX+n)/(IY+n)\n"); + } + } + ae->idx++; + } else if (!ae->wl[ae->idx+1].t && ae->wl[ae->idx+2].t!=2) { + if (strncmp(ae->wl[ae->idx+1].w,"(IX",3)==0) { + ___output(ae,0xDD); + } else if (strncmp(ae->wl[ae->idx+1].w,"(IY",3)==0) { + ___output(ae,0xFD); + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is RR (IX+n),reg8\n"); + } + ___output(ae,0xCB); + switch (GetCRC(ae->wl[ae->idx+2].w)) { + case CRC_B:PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);___output(ae,0x18);ae->nop+=7;break; + case CRC_C:PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);___output(ae,0x19);ae->nop+=7;break; + case CRC_D:PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);___output(ae,0x1A);ae->nop+=7;break; + case CRC_E:PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);___output(ae,0x1B);ae->nop+=7;break; + case CRC_H:PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);___output(ae,0x1C);ae->nop+=7;break; + case CRC_L:PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);___output(ae,0x1D);ae->nop+=7;break; + case CRC_A:PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);___output(ae,0x1F);ae->nop+=7;break; + default: + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is RR (IX+n),reg8\n"); + } + ae->idx++; + ae->idx++; + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is RR (IX+n),reg8 or RR reg8/(HL)/(IX+n)/(IY+n)\n"); + } +} + + + + + +void _SLA(struct s_assenv *ae) { + /* on check qu'il y a un ou deux parametres */ + if (ae->wl[ae->idx+1].t==1) { + switch (GetCRC(ae->wl[ae->idx+1].w)) { + case CRC_BC:___output(ae,0xCB);___output(ae,0x21);___output(ae,0xCB);___output(ae,0x10);ae->nop+=4;break; /* SLA C : RL B */ + case CRC_B:___output(ae,0xCB);___output(ae,0x20);ae->nop+=2;break; + case CRC_C:___output(ae,0xCB);___output(ae,0x21);ae->nop+=2;break; + case CRC_DE:___output(ae,0xCB);___output(ae,0x23);___output(ae,0xCB);___output(ae,0x12);ae->nop+=4;break; /* SLA E : RL D */ + case CRC_D:___output(ae,0xCB);___output(ae,0x22);ae->nop+=2;break; + case CRC_E:___output(ae,0xCB);___output(ae,0x23);ae->nop+=2;break; + case CRC_HL:___output(ae,0xCB);___output(ae,0x25);___output(ae,0xCB);___output(ae,0x14);ae->nop+=4;break; /* SLA L : RL H */ + case CRC_H:___output(ae,0xCB);___output(ae,0x24);ae->nop+=2;break; + case CRC_L:___output(ae,0xCB);___output(ae,0x25);ae->nop+=2;break; + case CRC_MHL:___output(ae,0xCB);___output(ae,0x26);ae->nop+=4;break; + case CRC_A:___output(ae,0xCB);___output(ae,0x27);ae->nop+=2;break; + default: + if (strncmp(ae->wl[ae->idx+1].w,"(IX",3)==0) { + ___output(ae,0xDD);___output(ae,0xCB); + PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8); + ___output(ae,0x26); + ae->nop+=7; + } else if (strncmp(ae->wl[ae->idx+1].w,"(IY",3)==0) { + ___output(ae,0xFD);___output(ae,0xCB); + PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8); + ___output(ae,0x26); + ae->nop+=7; + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is SLA reg8/(HL)/(IX+n)/(IY+n)\n"); + } + } + ae->idx++; + } else if (!ae->wl[ae->idx+1].t && ae->wl[ae->idx+2].t!=2) { + if (strncmp(ae->wl[ae->idx+1].w,"(IX",3)==0) { + ___output(ae,0xDD); + } else if (strncmp(ae->wl[ae->idx+1].w,"(IY",3)==0) { + ___output(ae,0xFD); + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is SLL (IX+n),reg8\n"); + } + ___output(ae,0xCB); + switch (GetCRC(ae->wl[ae->idx+2].w)) { + case CRC_B:PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);___output(ae,0x20);ae->nop+=7;break; + case CRC_C:PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);___output(ae,0x21);ae->nop+=7;break; + case CRC_D:PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);___output(ae,0x22);ae->nop+=7;break; + case CRC_E:PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);___output(ae,0x23);ae->nop+=7;break; + case CRC_H:PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);___output(ae,0x24);ae->nop+=7;break; + case CRC_L:PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);___output(ae,0x25);ae->nop+=7;break; + case CRC_A:PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);___output(ae,0x27);ae->nop+=7;break; + default: + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is SLA (IX+n),reg8\n"); + } + ae->idx++; + ae->idx++; + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is SLA reg8/(HL)/(IX+n)/(IY+n) or SLA (IX+n),reg8\n"); + } +} + +void _SRA(struct s_assenv *ae) { + /* on check qu'il y a un ou deux parametres */ + if (ae->wl[ae->idx+1].t==1) { + switch (GetCRC(ae->wl[ae->idx+1].w)) { + case CRC_BC:___output(ae,0xCB);___output(ae,0x28);___output(ae,0xCB);___output(ae,0x19);ae->nop+=4;break; /* SRA B : RR C */ + case CRC_B:___output(ae,0xCB);___output(ae,0x28);ae->nop+=2;break; + case CRC_C:___output(ae,0xCB);___output(ae,0x29);ae->nop+=2;break; + case CRC_DE:___output(ae,0xCB);___output(ae,0x2A);___output(ae,0xCB);___output(ae,0x1B);ae->nop+=4;break; /* SRA D : RR E */ + case CRC_D:___output(ae,0xCB);___output(ae,0x2A);ae->nop+=2;break; + case CRC_E:___output(ae,0xCB);___output(ae,0x2B);ae->nop+=2;break; + case CRC_HL:___output(ae,0xCB);___output(ae,0x2C);___output(ae,0xCB);___output(ae,0x1D);ae->nop+=4;break; /* SRA H : RR L */ + case CRC_H:___output(ae,0xCB);___output(ae,0x2C);ae->nop+=2;break; + case CRC_L:___output(ae,0xCB);___output(ae,0x2D);ae->nop+=2;break; + case CRC_MHL:___output(ae,0xCB);___output(ae,0x2E);ae->nop+=4;break; + case CRC_A:___output(ae,0xCB);___output(ae,0x2F);ae->nop+=2;break; + default: + if (strncmp(ae->wl[ae->idx+1].w,"(IX",3)==0) { + ___output(ae,0xDD);___output(ae,0xCB); + PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8); + ___output(ae,0x2E); + ae->nop+=7; + } else if (strncmp(ae->wl[ae->idx+1].w,"(IY",3)==0) { + ___output(ae,0xFD);___output(ae,0xCB); + PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8); + ___output(ae,0x2E); + ae->nop+=7; + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is SRA reg8/(HL)/(IX+n)/(IY+n)\n"); + } + } + ae->idx++; + } else if (!ae->wl[ae->idx+1].t && ae->wl[ae->idx+2].t!=2) { + if (strncmp(ae->wl[ae->idx+1].w,"(IX",3)==0) { + ___output(ae,0xDD); + } else if (strncmp(ae->wl[ae->idx+1].w,"(IY",3)==0) { + ___output(ae,0xFD); + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is SRA (IX+n),reg8\n"); + } + ___output(ae,0xCB); + switch (GetCRC(ae->wl[ae->idx+2].w)) { + case CRC_B:PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);___output(ae,0x28);ae->nop+=7;break; + case CRC_C:PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);___output(ae,0x29);ae->nop+=7;break; + case CRC_D:PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);___output(ae,0x2A);ae->nop+=7;break; + case CRC_E:PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);___output(ae,0x2B);ae->nop+=7;break; + case CRC_H:PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);___output(ae,0x2C);ae->nop+=7;break; + case CRC_L:PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);___output(ae,0x2D);ae->nop+=7;break; + case CRC_A:PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);___output(ae,0x2F);ae->nop+=7;break; + default: + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is SRA (IX+n),reg8\n"); + } + ae->idx++; + ae->idx++; + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is SRA reg8/(HL)/(IX+n)/(IY+n) or SRA (IX+n),reg8\n"); + } +} + + +void _SLL(struct s_assenv *ae) { + /* on check qu'il y a un ou deux parametres */ + if (ae->wl[ae->idx+1].t==1) { + switch (GetCRC(ae->wl[ae->idx+1].w)) { + case CRC_BC:___output(ae,0xCB);___output(ae,0x31);___output(ae,0xCB);___output(ae,0x10);ae->nop+=4;break; /* SLL C : RL B */ + case CRC_B:___output(ae,0xCB);___output(ae,0x30);ae->nop+=2;break; + case CRC_C:___output(ae,0xCB);___output(ae,0x31);ae->nop+=2;break; + case CRC_DE:___output(ae,0xCB);___output(ae,0x33);___output(ae,0xCB);___output(ae,0x12);ae->nop+=4;break; /* SLL E : RL D */ + case CRC_D:___output(ae,0xCB);___output(ae,0x32);ae->nop+=2;break; + case CRC_E:___output(ae,0xCB);___output(ae,0x33);ae->nop+=2;break; + case CRC_HL:___output(ae,0xCB);___output(ae,0x35);___output(ae,0xCB);___output(ae,0x14);ae->nop+=4;break; /* SLL L : RL H */ + case CRC_H:___output(ae,0xCB);___output(ae,0x34);ae->nop+=2;break; + case CRC_L:___output(ae,0xCB);___output(ae,0x35);ae->nop+=2;break; + case CRC_MHL:___output(ae,0xCB);___output(ae,0x36);ae->nop+=4;break; + case CRC_A:___output(ae,0xCB);___output(ae,0x37);ae->nop+=2;break; + default: + if (strncmp(ae->wl[ae->idx+1].w,"(IX",3)==0) { + ___output(ae,0xDD);___output(ae,0xCB); + PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8); + ___output(ae,0x36); + ae->nop+=7; + } else if (strncmp(ae->wl[ae->idx+1].w,"(IY",3)==0) { + ___output(ae,0xFD);___output(ae,0xCB); + PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8); + ___output(ae,0x36); + ae->nop+=7; + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is SLL reg8/(HL)/(IX+n)/(IY+n)\n"); + } + } + ae->idx++; + } else if (!ae->wl[ae->idx+1].t && ae->wl[ae->idx+2].t!=2) { + if (strncmp(ae->wl[ae->idx+1].w,"(IX",3)==0) { + ___output(ae,0xDD); + } else if (strncmp(ae->wl[ae->idx+1].w,"(IY",3)==0) { + ___output(ae,0xFD); + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is SLL (IX+n),reg8\n"); + } + ___output(ae,0xCB); + switch (GetCRC(ae->wl[ae->idx+2].w)) { + case CRC_B:PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);___output(ae,0x30);ae->nop+=7;break; + case CRC_C:PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);___output(ae,0x31);ae->nop+=7;break; + case CRC_D:PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);___output(ae,0x32);ae->nop+=7;break; + case CRC_E:PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);___output(ae,0x33);ae->nop+=7;break; + case CRC_H:PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);___output(ae,0x34);ae->nop+=7;break; + case CRC_L:PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);___output(ae,0x35);ae->nop+=7;break; + case CRC_A:PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);___output(ae,0x37);ae->nop+=7;break; + default: + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is SLL (IX+n),reg8\n"); + } + ae->idx++; + ae->idx++; + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is SLL reg8/(HL)/(IX+n)/(IY+n) or SLL (IX+n),reg8\n"); + } +} + +void _SRL(struct s_assenv *ae) { + /* on check qu'il y a un ou deux parametres */ + if (ae->wl[ae->idx+1].t==1) { + switch (GetCRC(ae->wl[ae->idx+1].w)) { + case CRC_BC:___output(ae,0xCB);___output(ae,0x38);___output(ae,0xCB);___output(ae,0x11);ae->nop+=4;break; /* SRL B : RL C */ + case CRC_B:___output(ae,0xCB);___output(ae,0x38);ae->nop+=2;break; + case CRC_C:___output(ae,0xCB);___output(ae,0x39);ae->nop+=2;break; + case CRC_DE:___output(ae,0xCB);___output(ae,0x3A);___output(ae,0xCB);___output(ae,0x13);ae->nop+=4;break; /* SRL D : RL E */ + case CRC_D:___output(ae,0xCB);___output(ae,0x3A);ae->nop+=2;break; + case CRC_E:___output(ae,0xCB);___output(ae,0x3B);ae->nop+=2;break; + case CRC_HL:___output(ae,0xCB);___output(ae,0x3C);___output(ae,0xCB);___output(ae,0x15);ae->nop+=4;break; /* SRL H : RL L */ + case CRC_H:___output(ae,0xCB);___output(ae,0x3C);ae->nop+=2;break; + case CRC_L:___output(ae,0xCB);___output(ae,0x3D);ae->nop+=2;break; + case CRC_MHL:___output(ae,0xCB);___output(ae,0x3E);ae->nop+=4;break; + case CRC_A:___output(ae,0xCB);___output(ae,0x3F);ae->nop+=2;break; + default: + if (strncmp(ae->wl[ae->idx+1].w,"(IX",3)==0) { + ___output(ae,0xDD);___output(ae,0xCB); + PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8); + ___output(ae,0x3E); + ae->nop+=7; + } else if (strncmp(ae->wl[ae->idx+1].w,"(IY",3)==0) { + ___output(ae,0xFD);___output(ae,0xCB); + PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8); + ___output(ae,0x3E); + ae->nop+=7; + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is SRL reg8/(HL)/(IX+n)/(IY+n)\n"); + } + } + ae->idx++; + } else if (!ae->wl[ae->idx+1].t && ae->wl[ae->idx+2].t!=2) { + if (strncmp(ae->wl[ae->idx+1].w,"(IX",3)==0) { + ___output(ae,0xDD); + } else if (strncmp(ae->wl[ae->idx+1].w,"(IY",3)==0) { + ___output(ae,0xFD); + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is SRL (IX+n),reg8\n"); + } + ___output(ae,0xCB); + switch (GetCRC(ae->wl[ae->idx+2].w)) { + case CRC_B:PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);___output(ae,0x38);ae->nop+=7;break; + case CRC_C:PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);___output(ae,0x39);ae->nop+=7;break; + case CRC_D:PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);___output(ae,0x3A);ae->nop+=7;break; + case CRC_E:PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);___output(ae,0x3B);ae->nop+=7;break; + case CRC_H:PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);___output(ae,0x3C);ae->nop+=7;break; + case CRC_L:PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);___output(ae,0x3D);ae->nop+=7;break; + case CRC_A:PushExpression(ae,ae->idx+1,E_EXPRESSION_IV8);___output(ae,0x3F);ae->nop+=7;break; + default: + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is SRL (IX+n),reg8\n"); + } + ae->idx++; + ae->idx++; + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is SRL reg8/(HL)/(IX+n)/(IY+n) or SRL (IX+n),reg8\n"); + } +} + + +void _BIT(struct s_assenv *ae) { + int o; + /* on check qu'il y a deux ou trois parametres */ + ExpressionFastTranslate(ae,&ae->wl[ae->idx+1].w,0); + o=RoundComputeExpressionCore(ae,ae->wl[ae->idx+1].w,ae->codeadr,0); + if (o<0 || o>7) { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is BIT <value from 0 to 7>,... (%d)\n",o); + } else { + o=0x40+o*8; + if (ae->wl[ae->idx+1].t==0 && ae->wl[ae->idx+2].t==1) { + switch (GetCRC(ae->wl[ae->idx+2].w)) { + case CRC_B:___output(ae,0xCB);___output(ae,0x0+o);ae->nop+=2;break; + case CRC_C:___output(ae,0xCB);___output(ae,0x1+o);ae->nop+=2;break; + case CRC_D:___output(ae,0xCB);___output(ae,0x2+o);ae->nop+=2;break; + case CRC_E:___output(ae,0xCB);___output(ae,0x3+o);ae->nop+=2;break; + case CRC_H:___output(ae,0xCB);___output(ae,0x4+o);ae->nop+=2;break; + case CRC_L:___output(ae,0xCB);___output(ae,0x5+o);ae->nop+=2;break; + case CRC_MHL:___output(ae,0xCB);___output(ae,0x6+o);ae->nop+=3;break; + case CRC_A:___output(ae,0xCB);___output(ae,0x7+o);ae->nop+=2;break; + default: + if (strncmp(ae->wl[ae->idx+2].w,"(IX",3)==0) { + ___output(ae,0xDD);___output(ae,0xCB); + PushExpression(ae,ae->idx+2,E_EXPRESSION_IV8); + ___output(ae,0x6+o); + ae->nop+=6; + } else if (strncmp(ae->wl[ae->idx+2].w,"(IY",3)==0) { + ___output(ae,0xFD);___output(ae,0xCB); + PushExpression(ae,ae->idx+2,E_EXPRESSION_IV8); + ___output(ae,0x6+o); + ae->nop+=6; + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is BIT n,reg8/(HL)/(IX+n)/(IY+n)\n"); + } + } + ae->idx+=2; + } else if (!ae->wl[ae->idx+1].t && !ae->wl[ae->idx+2].t && ae->wl[ae->idx+3].t==1) { + if (strncmp(ae->wl[ae->idx+2].w,"(IX",3)==0) { + ___output(ae,0xDD); + } else if (strncmp(ae->wl[ae->idx+2].w,"(IY",3)==0) { + ___output(ae,0xFD); + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is BIT (IX+n),reg8\n"); + } + ___output(ae,0xCB); + switch (GetCRC(ae->wl[ae->idx+3].w)) { + case CRC_B:PushExpression(ae,ae->idx+2,E_EXPRESSION_IV8);___output(ae,0x0+o);ae->nop+=6;break; + case CRC_C:PushExpression(ae,ae->idx+2,E_EXPRESSION_IV8);___output(ae,0x1+o);ae->nop+=6;break; + case CRC_D:PushExpression(ae,ae->idx+2,E_EXPRESSION_IV8);___output(ae,0x2+o);ae->nop+=6;break; + case CRC_E:PushExpression(ae,ae->idx+2,E_EXPRESSION_IV8);___output(ae,0x3+o);ae->nop+=6;break; + case CRC_H:PushExpression(ae,ae->idx+2,E_EXPRESSION_IV8);___output(ae,0x4+o);ae->nop+=6;break; + case CRC_L:PushExpression(ae,ae->idx+2,E_EXPRESSION_IV8);___output(ae,0x5+o);ae->nop+=6;break; + case CRC_A:PushExpression(ae,ae->idx+2,E_EXPRESSION_IV8);___output(ae,0x7+o);ae->nop+=6;break; + default: + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is BIT n,(IX+n),reg8\n"); + } + ae->idx+=3; + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is BIT n,reg8/(HL)/(IX+n)[,reg8]/(IY+n)[,reg8]\n"); + } + } +} + +void _RES(struct s_assenv *ae) { + int o; + /* on check qu'il y a deux ou trois parametres */ + ExpressionFastTranslate(ae,&ae->wl[ae->idx+1].w,0); + o=RoundComputeExpressionCore(ae,ae->wl[ae->idx+1].w,ae->codeadr,0); + if (o<0 || o>7) { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is RES <value from 0 to 7>,... (%d)\n",o); + } else { + o=0x80+o*8; + if (ae->wl[ae->idx+1].t==0 && ae->wl[ae->idx+2].t==1) { + switch (GetCRC(ae->wl[ae->idx+2].w)) { + case CRC_B:___output(ae,0xCB);___output(ae,0x0+o);ae->nop+=2;break; + case CRC_C:___output(ae,0xCB);___output(ae,0x1+o);ae->nop+=2;break; + case CRC_D:___output(ae,0xCB);___output(ae,0x2+o);ae->nop+=2;break; + case CRC_E:___output(ae,0xCB);___output(ae,0x3+o);ae->nop+=2;break; + case CRC_H:___output(ae,0xCB);___output(ae,0x4+o);ae->nop+=2;break; + case CRC_L:___output(ae,0xCB);___output(ae,0x5+o);ae->nop+=2;break; + case CRC_MHL:___output(ae,0xCB);___output(ae,0x6+o);ae->nop+=4;break; + case CRC_A:___output(ae,0xCB);___output(ae,0x7+o);ae->nop+=2;break; + default: + if (strncmp(ae->wl[ae->idx+2].w,"(IX",3)==0) { + ___output(ae,0xDD);___output(ae,0xCB); + PushExpression(ae,ae->idx+2,E_EXPRESSION_IV8); + ___output(ae,0x6+o); + ae->nop+=7; + } else if (strncmp(ae->wl[ae->idx+2].w,"(IY",3)==0) { + ___output(ae,0xFD);___output(ae,0xCB); + PushExpression(ae,ae->idx+2,E_EXPRESSION_IV8); + ___output(ae,0x6+o); + ae->nop+=7; + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is RES n,reg8/(HL)/(IX+n)/(IY+n)\n"); + } + } + ae->idx+=2; + } else if (!ae->wl[ae->idx+1].t && !ae->wl[ae->idx+2].t && ae->wl[ae->idx+3].t==1) { + if (strncmp(ae->wl[ae->idx+2].w,"(IX",3)==0) { + ___output(ae,0xDD); + } else if (strncmp(ae->wl[ae->idx+2].w,"(IY",3)==0) { + ___output(ae,0xFD); + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is RES n,(IX+n),reg8\n"); + } + ___output(ae,0xCB); + switch (GetCRC(ae->wl[ae->idx+3].w)) { + case CRC_B:PushExpression(ae,ae->idx+2,E_EXPRESSION_IV8);___output(ae,0x0+o);ae->nop+=7;break; + case CRC_C:PushExpression(ae,ae->idx+2,E_EXPRESSION_IV8);___output(ae,0x1+o);ae->nop+=7;break; + case CRC_D:PushExpression(ae,ae->idx+2,E_EXPRESSION_IV8);___output(ae,0x2+o);ae->nop+=7;break; + case CRC_E:PushExpression(ae,ae->idx+2,E_EXPRESSION_IV8);___output(ae,0x3+o);ae->nop+=7;break; + case CRC_H:PushExpression(ae,ae->idx+2,E_EXPRESSION_IV8);___output(ae,0x4+o);ae->nop+=7;break; + case CRC_L:PushExpression(ae,ae->idx+2,E_EXPRESSION_IV8);___output(ae,0x5+o);ae->nop+=7;break; + case CRC_A:PushExpression(ae,ae->idx+2,E_EXPRESSION_IV8);___output(ae,0x7+o);ae->nop+=7;break; + default: + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is RES n,(IX+n),reg8\n"); + } + ae->idx+=3; + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is RES n,reg8/(HL)/(IX+n)[,reg8]/(IY+n)[,reg8]\n"); + } + } +} + +void _SET(struct s_assenv *ae) { + int o; + /* on check qu'il y a deux ou trois parametres */ + ExpressionFastTranslate(ae,&ae->wl[ae->idx+1].w,0); + o=RoundComputeExpressionCore(ae,ae->wl[ae->idx+1].w,ae->codeadr,0); + if (o<0 || o>7) { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is SET <value from 0 to 7>,... (%d)\n",o); + } else { + o=0xC0+o*8; + if (ae->wl[ae->idx+1].t==0 && ae->wl[ae->idx+2].t==1) { + switch (GetCRC(ae->wl[ae->idx+2].w)) { + case CRC_B:___output(ae,0xCB);___output(ae,0x0+o);ae->nop+=2;break; + case CRC_C:___output(ae,0xCB);___output(ae,0x1+o);ae->nop+=2;break; + case CRC_D:___output(ae,0xCB);___output(ae,0x2+o);ae->nop+=2;break; + case CRC_E:___output(ae,0xCB);___output(ae,0x3+o);ae->nop+=2;break; + case CRC_H:___output(ae,0xCB);___output(ae,0x4+o);ae->nop+=2;break; + case CRC_L:___output(ae,0xCB);___output(ae,0x5+o);ae->nop+=2;break; + case CRC_MHL:___output(ae,0xCB);___output(ae,0x6+o);ae->nop+=4;break; + case CRC_A:___output(ae,0xCB);___output(ae,0x7+o);ae->nop+=2;break; + default: + if (strncmp(ae->wl[ae->idx+2].w,"(IX",3)==0) { + ___output(ae,0xDD);___output(ae,0xCB); + PushExpression(ae,ae->idx+2,E_EXPRESSION_IV8); + ___output(ae,0x6+o); + ae->nop+=7; + } else if (strncmp(ae->wl[ae->idx+2].w,"(IY",3)==0) { + ___output(ae,0xFD);___output(ae,0xCB); + PushExpression(ae,ae->idx+2,E_EXPRESSION_IV8); + ___output(ae,0x6+o); + ae->nop+=7; + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is SET n,reg8/(HL)/(IX+n)/(IY+n)\n"); + } + } + ae->idx+=2; + } else if (!ae->wl[ae->idx+1].t && !ae->wl[ae->idx+2].t && ae->wl[ae->idx+3].t==1) { + if (strncmp(ae->wl[ae->idx+2].w,"(IX",3)==0) { + ___output(ae,0xDD); + } else if (strncmp(ae->wl[ae->idx+2].w,"(IY",3)==0) { + ___output(ae,0xFD); + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is SET n,(IX+n),reg8\n"); + } + ___output(ae,0xCB); + switch (GetCRC(ae->wl[ae->idx+3].w)) { + case CRC_B:PushExpression(ae,ae->idx+2,E_EXPRESSION_IV8);___output(ae,0x0+o);ae->nop+=7;break; + case CRC_C:PushExpression(ae,ae->idx+2,E_EXPRESSION_IV8);___output(ae,0x1+o);ae->nop+=7;break; + case CRC_D:PushExpression(ae,ae->idx+2,E_EXPRESSION_IV8);___output(ae,0x2+o);ae->nop+=7;break; + case CRC_E:PushExpression(ae,ae->idx+2,E_EXPRESSION_IV8);___output(ae,0x3+o);ae->nop+=7;break; + case CRC_H:PushExpression(ae,ae->idx+2,E_EXPRESSION_IV8);___output(ae,0x4+o);ae->nop+=7;break; + case CRC_L:PushExpression(ae,ae->idx+2,E_EXPRESSION_IV8);___output(ae,0x5+o);ae->nop+=7;break; + case CRC_A:PushExpression(ae,ae->idx+2,E_EXPRESSION_IV8);___output(ae,0x7+o);ae->nop+=7;break; + default: + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is SET n,(IX+n),reg8\n"); + } + ae->idx+=3; + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is SET n,reg8/(HL)/(IX+n)[,reg8]/(IY+n)[,reg8]\n"); + } + } +} + +void _DEFS(struct s_assenv *ae) { + int i,r,v; + if (ae->wl[ae->idx].t) { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Syntax is DEFS repeat,value or DEFS repeat\n"); + } else do { + ae->idx++; + if (!ae->wl[ae->idx].t) { + ExpressionFastTranslate(ae,&ae->wl[ae->idx].w,0); + ExpressionFastTranslate(ae,&ae->wl[ae->idx+1].w,0); /* doing FastTranslate but not a complete evaluation */ + r=RoundComputeExpressionCore(ae,ae->wl[ae->idx].w,ae->codeadr,0); + if (r<0) { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"DEFS size must be greater or equal to zero\n"); + } + for (i=0;i<r;i++) { + /* keep flexibility */ + PushExpression(ae,ae->idx+1,E_EXPRESSION_0V8); + ae->nop+=1; + } + ae->idx++; + } else if (ae->wl[ae->idx].t==1) { + ExpressionFastTranslate(ae,&ae->wl[ae->idx].w,0); + r=RoundComputeExpressionCore(ae,ae->wl[ae->idx].w,ae->codeadr,0); + v=0; + if (r<0) { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"DEFS size must be greater or equal to zero\n"); + } + for (i=0;i<r;i++) { + ___output(ae,v); + ae->nop+=1; + } + } + } while (!ae->wl[ae->idx].t); +} + +void _DEFS_struct(struct s_assenv *ae) { + int i,r,v; + if (ae->wl[ae->idx].t) { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Syntax is DEFS repeat,value or DEFS repeat\n"); + } else do { + ae->idx++; + if (!ae->wl[ae->idx].t) { + ExpressionFastTranslate(ae,&ae->wl[ae->idx].w,0); + ExpressionFastTranslate(ae,&ae->wl[ae->idx+1].w,0); + r=RoundComputeExpressionCore(ae,ae->wl[ae->idx].w,ae->codeadr,0); + v=RoundComputeExpressionCore(ae,ae->wl[ae->idx+1].w,ae->codeadr,0); + if (r<0) { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"DEFS size must be greater or equal to zero\n"); + } + for (i=0;i<r;i++) { + ___output(ae,v); + ae->nop+=1; + } + ae->idx++; + } else if (ae->wl[ae->idx].t==1) { + ExpressionFastTranslate(ae,&ae->wl[ae->idx].w,0); + r=RoundComputeExpressionCore(ae,ae->wl[ae->idx].w,ae->codeadr,0); + v=0; + if (r<0) { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"DEFS size must be greater or equal to zero\n"); + } + for (i=0;i<r;i++) { + ___output(ae,v); + ae->nop+=1; + } + } + } while (!ae->wl[ae->idx].t); +} + +void _STR(struct s_assenv *ae) { + unsigned char c; + int i,tquote; + + if (!ae->wl[ae->idx].t) { + do { + ae->idx++; + if ((tquote=StringIsQuote(ae->wl[ae->idx].w))!=0) { + i=1; + while (ae->wl[ae->idx].w[i] && ae->wl[ae->idx].w[i]!=tquote) { + if (ae->wl[ae->idx].w[i]=='\\') { + i++; + /* no conversion on escaped chars */ + c=ae->wl[ae->idx].w[i]; + switch (c) { + case 'b':c='\b';break; + case 'v':c='\v';break; + case 'f':c='\f';break; + case '0':c='\0';break; + case 'r':c='\r';break; + case 'n':c='\n';break; + case 't':c='\t';break; + default:break; + } + if (ae->wl[ae->idx].w[i+1]!=tquote) { + ___output(ae,c); + } else { + ___output(ae,c|0x80); + } + } else { + /* charset conversion on the fly */ + if (ae->wl[ae->idx].w[i+1]!=tquote) { + ___output(ae,ae->charset[(int)ae->wl[ae->idx].w[i]]); + } else { + ___output(ae,ae->charset[(int)ae->wl[ae->idx].w[i]]|0x80); + } + } + + i++; + } + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"STR handle only quoted strings!\n"); + } + } while (ae->wl[ae->idx].t==0); + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"STR needs one or more quotes parameters\n"); + } +} + +void _DEFR(struct s_assenv *ae) { + if (!ae->wl[ae->idx].t) { + do { + ae->idx++; + PushExpression(ae,ae->idx,E_EXPRESSION_0VR); + } while (ae->wl[ae->idx].t==0); + } else { + if (ae->getstruct) { + ___output(ae,0);___output(ae,0);___output(ae,0);___output(ae,0);___output(ae,0); + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"DEFR needs one or more parameters\n"); + } + } +} +void _DEFR_struct(struct s_assenv *ae) { + unsigned char *rc; + double v; + if (!ae->wl[ae->idx].t) { + do { + ae->idx++; + /* conversion des symboles connus */ + ExpressionFastTranslate(ae,&ae->wl[ae->idx].w,0); + /* calcul de la valeur définitive de l'expression */ + v=ComputeExpressionCore(ae,ae->wl[ae->idx].w,ae->outputadr,0); + /* conversion en réel Amsdos */ + rc=__internal_MakeAmsdosREAL(ae,v,0); + ___output(ae,rc[0]);___output(ae,rc[1]);___output(ae,rc[2]);___output(ae,rc[3]);___output(ae,rc[4]); + } while (ae->wl[ae->idx].t==0); + } else { + if (ae->getstruct) { + ___output(ae,0);___output(ae,0);___output(ae,0);___output(ae,0);___output(ae,0); + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"DEFR needs one or more parameters\n"); + } + } +} + +void _DEFB(struct s_assenv *ae) { + int i,tquote; + unsigned char c; + if (!ae->wl[ae->idx].t) { + do { + ae->idx++; + if ((tquote=StringIsQuote(ae->wl[ae->idx].w))!=0) { + i=1; + while (ae->wl[ae->idx].w[i] && ae->wl[ae->idx].w[i]!=tquote) { + if (ae->wl[ae->idx].w[i]=='\\') { + i++; + /* no conversion on escaped chars */ + c=ae->wl[ae->idx].w[i]; + switch (c) { + case 'b':___output(ae,'\b');break; + case 'v':___output(ae,'\v');break; + case 'f':___output(ae,'\f');break; + case '0':___output(ae,'\0');break; + case 'r':___output(ae,'\r');break; + case 'n':___output(ae,'\n');break; + case 't':___output(ae,'\t');break; + default: + ___output(ae,c); + ae->nop+=1; + } + } else { + /* charset conversion on the fly */ + ___output(ae,ae->charset[(int)ae->wl[ae->idx].w[i]]); + ae->nop+=1; + } + i++; + } + } else { + PushExpression(ae,ae->idx,E_EXPRESSION_0V8); + ae->nop+=1; + } + } while (ae->wl[ae->idx].t==0); + } else { + if (ae->getstruct) { + ___output(ae,0); + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"DEFB needs one or more parameters\n"); + } + } +} +void _DEFB_struct(struct s_assenv *ae) { + int i,tquote; + unsigned char c; + if (!ae->wl[ae->idx].t) { + do { + ae->idx++; + if ((tquote=StringIsQuote(ae->wl[ae->idx].w))!=0) { + i=1; + while (ae->wl[ae->idx].w[i] && ae->wl[ae->idx].w[i]!=tquote) { + if (ae->wl[ae->idx].w[i]=='\\') { + i++; + /* no conversion on escaped chars */ + c=ae->wl[ae->idx].w[i]; + switch (c) { + case 'b':___output(ae,'\b');break; + case 'v':___output(ae,'\v');break; + case 'f':___output(ae,'\f');break; + case '0':___output(ae,'\0');break; + case 'r':___output(ae,'\r');break; + case 'n':___output(ae,'\n');break; + case 't':___output(ae,'\t');break; + default: + ___output(ae,c); + ae->nop+=1; + } + } else { + /* charset conversion on the fly */ + ___output(ae,ae->charset[(int)ae->wl[ae->idx].w[i]]); + ae->nop+=1; + } + i++; + } + } else { + int v; + ExpressionFastTranslate(ae,&ae->wl[ae->idx].w,0); + v=RoundComputeExpressionCore(ae,ae->wl[ae->idx].w,ae->outputadr,0); + ___output(ae,v); + ae->nop+=1; + } + } while (ae->wl[ae->idx].t==0); + } else { + if (ae->getstruct) { + ___output(ae,0); + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"DEFB needs one or more parameters\n"); + } + } +} + +void _DEFW(struct s_assenv *ae) { + if (!ae->wl[ae->idx].t) { + do { + ae->idx++; + PushExpression(ae,ae->idx,E_EXPRESSION_0V16); + } while (ae->wl[ae->idx].t==0); + } else { + if (ae->getstruct) { + ___output(ae,0);___output(ae,0); + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"DEFW needs one or more parameters\n"); + } + } +} + +void _DEFW_struct(struct s_assenv *ae) { + int v; + if (!ae->wl[ae->idx].t) { + do { + ae->idx++; + ExpressionFastTranslate(ae,&ae->wl[ae->idx].w,0); + v=RoundComputeExpressionCore(ae,ae->wl[ae->idx].w,ae->outputadr,0); + ___output(ae,v&0xFF);___output(ae,(v>>8)&0xFF); + } while (ae->wl[ae->idx].t==0); + } else { + if (ae->getstruct) { + ___output(ae,0);___output(ae,0); + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"DEFW needs one or more parameters\n"); + } + } +} + +void _DEFI(struct s_assenv *ae) { + if (!ae->wl[ae->idx].t) { + do { + ae->idx++; + PushExpression(ae,ae->idx,E_EXPRESSION_0V32); + } while (ae->wl[ae->idx].t==0); + } else { + if (ae->getstruct) { + ___output(ae,0);___output(ae,0);___output(ae,0);___output(ae,0); + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"DEFI needs one or more parameters\n"); + } + } +} + +void _DEFI_struct(struct s_assenv *ae) { + int v; + if (!ae->wl[ae->idx].t) { + do { + ae->idx++; + ExpressionFastTranslate(ae,&ae->wl[ae->idx].w,0); + v=RoundComputeExpressionCore(ae,ae->wl[ae->idx].w,ae->outputadr,0); + ___output(ae,v&0xFF);___output(ae,(v>>8)&0xFF);___output(ae,(v>>16)&0xFF);___output(ae,(v>>24)&0xFF); + } while (ae->wl[ae->idx].t==0); + } else { + if (ae->getstruct) { + ___output(ae,0);___output(ae,0);___output(ae,0);___output(ae,0); + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"DEFI needs one or more parameters\n"); + } + } +} + +void _DEFB_as80(struct s_assenv *ae) { + int i,tquote; + int modadr=0; + + if (!ae->wl[ae->idx].t) { + do { + ae->idx++; + if ((tquote=StringIsQuote(ae->wl[ae->idx].w))!=0) { + i=1; + while (ae->wl[ae->idx].w[i] && ae->wl[ae->idx].w[i]!=tquote) { + if (ae->wl[ae->idx].w[i]=='\\') i++; + /* charset conversion on the fly */ + ___output(ae,ae->charset[(int)ae->wl[ae->idx].w[i]]); + ae->nop+=1; + ae->codeadr--;modadr++; + i++; + } + } else { + PushExpression(ae,ae->idx,E_EXPRESSION_0V8); + ae->codeadr--;modadr++; + ae->nop+=1; + } + } while (ae->wl[ae->idx].t==0); + ae->codeadr+=modadr; + } else { + if (ae->getstruct) { + ___output(ae,0); + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"DEFB needs one or more parameters\n"); + } + } +} + +void _DEFW_as80(struct s_assenv *ae) { + int modadr=0; + if (!ae->wl[ae->idx].t) { + do { + ae->idx++; + PushExpression(ae,ae->idx,E_EXPRESSION_0V16); + ae->codeadr-=2;modadr+=2; + } while (ae->wl[ae->idx].t==0); + ae->codeadr+=modadr; + } else { + if (ae->getstruct) { + ___output(ae,0);___output(ae,0); + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"DEFW needs one or more parameters\n"); + } + } +} + +void _DEFI_as80(struct s_assenv *ae) { + int modadr=0; + if (!ae->wl[ae->idx].t) { + do { + ae->idx++; + PushExpression(ae,ae->idx,E_EXPRESSION_0V32); + ae->codeadr-=4;modadr+=4; + } while (ae->wl[ae->idx].t==0); + ae->codeadr+=modadr; + } else { + if (ae->getstruct) { + ___output(ae,0);___output(ae,0);___output(ae,0);___output(ae,0); + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"DEFI needs one or more parameters\n"); + } + } +} +#if 0 +void _DEFSTR(struct s_assenv *ae) { + int i,tquote; + unsigned char c; + if (!ae->wl[ae->idx].t && !ae->wl[ae->idx+1].t && ae->wl[ae->idx+2].t==1) { + if (StringIsQuote(ae->wl[ae->idx+1].w) && StringIsQuote(ae->wl[ae->idx+2].w)) { + i=1; + while (ae->wl[ae->idx].w[i] && ae->wl[ae->idx].w[i]!=tquote) { + if (ae->wl[ae->idx].w[i]=='\\') { + i++; + /* no conversion on escaped chars */ + c=ae->wl[ae->idx].w[i]; + switch (c) { + case 'b':___output(ae,'\b');break; + case 'v':___output(ae,'\v');break; + case 'f':___output(ae,'\f');break; + case '0':___output(ae,'\0');break; + case 'r':___output(ae,'\r');break; + case 'n':___output(ae,'\n');break; + case 't':___output(ae,'\t');break; + default: + ___output(ae,c); + } + } else { + /* charset conversion on the fly */ + ___output(ae,ae->charset[(int)ae->wl[ae->idx].w[i]]); + } + i++; + } + } + ae->idx+=2; + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"DEFSTR needs two parameters\n"); + } +} +#endif + +#undef FUNC +#define FUNC "Directive CORE" + +void __internal_UpdateLZBlockIfAny(struct s_assenv *ae) { + /* there was a crunched block opened in the previous bank */ + if (ae->lz>=0) { + //ae->lzsection[ae->ilz-1].iorgzone=ae->io-1; + //ae->lzsection[ae->ilz-1].ibank=ae->activebank; + } + ae->lz=-1; +} + + +void __AMSDOS(struct s_assenv *ae) { + ae->amsdos=1; +} + +void __internal_EXPORT(struct s_assenv *ae, int exportval) { + struct s_label *curlabel; + struct s_expr_dico *curdic; + int ialias,crc,freeflag; + char *localname; + + if (ae->wl[ae->idx].t) { + /* without parameter enable/disable export */ + ae->autorise_export=exportval; + } else while (!ae->wl[ae->idx].t) { + ae->idx++; + freeflag=0; + + /* local label */ + if (ae->wl[ae->idx].w[0]=='.' && ae->lastgloballabel) { + localname=MemMalloc(strlen(ae->wl[ae->idx].w)+1+ae->lastgloballabellen); + sprintf(localname,"%s%s",ae->lastgloballabel,ae->wl[ae->idx].w); + freeflag=1; + } else { + localname=ae->wl[ae->idx].w; + } + crc=GetCRC(localname); + + if ((curlabel=SearchLabel(ae,localname,crc))!=NULL) { + curlabel->autorise_export=exportval; + ae->label[curlabel->backidx].autorise_export=exportval; + } else { + if ((curdic=SearchDico(ae,ae->wl[ae->idx].w,crc))!=NULL) { + curdic->autorise_export=exportval; + } else { + if ((ialias=SearchAlias(ae,crc,ae->wl[ae->idx].w))!=-1) { + ae->alias[ialias].autorise_export=exportval; + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"(E)NOEXPORT did not found [%s] in variables, labels or aliases\n",ae->wl[ae->idx].w); + } + } + } + if (freeflag) MemFree(localname); + } +} +void __NOEXPORT(struct s_assenv *ae) { + __internal_EXPORT(ae,0); +} +void __ENOEXPORT(struct s_assenv *ae) { + __internal_EXPORT(ae,1); +} + +void __BUILDZX(struct s_assenv *ae) { + if (!ae->wl[ae->idx].t) { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"BUILDZX does not need a parameter\n"); + } + if (!ae->forcesnapshot && !ae->forcetape && !ae->forcecpr) { + ae->forcezx=1; + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Cannot select ZX output when already in Amstrad cartridge/snapshot/tape output\n"); + } +} +void __BUILDCPR(struct s_assenv *ae) { + if (!ae->wl[ae->idx].t) { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"BUILDCPR does not need a parameter\n"); + } + if (!ae->forcesnapshot && !ae->forcetape && !ae->forcezx) { + ae->forcecpr=1; + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Cannot select Amstrad cartridge output when already in snapshot/tape output\n"); + } +} +void __BUILDSNA(struct s_assenv *ae) { + if (!ae->wl[ae->idx].t) { + if (strcmp(ae->wl[ae->idx+1].w,"V2")==0) { + ae->snapshot.version=2; + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"BUILDSNA unrecognized option\n"); + } + } + if (!ae->forcecpr && !ae->forcetape && !ae->forcezx) { + ae->forcesnapshot=1; + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Cannot select snapshot output when already in cartridge/tape output\n"); + } +} +void __BUILDTAPE(struct s_assenv *ae) { + if (!ae->wl[ae->idx].t) { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"BUILDTAPE does not need a parameter\n"); + } + if (!ae->forcesnapshot && !ae->forcecpr && !ae->forcezx) { + ae->forcetape=1; + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Cannot select tape output when already in snapshot/cartridge output\n"); + } +} + + +void __LZ4(struct s_assenv *ae) { + struct s_lz_section curlz; + + if (!ae->wl[ae->idx].t) { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"LZ directive does not need any parameter\n"); + return; + } + #ifdef NO_3RD_PARTIES + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Cannot use 3rd parties cruncher with this version of RASM\n",GetCurrentFile(ae),ae->wl[ae->idx].l); + FreeAssenv(ae); + exit(-5); + #endif + + if (ae->lz>=0 && ae->lz<ae->ilz) { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Cannot start a new LZ section inside another one (%d)\n",GetCurrentFile(ae),ae->wl[ae->idx].l,ae->lz); + FreeAssenv(ae); + exit(-5); + } + curlz.iw=ae->idx; + curlz.iorgzone=ae->io-1; + curlz.ibank=ae->activebank; + curlz.memstart=ae->outputadr; + curlz.memend=-1; + curlz.lzversion=4; + ae->lz=ae->ilz; + ObjectArrayAddDynamicValueConcat((void**)&ae->lzsection,&ae->ilz,&ae->mlz,&curlz,sizeof(curlz)); +} +void __LZX7(struct s_assenv *ae) { + struct s_lz_section curlz; + + if (!ae->wl[ae->idx].t) { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"LZ directive does not need any parameter\n"); + return; + } + #ifdef NO_3RD_PARTIES + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Cannot use 3rd parties cruncher with this version of RASM\n",GetCurrentFile(ae),ae->wl[ae->idx].l); + FreeAssenv(ae); + exit(-5); + #endif + + if (ae->lz>=0 && ae->lz<ae->ilz) { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Cannot start a new LZ section inside another one (%d)\n",GetCurrentFile(ae),ae->wl[ae->idx].l,ae->lz); + FreeAssenv(ae); + exit(-5); + } + curlz.iw=ae->idx; + curlz.iorgzone=ae->io-1; + curlz.ibank=ae->activebank; + curlz.memstart=ae->outputadr; + curlz.memend=-1; + curlz.lzversion=7; + ae->lz=ae->ilz; + ObjectArrayAddDynamicValueConcat((void**)&ae->lzsection,&ae->ilz,&ae->mlz,&curlz,sizeof(curlz)); +} +void __LZEXO(struct s_assenv *ae) { + struct s_lz_section curlz; + + if (!ae->wl[ae->idx].t) { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"LZ directive does not need any parameter\n"); + return; + } + #ifdef NO_3RD_PARTIES + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Cannot use 3rd parties cruncher with this version of RASM\n",GetCurrentFile(ae),ae->wl[ae->idx].l); + FreeAssenv(ae); + exit(-5); + #endif + + if (ae->lz>=0 && ae->lz<ae->ilz) { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Cannot start a new LZ section inside another one (%d)\n",GetCurrentFile(ae),ae->wl[ae->idx].l,ae->lz); + FreeAssenv(ae); + exit(-5); + } + curlz.iw=ae->idx; + curlz.iorgzone=ae->io-1; + curlz.ibank=ae->activebank; + curlz.memstart=ae->outputadr; + curlz.memend=-1; + curlz.lzversion=8; + ae->lz=ae->ilz; + ObjectArrayAddDynamicValueConcat((void**)&ae->lzsection,&ae->ilz,&ae->mlz,&curlz,sizeof(curlz)); +} +void __LZ48(struct s_assenv *ae) { + struct s_lz_section curlz; + + if (!ae->wl[ae->idx].t) { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"LZ directive does not need any parameter\n"); + return; + } + if (ae->lz>=0 && ae->lz<ae->ilz) { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Cannot start a new LZ section inside another one (%d)\n",GetCurrentFile(ae),ae->wl[ae->idx].l,ae->lz); + FreeAssenv(ae); + exit(-5); + } + curlz.iw=ae->idx; + curlz.iorgzone=ae->io-1; + curlz.ibank=ae->activebank; + curlz.memstart=ae->outputadr; + curlz.memend=-1; + curlz.lzversion=48; + ae->lz=ae->ilz; + ObjectArrayAddDynamicValueConcat((void**)&ae->lzsection,&ae->ilz,&ae->mlz,&curlz,sizeof(curlz)); +} +void __LZ49(struct s_assenv *ae) { + struct s_lz_section curlz; + + if (!ae->wl[ae->idx].t) { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"LZ directive does not need any parameter\n"); + return; + } + if (ae->lz>=0 && ae->lz<ae->ilz) { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Cannot start a new LZ section inside another one (%d)\n",GetCurrentFile(ae),ae->wl[ae->idx].l,ae->lz); + FreeAssenv(ae); + exit(-5); + } + + curlz.iw=ae->idx; + curlz.iorgzone=ae->io-1; + curlz.ibank=ae->activebank; + curlz.memstart=ae->outputadr; + curlz.memend=-1; + curlz.lzversion=49; + ae->lz=ae->ilz; + ObjectArrayAddDynamicValueConcat((void**)&ae->lzsection,&ae->ilz,&ae->mlz,&curlz,sizeof(curlz)); +} +void __LZCLOSE(struct s_assenv *ae) { + if (!ae->ilz || ae->lz==-1) { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Cannot close LZ section as it wasn't opened\n"); + return; + } + + ae->lzsection[ae->ilz-1].memend=ae->outputadr; + ae->lzsection[ae->ilz-1].ilabel=ae->il; + ae->lzsection[ae->ilz-1].iexpr=ae->ie; + //ae->lz=ae->ilz; + ae->lz=-1; +} + +void __LIMIT(struct s_assenv *ae) { + if (ae->wl[ae->idx+1].t!=2) { + ExpressionFastTranslate(ae,&ae->wl[ae->idx+1].w,0); + ___output_set_limit(ae,RoundComputeExpression(ae,ae->wl[ae->idx+1].w,ae->outputadr,0,0)); + ae->idx++; + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"LIMIT directive need one integer parameter\n"); + } +} +void OverWriteCheck(struct s_assenv *ae) +{ + #undef FUNC + #define FUNC "OverWriteCheck" + + int i,j; + + /* overwrite checking */ + i=ae->io-1; { + if (ae->orgzone[i].memstart!=ae->orgzone[i].memend) { + for (j=0;j<ae->io-1;j++) { + if (ae->orgzone[j].memstart!=ae->orgzone[j].memend && !ae->orgzone[j].nocode) { + if (ae->orgzone[i].ibank==ae->orgzone[j].ibank) { + if ((ae->orgzone[i].memstart>=ae->orgzone[j].memstart && ae->orgzone[i].memstart<ae->orgzone[j].memend) + || (ae->orgzone[i].memend>ae->orgzone[j].memstart && ae->orgzone[i].memend<ae->orgzone[j].memend) + || (ae->orgzone[i].memstart<=ae->orgzone[j].memstart && ae->orgzone[i].memend>=ae->orgzone[j].memend)) { + ae->idx--; + if (ae->orgzone[j].protect) { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"PROTECTED section error [%s] L%d [#%04X-#%04X-B%d] with [%s] L%d [#%04X/#%04X]\n",ae->filename[ae->orgzone[j].ifile],ae->orgzone[j].iline,ae->orgzone[j].memstart,ae->orgzone[j].memend,ae->orgzone[j].ibank<32?ae->orgzone[j].ibank:0,ae->filename[ae->orgzone[i].ifile],ae->orgzone[i].iline,ae->orgzone[i].memstart,ae->orgzone[i].memend); + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Assembling overwrite [%s] L%d [#%04X-#%04X-B%d] with [%s] L%d [#%04X/#%04X]\n",ae->filename[ae->orgzone[j].ifile],ae->orgzone[j].iline,ae->orgzone[j].memstart,ae->orgzone[j].memend,ae->orgzone[j].ibank<32?ae->orgzone[j].ibank:0,ae->filename[ae->orgzone[i].ifile],ae->orgzone[i].iline,ae->orgzone[i].memstart,ae->orgzone[i].memend); + } + i=j=ae->io; + break; + } + } + } + } + } + } +} + +void ___new_memory_space(struct s_assenv *ae) +{ + #undef FUNC + #define FUNC "___new_memory_space" + + unsigned char *mem; + struct s_orgzone orgzone={0}; + + __internal_UpdateLZBlockIfAny(ae); + if (ae->io) { + ae->orgzone[ae->io-1].memend=ae->outputadr; + } + if (ae->lz>=0) { + if (!ae->nowarning) rasm_printf(ae,KWARNING"[%s:%d] Warning: LZ section wasn't closed before a new memory space directive\n",GetCurrentFile(ae),ae->wl[ae->idx].l); + __LZCLOSE(ae); + } + ae->activebank=ae->nbbank; + mem=MemMalloc(65536); + memset(mem,0,65536); + ObjectArrayAddDynamicValueConcat((void**)&ae->mem,&ae->nbbank,&ae->maxbank,&mem,sizeof(mem)); + + ae->outputadr=0; + ae->codeadr=0; + orgzone.memstart=0; + orgzone.ibank=ae->activebank; + orgzone.nocode=ae->nocode=0; + ObjectArrayAddDynamicValueConcat((void**)&ae->orgzone,&ae->io,&ae->mo,&orgzone,sizeof(orgzone)); + + OverWriteCheck(ae); +} + +void __BANK(struct s_assenv *ae) { + struct s_orgzone orgzone={0}; + int oldcode=0,oldoutput=0; + int i; + __internal_UpdateLZBlockIfAny(ae); + + if (ae->io) { + ae->orgzone[ae->io-1].memend=ae->outputadr; + } + /* without parameter, create a new empty space */ + if (ae->wl[ae->idx].t==1) { + ___new_memory_space(ae); + return; + } + + ae->bankmode=1; + if (!ae->forcecpr && !ae->forcesnapshot && !ae->forcezx) ae->forcecpr=1; + + if (ae->wl[ae->idx+1].t!=2) { + ExpressionFastTranslate(ae,&ae->wl[ae->idx+1].w,0); + ae->activebank=RoundComputeExpression(ae,ae->wl[ae->idx+1].w,ae->codeadr,0,0); + if (ae->forcecpr && (ae->activebank<0 || ae->activebank>31)) { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"FATAL - Bank selection must be from 0 to 31 in cartridge mode\n",GetCurrentFile(ae),ae->wl[ae->idx].l); + FreeAssenv(ae); + exit(2); + } else if (ae->forcezx && (ae->activebank<0 || ae->activebank>7)) { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"FATAL - Bank selection must be from 0 to 7 in ZX Spectrum mode\n",GetCurrentFile(ae),ae->wl[ae->idx].l); + FreeAssenv(ae); + exit(2); + } else if (ae->forcesnapshot && (ae->activebank<0 || ae->activebank>=260)) { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"FATAL - Bank selection must be from 0 to 259 in snapshot mode\n",GetCurrentFile(ae),ae->wl[ae->idx].l); + FreeAssenv(ae); + exit(2); + } + /* bankset control */ + if (ae->forcesnapshot && ae->bankset[ae->activebank/4]) { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Cannot BANK %d was already select by a previous BANKSET %d\n",ae->activebank,(int)ae->activebank/4); + ae->idx++; + return; + } else { + ae->bankused[ae->activebank]=1; + } + ae->idx++; + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"BANK directive need one integer parameter\n"); + return; + } + if (ae->lz>=0) { + if (!ae->nowarning) rasm_printf(ae,KWARNING"[%s:%d] Warning: LZ section wasn't closed before a new BANK directive\n",GetCurrentFile(ae),ae->wl[ae->idx].l); + __LZCLOSE(ae); + } + + /* try to get an old ORG settings backward */ + for (i=ae->io-1;i>=0;i--) { + if (ae->orgzone[i].ibank==ae->activebank) { + oldcode=ae->orgzone[i].memend; + oldoutput=ae->orgzone[i].memend; + break; + } + } + ae->outputadr=oldoutput; + ae->codeadr=oldcode; + orgzone.memstart=ae->outputadr; + /* legacy */ + orgzone.ibank=ae->activebank; + orgzone.nocode=ae->nocode=0; + ObjectArrayAddDynamicValueConcat((void**)&ae->orgzone,&ae->io,&ae->mo,&orgzone,sizeof(orgzone)); + + OverWriteCheck(ae); +} + +void __BANKSET(struct s_assenv *ae) { + struct s_orgzone orgzone={0}; + int ibank; + + __internal_UpdateLZBlockIfAny(ae); + + if (!ae->forcesnapshot && !ae->forcecpr && !ae->forcezx) ae->forcesnapshot=1; + if (!ae->forcesnapshot) { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"BANKSET directive is specific to snapshot output\n"); + return; + } + + if (ae->io) { + ae->orgzone[ae->io-1].memend=ae->outputadr; + } + ae->bankmode=1; + + if (ae->wl[ae->idx+1].t!=2) { + ExpressionFastTranslate(ae,&ae->wl[ae->idx+1].w,0); + ae->activebank=RoundComputeExpression(ae,ae->wl[ae->idx+1].w,ae->codeadr,0,0); + ae->activebank*=4; + if (ae->forcesnapshot && (ae->activebank<0 || ae->activebank>=260)) { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"FATAL - Bank set selection must be from 0 to 64 in snapshot mode\n",GetCurrentFile(ae),ae->wl[ae->idx].l); + FreeAssenv(ae); + exit(2); + } + /* control */ + ibank=ae->activebank; + if (ae->bankused[ibank] || ae->bankused[ibank+1]|| ae->bankused[ibank+2]|| ae->bankused[ibank+3]) { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Cannot BANKSET because bank %d was already selected in single page mode\n",ibank); + ae->idx++; + return; + } else { + ae->bankset[ae->activebank/4]=1; /* pas très heureux mais bon... */ + } + ae->idx++; + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"BANKSET directive need one integer parameter\n"); + return; + } + if (ae->lz>=0) { + if (!ae->nowarning) rasm_printf(ae,KWARNING"[%s:%d] Warning: LZ section wasn't closed before a new BANKSET directive\n",GetCurrentFile(ae),ae->wl[ae->idx].l); + __LZCLOSE(ae); + } + + ae->outputadr=0; + ae->codeadr=0; + orgzone.memstart=0; + orgzone.ibank=ae->activebank; + orgzone.nocode=ae->nocode=0; + ObjectArrayAddDynamicValueConcat((void**)&ae->orgzone,&ae->io,&ae->mo,&orgzone,sizeof(orgzone)); + + OverWriteCheck(ae); +} + + +void __NameBANK(struct s_assenv *ae) { + int ibank; + + ae->bankmode=1; + if (!ae->wl[ae->idx].t && !ae->wl[ae->idx+1].t && ae->wl[ae->idx+2].t==1) { + if (!StringIsQuote(ae->wl[ae->idx+2].w)) { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Syntax is NAMEBANK <bank number>,'<string>'\n"); + } else { + ExpressionFastTranslate(ae,&ae->wl[ae->idx+1].w,0); + ibank=RoundComputeExpression(ae,ae->wl[ae->idx+1].w,ae->codeadr,0,0); + if (ibank<0 || ibank>=BANK_MAX_NUMBER) { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"NAMEBANK selection must be from 0 to %d\n",BANK_MAX_NUMBER); + } else { + ae->iwnamebank[ibank]=ae->idx+2; + } + } + ae->idx+=2; + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"NAMEBANK directive need one integer parameter and a string\n"); + } +} + +/*** + Winape little compatibility for CPR writing! +*/ +void __WRITE(struct s_assenv *ae) { + int ok=0; + int lower=-1,upper=-1,bank=-1; + + if (!ae->wl[ae->idx].t && strcmp(ae->wl[ae->idx+1].w,"DIRECT")==0 && !ae->wl[ae->idx+1].t) { + ExpressionFastTranslate(ae,&ae->wl[ae->idx+2].w,0); + lower=RoundComputeExpression(ae,ae->wl[ae->idx+2].w,ae->codeadr,0,0); + if (!ae->wl[ae->idx+2].t) { + ExpressionFastTranslate(ae,&ae->wl[ae->idx+3].w,0); + upper=RoundComputeExpression(ae,ae->wl[ae->idx+3].w,ae->codeadr,0,0); + } + if (!ae->wl[ae->idx+3].t) { + ExpressionFastTranslate(ae,&ae->wl[ae->idx+4].w,0); + bank=RoundComputeExpression(ae,ae->wl[ae->idx+4].w,ae->codeadr,0,0); + } + + if (ae->maxam) { + if (lower==65535) lower=-1; + if (upper==65535) upper=-1; + if (bank==65535) bank=-1; + } + + if (lower!=-1) { + if (lower>=0 && lower<8) { + ae->idx+=1; + __BANK(ae); + ok=1; + } else { + if (!ae->nowarning) rasm_printf(ae,KWARNING"[%s:%d] Warning: WRITE DIRECT lower ROM ignored (value %d out of bounds 0-7)\n",GetCurrentFile(ae),ae->wl[ae->idx].l,lower); + } + } else if (upper!=-1) { + if (upper>=0 && ((ae->forcecpr && upper<32) || (ae->forcesnapshot && upper<BANK_MAX_NUMBER))) { + ae->idx+=2; + __BANK(ae); + ok=1; + } else { + if (!ae->forcecpr && !ae->forcesnapshot) { + if (!ae->nowarning) rasm_printf(ae,KWARNING"[%s:%d] Warning: WRITE DIRECT select a ROM without cartridge output\n",GetCurrentFile(ae),ae->wl[ae->idx].l); + } else { + if (!ae->nowarning) rasm_printf(ae,KWARNING"[%s:%d] Warning: WRITE DIRECT upper ROM ignored (value %d out of bounds 0-31)\n",GetCurrentFile(ae),ae->wl[ae->idx].l,upper); + } + } + } else if (bank!=-1) { + /* selection de bank on ouvre un nouvel espace */ + } else { + if (!ae->nowarning) rasm_printf(ae,KWARNING"[%s:%d] Warning: meaningless WRITE DIRECT\n",GetCurrentFile(ae),ae->wl[ae->idx].l); + } + } + while (!ae->wl[ae->idx].t) ae->idx++; + if (!ok) { + ___new_memory_space(ae); + } +} +void __CHARSET(struct s_assenv *ae) { + int i,s,e,v,tquote; + + if (ae->wl[ae->idx].t==1) { + /* reinit charset */ + for (i=0;i<256;i++) + ae->charset[i]=i; + } else if (!ae->wl[ae->idx].t && !ae->wl[ae->idx+1].t && ae->wl[ae->idx+2].t==1) { + /* string,value | byte,value */ + ExpressionFastTranslate(ae,&ae->wl[ae->idx+2].w,0); + v=RoundComputeExpression(ae,ae->wl[ae->idx+2].w,ae->codeadr,0,0); + if (ae->wl[ae->idx+1].w[0]=='\'' || ae->wl[ae->idx+1].w[0]=='"') { + tquote=ae->wl[ae->idx+1].w[0]; + if (ae->wl[ae->idx+1].w[strlen(ae->wl[ae->idx+1].w)-1]==tquote) { + i=1; + while (ae->wl[ae->idx+1].w[i] && ae->wl[ae->idx+1].w[i]!=tquote) { + if (ae->wl[ae->idx+1].w[i]=='\\') i++; + ae->charset[(int)ae->wl[ae->idx+1].w[i]]=(unsigned char)v++; + i++; + } + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"CHARSET string,value has invalid quote!\n"); + } + } else { + i=RoundComputeExpression(ae,ae->wl[ae->idx+1].w,ae->codeadr,0,0); + if (i>=0 && i<256) { + ae->charset[i]=(unsigned char)v; + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"CHARSET byte value must be 0-255\n"); + } + } + ae->idx+=2; + } else if (!ae->wl[ae->idx].t && !ae->wl[ae->idx+1].t && !ae->wl[ae->idx+2].t && ae->wl[ae->idx+3].t==1) { + /* start,end,value */ + ExpressionFastTranslate(ae,&ae->wl[ae->idx+1].w,0); + ExpressionFastTranslate(ae,&ae->wl[ae->idx+2].w,0); + ExpressionFastTranslate(ae,&ae->wl[ae->idx+3].w,0); + s=RoundComputeExpression(ae,ae->wl[ae->idx+1].w,ae->codeadr,0,0); + e=RoundComputeExpression(ae,ae->wl[ae->idx+2].w,ae->codeadr,0,0); + v=RoundComputeExpression(ae,ae->wl[ae->idx+3].w,ae->codeadr,0,0); + ae->idx+=3; + if (s<=e && s>=0 && e<256) { + for (i=s;i<=e;i++) { + ae->charset[i]=(unsigned char)v++; + } + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"CHARSET winape directive wrong interval value\n"); + } + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"CHARSET winape directive wrong parameter count\n"); + } +} + +void __MACRO(struct s_assenv *ae) { + struct s_macro curmacro={0}; + char *referentfilename,*zeparam; + int refidx,idx,getparam=1; + struct s_wordlist curwl; + + if (!ae->wl[ae->idx].t && ae->wl[ae->idx+1].t!=2) { + /* get the name */ + curmacro.mnemo=ae->wl[ae->idx+1].w; + curmacro.crc=GetCRC(curmacro.mnemo); + if (ae->wl[ae->idx+1].t) { + getparam=0; + } + /* overload forbidden */ + if (SearchMacro(ae,curmacro.crc,curmacro.mnemo)>=0) { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Macro already defined with this name\n"); + } + idx=ae->idx+2; + while (ae->wl[idx].t!=2 && (GetCRC(ae->wl[idx].w)!=CRC_MEND || strcmp(ae->wl[idx].w,"MEND")!=0) && (GetCRC(ae->wl[idx].w)!=CRC_ENDM || strcmp(ae->wl[idx].w,"ENDM")!=0)) { + if (GetCRC(ae->wl[idx].w)==CRC_MACRO || strcmp(ae->wl[idx].w,"MACRO")==0) { + /* inception interdite */ + referentfilename=GetCurrentFile(ae); + refidx=ae->idx; + ae->idx=idx; + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"You cannot define a macro inside another one (MACRO %s in [%s] L%d)\n",ae->wl[refidx+1].w,referentfilename,ae->wl[refidx].l); + __STOP(ae); + } + if (getparam) { + /* on prepare les parametres au remplacement */ + zeparam=MemMalloc(strlen(ae->wl[idx].w)+3); + if (ae->as80) { + sprintf(zeparam,"%s",ae->wl[idx].w); + } else { + sprintf(zeparam,"{%s}",ae->wl[idx].w); + } + curmacro.nbparam++; + curmacro.param=MemRealloc(curmacro.param,curmacro.nbparam*sizeof(char **)); + curmacro.param[curmacro.nbparam-1]=zeparam; + if (ae->wl[idx].t) { + /* duplicate parameters without brackets MUST be an OPTION */ + getparam=0; + } + } else { + /* copie la liste de mots */ + curwl=ae->wl[idx]; + ObjectArrayAddDynamicValueConcat((void **)&curmacro.wc,&curmacro.nbword,&curmacro.maxword,&curwl,sizeof(struct s_wordlist)); + } + idx++; + } + if (ae->wl[idx].t==2) { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Macro was not closed\n"); + } + ObjectArrayAddDynamicValueConcat((void**)&ae->macro,&ae->imacro,&ae->mmacro,&curmacro,sizeof(curmacro)); + /* le quicksort n'est pas optimal mais on n'est pas supposé en créer des milliers */ + qsort(ae->macro,ae->imacro,sizeof(struct s_macro),cmpmacros); + + /* ajustement des mots lus */ + if (ae->wl[idx].t==2) idx--; + ae->idx=idx; + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"MACRO definition need at least one parameter\n"); + } +} + +struct s_wordlist *__MACRO_EXECUTE(struct s_assenv *ae, int imacro) { + struct s_wordlist *cpybackup; + int nbparam=0,idx,i,j,idad; + int ifile,iline,iu,lenparam; + double v; + struct s_macro_position curmacropos={0}; + char *zeparam=NULL,*txtparamlist; + int reload=0; + + idx=ae->idx; + while (!ae->wl[idx].t) { + nbparam++; + idx++; + } + + /* hack to secure macro without parameters with void argument */ + if (!ae->macro[imacro].nbparam) { + if (nbparam) { + if (nbparam==1 && strcmp(ae->wl[ae->idx+1].w,"(VOID)")==0) { + nbparam=0; + reload=1; + } + } else { + if (ae->macrovoid) { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"MACRO [%s] used without (void) and option -void used!\n",ae->macro[imacro].mnemo); + while (!ae->wl[ae->idx].t) { + ae->idx++; + } + ae->idx++; + } + } + } + /* macro must avoid extra params! */ + + /* cannot VOID a macro with parameters! */ + if (ae->macro[imacro].nbparam && strcmp(ae->wl[ae->idx+1].w,"(VOID)")==0) { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"MACRO [%s] has %d parameter%s\n",ae->macro[imacro].mnemo,ae->macro[imacro].nbparam,ae->macro[imacro].nbparam>1?"s":""); + while (!ae->wl[ae->idx].t) { + ae->idx++; + } + ae->idx++; + } else { + if (nbparam!=ae->macro[imacro].nbparam) { + lenparam=1; // macro without parameters! + for (i=0;i<ae->macro[imacro].nbparam;i++) { + lenparam+=strlen(ae->macro[imacro].param[i])+3; + } + txtparamlist=MemMalloc(lenparam); + txtparamlist[0]=0; + for (i=0;i<ae->macro[imacro].nbparam;i++) { + strcat(txtparamlist,ae->macro[imacro].param[i]); + strcat(txtparamlist," "); + } + + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"MACRO [%s] was defined with %d parameter%s %s\n",ae->macro[imacro].mnemo,ae->macro[imacro].nbparam,ae->macro[imacro].nbparam>1?"s":"",txtparamlist); + while (!ae->wl[ae->idx].t) { + ae->idx++; + } + ae->idx++; + } else { + /* free macro call as we will overwrite it */ + MemFree(ae->wl[ae->idx].w); + /* is there a void to free? */ + if (reload) { + MemFree(ae->wl[ae->idx+1].w); + } + /* eval parameters? */ + for (i=0;i<nbparam;i++) { + if (strncmp(ae->wl[ae->idx+1+i].w,"{EVAL}",6)==0) { + /* parametre entre chevrons, il faut l'interpreter d'abord */ + zeparam=TxtStrDup(ae->wl[ae->idx+1+i].w+6); + ExpressionFastTranslate(ae,&zeparam,1); + v=ComputeExpressionCore(ae,zeparam,ae->codeadr,0); + MemFree(zeparam); + zeparam=MemMalloc(32); + snprintf(zeparam,31,"%lf",v); + zeparam[31]=0; + MemFree(ae->wl[ae->idx+1+i].w); + ae->wl[ae->idx+1+i].w=zeparam; + } + } + /* backup parameters */ + cpybackup=MemMalloc((nbparam+1)*sizeof(struct s_wordlist)); + for (i=0;i<nbparam;i++) { + cpybackup[i]=ae->wl[ae->idx+1+i]; + } + /* insert macro position */ + curmacropos.start=ae->idx; + curmacropos.end=ae->idx+ae->macro[imacro].nbword; + curmacropos.value=ae->macrocounter; + ObjectArrayAddDynamicValueConcat((void**)&ae->macropos,&ae->imacropos,&ae->mmacropos,&curmacropos,sizeof(curmacropos)); + + /* are we in a repeat/while block? */ + for (iu=0;iu<ae->ir;iu++) if (ae->repeat[iu].maxim<ae->imacropos) ae->repeat[iu].maxim=ae->imacropos; + for (iu=0;iu<ae->iw;iu++) if (ae->whilewend[iu].maxim<ae->imacropos) ae->whilewend[iu].maxim=ae->imacropos; + + /* update daddy macropos */ + for (idad=0;idad<ae->imacropos-1;idad++) { + if (ae->macropos[idad].end>curmacropos.start) { + ae->macropos[idad].end+=ae->macro[imacro].nbword-1-nbparam-reload; /* coz la macro compte un mot! */ + } + } + + #if 0 + for (idad=0;idad<ae->imacropos;idad++) { + printf("macropos[%d]=%d -> %d\n",idad,ae->macropos[idad].start,ae->macropos[idad].end); + } + #endif + /* insert at macro position and replace macro+parameters */ + if (ae->macro[imacro].nbword>1+nbparam+reload) { + ae->nbword+=ae->macro[imacro].nbword-1-nbparam-reload; + ae->wl=MemRealloc(ae->wl,ae->nbword*sizeof(struct s_wordlist)); + } else { + /* si on réduit pas de realloc pour ne pas perdre de donnees */ + ae->nbword+=ae->macro[imacro].nbword-1-nbparam-reload; + } + iline=ae->wl[ae->idx].l; + ifile=ae->wl[ae->idx].ifile; + MemMove(&ae->wl[ae->idx+ae->macro[imacro].nbword],&ae->wl[ae->idx+reload+nbparam+1],(ae->nbword-ae->idx-ae->macro[imacro].nbword)*sizeof(struct s_wordlist)); + + for (i=0;i<ae->macro[imacro].nbword;i++) { + ae->wl[i+ae->idx].w=TxtStrDup(ae->macro[imacro].wc[i].w); + ae->wl[i+ae->idx].l=iline; + ae->wl[i+ae->idx].ifile=ifile; + /* @@@sujet a evolution, ou double controle */ + ae->wl[i+ae->idx].t=ae->macro[imacro].wc[i].t; + ae->wl[i+ae->idx].e=ae->macro[imacro].wc[i].e; + } + /* replace */ + idx=ae->idx; + for (i=0;i<nbparam;i++) { + for (j=idx;j<idx+ae->macro[imacro].nbword;j++) { + /* tags in upper case for replacement in quotes */ + if (StringIsQuote(ae->wl[j].w)) { + int lm,touched; + for (lm=touched=0;ae->wl[j].w[lm];lm++) { + if (ae->wl[j].w[lm]=='{') touched++; else if (ae->wl[j].w[lm]=='}') touched--; else if (touched) ae->wl[j].w[lm]=toupper(ae->wl[j].w[lm]); + } + } + ae->wl[j].w=TxtReplace(ae->wl[j].w,ae->macro[imacro].param[i],cpybackup[i].w,0); + } + MemFree(cpybackup[i].w); + } + MemFree(cpybackup); + /* macro replaced, need to rollback index */ + //ae->idx--; + } + } + /* a chaque appel de macro on incremente le compteur pour les labels locaux */ + ae->macrocounter++; + + return ae->wl; +} + +/* + ticker start, <var> + ticker stop, <var> +*/ +void __TICKER(struct s_assenv *ae) { + struct s_expr_dico *tvar; + struct s_ticker ticker; + int crc,i; + + if (!ae->wl[ae->idx].t && !ae->wl[ae->idx+1].t && ae->wl[ae->idx+2].t==1) { + crc=GetCRC(ae->wl[ae->idx+2].w); + + if (strcmp(ae->wl[ae->idx+1].w,"START")==0) { + /* is there already a counter? */ + for (i=0;i<ae->iticker;i++) { + if (ae->ticker[i].crc==crc && strcmp(ae->wl[ae->idx+2].w,ae->ticker[i].varname)==0) { + break; + } + } + if (i==ae->iticker) { + ticker.varname=TxtStrDup(ae->wl[ae->idx+2].w); + ticker.crc=crc; + ObjectArrayAddDynamicValueConcat((void **)&ae->ticker,&ae->iticker,&ae->mticker,&ticker,sizeof(struct s_ticker)); + } + ae->ticker[i].nopstart=ae->nop; + } else if (strcmp(ae->wl[ae->idx+1].w,"STOP")==0) { + for (i=0;i<ae->iticker;i++) { + if (ae->ticker[i].crc==crc && strcmp(ae->wl[ae->idx+2].w,ae->ticker[i].varname)==0) { + break; + } + } + if (i<ae->iticker) { + /* set var */ + if ((tvar=SearchDico(ae,ae->wl[ae->idx+2].w,crc))!=NULL) { + /* compute nop count */ + tvar->v=ae->nop-ae->ticker[i].nopstart; + } else { + /* create var with nop count */ + ExpressionSetDicoVar(ae,ae->wl[ae->idx+2].w,ae->nop-ae->ticker[i].nopstart); + } + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"TICKER not found\n"); + } + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"usage is TICKER start/stop,<variable>\n"); + } + ae->idx+=2; + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"usage is TICKER start/stop,<variable>\n"); + } +} + +void __LET(struct s_assenv *ae) { + if (!ae->wl[ae->idx].t && ae->wl[ae->idx+1].t==1) { + ae->idx++; + ExpressionFastTranslate(ae,&ae->wl[ae->idx].w,0); + RoundComputeExpression(ae,ae->wl[ae->idx].w,ae->codeadr,0,0); + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"LET useless winape directive need one expression\n"); + } +} + +void __RUN(struct s_assenv *ae) { + int ramconf=0xC0; + + if (!ae->wl[ae->idx].t) { + ae->current_run_idx=ae->idx+1; + if (ae->forcezx) { + if (!ae->wl[ae->idx].t) { + PushExpression(ae,ae->idx+1,E_EXPRESSION_ZXRUN); // delayed RUN value + PushExpression(ae,ae->idx+2,E_EXPRESSION_ZXSTACK); // delayed STACK value + ae->idx+=2; + } else { + ae->idx++; + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"usage is RUN <adress>,<stack> (ZX mode)\n"); + } + } else { + PushExpression(ae,ae->idx+1,E_EXPRESSION_RUN); // delayed RUN value + ae->idx++; + if (!ae->wl[ae->idx].t) { + ExpressionFastTranslate(ae,&ae->wl[ae->idx+1].w,0); + ramconf=RoundComputeExpression(ae,ae->wl[ae->idx+1].w,ae->codeadr,0,0); + ae->idx++; + if (ramconf<0xC0 || ramconf>0xFF) { + if (!ae->nowarning) rasm_printf(ae,KWARNING"[%s:%d] Warning: ram configuration out of bound %X forced to #C0\n",GetCurrentFile(ae),ae->wl[ae->idx].l,ramconf); + } + } + } + } else { + if (ae->forcezx) MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"usage is RUN <adress>,<stack> (ZX mode)\n"); + else MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"usage is RUN <adress>[,<ppi>]\n"); + } + ae->snapshot.ramconfiguration=ramconf; + if (ae->rundefined && !ae->nowarning) rasm_printf(ae,KWARNING"[%s:%d] Warning: run adress redefinition\n",GetCurrentFile(ae),ae->wl[ae->idx].l); + ae->rundefined=1; +} +void __BREAKPOINT(struct s_assenv *ae) { + struct s_breakpoint breakpoint={0}; + + if (ae->activebank>3) breakpoint.bank=1; + if (ae->wl[ae->idx].t) { + breakpoint.address=ae->codeadr; + ObjectArrayAddDynamicValueConcat((void **)&ae->breakpoint,&ae->ibreakpoint,&ae->maxbreakpoint,&breakpoint,sizeof(struct s_breakpoint)); + } else if (!ae->wl[ae->idx].t && ae->wl[ae->idx+1].t==1) { + breakpoint.address=RoundComputeExpression(ae,ae->wl[ae->idx+1].w,ae->codeadr,0,0); + ObjectArrayAddDynamicValueConcat((void **)&ae->breakpoint,&ae->ibreakpoint,&ae->maxbreakpoint,&breakpoint,sizeof(struct s_breakpoint)); + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is BREAKPOINT [adress]\n"); + } +} +void __SETCPC(struct s_assenv *ae) { + int mycpc; + + if (!ae->forcecpr) { + ae->forcesnapshot=1; + } else { + if (!ae->nowarning) rasm_printf(ae,KWARNING"[%s:%d] Warning: Cannot SETCPC when already in cartridge output\n",GetCurrentFile(ae),ae->wl[ae->idx].l); + } + + if (!ae->wl[ae->idx].t && ae->wl[ae->idx+1].t==1) { + ExpressionFastTranslate(ae,&ae->wl[ae->idx+1].w,0); + mycpc=RoundComputeExpression(ae,ae->wl[ae->idx+1].w,ae->codeadr,0,0); + ae->idx++; + switch (mycpc) { + case 0: + case 1: + case 2: + case 4: + case 5: + case 6: + ae->snapshot.CPCType=mycpc; + break; + default: + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"SETCPC directive has wrong value (0,1,2,4,5,6 only)\n"); + } + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"SETCPC directive need one integer parameter\n"); + } +} +void __SETCRTC(struct s_assenv *ae) { + int mycrtc; + + if (!ae->forcecpr) { + ae->forcesnapshot=1; + } else { + if (!ae->nowarning) rasm_printf(ae,KWARNING"[%s:%d] Warning: Cannot SETCRTC when already in cartridge output\n",GetCurrentFile(ae),ae->wl[ae->idx].l); + } + + if (!ae->wl[ae->idx].t && ae->wl[ae->idx+1].t==1) { + ExpressionFastTranslate(ae,&ae->wl[ae->idx+1].w,0); + mycrtc=RoundComputeExpression(ae,ae->wl[ae->idx+1].w,ae->codeadr,0,0); + ae->idx++; + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"SETCRTC directive need one integer parameter\n"); + } + switch (mycrtc) { + case 0: + case 1: + case 2: + case 3: + case 4: + ae->snapshot.crtcstate.model=mycrtc; + break; + default: + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"SETCRTC directive has wrong value (0,1,2,3,4 only)\n"); + } +} + + +void __LIST(struct s_assenv *ae) { + if (!ae->wl[ae->idx].t) { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"LIST winape directive do not need parameter\n"); + } +} +void __NOLIST(struct s_assenv *ae) { + if (!ae->wl[ae->idx].t) { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"NOLIST winape directive do not need parameter\n"); + } +} + +void __BRK(struct s_assenv *ae) { + if (!ae->wl[ae->idx].t) { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"BRK winape directive do not need parameter\n"); + } +} + +void __STOP(struct s_assenv *ae) { + rasm_printf(ae,"[%s:%d] STOP assembling requested\n",GetCurrentFile(ae),ae->wl[ae->idx].l); + while (ae->wl[ae->idx].t!=2) ae->idx++; + ae->idx--; + ae->stop=1; +} + +void __PRINT(struct s_assenv *ae) { + while (ae->wl[ae->idx].t!=1) { + if (!StringIsQuote(ae->wl[ae->idx+1].w)) { + char *string2print=NULL; + int hex=0,bin=0,entier=0; + + if (strncmp(ae->wl[ae->idx+1].w,"{HEX}",5)==0) { + string2print=TxtStrDup(ae->wl[ae->idx+1].w+5); + hex=1; + } else if (strncmp(ae->wl[ae->idx+1].w,"{HEX2}",6)==0) { + string2print=TxtStrDup(ae->wl[ae->idx+1].w+6); + hex=2; + } else if (strncmp(ae->wl[ae->idx+1].w,"{HEX4}",6)==0) { + string2print=TxtStrDup(ae->wl[ae->idx+1].w+6); + hex=4; + } else if (strncmp(ae->wl[ae->idx+1].w,"{HEX8}",6)==0) { + string2print=TxtStrDup(ae->wl[ae->idx+1].w+6); + hex=8; + } else if (strncmp(ae->wl[ae->idx+1].w,"{BIN}",5)==0) { + string2print=TxtStrDup(ae->wl[ae->idx+1].w+5); + bin=1; + } else if (strncmp(ae->wl[ae->idx+1].w,"{BIN8}",6)==0) { + string2print=TxtStrDup(ae->wl[ae->idx+1].w+6); + bin=8; + } else if (strncmp(ae->wl[ae->idx+1].w,"{BIN16}",7)==0) { + string2print=TxtStrDup(ae->wl[ae->idx+1].w+7); + bin=16; + } else if (strncmp(ae->wl[ae->idx+1].w,"{BIN32}",7)==0) { + string2print=TxtStrDup(ae->wl[ae->idx+1].w+7); + bin=32; + } else if (strncmp(ae->wl[ae->idx+1].w,"{INT}",5)==0) { + string2print=TxtStrDup(ae->wl[ae->idx+1].w+5); + entier=1; + } else { + string2print=TxtStrDup(ae->wl[ae->idx+1].w); + } + + ExpressionFastTranslate(ae,&string2print,1); + if (hex) { + int zv; + zv=RoundComputeExpressionCore(ae,string2print,ae->codeadr,0); + switch (hex) { + case 1: + if (zv&0xFFFFFF00) { + if (zv&0xFFFF0000) { + rasm_printf(ae,"#%-8.08X ",zv); + } else { + rasm_printf(ae,"#%-4.04X ",zv); + } + } else { + rasm_printf(ae,"#%-2.02X ",zv); + } + break; + case 2:rasm_printf(ae,"#%-2.02X ",zv);break; + case 4:rasm_printf(ae,"#%-4.04X ",zv);break; + case 8:rasm_printf(ae,"#%-8.08X ",zv);break; + } + } else if (bin) { + int zv,d; + zv=RoundComputeExpressionCore(ae,string2print,ae->codeadr,0); + /* remove useless sign bits */ + if (bin<32 && (zv&0xFFFF0000)==0xFFFF0000) { + zv&=0xFFFF; + } + switch (bin) { + case 1:if (zv&0xFF00) d=15; else d=7;break; + case 8:d=7;break; + case 16:d=15;break; + case 32:d=31;break; + } + rasm_printf(ae,"%%"); + for (;d>=0;d--) { + if ((zv>>d)&1) rasm_printf(ae,"1"); else rasm_printf(ae,"0"); + } + rasm_printf(ae," "); + } else if (entier) { + rasm_printf(ae,"%d ",(int)RoundComputeExpressionCore(ae,string2print,ae->codeadr,0)); + } else { + rasm_printf(ae,"%.2lf ",ComputeExpressionCore(ae,string2print,ae->codeadr,0)); + } + MemFree(string2print); + } else { + char *varbuffer; + int lm,touched; + lm=strlen(ae->wl[ae->idx+1].w)-2; + if (lm) { + varbuffer=MemMalloc(lm+2); + sprintf(varbuffer,"%-*.*s ",lm,lm,ae->wl[ae->idx+1].w+1); + /* need to upper case tags */ + for (lm=touched=0;varbuffer[lm];lm++) { + if (varbuffer[lm]=='{') touched++; else if (varbuffer[lm]=='}') touched--; else if (touched) varbuffer[lm]=toupper(varbuffer[lm]); + } + /* translate tag will check tag consistency */ + varbuffer=TranslateTag(ae,varbuffer,&touched,1,E_TAGOPTION_REMOVESPACE); + varbuffer=TxtReplace(varbuffer,"\\b","\b",0); + varbuffer=TxtReplace(varbuffer,"\\v","\v",0); + varbuffer=TxtReplace(varbuffer,"\\f","\f",0); + varbuffer=TxtReplace(varbuffer,"\\r","\r",0); + varbuffer=TxtReplace(varbuffer,"\\n","\n",0); + varbuffer=TxtReplace(varbuffer,"\\t","\t",0); + rasm_printf(ae,"%s ",varbuffer); + MemFree(varbuffer); + } + } + ae->idx++; + } + rasm_printf(ae,"\n"); +} + +void __FAIL(struct s_assenv *ae) { + __PRINT(ae); + __STOP(ae); + MaxError(ae); +} + +void __ALIGN(struct s_assenv *ae) { + int aval,ifill=-1; + + if (ae->io) { + ae->orgzone[ae->io-1].memend=ae->outputadr; + } + if (!ae->wl[ae->idx].t) { + ExpressionFastTranslate(ae,&ae->wl[ae->idx+1].w,0); + aval=RoundComputeExpression(ae,ae->wl[ae->idx+1].w,ae->codeadr,0,0); + ae->idx++; + /* align with fill ? */ + if (!ae->wl[ae->idx].t) { + ExpressionFastTranslate(ae,&ae->wl[ae->idx+1].w,0); + ifill=RoundComputeExpression(ae,ae->wl[ae->idx+1].w,ae->codeadr,0,0); + ae->idx++; + if (ifill<0 || ifill>255) { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"ALIGN fill value must be 0 to 255\n"); + ifill=0; + } + } + + if (aval<1 || aval>65535) { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"ALIGN boundary must be greater than zero and lower than 65536\n"); + aval=1; + } + + /* touch codeadr only if adress is misaligned */ + if (ae->codeadr%aval) { + if (ifill==-1) { + /* virtual ALIGN is moving outputadr the same value as codeadr move */ + ae->outputadr=ae->outputadr-(ae->codeadr%aval)+aval; + ae->codeadr=ae->codeadr-(ae->codeadr%aval)+aval; + } else { + /* physical ALIGN fill bytes */ + while (ae->codeadr%aval) { + ___output(ae,ifill); + ae->nop+=1; + } + } + } + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"ALIGN <boundary>[,fill] directive need one or two integers parameters\n"); + } +} + +void ___internal_skip_loop_block(struct s_assenv *ae, int eloopstyle) { + int *loopstyle=NULL; + int iloop=0,mloop=0; + int cidx; + + cidx=ae->idx+2; + + IntArrayAddDynamicValueConcat(&loopstyle,&iloop,&mloop,eloopstyle); + /* look for WEND */ + while (iloop) { + if (strcmp(ae->wl[cidx].w,"REPEAT")==0) { + if (ae->wl[cidx].t) { + IntArrayAddDynamicValueConcat(&loopstyle,&iloop,&mloop,E_LOOPSTYLE_REPEATUNTIL); + } else if (ae->wl[cidx+1].t) { + IntArrayAddDynamicValueConcat(&loopstyle,&iloop,&mloop,E_LOOPSTYLE_REPEATN); + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Invalid REPEAT syntax\n"); + } + } else if (strcmp(ae->wl[cidx].w,"WHILE")==0) { + if (!ae->wl[cidx].t && ae->wl[cidx+1].t) { + IntArrayAddDynamicValueConcat(&loopstyle,&iloop,&mloop,E_LOOPSTYLE_WHILE); + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Invalid WHILE syntax\n"); + } + } else if (strcmp(ae->wl[cidx].w,"WEND")==0) { + iloop--; + if (iloop<0) { + iloop=0; + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"WEND encountered that was not expected\n"); + } else if (loopstyle[iloop]!=E_LOOPSTYLE_WHILE) { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"WEND encountered but expecting %s\n",loopstyle[iloop]==E_LOOPSTYLE_REPEATN?"REND":"UNTIL"); + } + } else if (strcmp(ae->wl[cidx].w,"REND")==0) { + iloop--; + if (iloop<0) { + iloop=0; + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"REND encountered that was not expected\n"); + } else if (loopstyle[iloop]!=E_LOOPSTYLE_REPEATN) { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"REND encountered but expecting %s\n",loopstyle[iloop]==E_LOOPSTYLE_REPEATUNTIL?"UNTIL":"WEND"); + } + } else if (strcmp(ae->wl[cidx].w,"UNTIL")==0) { + iloop--; + if (iloop<0) { + iloop=0; + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"UNTIL encountered that was not expected\n"); + } else if (loopstyle[iloop]!=E_LOOPSTYLE_REPEATUNTIL) { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"UNTIL encountered but expecting %s\n",loopstyle[iloop]==E_LOOPSTYLE_REPEATN?"REND":"WEND"); + } + } + while (!ae->wl[cidx].t) cidx++; + cidx++; + } + MemFree(loopstyle); + ae->idx=cidx-1; +} + +void __WHILE(struct s_assenv *ae) { + struct s_whilewend whilewend={0}; + + if (!ae->wl[ae->idx].t && ae->wl[ae->idx+1].t==1) { + ExpressionFastTranslate(ae,&ae->wl[ae->idx+1].w,0); + if (!ComputeExpression(ae,ae->wl[ae->idx+1].w,ae->codeadr,0,2)) { + /* skip while block */ + ___internal_skip_loop_block(ae,E_LOOPSTYLE_WHILE); + return; + } else { + ae->idx++; + whilewend.start=ae->idx; + whilewend.cpt=0; + whilewend.value=ae->whilecounter; + whilewend.maxim=ae->imacropos; + whilewend.while_counter=1; + ae->whilecounter++; + /* pour gérer les macros situés dans le while précedent après un repeat/while courant */ + if (ae->iw) whilewend.maxim=ae->whilewend[ae->iw-1].maxim; + if (ae->ir && ae->repeat[ae->ir-1].maxim>whilewend.maxim) whilewend.maxim=ae->repeat[ae->ir-1].maxim; + ObjectArrayAddDynamicValueConcat((void**)&ae->whilewend,&ae->iw,&ae->mw,&whilewend,sizeof(whilewend)); + } + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is WHILE <expression>\n"); + } +} +void __WEND(struct s_assenv *ae) { + if (ae->iw>0) { + if (ae->wl[ae->idx].t==1) { + if (ComputeExpression(ae,ae->wl[ae->whilewend[ae->iw-1].start].w,ae->codeadr,0,2)) { + if (ae->whilewend[ae->iw-1].while_counter>65536) { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Bypass infinite WHILE loop\n"); + ae->iw--; + /* refresh macro check index */ + if (ae->iw) ae->imacropos=ae->whilewend[ae->iw-1].maxim; + } else { + ae->whilewend[ae->iw-1].cpt++; /* for local label */ + ae->whilewend[ae->iw-1].while_counter++; + ae->idx=ae->whilewend[ae->iw-1].start; + /* refresh macro check index */ + ae->imacropos=ae->whilewend[ae->iw-1].maxim; + } + } else { + ae->iw--; + /* refresh macro check index */ + if (ae->iw) ae->imacropos=ae->whilewend[ae->iw-1].maxim; + } + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"WEND does not need any parameter\n"); + } + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"WEND encounter whereas there is no referent WHILE\n"); + } +} + +void __REPEAT(struct s_assenv *ae) { + struct s_repeat currepeat={0}; + struct s_expr_dico *rvar; + int *loopstyle; + int iloop,mloop; + int cidx,crc; + + if (ae->wl[ae->idx+1].t!=2) { + if (ae->wl[ae->idx].t==0) { + ExpressionFastTranslate(ae,&ae->wl[ae->idx+1].w,0); + currepeat.cpt=RoundComputeExpression(ae,ae->wl[ae->idx+1].w,0,0,0); + if (!currepeat.cpt) { + /* skip repeat block */ + ___internal_skip_loop_block(ae,E_LOOPSTYLE_REPEATN); + return; + } else if (currepeat.cpt<1 || currepeat.cpt>65536) { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Repeat value (%d) must be from 1 to 65535. Skipping block\n",currepeat.cpt); + ___internal_skip_loop_block(ae,E_LOOPSTYLE_REPEATN); + return; + } + ae->idx++; + currepeat.start=ae->idx; + if (ae->wl[ae->idx].t==0) { + ae->idx++; + if (ae->wl[ae->idx].t==1) { + /* la variable peut exister -> OK */ + crc=GetCRC(ae->wl[ae->idx].w); + if ((rvar=SearchDico(ae,ae->wl[ae->idx].w,crc))!=NULL) { + rvar->v=1; + } else { + /* mais ne peut être un label ou un alias */ + ExpressionSetDicoVar(ae,ae->wl[ae->idx].w, 1); + } + currepeat.repeatvar=ae->wl[ae->idx].w; + currepeat.repeatcrc=crc; + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"extended syntax is REPEAT <n>,<var>\n"); + } + } + } else { + currepeat.start=ae->idx; + currepeat.cpt=-1; + } + currepeat.value=ae->repeatcounter; + currepeat.repeat_counter=1; + ae->repeatcounter++; + /* pour gérer les macros situés dans le repeat précedent après le repeat courant */ + if (ae->ir) currepeat.maxim=ae->repeat[ae->ir-1].maxim; + if (ae->iw && ae->whilewend[ae->iw-1].maxim>currepeat.maxim) currepeat.maxim=ae->whilewend[ae->iw-1].maxim; + if (ae->imacropos>currepeat.maxim) currepeat.maxim=ae->imacropos; + ObjectArrayAddDynamicValueConcat((void**)&ae->repeat,&ae->ir,&ae->mr,&currepeat,sizeof(currepeat)); + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"wrong REPEAT usage\n"); + } +} + +void __REND(struct s_assenv *ae) { + struct s_expr_dico *rvar; + if (ae->ir>0) { + if (ae->repeat[ae->ir-1].cpt==-1) { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"REND encounter whereas referent REPEAT was waiting for UNTIL\n"); + } else { + ae->repeat[ae->ir-1].cpt--; + ae->repeat[ae->ir-1].repeat_counter++; + if ((rvar=SearchDico(ae,ae->repeat[ae->ir-1].repeatvar,ae->repeat[ae->ir-1].repeatcrc))!=NULL) { + rvar->v=ae->repeat[ae->ir-1].repeat_counter; + } + if (ae->repeat[ae->ir-1].cpt) { + ae->idx=ae->repeat[ae->ir-1].start; + /* refresh macro check index */ + ae->imacropos=ae->repeat[ae->ir-1].maxim; + } else { + ae->ir--; + /* refresh macro check index */ + if (ae->ir) ae->imacropos=ae->repeat[ae->ir-1].maxim; + } + } + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"REND encounter whereas there is no referent REPEAT\n"); + } +} + +void __UNTIL(struct s_assenv *ae) { + if (ae->ir>0) { + if (ae->repeat[ae->ir-1].cpt>=0) { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"[%s:%d] UNTIL encounter whereas referent REPEAT n was waiting for REND\n"); + } else { + if (ae->wl[ae->idx].t==0 && ae->wl[ae->idx+1].t==1) { + ae->repeat[ae->ir-1].repeat_counter++; + ExpressionFastTranslate(ae,&ae->wl[ae->idx+1].w,0); + if (!ComputeExpression(ae,ae->wl[ae->idx+1].w,ae->codeadr,0,2)) { + if (ae->repeat[ae->ir-1].repeat_counter>65536) { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Bypass infinite REPEAT loop\n"); + ae->ir--; + /* refresh macro check index */ + if (ae->ir) ae->imacropos=ae->repeat[ae->ir-1].maxim; + } else { + ae->idx=ae->repeat[ae->ir-1].start; + ae->repeat[ae->ir-1].cpt--; /* for local label */ + /* refresh macro check index */ + ae->imacropos=ae->repeat[ae->ir-1].maxim; + } + } else { + ae->ir--; + /* refresh macro check index */ + if (ae->ir) ae->imacropos=ae->repeat[ae->ir-1].maxim; + } + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"UNTIL need one expression/evaluation as parameter\n"); + } + } + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"UNTIL encounter whereas there is no referent REPEAT\n"); + } +} + +void __ASSERT(struct s_assenv *ae) { + char Dot3[4]; + int rexpr; + + if (!ae->wl[ae->idx].t) { + ExpressionFastTranslate(ae,&ae->wl[ae->idx+1].w,0); + if (strlen(ae->wl[ae->idx+1].w)>29) strcpy(Dot3,"..."); else strcpy(Dot3,""); + rexpr=!!RoundComputeExpression(ae,ae->wl[ae->idx+1].w,ae->codeadr,0,1); + if (!rexpr) { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx+1].l,"ASSERT %.29s%s failed with ",ae->wl[ae->idx+1].w,Dot3); + ExpressionFastTranslate(ae,&ae->wl[ae->idx+1].w,1); + rasm_printf(ae,"%s\n",ae->wl[ae->idx+1].w); + if (!ae->wl[ae->idx+1].t) { + ae->idx++; + rasm_printf(ae,"-> "); + __PRINT(ae); + } + __STOP(ae); + } else { + while (!ae->wl[ae->idx].t) ae->idx++; + } + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"ASSERT need one expression\n"); + } +} + +void __IF(struct s_assenv *ae) { + struct s_ifthen ifthen={0}; + int rexpr; + + if (!ae->wl[ae->idx].t && ae->wl[ae->idx+1].t==1) { + ExpressionFastTranslate(ae,&ae->wl[ae->idx+1].w,0); + rexpr=!!RoundComputeExpression(ae,ae->wl[ae->idx+1].w,ae->codeadr,0,1); + ifthen.v=rexpr; + ifthen.filename=GetCurrentFile(ae); + ifthen.line=ae->wl[ae->idx].l; + ifthen.type=E_IFTHEN_TYPE_IF; + ObjectArrayAddDynamicValueConcat((void **)&ae->ifthen,&ae->ii,&ae->mi,&ifthen,sizeof(ifthen)); + ae->idx++; + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"IF need one expression\n"); + } +} + +void __IF_light(struct s_assenv *ae) { + struct s_ifthen ifthen={0}; + + if (!ae->wl[ae->idx].t && ae->wl[ae->idx+1].t==1) { + /* do not need to compute the value in shadow execution */ + ifthen.v=0; + ifthen.filename=GetCurrentFile(ae); + ifthen.line=ae->wl[ae->idx].l; + ifthen.type=E_IFTHEN_TYPE_IF; + ObjectArrayAddDynamicValueConcat((void **)&ae->ifthen,&ae->ii,&ae->mi,&ifthen,sizeof(ifthen)); + ae->idx++; + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"IF need one expression\n"); + } +} + +/* test if a label or a variable where used before */ +void __IFUSED(struct s_assenv *ae) { + struct s_ifthen ifthen={0}; + int rexpr,crc; + + if (!ae->wl[ae->idx].t && ae->wl[ae->idx+1].t==1) { + crc=GetCRC(ae->wl[ae->idx+1].w); + if ((SearchDico(ae,ae->wl[ae->idx+1].w,crc))!=NULL) { + rexpr=1; + } else { + if ((SearchLabel(ae,ae->wl[ae->idx+1].w,crc))!=NULL) { + rexpr=1; + } else { + if ((SearchAlias(ae,crc,ae->wl[ae->idx+1].w))!=-1) { + rexpr=1; + } else { + rexpr=SearchUsed(ae,ae->wl[ae->idx+1].w,crc); + } + } + } + ifthen.v=rexpr; + ifthen.filename=GetCurrentFile(ae); + ifthen.line=ae->wl[ae->idx].l; + ifthen.type=E_IFTHEN_TYPE_IFUSED; + ObjectArrayAddDynamicValueConcat((void **)&ae->ifthen,&ae->ii,&ae->mi,&ifthen,sizeof(ifthen)); + ae->idx++; + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"IFUSED need one variable or label\n"); + } +} +void __IFNUSED(struct s_assenv *ae) { + __IFUSED(ae); + ae->ifthen[ae->ii-1].v=1-ae->ifthen[ae->ii-1].v; + ae->ifthen[ae->ii-1].type=E_IFTHEN_TYPE_IFNUSED; +} +void __IFUSED_light(struct s_assenv *ae) { + struct s_ifthen ifthen={0}; + + if (!ae->wl[ae->idx].t && ae->wl[ae->idx+1].t==1) { + ifthen.v=0; + ifthen.filename=GetCurrentFile(ae); + ifthen.line=ae->wl[ae->idx].l; + ifthen.type=E_IFTHEN_TYPE_IFUSED; + ObjectArrayAddDynamicValueConcat((void **)&ae->ifthen,&ae->ii,&ae->mi,&ifthen,sizeof(ifthen)); + ae->idx++; + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"IFUSED need one variable or label\n"); + } +} +void __IFNUSED_light(struct s_assenv *ae) { + __IFUSED_light(ae); + ae->ifthen[ae->ii-1].type=E_IFTHEN_TYPE_IFNUSED; +} + +/* test if a label or a variable exists */ +void __IFDEF(struct s_assenv *ae) { + struct s_ifthen ifthen={0}; + int rexpr,crc; + + if (!ae->wl[ae->idx].t && ae->wl[ae->idx+1].t==1) { + crc=GetCRC(ae->wl[ae->idx+1].w); + if ((SearchDico(ae,ae->wl[ae->idx+1].w,crc))!=NULL) { + rexpr=1; + } else { + if ((SearchLabel(ae,ae->wl[ae->idx+1].w,crc))!=NULL) { + rexpr=1; + } else { + if ((SearchAlias(ae,crc,ae->wl[ae->idx+1].w))!=-1) { + rexpr=1; + } else { + if (SearchMacro(ae,crc,ae->wl[ae->idx+1].w)>=0) { + rexpr=1; + } else { + rexpr=0; + } + } + } + } + ifthen.v=rexpr; + ifthen.filename=GetCurrentFile(ae); + ifthen.line=ae->wl[ae->idx].l; + ifthen.type=E_IFTHEN_TYPE_IFDEF; + ObjectArrayAddDynamicValueConcat((void **)&ae->ifthen,&ae->ii,&ae->mi,&ifthen,sizeof(ifthen)); + ae->idx++; + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"IFDEF need one variable or label\n"); + } +} +void __IFDEF_light(struct s_assenv *ae) { + struct s_ifthen ifthen={0}; + + if (!ae->wl[ae->idx].t && ae->wl[ae->idx+1].t==1) { + ifthen.v=0; + ifthen.filename=GetCurrentFile(ae); + ifthen.line=ae->wl[ae->idx].l; + ifthen.type=E_IFTHEN_TYPE_IFDEF; + ObjectArrayAddDynamicValueConcat((void **)&ae->ifthen,&ae->ii,&ae->mi,&ifthen,sizeof(ifthen)); + ae->idx++; + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"GetCurrentFile(ae),ae->wl[ae->idx].l[%s:%d] FATAL - IFDEF need one variable or label\n"); + } +} +void __IFNDEF(struct s_assenv *ae) { + struct s_expr_dico *curdic=NULL; + struct s_label *curlabel=NULL; + struct s_ifthen ifthen={0}; + int rexpr,crc; + + if (!ae->wl[ae->idx].t && ae->wl[ae->idx+1].t==1) { + crc=GetCRC(ae->wl[ae->idx+1].w); + if ((SearchDico(ae,ae->wl[ae->idx+1].w,crc))!=NULL) { + rexpr=0; + } else { + if ((SearchLabel(ae,ae->wl[ae->idx+1].w,crc))!=NULL) { + rexpr=0; + } else { + if ((SearchAlias(ae,crc,ae->wl[ae->idx+1].w))!=-1) { + rexpr=0; + } else { + if (SearchMacro(ae,crc,ae->wl[ae->idx+1].w)>=0) { + rexpr=0; + } else { + rexpr=1; + } + } + } + } + ifthen.v=rexpr; + ifthen.filename=GetCurrentFile(ae); + ifthen.line=ae->wl[ae->idx].l; + ifthen.type=E_IFTHEN_TYPE_IFNDEF; + ObjectArrayAddDynamicValueConcat((void **)&ae->ifthen,&ae->ii,&ae->mi,&ifthen,sizeof(ifthen)); + ae->idx++; + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"IFNDEF need one variable or label\n"); + } +} +void __IFNDEF_light(struct s_assenv *ae) { + struct s_expr_dico *curdic=NULL; + struct s_label *curlabel=NULL; + struct s_ifthen ifthen={0}; + + if (!ae->wl[ae->idx].t && ae->wl[ae->idx+1].t==1) { + ifthen.v=0; + ifthen.filename=GetCurrentFile(ae); + ifthen.line=ae->wl[ae->idx].l; + ifthen.type=E_IFTHEN_TYPE_IFNDEF; + ObjectArrayAddDynamicValueConcat((void **)&ae->ifthen,&ae->ii,&ae->mi,&ifthen,sizeof(ifthen)); + ae->idx++; + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"IFNDEF need one variable or label\n",GetCurrentFile(ae),ae->wl[ae->idx].l); + } +} + +void __UNDEF(struct s_assenv *ae) { + + if (!ae->wl[ae->idx].t && ae->wl[ae->idx+1].t==1) { + /* no error when the variable to UNDEF does not exist */ + DelDico(ae,ae->wl[ae->idx+1].w,GetCRC(ae->wl[ae->idx+1].w)); + ae->idx++; + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"syntax is UNDEF <variable>\n"); + } + +} + + +void __SWITCH(struct s_assenv *ae) { + struct s_switchcase curswitch={0}; + + if (!ae->wl[ae->idx].t && ae->wl[ae->idx+1].t==1) { + /* switch store the value */ + ExpressionFastTranslate(ae,&ae->wl[ae->idx+1].w,0); + curswitch.refval=RoundComputeExpression(ae,ae->wl[ae->idx+1].w,ae->codeadr,0,1); + ObjectArrayAddDynamicValueConcat((void**)&ae->switchcase,&ae->isw,&ae->msw,&curswitch,sizeof(curswitch)); + ae->idx++; + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"SWITCH need one expression\n"); + } +} +void __CASE(struct s_assenv *ae) { + int rexpr; + + if (ae->isw) { + if (!ae->wl[ae->idx].t && ae->wl[ae->idx+1].t==1) { + ExpressionFastTranslate(ae,&ae->wl[ae->idx+1].w,0); + rexpr=RoundComputeExpression(ae,ae->wl[ae->idx+1].w,ae->codeadr,0,1); + + if (ae->switchcase[ae->isw-1].refval==rexpr) { + ae->switchcase[ae->isw-1].execute=1; + ae->switchcase[ae->isw-1].casematch=1; + } + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"CASE not need one parameter\n"); + } + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"CASE encounter whereas there is no referent SWITCH\n"); + } +} +void __DEFAULT(struct s_assenv *ae) { + + if (ae->isw) { + if (ae->wl[ae->idx].t==1) { + /* aucun match avant, on active, sinon on laisse tel quel */ + if (!ae->switchcase[ae->isw-1].casematch) { + ae->switchcase[ae->isw-1].execute=1; + } + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"DEFAULT do not need parameter\n"); + } + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"DEFAULT encounter whereas there is no referent SWITCH\n"); + } +} +void __BREAK(struct s_assenv *ae) { + + if (ae->isw) { + if (ae->wl[ae->idx].t==1) { + ae->switchcase[ae->isw-1].execute=0; + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"BREAK do not need parameter\n"); + } + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"BREAK encounter whereas there is no referent SWITCH\n"); + } +} +void __SWITCH_light(struct s_assenv *ae) { + struct s_switchcase curswitch={0}; + + if (!ae->wl[ae->idx].t && ae->wl[ae->idx+1].t==1) { + /* shadow execution */ + curswitch.refval=0; + curswitch.execute=0; + ObjectArrayAddDynamicValueConcat((void**)&ae->switchcase,&ae->isw,&ae->msw,&curswitch,sizeof(curswitch)); + ae->idx++; + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"SWITCH need one expression\n"); + } +} +void __CASE_light(struct s_assenv *ae) { + if (ae->isw) { + /* shadowed execution */ + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"CASE encounter whereas there is no referent SWITCH\n"); + } +} +void __DEFAULT_light(struct s_assenv *ae) { + + if (ae->isw) { + /* shadowed execution */ + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"DEFAULT encounter whereas there is no referent SWITCH\n"); + } +} +void __BREAK_light(struct s_assenv *ae) { + if (ae->isw) { + /* shadowed execution */ + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"BREAK encounter whereas there is no referent SWITCH\n"); + } +} +void __ENDSWITCH(struct s_assenv *ae) { + if (ae->isw) { + if (ae->wl[ae->idx].t==1) { + ae->isw--; + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"ENDSWITCH does not need any parameter\n"); + } + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"ENDSWITCH encounter whereas there is no referent SWITCH\n"); + } +} + +void __IFNOT(struct s_assenv *ae) { + struct s_ifthen ifthen={0}; + int rexpr; + + if (!ae->wl[ae->idx].t && ae->wl[ae->idx+1].t==1) { + ExpressionFastTranslate(ae,&ae->wl[ae->idx+1].w,0); + rexpr=!RoundComputeExpression(ae,ae->wl[ae->idx+1].w,ae->codeadr,0,1); + ifthen.v=rexpr; + ifthen.filename=GetCurrentFile(ae); + ifthen.line=ae->wl[ae->idx].l; + ifthen.type=E_IFTHEN_TYPE_IFNOT; + ObjectArrayAddDynamicValueConcat((void **)&ae->ifthen,&ae->ii,&ae->mi,&ifthen,sizeof(ifthen)); + //IntArrayAddDynamicValueConcat(&ae->ifthen,&ae->ii,&ae->mi,rexpr); + ae->idx++; + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"IFNOT need one expression\n"); + } +} +void __IFNOT_light(struct s_assenv *ae) { + struct s_ifthen ifthen={0}; + + if (!ae->wl[ae->idx].t && ae->wl[ae->idx+1].t==1) { + ifthen.v=0; + ifthen.filename=GetCurrentFile(ae); + ifthen.line=ae->wl[ae->idx].l; + ifthen.type=E_IFTHEN_TYPE_IFNOT; + ObjectArrayAddDynamicValueConcat((void **)&ae->ifthen,&ae->ii,&ae->mi,&ifthen,sizeof(ifthen)); + //IntArrayAddDynamicValueConcat(&ae->ifthen,&ae->ii,&ae->mi,rexpr); + ae->idx++; + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"IFNOT need one expression\n"); + } +} + +void __ELSE(struct s_assenv *ae) { + if (ae->ii) { + if (ae->wl[ae->idx].t==1) { + /* ELSE a executer seulement si celui d'avant est a zero */ + switch (ae->ifthen[ae->ii-1].v) { + case -1:break; + case 0:ae->ifthen[ae->ii-1].v=1;break; + case 1:ae->ifthen[ae->ii-1].v=0;break; + } + ae->ifthen[ae->ii-1].type=E_IFTHEN_TYPE_ELSE; + ae->ifthen[ae->ii-1].line=ae->wl[ae->idx].l; + ae->ifthen[ae->ii-1].filename=GetCurrentFile(ae); + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"ELSE does not need any parameter\n"); + } + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"ELSE encounter whereas there is no referent IF\n"); + } +} +void __ELSEIF(struct s_assenv *ae) { + + if (!ae->wl[ae->idx].t && ae->wl[ae->idx+1].t==1) { + ae->ifthen[ae->ii-1].type=E_IFTHEN_TYPE_ELSEIF; + ae->ifthen[ae->ii-1].line=ae->wl[ae->idx].l; + ae->ifthen[ae->ii-1].filename=GetCurrentFile(ae); + if (ae->ifthen[ae->ii-1].v) { + /* il faut signifier aux suivants qu'on va jusqu'au ENDIF */ + ae->ifthen[ae->ii-1].v=-1; + } else { + ExpressionFastTranslate(ae,&ae->wl[ae->idx+1].w,0); + ae->ifthen[ae->ii-1].v=!!RoundComputeExpression(ae,ae->wl[ae->idx+1].w,ae->codeadr,0,1); + } + ae->idx++; + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"ELSEIF need one expression\n"); + } +} +void __ELSEIF_light(struct s_assenv *ae) { + + if (!ae->wl[ae->idx].t && ae->wl[ae->idx+1].t==1) { + ae->ifthen[ae->ii-1].type=E_IFTHEN_TYPE_ELSEIF; + ae->ifthen[ae->ii-1].line=ae->wl[ae->idx].l; + ae->ifthen[ae->ii-1].filename=GetCurrentFile(ae); + if (ae->ifthen[ae->ii-1].v) { + /* il faut signifier aux suivants qu'on va jusqu'au ENDIF */ + ae->ifthen[ae->ii-1].v=-1; + } else { + ae->ifthen[ae->ii-1].v=0; + } + ae->idx++; + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"ELSEIF need one expression\n"); + } +} +void __ENDIF(struct s_assenv *ae) { + if (ae->ii) { + if (ae->wl[ae->idx].t==1) { + ae->ii--; + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"ENDIF does not need any parameter\n"); + } + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"ENDIF encounter whereas there is no referent IF\n"); + } +} + +void __internal_PROTECT(struct s_assenv *ae, int memstart, int memend) { + struct s_orgzone orgzone={0}; + + /* add a fake ORG zone */ + ObjectArrayAddDynamicValueConcat((void**)&ae->orgzone,&ae->io,&ae->mo,&orgzone,sizeof(orgzone)); + /* then switch it with the current ORG */ + orgzone=ae->orgzone[ae->io-2]; + ae->orgzone[ae->io-2].memstart=memstart; + ae->orgzone[ae->io-2].memend=memend; + ae->orgzone[ae->io-2].ibank=ae->activebank; + ae->orgzone[ae->io-2].protect=1; + ae->orgzone[ae->io-1]=orgzone; +} + +void __PROTECT(struct s_assenv *ae) { + int memstart,memend; + + if (!ae->wl[ae->idx].t && !ae->wl[ae->idx+1].t && ae->wl[ae->idx+2].t==1) { + ExpressionFastTranslate(ae,&ae->wl[ae->idx+1].w,0); + ExpressionFastTranslate(ae,&ae->wl[ae->idx+2].w,0); + memstart=RoundComputeExpression(ae,ae->wl[ae->idx+1].w,0,0,0); + memend=RoundComputeExpression(ae,ae->wl[ae->idx+2].w,0,0,0); + __internal_PROTECT(ae,memstart,memend); + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"PROTECT need two parameters: startadr,endadr\n"); + } +} + +void ___org_close(struct s_assenv *ae) { + if (ae->lz>=0) { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Cannot ORG inside a LZ section\n"); + return; + } + __internal_UpdateLZBlockIfAny(ae); + /* close current ORG */ + if (ae->io) { + ae->orgzone[ae->io-1].memend=ae->outputadr; + } +} + +void ___org_new(struct s_assenv *ae, int nocode) { + struct s_orgzone orgzone={0}; + int i; + + /* check current ORG request */ + for (i=0;i<ae->io;i++) { + /* aucun contrôle sur les ORG non écrits ou en NOCODE */ + if (ae->orgzone[i].memstart!=ae->orgzone[i].memend && !ae->orgzone[i].nocode) { + if (ae->orgzone[i].ibank==ae->activebank) { + if (ae->outputadr<ae->orgzone[i].memend && ae->outputadr>=ae->orgzone[i].memstart) { + if (ae->orgzone[i].protect) { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"ORG located a PROTECTED section [#%04X-#%04X-B%d] file [%s] line %d\n",ae->orgzone[i].memstart,ae->orgzone[i].memend,ae->orgzone[i].ibank<32?ae->orgzone[i].ibank:0,ae->filename[ae->orgzone[i].ifile],ae->orgzone[i].iline); + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"ORG (output at #%04X) located in a previous ORG section [#%04X-#%04X-B%d] file [%s] line %d\n",ae->outputadr,ae->orgzone[i].memstart,ae->orgzone[i].memend,ae->orgzone[i].ibank<32?ae->orgzone[i].ibank:0,ae->filename[ae->orgzone[i].ifile],ae->orgzone[i].iline); + } + } + } + } + } + + OverWriteCheck(ae); + /* if there was a crunch block before, now closed */ + if (ae->lz>=0) { + ae->lz=-1; + } + orgzone.memstart=ae->outputadr; + orgzone.ibank=ae->activebank; + orgzone.ifile=ae->wl[ae->idx].ifile; + orgzone.iline=ae->wl[ae->idx].l; + orgzone.nocode=ae->nocode=nocode; + + if (nocode) { + ___output=___internal_output_nocode; + } else { + ___output=___internal_output; + } + + ObjectArrayAddDynamicValueConcat((void**)&ae->orgzone,&ae->io,&ae->mo,&orgzone,sizeof(orgzone)); +} + +void __ORG(struct s_assenv *ae) { + ___org_close(ae); + + if (ae->wl[ae->idx+1].t!=2) { + ExpressionFastTranslate(ae,&ae->wl[ae->idx+1].w,0); + ae->codeadr=RoundComputeExpression(ae,ae->wl[ae->idx+1].w,ae->outputadr,0,0); + if (!ae->wl[ae->idx+1].t && ae->wl[ae->idx+2].t!=2) { + ExpressionFastTranslate(ae,&ae->wl[ae->idx+2].w,0); + ae->outputadr=RoundComputeExpression(ae,ae->wl[ae->idx+2].w,ae->outputadr,0,0); + ae->idx+=2; + } else { + ae->outputadr=ae->codeadr; + ae->idx++; + } + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"[%s:%d] ORG code location[,output location]\n"); + return; + } + + ___org_new(ae,ae->nocode); +} +void __NOCODE(struct s_assenv *ae) { + if (ae->wl[ae->idx].t==1) { + ___org_close(ae); + ae->codeadrbackup=ae->codeadr; + ae->outputadrbackup=ae->outputadr; + ___org_new(ae,1); + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"NOCODE directive does not need parameter\n"); + } +} +void __CODE(struct s_assenv *ae) { + if (!ae->wl[ae->idx].t) { + if (strcmp(ae->wl[ae->idx+1].w,"SKIP")==0) { + ___org_close(ae); + ae->codeadr=ae->codeadrbackup; + ae->outputadr=ae->outputadrbackup; + ___org_new(ae,1); + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"unknown parameter for CODE directive\n"); + } + ae->idx++; + } else if (ae->wl[ae->idx].t==1) { + ___org_close(ae); + ___org_new(ae,0); + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"CODE directive does not need parameter\n"); + } +} +void __STRUCT(struct s_assenv *ae) { + #undef FUNC + #define FUNC "__STRUCT" + struct s_rasmstructfield rasmstructfield={0}; + struct s_rasmstruct rasmstruct={0}; + struct s_rasmstruct rasmstructalias={0}; + struct s_label curlabel={0}; + int crc,i,j,irs; + /* filler */ + int localsize,cursize; + double zeval; + + if (!ae->wl[ae->idx].t) { + if (ae->wl[ae->idx+1].t) { + /************************************************** + s t r u c t u r e d e c l a r a t i o n + **************************************************/ + if (!ae->getstruct) { + /* cannot be an existing label or EQU (but variable ok) */ + crc=GetCRC(ae->wl[ae->idx+1].w); + if ((SearchLabel(ae,ae->wl[ae->idx+1].w,crc))!=NULL || (SearchAlias(ae,crc,ae->wl[ae->idx+1].w))!=-1) { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"STRUCT name must be different from existing labels ou aliases\n"); + } else { + ae->backup_filename=GetCurrentFile(ae); + ae->backup_line=ae->wl[ae->idx].l; + ae->backup_outputadr=ae->outputadr; + ae->backup_codeadr=ae->codeadr; + ae->getstruct=1; + /* STRUCT = NOCODE + ORG 0 */ + ___org_close(ae); + ae->codeadr=0; + ___org_new(ae,1); + /* create struct */ + rasmstruct.name=TxtStrDup(ae->wl[ae->idx+1].w); + rasmstruct.crc=GetCRC(rasmstruct.name); + ObjectArrayAddDynamicValueConcat((void **)&ae->rasmstruct,&ae->irasmstruct,&ae->mrasmstruct,&rasmstruct,sizeof(rasmstruct)); + ae->idx++; + + /* wrapper for data capture */ + instruction[ICRC_DEFB].makemnemo=_DEFB_struct;instruction[ICRC_DB].makemnemo=_DEFB_struct; + instruction[ICRC_DEFW].makemnemo=_DEFW_struct;instruction[ICRC_DW].makemnemo=_DEFW_struct; + instruction[ICRC_DEFI].makemnemo=_DEFI_struct; + instruction[ICRC_DEFR].makemnemo=_DEFR_struct;instruction[ICRC_DR].makemnemo=_DEFR_struct; + instruction[ICRC_DEFS].makemnemo=_DEFS_struct;instruction[ICRC_DS].makemnemo=_DEFS_struct; + } + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"STRUCT cannot be declared inside previous opened STRUCT [%s] Line %d\n",ae->backup_filename,ae->backup_line); + } + } else { + /************************************************** + s t r u c t u r e i n s e r t i o n + **************************************************/ + int nbelem=1; +#if TRACE_STRUCT +printf("structure insertion\n"); +#endif + /* insert struct param1 in memory with name param2 */ + crc=GetCRC(ae->wl[ae->idx+1].w); + /* look for existing struct */ + for (irs=0;irs<ae->irasmstruct;irs++) { + if (ae->rasmstruct[irs].crc==crc && strcmp(ae->rasmstruct[irs].name,ae->wl[ae->idx+1].w)==0) break; + } + if (irs==ae->irasmstruct) { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Unknown STRUCT %s\n",ae->wl[ae->idx+1].w); + } else { + /* create alias for sizeof */ + if (!ae->getstruct) { + if (ae->wl[ae->idx+2].w[0]=='@') { + rasmstructalias.name=MakeLocalLabel(ae,ae->wl[ae->idx+2].w,NULL); + } else { + rasmstructalias.name=TxtStrDup(ae->wl[ae->idx+2].w); + } + } else { +#if TRACE_STRUCT +printf("struct [%s] inside struct\n",ae->wl[ae->idx+2].w); +#endif + /* struct inside struct */ + rasmstructalias.name=MemMalloc(strlen(ae->rasmstruct[ae->irasmstruct-1].name)+2+strlen(ae->wl[ae->idx+2].w)); + sprintf(rasmstructalias.name,"%s.%s",ae->rasmstruct[ae->irasmstruct-1].name,ae->wl[ae->idx+2].w); + } + rasmstructalias.crc=GetCRC(rasmstructalias.name); + rasmstructalias.size=ae->rasmstruct[irs].size; + rasmstructalias.ptr=ae->codeadr; +#if TRACE_STRUCT +printf("structalias [%s] ptr=%d size=%d\n",rasmstructalias.name,rasmstructalias.ptr,rasmstructalias.size); +#endif + /* extra parameter to declare an array? */ + if (!ae->wl[ae->idx+2].t && !StringIsQuote(ae->wl[ae->idx+3].w)) { + ExpressionFastTranslate(ae,&ae->wl[ae->idx+3].w,0); + nbelem=RoundComputeExpression(ae,ae->wl[ae->idx+3].w,ae->outputadr,0,0); + if (nbelem<1) { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Struct array need a positive number of elements!\n"); + nbelem=1; + } + ae->idx++; + } + rasmstructalias.nbelem=nbelem; +#if TRACE_STRUCT +printf("EVOL 119 - tableau! %d elem(s)\n",nbelem); +#endif + ObjectArrayAddDynamicValueConcat((void **)&ae->rasmstructalias,&ae->irasmstructalias,&ae->mrasmstructalias,&rasmstructalias,sizeof(rasmstructalias)); + + /* create label for global struct ptr */ + curlabel.iw=-1; + curlabel.ptr=ae->codeadr; + if (!ae->getstruct) { + if (ae->wl[ae->idx+2].w[0]=='@') curlabel.name=MakeLocalLabel(ae,ae->wl[ae->idx+2].w,NULL); else curlabel.name=TxtStrDup(ae->wl[ae->idx+2].w); + curlabel.crc=GetCRC(curlabel.name); + PushLabelLight(ae,&curlabel); + } else { + /* or check for non-local name in struct declaration */ + if (ae->wl[ae->idx+2].w[0]=='@') { + MakeError(ae,GetCurrentFile(ae),GetExpLine(ae,0),"Meaningless use of local label in a STRUCT definition\n"); + } else { + curlabel.name=TxtStrDup(rasmstructalias.name); + curlabel.crc=GetCRC(curlabel.name); + PushLabelLight(ae,&curlabel); + } + } + + /* first field is in fact the very beginning of the structure */ + if (ae->getstruct) { + rasmstructfield.name=TxtStrDup(ae->wl[ae->idx+2].w); + rasmstructfield.offset=ae->codeadr; + ObjectArrayAddDynamicValueConcat((void **)&ae->rasmstruct[ae->irasmstruct-1].rasmstructfield, + &ae->rasmstruct[ae->irasmstruct-1].irasmstructfield,&ae->rasmstruct[ae->irasmstruct-1].mrasmstructfield, + &rasmstructfield,sizeof(rasmstructfield)); + } + + /* create subfields */ +#if TRACE_STRUCT +printf("create subfields\n"); +#endif + curlabel.iw=-1; + curlabel.ptr=ae->codeadr; + for (i=0;i<ae->rasmstruct[irs].irasmstructfield;i++) { + curlabel.ptr=ae->codeadr+ae->rasmstruct[irs].rasmstructfield[i].offset; + if (!ae->getstruct) { + curlabel.name=MemMalloc(strlen(ae->wl[ae->idx+2].w)+strlen(ae->rasmstruct[irs].rasmstructfield[i].name)+2); + sprintf(curlabel.name,"%s.%s",ae->wl[ae->idx+2].w,ae->rasmstruct[irs].rasmstructfield[i].name); + if (ae->wl[ae->idx+2].w[0]=='@') { + char *newlabel; + newlabel=MakeLocalLabel(ae,curlabel.name,NULL); + MemFree(curlabel.name); + curlabel.name=newlabel; + } + curlabel.crc=GetCRC(curlabel.name); + PushLabelLight(ae,&curlabel); + /* are we using a struct in a struct definition? */ + } else { + /* copy structname+label+offset in the structure */ + rasmstructfield.name=MemMalloc(strlen(ae->wl[ae->idx+2].w)+strlen(ae->rasmstruct[irs].rasmstructfield[i].name)+2); + sprintf(rasmstructfield.name,"%s.%s",ae->wl[ae->idx+2].w,ae->rasmstruct[irs].rasmstructfield[i].name); + rasmstructfield.offset=curlabel.ptr; + ObjectArrayAddDynamicValueConcat((void **)&ae->rasmstruct[ae->irasmstruct-1].rasmstructfield, + &ae->rasmstruct[ae->irasmstruct-1].irasmstructfield,&ae->rasmstruct[ae->irasmstruct-1].mrasmstructfield, + &rasmstructfield,sizeof(rasmstructfield)); + + /* need to push also generic label */ + curlabel.name=MemMalloc(strlen(ae->rasmstruct[ae->irasmstruct-1].name)+strlen(rasmstructfield.name)+2); /* overwrite PTR */ + sprintf(curlabel.name,"%s.%s",ae->rasmstruct[ae->irasmstruct-1].name,rasmstructfield.name); + curlabel.crc=GetCRC(curlabel.name); + PushLabelLight(ae,&curlabel); + } + } + + /* is there any filler in the declaration? */ + localsize=0; + + /* déterminer si on est en remplissage par défaut ou remplissage surchargé */ + + + + + +#if TRACE_STRUCT +printf("struct new behaviour (scan for %d fields)\n",ae->rasmstruct[irs].irasmstructfield); +#endif +#if 0 + for (i=0;i<ae->rasmstruct[irs].irasmstructfield;i++) { + + if (!ae->wl[ae->idx+2+i].t || i+1>=ae->rasmstruct[irs].irasmstructfield) { + /* si le champ est sur le même offset que le précédent, on le saute */ + if (i && ae->rasmstruct[irs].rasmstructfield[i].offset>ae->rasmstruct[irs].rasmstructfield[i-1].offset) continue; + +#if TRACE_STRUCT +printf("get field? (%d)\n",irs); +#endif + if (!StringIsQuote(ae->wl[ae->idx+i].w)) { + ExpressionFastTranslate(ae,&ae->wl[ae->idx+i].w,1); + zeval=RoundComputeExpressionCore(ae,ae->wl[ae->idx+i].w,ae->codeadr,0); + } else { + // push string + } + + localsize+=ae->rasmstruct[irs].rasmstructfield[i].size; + + /* pour du single shot ? + pushbyte(s) at ae->codeadr+ae->rasmstruct[irs].rasmstructfield[i].offset + */ + + //ae->rasmstruct[irs].size; + + } else { +#if TRACE_STRUCT +printf("*break*\n"); +#endif + break; + } + } +#endif + + /* (LEGACY) filler, on balance des zéros */ +#if TRACE_STRUCT +printf("struct (almost) legacy filler from %d to %d-1\n",localsize,ae->rasmstruct[irs].size); +#endif + while (nbelem) { + for (i=cursize=0;i<ae->rasmstruct[irs].irasmstructfield && cursize<localsize;i++) { + cursize+=ae->rasmstruct[irs].rasmstructfield[i].size; + } + for (;i<ae->rasmstruct[irs].irasmstructfield;i++) { + for (j=0;j<ae->rasmstruct[irs].rasmstructfield[i].idata;j++) { + ___output(ae,ae->rasmstruct[irs].rasmstructfield[i].data[j]); + } + } + nbelem--; + } + +#if 0 + for (i=localsize;i<ae->rasmstruct[irs].size;i++) ___output(ae,0); +#endif + ae->idx+=2; // probablement à revoir dans le cas d'une init!!! + } + } + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"STRUCT directive needs one or two parameters\n"); + } +} +void __ENDSTRUCT(struct s_assenv *ae) { + #undef FUNC + #define FUNC "__ENDSTRUCT" + struct s_label curlabel={0}; + int i,newlen; +#if TRACE_STRUCT + printf("endstruct\n"); +#endif + + if (!ae->wl[ae->idx].t) { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"ENDSTRUCT directive does not need parameter\n"); + } else { + if (ae->getstruct) { + ae->rasmstruct[ae->irasmstruct-1].size=ae->codeadr; + ae->getstruct=0; + + /* SIZEOF like Vasm with struct name */ + curlabel.name=TxtStrDup(ae->rasmstruct[ae->irasmstruct-1].name); + curlabel.crc=ae->rasmstruct[ae->irasmstruct-1].crc; + curlabel.iw=-1; + curlabel.ptr=ae->rasmstruct[ae->irasmstruct-1].size; + PushLabelLight(ae,&curlabel); + + /* compute size for each field */ + newlen=strlen(ae->rasmstruct[ae->irasmstruct-1].name)+2; + for (i=0;i<ae->rasmstruct[ae->irasmstruct-1].irasmstructfield-1;i++) { + ae->rasmstruct[ae->irasmstruct-1].rasmstructfield[i].size=ae->rasmstruct[ae->irasmstruct-1].rasmstructfield[i+1].offset-ae->rasmstruct[ae->irasmstruct-1].rasmstructfield[i].offset; + ae->rasmstruct[ae->irasmstruct-1].rasmstructfield[i].fullname=MemMalloc(newlen+strlen(ae->rasmstruct[ae->irasmstruct-1].rasmstructfield[i].name)); + sprintf(ae->rasmstruct[ae->irasmstruct-1].rasmstructfield[i].fullname,"%s.%s",ae->rasmstruct[ae->irasmstruct-1].name,ae->rasmstruct[ae->irasmstruct-1].rasmstructfield[i].name); + ae->rasmstruct[ae->irasmstruct-1].rasmstructfield[i].crc=GetCRC(ae->rasmstruct[ae->irasmstruct-1].rasmstructfield[i].fullname); + } + ae->rasmstruct[ae->irasmstruct-1].rasmstructfield[i].size=ae->rasmstruct[ae->irasmstruct-1].size-ae->rasmstruct[ae->irasmstruct-1].rasmstructfield[i].offset; + ae->rasmstruct[ae->irasmstruct-1].rasmstructfield[i].fullname=MemMalloc(newlen+strlen(ae->rasmstruct[ae->irasmstruct-1].rasmstructfield[i].name)); + sprintf(ae->rasmstruct[ae->irasmstruct-1].rasmstructfield[i].fullname,"%s.%s",ae->rasmstruct[ae->irasmstruct-1].name,ae->rasmstruct[ae->irasmstruct-1].rasmstructfield[i].name); + ae->rasmstruct[ae->irasmstruct-1].rasmstructfield[i].crc=GetCRC(ae->rasmstruct[ae->irasmstruct-1].rasmstructfield[i].fullname); + + /* unwrap data capture */ + if (ae->as80==1) {/* not for UZ80 */ + instruction[ICRC_DEFB].makemnemo=_DEFB_as80;instruction[ICRC_DB].makemnemo=_DEFB_as80; + instruction[ICRC_DEFW].makemnemo=_DEFW_as80;instruction[ICRC_DW].makemnemo=_DEFW_as80; + instruction[ICRC_DEFI].makemnemo=_DEFI_as80; + } else { + instruction[ICRC_DEFB].makemnemo=_DEFB;instruction[ICRC_DB].makemnemo=_DEFB; + instruction[ICRC_DEFW].makemnemo=_DEFW;instruction[ICRC_DW].makemnemo=_DEFW; + instruction[ICRC_DEFI].makemnemo=_DEFI; + } + instruction[ICRC_DEFR].makemnemo=_DEFR;instruction[ICRC_DR].makemnemo=_DEFR; + instruction[ICRC_DEFS].makemnemo=_DEFS;instruction[ICRC_DS].makemnemo=_DEFS; + + /* like there was no byte */ + ae->outputadr=ae->backup_outputadr; + ae->codeadr=ae->backup_codeadr; + + ___org_close(ae); + ___org_new(ae,0); + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"ENDSTRUCT encountered outside STRUCT declaration\n"); + } + } +} + +void __MEMSPACE(struct s_assenv *ae) { + if (ae->wl[ae->idx].t) { + ___new_memory_space(ae); + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"MEMSPACE directive does not need parameter\n"); + } +} + +int (*_internal_getsample)(unsigned char *data, int *idx); +#undef FUNC +#define FUNC "_internal_AudioGetSampleValue" + +int __internal_getsample8(unsigned char *data, int *idx) { + unsigned char v; + v=data[*idx]-128;*idx=*idx+1;return v; +} +int __internal_getsample16little(unsigned char *data, int *idx) { + int cursample; + cursample=data[*idx+1]-0x80;*idx=*idx+2; + return cursample; +} +int __internal_getsample24little(unsigned char *data, int *idx) { + int cursample; + cursample=data[*idx+2-0x80];*idx=*idx+3; + return cursample; +} +/* big-endian */ +int __internal_getsample16big(unsigned char *data, int *idx) { + int cursample; + cursample=data[*idx]-0x80;*idx=*idx+2; + return cursample; +} +int __internal_getsample24big(unsigned char *data, int *idx) { + int cursample; + cursample=data[*idx]-0x80;*idx=*idx+3; + return cursample; +} +/* float & endian shit */ +int _isLittleEndian() /* from lz4.h */ +{ + const union { U32 u; BYTE c[4]; } one = { 1 }; + return one.c[0]; +} + +unsigned char * __internal_floatinversion(unsigned char *data) { + static unsigned char bswap[4]; + bswap[0]=data[3]; + bswap[1]=data[2]; + bswap[2]=data[1]; + bswap[3]=data[0]; + return bswap; +} + +int __internal_getsample32bigbig(unsigned char *data, int *idx) { + float fsample; + int cursample; + fsample=*((float*)(data+*idx)); + *idx=*idx+4; + cursample=(floor)((fsample+1.0)*127.5+0.5); + return cursample; +} +int __internal_getsample32biglittle(unsigned char *data, int *idx) { + float fsample; + int cursample; + fsample=*((float*)(__internal_floatinversion(data+*idx))); + *idx=*idx+4; + cursample=(floor)((fsample+1.0)*127.5+0.5); + return cursample; +} + +#define __internal_getsample32littlelittle __internal_getsample32bigbig +#define __internal_getsample32littlebig __internal_getsample32biglittle + + +void _AudioLoadSample(struct s_assenv *ae, unsigned char *data, int filesize, enum e_audio_sample_type sample_type, float normalize) +{ + #undef FUNC + #define FUNC "AudioLoadSample" + + struct s_wav_header *wav_header; + int i,j,n,idx,controlsize; + int nbchannel,bitspersample,nbsample; + int bigendian=0,cursample; + double accumulator; + unsigned char samplevalue=0, sampleprevious=0; + int samplerepeat=0,ipause; + + unsigned char *subchunk; + int subchunksize; + + if (filesize<sizeof(struct s_wav_header)) { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"WAV import - this file is too small to be a valid WAV!\n"); + return; + } + + wav_header=(struct s_wav_header *)data; + +#if TRACE_HEXBIN +printf("AudioLoadSample filesize=%d st=%d normalize=%.2lf\n",filesize,sample_type,normalize); +#endif + if (strncmp(wav_header->ChunkID,"RIFF",4)) { + if (strncmp(wav_header->ChunkID,"RIFX",4)) { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"WAV import - unsupported audio sample type (chunkid must be 'RIFF' or 'RIFX')\n"); + return; + } else { + bigendian=1; + } + } + if (strncmp(wav_header->Format,"WAVE",4)) { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"[%s:%d] WAV import - unsupported audio sample type (format must be 'WAVE')\n"); + return; + } + controlsize=wav_header->SubChunk1Size[0]+wav_header->SubChunk1Size[1]*256+wav_header->SubChunk1Size[2]*65536+wav_header->SubChunk1Size[3]*256*65536; + if (controlsize!=16) { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"WAV import - invalid wav chunk size (subchunk1 control)\n"); + return; + } + if (strncmp(wav_header->SubChunk1ID,"fmt",3)) { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"WAV import - unsupported audio sample type (subchunk1id must be 'fmt')\n"); + return; + } + +#if TRACE_HEXBIN +printf("AudioLoadSample getsubchunk\n"); +#endif + subchunk=(unsigned char *)&wav_header->SubChunk2ID; + while (strncmp(subchunk,"data",4)) { + subchunksize=8+subchunk[4]+subchunk[5]*256+subchunk[6]*65536+subchunk[7]*256*65536; + if (subchunksize>=filesize) { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"WAV import - data subchunk not found\n"); + return; + } + subchunk+=subchunksize; + } + subchunksize=subchunk[4]+subchunk[5]*256+subchunk[6]*65536+subchunk[7]*256*65536; + controlsize=subchunksize; + + nbchannel=wav_header->NumChannels[0]+wav_header->NumChannels[1]*256; + if (nbchannel<1) { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"WAV import - invalid number of audio channel\n"); + return; + } + bitspersample=wav_header->BitsPerSample[0]+wav_header->BitsPerSample[1]*256; +#if TRACE_HEXBIN +printf("AudioLoadSample bitpersample=%d\n",bitspersample); +#endif + switch (bitspersample) { + case 8:_internal_getsample=__internal_getsample8;break; + case 16:if (!bigendian) _internal_getsample=__internal_getsample16little; else _internal_getsample=__internal_getsample16big;break; + case 24:if (!bigendian) _internal_getsample=__internal_getsample24little; else _internal_getsample=__internal_getsample24big;break; + case 32:if (!bigendian) { + if (_isLittleEndian()) { + _internal_getsample=__internal_getsample32littlelittle; + } else { + _internal_getsample=__internal_getsample32littlebig; + } + } else { + if (_isLittleEndian()) { + _internal_getsample=__internal_getsample32biglittle; + } else { + _internal_getsample=__internal_getsample32bigbig; + } + } + break; + default: + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"WAV import - unsupported bits per sample (%d)\n",bitspersample); + return; + } + + nbsample=controlsize/nbchannel/(bitspersample/8); + if (controlsize+sizeof(struct s_wav_header)>filesize) { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"WAV import - cannot read %d byte%s of audio whereas the file is %d bytes big!\n",controlsize,controlsize>1?"s":"",filesize); + return; + } + +#if TRACE_HEXBIN +printf("nbsample=%d (sze=%d,chn=%d,bps=%d) st=%c\n",nbsample,controlsize,nbchannel,bitspersample,sample_type); +#endif + + idx=subchunk-data; + switch (sample_type) { + default: + case AUDIOSAMPLE_SMP: + for (i=0;i<nbsample;i++) { + /* downmixing */ + accumulator=0.0; + for (n=0;n<nbchannel;n++) { + accumulator+=_internal_getsample(data,&idx); + } + /* normalize */ + cursample=MinMaxInt(floor(((accumulator/nbchannel)*normalize)+0.5)+128,0,255); + + /* PSG levels */ + samplevalue=ae->psgfine[cursample]; + + /* output */ + ___output(ae,samplevalue); + } + break; + case AUDIOSAMPLE_SM2: + /* +1 pour éviter le segfault */ + for (i=0;i<nbsample+1;i+=2) { + for (j=0;j<2;j++) { + /* downmixing */ + accumulator=0.0; + for (n=0;n<nbchannel;n++) { + accumulator+=_internal_getsample(data,&idx); + } + /* normalize */ + cursample=MinMaxInt(floor(((accumulator/nbchannel)*normalize)+0.5)+128,0,255); + /* PSG levels & packing */ + samplevalue=(samplevalue<<4)+(ae->psgfine[cursample]); + } + + /* output */ + ___output(ae,samplevalue); + } + break; + case AUDIOSAMPLE_SM4: + /*** + SM4 format has two bits + bits -> PSG value + 00 -> 0 + 01 -> 13 + 10 -> 14 + 11 -> 15 + ***/ + /* +3 pour éviter le segfault */ + for (i=0;i<nbsample+3;i+=4) { + for (j=0;j<4;j++) { + /* downmixing */ + accumulator=0.0; + for (n=0;n<nbchannel;n++) { + accumulator+=_internal_getsample(data,&idx); + } + /* normalize */ + cursample=MinMaxInt(floor(((accumulator/nbchannel)*normalize)+0.5)+128,0,255); + /* PSG levels & packing */ + samplevalue=(samplevalue<<2)+(ae->psgtab[cursample]>>2); + } + /* output */ + ___output(ae,samplevalue); + } + break; + case AUDIOSAMPLE_DMA: + sampleprevious=255; + for (i=0;i<nbsample;i++) { + /* downmixing */ + accumulator=0.0; + for (n=0;n<nbchannel;n++) { + accumulator+=_internal_getsample(data,&idx); + } + /* normalize */ + cursample=MinMaxInt(floor(((accumulator/nbchannel)*normalize)+0.5)+128,0,255); + + /* PSG levels */ + samplevalue=ae->psgtab[cursample]; + + if (samplevalue==sampleprevious) { + samplerepeat++; + } else { + if (!samplerepeat) { + /* DMA output */ + ___output(ae,sampleprevious); + ___output(ae,0x0A); /* volume canal C */ + } else { + /* DMA pause */ + ___output(ae,sampleprevious); + ___output(ae,0x0A); /* volume canal C */ + while (samplerepeat) { + ipause=samplerepeat<4096?samplerepeat:4095; + ___output(ae,ipause&0xFF); + ___output(ae,0x10 | ((ipause>>8) &0xF)); /* pause */ + + samplerepeat-=4096; + if (samplerepeat<0) samplerepeat=0; + } + } + sampleprevious=samplevalue; + } + } + if (samplerepeat) { + /* DMA pause */ + ___output(ae,sampleprevious); + ___output(ae,0x0A); /* volume canal C */ + while (samplerepeat) { + ipause=samplerepeat<4096?samplerepeat:4095; + ___output(ae,ipause&0xFF); + ___output(ae,0x10 | ((ipause>>8) &0xF)); /* pause */ + + samplerepeat-=4096; + if (samplerepeat<0) samplerepeat=0; + } + } + ___output(ae,0); + ___output(ae,0x0A); /* volume canal C */ + ___output(ae,0x20); + ___output(ae,0x40); /* stop or reloop? */ + break; + } +} + +/* + meta fonction qui gère le INCBIN standard plus les variantes SMP et DMA +*/ +void __HEXBIN(struct s_assenv *ae) { + int hbinidx,overwritecheck=1,crc; + struct s_expr_dico *rvar; + unsigned int idx; + int size=0,offset=0; + float amplification=1.0; + int deload=0; + int vtiles=0,remap=0,revert=0; + int itiles=0,tilex; + + if (!ae->wl[ae->idx].t) { + ExpressionFastTranslate(ae,&ae->wl[ae->idx+1].w,1); + hbinidx=RoundComputeExpressionCore(ae,ae->wl[ae->idx+1].w,ae->codeadr,0); + if (hbinidx>ae->ih) { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"internal error with binary file import (index out of bounds)\n"); + return; + } +#if TRACE_HEXBIN +printf("Hexbin idx=[%s] filename=[%s]\n",ae->wl[ae->idx+1].w,ae->hexbin[hbinidx].filename); +#endif + + if (!ae->wl[ae->idx+1].t) { + if (strcmp("DSK",ae->wl[ae->idx+2].w)==0) { + /* import binary from DSK */ + } else if (strchr("SD",ae->wl[ae->idx+2].w[0]) && ae->wl[ae->idx+2].w[1]=='M' && + strchr("P24A",ae->wl[ae->idx+2].w[2]) && !ae->wl[ae->idx+2].w[3]) { + /* SMP,SM2,SM4,DMA */ + +#if TRACE_HEXBIN +printf("Hexbin -> %s\n",ae->wl[ae->idx+2].w); +#endif + if (!ae->wl[ae->idx+2].t) { + amplification=ComputeExpressionCore(ae,ae->wl[ae->idx+3].w,ae->codeadr,0); +#if TRACE_HEXBIN +printf("sample amplification=%.2lf\n",amplification); +#endif + } + + switch (ae->wl[ae->idx+2].w[2]) { + case 'P':_AudioLoadSample(ae,ae->hexbin[hbinidx].data,ae->hexbin[hbinidx].datalen, AUDIOSAMPLE_SMP,amplification);break; + case '2':_AudioLoadSample(ae,ae->hexbin[hbinidx].data,ae->hexbin[hbinidx].datalen, AUDIOSAMPLE_SM2,amplification);break; + case '4':_AudioLoadSample(ae,ae->hexbin[hbinidx].data,ae->hexbin[hbinidx].datalen, AUDIOSAMPLE_SM4,amplification);break; + case 'A':_AudioLoadSample(ae,ae->hexbin[hbinidx].data,ae->hexbin[hbinidx].datalen, AUDIOSAMPLE_DMA,amplification);break; + default:printf("warning remover\n");break; + } + ae->idx+=2; + return; + } else { + /* legacy binary file */ +#if TRACE_HEXBIN +printf("Hexbin legacy datalen=%d\n",ae->hexbin[hbinidx].datalen); +#endif + if (strcmp("REVERT",ae->wl[ae->idx+2].w)==0) { + /* revert data */ + if (!ae->wl[ae->idx+2].t) { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"INCBIN REVERT does not need extra parameters\n"); + } +#if TRACE_HEXBIN +printf(" -> REVERT loading\n"); +#endif + revert=1; + offset=size=0; // full file + ae->idx++; + + } else if (strcmp("REMAP",ae->wl[ae->idx+2].w)==0) { + /* reorder tiles data */ + if (!ae->wl[ae->idx+2].t) { + ExpressionFastTranslate(ae,&ae->wl[ae->idx+3].w,1); + remap=RoundComputeExpressionCore(ae,ae->wl[ae->idx+3].w,ae->codeadr,0); + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"INCBIN REMAP need a number of columns for reordering\n"); + } +#if TRACE_HEXBIN +printf(" -> REMAP loading\n"); +#endif + offset=size=0; // full file + ae->idx+=2; + + } else if (strcmp("ITILES",ae->wl[ae->idx+2].w)==0) { + /*** entrelace les tiles, besoin de hauteur et largeur de la tile ***/ + if (!ae->wl[ae->idx+2].t) { + ExpressionFastTranslate(ae,&ae->wl[ae->idx+3].w,1); + tilex=RoundComputeExpressionCore(ae,ae->wl[ae->idx+3].w,ae->codeadr,0); + itiles=1; + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"usage is INCBIN'file',ITILES,width\n"); + } +#if TRACE_HEXBIN +printf(" -> ITILES loading\n"); +#endif + offset=size=0; // full file + ae->idx+=2; + } else if (strcmp("VTILES",ae->wl[ae->idx+2].w)==0) { + /* import and reorder tiles */ + if (!ae->wl[ae->idx+2].t) { + ExpressionFastTranslate(ae,&ae->wl[ae->idx+3].w,1); + vtiles=RoundComputeExpressionCore(ae,ae->wl[ae->idx+3].w,ae->codeadr,0); + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"INCBIN VTILES need a number of lines for reordering\n"); + } +#if TRACE_HEXBIN +printf(" -> VTILES loading\n"); +#endif + offset=size=0; // full file + ae->idx+=2; + } else { + char *expwrk; + + expwrk=TxtStrDup(ae->wl[ae->idx+2].w); + ExpressionFastTranslate(ae,&expwrk,1); + offset=RoundComputeExpressionCore(ae,expwrk,ae->codeadr,0); + MemFree(expwrk); +#if TRACE_HEXBIN + printf("offset=%d\n",offset); +#endif + if (!ae->wl[ae->idx+2].t) { + if (ae->wl[ae->idx+3].w[0]) { + expwrk=TxtStrDup(ae->wl[ae->idx+3].w); + ExpressionFastTranslate(ae,&expwrk,1); + size=RoundComputeExpressionCore(ae,expwrk,ae->codeadr,0); + MemFree(expwrk); + } else { + size=0; + } +#if TRACE_HEXBIN + printf("size=%d\n",size); +#endif + if (size<-65535 || size>65536) { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"INCBIN invalid size\n"); + } + if (!ae->wl[ae->idx+3].t) { + if (ae->wl[ae->idx+4].w[0]) { + expwrk=TxtStrDup(ae->wl[ae->idx+4].w); + ExpressionFastTranslate(ae,&expwrk,1); + offset+=65536*RoundComputeExpressionCore(ae,expwrk,ae->codeadr,0); + MemFree(expwrk); + } + if (!ae->wl[ae->idx+4].t) { + if (strcmp(ae->wl[ae->idx+5].w,"OFF")==0) { + overwritecheck=0; + } else if (strcmp(ae->wl[ae->idx+5].w,"ON")==0) { + overwritecheck=1; +#if TRACE_HEXBIN + printf("mode OVERWRITE\n"); +#endif + } else if (ae->wl[ae->idx+5].w[0]) { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"INCBIN invalid overwrite value. Must be 'OFF' or 'ON'\n"); + } + if (!ae->wl[ae->idx+5].t) { + /* copy raw len to a (new) variable */ + crc=GetCRC(ae->wl[ae->idx+6].w); + if ((rvar=SearchDico(ae,ae->wl[ae->idx+6].w,crc))!=NULL) { + rvar->v=ae->hexbin[hbinidx].rawlen; + } else { + /* mais ne peut être un label ou un alias */ + ExpressionSetDicoVar(ae,ae->wl[ae->idx+6].w,ae->hexbin[hbinidx].rawlen); + } + ae->idx+=6; + } else { + ae->idx+=5; + } + } else { + ae->idx+=4; + } + } else { + ae->idx+=3; + } + } else { + ae->idx+=2; + } + } + } + } else { + ae->idx++; + } + + /* preprocessor cannot manage variables so here is the delayed load */ + if (ae->hexbin[hbinidx].datalen<0) { + struct s_hexbin *curhexbin; + char *newfilename; + int lm,touched; + unsigned char *newdata=NULL; + +#if TRACE_HEXBIN +printf("Hexbin -> as only the assembler know how to deal with var,\n"); +printf("we look for tags in the name of a file which were not found\n"); +#endif + curhexbin=&ae->hexbin[hbinidx]; + + newfilename=TxtStrDup(curhexbin->filename); + + /* need to upper case tags */ + for (lm=touched=0;newfilename[lm];lm++) { + if (newfilename[lm]=='{') touched++; else if (newfilename[lm]=='}') touched--; else if (touched) newfilename[lm]=toupper(newfilename[lm]); + } + /* on essaie d'interpréter le nom du fichier en dynamique */ + newfilename=TranslateTag(ae,newfilename,&touched,1,E_TAGOPTION_REMOVESPACE); + /* load */ + if (FileExists(newfilename)) { +#if TRACE_HEXBIN +printf("Hexbin -> surprise! we found the file!\n"); +#endif + curhexbin->rawlen=curhexbin->datalen=FileGetSize(newfilename); + curhexbin->data=MemMalloc(curhexbin->datalen*1.3+10); + if (FileReadBinary(newfilename,(char*)curhexbin->data,curhexbin->datalen)!=curhexbin->datalen) { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"read error on file [%s]\n",newfilename); + return; + } + FileReadBinaryClose(newfilename); + + switch (curhexbin->crunch) { + #ifndef NO_3RD_PARTIES + case 4: + newdata=LZ4_crunch(curhexbin->data,curhexbin->datalen,&curhexbin->datalen); + MemFree(curhexbin->data); + curhexbin->data=newdata; + #if TRACE_PREPRO + rasm_printf(ae,KVERBOSE"crunched with LZ4 into %d byte(s)\n",curhexbin->datalen); + #endif + break; + case 7: + { + size_t slzlen; + newdata=ZX7_compress(optimize(curhexbin->data, curhexbin->datalen), curhexbin->data, curhexbin->datalen, &slzlen); + curhexbin->datalen=slzlen; + MemFree(curhexbin->data); + curhexbin->data=newdata; + #if TRACE_PREPRO + rasm_printf(ae,KVERBOSE"crunched with ZX7 into %d byte(s)\n",curhexbin->datalen); + #endif + } + break; + case 8: + rasm_printf(ae,KWARNING"Exomizer is crunching %.1fkb this may take a while, be patient...\n",curhexbin->datalen/1024.0); + newdata=Exomizer_crunch(curhexbin->data,curhexbin->datalen,&curhexbin->datalen); + MemFree(curhexbin->data); + curhexbin->data=newdata; + #if TRACE_PREPRO + rasm_printf(ae,KVERBOSE"crunched with Exomizer into %d byte(s)\n",curhexbin->datalen); + #endif + break; + #endif + case 48: + newdata=LZ48_crunch(curhexbin->data,curhexbin->datalen,&curhexbin->datalen); + MemFree(curhexbin->data); + curhexbin->data=newdata; + #if TRACE_PREPRO + rasm_printf(ae,KVERBOSE"crunched with LZ48 into %d byte(s)\n",curhexbin->datalen); + #endif + break; + case 49: + newdata=LZ49_crunch(curhexbin->data,curhexbin->datalen,&curhexbin->datalen); + MemFree(curhexbin->data); + curhexbin->data=newdata; + #if TRACE_PREPRO + rasm_printf(ae,KVERBOSE"crunched with LZ49 into %d byte(s)\n",curhexbin->datalen); + #endif + break; + default:break; + } + deload=1; + } else { + /* still not found */ + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"file not found [%s]\n",newfilename); + return; + } + } + + if (ae->hexbin[hbinidx].datalen>0) { + if (hbinidx<ae->ih && hbinidx>=0) { + if (size<0) { +#if TRACE_HEXBIN +printf("taille négative %d -> conversion en %d\n",size,ae->hexbin[hbinidx].datalen+size); +#endif + size=ae->hexbin[hbinidx].datalen+size; + if (size<1) { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"INCBIN negative size is greater or equal to filesize\n"); + } + } + /* negative offset conversion */ + if (offset<0) { +#if TRACE_HEXBIN +printf("offset négatif %d -> conversion en %d\n",offset,ae->hexbin[hbinidx].datalen+offset); +#endif + offset=ae->hexbin[hbinidx].datalen+offset; + } + if (!size) { + if (!offset) { + size=ae->hexbin[hbinidx].datalen; + } else { + size=ae->hexbin[hbinidx].datalen-offset; + } +#if TRACE_HEXBIN +printf("taille nulle et offset=%d -> conversion en %d\n",offset,size); +#endif + } + if (size>ae->hexbin[hbinidx].datalen) { + rasm_printf(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"INCBIN size is greater than filesize\n"); + } else { + if (size+offset>ae->hexbin[hbinidx].datalen) { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"INCBIN size+offset is greater than filesize\n"); + } else { + if (revert) { + int p; + p=size-1; + while (p>=0) { + ___output(ae,ae->hexbin[hbinidx].data[p--]); + } + } else if (itiles) { + /* tiles data reordering */ + int tx,ty,it,width; + + if (size % (tilex*8)) { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"INCBIN ITILES cannot reorder tiles %d bytewidth with file of size %d\n",tilex,size); + } else { + it=0; + while (it<size) { + for (tx=0;tx<tilex;tx++) ___output(ae,ae->hexbin[hbinidx].data[it+tx+0*tilex]); + for (tx=tilex-1;tx>=0;tx--) ___output(ae,ae->hexbin[hbinidx].data[it+tx+1*tilex]); + for (tx=0;tx<tilex;tx++) ___output(ae,ae->hexbin[hbinidx].data[it+tx+3*tilex]); + for (tx=tilex-1;tx>=0;tx--) ___output(ae,ae->hexbin[hbinidx].data[it+tx+2*tilex]); + for (tx=0;tx<tilex;tx++) ___output(ae,ae->hexbin[hbinidx].data[it+tx+6*tilex]); + for (tx=tilex-1;tx>=0;tx--) ___output(ae,ae->hexbin[hbinidx].data[it+tx+7*tilex]); + for (tx=0;tx<tilex;tx++) ___output(ae,ae->hexbin[hbinidx].data[it+tx+5*tilex]); + for (tx=tilex-1;tx>=0;tx--) ___output(ae,ae->hexbin[hbinidx].data[it+tx+4*tilex]); + it+=tilex*8; + } + } + } else if (remap) { + /* tiles data reordering */ + int tx,ty,it,width; + + width=size/remap; + + if ((size % remap) || (remap*width>size)) { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"INCBIN REMAP cannot reorder %d columns%s with file of size %d\n",remap,remap>1?"s":"",size); + } else { + for (it=0;it<remap;it++) { + for (tx=0;tx<width;tx++) { + ___output(ae,ae->hexbin[hbinidx].data[it+tx*remap]); + } + } + } + + } else if (vtiles) { + /* tiles map reordering */ + int width,tilex,tiley; + + width=size/vtiles; + + if ((size % vtiles) || (vtiles*width>size)) { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"INCBIN VTILES cannot reorder %d line%s with file of size %d\n",vtiles,vtiles>1?"s":"",size); + } else { +#if TRACE_HEXBIN +printf("Hexbin -> re-tiling MAP! width=%d\n",width); +#endif + for (idx=tilex=tiley=0;idx<size;idx++) { + ___output(ae,ae->hexbin[hbinidx].data[tilex+tiley*width]); + tiley++; + if (tiley>=vtiles) { + tiley=0; + tilex++; + } + } + } + } else { + /* legacy HEXBIN */ + if (overwritecheck) { + for (idx=offset;idx<size+offset;idx++) { + ___output(ae,ae->hexbin[hbinidx].data[idx]); + } + } else { + ___org_close(ae); + ___org_new(ae,0); + for (idx=offset;idx<size+offset;idx++) { + ___output(ae,ae->hexbin[hbinidx].data[idx]); + } + /* hack to disable overwrite check */ + ae->orgzone[ae->io-1].nocode=2; + ___org_close(ae); + ___org_new(ae,0); + } + } + } + } + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"INTERNAL - HEXBIN refer to unknown structure\n"); + FreeAssenv(ae); + exit(2); + } + } + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"INTERNAL - HEXBIN need one HEX parameter\n"); + FreeAssenv(ae); + exit(2); + } + + /* generated names must be reloaded! */ + if (deload) { + ae->hexbin[hbinidx].datalen=-1; + MemFree(ae->hexbin[hbinidx].data); + } +} + +/* +save "nom",start,size -> save binary +save "nom",start,size,TAPE -> save tape file +save "nom",start,size,AMSDOS -> save binary with Amsdos header +save "nom",start,size,DSK,"dskname" -> save binary on DSK data format +save "nom",start,size,DSK,"dskname",B -> select face +save "nom",start,size,DSK,B -> current DSK, choose face +save "nom",start,size,DSK -> current DSK, current face +*/ +void __SAVE(struct s_assenv *ae) { + struct s_save cursave={0}; + unsigned int offset=0,size=0; + int ko=1; + + if (!ae->wl[ae->idx].t) { + /* nom de fichier entre quotes ou bien mot clef DSK */ + if (!StringIsQuote(ae->wl[ae->idx+1].w)) { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"SAVE invalid filename quote\n"); + ko=0; + } else { + if (!ae->wl[ae->idx+1].t) { + if (!ae->wl[ae->idx+2].t && ae->wl[ae->idx+3].t!=2) { + cursave.ibank=ae->activebank; + cursave.ioffset=ae->idx+2; + ExpressionFastTranslate(ae,&ae->wl[ae->idx+2].w,1); // si on utilise des variables ça évite la grouille post traitement... + cursave.isize=ae->idx+3; + ExpressionFastTranslate(ae,&ae->wl[ae->idx+3].w,1); // idem + cursave.iw=ae->idx+1; + cursave.irun=ae->current_run_idx; + if (!ae->wl[ae->idx+3].t) { + if (strcmp(ae->wl[ae->idx+4].w,"TAPE")==0) { + cursave.tape=1; + } else if (strcmp(ae->wl[ae->idx+4].w,"AMSDOS")==0) { + cursave.amsdos=1; + } else if (strcmp(ae->wl[ae->idx+4].w,"HOBETA")==0) { + cursave.hobeta=1; + } else if (strcmp(ae->wl[ae->idx+4].w,"DSK")==0) { +#if TRACE_EDSK + printf("DSK SAVE order [bnk: %d ioff: %d isiz: %d iw=%d [%s] [%s]\n",cursave.ibank,cursave.ioffset,cursave.isize,cursave.iw,ae->wl[ae->idx+2].w,ae->wl[ae->idx+3].w); +#endif + cursave.dsk=1; + if (!ae->wl[ae->idx+4].t) { + cursave.iwdskname=ae->idx+5; + if (!ae->wl[ae->idx+5].t) { + /* face selection - 0 as default */ + switch (ae->wl[ae->idx+6].w[0]) { + case '1': + case 'B': + cursave.face=1; + break; + case '0': + case 'A': + default: + cursave.face=0; + break; + } + } + } else { + if (ae->nbsave && ae->save[ae->nbsave-1].iwdskname!=-1) { + cursave.iwdskname=ae->save[ae->nbsave-1].iwdskname; /* previous DSK */ + cursave.face=ae->save[ae->nbsave-1].face; /* previous face */ + } else { + cursave.iwdskname=-1; + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"cannot autoselect DSK as there was not a previous selection\n"); + } + } + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"SAVE 4th parameter must be empty or AMSDOS or DSK\n"); + ko=0; + } + } + ObjectArrayAddDynamicValueConcat((void**)&ae->save,&ae->nbsave,&ae->maxsave,&cursave,sizeof(cursave)); + ko=0; + } + } + } + } + if (ko) { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"Use SAVE 'filename',offset,size[,AMSDOS|DSK[,A|B|'dskname'[,A|B]]]\n"); + } + while (!ae->wl[ae->idx].t) ae->idx++; +} + + +void __MODULE(struct s_assenv *ae) { + if (!ae->wl[ae->idx].t && ae->wl[ae->idx+1].t==1) { + if (StringIsQuote(ae->wl[ae->idx+1].w)) { + if (ae->modulen || ae->module) { + MemFree(ae->module); + } + ae->modulen=strlen(ae->wl[ae->idx+1].w); + ae->module=MemMalloc(ae->modulen); + /* duplicate and remove quotes */ + strcpy(ae->module,ae->wl[ae->idx+1].w+1); + ae->module[--ae->modulen]=0; + } else { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"MODULE directive need one text parameter\n"); + } + ae->idx++; + } else { + if (ae->module) MemFree(ae->module); + ae->module=NULL; + } +} + +void __TIMESTR(struct s_assenv *ae) { + +} + +struct s_asm_keyword instruction[]={ +{"LD",0,_LD}, +{"DEC",0,_DEC}, +{"INC",0,_INC}, +{"ADD",0,_ADD}, +{"SUB",0,_SUB}, +{"OR",0,_OR}, +{"AND",0,_AND}, +{"XOR",0,_XOR}, +{"POP",0,_POP}, +{"PUSH",0,_PUSH}, +{"DJNZ",0,_DJNZ}, +{"JR",0,_JR}, +{"JP",0,_JP}, +{"CALL",0,_CALL}, +{"RET",0,_RET}, +{"EX",0,_EX}, +{"ADC",0,_ADC}, +{"SBC",0,_SBC}, +{"EXA",0,_EXA}, +{"EXX",0,_EXX}, +{"CP",0,_CP}, +{"BIT",0,_BIT}, +{"RES",0,_RES}, +{"SET",0,_SET}, +{"IN",0,_IN}, +{"OUT",0,_OUT}, +{"RLC",0,_RLC}, +{"RRC",0,_RRC}, +{"RL",0,_RL}, +{"RR",0,_RR}, +{"SLA",0,_SLA}, +{"SRA",0,_SRA}, +{"SLL",0,_SLL}, +{"SL1",0,_SLL}, +{"SRL",0,_SRL}, +{"RST",0,_RST}, +{"HALT",0,_HALT}, +{"DI",0,_DI}, +{"EI",0,_EI}, +{"NOP",0,_NOP}, +{"DEFR",0,_DEFR}, +{"DEFB",0,_DEFB}, +{"DEFM",0,_DEFB}, +{"DR",0,_DEFR}, +{"DM",0,_DEFB}, +{"DB",0,_DEFB}, +{"DEFW",0,_DEFW}, +{"DW",0,_DEFW}, +{"DEFS",0,_DEFS}, +{"DS",0,_DEFS}, +{"STR",0,_STR}, +{"LDI",0,_LDI}, +{"LDIR",0,_LDIR}, +{"OUTI",0,_OUTI}, +{"INI",0,_INI}, +{"RLCA",0,_RLCA}, +{"RRCA",0,_RRCA}, +{"NEG",0,_NEG}, +{"RLA",0,_RLA}, +{"RRA",0,_RRA}, +{"RLD",0,_RLD}, +{"RRD",0,_RRD}, +{"DAA",0,_DAA}, +{"CPL",0,_CPL}, +{"SCF",0,_SCF}, +{"LDD",0,_LDD}, +{"LDDR",0,_LDDR}, +{"CCF",0,_CCF}, +{"OUTD",0,_OUTD}, +{"IND",0,_IND}, +{"RETI",0,_RETI}, +{"RETN",0,_RETN}, +{"IM",0,_IM}, +{"DEFI",0,_DEFI}, +{"CPD",0,_CPD}, +{"CPI",0,_CPI}, +{"CPDR",0,_CPDR}, +{"CPIR",0,_CPIR}, +{"OTDR",0,_OTDR}, +{"OTIR",0,_OTIR}, +{"INDR",0,_INDR}, +{"INIR",0,_INIR}, +{"REPEAT",0,__REPEAT}, +{"REND",0,__REND}, +{"ENDREPEAT",0,__REND}, +{"ENDREP",0,__REND}, +{"UNTIL",0,__UNTIL}, +{"ORG",0,__ORG}, +{"PROTECT",0,__PROTECT}, +{"WHILE",0,__WHILE}, +{"WEND",0,__WEND}, +{"HEXBIN",0,__HEXBIN}, +{"ALIGN",0,__ALIGN}, +{"ELSEIF",0,__ELSEIF}, +{"ELSE",0,__ELSE}, +{"IF",0,__IF}, +{"ENDIF",0,__ENDIF}, +{"IFNOT",0,__IFNOT}, +{"IFDEF",0,__IFDEF}, +{"IFNDEF",0,__IFNDEF}, +{"IFUSED",0,__IFUSED}, +{"IFNUSED",0,__IFNUSED}, +{"UNDEF",0,__UNDEF}, +{"CASE",0,__CASE}, +{"BREAK",0,__BREAK}, +{"DEFAULT",0,__DEFAULT}, +{"SWITCH",0,__SWITCH}, +{"ENDSWITCH",0,__ENDSWITCH}, +{"WRITE",0,__WRITE}, +{"CODE",0,__CODE}, +{"NOCODE",0,__NOCODE}, +{"MEMSPACE",0,__MEMSPACE}, +{"MACRO",0,__MACRO}, +{"TICKER",0,__TICKER}, +{"LET",0,__LET}, +{"ASSERT",0,__ASSERT}, +{"CHARSET",0,__CHARSET}, +{"RUN",0,__RUN}, +{"SAVE",0,__SAVE}, +{"BRK",0,__BRK}, +{"NOLIST",0,__NOLIST}, +{"LIST",0,__LIST}, +{"STOP",0,__STOP}, +{"PRINT",0,__PRINT}, +{"FAIL",0,__FAIL}, +{"BREAKPOINT",0,__BREAKPOINT}, +{"BANK",0,__BANK}, +{"BANKSET",0,__BANKSET}, +{"NAMEBANK",0,__NameBANK}, +{"LIMIT",0,__LIMIT}, +{"LZEXO",0,__LZEXO}, +{"LZX7",0,__LZX7}, +{"LZ4",0,__LZ4}, +{"LZ48",0,__LZ48}, +{"LZ49",0,__LZ49}, +{"LZCLOSE",0,__LZCLOSE}, +{"BUILDZX",0,__BUILDZX}, +{"BUILDCPR",0,__BUILDCPR}, +{"BUILDSNA",0,__BUILDSNA}, +{"BUILDTAPE",0,__BUILDTAPE}, +{"SETCPC",0,__SETCPC}, +{"SETCRTC",0,__SETCRTC}, +{"AMSDOS",0,__AMSDOS}, +{"OTD",0,_OUTD}, +{"OTI",0,_OUTI}, +{"SHL",0,_SLA}, +{"SHR",0,_SRL}, +{"STRUCT",0,__STRUCT}, +{"ENDSTRUCT",0,__ENDSTRUCT}, +{"ENDS",0,__ENDSTRUCT}, +{"NOEXPORT",0,__NOEXPORT}, +{"ENOEXPORT",0,__ENOEXPORT}, +{"",0,NULL} +}; + +int Assemble(struct s_assenv *ae, unsigned char **dataout, int *lenout, struct s_rasm_info **debug) +{ + #undef FUNC + #define FUNC "Assemble" + + unsigned char *AmsdosHeader; + struct s_expression curexp={0}; + struct s_wordlist *wordlist; + struct s_expr_dico curdico={0}; + struct s_label *curlabel; + int icrc,curcrc,i,j,k; + unsigned char *lzdata=NULL; + int lzlen,lzshift,input_size; + size_t slzlen; + unsigned char *input_data; + struct s_orgzone orgzone={0}; + int iorgzone,ibank,offset,endoffset; + int il,maxrom; + char *TMP_filename=NULL; + int minmem=65536,maxmem=0,lzmove; + char symbol_line[1024]; + int ifast,executed; + /* debug */ + int curii,inhibe; + int ok; + + rasm_printf(ae,KAYGREEN"Assembling\n"); +#if TRACE_ASSEMBLE +printf("assembling\n"); +#endif +#if TRACE_GENERALE +printf("*** assembling ***\n"); +#endif + + ae->retdebug=debug; + + srand((unsigned)time(0)); + + wordlist=ae->wl; + ae->wl=wordlist; + /* start outside crunched section */ + ae->lz=-1; + + /* default orgzone */ + orgzone.ibank=BANK_MAX_NUMBER; + ObjectArrayAddDynamicValueConcat((void**)&ae->orgzone,&ae->io,&ae->mo,&orgzone,sizeof(orgzone)); + ___output=___internal_output; + /* init des automates */ + InitAutomate(ae->AutomateHexa,AutomateHexaDefinition); + InitAutomate(ae->AutomateDigit,AutomateDigitDefinition); + InitAutomate(ae->AutomateValidLabel,AutomateValidLabelDefinition); + InitAutomate(ae->AutomateValidLabelFirst,AutomateValidLabelFirstDefinition); + InitAutomate(ae->AutomateExpressionValidCharExtended,AutomateExpressionValidCharExtendedDefinition); + InitAutomate(ae->AutomateExpressionValidCharFirst,AutomateExpressionValidCharFirstDefinition); + InitAutomate(ae->AutomateExpressionValidChar,AutomateExpressionValidCharDefinition); + ae->AutomateExpressionDecision['<']='<'; + ae->AutomateExpressionDecision['>']='>'; + ae->AutomateExpressionDecision['=']='='; + ae->AutomateExpressionDecision['!']='!'; + ae->AutomateExpressionDecision[0]='E'; + /* gestion d'alias */ + ae->AutomateExpressionDecision['~']='~'; + /* set operator precedence */ + if (!ae->maxam) { + for (i=0;i<256;i++) { + switch (i) { + /* priority 0 */ + case '(':ae->AutomateElement[i].operator=E_COMPUTE_OPERATION_OPEN;ae->AutomateElement[i].priority=0;break; + case ')':ae->AutomateElement[i].operator=E_COMPUTE_OPERATION_CLOSE;ae->AutomateElement[i].priority=0;break; + /* priority 1 */ + case 'b':ae->AutomateElement[i].operator=E_COMPUTE_OPERATION_NOT;ae->AutomateElement[i].priority=1;break; + /* priority 2 */ + case '*':ae->AutomateElement[i].operator=E_COMPUTE_OPERATION_MUL;ae->AutomateElement[i].priority=2;break; + case '/':ae->AutomateElement[i].operator=E_COMPUTE_OPERATION_DIV;ae->AutomateElement[i].priority=2;break; + case 'm':ae->AutomateElement[i].operator=E_COMPUTE_OPERATION_MOD;ae->AutomateElement[i].priority=2;break; + /* priority 3 */ + case '+':ae->AutomateElement[i].operator=E_COMPUTE_OPERATION_ADD;ae->AutomateElement[i].priority=3;break; + case '-':ae->AutomateElement[i].operator=E_COMPUTE_OPERATION_SUB;ae->AutomateElement[i].priority=3;break; + /* priority 4 */ + case '[':ae->AutomateElement[i].operator=E_COMPUTE_OPERATION_SHL;ae->AutomateElement[i].priority=4;break; + case ']':ae->AutomateElement[i].operator=E_COMPUTE_OPERATION_SHR;ae->AutomateElement[i].priority=4;break; + /* priority 5 */ + case 'l':ae->AutomateElement[i].operator=E_COMPUTE_OPERATION_LOWER;ae->AutomateElement[i].priority=5;break; + case 'g':ae->AutomateElement[i].operator=E_COMPUTE_OPERATION_GREATER;ae->AutomateElement[i].priority=5;break; + case 'e':ae->AutomateElement[i].operator=E_COMPUTE_OPERATION_EQUAL;ae->AutomateElement[i].priority=5;break; + case 'n':ae->AutomateElement[i].operator=E_COMPUTE_OPERATION_NOTEQUAL;ae->AutomateElement[i].priority=5;break; + case 'k':ae->AutomateElement[i].operator=E_COMPUTE_OPERATION_LOWEREQ;ae->AutomateElement[i].priority=5;break; + case 'h':ae->AutomateElement[i].operator=E_COMPUTE_OPERATION_GREATEREQ;ae->AutomateElement[i].priority=5;break; + /* priority 6 */ + case '&':ae->AutomateElement[i].operator=E_COMPUTE_OPERATION_AND;ae->AutomateElement[i].priority=6;break; + /* priority 7 */ + case '^':ae->AutomateElement[i].operator=E_COMPUTE_OPERATION_XOR;ae->AutomateElement[i].priority=7;break; + /* priority 8 */ + case '|':ae->AutomateElement[i].operator=E_COMPUTE_OPERATION_OR;ae->AutomateElement[i].priority=8;break; + /* priority 9 */ + case 'a':ae->AutomateElement[i].operator=E_COMPUTE_OPERATION_BAND;ae->AutomateElement[i].priority=9;break; + /* priority 10 */ + case 'o':ae->AutomateElement[i].operator=E_COMPUTE_OPERATION_BOR;ae->AutomateElement[i].priority=10;break; + default:ae->AutomateElement[i].operator=E_COMPUTE_OPERATION_END; + } + } + } else { + for (i=0;i<256;i++) { + switch (i) { + /* priority 0 */ + case '(':ae->AutomateElement[i].operator=E_COMPUTE_OPERATION_OPEN;ae->AutomateElement[i].priority=0;break; + case ')':ae->AutomateElement[i].operator=E_COMPUTE_OPERATION_CLOSE;ae->AutomateElement[i].priority=0;break; + /* priority 0.5 */ + case 'b':ae->AutomateElement[i].operator=E_COMPUTE_OPERATION_NOT;ae->AutomateElement[i].priority=128;break; + /* priority 1 */ + case '*':ae->AutomateElement[i].operator=E_COMPUTE_OPERATION_MUL;ae->AutomateElement[i].priority=464;break; + case '/':ae->AutomateElement[i].operator=E_COMPUTE_OPERATION_DIV;ae->AutomateElement[i].priority=464;break; + case 'm':ae->AutomateElement[i].operator=E_COMPUTE_OPERATION_MOD;ae->AutomateElement[i].priority=464;break; + case '+':ae->AutomateElement[i].operator=E_COMPUTE_OPERATION_ADD;ae->AutomateElement[i].priority=464;break; + case '-':ae->AutomateElement[i].operator=E_COMPUTE_OPERATION_SUB;ae->AutomateElement[i].priority=464;break; + case '[':ae->AutomateElement[i].operator=E_COMPUTE_OPERATION_SHL;ae->AutomateElement[i].priority=464;break; + case ']':ae->AutomateElement[i].operator=E_COMPUTE_OPERATION_SHR;ae->AutomateElement[i].priority=464;break; + case '&':ae->AutomateElement[i].operator=E_COMPUTE_OPERATION_AND;ae->AutomateElement[i].priority=464;break; + case '^':ae->AutomateElement[i].operator=E_COMPUTE_OPERATION_XOR;ae->AutomateElement[i].priority=464;break; + case '|':ae->AutomateElement[i].operator=E_COMPUTE_OPERATION_OR;ae->AutomateElement[i].priority=464;break; + /* priority 2 */ + case 'l':ae->AutomateElement[i].operator=E_COMPUTE_OPERATION_LOWER;ae->AutomateElement[i].priority=664;break; + case 'g':ae->AutomateElement[i].operator=E_COMPUTE_OPERATION_GREATER;ae->AutomateElement[i].priority=664;break; + case 'e':ae->AutomateElement[i].operator=E_COMPUTE_OPERATION_EQUAL;ae->AutomateElement[i].priority=664;break; + case 'n':ae->AutomateElement[i].operator=E_COMPUTE_OPERATION_NOTEQUAL;ae->AutomateElement[i].priority=664;break; + case 'k':ae->AutomateElement[i].operator=E_COMPUTE_OPERATION_LOWEREQ;ae->AutomateElement[i].priority=664;break; + case 'h':ae->AutomateElement[i].operator=E_COMPUTE_OPERATION_GREATEREQ;ae->AutomateElement[i].priority=664;break; + /* priority 3 */ + case 'a':ae->AutomateElement[i].operator=E_COMPUTE_OPERATION_BAND;ae->AutomateElement[i].priority=6128;break; + case 'o':ae->AutomateElement[i].operator=E_COMPUTE_OPERATION_BOR;ae->AutomateElement[i].priority=6128;break; + default:ae->AutomateElement[i].operator=E_COMPUTE_OPERATION_END; + } + } + } + + /* psg conversion */ + for (i=j=0;i<100;i++) ae->psgtab[j++]=0; + for (i=0;i<49;i++) ae->psgtab[j++]=13; + for (i=0;i<35;i++) ae->psgtab[j++]=14; + for (i=0;i<72;i++) ae->psgtab[j++]=15; + if (j!=256) { + rasm_printf(ae,"Internal error with PSG conversion table\n"); + exit(-44); + } + for (i=j=0;i<1;i++) ae->psgfine[j++]=0; + for (i=0;i<1;i++) ae->psgfine[j++]=1; + for (i=0;i<1;i++) ae->psgfine[j++]=2; + for (i=0;i<2;i++) ae->psgfine[j++]=3; + for (i=0;i<2;i++) ae->psgfine[j++]=4; + for (i=0;i<2;i++) ae->psgfine[j++]=5; + for (i=0;i<3;i++) ae->psgfine[j++]=6; + for (i=0;i<4;i++) ae->psgfine[j++]=7; + for (i=0;i<7;i++) ae->psgfine[j++]=8; + for (i=0;i<9;i++) ae->psgfine[j++]=9; + for (i=0;i<13;i++) ae->psgfine[j++]=10; + for (i=0;i<19;i++) ae->psgfine[j++]=11; + for (i=0;i<27;i++) ae->psgfine[j++]=12; + for (i=0;i<37;i++) ae->psgfine[j++]=13; + for (i=0;i<53;i++) ae->psgfine[j++]=14; + for (i=0;i<75;i++) ae->psgfine[j++]=15; + if (j!=256) { + rasm_printf(ae,"Internal error with PSG conversion table\n"); + exit(-44); + } + /* default var */ + ae->autorise_export=1; + ExpressionSetDicoVar(ae,"PI",3.1415926545); + ExpressionSetDicoVar(ae,"ASSEMBLER_RASM",1); + + /* add a fictive expression to simplify test when parsing expressions */ + ObjectArrayAddDynamicValueConcat((void **)&ae->expression,&ae->ie,&ae->me,&curexp,sizeof(curexp)); + + /* compute CRC for keywords and directives */ + for (icrc=0;instruction[icrc].mnemo[0];icrc++) instruction[icrc].crc=GetCRC(instruction[icrc].mnemo); + for (icrc=0;math_keyword[icrc].mnemo[0];icrc++) math_keyword[icrc].crc=GetCRC(math_keyword[icrc].mnemo); + + if (ae->as80==1) { /* not for UZ80 */ + for (icrc=0;instruction[icrc].mnemo[0];icrc++) { + if (strcmp(instruction[icrc].mnemo,"DEFB")==0 || strcmp(instruction[icrc].mnemo,"DB")==0) { + instruction[icrc].makemnemo=_DEFB_as80; + } else if (strcmp(instruction[icrc].mnemo,"DEFW")==0 || strcmp(instruction[icrc].mnemo,"DW")==0) { + instruction[icrc].makemnemo=_DEFW_as80; + } else if (strcmp(instruction[icrc].mnemo,"DEFI")==0) { + instruction[icrc].makemnemo=_DEFI_as80; + } + } + } + + for (icrc=0;instruction[icrc].mnemo[0];icrc++) { + /* get indexes for DEF instructions */ + if (strcmp(instruction[icrc].mnemo,"DEFB")==0) { + ICRC_DEFB=icrc; + } else if (strcmp(instruction[icrc].mnemo,"DB")==0) { + ICRC_DB=icrc; + } else if (strcmp(instruction[icrc].mnemo,"DEFW")==0) { + ICRC_DEFW=icrc; + } else if (strcmp(instruction[icrc].mnemo,"DW")==0) { + ICRC_DW=icrc; + } else if (strcmp(instruction[icrc].mnemo,"DEFR")==0) { + ICRC_DEFR=icrc; + } else if (strcmp(instruction[icrc].mnemo,"DR")==0) { + ICRC_DR=icrc; + } else if (strcmp(instruction[icrc].mnemo,"DEFS")==0) { + ICRC_DEFS=icrc; + } else if (strcmp(instruction[icrc].mnemo,"DS")==0) { + ICRC_DS=icrc; + } else if (strcmp(instruction[icrc].mnemo,"DEFI")==0) { + ICRC_DEFI=icrc; + } + } + + /* Execution des mots clefs */ + /********************************************************** + A S S E M B L I N G M A I N L O O P + **********************************************************/ +#if TRACE_ASSEMBLE +printf("init ok\n"); +#endif +#if TRACE_GENERALE +printf("-loop\n"); +#endif + + ae->idx=1; + while (wordlist[ae->idx].t!=2) { + curcrc=GetCRC(wordlist[ae->idx].w); + /********************* + d e b u g i n f o + *********************/ + #if TRACE_ASSEMBLE + { + int iiii=0; + printf(KVERBOSE"%d [%s] L%d [%s]e=%d ",ae->idx,ae->filename[wordlist[ae->idx].ifile],wordlist[ae->idx].l,wordlist[ae->idx].w,wordlist[ae->idx].e); + while (!wordlist[ae->idx+iiii++].t) rasm_printf(ae," [%s]e=%d ",wordlist[ae->idx+iiii].w,wordlist[ae->idx+iiii].e); + + for (iiii=0;iiii<ae->imacropos;iiii++) { + printf("M[%d] s=%d e=%d ",iiii,ae->macropos[iiii].start,ae->macropos[iiii].end); + } + printf("\n"); + } + #endif + + /******************************************************************** + c o n d i t i o n n a l a s s e m b l y m a n a g e m e n t + ********************************************************************/ + if (ae->ii || ae->isw) { + /* inhibition of if/endif */ + for (inhibe=curii=0;curii<ae->ii;curii++) { + if (!ae->ifthen[curii].v || ae->ifthen[curii].v==-1) { + inhibe=1; + break; + } + } + /* when inhibited we are looking only for a IF/IFDEF/IFNOT/IFNDEF/ELSE/ELSEIF/ENDIF or SWITCH/CASE/DEFAULT/ENDSWITCH */ + if (inhibe) { + /* this section does NOT need to be agressively optimized !!! */ + if (curcrc==CRC_ELSEIF && strcmp(wordlist[ae->idx].w,"ELSEIF")==0) { + /* true IF needs to be done ONLY on the active level */ + if (curii==ae->ii-1) __ELSEIF(ae); else __ELSEIF_light(ae); + } else if (curcrc==CRC_ELSE && strcmp(wordlist[ae->idx].w,"ELSE")==0) { + __ELSE(ae); + } else if (curcrc==CRC_ENDIF && strcmp(wordlist[ae->idx].w,"ENDIF")==0) { + __ENDIF(ae); + } else if (curcrc==CRC_IF && strcmp(wordlist[ae->idx].w,"IF")==0) { + /* as we are inhibited we do not have to truly compute IF */ + __IF_light(ae); + } else if (curcrc==CRC_IFDEF && strcmp(wordlist[ae->idx].w,"IFDEF")==0) { + __IFDEF_light(ae); + } else if (curcrc==CRC_IFNOT && strcmp(wordlist[ae->idx].w,"IFNOT")==0) { + __IFNOT_light(ae); + } else if (curcrc==CRC_IFUSED && strcmp(wordlist[ae->idx].w,"IFUSED")==0) { + __IFUSED_light(ae); + } else if (curcrc==CRC_IFNUSED && strcmp(wordlist[ae->idx].w,"IFNUSED")==0) { + __IFNUSED_light(ae); + } else if (curcrc==CRC_IFNDEF && strcmp(wordlist[ae->idx].w,"IFNDEF")==0) { + __IFNDEF_light(ae); + } else if (curcrc==CRC_SWITCH && strcmp(wordlist[ae->idx].w,"SWITCH")==0) { + __SWITCH_light(ae); + } else if (curcrc==CRC_CASE && strcmp(wordlist[ae->idx].w,"CASE")==0) { + __CASE_light(ae); + } else if (curcrc==CRC_ENDSWITCH && strcmp(wordlist[ae->idx].w,"ENDSWITCH")==0) { + __ENDSWITCH(ae); + } else if (curcrc==CRC_BREAK && strcmp(wordlist[ae->idx].w,"BREAK")==0) { + __BREAK_light(ae); + } else if (curcrc==CRC_DEFAULT && strcmp(wordlist[ae->idx].w,"DEFAULT")==0) { + __DEFAULT_light(ae); + } + while (wordlist[ae->idx].t==0) ae->idx++; + ae->idx++; + continue; + } else { + /* inhibition of switch/case */ + for (curii=0;curii<ae->isw;curii++) { + if (!ae->switchcase[curii].execute) { + inhibe=2; + break; + } + } + if (inhibe) { + /* this section does NOT need to be agressively optimized !!! */ + if (curcrc==CRC_CASE && strcmp(wordlist[ae->idx].w,"CASE")==0) { + __CASE(ae); + } else if (curcrc==CRC_ENDSWITCH && strcmp(wordlist[ae->idx].w,"ENDSWITCH")==0) { + __ENDSWITCH(ae); + } else if (curcrc==CRC_IF && strcmp(wordlist[ae->idx].w,"IF")==0) { + /* as we are inhibited we do not have to truly compute IF */ + __IF_light(ae); + } else if (curcrc==CRC_IFDEF && strcmp(wordlist[ae->idx].w,"IFDEF")==0) { + __IFDEF(ae); + } else if (curcrc==CRC_IFNOT && strcmp(wordlist[ae->idx].w,"IFNOT")==0) { + __IFNOT(ae); + } else if (curcrc==CRC_ELSE && strcmp(wordlist[ae->idx].w,"ELSE")==0) { + __ELSE(ae); + } else if (curcrc==CRC_ENDIF && strcmp(wordlist[ae->idx].w,"ENDIF")==0) { + __ENDIF(ae); + } else if (curcrc==CRC_ELSEIF && strcmp(wordlist[ae->idx].w,"ELSEIF")==0) { + __ELSEIF(ae); + } else if (curcrc==CRC_IFUSED && strcmp(wordlist[ae->idx].w,"IFUSED")==0) { + __IFUSED(ae); + } else if (curcrc==CRC_IFNUSED && strcmp(wordlist[ae->idx].w,"IFNUSED")==0) { + __IFNUSED(ae); + } else if (curcrc==CRC_IFNDEF && strcmp(wordlist[ae->idx].w,"IFNDEF")==0) { + __IFNDEF(ae); + } else if (curcrc==CRC_SWITCH && strcmp(wordlist[ae->idx].w,"SWITCH")==0) { + __SWITCH(ae); + } else if (curcrc==CRC_BREAK && strcmp(wordlist[ae->idx].w,"BREAK")==0) { + __BREAK(ae); + } else if (curcrc==CRC_DEFAULT && strcmp(wordlist[ae->idx].w,"DEFAULT")==0) { + __DEFAULT(ae); + } + while (wordlist[ae->idx].t==0) ae->idx++; + ae->idx++; + continue; + } + } + } + if (ae->imacropos) { + /* are we still in a macro? */ + if (ae->idx>=ae->macropos[0].end) { + /* are we out of all repetition blocks? */ + if (!ae->ir && !ae->iw) { + ae->imacropos=0; + + /* quand on sort du local, on récupère le dernier label global */ + if (ae->lastsuperglobal!=ae->lastgloballabel && ae->lastsuperglobal) { + if (ae->lastglobalalloc) { + MemFree(ae->lastgloballabel); + ae->lastglobalalloc=0; + } + ae->lastgloballabel=ae->lastsuperglobal; + ae->lastgloballabellen=strlen(ae->lastgloballabel); + } + } + } + } + /***************************************** + e x e c u t e i n s t r u c t i o n + *****************************************/ + executed=0; + if ((ifast=ae->fastmatch[(int)wordlist[ae->idx].w[0]])!=-1) { + while (instruction[ifast].mnemo[0]==wordlist[ae->idx].w[0]) { + if (instruction[ifast].crc==curcrc && strcmp(instruction[ifast].mnemo,wordlist[ae->idx].w)==0) { +#if TRACE_ASSEMBLE +printf("-> mnemo\n"); +#endif + instruction[ifast].makemnemo(ae); + executed=1; + break; + } + ifast++; + } + } + /***************************************** + e x e c u t e m a c r o + *****************************************/ + if (!executed) { + /* is it a macro? */ + if ((ifast=SearchMacro(ae,curcrc,wordlist[ae->idx].w))>=0) { +#if TRACE_ASSEMBLE +printf("-> macro\n"); +#endif + wordlist=__MACRO_EXECUTE(ae,ifast); + continue; + } + } + /********************************************************************* + e x e c u t e e x p r e s s i o n o r p u s h l a b e l + *********************************************************************/ + if (!ae->stop) { + if (!executed) { + /* no instruction executed, this is a label or an assignement */ + if (wordlist[ae->idx].e) { +#if TRACE_ASSEMBLE +printf("-> expr\n"); +#endif + ExpressionFastTranslate(ae,&wordlist[ae->idx].w,0); + ComputeExpression(ae,wordlist[ae->idx].w,ae->codeadr,0,0); + } else { +#if TRACE_ASSEMBLE +printf("-> label\n"); +#endif + PushLabel(ae); + } + } else { +#if TRACE_ASSEMBLE +printf("-> ajuste IDX\n"); +#endif + while (!wordlist[ae->idx].t) { + ae->idx++; + } + } + ae->idx++; + } else { +#if TRACE_ASSEMBLE +printf("-> STOP\n"); +#endif + break; + } + } +#if TRACE_ASSEMBLE + rasm_printf(ae,KVERBOSE"%d [%s] L%d [%s] fin de la liste de mots\n",ae->idx,ae->filename[wordlist[ae->idx].ifile],wordlist[ae->idx].l,wordlist[ae->idx].w); + printf("check ORG\n"); +#endif +#if TRACE_GENERALE +printf("-check ORG\n"); +#endif + + if (!ae->stop) { + /* end of assembly, check there is no opened struct */ + if (ae->getstruct) { + MakeError(ae,ae->backup_filename,ae->backup_line,"STRUCT declaration was not closed\n"); + } + /* end of assembly, close the last ORG zone */ + if (ae->io) { + ae->orgzone[ae->io-1].memend=ae->outputadr; + } + OverWriteCheck(ae); + /* end of assembly, close crunched zone (if any) */ + __internal_UpdateLZBlockIfAny(ae); + + /* end of assembly, check for opened repeat and opened while loop */ + for (i=0;i<ae->ir;i++) { + MakeError(ae,ae->filename[wordlist[ae->repeat[i].start].ifile],wordlist[ae->repeat[i].start].l,"REPEAT was not closed\n"); + } + for (i=0;i<ae->iw;i++) { + MakeError(ae,ae->filename[wordlist[ae->whilewend[i].start].ifile],wordlist[ae->whilewend[i].start].l,"WHILE was not closed\n"); + } + /* is there any IF opened? -> need an evolution for a better error message */ + for (i=0;i<ae->ii;i++) { + char instr[32]; + switch (ae->ifthen[i].type) { + case E_IFTHEN_TYPE_IF:strcpy(instr,"IF");break; + case E_IFTHEN_TYPE_IFNOT:strcpy(instr,"IFNOT");break; + case E_IFTHEN_TYPE_IFDEF:strcpy(instr,"IFDEF");break; + case E_IFTHEN_TYPE_IFNDEF:strcpy(instr,"IFNDEF");break; + case E_IFTHEN_TYPE_ELSE:strcpy(instr,"ELSE");break; + case E_IFTHEN_TYPE_ELSEIF:strcpy(instr,"ELSEIF");break; + case E_IFTHEN_TYPE_IFUSED:strcpy(instr,"IFUSED");break; + case E_IFTHEN_TYPE_IFNUSED:strcpy(instr,"IFNUSED");break; + default:strcpy(instr,"<unknown>"); + } + MakeError(ae,ae->ifthen[i].filename,ae->ifthen[i].line,"%s conditionnal block was not closed\n",instr); + } + } +#if TRACE_ASSEMBLE +printf("crunch if any\n"); +#endif + /*************************************************** + c r u n c h L Z s e c t i o n s + ***************************************************/ + if (!ae->stop || !ae->nberr) { + for (i=0;i<ae->ilz;i++) { + /* compute labels and expression inside crunched blocks */ + PopAllExpression(ae,i); + + ae->curlz=i; + iorgzone=ae->lzsection[i].iorgzone; + ibank=ae->lzsection[i].ibank; + input_data=&ae->mem[ae->lzsection[i].ibank][ae->lzsection[i].memstart]; + input_size=ae->lzsection[i].memend-ae->lzsection[i].memstart; +//printf("grouik (%d) %s\n",ae->lzsection[i].lzversion,ae->lzsection[i].lzversion==8?"mizou":""); + if (!input_size) { + rasm_printf(ae,KWARNING"[%s:%d] Warning: crunched section is empty\n",GetCurrentFile(ae),ae->wl[ae->idx].l); + } else { + switch (ae->lzsection[i].lzversion) { + case 7: + #ifndef NO_3RD_PARTIES + lzdata=ZX7_compress(optimize(input_data, input_size), input_data, input_size, &slzlen); + lzlen=slzlen; + #endif + break; + case 4: + #ifndef NO_3RD_PARTIES + lzdata=LZ4_crunch(input_data,input_size,&lzlen); + #endif + break; + case 8: + #ifndef NO_3RD_PARTIES + rasm_printf(ae,KWARNING"Exomizer is crunching %.1fkb this may take a while, be patient...\n",input_size/1024.0); + + lzdata=Exomizer_crunch(input_data,input_size,&lzlen); + #endif + break; + case 48: + lzdata=LZ48_crunch(input_data,input_size,&lzlen); + break; + case 49: + lzdata=LZ49_crunch(input_data,input_size,&lzlen); + break; + default: + rasm_printf(ae,"Internal error - unknown crunch method %d\n",ae->lzsection[i].lzversion); + exit(-12); + } + } + //rasm_printf(ae,"lzsection[%d] type=%d start=%04X end=%04X crunched size=%d\n",i,ae->lzsection[i].lzversion,ae->lzsection[i].memstart,ae->lzsection[i].memend,lzlen); + + if (input_size<lzlen) { + MakeError(ae,ae->filename[ae->wl[ae->lzsection[i].iw].ifile],ae->wl[ae->lzsection[i].iw].l,"As the LZ section cannot crunch data, Rasm may not guarantee assembled file!\n"); + } + + lzshift=lzlen-(ae->lzsection[i].memend-ae->lzsection[i].memstart); + if (lzshift>0) { + MemMove(ae->mem[ae->lzsection[i].ibank]+ae->lzsection[i].memend+lzshift,ae->mem[ae->lzsection[i].ibank]+ae->lzsection[i].memend,65536-ae->lzsection[i].memend-lzshift); + } else if (lzshift<0) { + lzmove=ae->orgzone[iorgzone].memend-ae->lzsection[i].memend; + if (lzmove) { + MemMove(ae->mem[ae->lzsection[i].ibank]+ae->lzsection[i].memend+lzshift,ae->mem[ae->lzsection[i].ibank]+ae->lzsection[i].memend,lzmove); + } + } + memcpy(ae->mem[ae->lzsection[i].ibank]+ae->lzsection[i].memstart,lzdata,lzlen); + MemFree(lzdata); + /******************************************************************* + l a b e l a n d e x p r e s s i o n r e l o c a t i o n + *******************************************************************/ + /* relocate labels in the same ORG zone AND after the current crunched section */ + il=ae->lzsection[i].ilabel; + while (il<ae->il && ae->label[il].iorgzone==iorgzone && ae->label[il].ibank==ibank) { + curlabel=SearchLabel(ae,ae->label[il].iw!=-1?wordlist[ae->label[il].iw].w:ae->label[il].name,ae->label[il].crc); + /* CANNOT be NULL */ + curlabel->ptr+=lzshift; + //printf("label [%s] shifte de %d valeur #%04X -> #%04X\n",curlabel->iw!=-1?wordlist[curlabel->iw].w:curlabel->name,lzshift,curlabel->ptr-lzshift,curlabel->ptr); + il++; + } + /* relocate expressions in the same ORG zone AND after the current crunched section */ + il=ae->lzsection[i].iexpr; + while (il<ae->ie && ae->expression[il].iorgzone==iorgzone && ae->expression[il].ibank==ibank) { + ae->expression[il].wptr+=lzshift; + ae->expression[il].ptr+=lzshift; + //printf("expression [%s] shiftee ptr=#%04X wptr=#%04X\n", ae->expression[il].reference?ae->expression[il].reference:wordlist[ae->expression[il].iw].w, ae->expression[il].ptr, ae->expression[il].wptr); + il++; + } + /* relocate crunched sections in the same ORG zone AND after the current crunched section */ + il=i+1; + while (il<ae->ilz && ae->lzsection[il].iorgzone==iorgzone && ae->lzsection[il].ibank==ibank) { + //rasm_printf(ae,"reloger lzsection[%d] O%d B%d\n",il,ae->lzsection[il].iorgzone,ae->lzsection[il].ibank); + ae->lzsection[il].memstart+=lzshift; + ae->lzsection[il].memend+=lzshift; + il++; + } + /* relocate current ORG zone */ + ae->orgzone[iorgzone].memend+=lzshift; + } + if (ae->ilz) { + /* compute expression placed after the last crunched block */ + PopAllExpression(ae,ae->ilz); + } + /* compute expression outside crunched blocks */ + PopAllExpression(ae,-1); + } + +/*************************************************************************************************************************************************************************************** +**************************************************************************************************************************************************************************************** + W R I T E O U T P U T F I L E S +**************************************************************************************************************************************************************************************** +***************************************************************************************************************************************************************************************/ + TMP_filename=MemMalloc(PATH_MAX); +#if 0 +for (i=0;i<ae->io;i++) { +printf("ORG[%02d] start=%04X end=%04X ibank=%d nocode=%d protect=%d\n",i,ae->orgzone[i].memstart,ae->orgzone[i].memend,ae->orgzone[i].ibank,ae->orgzone[i].nocode,ae->orgzone[i].protect); +} +#endif +#if TRACE_ASSEMBLE +printf("output files\n"); +#endif + + if (!ae->nberr && !ae->checkmode) { + + /* enregistrement des fichiers programmes par la commande SAVE */ + PopAllSave(ae); + + if (ae->nbsave==0 || ae->forcecpr || ae->forcesnapshot) { + /********************************************* + ********************************************** + C A R T R I D G E + ********************************************** + *********************************************/ + if (ae->forcecpr) { + char ChunkName[32]; + int ChunkSize; + int do_it=1; + unsigned char chunk_endian; + + if (ae->cartridge_name) { + sprintf(TMP_filename,"%s",ae->cartridge_name); + } else { + sprintf(TMP_filename,"%s.cpr",ae->outputfilename); + } + FileRemoveIfExists(TMP_filename); + + rasm_printf(ae,KIO"Write cartridge file %s\n",TMP_filename); + for (i=maxrom=0;i<ae->io;i++) { + if (ae->orgzone[i].ibank<32 && ae->orgzone[i].ibank>maxrom) maxrom=ae->orgzone[i].ibank; + } + /* construction du CPR */ + /* header blablabla */ + strcpy(ChunkName,"RIFF"); + FileWriteBinary(TMP_filename,ChunkName,4); + ChunkSize=(maxrom+1)*(16384+8)+4; + chunk_endian=ChunkSize&0xFF;FileWriteBinary(TMP_filename,(char*)&chunk_endian,1); + chunk_endian=(ChunkSize>>8)&0xFF;FileWriteBinary(TMP_filename,(char*)&chunk_endian,1); + chunk_endian=(ChunkSize>>16)&0xFF;FileWriteBinary(TMP_filename,(char*)&chunk_endian,1); + chunk_endian=(ChunkSize>>24)&0xFF;FileWriteBinary(TMP_filename,(char*)&chunk_endian,1); + sprintf(ChunkName,"AMS!"); + FileWriteBinary(TMP_filename,ChunkName,4); + +// for (j=0;j<ae->io;j++) { +//printf("ORG[%03d]=B%02d/#%04X/#%04X\n",j,ae->orgzone[j].ibank,ae->orgzone[j].memstart,ae->orgzone[j].memend); +// } + for (i=0;i<=maxrom;i++) { + offset=65536; + endoffset=0; + for (j=0;j<ae->io;j++) { + if (ae->orgzone[j].protect) continue; /* protected zones exclusion */ + /* bank data may start anywhere (typically #0000 or #C000) */ + if (ae->orgzone[j].ibank==i && ae->orgzone[j].memstart!=ae->orgzone[j].memend) { + if (ae->orgzone[j].memstart<offset) offset=ae->orgzone[j].memstart; + if (ae->orgzone[j].memend>endoffset) endoffset=ae->orgzone[j].memend; + } + } + if (endoffset>offset) { + int lm=0; + if (ae->iwnamebank[i]>0) { + lm=strlen(ae->wl[ae->iwnamebank[i]].w)-2; + } + rasm_printf(ae,KVERBOSE"WriteCPR bank %2d of %5d byte%s start at #%04X",i,endoffset-offset,endoffset-offset>1?"s":" ",offset); + if (endoffset-offset>16384) { + rasm_printf(ae,"\nROM is too big!!!\n"); + FileWriteBinaryClose(TMP_filename); + FileRemoveIfExists(TMP_filename); + FreeAssenv(ae); + exit(ABORT_ERROR); + } + if (lm) { + rasm_printf(ae," (%-*.*s)\n",lm,lm,ae->wl[ae->iwnamebank[i]].w+1); + } else { + rasm_printf(ae,"\n"); + } + } else { + rasm_printf(ae,KVERBOSE"WriteCPR bank %2d (empty)\n",i); + } + ChunkSize=16384; + sprintf(ChunkName,"cb%02d",i); + FileWriteBinary(TMP_filename,ChunkName,4); + chunk_endian=ChunkSize&0xFF;FileWriteBinary(TMP_filename,(char*)&chunk_endian,1); + chunk_endian=(ChunkSize>>8)&0xFF;FileWriteBinary(TMP_filename,(char*)&chunk_endian,1); + chunk_endian=(ChunkSize>>16)&0xFF;FileWriteBinary(TMP_filename,(char*)&chunk_endian,1); + chunk_endian=(ChunkSize>>24)&0xFF;FileWriteBinary(TMP_filename,(char*)&chunk_endian,1); + if (offset>0xC000) { + unsigned char filler[16384]={0}; + ChunkSize=65536-offset; + if (ChunkSize) FileWriteBinary(TMP_filename,(char*)ae->mem[i]+offset,ChunkSize); + /* ADD zeros until the end of the bank */ + FileWriteBinary(TMP_filename,(char*)filler,16384-ChunkSize); + } else { + FileWriteBinary(TMP_filename,(char*)ae->mem[i]+offset,ChunkSize); + } + } + FileWriteBinaryClose(TMP_filename); + rasm_printf(ae,"Total %d bank%s (%dK)\n",maxrom+1,maxrom+1>1?"s":"",(maxrom+1)*16); + /********************************************* + ********************************************** + S N A P S H O T + ********************************************** + *********************************************/ + } else if (ae->forcesnapshot) { + + if (ae->forcezx) { + unsigned char zxsnapheader[0x1A]={0}; + + if (ae->snapshot_name) { + sprintf(TMP_filename,"%s",ae->snapshot_name); + } else { + sprintf(TMP_filename,"%s.sna",ae->outputfilename); + } + FileRemoveIfExists(TMP_filename); + + /* do we have a bankset? */ + /* zx bootstrap */ + zxsnapheader[0x13]=0; /* 0:DI 4:EI */ + zxsnapheader[0x17]=ae->zxsnapshot.stack&0xFF; + zxsnapheader[0x18]=(ae->zxsnapshot.stack>>8)&0xFF; + zxsnapheader[0x19]=1; /* IM 1 */ + + //ae->zxsnapshot.stack&=0xFFFF; + ae->mem[0][ae->zxsnapshot.stack]=ae->zxsnapshot.run&0xFF; + ae->mem[0][ae->zxsnapshot.stack+1]=(ae->zxsnapshot.run>>8)&0xFF; + + rasm_printf(ae,KIO"Write 48K ZX snapshot file %s\n",TMP_filename); + + /* header */ + FileWriteBinary(TMP_filename,(char *)&zxsnapheader,27); + /* data */ + if (ae->bankset[0]) { + FileWriteBinary(TMP_filename,(char *)ae->mem[0]+16384,16384*3); + } else { + FileWriteBinary(TMP_filename,(char *)ae->mem[5],16384); + FileWriteBinary(TMP_filename,(char *)ae->mem[2],16384); + FileWriteBinary(TMP_filename,(char *)ae->mem[0],16384); + } + FileWriteBinaryClose(TMP_filename); + } else { + unsigned char packed[65536]={0}; + unsigned char *rlebank=NULL; + char ChunkName[16]; + int ChunkSize; + int do_it=1; + int bankset; + int noflood=0; + + if (ae->snapshot.version==2 && ae->snapshot.CPCType>2) { + if (!ae->nowarning) rasm_printf(ae,KWARNING"[%s:%d] Warning: V2 snapshot cannot select a Plus model (forced to 6128)\n",GetCurrentFile(ae),ae->wl[ae->idx].l); + ae->snapshot.CPCType=2; /* 6128 */ + } + + if (ae->snapshot_name) { + sprintf(TMP_filename,"%s",ae->snapshot_name); + } else { + sprintf(TMP_filename,"%s.sna",ae->outputfilename); + } + FileRemoveIfExists(TMP_filename); + + maxrom=-1; + for (i=0;i<ae->io;i++) { + if (ae->orgzone[i].ibank<BANK_MAX_NUMBER && ae->orgzone[i].ibank>maxrom && ae->orgzone[i].memstart!=ae->orgzone[i].memend) { + maxrom=ae->orgzone[i].ibank; + } + } + + //printf("maxrom=%d\n",maxrom); + /* construction du SNA */ + if (ae->snapshot.version==2) { + if (maxrom>=4) { + ae->snapshot.dumpsize[0]=128; + } else if (maxrom>=0) { + ae->snapshot.dumpsize[0]=64; + } + } + if (maxrom==-1) { + rasm_printf(ae,KWARNING"Warning: No byte were written in snapshot memory\n"); + } else { + rasm_printf(ae,KIO"Write snapshot v%d file %s\n",ae->snapshot.version,TMP_filename); + + /* header */ + FileWriteBinary(TMP_filename,(char *)&ae->snapshot,0x100); + /* write all memory crunched */ + for (i=0;i<=maxrom;i+=4) { + bankset=i>>2; + if (ae->bankset[bankset]) { + memcpy(packed,ae->mem[i],65536); + if (i<4 || i+4>maxrom) rasm_printf(ae,KVERBOSE"WriteSNA bank %2d,%d,%d,%d packed\n",i,i+1,i+2,i+3); + else if (!noflood) {rasm_printf(ae,KVERBOSE"[...]\n");noflood=1;} + } else { + memset(packed,0,65536); + for (k=0;k<4;k++) { + offset=65536; + endoffset=0; + for (j=0;j<ae->io;j++) { + if (ae->orgzone[j].protect) continue; /* protected zones exclusion */ + /* bank data may start anywhere (typically #0000 or #C000) */ + if (ae->orgzone[j].ibank==i+k && ae->orgzone[j].memstart!=ae->orgzone[j].memend) { + if (ae->orgzone[j].memstart<offset) offset=ae->orgzone[j].memstart; + if (ae->orgzone[j].memend>endoffset) endoffset=ae->orgzone[j].memend; + } + } + if (endoffset-offset>16384) { + rasm_printf(ae,KERROR"\nBANK is too big!!!\n"); + FileWriteBinaryClose(TMP_filename); + FileRemoveIfExists(TMP_filename); + FreeAssenv(ae); + exit(ABORT_ERROR); + } + /* banks are gathered in the 64K block */ + if (offset>0xC000) { + ChunkSize=65536-offset; + memcpy(packed+k*16384,(char*)ae->mem[i+k]+offset,ChunkSize); + } else { + memcpy(packed+k*16384,(char*)ae->mem[i+k]+offset,16384); + } + + if (endoffset>offset) { + int lm=0; + if (ae->iwnamebank[i]>0) { + lm=strlen(ae->wl[ae->iwnamebank[i]].w)-2; + } + if (i<4 || i+4>maxrom) rasm_printf(ae,KVERBOSE"WriteSNA bank %2d of %5d byte%s start at #%04X",i+k,endoffset-offset,endoffset-offset>1?"s":" ",offset); + else if (!noflood) {rasm_printf(ae,KVERBOSE"[...]\n");noflood=1;} + if (endoffset-offset>16384) { + rasm_printf(ae,KERROR"\nRAM block is too big!!!\n"); + FileWriteBinaryClose(TMP_filename); + FileRemoveIfExists(TMP_filename); + FreeAssenv(ae); + exit(ABORT_ERROR); + } + if (lm) { + if (i<4 || i+4>maxrom) rasm_printf(ae,KVERBOSE" (%-*.*s)\n",lm,lm,ae->wl[ae->iwnamebank[i+k]].w+1); + } else { + if (i<4 || i+4>maxrom) rasm_printf(ae,"\n"); + } + } else { + if (i<4 || i+4>maxrom) rasm_printf(ae,KVERBOSE"WriteSNA bank %2d (empty)\n",i+k); + else if (!noflood) {rasm_printf(ae,KVERBOSE"[...]\n");noflood=1;} + } + } + } + + if (ae->snapshot.version==2) { + /* snapshot v2 */ + FileWriteBinary(TMP_filename,(char*)&packed,65536); + if (bankset) { + /* v2 snapshot is 128K maximum */ + maxrom=7; + break; + } + } else { + /* compression par défaut avec snapshot v3 */ + rlebank=EncodeSnapshotRLE(packed,&ChunkSize); + + if (bankset>=0 && bankset<=8) { + sprintf(ChunkName,"MEM%d",bankset); + } else if (bankset>8 && bankset<=0x40) { + /* extended chunk for 4M extension -> MX09 to MX40 (hexa numbered) */ + sprintf(ChunkName,"MX%02X",bankset); + } else { + MakeError(ae,"(core)",0,"internal error during snapshot write, please report (%d)\n",bankset); + } + + FileWriteBinary(TMP_filename,ChunkName,4); + if (rlebank!=NULL) { + FileWriteBinary(TMP_filename,(char*)&ChunkSize,4); + FileWriteBinary(TMP_filename,(char*)rlebank,ChunkSize); + MemFree(rlebank); + } else { + ChunkSize=65536; + FileWriteBinary(TMP_filename,(char*)&packed,ChunkSize); + } + } + } + + /************************************************************** + snapshot additional chunks in v3+ only + **************************************************************/ + if (ae->snapshot.version>=3) { + /* export breakpoint */ + if (ae->export_snabrk) { + /* BRKS chunk for Winape emulator (unofficial) + + 2 bytes - adress + 1 byte - 0=base 64K / 1=extended + 2 bytes - condition (zeroed) + */ + struct s_breakpoint breakpoint={0}; + unsigned char *brkschunk=NULL; + unsigned int idx=8; + + /* add labels and local labels to breakpoint pool (if any) */ + for (i=0;i<ae->il;i++) { + if (!ae->label[i].name) { + if (strncmp(ae->wl[ae->label[i].iw].w,"BRK",3)==0) { + breakpoint.address=ae->label[i].ptr; + if (ae->label[i].ibank>3) breakpoint.bank=1; else breakpoint.bank=0; + ObjectArrayAddDynamicValueConcat((void **)&ae->breakpoint,&ae->ibreakpoint,&ae->maxbreakpoint,&breakpoint,sizeof(struct s_breakpoint)); + } + } else { + if (strncmp(ae->label[i].name,"@BRK",4)==0 || strstr(ae->label[i].name,".BRK")) { + breakpoint.address=ae->label[i].ptr; + if (ae->label[i].ibank>3) breakpoint.bank=1; else breakpoint.bank=0; + ObjectArrayAddDynamicValueConcat((void **)&ae->breakpoint,&ae->ibreakpoint,&ae->maxbreakpoint,&breakpoint,sizeof(struct s_breakpoint)); + } + } + } + + brkschunk=MemMalloc(ae->ibreakpoint*5+8); + strcpy((char *)brkschunk,"BRKS"); + + for (i=0;i<ae->ibreakpoint;i++) { + brkschunk[idx++]=ae->breakpoint[i].address&0xFF; + brkschunk[idx++]=(ae->breakpoint[i].address&0xFF00)/256; + brkschunk[idx++]=ae->breakpoint[i].bank; + brkschunk[idx++]=0; + brkschunk[idx++]=0; + } + + idx-=8; + brkschunk[4]=idx&0xFF; + brkschunk[5]=(idx>>8)&0xFF; + brkschunk[6]=(idx>>16)&0xFF; + brkschunk[7]=(idx>>24)&0xFF; + FileWriteBinary(TMP_filename,(char*)brkschunk,idx+8); // 8 bytes for the chunk header + MemFree(brkschunk); + + + /* BRKC chunk for ACE emulator + minimal integration + */ + brkschunk=MemMalloc(ae->ibreakpoint*256); + strcpy((char *)brkschunk,"BRKC"); + idx=8; + + for (i=0;i<ae->ibreakpoint;i++) { + brkschunk[idx++]=0; /* 0:Execution */ + brkschunk[idx++]=0; + brkschunk[idx++]=0; + brkschunk[idx++]=0; + brkschunk[idx++]=ae->breakpoint[i].address&0xFF; + brkschunk[idx++]=(ae->breakpoint[i].address&0xFF00)/256; + for (j=0;j<2+1+1+2+4+128;j++) { + brkschunk[idx++]=0; + } + sprintf((char *)brkschunk+idx,"breakpoint%d",i); /* breakpoint user name? */ + idx+=64+8; + } + idx-=8; + brkschunk[4]=idx&0xFF; + brkschunk[5]=(idx>>8)&0xFF; + brkschunk[6]=(idx>>16)&0xFF; + brkschunk[7]=(idx>>24)&0xFF; + FileWriteBinary(TMP_filename,(char *)brkschunk,idx+8); // 8 bytes for the chunk header + MemFree(brkschunk); + } + /* export optionnel des symboles */ + if (ae->export_sna) { + /* SYMB chunk for ACE emulator + + 1 byte - name size + n bytes - name (without 0 to end the string) + 6 bytes - reserved for future use + 2 bytes - shitty big endian adress for the symbol + */ + + unsigned char *symbchunk=NULL; + unsigned int idx=8; + int symbol_len; + + symbchunk=MemMalloc(8+ae->il*(1+255+6+2)); + strcpy((char *)symbchunk,"SYMB"); + + for (i=0;i<ae->il;i++) { + if (!ae->label[i].name) { + symbol_len=strlen(ae->wl[ae->label[i].iw].w); + if (symbol_len>255) symbol_len=255; + symbchunk[idx++]=symbol_len; + memcpy(symbchunk+idx,ae->wl[ae->label[i].iw].w,symbol_len); + idx+=symbol_len; + memset(symbchunk+idx,0,6); + idx+=6; + symbchunk[idx++]=(ae->label[i].ptr&0xFF00)/256; + symbchunk[idx++]=ae->label[i].ptr&0xFF; + } else { + if (ae->export_local || !ae->label[i].local) { + symbol_len=strlen(ae->label[i].name); + if (symbol_len>255) symbol_len=255; + symbchunk[idx++]=symbol_len; + memcpy(symbchunk+idx,ae->label[i].name,symbol_len); + idx+=symbol_len; + memset(symbchunk+idx,0,6); + idx+=6; + symbchunk[idx++]=(ae->label[i].ptr&0xFF00)/256; + symbchunk[idx++]=ae->label[i].ptr&0xFF; + } + } + } + if (ae->export_var) { + unsigned char *subchunk=NULL; + int retidx=0; + /* var are part of fast tree search structure */ + subchunk=SnapshotDicoTree(ae,&retidx); + if (retidx) { + symbchunk=MemRealloc(symbchunk,idx+retidx); + memcpy(symbchunk+idx,subchunk,retidx); + idx+=retidx; + SnapshotDicoInsert("FREE",0,&retidx); + } + } + if (ae->export_equ) { + symbchunk=MemRealloc(symbchunk,idx+ae->ialias*(1+255+6+2)); + + for (i=0;i<ae->ialias;i++) { + int tmpptr; + symbol_len=strlen(ae->alias[i].alias); + if (symbol_len>255) symbol_len=255; + symbchunk[idx++]=symbol_len; + memcpy(symbchunk+idx,ae->alias[i].alias,symbol_len); + idx+=symbol_len; + memset(symbchunk+idx,0,6); + idx+=6; + tmpptr=RoundComputeExpression(ae,ae->alias[i].translation,0,0,0); + symbchunk[idx++]=(tmpptr&0xFF00)/256; + symbchunk[idx++]=tmpptr&0xFF; + } + } + idx-=8; + symbchunk[4]=idx&0xFF; + symbchunk[5]=(idx>>8)&0xFF; + symbchunk[6]=(idx>>16)&0xFF; + symbchunk[7]=(idx>>24)&0xFF; + FileWriteBinary(TMP_filename,(char*)symbchunk,idx+8); // 8 bytes for the chunk header + } + } else { + if (ae->export_snabrk) { + if (!ae->nowarning) rasm_printf(ae,KWARNING"Warning: breakpoint export is not supported with snapshot version 2\n"); + } + if (ae->export_sna) { + if (!ae->nowarning) rasm_printf(ae,KWARNING"Warning: symbol export is not supported with snapshot version 2\n"); + } + } + + FileWriteBinaryClose(TMP_filename); + maxrom=(maxrom>>2)*4+4; + rasm_printf(ae,KAYGREEN"Total %d bank%s (%dK)\n",maxrom,maxrom>1?"s":"",(maxrom)*16); + } + } + /********************************************* + ********************************************** + B I N A R Y F I L E + ********************************************** + *********************************************/ + } else { + int lastspaceid=-1; + + if (ae->binary_name) { + sprintf(TMP_filename,"%s",ae->binary_name); + } else { + sprintf(TMP_filename,"%s.bin",ae->outputfilename); + } + FileRemoveIfExists(TMP_filename); + + /* en mode binaire classique on va recherche le dernier espace mémoire dans lequel on a travaillé qui n'est pas en 'nocode' */ + for (i=0;i<ae->io;i++) { + /* uniquement si le ORG a ete suivi d'ecriture */ + if (ae->orgzone[i].memstart!=ae->orgzone[i].memend && ae->orgzone[i].nocode!=1) { + lastspaceid=ae->orgzone[i].ibank; + } + } + if (lastspaceid!=-1) { + for (i=0;i<ae->io;i++) { + if (ae->orgzone[i].protect) continue; /* protected zones exclusion */ + /* uniquement si le ORG a ete suivi d'ecriture et n'est pas en 'nocode' */ + if (ae->orgzone[i].ibank==lastspaceid && ae->orgzone[i].memstart!=ae->orgzone[i].memend && ae->orgzone[i].nocode!=1) { + if (ae->orgzone[i].memstart<minmem) minmem=ae->orgzone[i].memstart; + if (ae->orgzone[i].memend>maxmem) maxmem=ae->orgzone[i].memend; + } + } + } + if (maxmem-minmem<=0) { + if (!ae->stop) { + if (!ae->nowarning) rasm_printf(ae,KWARNING"Warning: Not a single byte to output\n"); + } + if (ae->flux) { + *lenout=0; + } + } else { + if (!ae->flux) { + rasm_printf(ae,KIO"Write binary file %s (%d byte%s)\n",TMP_filename,maxmem-minmem,maxmem-minmem>1?"s":""); + if (ae->amsdos) { + AmsdosHeader=MakeAMSDOSHeader(minmem,minmem,maxmem,TMP_filename); //@@TODO + FileWriteBinary(TMP_filename,(char *)AmsdosHeader,128); + } + if (maxmem-minmem>0) { + FileWriteBinary(TMP_filename,(char*)ae->mem[lastspaceid]+minmem,maxmem-minmem); + FileWriteBinaryClose(TMP_filename); + } else { + if (ae->amsdos) { + FileWriteBinaryClose(TMP_filename); + } + } + } else { + *dataout=MemMalloc(maxmem-minmem+1); + memcpy(*dataout,ae->mem[lastspaceid]+minmem,maxmem-minmem); + *lenout=maxmem-minmem; + } + } + } + } + /******************************** + ********************************* + U N U S E D W A R N I N G + ********************************* + ********************************/ + if (ae->warn_unused) { + for (i=0;i<ae->ialias;i++) { + if (strcmp(ae->alias[i].alias,"IX") && strcmp(ae->alias[i].alias,"IY")) { + if (!ae->alias[i].used) { + rasm_printf(ae,KWARNING"[%s:%d] Warning: alias %s declared but not used\n",ae->filename[ae->wl[ae->alias[i].iw].ifile],ae->wl[ae->alias[i].iw].l,ae->alias[i].alias); + } + } + } + WarnLabelTree(ae); + WarnDicoTree(ae); + } + + /**************************** + ***************************** + S Y M B O L E X P O R T + ***************************** + ****************************/ + if (ae->export_sym && !ae->export_sna) { + char *SymbolFileName; + SymbolFileName=MemMalloc(PATH_MAX); + +#define MAKE_SYMBOL_NAME if (ae->symbol_name) {sprintf(TMP_filename,"%s",ae->symbol_name);} else {sprintf(TMP_filename,"%s.sym",ae->outputfilename);} + + MAKE_SYMBOL_NAME + FileRemoveIfExists(TMP_filename); + + if (ae->export_multisym) { + /* multi-remove before writes */ + for (i=0;i<ae->nbbank;i++) { + if (ae->symbol_name) { + sprintf(TMP_filename,"%s.bank%d",ae->symbol_name,i); + } else { + sprintf(TMP_filename,"%s.sym.bank%d",ae->outputfilename,i); + } + FileRemoveIfExists(TMP_filename); + } + rasm_printf(ae,KIO"Write symbol files %s.bank*\n",TMP_filename); + } else { + rasm_printf(ae,KIO"Write symbol file %s\n",TMP_filename); + } + + switch (ae->export_sym) { + case 5: + /* ZX export */ + for (i=0;i<ae->il;i++) { + if (ae->label[i].autorise_export) { + if (ae->export_multisym) { + if (ae->symbol_name) { + sprintf(TMP_filename,"%s.bank%d",ae->symbol_name,ae->label[i].ibank); + } else { + sprintf(TMP_filename,"%s.sym.bank%d",ae->outputfilename,ae->label[i].ibank); + } + } + + if (!ae->label[i].name) { + sprintf(symbol_line,"%d:%04X %s\n",ae->label[i].ibank,ae->label[i].ptr,ae->wl[ae->label[i].iw].w); + FileWriteLine(TMP_filename,symbol_line); + } else { + if (ae->export_local || !ae->label[i].local) { + sprintf(symbol_line,"%d:%04X %s\n",ae->label[i].ibank,ae->label[i].ptr,ae->label[i].name); + FileWriteLine(TMP_filename,symbol_line); + } + } + } + } + FileWriteLineClose(TMP_filename); + MAKE_SYMBOL_NAME + if (ae->export_var) { + /* var are part of fast tree search structure */ + ExportDicoTree(ae,TMP_filename,"%s %04X"); + } + if (ae->export_equ) { + for (i=0;i<ae->ialias;i++) { + if (strcmp(ae->alias[i].alias,"IX") && strcmp(ae->alias[i].alias,"IY")) { + sprintf(symbol_line,"%04X %s\n",RoundComputeExpression(ae,ae->alias[i].translation,0,-ae->alias[i].iw,0),ae->alias[i].alias); + FileWriteLine(TMP_filename,symbol_line); + } + } + } + break; + case 4: + /* flexible */ + for (i=0;i<ae->il;i++) { + if (ae->label[i].autorise_export) { + if (ae->export_multisym) { + if (ae->symbol_name) { + sprintf(TMP_filename,"%s.bank%d",ae->symbol_name,ae->label[i].ibank); + } else { + sprintf(TMP_filename,"%s.sym.bank%d",ae->outputfilename,ae->label[i].ibank); + } + } + + if (!ae->label[i].name) { + sprintf(symbol_line,ae->flexible_export,ae->wl[ae->label[i].iw].w,ae->label[i].ptr); + FileWriteLine(TMP_filename,symbol_line); + } else { + if (ae->export_local || !ae->label[i].local) { + sprintf(symbol_line,ae->flexible_export,ae->label[i].name,ae->label[i].ptr); + FileWriteLine(TMP_filename,symbol_line); + } + } + } + } + MAKE_SYMBOL_NAME + if (ae->export_var) { + /* var are part of fast tree search structure */ + ExportDicoTree(ae,TMP_filename,ae->flexible_export); + } + if (ae->export_equ) { + for (i=0;i<ae->ialias;i++) { + if (strcmp(ae->alias[i].alias,"IX") && strcmp(ae->alias[i].alias,"IY")) { + sprintf(symbol_line,ae->flexible_export,ae->alias[i].alias,RoundComputeExpression(ae,ae->alias[i].translation,0,-ae->alias[i].iw,0)); + FileWriteLine(TMP_filename,symbol_line); + } + } + } + FileWriteLineClose(TMP_filename); + break; + case 3: + /* winape */ + for (i=0;i<ae->il;i++) { + if (ae->label[i].autorise_export) { + if (ae->export_multisym) { + if (ae->symbol_name) { + sprintf(TMP_filename,"%s.bank%d",ae->symbol_name,ae->label[i].ibank); + } else { + sprintf(TMP_filename,"%s.sym.bank%d",ae->outputfilename,ae->label[i].ibank); + } + } + if (!ae->label[i].name) { + sprintf(symbol_line,"%s #%04X\n",ae->wl[ae->label[i].iw].w,ae->label[i].ptr); + FileWriteLine(TMP_filename,symbol_line); + } else { + if (ae->export_local || !ae->label[i].local) { + sprintf(symbol_line,"%s #%04X\n",ae->label[i].name,ae->label[i].ptr); + FileWriteLine(TMP_filename,symbol_line); + } + } + } + } + MAKE_SYMBOL_NAME + if (ae->export_var) { + /* var are part of fast tree search structure */ + ExportDicoTree(ae,TMP_filename,"%s #%04X\n"); + } + if (ae->export_equ) { + for (i=0;i<ae->ialias;i++) { + if (strcmp(ae->alias[i].alias,"IX") && strcmp(ae->alias[i].alias,"IY")) { + sprintf(symbol_line,"%s #%04X\n",ae->alias[i].alias,RoundComputeExpression(ae,ae->alias[i].translation,0,-ae->alias[i].iw,0)); + FileWriteLine(TMP_filename,symbol_line); + } + } + } + FileWriteLineClose(TMP_filename); + break; + case 2: + /* pasmo */ + for (i=0;i<ae->il;i++) { + if (ae->label[i].autorise_export) { + if (ae->export_multisym) { + if (ae->symbol_name) { + sprintf(TMP_filename,"%s.bank%d",ae->symbol_name,ae->label[i].ibank); + } else { + sprintf(TMP_filename,"%s.sym.bank%d",ae->outputfilename,ae->label[i].ibank); + } + } + if (!ae->label[i].name) { + sprintf(symbol_line,"%s EQU 0%04XH\n",ae->wl[ae->label[i].iw].w,ae->label[i].ptr); + FileWriteLine(TMP_filename,symbol_line); + } else { + if (ae->export_local || !ae->label[i].local) { + sprintf(symbol_line,"%s EQU 0%04XH\n",ae->label[i].name,ae->label[i].ptr); + FileWriteLine(TMP_filename,symbol_line); + } + } + } + } + MAKE_SYMBOL_NAME + if (ae->export_var) { + /* var are part of fast tree search structure */ + ExportDicoTree(ae,TMP_filename,"%s EQU 0%04XH\n"); + } + if (ae->export_equ) { + for (i=0;i<ae->ialias;i++) { + if (strcmp(ae->alias[i].alias,"IX") && strcmp(ae->alias[i].alias,"IY")) { + sprintf(symbol_line,"%s EQU 0%04XH\n",ae->alias[i].alias,RoundComputeExpression(ae,ae->alias[i].translation,0,-ae->alias[i].iw,0)); + FileWriteLine(TMP_filename,symbol_line); + } + } + } + FileWriteLineClose(TMP_filename); + break; + case 1: + /* Rasm */ + for (i=0;i<ae->il;i++) { + if (ae->label[i].autorise_export) { + if (ae->export_multisym) { + if (ae->symbol_name) { + sprintf(TMP_filename,"%s.bank%d",ae->symbol_name,ae->label[i].ibank); + } else { + sprintf(TMP_filename,"%s.sym.bank%d",ae->outputfilename,ae->label[i].ibank); + } + } + if (!ae->label[i].name) { + sprintf(symbol_line,"%s #%X B%d\n",ae->wl[ae->label[i].iw].w,ae->label[i].ptr,ae->label[i].ibank>31?0:ae->label[i].ibank); + FileWriteLine(TMP_filename,symbol_line); + } else { + if (ae->export_local) { + sprintf(symbol_line,"%s #%X B%d\n",ae->label[i].name,ae->label[i].ptr,ae->label[i].ibank>31?0:ae->label[i].ibank); + FileWriteLine(TMP_filename,symbol_line); + } + } + } + } + MAKE_SYMBOL_NAME + if (ae->export_var) { + /* var are part of fast tree search structure */ + ExportDicoTree(ae,TMP_filename,"%s #%X B0\n"); + } + if (ae->export_equ) { + for (i=0;i<ae->ialias;i++) { + if (strcmp(ae->alias[i].alias,"IX") && strcmp(ae->alias[i].alias,"IY") && ae->alias[i].autorise_export) { + sprintf(symbol_line,"%s #%X B0\n",ae->alias[i].alias,RoundComputeExpression(ae,ae->alias[i].translation,0,-ae->alias[i].iw,0)); + FileWriteLine(TMP_filename,symbol_line); + } + } + } + FileWriteLineClose(TMP_filename); + break; + case 0: + default:break; + } + MemFree(SymbolFileName); + } + /********************************* + ********************************** + B R E A K P O I N T S + ********************************** + *********************************/ + if (ae->export_brk) { + struct s_breakpoint breakpoint={0}; + + if (ae->breakpoint_name) { + sprintf(TMP_filename,"%s",ae->breakpoint_name); + } else { + sprintf(TMP_filename,"%s.brk",ae->outputfilename); + } + FileRemoveIfExists(TMP_filename); + + /* add labels and local labels to breakpoint pool (if any) */ + for (i=0;i<ae->il;i++) { + if (!ae->label[i].name) { + if (strncmp(ae->wl[ae->label[i].iw].w,"BRK",3)==0) { + breakpoint.address=ae->label[i].ptr; + if (ae->label[i].ibank>3) breakpoint.bank=1; else breakpoint.bank=0; + ObjectArrayAddDynamicValueConcat((void **)&ae->breakpoint,&ae->ibreakpoint,&ae->maxbreakpoint,&breakpoint,sizeof(struct s_breakpoint)); + } + } else { + if (strncmp(ae->label[i].name,"@BRK",4)==0) { + breakpoint.address=ae->label[i].ptr; + if (ae->label[i].ibank>3) breakpoint.bank=1; else breakpoint.bank=0; + ObjectArrayAddDynamicValueConcat((void **)&ae->breakpoint,&ae->ibreakpoint,&ae->maxbreakpoint,&breakpoint,sizeof(struct s_breakpoint)); + } + } + } + + if (ae->ibreakpoint) { + rasm_printf(ae,KIO"Write breakpoint file %s\n",TMP_filename); + for (i=0;i<ae->ibreakpoint;i++) { + sprintf(symbol_line,"#%04X\n",ae->breakpoint[i].address); + FileWriteLine(TMP_filename,symbol_line); + } + FileWriteLineClose(TMP_filename); + } else { + if (!ae->nowarning) rasm_printf(ae,KWARNING"Warning: no breakpoint to output (previous file [%s] deleted anyway)\n",TMP_filename); + } + } + + } else { + if (!ae->dependencies) rasm_printf(ae,KERROR"%d error%s\n",ae->nberr,ae->nberr>1?"s":""); + } +#if TRACE_ASSEMBLE +printf("dependencies\n"); +#endif +/******************************************************************************************* + E X P O R T D E P E N D E N C I E S +*******************************************************************************************/ + if (ae->dependencies) { + int trigdep=0; + + /* depends ALL */ + if (ae->outputfilename && strcmp(ae->outputfilename,"rasmoutput")) { + trigdep=1; + printf("%s",ae->outputfilename); + if (ae->dependencies==E_DEPENDENCIES_MAKE) printf(" "); else printf("\n"); + } + for (i=1;i<ae->ifile;i++) { + trigdep=1; + SimplifyPath(ae->filename[i]); + printf("%s",ae->filename[i]); + if (ae->dependencies==E_DEPENDENCIES_MAKE) printf(" "); else printf("\n"); + } + for (i=0;i<ae->ih;i++) { + trigdep=1; + SimplifyPath(ae->hexbin[i].filename); + printf("%s",ae->hexbin[i].filename); + if (ae->dependencies==E_DEPENDENCIES_MAKE) printf(" "); else printf("\n"); + } + if (ae->dependencies==E_DEPENDENCIES_MAKE && trigdep) printf("\n"); + } + +/******************************************************************************************* + V E R B O S E S H I T +*******************************************************************************************/ +#if TRACE_ASSEMBLE + rasm_printf(ae,KVERBOSE"------ statistics ------------------\n"); + rasm_printf(ae,KVERBOSE"%d file%s\n",ae->ifile,ae->ifile>1?"s":""); + rasm_printf(ae,KVERBOSE"%d binary include%s\n",ae->ih,ae->ih>1?"s":""); + rasm_printf(ae,KVERBOSE"%d word%s\n",ae->nbword-1,ae->nbword>2?"s":""); + rasm_printf(ae,KVERBOSE"%d label%s\n",ae->il,ae->il>1?"s":""); + rasm_printf(ae,KVERBOSE"%d struct%s\n",ae->irasmstruct,ae->irasmstruct>1?"s":""); + rasm_printf(ae,KVERBOSE"%d var%s\n",ae->idic,ae->idic>1?"s":""); + rasm_printf(ae,KVERBOSE"%d expression%s\n",ae->ie,ae->ie>1?"s":""); + rasm_printf(ae,KVERBOSE"%d macro%s\n",ae->imacro,ae->imacro>1?"s":""); + rasm_printf(ae,KVERBOSE"%d alias%s\n",ae->ialias,ae->ialias>1?"s":""); + rasm_printf(ae,KVERBOSE"%d ORG zone%s\n",ae->io-1,ae->io>2?"s":""); + rasm_printf(ae,KVERBOSE"%d virtual space%s\n",ae->nbbank,ae->nbbank>1?"s":""); +#endif + +/******************************************************************************************* + C L E A N U P +*******************************************************************************************/ +#if TRACE_ASSEMBLE +printf("cleanup\n"); +#endif +#if TRACE_GENERALE +printf("-cleanup\n"); +#endif + if (TMP_filename) MemFree(TMP_filename); + if (ae->nberr) { + ok=-1; + if (ae->flux && *dataout) { + MemFree(*dataout); + *dataout=NULL; + } + if (lenout) *lenout=0; + } else { + ok=0; + } + + FreeAssenv(ae); +#if TRACE_ASSEMBLE +printf("end of assembling\n"); +#endif +#if TRACE_GENERALE +printf("-end ok=%d\n",ok); +#endif + return ok; +} + + +void EarlyPrepSrc(struct s_assenv *ae, char **listing, char *filename) { + int l,idx,c,quote_type=0; + int mlc_start,mlc_idx; + + /* virer les commentaires en ;, // mais aussi multi-lignes et convertir les decalages, passer les chars en upper case */ + l=idx=0; + while (listing[l]) { + c=listing[l][idx++]; + + if (!c) { + l++; + idx=0; + continue; + } else if (!quote_type) { + /* upper case */ + if (c>='a' && c<='z') { + listing[l][idx-1]=c=c-'a'+'A'; + } + + if (c=='\'' && idx>2 && strncmp(&listing[l][idx-3],"AF'",3)==0) { + /* il ne faut rien faire */ + } else if (c=='"' || c=='\'') { + quote_type=c; + } else if (c==';' || (c=='/' && listing[l][idx]=='/')) { + idx--; + while (listing[l][idx] && listing[l][idx]!=0x0D && listing[l][idx]!=0x0A) listing[l][idx++]=':'; + idx--; + } else if (c=='>' && listing[l][idx]=='>' && !quote_type) { + listing[l][idx-1]=']'; + listing[l][idx++]=' '; + continue; + } else if (c=='<' && listing[l][idx]=='<' && !quote_type) { + listing[l][idx-1]='['; + listing[l][idx++]=' '; + continue; + } else if (c=='/' && listing[l][idx]=='*' && !quote_type) { + /* multi-line comment */ + mlc_start=l; + mlc_idx=idx-1; + idx++; + while (1) { + c=listing[l][idx++]; + if (!c) { + idx=0; + l++; + if (!listing[l]) { + MakeError(ae,GetCurrentFile(ae),ae->wl[ae->idx].l,"opened comment to the end of the file\n",filename,l+1); + return; + } + } else if (c=='*' && listing[l][idx]=='/') { + idx++; + break; + } + } + /* merge */ + if (mlc_start==l) { + /* on the same line */ + while (mlc_idx<idx) listing[l][mlc_idx++]=' '; /* raz with spaces */ + } else { + /* multi-line */ + listing[mlc_start][mlc_idx]=0; /* raz EOL */ + mlc_start++; + while (mlc_start<l) listing[mlc_start++][0]=0; /* raz line */ + mlc_idx=0; + while (mlc_idx<idx) listing[l][mlc_idx++]=' '; /* raz beginning of the line */ + } + } + } else { + /* in quote */ + if (c=='\\') { + if (listing[l][idx]) { + idx++; + } + } else if (c==quote_type) { + quote_type=0; + } + } + } +} + +void PreProcessingSplitListing(struct s_listing **listing, int *il, int *ml, int idx, int end, int start) +{ + #undef FUNC + #define FUNC "PreProcessingSplitListing" + + struct s_listing curlisting={0}; + + /* split current line because there will be "before" include and "after include" line */ + ObjectArrayAddDynamicValueConcat((void**)listing,il,ml,&curlisting,sizeof(curlisting)); + MemMove(&((*listing)[idx+2]),&((*listing)[idx+1]),(*il-idx-2)*sizeof(struct s_listing)); + (*listing)[idx+1].ifile=(*listing)[idx].ifile; + (*listing)[idx+1].iline=(*listing)[idx].iline; + if ((*listing)[idx].listing[start]) { + (*listing)[idx+1].listing=TxtStrDup((*listing)[idx].listing+start); + } else { + (*listing)[idx+1].listing=TxtStrDup(";"); + } + strcpy((*listing)[idx].listing+end,":"); +} + +void PreProcessingInsertListing(struct s_listing **reflisting, int *il, int *ml, int idx, char **zelines, int ifile) +{ + #undef FUNC + #define FUNC "PreProcessingSplitListing" + + struct s_listing *listing; + int nbinsert,li,bil; + for (li=nbinsert=0;zelines[li];li++) nbinsert++; + bil=*il; + if (*il+nbinsert>=*ml) { + *il=*ml=*il+nbinsert; + *reflisting=MemRealloc(*reflisting,sizeof(struct s_listing)*(*ml)); + } else { + *il=*il+nbinsert; + } + listing=*reflisting; + MemMove(&listing[idx+1+nbinsert],&listing[idx+1],(bil-idx-1)*sizeof(struct s_listing)); + + for (li=0;zelines[li];li++) { + listing[idx+1+li].ifile=ifile; + listing[idx+1+li].iline=li+1; + listing[idx+1+li].listing=zelines[li]; + } +} + +int cmpkeyword(const void * a, const void * b) +{ + struct s_asm_keyword *sa,*sb; + sa=(struct s_asm_keyword *)a; + sb=(struct s_asm_keyword *)b; + return strcmp(sa->mnemo,sb->mnemo); +} + +struct s_assenv *PreProcessing(char *filename, int flux, const char *datain, int datalen, struct s_parameter *param) +{ + #undef FUNC + #define FUNC "PreProcessing" + + #define CharWord "@ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.=_($)][+-*/^%#|&'\"\\m}{[]" + + struct s_assenv *ae=NULL; + struct s_wordlist curw={0}; + struct s_wordlist *wordlist=NULL; + int nbword=0,maxword=0; + char **zelines=NULL; + + char *filename_toread; + + struct s_macro_fast *MacroFast=NULL; + int idxmacrofast=0,maxmacrofast=0; + + struct s_listing *listing=NULL; + struct s_listing curlisting; + int ilisting=0,maxlisting=0; + + char **listing_include=NULL; + int i,j,l=0,idx=0,c=0,li,le; + char Automate[256]={0}; + struct s_hexbin curhexbin; + char *newlistingline=NULL; + unsigned char *newdata; + struct s_label curlabel={0}; + char *labelsep1; + char **labelines=NULL; + /* state machine buffer */ + unsigned char *mem=NULL; + char *w=NULL,*wtmp=NULL; + int lw=0,mw=256; + char *bval=NULL; + int ival=0,sval=256; + char *qval=NULL; + int iqval=0,sqval=256; + struct s_repeat_index *TABrindex=NULL; + struct s_repeat_index *TABwindex=NULL; + struct s_repeat_index rindex={0}; + struct s_repeat_index windex={0}; + int nri=0,mri=0,ri=0; + int nwi=0,mwi=0,wi=0; + /* state machine trigger */ + int waiting_quote=0,lquote; + int macro_trigger=0; + int escape_code=0; + int quote_type=0; + int incbin=0,include=0,crunch=0; + int rewrite=0,hadcomma=0; + int nbinstruction; + int ifast,texpr; + int ispace=0; + +#if TRACE_GENERALE +printf("*** preprocessing ***\n"); +#endif + +#if TRACE_PREPRO +printf("start prepro, alloc assenv\n"); +#endif + + windex.cl=-1; + windex.cidx=-1; + rindex.cl=-1; + rindex.cidx=-1; + +#if TRACE_PREPRO +printf("malloc\n"); +#endif + ae=MemMalloc(sizeof(struct s_assenv)); +#if TRACE_PREPRO +printf("memset\n"); +#endif + memset(ae,0,sizeof(struct s_assenv)); + +#if TRACE_PREPRO +printf("paramz 1\n"); +#endif + if (param) { + ae->export_local=param->export_local; + ae->export_sym=param->export_sym; + ae->export_var=param->export_var; + ae->export_equ=param->export_equ; + ae->export_sna=param->export_sna; + ae->export_snabrk=param->export_snabrk; + if (param->export_sna || param->export_snabrk) { + ae->forcesnapshot=1; + } + ae->export_brk=param->export_brk; + ae->warn_unused=param->warn_unused; + ae->edskoverwrite=param->edskoverwrite; + ae->rough=param->rough; + ae->as80=param->as80; + ae->dams=param->dams; + ae->macrovoid=param->macrovoid; + if (param->v2) { + ae->forcesnapshot=1; + ae->snapshot.version=2; + } else { + ae->snapshot.version=3; + } + ae->maxerr=param->maxerr; + ae->extended_error=param->extended_error; + ae->nowarning=param->nowarning; + ae->breakpoint_name=param->breakpoint_name; + ae->symbol_name=param->symbol_name; + ae->binary_name=param->binary_name; + ae->flexible_export=param->flexible_export; + ae->cartridge_name=param->cartridge_name; + ae->snapshot_name=param->snapshot_name; + ae->checkmode=param->checkmode; + if (param->rough) ae->maxam=0; else ae->maxam=1; + /* additional symbols */ + for (i=0;i<param->nsymb;i++) { + char *sep; + sep=strchr(param->symboldef[i],'='); + if (sep) { + *sep=0; + ExpressionSetDicoVar(ae,param->symboldef[i],atof(sep+1)); + } + } + if (param->msymb) { + MemFree(param->symboldef); + param->nsymb=param->msymb=0; + } + /* include paths */ + ae->includepath=param->pathdef; + ae->ipath=param->npath; + ae->mpath=param->mpath; + /* old inline params */ + ae->dependencies=param->dependencies; + } +#if TRACE_PREPRO +printf("init 0\n"); +#endif +#if TRACE_GENERALE +printf("-init\n"); +#endif + /* generic init */ + ae->ctx1.maxivar=1; + ae->ctx2.maxivar=1; + ae->computectx=&ae->ctx1; + ae->flux=flux; + /* check snapshot structure */ + if (sizeof(ae->snapshot)!=0x100 || &ae->snapshot.fdd.motorstate-(unsigned char*)&ae->snapshot!=0x9C || &ae->snapshot.crtcstate.model-(unsigned char*)&ae->snapshot!=0xA4 + || &ae->snapshot.romselect-(unsigned char*)&ae->snapshot!=0x55 + || &ae->snapshot.interruptrequestflag-(unsigned char*)&ae->snapshot!=0xB4 + || &ae->snapshot.CPCType-(unsigned char*)&ae->snapshot!=0x6D) { + rasm_printf(ae,"snapshot structure integrity check KO\n"); + exit(349); + } + + for (i=0;i<4;i++) { + ae->bankgate[i]=0x7FC0; /* video memory has no paging */ + ae->setgate[i]=0x7FC0; /* video memory has no paging */ + } + for (i=0;i<256;i++) { + /* 4M expansion support on lower gate array port */ + ae->bankgate[i+4]=0x7FC4+(i&3)+((i&31)>>2)*8-0x100*(i>>5); + ae->setgate[i+4] =0x7FC2 +((i&31)>>2)*8-0x100*(i>>5); + //printf("%04X %04X\n",ae->bankgate[i+4],ae->setgate[i+4]); + } + + memcpy(ae->snapshot.idmark,"MV - SNA",8); + ae->snapshot.registers.IM=1; + + ae->snapshot.gatearray.palette[0]=0x04; + ae->snapshot.gatearray.palette[1]=0x0A; + ae->snapshot.gatearray.palette[2]=0x15; + ae->snapshot.gatearray.palette[3]=0x1C; + ae->snapshot.gatearray.palette[4]=0x18; + ae->snapshot.gatearray.palette[5]=0x1D; + ae->snapshot.gatearray.palette[6]=0x0C; + ae->snapshot.gatearray.palette[7]=0x05; + ae->snapshot.gatearray.palette[8]=0x0D; + ae->snapshot.gatearray.palette[9]=0x16; + ae->snapshot.gatearray.palette[10]=0x06; + ae->snapshot.gatearray.palette[11]=0x17; + ae->snapshot.gatearray.palette[12]=0x1E; + ae->snapshot.gatearray.palette[13]=0x00; + ae->snapshot.gatearray.palette[14]=0x1F; + ae->snapshot.gatearray.palette[15]=0x0E; + ae->snapshot.gatearray.palette[16]=0x04; + + ae->snapshot.gatearray.multiconfiguration=0x8D; // lower/upper ROM off + mode 1 + ae->snapshot.CPCType=2; /* 6128 */ + ae->snapshot.crtcstate.model=0; /* CRTC 0 */ + ae->snapshot.vsyncdelay=2; + strcpy((char *)ae->snapshot.unused6+3+0x20+8,RASM_VERSION); + /* CRTC default registers */ + ae->snapshot.crtc.registervalue[0]=0x3F; + ae->snapshot.crtc.registervalue[1]=40; + ae->snapshot.crtc.registervalue[2]=46; + ae->snapshot.crtc.registervalue[3]=0x8E; + ae->snapshot.crtc.registervalue[4]=38; + ae->snapshot.crtc.registervalue[6]=25; + ae->snapshot.crtc.registervalue[7]=30; + ae->snapshot.crtc.registervalue[9]=7; + ae->snapshot.crtc.registervalue[12]=0x30; + ae->snapshot.psg.registervalue[7]=0x3F; /* audio mix all channels OFF */ + /* PPI Init */ + ae->snapshot.ppi.control=0x82; + /* standard stack */ + ae->snapshot.registers.HSP=0xC0; + + /* + winape sprintf(symbol_line,"%s #%4X\n",ae->label[i].name,ae->label[i].ptr); + pasmo sprintf(symbol_line,"%s EQU 0%4XH\n",ae->label[i].name,ae->label[i].ptr); + rasm sprintf(symbol_line,"%s #%X B%d\n",ae->wl[ae->label[i].iw].w,ae->label[i].ptr,ae->label[i].ibank>31?0:ae->label[i].ibank); + */ +#if TRACE_PREPRO +printf("paramz\n"); +#endif + if (param && param->labelfilename) { + for (j=0;param->labelfilename[j] && param->labelfilename[j][0];j++) { + rasm_printf(ae,"Label import from [%s]\n",param->labelfilename[j]); + ae->label_filename=param->labelfilename[j]; + ae->label_line=1; + labelines=FileReadLines(param->labelfilename[j]); + i=0; + while (labelines[i]) { + /* upper case */ + for (j=0;labelines[i][j];j++) labelines[i][j]=toupper(labelines[i][j]); + + if ((labelsep1=strstr(labelines[i],": EQU 0"))!=NULL) { + /* sjasm */ + *labelsep1=0; + curlabel.name=labelines[i]; + curlabel.iw=-1; + curlabel.crc=GetCRC(curlabel.name); + curlabel.ptr=strtol(labelsep1+6,NULL,16); + PushLabelLight(ae,&curlabel); + } else if ((labelsep1=strstr(labelines[i]," EQU 0"))!=NULL) { + /* pasmo */ + *labelsep1=0; + curlabel.name=labelines[i]; + curlabel.iw=-1; + curlabel.crc=GetCRC(curlabel.name); + curlabel.ptr=strtol(labelsep1+6,NULL,16); + //ObjectArrayAddDynamicValueConcat((void **)&ae->label,&ae->il,&ae->ml,&curlabel,sizeof(curlabel)); + PushLabelLight(ae,&curlabel); + } else if ((labelsep1=strstr(labelines[i]," "))!=NULL) { + /* winape / rasm */ + if (*(labelsep1+1)=='#') { + *labelsep1=0; + curlabel.name=labelines[i]; + curlabel.iw=-1; + curlabel.crc=GetCRC(curlabel.name); + curlabel.ptr=strtol(labelsep1+2,NULL,16); + //ObjectArrayAddDynamicValueConcat((void **)&ae->label,&ae->il,&ae->ml,&curlabel,sizeof(curlabel)); + PushLabelLight(ae,&curlabel); + } + } + i++; + ae->label_line++; + } + MemFree(labelines); + } + ae->label_filename=NULL; + ae->label_line=0; + } +#if TRACE_PREPRO +printf("init 3\n"); +#endif + /* 32 CPR default roms but 260+ max snapshot RAM pages + one workspace */ + for (i=0;i<BANK_MAX_NUMBER+1;i++) { + mem=MemMalloc(65536); + memset(mem,0,65536); + ObjectArrayAddDynamicValueConcat((void**)&ae->mem,&ae->nbbank,&ae->maxbank,&mem,sizeof(mem)); + } +#if TRACE_PREPRO +printf("nbbank=%d initialised\n",ae->nbbank); +#endif + ae->activebank=BANK_MAX_NUMBER; + ae->maxptr=65536; + for (i=0;i<256;i++) { ae->charset[i]=(unsigned char)i; } + + if (param && param->outputfilename) { + ae->outputfilename=TxtStrDup(param->outputfilename); + } else if (param && param->automatic_radix && param->filename) { + int rilook; + rilook=strlen(param->filename); + ae->outputfilename=TxtStrDup(param->filename); + /* look for extension */ + while (rilook && ae->outputfilename[rilook]!='.') { + /* end of scan with directory reference or nothing found */ + if (ae->outputfilename[rilook]=='/' || ae->outputfilename[rilook]=='\\') rilook=0; else rilook--; + } + if (ae->outputfilename[rilook]=='.') { + ae->outputfilename[rilook]=0; + } + } else { + ae->outputfilename=TxtStrDup("rasmoutput"); + } + /* si on est en ligne de commande ET que le fichier n'est pas trouvé */ + if (param && param->filename && !FileExists(param->filename)) { + char *LTryExtension[]={".asm",".z80",".o",".dam",".mxm",".txt", + ".ASM",".Z80",".O",".DAM",".MXM",".TXT",NULL}; + + int iguess=1; + l=strlen(param->filename); + filename=MemRealloc(param->filename,l+6); + /* si le nom du fichier termine par un . on n'ajoute que l'extension, sinon on l'ajoute avec le . */ + if (param->filename[l-1]=='.') strcat(param->filename,"asm"); else strcat(param->filename,".asm"); + + while (!FileExists(param->filename) && LTryExtension[iguess]!=NULL) { + TxtReplace(param->filename,LTryExtension[iguess-1],LTryExtension[iguess],0); /* no realloc with this */ + if (!FileExists(param->filename)) { + param->filename[l]=0; + } + iguess++; + } + } + + if (param && param->filename && !FileExists(param->filename)) { + rasm_printf(ae,"Cannot find file [%s]\n",param->filename); + exit(-1802); + } + + if (param) rasm_printf(ae,KAYGREEN"Pre-processing [%s]\n",param->filename); + for (nbinstruction=0;instruction[nbinstruction].mnemo[0];nbinstruction++); + qsort(instruction,nbinstruction,sizeof(struct s_asm_keyword),cmpkeyword); + for (i=0;i<256;i++) { ae->fastmatch[i]=-1; } + for (i=0;i<nbinstruction;i++) { if (ae->fastmatch[(int)instruction[i].mnemo[0]]==-1) ae->fastmatch[(int)instruction[i].mnemo[0]]=i; } + for (i=0;CharWord[i];i++) {Automate[((int)CharWord[i])&0xFF]=1;} + /* separators */ + Automate[' ']=2; + Automate[',']=2; + Automate['\t']=2; + /* end of line */ + Automate[':']=3; /* les 0x0A et 0x0D seront deja remplaces en ':' */ + /* expression */ + Automate['=']=4; /* on stocke l'emplacement de l'egalite */ + Automate['<']=4; /* ou des operateurs */ + Automate['>']=4; /* d'evaluation */ + Automate['!']=4; + + StateMachineResizeBuffer(&w,256,&mw); + StateMachineResizeBuffer(&bval,256,&sval); + StateMachineResizeBuffer(&qval,256,&sqval); + w[0]=0; + bval[0]=0; + qval[0]=0; + +#if TRACE_PREPRO +printf("read file/flux\n"); +#endif +#if TRACE_GENERALE +printf("-read/flux\n"); +#endif + + if (!ae->flux) { + zelines=FileReadLines(filename); + FieldArrayAddDynamicValueConcat(&ae->filename,&ae->ifile,&ae->maxfile,filename); + } else { + int flux_nblines=0; + int flux_curpos; + + /* copie des données */ + for (i=0;i<datalen;i++) { + if (datain[i]=='\n') flux_nblines++; + } + zelines=MemMalloc(sizeof(char *)*(flux_nblines+2)); + flux_nblines=0; + flux_curpos=0; + for (i=0;i<datalen;i++) { + if (datain[i]=='\n') { + /* almost empty lines must allocate CR+terminator */ + zelines[flux_nblines]=MemMalloc(i-flux_curpos+2); + /* copy data+CR */ + memcpy(zelines[flux_nblines],datain+flux_curpos,i-flux_curpos+1); + /* et on ajoute un petit zéro à la fin! */ + zelines[flux_nblines][i-flux_curpos+1]=0; +#if 0 +if (flux_nblines<50) printf("%02d[%s]\n",flux_nblines,zelines[flux_nblines]); +#endif + flux_curpos=i+1; + flux_nblines++; + } + } + if (i>flux_curpos) { + zelines[flux_nblines]=MemMalloc(i-flux_curpos+1); + memcpy(zelines[flux_nblines],datain+flux_curpos,i-flux_curpos); + zelines[flux_nblines][i-flux_curpos]=0; + flux_nblines++; + } + /* terminator */ + zelines[flux_nblines]=NULL; + + /* en mode flux on prend le repertoire courant en reference */ + FieldArrayAddDynamicValueConcat(&ae->filename,&ae->ifile,&ae->maxfile,CURRENT_DIR); + } + +#if TRACE_PREPRO +printf("remove comz, do includes\n"); +#endif +#if TRACE_GENERALE +printf("-comz/include\n"); +#endif + EarlyPrepSrc(ae,zelines,ae->filename[ae->ifile-1]); + + for (i=0;zelines[i];i++) { + curlisting.ifile=0; + curlisting.iline=i+1; + curlisting.listing=zelines[i]; + ObjectArrayAddDynamicValueConcat((void**)&listing,&ilisting,&maxlisting,&curlisting,sizeof(curlisting)); + } + MemFree(zelines); + + /* on s'assure que la derniere instruction est prise en compte a peu de frais */ + if (ilisting) { + datalen=strlen(listing[ilisting-1].listing); + listing[ilisting-1].listing=MemRealloc(listing[ilisting-1].listing,datalen+2); + listing[ilisting-1].listing[datalen]=':'; + listing[ilisting-1].listing[datalen+1]=0; + } + + waiting_quote=quote_type=0; + l=idx=0; + while (l<ilisting) { + c=listing[l].listing[idx++]; + if (!c) { + l++; + idx=0; + continue; + } else if (c=='\\' && !waiting_quote) { + idx++; + continue; + } else if (c==0x0D || c==0x0A) { + listing[l].listing[idx-1]=':'; + c=':'; + } else if (c=='\'' && idx>2 && strncmp(&listing[l].listing[idx-3],"AF'",3)==0) { + /* rien */ + } else if (c=='"' || c=='\'') { + if (!quote_type) { + quote_type=c; + lquote=l; + } else { + if (c==quote_type) { + quote_type=0; + } + } + } + + if (waiting_quote) { + /* expecting quote and nothing else */ + switch (waiting_quote) { + case 1: + if (c==quote_type) waiting_quote=2; + break; + case 2: + if (!quote_type) { + waiting_quote=3; + qval[iqval]=0; + } else { + qval[iqval++]=c; + StateMachineResizeBuffer(&qval,iqval,&sqval); + qval[iqval]=0; + } + } + if (waiting_quote==3) { + if (incbin) { + int fileok=0,ilookfile; + /* qval contient le nom du fichier a lire */ + filename_toread=MergePath(ae,ae->filename[listing[l].ifile],qval); + if (FileExists(filename_toread)) { + fileok=1; + } else { + for (ilookfile=0;ilookfile<ae->ipath && !fileok;ilookfile++) { + filename_toread=MergePath(ae,ae->includepath[ilookfile],qval); + if (FileExists(filename_toread)) { + fileok=1; + } + } + } + + curhexbin.filename=TxtStrDup(filename_toread); + curhexbin.crunch=crunch; + if (fileok) { + /* lecture */ + curhexbin.rawlen=curhexbin.datalen=FileGetSize(filename_toread); + curhexbin.data=MemMalloc(curhexbin.datalen*1.3+10); + #if TRACE_PREPRO + switch (crunch) { + case 0:rasm_printf(ae,KBLUE"incbin [%s] size=%d\n",filename_toread,curhexbin.datalen);break; + case 4:rasm_printf(ae,KBLUE"inclz4 [%s] size=%d\n",filename_toread,curhexbin.datalen);break; + case 7:rasm_printf(ae,KBLUE"incsx7 [%s] size=%d\n",filename_toread,curhexbin.datalen);break; + case 8:rasm_printf(ae,KBLUE"incexo [%s] size=%d\n",filename_toread,curhexbin.datalen);break; + case 88:rasm_printf(ae,KBLUE"incexb [%s] size=%d\n",filename_toread,curhexbin.datalen);break; + case 48:rasm_printf(ae,KBLUE"incl48 [%s] size=%d\n",filename_toread,curhexbin.datalen);break; + case 49:rasm_printf(ae,KBLUE"incl49 [%s] size=%d\n",filename_toread,curhexbin.datalen);break; + default:rasm_printf(ae,KBLUE"invalid crunch state!\n");exit(-42); + } + #endif + if (FileReadBinary(filename_toread,(char*)curhexbin.data,curhexbin.datalen)!=curhexbin.datalen) { + rasm_printf(ae,"read error on %s",filename_toread); + exit(2); + } + FileReadBinaryClose(filename_toread); + switch (crunch) { + #ifndef NO_3RD_PARTIES + case 4: + newdata=LZ4_crunch(curhexbin.data,curhexbin.datalen,&curhexbin.datalen); + MemFree(curhexbin.data); + curhexbin.data=newdata; + #if TRACE_PREPRO + rasm_printf(ae,KVERBOSE"crunched with LZ4 into %d byte(s)\n",curhexbin.datalen); + #endif + break; + case 7: + { + size_t slzlen; + newdata=ZX7_compress(optimize(curhexbin.data, curhexbin.datalen), curhexbin.data, curhexbin.datalen, &slzlen); + curhexbin.datalen=slzlen; + MemFree(curhexbin.data); + curhexbin.data=newdata; + #if TRACE_PREPRO + rasm_printf(ae,KVERBOSE"crunched with ZX7 into %d byte(s)\n",curhexbin.datalen); + #endif + } + break; + case 8: + rasm_printf(ae,KWARNING"Exomizer is crunching %.1fkb this may take a while, be patient...\n",curhexbin.datalen/1024.0); + newdata=Exomizer_crunch(curhexbin.data,curhexbin.datalen,&curhexbin.datalen); + MemFree(curhexbin.data); + curhexbin.data=newdata; + #if TRACE_PREPRO + rasm_printf(ae,KVERBOSE"crunched with Exomizer into %d byte(s)\n",curhexbin.datalen); + #endif + break; + #endif + case 48: + newdata=LZ48_crunch(curhexbin.data,curhexbin.datalen,&curhexbin.datalen); + MemFree(curhexbin.data); + curhexbin.data=newdata; + #if TRACE_PREPRO + rasm_printf(ae,KVERBOSE"crunched with LZ48 into %d byte(s)\n",curhexbin.datalen); + #endif + break; + case 49: + newdata=LZ49_crunch(curhexbin.data,curhexbin.datalen,&curhexbin.datalen); + MemFree(curhexbin.data); + curhexbin.data=newdata; + #if TRACE_PREPRO + rasm_printf(ae,KVERBOSE"crunched into with LZ49 %d byte(s)\n",curhexbin.datalen); + #endif + break; + default:break; + } + } else { + /* TAG + info */ + curhexbin.datalen=-1; + curhexbin.data=MemMalloc(2); + /* not yet an error, we will know later when executing the code */ + } + ObjectArrayAddDynamicValueConcat((void**)&ae->hexbin,&ae->ih,&ae->mh,&curhexbin,sizeof(curhexbin)); + /* insertion */ + le=strlen(listing[l].listing); + + newlistingline=MemMalloc(le+32); + memcpy(newlistingline,listing[l].listing,rewrite); + rewrite+=sprintf(newlistingline+rewrite,"HEXBIN #%X",ae->ih-1); + strcat(newlistingline+rewrite,listing[l].listing+idx); + idx=rewrite; + MemFree(listing[l].listing); + listing[l].listing=newlistingline; + incbin=0; + } else if (include) { + /* qval contient le nom du fichier a lire */ + int fileok=0,ilookfile; + /* qval contient le nom du fichier a lire */ + filename_toread=MergePath(ae,ae->filename[listing[l].ifile],qval); + if (FileExists(filename_toread)) { + fileok=1; + } else { + for (ilookfile=0;ilookfile<ae->ipath && !fileok;ilookfile++) { + filename_toread=MergePath(ae,ae->includepath[ilookfile],qval); + if (FileExists(filename_toread)) { + fileok=1; + } + } + } + + if (fileok) { + int newi,newj; + #if TRACE_PREPRO + rasm_printf(ae,KBLUE"include [%s]\n",filename_toread); + #endif + + /* lecture */ + listing_include=FileReadLines(filename_toread); + FieldArrayAddDynamicValueConcat(&ae->filename,&ae->ifile,&ae->maxfile,filename_toread); + /* virer les commentaires + pré-traitement */ + EarlyPrepSrc(ae,listing_include,ae->filename[ae->ifile-1]); + + /* split de la ligne en cours + suppression de l'instruction include */ + PreProcessingSplitListing(&listing,&ilisting,&maxlisting,l,rewrite,idx); + /* insertion des nouvelles lignes + reference fichier + numeros de ligne */ + PreProcessingInsertListing(&listing,&ilisting,&maxlisting,l,listing_include,ae->ifile-1); + + MemFree(listing_include); /* free le tableau mais pas les lignes */ + listing_include=NULL; + idx=0; /* on reste sur la meme ligne mais on se prepare a relire du caractere 0! */ + } else { + /* TAG + info */ + curhexbin.filename=TxtStrDup(filename_toread); + curhexbin.datalen=-2; + curhexbin.data=MemMalloc(2); + /* not yet an error, we will know later when executing the code */ + ObjectArrayAddDynamicValueConcat((void**)&ae->hexbin,&ae->ih,&ae->mh,&curhexbin,sizeof(curhexbin)); + /* insertion */ + le=strlen(listing[l].listing); + newlistingline=MemMalloc(le+32); + memcpy(newlistingline,listing[l].listing,rewrite); + rewrite+=sprintf(newlistingline+rewrite,"HEXBIN #%X",ae->ih-1); + strcat(newlistingline+rewrite,listing[l].listing+idx); + idx=rewrite; + MemFree(listing[l].listing); + listing[l].listing=newlistingline; + } + include=0; + } + waiting_quote=0; + qval[0]=0; + iqval=0; + } + } else { + /* classic behaviour */ + + /* looking for include/incbin */ + if (((c>='A' && c<='Z') || (c>='0' && c<='9') || c=='@' || c=='_')&& !quote_type) { + bval[ival++]=c; + StateMachineResizeBuffer(&bval,ival,&sval); + bval[ival]=0; + } else { + if (strcmp(bval,"INCLUDE")==0) { + include=1; + waiting_quote=1; + rewrite=idx-7-1; + /* quote right after keyword */ + if (c==quote_type) { + waiting_quote=2; + } + } else if (strcmp(bval,"READ")==0) { + include=1; + waiting_quote=1; + rewrite=idx-4-1; + /* quote right after keyword */ + if (c==quote_type) { + waiting_quote=2; + } + } else if (strcmp(bval,"INCLZ4")==0) { + incbin=1; + crunch=4; + waiting_quote=1; + rewrite=idx-6-1; + /* quote right after keyword */ + if (c==quote_type) { + waiting_quote=2; + } + } else if (strcmp(bval,"INCEXB")==0) { + incbin=1; + crunch=88; + waiting_quote=1; + rewrite=idx-6-1; + /* quote right after keyword */ + if (c==quote_type) { + waiting_quote=2; + } + } else if (strcmp(bval,"INCEXO")==0) { + incbin=1; + crunch=8; + waiting_quote=1; + rewrite=idx-6-1; + /* quote right after keyword */ + if (c==quote_type) { + waiting_quote=2; + } + } else if (strcmp(bval,"INCZX7")==0) { + incbin=1; + crunch=7; + waiting_quote=1; + rewrite=idx-6-1; + /* quote right after keyword */ + if (c==quote_type) { + waiting_quote=2; + } + } else if (strcmp(bval,"INCL48")==0) { + incbin=1; + crunch=48; + waiting_quote=1; + rewrite=idx-6-1; + /* quote right after keyword */ + if (c==quote_type) { + waiting_quote=2; + } + } else if (strcmp(bval,"INCL49")==0) { + incbin=1; + crunch=49; + waiting_quote=1; + rewrite=idx-6-1; + /* quote right after keyword */ + if (c==quote_type) { + waiting_quote=2; + } + } else if (strcmp(bval,"INCBIN")==0) { + incbin=1; + crunch=0; + waiting_quote=1; + rewrite=idx-6-1; + /* quote right after keyword */ + if (c==quote_type) { + waiting_quote=2; + } + } else if (strcmp(bval,"INCWAV")==0) { + incbin=1; + crunch=0; + waiting_quote=1; + rewrite=idx-6-1; + /* quote right after keyword */ + if (c==quote_type) { + waiting_quote=2; + } + } else if (strcmp(bval,"WHILE")==0) { + /* remplir la structure repeat_index */ + windex.ol=listing[l].iline; + windex.oidx=idx; + windex.ifile=ae->ifile-1; + ObjectArrayAddDynamicValueConcat((void**)&TABwindex,&nwi,&mwi,&windex,sizeof(windex)); + } else if (strcmp(bval,"REPEAT")==0) { + /* remplir la structure repeat_index */ + rindex.ol=listing[l].iline; + rindex.oidx=idx; + rindex.ifile=ae->ifile-1; + ObjectArrayAddDynamicValueConcat((void**)&TABrindex,&nri,&mri,&rindex,sizeof(rindex)); + } else if (strcmp(bval,"WEND")==0) { + /* retrouver la structure repeat_index correspondant a l'ouverture */ + for (wi=nwi-1;wi>=0;wi--) { + if (TABwindex[wi].cl==-1) { + TABwindex[wi].cl=c; + TABwindex[wi].cidx=idx; + break; + } + } + if (wi==-1) { + MakeError(ae,ae->filename[listing[l].ifile],listing[l].iline,"WEND refers to unknown WHILE\n"); + //exit(1); + } + } else if (strcmp(bval,"REND")==0 || strcmp(bval,"UNTIL")==0) { + /* retrouver la structure repeat_index correspondant a l'ouverture */ + for (ri=nri-1;ri>=0;ri--) { + if (TABrindex[ri].cl==-1) { + TABrindex[ri].cl=c; + TABrindex[ri].cidx=idx; + break; + } + } + if (ri==-1) { + MakeError(ae,ae->filename[listing[l].ifile],listing[l].iline,"%s refers to unknown REPEAT\n",bval); + //exit(1); + } + + } + bval[0]=0; + ival=0; + } + } + } +#if TRACE_PREPRO +printf("check quotes and repeats\n"); +#endif + if (quote_type) { + MakeError(ae,ae->filename[listing[lquote].ifile],listing[lquote].iline,"quote opened was not closed\n"); + //exit(1); + } + + /* repeat expansion check */ + for (ri=0;ri<nri;ri++) { + if (TABrindex[ri].cl==-1) { + MakeError(ae,ae->filename[TABrindex[ri].ifile],TABrindex[ri].ol,"REPEAT was not closed\n"); + } + } + + /* creer une liste de mots */ + curw.w=TxtStrDup("BEGIN"); + curw.l=0; + curw.ifile=0; + curw.t=1; + curw.e=0; + ObjectArrayAddDynamicValueConcat((void**)&wordlist,&nbword,&maxword,&curw,sizeof(curw)); + + /* pour les calculs d'adresses avec IX et IY on enregistre deux variables bidons du meme nom */ + curw.e=2; + curw.w=TxtStrDup("IX~0"); + ObjectArrayAddDynamicValueConcat((void**)&wordlist,&nbword,&maxword,&curw,sizeof(curw)); + curw.w=TxtStrDup("IY~0"); + ObjectArrayAddDynamicValueConcat((void**)&wordlist,&nbword,&maxword,&curw,sizeof(curw)); + curw.e=0; + +#if TRACE_PREPRO + l=0; + while (l<ilisting) { + rasm_printf(ae,"listing[%d]\n%s\n",l,listing[l].listing); + l++; + } +#endif +#if TRACE_GENERALE +printf("-build wordlist\n"); +#endif + + texpr=quote_type=0; + l=lw=idx=0; + ispace=0; + w[0]=0; + while (l<ilisting) { + c=listing[l].listing[idx++]; + if (!c) { + idx=0; + l++; + continue; + } + + if (!quote_type) { +#if TRACE_PREPRO +//printf("c='%c' automate[c]=%d\n",c>31?c:'.',Automate[((int)c)&0xFF]); +#endif + switch (Automate[((int)c)&0xFF]) { + case 0: + MakeError(ae,ae->filename[listing[l].ifile],listing[l].iline,"invalid char '%c' (%d) char %d\n",c,c,idx); +#if TRACE_PREPRO +printf("c='%c' automate[c]=%d\n",c>31?c:'.',Automate[((int)c)&0xFF]); +#endif + exit(0); + break; + case 1: + if (c=='\'' && idx>2 && strncmp(&listing[l].listing[idx-3],"AF'",3)==0) { + w[lw++]=c; + StateMachineResizeBuffer(&w,lw,&mw); + w[lw]=0; + break; + } else if (c=='\'' || c=='"') { + quote_type=c; + /* debut d'une quote, on finalise le mot -> POURQUOI DONC? */ + //idx--; +#if TRACE_PREPRO +printf("quote\n"); +#endif + /* on finalise le mot si on est en début d'une nouvelle instruction ET que c'est un SAVE */ + if (strcmp(w,"SAVE")==0) { + idx--; + } else { + w[lw++]=c; + StateMachineResizeBuffer(&w,lw,&mw); + w[lw]=0; + break; + } + } else { + if (c!=' ' && c!='\t') { + w[lw++]=c; + StateMachineResizeBuffer(&w,lw,&mw); + w[lw]=0; + } else { + /* Winape/Maxam operator compatibility on expressions */ +#if TRACE_PREPRO +printf("1/2 winape maxam operator test for [%s]\n",w+ispace); +#endif + if (texpr) { + if (strcmp(w+ispace,"AND")==0) { + w[ispace]='&'; + lw=ispace+1; + } else if (strcmp(w+ispace,"OR")==0) { +#if TRACE_PREPRO +printf("conversion OR vers |\n"); +#endif + w[ispace]='|'; + lw=ispace+1; + } else if (strcmp(w+ispace,"MOD")==0) { + w[ispace]='m'; + lw=ispace+1; + } else if (strcmp(w+ispace,"XOR")==0) { + w[ispace]='^'; + lw=ispace+1; + } else if (strcmp(w+ispace,"%")==0) { + w[ispace]='m'; + lw=ispace+1; + } + } + ispace=lw; + } + break; + } + case 2: + /* separator (space, tab, comma) */ +#if TRACE_PREPRO +printf("*** separator='%c'\n",c); +#endif + + /* patch argument suit une expression d'évaluation (ASSERT) */ + if (c==',') hadcomma=1; + + if (lw) { + w[lw]=0; + if (texpr && !wordlist[nbword-1].t && wordlist[nbword-1].e && !hadcomma) { + /* pour compatibilite winape avec AND,OR,XOR */ +#if TRACE_PREPRO +printf("2/2 winape maxam operator test for expression [%s]\n",w+ispace); +#endif + if (strcmp(w,"AND")==0) { + wtmp=TxtStrDup("&"); + } else if (strcmp(w,"OR")==0) { + wtmp=TxtStrDup("|"); + } else if (strcmp(w,"XOR")==0) { + wtmp=TxtStrDup("^"); + } else if (strcmp(w,"%")==0) { + wtmp=TxtStrDup("m"); + } else { + wtmp=TxtStrDup(w); + } + /* on concatène le nouveau mot à l'expression */ + nbword--; + lw=0; + for (li=0;wordlist[nbword].w[li];li++) { + w[lw++]=wordlist[nbword].w[li]; + StateMachineResizeBuffer(&w,lw,&mw); + } + w[lw]=0; + MemFree(wordlist[nbword].w); + + for (li=0;wtmp[li];li++) { + w[lw++]=wtmp[li]; + StateMachineResizeBuffer(&w,lw,&mw); + } + w[lw]=0; + MemFree(wtmp); + /* et on modifie l'automate pour la suite! */ + Automate[' ']=1; + Automate['\t']=1; + ispace=lw; + } else if (strcmp(w,"EQU")==0) { + /* il y avait un mot avant alors on va reorganiser la ligne */ + nbword--; + lw=0; + for (li=0;wordlist[nbword].w[li];li++) { + w[lw++]=wordlist[nbword].w[li]; + StateMachineResizeBuffer(&w,lw,&mw); + } + MemFree(wordlist[nbword].w); + curw.e=lw+1; + /* on ajoute l'egalite d'alias*/ + w[lw++]='~'; + StateMachineResizeBuffer(&w,lw,&mw); + w[lw]=0; + Automate[' ']=1; + Automate['\t']=1; + ispace=lw; + texpr=1; + } else { + curw.w=TxtStrDup(w); + curw.l=listing[l].iline; + curw.ifile=listing[l].ifile; + curw.t=0; +#if TRACE_PREPRO +if (curw.w[0]=='=') { + printf("(1) bug prout\n"); + exit(1); +} +printf("ajout du mot [%s]\n",curw.w); +#endif + ObjectArrayAddDynamicValueConcat((void**)&wordlist,&nbword,&maxword,&curw,sizeof(curw)); + //texpr=0; /* reset expr */ + curw.e=0; + lw=0; + w[lw]=0; + + /* match keyword? then next spaces will be ignored*/ + if (macro_trigger) { + struct s_macro_fast curmacrofast; + Automate[' ']=1; + Automate['\t']=1; + ispace=0; + texpr=1; +#if TRACE_PREPRO +printf("macro trigger w=[%s]\n",curw.w); +#endif + /* add macro name to instruction pool for preprocessor but not struct or write */ + if (macro_trigger=='M') { + curmacrofast.mnemo=curw.w; + curmacrofast.crc=GetCRC(curw.w); + ObjectArrayAddDynamicValueConcat((void**)&MacroFast,&idxmacrofast,&maxmacrofast,&curmacrofast,sizeof(struct s_macro_fast)); + } + macro_trigger=0; + } else { + int keymatched=0; + if ((ifast=ae->fastmatch[(int)curw.w[0]])!=-1) { + while (instruction[ifast].mnemo[0]==curw.w[0]) { + if (strcmp(instruction[ifast].mnemo,curw.w)==0) { + keymatched=1; + if (strcmp(curw.w,"MACRO")==0 || strcmp(curw.w,"STRUCT")==0 || strcmp(curw.w,"WRITE")==0) { +/* @@TODO AS80 compatibility patch!!! */ + macro_trigger=curw.w[0]; + } else { + Automate[' ']=1; + Automate['\t']=1; + ispace=0; + /* instruction en cours, le reste est a interpreter comme une expression */ +#if TRACE_PREPRO +printf("instruction en cours\n"); +#endif + texpr=1; + } + break; + } + ifast++; + } + } + if (!keymatched) { + int macrocrc; + macrocrc=GetCRC(curw.w); + for (keymatched=0;keymatched<idxmacrofast;keymatched++) { + if (MacroFast[keymatched].crc==macrocrc) + if (strcmp(MacroFast[keymatched].mnemo,curw.w)==0) { + Automate[' ']=1; + Automate['\t']=1; + ispace=0; + /* macro en cours, le reste est a interpreter comme une expression */ + texpr=1; + break; + } + } + } + } + } + } else { + if (hadcomma) { + MakeError(ae,ae->filename[listing[l].ifile],listing[l].iline,"empty parameter\n"); + } + } + break; + case 3: + /* fin de ligne, on remet l'automate comme il faut */ +#if TRACE_PREPRO +printf("EOL\n"); +#endif + macro_trigger=0; + Automate[' ']=2; + Automate['\t']=2; + ispace=0; + texpr=0; + /* si le mot lu a plus d'un caractère */ + if (lw) { + if (!wordlist[nbword-1].t && (wordlist[nbword-1].e || w[0]=='=') && !hadcomma) { + /* cas particulier d'ecriture libre */ + /* bugfix inhibition 19.06.2018 */ + /* ajout du terminateur? */ + w[lw]=0; +#if TRACE_PREPRO +printf("nbword=%d w=[%s] ->",nbword,w);fflush(stdout); +#endif + nbword--; + wordlist[nbword].w=MemRealloc(wordlist[nbword].w,strlen(wordlist[nbword].w)+lw+1); + strcat(wordlist[nbword].w,w); +#if TRACE_PREPRO +printf("%s\n",wordlist[nbword].w); +#endif + /* on change de type! */ + wordlist[nbword].t=1; + //ObjectArrayAddDynamicValueConcat((void**)&wordlist,&nbword,&maxword,&curw,sizeof(curw)); + curw.e=0; + lw=0; + w[lw]=0; + } else if (nbword && strcmp(w,"EQU")==0) { + /* il y avait un mot avant alors on va reorganiser la ligne */ + nbword--; + lw=0; + for (li=0;wordlist[nbword].w[li];li++) { + w[lw++]=wordlist[nbword].w[li]; + StateMachineResizeBuffer(&w,lw,&mw); + w[lw]=0; + } + MemFree(wordlist[nbword].w); + /* on ajoute l'egalite ou comparaison! */ + curw.e=lw+1; + w[lw++]='='; + StateMachineResizeBuffer(&w,lw,&mw); + w[lw]=0; + Automate[' ']=1; + Automate['\t']=1; + } else { + /* mot de fin de ligne, à priori pas une expression */ + curw.w=TxtStrDup(w); + curw.l=listing[l].iline; + curw.ifile=listing[l].ifile; + curw.t=1; +#if TRACE_PREPRO +printf("mot de fin de ligne = [%s]\n",curw.w); +if (curw.w[0]=='=') { + printf("(3) bug prout\n"); + exit(1); +} +#endif + ObjectArrayAddDynamicValueConcat((void**)&wordlist,&nbword,&maxword,&curw,sizeof(curw)); + curw.e=0; + lw=0; + w[lw]=0; + hadcomma=0; + } + } else { + /* sinon c'est le précédent qui était terminateur d'instruction */ + wordlist[nbword-1].t=1; + w[lw]=0; + } + hadcomma=0; + break; + case 4: +#if TRACE_PREPRO +printf("expr operator=%c\n",c); +#endif + /* expression/condition */ + texpr=1; + if (lw) { + Automate[' ']=1; + Automate['\t']=1; + if (!curw.e) { + curw.e=lw+1; + w[lw++]=c; + StateMachineResizeBuffer(&w,lw,&mw); + w[lw]=0; + } else { + w[lw++]=c; + StateMachineResizeBuffer(&w,lw,&mw); + w[lw]=0; + } + } else { + /* 2018.06.06 évolution sur le ! (not) */ +#if TRACE_PREPRO +printf("*** operateur commence le mot\n"); +printf("mot precedent=[%s] t=%d\n",wordlist[nbword-1].w,wordlist[nbword-1].t); +#endif + if (hadcomma && c=='!') { + /* on peut commencer un argument par un NOT */ + w[lw++]=c; + StateMachineResizeBuffer(&w,lw,&mw); + w[lw]=0; + /* automate déjà modifié rien de plus */ + } else if (!wordlist[nbword-1].t) { + /* il y avait un mot avant alors on va reorganiser la ligne */ + /* patch NOT -> SAUF si c'est une directive */ + int keymatched=0; + if ((ifast=ae->fastmatch[(int)wordlist[nbword-1].w[0]])!=-1) { + while (instruction[ifast].mnemo[0]==wordlist[nbword-1].w[0]) { + if (strcmp(instruction[ifast].mnemo,wordlist[nbword-1].w)==0) { + keymatched=1; + break; + } + ifast++; + } + } + if (!keymatched) { + int macrocrc; + macrocrc=GetCRC(wordlist[nbword-1].w); + for (i=0;i<idxmacrofast;i++) { + if (MacroFast[i].crc==macrocrc) + if (strcmp(MacroFast[i].mnemo,wordlist[nbword-1].w)==0) { + keymatched=1; + break; + } + } + } + if (!keymatched) { + nbword--; + for (li=0;wordlist[nbword].w[li];li++) { + w[lw++]=wordlist[nbword].w[li]; + StateMachineResizeBuffer(&w,lw,&mw); + w[lw]=0; + } + MemFree(wordlist[nbword].w); + /* on ajoute l'egalite ou comparaison! */ + curw.e=lw+1; + } + w[lw++]=c; + StateMachineResizeBuffer(&w,lw,&mw); + w[lw]=0; + /* et on modifie l'automate pour la suite! */ + Automate[' ']=1; + Automate['\t']=1; + } else { + MakeError(ae,ae->filename[listing[l].ifile],listing[l].iline,"cannot start expression with '=','!','<','>'\n"); + } + } + break; + default: + rasm_printf(ae,KERROR"Internal error (Automate wrong value=%d)\n",Automate[c]); + exit(-1); + } + } else { + /* lecture inconditionnelle de la quote */ +#if TRACE_PREPRO +printf("quote[%d]=%c\n",lw,c); +#endif + w[lw++]=c; + StateMachineResizeBuffer(&w,lw,&mw); + w[lw]=0; + if (!escape_code) { + if (c=='\\') escape_code=1; + if (lw>1 && c==quote_type) { + quote_type=0; + } + } else { + escape_code=0; + } + } + } + +#if TRACE_PREPRO +printf("END\n"); +#endif + + curw.w="END"; + curw.l=0; + curw.t=2; + curw.ifile=0; + ObjectArrayAddDynamicValueConcat((void**)&wordlist,&nbword,&maxword,&curw,sizeof(curw)); +#if TRACE_PREPRO + rasm_printf(ae,KVERBOSE"wordlist contains %d element%s\n",nbword,nbword>1?"s":""); +#endif + ae->nbword=nbword; + + /* switch words for macro declaration with AS80 & UZ80 */ + if (param && param->as80) { + for (l=0;l<nbword;l++) { + if (!wordlist[l].t && !wordlist[l].e && strcmp(wordlist[l+1].w,"MACRO")==0) { + char *wtmp; + wtmp=wordlist[l+1].w; + wordlist[l+1].w=wordlist[l].w; + wordlist[l].w=wtmp; + } + } + } + +#if TRACE_PREPRO + for (l=0;l<nbword;l++) { + rasm_printf(ae,"[%s]e=%d ",wordlist[l].w,wordlist[l].e); + if (wordlist[l].t) rasm_printf(ae,"(%d)\n",wordlist[l].t); + } + +printf("free\n"); +#endif + + MemFree(bval); + MemFree(qval); + MemFree(w); + + for (l=0;l<ilisting;l++) { + MemFree(listing[l].listing); + } + MemFree(listing); + /* wordlist + type 0: label or instruction followed by parameter(s) + type 1: last word of the line, last parameter of an instruction + type 2: very last word of the list + e tag: non zero if there is comparison or equality + */ + ae->wl=wordlist; + if (param) { + MemFree(param->filename); + } + if (MacroFast) MemFree(MacroFast); + if (TABwindex) MemFree(TABwindex); + if (TABrindex) MemFree(TABrindex); +#if TRACE_PREPRO +printf("return ae\n"); +#endif + return ae; +} + +int Rasm(struct s_parameter *param) +{ + #undef FUNC + #define FUNC "Rasm" + + struct s_assenv *ae=NULL; + + /* read and preprocess source */ + ae=PreProcessing(param->filename,0,NULL,0,param); + /* assemble */ + return Assemble(ae,NULL,NULL,NULL); +} + +/* fonction d'export */ + +int RasmAssemble(const char *datain, int lenin, unsigned char **dataout, int *lenout) +{ + struct s_assenv *ae=NULL; + + if (lenout) *lenout=0; + ae=PreProcessing(NULL,1,datain,lenin,NULL); + return Assemble(ae,dataout,lenout,NULL); +} + +int RasmAssembleInfo(const char *datain, int lenin, unsigned char **dataout, int *lenout, struct s_rasm_info **debug) +{ + struct s_assenv *ae=NULL; + int ret; + + ae=PreProcessing(NULL,1,datain,lenin,NULL); + ret=Assemble(ae,dataout,lenout,debug); + return ret; +} + + +#define AUTOTEST_PAGELABELGEN "buildsna: bank: cpt=5: ld bc,{page}miam{cpt}: bank cpt: nop: miam{cpt} nop: assert {page}miam{cpt}==0x7FC5 " + +#define AUTOTEST_NOINCLUDE "truc equ 0:if truc:include'bite':endif:nop" + +#define AUTOTEST_SETINSIDE "ld hl,0=0xC9FB" + +#define AUTOTEST_OPERATOR_CONVERSION "ld hl,10 OR 20:ld a,40 and 10:ld bc,5 MOD 2:ld a,(ix+45 xor 45)" + +#define AUTOTEST_OPERATOR_MODULO "revar=46: devar=5 : var=46%5 : assert var==1: var=46 % 5 : assert var==1: var=46 mod 5 : assert var==1:" \ + "var=revar%5 : assert var==1: var=revar%devar : assert var==1: var=46%devar : assert var==1: var=revar % 5 : assert var==1:" \ + "var=revar % devar : assert var==1: var=46 % devar : assert var==1: var=revar % %101 : assert var==1: var=revar%%101 : assert var==1: nop" + +#define AUTOTEST_UNDEF "mavar=10: ifdef mavar: undef mavar: endif: ifdef mavar: fail 'undef did not work': endif:nop " + +#define AUTOTEST_INSTRMUSTFAILED "ld a,b,c:ldi a: ldir bc:exx hl,de:exx de:ex bc,hl:ex hl,bc:ex af,af:ex hl,hl:ex hl:exx hl: "\ + "neg b:push b:push:pop:pop c:sub ix:add ix:add:sub:di 2:ei 3:ld i,c:ld r,e:rl:rr:rlca a:sla:sll:"\ + "ldd e:lddr hl:adc ix:adc b,a:xor 12,13:xor b,1:xor:or 12,13:or b,1:or:and 12,13:and b,1:and:inc:dec" + +#define AUTOTEST_VIRGULE "defb 5,5,,5" +#define AUTOTEST_VIRGULE2 "print '5,,5':nop" + +#define AUTOTEST_OVERLOADMACPRM "macro test,idx: defb idx:endm:macro test2,idx:defb {idx}:endm:repeat 2,idx:test idx-1:test2 idx-1:rend" + +#define AUTOTEST_IFDEFMACRO "macro test:nop:endm:ifndef test:error:else:test:endif:ifdef test:test:else:error:endif:nop" + +#define AUTOTEST_PRINTVAR "label1: macro test, param: print 'param {param}', {hex}{param}: endm:: test label1: nop" + +#define AUTOTEST_PRINTSPACE "idx=5: print 'grouik { idx + 3 } ':nop" + +#define AUTOTEST_NOT "myvar=10:myvar=10+myvar:if 5!=3:else:print glop:endif:ifnot 5:print glop:else:endif:" \ + "ifnot 0:else:print glop:endif:if !(5):print glop:endif:if !(0):else:print glop:endif:" \ + "ya=!0:if ya==1:else:print glop:endif:if !5:print glop:endif:ya = 0:ya =! 0:if ya == 1:" \ + "else:print glop:endif:if ! 5:print glop:endif:if 1-!( !0 && !0):else:print glop:endif:nop" + + +#define AUTOTEST_MACRO "macro glop:@glop:ld hl,@next:djnz @glop:@next:mend:macro glop2:@glop:glop:ld hl,@next:djnz @glop:glop:" \ + "@next:mend:cpti=0:repeat:glop:cpt=0:glop:repeat:glop2:repeat 1:@glop:dec a:ld hl,@next:glop2:glop2:" \ + "jr nz,@glop:@next:rend:cpt=cpt+1:glop2:until cpt<3:cpti=cpti+1:glop2:until cpti<3" + +#define AUTOTEST_MACRO_ADV "idx=10:macro mac2 param1,param2:ld hl,{param1}{idx+10}{param2}:{param1}{idx+10}{param2}:djnz {param1}{idx+10}{param2}:mend: " \ + "mac2 label,45:mac2 glop,10:djnz glop2010:jp label2045" + +#define AUTOTEST_MACROPAR "macro unemac, param1, param2:defb '{param1}':defb {param2}:mend:unemac grouik,'grouik'" + +#define AUTOTEST_OPCODES "nop::ld bc,#1234::ld (bc),a::inc bc:inc b:dec b:ld b,#12:rlca:ex af,af':add hl,bc:ld a,(bc):dec bc:" \ + "inc c:dec c:ld c,#12:rrca::djnz $:ld de,#1234:ld (de),a:inc de:inc d:dec d:ld d,#12:rla:jr $:" \ + "add hl,de:ld a,(de):dec de:inc e:dec e:ld e,#12:rra::jr nz,$:ld hl,#1234:ld (#1234),hl:inc hl:inc h:" \ + "dec h:ld h,#12:daa:jr z,$:add hl,hl:ld hl,(#1234):dec hl:inc l:dec l:ld l,#12:cpl::jr nc,$:" \ + "ld sp,#1234:ld (#1234),a:inc sp:inc (hl):dec (hl):ld (hl),#12:scf:jr c,$:add hl,sp:ld a,(#1234):" \ + "dec sp:inc a:dec a:ld a,#12:ccf::ld b,b:ld b,c:ld b,d:ld b,e:ld b,h:ld b,l:ld b,(hl):ld b,a:ld c,b:" \ + "ld c,c:ld c,d:ld c,e:ld c,h:ld c,l:ld c,(hl):ld c,a::ld d,b:ld d,c:ld d,d:ld d,e:ld d,h:ld d,l:" \ + "ld d,(hl):ld d,a:ld e,b:ld e,c:ld e,d:ld e,e:ld e,h:ld e,l:ld e,(hl):ld e,a::ld h,b:ld h,c:ld h,d:" \ + "ld h,e:ld h,h:ld h,l:ld h,(hl):ld h,a:ld l,b:ld l,c:ld l,d:ld l,e:ld l,h:ld l,l:ld l,(hl):ld l,a::" \ + "ld (hl),b:ld (hl),c:ld (hl),d:ld (hl),e:ld (hl),h:ld (hl),l:halt:ld (hl),a:ld a,b:ld a,c:ld a,d:" \ + "ld a,e:ld a,h:ld a,l:ld a,(hl):ld a,a::add b:add c:add d:add e:add h:add l:add (hl):add a:adc b:" \ + "adc c:adc d:adc e:adc h:adc l:adc (hl):adc a::sub b:sub c:sub d:sub e:sub h:sub l:sub (hl):sub a:" \ + "sbc b:sbc c:sbc d:sbc e:sbc h:sbc l:sbc (hl):sbc a::and b:and c:and d:and e:and h:and l:and (hl):" \ + "and a:xor b:xor c:xor d:xor e:xor h:xor l:xor (hl):xor a::or b:or c:or d:or e:or h:or l:or (hl):" \ + "or a:cp b:cp c:cp d:cp e:cp h:cp l:cp (hl):cp a::ret nz:pop bc:jp nz,#1234:jp #1234:call nz,#1234:" \ + "push bc:add #12:rst 0:ret z:ret:jp z,#1234:nop:call z,#1234:call #1234:adc #12:rst 8::ret nc:pop de:" \ + "jp nc,#1234:out (#12),a:call nc,#1234:push de:sub #12:rst #10:ret c:exx:jp c,#1234:in a,(#12):" \ + "call c,#1234:nop:sbc #12:rst #18::ret po:pop hl:jp po,#1234:ex (sp),hl:call po,#1234:push hl:" \ + "and #12:rst #20:ret pe:jp (hl):jp pe,#1234:ex de,hl:call pe,#1234:nop:xor #12:rst #28::ret p:pop af:" \ + "jp p,#1234:di:call p,#1234:push af:or #12:rst #30:ret m:ld sp,hl:jp m,#1234:ei:call m,#1234:nop:" \ + "cp #12:rst #38:in b,(c):out (c),b:sbc hl,bc:ld (#1234),bc:neg:retn:im 0:ld i,a:in c,(c):out (c),c:" \ + "adc hl,bc:ld bc,(#1234):reti:ld r,a::in d,(c):out (c),d:sbc hl,de:ld (#1234),de:retn:im 1:ld a,i:" \ + "in e,(c):out (c),e:adc hl,de:ld de,(#1234):im 2:ld a,r::in h,(c):out (c),h:sbc hl,hl:rrd:in l,(c):" \ + "out (c),l:adc hl,hl:rld::in 0,(c):out (c),0:sbc hl,sp:ld (#1234),sp:in a,(c):out (c),a:adc hl,sp:" \ + "ld sp,(#1234)::ldi:cpi:ini:outi:ldd:cpd:ind:outd::ldir:cpir:inir:otir:lddr:cpdr:indr:otdr::rlc b:" \ + "rlc c:rlc d:rlc e:rlc h:rlc l:rlc (hl):rlc a:rrc b:rrc c:rrc d:rrc e:rrc h:rrc l:rrc (hl):rrc a::" \ + "rl b:rl c:rl d:rl e:rl h:rl l:rl (hl):rl a:rr b:rr c:rr d:rr e:rr h:rr l:rr (hl):rr a:sla b:sla c:" \ + "sla d:sla e:sla h:sla l:sla (hl):sla a:sra b:sra c:sra d:sra e:sra h:sra l:sra (hl):sra a::sll b:" \ + "sll c:sll d:sll e:sll h:sll l:sll (hl):sll a:srl b:srl c:srl d:srl e:srl h:srl l:srl (hl):srl a::" \ + "bit 0,b:bit 0,c:bit 0,d:bit 0,e:bit 0,h:bit 0,l:bit 0,(hl):bit 0,a::bit 1,b:bit 1,c:bit 1,d:bit 1,e:" \ + "bit 1,h:bit 1,l:bit 1,(hl):bit 1,a::bit 2,b:bit 2,c:bit 2,d:bit 2,e:bit 2,h:bit 2,l:bit 2,(hl):" \ + "bit 2,a::bit 3,b:bit 3,c:bit 3,d:bit 3,e:bit 3,h:bit 3,l:bit 3,(hl):bit 3,a::bit 4,b:bit 4,c:" \ + "bit 4,d:bit 4,e:bit 4,h:bit 4,l:bit 4,(hl):bit 4,a::bit 5,b:bit 5,c:bit 5,d:bit 5,e:bit 5,h:bit 5,l:" \ + "bit 5,(hl):bit 5,a::bit 6,b:bit 6,c:bit 6,d:bit 6,e:bit 6,h:bit 6,l:bit 6,(hl):bit 6,a::bit 7,b:" \ + "bit 7,c:bit 7,d:bit 7,e:bit 7,h:bit 7,l:bit 7,(hl):bit 7,a::res 0,b:res 0,c:res 0,d:res 0,e:res 0,h:" \ + "res 0,l:res 0,(hl):res 0,a::res 1,b:res 1,c:res 1,d:res 1,e:res 1,h:res 1,l:res 1,(hl):res 1,a::" \ + "res 2,b:res 2,c:res 2,d:res 2,e:res 2,h:res 2,l:res 2,(hl):res 2,a::res 3,b:res 3,c:res 3,d:res 3,e:" \ + "res 3,h:res 3,l:res 3,(hl):res 3,a::res 4,b:res 4,c:res 4,d:res 4,e:res 4,h:res 4,l:res 4,(hl):" \ + "res 4,a::res 5,b:res 5,c:res 5,d:res 5,e:res 5,h:res 5,l:res 5,(hl):res 5,a::res 6,b:res 6,c:" \ + "res 6,d:res 6,e:res 6,h:res 6,l:res 6,(hl):res 6,a::res 7,b:res 7,c:res 7,d:res 7,e:res 7,h:res 7,l:" \ + "res 7,(hl):res 7,a::set 0,b:set 0,c:set 0,d:set 0,e:set 0,h:set 0,l:set 0,(hl):set 0,a::set 1,b:" \ + "set 1,c:set 1,d:set 1,e:set 1,h:set 1,l:set 1,(hl):set 1,a::set 2,b:set 2,c:set 2,d:set 2,e:set 2,h:" \ + "set 2,l:set 2,(hl):set 2,a::set 3,b:set 3,c:set 3,d:set 3,e:set 3,h:set 3,l:set 3,(hl):set 3,a::" \ + "set 4,b:set 4,c:set 4,d:set 4,e:set 4,h:set 4,l:set 4,(hl):set 4,a::set 5,b:set 5,c:set 5,d:set 5,e:" \ + "set 5,h:set 5,l:set 5,(hl):set 5,a::set 6,b:set 6,c:set 6,d:set 6,e:set 6,h:set 6,l:set 6,(hl):" \ + "set 6,a::set 7,b:set 7,c:set 7,d:set 7,e:set 7,h:set 7,l:set 7,(hl):set 7,a::add ix,bc::add ix,de::" \ + "ld ix,#1234:ld (#1234),ix:inc ix:inc xh:dec xh:ld xh,#12:add ix,ix:ld ix,(#1234):dec ix:inc xl:" \ + "dec xl:ld xl,#12::inc (ix+#12):dec (ix+#12):ld (ix+#12),#34:add ix,sp::ld b,xh:ld b,xl:" \ + "ld b,(ix+#12):ld c,xh:ld c,xl:ld c,(ix+#12):::ld d,xh:ld d,xl:ld d,(ix+#12):ld e,xh:ld e,xl:" \ + "ld e,(ix+#12)::ld xh,b:ld xh,c:ld xh,d:ld xh,e:ld xh,xh:ld xh,xl:ld h,(ix+#12):ld xh,a:ld xl,b:" \ + "ld xl,c:ld xl,d:ld xl,e:ld xl,xh:ld xl,xl:ld l,(ix+#12):ld xl,a::ld (ix+#12),b:ld (ix+#12),c:" \ + "ld (ix+#12),d:ld (ix+#12),e:ld (ix+#12),h:ld (ix+#12),l:ld (ix+#12),a:ld a,xh:ld a,xl:" \ + "ld a,(ix+#12)::add xh:add xl:add (ix+#12):adc xh:adc xl:adc (ix+#12)::sub xh:sub xl:sub (ix+#12):" \ + "sbc xh:sbc xl:sbc (ix+#12)::and xh:and xl:and (ix+#12):xor xh:xor xl:xor (ix+#12)::or xh:or xl:" \ + "or (ix+#12):cp xh:cp xl:cp (ix+#12)::pop ix:ex (sp),ix:push ix:jp (ix)::ld sp,ix:::rlc (ix+#12),b:" \ + "rlc (ix+#12),c:rlc (ix+#12),d:rlc (ix+#12),e:rlc (ix+#12),h:rlc (ix+#12),l:rlc (ix+#12):" \ + "rlc (ix+#12),a:rrc (ix+#12),b:rrc (ix+#12),c:rrc (ix+#12),d:rrc (ix+#12),e:rrc (ix+#12),h:" \ + "rrc (ix+#12),l:rrc (ix+#12):rrc (ix+#12),a::rl (ix+#12),b:rl (ix+#12),c:rl (ix+#12),d:rl (ix+#12),e:" \ + "rl (ix+#12),h:rl (ix+#12),l:rl (ix+#12):rl (ix+#12),a:rr (ix+#12),b:rr (ix+#12),c:rr (ix+#12),d:" \ + "rr (ix+#12),e:rr (ix+#12),h:rr (ix+#12),l:rr (ix+#12):rr (ix+#12),a::sla (ix+#12),b:sla (ix+#12),c:" \ + "sla (ix+#12),d:sla (ix+#12),e:sla (ix+#12),h:sla (ix+#12),l:sla (ix+#12):sla (ix+#12),a:" \ + "sra (ix+#12),b:sra (ix+#12),c:sra (ix+#12),d:sra (ix+#12),e:sra (ix+#12),h:sra (ix+#12),l:" \ + "sra (ix+#12):sra (ix+#12),a::sll (ix+#12),b:sll (ix+#12),c:sll (ix+#12),d:sll (ix+#12),e:" \ + "sll (ix+#12),h:sll (ix+#12),l:sll (ix+#12):sll (ix+#12),a:srl (ix+#12),b:srl (ix+#12),c:" \ + "srl (ix+#12),d:srl (ix+#12),e:srl (ix+#12),h:srl (ix+#12),l:srl (ix+#12):srl (ix+#12),a::" \ + "bit 0,(ix+#12):bit 1,(ix+#12):bit 2,(ix+#12):bit 3,(ix+#12):bit 4,(ix+#12):bit 5,(ix+#12):" \ + "bit 6,(ix+#12):bit 7,(ix+#12):bit 0,(ix+#12),d:bit 1,(ix+#12),b:bit 2,(ix+#12),c:bit 3,(ix+#12),d:" \ + "bit 4,(ix+#12),e:bit 5,(ix+#12),h:bit 6,(ix+#12),l:bit 7,(ix+#12),a:::res 0,(ix+#12),b:" \ + "res 0,(ix+#12),c:res 0,(ix+#12),d:res 0,(ix+#12),e:res 0,(ix+#12),h:res 0,(ix+#12),l:res 0,(ix+#12):" \ + "res 0,(ix+#12),a::res 1,(ix+#12),b:res 1,(ix+#12),c:res 1,(ix+#12),d:res 1,(ix+#12),e:" \ + "res 1,(ix+#12),h:res 1,(ix+#12),l:res 1,(ix+#12):res 1,(ix+#12),a::res 2,(ix+#12),b:" \ + "res 2,(ix+#12),c:res 2,(ix+#12),d:res 2,(ix+#12),e:res 2,(ix+#12),h:res 2,(ix+#12),l:res 2,(ix+#12):" \ + "res 2,(ix+#12),a::res 3,(ix+#12),b:res 3,(ix+#12),c:res 3,(ix+#12),d:res 3,(ix+#12),e:" \ + "res 3,(ix+#12),h:res 3,(ix+#12),l:res 3,(ix+#12):res 3,(ix+#12),a::res 4,(ix+#12),b:" \ + "res 4,(ix+#12),c:res 4,(ix+#12),d:res 4,(ix+#12),e:res 4,(ix+#12),h:res 4,(ix+#12),l:" \ + "res 4,(ix+#12):res 4,(ix+#12),a::res 5,(ix+#12),b:res 5,(ix+#12),c:res 5,(ix+#12),d:" \ + "res 5,(ix+#12),e:res 5,(ix+#12),h:res 5,(ix+#12),l:res 5,(ix+#12):res 5,(ix+#12),a::" \ + "res 6,(ix+#12),b:res 6,(ix+#12),c:res 6,(ix+#12),d:res 6,(ix+#12),e:res 6,(ix+#12),h:" \ + "res 6,(ix+#12),l:res 6,(ix+#12):res 6,(ix+#12),a::res 7,(ix+#12),b:res 7,(ix+#12),c:" \ + "res 7,(ix+#12),d:res 7,(ix+#12),e:res 7,(ix+#12),h:res 7,(ix+#12),l:res 7,(ix+#12):" \ + "res 7,(ix+#12),a::set 0,(ix+#12),b:set 0,(ix+#12),c:set 0,(ix+#12),d:set 0,(ix+#12),e:" \ + "set 0,(ix+#12),h:set 0,(ix+#12),l:set 0,(ix+#12):set 0,(ix+#12),a::set 1,(ix+#12),b:" \ + "set 1,(ix+#12),c:set 1,(ix+#12),d:set 1,(ix+#12),e:set 1,(ix+#12),h:set 1,(ix+#12),l:" \ + "set 1,(ix+#12):set 1,(ix+#12),a::set 2,(ix+#12),b:set 2,(ix+#12),c:set 2,(ix+#12),d:" \ + "set 2,(ix+#12),e:set 2,(ix+#12),h:set 2,(ix+#12),l:set 2,(ix+#12):set 2,(ix+#12),a::" \ + "set 3,(ix+#12),b:set 3,(ix+#12),c:set 3,(ix+#12),d:set 3,(ix+#12),e:set 3,(ix+#12),h:" \ + "set 3,(ix+#12),l:set 3,(ix+#12):set 3,(ix+#12),a::set 4,(ix+#12),b:set 4,(ix+#12),c:" \ + "set 4,(ix+#12),d:set 4,(ix+#12),e:set 4,(ix+#12),h:set 4,(ix+#12),l:set 4,(ix+#12):" \ + "set 4,(ix+#12),a::set 5,(ix+#12),b:set 5,(ix+#12),c:set 5,(ix+#12),d:set 5,(ix+#12),e:" \ + "set 5,(ix+#12),h:set 5,(ix+#12),l:set 5,(ix+#12):set 5,(ix+#12),a::set 6,(ix+#12),b:" \ + "set 6,(ix+#12),c:set 6,(ix+#12),d:set 6,(ix+#12),e:set 6,(ix+#12),h:set 6,(ix+#12),l:" \ + "set 6,(ix+#12):set 6,(ix+#12),a::set 7,(ix+#12),b:set 7,(ix+#12),c:set 7,(ix+#12),d:" \ + "set 7,(ix+#12),e:set 7,(ix+#12),h:set 7,(ix+#12),l:set 7,(ix+#12):set 7,(ix+#12),a::add iy,bc::" \ + "add iy,de::ld iy,#1234:ld (#1234),iy:inc iy:inc yh:dec yh:ld yh,#12:add iy,iy:ld iy,(#1234):dec iy:" \ + "inc yl:dec yl:ld yl,#12::inc (iy+#12):dec (iy+#12):ld (iy+#12),#34:add iy,sp::ld b,yh:ld b,yl:" \ + "ld b,(iy+#12):ld c,yh:ld c,yl:ld c,(iy+#12):::ld d,yh:ld d,yl:ld d,(iy+#12):ld e,yh:ld e,yl:" \ + "ld e,(iy+#12)::ld yh,b:ld yh,c:ld yh,d:ld yh,e:ld yh,yh:ld yh,yl:ld h,(iy+#12):ld yh,a:ld yl,b:" \ + "ld yl,c:ld yl,d:ld yl,e:ld yl,yh:ld yl,yl:ld l,(iy+#12):ld yl,a::ld (iy+#12),b:ld (iy+#12),c:" \ + "ld (iy+#12),d:ld (iy+#12),e:ld (iy+#12),h:ld (iy+#12),l:ld (iy+#12),a:ld a,yh:ld a,yl:" \ + "ld a,(iy+#12)::add yh:add yl:add (iy+#12):adc yh:adc yl:adc (iy+#12)::sub yh:sub yl:" \ + "sub (iy+#12):sbc yh:sbc yl:sbc (iy+#12)::and yh:and yl:and (iy+#12):xor yh:xor yl:xor (iy+#12)::" \ + "or yh:or yl:or (iy+#12):cp yh:cp yl:cp (iy+#12)::pop iy:ex (sp),iy:push iy:jp (iy)::ld sp,iy::" \ + "rlc (iy+#12),b:rlc (iy+#12),c:rlc (iy+#12),d:rlc (iy+#12),e:rlc (iy+#12),h:rlc (iy+#12),l:" \ + "rlc (iy+#12):rlc (iy+#12),a:rrc (iy+#12),b:rrc (iy+#12),c:rrc (iy+#12),d:rrc (iy+#12),e:" \ + "rrc (iy+#12),h:rrc (iy+#12),l:rrc (iy+#12):rrc (iy+#12),a::rl (iy+#12),b:rl (iy+#12),c:" \ + "rl (iy+#12),d:rl (iy+#12),e:rl (iy+#12),h:rl (iy+#12),l:rl (iy+#12):rl (iy+#12),a:rr (iy+#12),b:" \ + "rr (iy+#12),c:rr (iy+#12),d:rr (iy+#12),e:rr (iy+#12),h:rr (iy+#12),l:rr (iy+#12):rr (iy+#12),a::" \ + "sla (iy+#12),b:sla (iy+#12),c:sla (iy+#12),d:sla (iy+#12),e:sla (iy+#12),h:sla (iy+#12),l:" \ + "sla (iy+#12):sla (iy+#12),a:sra (iy+#12),b:sra (iy+#12),c:sra (iy+#12),d:sra (iy+#12),e:" \ + "sra (iy+#12),h:sra (iy+#12),l:sra (iy+#12):sra (iy+#12),a::sll (iy+#12),b:sll (iy+#12),c:" \ + "sll (iy+#12),d:sll (iy+#12),e:sll (iy+#12),h:sll (iy+#12),l:sll (iy+#12):sll (iy+#12),a:" \ + "srl (iy+#12),b:srl (iy+#12),c:srl (iy+#12),d:srl (iy+#12),e:srl (iy+#12),h:srl (iy+#12),l:" \ + "srl (iy+#12):srl (iy+#12),a::bit 0,(iy+#12):bit 1,(iy+#12):bit 2,(iy+#12):bit 3,(iy+#12):" \ + "bit 4,(iy+#12):bit 5,(iy+#12):bit 6,(iy+#12):bit 7,(iy+#12)::res 0,(iy+#12),b:res 0,(iy+#12),c:" \ + "res 0,(iy+#12),d:res 0,(iy+#12),e:res 0,(iy+#12),h:res 0,(iy+#12),l:res 0,(iy+#12):" \ + "res 0,(iy+#12),a::res 1,(iy+#12),b:res 1,(iy+#12),c:res 1,(iy+#12),d:res 1,(iy+#12),e:" \ + "res 1,(iy+#12),h:res 1,(iy+#12),l:res 1,(iy+#12):res 1,(iy+#12),a::res 2,(iy+#12),b:" \ + "res 2,(iy+#12),c:res 2,(iy+#12),d:res 2,(iy+#12),e:res 2,(iy+#12),h:res 2,(iy+#12),l:" \ + "res 2,(iy+#12):res 2,(iy+#12),a::res 3,(iy+#12),b:res 3,(iy+#12),c:res 3,(iy+#12),d:" \ + "res 3,(iy+#12),e:res 3,(iy+#12),h:res 3,(iy+#12),l:res 3,(iy+#12):res 3,(iy+#12),a::" \ + "res 4,(iy+#12),b:res 4,(iy+#12),c:res 4,(iy+#12),d:res 4,(iy+#12),e:res 4,(iy+#12),h:" \ + "res 4,(iy+#12),l:res 4,(iy+#12):res 4,(iy+#12),a::res 5,(iy+#12),b:res 5,(iy+#12),c:" \ + "res 5,(iy+#12),d:res 5,(iy+#12),e:res 5,(iy+#12),h:res 5,(iy+#12),l:res 5,(iy+#12):" \ + "res 5,(iy+#12),a::res 6,(iy+#12),b:res 6,(iy+#12),c:res 6,(iy+#12),d:res 6,(iy+#12),e:" \ + "res 6,(iy+#12),h:res 6,(iy+#12),l:res 6,(iy+#12):res 6,(iy+#12),a::res 7,(iy+#12),b:" \ + "res 7,(iy+#12),c:res 7,(iy+#12),d:res 7,(iy+#12),e:res 7,(iy+#12),h:res 7,(iy+#12),l:" \ + "res 7,(iy+#12):res 7,(iy+#12),a::set 0,(iy+#12),b:set 0,(iy+#12),c:set 0,(iy+#12),d:" \ + "set 0,(iy+#12),e:set 0,(iy+#12),h:set 0,(iy+#12),l:set 0,(iy+#12):set 0,(iy+#12),a::" \ + "set 1,(iy+#12),b:set 1,(iy+#12),c:set 1,(iy+#12),d:set 1,(iy+#12),e:set 1,(iy+#12),h:" \ + "set 1,(iy+#12),l:set 1,(iy+#12):set 1,(iy+#12),a::set 2,(iy+#12),b:set 2,(iy+#12),c:" \ + "set 2,(iy+#12),d:set 2,(iy+#12),e:set 2,(iy+#12),h:set 2,(iy+#12),l:set 2,(iy+#12):" \ + "set 2,(iy+#12),a::set 3,(iy+#12),b:set 3,(iy+#12),c:set 3,(iy+#12),d:set 3,(iy+#12),e:" \ + "set 3,(iy+#12),h:set 3,(iy+#12),l:set 3,(iy+#12):set 3,(iy+#12),a::set 4,(iy+#12),b:" \ + "set 4,(iy+#12),c:set 4,(iy+#12),d:set 4,(iy+#12),e:set 4,(iy+#12),h:set 4,(iy+#12),l:" \ + "set 4,(iy+#12):set 4,(iy+#12),a::set 5,(iy+#12),b:set 5,(iy+#12),c:set 5,(iy+#12),d:" \ + "set 5,(iy+#12),e:set 5,(iy+#12),h:set 5,(iy+#12),l:set 5,(iy+#12):set 5,(iy+#12),a::" \ + "set 6,(iy+#12),b:set 6,(iy+#12),c:set 6,(iy+#12),d:set 6,(iy+#12),e:set 6,(iy+#12),h:" \ + "set 6,(iy+#12),l:set 6,(iy+#12):set 6,(iy+#12),a::set 7,(iy+#12),b:set 7,(iy+#12),c:" \ + "set 7,(iy+#12),d:set 7,(iy+#12),e:set 7,(iy+#12),h:set 7,(iy+#12),l:set 7,(iy+#12):" \ + "set 7,(iy+#12),a:" + +#define AUTOTEST_LABNUM "mavar=67:label{mavar}truc:ld hl,7+2*label{mavar}truc:mnt=1234567:lab2{mavar}{mnt}:" \ + "ld de,lab2{mavar}{mnt}:lab3{mavar}{mnt}h:ld de,lab3{mavar}{mnt}h" + +#define AUTOTEST_EQUNUM "mavar = 9:monlabel{mavar+5}truc:unalias{mavar+5}heu equ 50:autrelabel{unalias14heu}:ld hl,autrelabel50" + +#define AUTOTEST_DELAYNUM "macro test label: dw {label}: endm: repeat 3, idx:idx2 = idx-1:" \ + " test label_{idx2}: rend:repeat 3, idx:label_{idx-1}:nop:rend" + +#define AUTOTEST_STRUCT "org #1000:label1 :struct male:age defb 0:height defb 0:endstruct:struct female:" \ + "age defb 0:height defb 0:endstruct:struct couple:struct male husband:" \ + "struct female wife:endstruct:if $-label1!=0:stop:endif:ld a,(ix+couple.wife.age):" \ + "ld bc,couple:ld bc,{sizeof}couple:struct couple mycouple:" \ + "if mycouple.husband != mycouple.husband.age:stop:endif:ld hl,mycouple:" \ + "ld hl,mycouple.wife.age:ld bc,{sizeof}mycouple:macro cmplastheight p1:" \ + "ld hl,@mymale.height:ld a,{p1}:cp (hl):ld bc,{sizeof}@mymale:ld hl,@mymale:ret:" \ + "struct male @mymale:mend:cmplastheight 5:cmplastheight 3:nop" + +#define AUTOTEST_STRUCT2 "struct bite: preums defb 0: deuze defw 1: troize defs 10: endstruct:" \ + " if {sizeof}bite.preums!=1 || {sizeof}bite.deuze!=2 || {sizeof}bite.troize!=10: stop: endif: nop " + +#define AUTOTEST_REPEAT "ce=100:repeat 2,ce:repeat 5,cx:repeat 5,cy:defb cx*cy:rend:rend:rend:assert cx==6 && cy==6 && ce==3:" \ + "cpt=0:repeat:cpt=cpt+1:until cpt>4:assert cpt==5" + +#define AUTOTEST_REPEATKO "repeat 5:nop" + +#define AUTOTEST_WHILEKO "while 5:nop" + +#define AUTOTEST_TICKER "repeat 2: ticker start, mc:out (0),a:out (c),a:out (c),h:out (c),0:ticker stop, mc:if mc!=15:ld hl,bite:else:nop:endif:rend" + +#define AUTOTEST_ORG "ORG #8000,#1000:defw $:ORG $:defw $" + +#define AUTOTEST_BANKORG "bank 0:nop:org #5:nop:bank 1:unevar=10:bank 0:assert $==6:ret:bank 1:assert $==0:bank 0:assert $==7" + +#define AUTOTEST_VAREQU "label1 equ #C000:label2 equ (label1*2)/16:label3 equ label1-label2:label4 equ 15:var1=50*3+2:var2=12*label1:var3=label4-8:var4=label2:nop" + +#define AUTOTEST_FORMAT "hexa=#12A+$23B+45Ch+0x56D:deci=123.45+-78.54*2-(7-7)*2:bina=0b101010+1010b-%1111:assert hexa==3374 && deci==-33.63 && bina==37:nop" + +#define AUTOTEST_CHARSET "charset 'abcde',0:defb 'abcde':defb 'a','b','c','d','e':defb 'a',1*'b','c'*1,1*'d','e'*1:charset:" \ + "defb 'abcde':defb 'a','b','c','d','e':defb 'a',1*'b','c'*1,1*'d','e'*1" + +#define AUTOTEST_CHARSET2 "charset 97,97+26,0:defb 'roua':charset:charset 97,10:defb 'roua':charset 'o',5:defb 'roua':charset 'ou',6:defb 'roua'" + +#define AUTOTEST_NOCODE "let monorg=$:NoCode:Org 0:Element1 db 0:Element2 dw 3:Element3 ds 50:Element4 defb 'rdd':Org 0:pouet defb 'nop':" \ + "Code:Org monorg:cpt=$+element2+element3+element4:defs cpt,0" + +#define AUTOTEST_LZSEGMENT "org #100:debut:jr nz,zend:lz48:repeat 128:nop:rend:lzclose:jp zend:lz48:repeat 2:dec a:jr nz,@next:ld a,5:@next:jp debut:rend:" \ + "lzclose:zend" + +#define AUTOTEST_PAGETAG "bankset 0:org #5000:label1:bankset 1:org #9000:label2:bankset 2:" \ + "assert {page}label1==0x7FC0:assert {page}label2==0x7FC6:assert {pageset}label1==#7FC0:assert {pageset}label2==#7FC2:nop" + +#define AUTOTEST_PAGETAG2 "bankset 0:call maroutine:bank 4:org #C000:autreroutine:nop:" \ + "ret:bank 5:org #8000:maroutine:ldir:ret:bankset 2:org #9000:troize:nop:" \ + "assert {page}maroutine==#7FC5:assert {pageset}maroutine==#7FC2:assert {page}autreroutine==#7FC4:" \ + "assert {pageset}autreroutine==#7FC2:assert {page}troize==#7FCE:assert {pageset}troize==#7FCA" + +#define AUTOTEST_PAGETAG3 "buildsna:bank 2:assert {bank}$==2:assert {page}$==0x7FC0:assert {pageset}$==#7FC0:" \ + "bankset 1:org #4000:assert {bank}$==5:assert {page}$==0x7FC5:assert {pageset}$==#7FC2" + +#define AUTOTEST_SWITCH "mavar=4:switch mavar:case 1:nop:case 4:defb 4:case 3:defb 3:break:case 2:nop:case 4:defb 4:endswitch" + +#define AUTOTEST_PREPRO0 "\n\n\n\n;bitch\n\nnop\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nxor a\n\n\n\n\n\n\n\n\n\n\n\n" + +#define AUTOTEST_PREPRO1 " macro DEBUG_INK0, coul: out (c), a: endm: ifndef NDEBUG:DEBUG_BORDER_NOPS = 0: endif" + +#define AUTOTEST_PREPRO2 " nop : mycode other tartine ldir : defw tartine,other, mycode : assert tartine==other && other==mycode && mycode==1 && $==9" + +#define AUTOTEST_PREPRO3 "nop\n \n /* test ' ici */\n nop /* retest */ 5\n /* bonjour\n prout\n caca\n pipi */" \ + " nop\n nop /* nop */ : nop\n ; grouik /*\n \n /* ; pouet */ ld hl,#2121\n " + +#define AUTOTEST_PREPRO4 "glop=0\n nop ; glop=1\n assert glop==0\n nop\n glop=0\n nop // glop=1\n assert glop==0\n nop\n glop=0 : /* glop=1 */ nop\n" \ + "assert glop==0\n nop\n \n glop=0 : /* glop=1 // */ nop\n assert glop==0\n nop\n glop=0 : /* glop=1 ; */ nop\n" \ + "assert glop==0\n nop\n glop=0 ; /* glop=1\n nop // glop=2 /*\n assert glop==0\n nop\n" + +#define AUTOTEST_PROXIM "routine:.step1:jp .step2:.step2:jp .step1:deuze:nop:.step1:djnzn .step1:djnz routine.step2" + +#define AUTOTEST_TAGPRINT "unevar=12:print 'trucmuche{unevar}':print '{unevar}':print '{unevar}encore','pouet{unevar}{unevar}':ret" + +#define AUTOTEST_TAGFOLLOW "ret:uv=1234567890:unlabel_commeca_{uv} equ pouetpouetpouettroulala:pouetpouetpouettroulala:assert unlabel_commeca_{uv}>0" + +#define AUTOTEST_TAGREALLOC "zoomscroller_scroller_height equ 10: another_super_long_equ equ 256: pinouille_super_long_useless_equ equ 0: " \ + "zz equ 1111111111: org #100: repeat zoomscroller_scroller_height,idx: " \ + "zoomscroller_buffer_line_{idx-1}_{pinouille_super_long_useless_equ} nop: zoomscroller_buffer_line_{idx-1}_{zz} nop: " \ + "rend: align 256: repeat zoomscroller_scroller_height,idx: zoomscroller_buffer_line_{idx-1}_{pinouille_super_long_useless_equ}_duplicate nop: " \ + "zoomscroller_buffer_line_{idx-1}_{zz}_duplicate nop: rend: repeat zoomscroller_scroller_height, line: idx = line - 1: " \ + "assert zoomscroller_buffer_line_{idx}_{pinouille_super_long_useless_equ} + another_super_long_equ == "\ + "zoomscroller_buffer_line_{idx}_{pinouille_super_long_useless_equ}_duplicate: " \ + "assert zoomscroller_buffer_line_{idx}_{zz} + another_super_long_equ == zoomscroller_buffer_line_{idx}_{zz}_duplicate: rend" + +#define AUTOTEST_DEFUSED "ld hl,labelused :ifdef labelused:fail 'labelexiste':endif:ifndef labelused:else:fail 'labelexiste':endif:" \ + "ifnused labelused:fail 'labelused':endif:ifused labelused:else:fail 'labelused':endif:labelused" + +#define AUTOTEST_SAVEINVALID1 "nop : save'gruik',20,-100" + +#define AUTOTEST_SAVEINVALID2 "nop : save'gruik',-20,100" + +#define AUTOTEST_SAVEINVALID3 "nop : save'gruik',40000,30000" + +#define AUTOTEST_MACROPROX " macro unemacro: nop: endm: global_label: ld hl, .table: .table" + +#define AUTOTEST_PROXBACK " macro grouik: @truc djnz @truc: .unprox djnz .unprox: mend:" \ + "ld b,2: unglobal nop: djnz unglobal: ld b,2: .unprox nop: djnz .unprox: beforelocal=$ :" \ + "repeat 2: @unlocal: ld b,2: .unprox nop: djnz .unprox: grouik : djnz .unprox : rend: assert .unprox < beforelocal : nop" +#define AUTOTEST_LOCAPROX "repeat 1: @label nop: .prox nop: @label2 nop: djnz @label.prox: rend" + +#define AUTOTEST_QUOTES "defb 'rdd':str 'rdd':charset 'rd',0:defb '\\r\\d':str '\\r\\d'" + +#define AUTOTEST_NEGATIVE "ld a,-5: ld bc,-0x3918:ld de,0+-#3918:ld de,-#3918:var1=-5+6:var2=-#3918+#3919:assert var1+var2==2" + +#define AUTOTEST_FORMULA1 "a=5:b=2:assert int(a/b)==3:assert !a+!b==0:a=a*100:b=b*100:assert a*b==100000:ld hl,a*b-65536:a=123+-5*(-6/2)-50*2<<1" + +#define AUTOTEST_FORMULA2 "vala= (0.5+(4*0.5))*6:valb= int((0.5+(4*0.5))*6):nop:if vala!=valb:push erreur:endif" + +#define AUTOTEST_FRAC "mavar=frac(5.5):assert mavar==0.5:assert frac(6.6)==0.6:assert frac(1.1)==0.1:assert frac(1)==0:assert frac(100000)==0:nop" + +/* test override control between bank and bankset in snapshot mode + temp workspace */ +#define AUTOTEST_BANKSET "buildsna:bank 0:nop:bank 1:nop:bank:nop:bank 2:nop:bank 3:nop:bankset 1:nop:bank 8:nop:bank 9:nop:bank 10:nop:bank 11:nop" + +#define AUTOTEST_LIMITOK "org #100:limit #102:nop:limit #103:ld a,0:protect #105,#107:limit #108:xor a:org $+3:inc a" + +#define AUTOTEST_LIMITKO "limit #100:org #100:add ix,ix" + +#define AUTOTEST_DEFS "defs 256,0" + +#define AUTOTEST_LIMIT03 "limit -1 : nop" +#define AUTOTEST_LIMIT04 "limit #10000 : nop" +#define AUTOTEST_LIMIT05 "org #FFFF : ldir" +#define AUTOTEST_LIMIT06 "org #FFFF : nop" + +#define AUTOTEST_LIMIT07 "org #ffff : Start: equ $ : di : ld hl,#c9fb : ld (#38),hl" + +#define AUTOTEST_DELAYED_RUN "run _start:nop:_start nop" + +#define AUTOTEST_INHIBITION "if 0:ifused truc:ifnused glop:ifdef bidule:ifndef machin:ifnot 1:nop:endif:nop:else:nop:endif:endif:endif:endif:endif" + +#define AUTOTEST_LZ4 "lz4:repeat 10:nop:rend:defb 'roudoudoudouoneatxkjhgfdskljhsdfglkhnopnopnopnop':lzclose" + +#define AUTOTEST_MAXERROR "repeat 20:aglapi:rend:nop" + +#define AUTOTEST_ENHANCED_LD "ld h,(ix+11): ld l,(ix+10): ld h,(iy+21): ld l,(iy+20): ld b,(ix+11): ld c,(ix+10):" \ + "ld b,(iy+21): ld c,(iy+20): ld d,(ix+11): ld e,(ix+10): ld d,(iy+21): ld e,(iy+20): ld hl,(ix+10): " \ + "ld hl,(iy+20):ld bc,(ix+10):ld bc,(iy+20): ld de,(ix+10):ld de,(iy+20)" + +#define AUTOTEST_ENHANCED_PUSHPOP "push bc,de,hl,ix,iy,af:pop hl,bc,de,iy,ix,af:nop 2:" \ + "push bc:push de:push hl:push ix:push iy:push af:"\ + "pop hl:pop bc:pop de:pop iy:pop ix:pop af:nop:nop" +#define AUTOTEST_ENHANCED_LD2 "ld (ix+0),hl: ld (ix+0),de: ld (ix+0),bc: ld (iy+0),hl: ld (iy+0),de: ld (iy+0),bc:"\ +"ld (ix+1),h: ld (ix+0),l: ld (ix+1),d: ld (ix+0),e: ld (ix+1),b: ld (ix+0),c: ld (iy+1),h: ld (iy+0),l: ld (iy+1),d: ld (iy+0),e: ld (iy+1),b: ld (iy+0),c" + +#define AUTOTEST_INHIBITION2 "ifdef roudoudou:macro glop bank,page,param:ld a,{bank}:ld hl,{param}{bank}:if {bank}:nop:else:exx:" \ + "endif::switch {param}:nop:case 4:nop:case {param}:nop:default:nop:break:endswitch:endif:defb 'coucou'" + +#define AUTOTEST_INHIBITIONMAX "roudoudou:ifndef roudoudou:if pouet:macro glop bank,page,param:ifdef nanamouskouri:ld hl,{param}{bank}:"\ + "elseif aglapi:exx:endif:if {bank}:nop:elseif {grouik}:exx:endif:switch {bite}:nop:case {nichon}:nop:default:nop:break:endswitch:else:"\ + "ifnot {jojo}:exx:endif:endif:else:defb 'coucou':endif" + +#define AUTOTEST_NOEXPORT "unlabel nop:.unlocal nop:unevar=5:unequ equ 10:noexport unlabel, .unlocal, "\ + "unlabel.unlocal, unevar, unequ:enoexport unlabel, .unlocal, unlabel.unlocal, unevar, unequ" + +#define AUTOTEST_UNDERVAR "coucou nop:_coucou nop:_mavar=5:jr _coucou+_mavar:print _mavar" + +#define AUTOTEST_CODESKIP "org #100: nop: old_dollar=$: nocode: defs 10: assert $==old_dollar+10: code: "\ + "assert $==old_dollar+10: org #200: nop: old_dollar=$: nocode: defs 10: assert $==old_dollar+10: code skip: assert $==old_dollar" + +#define AUTOTEST_EMBEDDED_ERRORS "nop : rien : rien : rien : glop nop : glapi nop" + +#define AUTOTEST_EMBEDDED_LABELS " disarkCounter = 0:MACRO dkps:PLY_AKG_DisarkPointerRegionStart_{disarkCounter}:ENDM" \ + ":MACRO dkpe\nPLY_AKG_DisarkPointerRegionEnd_{disarkCounter}:\ndisarkCounter = disarkCounter + 1:ENDM:\ndkps\ndkpe\ndkps" + +#define AUTOTEST_TAGLOOP "tab1 = #100:tab2 = #200:tab3 = #300:macro genreg channel,psg:label{channel} EQU tab{psg} + channeloffset:"\ + "endm:channel=1:while channel <= 9:psg=floor(((channel-1)/3)+1):channeloffset=((channel-1) % 3)*2:"\ + "genreg {channel},{psg}:channel=channel+1:wend:assert LABEL5==#0202:assert LABEL6==#0204:assert LABEL7==#0300:"\ + "assert LABEL8==#0302:nop" + +#define AUTOTEST_PLUSCOLOR " myval=0x123 : assert getr(myval)==2 : assert getb(myval)==3 : assert getg(myval)==1 : assert getv(myval)==1" \ + " : assert setr(2)+setv(1)+setb(3)==0x123 : assert setv(-2)==setv(0) && setb(220)==setb(15) : nop " + +#define AUTOTEST_ASSERT " macro zem,nom,adr: overflow equ $-{adr}: assert $<{adr},{nom}: nop: mend: zem 'bidule',#2FFF :" \ + " macro zem2,nom,adr: overflow2 equ $-{adr}: assert $<{adr},{nom},{adr},overflow2: nop: mend: zem2 'bidule',#2FFF :" \ + " macro zem3,nom,adr: overflow3 equ $-{adr}: assert $<{adr},{nom},{hex}{adr},overflow3: nop: mend: zem3 'bidule',#2FFF " + + +struct s_autotest_keyword { + char *keywordtest; + int result; +}; + +struct s_autotest_keyword autotest_keyword[]={ + {"ld ly,h",1}, {"ld lx,h",1}, {"ld ly,l",1}, {"ld lx,l",1}, {"ld hy,h",1}, {"ld hx,h",1}, {"ld hy,l",1}, {"ld hx,l",1}, + {"nop",0},{"nop 2",0},{"nop a",1},{"nop (hl)",1},{"nop nop",1},{"nop grouik",1}, + {"ldir",0},{"ldir 5",1},{"ldir ldir",1},{"ldir (hl)",1},{"ldir a",1},{"ldir grouik",1}, + {"ldi",0},{"ldi 5",1},{"ldi ldi",1},{"ldi (hl)",1},{"ldi a",1},{"ldi grouik",1}, + {"lddr",0},{"lddr 5",1},{"lddr lddr",1},{"lddr (hl)",1},{"lddr a",1},{"lddr groudk",1}, + {"ldd",0},{"ldd 5",1},{"ldd ldd",1},{"ldd (hl)",1},{"ldd a",1},{"ldd groudk",1}, + {"jr $",0},{"jr 0",0},{"jr jr",1},{"jr (hl)",1},{"jr a",1}, + {"jr c,$",0},{"jr c,0",0},{"jr c,jr",1},{"jr c,(hl)",1},{"jr c,a",1}, + {"jr nc,$",0},{"jr nc,0",0},{"jr nc,jr",1},{"jr nc,(hl)",1},{"jr nc,a",1}, + {"jr z,$",0},{"jr 0",0},{"jr jr",1},{"jr (hl)",1},{"jr a",1}, + {"jr nz,$",0},{"jr 0",0},{"jr jr",1},{"jr (hl)",1},{"jr a",1}, + {"jp $",0},{"jp 0",0},{"jp jp",1},{"jp (hl)",0},{"jp (ix)",0},{"jp (iy)",0},{"jp (de)",1},{"jp a",1}, + {"jp c,$",0},{"jp c,0",0},{"jp c,jp",1}, {"jp c,(hl)",1}, {"jp c,(ix)",1}, {"jp c,(iy)",1},{"jp c,(de)",1},{"jp c,a",1}, + {"jp nc,$",0},{"jp nc,0",0},{"jp nc,jp",1},{"jp nc,(hl)",1},{"jp nc,(ix)",1},{"jp nc,(iy)",1},{"jp nc,(de)",1},{"jp nc,a",1}, + {"jp z,$",0},{"jp z,0",0},{"jp z,jp",1}, {"jp z,(hl)",1}, {"jp z,(ix)",1}, {"jp z,(iy)",1},{"jp z,(de)",1},{"jp z,a",1}, + {"jp nz,$",0},{"jp nz,0",0},{"jp nz,jp",1},{"jp nz,(hl)",1},{"jp nz,(ix)",1},{"jp nz,(iy)",1},{"jp nz,(de)",1},{"jp nz,a",1}, + {"jp pe,$",0},{"jp pe,0",0},{"jp pe,jp",1},{"jp pe,(hl)",1},{"jp pe,(ix)",1},{"jp pe,(iy)",1},{"jp pe,(de)",1},{"jp pe,a",1}, + {"jp po,$",0},{"jp po,0",0},{"jp po,jp",1},{"jp po,(hl)",1},{"jp po,(ix)",1},{"jp po,(iy)",1},{"jp po,(de)",1},{"jp po,a",1}, + {"jp p,$",0},{"jp p,0",0},{"jp p,jp",1}, {"jp p,(hl)",1}, {"jp p,(ix)",1}, {"jp p,(iy)",1},{"jp p,(de)",1},{"jp p,a",1}, + {"jp m,$",0},{"jp m,0",0},{"jp m,jp",1}, {"jp m,(hl)",1}, {"jp m,(ix)",1}, {"jp m,(iy)",1},{"jp m,(de)",1},{"jp m,a",1}, + {"ret",0},{"ret c",0},{"ret nc",0},{"ret pe",0},{"ret po",0},{"ret m",0},{"ret p",0},{"reti",0},{"ret ret",1},{"ret 5",1},{"ret (hl)",1},{"ret a",1}, + {"xor a",0},{"xor a,b",1},{"xor",1},{"xor (de)",1},{"xor (hl)",0},{"xor (bc)",1},{"xor (ix+0)",0},{"xor (iy+0)",},{"xor xor",1}, + {"and a",0},{"and a,b",1},{"xor",1},{"and (de)",1},{"and (hl)",0},{"and (bc)",1},{"and (ix+0)",0},{"and (iy+0)",},{"and xor",1}, + {"or a",0},{"or a,b",1},{"xor",1},{"or (de)",1},{"or (hl)",0},{"or (bc)",1},{"or (ix+0)",0},{"or (iy+0)",},{"or xor",1}, + {"add",1},{"add a",0},{"add a,a",0},{"add add",1},{"add (hl)",0},{"add (de)",1},{"add xh",0},{"add grouik",1}, + {"add hl,ix",1},{"add hl,iy",1},{"add ix,iy",1},{"add iy,ix",1},{"add hl,0",1},{"add hl,grouik",1},{"add ix,hl",1},{"add iy,hl",1}, + {"adc",1},{"adc a",0},{"adc a,a",0},{"adc adc",1},{"adc (hl)",0},{"adc (de)",1},{"adc xh",0},{"adc grouik",1}, + {"adc hl,ix",1},{"adc hl,iy",1},{"adc ix,iy",1},{"adc iy,ix",1},{"adc hl,0",1},{"adc hl,grouik",1},{"adc ix,hl",1},{"adc iy,hl",1}, + {"sub",1},{"sub a",0},{"sub a,a",0},{"sub sub",1},{"sub (hl)",0},{"sub (de)",1},{"sub xh",0},{"sub grouik",1}, + {"sub hl,ix",1},{"sub hl,iy",1},{"sub ix,iy",1},{"sub iy,ix",1},{"sub hl,0",1},{"sub hl,grouik",1},{"sub ix,hl",1},{"sub iy,hl",1}, + {"sbc",1},{"sbc a",0},{"sbc a,a",0},{"sbc sbc",1},{"sbc (hl)",0},{"sbc (de)",1},{"sbc xh",0},{"sbc grouik",1}, + {"sbc hl,ix",1},{"sbc hl,iy",1},{"sbc ix,iy",1},{"sbc iy,ix",1},{"sbc hl,0",1},{"sbc hl,grouik",1},{"sbc ix,hl",1},{"sbc iy,hl",1}, + {"exx",0},{"exx hl",1},{"exx hl,de",1},{"exx af,af'",1},{"exx exx",1},{"exx 5",1}, + {"ex",1},{"ex af,af'",0},{"ex hl,de",0},{"ex hl,bc",1},{"ex hl,hl",1},{"ex hl,ix",1}, + {"cp",1},{"cp cp ",1},{"cp 5",0},{"cp c",0},{"cp a,5",0},{"cp a,c",0},{"cp hl",1},{"cp (hl)",0},{"cp a,(hl)",0},{"cp (de)",1},{"cp de",1}, + {"cpi",0},{"cpi (hl)",1},{"cpi a",1},{"cpi 5",1}, + {"cpd",0},{"cpd (hl)",1},{"cpd a",1},{"cpd 5",1}, + {"cpir",0},{"cpir (hl)",1},{"cpir a",1},{"cpir 5",1}, + {"cpdr",0},{"cpdr (hl)",1},{"cpdr a",1},{"cpdr 5",1}, + {"call #1234",0},{"call call",1},{"call (hl)",1},{"call (ix)",1},{"call (iy)",1},{"call (de)",1},{"call hl",1},{"call bc",1},{"call a",1},{"call 5,5",1}, + {"rst 5",1},{"rst",1},{"rst 0",0},{"rst rst",1},{"rst (hl)",1},{"rst (ix)",1},{"rst (iy)",1},{"rst z",1},{"rst z,0",1}, + {"djnz",1},{"djnz $",0},{"djnz $,0",1},{"djnz djnz",1},{"djnz (hl)",1}, + {"djnz (ix)",1},{"djnz (iy)",1},{"djnz (bc)",1},{"djnz bc",1},{"djnz ix",1},{"djnz iy",1},{"djnz hl",1}, + {"push",1},{"push push",1},{"push pop",1},{"push af'",1},{"push (ix)",1},{"push (hl)",1},{"push (#1234)",1},{"push #1234",1}, + {"pop",1},{"pop pop",1},{"pop push",1},{"pop af'",1},{"pop (ix)",1},{"pop (hl)",1},{"pop (#1234)",1},{"pop #1234",1}, + {"set -1,a",1},{"set 9,a",1},{"set 0,xh",1},{"set 0,ix",1},{"set 0",1},{"set",1},{"set set",1},{"set 0,a,a",1},{"set 0,(ix+0),xh",1}, + {"bit -1,a",1},{"bit 9,a",1},{"bit 0,xh",1},{"bit 0,ix",1},{"bit 0",1},{"bit",1},{"bit bit",1},{"bit 0,a,a",1},{"bit 0,(ix+0),xh",1}, + {"res -1,a",1},{"res 9,a",1},{"res 0,xh",1},{"res 0,ix",1},{"res 0",1},{"res",1},{"res res",1},{"res 0,a,a",1},{"res 0,(ix+0),xh",1}, + {"srl",1},{"srl srl",1},{"srl hl",0}, /* srl hl is a kind of macro */ + {"rld a",1},{"rld (hl)",1},{"rld rld",1},{"rld 5",1},{"rld (ix)",1}, + {"rrd a",1},{"rrd (hl)",1},{"rrd rrd",1},{"rrd 5",1},{"rrd (ix)",1}, + {"cpl a",1},{"cpl (hl)",1},{"cpl cpl",1},{"cpl 0",1}, + {"daa daa daa",1},{"daa 0",1},{"daa (hl)",1}, + {"scf scf",1},{"scf 0",1},{"scf (hl)",1}, + {"ccf ccf",1},{"ccf 0",1},{"ccf (hl)",1}, + {"out",1},{"out out",1},{"out (c)",1},{"out (c),xh",1},{"out 0",1}, + {"out (c),hl",1},{"out (hl),c",1},{"out (c),(ix+0)",1},{"out (c),a,b",1}, + {"outi 0",1},{"outi (hl)",1}, + {"otir 0",1},{"otir (hl)",1}, + {"otdr 0",1},{"otdr (hl)",1}, + {"outd 0",1},{"outd (hl)",1}, + {"in",1},{"in in",1},{"in (c)",1},{"in xh,(c)",1},{"in 0",1}, + {"in hl,(c)",1},{"in c,(hl)",1},{"in (c),(ix+0)",1},{"in a,(c),b",1}, + {"ini 0",1},{"ini (hl)",1}, + {"inir 0",1},{"inir (hl)",1}, + {"indr 0",1},{"indr (hl)",1}, + {"ind 0",1},{"ind (hl)",1}, + {"di 5",1},{"di di",1},{"di hl",1},{"di a",1}, + {"ei 5",1},{"ei ei",1},{"ei hl",1},{"ei a",1}, + {"im",1},{"im 3",1},{"im -1",1},{"im (hl)",1}, + {"halt 5",1},{"reti 5",1},{"retn 5",1},{"ld i,b",1},{"ld b,i",1}, + + {"repeat 5:nop:rend",0},{"repeat 100000:a=5:rend",1},{"repeat -5:nop:rend",1},{"repeat repeat:nop:rend",1}, + {"macro bidule:nop:mend:bidule",0},{"macro bidule:nop:macro glop:nop:mend:mend:bidule",1}, + {"macro bidule:nop",1},{"macro bidule:nop:mend:macro glop:nop:bidule",1}, + /* + {"",},{"",},{"",},{"",},{"",},{"",},{"",},{"",},{"",},{"",},{"",},{"",}, + {"",},{"",},{"",},{"",},{"",},{"",},{"",},{"",},{"",},{"",},{"",},{"",}, + {"",},{"",},{"",},{"",},{"",},{"",},{"",},{"",},{"",},{"",},{"",},{"",}, + */ + {NULL,0} +}; + +void MiniDump(unsigned char *opcode, int opcodelen) { + #undef FUNC + #define FUNC "MiniDump" + + int i; + printf("%d byte%s to dump\n",opcodelen,opcodelen>1?"s":""); + for (i=0;i<opcodelen;i++) { + printf("%02X \n",opcode[i]); + } + printf("\n"); +} + +void RasmAutotest(void) +{ + #undef FUNC + #define FUNC "RasmAutotest" + + struct s_rasm_info *debug; + unsigned char *opcode=NULL; + int opcodelen,ret,filelen; + int cpt=0,chk,i,idx; + char tmpstr1[256],tmpstr2[256],*tmpstr3,**tmpsplit; + +#ifdef RDD + printf("\n%d bytes\n",_static_library_memory_used); +#endif +#if 0 + /* Autotest CORE */ + #ifdef OS_WIN + printf(".");fflush(stdout); + strcpy(tmpstr1,".\\archives\\job.asm"); + strcpy(tmpstr2,".\\archives\\job.asm"); + SimplifyPath(tmpstr1);if (strcmp(tmpstr1,tmpstr2)) {printf("Autotest %03d ERROR (Core:SimplifyPath0) %s!=%s\n",cpt,tmpstr1,tmpstr2);exit(-1);} + strcpy(tmpstr1,".\\archives\\..\\job.asm"); + strcpy(tmpstr2,".\\job.asm"); + SimplifyPath(tmpstr1);if (strcmp(tmpstr1,tmpstr2)) {printf("Autotest %03d ERROR (Core:SimplifyPath1) %s!=%s\n",cpt,tmpstr1,tmpstr2);exit(-1);} + strcpy(tmpstr1,".\\archives\\gruik\\..\\..\\job.asm"); + strcpy(tmpstr2,".\\job.asm"); + SimplifyPath(tmpstr1);if (strcmp(tmpstr1,tmpstr2)) {printf("Autotest %03d ERROR (Core:SimplifyPath2) %s!=%s\n",cpt,tmpstr1,tmpstr2);exit(-1);} + strcpy(tmpstr1,".\\archives\\..\\gruik\\..\\job.asm"); + strcpy(tmpstr2,".\\job.asm"); + SimplifyPath(tmpstr1);if (strcmp(tmpstr1,tmpstr2)) {printf("Autotest %03d ERROR (Core:SimplifyPath3) %s!=%s\n",cpt,tmpstr1,tmpstr2);exit(-1);} + strcpy(tmpstr1,"..\\archives\\job.asm"); + strcpy(tmpstr2,"..\\archives\\job.asm"); + SimplifyPath(tmpstr1);if (strcmp(tmpstr1,tmpstr2)) {printf("Autotest %03d ERROR (Core:SimplifyPath4) %s!=%s\n",cpt,tmpstr1,tmpstr2);exit(-1);} + strcpy(tmpstr1,".\\src\\..\\..\\src\\job.asm"); + strcpy(tmpstr2,"..\\src\\job.asm"); + SimplifyPath(tmpstr1);if (strcmp(tmpstr1,tmpstr2)) {printf("Autotest %03d ERROR (Core:SimplifyPath5) %s!=%s\n",cpt,tmpstr1,tmpstr2);exit(-1);} + strcpy(tmpstr1,"src\\..\\..\\src\\job.asm"); + strcpy(tmpstr2,"..\\src\\job.asm"); + SimplifyPath(tmpstr1);if (strcmp(tmpstr1,tmpstr2)) {printf("Autotest %03d ERROR (Core:SimplifyPath6) %s!=%s\n",cpt,tmpstr1,tmpstr2);exit(-1);} + cpt++; + #else + printf(".");fflush(stdout); + strcpy(tmpstr1,"./archives/job.asm"); + strcpy(tmpstr2,"./archives/job.asm"); + SimplifyPath(tmpstr1);if (strcmp(tmpstr1,tmpstr2)) {printf("Autotest %03d ERROR (Core:SimplifyPath0) %s!=%s\n",cpt,tmpstr1,tmpstr2);exit(-1);} + strcpy(tmpstr1,"./archives/../job.asm"); + strcpy(tmpstr2,"./job.asm"); + SimplifyPath(tmpstr1);if (strcmp(tmpstr1,tmpstr2)) {printf("Autotest %03d ERROR (Core:SimplifyPath1) %s!=%s\n",cpt,tmpstr1,tmpstr2);exit(-1);} + strcpy(tmpstr1,"./archives/gruik/../../job.asm"); + strcpy(tmpstr2,"./job.asm"); + SimplifyPath(tmpstr1);if (strcmp(tmpstr1,tmpstr2)) {printf("Autotest %03d ERROR (Core:SimplifyPath2) %s!=%s\n",cpt,tmpstr1,tmpstr2);exit(-1);} + strcpy(tmpstr1,"./archives/../gruik/../job.asm"); + strcpy(tmpstr2,"./job.asm"); + SimplifyPath(tmpstr1);if (strcmp(tmpstr1,tmpstr2)) {printf("Autotest %03d ERROR (Core:SimplifyPath3) %s!=%s\n",cpt,tmpstr1,tmpstr2);exit(-1);} + strcpy(tmpstr1,"../archives/job.asm"); + strcpy(tmpstr2,"../archives/job.asm"); + SimplifyPath(tmpstr1);if (strcmp(tmpstr1,tmpstr2)) {printf("Autotest %03d ERROR (Core:SimplifyPath4) %s!=%s\n",cpt,tmpstr1,tmpstr2);exit(-1);} + strcpy(tmpstr1,"./src/../../src/job.asm"); + strcpy(tmpstr2,"../src/job.asm"); + SimplifyPath(tmpstr1);if (strcmp(tmpstr1,tmpstr2)) {printf("Autotest %03d ERROR (Core:SimplifyPath5) %s!=%s\n",cpt,tmpstr1,tmpstr2);exit(-1);} + strcpy(tmpstr1,"src/../../../src/job.asm"); + strcpy(tmpstr2,"../../src/job.asm"); + SimplifyPath(tmpstr1);if (strcmp(tmpstr1,tmpstr2)) {printf("Autotest %03d ERROR (Core:SimplifyPath6) %s!=%s\n",cpt,tmpstr1,tmpstr2);exit(-1);} + cpt++; + printf(".");fflush(stdout); + if (strcmp(GetPath("/home/roudoudou/"),"/home/roudoudou/")) {printf("Autotest %03d ERROR (Core:GetPath0) [%s]\n",cpt,GetPath("/home/roudoudou/"));exit(-1);} + if (strcmp(GetPath("/home/roudoudou"),"/home/")) {printf("Autotest %03d ERROR (Core:GetPath1) [%s]\n",cpt,GetPath("/home/roudoudou"));exit(-1);} + cpt++; + #endif +#endif + + /* Autotest preprocessing */ + ret=RasmAssemble(AUTOTEST_VIRGULE,strlen(AUTOTEST_VIRGULE),&opcode,&opcodelen); + if (ret) {} else {printf("Autotest %03d ERROR (double comma must trigger an error) ret=%d\n",cpt,ret);exit(-1);} + if (opcode) MemFree(opcode);opcode=NULL;cpt++; +printf("testing preprocessor comma management in a string OK\n"); + + ret=RasmAssemble(AUTOTEST_VIRGULE2,strlen(AUTOTEST_VIRGULE2),&opcode,&opcodelen); + if (!ret) {} else {printf("Autotest %03d ERROR (double comma in a string must be OK)\n",cpt);exit(-1);} + if (opcode) MemFree(opcode);opcode=NULL;cpt++; +printf("testing preprocessor comma management in a string OK\n"); + + ret=RasmAssemble(AUTOTEST_PREPRO0,strlen(AUTOTEST_PREPRO0),&opcode,&opcodelen); + if (!ret) {} else {printf("Autotest %03d ERROR (uninitialize memory read!)\n",cpt);exit(-1);} + if (opcode) MemFree(opcode);opcode=NULL;cpt++; +printf("testing preprocessor non regression OK\n"); + + ret=RasmAssemble(AUTOTEST_PREPRO1,strlen(AUTOTEST_PREPRO1),&opcode,&opcodelen); + if (!ret) {} else {printf("Autotest %03d ERROR (freestyle case 1)\n",cpt);exit(-1);} + if (opcode) MemFree(opcode);opcode=NULL;cpt++; +printf("testing preprocessor freestyle OK\n"); + + ret=RasmAssemble(AUTOTEST_PREPRO2,strlen(AUTOTEST_PREPRO2),&opcode,&opcodelen); + if (!ret) {} else {printf("Autotest %03d ERROR (freestyle case 2)\n",cpt);exit(-1);} + if (opcode) MemFree(opcode);opcode=NULL;cpt++; + if (opcode) MemFree(opcode);opcode=NULL;cpt++; +printf("testing preprocessor freestyle variation 2 OK\n"); + + ret=RasmAssemble(AUTOTEST_PREPRO3,strlen(AUTOTEST_PREPRO3),&opcode,&opcodelen); + if (!ret && opcodelen==12 && opcode[11]==0x21) {} else {printf("Autotest %03d ERROR (multi-line comment)\n",cpt);MiniDump(opcode,opcodelen);exit(-1);} + if (opcode) MemFree(opcode);opcode=NULL;cpt++; +printf("testing multi-line comments 1 OK\n"); + + ret=RasmAssemble(AUTOTEST_PREPRO4,strlen(AUTOTEST_PREPRO4),&opcode,&opcodelen); + if (!ret && opcodelen==12) {} else {printf("Autotest %03d ERROR (multi-line comment 2)\n",cpt);MiniDump(opcode,opcodelen);exit(-1);} + if (opcode) MemFree(opcode);opcode=NULL;cpt++; +printf("testing multi-line comments 2 OK\n"); + + ret=RasmAssemble(AUTOTEST_OPERATOR_CONVERSION,strlen(AUTOTEST_OPERATOR_CONVERSION),&opcode,&opcodelen); + if (!ret) {} else {printf("Autotest %03d ERROR (maxam operator conversion)\n",cpt);exit(-1);} + if (opcode) MemFree(opcode);opcode=NULL;cpt++; +printf("testing Maxam operator conversion OK\n"); + + ret=RasmAssemble(AUTOTEST_OPERATOR_MODULO,strlen(AUTOTEST_OPERATOR_MODULO),&opcode,&opcodelen); + if (!ret && opcodelen==1) {} else {printf("Autotest %03d ERROR (modulo operator conversion)\n",cpt);exit(-1);} + if (opcode) MemFree(opcode);opcode=NULL;cpt++; +printf("testing modulo operator conversion OK\n"); + + ret=RasmAssemble(AUTOTEST_NOINCLUDE,strlen(AUTOTEST_NOINCLUDE),&opcode,&opcodelen); + if (!ret) {} else {printf("Autotest %03d ERROR (include missing file)\n",cpt);exit(-1);} + if (opcode) MemFree(opcode);opcode=NULL;cpt++; +printf("include on a missing file OK\n"); + + ret=RasmAssemble(AUTOTEST_FORMAT,strlen(AUTOTEST_FORMAT),&opcode,&opcodelen); + if (!ret) {} else {printf("Autotest %03d ERROR (digit formats)\n",cpt);exit(-1);} + if (opcode) MemFree(opcode);opcode=NULL;cpt++; +printf("testing digit formats OK\n"); + + /* Autotest assembling */ + ret=RasmAssemble(AUTOTEST_OPCODES,strlen(AUTOTEST_OPCODES),&opcode,&opcodelen); + if (!ret) {} else {printf("Autotest %03d ERROR (all opcodes)\n",cpt);exit(-1);} + if (opcode) MemFree(opcode);opcode=NULL;cpt++; +printf("testing all opcodes OK\n"); + + /* Autotest single instruction writes that must failed */ + tmpstr3=TxtStrDup(AUTOTEST_INSTRMUSTFAILED); + tmpsplit=TxtSplitWithChar(tmpstr3,':'); + + for (i=0;tmpsplit[i];i++) { + ret=RasmAssemble(tmpsplit[i],strlen(tmpsplit[i]),&opcode,&opcodelen); + if (ret) {} else {printf("Autotest %03d ERROR (opcodes that must fail) -> [%s]\n",cpt,tmpsplit[i]);exit(-1);} + if (opcode) MemFree(opcode);opcode=NULL; + } + + MemFree(tmpstr3);FreeFields(tmpsplit); + cpt++; +printf("testing various opcode tests OK\n"); + + idx=0; + while (autotest_keyword[idx].keywordtest) { + ret=RasmAssemble(autotest_keyword[idx].keywordtest,strlen(autotest_keyword[idx].keywordtest),&opcode,&opcodelen); + if (!ret && !autotest_keyword[idx].result) { + } else if (ret && autotest_keyword[idx].result) { + } else { + printf("Autotest %03d ERROR ([%s] test) is %s instead of %s\n",cpt,autotest_keyword[idx].keywordtest,!ret?"ok":"ko",ret?"ok":"ko"); + } + if (opcode) MemFree(opcode);opcode=NULL; + idx++; + } + cpt++; +printf("testing moar various opcode tests OK\n"); + + ret=RasmAssemble(AUTOTEST_ORG,strlen(AUTOTEST_ORG),&opcode,&opcodelen); + if (!ret && opcodelen==4 && opcode[1]==0x80 && opcode[2]==2 && opcode[3]==0x10) {} else {printf("Autotest %03d ERROR (ORG relocation)\n",cpt);exit(-1);} + if (opcode) MemFree(opcode);opcode=NULL;cpt++; +printf("testing ORG relocation OK\n"); + + ret=RasmAssemble(AUTOTEST_MAXERROR,strlen(AUTOTEST_MAXERROR),&opcode,&opcodelen); + if (ret) {} else {printf("Autotest %03d ERROR (must return an error code!)\n",cpt);exit(-1);} + if (opcode) MemFree(opcode);opcode=NULL;cpt++; +printf("testing error code OK\n"); + + ret=RasmAssemble(AUTOTEST_BANKORG,strlen(AUTOTEST_BANKORG),&opcode,&opcodelen); + if (!ret) {} else {printf("Autotest %03d ERROR (BANK org adr)\n",cpt);exit(-1);} + if (opcode) MemFree(opcode);opcode=NULL;cpt++; +printf("testing BANK/ORG OK\n"); + + ret=RasmAssemble(AUTOTEST_LIMITOK,strlen(AUTOTEST_LIMITOK),&opcode,&opcodelen); + if (!ret) {} else {printf("Autotest %03d ERROR (limit ok)\n",cpt);exit(-1);} + if (opcode) MemFree(opcode);opcode=NULL;cpt++; +printf("testing LIMIT 1 OK\n"); + + ret=RasmAssemble(AUTOTEST_LIMITKO,strlen(AUTOTEST_LIMITKO),&opcode,&opcodelen); + if (ret) {} else {printf("Autotest %03d ERROR (out of limit)\n",cpt);exit(-1);} + if (opcode) MemFree(opcode);opcode=NULL;cpt++; +printf("testing LIMIT 2 OK\n"); + + ret=RasmAssemble(AUTOTEST_LIMIT03,strlen(AUTOTEST_LIMIT03),&opcode,&opcodelen); + if (ret) {} else {printf("Autotest %03d ERROR (limit: negative limit)\n",cpt);exit(-1);} + if (opcode) MemFree(opcode);opcode=NULL;cpt++; +printf("testing inegative LIMIT OK\n"); + + ret=RasmAssemble(AUTOTEST_LIMIT04,strlen(AUTOTEST_LIMIT04),&opcode,&opcodelen); + if (ret) {} else {printf("Autotest %03d ERROR (limit: max limit test)\n",cpt);exit(-1);} + if (opcode) MemFree(opcode);opcode=NULL;cpt++; +printf("testing max LIMIT OK\n"); + + ret=RasmAssemble(AUTOTEST_LIMIT05,strlen(AUTOTEST_LIMIT05),&opcode,&opcodelen); + if (ret) {} else {printf("Autotest %03d ERROR (limit: ldir in #FFFF)\n",cpt);exit(-1);} + if (opcode) MemFree(opcode);opcode=NULL;cpt++; +printf("testing 16 bits opcode overriding LIMIT OK\n"); + + ret=RasmAssemble(AUTOTEST_LIMIT06,strlen(AUTOTEST_LIMIT06),&opcode,&opcodelen); + if (!ret) {} else {printf("Autotest %03d ERROR (limit: nop in #FFFF)\n",cpt);exit(-1);} + if (opcode) MemFree(opcode);opcode=NULL;cpt++; +printf("testing opcode overriding LIMIT OK\n"); + + ret=RasmAssemble(AUTOTEST_LIMIT07,strlen(AUTOTEST_LIMIT07),&opcode,&opcodelen); + if (ret) {} else {printf("Autotest %03d ERROR (limit: ld hl,#1234 in #FFFF)\n",cpt);exit(-1);} + if (opcode) MemFree(opcode);opcode=NULL;cpt++; +printf("testing opcode with variable overriding LIMIT OK\n"); + + ret=RasmAssemble(AUTOTEST_DELAYED_RUN,strlen(AUTOTEST_DELAYED_RUN),&opcode,&opcodelen); + if (!ret) {} else {printf("Autotest %03d ERROR (delayed RUN set)\n",cpt);exit(-1);} + if (opcode) MemFree(opcode);opcode=NULL;cpt++; +printf("testing delayed RUN OK\n"); + + ret=RasmAssemble(AUTOTEST_LZSEGMENT,strlen(AUTOTEST_LZSEGMENT),&opcode,&opcodelen); + if (!ret && opcodelen==23 && opcode[1]==21 && opcode[9]==23) {} else {printf("Autotest %03d ERROR (LZ segment relocation)\n",cpt);exit(-1);} + if (opcode) MemFree(opcode);opcode=NULL;cpt++; +printf("testing LZ segment relocation OK\n"); + + ret=RasmAssemble(AUTOTEST_LZ4,strlen(AUTOTEST_LZ4),&opcode,&opcodelen); + if (!ret && opcodelen==49 && opcode[0]==0x15 && opcode[4]==0x44 && opcode[0xB]==0xF0) {} else {printf("Autotest %03d ERROR (LZ4 segment)\n",cpt);MiniDump(opcode,opcodelen);exit(-1);} + if (opcode) MemFree(opcode);opcode=NULL;cpt++; +printf("testing LZ4 segment OK\n"); + + ret=RasmAssemble(AUTOTEST_DEFS,strlen(AUTOTEST_DEFS),&opcode,&opcodelen); + if (!ret && opcodelen==256 && opcode[0]==0) {} else {printf("Autotest %03d ERROR (defs)\n",cpt);exit(-1);} + if (opcode) MemFree(opcode);opcode=NULL;cpt++; +printf("testing DEFS OK\n"); + + ret=RasmAssemble(AUTOTEST_BANKSET,strlen(AUTOTEST_BANKSET),&opcode,&opcodelen); + if (!ret) {} else {printf("Autotest %03d ERROR (bank/bankset)\n",cpt);exit(-1);} + if (opcode) MemFree(opcode);opcode=NULL;cpt++; +printf("testing BANK/BANKSET OK\n"); + + ret=RasmAssemble(AUTOTEST_PAGETAG,strlen(AUTOTEST_PAGETAG),&opcode,&opcodelen); + if (!ret) {} else {printf("Autotest %03d ERROR (page/pageset)\n",cpt);exit(-1);} + if (opcode) MemFree(opcode);opcode=NULL;cpt++; +printf("testing prefix PAGE/PAGESET 1 OK\n"); + + ret=RasmAssemble(AUTOTEST_PAGETAG2,strlen(AUTOTEST_PAGETAG2),&opcode,&opcodelen); + if (!ret) {} else {printf("Autotest %03d ERROR (page/pageset 2)\n",cpt);exit(-1);} + if (opcode) MemFree(opcode);opcode=NULL;cpt++; +printf("testing prefix PAGE/PAGESET 2 OK\n"); + + ret=RasmAssemble(AUTOTEST_PAGETAG3,strlen(AUTOTEST_PAGETAG3),&opcode,&opcodelen); + if (!ret) {} else {printf("Autotest %03d ERROR (page/pageset 3)\n",cpt);exit(-1);} + if (opcode) MemFree(opcode);opcode=NULL;cpt++; +printf("testing prefix PAGE/PAGESET 3 OK\n"); + + ret=RasmAssemble(AUTOTEST_UNDEF,strlen(AUTOTEST_UNDEF),&opcode,&opcodelen); + if (!ret) {} else {printf("Autotest %03d ERROR (simple undef)\n",cpt);exit(-1);} + if (opcode) MemFree(opcode);opcode=NULL;cpt++; +printf("testing UNDEF OK\n"); + + ret=RasmAssemble(AUTOTEST_TAGPRINT,strlen(AUTOTEST_TAGPRINT),&opcode,&opcodelen); + if (!ret && opcodelen==1 && opcode[0]==0xC9) {} else {printf("Autotest %03d ERROR (tag inside printed string)\n",cpt);exit(-1);} + if (opcode) MemFree(opcode);opcode=NULL;cpt++; +printf("testing tags inside printed string OK\n"); + + ret=RasmAssemble(AUTOTEST_TAGFOLLOW,strlen(AUTOTEST_TAGFOLLOW),&opcode,&opcodelen); + if (!ret && opcodelen==1 && opcode[0]==0xC9) {} else {printf("Autotest %03d ERROR (tag+alias fast translating)\n",cpt);exit(-1);} + if (opcode) MemFree(opcode);opcode=NULL;cpt++; +printf("testing tag+alias in fast translate OK\n"); + + ret=RasmAssemble(AUTOTEST_TAGREALLOC,strlen(AUTOTEST_TAGREALLOC),&opcode,&opcodelen); + if (!ret && opcodelen==276) {} else {printf("Autotest %03d ERROR (tag realloc with fast translate)\n",cpt);exit(-1);} + if (opcode) MemFree(opcode);opcode=NULL;cpt++; +printf("testing tag realloc with fast translate OK\n"); + + ret=RasmAssemble(AUTOTEST_TAGLOOP,strlen(AUTOTEST_TAGLOOP),&opcode,&opcodelen); + if (!ret) {} else {printf("Autotest %03d ERROR (generated alias inside loop with generated var names )\n",cpt);exit(-1);} + if (opcode) MemFree(opcode);opcode=NULL;cpt++; +printf("testing generated alias inside loop with generated var names OK\n"); + + ret=RasmAssemble(AUTOTEST_PRINTVAR,strlen(AUTOTEST_PRINTVAR),&opcode,&opcodelen); + if (!ret) {} else {printf("Autotest %03d ERROR (param inside printed string)\n",cpt);exit(-1);} + if (opcode) MemFree(opcode);opcode=NULL;cpt++; +printf("testing param inside printed string OK\n"); + + ret=RasmAssemble(AUTOTEST_PRINTSPACE,strlen(AUTOTEST_PRINTSPACE),&opcode,&opcodelen); + if (!ret) {} else {printf("Autotest %03d ERROR (space inside tag string for PRINT directive)\n",cpt);exit(-1);} + if (opcode) MemFree(opcode);opcode=NULL;cpt++; +printf("testing space inside tag string for PRINT directive OK\n"); + + ret=RasmAssemble(AUTOTEST_INHIBITION,strlen(AUTOTEST_INHIBITION),&opcode,&opcodelen); + if (!ret) {} else {printf("Autotest %03d ERROR (conditionnal inhibition)\n",cpt);exit(-1);} + if (opcode) MemFree(opcode);opcode=NULL;cpt++; +printf("testing conditionnal inhibition OK\n"); + + ret=RasmAssemble(AUTOTEST_SWITCH,strlen(AUTOTEST_SWITCH),&opcode,&opcodelen); + if (!ret && opcodelen==3 && opcode[0]==4 && opcode[1]==3 && opcode[2]==4) {} else {printf("Autotest %03d ERROR (switch case)\n",cpt);exit(-1);} + if (opcode) MemFree(opcode);opcode=NULL;cpt++; +printf("testing SWITCH/CASE OK\n"); + + ret=RasmAssemble(AUTOTEST_NOCODE,strlen(AUTOTEST_NOCODE),&opcode,&opcodelen); + if (!ret && opcodelen==57) {} else {printf("Autotest %03d ERROR (code/nocode)\n",cpt);exit(-1);} + if (opcode) MemFree(opcode);opcode=NULL;cpt++; +printf("testing code/nocode OK\n"); + + ret=RasmAssemble(AUTOTEST_VAREQU,strlen(AUTOTEST_VAREQU),&opcode,&opcodelen); + if (!ret) {} else {printf("Autotest %03d ERROR (var & equ)\n",cpt);exit(-1);} + if (opcode) MemFree(opcode);opcode=NULL;cpt++; +printf("testing var & equ OK\n"); + + ret=RasmAssemble(AUTOTEST_CHARSET,strlen(AUTOTEST_CHARSET),&opcode,&opcodelen); + if (!ret) {} else {printf("Autotest %03d ERROR (simple charset)\n",cpt);exit(-1);} + if (opcodelen!=30 || memcmp(opcode,opcode+5,5) || memcmp(opcode+10,opcode+5,5)) {printf("Autotest %03d ERROR (simple charset)\n",cpt);exit(-1);} + if (memcmp(opcode+15,opcode+20,5) || memcmp(opcode+15,opcode+25,5)) {printf("Autotest %03d ERROR (simple charset)\n",cpt);exit(-1);} + if (opcode) MemFree(opcode);opcode=NULL;cpt++; +printf("testing simple charset OK\n"); + + ret=RasmAssemble(AUTOTEST_CHARSET2,strlen(AUTOTEST_CHARSET2),&opcode,&opcodelen); + if (!ret) {} else {printf("Autotest %03d ERROR (extended charset)\n",cpt);exit(-1);} + for (i=chk=0;i<opcodelen;i++) chk+=opcode[i]; + if (opcodelen!=16 || chk!=0x312) {printf("Autotest %03d ERROR (extended charset)\n",cpt);exit(-1);} + if (opcode) MemFree(opcode);opcode=NULL;cpt++; +printf("testing extended charset OK\n"); + + ret=RasmAssemble(AUTOTEST_QUOTES,strlen(AUTOTEST_QUOTES),&opcode,&opcodelen); + if (!ret && opcodelen==10 && opcode[5]==0xE4 && opcode[6]==0x0D && opcode[7]==0x64 && opcode[8]==0x0D && opcode[9]==0xE4) {} + else {printf("Autotest %03d ERROR (quotes & escaped chars)\n",cpt);MiniDump(opcode,opcodelen);exit(-1);} + if (opcode) MemFree(opcode);opcode=NULL;cpt++; +printf("testing quoted & escaped chars OK\n"); + + ret=RasmAssemble(AUTOTEST_NOT,strlen(AUTOTEST_NOT),&opcode,&opcodelen); + if (!ret) {} else {printf("Autotest %03d ERROR (not operator)\n",cpt);exit(-1);} + if (opcode) MemFree(opcode);opcode=NULL;cpt++; +printf("testing NOT operator OK\n"); + + ret=RasmAssemble(AUTOTEST_MACRO,strlen(AUTOTEST_MACRO),&opcode,&opcodelen); + if (!ret) {} else {printf("Autotest %03d ERROR (macro usage)\n",cpt);exit(-1);} + if (opcode) MemFree(opcode);opcode=NULL;cpt++; +printf("testing macro usage OK\n"); + + ret=RasmAssemble(AUTOTEST_ASSERT,strlen(AUTOTEST_ASSERT),&opcode,&opcodelen); + if (!ret) {} else {printf("Autotest %03d ERROR (assert usage)\n",cpt);exit(-1);} + if (opcode) MemFree(opcode);opcode=NULL;cpt++; +printf("testing assert usage (+prepro regression cases) OK\n"); + + ret=RasmAssemble(AUTOTEST_MACROPAR,strlen(AUTOTEST_MACROPAR),&opcode,&opcodelen); + if (!ret && opcodelen==12 && memcmp(opcode,"GROUIKgrouik",12)==0) {} else {printf("Autotest %03d ERROR (macro string param)\n",cpt);exit(-1);} + if (opcode) MemFree(opcode);opcode=NULL;cpt++; +printf("testing macro string parameter OK\n"); + + ret=RasmAssemble(AUTOTEST_MACRO_ADV,strlen(AUTOTEST_MACRO_ADV),&opcode,&opcodelen); + if (!ret) {} else {printf("Autotest %03d ERROR (macro param)\n",cpt);exit(-1);} + if (opcode) MemFree(opcode);opcode=NULL;cpt++; +printf("testing macro parameters OK\n"); + + ret=RasmAssemble(AUTOTEST_OVERLOADMACPRM,strlen(AUTOTEST_OVERLOADMACPRM),&opcode,&opcodelen); + if (!ret && opcodelen==4 && opcode[0]==1 && opcode[1]==0 && opcode[2]==2 && opcode[3]==1) {} else {printf("Autotest %03d ERROR (macro param overload)\n",cpt);exit(-1);} + if (opcode) MemFree(opcode);opcode=NULL;cpt++; +printf("testing macro parameter overload OK\n"); + + ret=RasmAssemble(AUTOTEST_IFDEFMACRO,strlen(AUTOTEST_IFDEFMACRO),&opcode,&opcodelen); + if (!ret) {} else {printf("Autotest %03d ERROR (ifdef macro)\n",cpt);exit(-1);} + if (opcode) MemFree(opcode);opcode=NULL;cpt++; +printf("testing IFDEF & MACRO OK\n"); + + ret=RasmAssemble(AUTOTEST_LABNUM,strlen(AUTOTEST_LABNUM),&opcode,&opcodelen); + if (!ret) {} else {printf("Autotest %03d ERROR (variables in labels)\n",cpt);exit(-1);} + if (opcode) MemFree(opcode);opcode=NULL;cpt++; +printf("testing variables in labels OK\n"); + + ret=RasmAssemble(AUTOTEST_EQUNUM,strlen(AUTOTEST_EQUNUM),&opcode,&opcodelen); + if (!ret && opcodelen==3) {} else {printf("Autotest %03d ERROR (variables in aliases) r=%d l=%d\n",cpt,ret,opcodelen);MiniDump(opcode,opcodelen);exit(-1);} + if (opcode) MemFree(opcode);opcode=NULL;cpt++; +printf("testing variables in aliases OK\n"); + + ret=RasmAssemble(AUTOTEST_DELAYNUM,strlen(AUTOTEST_DELAYNUM),&opcode,&opcodelen); + if (!ret && opcodelen==9 && opcode[0]==6 && opcode[2]==7 && opcode[4]==8) {} else {printf("Autotest %03d ERROR (delayed expr labels) r=%d l=%d\n",cpt,ret,opcodelen);MiniDump(opcode,opcodelen);exit(-1);} + if (opcode) MemFree(opcode);opcode=NULL;cpt++; +printf("testing delayed expression labels OK\n"); + + ret=RasmAssemble(AUTOTEST_PROXIM,strlen(AUTOTEST_PROXIM),&opcode,&opcodelen); + if (!ret && opcode[1]==3) {} else {printf("Autotest %03d ERROR (proximity labels)\n",cpt);exit(-1);} + if (opcode) MemFree(opcode);opcode=NULL;cpt++; +printf("testing proximity labels OK\n"); + + ret=RasmAssemble(AUTOTEST_STRUCT,strlen(AUTOTEST_STRUCT),&opcode,&opcodelen); + if (!ret) {} else {printf("Autotest %03d ERROR (structs)\n",cpt);exit(-1);} + if (opcode) MemFree(opcode);opcode=NULL;cpt++; +printf("testing structs OK\n"); + + ret=RasmAssemble(AUTOTEST_STRUCT2,strlen(AUTOTEST_STRUCT2),&opcode,&opcodelen); + if (!ret && opcodelen==1) {} else {printf("Autotest %03d ERROR (sizeof struct fields)\n",cpt);exit(-1);} + if (opcode) MemFree(opcode);opcode=NULL;cpt++; +printf("testing SIZEOF struct fields OK\n"); + + ret=RasmAssemble(AUTOTEST_REPEAT,strlen(AUTOTEST_REPEAT),&opcode,&opcodelen); + if (!ret) {} else {printf("Autotest %03d ERROR (extended repeat)\n",cpt);exit(-1);} + if (opcode) MemFree(opcode);opcode=NULL;cpt++; +printf("testing REPEAT cases OK\n"); + + ret=RasmAssemble(AUTOTEST_REPEATKO,strlen(AUTOTEST_REPEATKO),&opcode,&opcodelen); + if (ret) {} else {printf("Autotest %03d ERROR (repeat without end must return error)\n",cpt);exit(-1);} +printf("testing REPEAT without REND OK\n"); + + ret=RasmAssemble(AUTOTEST_WHILEKO,strlen(AUTOTEST_WHILEKO),&opcode,&opcodelen); + if (ret) {} else {printf("Autotest %03d ERROR (while without end must return error)\n",cpt);exit(-1);} +printf("testing WHILE without WEND OK\n"); + + ret=RasmAssemble(AUTOTEST_TICKER,strlen(AUTOTEST_TICKER),&opcode,&opcodelen); + if (!ret && opcodelen==18) {} else {printf("Autotest %03d ERROR (ticker (re)count)\n",cpt);exit(-1);} + if (opcode) MemFree(opcode);opcode=NULL;cpt++; +printf("testing ticker OK\n"); + + ret=RasmAssemble(AUTOTEST_DEFUSED,strlen(AUTOTEST_DEFUSED),&opcode,&opcodelen); + if (!ret) {} else {printf("Autotest %03d ERROR (ifdef ifused)\n",cpt);exit(-1);} + if (opcode) MemFree(opcode);opcode=NULL;cpt++; +printf("testing IFDEF / IFUSED OK\n"); + + ret=RasmAssemble(AUTOTEST_SAVEINVALID1,strlen(AUTOTEST_SAVEINVALID1),&opcode,&opcodelen); + if (ret) {} else {printf("Autotest %03d ERROR (invalid size for SAVE)\n",cpt);exit(-1);} + if (opcode) MemFree(opcode);opcode=NULL;cpt++; +printf("testing invalid size for SAVE OK\n"); + + ret=RasmAssemble(AUTOTEST_SAVEINVALID2,strlen(AUTOTEST_SAVEINVALID2),&opcode,&opcodelen); + if (ret) {} else {printf("Autotest %03d ERROR (invalid offset for SAVE)\n",cpt);exit(-1);} + if (opcode) MemFree(opcode);opcode=NULL;cpt++; +printf("testing invalid offset (too low) for SAVE OK\n"); + + ret=RasmAssemble(AUTOTEST_SAVEINVALID3,strlen(AUTOTEST_SAVEINVALID3),&opcode,&opcodelen); + if (ret) {} else {printf("Autotest %03d ERROR (invalid offset for SAVE)\n",cpt);exit(-1);} + if (opcode) MemFree(opcode);opcode=NULL;cpt++; +printf("testing invalid offset (too high) for SAVE OK\n"); + + ret=RasmAssemble(AUTOTEST_INHIBITION2,strlen(AUTOTEST_INHIBITION2),&opcode,&opcodelen); + if (!ret) {} else {printf("Autotest %03d ERROR (if switch inhibition)\n",cpt);exit(-1);} + if (opcode) MemFree(opcode);opcode=NULL;cpt++; +printf("testing if/switch inhibition OK\n"); + + ret=RasmAssemble(AUTOTEST_INHIBITIONMAX,strlen(AUTOTEST_INHIBITIONMAX),&opcode,&opcodelen); + if (!ret) {} else {printf("Autotest %03d ERROR (moar inhibition)\n",cpt);exit(-1);} + if (opcode) MemFree(opcode);opcode=NULL;cpt++; +printf("testing more inhibition cases OK\n"); + + ret=RasmAssemble(AUTOTEST_MACROPROX,strlen(AUTOTEST_MACROPROX),&opcode,&opcodelen); + if (!ret) {} else {printf("Autotest %03d ERROR (macro + prox)\n",cpt);exit(-1);} + if (opcode) MemFree(opcode);opcode=NULL;cpt++; +printf("testing regression case with proximity label following macro definition OK\n"); + + ret=RasmAssemble(AUTOTEST_PROXBACK,strlen(AUTOTEST_PROXBACK),&opcode,&opcodelen); + if (!ret) {} else {printf("Autotest %03d ERROR (boucle/macro + prox = recup global)\n",cpt);MiniDump(opcode,opcodelen);exit(-1);} + if (opcode) MemFree(opcode);opcode=NULL;cpt++; +printf("testing global label back as reference when leaving macro and loops OK\n"); + + ret=RasmAssemble(AUTOTEST_LOCAPROX,strlen(AUTOTEST_LOCAPROX),&opcode,&opcodelen); + if (!ret) {} else {printf("Autotest %03d ERROR (far local+prox access)\n",cpt);MiniDump(opcode,opcodelen);exit(-1);} + if (opcode) MemFree(opcode);opcode=NULL;cpt++; +printf("testing far access of a proximity label refering to local OK\n"); + + ret=RasmAssemble(AUTOTEST_NEGATIVE,strlen(AUTOTEST_NEGATIVE),&opcode,&opcodelen); + if (!ret) {} else {printf("Autotest %03d ERROR (formula case 0)\n",cpt);exit(-1);} + if (opcode) MemFree(opcode);opcode=NULL;cpt++; +printf("testing formula case 0 OK\n"); + + ret=RasmAssemble(AUTOTEST_FORMULA1,strlen(AUTOTEST_FORMULA1),&opcode,&opcodelen); + if (!ret) {} else {printf("Autotest %03d ERROR (formula case 1)\n",cpt);exit(-1);} + if (opcode) MemFree(opcode);opcode=NULL;cpt++; +printf("testing formula case 1 OK\n"); + + ret=RasmAssemble(AUTOTEST_SETINSIDE,strlen(AUTOTEST_SETINSIDE),&opcode,&opcodelen); + if (ret) {} else {printf("Autotest %03d ERROR (set var inside expression must trigger error)\n",cpt);exit(-1);} + if (opcode) MemFree(opcode);opcode=NULL;cpt++; +printf("testing var set inside an expression must trigger an error OK\n"); + + ret=RasmAssemble(AUTOTEST_CODESKIP,strlen(AUTOTEST_CODESKIP),&opcode,&opcodelen); + if (!ret) {} else {printf("Autotest %03d ERROR (code skip)\n",cpt);exit(-1);} + if (opcode) MemFree(opcode);opcode=NULL;cpt++; +printf("testing code skip OK\n"); + + ret=RasmAssemble(AUTOTEST_FORMULA2,strlen(AUTOTEST_FORMULA2),&opcode,&opcodelen); + if (!ret) {} else {printf("Autotest %03d ERROR (formula case 2 function+multiple parenthesis)\n",cpt);exit(-1);} + if (opcode) MemFree(opcode);opcode=NULL;cpt++; +printf("testing formula functions + multiple parenthesis OK\n"); + + ret=RasmAssemble(AUTOTEST_PLUSCOLOR,strlen(AUTOTEST_PLUSCOLOR),&opcode,&opcodelen); + if (!ret) {} else {printf("Autotest %03d ERROR (formula func for Plus color management)\n",cpt);exit(-1);} + if (opcode) MemFree(opcode);opcode=NULL;cpt++; +printf("testing formula function for Plus color management OK\n"); + + ret=RasmAssemble(AUTOTEST_FRAC,strlen(AUTOTEST_FRAC),&opcode,&opcodelen); + if (!ret) {} else {printf("Autotest %03d ERROR (formula func FRAC)\n",cpt);exit(-1);} + if (opcode) MemFree(opcode);opcode=NULL;cpt++; +printf("testing formula FRAC function OK\n"); + + ret=RasmAssemble(AUTOTEST_UNDERVAR,strlen(AUTOTEST_UNDERVAR),&opcode,&opcodelen); + if (!ret && opcodelen==4) {} else {printf("Autotest %03d ERROR (var starting with _)\n",cpt);exit(-1);} + if (opcode) MemFree(opcode);opcode=NULL;cpt++; +printf("testing var names starting with '_' OK\n"); + + ret=RasmAssemble(AUTOTEST_NOEXPORT,strlen(AUTOTEST_NOEXPORT),&opcode,&opcodelen); + if (!ret && opcodelen==2) {} else {printf("Autotest %03d ERROR (noexport/enoexport)\n",cpt);exit(-1);} + if (opcode) MemFree(opcode);opcode=NULL;cpt++; +printf("testing export/noexport OK\n"); + + ret=RasmAssemble(AUTOTEST_ENHANCED_LD,strlen(AUTOTEST_ENHANCED_LD),&opcode,&opcodelen); + if (!ret && memcmp(opcode,opcode+opcodelen/2,opcodelen/2)==0) {} else {printf("Autotest %03d ERROR (enhanced LD)\n",cpt);exit(-1);} + if (opcode) MemFree(opcode);opcode=NULL;cpt++; +printf("testing enhanced LD OK\n"); + + ret=RasmAssemble(AUTOTEST_ENHANCED_LD2,strlen(AUTOTEST_ENHANCED_LD2),&opcode,&opcodelen); + if (!ret && memcmp(opcode,opcode+opcodelen/2,opcodelen/2)==0) {} else {printf("Autotest %03d ERROR (enhanced LD 2)\n",cpt);exit(-1);} + if (opcode) MemFree(opcode);opcode=NULL;cpt++; +printf("testing enhanced LD variante OK\n"); + + ret=RasmAssemble(AUTOTEST_ENHANCED_PUSHPOP,strlen(AUTOTEST_ENHANCED_PUSHPOP),&opcode,&opcodelen); + if (!ret && memcmp(opcode,opcode+opcodelen/2,opcodelen/2)==0) {} else {printf("Autotest %03d ERROR (enhanced PUSH/POP/NOP)\n",cpt);MiniDump(opcode,opcodelen);exit(-1);} + if (opcode) MemFree(opcode);opcode=NULL;cpt++; +printf("testing enhanced PUSH/POP OK\n"); + + ret=RasmAssemble(AUTOTEST_PAGELABELGEN,strlen(AUTOTEST_PAGELABELGEN),&opcode,&opcodelen); + if (!ret) {} else {printf("Autotest %03d ERROR (pagelabelgen)\n",cpt);MiniDump(opcode,opcodelen);exit(-1);} + if (opcode) MemFree(opcode);opcode=NULL;cpt++; +printf("testing page tag with generated label name OK\n"); + + ret=RasmAssembleInfo(AUTOTEST_EMBEDDED_ERRORS,strlen(AUTOTEST_EMBEDDED_ERRORS),&opcode,&opcodelen,&debug); + if (ret && debug->nberror==2 && debug->nbsymbol==3) { +/* + printf("\n"); + for (i=0;i<debug->nberror;i++) { + printf("%d -> %s\n",i,debug->error[i].msg); + } + for (i=0;i<debug->nbsymbol;i++) { + printf("%d -> %s=%d\n",i,debug->symbol[i].name,debug->symbol[i].v); + } + RasmFreeInfoStruct(debug); +*/ + } else {printf("Autotest %03d ERROR (embedded error struct) err=%d nberr=%d (2) nbsymb=%d (3)\n",cpt,ret,debug->nberror,debug->nbsymbol);MiniDump(opcode,opcodelen);exit(-1);} + if (opcode) MemFree(opcode);opcode=NULL;cpt++; +printf("testing internal error struct OK\n"); + + ret=RasmAssembleInfo(AUTOTEST_EMBEDDED_LABELS,strlen(AUTOTEST_EMBEDDED_LABELS),&opcode,&opcodelen,&debug); + if (!ret && debug->nbsymbol==3) { + /* + printf("\nnbsymbol=%d\n",debug->nbsymbol); + for (i=0;i<debug->nbsymbol;i++) { + printf("%d -> %s=%d\n",i,debug->symbol[i].name,debug->symbol[i].v); + }*/ + RasmFreeInfoStruct(debug); + } else {printf("Autotest %03d ERROR (embedded test)\n",cpt);MiniDump(opcode,opcodelen);exit(-1);} + if (opcode) MemFree(opcode);opcode=NULL;cpt++; +printf("testing internal label struct OK\n"); + +#ifdef RDD + printf("\n%d bytes\n",_static_library_memory_used); + + tmpstr3=FileReadContent("./test/PlayerAky.asm",&filelen); + printf(".");fflush(stdout);ret=RasmAssembleInfo(tmpstr3,filelen,&opcode,&opcodelen,&debug); + if (!ret) {} else {printf("Autotest %03d ERROR (PlayerAky)\n",cpt);MiniDump(opcode,opcodelen);exit(-1);} + if (opcode) MemFree(opcode);opcode=NULL;cpt++; + RasmFreeInfoStruct(debug); + MemFree(tmpstr3); + + printf("\n%d bytes\n",_static_library_memory_used); +#endif + + FileRemoveIfExists("rasmoutput.cpr"); + + printf("All internal tests OK\n"); + #ifdef RDD + /* private dev lib tools */ +printf("checking memory\n"); + CloseLibrary(); + #endif + exit(0); +} + + +/****************************************************** +LZ48 v005 / LZ49 v002 +******************************************************/ +int LZ48_encode_extended_length(unsigned char *odata, int length) +{ + int ioutput=0; + + while (length>=255) { + odata[ioutput++]=0xFF; + length-=255; + } + /* if the last value is 255 we must encode 0 to end extended length */ + /*if (length==0) rasm_printf(ae,"bugfixed!\n");*/ + odata[ioutput++]=(unsigned char)length; + return ioutput; +} + +int LZ48_encode_block(unsigned char *odata,unsigned char *data, int literaloffset,int literalcpt,int offset,int maxlength) +{ + int ioutput=1; + int token=0; + int i; + + if (offset<0 || offset>255) { + fprintf(stderr,"internal offset error!\n"); + exit(-2); + } + + if (literalcpt<15) { + token=literalcpt<<4; + } else { + token=0xF0; + ioutput+=LZ48_encode_extended_length(odata+ioutput,literalcpt-15); + } + + for (i=0;i<literalcpt;i++) odata[ioutput++]=data[literaloffset++]; + + if (maxlength<18) { + if (maxlength>2) { + token|=(maxlength-3); + } else { + /* endoffset has no length */ + } + } else { + token|=0xF; + ioutput+=LZ48_encode_extended_length(odata+ioutput,maxlength-18); + } + + odata[ioutput++]=(unsigned char)offset-1; + + odata[0]=(unsigned char)token; + return ioutput; +} + +unsigned char *LZ48_encode_legacy(unsigned char *data, int length, int *retlength) +{ + int i,startscan,current=1,token,ioutput=1,curscan; + int maxoffset=0,maxlength,matchlength,literal=0,literaloffset=1; + unsigned char *odata=NULL; + + odata=MemMalloc((size_t)length*1.5+10); + if (!odata) { + fprintf(stderr,"malloc(%.0lf) - memory full\n",(size_t)length*1.5+10); + exit(-1); + } + + /* first byte always literal */ + odata[0]=data[0]; + + /* force short data encoding */ + if (length<5) { + token=(length-1)<<4; + odata[ioutput++]=(unsigned char)token; + for (i=1;i<length;i++) odata[ioutput++]=data[current++]; + odata[ioutput++]=0xFF; + *retlength=ioutput; + return odata; + } + + while (current<length) { + maxlength=0; + startscan=current-255; + if (startscan<0) startscan=0; + while (startscan<current) { + matchlength=0; + curscan=current; + for (i=startscan;curscan<length;i++) { + if (data[i]==data[curscan++]) matchlength++; else break; + } + if (matchlength>=3 && matchlength>maxlength) { + maxoffset=startscan; + maxlength=matchlength; + } + startscan++; + } + if (maxlength) { + ioutput+=LZ48_encode_block(odata+ioutput,data,literaloffset,literal,current-maxoffset,maxlength); + current+=maxlength; + literaloffset=current; + literal=0; + } else { + literal++; + current++; + } + } + ioutput+=LZ48_encode_block(odata+ioutput,data,literaloffset,literal,0,0); + *retlength=ioutput; + return odata; +} + +int LZ49_encode_extended_length(unsigned char *odata, int length) +{ + int ioutput=0; + + while (length>=255) { + odata[ioutput++]=0xFF; + length-=255; + } + /* if the last value is 255 we must encode 0 to end extended length */ + /*if (length==0) rasm_printf(ae,"bugfixed!\n");*/ + odata[ioutput++]=(unsigned char)length; + return ioutput; +} + +int LZ49_encode_block(unsigned char *odata,unsigned char *data, int literaloffset,int literalcpt,int offset,int maxlength) +{ + int ioutput=1; + int token=0; + int i; + + if (offset<0 || offset>511) { + fprintf(stderr,"internal offset error!\n"); + exit(-2); + } + + if (literalcpt<7) { + token=literalcpt<<4; + } else { + token=0x70; + ioutput+=LZ49_encode_extended_length(odata+ioutput,literalcpt-7); + } + + for (i=0;i<literalcpt;i++) odata[ioutput++]=data[literaloffset++]; + + if (maxlength<18) { + if (maxlength>2) { + token|=(maxlength-3); + } else { + /* endoffset has no length */ + } + } else { + token|=0xF; + ioutput+=LZ49_encode_extended_length(odata+ioutput,maxlength-18); + } + + if (offset>255) { + token|=0x80; + offset-=256; + } + odata[ioutput++]=(unsigned char)offset-1; + + odata[0]=(unsigned char)token; + return ioutput; +} + +unsigned char *LZ49_encode_legacy(unsigned char *data, int length, int *retlength) +{ + int i,startscan,current=1,token,ioutput=1,curscan; + int maxoffset=0,maxlength,matchlength,literal=0,literaloffset=1; + unsigned char *odata=NULL; + + odata=MemMalloc((size_t)(length*1.5+10)); + if (!odata) { + fprintf(stderr,"malloc(%.0lf) - memory full\n",(size_t)length*1.5+10); + exit(-1); + } + + /* first byte always literal */ + odata[0]=data[0]; + + /* force short data encoding */ + if (length<5) { + token=(length-1)<<4; + odata[ioutput++]=(unsigned char)token; + for (i=1;i<length;i++) odata[ioutput++]=data[current++]; + odata[ioutput++]=0xFF; + *retlength=ioutput; + return odata; + } + + while (current<length) { + maxlength=0; + startscan=current-511; + if (startscan<0) startscan=0; + while (startscan<current) { + matchlength=0; + curscan=current; + for (i=startscan;curscan<length;i++) { + if (data[i]==data[curscan++]) matchlength++; else break; + } + if (matchlength>=3 && matchlength>maxlength) { + maxoffset=startscan; + maxlength=matchlength; + } + startscan++; + } + if (maxlength) { + ioutput+=LZ49_encode_block(odata+ioutput,data,literaloffset,literal,current-maxoffset,maxlength); + current+=maxlength; + literaloffset=current; + literal=0; + } else { + literal++; + current++; + } + } + ioutput+=LZ49_encode_block(odata+ioutput,data,literaloffset,literal,0,0); + *retlength=ioutput; + return odata; +} + + +/*************************************** + semi-generic body of program +***************************************/ + +#ifndef INTEGRATED_ASSEMBLY + +/* + Usage + display the mandatory parameters +*/ +void Usage(int help) +{ + #undef FUNC + #define FUNC "Usage" + + printf("%s (c) 2017 Edouard BERGE (use -n option to display all licenses)\n",RASM_VERSION); + #ifndef NO_3RD_PARTIES + printf("LZ4 (c) Yann Collet / ZX7 (c) Einar Saukas / Exomizer 2 (c) Magnus Lind\n"); + #endif + printf("\n"); + printf("SYNTAX: rasm <inputfile> [options]\n"); + printf("\n"); + + if (help) { + printf("FILENAMES:\n"); + printf("-oa automatic radix from input filename\n"); + printf("-o <outputfile radix> choose a common radix for all files\n"); + printf("-ob <binary filename> choose a full filename for binary output\n"); + printf("-oc <cartridge filename> choose a full filename for cartridge output\n"); + printf("-oi <snapshot filename> choose a full filename for snapshot output\n"); + printf("-os <symbol filename> choose a full filename for symbol output\n"); + printf("-ot <tape filename> choose a full filename for tape output\n"); + printf("-ok <breakpoint filename>choose a full filename for breakpoint output\n"); + printf("-I<path> set a path for files to read\n"); + printf("-no disable all file output\n"); + printf("DEPENDENCIES EXPORT:\n"); + printf("-depend=make output dependencies on a single line\n"); + printf("-depend=list output dependencies as a list\n"); + printf("if 'binary filename' is set then it will be outputed first\n"); + printf("SYMBOLS EXPORT:\n"); + printf("-s export symbols %%s #%%X B%%d (label,adr,cprbank)\n"); + printf("-sz export symbols with ZX emulator convention\n"); + printf("-sp export symbols with Pasmo convention\n"); + printf("-sw export symbols with Winape convention\n"); + printf("-ss export symbols in the snapshot (SYMB chunk for ACE)\n"); + printf("-sc <format> export symbols with source code convention\n"); + printf("-sm export symbol in multiple files (one per bank)\n"); + printf("-l <labelfile> import symbol file (winape,pasmo,rasm)\n"); + printf("-eb export breakpoints\n"); + printf("-wu warn for unused symbols (alias, var or label)\n"); + printf("SYMBOLS ADDITIONAL OPTIONS:\n"); + printf("-sl export also local symbol\n"); + printf("-sv export also variables symbol\n"); + printf("-sq export also EQU symbol\n"); + printf("-sa export all symbols (like -sl -sv -sq option)\n"); + printf("-Dvariable=value import value for variable\n"); + printf("COMPATIBILITY:\n"); + printf("-m Maxam style calculations\n"); + printf("-dams Dams 'dot' label convention\n"); + printf("-ass AS80 behaviour mimic\n"); + printf("-uz UZ80 behaviour mimic\n"); + + printf("EDSK generation/update:\n"); + printf("-eo overwrite files on disk if it already exists\n"); + printf("SNAPSHOT:\n"); + printf("-sb export breakpoints in snapshot (BRKS & BRKC chunks)\n"); + printf("-ss export symbols in the snapshot (SYMB chunk for ACE)\n"); + printf("-v2 export snapshot version 2 instead of version 3\n"); + printf("PARSING:\n"); + printf("-me <value> set maximum number of error (0 means no limit)\n"); + printf("-xr extended error display\n"); + printf("-w disable warnings\n"); + printf("-void force void usage with macro without parameter\n"); + printf("\n"); + } else { + printf("use option -h for help\n"); + printf("\n"); + } + + exit(ABORT_ERROR); +} + +void Licenses() +{ + #undef FUNC + #define FUNC "Licenses" + +printf(" ____ \n"); +printf(" | _ \\ __ _ ___ _ __ ___ \n"); +printf(" | |_) / _` / __| '_ ` _ \\ \n"); +printf(" | _ < (_| \\__ \\ | | | | |\n"); +printf(" |_| \\_\\__,_|___/_| |_| |_|\n"); +printf("\n"); +printf(" is using MIT 'expat' license\n"); +printf("\" Copyright (c) BERGE Edouard (roudoudou)\n\n"); + +printf("Permission is hereby granted, free of charge,\n"); +printf("to any person obtaining a copy of this software\n"); +printf("and associated documentation/source files of\n"); +printf("RASM, to deal in the Software without restriction,\n"); +printf("including without limitation the rights to use,\n"); +printf("copy, modify, merge, publish, distribute,\n"); +printf("sublicense, and/or sell copies of the Software,\n"); +printf("and to permit persons to whom the Software is\n"); +printf("furnished to do so, subject to the following\n"); +printf("conditions:\n"); + +printf("The above copyright notice and this permission\n"); +printf("notice shall be included in all copies or\n"); +printf("substantial portions of the Software.\n"); +printf("The Software is provided 'as is', without\n"); +printf("warranty of any kind, express or implied,\n"); +printf("including but not limited to the warranties of\n"); +printf("merchantability, fitness for a particular\n"); +printf("purpose and noninfringement. In no event shall\n"); +printf("the authors or copyright holders be liable for\n"); +printf("any claim, damages or other liability, whether\n"); +printf("in an action of contract, tort or otherwise,\n"); +printf("arising from, out of or in connection with the\n"); +printf("software or the use or other dealings in the\n"); +printf("Software. \"\n"); + +#ifndef NO_3RD_PARTIES +printf("\n\n\n\n"); +printf("******* license of LZ4 cruncher / sources were modified ***********\n\n\n\n"); + +printf("BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)\n"); + +printf("Redistribution and use in source and binary forms, with or without\n"); +printf("modification, are permitted provided that the following conditions are\n"); +printf("met:\n\n"); + +printf(" * Redistributions of source code must retain the above copyright\n"); +printf("notice, this list of conditions and the following disclaimer.\n"); +printf(" * Redistributions in binary form must reproduce the above\n"); +printf("copyright notice, this list of conditions and the following disclaimer\n"); +printf("in the documentation and/or other materials provided with the\n"); +printf("distribution.\n\n"); + +printf("THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n"); +printf("'AS IS' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n"); +printf("LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n"); +printf("A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n"); +printf("OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n"); +printf("SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n"); +printf("LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n"); +printf("DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n"); +printf("THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n"); +printf("(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n"); +printf("OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n"); + +printf("You can contact the author at :\n"); +printf(" - LZ4 homepage : http://www.lz4.org\n"); +printf(" - LZ4 source repository : https://github.com/lz4/lz4\n"); + + +printf("\n\n\n\n"); +printf("******* license of ZX7 cruncher / sources were modified ***********\n\n\n\n"); + + +printf(" * (c) Copyright 2012 by Einar Saukas. All rights reserved.\n"); +printf(" *\n"); +printf(" * Redistribution and use in source and binary forms, with or without\n"); +printf(" * modification, are permitted provided that the following conditions are met:\n"); +printf(" * * Redistributions of source code must retain the above copyright\n"); +printf(" * notice, this list of conditions and the following disclaimer.\n"); +printf(" * * Redistributions in binary form must reproduce the above copyright\n"); +printf(" * notice, this list of conditions and the following disclaimer in the\n"); +printf(" * documentation and/or other materials provided with the distribution.\n"); +printf(" * * The name of its author may not be used to endorse or promote products\n"); +printf(" * derived from this software without specific prior written permission.\n"); +printf(" *\n"); +printf(" * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 'AS IS' AND\n"); +printf(" * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n"); +printf(" * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n"); +printf(" * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY\n"); +printf(" * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n"); +printf(" * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n"); +printf(" * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n"); +printf(" * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n"); +printf(" * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n"); +printf(" * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"); + + +printf("\n\n\n\n"); +printf("******* license of exomizer cruncher / sources were modified ***********\n\n\n\n"); + + +printf(" * Copyright (c) 2005 Magnus Lind.\n"); +printf(" *\n"); +printf(" * This software is provided 'as-is', without any express or implied warranty.\n"); +printf(" * In no event will the authors be held liable for any damages arising from\n"); +printf(" * the use of this software.\n"); +printf(" *\n"); +printf(" * Permission is granted to anyone to use this software, alter it and re-\n"); +printf(" * distribute it freely for any non-commercial, non-profit purpose subject to\n"); +printf(" * the following restrictions:\n"); +printf(" *\n"); +printf(" * 1. The origin of this software must not be misrepresented; you must not\n"); +printf(" * claim that you wrote the original software. If you use this software in a\n"); +printf(" * product, an acknowledgment in the product documentation would be\n"); +printf(" * appreciated but is not required.\n"); +printf(" *\n"); +printf(" * 2. Altered source versions must be plainly marked as such, and must not\n"); +printf(" * be misrepresented as being the original software.\n"); +printf(" *\n"); +printf(" * 3. This notice may not be removed or altered from any distribution.\n"); +printf(" *\n"); +printf(" * 4. The names of this software and/or it's copyright holders may not be\n"); +printf(" * used to endorse or promote products derived from this software without\n"); +printf(" * specific prior written permission.\n"); +#endif + +printf("\n\n"); + + + + exit(0); +} + +int _internal_check_flexible(char *fxp) { + #undef FUNC + #define FUNC "_internal_check_flexible" + + char *posval,*posvar; + int cpt=0,i; + + posvar=strstr(fxp,"%s"); + posval=strstr(fxp,"%"); + if (!posval || !posvar) { + printf("invalid flexible export string, need 2 formated fields, example: \"%%s %%d\"\n"); + exit(1); + } + if (posval<posvar) { + printf("invalid flexible export string, must be %%s before the %% for value, example: \"%%s %%d\"\n"); + exit(1); + } + for (i=0;fxp[i];i++) if (fxp[i]=='%') cpt++; + if (cpt>2) { + printf("invalid flexible export string, must be only two formated fields, example: \"%%s %%d\"\n"); + exit(1); + } + + return 1; +} +/* + ParseOptions + + used to parse command line and configuration file +*/ +int ParseOptions(char **argv,int argc, struct s_parameter *param) +{ + #undef FUNC + #define FUNC "ParseOptions" + + char *sep; + int i=0; + + if (strcmp(argv[i],"-autotest")==0) { + RasmAutotest(); + } else if (strcmp(argv[i],"-uz")==0) { + param->as80=2; + } else if (strcmp(argv[i],"-ass")==0) { + param->as80=1; + } else if (strcmp(argv[i],"-eb")==0) { + param->export_brk=1; + } else if (strcmp(argv[i],"-wu")==0) { + param->warn_unused=1; + } else if (strcmp(argv[i],"-dams")==0) { + } else if (strcmp(argv[i],"-void")==0) { + param->macrovoid=1; + } else if (strcmp(argv[i],"-xr")==0) { + param->extended_error=1; + } else if (strcmp(argv[i],"-eo")==0) { + param->edskoverwrite=1; + } else if (strcmp(argv[i],"-depend=make")==0) { + param->dependencies=E_DEPENDENCIES_MAKE; + param->checkmode=1; + } else if (strcmp(argv[i],"-depend=list")==0) { + param->dependencies=E_DEPENDENCIES_LIST; + param->checkmode=1; + } else if (strcmp(argv[i],"-no")==0) { + param->checkmode=1; + } else if (strcmp(argv[i],"-w")==0) { + param->nowarning=1; + } else if (argv[i][0]=='-') { + switch(argv[i][1]) + { + case 'I': + if (argv[i][2]) { + char *curpath; + int l; + l=strlen(argv[i]); + curpath=MemMalloc(l); /* strlen(path)+2 */ + strcpy(curpath,argv[i]+2); +#ifdef OS_WIN + if (argv[i][l-1]!='/' && argv[i][l-1]!='\\') strcat(curpath,"\\"); +#else + if (argv[i][l-1]!='/' && argv[i][l-1]!='\\') strcat(curpath,"/"); +#endif + FieldArrayAddDynamicValueConcat(¶m->pathdef,¶m->npath,¶m->mpath,curpath); + MemFree(curpath); + } else { + Usage(1); + } + break; + case 'D': + if ((sep=strchr(argv[i],'='))!=NULL) { + if (sep!=argv[i]+2) { + FieldArrayAddDynamicValueConcat(¶m->symboldef,¶m->nsymb,¶m->msymb,argv[i]+2); + } else { + Usage(1); + } + } else { + Usage(1); + } + break; + case 'm': + switch (argv[i][2]) { + case 0: + param->rough=0.0; + return i; + case 'e': + if (argv[i][3]) Usage(1); + if (i+1<argc) { + param->maxerr=atoi(argv[++i]); + return i; + } + default:Usage(1); + } + Usage(1); + break; + case 's': + if (argv[i][2] && argv[i][3]) Usage(1); + switch (argv[i][2]) { + case 0:param->export_sym=1;return 0; + case 'z': + param->export_sym=5;return 0; + case 'm': + param->export_multisym=1;return 0; + case 'b': + param->export_snabrk=1;return 0; + case 'p': + param->export_sym=2;return 0; + case 'w': + param->export_sym=3;return 0; + case 'c': + if (i+1<argc) { + param->export_sym=4; + param->flexible_export=TxtStrDup(argv[++i]); + param->flexible_export=MemRealloc(param->flexible_export,strlen(param->flexible_export)+3); + strcat(param->flexible_export,"\n"); + /* check export string */ + if (_internal_check_flexible(param->flexible_export)) return i; else Usage(1); + } + Usage(1); + case 'l': + param->export_local=1;return 0; + case 'v': + param->export_var=1;return 0; + case 'q': + param->export_equ=1;return 0; + case 'a': + param->export_local=1; + param->export_var=1; + param->export_equ=1; + return 0; + case 's': + param->export_local=1; + param->export_sym=1; + param->export_sna=1;return 0; + default: + break; + } + Usage(1); + case 'l': + if (argv[i][2]) Usage(1); + if (i+1<argc) { + FieldArrayAddDynamicValue(¶m->labelfilename,argv[++i]); + break; + } + Usage(1); + case 'i': +printf("@@@\n@@@ --> deprecated option, there is no need to use -i option to set input file <--\n@@@\n"); + Usage(0); + case 'o': + if (argv[i][2] && argv[i][3]) Usage(1); + switch (argv[i][2]) { + case 0: + if (i+1<argc && param->outputfilename==NULL) { + param->outputfilename=argv[++i]; + break; + } + Usage(1); + case 'a': + param->automatic_radix=1; + break; + case 't': + if (i+1<argc && param->tape_name==NULL) { + param->tape_name=argv[++i]; + break; + } + Usage(1); + case 'i': + if (i+1<argc && param->snapshot_name==NULL) { + param->snapshot_name=argv[++i]; + break; + } + Usage(1); + case 'b': + if (i+1<argc && param->binary_name==NULL) { + param->binary_name=argv[++i]; + break; + } + Usage(1); + case 'c': + if (i+1<argc && param->cartridge_name==NULL) { + param->cartridge_name=argv[++i]; + break; + } + Usage(1); + case 'k': + if (i+1<argc && param->breakpoint_name==NULL) { + param->breakpoint_name=argv[++i]; + break; + } + Usage(1); + case 's': + if (i+1<argc && param->symbol_name==NULL) { + param->symbol_name=argv[++i]; + break; + } + Usage(1); + default: + Usage(1); + } + break; + case 'd':if (!argv[i][2]) printf("deprecated option -d\n"); else Usage(1); + break; + case 'a':if (!argv[i][2]) printf("deprecated option -a\n"); else Usage(1); + break; + case 'c':if (!argv[i][2]) printf("deprecated option -c\n"); else Usage(1); + break; + case 'v': + if (!argv[i][2]) { + printf("deprecated option -v\n"); + } else if (argv[i][2]=='2') { + param->v2=1; + } + break; + case 'n':if (!argv[i][2]) Licenses(); else Usage(1); + case 'h':Usage(1); + default: + Usage(1); + } + } else { + if (param->filename==NULL) { + param->filename=TxtStrDup(argv[i]); + } else if (param->outputfilename==NULL) { + param->outputfilename=argv[i]; + } else Usage(1); + } + return i; +} + +/* + GetParametersFromCommandLine + retrieve parameters from command line and fill pointers to file names +*/ +void GetParametersFromCommandLine(int argc, char **argv, struct s_parameter *param) +{ + #undef FUNC + #define FUNC "GetParametersFromCommandLine" + int i; + + for (i=1;i<argc;i++) + i+=ParseOptions(&argv[i],argc-i,param); + + if (!param->filename) Usage(0); + if (param->export_local && !param->export_sym) Usage(1); // à revoir? +} + +/* + main + + check parameters + execute the main processing +*/ + + +int main(int argc, char **argv) +{ + #undef FUNC + #define FUNC "main" + + struct s_parameter param={0}; + int ret; + + param.maxerr=20; + param.rough=0.5; + + GetParametersFromCommandLine(argc,argv,¶m); + ret=Rasm(¶m); + #ifdef RDD + /* private dev lib tools */ +printf("checking memory\n"); + CloseLibrary(); + #endif + exit(ret); + return 0; // Open WATCOM Warns without this... +} + +#endif + + diff --git a/tools/rasm_src/zx7.h b/tools/rasm_src/zx7.h new file mode 100644 index 0000000..f03659d --- /dev/null +++ b/tools/rasm_src/zx7.h @@ -0,0 +1,293 @@ +/*
+
+Warning! This is a modified version of original sources!
+
+To sum up:
+- all include files and C sources were merged in a single file
+- existing logs were removed (except error logs)
+- main were removed and wrapper added
+
+
+ * (c) Copyright 2012 by Einar Saukas. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * The name of its author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define ZX7_MAX_OFFSET 2176 /* range 1..2176 */
+#define ZX7_MAX_LEN 65536 /* range 2..65536 */
+
+typedef struct match_t {
+ size_t index;
+ struct match_t *next;
+} Match;
+
+typedef struct optimal_t {
+ size_t bits;
+ int offset;
+ int len;
+} Optimal;
+
+Optimal *optimize(unsigned char *input_data, size_t input_size);
+
+unsigned char *ZX7_compress(Optimal *optimal, unsigned char *input_data, size_t input_size, size_t *output_size);
+
+/*
+ * (c) Copyright 2012 by Einar Saukas. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * The name of its author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+int elias_gamma_bits(int value) {
+ int bits;
+
+ bits = 1;
+ while (value > 1) {
+ bits += 2;
+ value >>= 1;
+ }
+ return bits;
+}
+
+int count_bits(int offset, int len) {
+ return 1 + (offset > 128 ? 12 : 8) + elias_gamma_bits(len-1);
+}
+
+Optimal* optimize(unsigned char *input_data, size_t input_size) {
+ size_t *min;
+ size_t *max;
+ Match *matches;
+ Match *match_slots;
+ Optimal *optimal;
+ Match *match;
+ int match_index;
+ int offset;
+ size_t len;
+ size_t best_len;
+ size_t bits;
+ size_t i;
+
+ /* allocate all data structures at once */
+ min = (size_t *)calloc(ZX7_MAX_OFFSET+1, sizeof(size_t));
+ max = (size_t *)calloc(ZX7_MAX_OFFSET+1, sizeof(size_t));
+ matches = (Match *)calloc(256*256, sizeof(Match));
+ match_slots = (Match *)calloc(input_size, sizeof(Match));
+ optimal = (Optimal *)calloc(input_size, sizeof(Optimal));
+
+ if (!min || !max || !matches || !match_slots || !optimal) {
+ fprintf(stderr, "Error: Insufficient memory\n");
+ exit(1);
+ }
+
+ /* first byte is always literal */
+ optimal[0].bits = 8;
+
+ /* process remaining bytes */
+ for (i = 1; i < input_size; i++) {
+
+ optimal[i].bits = optimal[i-1].bits + 9;
+ match_index = input_data[i-1] << 8 | input_data[i];
+ best_len = 1;
+ for (match = &matches[match_index]; match->next != NULL && best_len < ZX7_MAX_LEN; match = match->next) {
+ offset = i - match->next->index;
+ if (offset > ZX7_MAX_OFFSET) {
+ match->next = NULL;
+ break;
+ }
+
+ for (len = 2; len <= ZX7_MAX_LEN; len++) {
+ if (len > best_len) {
+ best_len = len;
+ bits = optimal[i-len].bits + count_bits(offset, len);
+ if (optimal[i].bits > bits) {
+ optimal[i].bits = bits;
+ optimal[i].offset = offset;
+ optimal[i].len = len;
+ }
+ } else if (i+1 == max[offset]+len && max[offset] != 0) {
+ len = i-min[offset];
+ if (len > best_len) {
+ len = best_len;
+ }
+ }
+ if (i < offset+len || input_data[i-len] != input_data[i-len-offset]) {
+ break;
+ }
+ }
+ min[offset] = i+1-len;
+ max[offset] = i;
+ }
+ match_slots[i].index = i;
+ match_slots[i].next = matches[match_index].next;
+ matches[match_index].next = &match_slots[i];
+ }
+
+ /* save time by releasing the largest block only, the O.S. will clean everything else later */
+ free(match_slots);
+
+ return optimal;
+}
+
+/*
+ * (c) Copyright 2012 by Einar Saukas. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * The name of its author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+unsigned char* output_data;
+size_t output_index;
+size_t bit_index;
+int bit_mask;
+
+void write_byte(int value) {
+ output_data[output_index++] = value;
+}
+
+void write_bit(int value) {
+ if (bit_mask == 0) {
+ bit_mask = 128;
+ bit_index = output_index;
+ write_byte(0);
+ }
+ if (value > 0) {
+ output_data[bit_index] |= bit_mask;
+ }
+ bit_mask >>= 1;
+}
+
+void write_elias_gamma(int value) {
+ int i;
+
+ for (i = 2; i <= value; i <<= 1) {
+ write_bit(0);
+ }
+ while ((i >>= 1) > 0) {
+ write_bit(value & i);
+ }
+}
+
+unsigned char *ZX7_compress(Optimal *optimal, unsigned char *input_data, size_t input_size, size_t *output_size) {
+ size_t input_index;
+ size_t input_prev;
+ int offset1;
+ int mask;
+ int i;
+
+ /* calculate and allocate output buffer */
+ input_index = input_size-1;
+ *output_size = (optimal[input_index].bits+18+7)/8;
+ output_data = (unsigned char *)MemMalloc(*output_size);
+
+ /* un-reverse optimal sequence */
+ optimal[input_index].bits = 0;
+ while (input_index > 0) {
+ input_prev = input_index - (optimal[input_index].len > 0 ? optimal[input_index].len : 1);
+ optimal[input_prev].bits = input_index;
+ input_index = input_prev;
+ }
+
+ output_index = 0;
+ bit_mask = 0;
+
+ /* first byte is always literal */
+ write_byte(input_data[0]);
+
+ /* process remaining bytes */
+ while ((input_index = optimal[input_index].bits) > 0) {
+ if (optimal[input_index].len == 0) {
+
+ /* literal indicator */
+ write_bit(0);
+
+ /* literal value */
+ write_byte(input_data[input_index]);
+
+ } else {
+
+ /* sequence indicator */
+ write_bit(1);
+
+ /* sequence length */
+ write_elias_gamma(optimal[input_index].len-1);
+
+ /* sequence offset */
+ offset1 = optimal[input_index].offset-1;
+ if (offset1 < 128) {
+ write_byte(offset1);
+ } else {
+ offset1 -= 128;
+ write_byte((offset1 & 127) | 128);
+ for (mask = 1024; mask > 127; mask >>= 1) {
+ write_bit(offset1 & mask);
+ }
+ }
+ }
+ }
+
+ /* sequence indicator */
+ write_bit(1);
+
+ /* end marker > MAX_LEN */
+ for (i = 0; i < 16; i++) {
+ write_bit(0);
+ }
+ write_bit(1);
+
+ return output_data;
+}
|