From d195fd875557ef5b5af329fc32a7990234c43ee5 Mon Sep 17 00:00:00 2001 From: Andrew DeRosier Date: Thu, 13 Apr 2023 16:54:13 +0900 Subject: [PATCH] Try to be more toolchain agnostic --- .gitignore | 4 + .idea/.gitignore | 8 - .idea/cave-story-md.iml | 8 - .idea/codestream.xml | 6 - .idea/dictionaries/andy.xml | 3 - .idea/inspectionProfiles/Project_Default.xml | 12 - .idea/misc.xml | 38 - .idea/vcs.xml | 6 - .vscode/c_cpp_properties.json | 25 - Makefile | 24 +- README.md | 8 +- src/main.c | 12 - tools/mdtiler/LICENSE | 674 +++++ tools/mdtiler/batch.c | 1124 ++++++++ tools/mdtiler/batch.h | 30 + tools/mdtiler/bitmap.c | 253 ++ tools/mdtiler/bitmap.h | 44 + tools/mdtiler/main.c | 345 +++ tools/mdtiler/main.h | 46 + tools/mdtiler/map.c | 265 ++ tools/mdtiler/map.h | 34 + tools/mdtiler/offset.c | 89 + tools/mdtiler/offset.h | 37 + tools/mdtiler/palette.c | 166 ++ tools/mdtiler/palette.h | 42 + tools/mdtiler/sprite.c | 136 + tools/mdtiler/sprite.h | 36 + tools/mdtiler/tiles.c | 274 ++ tools/mdtiler/tiles.h | 54 + tools/sjasm/LICENSE.txt | 22 + tools/sjasm/direct.cpp | 756 ++++++ tools/sjasm/direct.h | 32 + tools/sjasm/loose.cpp | 58 + tools/sjasm/loose.h | 34 + tools/sjasm/parser.cpp | 497 ++++ tools/sjasm/parser.h | 35 + tools/sjasm/piz80.cpp | 2401 ++++++++++++++++++ tools/sjasm/piz80.h | 34 + tools/sjasm/reader.cpp | 474 ++++ tools/sjasm/reader.h | 62 + tools/sjasm/sjasm.cpp | 221 ++ tools/sjasm/sjasm.h | 127 + tools/sjasm/sjio.cpp | 581 +++++ tools/sjasm/sjio.h | 70 + tools/sjasm/tables.cpp | 816 ++++++ tools/sjasm/tables.h | 250 ++ tools/sjasm/targetver.h | 8 + tools/slz/LICENSE | 17 + tools/slz/compress.c | 216 ++ tools/slz/compress.h | 36 + tools/slz/decompress.c | 142 ++ tools/slz/decompress.h | 36 + tools/slz/main.c | 320 +++ tools/slz/main.h | 59 + tools/uftc/LICENSE | 17 + tools/uftc/compress.c | 226 ++ tools/uftc/compress.h | 36 + tools/uftc/decompress.c | 126 + tools/uftc/decompress.h | 36 + tools/uftc/main.c | 279 ++ tools/uftc/main.h | 58 + 61 files changed, 11759 insertions(+), 126 deletions(-) delete mode 100644 .idea/.gitignore delete mode 100644 .idea/cave-story-md.iml delete mode 100644 .idea/codestream.xml delete mode 100644 .idea/dictionaries/andy.xml delete mode 100644 .idea/inspectionProfiles/Project_Default.xml delete mode 100644 .idea/misc.xml delete mode 100644 .idea/vcs.xml delete mode 100644 .vscode/c_cpp_properties.json create mode 100644 tools/mdtiler/LICENSE create mode 100644 tools/mdtiler/batch.c create mode 100644 tools/mdtiler/batch.h create mode 100644 tools/mdtiler/bitmap.c create mode 100644 tools/mdtiler/bitmap.h create mode 100644 tools/mdtiler/main.c create mode 100644 tools/mdtiler/main.h create mode 100644 tools/mdtiler/map.c create mode 100644 tools/mdtiler/map.h create mode 100644 tools/mdtiler/offset.c create mode 100644 tools/mdtiler/offset.h create mode 100644 tools/mdtiler/palette.c create mode 100644 tools/mdtiler/palette.h create mode 100644 tools/mdtiler/sprite.c create mode 100644 tools/mdtiler/sprite.h create mode 100644 tools/mdtiler/tiles.c create mode 100644 tools/mdtiler/tiles.h create mode 100644 tools/sjasm/LICENSE.txt create mode 100644 tools/sjasm/direct.cpp create mode 100644 tools/sjasm/direct.h create mode 100644 tools/sjasm/loose.cpp create mode 100644 tools/sjasm/loose.h create mode 100644 tools/sjasm/parser.cpp create mode 100644 tools/sjasm/parser.h create mode 100644 tools/sjasm/piz80.cpp create mode 100644 tools/sjasm/piz80.h create mode 100644 tools/sjasm/reader.cpp create mode 100644 tools/sjasm/reader.h create mode 100644 tools/sjasm/sjasm.cpp create mode 100644 tools/sjasm/sjasm.h create mode 100644 tools/sjasm/sjio.cpp create mode 100644 tools/sjasm/sjio.h create mode 100644 tools/sjasm/tables.cpp create mode 100644 tools/sjasm/tables.h create mode 100644 tools/sjasm/targetver.h create mode 100644 tools/slz/LICENSE create mode 100644 tools/slz/compress.c create mode 100644 tools/slz/compress.h create mode 100644 tools/slz/decompress.c create mode 100644 tools/slz/decompress.h create mode 100644 tools/slz/main.c create mode 100644 tools/slz/main.h create mode 100644 tools/uftc/LICENSE create mode 100644 tools/uftc/compress.c create mode 100644 tools/uftc/compress.h create mode 100644 tools/uftc/decompress.c create mode 100644 tools/uftc/decompress.h create mode 100644 tools/uftc/main.c create mode 100644 tools/uftc/main.h diff --git a/.gitignore b/.gitignore index c57dd95d..0b26e922 100644 --- a/.gitignore +++ b/.gitignore @@ -1,11 +1,15 @@ doukutsu.bin doukutsu-*.bin temp.bin +bin/ *.lst *.cpxm +*.pal *.pat *.map *.tsb +*.spr +*.uftc *.pcm *.xgc *.patch diff --git a/.idea/.gitignore b/.idea/.gitignore deleted file mode 100644 index 13566b81..00000000 --- a/.idea/.gitignore +++ /dev/null @@ -1,8 +0,0 @@ -# Default ignored files -/shelf/ -/workspace.xml -# Editor-based HTTP Client requests -/httpRequests/ -# Datasource local storage ignored files -/dataSources/ -/dataSources.local.xml diff --git a/.idea/cave-story-md.iml b/.idea/cave-story-md.iml deleted file mode 100644 index 03036ae2..00000000 --- a/.idea/cave-story-md.iml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/.idea/codestream.xml b/.idea/codestream.xml deleted file mode 100644 index ee54622d..00000000 --- a/.idea/codestream.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - \ No newline at end of file diff --git a/.idea/dictionaries/andy.xml b/.idea/dictionaries/andy.xml deleted file mode 100644 index d13e7494..00000000 --- a/.idea/dictionaries/andy.xml +++ /dev/null @@ -1,3 +0,0 @@ - - - \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml deleted file mode 100644 index 51dea94a..00000000 --- a/.idea/inspectionProfiles/Project_Default.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml deleted file mode 100644 index fdc6ae56..00000000 --- a/.idea/misc.xml +++ /dev/null @@ -1,38 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml deleted file mode 100644 index 94a25f7f..00000000 --- a/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json deleted file mode 100644 index 5427e607..00000000 --- a/.vscode/c_cpp_properties.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "configurations": [ - { - "name": "MegaDrive", - "includePath": [ - "${workspaceRoot}/inc", - "${workspaceRoot}/res" - ], - "defines": [], - "intelliSenseMode": "clang-x64", - "browse": { - "path": [ - "${workspaceRoot}/inc", - "${workspaceRoot}/res", - "${workspaceRoot}/src/*" - ], - "limitSymbolsToIncludedHeaders": true, - "databaseFilename": "" - }, - "cStandard": "c11", - "cppStandard": "c++17" - } - ], - "version": 4 -} \ No newline at end of file diff --git a/Makefile b/Makefile index 9f397a59..62f81234 100644 --- a/Makefile +++ b/Makefile @@ -20,16 +20,16 @@ GCCVER := $(shell $(CC) -dumpversion) PLUGIN := $(MDROOT)/libexec/gcc/m68k-elf/$(GCCVER) # Z80 Assembler to build XGM driver -ASMZ80 := $(MDBIN)/sjasm +ASMZ80 := bin/sjasm # SGDK Tools BINTOS := bin/bintos RESCOMP := bin/rescomp WAVTORAW := bin/wavtoraw XGMTOOL := bin/xgmtool # Sik's Tools -MDTILER := $(MDBIN)/mdtiler -SLZ := $(MDBIN)/slz -UFTC := $(MDBIN)/uftc +MDTILER := bin/mdtiler +SLZ := bin/slz +UFTC := bin/uftc # Cave Story Tools AIGEN := python3 tools/aigen.py PATCHROM := bin/patchrom @@ -139,6 +139,7 @@ translate: $(TARGET)-es.bin $(TARGET)-fr.bin $(TARGET)-de.bin $(TARGET)-it.bin translate: $(TARGET)-pt.bin $(TARGET)-br.bin $(TARGET)-ja.bin $(TARGET)-zh.bin translate: $(TARGET)-ko.bin +prereq: $(ASMZ80) $(MDTILER) $(SLZ) $(UFTC) prereq: $(BINTOS) $(RESCOMP) $(XGMTOOL) $(WAVTORAW) $(TSCOMP) prereq: $(CPXMS) $(XGCS) $(PCMS) $(ZOBJ) $(TSBS) prereq: $(TSETO) $(CTSETP) $(CTSETO) $(SPRO) $(CSPRP) $(CSPRO) @@ -170,10 +171,13 @@ prereq: $(TSETO) $(CTSETP) $(CTSETO) $(SPRO) $(CSPRP) $(CSPRO) %.o80: %.s80 $(ASMZ80) $(Z80FLAGS) $< $@ z80_xgm.lst -# Old SGDK tools + bin: mkdir -p bin +$(ASMZ80): bin + c++ -w -DMAX_PATH=MAXPATHLEN tools/sjasm/*.cpp -o $(ASMZ80) + $(BINTOS): bin cc tools/bintos.c -o $@ @@ -186,7 +190,15 @@ $(XGMTOOL): bin $(WAVTORAW): bin cc tools/wavtoraw.c -o $@ -lm -# Cave Story tools +$(MDTILER): bin + cc tools/mdtiler/*.c -o $(MDTILER) -lpng + +$(SLZ): bin + cc tools/slz/*.c -o $(SLZ) + +$(UFTC): bin + cc tools/uftc/*.c -o $(UFTC) + $(TSCOMP): bin cc tools/tscomp/tscomp.c -o $@ diff --git a/README.md b/README.md index 4cdc91cf..b4868ce0 100644 --- a/README.md +++ b/README.md @@ -31,8 +31,12 @@ You can still get crushed, drown and fall out of bounds. Note that you can't load or save the game while cheating (including the counter). ## Compilation -1. Setup [marsdev](https://github.com/andwn/marsdev) and `python3` - - Marsdev targets: `m68k-toolchain z80-tools sik-tools` +1. Dependencies: `build-essential libpng-dev python3` and one of the following toolchains: + - [m68k-elf-toolchain](github.com/andwn/m68k-elf-toolchain) + - [Marsdev](github.com/andwn/marsdev) + - [SGDK](github.com/Stephane-D/SGDK) + - [Megadev](github.com/drojaazu/megadev) + - [Gendev](github.com/kubilus1/gendev) 2. Clone & `make` - For translations: `make translate` diff --git a/src/main.c b/src/main.c index be3e8970..85cf446c 100644 --- a/src/main.c +++ b/src/main.c @@ -23,22 +23,14 @@ uint8_t paused; uint8_t gameFrozen; void aftervsync() { - //disable_ints(); - //z80_pause_fast(); - vdp_fade_step_dma(); dma_flush(); dqueued = FALSE; if(ready) { if(inFade) vdp_sprites_clear(); - //if(gamemode == GM_GAME) stage_update(); // Scrolling vdp_sprites_update(); ready = FALSE; } - - //z80_resume(); - //enable_ints(); - vdp_fade_step_calc(); joy_update(); } @@ -54,9 +46,7 @@ void main() { if(system_checkdata() != SRAM_INVALID) { system_load_config(); } - //dma_clear(); joy_init(); - //enable_ints(); // Initialize time and speed tables (framerate adjusted) if(pal_mode) { time_tab = time_tab_pal; @@ -65,8 +55,6 @@ void main() { time_tab = time_tab_ntsc; speed_tab = speed_tab_ntsc; } - // let's the fun go on ! - sound_init(); // Error Tests diff --git a/tools/mdtiler/LICENSE b/tools/mdtiler/LICENSE new file mode 100644 index 00000000..94a9ed02 --- /dev/null +++ b/tools/mdtiler/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/tools/mdtiler/batch.c b/tools/mdtiler/batch.c new file mode 100644 index 00000000..951497b5 --- /dev/null +++ b/tools/mdtiler/batch.c @@ -0,0 +1,1124 @@ +//*************************************************************************** +// "batch.c" +// Batch file processing +//*************************************************************************** +// mdtiler - Bitmap to tile conversion tool +// Copyright 2011, 2012, 2016, 2017, 2018 Javier Degirolmo +// +// This file is part of mdtiler. +// +// mdtiler is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// mdtiler 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 mdtiler. If not, see . +//*************************************************************************** + +// Required headers +#include +#include +#include +#include +#include "main.h" +#include "bitmap.h" +#include "offset.h" +#include "map.h" +#include "palette.h" +#include "sprite.h" +#include "tiles.h" + +// Possible layout formats +typedef enum { + LAYOUT_TILEMAP, + LAYOUT_SPRITE +} Layout; + +// Structure for storing token information +typedef struct { + char **tokens; // Pointer to token list + unsigned num_tokens; // Number of tokens +} TokenList; + +// Function prototypes +static int read_line(FILE *, char **); +static int split_tokens(const char *, TokenList *); +static void free_tokens(TokenList *); +static void print_error_line(size_t, const char *); +static int is_integer(const char *); +static int is_color(const char *); +static unsigned string_to_integer(const char *); +static char *make_path(const char *, const char *); + +//*************************************************************************** +// build_batch +// Performs a batch build +//--------------------------------------------------------------------------- +// param infilename: name of batch file +// return: error code +//*************************************************************************** + +int build_batch(const char *infilename) { + // To store error codes + int errcode; + + // If there was any parsing error, this variable gets set + int failed = 0; + + // Open batch file + FILE *file = fopen(infilename, "r"); + if (file == NULL) + return ERR_OPENBATCH; + + // Current state + Bitmap *in = NULL; // Input bitmap + FILE *out[2] = { NULL, NULL }; // Output blobs + Layout layout = LAYOUT_TILEMAP; // Tile ordering + + // Default base directory + char *basedir = malloc(2); + if (basedir == NULL) { + fclose(file); + return ERR_NOMEMORY; + } + strcpy(basedir, "."); + + // Go through all lines + for (size_t curr_line = 1; !feof(file); curr_line++) { + // Get next line + char *line; + errcode = read_line(file, &line); + if (errcode) { + fclose(file); + return errcode; + } + + // Get a list of the arguments in this line + TokenList args; + errcode = split_tokens(line, &args); + if (errcode) { + // Syntax errors? + if (errcode == ERR_BADQUOTE) { + print_error_line(curr_line, infilename); + fputs("quote inside non-quoted token\n", stderr); + } else if (errcode == ERR_NOQUOTE) { + print_error_line(curr_line, infilename); + fputs("missing ending quote\n", stderr); + } + + // Nope, something more serious, give up + else { + free(line); + fclose(file); + free(basedir); + return errcode; + } + + // Can't continue with this line, move on... + failed = 1; + free(line); + continue; + } + + // Done with the line + free(line); + + // Get number of arguments in this line + size_t num_args = args.num_tokens; + + // Empty sentence? (ignore) + if (num_args == 0) + continue; + + // Retrieve what command is it + const char *command = args.tokens[0]; + + // Set input file? + if (!strcmp(command, "input")) { + // Check number of arguments + if (num_args != 2) { + // Determine error message + const char *msg = num_args == 1 ? + "input filename not specified\n" : + "too many parameters\n"; + + // Show message on screen + print_error_line(curr_line, infilename); + fputs(msg, stderr); + failed = 1; + } + + // Set input file if arguments are valid + else { + // Close old bitmap if needed + if (in != NULL) + destroy_bitmap(in); + + // Attempt to load input bitmap + char *filename = make_path(basedir, args.tokens[1]); + if (filename == NULL) { + errcode = ERR_NOMEMORY; + goto panic; + } + in = load_bitmap(filename); + + // Oops? + if (in == NULL) { + print_error_line(curr_line, infilename); + fprintf(stderr, "can't load input bitmap \"%s\"\n", filename); + failed = 1; + } + + free(filename); + } + } + + // Set main output files? + else if (!strcmp(command, "output") || !strcmp(command, "output2")) { + // Check number of arguments + if (num_args != 2) { + // Determine error message + const char *msg = num_args == 1 ? + "output filename not specified\n" : + "too many parameters\n"; + + // Show message on screen + print_error_line(curr_line, infilename); + fputs(msg, stderr); + failed = 1; + } + + // Set output file if arguments are valid + else { + // Determine which output file to mess with + // Yeah, this looks hackish XD but it's correct + int which = (command[6] == '2') ? 1 : 0; + + // Close old file if needed + if (out[which] != NULL) + fclose(out[which]); + + // Attempt to open output file + char *filename = make_path(basedir, args.tokens[1]); + if (filename == NULL) { + errcode = ERR_NOMEMORY; + goto panic; + } + out[which] = fopen(filename, "wb"); + + // Oops? + if (out[which] == NULL) { + print_error_line(curr_line, infilename); + fprintf(stderr, "can't open output bitmap \"%s\"\n", + filename); + failed = 1; + } + + free(filename); + } + } + + // Set palette? + else if (!strcmp(command, "palette")) { + // Check number of arguments + if (num_args != 17) { + // Determine error message + const char *msg; + if (num_args == 1) + msg = "no colors specified\n"; + else if (num_args > 17) + msg = "too many parameters\n"; + else if (num_args == 16) + msg = "1 color missing\n"; + else + msg = "%d colors missing\n"; + + // Show message on screen + print_error_line(curr_line, infilename); + fprintf(stderr, msg, 17 - num_args); + failed = 1; + } + + // Parse palette + else { + // Where we store the palette + uint16_t palette[0x10]; + + // Read entire palette + for (unsigned i = 0; i < 0x10; i++) { + // Get token for this entry + const char *arg = args.tokens[i + 1]; + + // Check that it's a valid color value + if (!is_color(arg)) { + print_error_line(curr_line, infilename); + fprintf(stderr, "\"%s\" is not a valid color\n", arg); + failed = 1; + } + + // Parse value + palette[i] = strtoul(arg, NULL, 0x10); + } + + // Set new palette + set_palette(palette); + } + } + + // Set tile ordering? + else if (!strcmp(command, "layout")) { + // Check number of arguments + if (num_args != 2) { + // Determine error message + const char *msg = num_args == 1 ? + "layout not specified\n" : + "too many parameters\n"; + + // Show message on screen + print_error_line(curr_line, infilename); + fputs(msg, stderr); + failed = 1; + } + + // Determine output layout + else { + // Get layout + const char *param = args.tokens[1]; + + // Check if it's a supported layout + if (!strcmp(param, "tilemap")) + layout = LAYOUT_TILEMAP; + else if (!strcmp(param, "sprite")) + layout = LAYOUT_SPRITE; + + // Nope + else { + print_error_line(curr_line, infilename); + fprintf(stderr, "unknown layout type \"%s\"\n", param); + failed = 1; + } + } + } + + // Set output format? + else if (!strcmp(command, "format")) { + // Check number of arguments + if (num_args != 2) { + // Determine error message + const char *msg = num_args == 1 ? + "format not specified\n" : + "too many parameters\n"; + + // Show message on screen + print_error_line(curr_line, infilename); + fputs(msg, stderr); + failed = 1; + } + + // Determine output format + else { + // Get format + const char *param = args.tokens[1]; + + // Check if it's a supported format + if (!strcmp(param, "4bpp")) + set_output_format(FORMAT_4BPP); + else if (!strcmp(param, "1bpp")) + set_output_format(FORMAT_1BPP); + + // Nope + else { + print_error_line(curr_line, infilename); + fprintf(stderr, "unknown format \"%s\"\n", param); + failed = 1; + } + } + } + + // Write tiles? + else if (!strcmp(command, "tiles")) { + // Check number of arguments + if (num_args != 5) { + // Determine error message + const char *msg; + switch (num_args) { + case 1: msg = "missing coordinates and dimensions\n"; break; + case 2: msg = "missing Y coordinate and dimensions\n"; break; + case 3: msg = "missing dimensions\n"; break; + case 4: msg = "missing height\n"; break; + default: msg = "too many parameters\n"; break; + } + + // Show message on screen + print_error_line(curr_line, infilename); + fputs(msg, stderr); + failed = 1; + } + + // Make sure there's a bitmap to read from... + else if (in == NULL) { + print_error_line(curr_line, infilename); + fputs("no input file to read from\n", stderr); + failed = 1; + } + + // Make sure there's a file to write into... + else if (out[0] == NULL) { + print_error_line(curr_line, infilename); + fputs("no output file to write into\n", stderr); + failed = 1; + } + + // Everything is seemingly OK, process command + else { + // Retrieve parameters + // To-do: check that they're indeed integers, but for now it + // isn't much of an issue because at worst atoi will return 0 + int x = string_to_integer(args.tokens[1]) << 3; + int y = string_to_integer(args.tokens[2]) << 3; + int width = string_to_integer(args.tokens[3]); + int height = string_to_integer(args.tokens[4]); + + // Process tiles and write them in the output file + switch (layout) { + // Tilemap ordering + case LAYOUT_TILEMAP: + errcode = write_tilemap(in, out[0], x, y, width, height); + break; + + // Sprite ordering + case LAYOUT_SPRITE: + errcode = write_sprite(in, out[0], x, y, width, height); + break; + } + + // Gah! + if (errcode) { + free_tokens(&args); + goto panic; + } + } + } + + // Generate map? + else if (!strcmp(command, "map")) { + // Check number of arguments + if (num_args != 5) { + // Determine error message + const char *msg; + switch (num_args) { + case 1: msg = "missing coordinates and dimensions\n"; break; + case 2: msg = "missing Y coordinate and dimensions\n"; break; + case 3: msg = "missing dimensions\n"; break; + case 4: msg = "missing height\n"; break; + default: msg = "too many parameters\n"; break; + } + + // Show message on screen + print_error_line(curr_line, infilename); + fputs(msg, stderr); + failed = 1; + } + + // Make sure there's a bitmap to read from... + else if (in == NULL) { + print_error_line(curr_line, infilename); + fputs("no input file to read from\n", stderr); + failed = 1; + } + + // Make sure there's a file to write tiles into... + else if (out[0] == NULL) { + print_error_line(curr_line, infilename); + fputs("no output file to write tiles\n", stderr); + failed = 1; + } + + // Make sure there's a file to write mappings into... + else if (out[1] == NULL) { + print_error_line(curr_line, infilename); + fputs("no output file to write tilemap mappings\n", stderr); + failed = 1; + } + + // Everything is seemingly OK, process command + else { + // Retrieve parameters + // To-do: check that they're indeed integers, but for now it + // isn't much of an issue because at worst atoi will return 0 + int x = string_to_integer(args.tokens[1]) << 3; + int y = string_to_integer(args.tokens[2]) << 3; + int width = string_to_integer(args.tokens[3]); + int height = string_to_integer(args.tokens[4]); + + // Generate map + errcode = generate_map(in, out[0], out[1], x, y, width, height, + layout == LAYOUT_SPRITE); + + // Gah! + if (errcode) { + free_tokens(&args); + goto panic; + } + } + } + + // Generate sprite mapping? + else if (!strcmp(command, "sprite")) { + // This command only makes sense with sprite ordering! + if (layout != LAYOUT_SPRITE) { + print_error_line(curr_line, infilename); + fputs("\"sprite\" command only works with sprite ordering\n", + stderr); + failed = 1; + } + + // End of sprite? + else if (num_args == 2 && strcmp(args.tokens[1], "end") == 0) { + errcode = generate_sprite_end(out[1]); + if (errcode) { + free_tokens(&args); + goto panic; + } + } + + // Check number of arguments then + // Should be identical to "tiles" + else if (num_args != 5) { + // Determine error message + const char *msg; + switch (num_args) { + case 1: msg = "missing coordinates and dimensions\n"; break; + case 2: msg = "missing Y coordinate and dimensions\n"; break; + case 3: msg = "missing dimensions\n"; break; + case 4: msg = "missing height\n"; break; + default: msg = "too many parameters\n"; break; + } + + // Show message on screen + print_error_line(curr_line, infilename); + fputs(msg, stderr); + failed = 1; + } + + // Make sure there's a bitmap to read from... + else if (in == NULL) { + print_error_line(curr_line, infilename); + fputs("no input file to read from\n", stderr); + failed = 1; + } + + // Make sure there's a file to write into... + else if (out[0] == NULL) { + print_error_line(curr_line, infilename); + fputs("no output file to write into\n", stderr); + failed = 1; + } + + // Make sure there's a file to write mappings into... + else if (out[1] == NULL) { + print_error_line(curr_line, infilename); + fputs("no output file to write sprite mappings\n", stderr); + failed = 1; + } + + // Everything is seemingly OK, process command + else { + // Retrieve parameters + // To-do: check that they're indeed integers, but for now it + // isn't much of an issue because at worst atoi will return 0 + int x = string_to_integer(args.tokens[1]) << 3; + int y = string_to_integer(args.tokens[2]) << 3; + int width = string_to_integer(args.tokens[3]); + int height = string_to_integer(args.tokens[4]); + + // Generate sprite mapping + errcode = generate_sprite(in, out[0], out[1], + x, y, width, height); + if (errcode) { + free_tokens(&args); + goto panic; + } + } + } + + // Set offset for map/sprite? + else if (!strcmp(command, "offset")) { + // Check number of arguments + if (num_args != 2) { + // Determine error message + const char *msg; + switch (num_args) { + case 1: msg = "missing offset\n"; break; + default: msg = "too many parameters\n"; break; + } + + // Show message on screen + print_error_line(curr_line, infilename); + fputs(msg, stderr); + failed = 1; + } + + // Check if the argument is to toggle continuous mode + int done = 0; + if (!failed) { + if (strcmp(args.tokens[1], "continuous") == 0) { + set_continuous_offset(1); + done = 1; + } else if (strcmp(args.tokens[1], "restarting") == 0) { + set_continuous_offset(0); + done = 1; + } + } + + // If not check that the offset is indeed an integer + if (!failed && !done) { + if (!is_integer(args.tokens[1])) { + print_error_line(curr_line, infilename); + fputs("offset must be an integer\n", stderr); + failed = 1; + } + } + + // All OK, set new offset + if (!failed && !done) { + set_map_offset(string_to_integer(args.tokens[1])); + } + } + + // Set origin for sprite mappings? + else if (!strcmp(command, "origin")) { + // Check number of arguments + if (num_args != 3) { + // Determine error message + const char *msg; + switch (num_args) { + case 1: msg = "missing coordinates\n"; break; + case 2: msg = "missing Y coordinate\n"; break; + default: msg = "too many parameters\n"; break; + } + + // Show message on screen + print_error_line(curr_line, infilename); + fputs(msg, stderr); + failed = 1; + } + + // Change sprite origin + if (!failed) { + int x = string_to_integer(args.tokens[1]); + int y = string_to_integer(args.tokens[2]); + set_sprite_origin(x, y); + } + } + + // Remap palettes? + else if (!strcmp(command, "remappal")) { + // group -> palette notation? + if (num_args == 4 && strcmp(args.tokens[2], "->") == 0) { + int group = string_to_integer(args.tokens[1]); + int palette = string_to_integer(args.tokens[3]); + + if (group < 0 || group > 15) { + print_error_line(curr_line, infilename); + fputs("color group must be between 0 and 15\n", stderr); + failed = 1; + } + if (palette < 0 || palette > 7) { + print_error_line(curr_line, infilename); + fputs("palette must be between 0 and 7\n", stderr); + failed = 1; + } + + remap_palette(group, palette); + } + + // Other notations not implemented yet + else { + print_error_line(curr_line, infilename); + fputs("invalid arguments\n", stderr); + failed = 1; + } + } + + // Dump bitmap palette? + else if (!strcmp(command, "dumppal")) { + // Check number of arguments + if (num_args != 2) { + // Determine error message + const char *msg = num_args == 1 ? + "palette filename not specified\n" : + "too many parameters\n"; + + // Show message on screen + print_error_line(curr_line, infilename); + fputs(msg, stderr); + failed = 1; + } + + else { + // Attempt to open output file + char *filename = make_path(basedir, args.tokens[1]); + if (filename == NULL) { + errcode = ERR_NOMEMORY; + goto panic; + } + + FILE *file = fopen(filename, "wb"); + if (file == NULL) { + print_error_line(curr_line, infilename); + fprintf(stderr, "can't open output palette \"%s\"\n", + filename); + failed = 1; + } + + // Try to write it + int success = 1; + if (!failed) { + success = dump_bitmap_palette(file); + fclose(file); + } + if (!success) { + print_error_line(curr_line, infilename); + fprintf(stderr, "can't write output palette \"%s\"\n", + filename); + failed = 1; + } + + free(filename); + } + } + + // Change base directory? + else if (!strcmp(command, "basedir")) { + if (num_args != 2) { + // Determine error message + const char *msg; + switch (num_args) { + case 1: msg = "missing directory\n"; break; + default: msg = "too many parameters\n"; break; + } + + // Show message on screen + print_error_line(curr_line, infilename); + fputs(msg, stderr); + failed = 1; + } + + // Change base directory + if (!failed) { + free(basedir); + basedir = malloc(strlen(args.tokens[1]) + 1); + if (basedir == NULL) { + errcode = ERR_NOMEMORY; + goto panic; + } + strcpy(basedir, args.tokens[1]); + } + } + + // Unknown command? + else { + print_error_line(curr_line, infilename); + fprintf(stderr, "unknown command \"%s\"\n", command); + failed = 1; + } + + // Get rid of arguments + free_tokens(&args); + } + + // Done with the resources + if (in) destroy_bitmap(in); + if (out[0]) fclose(out[0]); + if (out[1]) fclose(out[1]); + + // We're done + free(basedir); + fclose(file); + return failed ? ERR_PARSE : ERR_NONE; + + // If we get here then something went SERIOUSLY wrong +panic: + if (in) destroy_bitmap(in); + if (out[0]) fclose(out[0]); + if (out[1]) fclose(out[1]); + free(basedir); + fclose(file); + return errcode; +} + +//*************************************************************************** +// read_line +// Reads a line from a text file. The pointer to the buffer should be freed +// once it isn't needed anymore. +//--------------------------------------------------------------------------- +// param file: input file +// param where: where to store a pointer to the line +// return: error code +//*************************************************************************** + +#define BUFLINE_SIZE 0x80 +#define BUFLINE_STEP 0x20 + +static int read_line(FILE *file, char **where) { + // Set the pointer to null for now (if there's an error, this is what the + // caller will see) + *where = NULL; + + // Initial size of the buffer + size_t bufsize = BUFLINE_SIZE; + size_t bufpos = 0; + + // Allocate initial buffer for the line + // Allocate one extra char for the terminating null + char *buffer = (char *) malloc(bufsize+1); + if (buffer == NULL) + return ERR_NOMEMORY; + + // Keep reading until the end of the line + for (;;) { + // Get next character + int c = fgetc(file); + + // End of file? Error? + if (c == EOF) { + if (feof(file)) + break; + else { + free(buffer); + return ERR_CANTREAD; + } + } + + // End of line? (take backslash-newline combination into account) + if (c == '\n' || c == '\r') { + if (bufpos > 0 && buffer[bufpos-1] == '\\') + bufpos--; + else + break; + } + + // Null character? (skip!) + if (c == '\0') + continue; + + // Put character into the buffer + buffer[bufpos] = c; + bufpos++; + + // Do we need a larger buffer? + if (bufpos == bufsize) { + bufsize += BUFLINE_STEP; + char *temp = (char *) malloc(bufsize+1); + if (temp == NULL) { + free(buffer); + return ERR_NOMEMORY; + } else + buffer = temp; + } + } + + // Success! + buffer[bufpos] = '\0'; + *where = buffer; + return ERR_NONE; +} + +//*************************************************************************** +// split_tokens [internal] +// Parses a line and splits it into tokens (generating a token list). +//--------------------------------------------------------------------------- +// param str: line to split into tokens +// param list: where to store token information +// return: error code +//*************************************************************************** + +static int split_tokens(const char *str, TokenList *list) { + // Reset list + list->tokens = NULL; + list->num_tokens = 0; + + // Go through entire line + while (*str) { + // Skip whitespace + if (isspace(*str)) { + str++; + continue; + } + + // Comment? End of line? + if (*str == '#' || *str == '\0') + break; + + // Unquoted token? + if (*str != '\"') { + // Make room for a new token in the list + char **temp_list = (char **) realloc(list->tokens, + sizeof(char *) * (list->num_tokens + 1)); + if (temp_list == NULL) { + free_tokens(list); + return ERR_NOMEMORY; + } else + list->tokens = temp_list; + + // Token starts here + const char *start = str; + + // Try to find the end of the token + // Also make sure there aren't any quotes inside... + do { + str++; + if (*str == '\"') { + free_tokens(list); + return ERR_BADQUOTE; + } + } while (!(isspace(*str) || *str == '\0')); + + // Get length of the token + size_t length = str - start; + + // Allocate memory for this token + char *buffer = (char *) malloc(length + 1); + if (buffer == NULL) { + free_tokens(list); + return ERR_NOMEMORY; + } else { + memcpy(buffer, start, length); + buffer[length] = '\0'; + } + + // Put token inside the token list + list->tokens[list->num_tokens] = buffer; + list->num_tokens++; + } + + // Quoted token? + else { + // Make room for a new token in the list + char **temp_list = (char **) realloc(list->tokens, + sizeof(char *) * (list->num_tokens + 1)); + if (temp_list == NULL) { + free_tokens(list); + return ERR_NOMEMORY; + } else + list->tokens = temp_list; + + // Skip quote + str++; + + // Mark where the token starts + // Also we need to count this character by character, since escaped + // quotes take up two bytes in the string but they will be kept as + // a single one. + const char *start = str; + size_t length = 0; + + // Look for the ending quote + for (;;) { + // Uh oh, missing quote... + if (*str == '\0') { + free_tokens(list); + return ERR_NOQUOTE; + } + + // Quote? Check if it's an escaped quote or the end of the token + else if (*str == '\"') { + str++; + if (*str == '\"') { + length++; + str++; + } else + break; + } + + // Normal character, count it as usual + else { + length++; + str++; + } + } + + // Allocate memory for this token + char *buffer = (char *) malloc(length + 1); + if (buffer == NULL) { + free_tokens(list); + return ERR_NOMEMORY; + } + + // Copy token into buffer, taking into account escaped quotes (which + // will be stored as a single quote) + char *ptr = buffer; + for (size_t i = 0; i < length; i++) { + if (*start == '\"') { + *ptr++ = '\"'; + start += 2; + } else + *ptr++ = *start++; + } + *ptr = '\0'; + + // Put token inside the token list + list->tokens[list->num_tokens] = buffer; + list->num_tokens++; + } + } + + // Success! + return ERR_NONE; +} + +//*************************************************************************** +// free_tokens [internal] +// Deallocates the information in a token list. +//--------------------------------------------------------------------------- +// param list: pointer to tokens list +//*************************************************************************** + +static void free_tokens(TokenList *list) { + // Any tokens? + if (list->tokens != NULL) { + // Get rid of the tokens themselves + for (unsigned i = 0; i < list->num_tokens; i++) + free(list->tokens[i]); + + // Get rid of pointer list + free(list->tokens); + } + + // Reset list + list->tokens = NULL; + list->num_tokens = 0; +} + +//*************************************************************************** +// print_error_line +// Prints the header for syntax error messages +//--------------------------------------------------------------------------- +// param line: current line ID +// param filename: name of file with error (can be NULL) +//*************************************************************************** + +static void print_error_line(size_t line, const char *filename) { + if (filename == NULL) filename = ""; + + if (filename[0] == '\0') + fprintf(stderr, "Error[%zu]: ", line); + else + fprintf(stderr, "Error[%s:%zu]: ", filename, line); +} + +//*************************************************************************** +// is_integer +// Checks if a string is a valid integer value +//--------------------------------------------------------------------------- +// param str: string to check +// return: non-zero if valid, zero if not valid +//*************************************************************************** + +static int is_integer(const char *str) { + // Er... + if (str == NULL) + return 0; + if (*str == '\0') + return 0; + + // Determine which characters are allowed + const char *allowed; + if (*str == '$') { + allowed = "0123456789ABCDEFabcdef"; + str++; + } else { + allowed = "0123456789"; + } + + // Check that only allowed characters are inside + for (; *str != '\0'; str++) { + if (strchr(allowed, *str) == NULL) + return 0; + } + + // Valid integer! + return 1; +} + +//*************************************************************************** +// is_color +// Checks if a string is a valid Mega Drive color value +//--------------------------------------------------------------------------- +// param str: string to check +// return: non-zero if valid, zero if not valid +//*************************************************************************** + +static int is_color(const char *str) { + // Er... + if (str == NULL) + return 0; + if (*str == '\0') + return 0; + + // Allowed characters + static const char *valid = "02468ACEace"; + + // Check that it only consists of valid characters + for (unsigned i = 0; i < 3; i++) { + if (strchr(valid, *str) == NULL) + return 0; + } + + // Colors are always three characters long + if (str[3] != '\0') + return 0; + + // Valid color! + return 1; +} + +//*************************************************************************** +// string_to_integer +// Converts a string into an integer. Handles both decimal and hexadecimal. +//--------------------------------------------------------------------------- +// param str: string to convert +// return: integer value in string +//*************************************************************************** + +static unsigned string_to_integer(const char *str) { + // Hexadecimal? + if (*str == '$') { + str++; + return strtoul(str, NULL, 16); + } + + // Decimal? + else { + return strtoul(str, NULL, 10); + } +} + +//*************************************************************************** +// make_path +// Takes a base directory and a filename (which may itself contain +// subdirectories) and returns a string with them combined (literally +// "basedir/filename"). You must call free() once you're done with the +// string. +//--------------------------------------------------------------------------- +// param basedir: base directory +// param filename: filename +// return: pointer to string (NULL if failure) +//*************************************************************************** + +static char *make_path(const char *basedir, const char *filename) +{ + size_t len = strlen(basedir) + strlen(filename) + 2; + char *buffer = malloc(len); + if (buffer == NULL) return NULL; + + sprintf(buffer, "%s/%s", basedir, filename); + return buffer; +} diff --git a/tools/mdtiler/batch.h b/tools/mdtiler/batch.h new file mode 100644 index 00000000..3dc34dba --- /dev/null +++ b/tools/mdtiler/batch.h @@ -0,0 +1,30 @@ +//*************************************************************************** +// "batch.h" +// Header file for "batch.c" +//*************************************************************************** +// mdtiler - Bitmap to tile conversion tool +// Copyright 2011, 2012 Javier Degirolmo +// +// This file is part of mdtiler. +// +// mdtiler is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// mdtiler 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 mdtiler. If not, see . +//*************************************************************************** + +#ifndef BATCH_H +#define BATCH_H + +// Function prototypes +int build_batch(const char *); + +#endif diff --git a/tools/mdtiler/bitmap.c b/tools/mdtiler/bitmap.c new file mode 100644 index 00000000..73a672be --- /dev/null +++ b/tools/mdtiler/bitmap.c @@ -0,0 +1,253 @@ +//*************************************************************************** +// "bitmap.c" +// Manipulating bitmaps +//*************************************************************************** +// mdtiler - Bitmap to tile conversion tool +// Copyright 2011, 2012, 2018 Javier Degirolmo +// +// This file is part of mdtiler. +// +// mdtiler is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// mdtiler 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 mdtiler. If not, see . +//*************************************************************************** + +// Required headers +#include +#include +#include +#include +#include +#include +#include "bitmap.h" +#include "palette.h" + +// Prototype of callback function used by libpng +static void read_callback(png_structp, png_bytep, png_size_t); + +//*************************************************************************** +// load_bitmap +// Loads a bitmap from a PNG file. Returns a pointer to the bitmap object on +// success, or NULL in case of failure. +//--------------------------------------------------------------------------- +// param filename: name of file to load from +// return: pointer to bitmap or NULL on failure +//*************************************************************************** + +Bitmap *load_bitmap(const char *filename) { + // Open file + FILE *file = fopen(filename, "rb"); + if (file == NULL) + return NULL; + + // Create PNG reading structure + png_structp png_ptr = png_create_read_struct + (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + if (png_ptr == NULL) { + fclose(file); + return NULL; + } + + // Create PNG info structure + png_infop info_ptr = png_create_info_struct(png_ptr); + if (info_ptr == NULL) { + png_destroy_read_struct(&png_ptr, NULL, NULL); + fclose(file); + return NULL; + } + + // Set up setjmp stuff which libpng wants (libpng uses setjmp to emulate + // exception-like behavior in case of error) + if (setjmp(png_jmpbuf(png_ptr))) { + png_destroy_read_struct(&png_ptr, &info_ptr, NULL); + fclose(file); + return NULL; + } + + // Set up I/O functions + png_set_read_fn(png_ptr, file, read_callback); + + // Let's not impose stupid limits... (though it may not be a good idea to + // load such a big bitmap into RAM!) + png_set_user_limits(png_ptr, 0x7FFFFFFF, 0x7FFFFFFF); + + // Read bitmap into memory + png_read_png(png_ptr, info_ptr, PNG_TRANSFORM_STRIP_16 | + PNG_TRANSFORM_PACKING | PNG_TRANSFORM_SHIFT | + PNG_TRANSFORM_STRIP_ALPHA, NULL); + + // Get pointers to each row + png_bytepp rows = png_get_rows(png_ptr, info_ptr); + + // Get bitmap properties + int32_t width = png_get_image_width(png_ptr, info_ptr); + int32_t height = png_get_image_height(png_ptr, info_ptr); + int type = png_get_color_type(png_ptr, info_ptr); + + // Get palette, if any + if (type == PNG_COLOR_TYPE_PALETTE) { + // Read palette from PNG file + png_color *png_palette; + int num_colors; + png_get_PLTE(png_ptr, info_ptr, &png_palette, &num_colors); + + // Get the colors into a neat array + int max_colors = 16; + if (max_colors > num_colors) + max_colors = num_colors; + + uint16_t md_palette[16] = { 0 }; + for (int i = 0; i < max_colors; i++) { + uint8_t r = png_palette[i].red >> 5; + uint8_t g = png_palette[i].green >> 5; + uint8_t b = png_palette[i].blue >> 5; + md_palette[i] = b << 9 | g << 5 | r << 1; + } + + // Save the palette in case 'dumppal' gets used later + set_bitmap_palette(md_palette); + } + + // Bitmap wasn't palette, load whatever was used with the 'palette' command + // (i.e. the palette the bitmap was converted with) + else { + set_fallback_palette(); + } + + // Create structure to hold the bitmap object + Bitmap *ptr = (Bitmap *) malloc(sizeof(Bitmap)); + if (ptr == NULL) { + png_destroy_read_struct(&png_ptr, NULL, NULL); + fclose(file); + return NULL; + } + ptr->width = width; + ptr->height = height; + ptr->data = NULL; + ptr->rows = NULL; + + // Allocate enough memory to hold the pixel data + ptr->data = (uint8_t *) malloc(width * height); + if (ptr->data == NULL) { + png_destroy_read_struct(&png_ptr, NULL, NULL); + destroy_bitmap(ptr); + fclose(file); + return NULL; + } + + // Allocate enough memory to hold the rows + ptr->rows = (uint8_t **) malloc(sizeof(uint8_t *) * height); + if (ptr->rows == NULL) { + png_destroy_read_struct(&png_ptr, NULL, NULL); + destroy_bitmap(ptr); + fclose(file); + return NULL; + } + + // Fill up row table in the bitmap object + for (int i = 0; i < height; i++) + ptr->rows[i] = &ptr->data[i * width]; + + // Copy data into the bitmap object + uint8_t *dest = ptr->data; + switch (type) { + // Paletted + case PNG_COLOR_TYPE_PALETTE: + for (int y = 0; y < height; y++) { + const uint8_t *src = rows[y]; + for (int x = 0; x < width; x++) + *dest++ = *src++; + } + break; + + // Grayscale + case PNG_COLOR_TYPE_GRAY: + for (int y = 0; y < height; y++) { + const uint8_t *src = rows[y]; + for (int x = 0; x < width; x++) { + uint8_t val = *src++ >> 5; + *dest++ = pal_table[val << 6 | val << 3 | val]; + } + } + break; + + // True color + case PNG_COLOR_TYPE_RGB: + for (int y = 0; y < height; y++) { + const uint8_t *src = rows[y]; + for (int x = 0; x < width; x++) { + uint16_t r = (*src++ & 0xF8) >> 3; + uint16_t g = (*src++ & 0xF8) << 2; + uint16_t b = (*src++ & 0xF8) << 7; + *dest++ = pal_table[b|g|r]; + } + } + break; + } + + // Success! + png_destroy_read_struct(&png_ptr, &info_ptr, NULL); + fclose(file); + return ptr; +} + +//*************************************************************************** +// read_callback [internal] +// Callback function used by libpng to read from the PNG file. +//--------------------------------------------------------------------------- +// param png_ptr: pointer to PNG object +// param buffer: where to store the data +// param size: amount of bytes to read +//*************************************************************************** + +static void read_callback(png_structp png_ptr, png_bytep buffer, +png_size_t size) { + // Attempt to read data from the file + // In case of error let libpng know about it + if (fread(buffer, 1, size, (FILE *) png_get_io_ptr(png_ptr)) < size) + png_error(png_ptr, ""); +} + +//*************************************************************************** +// get_pixel +// Retrieves the value of a pixel in a bitmap. +//--------------------------------------------------------------------------- +// param ptr: pointer to bitmap +// param x: horizontal coordinate +// param y: vertical coordinate +// return: value of pixel +//*************************************************************************** + +uint8_t get_pixel(const Bitmap *ptr, int x, int y) { + // Outbounds? (return a fallback value) + if (x < 0 || y < 0 || x >= ptr->width || y >= ptr->height) + return 0x00; + + // Return the value of the pixel at this position + return ptr->rows[y][x]; +} + +//*************************************************************************** +// destroy_bitmap +// Destroys a bitmap object. +//--------------------------------------------------------------------------- +// param ptr: pointer to bitmap +//*************************************************************************** + +void destroy_bitmap(Bitmap *ptr) { + // Deallocate memory used by the bitmap + if (ptr->rows) + free(ptr->rows); + if (ptr->data) + free(ptr->data); + free(ptr); +} diff --git a/tools/mdtiler/bitmap.h b/tools/mdtiler/bitmap.h new file mode 100644 index 00000000..0851ba5a --- /dev/null +++ b/tools/mdtiler/bitmap.h @@ -0,0 +1,44 @@ +//*************************************************************************** +// "bitmap.h" +// Header file for "bitmap.c" +//*************************************************************************** +// mdtiler - Bitmap to tile conversion tool +// Copyright 2011, 2012 Javier Degirolmo +// +// This file is part of mdtiler. +// +// mdtiler is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// mdtiler 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 mdtiler. If not, see . +//*************************************************************************** + +#ifndef BITMAP_H +#define BITMAP_H + +// Required headers +#include + +// Definition of a bitmap +typedef struct { + int width; // Width in pixels + int height; // Height in pixels + uint8_t *data; // Bitmap data + uint8_t **rows; // Pointers to each row +} Bitmap; + +// Function prototypes +void set_palette(const uint16_t *); +Bitmap *load_bitmap(const char *); +uint8_t get_pixel(const Bitmap *, int, int); +void destroy_bitmap(Bitmap *); + +#endif diff --git a/tools/mdtiler/main.c b/tools/mdtiler/main.c new file mode 100644 index 00000000..07fc1355 --- /dev/null +++ b/tools/mdtiler/main.c @@ -0,0 +1,345 @@ +//*************************************************************************** +// "main.c" +// Program entry point, parses command line and runs stuff as required +//*************************************************************************** +// mdtiler - Bitmap to tile conversion tool +// Copyright 2011, 2012, 2016, 2017, 2018 Javier Degirolmo +// +// This file is part of mdtiler. +// +// mdtiler is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// mdtiler 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 mdtiler. If not, see . +//*************************************************************************** + +// Required headers +#include +#include +#include +#include "main.h" +#include "batch.h" +#include "bitmap.h" +#include "tiles.h" + +// Actions that may be performed +typedef enum { + ACTION_DEFAULT, // Default action + + ACTION_BATCH, // Batch build + ACTION_TILEMAP, // Quick build (tilemap ordering) + ACTION_SPRITE, // Quick build (sprite ordering) + + ACTION_TOOMANY // Too many actions specified +} Action; + +// Default palette when no palette is given +// Bonus points to whoever can tell where this palette is from +static const uint16_t default_pal[] = { + 0x000, 0xA00, 0x0A0, 0xAA0, 0x00A, 0xA0A, 0x06A, 0xAAA, + 0x666, 0xE66, 0x6E6, 0xEE6, 0x66E, 0xE6E, 0x6EE, 0xEEE +}; + +// Function prototypes +int build_tilemap(const char *, const char *); +int build_sprite(const char *, const char *); + +//*************************************************************************** +// Program entry point +//*************************************************************************** + +int main(int argc, char **argv) { + // Set default palette + set_palette(default_pal); + + // To know if there was an error or not + int errcode = 0; + + // Scan all arguments + int show_help = 0; + int show_ver = 0; + Action action = ACTION_DEFAULT; + Format format = FORMAT_DEFAULT; + const char *infilename = NULL; + const char *outfilename = NULL; + + int scan_ok = 1; + int err_manyfiles = 0; + + for (int curr_arg = 1; curr_arg < argc; curr_arg++) { + // Get pointer to argument, to make our lives easier + const char *arg = argv[curr_arg]; + + // If it's an option, parse it + if (scan_ok && arg[0] == '-') { + // Stop parsing options? + if (!strcmp(arg, "--")) + scan_ok = 0; + + // Show help or version? + else if (!strcmp(arg, "-h") || !strcmp(arg, "--help")) + show_help = 1; + else if (!strcmp(arg, "-v") || !strcmp(arg, "--version")) + show_ver = 1; + + // Batch build? + else if (!strcmp(arg, "-b") || !strcmp(arg, "--batch")) + action = action == ACTION_DEFAULT ? + ACTION_BATCH : ACTION_TOOMANY; + + // Quick build? + else if (!strcmp(arg, "-t") || !strcmp(arg, "--tilemap")) + action = action == ACTION_DEFAULT ? + ACTION_TILEMAP : ACTION_TOOMANY; + else if (!strcmp(arg, "-s") || !strcmp(arg, "--sprite")) + action = action == ACTION_DEFAULT ? + ACTION_SPRITE : ACTION_TOOMANY; + + // Format for quick build? + else if (!strcmp(arg, "-4") || !strcmp(arg, "--4bpp")) + format = format == FORMAT_DEFAULT ? + FORMAT_4BPP : FORMAT_TOOMANY; + else if (!strcmp(arg, "-1") || !strcmp(arg, "--1bpp")) + format = format == FORMAT_DEFAULT ? + FORMAT_1BPP : FORMAT_TOOMANY; + + // Unknown argument + else { + fprintf(stderr, "Error: unknown option \"%s\"\n", arg); + errcode = 1; + } + } + + // Input filename? + else if (infilename == NULL) + infilename = arg; + + // Output filename? + else if (outfilename == NULL) + outfilename = arg; + + // Too many files specified? + else + err_manyfiles = 1; + } + + // No action specified? + if (action == ACTION_DEFAULT) + action = ACTION_BATCH; + + // No format specified? + if (format == FORMAT_DEFAULT) + format = FORMAT_4BPP; + + // Batch building only takes an input file, so error out with a too many + // files error if an output file is specified + if (action == ACTION_BATCH && outfilename != NULL) + err_manyfiles = 1; + + // Look for error conditions + if (action == ACTION_TOOMANY) { + errcode = 1; + fprintf(stderr, "Error: can't specify more than one action\n"); + } else if (!show_help && !show_ver) { + if (infilename == NULL) { + errcode = 1; + fprintf(stderr, "Error: input filename missing\n"); + } else if (outfilename == NULL && action != ACTION_BATCH) { + errcode = 1; + fprintf(stderr, "Error: output filename missing\n"); + } else if (err_manyfiles) { + errcode = 1; + fprintf(stderr, "Error: too many filenames specified\n"); + } else if (format == FORMAT_TOOMANY) { + errcode = 1; + fprintf(stderr, "Error: can't specify more than one format\n"); + } + } + + // If there was an error then quit + if (errcode) + return EXIT_FAILURE; + + // Show tool version? + if (show_ver) { + puts("1.90"); + return EXIT_SUCCESS; + } + + // Show tool usage? + if (show_help) { + printf("Usage:\n" + " %s -b \n" + " %s -t \n" + " %s -s \n" + "\n" + "Options:\n" + " -b or --batch ..... Batch build (default)\n" + " -t or --tilemap ... Quick build, tilemap tile order\n" + " -s or --sprite .... Quick build, sprite tile order\n" + " -4 or --4bpp ...... Output 4bpp tiles (quick build)\n" + " -1 or --1bpp ...... Output 1bpp tiles (quick build)\n" + " -h or --help ...... Show this help\n" + " -v or --version ... Show tool version\n", + argv[0], argv[0], argv[0]); + return EXIT_SUCCESS; + } + + // Start the job! + switch (action) { + // Batch build + case ACTION_BATCH: + errcode = build_batch(infilename); + break; + + // Quick build (tilemap ordering) + case ACTION_TILEMAP: + set_output_format(format); + errcode = build_tilemap(infilename, outfilename); + break; + + // Quick build (sprite ordering) + case ACTION_SPRITE: + set_output_format(format); + errcode = build_sprite(infilename, outfilename); + break; + + // Oops?! + default: + errcode = ERR_UNKNOWN; + break; + } + + // If there was an error, show a message + if (errcode) { + // Determine message to show + const char *msg; + const char *errfile = NULL; + + switch(errcode) { + case ERR_OPENINPUT: + msg = "can't open input file"; + errfile = infilename; + break; + case ERR_OPENOUTPUT: + msg = "can't open output file"; + errfile = outfilename; + break; + case ERR_OPENBATCH: + msg = "can't open batch file"; + errfile = infilename; + break; + case ERR_CANTREAD: + msg = "can't read batch file"; + errfile = infilename; + break; + case ERR_CANTWRITE: + msg = "can't write to output file"; + errfile = outfilename; + break; + case ERR_CANTWRITEGFX: + msg = "can't write to tiles file"; + errfile = infilename; + break; + case ERR_CANTWRITEMAP: + msg = "can't write to tilemap mappings file"; + errfile = infilename; + break; + case ERR_CANTWRITESPR: + msg = "can't write to sprite mappings file"; + errfile = infilename; + break; + case ERR_MANYTILES: + msg = "too many unique tiles"; + errfile = infilename; + break; + case ERR_NOMEMORY: msg = "ran out of memory"; break; + case ERR_PARSE: + msg = "unable to process batch file"; + errfile = infilename; + break; + default: msg = "unknown error"; break; + } + + // Show message on screen + if (errfile != NULL) + fprintf(stderr, "Error[%s]: %s\n", errfile, msg); + else + fprintf(stderr, "Error: %s\n", msg); + } + + // Quit program + return errcode ? EXIT_FAILURE : EXIT_SUCCESS; +} + +//*************************************************************************** +// build_tilemap +// Does a quick build using tilemap arrangement +//--------------------------------------------------------------------------- +// param infilename: name of input file +// param outfilename: name of output file +// return: error code +//*************************************************************************** + +int build_tilemap(const char *infilename, const char *outfilename) { + // Load input bitmap + Bitmap *in = load_bitmap(infilename); + if (in == NULL) + return ERR_OPENINPUT; + + // Open output file + FILE *out = fopen(outfilename, "wb"); + if (out == NULL) { + destroy_bitmap(in); + return ERR_OPENOUTPUT; + } + + // Parse the bitmap as a massive tilemap + int errcode = write_tilemap(in, out, 0, 0, + in->width >> 3, in->height >> 3); + + // We're done, return whatever happened + fclose(out); + destroy_bitmap(in); + return errcode; +} + +//*************************************************************************** +// build_sprite +// Does a quick build using sprite arrangement +//--------------------------------------------------------------------------- +// param infilename: name of input file +// param outfilename: name of output file +// return: error code +//*************************************************************************** + +int build_sprite(const char *infilename, const char *outfilename) { + // Load input bitmap + Bitmap *in = load_bitmap(infilename); + if (in == NULL) + return ERR_OPENINPUT; + + // Open output file + FILE *out = fopen(outfilename, "wb"); + if (out == NULL) { + destroy_bitmap(in); + return ERR_OPENOUTPUT; + } + + // Parse the bitmap as a massive sprite + int errcode = write_sprite(in, out, 0, 0, + in->width >> 3, in->height >> 3); + + // We're done, return whatever happened + fclose(out); + destroy_bitmap(in); + return errcode; +} diff --git a/tools/mdtiler/main.h b/tools/mdtiler/main.h new file mode 100644 index 00000000..a31f7f6d --- /dev/null +++ b/tools/mdtiler/main.h @@ -0,0 +1,46 @@ +//*************************************************************************** +// "main.h" +// Some common definitions and such +//*************************************************************************** +// mdtiler - Bitmap to tile conversion tool +// Copyright 2011, 2012, 2018 Javier Degirolmo +// +// This file is part of mdtiler. +// +// mdtiler is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// mdtiler 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 mdtiler. If not, see . +//*************************************************************************** + +#ifndef MAIN_H +#define MAIN_H + +// Error codes +enum { + ERR_NONE, // No error + ERR_OPENINPUT, // Can't open input file + ERR_OPENOUTPUT, // Can't open output file + ERR_OPENBATCH, // Can't open batch file + ERR_CANTREAD, // Can't read from batch file + ERR_CANTWRITE, // Can't write to output file + ERR_CANTWRITEGFX, // Can't write to output file (tiles) + ERR_CANTWRITEMAP, // Can't write to output file (tilemap mappings) + ERR_CANTWRITESPR, // Can't write to output file (sprite mappings) + ERR_MANYTILES, // Too many unique tiles + ERR_NOMEMORY, // Ran out of memory + ERR_PARSE, // Parsing error + ERR_BADQUOTE, // Quote inside non-quoted token + ERR_NOQUOTE, // Missing ending quote + ERR_UNKNOWN // Unknown error +}; + +#endif diff --git a/tools/mdtiler/map.c b/tools/mdtiler/map.c new file mode 100644 index 00000000..8e5490be --- /dev/null +++ b/tools/mdtiler/map.c @@ -0,0 +1,265 @@ +//*************************************************************************** +// "map.c" +// Generates map data out of a bitmap +//*************************************************************************** +// mdtiler - Bitmap to tile conversion tool +// Copyright 2011, 2012, 2016, 2017, 2018 Javier Degirolmo +// +// This file is part of mdtiler. +// +// mdtiler is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// mdtiler 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 mdtiler. If not, see . +//*************************************************************************** + +// Required headers +#include +#include +#include +#include +#include "main.h" +#include "bitmap.h" +#include "offset.h" +#include "tiles.h" + +//*************************************************************************** +// generate_map +// Takes care of the "map" command in mdtiler. Generates the tilemap and the +// tiles out of it. +//--------------------------------------------------------------------------- +// param in: input bitmap +// param outgfx: output file where tiles are stored +// param outmap: output file where mappings are stored +// param x: base X coordinate (leftmost tile) +// param y: base Y coordinate (topmost tile) +// param width: width in tiles +// param height: height in tiles +// param order: zero for tilemap order, non-zero for sprite order +// return: error code +//*************************************************************************** + +int generate_map(const Bitmap *in, FILE *outgfx, FILE *outmap, +int x, int y, int width, int height, int order) { + // Um... + if (width <= 0 || height <= 0) + return ERR_PARSE; + + // To store the mappings + uint16_t *mappings = (uint16_t *) malloc(sizeof(uint16_t) * + width * height); + if (mappings == NULL) + return ERR_NOMEMORY; + + // Get current offset + uint16_t offset = get_map_offset(); + + // To store each tile + Tile *tiles = NULL; + uint16_t num_tiles = 0; + + // To store the tile we're just checking + Tile curr_tile; + + // Use a mask to filter out bits we don't care about depending on the + // format (this is used to ensure palette indices in 1bpp are treated + // as normally expected) + uint32_t mask = (get_output_format() == FORMAT_1BPP) ? + 0x11111111 : 0xFFFFFFFF; + + // Scan all tiles in the tilemap + int limit1 = order ? height : width; + int limit2 = order ? width : height; + + for (int pos2 = 0; pos2 < limit2; pos2++) + for (int pos1 = 0; pos1 < limit1; pos1++) { + // Retrieve tile + if (order) + get_tile(in, &curr_tile, x + (pos2 << 3), y + (pos1 << 3)); + else + get_tile(in, &curr_tile, x + (pos1 << 3), y + (pos2 << 3)); + + for (unsigned i = 0; i < 8; i++) { + curr_tile.normal[i] &= mask; + curr_tile.flipped[i] &= mask; + } + + // Where we store the ID of this tile + uint16_t this_id = num_tiles; + + // Compare against all other tiles + uint16_t match = 0; + for (; match < num_tiles; match++) { + // To make code more readable + Tile *other = &tiles[match]; + + // Is it this tile, non flipped? + if (curr_tile.normal[0] == other->normal[0] && + curr_tile.normal[1] == other->normal[1] && + curr_tile.normal[2] == other->normal[2] && + curr_tile.normal[3] == other->normal[3] && + curr_tile.normal[4] == other->normal[4] && + curr_tile.normal[5] == other->normal[5] && + curr_tile.normal[6] == other->normal[6] && + curr_tile.normal[7] == other->normal[7]) + { + this_id = match; + break; + } + + // Is it this tile, flipped horizontally? + if (curr_tile.normal[0] == other->flipped[0] && + curr_tile.normal[1] == other->flipped[1] && + curr_tile.normal[2] == other->flipped[2] && + curr_tile.normal[3] == other->flipped[3] && + curr_tile.normal[4] == other->flipped[4] && + curr_tile.normal[5] == other->flipped[5] && + curr_tile.normal[6] == other->flipped[6] && + curr_tile.normal[7] == other->flipped[7]) + { + this_id = match | 0x0800; + break; + } + + // Is it this tile, flipped vertically? + if (curr_tile.normal[0] == other->normal[7] && + curr_tile.normal[1] == other->normal[6] && + curr_tile.normal[2] == other->normal[5] && + curr_tile.normal[3] == other->normal[4] && + curr_tile.normal[4] == other->normal[3] && + curr_tile.normal[5] == other->normal[2] && + curr_tile.normal[6] == other->normal[1] && + curr_tile.normal[7] == other->normal[0]) + { + this_id = match | 0x1000; + break; + } + + // Is it this tile, flipped both ways? + if (curr_tile.normal[0] == other->flipped[7] && + curr_tile.normal[1] == other->flipped[6] && + curr_tile.normal[2] == other->flipped[5] && + curr_tile.normal[3] == other->flipped[4] && + curr_tile.normal[4] == other->flipped[3] && + curr_tile.normal[5] == other->flipped[2] && + curr_tile.normal[6] == other->flipped[1] && + curr_tile.normal[7] == other->flipped[0]) + { + this_id = match | 0x1800; + break; + } + } + + // Unique tile? + if (match == num_tiles) { + // Increment tile count + num_tiles++; + if (num_tiles > 0x0800) { + free(tiles); + free(mappings); + return ERR_MANYTILES; + } + + // Allocate memory for new tile + tiles = (Tile *) realloc(tiles, sizeof(Tile) * num_tiles); + if (tiles == NULL) { + free(tiles); + free(mappings); + return ERR_NOMEMORY; + } + + // Store tile in the list + memcpy(&tiles[num_tiles - 1], &curr_tile, sizeof(Tile)); + } + + // Add palette and priority + this_id |= curr_tile.flags << 13; + + // Write tile in the mappings + mappings[pos2 * limit1 + pos1] = this_id; + } + + // Write all the tiles + for (size_t i = 0; i < num_tiles; i++) { + for (unsigned row = 0; row < 8; row++) { + // 4bpp format? + if (get_output_format() == FORMAT_4BPP) { + // Split each row into bytes + // We need to do this due to endianess shenanigans :P + uint8_t buffer[4]; + buffer[0] = tiles[i].normal[row] >> 24; + buffer[1] = tiles[i].normal[row] >> 16; + buffer[2] = tiles[i].normal[row] >> 8; + buffer[3] = tiles[i].normal[row]; + + // Write row into file + if (fwrite(buffer, 1, 4, outgfx) < 4) { + free(tiles); + free(mappings); + return ERR_CANTWRITEGFX; + } + } + + // 1bpp format? + else { + // Each row is just one byte, but so far we've been storing the + // tiles as 4bpp to make our life easier, so we need to convert + // it to 1bpp first + uint8_t buffer = + (tiles[i].normal[row] & 0x10000000 ? 0x80 : 0x00) | + (tiles[i].normal[row] & 0x01000000 ? 0x40 : 0x00) | + (tiles[i].normal[row] & 0x00100000 ? 0x20 : 0x00) | + (tiles[i].normal[row] & 0x00010000 ? 0x10 : 0x00) | + (tiles[i].normal[row] & 0x00001000 ? 0x08 : 0x00) | + (tiles[i].normal[row] & 0x00000100 ? 0x04 : 0x00) | + (tiles[i].normal[row] & 0x00000010 ? 0x02 : 0x00) | + (tiles[i].normal[row] & 0x00000001 ? 0x01 : 0x00); + + // Write row into file + if (fwrite(&buffer, 1, 1, outgfx) < 1) { + free(tiles); + free(mappings); + return ERR_CANTWRITEGFX; + } + } + } + } + + // Write the mappings + size_t mapsize = width * height; + for (size_t i = 0; i < mapsize; i++) { + // Get ID of tile, offset included + uint16_t tile = mappings[i] + offset; + + // Split each word into two bytes + // We need to do this due to endianess shenanigans :P + uint8_t buffer[2] = { + tile >> 8, + tile + }; + + // Write word into file + if (fwrite(buffer, 1, 2, outmap) < 2) { + free(tiles); + free(mappings); + return ERR_CANTWRITEMAP; + } + } + + // If continuous then adjust the offset + if (is_continuous_offset()) + increment_offset(num_tiles); + + // Success! + free(tiles); + free(mappings); + return ERR_NONE; +} diff --git a/tools/mdtiler/map.h b/tools/mdtiler/map.h new file mode 100644 index 00000000..78036c5a --- /dev/null +++ b/tools/mdtiler/map.h @@ -0,0 +1,34 @@ +//*************************************************************************** +// "map.h" +// Header file for "map.c" +//*************************************************************************** +// mdtiler - Bitmap to tile conversion tool +// Copyright 2011, 2012, 2016, 2017, 2018 Javier Degirolmo +// +// This file is part of mdtiler. +// +// mdtiler is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// mdtiler 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 mdtiler. If not, see . +//*************************************************************************** + +#ifndef MAP_H +#define MAP_H + +// Required headers +#include +#include "bitmap.h" + +// Function prototypes +int generate_map(const Bitmap *, FILE *, FILE *, int, int, int, int, int); + +#endif diff --git a/tools/mdtiler/offset.c b/tools/mdtiler/offset.c new file mode 100644 index 00000000..4a2ef011 --- /dev/null +++ b/tools/mdtiler/offset.c @@ -0,0 +1,89 @@ +//*************************************************************************** +// "offset.c" +// Handles tile ID offsets for map and sprite commands +//*************************************************************************** +// mdtiler - Bitmap to tile conversion tool +// Copyright 2018 Javier Degirolmo +// +// This file is part of mdtiler. +// +// mdtiler is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// mdtiler 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 mdtiler. If not, see . +//*************************************************************************** + +// Required headers +#include +#include "offset.h" + +// Offset for the tiles in the map +static uint16_t offset = 0; + +// If set, the offset changes after each call to 'map' +// This allows to allocate tiles for multiple maps without knowing ahead of +// time how many tiles will be actually needed +static int continuous = 0; + +//*************************************************************************** +// get_map_offset +// Retrieves the current tile ID offset +//--------------------------------------------------------------------------- +// return: current tile ID offset +//*************************************************************************** + +uint16_t get_map_offset(void) { + return offset; +} + +//*************************************************************************** +// set_map_offset +// Sets the tile ID offset +//--------------------------------------------------------------------------- +// param new_offset: new tile ID offset +//*************************************************************************** + +void set_map_offset(uint16_t new_offset) { + offset = new_offset; +} + +//*************************************************************************** +// increment_offset +// Increments the tile ID offset +//--------------------------------------------------------------------------- +// param amount: how much to increment tile ID offset +//*************************************************************************** + +void increment_offset(uint16_t amount) { + offset += amount; +} + +//*************************************************************************** +// set_continuous_offset +// Sets whether offset should be continuous or not +//--------------------------------------------------------------------------- +// param enable: 1 for continuous, 0 otherwise +//*************************************************************************** + +void set_continuous_offset(int enable) { + continuous = !!enable; +} + +//*************************************************************************** +// is_continuous_offset +// Returns whether offset is continuous or not +//--------------------------------------------------------------------------- +// return: 1 if continuous, 0 if resets +//*************************************************************************** + +int is_continuous_offset(void) { + return continuous; +} diff --git a/tools/mdtiler/offset.h b/tools/mdtiler/offset.h new file mode 100644 index 00000000..24af799f --- /dev/null +++ b/tools/mdtiler/offset.h @@ -0,0 +1,37 @@ +//*************************************************************************** +// "offset.h" +// Header file for "offset.c" +//*************************************************************************** +// mdtiler - Bitmap to tile conversion tool +// Copyright 2018 Javier Degirolmo +// +// This file is part of mdtiler. +// +// mdtiler is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// mdtiler 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 mdtiler. If not, see . +//*************************************************************************** + +#ifndef OFFSET_H +#define OFFSET_H + +// Required headers +#include + +// Function prototypes +uint16_t get_map_offset(void); +void set_map_offset(uint16_t); +void increment_offset(uint16_t); +void set_continuous_offset(int); +int is_continuous_offset(void); + +#endif diff --git a/tools/mdtiler/palette.c b/tools/mdtiler/palette.c new file mode 100644 index 00000000..6ea671d5 --- /dev/null +++ b/tools/mdtiler/palette.c @@ -0,0 +1,166 @@ +//*************************************************************************** +// "palette.c" +// Handles palette remapping functionality +//*************************************************************************** +// mdtiler - Bitmap to tile conversion tool +// Copyright 2018 Javier Degirolmo +// +// This file is part of mdtiler. +// +// mdtiler is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// mdtiler 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 mdtiler. If not, see . +//*************************************************************************** + +#include +#include +#include +#include +#include +#include +#include "palette.h" + +// Look-up table used to convert true color bitmaps into paletted ones +// Contains which color to use for each BGR combination. +#define PALTABLE_SIZE (0x20*0x20*0x20) +uint8_t pal_table[PALTABLE_SIZE]; + +// Row-to-palette mappings +static unsigned mappings[0x10] = { + 0, 1, 2, 3, 4, 5, 6, 7, + 0, 1, 2, 3, 4, 5, 6, 7 +}; + +// Array to hold the bitmap's original palette +static uint16_t bitmap_pal[0x10] = { 0 }; + +// Array to hold the palette specified using the "palette" command +// This is used so we can dump it with "dumppal" if needed +static uint16_t fallback_pal[0x10] = { 0 }; + +//*************************************************************************** +// set_palette +// Sets the palette to use to convert true color bitmaps into paletted ones. +// This function takes an array of 16 colors, each color being a MD color +// (i.e. BXGXRX 3.1.3.1.3.1) +//--------------------------------------------------------------------------- +// param colors: pointer to palette (16 entries) +//*************************************************************************** + +void set_palette(const uint16_t *colors) { + // Keep a copy of the palette so we can use it with dumppal + memcpy(fallback_pal, colors, sizeof(fallback_pal)); + set_fallback_palette(); + + // Go through the entire palette + for (uint16_t i = 0; i < PALTABLE_SIZE; i++) { + // Get BGR components for the color to check + uint8_t sb = i >> 10 & 0x1F; + uint8_t sg = i >> 5 & 0x1F; + uint8_t sr = i & 0x1F; + + // Used to determine which is the best match + uint8_t best = 0; + unsigned min_diff = UINT_MAX; + + // Check it against every color in the palette + for (uint8_t i = 0; i < 0x10; i++) { + // Get BGR components for this palette entry + uint8_t db = colors[i] >> 9 & 0x07; + uint8_t dg = colors[i] >> 5 & 0x07; + uint8_t dr = colors[i] >> 1 & 0x07; + db = db << 2 | db >> 3; + dg = dg << 2 | dg >> 3; + dr = dr << 2 | dr >> 3; + + // Get how different are the colors + unsigned diff = abs(dr - sr) + abs(dg - sg) + abs(db - sb); + + // Is this color a better match? + if (diff <= min_diff) { + best = i; + min_diff = diff; + if (diff == 0) + break; + } + } + + // Store best match into the look-up table + pal_table[i] = best; + } +} + +//*************************************************************************** +// get_palette_mapping +// Gets the palette mapping corresponding to a given color group +//--------------------------------------------------------------------------- +// param group: 16-color group to get palette for +// return: palette ID +//*************************************************************************** + +unsigned get_palette_mapping(unsigned group) { + return mappings[group]; +} + +//*************************************************************************** +// remap_palette +// Remaps a color group to another palette +//--------------------------------------------------------------------------- +// param group: 16-color group to affect +// param which: new palette to remap to +//*************************************************************************** + +void remap_palette(unsigned group, unsigned which) { + mappings[group & 0x0F] = which & 0x07; +} + +//*************************************************************************** +// set_bitmap_palette +// Saves a copy of the bitmap's original palette +//--------------------------------------------------------------------------- +// param colors: pointer to palette (16 entries, Mega Drive format) +//*************************************************************************** + +void set_bitmap_palette(const uint16_t *colors) { + memcpy(bitmap_pal, colors, sizeof(bitmap_pal)); +} + +//*************************************************************************** +// set_fallback_palette +// Same as above but using whatever was loaded with the last "palette" command +//*************************************************************************** + +void set_fallback_palette(void) { + set_bitmap_palette(fallback_pal); +} + +//*************************************************************************** +// dump_bitmap_palette +// Dumps the bitmap's original palette into a file. +//--------------------------------------------------------------------------- +// param file: pointer to file handle +// return: non-zero on success, zero on failure +//*************************************************************************** + +int dump_bitmap_palette(FILE *file) { + // Reformat the bitmap into a way that doesn't rely on endianness + uint8_t blob[16*2]; + uint8_t *ptr = blob; + for (int i = 0; i < 16; i++) { + uint16_t value = bitmap_pal[i]; + *ptr++ = value >> 8; + *ptr++ = value; + } + + // Try to write it into the file + return fwrite(blob, 1, 32, file) == 32; +} diff --git a/tools/mdtiler/palette.h b/tools/mdtiler/palette.h new file mode 100644 index 00000000..b009c5ec --- /dev/null +++ b/tools/mdtiler/palette.h @@ -0,0 +1,42 @@ +//*************************************************************************** +// "palette.h" +// Header file for "palette.c" +//*************************************************************************** +// mdtiler - Bitmap to tile conversion tool +// Copyright 2018 Javier Degirolmo +// +// This file is part of mdtiler. +// +// mdtiler is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// mdtiler 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 mdtiler. If not, see . +//*************************************************************************** + +#ifndef PALETTE_H +#define PALETTE_H + +#include + +// Look-up table used to convert true color bitmaps into paletted ones +// Contains which color to use for each BGR combination. +#define PALTABLE_SIZE (0x20*0x20*0x20) +extern uint8_t pal_table[PALTABLE_SIZE]; + +// Function prototypes +void set_palette(const uint16_t *); +unsigned get_palette_mapping(unsigned); +void remap_palette(unsigned, unsigned); +void set_bitmap_palette(const uint16_t *); +void set_fallback_palette(void); +int dump_bitmap_palette(FILE *); + +#endif // PALETTE_H diff --git a/tools/mdtiler/sprite.c b/tools/mdtiler/sprite.c new file mode 100644 index 00000000..ba92629b --- /dev/null +++ b/tools/mdtiler/sprite.c @@ -0,0 +1,136 @@ +//*************************************************************************** +// "sprite.c" +// Generates sprite mappings and tiles out of a bitmap +//*************************************************************************** +// mdtiler - Bitmap to tile conversion tool +// Copyright 2018 Javier Degirolmo +// +// This file is part of mdtiler. +// +// mdtiler is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// mdtiler 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 mdtiler. If not, see . +//*************************************************************************** + +// Required headers +#include +#include +#include "main.h" +#include "offset.h" +#include "sprite.h" +#include "tiles.h" + +// Sprite tile ID offset within a mapping +static size_t sprite_offset = 0; + +// Center point of the sprite mapping +static int origin_x = 0; +static int origin_y = 0; + +//*************************************************************************** +// set_sprite_origin +// Takes care of the "origin" command in mdtiler. Changes the origin for +// sprite mappings. +//--------------------------------------------------------------------------- +// param x: new X coordinate for origin +// param y: new Y coordinate for origin +//*************************************************************************** + +void set_sprite_origin(int x, int y) +{ + origin_x = x; + origin_y = y; +} + +//*************************************************************************** +// generate_sprite +// Takes care of the "sprite" command in mdtiler. Generates the sprite +// mapping entry and the tiles out of it. +//--------------------------------------------------------------------------- +// param in: input bitmap +// param outgfx: output file where tiles are stored +// param outmap: output file where mappings are stored +// param x: base X coordinate (leftmost tile) +// param y: base Y coordinate (topmost tile) +// param width: width in tiles +// param height: height in tiles +// return: error code +//*************************************************************************** + +int generate_sprite(const Bitmap *in, FILE *outgfx, FILE *outmap, +int x, int y, int width, int height) +{ + // Um... + if (width < 1 || width > 4 || height < 1 || height > 4) + return ERR_PARSE; + + // Determine sprite coordinates + // Ugly hackish way because C doesn't guarantee anything about signed + // integers and compilers are known to be brutal about that... + int temp_x = x - origin_x; + int temp_y = y - origin_y; + if (temp_x < 0) temp_x = (temp_x + 0x10000) % 0xFFFF; + if (temp_y < 0) temp_y = (temp_y + 0x10000) % 0xFFFF; + uint16_t sprite_x = temp_x; + uint16_t sprite_y = temp_y; + + // Determine how many tiles the sprite takes up + // For now we always insert new sprites (no deduplication attempted) + size_t num_tiles = width * height; + + // Determine tile ID + uint16_t tile_id = sprite_offset + get_map_offset(); + sprite_offset += num_tiles; + + // Sprite size + uint8_t size = (width - 1) * 4 + (height - 1); + + // Write sprite mapping entry + uint8_t buffer[8] = { + sprite_x >> 8, sprite_x, // X offset + sprite_y >> 8, sprite_y, // Y offset + tile_id >> 8, tile_id, // tile ID + flags + 0, size // sprite size + }; + if (fwrite(buffer, 1, sizeof(buffer), outmap) != sizeof(buffer)) { + return ERR_CANTWRITESPR; + } + + // Write sprite tiles + return write_sprite(in, outgfx, x, y, width, height); +} + +//*************************************************************************** +// generate_sprite_end +// Takes care of the "sprite end" command in mdtiler. Generates the sentinel +// value indicating the end of a sprite mapping. +//--------------------------------------------------------------------------- +// param outmap: output file where mappings are stored +// return: error code +//*************************************************************************** + +int generate_sprite_end(FILE *outmap) +{ + // Write sentinel value + uint8_t buffer[2] = { 0x80, 0x00 }; + if (fwrite(buffer, 1, sizeof(buffer), outmap) != sizeof(buffer)) { + return ERR_CANTWRITESPR; + } + + // Reset mapping offset for next sprite + if (is_continuous_offset()) + increment_offset(sprite_offset); + sprite_offset = 0; + + // No error :) + return ERR_NONE; +} diff --git a/tools/mdtiler/sprite.h b/tools/mdtiler/sprite.h new file mode 100644 index 00000000..8e4abfd6 --- /dev/null +++ b/tools/mdtiler/sprite.h @@ -0,0 +1,36 @@ +//*************************************************************************** +// "sprite.h" +// Header file for "sprite.c" +//*************************************************************************** +// mdtiler - Bitmap to tile conversion tool +// Copyright 2018 Javier Degirolmo +// +// This file is part of mdtiler. +// +// mdtiler is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// mdtiler 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 mdtiler. If not, see . +//*************************************************************************** + +#ifndef SPRITE_H +#define SPRITE_H + +// Required headers +#include +#include "bitmap.h" + +// Function prototypes +void set_sprite_origin(int, int); +int generate_sprite(const Bitmap *, FILE *, FILE *, int, int, int, int); +int generate_sprite_end(FILE *); + +#endif diff --git a/tools/mdtiler/tiles.c b/tools/mdtiler/tiles.c new file mode 100644 index 00000000..d0a45f42 --- /dev/null +++ b/tools/mdtiler/tiles.c @@ -0,0 +1,274 @@ +//*************************************************************************** +// "tiles.c" +// Tile fetching stuff +//*************************************************************************** +// mdtiler - Bitmap to tile conversion tool +// Copyright 2011, 2012, 2015, 2018 Javier Degirolmo +// +// This file is part of mdtiler. +// +// mdtiler is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// mdtiler 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 mdtiler. If not, see . +//*************************************************************************** + +// Required headers +#include +#include +#include "main.h" +#include "bitmap.h" +#include "palette.h" +#include "tiles.h" + +// Prototype for functions used to fetch tiles +typedef int TileFunc(const Bitmap *, FILE *, int, int); + +// Current tile output format +static Format format = FORMAT_4BPP; + +//*************************************************************************** +// get_tile +// Retrieves the contents of a tile. Format is always 4bpp. +//--------------------------------------------------------------------------- +// param in: input bitmap +// param out: where to store data +// param xr: base X coordinate (leftmost pixel of tile) +// param yr: base Y coordinate (topmost pixel of tile) +//*************************************************************************** + +void get_tile(const Bitmap *in, Tile *out, int bx, int by) { + // To keep track of the flags (palette and priority) + uint8_t flags = 0; + + // Scan all rows + for (int y = 0; y < 8; y++) { + // To store the pixels as we fetch them + uint32_t normal = 0; // Left to right + uint32_t flipped = 0; // Right to left + + // Retrieve pixels of this row + for (int x = 0; x < 8; x++) { + uint8_t pixel = get_pixel(in, bx + x, by + y); + flags |= (pixel >> 4) & 0x0F; + pixel &= 0x0F; + normal = normal << 4 | pixel; + flipped = flipped >> 4 | pixel << 28; + } + + // Store rows in tile data + out->normal[y] = normal; + out->flipped[y] = flipped; + } + + // Store flags + out->flags = get_palette_mapping(flags); +} + +//*************************************************************************** +// get_output_format +// Retrieves what's the current output format for tiles +//--------------------------------------------------------------------------- +// return: current format +//*************************************************************************** + +Format get_output_format(void) { + return format; +} + +//*************************************************************************** +// set_output_format +// Changes the output format for tiles +//--------------------------------------------------------------------------- +// param value: new format +//*************************************************************************** + +void set_output_format(Format value) { + format = value; +} + +//*************************************************************************** +// write_tile_1bpp +// Takes a tile from the bitmap and outputs a 1bpp tile +//--------------------------------------------------------------------------- +// param in: input bitmap +// param out: output file +// param xr: base X coordinate (leftmost pixel of tile) +// param yr: base Y coordinate (topmost pixel of tile) +// return: error code +//*************************************************************************** + +static int write_tile_1bpp(const Bitmap *in, FILE *out, int bx, int by) +{ + // To store the tile data + uint8_t data[8]; + + // Read the tile from the bitmap to generate the 4bpp data + uint8_t *ptr = data; + for (int y = 0; y < 8; y++, ptr++) { + uint8_t temp = 0; + for (int x = 0; x < 8; x++) { + temp <<= 1; + temp |= get_pixel(in, bx + x, by + y) & 0x01; + } + *ptr = temp; + } + + // Write tile blob into output file + if (fwrite(data, 1, 8, out) < 8) + return ERR_CANTWRITE; + + // Success! + return ERR_NONE; +} + +//*************************************************************************** +// write_tile_4bpp +// Takes a tile from the bitmap and outputs a 4bpp tile +//--------------------------------------------------------------------------- +// param in: input bitmap +// param out: output file +// param xr: base X coordinate (leftmost pixel of tile) +// param yr: base Y coordinate (topmost pixel of tile) +// return: error code +//*************************************************************************** + +static int write_tile_4bpp(const Bitmap *in, FILE *out, int bx, int by) +{ + // To store the tile data + uint8_t data[32]; + + // Read the tile from the bitmap to generate the 4bpp data + uint8_t *ptr = data; + for (int y = 0; y < 8; y++) + for (int x = 0; x < 8; x += 2) { + *ptr++ = (get_pixel(in, bx + x, by + y) << 4) | + (get_pixel(in, bx + x + 1, by + y) & 0x0F); + } + + // Write tile blob into output file + if (fwrite(data, 1, 32, out) < 32) + return ERR_CANTWRITE; + + // Success! + return ERR_NONE; +} + +//*************************************************************************** +// write_tile_error +// Tile writing function to use if there was an error with the format +//--------------------------------------------------------------------------- +// param in: (ignored) +// param out: (ignored) +// param xr: (ignored) +// param yr: (ignored) +// return: ERR_UNKNOWN +//*************************************************************************** + +static int write_tile_error(const Bitmap *in, FILE *out, int bx, int by) +{ + // To shut up the compiler + (void) in; + (void) out; + (void) bx; + (void) by; + + // Don't do anything, just panic + return ERR_UNKNOWN; +} + +//*************************************************************************** +// get_write_func +// Returns which function to use to write tiles in the current format +//--------------------------------------------------------------------------- +// return: pointer to function +//*************************************************************************** + +static inline TileFunc *get_write_func(void) { + // Return the adequate function for this format + // write_tile_error is used if there's a bug in the program... + switch (format) { + case FORMAT_4BPP: return write_tile_4bpp; + case FORMAT_1BPP: return write_tile_1bpp; + default: return write_tile_error; + } +} + +//*************************************************************************** +// write_tilemap +// Outputs a block of tiles using tilemap ordering +//--------------------------------------------------------------------------- +// param in: input bitmap +// param out: output file +// param xr: base X coordinate (leftmost pixel of tile) +// param yr: base Y coordinate (topmost pixel of tile) +// param width: width in tiles +// param height: height in tiles +// return: error code +//*************************************************************************** + +int write_tilemap(const Bitmap *in, FILE *out, int bx, int by, +int width, int height) { + // Determine function we're going to use to fetch tiles + TileFunc *func = get_write_func(); + + // Traverse through all tiles in tilemap ordering + // (left-to-right, then top-to-bottom) + for (int y = 0; y < height; y++) + for (int x = 0; x < width; x++) { + int errcode = func(in, out, bx + (x << 3), by + (y << 3)); + if (errcode) return errcode; + } + + // Success! + return ERR_NONE; +} + +//*************************************************************************** +// write_sprite +// Outputs a block of tiles using sprite ordering +//--------------------------------------------------------------------------- +// param in: input bitmap +// param out: output file +// param xr: base X coordinate (rightmost pixel of tile) +// param yr: base Y coordinate (topmost pixel of tile) +// param width: width in tiles +// param height: height in tiles +// return: error code +//*************************************************************************** + +int write_sprite(const Bitmap *in, FILE *out, int bx, int by, +int width, int height) { + // Determine function we're going to use to fetch tiles + TileFunc *func = get_write_func(); + + // Sprites are at most 4 tiles high, so split sprites into strips that + // have at most that length + while (height > 0) { + // Determine height for this strip + int strip_height = height > 4 ? 4 : height; + + // Traverse through all tiles in sprite ordering + // (top-to-bottom, then left-to-right) + for (int x = 0; x < width; x++) + for (int y = 0; y < strip_height; y++) { + int errcode = func(in, out, bx + (x << 3), by + (y << 3)); + if (errcode) return errcode; + } + + // Move onto the next strip + height -= strip_height; + by += strip_height << 3; + } + + // Success! + return ERR_NONE; +} diff --git a/tools/mdtiler/tiles.h b/tools/mdtiler/tiles.h new file mode 100644 index 00000000..19db5da8 --- /dev/null +++ b/tools/mdtiler/tiles.h @@ -0,0 +1,54 @@ +//*************************************************************************** +// "tiles.h" +// Header file for "tiles.c" +//*************************************************************************** +// mdtiler - Bitmap to tile conversion tool +// Copyright 2011, 2012, 2015 Javier Degirolmo +// +// This file is part of mdtiler. +// +// mdtiler is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// mdtiler 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 mdtiler. If not, see . +//*************************************************************************** + +#ifndef TILES_H +#define TILES_H + +// Required headers +#include +#include +#include "bitmap.h" + +// Possible tile output formats +typedef enum { + FORMAT_DEFAULT, // Use default format + FORMAT_4BPP, // 4bpp tiles + FORMAT_1BPP, // 1bpp tiles + FORMAT_TOOMANY // Too many formats specified +} Format; + +// Structure to hold data for a tile +typedef struct { + uint32_t normal[8]; // Row data (not flipped) + uint32_t flipped[8]; // Row data (flipped horizontally) + uint8_t flags; // Palette and priority +} Tile; + +// Function prototypes +void get_tile(const Bitmap *, Tile *, int, int); +Format get_output_format(void); +void set_output_format(Format); +int write_tilemap(const Bitmap *, FILE *, int, int, int, int); +int write_sprite(const Bitmap *, FILE *, int, int, int, int); + +#endif diff --git a/tools/sjasm/LICENSE.txt b/tools/sjasm/LICENSE.txt new file mode 100644 index 00000000..b90365c1 --- /dev/null +++ b/tools/sjasm/LICENSE.txt @@ -0,0 +1,22 @@ +Sjasm Z80 Assembler + +Copyright (c) 2021 Konamiman +Based on Sjasm 0.39g6 - Copyright (c) 2006 Sjoerd Mastijn + +This software is provided 'as-is', without any express or implied warranty. +In no event will the authors be held liable for any damages arising from the +use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it freely, +subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not claim + that you wrote the original software. If you use this software in a product, + an acknowledgment in the product documentation would be appreciated but is + not required. + +2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + +3. This notice may not be removed or altered from any source distribution. \ No newline at end of file diff --git a/tools/sjasm/direct.cpp b/tools/sjasm/direct.cpp new file mode 100644 index 00000000..b8eaab19 --- /dev/null +++ b/tools/sjasm/direct.cpp @@ -0,0 +1,756 @@ +/* + + SjASM Z80 Assembler + + Copyright (c) 2015 Konamiman + Based on Sjasm 0.39g6 - Copyright (c) 2006 Sjoerd Mastijn + + This software is provided 'as-is', without any express or implied warranty. + In no event will the authors be held liable for any damages arising from the + use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it freely, + subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not claim + that you wrote the original software. If you use this software in a product, + an acknowledgment in the product documentation would be appreciated but is + not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source distribution. + +*/ + +// direct.cpp + +#include "sjasm.h" + +funtabcls dirtab; + +int ParseDirective() { + char *olp=lp; + char *n; + bp=lp; + if (!(n=getinstr(lp))) + if (*lp=='#' && *(lp+1)=='#') { + lp+=2; + aint val; + synerr=0; if (!ParseExpression(lp,val)) val=4; synerr=1; + mapadr+=((~mapadr+1)&(val-1)); + return 1; + } + else { lp=olp; return 0; } + if (dirtab.zoek(n)) { return 1; } + lp=olp; + return 0; +} + +#ifdef SECTIONS +void dirPOOL() { + sections osection=section; + section=POOL; + pooltab.emit(); + section=osection; +} + +void dirTEXT() { + section=TEXT; +} + +void dirDATA() { + section=DATA; +} +#endif + +void dirBYTE() { + int teller,e[129]; + teller=getBytes(lp,e,0,0); + if (!teller) { error(".byte with no arguments",0); return; } +#ifdef SECTIONS + switch (section) { + case TEXT: case POOL: EmitBytes(e); break; + case DATA: pooltab.add(bp); break; + default: error ("Unknown section",0,FATAL); break; + } +#else + EmitBytes(e); +#endif +} + +void dirDC() { + int teller,e[129]; + teller=getBytes(lp,e,0,1); + if (!teller) { error(".byte with no arguments",0); return; } +#ifdef SECTIONS + switch (section) { + case TEXT: case POOL: EmitBytes(e); break; + case DATA: pooltab.add(bp); break; + default: error ("Unknown section",0,FATAL); break; + } +#else + EmitBytes(e); +#endif +} + +void dirDZ() { + int teller,e[130]; + teller=getBytes(lp,e,0,0); + if (!teller) { error(".byte with no arguments",0); return; } + e[teller++]=0; e[teller]=-1; +#ifdef SECTIONS + switch (section) { + case TEXT: case POOL: EmitBytes(e); break; + case DATA: pooltab.add(bp); break; + default: error ("Unknown section",0,FATAL); break; + } +#else + EmitBytes(e); +#endif +} + +void dirABYTE() { + aint add; + int teller=0,e[129]; + if (ParseExpression(lp,add)) { + check8(add); add&=255; + teller=getBytes(lp,e,add,0); + if (!teller) { error(".abyte with no arguments",0); return; } +#ifdef SECTIONS + switch (section) { + case TEXT: case POOL: EmitBytes(e); break; + case DATA: pooltab.add(bp); break; + default: error ("Unknown section",0,FATAL); break; + } +#else + EmitBytes(e); +#endif + } else error("Expression expected",0); +} + +void dirABYTEC() { + aint add; + int teller=0,e[129]; + if (ParseExpression(lp,add)) { + check8(add); add&=255; + teller=getBytes(lp,e,add,1); + if (!teller) { error(".abyte with no arguments",0); return; } +#ifdef SECTIONS + switch (section) { + case TEXT: case POOL: EmitBytes(e); break; + case DATA: pooltab.add(bp); break; + default: error ("Unknown section",0,FATAL); break; + } +#else + EmitBytes(e); +#endif + } else error("Expression expected",0); +} + +void dirABYTEZ() { + aint add; + int teller=0,e[129]; + if (ParseExpression(lp,add)) { + check8(add); add&=255; + teller=getBytes(lp,e,add,0); + if (!teller) { error(".abyte with no arguments",0); return; } + e[teller++]=0; e[teller]=-1; +#ifdef SECTIONS + switch (section) { + case TEXT: case POOL: EmitBytes(e); break; + case DATA: pooltab.add(bp); break; + default: error ("Unknown section",0,FATAL); break; + } +#else + EmitBytes(e); +#endif + } else error("Expression expected",0); +} + +void dirWORD() { + aint val; + int teller=0,e[129]; + skipblanks(); + while (*lp) { + if (ParseExpression(lp,val)) { + check16(val); + if (teller>127) error("Over 128 values in .word",0,FATAL); + e[teller++]=val & 65535; + } else { error("Syntax error",lp,CATCHALL); return; } + skipblanks(); + if (*lp!=',') break; + ++lp; skipblanks(); + } + e[teller]=-1; + if (!teller) { error(".word with no arguments",0); return; } +#ifdef SECTIONS + switch (section) { + case TEXT: case POOL: EmitWords(e); break; + case DATA: pooltab.add(bp); break; + default: error ("Unknown section",0,FATAL); break; + } +#else + EmitWords(e); +#endif +} + +void dirDWORD() { + aint val; + int teller=0,e[129*2]; + skipblanks(); + while (*lp) { + if (ParseExpression(lp,val)) { + if (teller>127) error("Over 128 values in .dword",0,FATAL); + e[teller*2]=val & 65535; e[teller*2+1]=val >> 16; ++teller; + } else { error("Syntax error",lp,CATCHALL); return; } + skipblanks(); + if (*lp!=',') break; + ++lp; skipblanks(); + } + e[teller*2]=-1; + if (!teller) { error(".dword with no arguments",0); return; } +#ifdef SECTIONS + switch (section) { + case TEXT: case POOL: EmitWords(e); break; + case DATA: pooltab.add(bp); break; + default: error ("Unknown section",0,FATAL); break; + } +#else + EmitWords(e); +#endif +} + +void dirD24() { + aint val; + int teller=0,e[129*3]; + skipblanks(); + while (*lp) { + if (ParseExpression(lp,val)) { + check24(val); + if (teller>127) error("Over 128 values in .d24",0,FATAL); + e[teller*3]=val & 255; e[teller*3+1]=(val>>8)&255; e[teller*3+2]=(val>>16)&255; ++teller; + } else { error("Syntax error",lp,CATCHALL); return; } + skipblanks(); + if (*lp!=',') break; + ++lp; skipblanks(); + } + e[teller*3]=-1; + if (!teller) { error(".d24 with no arguments",0); return; } +#ifdef SECTIONS + switch (section) { + case TEXT: case POOL: EmitBytes(e); break; + case DATA: pooltab.add(bp); break; + default: error ("Unknown section",0,FATAL); break; + } +#else + EmitBytes(e); +#endif +} + +void dirBLOCK() { + aint teller,val=0; + if (ParseExpression(lp,teller)) { + if ((signed)teller<0) { error("block with negative size",0); teller=0; } + if (comma(lp)) ParseExpression(lp,val); +#ifdef SECTIONS + switch (section) { + case TEXT: case POOL: EmitBlock(val,teller); break; + case DATA: pooltab.add(bp); break; + default: error ("Unknown section",0,FATAL); break; + } +#else + EmitBlock(val,teller); +#endif + } else error("Syntax Error",lp,CATCHALL); +} + +void dirORG() { + aint val; +#ifdef SECTIONS + if (section!=TEXT) { error(".org only allowed in text sections",0); *lp=0; return; } +#endif + if (ParseExpression(lp,val)) adres=val; else error("Syntax error",0,CATCHALL); +} + +void dirMAP() { +#ifdef SECTIONS + if (section!=TEXT) { error(".map only allowed in text sections",0); *lp=0; return; } +#endif + maplstp=new adrlst(mapadr,maplstp); + aint val; + labelnotfound=0; + if (ParseExpression(lp,val)) mapadr=val; else error("Syntax error",0,CATCHALL); + if (labelnotfound) error("Forward reference",0,ALL); +} + +void dirENDMAP() { +#ifdef SECTIONS + if (section!=TEXT) { error(".endmap only allowed in text sections",0); *lp=0; return; } +#endif + if (maplstp) { mapadr=maplstp->val; maplstp=maplstp->next; } + else error(".endmodule without module",0); +} + + +void dirALIGN() { + aint val; + if (!ParseExpression(lp,val)) val=4; + switch (val) { + case 1: break; + case 2: case 4: case 8: case 16: case 32: case 64: case 128: case 256: + case 512: case 1024: case 2048: case 4096: case 8192: case 16384: case 32768: + val=(~adres+1)&(val-1); + EmitBlock(0,val); + break; + default: + error("Illegal align",0); break; + } +} + +void dirMODULE() { +#ifdef SECTIONS + if (section!=TEXT) { error(".module only allowed in text sections",0); *lp=0; return; } +#endif + char *n; + stringlst* old_modlstp; + + old_modlstp = modlstp; + modlstp=new stringlst(); + modlstp->string=modlabp; + modlstp->next=old_modlstp; + + skipblanks(lp); if (!*lp) { modlabp=0; return; } + if (n=getid(lp)) modlabp=n; else error("Syntax error",0,CATCHALL); +} + +void dirENDMODULE() { +#ifdef SECTIONS + if (section!=TEXT) { error(".endmodule only allowed in text sections",0); *lp=0; return; } +#endif + if (modlstp) { modlabp=modlstp->string; modlstp=modlstp->next; } + else error(".endmodule without module",0); +} + +void dirZ80() { +#ifdef SECTIONS + dirPOOL(); + section=TEXT; +#endif +#ifdef METARM + cpu=Z80; +#endif + piCPUp=piZ80; +} + +void dirARM() { +#ifdef METARM +#ifdef SECTIONS + dirPOOL(); + section=TEXT; +#endif + cpu=ARM; piCPUp=piARM; +#else + error("No ARM support in this version",0,FATAL); +#endif +} + +void dirTHUMB() { +#ifdef METARM +#ifdef SECTIONS + dirPOOL(); + section=TEXT; +#endif + cpu=THUMB; piCPUp=piTHUMB; +#else + error("No ARM support in this version",0,FATAL); +#endif +} + +void dirEND() { +#ifdef SECTIONS + dirPOOL(); +#endif + running=0; +} + +void dirSIZE() { + aint val; +#ifdef SECTIONS + if (section!=TEXT) error(".size is only allowed in text sections",0,FATAL); +#endif + if (!ParseExpression(lp,val)) { error("Syntax error",bp,CATCHALL); return; } + if (pass==2) return; + if (size!=(aint)-1) { error("Multiple sizes?",0); return; } + size=val; +} + +void dirINCBIN() { + aint val; + char *fnaam; + int offset=-1,length=-1; +#ifdef SECTIONS + if (section!=TEXT) error(".include only allowed in text sections",0,FATAL); +#endif + fnaam=getfilename(lp); + if (comma(lp)) { + if (!comma(lp)) { + if (!ParseExpression(lp,val)) { error("Syntax error",bp,CATCHALL); return; } + if (val<0) { error("Negative values are not allowed",bp); return; } + offset=val; + } + if (comma(lp)) { + if (!ParseExpression(lp,val)) { error("Syntax error",bp,CATCHALL); return; } + if (val<0) { error("Negative values are not allowed",bp); return; } + length=val; + } + } + BinIncFile(fnaam,offset,length); +} + +void dirTEXTAREA() { +#ifdef SECTIONS + if (section!=TEXT) { error(".textarea only allowed in text sections",0); *lp=0; return; } +#endif + aint oadres=adres,val; + labelnotfound=0; + if (!ParseExpression(lp,val)) { error("No adress given",0); return; } + if (labelnotfound) error("Forward reference",0,ALL); + ListFile(); + adres=val; if (ReadFile()!=ENDTEXTAREA) error("No end of textarea",0); +#ifdef SECTIONS + dirPOOL(); +#endif + adres=oadres+adres-val; +} + +void dirIFCOND(char* errorMessage) { + aint val; + labelnotfound=0; + if (!ParseExpression(lp,val)) { error("Syntax error",0,CATCHALL); return; } + if (labelnotfound) error("Forward reference",0,ALL); + if (val) { + ListFile(); + switch (ReadFile()) { + case ELSE: if (SkipFile()!=ENDIF) error(errorMessage,0); break; + case ENDIF: break; + default: error(errorMessage,0); break; + } + } + else { + ListFile(); + switch (SkipFile()) { + case ELSE: if (ReadFile()!=ENDIF) error(errorMessage,0); break; + case ENDIF: break; + default: error(errorMessage,0); break; + } + } + *lp=0; +} + +void dirCOND() { + dirIFCOND("No endc"); +} + +void dirIF() { + dirIFCOND("No endif"); +} + +void dirELSE() { + error("Else without if",0); +} + +void dirENDC() { + error("Endc without cond", 0); +} + +void dirENDIF() { + error("Endif without if",0); +} + +void dirENDTEXTAREA() { + error("Endt without textarea",0); +} + +void dirNOOP() { + while (*lp) lp++; +} + +void dirINCLUDE() { + char *fnaam; +#ifdef SECTIONS + if (section!=TEXT) error(".include only allowed in text sections",0,FATAL); +#endif + fnaam=getfilename(lp); + ListFile(); OpenFile(fnaam); donotlist=1; +} + +void dirOUTPUT() { + char *fnaam; +#ifdef SECTIONS + if (section!=TEXT) error(".output only allowed in text sections",0,FATAL); +#endif + fnaam=getfilename(lp); if (fnaam[0]=='<') fnaam++; + int mode=OUTPUT_TRUNCATE; + if(comma(lp)) + { + char modechar=(*lp) | 0x20; + lp++; + if(modechar=='t') mode=OUTPUT_TRUNCATE; + else if(modechar=='r') mode=OUTPUT_REWIND; + else if(modechar=='a') mode=OUTPUT_APPEND; + else error("Syntax error",bp,CATCHALL); + } + if (pass==2) NewDest(fnaam,mode); +} + +void dirDEFINE() { + char *id; + char *p=line; +#ifdef SECTIONS + if (section!=TEXT) { error(".define only allowed in text sections",0); *lp=0; return; } +#endif + while ('o') { + if (!*p) error("define error",0,FATAL); + if (*p=='.') { ++p; continue; } + if (*p=='d' || *p=='D') break; + ++p; + } + if (!cmphstr(p,"define")) error("define error",0,FATAL); + if (!(id=getid(p))) { error("illegal define",0); return; } + definetab.add(id,p); + while (*lp) ++lp; +} + +void dirIFDEF() { + char *p=line,*id; + while ('o') { + if (!*p) error("ifdef error",0,FATAL); + if (*p=='.') { ++p; continue; } + if (*p=='i' || *p=='I') break; + ++p; + } + if (!cmphstr(p,"ifdef")) error("ifdef error",0,FATAL); + Ending res; + if (!(id=getid(p))) { error("Illegal identifier",0,PASS1); return; } + if (definetab.bestaat(id)) { + ListFile(); + switch (res=ReadFile()) { + case ELSE: if (SkipFile()!=ENDIF) error("No endif",0); break; + case ENDIF: break; + default: error("No endif!",0); break; + } + } + else { + ListFile(); + switch (res=SkipFile()) { + case ELSE: if (ReadFile()!=ENDIF) error("No endif",0); break; + case ENDIF: break; + default: error("No endif!",0); break; + } + } + *lp=0; +} + +void dirIFNDEF() { + char *p=line,*id; + while ('o') { + if (!*p) error("ifndef error",0,FATAL); + if (*p=='.') { ++p; continue; } + if (*p=='i' || *p=='I') break; + ++p; + } + if (!cmphstr(p,"ifndef")) error("ifndef error",0,FATAL); + Ending res; + if (!(id=getid(p))) { error("Illegal identifier",0,PASS1); return; } + if (!definetab.bestaat(id)) { + ListFile(); + switch (res=ReadFile()) { + case ELSE: if (SkipFile()!=ENDIF) error("No endif",0); break; + case ENDIF: break; + default: error("No endif!",0); break; + } + } + else { + ListFile(); + switch (res=SkipFile()) { + case ELSE: if (ReadFile()!=ENDIF) error("No endif",0); break; + case ENDIF: break; + default: error("No endif!",0); break; + } + } + *lp=0; +} + +void dirEXPORT() { + aint val; + char *n,*p; + if (pass==1) return; + if (!(n=p=getid(lp))) { error("Syntax error",lp,CATCHALL); return; } + labelnotfound=0; + getLabelValue(n,val); if (labelnotfound) { error("Label not found",p,SUPPRES); return; } + WriteExp(p,val); +} + +void dirMACRO() { +#ifdef SECTIONS + if (section!=TEXT) error("macro definitions only allowed in text sections",0,FATAL); +#endif + + if (lijst) error("No macro definitions allowed here",0,FATAL); + char *n; + if (!(n=getid(lp))) { error("Illegal macroname",0,PASS1); return; } + macrotab.add(n,lp); +} + +void dirENDM() { + insideCompassStyleMacroDefinition = 0; + error("End macro without macro",0); +} + +void dirENDS() { + error("End structre without structure",0); +} + +void dirASSERT() { + char *p=lp; + aint val; + if (!ParseExpression(lp,val)) { error("Syntax error",0,CATCHALL); return; } + if (pass==2 && !val) error("Assertion failed",p); + *lp=0; +} + +void dirSTRUCT() { +#ifdef SECTIONS + if (section!=TEXT) error("structure definitions only allowed in text sections",0,FATAL); +#endif + structcls *st; + int global=0; + aint offset=0,bind=0; + char *naam; + skipblanks(); + if (*lp=='@') { ++lp; global=1; } + if (!(naam=getid(lp))) { error("Illegal structurename",0,PASS1); return; } + if (comma(lp)) { + labelnotfound=0; + if (!ParseExpression(lp,offset)) { error("Syntax error",0,CATCHALL); return; } + if (labelnotfound) error("Forward reference",0,ALL); + } + st=structtab.add(naam,offset,bind,global); + ListFile(); + while ('o') { + if (!ReadLine()) { error("Unexpected end of structure",0,PASS1); break; } + lp=line; if (white()) { skipblanks(lp); if (*lp=='.') ++lp; if (cmphstr(lp,"ends")) break; } + ParseStructLine(st); + ListFileSkip(line); + } + st->deflab(); +} + +void dirFORG() { + aint val; + int method=SEEK_SET; + skipblanks(lp); + if((*lp=='+') || (*lp=='-')) method=SEEK_CUR; + if (!ParseExpression(lp,val)) error("Syntax error",0,CATCHALL); + if (pass==2) SeekDest(val,method); +} + +/* +void dirBIND() { +} +*/ + +void dirREPT() { + aint val; + char *ml; + int olistmacro; + stringlst *s,*f; + labelnotfound=0; + if (!ParseExpression(lp,val)) { error("Syntax error",0,CATCHALL); return; } + if (labelnotfound) error("Forward reference",0,ALL); + if ((int)val<0) { error("Illegal repeat value",0,CATCHALL); return; } + ListFile(); + if (!ReadFileToStringLst(f,"endm")) error("Unexpected end of repeat",0,PASS1); + insideCompassStyleMacroDefinition = 0; + ListFile(); + olistmacro=listmacro; listmacro=1; ml=strdup(line); + while (val--) { s=f; while (s) { strcpy(line,s->string); s=s->next; ParseLine(); } } + strcpy(line,ml); listmacro=olistmacro; donotlist=1; +} + +void InsertDirectives() { + dirtab.insertd("assert",dirASSERT); + dirtab.insertd("byte",dirBYTE); + dirtab.insertd("abyte",dirABYTE); + dirtab.insertd("abytec",dirABYTEC); + dirtab.insertd("abytez",dirABYTEZ); + dirtab.insertd("word",dirWORD); + dirtab.insertd("block",dirBLOCK); + dirtab.insertd("dword",dirDWORD); + dirtab.insertd("d24",dirD24); + dirtab.insertd("org",dirORG); + dirtab.insertd("map",dirMAP); + dirtab.insertd("align",dirALIGN); + dirtab.insertd("module",dirMODULE); + dirtab.insertd("z80",dirZ80); + dirtab.insertd("arm",dirARM); + dirtab.insertd("thumb",dirTHUMB); + dirtab.insertd("size",dirSIZE); + dirtab.insertd("textarea",dirTEXTAREA); + dirtab.insertd("phase",dirTEXTAREA); + dirtab.insertd("msx",dirZ80); + dirtab.insertd("else",dirELSE); + dirtab.insertd("export",dirEXPORT); + dirtab.insertd("end",dirEND); + dirtab.insertd("include",dirINCLUDE); + dirtab.insertd("incbin",dirINCBIN); + dirtab.insertd("if",dirIF); + dirtab.insertd("output",dirOUTPUT); + dirtab.insertd("define",dirDEFINE); + dirtab.insertd("ifdef",dirIFDEF); + dirtab.insertd("ifndef",dirIFNDEF); + dirtab.insertd("macro",dirMACRO); + dirtab.insertd("struct",dirSTRUCT); + dirtab.insertd("dc",dirDC); + dirtab.insertd("dz",dirDZ); + dirtab.insertd("db",dirBYTE); + dirtab.insertd("dw",dirWORD); + dirtab.insertd("ds",dirBLOCK); + dirtab.insertd("dd",dirDWORD); + dirtab.insertd("dm",dirBYTE); + dirtab.insertd("defb",dirBYTE); + dirtab.insertd("defw",dirWORD); + dirtab.insertd("defs",dirBLOCK); + dirtab.insertd("defd",dirDWORD); + dirtab.insertd("defm",dirBYTE); + dirtab.insertd("endmod",dirENDMODULE); + dirtab.insertd("endmodule",dirENDMODULE); + dirtab.insertd("endmap",dirENDMAP); + dirtab.insertd("rept",dirREPT); + dirtab.insertd("fpos",dirFORG); +// dirtab.insertd("bind",dirBIND); + dirtab.insertd("endif",dirENDIF); + dirtab.insertd("endt",dirENDTEXTAREA); + dirtab.insertd("dephase",dirENDTEXTAREA); + dirtab.insertd("endm",dirENDM); + dirtab.insertd("ends",dirENDS); +#ifdef SECTIONS + dirtab.insertd("code",dirTEXT); + dirtab.insertd("data",dirDATA); + dirtab.insertd("text",dirTEXT); + dirtab.insertd("pool",dirPOOL); +#endif + + if (compassCompatibilityEnabled) { + dirtab.insertd("cond", dirCOND); + dirtab.insertd("endc", dirENDC); + dirtab.insertd(".label", dirNOOP); + dirtab.insertd(".upper", dirNOOP); + dirtab.insertd("tsrhooks", dirNOOP); + dirtab.insertd("breakp", dirNOOP); + } +} +//eof direct.cpp diff --git a/tools/sjasm/direct.h b/tools/sjasm/direct.h new file mode 100644 index 00000000..a99fcd12 --- /dev/null +++ b/tools/sjasm/direct.h @@ -0,0 +1,32 @@ +/* + + SjASM Z80 Assembler + + Copyright (c) 2015 Konamiman + Based on Sjasm 0.39g6 - Copyright (c) 2006 Sjoerd Mastijn + + This software is provided 'as-is', without any express or implied warranty. + In no event will the authors be held liable for any damages arising from the + use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it freely, + subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not claim + that you wrote the original software. If you use this software in a product, + an acknowledgment in the product documentation would be appreciated but is + not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source distribution. + +*/ + +// direct.h + +void InsertDirectives(); +//eof direct.h + diff --git a/tools/sjasm/loose.cpp b/tools/sjasm/loose.cpp new file mode 100644 index 00000000..cad9670a --- /dev/null +++ b/tools/sjasm/loose.cpp @@ -0,0 +1,58 @@ +/* + + SjASM Z80 Assembler + + Copyright (c) 2015 Konamiman + Based on Sjasm 0.39g6 - Copyright (c) 2006 Sjoerd Mastijn + + This software is provided 'as-is', without any express or implied warranty. + In no event will the authors be held liable for any damages arising from the + use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it freely, + subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not claim + that you wrote the original software. If you use this software in a product, + an acknowledgment in the product documentation would be appreciated but is + not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source distribution. + +*/ + +// loose.cpp + +#ifndef WIN32 + +#include "sjasm.h" + +void GetCurrentDirectory(int whatever, char *pad) { + pad[0]=0; +} + +int SearchPath(char*oudzp,char*filename,char*whatever,int maxlen,char*nieuwzp,char**ach) { + FILE *fp; + char *p,*f; + if (filename[0]=='/') strcpy(nieuwzp,filename); + else { + strcpy(nieuwzp,oudzp); + if (*nieuwzp && nieuwzp[strlen(nieuwzp)]!='/') strcat(nieuwzp,"/"); + strcat(nieuwzp,filename); + } + if (ach) { + p=f=nieuwzp; + while (*p) { if (*p=='/') f=p+1; ++p; } + *ach=f; + } + fp=fopen(nieuwzp,"r"); if (fp) fclose(fp); + return (long)fp; +} + +#endif + +//eof loose.cpp diff --git a/tools/sjasm/loose.h b/tools/sjasm/loose.h new file mode 100644 index 00000000..04d928d3 --- /dev/null +++ b/tools/sjasm/loose.h @@ -0,0 +1,34 @@ +/* + + SjASM Z80 Assembler + + Copyright (c) 2015 Konamiman + Based on Sjasm 0.39g6 - Copyright (c) 2006 Sjoerd Mastijn + + This software is provided 'as-is', without any express or implied warranty. + In no event will the authors be held liable for any damages arising from the + use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it freely, + subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not claim + that you wrote the original software. If you use this software in a product, + an acknowledgment in the product documentation would be appreciated but is + not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source distribution. + +*/ + +// loose.h + +#include +#define TCHAR char +void GetCurrentDirectory(int,char*); +int SearchPath(char*,char*,char*,int,char*,char**); +//eof loose.h diff --git a/tools/sjasm/parser.cpp b/tools/sjasm/parser.cpp new file mode 100644 index 00000000..d59095a9 --- /dev/null +++ b/tools/sjasm/parser.cpp @@ -0,0 +1,497 @@ +/* + + SjASM Z80 Assembler + + Copyright (c) 2015 Konamiman + Based on Sjasm 0.39g6 - Copyright (c) 2006 Sjoerd Mastijn + + This software is provided 'as-is', without any express or implied warranty. + In no event will the authors be held liable for any damages arising from the + use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it freely, + subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not claim + that you wrote the original software. If you use this software in a product, + an acknowledgment in the product documentation would be appreciated but is + not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source distribution. + +*/ + +// parser.cpp + +#include "sjasm.h" + +int replacedefineteller=0,comnxtlin; + +int ParseExpPrim(char *&p, aint &nval) { + int res=0; + skipblanks(p); + if (!*p) { return 0; } + if (*p=='(') { ++p; res=ParseExpression(p,nval); if (!need(p,')')) { error("')' expected",0); return 0; } } + else if (isdigit(*p) || (*p=='#' && isalnum(*(p+1))) || (*p=='$' && isalnum(*(p+1))) || *p=='%' || (*p == '&' && isalnum(*(p + 1)))) + { res=getConstant(p,nval); } + else if (isalpha(*p) || *p=='_' || *p=='.' || *p=='@') { res=getLabelValue(p,nval); } + else if (*p=='$') { ++p; nval=adres; return 1; } + else if (!(res=getCharConst(p,nval))) { if(synerr) error("Syntax error",p,CATCHALL); return 0; } + return res; +} + +int ParseExpUnair(char *&p, aint &nval) { + aint right; + int oper; + if ((oper=need(p,"! ~ + - ")) || (oper=needa(p,"not",'!',"low",'l',"high",'h'))) { + switch (oper) { + case '!': if(!ParseExpUnair(p,right)) return 0; nval=-!right; break; + case '~': if(!ParseExpUnair(p,right)) return 0; nval=~right; break; + case '+': if(!ParseExpUnair(p,right)) return 0; nval=right; break; + case '-': if(!ParseExpUnair(p,right)) return 0; nval=~right+1; break; + case 'l': if(!ParseExpUnair(p,right)) return 0; nval=right&255; break; + case 'h': if(!ParseExpUnair(p,right)) return 0; nval=(right>>8)&255; break; + default: error("Parser error",0); break; + } + return 1; + } else return ParseExpPrim(p,nval); +} + +int ParseExpMul(char *&p, aint &nval) { + aint left,right; + int oper; + if (!ParseExpUnair(p,left)) return 0; + while ((oper=need(p,"* / % ")) || (oper=needa(p,"mod",'%'))) { + if (!ParseExpUnair(p,right)) return 0; + switch (oper) { + case '*': left*=right; break; + case '/': if (right) left/=right; else { error("Division by zero",0); left=0; } break; + case '%': if (right) left%=right; else { error("Division by zero",0); left=0; } break; + default: error("Parser error",0); break; + } + } + nval=left; return 1; +} + +int ParseExpAdd(char *&p, aint &nval) { + aint left,right; + int oper; + if (!ParseExpMul(p,left)) return 0; + while (oper=need(p,"+ - ")) { + if (!ParseExpMul(p,right)) return 0; + switch (oper) { + case '+': left+=right; break; + case '-': left-=right; break; + default: error("Parser error",0); break; + } + } + nval=left; return 1; +} + +int ParseExpShift(char *&p, aint &nval) { + aint left,right; + unsigned long l; + int oper; + if (!ParseExpAdd(p,left)) return 0; + while ((oper=need(p,"<<>>")) || (oper=needa(p,"shl",'<'+'<',"shr",'>'))) { + if (oper=='>'+'>' && *p=='>') { ++p; oper='>'+'@'; } + if (!ParseExpAdd(p,right)) return 0; + switch (oper) { + case '<'+'<': left<<=right; break; + case '>': + case '>'+'>': left>>=right; break; + case '>'+'@': l=left; l>>=right; left=l; break; + default: error("Parser error",0); break; + } + } + nval=left; return 1; +} + +int ParseExpMinMax(char *&p, aint &nval) { + aint left,right; + int oper; + if (!ParseExpShift(p,left)) return 0; + while (oper=need(p,"?")) { + if (!ParseExpShift(p,right)) return 0; + switch (oper) { + case '<'+'?': left=left'+'?': left=left>right?left:right; break; + default: error("Parser error",0); break; + } + } + nval=left; return 1; +} + +int ParseExpCmp(char *&p, aint &nval) { + aint left,right; + int oper; + if (!ParseExpMinMax(p,left)) return 0; + while (oper=need(p,"<=>=< > ")) { + if (!ParseExpMinMax(p,right)) return 0; + switch (oper) { + case '<': left=-(left': left=-(left>right); break; + case '<'+'=': left=-(left<=right); break; + case '>'+'=': left=-(left>=right); break; + default: error("Parser error",0); break; + } + } + nval=left; return 1; +} + +int ParseExpEqu(char *&p, aint &nval) { + aint left,right; + int oper; + if (!ParseExpCmp(p,left)) return 0; + while (oper=need(p,"=_==!=")) { + if (!ParseExpCmp(p,right)) return 0; + switch (oper) { + case '=': + case '='+'=': left=-(left==right); break; + case '!'+'=': left=-(left!=right); break; + default: error("Parser error",0); break; + } + } + nval=left; return 1; +} + +int ParseExpBitAnd(char *&p, aint &nval) { + aint left,right; + if (!ParseExpEqu(p,left)) return 0; + while (need(p,"&_") || needa(p,"and",'&')) { + if (!ParseExpEqu(p,right)) return 0; + left&=right; + } + nval=left; return 1; +} + +int ParseExpBitXor(char *&p, aint &nval) { + aint left,right; + if (!ParseExpBitAnd(p,left)) return 0; + while (need(p,"^ ") || needa(p,"xor",'^')) { + if (!ParseExpBitAnd(p,right)) return 0; + left^=right; + } + nval=left; return 1; +} + +int ParseExpBitOr(char *&p, aint &nval) { + aint left,right; + if (!ParseExpBitXor(p,left)) return 0; + while (need(p,"|_") || needa(p,"or",'|')) { + if (!ParseExpBitXor(p,right)) return 0; + left|=right; + } + nval=left; return 1; +} + +int ParseExpLogAnd(char *&p, aint &nval) { + aint left,right; + if (!ParseExpBitOr(p,left)) return 0; + while (need(p,"&&")) { + if (!ParseExpBitOr(p,right)) return 0; + left=-(left&&right); + } + nval=left; return 1; +} + +int ParseExpLogOr(char *&p, aint &nval) { + aint left,right; + if (!ParseExpLogAnd(p,left)) return 0; + while (need(p,"||")) { + if (!ParseExpLogAnd(p,right)) return 0; + left=-(left||right); + } + nval=left; return 1; +} + +int ParseExpDot(char *&p, aint &nval) { + aint left,right; + if (!ParseExpLogOr(p,left)) return 0; + while (need(p,": ")) { + if (!ParseExpLogOr(p,right)) return 0; + left=left*256+right; + } + nval=left; return 1; +} + +int ParseExpression(char *&p, aint &nval) { + if (ParseExpDot(p,nval)) return 1; + nval=0; + return 0; +} + +char *ReplaceDefine(char*lp) { + int definegereplaced=0,dr; + char *nl=new char[LINEMAX*2]; + char *rp=nl,*nid,*ver,a; + if (++replacedefineteller>20) error("Over 20 defines nested",0,FATAL); + while ('o') { + if (comlin || comnxtlin) + if (*lp=='*' && *(lp+1)=='/') { + *rp=' '; ++rp; + lp+=2; if (comnxtlin) --comnxtlin; else --comlin; continue; + } + + if (*lp==';' && !comlin && !comnxtlin) { *rp=0; return nl; } + if (*lp=='/' && *(lp+1)=='/' && !comlin && !comnxtlin) { *rp=0; return nl; } + if (*lp=='/' && *(lp+1)=='*') { lp+=2; ++comnxtlin; continue; } + + if (*lp=='"' || ((!compassCompatibilityEnabled) && *lp == '\'')) { + a=*lp; if (!comlin && !comnxtlin) { *rp=*lp; ++rp; } ++lp; + if (a!='\'' || (*(lp-2)!='f' || *(lp-3)!='a') && (*(lp-2)!='F' && *(lp-3)!='A')) + while ('o') { + if (!*lp) { *rp=0; return nl; } + if (!comlin && !comnxtlin) *rp=*lp; + if (*lp==a) { if (!comlin && !comnxtlin) ++rp; ++lp; break; } + if (*lp=='\\' && !compassCompatibilityEnabled) { ++lp; if (!comlin && !comnxtlin) { ++rp; *rp=*lp; } } + if (!comlin && !comnxtlin) ++rp; ++lp; + } + continue; + } + + if (comlin || comnxtlin) { if (!*lp) { *rp=0; break; } ++lp; continue; } + if (!isalpha(*lp) && *lp!='_') { if (!(*rp=*lp)) break; ++rp; ++lp; continue; } + nid=getid(lp); dr=1; + if (!(ver=definetab.getverv(nid))) if (!macrolabp || !(ver=macdeftab.getverv(nid))) { dr=0; ver=nid; } + if (dr) definegereplaced=1; + while (*rp=*ver) { ++rp; ++ver; } + } + if (strlen(nl)>LINEMAX-1) error("line too long after macro expansion",0,FATAL); + if (definegereplaced) return ReplaceDefine(nl); + return nl; +} + +void ParseLabel() { + char *tp,temp[LINEMAX],*ttp; + aint val,oval; + if (white()) return; + tp=temp; + while (*lp && !white() && *lp!=':' && *lp!='=') { *tp=*lp; ++tp; ++lp; } + *tp=0; if (*lp==':') ++lp; + tp=temp; skipblanks(); + labelnotfound=0; +#ifdef SECTIONS + switch (section) { + case TEXT: +#endif + if (isdigit(*tp)) { + if (needequ() || needfield()) { + error("Numberlabels only allowed as adresslabels",0); +#ifdef SECTIONS + break; +#else + return; +#endif + } + val=atoi(tp); if (pass==1) loklabtab.insert(val,adres); + } else { + if (needequ()) { + if (!ParseExpression(lp,val)) { error("Expression error",lp); val=0; } + if (labelnotfound) { error("Forward reference",0,PASS1); } + } else if (needfield()) { + aint nv; + val=mapadr; + synerr=0; if (ParseExpression(lp,nv)) mapadr+=nv; synerr=1; + if (labelnotfound) error("Forward reference",0,PASS1); + } else { + int gl=0; + char *p=lp,*n; + skipblanks(p); + if (*p=='@') { ++p; gl=1; } + if ((n=getid(p)) && structtab.emit(n,tp,p,gl)) { lp=p; return; } + val=adres; + } + ttp=tp; + if (!(tp=MaakLabNaam(tp))) +#ifdef SECTIONS + break; +#else + return; +#endif + if (pass==2) { + if (!getLabelValue(ttp,oval)) error("Internal error. ParseLabel()",0,FATAL); + if (val!=oval) error("Label has different value in pass 2",temp); + } else + if (!labtab.insert(tp,val)) error("Duplicate label",tp,PASS1); + } +#ifdef SECTIONS + break; + case DATA: + if (isdigit(*tp)) { error("Number labels not allowed in data sections",tp); break; } + if (needequ()) { error("Equ not allowed in data sections",0); break; } + if (needfield()) { error("Field not allowed in data sections",0); break; } + if (!(tp=MaakLabNaam(tp))) break; + pooltab.addlabel(tp); + break; + default: + error("internal error parselabel",0,FATAL); + break; + } +#endif +} + +void ParseMacro() { + int gl=0; + char *p=lp,*n; + skipblanks(p); if (*p=='@') { gl=1; ++p; } + if (!(n=getid(p))) return; + if (structtab.emit(n,0,p,gl) || !gl && macrotab.emit(n,p)) *lp=0; +} + +void ParseInstruction() { + if (ParseDirective()) return; +#ifdef SECTIONS + if (section!=TEXT) { error("No instructions allowed outside text sections",lp); return; } +#endif +#ifdef METARM + (*piCPUp)(); +#else + piZ80(); +#endif +} + +int insideCompassStyleMacroDefinition = 0; + +void ReformatCompassStyleMacro(char* line) +{ + char* labelStart; + char* labelEnd; + char* paramsStart; + int i; + int labelLength; + + char* lp = line; + if (!*lp || *lp <= ' ' || *lp == ';') + return; + + labelStart = line; + while(*lp && *lp > ' ' && *lp != ':' && *lp != ';') lp++; + if (!*lp || *lp == ';') + return; + + labelEnd = lp; + labelLength = labelEnd - labelStart; + + while (*lp == ':' || *lp == ' ' || *lp == '\t') lp++; + if (!*lp) + return; + + if (tolower(lp[0]) != 'm') return; + if (tolower(lp[1]) != 'a') return; + if (tolower(lp[2]) != 'c') return; + if (tolower(lp[3]) != 'r') return; + if (tolower(lp[4]) != 'o') return; + if (lp[5] > ' ') return; + + lp += 5; + while (*lp && *lp <= ' ') ++lp; + paramsStart = lp; + + //cout << "!!1 " << paramsStart << endl; + + /* It's a Compass style macro */ + + strcpy(temp, " macro "); + memcpy(temp + 7, labelStart, labelLength); + strcpy(temp + 7 + labelLength, paramsStart - 1); + + strcpy(line, temp); + + insideCompassStyleMacroDefinition = 1; +} + +void ParseLine() { + char* tempLp; + + if(compassCompatibilityEnabled) + ReformatCompassStyleMacro(line); + + if(insideCompassStyleMacroDefinition) + ReplaceAtToUnderscore(line); + + ++gcurlin; + replacedefineteller=comnxtlin=0; + lp = ReplaceDefine(line); + if (comlin) { comlin+=comnxtlin; ListFileSkip(line); return; } + comlin+=comnxtlin; if (!*lp) { ListFile(); return; } + ParseLabel(); if (skipblanks()) { ListFile(); return; } + ParseMacro(); if (skipblanks()) { ListFile(); return; } + ParseInstruction(); if (skipblanks()) { ListFile(); return; } + if (*lp) error("Unexpected",lp); ListFile(); +} + +void ParseStructLabel(structcls *st) { + char *tp,temp[LINEMAX]; + prevlab=0; + if (white()) return; + tp=temp; if (*lp=='.') ++lp; + while (*lp && islabchar(*lp)) { *tp=*lp; ++tp; ++lp; } + *tp=0; if (*lp==':') ++lp; + tp=temp; skipblanks(); + if (isdigit(*tp)) { error("Numberlabels not allowed within structs",0); return; } + prevlab=strdup(tp); st->addlabel(tp); +} + +void ParseInSTRUCTion(structcls *st) { + structmembicls *smp; + aint val,len; + bp=lp; + switch (GetStructMemberId(lp)) { + case SMEMBBLOCK: + if (!ParseExpression(lp,len)) { len=1; error("Expression expected",0,PASS1); } + if (comma(lp)) { + if (!ParseExpression(lp,val)) { val=0; error("Expression expected",0,PASS1); } + } else val=0; + check8(val); + smp=new structmembicls(st->noffset,len,val&255,SMEMBBLOCK); + st->addmemb(smp); + break; + case SMEMBBYTE: + if (!ParseExpression(lp,val)) val=0; check8(val); + smp=new structmembicls(st->noffset,1,val,SMEMBBYTE); + st->addmemb(smp); + break; + case SMEMBWORD: + if (!ParseExpression(lp,val)) val=0; check16(val); + smp=new structmembicls(st->noffset,2,val,SMEMBWORD); + st->addmemb(smp); + break; + case SMEMBD24: + if (!ParseExpression(lp,val)) val=0; check24(val); + smp=new structmembicls(st->noffset,3,val,SMEMBD24); + st->addmemb(smp); + break; + case SMEMBDWORD: + if (!ParseExpression(lp,val)) val=0; + smp=new structmembicls(st->noffset,4,val,SMEMBDWORD); + st->addmemb(smp); + break; + case SMEMBALIGN: + if (!ParseExpression(lp,val)) val=4; + st->noffset+=((~st->noffset+1)&(val-1)); + break; + default: + char *pp=lp,*n; + int gl=0; + structcls *s; + skipblanks(pp); if (*pp=='@') { ++pp; gl=1; } + if ((n=getid(pp)) && (s=structtab.zoek(n,gl))) { lp=pp; st->cpylabels(s); st->cpymembs(s,lp); } + break; + } +} + +void ParseStructLine(structcls *st) { + replacedefineteller=comnxtlin=0; + lp=ReplaceDefine(line); + if (comlin) { comlin+=comnxtlin; return; } + comlin+=comnxtlin; if (!*lp) return; + ParseStructLabel(st); if (skipblanks()) return; + ParseInSTRUCTion(st); if (skipblanks()) return; + if (*lp) error("Unexpected",lp); +} +//eof parser.cpp diff --git a/tools/sjasm/parser.h b/tools/sjasm/parser.h new file mode 100644 index 00000000..5c75b1ef --- /dev/null +++ b/tools/sjasm/parser.h @@ -0,0 +1,35 @@ +/* + + SjASM Z80 Assembler + + Copyright (c) 2015 Konamiman + Based on Sjasm 0.39g6 - Copyright (c) 2006 Sjoerd Mastijn + + This software is provided 'as-is', without any express or implied warranty. + In no event will the authors be held liable for any damages arising from the + use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it freely, + subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not claim + that you wrote the original software. If you use this software in a product, + an acknowledgment in the product documentation would be appreciated but is + not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source distribution. + +*/ + +// parser.h + +int ParseExpression(char *&lp, aint &val); +int ParseDirective(); +void ParseLine(); +void ParseStructLine(structcls *st); +//eof parser.h + diff --git a/tools/sjasm/piz80.cpp b/tools/sjasm/piz80.cpp new file mode 100644 index 00000000..850a39f4 --- /dev/null +++ b/tools/sjasm/piz80.cpp @@ -0,0 +1,2401 @@ +/* + + SjASM Z80 Assembler + + Copyright (c) 2015 Konamiman + Based on Sjasm 0.39g6 - Copyright (c) 2006 Sjoerd Mastijn + + This software is provided 'as-is', without any express or implied warranty. + In no event will the authors be held liable for any damages arising from the + use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it freely, + subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not claim + that you wrote the original software. If you use this software in a product, + an acknowledgment in the product documentation would be appreciated but is + not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source distribution. + +*/ + +// piz80.cpp + +#include "sjasm.h" + +enum Z80Reg { Z80_B=0,Z80_C,Z80_D,Z80_E,Z80_H,Z80_L,Z80_A=7, + Z80_I,Z80_R,Z80_F,Z80_BC=0x10,Z80_DE=0x20,Z80_HL=0x30, + Z80_IXH,Z80_IXL,Z80_IYH,Z80_IYL, + Z80_SP=0x40,Z80_AF=0x50,Z80_IX=0xdd,Z80_IY=0xfd,Z80_UNK=-1 }; +enum Z80Cond { Z80C_C,Z80C_M,Z80C_NC,Z80C_NZ,Z80C_P,Z80C_PE,Z80C_PO,Z80C_Z,Z80C_UNK }; + +funtabcls z80funtab; + +void piZ80() { + char *n; + bp=lp; + if (!(n=getinstr(lp))) + if (*lp=='#' && *(lp+1)=='#') { + lp+=2; + aint val; + synerr=0; if (!ParseExpression(lp,val)) val=4; synerr=1; + mapadr+=((~mapadr+1)&(val-1)); + return; + } else { + error ("Unrecognized instruction",lp); return; + } + if (!z80funtab.zoek(n)) { error ("Unrecognized instruction",bp); *lp=0; } +} + +int z80getbyte(char *&p) { + aint val; + if (!ParseExpression(p,val)) { error("Operand expected",NULL); return 0; } + check8(val); + return val & 255; +} + +int z80getword(char *&p) { + aint val; + if (!ParseExpression(p,val)) { error("Operand expected",NULL); return 0; } + check16(val); + return val & 65535; +} + +int z80getidxoffset(char *&p) { + aint val; + char *pp=p; + skipblanks(pp); + if (*pp==')') return 0; + if (*pp==']') return 0; + if (!ParseExpression(p,val)) { error("Operand expected",NULL); return 0; } + check8o(val); + return val & 255; +} + +int z80getadres(char *&p, aint &ad) { + if (getLocaleLabelValue(p,ad)) return 1; + if (ParseExpression(p,ad)) return 1; + error("Operand expected",0,CATCHALL); + return 0; +} + +Z80Cond getz80cond(char *&p) { + char *pp=p; + skipblanks(p); + switch (*(p++)) { + case 'n': + switch (*(p++)) { + case 'z': if (!islabchar(*p)) return Z80C_NZ; break; + case 'c': if (!islabchar(*p)) return Z80C_NC; break; + case 's': if (!islabchar(*p)) return Z80C_P; break; + default: break; + } + break; + case 'N': + switch (*(p++)) { + case 'Z': if (!islabchar(*p)) return Z80C_NZ; break; + case 'C': if (!islabchar(*p)) return Z80C_NC; break; + case 'S': if (!islabchar(*p)) return Z80C_P; break; + default: break; + } + break; + case 'z': case 'Z': if (!islabchar(*p)) return Z80C_Z; break; + case 'c': case 'C': if (!islabchar(*p)) return Z80C_C; break; + case 'm': case 'M': case 's': case 'S': if (!islabchar(*p)) return Z80C_M; break; + case 'p': + if (!islabchar(*p)) return Z80C_P; + switch (*(p++)) { + case 'e': if (!islabchar(*p)) return Z80C_PE; break; + case 'o': if (!islabchar(*p)) return Z80C_PO; break; + default: break; + } + break; + case 'P': + if (!islabchar(*p)) return Z80C_P; + switch (*(p++)) { + case 'E': if (!islabchar(*p)) return Z80C_PE; break; + case 'O': if (!islabchar(*p)) return Z80C_PO; break; + default: break; + } + break; + default: break; + } + p=pp; + return Z80C_UNK; +} + +Z80Reg getz80reg(char *&p) { + char *pp=p; + skipblanks(p); + switch (*(p++)) { + case 'a': + if (!islabchar(*p)) return Z80_A; + if (*p=='f' && !islabchar(*(p+1))) { ++p; return Z80_AF; } + break; + case 'b': + if (!islabchar(*p)) return Z80_B; + if (*p=='c' && !islabchar(*(p+1))) { ++p; return Z80_BC; } + break; + case 'c': + if (!islabchar(*p)) return Z80_C; + break; + case 'd': + if (!islabchar(*p)) return Z80_D; + if (*p=='e' && !islabchar(*(p+1))) { ++p; return Z80_DE; } + break; + case 'e': + if (!islabchar(*p)) return Z80_E; + break; + case 'f': + if (!islabchar(*p)) return Z80_F; + break; + case 'h': + if (!islabchar(*p)) return Z80_H; + if (*p=='l' && !islabchar(*(p+1))) { ++p; return Z80_HL; } + break; + case 'i': + if (*p=='x') { + if (!islabchar(*(p+1))) { ++p; return Z80_IX; } + if (*(p+1)=='h' && !islabchar(*(p+2))) { p+=2; return Z80_IXH; } + if (*(p+1)=='l' && !islabchar(*(p+2))) { p+=2; return Z80_IXL; } + } + if (*p=='y') { + if (!islabchar(*(p+1))) { ++p; return Z80_IY; } + if (*(p+1)=='h' && !islabchar(*(p+2))) { p+=2; return Z80_IYH; } + if (*(p+1)=='l' && !islabchar(*(p+2))) { p+=2; return Z80_IYL; } + } + if (!islabchar(*p)) return Z80_I; + break; + case 'l': + if (!islabchar(*p)) return Z80_L; + break; + case 'r': + if (!islabchar(*p)) return Z80_R; + break; + case 's': + if (*p=='p' && !islabchar(*(p+1))) { ++p; return Z80_SP; } + break; + case 'A': + if (!islabchar(*p)) return Z80_A; + if (*p=='F' && !islabchar(*(p+1))) { ++p; return Z80_AF; } + break; + case 'B': + if (!islabchar(*p)) return Z80_B; + if (*p=='C' && !islabchar(*(p+1))) { ++p; return Z80_BC; } + break; + case 'C': + if (!islabchar(*p)) return Z80_C; + break; + case 'D': + if (!islabchar(*p)) return Z80_D; + if (*p=='E' && !islabchar(*(p+1))) { ++p; return Z80_DE; } + break; + case 'E': + if (!islabchar(*p)) return Z80_E; + break; + case 'F': + if (!islabchar(*p)) return Z80_F; + break; + case 'H': + if (!islabchar(*p)) return Z80_H; + if (*p=='L' && !islabchar(*(p+1))) { ++p; return Z80_HL; } + break; + case 'I': + if (*p=='X') { + if (!islabchar(*(p+1))) { ++p; return Z80_IX; } + if (*(p+1)=='H' && !islabchar(*(p+2))) { p+=2; return Z80_IXH; } + if (*(p+1)=='L' && !islabchar(*(p+2))) { p+=2; return Z80_IXL; } + } + if (*p=='Y') { + if (!islabchar(*(p+1))) { ++p; return Z80_IY; } + if (*(p+1)=='H' && !islabchar(*(p+2))) { p+=2; return Z80_IYH; } + if (*(p+1)=='L' && !islabchar(*(p+2))) { p+=2; return Z80_IYL; } + } + if (!islabchar(*p)) return Z80_I; + break; + case 'L': + if (!islabchar(*p)) return Z80_L; + break; + case 'R': + if (!islabchar(*p)) return Z80_R; + break; + case 'S': + if (*p=='P' && !islabchar(*(p+1))) { ++p; return Z80_SP; } + break; + default: break; + } + p=pp; + return Z80_UNK; +} + +void pizADC() { + Z80Reg reg; + int e[4]; + e[0]=e[1]=e[2]=e[3]=-1; + switch (reg=getz80reg(lp)) { + case Z80_HL: + if (!comma(lp)) { error("Comma expected",0); break; } + switch (getz80reg(lp)) { + case Z80_BC: e[0]=0xed; e[1]=0x4a; break; + case Z80_DE: e[0]=0xed; e[1]=0x5a; break; + case Z80_HL: e[0]=0xed; e[1]=0x6a; break; + case Z80_SP: e[0]=0xed; e[1]=0x7a; break; + default: ; + } + break; + case Z80_A: + if (!comma(lp)) { e[0]=0x8f; break; } + reg=getz80reg(lp); + default: + switch (reg) { + case Z80_IXH: e[0]=0xdd; e[1]=0x8c; break; + case Z80_IXL: e[0]=0xdd; e[1]=0x8d; break; + case Z80_IYH: e[0]=0xfd; e[1]=0x8c; break; + case Z80_IYL: e[0]=0xfd; e[1]=0x8d; break; + case Z80_B: case Z80_C: case Z80_D: case Z80_E: + case Z80_H: case Z80_L: case Z80_A: e[0]=0x88+reg; break; + case Z80_F: case Z80_I: case Z80_R: + case Z80_AF: case Z80_BC: case Z80_DE: case Z80_HL: case Z80_SP: + case Z80_IX: case Z80_IY: + break; + default: + reg=Z80_UNK; + if (oparen(lp,'[')) { + if ((reg=getz80reg(lp))==Z80_UNK) break; + } else if (oparen(lp,'(')) { + if ((reg=getz80reg(lp))==Z80_UNK) --lp; + } + switch (reg) { + case Z80_HL: + if (cparen(lp)) e[0]=0x8e; + break; + case Z80_IX: case Z80_IY: + e[1]=0x8e; e[2]=z80getidxoffset(lp); + if (cparen(lp)) e[0]=reg; + break; + default: e[0]=0xce; e[1]=z80getbyte(lp); break; + } + } + } + EmitBytes(e); +} + +void pizADD() { + Z80Reg reg; + int e[4]; + e[0]=e[1]=e[2]=e[3]=-1; + switch (reg=getz80reg(lp)) { + case Z80_HL: + if (!comma(lp)) { error("Comma expected",0); break; } + switch (getz80reg(lp)) { + case Z80_BC: e[0]=0x09; break; + case Z80_DE: e[0]=0x19; break; + case Z80_HL: e[0]=0x29; break; + case Z80_SP: e[0]=0x39; break; + default: ; + } + break; + case Z80_IX: + if (!comma(lp)) { error("Comma expected",0); break; } + switch (getz80reg(lp)) { + case Z80_BC: e[0]=0xdd; e[1]=0x09; break; + case Z80_DE: e[0]=0xdd; e[1]=0x19; break; + case Z80_IX: e[0]=0xdd; e[1]=0x29; break; + case Z80_SP: e[0]=0xdd; e[1]=0x39; break; + default: ; + } + break; + case Z80_IY: + if (!comma(lp)) { error("Comma expected",0); break; } + switch (getz80reg(lp)) { + case Z80_BC: e[0]=0xfd; e[1]=0x09; break; + case Z80_DE: e[0]=0xfd; e[1]=0x19; break; + case Z80_IY: e[0]=0xfd; e[1]=0x29; break; + case Z80_SP: e[0]=0xfd; e[1]=0x39; break; + default: ; + } + break; + case Z80_A: + if (!comma(lp)) { e[0]=0x87; break; } + reg=getz80reg(lp); + default: + switch (reg) { + case Z80_IXH: e[0]=0xdd; e[1]=0x84; break; + case Z80_IXL: e[0]=0xdd; e[1]=0x85; break; + case Z80_IYH: e[0]=0xfd; e[1]=0x84; break; + case Z80_IYL: e[0]=0xfd; e[1]=0x85; break; + case Z80_B: case Z80_C: case Z80_D: case Z80_E: + case Z80_H: case Z80_L: case Z80_A: e[0]=0x80+reg; break; + case Z80_F: case Z80_I: case Z80_R: + case Z80_AF: case Z80_BC: case Z80_DE: case Z80_HL: case Z80_SP: + case Z80_IX: case Z80_IY: + break; + default: + reg=Z80_UNK; + if (oparen(lp,'[')) { + if ((reg=getz80reg(lp))==Z80_UNK) break; + } else if (oparen(lp,'(')) { + if ((reg=getz80reg(lp))==Z80_UNK) --lp; + } + switch (reg) { + case Z80_HL: + if (cparen(lp)) e[0]=0x86; + break; + case Z80_IX: case Z80_IY: + e[1]=0x86; e[2]=z80getidxoffset(lp); + if (cparen(lp)) e[0]=reg; + break; + default: e[0]=0xc6; e[1]=z80getbyte(lp); break; + } + } + } + EmitBytes(e); +} + +void pizAND() { + Z80Reg reg; + int e[4]; + e[0]=e[1]=e[2]=e[3]=-1; + switch (reg=getz80reg(lp)) { + case Z80_A: + if (!comma(lp)) { e[0]=0xa7; break; } + reg=getz80reg(lp); + default: + switch (reg) { + case Z80_IXH: e[0]=0xdd; e[1]=0xa4; break; + case Z80_IXL: e[0]=0xdd; e[1]=0xa5; break; + case Z80_IYH: e[0]=0xfd; e[1]=0xa4; break; + case Z80_IYL: e[0]=0xfd; e[1]=0xa5; break; + case Z80_B: case Z80_C: case Z80_D: case Z80_E: + case Z80_H: case Z80_L: case Z80_A: e[0]=0xa0+reg; break; + case Z80_F: case Z80_I: case Z80_R: + case Z80_AF: case Z80_BC: case Z80_DE: case Z80_HL: case Z80_SP: + case Z80_IX: case Z80_IY: + break; + default: + reg=Z80_UNK; + if (oparen(lp,'[')) { + if ((reg=getz80reg(lp))==Z80_UNK) break; + } else if (oparen(lp,'(')) { + if ((reg=getz80reg(lp))==Z80_UNK) --lp; + } + switch (reg) { + case Z80_HL: + if (cparen(lp)) e[0]=0xa6; + break; + case Z80_IX: case Z80_IY: + e[1]=0xa6; e[2]=z80getidxoffset(lp); + if (cparen(lp)) e[0]=reg; + break; + default: e[0]=0xe6; e[1]=z80getbyte(lp); break; + } + } + } + EmitBytes(e); +} + +void pizBIT() { + Z80Reg reg; + int e[5],bit; + e[0]=e[1]=e[2]=e[3]=e[4]=-1; + bit=z80getbyte(lp); + if (!comma(lp)) bit=-1; + switch (reg=getz80reg(lp)) { + case Z80_B: case Z80_C: case Z80_D: case Z80_E: + case Z80_H: case Z80_L: case Z80_A: + e[0]=0xcb; e[1]=8*bit+0x40+reg; break; + default: + if (!oparen(lp,'[') && !oparen(lp,'(')) break; + switch (reg=getz80reg(lp)) { + case Z80_HL: + if (cparen(lp)) e[0]=0xcb; + e[1]=8*bit+0x46; break; + case Z80_IX: case Z80_IY: + e[1]=0xcb; e[2]=z80getidxoffset(lp); e[3]=8*bit+0x46; + if (cparen(lp)) e[0]=reg; + break; + default: ; + } + } + if (bit<0 || bit>7) e[0]=-1; + EmitBytes(e); +} + +void pizCALL() { + aint callad; + int e[4],b; + e[0]=e[1]=e[2]=e[3]=-1; + switch (getz80cond(lp)) { + case Z80C_C: if (comma(lp)) e[0]=0xdc; break; + case Z80C_M: if (comma(lp)) e[0]=0xfc; break; + case Z80C_NC: if (comma(lp)) e[0]=0xd4; break; + case Z80C_NZ: if (comma(lp)) e[0]=0xc4; break; + case Z80C_P: if (comma(lp)) e[0]=0xf4; break; + case Z80C_PE: if (comma(lp)) e[0]=0xec; break; + case Z80C_PO: if (comma(lp)) e[0]=0xe4; break; + case Z80C_Z: if (comma(lp)) e[0]=0xcc; break; + default: e[0]=0xcd; break; + } + if (!(z80getadres(lp,callad))) callad=0; + b=(signed)callad; + e[1]=callad&255; e[2]=(callad>>8)&255; + if (b>65535) error("Bytes lost",0); + EmitBytes(e); +} + +void pizCCF() { + EmitByte(0x3f); +} + +void pizCP() { + Z80Reg reg; + int e[4]; + e[0]=e[1]=e[2]=e[3]=-1; + switch (reg=getz80reg(lp)) { + case Z80_A: + if (!comma(lp)) { e[0]=0xbf; break; } + reg=getz80reg(lp); + default: + switch (reg) { + case Z80_IXH: e[0]=0xdd; e[1]=0xbc; break; + case Z80_IXL: e[0]=0xdd; e[1]=0xbd; break; + case Z80_IYH: e[0]=0xfd; e[1]=0xbc; break; + case Z80_IYL: e[0]=0xfd; e[1]=0xbd; break; + case Z80_B: case Z80_C: case Z80_D: case Z80_E: + case Z80_H: case Z80_L: case Z80_A: e[0]=0xb8+reg; break; + case Z80_F: case Z80_I: case Z80_R: + case Z80_AF: case Z80_BC: case Z80_DE: case Z80_HL: case Z80_SP: + case Z80_IX: case Z80_IY: + break; + default: + reg=Z80_UNK; + if (oparen(lp,'[')) { + if ((reg=getz80reg(lp))==Z80_UNK) break; + } else if (oparen(lp,'(')) { + if ((reg=getz80reg(lp))==Z80_UNK) --lp; + } + switch (reg) { + case Z80_HL: + if (cparen(lp)) e[0]=0xbe; + break; + case Z80_IX: case Z80_IY: + e[1]=0xbe; e[2]=z80getidxoffset(lp); + if (cparen(lp)) e[0]=reg; + break; + default: e[0]=0xfe; e[1]=z80getbyte(lp); break; + } + } + } + EmitBytes(e); +} + +void pizCPD() { + int e[3]; + e[0]=0xed; + e[1]=0xa9; + e[2]=-1; + EmitBytes(e); +} + +void pizCPDR() { + int e[3]; + e[0]=0xed; + e[1]=0xb9; + e[2]=-1; + EmitBytes(e); +} + +void pizCPI() { + int e[3]; + e[0]=0xed; + e[1]=0xa1; + e[2]=-1; + EmitBytes(e); +} + +void pizCPIR() { + int e[3]; + e[0]=0xed; + e[1]=0xb1; + e[2]=-1; + EmitBytes(e); +} + +void pizCPL() { + EmitByte(0x2f); +} + +void pizDAA() { + EmitByte(0x27); +} + +void pizDEC() { + Z80Reg reg; + int e[4]; + e[0]=e[1]=e[2]=e[3]=-1; + switch (getz80reg(lp)) { + case Z80_A: e[0]=0x3d; break; + case Z80_B: e[0]=0x05; break; + case Z80_BC: e[0]=0x0b; break; + case Z80_C: e[0]=0x0d; break; + case Z80_D: e[0]=0x15; break; + case Z80_DE: e[0]=0x1b; break; + case Z80_E: e[0]=0x1d; break; + case Z80_H: e[0]=0x25; break; + case Z80_HL: e[0]=0x2b; break; + case Z80_IX: e[0]=0xdd; e[1]=0x2b; break; + case Z80_IY: e[0]=0xfd; e[1]=0x2b; break; + case Z80_L: e[0]=0x2d; break; + case Z80_SP: e[0]=0x3b; break; + case Z80_IXH: e[0]=0xdd; e[1]=0x25; break; + case Z80_IXL: e[0]=0xdd; e[1]=0x2d; break; + case Z80_IYH: e[0]=0xfd; e[1]=0x25; break; + case Z80_IYL: e[0]=0xfd; e[1]=0x2d; break; + default: + if (!oparen(lp,'[') && !oparen(lp,'(')) break; + switch (reg=getz80reg(lp)) { + case Z80_HL: + if (cparen(lp)) e[0]=0x35; break; + case Z80_IX: case Z80_IY: + e[1]=0x35; e[2]=z80getidxoffset(lp); + if (cparen(lp)) e[0]=reg; + break; + default: ; + } + } + EmitBytes(e); +} + +void pizDI() { + EmitByte(0xf3); +} + +void pizDJNZ() { + int jmp; + aint nad; + int e[3]; + e[0]=e[1]=e[2]=-1; + if (!z80getadres(lp,nad)) nad=adres+2; + jmp=nad-adres-2; + if (jmp<-128 || jmp>127) { + char el[LINEMAX]; + sprintf(el,"Target out of range (%i)",jmp); + error(el,0); jmp=0; + } + e[0]=0x10; e[1]=jmp<0?256+jmp:jmp; + EmitBytes(e); +} + +void pizEI() { + EmitByte(0xfb); +} + +void pizEX() { + Z80Reg reg; + int e[4]; + e[0]=e[1]=e[2]=e[3]=-1; + switch (getz80reg(lp)) { + case Z80_AF: + if (comma(lp)) { + if (getz80reg(lp)==Z80_AF) { + if (*lp=='\'') ++lp; + } else break; + } + e[0]=0x08; + break; + case Z80_DE: + if (!comma(lp)) { error("Comma expected",0); break; } + if (getz80reg(lp)!=Z80_HL) break; + e[0]=0xeb; + break; + default: + if (!oparen(lp,'[') && !oparen(lp,'(')) break; + if (getz80reg(lp)!=Z80_SP) break; + if (!cparen(lp)) break; + if (!comma(lp)) { error("Comma expected",0); break; } + switch (reg=getz80reg(lp)) { + case Z80_HL: e[0]=0xe3; break; + case Z80_IX: case Z80_IY: + e[0]=reg; e[1]=0xe3; break; + default: ; + } + } + EmitBytes(e); +} + +void pizEXX() { + EmitByte(0xd9); +} + +void pizHALT() { + EmitByte(0x76); +} + +void pizIM() { + int e[3]; + e[0]=0xed; e[2]=-1; + switch(z80getbyte(lp)) { + case 0: e[1]=0x46; break; + case 1: e[1]=0x56; break; + case 2: e[1]=0x5e; break; + default: e[0]=-1; + } + EmitBytes(e); +} + +void pizIN() { + Z80Reg reg; + int e[3]; + e[0]=e[1]=e[2]=-1; + switch (reg=getz80reg(lp)) { + case Z80_A: + if (!comma(lp)) break; + if (!oparen(lp,'[') && !oparen(lp,'(')) break; + if (getz80reg(lp)==Z80_C) { + e[1]=0x78; if (cparen(lp)) e[0]=0xed; + } else { + e[1]=z80getbyte(lp); if (cparen(lp)) e[0]=0xdb; + } + break; + case Z80_B: case Z80_C: case Z80_D: + case Z80_E: case Z80_H: case Z80_L: case Z80_F: + if (!comma(lp)) break; + if (!oparen(lp,'[') && !oparen(lp,'(')) break; + if (getz80reg(lp)!=Z80_C) break; + if (cparen(lp)) e[0]=0xed; + switch (reg) { + case Z80_B: e[1]=0x40; break; + case Z80_C: e[1]=0x48; break; + case Z80_D: e[1]=0x50; break; + case Z80_E: e[1]=0x58; break; + case Z80_H: e[1]=0x60; break; + case Z80_L: e[1]=0x68; break; + case Z80_F: e[1]=0x70; break; + default: ; + } + default: + if (!oparen(lp,'[') && !oparen(lp,'(')) break; + if (getz80reg(lp)!=Z80_C) break; + if (cparen(lp)) e[0]=0xed; + e[1]=0x70; + } + EmitBytes(e); +} + +void pizINC() { + Z80Reg reg; + int e[4]; + e[0]=e[1]=e[2]=e[3]=-1; + switch (getz80reg(lp)) { + case Z80_A: e[0]=0x3c; break; + case Z80_B: e[0]=0x04; break; + case Z80_BC: e[0]=0x03; break; + case Z80_C: e[0]=0x0c; break; + case Z80_D: e[0]=0x14; break; + case Z80_DE: e[0]=0x13; break; + case Z80_E: e[0]=0x1c; break; + case Z80_H: e[0]=0x24; break; + case Z80_HL: e[0]=0x23; break; + case Z80_IX: e[0]=0xdd; e[1]=0x23; break; + case Z80_IY: e[0]=0xfd; e[1]=0x23; break; + case Z80_L: e[0]=0x2c; break; + case Z80_SP: e[0]=0x33; break; + case Z80_IXH: e[0]=0xdd; e[1]=0x24; break; + case Z80_IXL: e[0]=0xdd; e[1]=0x2c; break; + case Z80_IYH: e[0]=0xfd; e[1]=0x24; break; + case Z80_IYL: e[0]=0xfd; e[1]=0x2c; break; + default: + if (!oparen(lp,'[') && !oparen(lp,'(')) break; + switch (reg=getz80reg(lp)) { + case Z80_HL: + if (cparen(lp)) e[0]=0x34; break; + case Z80_IX: case Z80_IY: + e[1]=0x34; e[2]=z80getidxoffset(lp); + if (cparen(lp)) e[0]=reg; + break; + default: ; + } + } + EmitBytes(e); +} + +void pizIND() { + int e[3]; + e[0]=0xed; + e[1]=0xaa; + e[2]=-1; + EmitBytes(e); +} + +void pizINDR() { + int e[3]; + e[0]=0xed; + e[1]=0xba; + e[2]=-1; + EmitBytes(e); +} + +void pizINI() { + int e[3]; + e[0]=0xed; + e[1]=0xa2; + e[2]=-1; + EmitBytes(e); +} + +void pizINIR() { + int e[3]; + e[0]=0xed; + e[1]=0xb2; + e[2]=-1; + EmitBytes(e); +} + +void pizJP() { + Z80Reg reg; + int haakjes=0; + aint jpad; + int e[4],b,k=0; + e[0]=e[1]=e[2]=e[3]=-1; + switch (getz80cond(lp)) { + case Z80C_C: if (comma(lp)) e[0]=0xda; break; + case Z80C_M: if (comma(lp)) e[0]=0xfa; break; + case Z80C_NC: if (comma(lp)) e[0]=0xd2; break; + case Z80C_NZ: if (comma(lp)) e[0]=0xc2; break; + case Z80C_P: if (comma(lp)) e[0]=0xf2; break; + case Z80C_PE: if (comma(lp)) e[0]=0xea; break; + case Z80C_PO: if (comma(lp)) e[0]=0xe2; break; + case Z80C_Z: if (comma(lp)) e[0]=0xca; break; + default: + reg=Z80_UNK; + if (oparen(lp,'[')) { + if ((reg=getz80reg(lp))==Z80_UNK) break; + haakjes=1; + } else if (oparen(lp,'(')) { + if ((reg=getz80reg(lp))==Z80_UNK) --lp; else haakjes=1; + } + if (reg==Z80_UNK) reg=getz80reg(lp); + switch (reg) { + case Z80_HL: if (haakjes && !cparen(lp)) break; e[0]=0xe9; k=1; break; + case Z80_IX: case Z80_IY: e[1]=0xe9; if (haakjes && !cparen(lp)) break; e[0]=reg; k=1; break; + default: e[0]=0xc3; + } + } + if (!k) { + if (!(z80getadres(lp,jpad))) jpad=0; + b=(signed)jpad; + e[1]=jpad&255; e[2]=(jpad>>8)&255; + if (b>65535) error("Bytes lost",0); + } + EmitBytes(e); +} + +void pizJR() { + aint jrad; + int e[4],jmp; + e[0]=e[1]=e[2]=e[3]=-1; + switch (getz80cond(lp)) { + case Z80C_C: if (comma(lp)) e[0]=0x38; break; + case Z80C_NC: if (comma(lp)) e[0]=0x30; break; + case Z80C_NZ: if (comma(lp)) e[0]=0x20; break; + case Z80C_Z: if (comma(lp)) e[0]=0x28; break; + case Z80C_M: case Z80C_P: case Z80C_PE: case Z80C_PO: error("Illegal condition",0); break; + default: e[0]=0x18; break; + } + if (!(z80getadres(lp,jrad))) jrad=adres+2; + jmp=jrad-adres-2; + if (jmp<-128 || jmp>127) { char el[LINEMAX]; sprintf(el,"Target out of range (%i)",jmp); error(el,0); jmp=0; } + e[1]=jmp<0?256+jmp:jmp; + EmitBytes(e); +} + +void pizLD() { + Z80Reg reg; + int e[7],beginhaakje; + aint b; + char *olp; + e[0]=e[1]=e[2]=e[3]=e[4]=e[5]=e[6]=-1; + switch (getz80reg(lp)) { + case Z80_F: case Z80_AF: break; + + case Z80_A: + if (!comma(lp)) break; + switch (reg=getz80reg(lp)) { + case Z80_F: case Z80_BC: case Z80_DE: case Z80_HL: + case Z80_SP: case Z80_AF: case Z80_IX: case Z80_IY: + break; + case Z80_A: case Z80_B: case Z80_C: case Z80_D: case Z80_E: case Z80_H: case Z80_L: + e[0]=0x78+reg; break; + case Z80_I: e[0]=0xed; e[1]=0x57; break; + case Z80_R: e[0]=0xed; e[1]=0x5f; break; + case Z80_IXL: e[0]=0xdd; e[1]=0x7d; break; + case Z80_IXH: e[0]=0xdd; e[1]=0x7c; break; + case Z80_IYL: e[0]=0xfd; e[1]=0x7d; break; + case Z80_IYH: e[0]=0xfd; e[1]=0x7c; break; + default: + if (oparen(lp,'[')) { + if ((reg=getz80reg(lp))==Z80_UNK) { + b=z80getword(lp); e[1]=b&255; e[2]=(b>>8)&255; if (cparen(lp)) e[0]=0x3a; break; + } + } else { + if (oparen(lp,'(')) { + if ((reg=getz80reg(lp))==Z80_UNK) { + olp=--lp; + if (!ParseExpression(lp,b)) break; + if (getparen(olp)==lp) { check16(b); e[0]=0x3a; e[1]=b&255; e[2]=(b>>8)&255; } + else { check8(b); e[0]=0x3e; e[1]=b&255; } + } + } else { e[0]=0x3e; e[1]=z80getbyte(lp); break; } + } + switch (reg) { + case Z80_BC: if (cparen(lp)) e[0]=0x0a; break; + case Z80_DE: if (cparen(lp)) e[0]=0x1a; break; + case Z80_HL: if (cparen(lp)) e[0]=0x7e; break; + case Z80_IX: case Z80_IY: + e[1]=0x7e; e[2]=z80getidxoffset(lp); if (cparen(lp)) e[0]=reg; break; + default: break; + } + } + break; + + case Z80_B: + if (!comma(lp)) break; + switch (reg=getz80reg(lp)) { + case Z80_F: case Z80_BC: case Z80_DE: case Z80_HL: case Z80_I: case Z80_R: + case Z80_SP: case Z80_AF: case Z80_IX: case Z80_IY: + break; + case Z80_A: case Z80_B: case Z80_C: case Z80_D: case Z80_E: case Z80_H: case Z80_L: + e[0]=0x40+reg; break; + case Z80_IXL: e[0]=0xdd; e[1]=0x45; break; + case Z80_IXH: e[0]=0xdd; e[1]=0x44; break; + case Z80_IYL: e[0]=0xfd; e[1]=0x45; break; + case Z80_IYH: e[0]=0xfd; e[1]=0x44; break; + default: + if (oparen(lp,'[')) { if ((reg=getz80reg(lp))==Z80_UNK) break; } + else if (oparen(lp,'(')) { if ((reg=getz80reg(lp))==Z80_UNK) { --lp; e[0]=0x06; e[1]=z80getbyte(lp); break; } } + else { e[0]=0x06; e[1]=z80getbyte(lp); break; } + switch (reg) { + case Z80_HL: if (cparen(lp)) e[0]=0x46; break; + case Z80_IX: case Z80_IY: + e[1]=0x46; e[2]=z80getidxoffset(lp); if (cparen(lp)) e[0]=reg; break; + default: break; + } + } + break; + + case Z80_C: + if (!comma(lp)) break; + switch (reg=getz80reg(lp)) { + case Z80_F: case Z80_BC: case Z80_DE: case Z80_HL: case Z80_I: case Z80_R: + case Z80_SP: case Z80_AF: case Z80_IX: case Z80_IY: + break; + case Z80_A: case Z80_B: case Z80_C: case Z80_D: case Z80_E: case Z80_H: case Z80_L: + e[0]=0x48+reg; break; + case Z80_IXL: e[0]=0xdd; e[1]=0x4d; break; + case Z80_IXH: e[0]=0xdd; e[1]=0x4c; break; + case Z80_IYL: e[0]=0xfd; e[1]=0x4d; break; + case Z80_IYH: e[0]=0xfd; e[1]=0x4c; break; + default: + if (oparen(lp,'[')) { if ((reg=getz80reg(lp))==Z80_UNK) break; } + else if (oparen(lp,'(')) { if ((reg=getz80reg(lp))==Z80_UNK) { --lp; e[0]=0x0e; e[1]=z80getbyte(lp); break; } } + else { e[0]=0x0e; e[1]=z80getbyte(lp); break; } + switch (reg) { + case Z80_HL: if (cparen(lp)) e[0]=0x4e; break; + case Z80_IX: case Z80_IY: + e[1]=0x4e; e[2]=z80getidxoffset(lp); if (cparen(lp)) e[0]=reg; break; + default: break; + } + } + break; + + case Z80_D: + if (!comma(lp)) break; + switch (reg=getz80reg(lp)) { + case Z80_F: case Z80_BC: case Z80_DE: case Z80_HL: case Z80_I: case Z80_R: + case Z80_SP: case Z80_AF: case Z80_IX: case Z80_IY: + break; + case Z80_A: case Z80_B: case Z80_C: case Z80_D: case Z80_E: case Z80_H: case Z80_L: + e[0]=0x50+reg; break; + case Z80_IXL: e[0]=0xdd; e[1]=0x55; break; + case Z80_IXH: e[0]=0xdd; e[1]=0x54; break; + case Z80_IYL: e[0]=0xfd; e[1]=0x55; break; + case Z80_IYH: e[0]=0xfd; e[1]=0x54; break; + default: + if (oparen(lp,'[')) { if ((reg=getz80reg(lp))==Z80_UNK) break; } + else if (oparen(lp,'(')) { if ((reg=getz80reg(lp))==Z80_UNK) { --lp; e[0]=0x16; e[1]=z80getbyte(lp); break; } } + else { e[0]=0x16; e[1]=z80getbyte(lp); break; } + switch (reg) { + case Z80_HL: if (cparen(lp)) e[0]=0x56; break; + case Z80_IX: case Z80_IY: + e[1]=0x56; e[2]=z80getidxoffset(lp); if (cparen(lp)) e[0]=reg; break; + default: break; + } + } + break; + + case Z80_E: + if (!comma(lp)) break; + switch (reg=getz80reg(lp)) { + case Z80_F: case Z80_BC: case Z80_DE: case Z80_HL: case Z80_I: case Z80_R: + case Z80_SP: case Z80_AF: case Z80_IX: case Z80_IY: + break; + case Z80_A: case Z80_B: case Z80_C: case Z80_D: case Z80_E: case Z80_H: case Z80_L: + e[0]=0x58+reg; break; + case Z80_IXL: e[0]=0xdd; e[1]=0x5d; break; + case Z80_IXH: e[0]=0xdd; e[1]=0x5c; break; + case Z80_IYL: e[0]=0xfd; e[1]=0x5d; break; + case Z80_IYH: e[0]=0xfd; e[1]=0x5c; break; + default: + if (oparen(lp,'[')) { if ((reg=getz80reg(lp))==Z80_UNK) break; } + else if (oparen(lp,'(')) { if ((reg=getz80reg(lp))==Z80_UNK) { --lp; e[0]=0x1e; e[1]=z80getbyte(lp); break; } } + else { e[0]=0x1e; e[1]=z80getbyte(lp); break; } + switch (reg) { + case Z80_HL: if (cparen(lp)) e[0]=0x5e; break; + case Z80_IX: case Z80_IY: + e[1]=0x5e; e[2]=z80getidxoffset(lp); if (cparen(lp)) e[0]=reg; break; + default: break; + } + } + break; + + case Z80_H: + if (!comma(lp)) break; + switch (reg=getz80reg(lp)) { + case Z80_F: case Z80_BC: case Z80_DE: case Z80_HL: case Z80_I: case Z80_R: + case Z80_SP: case Z80_AF: case Z80_IX: case Z80_IY: + case Z80_IXL: case Z80_IXH: case Z80_IYL: case Z80_IYH: + break; + case Z80_A: case Z80_B: case Z80_C: case Z80_D: case Z80_E: case Z80_H: case Z80_L: + e[0]=0x60+reg; break; + default: + if (oparen(lp,'[')) { if ((reg=getz80reg(lp))==Z80_UNK) break; } + else if (oparen(lp,'(')) { if ((reg=getz80reg(lp))==Z80_UNK) { --lp; e[0]=0x26; e[1]=z80getbyte(lp); break; } } + else { e[0]=0x26; e[1]=z80getbyte(lp); break; } + switch (reg) { + case Z80_HL: if (cparen(lp)) e[0]=0x66; break; + case Z80_IX: case Z80_IY: + e[1]=0x66; e[2]=z80getidxoffset(lp); if (cparen(lp)) e[0]=reg; break; + default: break; + } + } + break; + + case Z80_L: + if (!comma(lp)) break; + switch (reg=getz80reg(lp)) { + case Z80_F: case Z80_BC: case Z80_DE: case Z80_HL: case Z80_I: case Z80_R: + case Z80_SP: case Z80_AF: case Z80_IX: case Z80_IY: + case Z80_IXL: case Z80_IXH: case Z80_IYL: case Z80_IYH: + break; + case Z80_A: case Z80_B: case Z80_C: case Z80_D: case Z80_E: case Z80_H: case Z80_L: + e[0]=0x68+reg; break; + default: + if (oparen(lp,'[')) { if ((reg=getz80reg(lp))==Z80_UNK) break; } + else if (oparen(lp,'(')) { if ((reg=getz80reg(lp))==Z80_UNK) { --lp; e[0]=0x2e; e[1]=z80getbyte(lp); break; } } + else { e[0]=0x2e; e[1]=z80getbyte(lp); break; } + switch (reg) { + case Z80_HL: if (cparen(lp)) e[0]=0x6e; break; + case Z80_IX: case Z80_IY: + e[1]=0x6e; e[2]=z80getidxoffset(lp); if (cparen(lp)) e[0]=reg; break; + default: break; + } + } + break; + + case Z80_I: + if (!comma(lp)) break; + if (getz80reg(lp)==Z80_A) e[0]=0xed; + e[1]=0x47; break; + break; + + case Z80_R: + if (!comma(lp)) break; + if (getz80reg(lp)==Z80_A) e[0]=0xed; + e[1]=0x4f; break; + break; + + case Z80_IXL: + if (!comma(lp)) break; + switch (reg=getz80reg(lp)) { + case Z80_F: case Z80_BC: case Z80_DE: case Z80_HL: case Z80_I: case Z80_R: + case Z80_SP: case Z80_AF: case Z80_IX: case Z80_IY: case Z80_H: case Z80_L: + case Z80_IYL: case Z80_IYH: + break; + case Z80_A: case Z80_B: case Z80_C: case Z80_D: case Z80_E: + e[0]=0xdd; e[1]=0x68+reg; break; + case Z80_IXL: e[0]=0xdd; e[1]=0x6d; break; + case Z80_IXH: e[0]=0xdd; e[1]=0x6c; break; + default: + e[0]=0xdd; e[1]=0x2e; e[2]=z80getbyte(lp); break; + } + break; + + case Z80_IXH: + if (!comma(lp)) break; + switch (reg=getz80reg(lp)) { + case Z80_F: case Z80_BC: case Z80_DE: case Z80_HL: case Z80_I: case Z80_R: + case Z80_SP: case Z80_AF: case Z80_IX: case Z80_IY: case Z80_H: case Z80_L: + case Z80_IYL: case Z80_IYH: + break; + case Z80_A: case Z80_B: case Z80_C: case Z80_D: case Z80_E: + e[0]=0xdd; e[1]=0x60+reg; break; + case Z80_IXL: e[0]=0xdd; e[1]=0x65; break; + case Z80_IXH: e[0]=0xdd; e[1]=0x64; break; + default: + e[0]=0xdd; e[1]=0x26; e[2]=z80getbyte(lp); break; + } + break; + + case Z80_IYL: + if (!comma(lp)) break; + switch (reg=getz80reg(lp)) { + case Z80_F: case Z80_BC: case Z80_DE: case Z80_HL: case Z80_I: case Z80_R: + case Z80_SP: case Z80_AF: case Z80_IX: case Z80_IY: case Z80_H: case Z80_L: + case Z80_IXL: case Z80_IXH: + break; + case Z80_A: case Z80_B: case Z80_C: case Z80_D: case Z80_E: + e[0]=0xfd; e[1]=0x68+reg; break; + case Z80_IYL: e[0]=0xfd; e[1]=0x6d; break; + case Z80_IYH: e[0]=0xfd; e[1]=0x6c; break; + default: + e[0]=0xfd; e[1]=0x2e; e[2]=z80getbyte(lp); break; + } + break; + + case Z80_IYH: + if (!comma(lp)) break; + switch (reg=getz80reg(lp)) { + case Z80_F: case Z80_BC: case Z80_DE: case Z80_HL: case Z80_I: case Z80_R: + case Z80_SP: case Z80_AF: case Z80_IX: case Z80_IY: case Z80_H: case Z80_L: + case Z80_IXL: case Z80_IXH: + break; + case Z80_A: case Z80_B: case Z80_C: case Z80_D: case Z80_E: + e[0]=0xfd; e[1]=0x60+reg; break; + case Z80_IYL: e[0]=0xfd; e[1]=0x65; break; + case Z80_IYH: e[0]=0xfd; e[1]=0x64; break; + default: + e[0]=0xfd; e[1]=0x26; e[2]=z80getbyte(lp); break; + } + break; + + case Z80_BC: + if (!comma(lp)) break; + switch (getz80reg(lp)) { + case Z80_BC: e[0]=0x40; e[1]=0x49; break; + case Z80_DE: e[0]=0x42; e[1]=0x4b; break; + case Z80_HL: e[0]=0x44; e[1]=0x4d; break; + case Z80_IX: e[0]=e[2]=0xdd; e[1]=0x44; e[3]=0x4d; break; + case Z80_IY: e[0]=e[2]=0xfd; e[1]=0x44; e[3]=0x4d; break; + default: + if (oparen(lp,'[')) { + if ((reg=getz80reg(lp))==Z80_UNK) { + b=z80getword(lp); e[1]=0x4b; e[2]=b&255; e[3]=(b>>8)&255; if (cparen(lp)) e[0]=0xed; break; + } + } else { + if (oparen(lp,'(')) { + if ((reg=getz80reg(lp))==Z80_UNK) { + olp=--lp; + b=z80getword(lp); + if (getparen(olp)==lp) { e[0]=0xed; e[1]=0x4b; e[2]=b&255; e[3]=(b>>8)&255; } + else { e[0]=0x01; e[1]=b&255; e[2]=(b>>8)&255; } + } + } else { e[0]=0x01; b=z80getword(lp); e[1]=b&255; e[2]=(b>>8)&255; break; } + } + switch (reg) { + case Z80_HL: if (cparen(lp)) e[0]=0x4e; e[1]=0x23; e[2]=0x46; e[3]=0x2b; break; + case Z80_IX: case Z80_IY: + if ((b=z80getidxoffset(lp))==127) error("Offset out of range",0); + if (cparen(lp)) e[0]=e[3]=reg; e[1]=0x4e; e[4]=0x46; e[2]=b; e[5]=b+1; break; + default: break; + } + } + break; + + case Z80_DE: + if (!comma(lp)) break; + switch (getz80reg(lp)) { + case Z80_BC: e[0]=0x50; e[1]=0x59; break; + case Z80_DE: e[0]=0x52; e[1]=0x5b; break; + case Z80_HL: e[0]=0x54; e[1]=0x5d; break; + case Z80_IX: e[0]=e[2]=0xdd; e[1]=0x54; e[3]=0x5d; break; + case Z80_IY: e[0]=e[2]=0xfd; e[1]=0x54; e[3]=0x5d; break; + default: + if (oparen(lp,'[')) { + if ((reg=getz80reg(lp))==Z80_UNK) { + b=z80getword(lp); e[1]=0x5b; e[2]=b&255; e[3]=(b>>8)&255; if (cparen(lp)) e[0]=0xed; break; + } + } else { + if (oparen(lp,'(')) { + if ((reg=getz80reg(lp))==Z80_UNK) { + olp=--lp; + b=z80getword(lp); + if (getparen(olp)==lp) { e[0]=0xed; e[1]=0x5b; e[2]=b&255; e[3]=(b>>8)&255; } + else { e[0]=0x11; e[1]=b&255; e[2]=(b>>8)&255; } + } + } else { e[0]=0x11; b=z80getword(lp); e[1]=b&255; e[2]=(b>>8)&255; break; } + } + switch (reg) { + case Z80_HL: if (cparen(lp)) e[0]=0x5e; e[1]=0x23; e[2]=0x56; e[3]=0x2b; break; + case Z80_IX: case Z80_IY: + if ((b=z80getidxoffset(lp))==127) error("Offset out of range",0); + if (cparen(lp)) e[0]=e[3]=reg; e[1]=0x5e; e[4]=0x56; e[2]=b; e[5]=b+1; break; + default: break; + } + } + break; + + case Z80_HL: + if (!comma(lp)) break; + switch (getz80reg(lp)) { + case Z80_BC: e[0]=0x60; e[1]=0x69; break; + case Z80_DE: e[0]=0x62; e[1]=0x6b; break; + case Z80_HL: e[0]=0x64; e[1]=0x6d; break; + case Z80_IX: e[0]=0xdd; e[1]=0xe5; e[2]=0xe1; break; + case Z80_IY: e[0]=0xfd; e[1]=0xe5; e[2]=0xe1; break; + default: + if (oparen(lp,'[')) { + if ((reg=getz80reg(lp))==Z80_UNK) { + b=z80getword(lp); e[1]=b&255; e[2]=(b>>8)&255; if (cparen(lp)) e[0]=0x2a; break; + } + } else { + if (oparen(lp,'(')) { + if ((reg=getz80reg(lp))==Z80_UNK) { + olp=--lp; + b=z80getword(lp); + if (getparen(olp)==lp) { e[0]=0x2a; e[1]=b&255; e[2]=(b>>8)&255; } + else { e[0]=0x21; e[1]=b&255; e[2]=(b>>8)&255; } + } + } else { e[0]=0x21; b=z80getword(lp); e[1]=b&255; e[2]=(b>>8)&255; break; } + } + switch (reg) { + case Z80_IX: case Z80_IY: + if ((b=z80getidxoffset(lp))==127) error("Offset out of range",0); + if (cparen(lp)) e[0]=e[3]=reg; e[1]=0x6e; e[4]=0x66; e[2]=b; e[5]=b+1; break; + default: break; + } + } + break; + + case Z80_SP: + if (!comma(lp)) break; + switch (reg=getz80reg(lp)) { + case Z80_HL: e[0]=0xf9; break; + case Z80_IX: case Z80_IY: e[0]=reg; e[1]=0xf9; break; + default: + if (oparen(lp,'(') || oparen(lp,'[')) { + b=z80getword(lp); e[1]=0x7b; e[2]=b&255; e[3]=(b>>8)&255; if (cparen(lp)) e[0]=0xed; + } else { b=z80getword(lp); e[0]=0x31; e[1]=b&255; e[2]=(b>>8)&255; } + } + break; + + case Z80_IX: + if (!comma(lp)) break; + switch(reg=getz80reg(lp)) { + case Z80_BC: e[0]=e[2]=0xdd; e[1]=0x69; e[3]=0x60; break; + case Z80_DE: e[0]=e[2]=0xdd; e[1]=0x6b; e[3]=0x62; break; + case Z80_HL: e[0]=0xe5; e[1]=0xdd; e[2]=0xe1; break; + case Z80_IX: e[0]=e[2]=0xdd; e[1]=0x6d; e[3]=0x64; break; + case Z80_IY: e[0]=0xfd; e[1]=0xe5; e[2]=0xdd; e[3]=0xe1; break; + default: + if (oparen(lp,'[')) { + b=z80getword(lp); e[1]=0x2a; e[2]=b&255; e[3]=(b>>8)&255; if (cparen(lp)) e[0]=0xdd; break; + } + if (beginhaakje=oparen(lp,'(')) olp=--lp; + b=z80getword(lp); + if (beginhaakje && getparen(olp)==lp) { e[0]=0xdd; e[1]=0x2a; e[2]=b&255; e[3]=(b>>8)&255; } + else { e[0]=0xdd; e[1]=0x21; e[2]=b&255; e[3]=(b>>8)&255; } + break; + } + break; + + case Z80_IY: + if (!comma(lp)) break; + switch(reg=getz80reg(lp)) { + case Z80_BC: e[0]=e[2]=0xfd; e[1]=0x69; e[3]=0x60; break; + case Z80_DE: e[0]=e[2]=0xfd; e[1]=0x6b; e[3]=0x62; break; + case Z80_HL: e[0]=0xe5; e[1]=0xfd; e[2]=0xe1; break; + case Z80_IX: e[0]=0xdd; e[1]=0xe5; e[2]=0xfd; e[3]=0xe1; break; + case Z80_IY: e[0]=e[2]=0xfd; e[1]=0x6d; e[3]=0x64; break; + default: + if (oparen(lp,'[')) { + b=z80getword(lp); e[1]=0x2a; e[2]=b&255; e[3]=(b>>8)&255; if (cparen(lp)) e[0]=0xfd; break; + } + if (beginhaakje=oparen(lp,'(')) olp=--lp; + b=z80getword(lp); + if (beginhaakje && getparen(olp)==lp) { e[0]=0xfd; e[1]=0x2a; e[2]=b&255; e[3]=(b>>8)&255; } + else { e[0]=0xfd; e[1]=0x21; e[2]=b&255; e[3]=(b>>8)&255; } + break; + } + break; + + default: + if (!oparen(lp,'(') && !oparen(lp,'[')) break; + switch(getz80reg(lp)) { + case Z80_BC: + if (!cparen(lp)) break; + if (!comma(lp)) break; + if (getz80reg(lp)!=Z80_A) break; + e[0]=0x02; break; + case Z80_DE: + if (!cparen(lp)) break; + if (!comma(lp)) break; + if (getz80reg(lp)!=Z80_A) break; + e[0]=0x12; break; + case Z80_HL: + if (!cparen(lp)) break; + if (!comma(lp)) break; + switch (reg=getz80reg(lp)) { + case Z80_A: case Z80_B: case Z80_C: case Z80_D: case Z80_E: case Z80_H: case Z80_L: + e[0]=0x70+reg; break; + case Z80_I: case Z80_R: case Z80_F: + break; + case Z80_BC: e[0]=0x71; e[1]=0x23; e[2]=0x70; e[3]=0x2b; break; + case Z80_DE: e[0]=0x73; e[1]=0x23; e[2]=0x72; e[3]=0x2b; break; + case Z80_HL: case Z80_IX: case Z80_IY: break; + default: + e[0]=0x36; e[1]=z80getbyte(lp); + break; + } + break; + case Z80_IX: + e[2]=z80getidxoffset(lp); + if (!cparen(lp)) break; + if (!comma(lp)) break; + switch (reg=getz80reg(lp)) { + case Z80_A: case Z80_B: case Z80_C: case Z80_D: case Z80_E: case Z80_H: case Z80_L: + e[0]=0xdd; e[1]=0x70+reg; break; + case Z80_F: case Z80_I: case Z80_R: + case Z80_SP: case Z80_AF: case Z80_IX: case Z80_IY: + case Z80_IXL: case Z80_IXH: case Z80_IYL: case Z80_IYH: + break; + case Z80_BC: + if (e[2]==127) error("Offset out of range",0); + e[0]=e[3]=0xdd; e[1]=0x71; e[4]=0x70; e[5]=e[2]+1; break; + case Z80_DE: + if (e[2]==127) error("Offset out of range",0); + e[0]=e[3]=0xdd; e[1]=0x73; e[4]=0x72; e[5]=e[2]+1; break; + case Z80_HL: + if (e[2]==127) error("Offset out of range",0); + e[0]=e[3]=0xdd; e[1]=0x75; e[4]=0x74; e[5]=e[2]+1; break; + default: + e[0]=0xdd; e[1]=0x36; e[3]=z80getbyte(lp); + break; + } + break; + case Z80_IY: + e[2]=z80getidxoffset(lp); + if (!cparen(lp)) break; + if (!comma(lp)) break; + switch (reg=getz80reg(lp)) { + case Z80_A: case Z80_B: case Z80_C: case Z80_D: case Z80_E: case Z80_H: case Z80_L: + e[0]=0xfd; e[1]=0x70+reg; break; + case Z80_F: case Z80_I: case Z80_R: + case Z80_SP: case Z80_AF: case Z80_IX: case Z80_IY: + case Z80_IXL: case Z80_IXH: case Z80_IYL: case Z80_IYH: + break; + case Z80_BC: + if (e[2]==127) error("Offset out of range",0); + e[0]=e[3]=0xfd; e[1]=0x71; e[4]=0x70; e[5]=e[2]+1; break; + case Z80_DE: + if (e[2]==127) error("Offset out of range",0); + e[0]=e[3]=0xfd; e[1]=0x73; e[4]=0x72; e[5]=e[2]+1; break; + case Z80_HL: + if (e[2]==127) error("Offset out of range",0); + e[0]=e[3]=0xfd; e[1]=0x75; e[4]=0x74; e[5]=e[2]+1; break; + default: + e[0]=0xfd; e[1]=0x36; e[3]=z80getbyte(lp); + break; + } + break; + default: + b=z80getword(lp); + if (!cparen(lp)) break; + if (!comma(lp)) break; + switch (getz80reg(lp)) { + case Z80_A: e[0]=0x32; e[1]=b&255; e[2]=(b>>8)&255; break; + case Z80_BC: e[0]=0xed; e[1]=0x43; e[2]=b&255; e[3]=(b>>8)&255; break; + case Z80_DE: e[0]=0xed; e[1]=0x53; e[2]=b&255; e[3]=(b>>8)&255; break; + case Z80_HL: e[0]=0x22; e[1]=b&255; e[2]=(b>>8)&255; break; + case Z80_IX: e[0]=0xdd; e[1]=0x22; e[2]=b&255; e[3]=(b>>8)&255; break; + case Z80_IY: e[0]=0xfd; e[1]=0x22; e[2]=b&255; e[3]=(b>>8)&255; break; + case Z80_SP: e[0]=0xed; e[1]=0x73; e[2]=b&255; e[3]=(b>>8)&255; break; + default: break; + } + break; + } + break; + } + EmitBytes(e); +} + +void pizLDD() { + Z80Reg reg, reg2; + int e[7],b; + e[0]=e[1]=e[2]=e[3]=e[4]=e[5]=e[6]=-1; + switch(reg=getz80reg(lp)) { + case Z80_A: + if (!comma(lp)) break; + if (!oparen(lp,'[') && !oparen(lp,'(')) break; + switch(reg=getz80reg(lp)) { + case Z80_BC: if (cparen(lp)) e[0]=0x0a; e[1]=0x0b; break; + case Z80_DE: if (cparen(lp)) e[0]=0x1a; e[1]=0x1b; break; + case Z80_HL: if (cparen(lp)) e[0]=0x7e; e[1]=0x2b; break; + case Z80_IX: + case Z80_IY: e[1]=0x7e; e[2]=z80getidxoffset(lp); if (cparen(lp)) e[0]=e[3]=reg; e[4]=0x2b; break; + default: break; + } + break; + case Z80_B: case Z80_C: case Z80_D: case Z80_E: case Z80_H: case Z80_L: + if (!comma(lp)) break; + if (!oparen(lp,'[') && !oparen(lp,'(')) break; + switch(reg2=getz80reg(lp)) { + case Z80_HL: if (cparen(lp)) e[0]=0x46+reg*8; e[1]=0x2b; break; + case Z80_IX: + case Z80_IY: e[2]=z80getidxoffset(lp); if (cparen(lp)) e[0]=e[3]=reg2; e[1]=0x46+reg*8; e[4]=0x2b; break; + default: break; + } + break; + default: + if (oparen(lp,'[') || oparen(lp,'(')) { + reg=getz80reg(lp); + if (reg==Z80_IX || reg==Z80_IY) b=z80getidxoffset(lp); + if (!cparen(lp) || !comma(lp)) break; + switch(reg) { + case Z80_BC: case Z80_DE: + if (getz80reg(lp)==Z80_A) e[0]=reg-14; e[1]=reg-5; + break; + case Z80_HL: + switch (reg=getz80reg(lp)) { + case Z80_A: case Z80_B: case Z80_C: case Z80_D: case Z80_E: case Z80_H: case Z80_L: + e[0]=0x70+reg; e[1]=0x2b; break; + case Z80_UNK: + e[0]=0x36; e[1]=z80getbyte(lp); e[2]=0x2b; break; + default: break; + } + break; + case Z80_IX: case Z80_IY: + switch (reg2=getz80reg(lp)) { + case Z80_A: case Z80_B: case Z80_C: case Z80_D: case Z80_E: case Z80_H: case Z80_L: + e[0]=e[3]=reg; e[2]=b; e[1]=0x70+reg2; e[4]=0x2b; break; + case Z80_UNK: + e[0]=e[4]=reg; e[1]=0x36; e[2]=b; e[3]=z80getbyte(lp); e[5]=0x2b; break; + default: break; + } + break; + default: break; + } + } else { + e[0]=0xed; e[1]=0xa8; break; + } + } + EmitBytes(e); +} + +void pizLDDR() { + int e[3]; + e[0]=0xed; + e[1]=0xb8; + e[2]=-1; + EmitBytes(e); +} + +void pizLDI() { + Z80Reg reg, reg2; + int e[11],b; + e[0]=e[1]=e[2]=e[3]=e[4]=e[5]=e[6]=e[10]=-1; + switch(reg=getz80reg(lp)) { + case Z80_A: + if (!comma(lp)) break; + if (!oparen(lp,'[') && !oparen(lp,'(')) break; + switch(reg=getz80reg(lp)) { + case Z80_BC: if (cparen(lp)) e[0]=0x0a; e[1]=0x03; break; + case Z80_DE: if (cparen(lp)) e[0]=0x1a; e[1]=0x13; break; + case Z80_HL: if (cparen(lp)) e[0]=0x7e; e[1]=0x23; break; + case Z80_IX: + case Z80_IY: e[1]=0x7e; e[2]=z80getidxoffset(lp); if (cparen(lp)) e[0]=e[3]=reg; e[4]=0x23; break; + default: break; + } + break; + case Z80_B: case Z80_C: case Z80_D: case Z80_E: case Z80_H: case Z80_L: + if (!comma(lp)) break; + if (!oparen(lp,'[') && !oparen(lp,'(')) break; + switch(reg2=getz80reg(lp)) { + case Z80_HL: if (cparen(lp)) e[0]=0x46+reg*8; e[1]=0x23; break; + case Z80_IX: + case Z80_IY: e[2]=z80getidxoffset(lp); if (cparen(lp)) e[0]=e[3]=reg2; e[1]=0x46+reg*8; e[4]=0x23; break; + default: break; + } + break; + case Z80_BC: + if (!comma(lp)) break; + if (!oparen(lp,'[') && !oparen(lp,'(')) break; + switch(reg=getz80reg(lp)) { + case Z80_HL: if(cparen(lp)) e[0]=0x4e; e[1]=e[3]=0x23; e[2]=0x46; break; + case Z80_IX: case Z80_IY: + e[2]=e[7]=z80getidxoffset(lp); if (cparen(lp)) e[0]=e[3]=e[5]=e[8]=reg; + e[1]=0x4e; e[6]=0x46; e[4]=e[9]=0x23; break; + default: break; + } + break; + case Z80_DE: + if (!comma(lp)) break; + if (!oparen(lp,'[') && !oparen(lp,'(')) break; + switch(reg=getz80reg(lp)) { + case Z80_HL: if(cparen(lp)) e[0]=0x5e; e[1]=e[3]=0x23; e[2]=0x56; break; + case Z80_IX: case Z80_IY: + e[2]=e[7]=z80getidxoffset(lp); if (cparen(lp)) e[0]=e[3]=e[5]=e[8]=reg; + e[1]=0x5e; e[6]=0x56; e[4]=e[9]=0x23; break; + default: break; + } + break; + case Z80_HL: + if (!comma(lp)) break; + if (!oparen(lp,'[') && !oparen(lp,'(')) break; + switch(reg=getz80reg(lp)) { + case Z80_IX: case Z80_IY: + e[2]=e[7]=z80getidxoffset(lp); if (cparen(lp)) e[0]=e[3]=e[5]=e[8]=reg; + e[1]=0x6e; e[6]=0x66; e[4]=e[9]=0x23; break; + default: break; + } + break; + default: + if (oparen(lp,'[') || oparen(lp,'(')) { + reg=getz80reg(lp); + if (reg==Z80_IX || reg==Z80_IY) b=z80getidxoffset(lp); + if (!cparen(lp) || !comma(lp)) break; + switch(reg) { + case Z80_BC: case Z80_DE: + if (getz80reg(lp)==Z80_A) e[0]=reg-14; e[1]=reg-13; + break; + case Z80_HL: + switch (reg=getz80reg(lp)) { + case Z80_A: case Z80_B: case Z80_C: case Z80_D: case Z80_E: case Z80_H: case Z80_L: + e[0]=0x70+reg; e[1]=0x23; break; + case Z80_BC: e[0]=0x71; e[1]=e[3]=0x23; e[2]=0x70; break; + case Z80_DE: e[0]=0x73; e[1]=e[3]=0x23; e[2]=0x72; break; + case Z80_UNK: + e[0]=0x36; e[1]=z80getbyte(lp); e[2]=0x23; break; + default: break; + } + break; + case Z80_IX: case Z80_IY: + switch (reg2=getz80reg(lp)) { + case Z80_A: case Z80_B: case Z80_C: case Z80_D: case Z80_E: case Z80_H: case Z80_L: + e[0]=e[3]=reg; e[2]=b; e[1]=0x70+reg2; e[4]=0x23; break; + case Z80_BC: e[0]=e[3]=e[5]=e[8]=reg; e[1]=0x71; e[6]=0x70; e[4]=e[9]=0x23; e[2]=e[7]=b; break; + case Z80_DE: e[0]=e[3]=e[5]=e[8]=reg; e[1]=0x73; e[6]=0x72; e[4]=e[9]=0x23; e[2]=e[7]=b; break; + case Z80_HL: e[0]=e[3]=e[5]=e[8]=reg; e[1]=0x75; e[6]=0x74; e[4]=e[9]=0x23; e[2]=e[7]=b; break; + case Z80_UNK: + e[0]=e[4]=reg; e[1]=0x36; e[2]=b; e[3]=z80getbyte(lp); e[5]=0x23; break; + default: break; + } + break; + default: break; + } + } else { + e[0]=0xed; e[1]=0xa0; break; + } + } + EmitBytes(e); +} + +void pizLDIR() { + int e[3]; + e[0]=0xed; + e[1]=0xb0; + e[2]=-1; + EmitBytes(e); +} + +void pizMULUB() { + Z80Reg reg; + int e[3]; + e[0]=e[1]=e[2]=-1; + if ((reg=getz80reg(lp))==Z80_A && comma(lp)) reg=getz80reg(lp); + switch (reg) { + case Z80_B: case Z80_C: case Z80_D: case Z80_E: + case Z80_H: case Z80_L: case Z80_A: e[0]=0xed; e[1]=0xc1+reg*8; break; + default: ; + } + EmitBytes(e); +} + +void pizMULUW() { + Z80Reg reg; + int e[3]; + e[0]=e[1]=e[2]=-1; + if ((reg=getz80reg(lp))==Z80_HL && comma(lp)) reg=getz80reg(lp); + switch (reg) { + case Z80_BC: case Z80_SP: e[0]=0xed; e[1]=0xb3+reg; break; + default: ; + } + EmitBytes(e); +} + +void pizNEG() { + int e[3]; + e[0]=0xed; + e[1]=0x44; + e[2]=-1; + EmitBytes(e); +} + +void pizNOP() { + EmitByte(0x0); +} + +void pizOR() { + Z80Reg reg; + int e[4]; + e[0]=e[1]=e[2]=e[3]=-1; + switch (reg=getz80reg(lp)) { + case Z80_A: + if (!comma(lp)) { e[0]=0xb7; break; } + reg=getz80reg(lp); + default: + switch (reg) { + case Z80_IXH: e[0]=0xdd; e[1]=0xb4; break; + case Z80_IXL: e[0]=0xdd; e[1]=0xb5; break; + case Z80_IYH: e[0]=0xfd; e[1]=0xb4; break; + case Z80_IYL: e[0]=0xfd; e[1]=0xb5; break; + case Z80_B: case Z80_C: case Z80_D: case Z80_E: + case Z80_H: case Z80_L: case Z80_A: e[0]=0xb0+reg; break; + case Z80_F: case Z80_I: case Z80_R: + case Z80_AF: case Z80_BC: case Z80_DE: case Z80_HL: case Z80_SP: + case Z80_IX: case Z80_IY: + break; + default: + reg=Z80_UNK; + if (oparen(lp,'[')) { + if ((reg=getz80reg(lp))==Z80_UNK) break; + } else if (oparen(lp,'(')) { + if ((reg=getz80reg(lp))==Z80_UNK) --lp; + } + switch (reg) { + case Z80_HL: + if (cparen(lp)) e[0]=0xb6; + break; + case Z80_IX: case Z80_IY: + e[1]=0xb6; e[2]=z80getidxoffset(lp); + if (cparen(lp)) e[0]=reg; + break; + default: e[0]=0xf6; e[1]=z80getbyte(lp); break; + } + } + } + EmitBytes(e); +} + +void pizOTDR() { + int e[3]; + e[0]=0xed; + e[1]=0xbb; + e[2]=-1; + EmitBytes(e); +} + +void pizOTIR() { + int e[3]; + e[0]=0xed; + e[1]=0xb3; + e[2]=-1; + EmitBytes(e); +} + +void pizOUT() { + Z80Reg reg; + int e[3]; + e[0]=e[1]=e[2]=-1; + if (oparen(lp,'[') || oparen(lp,'(')) { + if (getz80reg(lp)==Z80_C) { + if (cparen(lp)) + if (comma(lp)) + switch (reg=getz80reg(lp)) { + case Z80_A: e[0]=0xed; e[1]=0x79; break; + case Z80_B: e[0]=0xed; e[1]=0x41; break; + case Z80_C: e[0]=0xed; e[1]=0x49; break; + case Z80_D: e[0]=0xed; e[1]=0x51; break; + case Z80_E: e[0]=0xed; e[1]=0x59; break; + case Z80_H: e[0]=0xed; e[1]=0x61; break; + case Z80_L: e[0]=0xed; e[1]=0x69; break; + default: + if (!z80getbyte(lp)) e[0]=0xed; e[1]=0x71; break; + } + } + else { + e[1]=z80getbyte(lp); + if (cparen(lp)) + if (comma(lp)) + if (getz80reg(lp)==Z80_A) e[0]=0xd3; + } + } + EmitBytes(e); +} + +void pizOUTD() { + int e[3]; + e[0]=0xed; + e[1]=0xab; + e[2]=-1; + EmitBytes(e); +} + +void pizOUTI() { + int e[3]; + e[0]=0xed; + e[1]=0xa3; + e[2]=-1; + EmitBytes(e); +} + +void pizPOPoriginal() { + int e[30],t=29,c=1; + e[t]=-1; + do { + switch (getz80reg(lp)) { + case Z80_AF: e[--t]=0xf1; break; + case Z80_BC: e[--t]=0xc1; break; + case Z80_DE: e[--t]=0xd1; break; + case Z80_HL: e[--t]=0xe1; break; + case Z80_IX: e[--t]=0xe1; e[--t]=0xdd; break; + case Z80_IY: e[--t]=0xe1; e[--t]=0xfd; break; + default: c=0; break; + } + if (!comma(lp) || t<2) c=0; + } while (c); + EmitBytes(&e[t]); +} + +void pizPOPreversed() { + int e[30], t = 0, c = 1; + do { + switch (getz80reg(lp)) { + case Z80_AF: e[t++] = 0xf1; break; + case Z80_BC: e[t++] = 0xc1; break; + case Z80_DE: e[t++] = 0xd1; break; + case Z80_HL: e[t++] = 0xe1; break; + case Z80_IX: e[t++] = 0xdd; e[t++] = 0xe1; break; + case Z80_IY: e[t++] = 0xfd; e[t++] = 0xe1; break; + default: c = 0; break; + } + if (!comma(lp) || t>27) c = 0; + } while (c); + e[t] = -1; + EmitBytes(e); +} + +void pizPOP() { + if(compassCompatibilityEnabled) + pizPOPreversed(); + else + pizPOPoriginal(); +} + +void pizPUSH() { + int e[30],t=0,c=1; + do { + switch (getz80reg(lp)) { + case Z80_AF: e[t++]=0xf5; break; + case Z80_BC: e[t++]=0xc5; break; + case Z80_DE: e[t++]=0xd5; break; + case Z80_HL: e[t++]=0xe5; break; + case Z80_IX: e[t++]=0xdd; e[t++]=0xe5; break; + case Z80_IY: e[t++]=0xfd; e[t++]=0xe5; break; + default: c=0; break; + } + if (!comma(lp) || t>27) c=0; + } while (c); + e[t]=-1; + EmitBytes(e); +} + +void pizRES() { + Z80Reg reg; + int e[5],bit; + e[0]=e[1]=e[2]=e[3]=e[4]=-1; + bit=z80getbyte(lp); + if (!comma(lp)) bit=-1; + switch (reg=getz80reg(lp)) { + case Z80_B: case Z80_C: case Z80_D: case Z80_E: + case Z80_H: case Z80_L: case Z80_A: + e[0]=0xcb; e[1]=8*bit+0x80+reg ; break; + default: + if (!oparen(lp,'[') && !oparen(lp,'(')) break; + switch (reg=getz80reg(lp)) { + case Z80_HL: + if (cparen(lp)) e[0]=0xcb; + e[1]=8*bit+0x86; break; + case Z80_IX: case Z80_IY: + e[1]=0xcb; e[2]=z80getidxoffset(lp); e[3]=8*bit+0x86; + if (cparen(lp)) e[0]=reg; + if (comma(lp)) { + switch(reg=getz80reg(lp)) { + case Z80_B: case Z80_C: case Z80_D: case Z80_E: + case Z80_H: case Z80_L: case Z80_A: + e[3]=8*bit+0x80+reg; + break; + default: error("Illegal operand",lp,SUPPRES); + } + } + break; + default: ; + } + } + if (bit<0 || bit>7) e[0]=-1; + EmitBytes(e); +} + +void pizRET() { + int e; + switch (getz80cond(lp)) { + case Z80C_C: e=0xd8; break; + case Z80C_M: e=0xf8; break; + case Z80C_NC: e=0xd0; break; + case Z80C_NZ: e=0xc0; break; + case Z80C_P: e=0xf0; break; + case Z80C_PE: e=0xe8; break; + case Z80C_PO: e=0xe0; break; + case Z80C_Z: e=0xc8; break; + default: e=0xc9; break; + } + EmitByte(e); +} + +void pizRETI() { + int e[3]; + e[0]=0xed; + e[1]=0x4d; + e[2]=-1; + EmitBytes(e); +} + +void pizRETN() { + int e[3]; + e[0]=0xed; + e[1]=0x45; + e[2]=-1; + EmitBytes(e); +} + +void pizRL() { + Z80Reg reg; + int e[5]; + e[0]=e[1]=e[2]=e[3]=e[4]=-1; + switch (reg=getz80reg(lp)) { + case Z80_B: case Z80_C: case Z80_D: case Z80_E: + case Z80_H: case Z80_L: case Z80_A: + e[0]=0xcb; e[1]=0x10+reg ; break; + case Z80_BC: + e[0]=e[2]=0xcb; e[1]=0x11; e[3]=0x10; break; + case Z80_DE: + e[0]=e[2]=0xcb; e[1]=0x13; e[3]=0x12; break; + case Z80_HL: + e[0]=e[2]=0xcb; e[1]=0x15; e[3]=0x14; break; + default: + if (!oparen(lp,'[') && !oparen(lp,'(')) break; + switch (reg=getz80reg(lp)) { + case Z80_HL: + if (cparen(lp)) e[0]=0xcb; + e[1]=0x16; break; + case Z80_IX: case Z80_IY: + e[1]=0xcb; e[2]=z80getidxoffset(lp); e[3]=0x16; + if (cparen(lp)) e[0]=reg; + if (comma(lp)) { + switch(reg=getz80reg(lp)) { + case Z80_B: case Z80_C: case Z80_D: case Z80_E: + case Z80_H: case Z80_L: case Z80_A: + e[3]=0x10+reg; + break; + default: error("Illegal operand",lp,SUPPRES); + } + } + break; + default: ; + } + } + EmitBytes(e); +} + +void pizRLA() { + EmitByte(0x17); +} + +void pizRLC() { + Z80Reg reg; + int e[5]; + e[0]=e[1]=e[2]=e[3]=e[4]=-1; + switch (reg=getz80reg(lp)) { + case Z80_B: case Z80_C: case Z80_D: case Z80_E: + case Z80_H: case Z80_L: case Z80_A: + e[0]=0xcb; e[1]=0x0+reg ; break; + default: + if (!oparen(lp,'[') && !oparen(lp,'(')) break; + switch (reg=getz80reg(lp)) { + case Z80_HL: + if (cparen(lp)) e[0]=0xcb; + e[1]=0x6; break; + case Z80_IX: case Z80_IY: + e[1]=0xcb; e[2]=z80getidxoffset(lp); e[3]=0x6; + if (cparen(lp)) e[0]=reg; + if (comma(lp)) { + switch(reg=getz80reg(lp)) { + case Z80_B: case Z80_C: case Z80_D: case Z80_E: + case Z80_H: case Z80_L: case Z80_A: + e[3]=reg; + break; + default: error("Illegal operand",lp,SUPPRES); + } + } + break; + default: ; + } + } + EmitBytes(e); +} + +void pizRLCA() { + EmitByte(0x7); +} + +void pizRLD() { + int e[3]; + e[0]=0xed; + e[1]=0x6f; + e[2]=-1; + EmitBytes(e); +} + +void pizRR() { + Z80Reg reg; + int e[5]; + e[0]=e[1]=e[2]=e[3]=e[4]=-1; + switch (reg=getz80reg(lp)) { + case Z80_B: case Z80_C: case Z80_D: case Z80_E: + case Z80_H: case Z80_L: case Z80_A: + e[0]=0xcb; e[1]=0x18+reg ; break; + case Z80_BC: + e[0]=e[2]=0xcb; e[1]=0x18; e[3]=0x19; break; + case Z80_DE: + e[0]=e[2]=0xcb; e[1]=0x1a; e[3]=0x1b; break; + case Z80_HL: + e[0]=e[2]=0xcb; e[1]=0x1c; e[3]=0x1d; break; + default: + if (!oparen(lp,'[') && !oparen(lp,'(')) break; + switch (reg=getz80reg(lp)) { + case Z80_HL: + if (cparen(lp)) e[0]=0xcb; + e[1]=0x1e; break; + case Z80_IX: case Z80_IY: + e[1]=0xcb; e[2]=z80getidxoffset(lp); e[3]=0x1e; + if (cparen(lp)) e[0]=reg; + if (comma(lp)) { + switch(reg=getz80reg(lp)) { + case Z80_B: case Z80_C: case Z80_D: case Z80_E: + case Z80_H: case Z80_L: case Z80_A: + e[3]=0x18+reg; + break; + default: error("Illegal operand",lp,SUPPRES); + } + } + break; + default: ; + } + } + EmitBytes(e); +} + +void pizRRA() { + EmitByte(0x1f); +} + +void pizRRC() { + Z80Reg reg; + int e[5]; + e[0]=e[1]=e[2]=e[3]=e[4]=-1; + switch (reg=getz80reg(lp)) { + case Z80_B: case Z80_C: case Z80_D: case Z80_E: + case Z80_H: case Z80_L: case Z80_A: + e[0]=0xcb; e[1]=0x8+reg ; break; + default: + if (!oparen(lp,'[') && !oparen(lp,'(')) break; + switch (reg=getz80reg(lp)) { + case Z80_HL: + if (cparen(lp)) e[0]=0xcb; + e[1]=0xe; break; + case Z80_IX: case Z80_IY: + e[1]=0xcb; e[2]=z80getidxoffset(lp); e[3]=0xe; + if (cparen(lp)) e[0]=reg; + if (comma(lp)) { + switch(reg=getz80reg(lp)) { + case Z80_B: case Z80_C: case Z80_D: case Z80_E: + case Z80_H: case Z80_L: case Z80_A: + e[3]=0x8+reg; + break; + default: error("Illegal operand",lp,SUPPRES); + } + } + break; + default: ; + } + } + EmitBytes(e); +} + +void pizRRCA() { + EmitByte(0xf); +} + +void pizRRD() { + int e[3]; + e[0]=0xed; + e[1]=0x67; + e[2]=-1; + EmitBytes(e); +} + +void pizRST() { + int e; + switch(z80getbyte(lp)) { + case 0x00: e=0xc7; break; + case 0x08: e=0xcf; break; + case 0x10: e=0xd7; break; + case 0x18: e=0xdf; break; + case 0x20: e=0xe7; break; + case 0x28: e=0xef; break; + case 0x30: e=0xf7; break; + case 0x38: e=0xff; break; + default: error("Illegal operand",line); *lp=0; return; + } + EmitByte(e); +} + +void pizSBC() { + Z80Reg reg; + int e[4]; + e[0]=e[1]=e[2]=e[3]=-1; + switch (reg=getz80reg(lp)) { + case Z80_HL: + if (!comma(lp)) { error("Comma expected",0); break; } + switch (getz80reg(lp)) { + case Z80_BC: e[0]=0xed; e[1]=0x42; break; + case Z80_DE: e[0]=0xed; e[1]=0x52; break; + case Z80_HL: e[0]=0xed; e[1]=0x62; break; + case Z80_SP: e[0]=0xed; e[1]=0x72; break; + default: ; + } + break; + case Z80_A: + if (!comma(lp)) { e[0]=0x9f; break; } + reg=getz80reg(lp); + default: + switch (reg) { + case Z80_IXH: e[0]=0xdd; e[1]=0x9c; break; + case Z80_IXL: e[0]=0xdd; e[1]=0x9d; break; + case Z80_IYH: e[0]=0xfd; e[1]=0x9c; break; + case Z80_IYL: e[0]=0xfd; e[1]=0x9d; break; + case Z80_B: case Z80_C: case Z80_D: case Z80_E: + case Z80_H: case Z80_L: case Z80_A: e[0]=0x98+reg; break; + case Z80_F: case Z80_I: case Z80_R: + case Z80_AF: case Z80_BC: case Z80_DE: case Z80_HL: case Z80_SP: + case Z80_IX: case Z80_IY: + break; + default: + reg=Z80_UNK; + if (oparen(lp,'[')) { + if ((reg=getz80reg(lp))==Z80_UNK) break; + } else if (oparen(lp,'(')) { + if ((reg=getz80reg(lp))==Z80_UNK) --lp; + } + switch (reg) { + case Z80_HL: + if (cparen(lp)) e[0]=0x9e; + break; + case Z80_IX: case Z80_IY: + e[1]=0x9e; e[2]=z80getidxoffset(lp); + if (cparen(lp)) e[0]=reg; + break; + default: e[0]=0xde; e[1]=z80getbyte(lp); break; + } + } + } + EmitBytes(e); +} + +void pizSCF() { + EmitByte(0x37); +} + +void pizSET() { + Z80Reg reg; + int e[5],bit; + e[0]=e[1]=e[2]=e[3]=e[4]=-1; + bit=z80getbyte(lp); + if (!comma(lp)) bit=-1; + switch (reg=getz80reg(lp)) { + case Z80_B: case Z80_C: case Z80_D: case Z80_E: + case Z80_H: case Z80_L: case Z80_A: + e[0]=0xcb; e[1]=8*bit+0xc0+reg ; break; + default: + if (!oparen(lp,'[') && !oparen(lp,'(')) break; + switch (reg=getz80reg(lp)) { + case Z80_HL: + if (cparen(lp)) e[0]=0xcb; + e[1]=8*bit+0xc6; break; + case Z80_IX: case Z80_IY: + e[1]=0xcb; e[2]=z80getidxoffset(lp); e[3]=8*bit+0xc6; + if (cparen(lp)) e[0]=reg; + if (comma(lp)) { + switch(reg=getz80reg(lp)) { + case Z80_B: case Z80_C: case Z80_D: case Z80_E: + case Z80_H: case Z80_L: case Z80_A: + e[3]=8*bit+0xc0+reg; + break; + default: error("Illegal operand",lp,SUPPRES); + } + } + break; + default: ; + } + } + if (bit<0 || bit>7) e[0]=-1; + EmitBytes(e); +} + +void pizSLA() { + Z80Reg reg; + int e[5]; + e[0]=e[1]=e[2]=e[3]=e[4]=-1; + switch (reg=getz80reg(lp)) { + case Z80_B: case Z80_C: case Z80_D: case Z80_E: + case Z80_H: case Z80_L: case Z80_A: + e[0]=0xcb; e[1]=0x20+reg ; break; + case Z80_BC: + e[0]=e[2]=0xcb; e[1]=0x21; e[3]=0x10; break; + case Z80_DE: + e[0]=e[2]=0xcb; e[1]=0x23; e[3]=0x12; break; + case Z80_HL: + e[0]=0x29; break; + default: + if (!oparen(lp,'[') && !oparen(lp,'(')) break; + switch (reg=getz80reg(lp)) { + case Z80_HL: + if (cparen(lp)) e[0]=0xcb; + e[1]=0x26; break; + case Z80_IX: case Z80_IY: + e[1]=0xcb; e[2]=z80getidxoffset(lp); e[3]=0x26; + if (cparen(lp)) e[0]=reg; + if (comma(lp)) { + switch(reg=getz80reg(lp)) { + case Z80_B: case Z80_C: case Z80_D: case Z80_E: + case Z80_H: case Z80_L: case Z80_A: + e[3]=0x20+reg; + break; + default: error("Illegal operand",lp,SUPPRES); + } + } + break; + default: ; + } + } + EmitBytes(e); +} + +void pizSLL() { + Z80Reg reg; + int e[5]; + e[0]=e[1]=e[2]=e[3]=e[4]=-1; + switch (reg=getz80reg(lp)) { + case Z80_B: case Z80_C: case Z80_D: case Z80_E: + case Z80_H: case Z80_L: case Z80_A: + e[0]=0xcb; e[1]=0x30+reg ; break; + case Z80_BC: + e[0]=e[2]=0xcb; e[1]=0x31; e[3]=0x10; break; + case Z80_DE: + e[0]=e[2]=0xcb; e[1]=0x33; e[3]=0x12; break; + case Z80_HL: + e[0]=e[2]=0xcb; e[1]=0x35; e[3]=0x14; break; + default: + if (!oparen(lp,'[') && !oparen(lp,'(')) break; + switch (reg=getz80reg(lp)) { + case Z80_HL: + if (cparen(lp)) e[0]=0xcb; + e[1]=0x36; break; + case Z80_IX: case Z80_IY: + e[1]=0xcb; e[2]=z80getidxoffset(lp); e[3]=0x36; + if (cparen(lp)) e[0]=reg; + if (comma(lp)) { + switch(reg=getz80reg(lp)) { + case Z80_B: case Z80_C: case Z80_D: case Z80_E: + case Z80_H: case Z80_L: case Z80_A: + e[3]=0x30+reg; + break; + default: error("Illegal operand",lp,SUPPRES); + } + } + break; + default: ; + } + } + EmitBytes(e); +} + +void pizSRA() { + Z80Reg reg; + int e[5]; + e[0]=e[1]=e[2]=e[3]=e[4]=-1; + switch (reg=getz80reg(lp)) { + case Z80_B: case Z80_C: case Z80_D: case Z80_E: + case Z80_H: case Z80_L: case Z80_A: + e[0]=0xcb; e[1]=0x28+reg ; break; + case Z80_BC: + e[0]=e[2]=0xcb; e[1]=0x28; e[3]=0x19; break; + case Z80_DE: + e[0]=e[2]=0xcb; e[1]=0x2a; e[3]=0x1b; break; + case Z80_HL: + e[0]=e[2]=0xcb; e[1]=0x2c; e[3]=0x1d; break; + default: + if (!oparen(lp,'[') && !oparen(lp,'(')) break; + switch (reg=getz80reg(lp)) { + case Z80_HL: + if (cparen(lp)) e[0]=0xcb; + e[1]=0x2e; break; + case Z80_IX: case Z80_IY: + e[1]=0xcb; e[2]=z80getidxoffset(lp); e[3]=0x2e; + if (cparen(lp)) e[0]=reg; + if (comma(lp)) { + switch(reg=getz80reg(lp)) { + case Z80_B: case Z80_C: case Z80_D: case Z80_E: + case Z80_H: case Z80_L: case Z80_A: + e[3]=0x28+reg; + break; + default: error("Illegal operand",lp,SUPPRES); + } + } + break; + default: ; + } + } + EmitBytes(e); +} + +void pizSRL() { + Z80Reg reg; + int e[5]; + e[0]=e[1]=e[2]=e[3]=e[4]=-1; + switch (reg=getz80reg(lp)) { + case Z80_B: case Z80_C: case Z80_D: case Z80_E: + case Z80_H: case Z80_L: case Z80_A: + e[0]=0xcb; e[1]=0x38+reg ; break; + case Z80_BC: + e[0]=e[2]=0xcb; e[1]=0x38; e[3]=0x19; break; + case Z80_DE: + e[0]=e[2]=0xcb; e[1]=0x3a; e[3]=0x1b; break; + case Z80_HL: + e[0]=e[2]=0xcb; e[1]=0x3c; e[3]=0x1d; break; + default: + if (!oparen(lp,'[') && !oparen(lp,'(')) break; + switch (reg=getz80reg(lp)) { + case Z80_HL: + if (cparen(lp)) e[0]=0xcb; + e[1]=0x3e; break; + case Z80_IX: case Z80_IY: + e[1]=0xcb; e[2]=z80getidxoffset(lp); e[3]=0x3e; + if (cparen(lp)) e[0]=reg; + if (comma(lp)) { + switch(reg=getz80reg(lp)) { + case Z80_B: case Z80_C: case Z80_D: case Z80_E: + case Z80_H: case Z80_L: case Z80_A: + e[3]=0x38+reg; + break; + default: error("Illegal operand",lp,SUPPRES); + } + } + break; + default: ; + } + } + EmitBytes(e); +} + +void pizSUB() { + Z80Reg reg; + int e[4]; + e[0]=e[1]=e[2]=e[3]=-1; + switch (reg=getz80reg(lp)) { + case Z80_HL: + if (!needcomma(lp)) break; + switch (getz80reg(lp)) { + case Z80_BC: e[0]=0xb7; e[1]=0xed; e[2]=0x42; break; + case Z80_DE: e[0]=0xb7; e[1]=0xed; e[2]=0x52; break; + case Z80_HL: e[0]=0xb7; e[1]=0xed; e[2]=0x62; break; + case Z80_SP: e[0]=0xb7; e[1]=0xed; e[2]=0x72; break; + } + break; + case Z80_A: + if (!comma(lp)) { e[0]=0x97; break; } + reg=getz80reg(lp); + default: + switch (reg) { + case Z80_IXH: e[0]=0xdd; e[1]=0x94; break; + case Z80_IXL: e[0]=0xdd; e[1]=0x95; break; + case Z80_IYH: e[0]=0xfd; e[1]=0x94; break; + case Z80_IYL: e[0]=0xfd; e[1]=0x95; break; + case Z80_B: case Z80_C: case Z80_D: case Z80_E: + case Z80_H: case Z80_L: case Z80_A: e[0]=0x90+reg; break; + case Z80_F: case Z80_I: case Z80_R: + case Z80_AF: case Z80_BC: case Z80_DE: case Z80_HL: case Z80_SP: + case Z80_IX: case Z80_IY: + break; + default: + reg=Z80_UNK; + if (oparen(lp,'[')) { + if ((reg=getz80reg(lp))==Z80_UNK) break; + } else if (oparen(lp,'(')) { + if ((reg=getz80reg(lp))==Z80_UNK) --lp; + } + switch (reg) { + case Z80_HL: + if (cparen(lp)) e[0]=0x96; + break; + case Z80_IX: case Z80_IY: + e[1]=0x96; e[2]=z80getidxoffset(lp); + if (cparen(lp)) e[0]=reg; + break; + default: e[0]=0xd6; e[1]=z80getbyte(lp); break; + } + } + } + EmitBytes(e); +} + +void pizXOR() { + Z80Reg reg; + int e[4]; + e[0]=e[1]=e[2]=e[3]=-1; + switch (reg=getz80reg(lp)) { + case Z80_A: + if (!comma(lp)) { e[0]=0xaf; break; } + reg=getz80reg(lp); + default: + switch (reg) { + case Z80_IXH: e[0]=0xdd; e[1]=0xac; break; + case Z80_IXL: e[0]=0xdd; e[1]=0xad; break; + case Z80_IYH: e[0]=0xfd; e[1]=0xac; break; + case Z80_IYL: e[0]=0xfd; e[1]=0xad; break; + case Z80_B: case Z80_C: case Z80_D: case Z80_E: + case Z80_H: case Z80_L: case Z80_A: e[0]=0xa8+reg; break; + case Z80_F: case Z80_I: case Z80_R: + case Z80_AF: case Z80_BC: case Z80_DE: case Z80_HL: case Z80_SP: + case Z80_IX: case Z80_IY: + break; + default: + reg=Z80_UNK; + if (oparen(lp,'[')) { + if ((reg=getz80reg(lp))==Z80_UNK) break; + } else if (oparen(lp,'(')) { + if ((reg=getz80reg(lp))==Z80_UNK) --lp; + } + switch (reg) { + case Z80_HL: + if (cparen(lp)) e[0]=0xae; + break; + case Z80_IX: case Z80_IY: + e[1]=0xae; e[2]=z80getidxoffset(lp); + if (cparen(lp)) e[0]=reg; + break; + default: e[0]=0xee; e[1]=z80getbyte(lp); break; + } + } + } + EmitBytes(e); +} + +void InitpiZ80() { + z80funtab.insert("adc",pizADC); + z80funtab.insert("add",pizADD); + z80funtab.insert("and",pizAND); + z80funtab.insert("bit",pizBIT); + z80funtab.insert("call",pizCALL); + z80funtab.insert("ccf",pizCCF); + z80funtab.insert("cp",pizCP); + z80funtab.insert("cpd",pizCPD); + z80funtab.insert("cpdr",pizCPDR); + z80funtab.insert("cpi",pizCPI); + z80funtab.insert("cpir",pizCPIR); + z80funtab.insert("cpl",pizCPL); + z80funtab.insert("daa",pizDAA); + z80funtab.insert("dec",pizDEC); + z80funtab.insert("di",pizDI); + z80funtab.insert("djnz",pizDJNZ); + z80funtab.insert("ei",pizEI); + z80funtab.insert("ex",pizEX); + z80funtab.insert("exx",pizEXX); + z80funtab.insert("halt",pizHALT); + z80funtab.insert("im",pizIM); + z80funtab.insert("in",pizIN); + z80funtab.insert("inc",pizINC); + z80funtab.insert("ind",pizIND); + z80funtab.insert("indr",pizINDR); + z80funtab.insert("ini",pizINI); + z80funtab.insert("inir",pizINIR); + z80funtab.insert("jp",pizJP); + z80funtab.insert("jr",pizJR); + z80funtab.insert("ld",pizLD); + z80funtab.insert("ldd",pizLDD); + z80funtab.insert("lddr",pizLDDR); + z80funtab.insert("ldi",pizLDI); + z80funtab.insert("ldir",pizLDIR); + z80funtab.insert("mulub",pizMULUB); + z80funtab.insert("muluw",pizMULUW); + z80funtab.insert("neg",pizNEG); + z80funtab.insert("nop",pizNOP); + z80funtab.insert("or",pizOR); + z80funtab.insert("otdr",pizOTDR); + z80funtab.insert("otir",pizOTIR); + z80funtab.insert("out",pizOUT); + z80funtab.insert("outd",pizOUTD); + z80funtab.insert("outi",pizOUTI); + z80funtab.insert("pop",pizPOP); + z80funtab.insert("push",pizPUSH); + z80funtab.insert("res",pizRES); + z80funtab.insert("ret",pizRET); + z80funtab.insert("reti",pizRETI); + z80funtab.insert("retn",pizRETN); + z80funtab.insert("rl",pizRL); + z80funtab.insert("rla",pizRLA); + z80funtab.insert("rlc",pizRLC); + z80funtab.insert("rlca",pizRLCA); + z80funtab.insert("rld",pizRLD); + z80funtab.insert("rr",pizRR); + z80funtab.insert("rra",pizRRA); + z80funtab.insert("rrc",pizRRC); + z80funtab.insert("rrca",pizRRCA); + z80funtab.insert("rrd",pizRRD); + z80funtab.insert("rst",pizRST); + z80funtab.insert("sbc",pizSBC); + z80funtab.insert("scf",pizSCF); + z80funtab.insert("set",pizSET); + z80funtab.insert("sla",pizSLA); + z80funtab.insert("sli",pizSLL); + z80funtab.insert("sll",pizSLL); + z80funtab.insert("sra",pizSRA); + z80funtab.insert("srl",pizSRL); + z80funtab.insert("sub",pizSUB); + z80funtab.insert("xor",pizXOR); +} + +void Initpi() { + InitpiZ80(); +#ifdef METARM + InitpiARM(); + InitpiTHUMB(); +#endif + InsertDirectives(); +} +//eof piz80.cpp diff --git a/tools/sjasm/piz80.h b/tools/sjasm/piz80.h new file mode 100644 index 00000000..d3a5be37 --- /dev/null +++ b/tools/sjasm/piz80.h @@ -0,0 +1,34 @@ +/* + + SjASM Z80 Assembler + + Copyright (c) 2015 Konamiman + Based on Sjasm 0.39g6 - Copyright (c) 2006 Sjoerd Mastijn + + This software is provided 'as-is', without any express or implied warranty. + In no event will the authors be held liable for any damages arising from the + use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it freely, + subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not claim + that you wrote the original software. If you use this software in a product, + an acknowledgment in the product documentation would be appreciated but is + not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source distribution. + +*/ + +// piz80.h + +void piZ80(); +void InitpiZ80(); +void Initpi(); +//eof piz80.h + diff --git a/tools/sjasm/reader.cpp b/tools/sjasm/reader.cpp new file mode 100644 index 00000000..25db424a --- /dev/null +++ b/tools/sjasm/reader.cpp @@ -0,0 +1,474 @@ +/* + + SjASM Z80 Assembler + + Copyright (c) 2015 Konamiman + Based on Sjasm 0.39g6 - Copyright (c) 2006 Sjoerd Mastijn + + This software is provided 'as-is', without any express or implied warranty. + In no event will the authors be held liable for any damages arising from the + use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it freely, + subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not claim + that you wrote the original software. If you use this software in a product, + an acknowledgment in the product documentation would be appreciated but is + not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source distribution. + +*/ + +// reader.cpp + +#include "sjasm.h" + +int cmphstr(char *&p1, char *p2) { + int i=0; + if (isupper(*p1)) + while (p2[i]) { + if (p1[i]!=toupper(p2[i])) return 0; + ++i; + } + else + while (p2[i]) { + if (p1[i]!=p2[i]) return 0; + ++i; + } + if (p1[i]>' ') return 0; + p1+=i; + return 1; +} + +int white() { + return (*lp && *lp<=' '); +} + +int skipblanks() { + while(*lp && *lp<=' ') ++lp; + return (*lp==0); +} + +void skipblanks(char *&p) { + while (*p && *p<=' ') ++p; +} + +int needequ() { + char *olp=lp; + skipblanks(); + if (*lp=='=') { ++lp; return 1; } + if (*lp=='.') ++lp; + if (cmphstr(lp,"equ")) return 1; + lp=olp; + return 0; +} + +int needfield() { + char *olp=lp; + skipblanks(); + if (*lp=='#') { ++lp; return 1; } + if (*lp=='.') ++lp; + if (cmphstr(lp,"field")) return 1; + lp=olp; + return 0; +} + +int comma(char *&p) { + skipblanks(p); + if (*p!=',') return 0; + ++p; return 1; +} + +int cpc='4'; + +int oparen(char *&p, char c) { + skipblanks(p); + if (*p!=c) return 0; + if (c=='[') cpc=']'; + if (c=='(') cpc=')'; + if (c=='{') cpc='}'; + ++p; return 1; +} + +int cparen(char *&p) { + skipblanks(p); + if (*p!=cpc) return 0; + ++p; return 1; +} + +char *getparen(char *p) { + int teller=0; + skipblanks(p); + while (*p) { + if (*p=='(') ++teller; + else if (*p==')') + if (teller==1) { skipblanks(++p); return p; } else --teller; + ++p; + } + return 0; +} + +char *getid(char *&p) { + char nid[LINEMAX],*np; + np=nid; + skipblanks(p); + if (!isalpha(*p) && *p!='_') return 0; + while(*p) { + if (IsNotValidIdChar(*p)) break; + *np=*p; ++p; ++np; + } + *np=0; + return strdup(nid); +} + +char *getinstr(char *&p) { + char nid[LINEMAX],*np; + np=nid; + skipblanks(p); + if (!isalpha(*p) && *p!='.') return 0; else { *np=*p; ++p; ++np; } + while(*p) { + if (!isalnum(*p)) break; + *np=*p; ++p; ++np; + } + *np=0; + return strdup(nid); +} + +int check8(unsigned aint val) { + if (val!=(val&255) && ~val>127) { error("Bytes lost",0); return 0; } + return 1; +} + +int check8o(long val) { + if (val<-128 || val>127) { error("Offset out of range",0); return 0; } + return 1; +} + +int check16(unsigned aint val) { + if (val!=(val&65535) && ~val>32767) { error("Bytes lost",0); return 0; } + return 1; +} + +int check24(unsigned aint val) { + if (val!=(val&16777215) && ~val>8388607) { error("Bytes lost",0); return 0; } + return 1; +} + +int need(char *&p, char c) { + skipblanks(p); + if (*p!=c) return 0; + ++p; return 1; +} + +int needa(char *&p, char *c1, int r1, char *c2, int r2, char *c3, int r3) { +// skipblanks(p); + if (!isalpha(*p)) return 0; + if (cmphstr(p,c1)) return r1; + if (c2 && cmphstr(p,c2)) return r2; + if (c3 && cmphstr(p,c3)) return r3; + return 0; +} + +int need(char *&p, char *c) { + skipblanks(p); + while (*c) { + if (*p!=*c) { c+=2; continue; } + ++c; + if (*c==' ') { ++p; return *(c-1); } + if (*c=='_' && *(p+1)!=*(c-1)) { ++p; return *(c-1); } + if (*(p+1)==*c) { p+=2; return *(c-1)+*c; } + ++c; + } + return 0; +} + +int getval(int p) { + switch (p) { + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + return p-'0'; + default: + if (isupper(p)) return p-'A'+10; + if (islower(p)) return p-'a'+10; + return 200; + } +} + +int isValidSpaceInsideConstant(char c) { + return compassCompatibilityEnabled && (c == ' ' || c == '\t'); +} + +int getConstant(char *&op, aint &val) { + aint base,pb=1,v,oval; + char *p=op,*p2,*p3; + skipblanks(p); p3=p; + val=0; + char prefix; + + if (*p == '&') { + p++; + prefix = tolower(*p); + if(prefix != 'h' && prefix != 'b') { + error("Syntax error", op, CATCHALL); return 0; + } + if(prefix == 'h') + *p = '#'; + else + *p = '%'; + } + + switch (*p) { + case '#': + case '$': + ++p; + while (isalnum(*p) || isValidSpaceInsideConstant(*p)) { + if (isValidSpaceInsideConstant(*p)) { + p++; + continue; + } + if ((v=getval(*p))>=16) { error("Digit not in base",op); return 0; } + oval=val; val=val*16+v; ++p; if (oval>val) error("Overflow",0,SUPPRES); + } + if (p-p3<2) { error("Syntax error",op,CATCHALL); return 0; } + op=p; return 1; + case '%': + ++p; + while (isdigit(*p) || isValidSpaceInsideConstant(*p)) { + if (isValidSpaceInsideConstant(*p)) { + p++; + continue; + } + if ((v=getval(*p))>=2) { error("Digit not in base",op); return 0; } + oval=val; val=val*2+v; ++p; if (oval>val) error("Overflow",0,SUPPRES); + } + if (p-p3<2) { error("Syntax error",op,CATCHALL); return 0; } + op=p; return 1; + case '0': + ++p; + if (*p=='x' || *p=='X') { + ++p; + while (isalnum(*p) || isValidSpaceInsideConstant(*p)) { + if (isValidSpaceInsideConstant(*p)) { + p++; + continue; + } + if ((v=getval(*p))>=16) { error("Digit not in base",op); return 0; } + oval=val; val=val*16+v; ++p; if (oval>val) error("Overflow",0,SUPPRES); + } + if (p-p3<3) { error("Syntax error",op,CATCHALL); return 0; } + op=p; return 1; + } + default: + while(isalnum(*p) || isValidSpaceInsideConstant(*p)) ++p; + p2=p--; + + while(isValidSpaceInsideConstant(*p)) p--; + + if (isdigit(*p)) base=10; + else if (*p=='b') { base=2; --p; } + else if (*p=='h') { base=16; --p; } + else if (*p=='B') { base=2; --p; } + else if (*p=='H') { base=16; --p; } + else if (*p=='o') { base=8; --p; } + else if (*p=='q') { base=8; --p; } + else if (*p=='d') { base=10; --p; } + else if (*p=='O') { base=8; --p; } + else if (*p=='Q') { base=8; --p; } + else if (*p=='D') { base=10; --p; } + else return 0; + do { + if (isValidSpaceInsideConstant(*p)) continue; + if ((v=getval(*p))>=base) { error("Digit not in base",op); return 0; } + oval=val; val+=v*pb; if (oval>val) error("Overflow",0,SUPPRES); + pb*=base; + } while (p--!=p3); + op=p2; + return 1; + } +} + +int getCharConstChar(char *&op, aint &val) { + if ((val=*op++)!='\\') return 1; + switch (val=*op++) { + case '\\': case '\'': case '\"': case '\?': + return 1; + case 'n': case 'N': val=10; return 1; + case 't': case 'T': val=9; return 1; + case 'v': case 'V': val=11; return 1; + case 'b': case 'B': val=8; return 1; + case 'r': case 'R': val=13; return 1; + case 'f': case 'F': val=12; return 1; + case 'a': case 'A': val=7; return 1; + case 'e': case 'E': val=27; return 1; + case 'd': case 'D': val=127; return 1; + default: --op; val='\\'; error("Unknown escape",op); return 1; + } + return 0; +} + +int getCharConst(char *&p, aint &val) { + aint s=24,r,t=0; val=0; + char *op=p,q; + if (*p!='\'' && *p!='"') return 0; + if (compassCompatibilityEnabled && ((p[0] == '"' && p[1] == '"') || (p[0] == '\'' && p[1] == '\''))) { + val = 0; + p += 2; + return 1; + } + if (compassCompatibilityEnabled && (p[0] == '"' || p[0] == '\'') && p[1] == '\\' && (p[2] == p[0])) { + val = '\\'; + p += 3; + return 1; + } + q=*p++; + do { + if (!*p || *p==q) { p=op; return 0; } + getCharConstChar(p,r); + val+=r<4) error("Overflow",0,SUPPRES); + val=val>>(s+8); + ++p; + return 1; +} + +int getBytes(char *&p, int e[], int add, int dc) { + aint val; + int t=0; + while ('o') { + skipblanks(p); + if (!*p) { error("Expression expected",0,SUPPRES); break; } + if (t==128) { error("Too many arguments",p,SUPPRES); break; } + if (*p=='"') { + p++; + do { + if (!*p || *p=='"') { error("Syntax error",p,SUPPRES); e[t]=-1; return t; } + if (t==128) { error("Too many arguments",p,SUPPRES); e[t]=-1; return t; } + if (compassCompatibilityEnabled && *p == '\\') { + e[t++] = (92 + add) & 255; + p++; + } + else { + getCharConstChar(p, val); check8(val); e[t++] = (val + add) & 255; + } + } while (*p!='"'); + ++p; if (dc && t) e[t-1]|=128; + } else { + if (ParseExpression(p,val)) { check8(val); e[t++]=(val+add)&255; } + else { error("Syntax error",p,SUPPRES); break; } + } + skipblanks(p); if (*p!=',') break; ++p; + } + e[t]=-1; return t; +} + +char *getfilename(char *&p) { + int o=0; + char *fn,*np; + np=fn=new char[LINEMAX]; + skipblanks(p); + + switch(*p) { + case '"': + ++p; + while (*p && *p!='"') { *np=*p; ++np; ++p; } + if (*p=='"') ++p; else error("No closing '\"'",0); + break; + case '<': + while (*p && *p!='>') { *np=*p; ++np; ++p; } + if (*p=='>') ++p; else error("No closing '>'",0); + break; + default: + while (!white() && *p!=',') { *np=*p; ++np; ++p; } + break; + } +/* + if (*p=='"') { o=1; ++p; } + else if (*p=='<') { o=2; } + while (!white() && *p!='"' && *p!='>') { *np=*p; ++np; ++p; } + if (o==1) if (*p=='"') ++p; else error("No closing '\"'",0); + else if (o==2 && *p!='>') error("No closing '>'",0); +*/ + *np=0; + for(np=fn;*np;++np) { +#ifdef WIN32 + if (*np=='/') *np='\\'; +#else + if (*np=='\\') *np='/'; +#endif + } + return fn; +} + + +#ifdef METARM +char *getid3(char *&p) { + int tel=3; + char nid[4],*np; + np=nid; + skipblanks(p); + if (!isalpha(*p) && *p!='_') return NULL; + while(*p && tel--) { + if (!isalnum(*p) && *p!='_') break; + *np=*p; ++p; ++np; + } + *np=0; + np=new char[strlen(nid)]; + strcpy(np,nid); + return np; +} +#endif + +int needcomma(char *&p) { + skipblanks(p); + if (*p!=',') error("Comma expected",0); + return (*(p++)==','); +} + +int needbparen(char *&p) { + skipblanks(p); + if (*p!=']') error("']' expected",0); + return (*(p++)==']'); +} + +int islabchar(char p) { + if (isalnum(p) || p=='_' || p=='.' || p=='?' || p=='!' || p=='#' || p=='@') return 1; + return 0; +} + +structmembs GetStructMemberId(char *&p) { + if (*p=='#') { ++p; if (*p=='#') { ++p; return SMEMBALIGN; } return SMEMBBLOCK; } +// if (*p=='.') ++p; + switch (*p*2+*(p+1)) { + case 'b'*2+'y': case 'B'*2+'Y': if (cmphstr(p,"byte")) return SMEMBBYTE; break; + case 'w'*2+'o': case 'W'*2+'O': if (cmphstr(p,"word")) return SMEMBWORD; break; + case 'b'*2+'l': case 'B'*2+'L': if (cmphstr(p,"block")) return SMEMBBLOCK; break; + case 'd'*2+'b': case 'D'*2+'B': if (cmphstr(p,"db")) return SMEMBBYTE; break; + case 'd'*2+'w': case 'D'*2+'W': + if (cmphstr(p,"dw")) return SMEMBWORD; + if (cmphstr(p,"dword")) return SMEMBDWORD; + break; + case 'd'*2+'s': case 'D'*2+'S': if (cmphstr(p,"ds")) return SMEMBBLOCK; break; + case 'd'*2+'d': case 'D'*2+'D': if (cmphstr(p,"dd")) return SMEMBDWORD; break; + case 'a'*2+'l': case 'A'*2+'L': if (cmphstr(p,"align")) return SMEMBALIGN; break; + case 'd'*2+'e': case 'D'*2+'E': + if (cmphstr(p,"defs")) return SMEMBBLOCK; + if (cmphstr(p,"defb")) return SMEMBBYTE; + if (cmphstr(p,"defw")) return SMEMBWORD; + if (cmphstr(p,"defd")) return SMEMBDWORD; + break; + case 'd'*2+'2': case 'D'*2+'2': + if (cmphstr(p,"d24")) return SMEMBD24; + break; + default: + break; + } + return SMEMBUNKNOWN; +} +//eof reader.cpp diff --git a/tools/sjasm/reader.h b/tools/sjasm/reader.h new file mode 100644 index 00000000..712943b6 --- /dev/null +++ b/tools/sjasm/reader.h @@ -0,0 +1,62 @@ +/* + + SjASM Z80 Assembler + + Copyright (c) 2015 Konamiman + Based on Sjasm 0.39g6 - Copyright (c) 2006 Sjoerd Mastijn + + This software is provided 'as-is', without any express or implied warranty. + In no event will the authors be held liable for any damages arising from the + use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it freely, + subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not claim + that you wrote the original software. If you use this software in a product, + an acknowledgment in the product documentation would be appreciated but is + not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source distribution. + +*/ + +// reader.h + +int white(); +int skipblanks(); +void skipblanks(char *&p); +int needequ(); +int needfield(); +char *getid(char *&p); +char *getinstr(char *&p); +int comma(char *&p); +int oparen(char *&p,char c); +int cparen(char *&p); +char *getparen(char *p); +int check8(unsigned aint val); +int check8o(long val); +int check16(unsigned aint val); +int check24(unsigned aint val); +int need(char *&p, char c); +int need(char *&p, char *c); +int needa(char *&p, char *c1, int r1, char *c2=0, int r2=0, char *c3=0, int r3=0); +int getConstant(char *&op, aint &val); +int getCharConst(char *&p, aint &val); +int getCharConstChar(char *&op, aint &val); +int getBytes(char *&p, int e[], int add, int dc); +int cmphstr(char *&p1, char *p2); +char *getfilename(char *&p); +int needcomma(char *&p); +int needbparen(char *&p); +int islabchar(char p); +structmembs GetStructMemberId(char *&p); +#ifdef METARM +char *getid3(char *&p); +#endif +//eof reader.h + diff --git a/tools/sjasm/sjasm.cpp b/tools/sjasm/sjasm.cpp new file mode 100644 index 00000000..49f193f2 --- /dev/null +++ b/tools/sjasm/sjasm.cpp @@ -0,0 +1,221 @@ +/* + + SjASM Z80 Assembler + + Copyright (c) 2015 Konamiman + Based on Sjasm 0.39g6 - Copyright (c) 2006 Sjoerd Mastijn + + This software is provided 'as-is', without any express or implied warranty. + In no event will the authors be held liable for any damages arising from the + use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it freely, + subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not claim + that you wrote the original software. If you use this software in a product, + an acknowledgment in the product documentation would be appreciated but is + not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source distribution. + +*/ + +// sjasm.cpp + +#include "sjasm.h" + +char destfilename[LINEMAX],listfilename[LINEMAX],expfilename[LINEMAX],sourcefilename[LINEMAX],symfilename[LINEMAX]; + +char filename[LINEMAX],*lp,line[LINEMAX],temp[LINEMAX],*tp,pline[LINEMAX*2],eline[LINEMAX*2],*bp; + +int pass,labelnotfound,nerror,include=-1,running,labellisting=0,listfile=1,donotlist,listdata,listmacro; +int useStdError = 0; +int useVsErrorFormat = 0; +int compassCompatibilityEnabled = 0; +int macronummer,lijst,reglenwidth,synerr=1,symfile=0; +aint adres,mapadr,gcurlin,lcurlin,curlin,destlen,size=(aint)-1,preverror=(aint)-1,maxlin=0,comlin; +#ifdef METARM +cpus cpu; +#endif +char *huidigzoekpad; + +void (*piCPUp)(void); + +#ifdef SECTIONS +sections section; +#endif + +char *modlabp,*vorlabp,*macrolabp; + +stringlst *lijstp; +labtabcls labtab; +loklabtabcls loklabtab; +definetabcls definetab; +macdefinetabcls macdeftab; +macrotabcls macrotab; +structtabcls structtab; +adrlst *maplstp=0; +stringlst *modlstp=0,*dirlstp=0; +#ifdef SECTIONS +pooldatacls pooldata; +pooltabcls pooltab;bluemsx +#endif + +void ReplaceAtToUnderscore(char* string) { + char* tempLp = string; + char c; + int insideDoubleQuotedString = 0; + int insideSingleQuotedString = 0; + + while (c = *tempLp) { + if(c == '"' && !insideSingleQuotedString) { + insideDoubleQuotedString = !insideDoubleQuotedString; + } + if (c == '\'' && !insideDoubleQuotedString) { + insideSingleQuotedString = !insideSingleQuotedString; + } + if (c == '@' && !insideDoubleQuotedString && !insideSingleQuotedString) + *tempLp = '_'; + + tempLp++; + } +} + +void InitPass(int p) { +#ifdef SECTIONS + section=TEXT; +#endif + reglenwidth=1; + if (maxlin>9) reglenwidth=2; + if (maxlin>99) reglenwidth=3; + if (maxlin>999) reglenwidth=4; + if (maxlin>9999) reglenwidth=5; + if (maxlin>99999) reglenwidth=6; + if (maxlin>999999) reglenwidth=7; + modlabp=0; vorlabp="_"; macrolabp=0; listmacro=0; + pass=p; adres=mapadr=0; running=1; gcurlin=lcurlin=curlin=0; + eadres=0; epadres=0; macronummer=0; lijst=0; comlin=0; + modlstp=0; +#ifdef METARM + cpu=Z80; piCPUp=piZ80; +#endif + structtab.init(); + macrotab.init(); + definetab.init(); + macdeftab.init(); +} + +void getOptions(char **&argv,int &i) { + char *p,c; + char ps[2]; + while (argv[i] && *argv[i]=='-') { + p=argv[i++]+1; + do { + c=*p++; + switch (tolower(c)) { + case 'q': listfile=0; break; + case 's': symfile=1; break; + case 'l': labellisting=1; break; + case 'i': dirlstp=new stringlst(p,dirlstp); p=""; break; + case 'e': useStdError = 1; break; + case 'c': compassCompatibilityEnabled = 1; break; + case 'v': useVsErrorFormat = 1; break; + default: + ps[0] = c; + ps[1] = '\0'; + ErrorAndExit2("Unrecognised option: ", ps, ERR_INVALID_OPTION); + } + } while (*p); + } +} + +void ErrorAndExit2(char* message, char* param, int exitCode) { + if (useVsErrorFormat) + errout << "sjasm : error FATAL : " << message << param << endl; + else { + errout << message << param << endl; + } + exit(exitCode); +} + +int main(int argc, char *argv[]) { + char zoekpad[MAX_PATH]; + char *p; + int i=1; + + cout << "SjASM Z80 Assembler v0.39j" << endl; + sourcefilename[0]=destfilename[0]=listfilename[0]=expfilename[0]=0; + if (argc==1) { + cout << "Copyright 2006 Sjoerd Mastijn - www.xl2s.tk" << endl; + cout << "Copyright 2022 Konamiman - www.konamiman.com" << endl; + cout << "\nUsage:\nsjasm [-options] sourcefile [targetfile [listfile [exportfile]]]\n"; + cout << "\nOption flags as follows:\n"; + cout << " -l Label table in listing\n"; + cout << " -s Generate .SYM symbol file\n"; + cout << " -q No listing\n"; + cout << " -i Includepath\n"; + cout << " -e Send errors to standard error pipe\n"; + cout << " -c Enable Compass compatibility\n"; + cout << " -v Produce error messages with Visual Studio format\n"; + cout << " (should be the first option)\n"; + exit(ERR_NO_INPUT); + } + + GetCurrentDirectory(MAX_PATH,zoekpad); + huidigzoekpad=zoekpad; + + getOptions(argv,i); if (argv[i]) strcpy(sourcefilename,argv[i++]); + getOptions(argv,i); if (argv[i]) strcpy(destfilename,argv[i++]); + getOptions(argv,i); if (argv[i]) strcpy(listfilename,argv[i++]); + getOptions(argv,i); if (argv[i]) strcpy(expfilename,argv[i++]); + getOptions(argv,i); + + if (!sourcefilename[0]) { ErrorAndExit("No inputfile", ERR_NO_INPUT); } + if (!destfilename[0]) { + strcpy(destfilename,sourcefilename); + if (!(p=strchr(destfilename,'.'))) p=destfilename; else *p=0; + strcat(p,".out"); + } + if (!listfilename[0]) { + strcpy(listfilename,sourcefilename); + if (!(p=strchr(listfilename,'.'))) p=listfilename; else *p=0; + strcat(p,".lst"); + } + strcpy(symfilename,expfilename); + if (!expfilename[0]) { + strcpy(expfilename,sourcefilename); + if (!(p=strchr(expfilename,'.'))) p=expfilename; else *p=0; + strcat(p,".exp"); + } + if (!symfilename[0]) { + strcpy(symfilename,sourcefilename); + if (!(p=strchr(symfilename,'.'))) p=symfilename; else *p=0; + strcat(p,".sym"); + } + + Initpi(); + + InitPass(1); OpenList(); OpenFile(sourcefilename); + + cout << "Pass 1 complete (" << nerror << " errors)" << endl; + + InitPass(2); OpenDest(); OpenFile(sourcefilename); + + if (labellisting) labtab.dump(); + + cout << "Pass 2 complete" << endl; + + Close(); + + if (symfile) labtab.dumpsym(); + + cout << "Errors: " << nerror << endl << flush; + + return (nerror==0 ? 0 : ERR_BAD_CODE); +} +//eof sjasm.cpp diff --git a/tools/sjasm/sjasm.h b/tools/sjasm/sjasm.h new file mode 100644 index 00000000..8d065b1a --- /dev/null +++ b/tools/sjasm/sjasm.h @@ -0,0 +1,127 @@ +/* + + SjASM Z80 Assembler + + Copyright (c) 2015 Konamiman + Based on Sjasm 0.39g6 - Copyright (c) 2006 Sjoerd Mastijn + + This software is provided 'as-is', without any express or implied warranty. + In no event will the authors be held liable for any damages arising from the + use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it freely, + subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not claim + that you wrote the original software. If you use this software in a product, + an acknowledgment in the product documentation would be appreciated but is + not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source distribution. + +*/ + +// sjasm.h + +//#define METARM +//#define SECTIONS + +#ifndef SjINCLUDE +#define SjINCLUDE + +#ifdef WIN32 +#include +#else +#include "loose.h" +#endif +#include +using std::cout; +using std::cerr; +using std::endl; +using std::flush; +#include +#include +#include +#include + +#define errout (useStdError ? cerr : cout) + +#define ERR_BAD_CODE 1 +#define ERR_OPEN_FILE 2 +#define ERR_FATAL 3 +#define ERR_NO_INPUT 4 +#define ERR_INVALID_OPTION 5 + +#define LINEMAX 300 +#define LABMAX 70 +#define LABTABSIZE 32771 +#define FUNTABSIZE 4497 +#define aint long + +extern char filename[],*lp,line[],temp[],*tp,pline[],eline[],*bp; +extern int pass,labelnotfound,nerror,include,running,labellisting,listfile,donotlist,listdata,listmacro; +extern int useStdError; +extern int useVsErrorFormat; +extern int compassCompatibilityEnabled; +extern int insideCompassStyleMacroDefinition; +extern int macronummer,lijst,reglenwidth,synerr,symfile; +extern aint adres,mapadr,gcurlin,lcurlin,curlin,destlen,size,preverror,maxlin,comlin; +extern FILE *input; +extern void (*piCPUp)(void); +extern char destfilename[],listfilename[],sourcefilename[],expfilename[],symfilename[]; +extern char *modlabp,*vorlabp,*macrolabp; + +void ReplaceAtToUnderscore(char* string); + +void ErrorAndExit2(char* message, char* param, int exitCode); +#define ErrorAndExit(message, exitCode) ErrorAndExit2(message, "", exitCode) +#define ErrorOpeningFile(fname) ErrorAndExit2("Error opening file: ", fname, ERR_OPEN_FILE) + +#define IsNotValidIdChar(p) (!isalnum(p) && p!='_' && p!='.' && p!='?' && p!='!' && p!='#' && p!='@') +#define IsValidIdChar(p) (!IsNotValidIdChar(p)) + +extern FILE *listfp; + +#ifdef SECTIONS +enum sections { TEXT, DATA, POOL }; +extern sections section; +#endif +#ifdef METARM +enum cpus { ARM, THUMB, Z80 }; +extern cpus cpu; +#endif +enum structmembs { SMEMBUNKNOWN,SMEMBALIGN,SMEMBBYTE,SMEMBWORD,SMEMBBLOCK, SMEMBDWORD, SMEMBD24, SMEMBPARENOPEN, SMEMBPARENCLOSE }; +extern char *huidigzoekpad; + +#include "reader.h" +#include "tables.h" +extern stringlst *lijstp; +#include "sjio.h" + +extern labtabcls labtab; +extern loklabtabcls loklabtab; +extern definetabcls definetab; +extern macdefinetabcls macdeftab; +extern macrotabcls macrotab; +extern structtabcls structtab; +extern adrlst *maplstp; +extern stringlst *modlstp,*dirlstp; +#ifdef SECTIONS +extern pooldatacls pooldata; +extern pooltabcls pooltab; +#endif + +#include "parser.h" +#include "piz80.h" +#ifdef METARM +#include "piarm.h" +#include "pithumb.h" +#endif +#include "direct.h" + +#endif +//eof sjasm.h diff --git a/tools/sjasm/sjio.cpp b/tools/sjasm/sjio.cpp new file mode 100644 index 00000000..e75733e3 --- /dev/null +++ b/tools/sjasm/sjio.cpp @@ -0,0 +1,581 @@ +/* + + SjASM Z80 Assembler + + Copyright (c) 2015 Konamiman + Based on Sjasm 0.39g6 - Copyright (c) 2006 Sjoerd Mastijn + + This software is provided 'as-is', without any express or implied warranty. + In no event will the authors be held liable for any damages arising from the + use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it freely, + subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not claim + that you wrote the original software. If you use this software in a product, + an acknowledgment in the product documentation would be appreciated but is + not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source distribution. + +*/ + +// sjio.cpp + +#include "sjasm.h" + +#include +#include +#include + +#define DESTBUFLEN 8192 + +int EB[1024*64],nEB=0; +char destbuf[DESTBUFLEN]; +FILE *input, *output; +FILE *listfp,*expfp=NULL; +aint eadres,epadres,desttel=0,skiperrors=0;; +char hd[]={'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'}; +char* errorSorts[] = { "ALL", "PASS1", "PASS2", "FATAL", "CATCHALL", "SUPPRES" }; + +void error(char *fout,char *bd,int soort) { + char *ep=eline; + if (skiperrors && preverror==lcurlin && soort!=FATAL) return; + if (soort==CATCHALL && preverror==lcurlin) return; + if (soort==PASS1 && pass!=1) return; + if ((soort==CATCHALL || soort==SUPPRES || soort==PASS2) && pass!=2) return; + skiperrors=(soort==SUPPRES); + preverror=lcurlin; + ++nerror; + + if(useVsErrorFormat) + sprintf(ep, "%s(%lu) : error %s : %s", filename, lcurlin, errorSorts[soort], fout); + else + sprintf(ep,"%s line %lu: %s", filename, lcurlin, fout); + + if (bd) { strcat(ep,": "); strcat(ep,bd); } + if (!strchr(ep,'\n')) strcat(ep,"\n"); + if (listfile) fputs(eline,listfp); + errout << eline; + if (soort==FATAL) exit(ERR_FATAL); +} + +void WriteDest() { + if (!desttel) return; + destlen+=desttel; + if(fwrite(destbuf,1,desttel,output)>4]; + *(p++)=hd[hh&15]; +} + +void listbytes(char *&p) { + int i=0; + while (nEB--) { printhex8(p,EB[i++]); *(p++)=' '; } + i=4-i; + while (i--) { *(p++)=' '; *(p++)=' '; *(p++)=' '; } +} + +void listbytes2(char *&p) { + for (int i=0;i!=5;++i) printhex8(p,EB[i]); + *(p++)=' '; *(p++)=' '; +} + +void printlcurlin(char *&p) { + aint v=lcurlin; + switch (reglenwidth) { + default: *(p++)=(unsigned char)('0'+v/1000000); v%=1000000; + case 6: *(p++)=(unsigned char)('0'+v/100000); v%=100000; + case 5: *(p++)=(unsigned char)('0'+v/10000); v%=10000; + case 4: *(p++)=(unsigned char)('0'+v/1000); v%=1000; + case 3: *(p++)=(unsigned char)('0'+v/100); v%=100; + case 2: *(p++)=(unsigned char)('0'+v/10); v%=10; + case 1: *(p++)=(unsigned char)('0'+v); + } + *(p++)=include>0?'+':' '; + *(p++)=include>1?'+':' '; + *(p++)=include>2?'+':' '; +} + +void printhex32(char *&p, aint h) { + unsigned hh=h&0xffffffff; + *(p++)=hd[hh>>28]; hh&=0xfffffff; + *(p++)=hd[hh>>24]; hh&=0xffffff; + *(p++)=hd[hh>>20]; hh&=0xfffff; + *(p++)=hd[hh>>16]; hh&=0xffff; + *(p++)=hd[hh>>12]; hh&=0xfff; + *(p++)=hd[hh>>8]; hh&=0xff; + *(p++)=hd[hh>>4]; hh&=0xf; + *(p++)=hd[hh]; +} + +void printhex16(char *&p, aint h) { + unsigned hh=h&0xffff; + *(p++)=hd[hh>>12]; hh&=0xfff; + *(p++)=hd[hh>>8]; hh&=0xff; + *(p++)=hd[hh>>4]; hh&=0xf; + *(p++)=hd[hh]; +} + +void listbytes3(int pad) { + int i=0,t; + char *pp,*sp=pline+3+reglenwidth; + while (nEB) { + pp=sp; +#ifdef METARM + if (cpu==Z80) printhex16(pp,pad); else printhex32(pp,pad); +#else + printhex16(pp,pad); +#endif + *(pp++)=' '; t=0; + while (nEB && t<32) { printhex8(pp,EB[i++]); --nEB; ++t; } + *(pp++)='\n'; *pp=0; + fputs(pline,listfp); + pad+=32; + } +} + +#ifdef METARM +void listi32(char *&p) { + printhex8(p,EB[3]); + printhex8(p,EB[2]); + printhex8(p,EB[1]); + printhex8(p,EB[0]); + *p=0; strcat(pline," "); p+=4; +} + +void listi16(char *&p) { + printhex8(p,EB[1]); + printhex8(p,EB[0]); + *p=0; strcat(pline," "); p+=8; +} +#endif + +void ListFile() { + char *pp=pline; + aint pad; + if (pass==1 || !listfile || donotlist) { donotlist=nEB=0; return; } + if (listmacro) if (!nEB) return; + if ((pad=eadres)==(aint)-1) pad=epadres; + if (strlen(line) && line[strlen(line)-1]!=10) strcat(line,"\n"); + *pp=0; + printlcurlin(pp); +#ifdef METARM + if (cpu==Z80) printhex16(pp,pad); else printhex32(pp,pad); +#else + printhex16(pp,pad); +#endif + *(pp++)=' '; +#ifdef METARM + switch (cpu) { + case ARM: + if (nEB==4 && !listdata) { listi32(pp); *pp=0; if (listmacro) strcat(pp,">"); strcat(pp,line); fputs(pline,listfp); break; } + if (nEB<5) { listbytes(pp); *pp=0; if (listmacro) strcat(pp,">"); strcat(pp,line); fputs(pline,listfp); } + else if (nEB<6) { listbytes2(pp); *pp=0; if (listmacro) strcat(pp,">"); strcat(pp,line); fputs(pline,listfp); } + else { listbytes2(pp); *pp=0; if (listmacro) strcat(pp,">"); strcat(pp,line); fputs(pline,listfp); listbytes3(pad); fputs(pline,listfp); } + break; + case THUMB: + if (nEB==2 && !listdata) { listi16(pp); *pp=0; if (listmacro) strcat(pp,">"); strcat(pp,line); fputs(pline,listfp); break; } + if (nEB==4 && !listdata) { listi32(pp); *pp=0; if (listmacro) strcat(pp,">"); strcat(pp,line); fputs(pline,listfp); break; } + if (nEB<5) { listbytes(pp); *pp=0; if (listmacro) strcat(pp,">"); strcat(pp,line); fputs(pline,listfp); } + else if (nEB<6) { listbytes2(pp); *pp=0; if (listmacro) strcat(pp,">"); strcat(pp,line); fputs(pline,listfp); } + else { listbytes2(pp); *pp=0; if (listmacro) strcat(pp,">"); strcat(pp,line); fputs(pline,listfp); listbytes3(pad); fputs(pline,listfp); } + break; + case Z80: +#endif + if (nEB<5) { listbytes(pp); *pp=0; if (listmacro) strcat(pp,">"); strcat(pp,line); fputs(pline,listfp); } + else if (nEB<6) { listbytes2(pp); *pp=0; if (listmacro) strcat(pp,">"); strcat(pp,line); fputs(pline,listfp); } + else { for (int i=0;i!=12;++i) *(pp++)=' '; *pp=0; if (listmacro) strcat(pp,">"); strcat(pp,line); fputs(pline,listfp); listbytes3(pad); } +#ifdef METARM + break; + default: + error("internal error listfile",0,FATAL); + } +#endif + epadres=adres; eadres=(aint)-1; nEB=0; listdata=0; +} + +void ListFileSkip(char *line) { + char *pp=pline; + aint pad; + if (pass==1 || !listfile || donotlist) { donotlist=nEB=0; return; } + if (listmacro) return; + if ((pad=eadres)==(aint)-1) pad=epadres; + if (strlen(line) && line[strlen(line)-1]!=10) strcat(line,"\n"); + *pp=0; + printlcurlin(pp); +#ifdef METARM + if (cpu==Z80) printhex16(pp,pad); else printhex32(pp,pad); +#else + printhex16(pp,pad); +#endif + *pp=0; strcat(pp,"~ "); + if (nEB) error("Internal error lfs",0,FATAL); + if (listmacro) strcat(pp,">"); strcat(pp,line); fputs(pline,listfp); + epadres=adres; eadres=(aint)-1; nEB=0; listdata=0; +} + +void emit(int byte) { + EB[nEB++]=byte; + if (pass==2) { + destbuf[desttel++]=(char)byte; + if (desttel==DESTBUFLEN) WriteDest(); + } + ++adres; +} + +void EmitByte(int byte) { + eadres=adres; + emit(byte); +} + +void EmitBytes(int *bytes) { + eadres=adres; + if (*bytes==-1) { error("Illegal instruction",line,CATCHALL); *lp=0; } + while (*bytes!=-1) emit(*bytes++); +} + +void EmitWords(int *words) { + eadres=adres; + while (*words!=-1) { + emit((*words)%256); + emit((*words++)/256); + } +} + +void EmitBlock(aint byte, aint len) { + eadres=adres; + if (len) { EB[nEB++]=byte; } + while (len--) { + if (pass==2) { + destbuf[desttel++]=(char)byte; + if (desttel==DESTBUFLEN) WriteDest(); + } + ++adres; + } +} + +char *getpath(char *fname, TCHAR **filenamebegin) { + int g=0; + char *kip,nieuwzoekpad[MAX_PATH]; + g=SearchPath(huidigzoekpad,fname,NULL,MAX_PATH,nieuwzoekpad,filenamebegin); + if (!g) { + if (fname[0]=='<') fname++; + stringlst *dir=dirlstp; + while (dir) { + if (SearchPath(dir->string,fname,NULL,MAX_PATH,nieuwzoekpad,filenamebegin)) { g=1; break; } + dir=dir->next; + } + } + if (!g) SearchPath(huidigzoekpad,fname,NULL,MAX_PATH,nieuwzoekpad,filenamebegin); + kip=strdup(nieuwzoekpad); + if (filenamebegin) *filenamebegin+=kip-nieuwzoekpad; + return kip; +} + +void BinIncFile(char *fname,int offset,int len) { + char *bp; + FILE *bif; + int res; + char *nieuwzoekpad; + nieuwzoekpad=getpath(fname,NULL); + if (*fname=='<') fname++; + if (!(bif=fopen(nieuwzoekpad,"rb"))) { + ErrorOpeningFile(fname); + } + if (offset>0) { + bp=new char[offset+1]; + res=fread(bp,1,offset,bif); + if (res==-1) error("Read error",fname,FATAL); + if (res!=offset) error("Offset beyond filelength",fname,FATAL); + } + if (len>0) { + bp=new char[len+1]; + res=fread(bp,1,len,bif); + if (res==-1) error("Read error",fname,FATAL); + if (res!=len) error("Unexpected end of file",fname,FATAL); + while (len--) { + if (pass==2) { destbuf[desttel++]=*bp++; if (desttel==DESTBUFLEN) WriteDest(); } + ++adres; + } + } else { + if (pass==2) WriteDest(); + do { + res=fread(destbuf,1,DESTBUFLEN,bif); + if (res==-1) error("Read error",fname,FATAL); + if (pass==2) { desttel=res; WriteDest(); } + adres+=res; + } while (res==DESTBUFLEN); + } + fclose(bif); +} + +void OpenFile(char *nfilename) { + char ofilename[LINEMAX]; + char *ohuidigzoekpad,*nieuwzoekpad; + TCHAR *filenamebegin; + aint olcurlin=lcurlin; + lcurlin=0; + FILE *oinput=input; + input=0; + strcpy(ofilename,filename); + if (++include>20) error("Over 20 files nested",0,FATAL); + nieuwzoekpad=getpath(nfilename,&filenamebegin); + if (*nfilename=='<') nfilename++; + strcpy(filename,nfilename); + if ((input=fopen(nieuwzoekpad,"r"))==NULL) { ErrorOpeningFile(nfilename); } + ohuidigzoekpad=huidigzoekpad; *filenamebegin=0; huidigzoekpad=nieuwzoekpad; + while(running && fgets(line,LINEMAX,input)) { + ++lcurlin; ++curlin; + if (strlen(line)==LINEMAX-1) error("Line too long",0,FATAL); + ParseLine(); + } + fclose(input); + --include; + huidigzoekpad=ohuidigzoekpad; + strcpy(filename,ofilename); + if (lcurlin>maxlin) maxlin=lcurlin; + input=oinput; lcurlin=olcurlin; +} + +void OpenList() { + if (listfile) + if (!(listfp=fopen(listfilename,"w"))) { + ErrorOpeningFile(listfilename); + } +} + +void CloseDest() { + long pad; + if (desttel) WriteDest(); + if (size!=-1) { + if (destlen>size) error("File exceeds 'size'",0); + else { + pad=size-destlen; + if (pad>0) + while (pad--) { + destbuf[desttel++]=0; + if (desttel==256) WriteDest(); + } + if (desttel) WriteDest(); + } + } + fclose(output); +} + +void SeekDest(long offset,int method) { + WriteDest(); + if(fseek(output,offset,method)) error("File seek error (FORG)",0,FATAL); +} + +void NewDest(char *ndestfilename) { + NewDest(ndestfilename,OUTPUT_TRUNCATE); +} + +void NewDest(char *ndestfilename,int mode) { + CloseDest(); + strcpy(destfilename,ndestfilename); + OpenDest(mode); +} + +void OpenDest() { + OpenDest(OUTPUT_TRUNCATE); +} + +void OpenDest(int mode) { + destlen=0; + if(mode!=OUTPUT_TRUNCATE && !FileExists(destfilename)) mode=OUTPUT_TRUNCATE; + if ((output = fopen( destfilename, mode==OUTPUT_TRUNCATE ? "wb" : "r+b" )) == NULL ) + { + ErrorOpeningFile(destfilename); + } + if(mode!=OUTPUT_TRUNCATE) + { + if(fseek(output,0,mode==OUTPUT_REWIND ? SEEK_SET : SEEK_END)) error("File seek error (OUTPUT)",0,FATAL); + } +} + +int FileExists(char* filename) { + int exists=0; + FILE* test=fopen(filename,"r"); + if(test!=NULL) { + exists=-1; + fclose(test); + } + return exists; +} + +void Close() { + CloseDest(); + if (expfp) { + fclose(expfp); + expfp = NULL; + } + if (listfile) fclose(listfp); +} + +Ending ReadFile() { + stringlst *ol; + char *p; + while ('o') { + if (!running) return END; + if (lijst) { + if (!lijstp) return END; + p=strcpy(line,lijstp->string); ol=lijstp; lijstp=lijstp->next; + } else { + if (!fgets(p=line,LINEMAX,input)) error("Unexpected end of file",0,FATAL); + ++lcurlin; ++curlin; + if (strlen(line)==LINEMAX-1) error("Line too long",0,FATAL); + } + skipblanks(p); + if (*p=='.') ++p; + if (cmphstr(p,"endif")) { return ENDIF; } + if (cmphstr(p,"else")) { ListFile(); return ELSE; } + if (cmphstr(p,"endt")) { return ENDTEXTAREA; } + if (cmphstr(p,"dephase")) { return ENDTEXTAREA; } + if (compassCompatibilityEnabled && cmphstr(p, "endc")) { return ENDIF; } + ParseLine(); + } +} + +Ending SkipFile() { + stringlst *ol; + char *p; + int iflevel=0; + while ('o') { + if (!running) return END; + if (lijst) { + if (!lijstp) return END; + p=strcpy(line,lijstp->string); ol=lijstp; lijstp=lijstp->next; + } else { + if (!fgets(p=line,LINEMAX,input)) error("Unexpected end of file",0,FATAL); + ++lcurlin; ++curlin; + if (strlen(line)==LINEMAX-1) error("Line too long",0,FATAL); + } + skipblanks(p); + if (*p=='.') ++p; + if (cmphstr(p,"if")) { ++iflevel; } + if (cmphstr(p,"ifexist")) { ++iflevel; } + if (cmphstr(p,"ifnexist")) { ++iflevel; } + if (cmphstr(p,"ifdef")) { ++iflevel; } + if (cmphstr(p,"ifndef")) { ++iflevel; } + if (cmphstr(p,"endif")) { if (iflevel) --iflevel; else return ENDIF; } + if (cmphstr(p,"else")) { if (!iflevel) { ListFile(); return ELSE; } } + ListFileSkip(line); + } +} + +int ReadLine() { + if (!running) return 0; + if (!fgets(line,LINEMAX,input)) error("Unexpected end of file",0,FATAL); + ++lcurlin; ++curlin; + if (strlen(line)==LINEMAX-1) error("Line too long",0,FATAL); + return 1; +} + +void ConvertCompassStyleLocalLabels(char* line) { + //Replaces "label@sym" into ".labelSym" + + char* lp = line; + char* atSymPointer; + char* labelStartPointer; + int labelLength; + int i; + + while (*lp) { + if (!(*lp == '@' && tolower(lp[1]) == 's' && tolower(lp[2]) == 'y' && tolower(lp[3]) == 'm')) { + lp++; + continue; + } + + atSymPointer = lp; + lp--; + if(lp < line) return; + + while(lp >= line && IsValidIdChar(*lp)) + lp--; + + labelStartPointer = lp + 1; + labelLength = atSymPointer - labelStartPointer; + if (labelLength == 0) { + lp = atSymPointer + 4; + continue; + } + + for (i = labelLength - 1; i >= 0; i--) + labelStartPointer[i + 1] = labelStartPointer[i]; + + *labelStartPointer = '.'; + atSymPointer[1] = 'S'; + + lp = atSymPointer + 4; + } +} + +int ReadFileToStringLst(stringlst *&f,char *end) { + stringlst *s,*l=NULL; + char *p; f=NULL; + char* tempLp; + while ('o') { + if (!running) return 0; + if (!fgets(p=line,LINEMAX,input)) error("Unexpected end of file",0,FATAL); + ++lcurlin; ++curlin; + if (strlen(line)==LINEMAX-1) error("Line too long",0,FATAL); + + if (insideCompassStyleMacroDefinition) { + ConvertCompassStyleLocalLabels(line); + ReplaceAtToUnderscore(line); + } + + if (*p && *p<=' ') { + skipblanks(p); if (*p=='.') ++p; + if (cmphstr(p,end)) { return 1; } + } + s=new stringlst(line,NULL); if (!f) f=s; if (l) l->next=s; l=s; + ListFileSkip(line); + } +} + +void WriteExp(char *n, aint v) { + char lnrs[16],*l=lnrs; + if (!expfp) { + if (!(expfp=fopen(expfilename,"w"))) { + ErrorOpeningFile(expfilename); + } + } + strcpy(eline,n); strcat(eline,": EQU "); + printhex32(l,v); *l=0; strcat(eline,lnrs); strcat(eline,"h\n"); + fputs(eline,expfp); +} + +void emitarm(aint data) { + eadres=adres; + emit(data&255); + emit((data>>8)&255); + emit((data>>16)&255); + emit((data>>24)&255); +} + +#ifdef METARM +void emitthumb(aint data) { + eadres=adres; + emit(data&255); + emit((data>>8)&255); +} + +void emitarmdataproc(int cond, int I,int opcode,int S,int Rn,int Rd,int Op2) { + aint i; + i=(cond<<28)+(I<<25)+(opcode<<21)+(S<<20)+(Rn<<16)+(Rd<<12)+Op2; + emitarm(i); +} +#endif +//eof sjio.cpp diff --git a/tools/sjasm/sjio.h b/tools/sjasm/sjio.h new file mode 100644 index 00000000..bd806396 --- /dev/null +++ b/tools/sjasm/sjio.h @@ -0,0 +1,70 @@ +/* + + SjASM Z80 Assembler + + Copyright (c) 2015 Konamiman + Based on Sjasm 0.39g6 - Copyright (c) 2006 Sjoerd Mastijn + + This software is provided 'as-is', without any express or implied warranty. + In no event will the authors be held liable for any damages arising from the + use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it freely, + subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not claim + that you wrote the original software. If you use this software in a product, + an acknowledgment in the product documentation would be appreciated but is + not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source distribution. + +*/ + +// sjio.h + +enum _fouten { ALL, PASS1, PASS2, FATAL, CATCHALL, SUPPRES }; +enum Ending { END, ELSE, ENDIF, ENDTEXTAREA, ENDM }; + +#define str(x) #x + +extern aint eadres,epadres; + +#define OUTPUT_TRUNCATE 0 +#define OUTPUT_REWIND 1 +#define OUTPUT_APPEND 2 + +void OpenDest(int); +void NewDest(char *ndestfilename, int mode); +int FileExists(char* filename); +void error(char*,char*,int=PASS2); +void ListFile(); +void ListFileSkip(char*); +void EmitByte(int byte); +void EmitBytes(int *bytes); +void EmitWords(int *words); +void EmitBlock(aint byte, aint lengte); +void OpenFile(char *nfilename); +void Close(); +void OpenList(); +void OpenDest(); +void printhex32(char *&p, aint h); +void BinIncFile(char *fname,int offset,int length); +int ReadLine(); +Ending ReadFile(); +Ending SkipFile(); +void NewDest(char *ndestfilename); +void SeekDest(long,int); +int ReadFileToStringLst(stringlst *&f,char *end); +void WriteExp(char *n, aint v); +void emitarm(aint data); +#ifdef METARM +void emitarmdataproc(int cond, int I,int opcode,int S,int Rn,int Rd,int Op2); +void emitthumb(aint data); +#endif +//eof sjio.h + diff --git a/tools/sjasm/tables.cpp b/tools/sjasm/tables.cpp new file mode 100644 index 00000000..c3482f74 --- /dev/null +++ b/tools/sjasm/tables.cpp @@ -0,0 +1,816 @@ +/* + + SjASM Z80 Assembler + + Copyright (c) 2015 Konamiman + Based on Sjasm 0.39g6 - Copyright (c) 2006 Sjoerd Mastijn + + This software is provided 'as-is', without any express or implied warranty. + In no event will the authors be held liable for any damages arising from the + use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it freely, + subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not claim + that you wrote the original software. If you use this software in a product, + an acknowledgment in the product documentation would be appreciated but is + not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source distribution. + +*/ + +// tables.cpp + +#include "sjasm.h" + +char *prevlab; + +char *MaakLabNaam(char *naam) { + char *np=naam,*lp,*label,*mlp=macrolabp; + int p=0,l=0; + label=new char[LINEMAX]; + lp=label; + label[0]=0; + if (mlp && *np=='@') { ++np; mlp=0; } + switch (*np) { + case '@': p=1; ++np; break; + case '.': l=1; ++np; break; + default: break; + } + naam=np; + if (!isalpha(*np) && *np!='_') { error("Invalid labelname",naam); return 0; } + while (*np) { + if (isalnum(*np) || *np=='_' || *np=='.' || *np=='?' || *np=='!' || *np=='#' || *np=='@') ++np; + else { error("Invalid labelname",naam); return 0; } + } + if (strlen(naam)>LABMAX) { + error("1Label too long",naam,PASS1); + naam[LABMAX]=0; + } + if (mlp && l) { + strcat(lp,macrolabp); strcat(lp,">"); + } else { + if (!p && modlabp) { + int len1=strlen(lp); + int len2=strlen(modlabp); + strcat(lp,modlabp); + strcat(lp,"."); + } + if (l) { + strcat(lp,vorlabp); strcat(lp,"."); + } else + vorlabp=strdup(naam); + } + strcat(lp,naam); + return label; +} + +int getLabelValue(char *&p, aint &val) { + char *mlp=macrolabp,*op=p; + int g=0,l=0,len,olabelnotfound=labelnotfound,plen; + char *np; + if (mlp && *p=='@') { ++op; mlp=0; } + if (mlp) { + switch (*p) { + case '@': g=1; ++p; break; + case '.': l=1; ++p; break; + default: break; + } + temp[0]=0; + if (l) { + strcat(temp,macrolabp); strcat(temp,">"); + len=strlen(temp); np=temp+len; plen=0; + if (!isalpha(*p) && *p!='_') { error("Invalid labelname",temp); return 0; } + while (isalnum(*p) || *p=='_' || *p=='.' || *p=='?' || *p=='!' || *p=='#' || *p=='@') { + *np=*p; ++np; ++p; + } + *np=0; + if (strlen(temp)>LABMAX+len) { + error("2Label too long",temp+len); + temp[LABMAX+len]=0; + } + np=temp; g=1; + do { + if (labtab.zoek(np,val)) return 1; + labelnotfound=olabelnotfound; + while ('o') { + if (*np=='>') { g=0; break; } + if (*np=='.') { ++np; break; } + ++np; + } + } while (g); + } + } + + p=op; + switch (*p) { + case '@': g=1; ++p; break; + case '.': l=1; ++p; break; + default: break; + } + temp[0]=0; + if (!g && modlabp) { strcat(temp,modlabp); strcat(temp,"."); } + if (l) { strcat(temp,vorlabp); strcat(temp,"."); } + len=strlen(temp); np=temp+len; + if (!isalpha(*p) && *p!='_') { error("Invalid labelname",temp); return 0; } + while (isalnum(*p) || *p=='_' || *p=='.' || *p=='?' || *p=='!' || *p=='#' || *p=='@') { + *np=*p; ++np; ++p; + } + *np=0; + if (strlen(temp)>LABMAX+len) { + error("3Label too long",temp+len); + temp[LABMAX+len]=0; + } + if (labtab.zoek(temp,val)) return 1; + labelnotfound=olabelnotfound; + if (!l && !g && labtab.zoek(temp+len,val)) return 1; + if (pass==2) { error("Label not found",temp); return 1; } + val=0; + return 1; +} + +int getLocaleLabelValue(char *&op,aint &val) { + aint nval; + int nummer; + char *p=op,naam[LINEMAX],*np,ch; + skipblanks(p); + np=naam; + if (!isdigit(*p)) return 0; + while (*p) { + if (!isdigit(*p)) break; + *np=*p; ++p; ++np; + } + *np=0; nummer=atoi(naam); + ch=*p++; + if (isalnum(*p)) return 0; + switch (ch) { + case 'b': case 'B': nval=loklabtab.zoekb(nummer); break; + case 'f': case 'F': nval=loklabtab.zoekf(nummer); break; + default: return 0; + } + if (nval==(aint)-1) + if (pass==2) { error("Label not found",naam,SUPPRES); return 1; } + else nval=0; + op=p; val=nval; + return 1; +} + +labtabentrycls::labtabentrycls() { + name=NULL; value=used=0; +} + +labtabcls::labtabcls() { + nextlocation=1; +} + +int labtabcls::insert(char *nname,aint nvalue) { + if (nextlocation>=LABTABSIZE*2/3) error("Label table full",0,FATAL); + int tr,htr; + tr=hash(nname); + while(htr=hashtable[tr]) { + if (!strcmp((labtab[htr].name),nname)) return 0; + else if (++tr>=LABTABSIZE) tr=0; + } + hashtable[tr]=nextlocation; + labtab[nextlocation].name=strdup(nname); + labtab[nextlocation].value=nvalue; labtab[nextlocation].used=-1; + ++nextlocation; + return 1; +} + +int labtabcls::zoek(char *nname,aint &nvalue) { + int tr,htr,otr; + otr=tr=hash(nname); + while(htr=hashtable[tr]) { + if (!strcmp((labtab[htr].name),nname)) { + nvalue=labtab[htr].value; if (pass==2) ++labtab[htr].used; return 1; + } + if (++tr>=LABTABSIZE) tr=0; + if (tr==otr) break; + } + labelnotfound=1; + nvalue=0; + return 0; +} + +int labtabcls::hash(char* s) { + char *ss=s; + unsigned int h=0,g; + for (;*ss!='\0';ss++) { + h=(h<<4)+ *ss; + if (g=h & 0xf0000000) { h^=g>>24; h^=g; } + } + return h % LABTABSIZE; +} + +void labtabcls::dump() { + char line[LINEMAX],*ep; + if (!listfile) { listfile=1; OpenList(); } + fputs("\nvalue label\n",listfp); + fputs("-------- - -----------------------------------------------------------\n",listfp); + for (int i=1; i=FUNTABSIZE*2/3) { ErrorAndExit("funtab full", ERR_FATAL); } + int tr,htr; + tr=hash(nname); + while(htr=hashtable[tr]) { + if (!strcmp((funtab[htr].name),nname)) return 0; + else if (++tr>=FUNTABSIZE) tr=0; + } + hashtable[tr]=nextlocation; + funtab[nextlocation].name=strdup(nname); + funtab[nextlocation].funp=nfunp; + ++nextlocation; + + strcpy(p=temp,nname); while(*p=(char)toupper(*p)) ++p; + + if (nextlocation>=FUNTABSIZE*2/3) { ErrorAndExit("funtab full", ERR_FATAL); } + tr=hash(temp); + while(htr=hashtable[tr]) { + if (!strcmp((funtab[htr].name),temp)) return 0; + else if (++tr>=FUNTABSIZE) tr=0; + } + hashtable[tr]=nextlocation; + funtab[nextlocation].name=strdup(temp); + funtab[nextlocation].funp=nfunp; + ++nextlocation; + + return 1; +} + +int funtabcls::insertd(char *nname, void(*nfunp)(void)) { + char *buf=new char[strlen(nname)+2]; + strcpy(buf,nname); if (!insert(buf,nfunp)) return 0; + strcpy(buf+1,nname); buf[0]='.'; return insert(buf,nfunp); +} + +int funtabcls::zoek(char *nname) { + int tr,htr,otr; + otr=tr=hash(nname); + while(htr=hashtable[tr]) { + if (!strcmp((funtab[htr].name),nname)) { + (*funtab[htr].funp)(); return 1; + } + if (++tr>=FUNTABSIZE) tr=0; + if (tr==otr) break; + } + return 0; +} + +int funtabcls::hash(char* s) { + char* ss=s; + unsigned int h=0; + for (;*ss!='\0';ss++) { + h=(h<<3)+ *ss; + } + return h % FUNTABSIZE; +} + +loklabtabentrycls::loklabtabentrycls(aint nnummer, aint nvalue, loklabtabentrycls *n) { + regel=gcurlin; nummer=nnummer; value=nvalue; + prev=n; next=NULL; if (n) n->next=this; +} + +loklabtabcls::loklabtabcls() { + first=last=NULL; +} + +void loklabtabcls::insert(aint nnummer, aint nvalue) { + last=new loklabtabentrycls(nnummer,nvalue,last); + if (!first) first=last; +} + +aint loklabtabcls::zoekf(aint nnum) { + loklabtabentrycls *l=first; + while (l) if (l->regel<=gcurlin) l=l->next; else break; + while (l) if (l->nummer==nnum) return l->value; else l=l->next; + return (aint)-1; +} + +aint loklabtabcls::zoekb(aint nnum) { + loklabtabentrycls *l=last; + while (l) if (l->regel>gcurlin) l=l->prev; else break; + while (l) if (l->nummer==nnum) return l->value; else l=l->prev; + return (aint)-1; +} + +definetabentrycls::definetabentrycls(char *nnaam,char *nvervanger, definetabentrycls *nnext) { + char *s1, *s2; + naam=strdup(nnaam); + vervanger=new char[strlen(nvervanger)+1]; + s1=vervanger; s2=nvervanger; skipblanks(s2); + while (*s2 && *s2!='\n' && *s2!='\r') { *s1=*s2; ++s1; ++s2; } *s1=0; + next=nnext; +} + +void definetabcls::init() { + for (int i=0; i<128; defs[i++]=0); +} + +void definetabcls::add(char *naam, char *vervanger) { + if (bestaat(naam)) error("Duplicate define",naam); + defs[*naam]=new definetabentrycls(naam,vervanger,defs[*naam]); +} + +char *definetabcls::getverv(char *naam) { + definetabentrycls *p=defs[*naam]; + while (p) { + if (!strcmp(naam,p->naam)) return p->vervanger; + p=p->next; + } + return NULL; +} + +int definetabcls::bestaat(char *naam) { + definetabentrycls *p=defs[*naam]; + while (p) { + if (!strcmp(naam,p->naam)) return 1; + p=p->next; + } + return 0; +} + +void macdefinetabcls::init() { + defs=NULL; + for (int i=0; i<128; used[i++]=0); +} + +void macdefinetabcls::macroadd(char *naam, char *vervanger) { + defs=new definetabentrycls(naam,vervanger,defs); + used[*naam]=1; +} + +definetabentrycls *macdefinetabcls::getdefs() { + return defs; +} + +void macdefinetabcls::setdefs(definetabentrycls *ndefs) { + defs=ndefs; +} + +char *macdefinetabcls::getverv(char *naam) { + definetabentrycls *p=defs; + if (!used[*naam]) return NULL; + while (p) { + if (!strcmp(naam,p->naam)) return p->vervanger; + p=p->next; + } + return NULL; +} + +int macdefinetabcls::bestaat(char *naam) { + definetabentrycls *p=defs; + if (!used[*naam]) return 0; + while (p) { + if (!strcmp(naam,p->naam)) return 1; + p=p->next; + } + return 0; +} + +stringlst::stringlst(char*nstring,stringlst*nnext) { + string=strdup(nstring); + next=nnext; +} + +macrotabentrycls::macrotabentrycls(char *nnaam,macrotabentrycls *nnext) { + naam=nnaam; next=nnext; args=body=NULL; +} + +void macrotabcls::init() { + macs=NULL; + for (int i=0; i<128; used[i++]=0); +} + +int macrotabcls::bestaat(char *naam) { + macrotabentrycls *p=macs; + if (!used[*naam]) return 0; + while (p) { + if (!strcmp(naam,p->naam)) return 1; + p=p->next; + } + return 0; +} + +void macrotabcls::add(char *nnaam,char *&p) { + char *n; + stringlst *s,*l=NULL,*f=NULL; + if (bestaat(nnaam)) error("Duplicate macroname",0,PASS1); + macs=new macrotabentrycls(nnaam,macs); + used[*nnaam]=1; + skipblanks(p); + while (*p) { + if (!(n=getid(p))) { error("Illegal macro argument",p,PASS1); break; } + s=new stringlst(n,NULL); if (!f) f=s; if (l) l->next=s; l=s; + skipblanks(p); if (*p==',') ++p; else break; + } + macs->args=f; + if (*p) error("Unexpected",p,PASS1); + ListFile(); + if (!ReadFileToStringLst(macs->body,"endm")) error("Unexpected end of macro",0,PASS1); + + insideCompassStyleMacroDefinition = 0; +} + +int macrotabcls::emit(char *naam, char *&p) { + stringlst *a,*olijstp; + char *n,labnr[LINEMAX],ml[LINEMAX],*omacrolabp; + macrotabentrycls *m=macs; + definetabentrycls *odefs; + int olistmacro,olijst; + if (!used[*naam]) return 0; + while (m) { + if (!strcmp(naam,m->naam)) break; + m=m->next; + } + if (!m) return 0; + omacrolabp=macrolabp; + sprintf(labnr,"%d",macronummer++); + macrolabp=labnr; + if (omacrolabp) { strcat(macrolabp,"."); strcat(macrolabp,omacrolabp); } else macdeftab.init(); + odefs=macdeftab.getdefs(); + a=m->args; + while (a) { + n=ml; + skipblanks(p); + if (!*p) { error("Not enough arguments",0); return 1; } + if (*p=='<') { + ++p; + while (*p!='>') { + if (!*p) { error("Not enough arguments",0); return 1; } + if (*p=='!') { + ++p; if (!*p) { error("Not enough arguments",0); return 1; } + } + *n=*p; ++n; ++p; + } + ++p; + } else while (*p!=',' && *p) { *n=*p; ++n; ++p; } + *n=0; macdeftab.macroadd(a->string,ml); + skipblanks(p); a=a->next; if (a && *p!=',') { error("Not enough arguments",0); return 1; } + if (*p==',') ++p; + } + skipblanks(p); if (*p) error("Too many arguments",0); + ListFile(); + olistmacro=listmacro; listmacro=1; + olijstp=lijstp; olijst=lijst; + lijstp=m->body; lijst=1; + strcpy(ml,line); + while (lijstp) { + strcpy(line,lijstp->string); lijstp=lijstp->next; + ParseLine(); + } + strcpy(line,ml); lijst=olijst; lijstp=olijstp; + macdeftab.setdefs(odefs); macrolabp=omacrolabp; + listmacro=olistmacro; donotlist=1; return 0; +} + +structmembncls::structmembncls(char *nnaam, aint noffset) { + next=0; naam=strdup(nnaam); offset=noffset; +} + +structmembicls::structmembicls(aint noffset,aint nlen,aint ndef,structmembs nsoort) { + next=0; offset=noffset; len=nlen; def=ndef; soort=nsoort; +} + +structcls::structcls(char *nnaam,char *nid,int idx,int no,int ngl,structcls *p) { + mnf=mnl=0; mbf=mbl=0; + naam=strdup(nnaam); id=strdup(nid); binding=idx; next=p; noffset=no; global=ngl; +} + +void structcls::addlabel(char *nnaam) { + structmembncls *n=new structmembncls(nnaam,noffset); + if (!mnf) mnf=n; if (mnl) mnl->next=n; mnl=n; +} + +void structcls::addmemb(structmembicls *n) { + if (!mbf) mbf=n; if (mbl) mbl->next=n; mbl=n; + noffset+=n->len; +} + +void structcls::copylabel(char *nnaam, aint offset) { + structmembncls *n=new structmembncls(nnaam,noffset+offset); + if (!mnf) mnf=n; if (mnl) mnl->next=n; mnl=n; +} + +void structcls::cpylabels(structcls *st) { + char str[LINEMAX],str2[LINEMAX]; + structmembncls *np=st->mnf; + if (!np || !prevlab) return; + str[0]=0; strcat(str,prevlab); strcat(str,"."); + while (np) { strcpy(str2,str); strcat(str2,np->naam); copylabel(str2,np->offset); np=np->next; } +} + +void structcls::copymemb(structmembicls *ni, aint ndef) { + structmembicls *n=new structmembicls(noffset,ni->len,ndef,ni->soort); + if (!mbf) mbf=n; if (mbl) mbl->next=n; mbl=n; + noffset+=n->len; +} + +void structcls::cpymembs(structcls *st,char *&lp) { + structmembicls *ip; + aint val; + int haakjes=0; + ip=new structmembicls(noffset,0,0,SMEMBPARENOPEN); addmemb(ip); + skipblanks(lp); if (*lp=='{') { ++haakjes; ++lp; } + ip=st->mbf; + while (ip) { + switch(ip->soort) { + case SMEMBBLOCK: copymemb(ip,ip->def); break; + case SMEMBBYTE: + case SMEMBWORD: + case SMEMBD24: + case SMEMBDWORD: + synerr=0; if (!ParseExpression(lp,val)) val=ip->def; synerr=1; copymemb(ip,val); comma(lp); break; + case SMEMBPARENOPEN: skipblanks(lp); if (*lp=='{') { ++haakjes; ++lp; } break; + case SMEMBPARENCLOSE: skipblanks(lp); if (haakjes && *lp=='}') { --haakjes; ++lp; comma(lp); } break; + default: error("internalerror structcls::cpymembs",0,FATAL); + } + ip=ip->next; + } + while (haakjes--) if (!need(lp,'}')) error("closing } missing",0); + ip=new structmembicls(noffset,0,0,SMEMBPARENCLOSE); addmemb(ip); +} + +void structcls::deflab() { + char ln[LINEMAX],sn[LINEMAX],*p,*op; + aint oval; + structmembncls *np=mnf; + strcpy(sn,"@"); strcat(sn,id); + op=p=sn; + p=MaakLabNaam(p); + if (pass==2) { + if (!getLabelValue(op,oval)) error("Internal error. ParseLabel()",0,FATAL); + if (noffset!=oval) error("Label has different value in pass 2",temp); + } else { + if (!labtab.insert(p,noffset)) error("Duplicate label",tp,PASS1); + } + strcat(sn,"."); + while (np) { + strcpy(ln,sn); strcat(ln,np->naam); + op=ln; + if (!(p=MaakLabNaam(ln))) error("Illegal labelname",ln,PASS1); + if (pass==2) { + if (!getLabelValue(op,oval)) error("Internal error. ParseLabel()",0,FATAL); + if (np->offset!=oval) error("Label has different value in pass 2",temp); + } else { + if (!labtab.insert(p,np->offset)) error("Duplicate label",tp,PASS1); + } + np=np->next; + } +} + +void structcls::emitlab(char *iid) { + char ln[LINEMAX],sn[LINEMAX],*p,*op; + aint oval; + structmembncls *np=mnf; + strcpy(sn,iid); + op=p=sn; + p=MaakLabNaam(p); + if (pass==2) { + if (!getLabelValue(op,oval)) error("Internal error. ParseLabel()",0,FATAL); + if (adres!=oval) error("Label has different value in pass 2",temp); + } else { + if (!labtab.insert(p,adres)) error("Duplicate label",tp,PASS1); + } + strcat(sn,"."); + while (np) { + strcpy(ln,sn); strcat(ln,np->naam); + op=ln; + if (!(p=MaakLabNaam(ln))) error("Illegal labelname",ln,PASS1); + if (pass==2) { + if (!getLabelValue(op,oval)) error("Internal error. ParseLabel()",0,FATAL); + if (np->offset+adres!=oval) error("Label has different value in pass 2",temp); + } else { + if (!labtab.insert(p,np->offset+adres)) error("Duplicate label",tp,PASS1); + } + np=np->next; + } +} + +void structcls::emitmembs(char*&p) { + int *e,et=0,t; + e=new int[noffset]; + structmembicls *ip=mbf; + aint val; + int haakjes=0; + skipblanks(p); if (*p=='{') { ++haakjes; ++p; } + while (ip) { + switch(ip->soort) { + case SMEMBBLOCK: t=ip->len; while (t--) { e[et++]=ip->def; } break; + case SMEMBBYTE: + synerr=0; if (!ParseExpression(p,val)) val=ip->def; synerr=1; + e[et++]=val%256; + check8(val); comma(p); + break; + case SMEMBWORD: + synerr=0; if (!ParseExpression(p,val)) val=ip->def; synerr=1; + e[et++]=val%256; e[et++]=(val>>8)%256; + check16(val); comma(p); + break; + case SMEMBD24: + synerr=0; if (!ParseExpression(p,val)) val=ip->def; synerr=1; + e[et++]=val%256; e[et++]=(val>>8)%256; e[et++]=(val>>16)%256; + check24(val); comma(p); + break; + case SMEMBDWORD: + synerr=0; if (!ParseExpression(p,val)) val=ip->def; synerr=1; + e[et++]=val%256; e[et++]=(val>>8)%256; e[et++]=(val>>16)%256; e[et++]=(val>>24)%256; + comma(p); + break; + case SMEMBPARENOPEN: skipblanks(p); if (*p=='{') { ++haakjes; ++p; } break; + case SMEMBPARENCLOSE: skipblanks(p); if (haakjes && *p=='}') { --haakjes; ++p; comma(p); } break; + default: error("internalerror structcls::emitmembs",0,FATAL); + } + ip=ip->next; + } + while (haakjes--) if (!need(p,'}')) error("closing } missing",0); + skipblanks(p); if (*p) error("Syntax error - too many arguments?",0); + e[et]=-1; EmitBytes(e); +} + +void structtabcls::init() { + for (int i=0; i<128; strs[i++]=0); +} + +structcls* structtabcls::add(char *naam,int no,int idx,int gl) { + char sn[LINEMAX],*sp; + sn[0]=0; if (!gl && modlabp) { strcpy(sn,modlabp); strcat(sn,"."); } + sp=strcat(sn,naam); + if (bestaat(sp)) error("Duplicate structurename",naam,PASS1); + strs[*sp]=new structcls(naam,sp,idx,0,gl,strs[*sp]); + if (no) strs[*sp]->addmemb(new structmembicls(0,no,0,SMEMBBLOCK)); + return strs[*sp]; +} + +structcls *structtabcls::zoek(char *naam,int gl) { + char sn[LINEMAX],*sp; + sn[0]=0; if (!gl && modlabp) { strcpy(sn,modlabp); strcat(sn,"."); } + sp=strcat(sn,naam); + structcls *p=strs[*sp]; + while (p) { if (!strcmp(sp,p->id)) return p; p=p->next; } + if (!gl && modlabp) { + sp+=1+strlen(modlabp); p=strs[*sp]; + while (p) { if (!strcmp(sp,p->id)) return p; p=p->next; } + } + return 0; +} + +int structtabcls::bestaat(char *naam) { + structcls *p=strs[*naam]; + while (p) { if (!strcmp(naam,p->naam)) return 1; p=p->next; } + return 0; +} + +int structtabcls::emit(char *naam, char *l, char *&p, int gl) { + structcls *st=zoek(naam,gl); + if (!st) return 0; + if (l) st->emitlab(l); + st->emitmembs(p); + return 1; +} + +#ifdef SECTIONS +pooldatacls::pooldatacls() { + zp=pp=first=last=NULL; +} + +void PoolData() { + aint val, regel; + ++lp; + if (!ParseExpression(lp,val)) error("internal error pooldata",0,FATAL); + if (!ParseExpression(lp,regel)) error("internal error pooldata",0,FATAL); + pooldata.pool(regel,adres); emitarm(val); +} + +void pooldatacls::add(aint nregel, aint ndata, int nwok) { + pooldataentrycls *tmp; + tmp=new pooldataentrycls; + tmp->regel=nregel; + tmp->data=ndata; + tmp->adres=~0; + tmp->wok=nwok; + if (!first) first=tmp; + if (last) last->next=tmp; + last=tmp; tmp->next=NULL; +} + +int pooldatacls::zoekregel(aint nregel, aint &data, aint &adres, int &wok) { + if (!zp) { zp=first; if (!zp) return 0; } + while (zp && zp->regel!=nregel) zp=zp->next; + if (!zp) { + zp=first; while (zp && zp->regel!=nregel) zp=zp->next; + if (!zp) return 0; + } + adres=zp->adres; wok=zp->wok; data=zp->data; + return 1; +} + +int pooldatacls::zoek(aint data) { + p=first; zoekdit=data; + return p!=NULL; +} + +int pooldatacls::zoeknext(aint &adres, int ®) { + while (p) { + if (p->data==zoekdit && !p->wok) { + adres=p->adres; + reg=(p->regel==curlin); + p=p->next; + return 1; + } + p=p->next; + } + return 0; +} + +void pooldatacls::pool(aint regel, aint nadres) { + if (!pp) { pp=first; if (!pp) error("internal error pooldatacls",0,FATAL); } + while (pp && pp->regel!=regel) pp=pp->next; + if (!pp) { + pp=first; while (pp && pp->regel!=regel) pp=pp->next; + if (!pp) error("internal error pooldatacls",0,FATAL); + } + pp->adres=nadres; +} + +pooltabcls::pooltabcls() { + first=last=NULL; +} + +void pooltabcls::add(char *ndata) { + char *data; + data=new char[strlen(ndata)+2]; + strcpy(data," "); strcat(data,ndata); + addlabel(data); +} + +void pooltabcls::addlabel(char *ndata) { + pooltabentrycls *tmp; + tmp=new pooltabentrycls; + tmp->data=strdup(ndata); + tmp->regel=lcurlin; + if (!first) first=tmp; + if (last) last->next=tmp; + last=tmp; tmp->next=NULL; +} + +void pooltabcls::emit() { + int olistdata=listdata; + char *olp=lp,oline[LINEMAX]; + strcpy(oline,line); strcpy(line,"| Pool\n"); + if (first +#ifdef METARM + && cpu!=Z80 +#endif + ) { EmitBlock(0,(~adres+1)&3); ListFile(); } + while (first) { + if (*(lp=first->data)==' ') { + if (*(lp+1)=='!') { + lp++; PoolData(); listdata=0; ListFile(); listdata=olistdata; + } else { + ParseDirective(); ListFile(); + } + } else { + if (pass==1) if(!labtab.insert(lp,adres)) error("Duplicate label",tp,PASS1); + break; + } + first=first->next; + } + last=first; lp=olp; + strcpy(line,oline); +} +#endif +//eof tables.cpp diff --git a/tools/sjasm/tables.h b/tools/sjasm/tables.h new file mode 100644 index 00000000..531f3523 --- /dev/null +++ b/tools/sjasm/tables.h @@ -0,0 +1,250 @@ +/* + + SjASM Z80 Assembler + + Copyright (c) 2015 Konamiman + Based on Sjasm 0.39g6 - Copyright (c) 2006 Sjoerd Mastijn + + This software is provided 'as-is', without any express or implied warranty. + In no event will the authors be held liable for any damages arising from the + use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it freely, + subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not claim + that you wrote the original software. If you use this software in a product, + an acknowledgment in the product documentation would be appreciated but is + not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source distribution. + +*/ + +// tables.h + +char *MaakLabNaam(char*); +extern char *prevlab; +int getLabelValue(char *&p, aint &val); +int getLocaleLabelValue(char *&op,aint &val); +#ifdef SECTIONS +void PoolData(); +#endif + +class labtabentrycls { +public: + char *name; + aint value,used; + labtabentrycls(); +}; + +class labtabcls { +public: + labtabcls(); + int insert(char*,aint); + int zoek(char*,aint&); + void dump(); + void dumpsym(); +private: + int hashtable[LABTABSIZE],nextlocation; + labtabentrycls labtab[LABTABSIZE]; + int hash(char*); +}; + +class funtabentrycls { +public: + char *name; + void (*funp)(void); +}; + +class funtabcls { +public: + funtabcls(); + int insert(char*,void(*)(void)); + int insertd(char*,void(*)(void)); + int zoek(char*); +private: + int hashtable[FUNTABSIZE],nextlocation; + funtabentrycls funtab[FUNTABSIZE]; + int hash(char*); +}; + +class loklabtabentrycls { +public: + aint regel,nummer,value; + loklabtabentrycls *next,*prev; + loklabtabentrycls(aint,aint,loklabtabentrycls*); +}; + +class loklabtabcls { +public: + loklabtabcls(); + aint zoekf(aint); + aint zoekb(aint); + void insert(aint,aint); +private: + loklabtabentrycls *first,*last; +}; + +class definetabentrycls { +public: + char *naam, *vervanger; + definetabentrycls *next; + definetabentrycls(char*,char*,definetabentrycls*); +}; + +class definetabcls { +public: + void init(); + void add(char*,char*); + char *getverv(char*); + int bestaat(char*); + definetabcls() { init(); } +private: + definetabentrycls *defs[128]; +}; + +class macdefinetabcls { +public: + void init(); + void macroadd(char*,char*); + definetabentrycls *getdefs(); + void setdefs(definetabentrycls *); + char *getverv(char*); + int bestaat(char*); + macdefinetabcls() { init(); } +private: + int used[128]; + definetabentrycls *defs; +}; + +class adrlst { +public: + aint val; + adrlst *next; + adrlst() { next=0; } + adrlst(aint nval,adrlst*nnext) { val=nval; next=nnext; } +}; + +class stringlst { +public: + char *string; + stringlst *next; + stringlst() { next=0; } + stringlst(char*,stringlst*); +}; + +class macrotabentrycls { +public: + char *naam; + stringlst *args, *body; + macrotabentrycls *next; + macrotabentrycls(char*,macrotabentrycls*); +}; + +class macrotabcls { +public: + void add(char*,char*&); + int emit(char*,char*&); + int bestaat(char*); + void init(); + macrotabcls() { init(); } +private: + int used[128]; + macrotabentrycls *macs; +}; + +class structmembncls { +public: + char *naam; + aint offset; + structmembncls *next; + structmembncls(char*,aint); +}; + +class structmembicls { +public: + aint offset,len,def; + structmembs soort; + structmembicls *next; + structmembicls(aint,aint,aint,structmembs); +}; + +class structcls { +public: + char *naam,*id; + int binding; + int global; + aint noffset; + void addlabel(char*); + void addmemb(structmembicls*); + void copylabel(char*,aint); + void cpylabels(structcls*); + void copymemb(structmembicls*,aint); + void cpymembs(structcls*,char*&); + void deflab(); + void emitlab(char*); + void emitmembs(char*&); + structcls *next; + structcls(char*,char*,int,int,int,structcls*); +private: + structmembncls *mnf,*mnl; + structmembicls *mbf,*mbl; +}; + +class structtabcls { +public: + structcls* add(char*,int,int,int); + void init(); + structtabcls() { init(); } + structcls *zoek(char*,int); + int bestaat(char*); + int emit(char*,char*,char*&,int); +private: + structcls *strs[128]; +}; + +#ifdef SECTIONS +class pooldataentrycls { +public: + int wok; + aint regel, data, adres; + pooldataentrycls *next; +}; + +class pooldatacls { +public: + pooldatacls(); + void add(aint,aint,int); + int zoek(aint); + int zoeknext(aint&,int&); + void pool(aint,aint); + int zoekregel(aint,aint&,aint&,int&); +private: + aint zoekdit; + pooldataentrycls *first, *last, *p, *pp, *zp; +}; + +class pooltabentrycls { +public: + char *data; + aint regel; + pooltabentrycls *next; +}; + +class pooltabcls { +public: + pooltabcls(); + void add(char*); + void addlabel(char*); + void emit(); +private: + pooltabentrycls *first,*last; +}; +#endif +//eof tables.h + diff --git a/tools/sjasm/targetver.h b/tools/sjasm/targetver.h new file mode 100644 index 00000000..90e767bf --- /dev/null +++ b/tools/sjasm/targetver.h @@ -0,0 +1,8 @@ +#pragma once + +// Including SDKDDKVer.h defines the highest available Windows platform. + +// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and +// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. + +#include diff --git a/tools/slz/LICENSE b/tools/slz/LICENSE new file mode 100644 index 00000000..77f1200d --- /dev/null +++ b/tools/slz/LICENSE @@ -0,0 +1,17 @@ +© 2011-2018 Javier Degirolmo + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. diff --git a/tools/slz/compress.c b/tools/slz/compress.c new file mode 100644 index 00000000..c3a504cc --- /dev/null +++ b/tools/slz/compress.c @@ -0,0 +1,216 @@ +//*************************************************************************** +// "compress.c" +// Compresses a raw blob into an SLZ file +//*************************************************************************** +// Slz compression tool +// Copyright 2011, 2017 Javier Degirolmo +// +// This file is part of the slz tool. +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source distribution. +//*************************************************************************** + +// Required headers +#include +#include +#include +#include +#include "main.h" + +//*************************************************************************** +// compress +// Reads an uncompressed blob from a file and outputs SLZ compressed data +//--------------------------------------------------------------------------- +// param infile: input file +// param outfile: output file +// param format: SLZ variant in use +// return: error code +//*************************************************************************** + +int compress(FILE *infile, FILE *outfile, int format) { + // THIS SHOULDN'T HAPPEN + // (also this check is to help the compiler optimize) + if (format != FORMAT_SLZ16 && format != FORMAT_SLZ24) + return ERR_UNKNOWN; + + // To store error codes + int errcode; + + // Get filesize (sorry, we have to seek here!) + if (fseek(infile, 0, SEEK_END)) return ERR_CANTREAD; + long eof_pos = ftell(infile); + if (eof_pos == -1) return ERR_CANTREAD; + size_t filesize = (size_t)(eof_pos); + if (fseek(infile, 0, SEEK_SET)) return ERR_CANTREAD; + + // Empty? + if (filesize == 0) { + if (format == FORMAT_SLZ16) + errcode = write_word(outfile, 0); + else + errcode = write_tribyte(outfile, 0); + return errcode; + } + + // Too large for SLZ? + if (filesize > 0xFFFF && format == FORMAT_SLZ16) + return ERR_TOOLARGE16; + else if (filesize > 0xFFFFFF && format == FORMAT_SLZ24) + return ERR_TOOLARGE24; + + // Allocate memory to store blob + uint8_t *blob = (uint8_t *) malloc(sizeof(uint8_t) * filesize); + if (blob == NULL) return ERR_NOMEMORY; + + // Load input file into memory + if (fread(blob, 1, filesize, infile) < filesize) { + free(blob); + return ERR_CANTREAD; + } + + // Write uncompressed size + if (format == FORMAT_SLZ16) + errcode = write_word(outfile, (uint16_t)(filesize)); + else + errcode = write_tribyte(outfile, (uint32_t)(filesize)); + if (errcode) return errcode; + + // To store token data + uint8_t tokens = 0; + int num_tokens = 0; + + // Buffer to store the compressed data (we need to buffer this because the + // tokens need to be written first) + uint8_t buffer[0x10]; + size_t bufsize = 0; + + // Scan through all bytes + size_t pos = 0; + while (pos < filesize) { + // Used to store token information + int compressed = 0; + uint16_t dist = 3; + uint8_t len = 2; + + // Make room for next token + tokens <<= 1; + num_tokens++; + + // First byte is always uncompressed + // Also if there are too few bytes left, we can't compress them, so + // don't bother scanning those either + if (pos > 0 && filesize - pos >= 3) { + // Determine maximum distance to look for + uint16_t max_dist = (uint16_t)(pos > 0x1002 ? 0x1002 : pos); + + // Determine maximum length to check for (to avoid overflow issues) + uint8_t max_len = (uint8_t)(filesize - pos > 18 ? 18 : filesize - pos); + + // Pointer to the strings we're going to compare + // Making them pointers to help the compiler optimize + const uint8_t *target = &blob[pos]; + const uint8_t *other = &blob[pos - max_dist]; + + // Scan for a possible match + size_t curr_dist; + for (curr_dist = max_dist; curr_dist >= 3; curr_dist--, other++) + { + // To avoid wasting time on matches that WILL fail... + if (*other != *target) + continue; + + // Check all lengths that are larger than the longest string we + // found so far (don't bother with shorter strings as that'd be + // a waste) + uint8_t curr_len; + for (curr_len = max_len; curr_len > len; curr_len--) { + // Did we find a match? (if so, don't bother with smaller + // string, also mark the token as compressable) + if (!memcmp(other, target, curr_len)) { + dist = (uint16_t)(curr_dist); + len = curr_len; + compressed = 1; + break; + } + } + } + } + + // Compressed token? + if (compressed) { + // Skip repeated string in the blob + pos += len; + + // Store contents of the token + dist -= 3; + len -= 3; + buffer[bufsize] = dist >> 4; + buffer[bufsize+1] = dist << 4 | len; + bufsize += 2; + + // Mark token as compressed + tokens |= 1; + } + + // Uncompressed token? + else { + buffer[bufsize] = blob[pos]; + pos++; + bufsize++; + } + + // Huh, done with this group of tokens? + if (num_tokens == 8) { + // Write token types + if (fwrite(&tokens, 1, 1, outfile) < 1) { + free(blob); + return ERR_CANTWRITE; + } + + // Write buffered data + if (fwrite(buffer, 1, bufsize, outfile) < bufsize) { + free(blob); + return ERR_CANTWRITE; + } + + // Reset tokens and buffer + tokens = 0; + num_tokens = 0; + bufsize = 0; + } + } + + // We don't need the blob anymore + free(blob); + + // If there are any tokens left, make sure they're written too + if (num_tokens < 8) { + // Make sure token types are MSB aligned + tokens <<= 8 - num_tokens; + + // Write token types + if (fwrite(&tokens, 1, 1, outfile) < 1) + return ERR_CANTWRITE; + + // Write buffered data + if (fwrite(buffer, 1, bufsize, outfile) < bufsize) + return ERR_CANTWRITE; + } + + // Success! + return ERR_NONE; +} diff --git a/tools/slz/compress.h b/tools/slz/compress.h new file mode 100644 index 00000000..4ca5758e --- /dev/null +++ b/tools/slz/compress.h @@ -0,0 +1,36 @@ +//*************************************************************************** +// "compress.h" +// Header file for "compress.c" +//*************************************************************************** +// Slz compression tool +// Copyright 2011 Javier Degirolmo +// +// This file is part of the slz tool. +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source distribution. +//*************************************************************************** + +#ifndef COMPRESS_H +#define COMPRESS_H + +// Required headers +#include + +// Function prototypes +int compress(FILE *, FILE *, int); + +#endif diff --git a/tools/slz/decompress.c b/tools/slz/decompress.c new file mode 100644 index 00000000..d30601e1 --- /dev/null +++ b/tools/slz/decompress.c @@ -0,0 +1,142 @@ +//*************************************************************************** +// "decompress.c" +// Decompresses a SLZ file into a raw blob +//*************************************************************************** +// Slz compression tool +// Copyright 2011 Javier Degirolmo +// +// This file is part of the slz tool. +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source distribution. +//*************************************************************************** + +// Required headers +#include +#include +#include +#include +#include "main.h" + +//*************************************************************************** +// decompress +// Reads a file in SLZ format and outputs an uncompressed blob +//--------------------------------------------------------------------------- +// param infile: input file +// param outfile: output file +// param format: SLZ variant in use +// return: error code +//*************************************************************************** + +int decompress(FILE *infile, FILE *outfile, int format) { + // THIS SHOULDN'T HAPPEN + // (also this check is to help the compiler optimize) + if (format != FORMAT_SLZ16 && format != FORMAT_SLZ24) + return ERR_UNKNOWN; + + // To store error codes + int errcode; + + // Read uncompressed size + uint32_t size; + if (format == FORMAT_SLZ16) { + uint16_t temp; + errcode = read_word(infile, &temp); + size = temp; + } else + errcode = read_tribyte(infile, &size); + if (errcode) return errcode; + + // No data? If so, we're done! + if (size == 0) + return ERR_NONE; + + // Allocate memory to store the data + uint8_t *data = (uint8_t *) malloc(sizeof(uint8_t) * size); + if (data == NULL) + return ERR_NOMEMORY; + + // To store token data + uint8_t tokens; + uint8_t num_tokens = 0; + + // Decompress all data + size_t pos = 0; + while (pos < size) { + // Need more tokens? + if (num_tokens == 0) { + num_tokens = 8; + if (fread(&tokens, 1, 1, infile) < 1) { + free(data); + return ferror(infile) ? ERR_CANTREAD : ERR_CORRUPT; + } + } + + // Compressed string? + if (tokens & 0x80) { + // Get string information + uint16_t info; + errcode = read_word(infile, &info); + if (errcode) { free(data); return errcode; } + + // Get distance and length + uint16_t dist = (info >> 4) + 3; + uint8_t len = (info & 0x0F) + 3; + + // Er, make sure parameters are valid... + if (dist > pos) { + free(data); + return ERR_CORRUPT; + } + + // Copy string! + uint8_t *dest = data + pos; + uint8_t *src = dest - dist; + pos += len; + while (len-- > 0) + *dest++ = *src++; + } + + // Uncompressed byte? + else { + if (fread(&data[pos], 1, 1, infile) < 1) { + free(data); + return ferror(infile) ? ERR_CANTREAD : ERR_CORRUPT; + } + pos++; + } + + // Go for next token + tokens <<= 1; + num_tokens--; + } + + // Not the expected size? + if (pos != size) { + free(data); + return ERR_CORRUPT; + } + + // Write decompressed data into output file + if (fwrite(data, 1, size, outfile) < size) { + free(data); + return ERR_CANTWRITE; + } + + // Success! + free(data); + return ERR_NONE; +} diff --git a/tools/slz/decompress.h b/tools/slz/decompress.h new file mode 100644 index 00000000..f195fdf9 --- /dev/null +++ b/tools/slz/decompress.h @@ -0,0 +1,36 @@ +//*************************************************************************** +// "decompress.h" +// Header file for "decompress.c" +//*************************************************************************** +// Slz compression tool +// Copyright 2011 Javier Degirolmo +// +// This file is part of the slz tool. +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source distribution. +//*************************************************************************** + +#ifndef DECOMPRESS_H +#define DECOMPRESS_H + +// Required headers +#include + +// Function prototypes +int decompress(FILE *, FILE *, int); + +#endif diff --git a/tools/slz/main.c b/tools/slz/main.c new file mode 100644 index 00000000..1a42a975 --- /dev/null +++ b/tools/slz/main.c @@ -0,0 +1,320 @@ +//*************************************************************************** +// "main.c" +// Program entry point, parses command line and runs stuff as required +//*************************************************************************** +// Slz compression tool +// Copyright 2011 Javier Degirolmo +// +// This file is part of the slz tool. +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source distribution. +//*************************************************************************** + +// Required headers +#include +#include +#include +#include "main.h" +#include "compress.h" +#include "decompress.h" + +// Possible actions +enum { + ACTION_DEFAULT, // No action specified + ACTION_COMPRESS, // Compress + ACTION_DECOMPRESS, // Decompress + ACTION_TOOMANY // Too many actions specified +}; + +//*************************************************************************** +// Program entry point +//*************************************************************************** + +int main(int argc, char **argv) { + // To know if there was an error or not + int errcode = 0; + + // Scan all arguments + int show_help = 0; + int show_ver = 0; + int action = ACTION_DEFAULT; + int format = FORMAT_DEFAULT; + const char *infilename = NULL; + const char *outfilename = NULL; + + int scan_ok = 1; + int err_manyfiles = 0; + + int curr_arg; + for (curr_arg = 1; curr_arg < argc; curr_arg++) { + // Get pointer to argument, to make our lives easier + const char *arg = argv[curr_arg]; + + // If it's an option, parse it + if (scan_ok && arg[0] == '-') { + // Stop parsing options? + if (!strcmp(arg, "--")) + scan_ok = 0; + + // Show help or version? + else if (!strcmp(arg, "-h") || !strcmp(arg, "--help")) + show_help = 1; + else if (!strcmp(arg, "-v") || !strcmp(arg, "--version")) + show_ver = 1; + + // Compress? + else if (!strcmp(arg, "-c") || !strcmp(arg, "--compress")) + action = action == ACTION_DEFAULT ? + ACTION_COMPRESS : ACTION_TOOMANY; + + // Decompress? + else if (!strcmp(arg, "-d") || !strcmp(arg, "--decompress")) + action = action == ACTION_DEFAULT ? + ACTION_DECOMPRESS : ACTION_TOOMANY; + + // Specify format? + else if (!strcmp(arg, "-16") || !strcmp(arg, "--slz16")) + format = format == FORMAT_DEFAULT ? + FORMAT_SLZ16 : FORMAT_TOOMANY; + else if (!strcmp(arg, "-24") || !strcmp(arg, "--slz24")) + format = format == FORMAT_DEFAULT ? + FORMAT_SLZ24 : FORMAT_TOOMANY; + + // Unknown argument + else { + fprintf(stderr, "Error: unknown option \"%s\"\n", arg); + errcode = 1; + } + } + + // Input filename? + else if (infilename == NULL) + infilename = arg; + + // Output filename? + else if (outfilename == NULL) + outfilename = arg; + + // Too many files specified? + else + err_manyfiles = 1; + } + + // Look for error conditions + if (action == ACTION_TOOMANY) { + errcode = 1; + fprintf(stderr, "Error: can't specify more than one action\n"); + } else if (!show_help && !show_ver) { + if (infilename == NULL) { + errcode = 1; + fprintf(stderr, "Error: input filename missing\n"); + } else if (outfilename == NULL) { + errcode = 1; + fprintf(stderr, "Error: output filename missing\n"); + } else if (err_manyfiles) { + errcode = 1; + fprintf(stderr, "Error: too many filenames specified\n"); + } + } + if (format == FORMAT_TOOMANY) { + errcode = 1; + fprintf(stderr, "Error: too many formats specified\n"); + } + + // If there was an error then quit + if (errcode) + return EXIT_FAILURE; + + // No action specified? + if (action == ACTION_DEFAULT) + action = ACTION_COMPRESS; + + // No format specified? + if (format == FORMAT_DEFAULT) + format = FORMAT_SLZ16; + + // Show tool version? + if (show_ver) { + puts("1.2b"); + return EXIT_SUCCESS; + } + + // Show tool usage? + if (show_help) { + printf("Usage:\n" + " %s -c \n" + " %s -d \n" + "\n" + "Options:\n" + " -c or --compress ..... Compress a blob into SLZ\n" + " -d or --decompress ... Decompress SLZ into a blob\n" + " -16 or --slz16 ....... Use SLZ16 format (64KB limit)\n" + " -24 or --slz24 ....... Use SLZ24 format (16MB limit)\n" + " -h or --help ......... Show this help\n" + " -v or --version ...... Show tool version\n" + "\n" + "If no option is specified, compression is done by default.\n" + "If no format is specified, SLZ16 is used by default.\n", + argv[0], argv[0]); + return EXIT_SUCCESS; + } + + // Open input file + FILE *infile = fopen(infilename, "rb"); + if (infile == NULL) { + fprintf(stderr, "Error: can't open input file \"%s\"\n", infilename); + return EXIT_FAILURE; + } + + // Open output file + FILE *outfile = fopen(outfilename, "wb"); + if (outfile == NULL) { + fprintf(stderr, "Error: can't open output file \"%s\"\n", outfilename); + fclose(infile); + return EXIT_FAILURE; + } + + // Perform the requested action + switch (action) { + // Compress file + case ACTION_COMPRESS: + errcode = compress(infile, outfile, format); + break; + + // Decompress file + case ACTION_DECOMPRESS: + errcode = decompress(infile, outfile, format); + break; + + // Oops! + default: + errcode = ERR_UNKNOWN; + break; + } + + // If there was an error, show a message + if (errcode) { + // Determine message to show + const char *msg; + switch(errcode) { + case ERR_CANTREAD: msg = "can't read from input file"; break; + case ERR_CANTWRITE: msg = "can't write to output file"; break; + case ERR_TOOLARGE16: msg = "input file size can't be over " + "65,535 bytes"; break; + case ERR_TOOLARGE24: msg = "input file size can't be over " + "16,777,215 bytes"; break; + case ERR_CORRUPT: msg = "input file isn't valid SLZ"; break; + case ERR_NOMEMORY: msg = "ran out of memory"; break; + default: msg = "unknown error"; break; + } + + // Show message on screen + fprintf(stderr, "Error: %s\n", msg); + } + + // Quit program + fclose(outfile); + fclose(infile); + if (errcode) remove(outfilename); + return errcode ? EXIT_FAILURE : EXIT_SUCCESS; +} + +//*************************************************************************** +// read_word +// Reads a 16-bit value from a file +//--------------------------------------------------------------------------- +// param infile: input file +// param buffer: where to store value +// return: error code +//*************************************************************************** + +int read_word(FILE *infile, uint16_t *buffer) { + // Try to read from input file + uint8_t temp[2]; + if (fread(temp, 1, 2, infile) < 2) + return ferror(infile) ? ERR_CANTREAD : ERR_CORRUPT; + + // Parse value + *buffer = temp[0] << 8 | temp[1]; + + // Success! + return ERR_NONE; +} + +//*************************************************************************** +// read_tribyte +// Reads a 24-bit value from a file +//--------------------------------------------------------------------------- +// param infile: input file +// param buffer: where to store value +// return: error code +//*************************************************************************** + +int read_tribyte(FILE *infile, uint32_t *buffer) { + // Try to read from input file + uint8_t temp[3]; + if (fread(temp, 1, 3, infile) < 2) + return ferror(infile) ? ERR_CANTREAD : ERR_CORRUPT; + + // Parse value + *buffer = temp[0] << 16 | temp[1] << 8 | temp[2]; + + // Success! + return ERR_NONE; +} + +//*************************************************************************** +// write_word +// Writes a 16-bit value into a file +//--------------------------------------------------------------------------- +// param outfile: output file +// param value: value to be written +// return: error code +//*************************************************************************** + +int write_word(FILE *outfile, const uint16_t value) { + // Split value into bytes + uint8_t temp[2] = { value >> 8, value & 0xFF }; + + // Try to write into file + if (fwrite(temp, 1, 2, outfile) < 2) + return ERR_CANTWRITE; + + // Success! + return ERR_NONE; +} + +//*************************************************************************** +// write_tribyte +// Writes a 24-bit value into a file +//--------------------------------------------------------------------------- +// param outfile: output file +// param value: value to be written +// return: error code +//*************************************************************************** + +int write_tribyte(FILE *outfile, const uint32_t value) { + // Split value into bytes + uint8_t temp[3] = { value >> 16, value >> 8, value & 0xFF }; + + // Try to write into file + if (fwrite(temp, 1, 3, outfile) < 3) + return ERR_CANTWRITE; + + // Success! + return ERR_NONE; +} diff --git a/tools/slz/main.h b/tools/slz/main.h new file mode 100644 index 00000000..515f6b58 --- /dev/null +++ b/tools/slz/main.h @@ -0,0 +1,59 @@ +//*************************************************************************** +// "main.h" +// Some common definitions and such +//*************************************************************************** +// Slz compression tool +// Copyright 2011 Javier Degirolmo +// +// This file is part of the slz tool. +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source distribution. +//*************************************************************************** + +#ifndef MAIN_H +#define MAIN_H + +// Required headers +#include + +// Error codes +enum { + ERR_NONE, // No error + ERR_CANTREAD, // Can't read from input file + ERR_CANTWRITE, // Can't write into output file + ERR_TOOLARGE16, // File is too large for SLZ16 + ERR_TOOLARGE24, // File is too large for SLZ24 + ERR_CORRUPT, // File is corrupt? + ERR_NOMEMORY, // Ran out of memory + ERR_UNKNOWN // Unknown error +}; + +// Possible formats +enum { + FORMAT_DEFAULT, // No format specified + FORMAT_SLZ16, // SLZ16 (16-bit size) + FORMAT_SLZ24, // SLZ24 (24-bit size) + FORMAT_TOOMANY // Too many formats specified +}; + +// Function prototypes +int read_word(FILE *, uint16_t *); +int read_tribyte(FILE *, uint32_t *); +int write_word(FILE *, const uint16_t); +int write_tribyte(FILE *, const uint32_t); + +#endif diff --git a/tools/uftc/LICENSE b/tools/uftc/LICENSE new file mode 100644 index 00000000..77f1200d --- /dev/null +++ b/tools/uftc/LICENSE @@ -0,0 +1,17 @@ +© 2011-2018 Javier Degirolmo + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. diff --git a/tools/uftc/compress.c b/tools/uftc/compress.c new file mode 100644 index 00000000..89a7f34d --- /dev/null +++ b/tools/uftc/compress.c @@ -0,0 +1,226 @@ +//*************************************************************************** +// "compress.c" +// Compresses a raw blob into an UFTC file +//*************************************************************************** +// Uftc compression tool +// Copyright 2011, 2012 Javier Degirolmo +// +// This file is part of the uftc tool. +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source distribution. +//*************************************************************************** + +// Required headers +#include +#include +#include +#include +#include "main.h" + +// Function prototypes +static int putblock(uint8_t **, size_t *, const uint8_t *, uint16_t *); + +//*************************************************************************** +// compress +// Reads an uncompressed blob from a file and outputs UFTC compressed data +//--------------------------------------------------------------------------- +// param infile: input file +// param outfile: output file +// param format: format to output for +// return: error code +//*************************************************************************** + +int compress(FILE *infile, FILE *outfile, int format) { + // To store error codes + int errcode; + + // To store the dictionary + uint8_t *dictionary = NULL; + size_t dicsize = 0; + + // To store compressed tiles + uint8_t *tiles = NULL; + size_t tilesize = 0; + + // While there're tiles to read... + for (;;) { + // Read next raw tile, if any + uint8_t rawtile[0x20]; + size_t numread = fread(rawtile, 1, 0x20, infile); + if (numread == 0 && feof(infile)) + break; + else if (numread < 0x20) + return ferror(infile) ? ERR_CANTREAD : ERR_BADSIZE; + + // To store block contents + uint8_t block[8]; + uint16_t blockid[4]; + + // Look for first block + block[0] = rawtile[0]; block[1] = rawtile[1]; + block[2] = rawtile[4]; block[3] = rawtile[5]; + block[4] = rawtile[8]; block[5] = rawtile[9]; + block[6] = rawtile[12]; block[7] = rawtile[13]; + + errcode = putblock(&dictionary, &dicsize, block, &blockid[0]); + if (errcode) { + if (dictionary) free(dictionary); + if (tiles) free(tiles); + return errcode; + } + + // Look for second block + block[0] = rawtile[2]; block[1] = rawtile[3]; + block[2] = rawtile[6]; block[3] = rawtile[7]; + block[4] = rawtile[10]; block[5] = rawtile[11]; + block[6] = rawtile[14]; block[7] = rawtile[15]; + + errcode = putblock(&dictionary, &dicsize, block, &blockid[1]); + if (errcode) { + if (dictionary) free(dictionary); + if (tiles) free(tiles); + return errcode; + } + + // Look for third block + block[0] = rawtile[16]; block[1] = rawtile[17]; + block[2] = rawtile[20]; block[3] = rawtile[21]; + block[4] = rawtile[24]; block[5] = rawtile[25]; + block[6] = rawtile[28]; block[7] = rawtile[29]; + + errcode = putblock(&dictionary, &dicsize, block, &blockid[2]); + if (errcode) { + if (dictionary) free(dictionary); + if (tiles) free(tiles); + return errcode; + } + + // Look for fourth block + block[0] = rawtile[18]; block[1] = rawtile[19]; + block[2] = rawtile[22]; block[3] = rawtile[23]; + block[4] = rawtile[26]; block[5] = rawtile[27]; + block[6] = rawtile[30]; block[7] = rawtile[31]; + + errcode = putblock(&dictionary, &dicsize, block, &blockid[3]); + if (errcode) { + if (dictionary) free(dictionary); + if (tiles) free(tiles); + return errcode; + } + + // Put compressed tile into list + tilesize += 8; + uint8_t *temp = (uint8_t *) realloc(tiles, tilesize); + if (temp == NULL) { + if (dictionary) free(dictionary); + if (tiles) free(tiles); + return ERR_NOMEMORY; + } + tiles = temp; + temp = tiles + tilesize - 8; + + *temp++ = blockid[0] >> 8; *temp++ = blockid[0] & 0xFF; + *temp++ = blockid[1] >> 8; *temp++ = blockid[1] & 0xFF; + *temp++ = blockid[2] >> 8; *temp++ = blockid[2] & 0xFF; + *temp++ = blockid[3] >> 8; *temp++ = blockid[3] & 0xFF; + } + + // Check that dictionary is OK + if (dicsize == 0) + return ERR_TOOSMALL; + else if ((dicsize >= 0x8000 && format == FORMAT_UFTC15) || + (dicsize >= 0x10000 && format == FORMAT_UFTC16)) { + free(dictionary); + free(tiles); + return ERR_TOOBIG; + } + + // Write dictionary size + errcode = write_word(outfile, dicsize); + if (errcode) { + free(dictionary); + free(tiles); + return errcode; + } + + // Write dictionary + if (fwrite(dictionary, 1, dicsize, outfile) < dicsize) { + free(dictionary); + free(tiles); + return ERR_CANTWRITE; + } + + // Write compressed tiles + if (fwrite(tiles, 1, tilesize, outfile) < tilesize) { + free(dictionary); + free(tiles); + return ERR_CANTWRITE; + } + + // We don't need this anymore + free(dictionary); + free(tiles); + + // Success! + return ERR_NONE; +} + +//*************************************************************************** +// putblock [internal] +// Looks up for a 4x4 block in the dictionary (adding it if needed), and +// returns its ID unless something went wrong +//--------------------------------------------------------------------------- +// param dictionary: pointer to dictionary +// param dicsize: pointer to dictionary size +// param block: pointer to 4x4 block data +// param id: where to store block ID +// return: error code +//*************************************************************************** + +static int putblock(uint8_t **dictionary, size_t *dicsize, +const uint8_t *block, uint16_t *id) { + // Look for a match in the dictionary only if there's a dictionary yet... + uint8_t *ptr = *dictionary; + if (*dictionary != NULL) { + // Look if block is in the dictionary already + size_t limit = *dicsize; + while (limit >= 8) { + // Is it in the dictionary already? + if (!memcmp(ptr, block, 8)) { + *id = (uint16_t)(ptr - *dictionary); + return ERR_NONE; + } + + // Nope, keep looking + ptr += 2; + limit -= 2; + }; + } + + // Block isn't in dictionary, so allocate memory for it + *dicsize += 8; + ptr = (uint8_t *) realloc(*dictionary, *dicsize); + if (ptr == NULL) return ERR_NOMEMORY; + *dictionary = ptr; + + // Store block in dictionary + *id = *dicsize - 8; + memcpy(*dictionary + *id, block, 8); + + // Success! + return ERR_NONE; +} diff --git a/tools/uftc/compress.h b/tools/uftc/compress.h new file mode 100644 index 00000000..967ebd77 --- /dev/null +++ b/tools/uftc/compress.h @@ -0,0 +1,36 @@ +//*************************************************************************** +// "compress.h" +// Header file for "compress.c" +//*************************************************************************** +// Uftc compression tool +// Copyright 2011, 2012 Javier Degirolmo +// +// This file is part of the uftc tool. +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source distribution. +//*************************************************************************** + +#ifndef COMPRESS_H +#define COMPRESS_H + +// Required headers +#include + +// Function prototypes +int compress(FILE *, FILE *, int); + +#endif diff --git a/tools/uftc/decompress.c b/tools/uftc/decompress.c new file mode 100644 index 00000000..5518e4a7 --- /dev/null +++ b/tools/uftc/decompress.c @@ -0,0 +1,126 @@ +//*************************************************************************** +// "decompress.c" +// Decompresses a UFTC file into a raw blob +//*************************************************************************** +// Uftc compression tool +// Copyright 2011, 2012 Javier Degirolmo +// +// This file is part of the uftc tool. +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source distribution. +//*************************************************************************** + +// Required headers +#include +#include +#include +#include "main.h" + +//*************************************************************************** +// decompress +// Reads a file in UFTC format and outputs an uncompressed blob +//--------------------------------------------------------------------------- +// param infile: input file +// param outfile: output file +// param format: which format to parse for +// return: error code +//*************************************************************************** + +int decompress(FILE *infile, FILE *outfile, int format) { + // To store error codes + int errcode; + + // Try to read dictionary size + uint16_t dicsize; + errcode = read_word(infile, &dicsize); + if (errcode) return errcode; + + // Dictionary size must be valid! + if (dicsize & 0x0001) + return ERR_CORRUPT; + if ((dicsize & 0x8000) && format == FORMAT_UFTC15) + return ERR_CORRUPT; + if (dicsize < 0x0008) + return ERR_CORRUPT; + + // Allocate memory for dictionary + uint8_t *dictionary = (uint8_t *) malloc(sizeof(uint8_t) * dicsize); + if (dictionary == NULL) + return ERR_NOMEMORY; + + // Read dictionary into memory + if (fread(dictionary, 1, dicsize, infile) < dicsize) { + free(dictionary); + return ferror(infile) ? ERR_CANTREAD : ERR_CORRUPT; + } + + // Determine location of last entry in dictionary + uint16_t limit = dicsize - 0x0008; + + // Read all tiles in the UFTC file + for (;;) { + // Read compressed tile + uint8_t ctile[8]; + size_t numread = fread(ctile, 1, 8, infile); + if (numread == 0 && feof(infile)) + break; + if (numread < 8) + return ferror(infile) ? ERR_CANTREAD : ERR_CORRUPT; + + // Get positions for each 4x4 block in the dictionary + uint16_t pos[4]; + pos[0] = ctile[0] << 8 | ctile[1]; + pos[1] = ctile[2] << 8 | ctile[3]; + pos[2] = ctile[4] << 8 | ctile[5]; + pos[3] = ctile[6] << 8 | ctile[7]; + + // Ensure positions are valid + if (pos[0] & 0x0001 || pos[0] > limit) return ERR_CORRUPT; + if (pos[1] & 0x0001 || pos[1] > limit) return ERR_CORRUPT; + if (pos[2] & 0x0001 || pos[2] > limit) return ERR_CORRUPT; + if (pos[3] & 0x0001 || pos[3] > limit) return ERR_CORRUPT; + + // To store the uncompressed tile + uint8_t tile[0x20]; + uint8_t *ptr = tile; + + // Decompress tile + *ptr++ = dictionary[pos[0] + 0]; *ptr++ = dictionary[pos[0] + 1]; + *ptr++ = dictionary[pos[1] + 0]; *ptr++ = dictionary[pos[1] + 1]; + *ptr++ = dictionary[pos[0] + 2]; *ptr++ = dictionary[pos[0] + 3]; + *ptr++ = dictionary[pos[1] + 2]; *ptr++ = dictionary[pos[1] + 3]; + *ptr++ = dictionary[pos[0] + 4]; *ptr++ = dictionary[pos[0] + 5]; + *ptr++ = dictionary[pos[1] + 4]; *ptr++ = dictionary[pos[1] + 5]; + *ptr++ = dictionary[pos[0] + 6]; *ptr++ = dictionary[pos[0] + 7]; + *ptr++ = dictionary[pos[1] + 6]; *ptr++ = dictionary[pos[1] + 7]; + *ptr++ = dictionary[pos[2] + 0]; *ptr++ = dictionary[pos[2] + 1]; + *ptr++ = dictionary[pos[3] + 0]; *ptr++ = dictionary[pos[3] + 1]; + *ptr++ = dictionary[pos[2] + 2]; *ptr++ = dictionary[pos[2] + 3]; + *ptr++ = dictionary[pos[3] + 2]; *ptr++ = dictionary[pos[3] + 3]; + *ptr++ = dictionary[pos[2] + 4]; *ptr++ = dictionary[pos[2] + 5]; + *ptr++ = dictionary[pos[3] + 4]; *ptr++ = dictionary[pos[3] + 5]; + *ptr++ = dictionary[pos[2] + 6]; *ptr++ = dictionary[pos[2] + 7]; + *ptr++ = dictionary[pos[3] + 6]; *ptr++ = dictionary[pos[3] + 7]; + + // Write decompressed tile to blob + if (fwrite(tile, 1, 0x20, outfile) < 0x20) + return ERR_CANTWRITE; + } + + // Success! + return ERR_NONE; +} diff --git a/tools/uftc/decompress.h b/tools/uftc/decompress.h new file mode 100644 index 00000000..e75876e2 --- /dev/null +++ b/tools/uftc/decompress.h @@ -0,0 +1,36 @@ +//*************************************************************************** +// "decompress.h" +// Header file for "decompress.c" +//*************************************************************************** +// Uftc compression tool +// Copyright 2011, 2012 Javier Degirolmo +// +// This file is part of the uftc tool. +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source distribution. +//*************************************************************************** + +#ifndef DECOMPRESS_H +#define DECOMPRESS_H + +// Required headers +#include + +// Function prototypes +int decompress(FILE *, FILE *, int); + +#endif diff --git a/tools/uftc/main.c b/tools/uftc/main.c new file mode 100644 index 00000000..ec4d2566 --- /dev/null +++ b/tools/uftc/main.c @@ -0,0 +1,279 @@ +//*************************************************************************** +// "main.c" +// Program entry point, parses command line and runs stuff as required +//*************************************************************************** +// Uftc compression tool +// Copyright 2011, 2012 Javier Degirolmo +// +// This file is part of the uftc tool. +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source distribution. +//*************************************************************************** + +// Required headers +#include +#include +#include +#include +#include "main.h" +#include "compress.h" +#include "decompress.h" + +// Possible actions +enum { + ACTION_DEFAULT, // No action specified + ACTION_COMPRESS, // Compress + ACTION_DECOMPRESS, // Decompress + ACTION_TOOMANY // Too many actions specified +}; + +//*************************************************************************** +// Program entry point +//*************************************************************************** + +int main(int argc, char **argv) { + // To know if there was an error or not + int errcode = 0; + + // Scan all arguments + int show_help = 0; + int show_ver = 0; + int action = ACTION_DEFAULT; + int format = FORMAT_DEFAULT; + const char *infilename = NULL; + const char *outfilename = NULL; + + int scan_ok = 1; + int err_manyfiles = 0; + + int curr_arg; + for (curr_arg = 1; curr_arg < argc; curr_arg++) { + // Get pointer to argument, to make our lives easier + const char *arg = argv[curr_arg]; + + // If it's an option, parse it + if (scan_ok && arg[0] == '-') { + // Stop parsing options? + if (!strcmp(arg, "--")) + scan_ok = 0; + + // Show help or version? + else if (!strcmp(arg, "-h") || !strcmp(arg, "--help")) + show_help = 1; + else if (!strcmp(arg, "-v") || !strcmp(arg, "--version")) + show_ver = 1; + + // Compress? + else if (!strcmp(arg, "-c") || !strcmp(arg, "--compress")) + action = action == ACTION_DEFAULT ? + ACTION_COMPRESS : ACTION_TOOMANY; + + // Decompress? + else if (!strcmp(arg, "-d") || !strcmp(arg, "--decompress")) + action = action == ACTION_DEFAULT ? + ACTION_DECOMPRESS : ACTION_TOOMANY; + + // Specify format? + else if (!strcmp(arg, "-16") || !strcmp(arg, "--uftc16")) + format = format == FORMAT_DEFAULT ? + FORMAT_UFTC16 : FORMAT_TOOMANY; + else if (!strcmp(arg, "-15") || !strcmp(arg, "--uftc15")) + format = format == FORMAT_DEFAULT ? + FORMAT_UFTC15 : FORMAT_TOOMANY; + + // Unknown argument + else { + fprintf(stderr, "Error: unknown option \"%s\"\n", arg); + errcode = 1; + } + } + + // Input filename? + else if (infilename == NULL) + infilename = arg; + + // Output filename? + else if (outfilename == NULL) + outfilename = arg; + + // Too many files specified? + else + err_manyfiles = 1; + } + + // Look for error conditions + if (action == ACTION_TOOMANY) { + errcode = 1; + fprintf(stderr, "Error: can't specify more than one action\n"); + } else if (!show_help && !show_ver) { + if (infilename == NULL) { + errcode = 1; + fprintf(stderr, "Error: input filename missing\n"); + } else if (outfilename == NULL) { + errcode = 1; + fprintf(stderr, "Error: output filename missing\n"); + } else if (err_manyfiles) { + errcode = 1; + fprintf(stderr, "Error: too many filenames specified\n"); + } + } + if (format == FORMAT_TOOMANY) { + errcode = 1; + fprintf(stderr, "Error: too many formats specified\n"); + } + + // If there was an error then quit + if (errcode) + return EXIT_FAILURE; + + // No action specified? + if (action == ACTION_DEFAULT) + action = ACTION_COMPRESS; + + // No format specified? + if (format == FORMAT_DEFAULT) + format = FORMAT_UFTC16; + + // Show tool version? + if (show_ver) { + puts("1.2b"); + return EXIT_SUCCESS; + } + + // Show tool usage? + if (show_help) { + printf("Usage:\n" + " %s -c \n" + " %s -d \n" + "\n" + "Options:\n" + " -c or --compress ..... Compress a blob into UFTC\n" + " -d or --decompress ... Decompress UFTC into a blob\n" + " -16 or --uftc16 ...... Use UFTC16 format (8192 limit)\n" + " -15 or --uftc15 ...... Use UFTC15 format (4096 limit)\n" + " -h or --help ......... Show this help\n" + " -v or --version ...... Show tool version\n" + "\n" + "If no option is specified, compression is done by default.\n" + "If no format is specified, UFTC16 is used by default.\n", + argv[0], argv[0]); + return EXIT_SUCCESS; + } + + // Open input file + FILE *infile = fopen(infilename, "rb"); + if (infile == NULL) { + fprintf(stderr, "Error: can't open input file \"%s\"\n", infilename); + return EXIT_FAILURE; + } + + // Open output file + FILE *outfile = fopen(outfilename, "wb"); + if (outfile == NULL) { + fprintf(stderr, "Error: can't open output file \"%s\"\n", outfilename); + fclose(infile); + return EXIT_FAILURE; + } + + // Perform the requested action + switch (action) { + // Compress file + case ACTION_COMPRESS: + errcode = compress(infile, outfile, format); + break; + + // Decompress file + case ACTION_DECOMPRESS: + errcode = decompress(infile, outfile, format); + break; + + // Oops! + default: + errcode = ERR_UNKNOWN; + break; + } + + // If there was an error, show a message + if (errcode) { + // Determine message to show + const char *msg; + switch(errcode) { + case ERR_CANTREAD: msg = "can't read from input file"; break; + case ERR_CANTWRITE: msg = "can't write to output file"; break; + case ERR_BADSIZE: msg = "input file size isn't a multiple of 32 " + "bytes"; break; + case ERR_TOOSMALL: msg = "input file needs to have at least one " + "tile"; break; + case ERR_TOOBIG: msg = "output file is too big for UFTC"; break; + case ERR_CORRUPT: msg = "input file isn't valid UFTC"; break; + case ERR_NOMEMORY: msg = "ran out of memory"; break; + default: msg = "unknown error"; break; + } + + // Show message on screen + fprintf(stderr, "Error: %s\n", msg); + } + + // Quit program + fclose(outfile); + fclose(infile); + if (errcode) remove(outfilename); + return errcode ? EXIT_FAILURE : EXIT_SUCCESS; +} + +//*************************************************************************** +// read_word +// Reads a 16-bit value from a file +//--------------------------------------------------------------------------- +// param infile: input file +// param buffer: where to store value +// return: error code +//*************************************************************************** + +int read_word(FILE *infile, uint16_t *buffer) { + // Try to read from input file + uint8_t temp[2]; + if (fread(temp, 1, 2, infile) < 2) + return ferror(infile) ? ERR_CANTREAD : ERR_CORRUPT; + + // Parse value + *buffer = temp[0] << 8 | temp[1]; + + // Success! + return ERR_NONE; +} + +//*************************************************************************** +// write_word +// Writes a 16-bit value into a file +//--------------------------------------------------------------------------- +// param outfile: output file +// param value: value to be written +// return: error code +//*************************************************************************** + +int write_word(FILE *outfile, const uint16_t value) { + // Split value into bytes + uint8_t temp[2] = { value >> 8, value & 0xFF }; + + // Try to write into file + if (fwrite(temp, 1, 2, outfile) < 2) + return ERR_CANTWRITE; + + // Success! + return ERR_NONE; +} diff --git a/tools/uftc/main.h b/tools/uftc/main.h new file mode 100644 index 00000000..fdb6b77d --- /dev/null +++ b/tools/uftc/main.h @@ -0,0 +1,58 @@ +//*************************************************************************** +// "main.h" +// Some common definitions and such +//*************************************************************************** +// Uftc compression tool +// Copyright 2011, 2012 Javier Degirolmo +// +// This file is part of the uftc tool. +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source distribution. +//*************************************************************************** + +#ifndef MAIN_H +#define MAIN_H + +// Required headers +#include + +// Error codes +enum { + ERR_NONE, // No error + ERR_CANTREAD, // Can't read from input file + ERR_CANTWRITE, // Can't write into output file + ERR_BADSIZE, // Input file isn't suitable + ERR_TOOSMALL, // Input file must contain at least one tile + ERR_TOOBIG, // Dictionary has become too big + ERR_CORRUPT, // File is corrupt? + ERR_NOMEMORY, // Ran out of memory + ERR_UNKNOWN // Unknown error +}; + +// Possible formats +enum { + FORMAT_DEFAULT, // No format specified + FORMAT_UFTC15, // UFTC15 (15-bit offsets) + FORMAT_UFTC16, // UFTC16 (16-bit offsets) + FORMAT_TOOMANY // Too many formats specified +}; + +// Function prototypes +int read_word(FILE *, uint16_t *); +int write_word(FILE *, const uint16_t); + +#endif