From 809e2ff3775f4d51e52885d16e1223d95a343ba6 Mon Sep 17 00:00:00 2001 From: Dwscdv3 Date: Fri, 27 Nov 2020 20:59:39 +0800 Subject: [PATCH] Initial release --- .build.cmd | 4 + .gitignore | 3 + CodingStyles.txt | 6 + LICENSE | 674 ++++++++++++++++++++++++++++ README.md | 100 +++++ lib/hardware.inc | 843 ++++++++++++++++++++++++++++++++++++ res/tileset.bin | Bin 0 -> 1024 bytes res/tileset.gbr | Bin 0 -> 10069 bytes res/tileset.ver1.gbr | Bin 0 -> 10069 bytes src/alloctable.inc | 65 +++ src/constants.inc | 36 ++ src/data.asm | 18 + src/dwlib80/animation.asm | 71 +++ src/dwlib80/controlflow.asm | 43 ++ src/dwlib80/convert.asm | 59 +++ src/dwlib80/input.asm | 51 +++ src/dwlib80/math.asm | 143 ++++++ src/dwlib80/memory.asm | 104 +++++ src/dwlib80/oam.asm | 147 +++++++ src/dwlib80/pathfinding.asm | 234 ++++++++++ src/dwlib80/rand.asm | 127 ++++++ src/dwlib80/tmp.asm | 15 + src/header.asm | 43 ++ src/includes.inc | 19 + src/macro.asm | 265 ++++++++++++ src/main.asm | 633 +++++++++++++++++++++++++++ src/test.asm | 111 +++++ 27 files changed, 3814 insertions(+) create mode 100644 .build.cmd create mode 100644 .gitignore create mode 100644 CodingStyles.txt create mode 100644 LICENSE create mode 100644 README.md create mode 100644 lib/hardware.inc create mode 100644 res/tileset.bin create mode 100644 res/tileset.gbr create mode 100644 res/tileset.ver1.gbr create mode 100644 src/alloctable.inc create mode 100644 src/constants.inc create mode 100644 src/data.asm create mode 100644 src/dwlib80/animation.asm create mode 100644 src/dwlib80/controlflow.asm create mode 100644 src/dwlib80/convert.asm create mode 100644 src/dwlib80/input.asm create mode 100644 src/dwlib80/math.asm create mode 100644 src/dwlib80/memory.asm create mode 100644 src/dwlib80/oam.asm create mode 100644 src/dwlib80/pathfinding.asm create mode 100644 src/dwlib80/rand.asm create mode 100644 src/dwlib80/tmp.asm create mode 100644 src/header.asm create mode 100644 src/includes.inc create mode 100644 src/macro.asm create mode 100644 src/main.asm create mode 100644 src/test.asm diff --git a/.build.cmd b/.build.cmd new file mode 100644 index 0000000..1fca7c9 --- /dev/null +++ b/.build.cmd @@ -0,0 +1,4 @@ +@echo off +rgbasm --export-all --halt-without-nop --include src --include src/dwlib80 --output obj/main.o src/main.asm +rgblink -d -m bin/gblinez.map -n bin/gblinez.sym -o bin/gblinez.gb obj/main.o +rgbfix -v -p 0 -j -k DW -t gblinez bin/gblinez.gb diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5137440 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.vscode +bin +obj \ No newline at end of file diff --git a/CodingStyles.txt b/CodingStyles.txt new file mode 100644 index 0000000..3363caf --- /dev/null +++ b/CodingStyles.txt @@ -0,0 +1,6 @@ +- Variables and local labels are camelCase +- Routines and (simulated) namespaces are PascalCase +- _ is used as namespace delimiter +- Private members start with an _ + +- Uses intuitive but less optimized code outside critical loops diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..f288702 --- /dev/null +++ b/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/README.md b/README.md new file mode 100644 index 0000000..60ddd49 --- /dev/null +++ b/README.md @@ -0,0 +1,100 @@ +# gblinez + +Yet another Color Lines game. For Game Boy. + +This replica features the same gameplay as the original version, with smooth animation. + +## How to Play + +There is plenty of resources over the Internet, just [Google the original title](https://www.google.com/search?q=color+lines+gameplay). + +## Key Bindings + +| Key | Function | +|-------|--------------------------------------------------| +| D-Pad | Navigate the cursor | +| A | Select, or move the selected stone to the cursor | +| B | Hold for auto-repeating D-Pad (rapid mode) | +| Start | Restart the game | + +## What's Next? + +This would be the final release if nobody is interested in this project. I have currently no plan for implementing more features such as sound effects or high scores. + +## For Fellow Developers + +You may find a bunch of useful libraries in the `src/dwlib80/` directory. + +Many of the functions are properly documented, you can use the VS Code extension [RGBDS Z80](https://marketplace.visualstudio.com/items?itemName=donaldhays.rgbds-z80) for hovering documents. + +Members start with a `_` is considered private, they are not meant to be used by end users. + +### memory.asm + +A must have for every program beyond "Hello, world!". + +* `memset` +* `memcpy` +* `reverse` +* macro `read` - A one-liner for reading memory into any register. +* macro `write` - A one-liner for writing any register into memory. +* macro `readh` - Same as above, but optimized with `ldh` instruction. +* macro `writeh` - Same as above, but optimized with `ldh` instruction. + +### math.asm + +Nothing here. No trigonometric functions, floating-point arithmetics, nor even integer multiplying. Don't expect this as something like `math.h` in C. + +* `Divide` - 8-bit general division algorithm. +* `ToDecimal8` - 8-bit BCD. +* macro `AddR16D8` - A handy macro for adding 8-bit immediate values to any register pair (using carry flag, not HL register). + +### rand.asm + +For best performance, random numbers are not generated by RNG algorithms but rely on a 256 bytes long circular table and entropy provided by user input. To make the RNG unbiased another bias check table is used. + +You should call macro `SeedRandom` in your joypad interrupt handler to collect entropy. + +### animation.asm + +Linear animation, with arguments of delta X, delta Y, and duration in frames. + +The current implementation will block the execution until the animation is ended. + +### pathfinding.asm + +A standard (?) BFS algorithm. + +* 512 bytes used for buffer. +* Requirements for a compatible gameboard: + * Should be a `byte[]` array. + * Less than 256 bytes. + * Value 0 is passable, otherwise obstacles. + +After a successful search, `Pathfinding_backtrace` is filled with a unidirectional linked list, from search target to search origin. If you want the reverse path, simply exchange the origin argument with the target argument. + +### controlflow.asm + +An elegant way to write counter loops. + +Search `BeginLoop` in this repository for example. + +### oam.asm + +All the OAM and DMA stuff goes here. + +Sprites related functions are organized to 8x8, 8x16, 8x8x4, 8x16x2. Currently, only 8x16x2 functions are implemented. + +* `OAMBuffer` - Don't allocate it twice, just use this for your game. +* `ClearOAMBuffer` +* `SetSprite` - Give a sprite a tile. +* `SetSpritePos` +* `HideSprite` - Technically just set its Y pos to 0. +* `InitDMA` - Copy DMA waiting routine to HRAM. +* `StartDMA` + +### input.asm + +An input handler, with keypress support (only trigger once until pressed that key again). + +Call `Input` from your game loop, then read `JoypadState` or `PressedKeys`. diff --git a/lib/hardware.inc b/lib/hardware.inc new file mode 100644 index 0000000..cd9f79a --- /dev/null +++ b/lib/hardware.inc @@ -0,0 +1,843 @@ +;* +;* Gameboy Hardware definitions +;* +;* Based on Jones' hardware.inc +;* And based on Carsten Sorensen's ideas. +;* +;* Rev 1.1 - 15-Jul-97 : Added define check +;* Rev 1.2 - 18-Jul-97 : Added revision check macro +;* Rev 1.3 - 19-Jul-97 : Modified for RGBASM V1.05 +;* Rev 1.4 - 27-Jul-97 : Modified for new subroutine prefixes +;* Rev 1.5 - 15-Aug-97 : Added _HRAM, PAD, CART defines +;* : and Nintendo Logo +;* Rev 1.6 - 30-Nov-97 : Added rDIV, rTIMA, rTMA, & rTAC +;* Rev 1.7 - 31-Jan-98 : Added _SCRN0, _SCRN1 +;* Rev 1.8 - 15-Feb-98 : Added rSB, rSC +;* Rev 1.9 - 16-Feb-98 : Converted I/O registers to $FFXX format +;* Rev 2.0 - : Added GBC registers +;* Rev 2.1 - : Added MBC5 & cart RAM enable/disable defines +;* Rev 2.2 - : Fixed NR42,NR43, & NR44 equates +;* Rev 2.3 - : Fixed incorrect _HRAM equate +;* Rev 2.4 - 27-Apr-13 : Added some cart defines (AntonioND) +;* Rev 2.5 - 03-May-15 : Fixed format (AntonioND) +;* Rev 2.6 - 09-Apr-16 : Added GBC OAM and cart defines (AntonioND) +;* Rev 2.7 - 19-Jan-19 : Added rPCMXX (ISSOtm) +;* Rev 2.8 - 03-Feb-19 : Added audio registers flags (Álvaro Cuesta) +;* Rev 2.9 - 28-Feb-20 : Added utility rP1 constants + +; If all of these are already defined, don't do it again. + + IF !DEF(HARDWARE_INC) +HARDWARE_INC SET 1 + +rev_Check_hardware_inc : MACRO +;NOTE: REVISION NUMBER CHANGES MUST BE ADDED +;TO SECOND PARAMETER IN FOLLOWING LINE. + IF \1 > 2.9 ;PUT REVISION NUMBER HERE + WARN "Version \1 or later of 'hardware.inc' is required." + ENDC +ENDM + +_HW EQU $FF00 + +_VRAM EQU $8000 ; $8000->$9FFF +_SCRN0 EQU $9800 ; $9800->$9BFF +_SCRN1 EQU $9C00 ; $9C00->$9FFF +_SRAM EQU $A000 ; $A000->$BFFF +_RAM EQU $C000 ; $C000->$DFFF +_OAMRAM EQU $FE00 ; $FE00->$FE9F +_AUD3WAVERAM EQU $FF30 ; $FF30->$FF3F +_HRAM EQU $FF80 ; $FF80->$FFFE + +; *** MBC5 Equates *** + +rRAMG EQU $0000 ; $0000->$1fff +rROMB0 EQU $2000 ; $2000->$2fff +rROMB1 EQU $3000 ; $3000->$3fff - If more than 256 ROM banks are present. +rRAMB EQU $4000 ; $4000->$5fff - Bit 3 enables rumble (if present) + + +; -- +; -- OAM flags +; -- + +OAMF_PRI EQU %10000000 ; Priority +OAMF_YFLIP EQU %01000000 ; Y flip +OAMF_XFLIP EQU %00100000 ; X flip +OAMF_PAL0 EQU %00000000 ; Palette number; 0,1 (DMG) +OAMF_PAL1 EQU %00010000 ; Palette number; 0,1 (DMG) +OAMF_BANK0 EQU %00000000 ; Bank number; 0,1 (GBC) +OAMF_BANK1 EQU %00001000 ; Bank number; 0,1 (GBC) + +OAMF_PALMASK EQU %00000111 ; Palette (GBC) + +OAMB_PRI EQU 7 ; Priority +OAMB_YFLIP EQU 6 ; Y flip +OAMB_XFLIP EQU 5 ; X flip +OAMB_PAL1 EQU 4 ; Palette number; 0,1 (DMG) +OAMB_BANK1 EQU 3 ; Bank number; 0,1 (GBC) + + +;*************************************************************************** +;* +;* Custom registers +;* +;*************************************************************************** + +; -- +; -- P1 ($FF00) +; -- Register for reading joy pad info. (R/W) +; -- +rP1 EQU $FF00 + +P1F_5 EQU %00100000 ; P15 out port, set to 0 to get buttons +P1F_4 EQU %00010000 ; P14 out port, set to 0 to get dpad +P1F_3 EQU %00001000 ; P13 in port +P1F_2 EQU %00000100 ; P12 in port +P1F_1 EQU %00000010 ; P11 in port +P1F_0 EQU %00000001 ; P10 in port + +P1F_GET_DPAD EQU P1F_5 +P1F_GET_BTN EQU P1F_4 +P1F_GET_NONE EQU P1F_4 | P1F_5 + +; -- +; -- SB ($FF01) +; -- Serial Transfer Data (R/W) +; -- +rSB EQU $FF01 + +; -- +; -- SC ($FF02) +; -- Serial I/O Control (R/W) +; -- +rSC EQU $FF02 + +; -- +; -- DIV ($FF04) +; -- Divider register (R/W) +; -- +rDIV EQU $FF04 + + +; -- +; -- TIMA ($FF05) +; -- Timer counter (R/W) +; -- +rTIMA EQU $FF05 + + +; -- +; -- TMA ($FF06) +; -- Timer modulo (R/W) +; -- +rTMA EQU $FF06 + + +; -- +; -- TAC ($FF07) +; -- Timer control (R/W) +; -- +rTAC EQU $FF07 + +TACF_START EQU %00000100 +TACF_STOP EQU %00000000 +TACF_4KHZ EQU %00000000 +TACF_16KHZ EQU %00000011 +TACF_65KHZ EQU %00000010 +TACF_262KHZ EQU %00000001 + +; -- +; -- IF ($FF0F) +; -- Interrupt Flag (R/W) +; -- +rIF EQU $FF0F + +; -- +; -- LCDC ($FF40) +; -- LCD Control (R/W) +; -- +rLCDC EQU $FF40 + +LCDCF_OFF EQU %00000000 ; LCD Control Operation +LCDCF_ON EQU %10000000 ; LCD Control Operation +LCDCF_WIN9800 EQU %00000000 ; Window Tile Map Display Select +LCDCF_WIN9C00 EQU %01000000 ; Window Tile Map Display Select +LCDCF_WINOFF EQU %00000000 ; Window Display +LCDCF_WINON EQU %00100000 ; Window Display +LCDCF_BG8800 EQU %00000000 ; BG & Window Tile Data Select +LCDCF_BG8000 EQU %00010000 ; BG & Window Tile Data Select +LCDCF_BG9800 EQU %00000000 ; BG Tile Map Display Select +LCDCF_BG9C00 EQU %00001000 ; BG Tile Map Display Select +LCDCF_OBJ8 EQU %00000000 ; OBJ Construction +LCDCF_OBJ16 EQU %00000100 ; OBJ Construction +LCDCF_OBJOFF EQU %00000000 ; OBJ Display +LCDCF_OBJON EQU %00000010 ; OBJ Display +LCDCF_BGOFF EQU %00000000 ; BG Display +LCDCF_BGON EQU %00000001 ; BG Display +; "Window Character Data Select" follows BG + + +; -- +; -- STAT ($FF41) +; -- LCDC Status (R/W) +; -- +rSTAT EQU $FF41 + +STATF_LYC EQU %01000000 ; LYCEQULY Coincidence (Selectable) +STATF_MODE10 EQU %00100000 ; Mode 10 +STATF_MODE01 EQU %00010000 ; Mode 01 (V-Blank) +STATF_MODE00 EQU %00001000 ; Mode 00 (H-Blank) +STATF_LYCF EQU %00000100 ; Coincidence Flag +STATF_HB EQU %00000000 ; H-Blank +STATF_VB EQU %00000001 ; V-Blank +STATF_OAM EQU %00000010 ; OAM-RAM is used by system +STATF_LCD EQU %00000011 ; Both OAM and VRAM used by system +STATF_BUSY EQU %00000010 ; When set, VRAM access is unsafe + + +; -- +; -- SCY ($FF42) +; -- Scroll Y (R/W) +; -- +rSCY EQU $FF42 + + +; -- +; -- SCY ($FF43) +; -- Scroll X (R/W) +; -- +rSCX EQU $FF43 + + +; -- +; -- LY ($FF44) +; -- LCDC Y-Coordinate (R) +; -- +; -- Values range from 0->153. 144->153 is the VBlank period. +; -- +rLY EQU $FF44 + + +; -- +; -- LYC ($FF45) +; -- LY Compare (R/W) +; -- +; -- When LYEQUEQULYC, STATF_LYCF will be set in STAT +; -- +rLYC EQU $FF45 + + +; -- +; -- DMA ($FF46) +; -- DMA Transfer and Start Address (W) +; -- +rDMA EQU $FF46 + + +; -- +; -- BGP ($FF47) +; -- BG Palette Data (W) +; -- +; -- Bit 7-6 - Intensity for %11 +; -- Bit 5-4 - Intensity for %10 +; -- Bit 3-2 - Intensity for %01 +; -- Bit 1-0 - Intensity for %00 +; -- +rBGP EQU $FF47 + + +; -- +; -- OBP0 ($FF48) +; -- Object Palette 0 Data (W) +; -- +; -- See BGP for info +; -- +rOBP0 EQU $FF48 + + +; -- +; -- OBP1 ($FF49) +; -- Object Palette 1 Data (W) +; -- +; -- See BGP for info +; -- +rOBP1 EQU $FF49 + + +; -- +; -- WY ($FF4A) +; -- Window Y Position (R/W) +; -- +; -- 0 SO2 ON/OFF (Vin??) +; -- Bit 6-4 - SO2 output level (volume) (# 0-7) +; -- Bit 3 - Vin->SO1 ON/OFF (Vin??) +; -- Bit 2-0 - SO1 output level (volume) (# 0-7) +; -- +rNR50 EQU $FF24 +rAUDVOL EQU rNR50 + +AUDVOL_VIN_LEFT EQU %10000000 ; SO2 +AUDVOL_VIN_RIGHT EQU %00001000 ; SO1 + + +; -- +; -- AUDTERM/NR51 ($FF25) +; -- Selection of Sound output terminal (R/W) +; -- +; -- Bit 7 - Output sound 4 to SO2 terminal +; -- Bit 6 - Output sound 3 to SO2 terminal +; -- Bit 5 - Output sound 2 to SO2 terminal +; -- Bit 4 - Output sound 1 to SO2 terminal +; -- Bit 3 - Output sound 4 to SO1 terminal +; -- Bit 2 - Output sound 3 to SO1 terminal +; -- Bit 1 - Output sound 2 to SO1 terminal +; -- Bit 0 - Output sound 0 to SO1 terminal +; -- +rNR51 EQU $FF25 +rAUDTERM EQU rNR51 + +; SO2 +AUDTERM_4_LEFT EQU %10000000 +AUDTERM_3_LEFT EQU %01000000 +AUDTERM_2_LEFT EQU %00100000 +AUDTERM_1_LEFT EQU %00010000 +; SO1 +AUDTERM_4_RIGHT EQU %00001000 +AUDTERM_3_RIGHT EQU %00000100 +AUDTERM_2_RIGHT EQU %00000010 +AUDTERM_1_RIGHT EQU %00000001 + + +; -- +; -- AUDENA/NR52 ($FF26) +; -- Sound on/off (R/W) +; -- +; -- Bit 7 - All sound on/off (sets all audio regs to 0!) +; -- Bit 3 - Sound 4 ON flag (doesn't work!) +; -- Bit 2 - Sound 3 ON flag (doesn't work!) +; -- Bit 1 - Sound 2 ON flag (doesn't work!) +; -- Bit 0 - Sound 1 ON flag (doesn't work!) +; -- +rNR52 EQU $FF26 +rAUDENA EQU rNR52 + +AUDENA_ON EQU %10000000 +AUDENA_OFF EQU %00000000 ; sets all audio regs to 0! + + +;*************************************************************************** +;* +;* SoundChannel #1 registers +;* +;*************************************************************************** + +; -- +; -- AUD1SWEEP/NR10 ($FF10) +; -- Sweep register (R/W) +; -- +; -- Bit 6-4 - Sweep Time +; -- Bit 3 - Sweep Increase/Decrease +; -- 0: Addition (frequency increases???) +; -- 1: Subtraction (frequency increases???) +; -- Bit 2-0 - Number of sweep shift (# 0-7) +; -- Sweep Time: (n*7.8ms) +; -- +rNR10 EQU $FF10 +rAUD1SWEEP EQU rNR10 + +AUD1SWEEP_UP EQU %00000000 +AUD1SWEEP_DOWN EQU %00001000 + + +; -- +; -- AUD1LEN/NR11 ($FF11) +; -- Sound length/Wave pattern duty (R/W) +; -- +; -- Bit 7-6 - Wave Pattern Duty (00:12.5% 01:25% 10:50% 11:75%) +; -- Bit 5-0 - Sound length data (# 0-63) +; -- +rNR11 EQU $FF11 +rAUD1LEN EQU rNR11 + + +; -- +; -- AUD1ENV/NR12 ($FF12) +; -- Envelope (R/W) +; -- +; -- Bit 7-4 - Initial value of envelope +; -- Bit 3 - Envelope UP/DOWN +; -- 0: Decrease +; -- 1: Range of increase +; -- Bit 2-0 - Number of envelope sweep (# 0-7) +; -- +rNR12 EQU $FF12 +rAUD1ENV EQU rNR12 + + +; -- +; -- AUD1LOW/NR13 ($FF13) +; -- Frequency lo (W) +; -- +rNR13 EQU $FF13 +rAUD1LOW EQU rNR13 + + +; -- +; -- AUD1HIGH/NR14 ($FF14) +; -- Frequency hi (W) +; -- +; -- Bit 7 - Initial (when set, sound restarts) +; -- Bit 6 - Counter/consecutive selection +; -- Bit 2-0 - Frequency's higher 3 bits +; -- +rNR14 EQU $FF14 +rAUD1HIGH EQU rNR14 + + +;*************************************************************************** +;* +;* SoundChannel #2 registers +;* +;*************************************************************************** + +; -- +; -- AUD2LEN/NR21 ($FF16) +; -- Sound Length; Wave Pattern Duty (R/W) +; -- +; -- see AUD1LEN for info +; -- +rNR21 EQU $FF16 +rAUD2LEN EQU rNR21 + + +; -- +; -- AUD2ENV/NR22 ($FF17) +; -- Envelope (R/W) +; -- +; -- see AUD1ENV for info +; -- +rNR22 EQU $FF17 +rAUD2ENV EQU rNR22 + + +; -- +; -- AUD2LOW/NR23 ($FF18) +; -- Frequency lo (W) +; -- +rNR23 EQU $FF18 +rAUD2LOW EQU rNR23 + + +; -- +; -- AUD2HIGH/NR24 ($FF19) +; -- Frequency hi (W) +; -- +; -- see AUD1HIGH for info +; -- +rNR24 EQU $FF19 +rAUD2HIGH EQU rNR24 + + +;*************************************************************************** +;* +;* SoundChannel #3 registers +;* +;*************************************************************************** + +; -- +; -- AUD3ENA/NR30 ($FF1A) +; -- Sound on/off (R/W) +; -- +; -- Bit 7 - Sound ON/OFF (1EQUON,0EQUOFF) +; -- +rNR30 EQU $FF1A +rAUD3ENA EQU rNR30 + + +; -- +; -- AUD3LEN/NR31 ($FF1B) +; -- Sound length (R/W) +; -- +; -- Bit 7-0 - Sound length +; -- +rNR31 EQU $FF1B +rAUD3LEN EQU rNR31 + + +; -- +; -- AUD3LEVEL/NR32 ($FF1C) +; -- Select output level +; -- +; -- Bit 6-5 - Select output level +; -- 00: 0/1 (mute) +; -- 01: 1/1 +; -- 10: 1/2 +; -- 11: 1/4 +; -- +rNR32 EQU $FF1C +rAUD3LEVEL EQU rNR32 + + +; -- +; -- AUD3LOW/NR33 ($FF1D) +; -- Frequency lo (W) +; -- +; -- see AUD1LOW for info +; -- +rNR33 EQU $FF1D +rAUD3LOW EQU rNR33 + + +; -- +; -- AUD3HIGH/NR34 ($FF1E) +; -- Frequency hi (W) +; -- +; -- see AUD1HIGH for info +; -- +rNR34 EQU $FF1E +rAUD3HIGH EQU rNR34 + + +; -- +; -- AUD4LEN/NR41 ($FF20) +; -- Sound length (R/W) +; -- +; -- Bit 5-0 - Sound length data (# 0-63) +; -- +rNR41 EQU $FF20 +rAUD4LEN EQU rNR41 + + +; -- +; -- AUD4ENV/NR42 ($FF21) +; -- Envelope (R/W) +; -- +; -- see AUD1ENV for info +; -- +rNR42 EQU $FF21 +rAUD4ENV EQU rNR42 + + +; -- +; -- AUD4POLY/NR43 ($FF22) +; -- Polynomial counter (R/W) +; -- +; -- Bit 7-4 - Selection of the shift clock frequency of the (scf) +; -- polynomial counter (0000-1101) +; -- freqEQUdrf*1/2^scf (not sure) +; -- Bit 3 - Selection of the polynomial counter's step +; -- 0: 15 steps +; -- 1: 7 steps +; -- Bit 2-0 - Selection of the dividing ratio of frequencies (drf) +; -- 000: f/4 001: f/8 010: f/16 011: f/24 +; -- 100: f/32 101: f/40 110: f/48 111: f/56 (fEQU4.194304 Mhz) +; -- +rNR43 EQU $FF22 +rAUD4POLY EQU rNR43 + + +; -- +; -- AUD4GO/NR44 ($FF23) +; -- (has wrong name and value (ff30) in Dr.Pan's doc!) +; -- +; -- Bit 7 - Inital +; -- Bit 6 - Counter/consecutive selection +; -- +rNR44 EQU $FF23 +rAUD4GO EQU rNR44 ; silly name! + + +; -- +; -- PCM12 ($FF76) +; -- Sound channel 1&2 PCM amplitude (R) +; -- +; -- Bit 7-4 - Copy of sound channel 2's PCM amplitude +; -- Bit 3-0 - Copy of sound channel 1's PCM amplitude +; -- +rPCM12 EQU $FF76 + + +; -- +; -- PCM34 ($FF77) +; -- Sound channel 3&4 PCM amplitude (R) +; -- +; -- Bit 7-4 - Copy of sound channel 4's PCM amplitude +; -- Bit 3-0 - Copy of sound channel 3's PCM amplitude +; -- +rPCM34 EQU $FF77 + + +;*************************************************************************** +;* +;* Flags common to multiple sound channels +;* +;*************************************************************************** + +; -- +; -- Square wave duty cycle +; -- +; -- Can be used with AUD1LEN and AUD2LEN +; -- See AUD1LEN for more info +; -- +AUDLEN_DUTY_12_5 EQU %00000000 ; 12.5% +AUDLEN_DUTY_25 EQU %01000000 ; 25% +AUDLEN_DUTY_50 EQU %10000000 ; 50% +AUDLEN_DUTY_75 EQU %11000000 ; 75% + + +; -- +; -- Audio envelope flags +; -- +; -- Can be used with AUD1ENV, AUD2ENV, AUD4ENV +; -- See AUD1ENV for more info +; -- +AUDENV_UP EQU %00001000 +AUDENV_DOWN EQU %00000000 + + +; -- +; -- Audio trigger flags +; -- +; -- Can be used with AUD1HIGH, AUD2HIGH, AUD3HIGH +; -- See AUD1HIGH for more info +; -- + +AUDHIGH_RESTART EQU %10000000 +AUDHIGH_LENGTH_ON EQU %01000000 +AUDHIGH_LENGTH_OFF EQU %00000000 + + +;*************************************************************************** +;* +;* Cart related +;* +;*************************************************************************** + +CART_COMPATIBLE_DMG EQU $00 +CART_COMPATIBLE_DMG_GBC EQU $80 +CART_COMPATIBLE_GBC EQU $C0 + +CART_ROM EQU $00 +CART_ROM_MBC1 EQU $01 +CART_ROM_MBC1_RAM EQU $02 +CART_ROM_MBC1_RAM_BAT EQU $03 +CART_ROM_MBC2 EQU $05 +CART_ROM_MBC2_BAT EQU $06 +CART_ROM_RAM EQU $08 +CART_ROM_RAM_BAT EQU $09 +CART_ROM_MBC3_BAT_RTC EQU $0F +CART_ROM_MBC3_RAM_BAT_RTC EQU $10 +CART_ROM_MBC3 EQU $11 +CART_ROM_MBC3_RAM EQU $12 +CART_ROM_MBC3_RAM_BAT EQU $13 +CART_ROM_MBC5 EQU $19 +CART_ROM_MBC5_BAT EQU $1A +CART_ROM_MBC5_RAM_BAT EQU $1B +CART_ROM_MBC5_RUMBLE EQU $1C +CART_ROM_MBC5_RAM_RUMBLE EQU $1D +CART_ROM_MBC5_RAM_BAT_RUMBLE EQU $1E +CART_ROM_MBC7_RAM_BAT_GYRO EQU $22 +CART_ROM_POCKET_CAMERA EQU $FC + +CART_ROM_256K EQU 0 ; 2 banks +CART_ROM_512K EQU 1 ; 4 banks +CART_ROM_1M EQU 2 ; 8 banks +CART_ROM_2M EQU 3 ; 16 banks +CART_ROM_4M EQU 4 ; 32 banks +CART_ROM_8M EQU 5 ; 64 banks +CART_ROM_16M EQU 6 ; 128 banks +CART_ROM_32M EQU 7 ; 256 banks +CART_ROM_64M EQU 8 ; 512 banks + +CART_RAM_NONE EQU 0 +CART_RAM_16K EQU 1 ; 1 incomplete bank +CART_RAM_64K EQU 2 ; 1 bank +CART_RAM_256K EQU 3 ; 4 banks +CART_RAM_1M EQU 4 ; 16 banks + +CART_RAM_ENABLE EQU $0A +CART_RAM_DISABLE EQU $00 + +;*************************************************************************** +;* +;* Keypad related +;* +;*************************************************************************** + +PADF_DOWN EQU $80 +PADF_UP EQU $40 +PADF_LEFT EQU $20 +PADF_RIGHT EQU $10 +PADF_START EQU $08 +PADF_SELECT EQU $04 +PADF_B EQU $02 +PADF_A EQU $01 + +PADB_DOWN EQU $7 +PADB_UP EQU $6 +PADB_LEFT EQU $5 +PADB_RIGHT EQU $4 +PADB_START EQU $3 +PADB_SELECT EQU $2 +PADB_B EQU $1 +PADB_A EQU $0 + +;*************************************************************************** +;* +;* Screen related +;* +;*************************************************************************** + +SCRN_X EQU 160 ; Width of screen in pixels +SCRN_Y EQU 144 ; Height of screen in pixels +SCRN_X_B EQU 20 ; Width of screen in bytes +SCRN_Y_B EQU 18 ; Height of screen in bytes + +SCRN_VX EQU 256 ; Virtual width of screen in pixels +SCRN_VY EQU 256 ; Virtual height of screen in pixels +SCRN_VX_B EQU 32 ; Virtual width of screen in bytes +SCRN_VY_B EQU 32 ; Virtual height of screen in bytes + +;* +;* Nintendo scrolling logo +;* (Code won't work on a real GameBoy) +;* (if next lines are altered.) +NINTENDO_LOGO : MACRO + DB $CE,$ED,$66,$66,$CC,$0D,$00,$0B,$03,$73,$00,$83,$00,$0C,$00,$0D + DB $00,$08,$11,$1F,$88,$89,$00,$0E,$DC,$CC,$6E,$E6,$DD,$DD,$D9,$99 + DB $BB,$BB,$67,$63,$6E,$0E,$EC,$CC,$DD,$DC,$99,$9F,$BB,$B9,$33,$3E +ENDM + + ENDC ;HARDWARE_INC \ No newline at end of file diff --git a/res/tileset.bin b/res/tileset.bin new file mode 100644 index 0000000000000000000000000000000000000000..8cbd1d5e496af81680cf886a5a1726fac0aa6c63 GIT binary patch literal 1024 zcmb_ZF=!J}7=D*~O|S7xE{)1HM(-NiB$W`a(jkU5*{2ttU|M&gh_ujh) zzO^Z2a8qK702~d0(LW z;|>YP0hzzUP0?{BF2;p>k~DOaj}?KZJhZ3ODYc~jPu>x~9O zvyk<9ukkaY5_EMQHR=g*74KNvuXe#eDF|uUtk5Kp8Pj zIz*WC%zJO7c)Km5f;=b;PVD6Ua11k%VFClfAa*P24~;DN^m(0GxEdOZT~CE?GW0PK zTCbOOR}zO+^L4CMG3{TLas2#r{SYAlYwA2qEp~I8jU_vRTvQ zFaR)3P21Xn7*r>I!sr+z&ooII$s?e7L~(VxQdQUQ{u6iB(7z+!EVdRqWE5NZriMtp ze30ueAJ95mGa7^`wv0x$rlDu(+{cD5Z2QDymnc4?^C%JnOIMx(Nq>la%Z W%GSy|`R1$N`DS^C_}z2-ulWxx2X9eu6|R zq15p_=j*-i%-S(FOK|U)x#u}w&pGGbnU&eSa`F1`Q3rhlm! zE!|mdHlWvac5pUbNT0qOO;^`en)Cgwt*ybWg;BGzFj#FiPd&c7zH)#0b{61)Q};Sm z=|q2;+mXaas_Nb}&ponNoKv>Oac24xVujW6s$A8kp@j~n0Hj=3c+jH)tm~d*n(H1Q z?HIS1iK$TxXEi$p60;l+nV6nQnIDlveAUw;Mili(hXXt3kuz?~dAeKy*iPB(gkgSIhAhNzM}54rcA2 z@oh9l&O;0ZVWtpk>!HpOq5_IF52P^}3IM}s23??J^A{+CQiYEX#F5b(6opTNr9qKH zC_2-i%M6Rd&?P}J2Pqf1_07NwAq5eezheo(q3ndKt{urUf1aP+Cp4$&gU{K_UoJZX z@(VCSyW_5M`{nfYeE>Q%jaGcbI4Z_Cq!Ef1QsKplpBTl3agqw{sYDH8xkOY5Hsmmc z&sk}ei1k_jyhM@|gNPI}2q;y(RlrqJg$Z%aCh%B<#|1!r1GaaLm)Em0x7^RRKRwc{ zSfpx$5s9Tq5gYZ$I|3MX;}^lpKvK;SnL^O^c6`mIkGb-W<3%f$N%M%3Oq#{Zhqc#1 z1wVkSEQINMnLB&u@$$34?u3T(6x{*4)b1A;9@@T6h_%q}1TI6N4mb{Z@8rjy%bmtK zS2d~>P=9Ec%|mE)nfo?4H^ZR|XGdcRrt^0KSRaTaIRXnbRgIGTL|o1<4VW@nT{8o$>e zDZ4L56ZteO;w_EE!02gXr{K>v%oPHw{P{2o7%UucqnYQB>`SK!`)^h}F}{OoKJDY=(}DEi>-+TV)p|O8_eMH!yq`XNKAFzF zyqNm;&!qk5278X@_MBl_M~BkBbZCy#z@Mh}i``&YA)<`%z+fUY$5W-7*b~=N+d+@6Htk=(*5AsoO zZC#~JiC?9lM!+d=2&Hmxwxc|J8;E8khG$i3pb6(h5 zSPKYB(oBXZ!jnlA5rylL3N-*VfJr(D*xd=10G6l9Noo|(#sD)wrJN#HA`+-6iB+BM zG=oY#BcqV0)1o?p)$KYoaZ|Te3=lOSli_^Q9biz+R2XDs;uQcP!;)B(mE9gFQqxss zMUCfE<+OsIe&kw+%)$Y!BRl}8aEVt26$TJMUE(rLD0Oud@$OLMnX5p@=A6p$tHtRE zKuZGt20!yl{9qXE96kvbR@@-oGD3rk6csS6MIg&$7@(LAFu(<3EM8Ors0ODGor{V> zqZ^zJj)n+VWmIss*!i=FET)qTU1DgySjBIu3tn9(kfG&WaVV^e{%k7+S)KD3Uvet);F&uP9Tjn2#d7Pq$NSFqEH6zg z$6isdRr6brY<$?uY645?8%;qbrvW05kW=F5_%OlB2>&iGGLPY~>wjka{$$hgyZ zDBr>~VFbJX07yiBuDqZG;}?Li1hK+Hx)7Jx_a9u;5pDRfNiwgnAePEdgdL2--VC<} zF2kVlNRxL*(&yo47ww}`p$h-*Gv>>GN6aw8@1eTuM3c6Kv|0f)tftTCKjkk{7;K9* zT(0fa@_G-emdW;)(LV(j20>^$mD;BOt#({5r9?J5RWG0FFy6c_8=GlzGpoCO90ZjW zHDglua%I)3pR(v>-7;|203IjbJtYSyqGc=xikB^R3jQhDx?u6VZ&qSrgIOHKUhzsz zb<daQq>pkKQ&M^L4|{GMb-y7zy+hhEF!zdrOy1n7~anj&lQH*N!X~R@`ziOAC zJ8o7jA};~V2T1m7$dvzr4R5XAjj$A!$$VG|r$4@h_n!{K`u@|fa=RH$zuyg;AGbsE y_ +; +; 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 . + + + +INCLUDE "controlflow.asm" +INCLUDE "oam.asm" + + + +SECTION "DwLib80_BlockingAnimation", ROM0 + +; [in] b = total sprites +; [in] c = total frames +; [in] d = delta X per frame +; [in] e = delta Y per frame +; [in] hl = base sprite address +; Move a range of sprites linearly. +; Will block execution until animation is completed. +SimpleLinearAnimation: + ; Switch to V-Blank interrupt + ldh a, [rIE] + ld [animation_rIE_backup], a + ld a, IEF_VBLANK + ldh [rIE], a + ei + + ld h, HIGH(OAMBuffer) + ld a, b + ld [animation_sprites_backup], a + BeginLoop frame, c + ld a, [animation_sprites_backup] + ld b, a + push hl + BeginLoop sprite, b + ld a, [hl] + add e + ld [hl+], a + ld a, [hl] + add d + ld [hl+], a + inc l + inc l + EndLoop sprite, b + halt + pop hl + EndLoop frame, c + + ; Restore interrupt + ld a, [animation_rIE_backup] + ldh [rIE], a + + ret + + +SECTION "DwLib80_BlockingAnimation_Temp", WRAM0 + +animation_rIE_backup: db +animation_sprites_backup: db diff --git a/src/dwlib80/controlflow.asm b/src/dwlib80/controlflow.asm new file mode 100644 index 0000000..c284133 --- /dev/null +++ b/src/dwlib80/controlflow.asm @@ -0,0 +1,43 @@ +; Copyright (C) 2020 Dwscdv3 +; +; 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 . + + + +IF !DEF(src_control_flow) +src_control_flow = 1 + + + +SECTION "DwLib80_ControlFlow", ROM0 + +; [in] \1 = identifier +; [in] \2 = counter register +BeginLoop: MACRO + inc \2 + jr .loop_\1_step +.loop_\1_body +ENDM + +; [in] \1 = identifier +; [in] \2 = counter register +EndLoop: MACRO +.loop_\1_step + dec \2 + jr nz, .loop_\1_body +ENDM + + + +ENDC diff --git a/src/dwlib80/convert.asm b/src/dwlib80/convert.asm new file mode 100644 index 0000000..a4bf6ad --- /dev/null +++ b/src/dwlib80/convert.asm @@ -0,0 +1,59 @@ +; Copyright (C) 2020 Dwscdv3 +; +; 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 . + + + +IF !DEF(src_convert) +src_convert = 1 + + + +SECTION "Convert", ROM0 + +; [in] \1 = X +; [in] \2 = Y +; [out] a = 1D index +; 6 cycles +Coord2DTo1D_9: MACRO + ld a, \2 + rlca + rlca + rlca + add \2 + add \1 +ENDM + +; [in] \1 = 1D index +; [in] \2 = width, bdehl or immediate value +; [out] a, b = X +; [out] c = Y +; 13 bytes +; 6n + 13 cycles, or 5n + 11 cycles +Coord1DTo2D: MACRO + ld c, 0 ; 2 2 + ld a, \1 ; 1 1 + jr .Coord1DTo2D_loop_start\@ ; 2 3 +.Coord1DTo2D_loop_body\@ + inc c ; 1 1 +.Coord1DTo2D_loop_start\@ + sub \2 ; 2 2 + jr nc, .Coord1DTo2D_loop_body\@ ; 2 3|2 + add \2 ; 2 2 + ld b, a ; 1 1 +ENDM + + + +ENDC diff --git a/src/dwlib80/input.asm b/src/dwlib80/input.asm new file mode 100644 index 0000000..25eeed5 --- /dev/null +++ b/src/dwlib80/input.asm @@ -0,0 +1,51 @@ +INCLUDE "lib/hardware.inc" +INCLUDE "rand.asm" + + + +SECTION "DwLib80_Input", ROM0 + +Input: +.getDPadState + ld c, LOW(rP1) + ld a, P1F_GET_DPAD + ldh [c], a + REPT 6 + ldh a, [c] + ENDR + cpl + and %00001111 + swap a + ld b, a ; b = ▼▲◀▶???? +.getButtonState + ld a, P1F_GET_BTN + ldh [c], a + REPT 6 + ldh a, [c] + ENDR + cpl + and %00001111 + or b + ld b, a ; b = ▼▲◀▶RLBA +.releaseDevice ; seems unnecessary? + ld a, P1F_GET_DPAD | P1F_GET_BTN + ld [c], a +.storeJoypadState + ldh a, [JoypadState] + cpl + and b + ldh [PressedKeys], a + ld a, b + ldh [JoypadState], a + ret + +Exception: + ld b, b + ret + + + +SECTION "DwLib80_Input_Variables", HRAM + +JoypadState: db +PressedKeys: db diff --git a/src/dwlib80/math.asm b/src/dwlib80/math.asm new file mode 100644 index 0000000..caa7298 --- /dev/null +++ b/src/dwlib80/math.asm @@ -0,0 +1,143 @@ +; Copyright (C) 2020 Dwscdv3 +; +; 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 . + + + +IF !DEF(src_math) +src_math = 1 + + + +SECTION "DwLib80_Math", ROM0 + +neg: MACRO + cpl + inc a +ENDM + +; [in] \1 = R16 (high) +; [in] \2 = R16 (low) +; [in] \3 = D8 +; [corrupt] a +; 7 bytes, 7 cycles +AddR16D8: MACRO + ld a, \2 ; 1 1 + add \3 ; 2 2 + ld \2, a ; 1 1 + jr nc, .noCarry\@ ; 2 3|2 + inc \1 ; 1 1 +.noCarry\@ +ENDM + +; [in] \1 = R16 (high) +; [in] \2 = R16 (low) +; [in] \3 = D8 +; 7 cycles +SubR16D8: MACRO + ld a, \2 ; 1 + sub \3 ; 2 + ld \2, a ; 1 + jr nc, .noCarry\@ ; 3|2 + dec \1 ; 1 +.noCarry\@ +ENDM + +; [in] a = binary integer +; [out] a = 10^0 place +; [out] b = 10^1 place +; [out] c = 10^2 place +; 94 cycles (worst case: 190 ~ 199) +ToDecimal8: + ld b, 0 ; 2 + ld c, 0 ; 2 +.hundreds + cp 100 ; 1 + jr c, .tens ; 3|2 + sub 100 ; 1 + inc c ; 1 + jr .hundreds ; 3 +.tens + cp 10 ; 1 + ret c ; 5|2 + sub 10 ; 1 + inc b ; 1 + jr .tens ; 3 + +_DivideBit: MACRO +.bit\1 + sub c ; 1 + jr c, .bit\1_is0 ; 3|2 +.bit\1_is1 + set \1, b ; 2 + jr .bit\1_step ; 2 +.bit\1_is0 + add c ; 1 +.bit\1_step + srl c ; 2 +ENDM + +; [in] b = dividend +; [in] c = divisor +; [out] a = remainder +; [out] b = quotient +; [corrupt] c, d, e, h, l +; 144 cycles (worst case: quotient = 1), 37 cycles (best case: quotient >= 128) +Divide: + ld a, c ; 1 + or a ; 1 + ret z ; 5|2 + + ld de, 0 ; 3 + + jr .checkDivisorLessThan128 ; 2 +.shlDivisor + rlca ; 1 + inc e ; 1 +.checkDivisorLessThan128 + cp $80 ; 1 + jr c, .shlDivisor ; 3|2 + ld c, a ; 1 + + ld a, b ; 1 + ld b, 0 ; 2 + ld hl, .jumpTable ; 3 + sla e ; 2 + add hl, de ; 2 + jp hl ; 1 +.jumpTable ; 2 + jr .bit0 + jr .bit1 + jr .bit2 + jr .bit3 + jr .bit4 + jr .bit5 + jr .bit6 + jr .bit7 + + _DivideBit 7 + _DivideBit 6 + _DivideBit 5 + _DivideBit 4 + _DivideBit 3 + _DivideBit 2 + _DivideBit 1 + _DivideBit 0 + +.end + ret ; 4 + + + +ENDC diff --git a/src/dwlib80/memory.asm b/src/dwlib80/memory.asm new file mode 100644 index 0000000..aa416b3 --- /dev/null +++ b/src/dwlib80/memory.asm @@ -0,0 +1,104 @@ +; Copyright (C) 2020 Dwscdv3 +; +; 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 . + + + +IF !DEF(src_memory) +src_memory = 1 + + + +SECTION "DwLib80_Memory", ROM0 + +read: MACRO + ld a, [\2] + ld \1, a +ENDM + +write: MACRO + ld a, \1 + ld [\2], a +ENDM + +readh: MACRO + ldh a, [\2] + ld \1, a +ENDM + +writeh: MACRO + ld a, \1 + ldh [\2], a +ENDM + +; [in] a = value to set +; [in] bc = length +; [in] hl = destination address +; [corrupt] b, c, h, l +memset: + inc c + inc b + jr .start +.loop: + ld [hl+], a +.start: + dec c + jr nz, .loop + dec b + jr nz, .loop + ret + +; [in] bc = length +; [in] de = destination address +; [in] hl = source address +; [corrupt] a, b = 0, c = 0, d, e, h, l +memcpy: + inc c + inc b + jr .start +.loop: + ld a, [hl+] + ld [de], a + inc de +.start: + dec c + jr nz, .loop + dec b + jr nz, .loop + ret + +; [in] c = length +; [in] hl = base address +; [corrupt] a, b, c, d, e, h, l +; 15 + c * 17 cycles +reverse: + ld d, h ; 1 + ld e, l ; 1 + ld b, 0 ; 2 + add hl, bc ; 2 + dec hl ; 2 + srl c ; 2 + inc c ; 1 +.loop + dec c ; 1 + ret z ; 5|2 + ld b, [hl] ; 2 + ld a, [de] ; 2 + ld [hl-], a ; 2 + ld a, b ; 1 + ld [de], a ; 2 + inc de ; 2 + jr .loop ; 3 + +ENDC diff --git a/src/dwlib80/oam.asm b/src/dwlib80/oam.asm new file mode 100644 index 0000000..c305121 --- /dev/null +++ b/src/dwlib80/oam.asm @@ -0,0 +1,147 @@ +; Copyright (C) 2020 Dwscdv3 +; +; 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 . + + + +; Not used the LOAD command due to unknown crashes in the linker. + + + +IF !DEF(src_oam) +src_oam = 1 + + + +INCLUDE "../lib/hardware.inc" +INCLUDE "memory.asm" + + + +SECTION "DwLib80_OAM_Buffer", WRAM0, ALIGN[8] + +OAMBuffer: ds $A0 + + + +SECTION "DwLib80_OAM", ROM0 + +ClearOAMBuffer: + xor a + ld bc, $A0 + ld hl, OAMBuffer + call memset + ret + +; [in] a = tile address +; [in] l = sprite address +; [corrupt] a, h, l +; 25 cycles +SetSprite_8x16x2: + push hl ; 4 + ld h, HIGH(OAMBuffer) ; 2 + inc l ; 1 + inc l ; 1 + ld [hl], a ; 2 + inc l ; 1 + inc l ; 1 + inc l ; 1 + inc l ; 1 + add 2 ; 2 + ld [hl], a ; 2 + pop hl ; 3 + ret ; 4 + +; [in] b = X +; [in] c = Y +; [in] l = sprite address +; [corrupt] a, h, l +; 45 cycles +SetSpritePos_8x16x2: + push hl ; 4 + ld h, HIGH(OAMBuffer) ; 2 + ld a, c ; 1 + REPT 4 ; 4 + rlca + ENDR + add 16 ; 2 + ld [hl+], a ; 2 tile[0].Y + inc l ; 1 + inc l ; 1 + inc l ; 1 + ld [hl-], a ; 2 tile[1].Y + dec l ; 1 + dec l ; 1 + ld a, b ; 1 + REPT 4 ; 4 + rlca + ENDR + add 8 ; 2 + ld [hl+], a ; 2 tile[0].X + inc l ; 1 + inc l ; 1 + inc l ; 1 + add 8 ; 2 + ld [hl], a ; 2 tile[1].X + pop hl ; 3 + ret ; 4 + +; [in] l = sprite address +; 23 cycles +HideSprite_8x16x2: + push hl ; 4 + ld h, HIGH(OAMBuffer) ; 2 + ld [hl], 0 ; 3 + REPT 4 ; 4 + inc l + ENDR + ld [hl], 0 ; 3 + pop hl ; 3 + ret ; 4 + + + +SECTION "DwLib80_DMA", ROM0 + +InitDMA: + ld bc, DMAProc.end - DMAProc + ld de, DMAProc_HRAM + ld hl, DMAProc + call memcpy + ret + +; [corrupt] a, b, c +StartDMA: + ld a, HIGH(OAMBuffer) + ld bc, 41 * $100 + LOW(rDMA) + jp DMAProc_HRAM + +; 5 bytes +DMAProc: + ldh [c], a +.wait + dec b ; 1 + jr nz, .wait ; 3|2 + ret ; 4 +.end + + + +SECTION "DwLib80_DMA_HRAM", HRAM + +DMAProc_HRAM: ds DMAProc.end - DMAProc + + + +ENDC diff --git a/src/dwlib80/pathfinding.asm b/src/dwlib80/pathfinding.asm new file mode 100644 index 0000000..6043e2a --- /dev/null +++ b/src/dwlib80/pathfinding.asm @@ -0,0 +1,234 @@ +; Copyright (C) 2020 Dwscdv3 +; +; 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 . + + + +INCLUDE "tmp.asm" +INCLUDE "memory.asm" +INCLUDE "math.asm" +INCLUDE "convert.asm" + + + +SECTION "DwLib80_Pathfinding_Buffer", WRAM0, ALIGN[8] + +BACKTRACE_FROM_BOTTOM EQU $F1 +BACKTRACE_FROM_LEFT EQU $F2 +BACKTRACE_FROM_TOP EQU $F3 +BACKTRACE_FROM_RIGHT EQU $F4 +BACKTRACE_ORIGIN EQU $F5 + +; This is a hybrid buffer for both board clone and backtrace info. +; For board, 0 is treated as passable, otherwise obstacle. +; For backtrace: +; 0 = not visited yet +; 1 = from bottom +; 2 = from left +; 3 = from top +; 4 = from right +; 5 = origin +Pathfinding_backtrace: ds 256 +_Pathfinding_frontier: ds 256 + + + +SECTION "DwLib80_Pathfinding_Variables", HRAM + +; board should be aligned to $XX00 and less than 256 bytes +Pathfinding_BoardAddressHigh: db +Pathfinding_BoardWidth: db +Pathfinding_BoardHeight: db +Pathfinding_BoardSize: db + + + +SECTION "DwLib80_Pathfinding", ROM0 + +; [in] \1 = data to be enqueued +; 18 cycles +_Pathfinding_frontier_enqueue: MACRO + push hl + readh l, r0 + ld [hl], \1 + inc l + writeh l, r0 + pop hl +ENDM + +; [out] \1 = register to receive data +; 3 cycles +_Pathfinding_frontier_dequeue: MACRO + ld \1, [hl] + inc l +ENDM + +; [in] \1 = backtrace info +_Pathfinding_addToFrontier: MACRO + _Pathfinding_frontier_enqueue e + ld a, \1 + ld [de], a +ENDM + +; [in] b = X +; [in] c = Y +; [in] d = HIGH(Pathfinding_backtrace) +; [in] e = 1D index +; [out] a = boolean +; [corrupt] r2 +; 0 is treated as empty, otherwise obstacle +; Valid when: in bound && empty && not visited before +; invalid any cells after target +; 37 cycles (worst case) +_Pathfinding_IsValidCell: +.checkX ; 8|7 + ldh a, [Pathfinding_BoardWidth] + dec a + cp b + jr c, .false +.checkY ; 8|7 + ldh a, [Pathfinding_BoardHeight] + dec a + cp c + jr c, .false +.checkIsTarget ; 13|12 + ldh a, [r2] + or a + jr nz, .false + ldh a, [r1] + cp e + jr z, .target +.checkEmptyAndVisited ; 6|5 + ld a, [de] + or a + jr nz, .false +.true ; 6 + ld a, 1 + ret +.false ; 5 + xor a + ret +.target ; 8 + writeh 1, r2 + ret + +; [in] e = origin index +; [in] r1 = target +; [out] a = found (boolean) +; [out] Pathfinding_backtrace +; [corrupt] b, c, d, e, h, l, r0 +Pathfinding_BFS: + ; Copy board to backtrace buffer to save 2 registers from tracing board address. + push bc + push de + xor a + ld b, a + ld e, a + ld l, a + ldh a, [Pathfinding_BoardSize] + ld c, a + ld d, HIGH(Pathfinding_backtrace) + ldh a, [Pathfinding_BoardAddressHigh] + ld h, a + call memcpy + pop de + pop bc + ld d, HIGH(Pathfinding_backtrace) + ld hl, _Pathfinding_frontier + write 0, r0 + write 0, r2 + + _Pathfinding_frontier_enqueue e + ld a, BACKTRACE_ORIGIN + ld [de], a ; backtrace[origin] = ORIGIN + + ; de = backtrace buffer + ; hl = frontier buffer + ; r0 = frontier queue tail (entrance) + ; r2 = target accessed indicator +.loop_condition ; while (l != r0) + ldh a, [r0] + cp l + jp z, .notFound +.loop_body + _Pathfinding_frontier_dequeue e + + ldh a, [r1] + cp e + jr z, .found + + ldh a, [Pathfinding_BoardWidth] + ld b, a + Coord1DTo2D e, b + +.up + dec c + ldh a, [Pathfinding_BoardWidth] + sub e + neg + ld e, a + call _Pathfinding_IsValidCell + or a + jr z, .up_skip + _Pathfinding_addToFrontier BACKTRACE_FROM_BOTTOM +.up_skip ; revert position change here + ldh a, [Pathfinding_BoardWidth] + add e + ld e, a + inc c +.right + inc b + inc e + call _Pathfinding_IsValidCell + or a + jr z, .right_skip + _Pathfinding_addToFrontier BACKTRACE_FROM_LEFT +.right_skip + dec e + dec b +.down + inc c + ldh a, [Pathfinding_BoardWidth] + add e + ld e, a + call _Pathfinding_IsValidCell + or a + jr z, .down_skip + _Pathfinding_addToFrontier BACKTRACE_FROM_TOP +.down_skip + ldh a, [Pathfinding_BoardWidth] + sub e + neg + ld e, a + dec c +.left + dec b + dec e + call _Pathfinding_IsValidCell + or a + jr z, .left_skip + _Pathfinding_addToFrontier BACKTRACE_FROM_RIGHT +.left_skip + inc e + inc b + + jp .loop_condition + +.notFound + xor a + ret +.found + ld a, 1 + ret + diff --git a/src/dwlib80/rand.asm b/src/dwlib80/rand.asm new file mode 100644 index 0000000..e5b1b36 --- /dev/null +++ b/src/dwlib80/rand.asm @@ -0,0 +1,127 @@ +; Copyright (C) 2020 Dwscdv3 +; +; 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 . + + + +IF !DEF(src_rand) +src_rand = 1 + + + +SECTION "DwLib80_Random_Data", ROM0, ALIGN[8] + +RandomTable: +db 157, 20, 90, 166, 10, 158, 132, 211, 35, 4, 224, 167, 176, 88, 143, 96 +db 177, 34, 174, 6, 205, 62, 200, 39, 155, 1, 251, 21, 169, 15, 170, 124 +db 234, 201, 53, 44, 138, 153, 185, 214, 133, 139, 252, 100, 195, 243, 140, 80 +db 221, 3, 145, 227, 87, 194, 228, 98, 119, 181, 173, 37, 41, 126, 93, 236 +db 57, 215, 136, 247, 94, 192, 202, 27, 114, 8, 254, 172, 125, 144, 168, 242 +db 50, 47, 68, 137, 106, 63, 180, 77, 134, 74, 52, 30, 238, 212, 49, 109 +db 67, 13, 246, 36, 55, 163, 237, 182, 42, 28, 24, 146, 115, 220, 249, 164 +db 107, 92, 160, 83, 127, 101, 97, 175, 248, 206, 149, 128, 17, 81, 226, 0 +db 135, 179, 116, 16, 148, 56, 69, 141, 5, 207, 104, 230, 203, 85, 32, 129 +db 54, 26, 239, 38, 43, 61, 150, 89, 75, 64, 12, 216, 45, 154, 111, 122 +db 223, 76, 229, 217, 105, 2, 60, 196, 7, 59, 151, 91, 113, 204, 108, 178 +db 190, 22, 110, 189, 131, 253, 162, 112, 209, 86, 82, 210, 156, 241, 48, 219 +db 161, 191, 142, 65, 14, 73, 40, 199, 23, 46, 187, 102, 25, 103, 225, 95 +db 19, 66, 58, 147, 188, 9, 244, 165, 255, 33, 232, 184, 117, 99, 121, 31 +db 159, 11, 198, 233, 235, 218, 79, 118, 240, 222, 72, 18, 51, 183, 171, 71 +db 130, 250, 208, 193, 197, 29, 84, 152, 123, 245, 186, 78, 120, 213, 70, 231 + +RandomBiasCheckTable: +db 255, 255, 255, 254, 255, 254, 251, 251, 255, 251, 249, 252, 251, 246, 251, 254 +db 255, 254, 251, 246, 239, 251, 241, 252, 239, 249, 233, 242, 251, 231, 239, 247 +db 255, 230, 237, 244, 251, 221, 227, 233, 239, 245, 251, 214, 219, 224, 229, 234 +db 239, 244, 249, 254, 207, 211, 215, 219, 223, 227, 231, 235, 239, 243, 247, 251 +db 255, 194, 197, 200, 203, 206, 209, 212, 215, 218, 221, 224, 227, 230, 233, 236 +db 239, 242, 245, 248, 251, 254, 171, 173, 175, 177, 179, 181, 183, 185, 187, 189 +db 191, 193, 195, 197, 199, 201, 203, 205, 207, 209, 211, 213, 215, 217, 219, 221 +db 223, 225, 227, 229, 231, 233, 235, 237, 239, 241, 243, 245, 247, 249, 251, 253 +db 255, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142 +db 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158 +db 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174 +db 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190 +db 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206 +db 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222 +db 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238 +db 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254 + + + +SECTION "DwLib80_Random", ROM0 + +; [corrupt] a +; 7 cycles +SeedRandom: MACRO + ldh a, [rDIV] + ; Map even numbers to odd numbers for best RNG quality + set 0, a + ldh [rand_seed], a +ENDM + +; [out] a = random number between [0 ~ 256) +; [corrupt] h, l +; 20 cycles +Random8: + ldh a, [rand_cursor] ; 3 + ld l, a ; 1 + ldh a, [rand_seed] ; 3 + add l ; 1 + ldh [rand_cursor], a ; 3 + ld l, a ; 1 + ld h, HIGH(RandomTable) ; 2 + ld a, [hl] ; 2 + ret ; 4 + +; [in] c = upper bound (exclusive), cannot be 0 +; [out] a = random number between [0 ~ c) +; [corrupt] b, c, d, e, h, l +; ~170 cycles (worst case) +Random: + call Random8 ; 6 +20 + ld h, HIGH(RandomBiasCheckTable) ; 2 + ld l, c ; 1 + cp [hl] ; 2 + jr c, .mod ; 3|2 + jr z, .mod ; 3|2 + jr Random ; 2 +.mod + ld b, a ; 1 + call Divide ; 6 +35~142 + ret ; 4 + +; Random with register preservation +; [in] c = upper bound (exclusive), cannot be 0 +; [out] a = random number between [0 ~ c) +; ~170 + 34 cycles (worst case) +Random_Safe: + push bc + push de + push hl + call Random + pop hl + pop de + pop bc + ret + + +SECTION "DwLib80_Random_Variables", HRAM + +rand_cursor: db +rand_seed: db + + + +ENDC diff --git a/src/dwlib80/tmp.asm b/src/dwlib80/tmp.asm new file mode 100644 index 0000000..735254b --- /dev/null +++ b/src/dwlib80/tmp.asm @@ -0,0 +1,15 @@ +IF !DEF(src_tmp) +src_tmp = 1 + + + +SECTION "ExtendedRegisters", HRAM, ALIGN[3] + +r0: db +r1: db +r2: db +r3: db + + + +ENDC diff --git a/src/header.asm b/src/header.asm new file mode 100644 index 0000000..3fd5d5c --- /dev/null +++ b/src/header.asm @@ -0,0 +1,43 @@ +SECTION "RST00", ROM0[$00] + ret + +SECTION "RST08", ROM0[$08] + +SECTION "RST10", ROM0[$10] + +SECTION "RST18", ROM0[$18] + +SECTION "RST20", ROM0[$20] + +SECTION "RST28", ROM0[$28] + +SECTION "RST30", ROM0[$30] + +SECTION "RST38", ROM0[$38] + + + +SECTION "Interrupt_VBlank", ROM0[$40] + jp VBlankInterrupt + +SECTION "Interrupt_LCDStat", ROM0[$48] + reti + +SECTION "Interrupt_Timer", ROM0[$50] + reti + +SECTION "Interrupt_Serial", ROM0[$58] + reti + +SECTION "Interrupt_Joypad", ROM0[$60] + jp JoypadInterrupt + + + +SECTION "Entry", ROM0[$100] + jp Entry + + + +SECTION "Header", ROM0[$104] + ds $4C diff --git a/src/includes.inc b/src/includes.inc new file mode 100644 index 0000000..c037076 --- /dev/null +++ b/src/includes.inc @@ -0,0 +1,19 @@ +INCLUDE "lib/hardware.inc" +INCLUDE "constants.inc" +INCLUDE "alloctable.inc" + +INCLUDE "dwlib80/controlflow.asm" +INCLUDE "dwlib80/memory.asm" +INCLUDE "dwlib80/math.asm" +INCLUDE "dwlib80/rand.asm" +INCLUDE "dwlib80/oam.asm" +INCLUDE "dwlib80/animation.asm" +INCLUDE "dwlib80/input.asm" +INCLUDE "dwlib80/convert.asm" +INCLUDE "dwlib80/pathfinding.asm" +INCLUDE "dwlib80/tmp.asm" + +INCLUDE "header.asm" +INCLUDE "data.asm" +INCLUDE "macro.asm" +; INCLUDE "test.asm" diff --git a/src/macro.asm b/src/macro.asm new file mode 100644 index 0000000..bef5eb2 --- /dev/null +++ b/src/macro.asm @@ -0,0 +1,265 @@ +SECTION "Abstractions", ROM0 + +; From least significant to most significant, one call per digit +; \1 = X coordinate +; \2 = Y coordinate +; \3 = tile index offset +; 10 cycles +_CopyAddressDEToScreen: MACRO + ld hl, _SCRN0 + SCRN_VX_B * \2 + \1 ; 3 + ld a, [de] ; 2 + add \3 ; 2 + inc e ; 1 + ld [hl], a ; 2 +ENDM + +; 8 cycles waste if not halt +HaltIfVRAMInaccessible: MACRO + ldh a, [rSTAT] ; 3 + and STATF_BUSY ; 2 + jr z, .noHalt\@ ; 3|2 + halt +.noHalt\@ +ENDM + +; \1 = base sprite address (high byte is not used) +; \2 = total sprites +; \3 = delta X per frame +; \4 = delta Y per frame +; \5 = total frames +StartAnimation: MACRO + ld hl, \1 + ld b, \2 + ld d, \3 + ld e, \4 + ld c, \5 + call SimpleLinearAnimation +ENDM + +; [out] a = Random(7) + 1 +GetRandomVariant: MACRO + ld c, 7 + call Random_Safe + inc a +ENDM + +; [in] a = tile index +; [out] a = tile address +TileIndexToAddress: MACRO + rlca + rlca +ENDM + +; [in] a = tile address +; [a] a = tile index +TileAddressToIndex: MACRO + rrca + rrca +ENDM + +; [in] \1 = index (0, 1, 2) +UpdateUpcomingStoneSprite: MACRO + ld l, SPRITE_NEXT\1 + ld de, upcomingStones + \1 + ld a, [de] + TileIndexToAddress + call SetSprite_8x16x2 + ld b, 9 + ld c, \1 + call SetSpritePos_8x16x2 +ENDM + +SelectCursor: MACRO + ldh a, [cursor] + ldh [selected], a + call UpdateSelectionPos +ENDM + +; \1 = delta X +; \2 = delta Y +; \3 = X warp bound, -1 for undefined +; \4 = Y warp bound, -1 for undefined +_CursorMoveHandler: MACRO + GetCursorXY + IF \3 >= 0 + cp \3 + jr nz, .noWarp\@ + ELSE + ld a, c + cp \4 + jr nz, .noWarp\@ + ENDC +.warp\@ + StartAnimation \ + SPRITE_CURSOR, 2, \ + ANIM_CURSOR_SPEED * (-\1) * 8, \ + ANIM_CURSOR_SPEED * (-\2) * 8, \ + 16 / ANIM_CURSOR_SPEED + IF \3 >= 0 + ld b, (-\1) * 8 + ELSE + ld b, (-\2) * 8 * BOARD_HEIGHT + ENDC + call .moveCursor + ret +.noWarp\@ + StartAnimation \ + SPRITE_CURSOR, 2, \ + ANIM_CURSOR_SPEED * \1, \ + ANIM_CURSOR_SPEED * \2, \ + 16 / ANIM_CURSOR_SPEED + IF \3 >= 0 + ld b, \1 + ELSE + ld b, \2 * BOARD_HEIGHT + ENDC + call .moveCursor + ret +ENDM + +; [out] b = X +; [out] c = Y +; 16 ~ 64 cycles +GetCursorXY: MACRO + ldh a, [cursor] ; 3 + Coord1DTo2D a, BOARD_WIDTH ; 13 ~ 61 +ENDM + +; [out] a +; 8 cycles +GetCursorStoneVariant: MACRO + ldh a, [cursor] ; 2 3 + ld h, HIGH(board) ; 2 2 + ld l, a ; 1 1 + ld a, [hl] ; 1 2 +ENDM + +_InputHandler_Button: MACRO + ldh a, [PressedKeys] + bit \1, a + call nz, \2 +ENDM + +_InputHandler_DPad: MACRO + push bc + ldh a, [c] + bit \1, a + call nz, \2 + pop bc +ENDM + +; [in] \1 = delta X +; [in] \2 = delta Y +_MoveAnimation: MACRO + StartAnimation \ + SPRITE_MOVE, 2, \ + \1 * ANIM_MOVEMENT_SPEED, \ + \2 * ANIM_MOVEMENT_SPEED, \ + 16 / ANIM_MOVEMENT_SPEED +ENDM + +; [in] \1 = identifier (top, right, ...) +; [in] \2 = delta X (-1, 0, 1) +; [in] \3 = delta Y (-1, 0, 1) +; [in] \4 = address change +_MoveAnimation_Direction: MACRO +.dir_\1 + push hl + _MoveAnimation \2, \3 + pop hl + ld a, l + add \4 + ld l, a + jr .anim_loop +ENDM + +_PlaceUpcomingStone: MACRO + call GetRandomEmptyCell + write l, r1 + Coord1DTo2D a, BOARD_WIDTH + ld a, b + sub 9 ; Not BOARD_WIDTH because upcomings are always at right edge + ld d, a + ld a, c + sub \1 + ld e, a + StartAnimation SPRITE_NEXT\1, 2, d, e, 16 + REPT ANIM_PLACE_STONE_DELAY + halt + ENDR + ld a, [OAMBuffer + SPRITE_NEXT\1 + 2] + TileAddressToIndex + ld b, a + read l, r1 + call PlaceStone + call CheckForMatch + ld l, SPRITE_NEXT\1 + call HideSprite_8x16x2 + ldh a, [remainingSpace] + or a + jp z, GameOver +ENDM + +; [in] de = coordinate +; [corrupt] a +; 25 cycles +_AddDestroyCandidates: MACRO + push hl ; 4 + ld h, HIGH(destroyCandidates) ; 2 + ldh a, [destroyCandidatesCount] ; 3 + ld l, a ; 1 + inc a ; 1 + ldh [destroyCandidatesCount], a ; 3 + Coord2DTo1D_9 d, e ; 6 + ld [hl], a ; 2 + pop hl ; 3 +ENDM + +; [corrupt] a +; 7 cycles +_CancelDestroyCandidates: MACRO + ldh a, [destroyCandidatesCount] + sub b + ldh [destroyCandidatesCount], a +ENDM + +; [corrupt] a, l +; 10 cycles +_CompareToTarget: MACRO + Coord2DTo1D_9 d, e ; 6 + ld l, a ; 1 + ld a, [hl] ; 2 + cp c ; 1 +ENDM + +; [in] \1 = register to be decreased +; [in] \2 = upper bound (exclusive) +; [out] \1 += 1 +; [out] a = 0 +; return if overflowed +_IncreaseWithBoundCheck: MACRO + inc \1 + ld a, \1 + cp \2 + ld a, 0 ; here we must use `LD` because it doesn't change flags + ret nc +ENDM + +; [in] \1 = register to be decreased +; [out] \1 -= 1 +; [out] a = 0 +; return if overflowed +_DecreaseWithBoundCheck: MACRO + ld a, \1 + sub 1 + ld \1, a + ld a, 0 ; here we must use `LD` because it doesn't change flags + ret c +ENDM + +_CheckDirectionPair: MACRO + ld a, HIGH(\1) + ld b, LOW(\1) + ld hl, \2 + call CheckDirectionPair +ENDM diff --git a/src/main.asm b/src/main.asm new file mode 100644 index 0000000..de0a37b --- /dev/null +++ b/src/main.asm @@ -0,0 +1,633 @@ +; Copyright (C) 2020 Dwscdv3 +; +; 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 . + + + +INCLUDE "includes.inc" + + + +SECTION "Game", ROM0 + +Entry: + ld sp, STACK_BASE + call InitDMA +.initSystemVariables + xor a + ldh [FrameCount], a + ldh [JoypadState], a + ldh [PressedKeys], a + ldh [VBlankHandlerEnabled], a + ldh a, [rand_seed] ; rand_seed * 2 + 1, ensure it's an odd number + sla a + inc a + ldh [rand_seed], a +.configureInterrupts + ld a, P1F_GET_DPAD + ldh [rP1], a + ld a, IEF_VBLANK | IEF_HILO + ldh [rIE], a + ei +.waitForVBlank + halt +.screenOff + ld a, LCDCF_OFF + ld [rLCDC], a +.initOAM + call ClearOAMBuffer + ld l, SPRITE_CURSOR + ld a, TILE_CURSOR + call SetSprite_8x16x2 + ld l, SPRITE_SELECTION + ld a, TILE_SELECTION + call SetSprite_8x16x2 +.loadTileset + ld de, _VRAM + ld hl, Tileset + ld bc, Tileset.end - Tileset + call memcpy +.initBGMap + ld hl, _SCRN0 + SCRN_VX_B * 0 + 19 + ld c, SCRN_Y_B + BeginLoop clearBGMap, c + ld a, TILE_EMPTY + ld [hl], a + AddR16D8 h, l, SCRN_VX_B + EndLoop clearBGMap, c + ld hl, _SCRN0 + SCRN_VX_B * 0 + 18 + ld c, SCRN_Y_B + BeginLoop loadBorder, c + ld a, TILE_BORDER + ld [hl], a + AddR16D8 h, l, SCRN_VX_B + EndLoop loadBorder, c +.initDisplay + ld a, LCDCF_ON | LCDCF_BGON | LCDCF_BG8000 | LCDCF_OBJON | LCDCF_OBJ16 + ld [rLCDC], a +.initPalette + ld a, %11100100 + ld [rBGP], a + ld [rOBP0], a + ld [rOBP1], a +.enableVBlankHandler + write 1, VBlankHandlerEnabled +.soundOff + ld a, AUDENA_OFF + ld [rAUDENA], a +.initPathfinding + write BOARD_WIDTH, Pathfinding_BoardWidth + write BOARD_HEIGHT, Pathfinding_BoardHeight + write BOARD_WIDTH * BOARD_HEIGHT, Pathfinding_BoardSize + write HIGH(board), Pathfinding_BoardAddressHigh +.unitTest + IF DEF(UnitTest) + call UnitTest + ENDC + +InitGame: +.resetStack + ld sp, STACK_BASE +.initVariables + write 0, FrameCount + write 0, scoreLow + ld a, 0 + ld bc, SCORE_DIGIT_COUNT + ld hl, scoreDigits + call memset + ld a, 0 + ld bc, 7 + ld hl, easterEgg_codebook + call memset + write 40, cursor + call UpdateCursorPos + write -1, selected + call UpdateSelectionPos + write BOARD_SIZE, remainingSpace +.initBoard + ld a, 0 + ld bc, BOARD_SIZE + ld hl, board + call memset +.initStones + REPT 5 + GetRandomVariant + ld b, a + call GetRandomEmptyCell + call PlaceStone + ENDR + call GenerateUpcomingStones + +Main: +.waitForVBlank + halt +.checkIsVBlankInterrupt + ldh a, [VBlankFlag] + or a + jr z, Main + xor a + ldh [VBlankFlag], a + + call Input + call Game + + jr Main + +Draw: + ; Switch to H-Blank interrupt + ld a, IEF_LCDC + ldh [rIE], a + ld a, STATF_MODE00 + ldh [rSTAT], a +IF !DEF(EASTER_EGG) || EASTER_EGG == 0 +.score + ld de, scoreDigits + _CopyAddressDEToScreen 19, 17, TILE_DIGIT_0 + _CopyAddressDEToScreen 19, 16, TILE_DIGIT_0 + _CopyAddressDEToScreen 19, 15, TILE_DIGIT_0 + _CopyAddressDEToScreen 19, 14, TILE_DIGIT_0 + _CopyAddressDEToScreen 19, 13, TILE_DIGIT_0 +ENDC +IF EASTER_EGG > 0 +.easterEgg +EASTER_EGG_SYMBOL = 0 + REPT 7 + ld a, [easterEgg_codebook + EASTER_EGG_SYMBOL] + or a + ld a, TILE_BORDER + ld b, TILE_EMPTY + jr z, .easterEgg_\@_skip + ld a, TILE_SYMBOL_0 + EASTER_EGG_SYMBOL + ld b, TILE_DIGIT_0 + 1 + EASTER_EGG_SYMBOL +.easterEgg_\@_skip + ld [_SCRN0 + 18 + (SCRN_Y_B - 1 - EASTER_EGG_SYMBOL) * SCRN_VX_B], a + ld a, b + ld [_SCRN0 + 19 + (SCRN_Y_B - 1 - EASTER_EGG_SYMBOL) * SCRN_VX_B], a +EASTER_EGG_SYMBOL = EASTER_EGG_SYMBOL + 1 + ENDR +ENDC +.board + ld de, board + ld hl, _SCRN0 ; (0, 0) of the game area + ld c, BOARD_HEIGHT ; Y counter + BeginLoop y, c + ld b, BOARD_WIDTH ; X counter + BeginLoop x, b + HaltIfVRAMInaccessible ; 8 + ld a, [de] ; 2 + TileIndexToAddress ; 2 + ld [hl+], a ; 2 + add 2 ; 2 + ld [hl-], a ; 2 + AddR16D8 h, l, SCRN_VX_B ; 7 + HaltIfVRAMInaccessible ; 8 + ld a, [de] ; 2 + TileIndexToAddress ; 2 + inc a ; 1 + ld [hl+], a ; 2 + add 2 ; 2 + ld [hl+], a ; 2 + SubR16D8 h, l, SCRN_VX_B ; 7 + inc e ; 1 + EndLoop x, b + AddR16D8 h, l, SCRN_VX_B * 2 - BOARD_WIDTH * 2 + ld l, a + EndLoop y, c + + ; Restore normal interrupt + ld a, IEF_VBLANK | IEF_HILO + ldh [rIE], a + ret + +Game: + call InputHandler + ret + +InputHandler: + _InputHandler_Button PADB_A, .btnA + _InputHandler_Button PADB_SELECT, .btnSelect + _InputHandler_Button PADB_START, .btnStart + ld c, LOW(JoypadState) + ldh a, [c] + bit PADB_B, a + jr nz, .rapidMode + ld c, LOW(PressedKeys) +.rapidMode + _InputHandler_DPad PADB_RIGHT, .right + _InputHandler_DPad PADB_LEFT, .left + _InputHandler_DPad PADB_UP, .up + _InputHandler_DPad PADB_DOWN, .down + ret +.btnA + GetCursorStoneVariant + cp 0 + jr z, .emptyGrid + SelectCursor + ret +.emptyGrid + call TryMove + ret +.btnB + ret +.btnSelect + ret +.btnStart + jp InitGame +.right + _CursorMoveHandler 1, 0, 8, -1 +.left + _CursorMoveHandler -1, 0, 0, -1 +.up + _CursorMoveHandler 0, -1, -1, 0 +.down + _CursorMoveHandler 0, 1, -1, 8 +.moveCursor + ldh a, [cursor] + add b + ldh [cursor], a + call UpdateCursorPos + ret + +TryMove: + ldh a, [selected] + cp 255 + ret z + ldh [r1], a + read e, cursor + call Pathfinding_BFS + or a + call nz, Move + ret + +Move: + ld h, HIGH(board) + read l, selected + write $FF, selected + call UpdateSelectionPos + ld a, [hl] + TileIndexToAddress + write a, r0 ; r0 = stone sprite address + ld [hl], 0 ; remove stone from old place + ld e, l + ld l, SPRITE_MOVE + call SetSprite_8x16x2 + Coord1DTo2D e, BOARD_WIDTH + ld l, SPRITE_MOVE + call SetSpritePos_8x16x2 + ld h, HIGH(Pathfinding_backtrace) + ld l, e +.anim_loop + ld a, BACKTRACE_FROM_BOTTOM + cp [hl] + jr z, .dir_down + ld a, BACKTRACE_FROM_LEFT + cp [hl] + jr z, .dir_left + ld a, BACKTRACE_FROM_TOP + cp [hl] + jr z, .dir_up + ld a, BACKTRACE_FROM_RIGHT + cp [hl] + jr z, .dir_right + ld a, BACKTRACE_ORIGIN + cp [hl] + jr z, .anim_loop_end + call Exception + _MoveAnimation_Direction down, 0, 1, BOARD_WIDTH + _MoveAnimation_Direction left, -1, 0, -1 + _MoveAnimation_Direction up, 0, -1, -BOARD_WIDTH + _MoveAnimation_Direction right, 1, 0, 1 +.anim_loop_end + ld h, HIGH(board) ; l = cursor + ldh a, [r0] + srl a + srl a + ld [hl], a + ld l, SPRITE_MOVE + call HideSprite_8x16x2 + read l, cursor + call CheckForMatch + cp 5 + jr nc, .matched + call PlaceUpcomingStones + call GenerateUpcomingStones + jr .end +.matched +IF EASTER_EGG > 0 + ld hl, easterEgg_codebook + read a, r0 + dec a + add l + ld l, a + ld [hl], 1 +ENDC +.end + ret + +PlaceUpcomingStones: + _PlaceUpcomingStone 0 + _PlaceUpcomingStone 1 + _PlaceUpcomingStone 2 + ret + +GenerateUpcomingStones: + push bc + push de + push hl + ld hl, upcomingStones + REPT 3 + GetRandomVariant + ld [hl+], a + ENDR + ld de, upcomingStones + UpdateUpcomingStoneSprite 0 + UpdateUpcomingStoneSprite 1 + UpdateUpcomingStoneSprite 2 + pop hl + pop de + pop bc + ret + +UpdateSelectionPos: + push bc + push hl + ldh a, [selected] + Coord1DTo2D a, BOARD_WIDTH + ld hl, SPRITE_SELECTION + call SetSpritePos_8x16x2 + pop hl + pop bc + ret + +UpdateCursorPos: + push bc + push hl + GetCursorXY + ld l, SPRITE_CURSOR + call SetSpritePos_8x16x2 + pop hl + pop bc + ret + +; [out] a, l = an empty position +; [corrupt] h +GetRandomEmptyCell: + push bc + ldh a, [remainingSpace] + ld c, a + call Random_Safe + ; Find nth empty cell + ld c, a + ld hl, board - 1 + inc c + xor a +.loop + inc hl + cp [hl] + jr nz, .loop ; jump if cell is occupied + dec c + jr nz, .loop ; jump if counter > 0 + pop bc + ret + +RecountRemainingSpace: + xor a + ld b, a + ld hl, board + ld c, BOARD_SIZE + BeginLoop count, c + cp [hl] + jr nz, .notEmpty + inc b +.notEmpty + inc l + EndLoop count, c + write b, remainingSpace + ret + +; [in] l = search origin +; [out] a = destroyCandidatesCount +; [out] r0 = target variant checked +; [out] destroyCandidates +; [corrupt] b, c, d, e, h, l +CheckForMatch: + Coord1DTo2D l, BOARD_WIDTH + ld d, b + ld e, c + write 0, destroyCandidatesCount + ld h, HIGH(board) + ld c, [hl] ; c = target stone variant + _CheckDirectionPair .toTopLeft, .toBottomRight + _CheckDirectionPair .toTop, .toBottom + _CheckDirectionPair .toTopRight, .toBottomLeft + _CheckDirectionPair .toLeft, .toRight +.destroy + write c, r0 + read c, destroyCandidatesCount + ld h, HIGH(ScoreTable) + ld l, c + ld b, [hl] + call AddScore + read c, destroyCandidatesCount + ld hl, destroyCandidates + ld d, HIGH(board) + xor a + BeginLoop destroy, c + ld e, [hl] + ld [de], a + inc l + EndLoop destroy, c + call RecountRemainingSpace + ldh a, [destroyCandidatesCount] + ret + +.toTopLeft + _DecreaseWithBoundCheck, d + _DecreaseWithBoundCheck, e + inc a + ret + +.toBottomRight + _IncreaseWithBoundCheck d, BOARD_WIDTH + _IncreaseWithBoundCheck e, BOARD_HEIGHT + inc a + ret + +.toTop + _DecreaseWithBoundCheck e + inc a + ret + +.toBottom + _IncreaseWithBoundCheck e, BOARD_HEIGHT + inc a + ret + +.toTopRight + _IncreaseWithBoundCheck d, BOARD_WIDTH + _DecreaseWithBoundCheck e + inc a + ret + +.toBottomLeft + _DecreaseWithBoundCheck d + _IncreaseWithBoundCheck e, BOARD_HEIGHT + inc a + ret + +.toLeft + _DecreaseWithBoundCheck d + inc a + ret + +.toRight + _IncreaseWithBoundCheck d, BOARD_WIDTH + inc a + ret + +; [in] b = consecutive stones in a row +; [in] c = target stone variant +; [in] de = coordinate of the origin +; [in] hl = address of the traverse handler (return 0 = outside bound, 1 = inside bound) +; [out] b += stones of the same variant found along the way +; [out] destroyCandidatesCount += b, if b >= 5 +; [out] destroyCandidates +; [corrupt] a +; 12 cycles + 75 cycles per iteration +CheckSingleDirection: + push de ; 4 +.loop + ; bound check + push bc ; 4 + ld bc, .returningPoint ; 3 + push bc ; 4 no need to pop + push hl ; 4 no need to pop + ret ; 4 +? call [hl] +.returningPoint + pop bc ; 3 + cp 0 ; 1 + jr z, .cleanUp ; 3|2 + ; variant check + push hl ; 4 + ld h, HIGH(board) ; 2 + _CompareToTarget ; 10 + pop hl ; 3 + jr nz, .cleanUp ; 3|2 + inc b ; 1 + _AddDestroyCandidates ; 25 + jr .loop ; 3 +.cleanUp + pop de ; 3 + ret ; 4 + +; [in] a, b = address of the traverse handler 1 +; [in] c = target stone variant +; [in] de = coordinate of the origin +; [in] hl = address of the traverse handler 2 +CheckDirectionPair: + push hl + ld h, a + ld l, b + ld b, 1 ; b = consecutive stones in a row + _AddDestroyCandidates + call CheckSingleDirection + pop hl + call CheckSingleDirection + ld a, b + cp 5 + jr nc, .confirmCandidates + _CancelDestroyCandidates +.confirmCandidates + ret + +; [in] b = stone variant +; [in] l = position to be placed +; [corrupt] a, h +PlaceStone: + ld h, HIGH(board) + xor a + cp [hl] + call nz, Exception + ld [hl], b ; place stone + ldh a, [remainingSpace] + dec a + ldh [remainingSpace], a + ret + +; [in] b = score to add +; [corrupt] a, b, c, h, l +AddScore: + ldh a, [scoreLow] + add b + ldh [scoreLow], a + call ToDecimal8 + ld hl, scoreDigits + ld [hl+], a + ld [hl], b +.hasDecimalCarry + ldh a, [scoreLow] + cp 100 + jr c, .noRipple + sub 100 + ldh [scoreLow], a + ld hl, scoreDigits + 2 +.ripple + REPT SCORE_DIGIT_COUNT - 2 + inc [hl] + ld a, [hl] + cp 10 + jr c, .hasDecimalCarry + sub 10 + ld [hl], a + inc hl + ENDR + jr .hasDecimalCarry +.noRipple + ret + +GameOver: + ; TODO + jp InitGame + +VBlankInterrupt: + ei + push af + push bc + push de + push hl + ldh a, [VBlankHandlerEnabled] + or a + jr z, .skip + ldh a, [rLY] + cp SCRN_Y + jr nz, .skip + call StartDMA + call Draw +.skip + ld a, 1 + ldh [VBlankFlag], a + ldh a, [FrameCount] + inc a + ldh [FrameCount], a + pop hl + pop de + pop bc + pop af + reti + +JoypadInterrupt: + push af + SeedRandom + pop af + reti diff --git a/src/test.asm b/src/test.asm new file mode 100644 index 0000000..60253cc --- /dev/null +++ b/src/test.asm @@ -0,0 +1,111 @@ +; Copyright (C) 2020 Dwscdv3 +; +; 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 . + + + +SECTION "UnitTest", ROM0 + +; [in] \1 = dividend +; [in] \2 = divisor +; [in] \3 = expected quotient +; [in] \4 = expected remainder +UnitTest_Divide: MACRO + ld b, \1 + ld c, \2 + call Divide + cp \4 + call nz, Exception + ld a, b + cp \3 + call nz, Exception +ENDM + +UnitTest_AddScore: MACRO +.UnitTest_AddScore_\1_\2 + write \1 % 100000 / 10000, scoreDigits + 4 + write \1 % 10000 / 1000, scoreDigits + 3 + write \1 % 1000 / 100, scoreDigits + 2 + write \1 % 100 / 10, scoreDigits + 1 + write \1 % 10 / 1, scoreDigits + 0 + write \1 % 100, scoreLow + ld b, \2 + call AddScore + ld hl, scoreDigits + 4 + ld a, (\1 + \2) % 100000 / 10000 + cp [hl] + call nz, Exception + ld hl, scoreDigits + 3 + ld a, (\1 + \2) % 10000 / 1000 + cp [hl] + call nz, Exception + ld hl, scoreDigits + 2 + ld a, (\1 + \2) % 1000 / 100 + cp [hl] + call nz, Exception + ld hl, scoreDigits + 1 + ld a, (\1 + \2) % 100 / 10 + cp [hl] + call nz, Exception + ld hl, scoreDigits + 0 + ld a, (\1 + \2) % 10 / 1 + cp [hl] + call nz, Exception + ld hl, scoreLow + ld a, (\1 + \2) % 100 + cp [hl] + call nz, Exception +ENDM + +UnitTest: + UnitTest_Divide 0, 0, 0, 0 + UnitTest_Divide 1, 0, 1, 0 + UnitTest_Divide 128, 1, 128, 0 + UnitTest_Divide 127, 2, 63, 1 + UnitTest_Divide 20, 3, 6, 2 + UnitTest_Divide 255, 85, 3, 0 + UnitTest_Divide 254, 255, 0, 254 + UnitTest_Divide 0, 6, 0, 0 + UnitTest_Divide 255, 84, 3, 3 + UnitTest_AddScore 12138, 10 + UnitTest_AddScore 12138, 108 + UnitTest_AddScore 12138, 156 + + ; Random uniform test +; ld a, 17 +; ldh [rand_seed], a +; ld c, 254 +; xor a +; ld h, HIGH(_RAM) +; ld l, a +; ld d, c +; .random_init_loop +; ld [hl+], a +; dec d +; jr nz, .random_init_loop + +; REPT 16 +; ld b, 255 +; .random_loop\@ +; push bc +; call Random +; pop bc +; ld h, HIGH(_RAM) +; ld l, a +; inc [hl] +; dec b +; jr nz, .random_loop\@ +; ENDR + + ret