From b1a0287adbbba470598e201dde6e38dd51ae719f Mon Sep 17 00:00:00 2001 From: Peter Karlsson Date: Sun, 18 Jan 1998 13:56:52 +0100 Subject: [PATCH] Imported BasText 1.0 --- COPYING | 339 ++++++++++++++++++++++++++++ Makefile | 46 ++++ Makefile.os2 | 49 +++++ bastext.1 | 420 +++++++++++++++++++++++++++++++++++ bastext.doc | 330 +++++++++++++++++++++++++++ dtokeniz.c | 195 ++++++++++++++++ inmode.c | 240 ++++++++++++++++++++ inmode.h | 10 + main.c | 185 ++++++++++++++++ outmode.c | 362 ++++++++++++++++++++++++++++++ outmode.h | 11 + select.c | 52 +++++ select.h | 11 + t64.c | 44 ++++ t64.h | 53 +++++ tidy.c | 40 ++++ tokenize.c | 360 ++++++++++++++++++++++++++++++ tokenize.h | 15 ++ tokens.c | 612 +++++++++++++++++++++++++++++++++++++++++++++++++++ tokens.h | 27 +++ version.h | 9 + 21 files changed, 3410 insertions(+) create mode 100644 COPYING create mode 100644 Makefile create mode 100644 Makefile.os2 create mode 100644 bastext.1 create mode 100644 bastext.doc create mode 100644 dtokeniz.c create mode 100644 inmode.c create mode 100644 inmode.h create mode 100644 main.c create mode 100644 outmode.c create mode 100644 outmode.h create mode 100644 select.c create mode 100644 select.h create mode 100644 t64.c create mode 100644 t64.h create mode 100644 tidy.c create mode 100644 tokenize.c create mode 100644 tokenize.h create mode 100644 tokens.c create mode 100644 tokens.h create mode 100644 version.h diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..aca3fe3 --- /dev/null +++ b/COPYING @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 675 Mass Ave, Cambridge, MA 02139, 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 + + Appendix: 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. + + + Copyright (C) 19yy + + 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., 675 Mass Ave, Cambridge, MA 02139, 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. + + , 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/Makefile b/Makefile new file mode 100644 index 0000000..7ccbf3f --- /dev/null +++ b/Makefile @@ -0,0 +1,46 @@ +# Makefile for bastext ------------------------------------------------------- +OBJS=main.o inmode.o outmode.o tokens.o tokenize.o dtokeniz.o select.o t64.o + +# All targets ---------------------------------------------------------------- +all: bastext bastext.doc + +# Main executable ------------------------------------------------------------ +bastext: $(OBJS) + gcc -o bastext $(OBJS) + +tokens.o: tokens.c tokens.h + gcc -c tokens.c + +tokenize.o: tokenize.c tokenize.h tokens.h + gcc -c tokenize.c + +dtokeniz.o: dtokeniz.c tokenize.h tokens.h + gcc -c dtokeniz.c + +main.o: main.c inmode.h outmode.h tokenize.h + gcc -c main.c + +inmode.o: inmode.c tokenize.h version.h inmode.h select.h t64.h + gcc -c inmode.c + +outmode.o: outmode.c tokenize.h version.h outmode.h select.h t64.h + gcc -c outmode.c + +select.o: select.c select.h tokenize.h + gcc -c select.c + +t64.o: t64.c t64.h + gcc -c t64.c + +# Plaintext documentation ---------------------------------------------------- +bastext.doc: bastext.1 tidy + groff -Tlatin1 -mandoc bastext.1 > /tmp/bastext.doc + tidy /tmp/bastext.doc ./bastext.doc + rm /tmp/bastext.doc + +tidy: tidy.c + gcc -o tidy tidy.c + +# Cleanup -------------------------------------------------------------------- +clean: + -rm core *.o *~ tidy diff --git a/Makefile.os2 b/Makefile.os2 new file mode 100644 index 0000000..232a5f6 --- /dev/null +++ b/Makefile.os2 @@ -0,0 +1,49 @@ +# Makefile for bastext ------------------------------------------------------- +OBJS=main.o inmode.o outmode.o tokens.o tokenize.o dtokeniz.o select.o t64.o + +# All targets ---------------------------------------------------------------- +all: bastext.exe +# bastext.doc + +# Main executable ------------------------------------------------------------ +bastext.exe: $(OBJS) + gcc -o bastext.exe $(OBJS) + +tokens.o: tokens.c tokens.h + gcc -c tokens.c + +tokenize.o: tokenize.c tokenize.h tokens.h + gcc -c tokenize.c + +dtokeniz.o: dtokeniz.c tokenize.h tokens.h + gcc -c dtokeniz.c + +main.o: main.c inmode.h outmode.h tokenize.h + gcc -c main.c + +inmode.o: inmode.c tokenize.h version.h inmode.h select.h t64.h + gcc -c inmode.c + +outmode.o: outmode.c tokenize.h version.h outmode.h select.h t64.h + gcc -c outmode.c + +select.o: select.c select.h tokenize.h + gcc -c select.c + +t64.o: t64.c t64.h + gcc -c t64.c + +# Plaintext documentation ---------------------------------------------------- +#bastext.doc: bastext.1 tidy +# groff -Tlatin1 -mandoc bastext.1 > /tmp/bastext.doc +# tidy /tmp/bastext.doc ./bastext.doc +# rm /tmp/bastext.doc +# +#tidy: tidy.c +# gcc -o tidy tidy.c +# +# Cleanup -------------------------------------------------------------------- +clean: + -rm core *.o *~ + + \ No newline at end of file diff --git a/bastext.1 b/bastext.1 new file mode 100644 index 0000000..5066c0c --- /dev/null +++ b/bastext.1 @@ -0,0 +1,420 @@ +.TH BASTEXT 1 1998-01-18 "Softwolves Software" "" +.SH NAME +bastext \- convert Commodore BASIC to text +.SH SYNOPSIS +.PD 0 +.B bastext +\-i [\-t] [\-a] [\-s] [\-d filename] +filename(s) +.PP +.B bastext +\-o +[\-t] [\-2|\-3|\-5|\-7|\-1] +filename(s) +.PP +.B bastext +\-h +.PD +.SH DESCRIPTION +.B bastext +is a program that is used to convert between binary (tokenized) +BASIC files from the Commodore C64 and C128 to a clean ASCII text +format that is human and machine readable, as well as transportable +via electronic mail. +This program is designed to be compatible with +.BR tok64 , +while extending the support for Commodore BASIC extensions and +dialects. +.PP +The following Commodore BASIC versions and extensions are supported +by +.BR bastext : +.RS +.PP +.I Commodore BASIC 2.0 +.PD 0 +.PP +.I Commodore BASIC 7.0 +.PP +.I Software Unlimited's Graphics52 for Commodore 64 +.PP +.I Riska BV's The Final Cartridge III for Commodore 64 +.PP +.I Rick Simon's BASIC 7.1 for Commodore 128 +.RE +.PD +.PP +.B bastext +tries to autodetect the Commodore BASIC dialect used in the file +it is processing. +At the moment, this is done by checking the starting address of the +file. +These starting addresses are recognized, and interpreted (addresses +written in hexadecimal): +.TP +0401 +.I VIC-20 BASIC 2.0 +or +.IR "Commodore 64 Graphics52 BASIC extension" . +Since Graphics52 is the super-set, the file will be interpreted +as such. +.TP +0801 +.I Commodore 64 BASIC 2.0 +or +.IR "Commodore 64 The Final Cartridge III BASIC extension" . +Since TFC3 is the super-set, the file will be interpreted +as such. +.TP +132D +.IR "Commodore 128 BASIC 7.1 extension by Rick Simon" . +This file is a combined file, with both the BASIC 7.1 +extension binary, and the BASIC source in one file (saved +with BASIC 7.1's +.B ESAVE +command). +The preamble will be ignored, and the file will be +interpreted as BASIC 7.1. +.TP +1C01 +.I Commodore 128 BASIC 7.0 +or +.IR "Commodore 128 BASIC 7.1 extension by Rick Simon" . +Since BASIC 7.1 is the super-set, the file will be +interpreted as such. +.TP +4001 +.I Commodore 128 BASIC 7.0 +saved with graphics mode enabled. +.PP +The text files created by this program are supposed to be +compatible with those of +.BR tok64 , +meaning that the files it creates should be possible to +interpret with this one, and the files this program creates +should be possible to interpret with it. +However, an extra header, in front of +.BR tok64 \'s +.I start tok64 +header is added, +.IR "start bastext" , +which contains the starting address of the original binary +file. +This is to make the autodetection possible for the text-to-binary +direction. +The header is not included when the program starts at 0801 +(see also +.BR BUGS ) +or 1C01. +For Commodore 128 BASIC programs, the header is changed to +.IR "start tok128" , +since +.B tok64 +only supports C64 programs, this will make it skip those +programs. +.PP +When +.B bastext +interprets text representations of files back into binary mode, +it will use the start address as saved in the +.I start bastext +header, to autodetect the file format in the same way as in +binary-to-text. +If no such header exists (e.g if the file was created with +.BR tok64 ), +.B bastext +will assume that the program's start address is 0801 +(standard for Commodore 64), and that it should interpret it as +Commodore 128 BASIC 7.0, as this is a super-set of BASIC 2.0. +This should make +.B bastext +able to interpret most text files without any big problems. +If autodetection in this mode does not work, use one of the +"force" parameters described below. +.SH OPTIONS +One of the three mode selectors must be given: +.TP +.I \-i +Set input mode (converting from binary Commodore tokenized BASIC to +text). +.TP +.I \-o +Set output mode (converting from text to binary Commodore tokenized +BASIC). +.TP +.I \-h +Shows a brief help screen, with an overview of the available options. +.SS "GENERAL MODIFIERS" +.PP +These general modifiers (works in both input and output modes) +are available: +.TP +.I \-t +Enable T64 (Commodore 64 emulator tape archive) mode. +When in input mode, this means that instead of the specified file +names being binary Commodore BASIC files, they are T64 archives. +When in output mode, this means that instead of writing the +binary Commodore BASIC files to files in the current directory, +they will be written to a T64 archive named +.B bastext.t64 +in the current directory. +If the archive already exists, it will be appended to. +The default directory size for the +.B bastext.t64 +file is 30 entries. +If you try to add more files to it, the program will abort with +an error message. +The default directory size is controlled in the +.I t64.h +file. +.SS "INPUT MODE MODIFIERS" +.PP +These modifiers are available only when in input mode: +.TP +.I \-a +Convert all input files, not only those that have a \"recognized\" +starting address. +.TP +.I \-s +Maintain strict compatibility with +.BR tok64 . +This means that +.BR bastext \'s +"extended" escape codes for charactes 92 (British pound), +95 (left arrow), 160\-192 (shift space to shift asterisk), +219\-221 (shift plus, commodore minus and shift minus), and 223 +(commodore asterisk), will be printed as three-digit numeric +escape codes, not as textual escapes. +The "strict" mode will not, however, undo the problems with +.BR tok64 's +"uppercase in quoted strings"-bug (see under +.BR BUGS ). +.TP +.I \-d filename +Selects the filename to write the output to. +If the filename is not given, or is given as "-", the listings +will be output on the standard output device (normally the +console). +.SS "OUTPUT MODE MODIFIERS" +.PP +These modifiers are available only when in output mode: +.TP +.I \-2 +Force +.I Commodore BASIC 2.0 +interpretation of +.I all +programs. +.TP +.I \-3 +Force +.I Commodore 64 The Final Cartridge III BASIC extension +interpretation of +.I all +programs. +(See also +.BR BUGS ). +.TP +.I \-5 +Force +.I Commodore 64 Graphics52 BASIC extension +interpretation of +.I all +programs. +.TP +.I \-7 +Force +.I Commodore 128 BASIC 7.0 +interpretation of +.I all +programs. +.TP +.I \-1 +Force +.I Commodore 128 BASIC 7.1 extension +interpretation of +.I all +programs. +.PP +Please note that the MS-DOS and OS/2 versions (EMX compiled) +uses +.B / +(slash) as parameter character. +.SH EXAMPLES +.TP +.B bastext \-i sample.prg +Converts +.I sample.prg +to text, and displays it on the standard output. +.TP +.B bastext \-i \-s \-d programs.txt *.prg +Converts all Commodore BASIC binary files with a +.I .prg +extension to text, writing it to +.I programs.txt +in the current directory, while maintaining +.B tok64 +compatibility. +.TP +.B bastext \-it *.t64 | more +Converts all files in all T64 archives (with filename suffix +.IR .t64 ) +in the current directory into listings, displaying them +one page at a time. +.TP +.B bastext \-o7 programs.txt +Converts all programs in the +.I programs.txt +text file into Commodore BASIC 7.0 programs. +.SH "SEE ALSO" +.PD 0 +.PP +.I http://ip64.home.ml.org/ +.PP +.I http://ourworld.compuserve.com/homepages/pcgeek/proj64.htm +.PD +.SH FILES +.I ./bastext.t64 +.SH "DISTRIBUTED FILES" +These source files comes with the +.B bastext +distribution: +.PD 0 +.TP +.I COPYING +The GNU Public License. +.TP +.I Makefile +File used by +.BR make (1) +to automate compilation. +.TP +.I Makefile.os2 +Makefile for DOS/OS2 version (using EMX). +.TP +.I bastext.1 +Source code for manual page/documentation. +.TP +.I bastext.doc +Manual page/documentation. +.TP +.I dtokeniz.c +Routines for detokenization. +.TP +.I inmode.c +Routines used for the input mode. +.TP +.I inmode.h +Header file for +.IR inmode.c . +.TP +.I main.c +Start-up routines. +.TP +.I outmode.c +Routines used for the output mode. +.TP +.I outmode.h +Header file for +.IR outmode.c . +.TP +.I select.c +Routines for BASIC dialect autodetection. +.TP +.I select.h +Header file for +.IR select.c . +.TP +.I t64.c +Routines used with T64 files. +.TP +.I t64.h +Header file for +.IR t64.c , +including definition of T64 file format. +.TP +.I tidy.c +Utility program used to create +.IR bastext.doc . +.TP +.I tokenize.c +Routines for tokenization. +.TP +.I tokenize.h +Header file for +.I tokenize.c +and +.IR dtokeniz.c . +.TP +.I tokens.c +Tokens and PETSCII tables. +.TP +.I tokens.h +Header file for +.IR tokens.c . +.TP +.I version.h +Header file contaning program name and version. +.PD +.PP +These binary files comes with the +.B bastext +distribution: +.PD 0 +.TP +.I linux/bastext.i86 +Binary version for Linux/i86. +.TP +.I linux/bastext.sun +Binary version for Linux/Sparc. +.TP +.I sun/bastext.sun +Binary version for SunOS/Sparc. +.TP +.I dos/bastext.exe +Binary version for DOS and OS/2 (EMX). +Requires the EMX/RSX runtime libraries. +.PD +.SH BUGS +TFC3 BASIC files that are converted to text will not have a +.I start bastext +header, so when converted back to binary, they will be +interpreted as Commodore 128 BASIC 7.0. +You have to use the +.I \-3 +option (force TFC3) to convert TFC3 BASIC text files back to +binary. +.PP +.B tok64 +seems to parse uppercase characters in quoted strings +incorrectly. +It converts them into characters in the range of 97-122, +whereas a Commodore computer (and +.BR bastext ) +usually uses the "shadow" range of 193-208. +This will not look any different when used on a Commodore +computer, but it will make the binaries differ, and could +make a difference if the program needs the correct PETSCII +values. +The problem will appear when you convert the program in +one direction with +.B bastext +and in the other with +.BR tok64 , +not when using the same program in both directions. +.PP +.B bastext +does not yet support the "bare" format that tok64 normally +outputs (without the +.I start tok64 +header). +.SH VERSION\ INFORMATION +This documentation covers version 1.0 of +.BR bastext . +.SH AUTHOR +BASTEXT was written by Peter Karlsson. +If you have bug-reports or questions, mail them to pk@abc.se. +.PP +A Softwolves Software (tm) Release in 1998. +.PP +This program is released under the GNU Public License. \ No newline at end of file diff --git a/bastext.doc b/bastext.doc new file mode 100644 index 0000000..3692267 --- /dev/null +++ b/bastext.doc @@ -0,0 +1,330 @@ + + + +BASTEXT(1) BASTEXT(1) + + +NAME + bastext - convert Commodore BASIC to text + +SYNOPSIS + bastext -i [-t] [-a] [-s] [-d filename] filename(s) + bastext -o [-t] [-2|-3|-5|-7|-1] filename(s) + bastext -h + +DESCRIPTION + bastext is a program that is used to convert between + binary (tokenized) BASIC files from the Commodore C64 and + C128 to a clean ASCII text format that is human and + machine readable, as well as transportable via electronic + mail. This program is designed to be compatible with + tok64, while extending the support for Commodore BASIC + extensions and dialects. + + The following Commodore BASIC versions and extensions are + supported by bastext: + + Commodore BASIC 2.0 + Commodore BASIC 7.0 + Software Unlimited's Graphics52 for Commodore 64 + Riska BV's The Final Cartridge III for Commodore 64 + Rick Simon's BASIC 7.1 for Commodore 128 + + bastext tries to autodetect the Commodore BASIC dialect + used in the file it is processing. At the moment, this is + done by checking the starting address of the file. These + starting addresses are recognized, and interpreted + (addresses written in hexadecimal): + + 0401 VIC-20 BASIC 2.0 or Commodore 64 Graphics52 BASIC + extension. Since Graphics52 is the super-set, the + file will be interpreted as such. + + 0801 Commodore 64 BASIC 2.0 or Commodore 64 The Final + Cartridge III BASIC extension. Since TFC3 is the + super-set, the file will be interpreted as such. + + 132D Commodore 128 BASIC 7.1 extension by Rick Simon. + This file is a combined file, with both the BASIC + 7.1 extension binary, and the BASIC source in one + file (saved with BASIC 7.1's ESAVE command). The + preamble will be ignored, and the file will be + interpreted as BASIC 7.1. + + 1C01 Commodore 128 BASIC 7.0 or Commodore 128 BASIC 7.1 + extension by Rick Simon. Since BASIC 7.1 is the + super-set, the file will be interpreted as such. + + 4001 Commodore 128 BASIC 7.0 saved with graphics mode + enabled. + + + + +Softwolves Software 1998-01-18 1 + + + + + +BASTEXT(1) BASTEXT(1) + + + The text files created by this program are supposed to be + compatible with those of tok64, meaning that the files it + creates should be possible to interpret with this one, and + the files this program creates should be possible to + interpret with it. However, an extra header, in front of + tok64´s start tok64 header is added, start bastext, which + contains the starting address of the original binary file. + This is to make the autodetection possible for the text- + to-binary direction. The header is not included when the + program starts at 0801 (see also BUGS) or 1C01. For Com­ + modore 128 BASIC programs, the header is changed to start + tok128, since tok64 only supports C64 programs, this will + make it skip those programs. + + When bastext interprets text representations of files back + into binary mode, it will use the start address as saved + in the start bastext header, to autodetect the file format + in the same way as in binary-to-text. If no such header + exists (e.g if the file was created with tok64), bastext + will assume that the program's start address is 0801 + (standard for Commodore 64), and that it should interpret + it as Commodore 128 BASIC 7.0, as this is a super-set of + BASIC 2.0. This should make bastext able to interpret + most text files without any big problems. If autodetec­ + tion in this mode does not work, use one of the "force" + parameters described below. + +OPTIONS + One of the three mode selectors must be given: + + -i Set input mode (converting from binary Commodore + tokenized BASIC to text). + + -o Set output mode (converting from text to binary + Commodore tokenized BASIC). + + -h Shows a brief help screen, with an overview of the + available options. + + GENERAL MODIFIERS + These general modifiers (works in both input and output + modes) are available: + + -t Enable T64 (Commodore 64 emulator tape archive) + mode. When in input mode, this means that instead + of the specified file names being binary Commodore + BASIC files, they are T64 archives. When in output + mode, this means that instead of writing the binary + Commodore BASIC files to files in the current + directory, they will be written to a T64 archive + named bastext.t64 in the current directory. If the + archive already exists, it will be appended to. + The default directory size for the bastext.t64 file + is 30 entries. If you try to add more files to it, + + + +Softwolves Software 1998-01-18 2 + + + + + +BASTEXT(1) BASTEXT(1) + + + the program will abort with an error message. The + default directory size is controlled in the t64.h + file. + + INPUT MODE MODIFIERS + These modifiers are available only when in input mode: + + -a Convert all input files, not only those that have a + starting address. + + -s Maintain strict compatibility with tok64. This + means that bastext´s "extended" escape codes for + charactes 92 (British pound), 95 (left arrow), + 160-192 (shift space to shift asterisk), 219-221 + (shift plus, commodore minus and shift minus), and + 223 (commodore asterisk), will be printed as three- + digit numeric escape codes, not as textual escapes. + The "strict" mode will not, however, undo the prob­ + lems with tok64's "uppercase in quoted strings"-bug + (see under BUGS). + + -d filename + Selects the filename to write the output to. If + the filename is not given, or is given as "-", the + listings will be output on the standard output + device (normally the console). + + OUTPUT MODE MODIFIERS + These modifiers are available only when in output mode: + + -2 Force Commodore BASIC 2.0 interpretation of all + programs. + + -3 Force Commodore 64 The Final Cartridge III BASIC + extension interpretation of all programs. (See + also BUGS). + + -5 Force Commodore 64 Graphics52 BASIC extension + interpretation of all programs. + + -7 Force Commodore 128 BASIC 7.0 interpretation of all + programs. + + -1 Force Commodore 128 BASIC 7.1 extension interpreta­ + tion of all programs. + + Please note that the MS-DOS and OS/2 versions (EMX com­ + piled) uses / (slash) as parameter character. + +EXAMPLES + bastext -i sample.prg + Converts sample.prg to text, and displays it on the + standard output. + + + + +Softwolves Software 1998-01-18 3 + + + + + +BASTEXT(1) BASTEXT(1) + + + bastext -i -s -d programs.txt *.prg + Converts all Commodore BASIC binary files with a + .prg extension to text, writing it to programs.txt + in the current directory, while maintaining tok64 + compatibility. + + bastext -it *.t64 | more + Converts all files in all T64 archives (with file­ + name suffix .t64) in the current directory into + listings, displaying them one page at a time. + + bastext -o7 programs.txt + Converts all programs in the programs.txt text file + into Commodore BASIC 7.0 programs. + +SEE ALSO + http://ip64.home.ml.org/ + http://ourworld.compuserve.com/homepages/pcgeek/proj64.htm + +FILES + ./bastext.t64 + +DISTRIBUTED FILES + These source files comes with the bastext distribution: + COPYING + The GNU Public License. + Makefile + File used by make(1) to automate compilation. + Makefile.os2 + Makefile for DOS/OS2 version (using EMX). + bastext.1 + Source code for manual page/documentation. + bastext.doc + Manual page/documentation. + dtokeniz.c + Routines for detokenization. + inmode.c + Routines used for the input mode. + inmode.h + Header file for inmode.c. + main.c Start-up routines. + outmode.c + Routines used for the output mode. + outmode.h + Header file for outmode.c. + select.c + Routines for BASIC dialect autodetection. + select.h + Header file for select.c. + t64.c Routines used with T64 files. + t64.h Header file for t64.c, including definition of T64 + file format. + tidy.c Utility program used to create bastext.doc. + + + + +Softwolves Software 1998-01-18 4 + + + + + +BASTEXT(1) BASTEXT(1) + + + tokenize.c + Routines for tokenization. + tokenize.h + Header file for tokenize.c and dtokeniz.c. + tokens.c + Tokens and PETSCII tables. + tokens.h + Header file for tokens.c. + version.h + Header file contaning program name and version. + + These binary files comes with the bastext distribution: + linux/bastext.i86 + Binary version for Linux/i86. + linux/bastext.sun + Binary version for Linux/Sparc. + sun/bastext.sun + Binary version for SunOS/Sparc. + dos/bastext.exe + Binary version for DOS and OS/2 (EMX). Requires + the EMX/RSX runtime libraries. + +BUGS + TFC3 BASIC files that are converted to text will not have + a start bastext header, so when converted back to binary, + they will be interpreted as Commodore 128 BASIC 7.0. You + have to use the -3 option (force TFC3) to convert TFC3 + BASIC text files back to binary. + + tok64 seems to parse uppercase characters in quoted + strings incorrectly. It converts them into characters in + the range of 97-122, whereas a Commodore computer (and + bastext) usually uses the "shadow" range of 193-208. This + will not look any different when used on a Commodore com­ + puter, but it will make the binaries differ, and could + make a difference if the program needs the correct PETSCII + values. The problem will appear when you convert the pro­ + gram in one direction with bastext and in the other with + tok64, not when using the same program in both directions. + + bastext does not yet support the "bare" format that tok64 + normally outputs (without the start tok64 header). + +VERSION INFORMATION + This documentation covers version 1.0 of bastext. + +AUTHOR + BASTEXT was written by Peter Karlsson. If you have bug- + reports or questions, mail them to pk@abc.se. + + A Softwolves Software (tm) Release in 1998. + + This program is released under the GNU Public License. + + + + +Softwolves Software 1998-01-18 5 + + diff --git a/dtokeniz.c b/dtokeniz.c new file mode 100644 index 0000000..bf3f620 --- /dev/null +++ b/dtokeniz.c @@ -0,0 +1,195 @@ +/* detokenize.c + * - routines to detokenize C64/C128 BASIC + */ + +#include +#include + +#include "tokenize.h" +#include "tokens.h" + +#define FALSE 0 +#define TRUE 1 + +/* The bytestream buffer used in the function (input) is from the line + * number up to the ending null character. The "next line" pointer is + * not included + */ + +/* detokenize + * - detokenize a C64/C128 BASIC (in binary) line + * in: input_p - pointer to a bytestream to detokenize + * output_p - pointer to a string to put results in, MUST BE ALLOCATED + * mode - BASIC version to detokenize + * strict - flag for using strict tok64 compatibility + * out: nonzero on error + */ +int detokenize(const char *input_p, char *output_p, basic_t mode, int strict) +{ + int quotemode = FALSE; /* flag for quote mode */ + unsigned short i; /* loop counter */ + unsigned linenumber; /* line number */ + int rc = 0; /* return code */ + int isspecial; /* flag for special characters */ + const unsigned char *ch_p; /* pointer moving over input */ + const char *escape_p; /* pointer to current escape sequence */ + char numeric[4]; /* threedigit numeric escape for strict tok64 + compatibility */ + + ch_p = input_p; + + /* First two bytes is the line number as (low,high) */ + linenumber = (*ch_p) | (*(ch_p + 1)) << 8; + ch_p += 2; + + /* print it to the output string, and move the character pointer beyond */ + output_p += sprintf(output_p, "%u ", linenumber); + + /* Next comes a bytestream of line data, ending in a null character */ + while (*ch_p) { + /* Point to PETSCII sequence */ + escape_p = petscii[*ch_p]; + if (strict && nontok64compatible(*ch_p)) { + /* Maintain tok64 compatibility */ + sprintf(numeric, "%03d", (int) *ch_p); + escape_p = numeric; + } + + /* Process token */ + if (quotemode) { /* quoted string? */ + /* Convert from PETSCII to ASCII, + * and write repetitions as a multiple of the character. + * Repetitions of non-special characters is only written if + * there are three or more repetitions. + * Repetitions of * is not written ({**n} is not parsed correctly) + * Repetitions of " is not written (quotemode on/off) + */ + if (34 == *ch_p) { /* quote */ + *(output_p ++) = '\"'; + quotemode = FALSE; /* go out of quotemode */ + } /* if */ + else if (42 == *ch_p) { /* asterisk */ + *(output_p ++) = '*'; + } /* else */ + else { + /* Check for special token (escape is multibyte) */ + if (escape_p[1] == 0) { + isspecial = FALSE; + } /* if */ + else { + isspecial = TRUE; + } /* else */ + + /* Check repetition if: + * current and next character match + * AND (at least) one of the following: + * + * OR current and third character match + * AND (at least) one of the following: + * we are not in tok64 strict compatibility mode + * OR the character is space + * OR the escape code is not a single character + */ + if (*ch_p == ch_p[1] && + ((isspecial || 32 == *ch_p) || + *ch_p == ch_p[2]) && + (!strict || 32 == *ch_p || strlen(escape_p) > 1)) { + /* Count repetitions */ + i = 2; + while (ch_p[i] == *ch_p) i ++; + + /* We know the repetition number, now print it */ + if (32 == *ch_p) { /* space */ + output_p += sprintf(output_p, "{space*%hd}", i); + } /* if */ + else { + output_p += sprintf(output_p, "{%s*%hd}", escape_p, i); + } /* else */ + + ch_p += i - 1; /* point to last repetition */ + } /* if */ + else { /* not repetition */ + if (isspecial) { + output_p += sprintf(output_p, "{%s}", escape_p); + } /* if */ + else { /* normal character */ + *(output_p ++) = *escape_p; + } /* else */ + } /* else */ + } /* else */ + } /* if */ + else { /* command mode */ + if (*ch_p >= 128 && *ch_p <= 254) { /* Probable BASIC command */ + if ((unsigned char) *ch_p <= 203) { + /* C64 BASIC 2.0 */ + output_p += sprintf(output_p, "%s", + c64tokens[*ch_p - 128]); + } /* if */ + else if (*ch_p == 0xCE && + (*(ch_p + 1) >= 2 && *(ch_p + 1) <= 0xA) && + (Basic7 == mode || Basic71 == mode)) { + /* C128 BASIC 7.0 CE prefix */ + ch_p ++; + output_p += sprintf(output_p, "%s", + c128CEtokens[*ch_p]); + } /* else */ + else if (*ch_p == 0xFE && *(ch_p + 1) >= 2 && + ((*(ch_p + 1) <= 0x26 && Basic7 == mode) || + (*(ch_p + 1) <= 0x37 && Basic71 == mode))) { + /* C128 BASIC 7.0/7.1 FE prefix */ + ch_p ++; + output_p += sprintf(output_p, "%s", + c128FEtokens[*ch_p]); + } /* else */ + else if (Basic7 == mode || Basic71 == mode) { + /* C128 BASIC 7.0 */ + output_p += sprintf(output_p, "%s", + c128tokens[*ch_p - 204]); + } /* else */ + else if (Graphics52 == mode) { + /* C64 Graphics52 */ + output_p += sprintf(output_p, "%s", + graphics52tokens[*ch_p - 204]); + } /* else */ + else if (*ch_p <= 232 && TFC3 == mode) { + /* C64 TFC3 */ + output_p += sprintf(output_p, "%s", + tfc3tokens[*ch_p - 204]); + } /* else */ + else { + /* Errorneous token */ + output_p += sprintf(output_p, "{%d}", *ch_p); + } + + } /* if */ + else { /* text */ + /* PETSCII text in BASIC: + * The only possible case of text is unshifted. To increase + * readability, this is written as lowercase ASCII, whereas + * keywords are written as uppercase. + * There can also be special characters (32-64), they are + * printed as-is. + */ + if ((*ch_p >= 32 && *ch_p <= 64) || /* ' ' - '@', */ + 91 == *ch_p || 93 == *ch_p) { /* '[', ']' */ + *(output_p ++) = *ch_p; + if (34 == *ch_p) { + quotemode = TRUE; /* go to quotemode */ + } /* if */ + } /* if */ + else if (*ch_p >= 65 && *ch_p <= 90) { /* 'A' - 'Z' */ + *(output_p ++) = tolower(*ch_p); + } /* else */ + else { /* Possibly illegal character, write petscii escape */ + output_p += sprintf(output_p, "{%s}", escape_p); + } /* else */ + } /* else */ + } /* else */ + + ch_p ++; /* next character */ + } /* while */ + + *output_p = 0; + + return rc; +} \ No newline at end of file diff --git a/inmode.c b/inmode.c new file mode 100644 index 0000000..dd54528 --- /dev/null +++ b/inmode.c @@ -0,0 +1,240 @@ +/* inmode.c + * Routines for converting binary to text + */ + +#include +#include + +#include "inmode.h" +#include "tokenize.h" +#include "version.h" +#include "select.h" +#include "t64.h" + +#define FALSE 0 +#define TRUE 1 + +void inconvert(FILE *, FILE *, const char *, int, int, int); + +/* bas2txt + * - converts a binary file into a text file + * in: infile - file name of file to read + * allfiles - flag whether or not to convert "non-BASIC" files + * strict - flag for using strict tok64 compatibility + * out: none + */ +void bas2txt(const char *infile, FILE *output, int allfiles, int strict) +{ + FILE *input; + const char *title_p; + int adr; + + /* First, open input file */ + input = fopen(infile, "rb"); + if (!input) { + fprintf(stderr, "Unable to open input file: %s\n", infile); + exit(1); + } + + /* Name to print in header is the last part of the file name */ +#ifdef __EMX__ + title_p = strrchr(infile, '\\'); +#else + title_p = strrchr(infile, '/'); +#endif + if (title_p) { /* Found, make pointer point past the slash */ + title_p ++; + } + else { /* Not found, point to the whole file name */ + title_p = infile; + } + + /* First read the start address */ + adr = fgetc(input); /* low byte */ + adr |= fgetc(input) << 8; /* high byte */ + + /* Now convert the file to text */ + inconvert(input, output, title_p, adr, allfiles, strict); + + /* Close files */ + fclose(input); +} + +void t642txt(const char *infile, FILE *output, int allfiles, int strict) +{ + FILE *input; + char title[21], *c_p; + t64header_t header; + t64record_t record; + unsigned int totalentries, usedentries, i; + int adr; + long fptr; + + /* First, open input file */ + input = fopen(infile, "rb"); + if (!input) { + fprintf(stderr, "Unable to open input file: %s\n", infile); + exit(1); + } + + /* Read the T64 header */ + fread(&header, sizeof(header), 1, input); + + /* Check that it is a T64 file */ + if (checkvalidheader(&header, &totalentries, &usedentries, infile)) { + /* It wasn't -> panic */ + exit(1); + } + + /* Cycle through the entries */ + for (i = 0; i < usedentries; i ++) { + /* Seek to the directory entry and read it */ + fseek(input, sizeof(t64header_t) + sizeof(t64record_t) * i, SEEK_SET); + fread(&record, sizeof(t64record_t), 1, input); + + /* Check filetype */ + if (ALLOC_NORM == record.allocflag) { + /* This is an allocated entry, with a normal program file in it */ + + /* Get the file title */ + strncpy(title, record.filename, 16); + title[16] = 0; /* null terminate */ + + while ((char) 32 == title[strlen(title) - 1] || + (char) 160 == title[strlen(title) - 1]) { + /* Remove trailing spaces */ + title[strlen(title) - 1] = 0; + } + + /* Convert to uppercase ASCII, and change spaces to underscores */ + c_p = title; + while (*c_p) { + *c_p &= 0x7F; /* Strip highbit */ + if (0x60 == (*c_p & 0x60)) { + *c_p &= ~0x20; /* Lowercase => uppercase */ + } + else if (' ' == *c_p) { + *c_p = '_'; + } + c_p ++; + } + + /* Add .prg suffix */ + strcat(title, ".prg"); + + /* Retrieve the starting address */ + adr = record.startaddress[0] | (record.startaddress[1] << 8); + + /* Position file pointer to the start of data */ + fptr = (record.offset[0] ) | (record.offset[1] << 8 ) | + (record.offset[2] << 16) | (record.offset[3] << 24); + fseek(input, fptr, SEEK_SET); + + /* Now convert the file to text */ + fprintf(stderr, "Converting: %s\n", title); + inconvert(input, output, title, adr, allfiles, strict); + } + } + + /* Close files */ + fclose(input); +} + +/* inconvert + * - performs the actual conversion + * in: input - open file, positioned at start of BASIC program + * output - open file, to write to + * title - program title to print in header + * allfiles - flag whether or not to convert "non-BASIC" files + * out: none + */ +void inconvert(FILE *input, FILE *output, const char *title, int adr, + int allfiles, int strict) +{ + int ch, nextadr; + char buf[256], text[512]; + basic_t mode; + + /* Check for valid BASIC file */ + if (allfiles || 0x0401 == adr || 0x0801 == adr || 0x1c01 == adr || + 0x4001 == adr || 0x132D == adr) { + mode = selectbasic(adr); + + /* Print bastext header if start is != 0x0801 and != 0x1C01 */ + if (0x0801 != adr && 0x1C01 != adr) { + fprintf(output, "\nstart bastext %d", adr); + } + + /* Print tok64 header */ + if (Basic7 == mode || Basic71 == mode) { + if (strict) { + /* tok64 doesn't handle C128 programs, so skip strict mode */ + strict = FALSE; + fprintf(stderr, "Strict mode ignored for C128 program: %s\n", + title); + } + fprintf(output, "\nstart tok128 %s\n", title); + } + else { + fprintf(output, "\nstart tok64 %s\n", title); + } + + /* If this is a combined BASIC 7.1 extension + BASIC text, + * skip over the header (0x132D - 0x1C00) + */ + if (0x132D == adr) { + fseek(input, 0x1C01 - 0x132D, SEEK_CUR); + adr = 0x1C01; + } + + /* We suppose this is a valid BASIC file, so start reading it + * line for line. + * Line format is this: + * [0-1]- address to next line + * [2-3]- line number \_ sent to + * [4-n]- tokenized line, null terminated / detokenize + */ + + /* Read address to next line */ + nextadr = fgetc(input); /* low byte */ + nextadr |= fgetc(input) << 8; /* high byte */ + + /* Address to next line is null when the program is ended. + * Address to next line must be higher than the current address. + * The line cannot be longer than 256 bytes + */ + while (nextadr && nextadr > adr && nextadr - adr < 256) { + /* Read the line into the buffer */ + fread(buf, nextadr - adr - 2, 1, input); + adr = nextadr; + + /* Convert to text */ + detokenize(buf, text, mode, strict); + + /* Write to output */ + fputs(text, output); + fputc('\n', output); + + /* Read address to next line */ + nextadr = fgetc(input); /* low byte */ + nextadr |= fgetc(input) << 8; /* high byte */ + } + + /* If nextadr != null, then the program was invalid */ + if (nextadr != 0) { + fprintf(stderr, "Invalid BASIC file: %s\n", title); + fprintf(output, "63999 REM \"Invalid BASIC input %s\n", title); + } + + /* Print tok64 footer */ + if (Basic7 == mode || Basic71 == mode) { + fprintf(output, "stop tok128\n(" PROGNAME ")\n"); + } + else { + fprintf(output, "stop tok64\n(" PROGNAME ")\n"); + } + } + else { + fprintf(stderr, "Invalid BASIC start address: %04x (%d)\n", adr, adr); + } +} \ No newline at end of file diff --git a/inmode.h b/inmode.h new file mode 100644 index 0000000..cc4e3b2 --- /dev/null +++ b/inmode.h @@ -0,0 +1,10 @@ +/* inmode.h + */ + +#ifndef __INMODE_H +#define __INMODE_H + +void bas2txt(const char *infile, FILE *output, int allfiles, int strict); +void t642txt(const char *infile, FILE *output, int allfiles, int strict); + +#endif diff --git a/main.c b/main.c new file mode 100644 index 0000000..7fc4b6a --- /dev/null +++ b/main.c @@ -0,0 +1,185 @@ +#include +#ifdef __EMX__ +# include +#else +# include +#endif + +#include "inmode.h" +#include "outmode.h" +#include "tokenize.h" + +#define TRUE 1 +#define FALSE 0 + +typedef enum runmode_e { None, In, Out } runmode_t; + +#ifdef __EMX__ +# define SWITCH "/" +#else +# define SWITCH "-" +#endif + +/* main + * - main routine + * evaluates arguments and call the appropriate routines + */ +int main(int argc, char *argv[]) +{ + int option, i; + int allfiles = FALSE; + int t64mode = FALSE; + int strict = FALSE; + runmode_t mode = None; + basic_t force = Any; + char *outfile = "-"; + FILE *output; + +#ifdef __EMX__ + /* OS/2 uses '/' as switch character */ + optswchar = SWITCH; +#endif + + /* Recognized options: + * i (in) - convert from binary to text + * o (out) - convert from text to binary + * t (t64) - T64 mode + * 2 (2.0) - force BASIC 2.0 -\ + * 3 (TFC3) - force TFC3 BASIC \ + * 5 (G52) - force Graphics52 BASIC >- out mode only, + * 7 (7.0) - force BASIC 7.0 / if not specified, looks at + * 1 (7.1) - force BASIC 7.1 -/ "start bastext" header + * a (all) - convert all programs, not only those with recognized start + * address (0401/0801/132D/1C01/4001) + * s (strict)-strict tok64 encoding + * d (dest) - gives destination filename (followed by filename) + * h (help) - print help page + * ? (help) + */ + while (-1 != (option = getopt(argc, argv, "iot23571asd:h?"))) { + switch (option) { + case 'i': + mode = In; + break; + + case 'o': + mode = Out; + break; + + case 't': + t64mode = TRUE; + break; + + case '2': + force = Basic2; + break; + + case '3': + force = TFC3; + break; + + case '5': + force = Graphics52; + break; + + case '7': + force = Basic7; + break; + + case '1': + force = Basic71; + break; + + case 'a': + allfiles = TRUE; + break; + + case 's': + strict = TRUE; + break; + + case 'd': + outfile = optarg; + break; + + case 'h': + case '?': + case ':': + fprintf(stderr, "Usage: %s " SWITCH "i|" SWITCH "o|" SWITCH "h [modifiers] infile(s) outfile\n" + "\n Mode (one of these required):\n" + " " SWITCH "i\tInput mode (binary to text)\n" + " " SWITCH "o\tOutput mode (text to binary)\n" + " " SWITCH "h\tPrint help page\n" + "\n General modfiers:\n" + " " SWITCH "t\tT64 mode (in: reads from specified T64 archive(s)\n" + " \t out: creates/appends to bastext.t64)\n" + "\n Input mode modfiers:\n" + " " SWITCH "a\tConvert all, not just recognized start addresses\n" + " \t (0401/0801/132D/1C01/4001)\n" + " " SWITCH "s\tStrict tok64 compatibility\n" + " " SWITCH "d fn\tSend output to file fn\n" + "\n Output mode modifiers:\n" + " " SWITCH "2\tForce C64 BASIC 2.0 interpretation\n" + " " SWITCH "3\tForce C64 TFC3 interpretation\n" + " " SWITCH "5\tForce C64 Graphics52 interpretation\n" + " " SWITCH "7\tForce C128 BASIC 7.0 interpretation\n" + " " SWITCH "1\tForce C128 BASIC 7.1 interpretation\n", + argv[0]); + return 0; + break; + + default: + fprintf(stderr, "getopt error: %d\n", option); + break; + } + } + + if (None == mode) { /* missing mode option */ + fprintf(stderr, "No operation mode specified\n" + "-- use '%s " SWITCH "h' for help\n", + argv[0]); + return 1; + } + + if (argc - optind < 1) { /* missing filenames */ + fprintf(stderr, "Filename missing\n"); + return 1; + } + + /* If in input mode, and destination file is other than '-' (stdout), + * open the output file, else set it to stdout + */ + if (In == mode && 0 != strcmp(outfile, "-")) { + output = fopen(outfile, "at"); + if (NULL == output) { + output = fopen(outfile, "wt"); + if (NULL == output) { + fprintf(stderr, "%s: Unable to open output file: %s\n", + argv[0], outfile); + exit(1); + } + } + } + else { + output = stdout; + } + + /* Filename to read first is in argv[optind] */ + for (i = optind; i < argc; i ++) { + fprintf(stderr, "Processing: %s\n", argv[i]); + + switch (mode) { + case In: + if (t64mode) t642txt(argv[i], output, allfiles, strict); + else bas2txt(argv[i], output, allfiles, strict); + break; + + case Out: + txt2bas(argv[i], force, t64mode); + break; + } + } + + /* Close output file, if any */ + if (output != stdout) fclose(output); +} \ No newline at end of file diff --git a/outmode.c b/outmode.c new file mode 100644 index 0000000..84b3e83 --- /dev/null +++ b/outmode.c @@ -0,0 +1,362 @@ +/* outmode.c + * Routines for converting text to binary + */ + +#include +#include + +#include "outmode.h" +#include "tokenize.h" +#include "version.h" +#include "t64.h" + +#define FALSE 0 +#define TRUE 1 + +#ifdef __EMX__ +#define strncasecmp strnicmp +#endif + +int outconvert(FILE *, FILE *, int, basic_t); + +/* txt2bas + * - converts a text file into a binary file + * in: infile - file name of file to read + * force - flag for forcing a BASIC mode (Any for autodetect) + * t64mode - flag for putting the files in a T64 archive + * out: none + */ +void txt2bas(const char *infile, basic_t force, int t64mode) +{ + FILE *input, *output; + int adr; + basic_t mode; + char text[256], filename[256], *c_p; + int morefiles = TRUE; + int foundheader, foundextraheader; + t64header_t header; + t64record_t record; + unsigned int totalentries, usedentries, i; + long fptr; + + /* First, open input file */ + input = fopen(infile, "rt"); + if (!input) { + fprintf(stderr, "Unable to open input file: %s\n", infile); + exit(1); + } + + /* Secondly, if in T64 mode, open the T64 archive */ + if (t64mode) { + /* If the T64 file exists, we want to continue adding to it */ + output = fopen("bastext.t64", "r+b"); + if (NULL == output) { + /* Otherwise, create a new file */ + output = fopen("bastext.t64", "w+b"); + if (NULL == output) { + fprintf(stderr, "Unable to create output file bastext.t64\n"); + } + + /* Create standard header */ + memset(&header, 0, sizeof(header)); + strcpy(header.description, "C64 tape archive " PROGNAME "\x1a"); + strncpy(header.title, "CREATED BY BASTEXT ", 24); + header.version[0] = 0x00; /* low */ + header.version[1] = 0x01; /* high */ + header.maxfiles[0] = STD_DIRSIZE & 0xFF; /* low */ + header.maxfiles[1] = STD_DIRSIZE >> 8; /* high */ + + totalentries = STD_DIRSIZE; + usedentries = 0; + fwrite(&header, sizeof(header), 1, output); + + /* Fill entries */ + memset(&record, 0, sizeof(record)); + for (i = 0; i < STD_DIRSIZE; i ++) { + fwrite(&record, sizeof(record), 1, output); + } + } + else { + /* We opened an old file, now check that it is valid */ + + /* Read the T64 header */ + fread(&header, sizeof(header), 1, output); + + /* Check that it is a T64 file */ + if (checkvalidheader(&header, &totalentries, &usedentries, + "bastext.t64")) { + /* It wasn't -> panic */ + exit(1); + } + } + } + + /* Read each available file */ + while (morefiles) { + /* Set default values */ + adr = 0x0801; /* default start address */ + mode = (Any == force) ? Basic7 + : force; /* default BASIC mode */ + + /* Locate the bastext/tok64 headers */ + foundextraheader = FALSE; + foundheader = FALSE; + while (!foundheader && NULL != fgets(text, sizeof(text), input)) { + /* Remove the trailing newline marker that fgets stuck there */ + text[sizeof(text) - 1] = 0; /* if buffer was full */ + text[strlen(text) - 1] = 0; /* overwrite newline */ + + /* Check for trailing CR (when reading DOS text files under Unix) */ + if ('\r' == text[strlen(text) - 1]) { + text[strlen(text) - 1] = 0; + } + + /* Got a text line, check for tok64 / bastext header */ + if (strncasecmp(text, "start bastext ", 14) == 0) { + /* Retrieve program start address */ + sscanf(&text[13], "%d", &adr); + + /* If not in force mode, select BASIC dialect from start + * address. + */ + if (Any == force) mode = selectbasic(adr); + + /* If the start address was 0x132D, the original file was + * a C128 BASIC 7.1 file with the BASIC extension bound to + * it. Since we don't have the extension here, we want + * instead to start the new file at 0x1C01 + */ + if (0x132D == adr) adr = 0x1C01; + + foundextraheader = TRUE; + } + else if (strncasecmp(text, "start tok64 ", 12) == 0) { + /* This is the header that starts the actual BASIC text */ + foundheader = TRUE; + + /* Retrieve the file name */ + strcpy(filename, &text[12]); + } + else if (strncasecmp(text, "start tok128 ", 13) == 0) { + /* This is the header that starts the actual BASIC text */ + foundheader = TRUE; + + /* Retrieve the file name */ + strcpy(filename, &text[13]); + + /* If we didn't find a 'start bastext' header, this was a + * standard 0x1C01 C128 BASIC file. Since program starting + * here could be BASIC 7.1 too, we set the BASIC version to + * 7.1 (superset of 7.0), if it wasn't forced + */ + if (!foundextraheader) { + adr = 0x1C01; + if (Any == force) mode = Basic71; + } + } + } + + if (foundheader) { + /* A header was found, write a message and open a file + * (in T64 mode: create dir entry) + */ + fprintf(stderr, "Tokenizing: %s\n", filename); + + if (t64mode) { + /* Check if the T64 is full */ + if (usedentries >= totalentries) { + fprintf(stderr, "T64 archive full: bastext.t64\n"); + fclose(input); + fclose(output); + exit(1); + } + + /* Create a T64 file record */ + memset(&record, 0, sizeof(record)); + record.allocflag = ALLOC_NORM; + record.filetype = 1; /* 0x82? */ /* PRG */ + record.startaddress[0] = adr & 0xFF; /* low */ + record.startaddress[1] = adr >> 8; /* high */ + + /* Remove .prg from filename, copy it to the T64 record, + * and make uppercase + */ + strcpy(text, filename); + if (NULL != (c_p = strstr(text, ".prg"))) { + *c_p = 0; + } + strncpy(record.filename, text, sizeof(record.filename)); + /* Make uppercase, convert _ to spaces, and fill with spaces. + * (strncpy pads with nulls if src is less than 'n') + */ + for (i = 0; i < sizeof(filename); i ++) { + if (0 == record.filename[i] || '_' == record.filename[i]) { + record.filename[i] = ' '; + } + else if (0x60 == (0x60 & record.filename[i])) { + record.filename[i] &= ~0x20; + } + } + + /* Seek to end of file, and enter start offset into the + * file record + */ + fseek(output, 0, SEEK_END); + fptr = ftell(output); + record.offset[0] = fptr & 0xFF; /* low */ + record.offset[1] = (fptr >> 8) & 0xFF; + record.offset[2] = (fptr >> 16) & 0xFF; + record.offset[3] = fptr >> 24; /* high */ + } + else { + output = fopen(filename, "wb"); + + /* Write the start address */ + fputc(adr & 0xFF, output); /* low */ + fputc(adr >> 8, output); /* high */ + } + + /* Now convert the file to text */ + adr = outconvert(input, output, adr, mode); + + if (t64mode) { + /* Finish the T64 record (we now know the ending address) + * and write it to the first unused position. + */ + record.endaddress[0] = adr & 0xFF; /* low */ + record.endaddress[1] = adr >> 8; /* high */ + + fseek(output, sizeof(t64header_t) + + sizeof(t64record_t) * usedentries, SEEK_SET); + fwrite(&record, sizeof(t64record_t), 1, output); + + /* Update the T64 header */ + usedentries ++; + header.numfiles[0] = usedentries & 0xFF; /* low */ + header.numfiles[1] = usedentries >> 8; /* high */ + fseek(output, (long) ((t64header_t *) NULL)->numfiles, + SEEK_SET); + fwrite(&header.numfiles, sizeof(header.numfiles), + 1, output); + } + else { + /* Close output */ + fclose(output); + } + } + else { + /* If we get here, we have reached EOF */ + morefiles = FALSE; + } + } + + /* Close input */ + fclose(input); + + if (t64mode) { + /* Close T64 */ + fclose(output); + } +} + +/* outconvert + * - performs the actual conversion + * in: input - open file, positioned at start of BASIC text + * output - open file, to write to + * adr - address to BASIC start + * mode - BASIC version to tokenize + * out: last address of file + */ +int outconvert(FILE *input, FILE *output, int adr, basic_t mode) +{ + char text[512], buf[256]; + int goon = TRUE; + int linelength; + unsigned errors = 0; + + /* Read the file until we either find a "stop tok64/tok128" footer, + * or get to the end-of-file marker + */ + while (goon && NULL != fgets(text, sizeof(text), input)) { + /* Remove the trailing newline marker that fgets stuck there */ + text[sizeof(text) - 1] = 0; /* if buffer was full */ + text[strlen(text) - 1] = 0; /* overwrite newline */ + + /* Check for trailing CR (when reading DOS text files under Unix) */ + if ('\r' == text[strlen(text) - 1]) { + text[strlen(text) - 1] = 0; + } + + /* Check for trailing backslash (line continuation) */ + while ('\\' == text[strlen(text) - 1]) { + char *c_p; + + /* Remove the backslash */ + text[strlen(text) - 1] = 0; + + /* Get next line */ + fgets(buf, sizeof(buf), input); + + /* Remove the trailing newline marker that fgets stuck there */ + buf[sizeof(buf) - 1] = 0; /* if buffer was full */ + buf[strlen(buf) - 1] = 0; /* overwrite newline */ + + /* Check for trailing CR (when reading DOS text files under Unix) */ + if ('\r' == buf[strlen(buf) - 1]) { + buf[strlen(buf) - 1] = 0; + } + + /* Make c_p point to first non-space character */ + c_p = buf; + while (' ' == *c_p) c_p ++; + + /* If the combined line isn't too long, combine it */ + if (strlen(text) + strlen(c_p) >= sizeof(text)) { + fprintf(stderr, "Line too long"); + } + else { + strcat(text, buf); + } + } + + /* Check if "stop tok64/tok128" marker */ + if (strncasecmp(text, "stop tok", 8) == 0) { + goon = FALSE; + } + else { + /* Tokenize */ + if (tokenize(text, buf, &linelength, mode)) { + errors ++; /* error if nonzero */ + } + + /* Write next-line pointer */ + adr += linelength + 2; + fputc(adr & 0xFF, output); /* low */ + fputc(adr >> 8, output); /* high */ + + /* Write line */ + fwrite(&buf, linelength, 1, output); + } + } + + /* If we had errors while interpreting the source, say so */ + if (errors) { + sprintf(text, "63999 REM\"%u errors in tokenization", errors); + tokenize(text, buf, &linelength, mode); + + /* Write tokenized line to program */ + adr += linelength + 2; + fputc(adr & 0x7F, output); + fputc(adr >> 8, output); + fwrite(&buf, linelength, 1, output); + } + + /* The program is ended by having a null nextline pointer */ + fputc(0, output); + fputc(0, output); + + /* adr points to last line pointer, which contains two nulls, so the + * last used address is adr+1 + */ + return adr + 1; +} \ No newline at end of file diff --git a/outmode.h b/outmode.h new file mode 100644 index 0000000..1bd4553 --- /dev/null +++ b/outmode.h @@ -0,0 +1,11 @@ +/* outmode.h + */ + +#ifndef __OUTMODE_H +#define __OUTMODE_H + +#include "tokenize.h" + +void txt2bas(const char *infile, basic_t force, int t64mode); + +#endif \ No newline at end of file diff --git a/select.c b/select.c new file mode 100644 index 0000000..00f8a1c --- /dev/null +++ b/select.c @@ -0,0 +1,52 @@ +/* select.c + */ + +#include + +#include "select.h" +#include "tokenize.h" + +/* selectbasic + * - Selects a BASIC dialect with regard to the starting address + * in: adr - starting address + * out: BASIC dialect + */ +basic_t selectbasic(int adr) +{ + /* With regard to the starting address, select a probable + * BASIC version + * 0401 => BASIC 2.0 (VIC20) or Graphics52 (C64) + * Graphics52 is the super-set, select it + * 0801 => BASIC 2.0 (C64) or TFC3 BASIC (C64) + * TFC3 is the super-set, select it + * 132D => BASIC 7.1 (C128) with bound extension file + * 1C01 => BASIC 7.0 (C128) or BASIC 7.1 (C128) + * BASIC 7.1 is the super-set, select it + * 4001 => BASIC 7.0 (C128) + * other=> select BASIC 7.1 (includes BASIC 2.0 and 7.0) + */ + switch (adr) { + case 0x0401: + return Graphics52; + break; + + case 0x0801: + return TFC3; + break; + + case 0x132D: + case 0x1C01: + return Basic71; + break; + + case 0x4001: + return Basic7; + break; + + default: + fprintf(stderr, "* Unrecognized start address of BASIC: %04x\n", + adr); + return Basic71; + break; + } +} \ No newline at end of file diff --git a/select.h b/select.h new file mode 100644 index 0000000..0fceef5 --- /dev/null +++ b/select.h @@ -0,0 +1,11 @@ +/* select.h + */ + +#ifndef __SELECT_H +#define __SELECT_H + +#include "tokenize.h" + +basic_t selectbasic(int adr); + +#endif \ No newline at end of file diff --git a/t64.c b/t64.c new file mode 100644 index 0000000..135c10f --- /dev/null +++ b/t64.c @@ -0,0 +1,44 @@ +/* t64.c + * - functions that operates on the T64 files + */ + +#include +#include + +#include "t64.h" + +/* checkvalidheader + * - checks for T64 file header validity + * in: header_p: pointer to header structure + * totalentries_p: pointer to where to fill in the max directory size + * usedentries_p: pointer to where to fill in the number of used entries + * out: the totalentries and usedentries are filled in + * zero for valid header + * nonzero for invalid header + */ +int checkvalidheader(t64header_t *header_p, unsigned int *totalentries_p, + unsigned int *usedentries_p, const char *filename) +{ + /* Locate the strings "C64" and "tape" in the description header */ + if (!strstr(header_p->description, "C64") || + !strstr(header_p->description, "tape")) { + fprintf(stderr, "File is not a T64 archive: %s\n", filename); + return 1; + } + + /* Copy the header data to local data (since this program is supposed to + * be runnable independent of if the current architecture is little- or + * big-endian, we have to do this). + */ + + *totalentries_p = header_p->maxfiles[0] | (header_p->maxfiles[1] << 8); + *usedentries_p = header_p->numfiles[0] | (header_p->numfiles[1] << 8); + + /* Check for data validity */ + if (0 == *totalentries_p || *usedentries_p > *totalentries_p) { + fprintf(stderr, "Error in T64 archive header: %s\n", filename); + return 1; + } + + return 0; /* No error */ +} \ No newline at end of file diff --git a/t64.h b/t64.h new file mode 100644 index 0000000..adb64e2 --- /dev/null +++ b/t64.h @@ -0,0 +1,53 @@ +/* t64.h + */ + +#ifndef __T64_H +#define __T64_H + +/* Structure definitions for the T64 archive format. + * NB: The structures are written here to be useable without knowing whether + * the machine's architecture is little- or big-endian (the file format + * is little-endian). + */ + +#pragma pack(1) + +/* Default number of entries */ +#define STD_DIRSIZE 30 + +/* T64 archive header, 64 bytes */ +typedef struct t64header_s { + char description[32]; /* "C64 tape image"+EOF+nulls */ + unsigned char version[2]; /* $00 / $01 (=$0100) */ + unsigned char maxfiles[2]; /* word */ + unsigned char numfiles[2]; /* word */ + unsigned char reserved[2]; + char title[24]; /* Title (PETSCII), space padded */ +} t64header_t; + +/* Values for t64record_t.allocflag */ +#define ALLOC_FREE 0 +#define ALLOC_NORM 1 + +/* T64 file record */ +typedef struct t64record_s { + unsigned char allocflag; /* 0 = free, 1 = normal, 2.. = others */ + unsigned char filetype; /* Filetype (1 = program) / 2ndry address? */ + unsigned char startaddress[2]; /* Start address of C64 file */ + unsigned char endaddress[2]; /* Ending address of C64 file */ + unsigned char reserved1[2]; + unsigned char offset[4]; /* Start address in T64 */ + unsigned char reserved2[4]; + char filename[16]; /* Filename (PETSCII), space padded */ +} t64record_t; + +/* T64 file layout: + * 0 t64header_t + * 64 t64record_t[t64header_t.maxfiles] + * 64+32*n start of file data + */ + +int checkvalidheader(t64header_t *header_p, unsigned int *totalentries_p, + unsigned int *usedentries_p, const char *filename); + +#endif \ No newline at end of file diff --git a/tidy.c b/tidy.c new file mode 100644 index 0000000..cb784b0 --- /dev/null +++ b/tidy.c @@ -0,0 +1,40 @@ +/* tidy.c + * - a small program that makes the groff outputted doc file not contain + * rubout sequences for bold and underline styles + */ + +#include + +int main(int argc, char *argv[]) +{ + FILE *infile, *outfile; + int ch, nch; + + if (argc != 3) { + fprintf(stderr, "Usage: %s infile outfile\n", argv[0]); + return 1; + } + + infile = fopen(argv[1], "rt"); + outfile = fopen(argv[2], "wt"); + + if (!infile || !outfile) { + fprintf(stderr, "%s: error opening files", argv[0]); + return 1; + } + + ch = 0; + while (EOF != (nch = fgetc(infile))) { + if (8 == nch) { /* ^H = rubout, kill this and the previous character */ + nch = fgetc(infile); + } + else { + if (ch) fputc(ch, outfile); + } + ch = nch; + } + fputc(ch, outfile); + + fclose(infile); + fclose(outfile); +} \ No newline at end of file diff --git a/tokenize.c b/tokenize.c new file mode 100644 index 0000000..866374b --- /dev/null +++ b/tokenize.c @@ -0,0 +1,360 @@ +/* tokenize.c + * - routines to tokenize C64/C128 BASIC + */ + +#include +#include +#include + +#include "tokenize.h" +#include "tokens.h" + +#define FALSE 0 +#define TRUE 1 + +#ifdef __EMX__ +#define strcasecmp stricmp +#define strncasecmp strnicmp +#endif + +/* The bytestream buffer used in the function (output) is from the line + * number up to the ending null character. The "next line" pointer is + * not included + */ + +/* tokenize + * - tokenizes a C64/C128 BASIC (in tok64 pseudocode) line + * in: input_p - pointer to string to tokenize + * output_p - pointer to bytestream to put results in, MUST BE ALLOCATED + * length_p - pointer to integer to write length counter to + * mode - BASIC version to tokenize + * out: nonzero on error + */ +int tokenize(const char *input_p, char *output_p, int *length_p, basic_t mode) +{ + int quotemode = FALSE; /* flag for quote mode */ + unsigned short i; /* loop counter */ + unsigned linenumber; /* line number */ + int inputleft = strlen(input_p); /* amount left of line to tokenize */ + int tokenlen; /* length of current token */ + int match; /* match found flag */ + int notokenize = FALSE; /* REM/DATA no tokenize flag */ + int rc = 0; /* return code */ + char buf[16]; /* buffer for special character match */ + char *start_p = output_p; /* pointer to input */ + + /* Skip any initial whitespace */ + while (' ' == *input_p || '\t' == *input_p) input_p ++; + + /* Get the line number */ + linenumber = 0; + while (isdigit(*input_p)) { /* line number consits of numerals */ + linenumber = linenumber * 10 + (*input_p - '0'); + input_p ++; + } /* while */ + + if (linenumber >= 64000) { + rc = 1; + fprintf(stderr, "* Illegal line number: %u\n", linenumber); + } /* if */ + + /* Insert line number in byte stream */ + *(output_p ++) = linenumber & 255; /* low */ + *(output_p ++) = linenumber >> 8; /* high */ + + /* Kill off any extraneous spaces */ + while (' ' == *input_p) input_p ++; + + /* Now process the rest of the line */ + while (*input_p) { /* while string isn't ended */ + if ('{' == *input_p) { /* special character */ + /* copy special character name to buffer + * character name ends with '}' or '*' + */ + i = 0; /* buffer position counter */ + input_p ++; /* position at first character of name */ + while (i < 16 && *input_p != '*' && *input_p != '}' && *input_p) { + /* terminate loop on: + * . buffer size overrun (error) + * . '*' in input stream + * . '}' in input stream + * . null in input stream (error) + * + * else: copy from character name to buffer + */ + buf[i ++] = *(input_p ++); + } /* while */ + buf[i] = 0; /* terminate with null */ + /* Check for error condition */ + if (*input_p != '*' && *input_p != '}') { + rc = 1; + fprintf(stderr, "* Special character sequence incorrect: '{%s'" + " at line %u\n", + buf, linenumber); + } /* if */ + else { + /* It seems to be ok, so look it up */ + match = FALSE; + + /* Threedigit numeric? */ + if (strlen(buf) == 3 && + isdigit(buf[0]) && isdigit(buf[1]) && isdigit(buf[2])) { + sscanf(buf, "%d", &match); + } + + /* Look it up in the PETSCII table */ + for (i = 1; i <= 255 && !match; i ++) { + if ((i & 0x7f) >= 0x41 && (i & 0x7f) <= 0x5A) { + /* Upper-/lowercase PETSCII must be matched with + * case also (otherwise 'e' would match 'E') + */ + if (0 == strcmp(petscii[i], buf)) + match = i; /* match */ + } /* if */ + else { + if (0 == strcasecmp(petscii[i], buf)) + match = i; /* match */ + } /* else */ + } /* for */ + + /* Special condition: space (32) can be repeated, and + * is then called 'space', which is not in the petscii + * table + */ + + if (!match && 0 == strcasecmp("space", buf)) { + match = ' '; + } /* if */ + + /* Now check whether or not we got a match */ + if (match) { + /* We have found which PETSCII character was meant, + * now we check whether or not we wanted more than one + * character of this kind ("{char*n}") + */ + i = 1; /* one copy wanted */ + if ('*' == *input_p) { /* multiple copies wanted */ + input_p ++; /* adjust to point to counter */ + i = 0; + while (isdigit(*input_p)) { + i = i * 10 + (*input_p - '0'); /* count */ + input_p ++; + } /* while */ + + /* Check for error condition */ + if ('}' != *input_p || i == 0 || i > 255) { + rc = 1; + i = 0; + fprintf(stderr, "* Illegal character count at line " + "%u\n", + linenumber); + } /* if */ + } /* if */ + + if (i > 0) { + /* Copy the wanted number of characters */ + while (i) { + *(output_p ++) = match; + i --; + } /* while */ + + /* Input should now point to the } end delimeter, + * skip this + */ + input_p ++; + } /* if */ + } /* if */ + else { + rc = 1; + fprintf(stderr, "* Illegal special character: {%s} at " + "line %u\n", + buf, linenumber); + } /* else */ + } /* else */ + } /* if */ + else if (!quotemode) { /* check for token */ + match = FALSE; + + /* Skip tokenization attempt if: + * . No tokenization flag is set + * . Input string starts with numeral or space + * (no tokens starts with numerals or spaces) + */ + if (notokenize || ' ' == *input_p || isdigit(*input_p)) + goto skiptokenize; /* Looks better than nested if */ + + /* C64 BASIC */ + for (i = 0; i <= 75 && !match; i ++) { + tokenlen = strlen(c64tokens[i]); /* -=TODO:=- table lookup */ + if (inputleft >= tokenlen && + 0 == strncasecmp(input_p, c64tokens[i], tokenlen)) { + /* token match found */ + match = TRUE; + (*output_p ++) = i + 128; /* write token */ + input_p += tokenlen; /* skip token */ + inputleft -= tokenlen; + + if (15 == i || 3 == i) { /* REM & DATA */ + notokenize = TRUE; + } + } /* if */ + } /* for */ + + /* C128 BASIC 7.0/7.1 */ + if (!match && (Basic7 == mode || Basic71 == mode)) { + for (i = 2; i <= ((mode == Basic7) ? 38 : 55) && !match; + i ++) { + tokenlen = strlen(c128FEtokens[i]); /* as above */ + if (tokenlen && inputleft >= tokenlen && + 0 == strncasecmp(input_p, c128FEtokens[i], tokenlen)) { + /* token match found */ + match = TRUE; + (*output_p ++) = 0xFE; /* token escape */ + (*output_p ++) = i; /* write token */ + input_p += tokenlen; /* skip token */ + inputleft -= tokenlen; + } /* if */ + } /* for */ + + if (match) goto skipover; /* nicer than nested ifs */ + + for (i = 2; i <= 9 && !match; i ++) { + tokenlen = strlen(c128CEtokens[i]); /* as above */ + if (tokenlen && inputleft >= tokenlen && + 0 == strncasecmp(input_p, c128CEtokens[i], tokenlen)) { + /* token match found */ + match = TRUE; + (*output_p ++) = 0xCE; /* token escape */ + (*output_p ++) = i; /* write token */ + input_p += tokenlen; /* skip token */ + inputleft -= tokenlen; + } /* if */ + } /* for */ + + if (match) goto skipover; + + for (i = 0; i <= 49 && !match; i ++) { + if (0xCE == i) i ++; /* skip prefix 0xCE */ + tokenlen = strlen(c128tokens[i]); /* as above */ + if (tokenlen && inputleft >= tokenlen && + 0 == strncasecmp(input_p, c128tokens[i], tokenlen)) { + /* token match found */ + match = TRUE; + (*output_p ++) = i + 204; /* write token */ + input_p += tokenlen; /* skip token */ + inputleft -= tokenlen; + } /* if */ + } /* for */ + +skipover: + } /* if */ + + /* TFC3 */ + if (!match && TFC3 == mode) { + for (i = 0; i <= 28 && !match; i ++) { + tokenlen = strlen(tfc3tokens[i]); /* as above */ + if (inputleft >= tokenlen && + 0 == strncasecmp(input_p, tfc3tokens[i], tokenlen)) { + /* token match found */ + match = TRUE; + (*output_p ++) = i + 204; /* write token */ + input_p += tokenlen; /* skip token */ + inputleft -= tokenlen; + } /* if */ + } /* for */ + } /* if */ + + /* Graphics52 */ + if (!match && Graphics52 == mode) { + for (i = 0; i <= 50 && !match; i ++) { + tokenlen = strlen(graphics52tokens[i]); /* as above */ + if (inputleft >= tokenlen && + 0 == strncasecmp(input_p, graphics52tokens[i], + tokenlen)) { + /* token match found */ + match = TRUE; + (*output_p ++) = i + 204; /* write token */ + input_p += tokenlen; /* skip token */ + inputleft -= tokenlen; + } /* if */ + } /* for */ + } /* if */ + +skiptokenize: + + if (!match) { + /* there was no match on a token, just convert to + * petscii and write out. + * Since everything other than lowercase letters and + * special characters should have been catched here, + * everything should be written as such. + * Other characters are reported as errors. + */ + if ((*input_p >= 32 && *input_p <= 91) || + *input_p == 93) { + /* these need not to be converted + * (uppercase ASCII => lowercase PETSCII) + */ + *(output_p ++) = *input_p; + if ('\"' == *input_p) { + quotemode = !quotemode; /* invert quotemode */ + } /* if */ + input_p ++; + inputleft --; + } /* if */ + else if (*input_p >= 96 && *input_p <= 122) { + /* lowercase ASCII, convert to lowercase PETSCII) */ + *(output_p ++) = *input_p - 32; + input_p ++; + inputleft --; + } /* if */ + else { /* illegal character */ + fprintf(stderr, "* Illegal character in input (nonquoted): " + "%c (%hu) at line %u\n", + *input_p, (unsigned short) *input_p, linenumber); + input_p ++; + rc = 1; + } /* else */ + } /* if */ + } /* else */ + else if (quotemode) { /* non-special character quoted */ + /* Map special characters (32-64) on themselves + * uppercase ASCII (65-90) on uppercase PETSCII (193-228) + * lowercase ASCII (97-122) on lowercase PETSCII (65-90) + * other specials (91,93,94) on themselves + * other characters should not be there + */ + if ((*input_p >= 32 && *input_p <= 64) || + (91 == *input_p || 93 == *input_p || 94 == *input_p)) { + *(output_p ++) = *input_p; + if ('\"' == *input_p) { + quotemode = !quotemode; /* invert quotemode */ + } /* if */ + input_p ++; + inputleft --; + } /* if */ + else if (*input_p >= 65 && *input_p <= 90) { + *(output_p ++) = *input_p | 128; + input_p ++; + inputleft --; + } /* if */ + else if (*input_p >= 97 && *input_p <= 122) { + *(output_p ++) = *input_p & (~32); + input_p ++; + inputleft --; + } /* if */ + else { + /* Unknown character */ + fprintf(stderr, "* Illegal character in input (quoted): " + "%c (%hu) at line %u\n", + *input_p, (unsigned short) *input_p, linenumber); + input_p ++; + rc = 1; + } /* else */ + } /* else */ + } /* while */ + + *output_p = 0; /* end bytestream with a 0 */ + *length_p = output_p - start_p + 1; /* tokenized stream length */ + return rc; /* return errorcode */ +} \ No newline at end of file diff --git a/tokenize.h b/tokenize.h new file mode 100644 index 0000000..e797a83 --- /dev/null +++ b/tokenize.h @@ -0,0 +1,15 @@ +/* tokenize.h + */ + +#ifndef __TOKENIZE_H +#define __TOKENIZE_H + +/* BASIC mode selected */ +typedef enum basic_e { + Any, Basic2, Graphics52, TFC3, Basic7, Basic71 +} basic_t; + +int tokenize(const char *input_p, char *output_p, int *length_p, basic_t mode); +int detokenize(const char *input_p, char *output_p, basic_t mode, int strict); + +#endif \ No newline at end of file diff --git a/tokens.c b/tokens.c new file mode 100644 index 0000000..a05ed90 --- /dev/null +++ b/tokens.c @@ -0,0 +1,612 @@ +/* tokens.c + * contains a list of C64 BASIC and C128 BASIC tokens + * as well as a PETSCII table + */ + +#include "tokens.h" + +/* C64 BASIC + * offset: 128 + */ + +const char *c64tokens[] = { + /* instructions */ + "END", /* 128 */ /* 0x80 */ + "FOR", + "NEXT", /* 130 */ + "DATA", + "INPUT#", + "INPUT", + "DIM", + "READ", + "LET", + "GOTO", + "RUN", + "IF", + "RESTORE", /* 140 */ + "GOSUB", + "RETURN", + "REM", + "STOP", /* 0x90 */ + "ON", + "WAIT", + "LOAD", + "SAVE", + "VERIFY", + "DEF", /* 150 */ + "POKE", + "PRINT#", + "PRINT", + "CONT", + "LIST", + "CLR", + "CMD", + "SYS", + "OPEN", + "CLOSE", /* 160 */ /* 0xA0 */ + "GET", + "NEW", + "TAB(", + "TO", + "FN", + "SPC(", + "THEN", + "NOT", + "STEP", + + /* mathematical functions */ + "+", /* 170 */ /* 0xAA */ + "-", + "*", + "/", + "^", /* (arrow up) */ + "AND", + "OR", /* 0xB0 */ + ">", + "=", + "<", + + /* unary functions */ + "SGN", /* 180 */ /* 0xB4 */ + "INT", + "ABS", + "USR", + "FRE", + "POS", + "SQR", + "RND", + "LOG", + "EXP", + "COS", /* 190 */ + "SIN", + "TAN", /* 0xC0 */ + "ATN", + "PEEK", + "LEN", + "STR$", + "VAL", + "ASC", + "CHR$", + + /* functions with more than one parameter */ + "LEFT$", /* 200 */ /* 0xC8 */ + "RIGHT$", + "MID$", + + /* special */ + "GO" /*("GO TO")*/ /* 203 */ /* 0xCB */ +}; + +/* C64 Graphics52 BASIC extension (Software Unlimited) + * offset: 204 + */ + +const char *graphics52tokens[] = { + "SCREEN", /* 204 */ /* 0xCC */ + "SPRCL", + "PLOT", + "DRAW", + "CLEAR", /* 0xD0 */ + "TOGL", + "ERASE", /* 210 */ + "CHAR", + "SMOVE", + "COLOR", + "SPRITE", + "SPROG", + "CPROG", + "PEN", + "FLIP", + "TRANSFER", + "BLOCK", /* 220 */ + "BOTTOM", + "SDP", + "SCRSV", + "LOSCR", /* 0xE0 */ + "LOSPR", + "LOCHR", + "SPRSV", + "CHRSV", + "SMOOTH", + "VOLUME", /* 230 */ + "ADSR", + "SHIFT", + "PITCH", + "WAVE", + "PULSE", + "DETECT", + "PUT", + "MOVE", + "PLACE", /* 240 */ /* 0xF0 */ + "COPY", + "MEMSV", + "LOMEM", + "SWAP", + "BRD&BKG", + "SWITCH", + "UNLESS", + "MULTI", + "SHRINK", + "PADL(", /* 250 */ + "JOY(", + "BIT(", + "LOC(", + "POINT(" +}; + +/* C64 TFC3 BASIC extension (Riska BV) + * offset: 204 + */ + +const char *tfc3tokens[] = { + "OFF", /* 204 */ /* 0xCC */ + "AUTO", + "DEL", + "RENUM", + "HELP", /* 0xD0 */ + "FIND", + "OLD", /* 210 */ + "DLOAD", + "DVERIFY", + "DSAVE", + "APPEND", + "DAPPEND", + "DOS", + "KILL", + "MON", + "PDIR", + "PLIST", /* 220 */ + "BAR", + "DESKTOP", + "DUMP", + "ARRAY", /* 0xE0 */ + "MEM", + "TRACE", + "REPLACE", + "ORDER", + "PACK", + "UNPACK", /* 230 */ + "MREAD", + "MWRITE" +}; + +/* C128 BASIC + * offset: 204 + */ + +const char *c128tokens[] = { + "RGR", /* 204 */ /* 0xCC */ + "RCLR", + "", /* (prefix) */ + "JOY", + "RDOT", /* 0xD0 */ + "DEC", + "HEX$", /* 210 */ + "ERR$", + "INSTR", + "ELSE", + "RESUME", + "TRAP", + "TRON", + "TROFF", + "SOUND", + "VOL", + "AUTO", /* 220 */ + "PUDEF", + "GRAPHIC", + "PAINT", + "CHAR", /* 0xE0 */ + "BOX", + "CIRCLE", + "GSHAPE", + "SSHAPE", + "DRAW", + "LOCATE", /* 230 */ + "COLOR", + "SCNCLR", + "SCALE", + "HELP", + "DO", + "LOOP", + "EXIT", + "DIRECTORY", + "DSAVE", + "DLOAD", /* 240 */ /* 0xF0 */ + "HEADER", + "SCRATCH", + "COLLECT", + "COPY", + "RENAME", + "BACKUP", + "DELETE", + "RENUMBER", + "KEY", + "MONITOR", /* 250 */ + "USING", + "UNTIL", + "WHILE" /* 253 */ /* 0xFD */ +}; + +/* C128 BASIC + * CE prefix - sprite commands + */ + +const char *c128CEtokens[] = { + "", /* 0 */ /* 0x0 */ + "", + "POT", + "BUMP", + "PEN", + "RSPOS", + "RSPRITE", + "RSPCOLOR", + "XOR", + "RWINDOW" /* 9 */ /* 0x9 */ +}; + +/* C128 BASIC + * FE prefix + * includes Rick Simon's BASIC 7.1 + */ + +const char *c128FEtokens[] = { + "", /* 0 */ /* 0x0 */ + "", + "BANK", + "FILTER", + "PLAY", + "TEMPO", + "MOVSPR", + "SPRITE", + "SPRCOLOR", + "RREG", + "ENVELOPE", /* 10 */ + "SLEEP", + "CATALOG", + "DOPEN", + "APPEND", + "DCLOSE", + "BSAVE", /* 0x10 */ + "BLOAD", + "RECORD", + "CONCAT", + "DVERIFY", /* 20 */ + "DCLEAR", + "SPRSAV", + "COLLISION", + "BEGIN", + "BEND", + "WINDOW", + "BOOT", + "WIDTH", + "SPRDEF", + "QUIT", /* 30 */ + "STASH", + "", /* (space) */ /* 0x20 */ + "FETCH", + "", /* (quote) */ + "SWAP", + "OFF", + "FAST", + "SLOW", /* 38 */ /* 0x26 */ + /* Rick Simon's BASIC 7.1 extension */ + "CWIND", /* 39 */ /* 0x27 */ + "SSCRN", /* 40 */ + "LSCRN", + "HIDE", + "SHOW", + "SFONT", + "LFONT", + "VIEW", + "FCOPY", + "ESAVE", /* 0x30 */ + "SEND", + "CHECK", /* 50 */ + "ESC", + "OLD", + "FIND", + "DUMP", + "MERGE" /* 55 */ /* 0x37 */ +}; + +/* petscii conversion tables + * singlebyte => characters + * multibyte => escape sequences (written as {sequence} in the text format) + */ + +const char *petscii[] = { + "null", /* 0 */ /* 0x0 */ + "ct a", + "ct b", + "ct c", + "ct d", + "white", + "ct f", + "ct g", + "ct h", /* (disable charset switch (C64)) */ + "ct i", /* (enable charset switch (C64)) */ + "ct j", /* 10 */ + "ct k", + "ct l", + "return", + "ct n", + "ct o", + "ct p", /* 0x10 */ + "down", + "reverse on", + "home", + "delete", /* 20 */ + "ct u", + "ct v", + "ct w", + "ct x", + "ct y", + "ct z", + "027", /* (c128) */ + "red", + "right", + "green", /* 30 */ + "blue", + " ", /* (space) */ /* 0x20 */ + "!", + "\"", + "#", + "$", + "%", + "&", + "'", + "(", /* 40 */ + ")", + "*", + "+", + ",", + "-", + ".", + "/", + "0", /* 0x30 */ + "1", + "2", /* 50 */ + "3", + "4", + "5", + "6", + "7", + "8", + "9", + ":", + ";", + "<", /* 60 */ + "=", + ">", + "?", + "@", /* 0x40 */ + "a", + "b", + "c", + "d", + "e", + "f", /* 70 */ + "g", + "h", + "i", + "j", + "k", + "l", + "m", + "n", + "o", + "p", /* 80 */ /* 0x50 */ + "q", + "r", + "s", + "t", + "u", + "v", + "w", + "x", + "y", + "z", /* 90 */ + "[", + "pound", /* pound */ + "]", + "^", + "arrow left", /* <- */ + "096", /* 0x60 */ + "097", + "098", + "099", + "100", /* 100 */ + "101", + "102", + "103", + "104", + "105", + "106", + "107", + "108", + "109", + "110", /* 110 */ + "111", + "112", /* 0x70 */ + "113", + "114", + "115", + "116", + "117", + "118", + "119", + "120", /* 120 */ + "121", + "122", + "123", + "124", + "125", + "126", + "127", + "128", /* 0x80 */ + "orange", + "130", /* 130 */ + "131", + "132", + "f1", + "f3", + "f5", + "f7", + "f2", + "f4", + "f6", + "f8", /* 140 */ + "141", + "142", + "143", + "black", /* 0x90 */ + "up", + "reverse off", + "clear", + "148", /* insert */ + "brown", + "pink", /* 150 */ + "dark gray", + "gray", + "light green", + "light blue", + "light gray", + "156", /* run */ + "left", + "yellow", + "cyan", + "sh space", /* 160 */ /* 0xA0 */ + "cm k", + "cm i", + "cm t", + "cm @", + "cm g", + "cm +", + "cm m", + "cm pound", + "sh pound", + "cm n", /* 170 */ + "cm q", + "cm d", + "cm z", + "cm s", + "cm p", + "cm a", /* 0xB0 */ + "cm e", + "cm r", + "cm w", + "cm h", /* 180 */ + "cm j", + "cm l", + "cm y", + "cm u", + "cm d", + "sh @", + "cm f", + "cm c", + "cm x", + "cm v", /* 190 */ + "cm b", + "sh asterisk", /* 0xC0 */ + "A", + "B", + "C", + "D", + "E", + "F", + "G", + "H", /* 200 */ + "I", + "J", + "K", + "L", + "M", + "N", + "O", + "P", /* 0xD0 */ + "Q", + "R", /* 210 */ + "S", + "T", + "U", + "V", + "W", + "X", + "Y", + "Z", + "sh +", + "cm -", /* 220 */ + "sh -", + "222", + "cm asterisk", + "224", /* 0xE0 */ + "225", + "226", + "227", + "228", + "229", + "230", /* 230 */ + "231", + "232", + "233", + "234", + "235", + "236", + "237", + "238", + "239", + "240", /* 240 */ /* 0xF0 */ + "241", + "242", + "243", + "244", + "245", + "246", + "247", + "248", + "249", + "250", /* 250 */ + "251", + "252", + "253", + "254", + "pi", /* 255 */ /* 0xFF */ +}; + +/* tok64compatible + * - checks whether a token that is to be used is tok64 compatible or not + * (for strict mode) + * in: petscii - petscii character value + * out: TRUE / FALSE + */ +int nontok64compatible(int petscii) +{ + return ( (petscii >= 1 && petscii <= 4) + || (petscii >= 6 && petscii <= 16) + || (petscii >= 20 && petscii <= 26) + || (petscii >= 160 && petscii <= 192) + || 92 == petscii + || 95 == petscii + || (petscii >= 219 && petscii <= 221) + || 223 == petscii); +} \ No newline at end of file diff --git a/tokens.h b/tokens.h new file mode 100644 index 0000000..1bcba19 --- /dev/null +++ b/tokens.h @@ -0,0 +1,27 @@ +/* tokens.h + */ + +#ifndef __TOKENS_H +#define __TOKENS_H + +/* C64 BASIC 2.0 */ +extern const char *c64tokens[]; + +/* C64 Graphics52 BASIC extension (Software Unlimited) */ +extern const char *graphics52tokens[]; + +/* C64 TFC3 BASIC extension (Riska BV) */ +extern const char *tfc3tokens[]; + +/* C128 BASIC 7.0 + * includes Rick Simon's BASIC 7.1 extensions + */ +extern const char *c128tokens[]; +extern const char *c128CEtokens[]; +extern const char *c128FEtokens[]; + +/* PETSCII */ +extern const char *petscii[]; +int nontok64compatible(int petscii); + +#endif \ No newline at end of file diff --git a/version.h b/version.h new file mode 100644 index 0000000..ec1c3de --- /dev/null +++ b/version.h @@ -0,0 +1,9 @@ +/* version.h + */ + +#ifndef __VERSION_H +#define __VERSION_H + +#define PROGNAME "bastext 1.0" + +#endif \ No newline at end of file