aboutsummaryrefslogtreecommitdiff
path: root/tools/2CDT
diff options
context:
space:
mode:
authorJuan J. Martinez <jjm@usebox.net>2021-05-03 08:21:10 +0100
committerJuan J. Martinez <jjm@usebox.net>2021-05-03 10:00:00 +0100
commitc3b0fa04a663fe233765b83d3be41a42aa08c25d (patch)
tree0befda349001ef6ce306b39378f9c70ad917363e /tools/2CDT
downloadreturn-of-traxtor-cpc-c3b0fa04a663fe233765b83d3be41a42aa08c25d.tar.gz
return-of-traxtor-cpc-c3b0fa04a663fe233765b83d3be41a42aa08c25d.zip
Initial import for public releaseHEADmain
Diffstat (limited to 'tools/2CDT')
-rw-r--r--tools/2CDT/2cdt.cbp43
-rw-r--r--tools/2CDT/2cdt.depend22
-rw-r--r--tools/2CDT/2cdt.layout7
-rw-r--r--tools/2CDT/COPYING340
-rw-r--r--tools/2CDT/Makefile24
-rw-r--r--tools/2CDT/Makefile.win33
-rw-r--r--tools/2CDT/file_id.diz16
-rw-r--r--tools/2CDT/readme.txt135
-rw-r--r--tools/2CDT/src/2cdt.c1290
-rw-r--r--tools/2CDT/src/defs.h36
-rw-r--r--tools/2CDT/src/tzxfile.c445
-rw-r--r--tools/2CDT/src/tzxfile.h79
12 files changed, 2470 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..c3fb520
--- /dev/null
+++ b/tools/2CDT/src/2cdt.c
@@ -0,0 +1,1290 @@
+/*
+ * 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"
+
+#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;
+ char 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
+