+
+
+
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
deleted file mode 100644
index 6a29d3ee..00000000
--- a/CONTRIBUTING.md
+++ /dev/null
@@ -1,32 +0,0 @@
-# Contributing
-
-## Resources page
-
-`website/resources.md` (served at [gbdev.io/resources](https://gbdev.io/resources)) is not committed: it's generated from the [awesome-gbdev](https://github.com/gbdev/awesome-gbdev) README by `yarn build-resources`. The website-only top of the page (front matter, videos, notes) lives in [`scripts/resources/header.md`](scripts/resources/header.md), and everything between the `` and `` markers of the awesome-gbdev README is appended to it.
-
-By default the README is fetched from the master branch of awesome-gbdev. Run this before `yarn dev`, and again whenever you want to pull in changes to the list (requires Node 18 or later):
-
-```bash
-# From the master branch of awesome-gbdev
-yarn build-resources
-
-# From a specific branch, tag or commit
-yarn build-resources 0c457e7833cd22985976dc0eecc859919ae589b0
-
-# From a local copy of the README (e.g. to preview changes to the list md)
-yarn build-resources ../awesome-gbdev/README.md
-```
-
-`yarn build` runs `yarn build-resources` automatically before building. By default (when `RESOURCES_MD_SRC` is not set) the script fetches the README from the master branch of awesome-gbdev on GitHub by itself, so deployed builds always use the current list. To build from another source instead, set the `RESOURCES_MD_SRC` environment variable to any of the values accepted by `yarn build-resources` (a branch, tag, commit or local path):
-
-```bash
-# Fetches the README from awesome-gbdev master
-yarn build
-
-# Uses a local copy of the README instead
-RESOURCES_MD_SRC=../awesome-gbdev/README.md yarn build
-```
-
-## CI
-
-The website is rebuilt and deployed on every push to `dev`, and also whenever the awesome-gbdev README changes: awesome-gbdev sends an `awesome-gbdev-updated` [`repository_dispatch`](https://docs.github.com/en/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows#repository_dispatch) event to this repository, which runs the deploy workflow. To do so, awesome-gbdev holds a fine-grained token with access to this repository (Contents: read and write): if it expires or is revoked, the resources page stops updating until the next push here. How the event is sent is documented in the [awesome-gbdev CONTRIBUTING.md](https://github.com/gbdev/awesome-gbdev/blob/master/CONTRIBUTING.md). A deploy may also be started manually from the Actions tab ("Run workflow").
diff --git a/LICENSE b/LICENSE
deleted file mode 100644
index 9b636532..00000000
--- a/LICENSE
+++ /dev/null
@@ -1,676 +0,0 @@
-(C) 2015-2018 Antonio Vivace and gbdev contributors.
-
- 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
deleted file mode 100644
index a451a804..00000000
--- a/README.md
+++ /dev/null
@@ -1,21 +0,0 @@
-# gbdev website
-
-This is the source repository of main website of the Game Boy development iniative, hosted at [gbdev.io](https://gbdev.io). Powered by Vuepress 2.x
-
-You need Node (> 14.x) and yarn.
-
-```bash
-# Install dependencies
-yarn
-
-# Generate the resources page
-yarn build-resources
-
-# Start a local hot-reloading development server at http://localhost:8080
-yarn dev
-
-# Build website (also generates the resources page)
-yarn build
-```
-
-See [CONTRIBUTING.md](CONTRIBUTING.md) to learn how to contribute to the website, including how the resources page is generated.
diff --git a/aipolicy.html b/aipolicy.html
new file mode 100644
index 00000000..256b8acf
--- /dev/null
+++ b/aipolicy.html
@@ -0,0 +1,55 @@
+
+
+
+
+
+
+
+
+ AI Coding Assistants | gbdev.io
+
+
+
+
+
+
This document provides guidance for AI tools and developers using AI assistance when contributing to gbdev (github.com/gbdev) projects.
This is the generic, gbdev-wide AI policy. Individual projects under the gbdev umbrella may define their own policy, which takes precedence over this document; when present, such project-specific policies can be found in the repository's CONTRIBUTING.md (except for the attribution/disclosure requirement below, which is mandatory and cannot be overridden).
Policy
LLM-assisted contributions are accepted but discouraged, provided they are disclosed as described in Attribution.
Some projects may not agree with this generic policy at all, and may reject AI-assisted contributions entirely. Even where a project's policy allows AI-assisted contributions, an individual maintainer may still decline to review a specific PR on that basis, at their discretion.
gbdev as a community does not endorse or recommend the use of generative AI assistants for software development, as it raises multiple concerns about ethics, legality, copyright, etc.
Nevertheless, gbdev acknowledges that these practices are already in use and likely here to stay. Rather than banning their use, the project chooses to place responsibility on contributors and therefore defines the following guidelines:
Contributions to gbdev projects are welcome, but should be your own work. Anything you submit is your responsibility, even if you used AI or other tool assistance to help create it. You must explain which tools you used and what they did (like citing sources for written work). You must have sufficiently evaluated and properly understood the work you intend to submit, and be able to explain and defend it.
Contributors MUST NOT submit "unattended" LLM-generated contributions - that is, prompt-generated contributions that received no further human vetting, and more broadly any submission produced with little to no meaningful human review, understanding, or involvement - unless the project has an area explicitly designated for such contributions. If no such area exists, unattended contributions are forbidden.
Contributors are expected to follow the standard gbdev contribution process and community guidelines, as well as any project-specific guidelines or rules where applicable.
gbdev is more than just code: it is an open source community built on a shared interest in Game Boy development, and that community grows largely through mentorship. Education and guidance of new contributors are part of our mission. Maintainers spend their limited time reviewing new contributors' work not only to get a given change merged, but to help those contributors become effective, self-sufficient contributors in their own right (see "Contributor Poker").
Heavily AI-assisted contributions undermine this. A new contributor submitting largely LLM-generated output places an outsized review burden on maintainers, who must scrutinize code the contributor may not fully understand, which can contribute to reviewer burnout. Just as importantly, a contributor who leans on an LLM instead of working through a project's internals does not build the understanding needed to become an effective contributor, so the time invested in reviewing and mentoring them produces a return for the project that is not only less lasting, but potentially damaging.
A Living Document
This policy reflects our current thinking and is not final. AI tooling, community norms, and the legal landscape around generative AI are all still shifting, and we may revise these guidelines as new facts, tools, and experiences warrant. An open RFC can be found here.
All contributions MUST comply with the licensing terms of the project you are contributing to. Verify that any AI-generated content does not introduce incompatible or unclear licensing. Contributors are responsible for ensuring the contribution is clean.
When AI tools have meaningfully assisted in producing a contribution, it MUST be disclosed with an Assisted-by tag in the git commit message, in the following format:
Written by avivace, on behalf of the gbdev org. Originally published on "getrevue" on the 21st of February, 2022.
Hey all and welcome to the first Issue of the new gbdev curated digest! Our idea is to prepare a collection of curated news about what is happening in our community, with a particular focus on our Open Source projects. Whatever you are interested in what’s happening the gbdev scene, new homebrew, tools, and documentation releases, find stuff where you can contribute with code or provide feedback, this newsletter is for you!
The following is a quick clarification since this is the first issue of the Gbdev Digest. Feel free to shamelessly skip it.
This newsletter is curated by the team behind the gbdev.io community, with a lot of help from members of other smaller communities and groups. We are directly involved with the development and maintaining of a lot of projects (e.g. Pan Docs, RGBDS, and Homebrew Hub), but of course not with everything that will be mentioned in this newsletter.
The gbdev initiative promotes the development of tools, documentation and homebrew games. We also push research and emulation efforts while trying to keep everything as accessible as possible to everyone, no matter the technical background. Another big commitment is the archiving one: we are working on salvaging old and unavailable content, creating a digital memory of a scene that is now three decades old.
Since our inception in 2015, open source and free software values are at the core of our commitment. We release everything we work on under OSS licenses (or in the Public Domain), maintaining a strong non-profit approach. Even if we reshare and give exposure to commercial products related to gbdev, we don’t get any cash from any of the involved companies and we don’t sponsor or endorse any particular release or publisher. No referral links are ever shared through our channels.
Our expenses are entirely sustained by voluntary contributions (for which donators don’t get any benefit) and sponsorships (such as the DigitalOcean one). Everything is tracked trasparently on our OpenCollective.
While a core team of maintainers is behind every project, we welcome any type of contribution, from feedback to participation in Request For Comments threads. Check the bottom of this page to learn how to join our community.
2021 was a huge success for gbdev. The Game Boy scene was never this alive and flourishing, with an unprecedented number of releases. From the incredible gbcompo21 (for which we are preparing a dedicated issue of this newsletter) to the number of new releases of our projects, we enjoyed the most traffic we ever saw, for the fourth year in a row.
We finished revamping a quite comprehensive guide on how to choose tools for Game Boy, giving an overview on the different available approaches to developing homebrews for the grey brick.
Homebrew Hub is a community-led attempt to collect, archive and preserve every unofficial game and homebrew released for Game Boy produced through decades of passionate work. Every entry , with its metadata and related assets is exposed through an API available to everyone.
We started a complete rewrite of the backend and the frontend software behind Homebrew Hub. Originally built with Express in 2016 the codebase started to show its age… The new backend, powered by Django, will offer a full Restful API to access all the games, assets, metadata and ROMs we have in the JSON database. The new frontend is powered by the binjgb emulator, running in the browser via WebAssembly. This will bring better emulation accuracy and more features!
The Homebrew Hub database is community maintained. Everyone can contribute adding a game or writing a scraper to mass-import games on the website.
In parallel to the rewrite, a lot of stuff is happening on the database, too:
New CI scripts are now in place to detect potentially duplicated entries, calculating checksums of assets. The pipeline will now run on Pull Requests and also validate the submitted metadata and the referenced files, to aid contributors.
The following new entries have been added: Crystal Lake, Grub Glide, Labirinth, Bannerprint and Europa Rescue (thanks to N•I•L, v666, reini1305, Exetric and godai78).
All the 20 shortlisted entries from the GB Competition 2021 were also added and are ready to play.
dag7dev worked on new scrapers, cleaned up a bunch of entries metadata and merged some duplicates. Hundreds of new homebrews are now available thanks to his work!
A new game metadata schema is in preparation, enabling game translations and specific tags for hardware support (e.g. gb-printer, gb-camera, ..).
The total number of games is now 748! Go play with them!
RGBDS is the standard toolchain for developing homebrew Game Boy programs in Assembly. First released in 1997, it’s now enjoying renewed community attention.
A new version of the RGBDS website, providing downloads, build instructions and full documentation is now work in progress.
From a statically built HTML version we are migrating to a solution based on Docusaurus, a documentation framework powered by React.
The rgbgfx program, part of the RGBDS suite, converts PNG images into the Nintendo Game Boy’s planar tile format.
A new version is being rewritten in modern C++17, enabling a series of improvements such as better error messages (more explicit), proper transparency support, explicit palette specification and proper 8×16 support.
GBDK is a C compiler, assembler, linker and set of libraries for the Z80 like Nintendo Gameboy.
Our friends over at the GBDK team worked on a big release, the first adding more targets to the toolchain: GBDK can now compile to Sega Master System/Sega Game Gear and the Analogue Pocket.
4.0.6 shipped support for the Mega Duck, too!
Other notable features added in 2021 were:
Metasprite API and conversion tool
Auto ROM bank assignment and packing
Compression API and console tool
More information can be found in the release notes.
For a long time now the main GBDK tutorials online have been mostly outdated and based on the 20 year old version GBDK but this is now changing: Larold is working on a series of tutorials where they break down the Game Boy game creation process into multiple high-level steps.
GB Studio is a quick and easy to use drag and drop retro game creator for the Game Boy. No programming required. Here are some of the highlights from the new release:
Thanks for reading! That’s all for this issue. We hope you’re having fun developing or playing games for Game Boy. As always, your contributions to our initiatives are appreciated. You’re welcome to browse around our GitHub organisation or join our Discord server to see what everyone is working on.
If you still want more gbdev, our Twitter feed provides a curated (and frequent) collection of new releases, WIP content and news about our projects.
If you have any feedback, want to send us some gbdev links or comment on any of the topics brought up in this issue (or on the newsletter itself) feel free to reach us.
This digest won’t try to respect an actual schedule but will rather go online when enough quality content is ready. On the next issue, we will continue to shed some light on the incredible accomplishments we made in 2021, such as the GB Competition 2021, showing you how some games enjoyed continued support and physical releases. We will also tell you how we managed to make Pan Docs this good and what’s new in the latest RGBDS releases.
If you’d like to send us some gbdev content you found interesting and you think would fit this newsletter, feel free to reach out.
Special thanks to bbbbbr, ISSOtm, toxa and Emi Paternostro.
`,58)]])}var s=r(a,[[`render`,o]]);export{i as _pageData,s as default};
\ No newline at end of file
diff --git a/assets/2023-11-04-sc-CRZP3gUK.js b/assets/2023-11-04-sc-CRZP3gUK.js
new file mode 100644
index 00000000..04f38264
--- /dev/null
+++ b/assets/2023-11-04-sc-CRZP3gUK.js
@@ -0,0 +1 @@
+import{a as e,d as t,i as n,t as r}from"./app-oZCJRxfU.js";var i=JSON.parse(`{"path":"/meetings/2023-11-04-sc.html","title":"Steering Committee - November 2023","lang":"en-US","frontmatter":{"sidebar":false},"git":{"contributors":[{"name":"Antonio Vivace","username":"","email":"avivace4@gmail.com","commits":1}],"changelog":[{"hash":"a3a0422fa3b8e16a67dc8a661e1fa6eae5cb0fa8","time":1792000514000,"email":"avivace4@gmail.com","author":"Antonio Vivace","message":"ci, docs: rebuild on awesome-gbdev changes, add CONTRIBUTING.md (#8)"}]},"filePathRelative":"meetings/2023-11-04-sc.md"}`),a={name:`2023-11-04-sc.md`};function o(r,i,a,o,s,c){return t(),n(`div`,null,[...i[0]||=[e(`
Participants were asked to fill in discussion points beforehand. The results of these discussions (decisions, suggestions, comments, etc.) are reported as subitems.
Discussion points marked * were not brought up due to lack of time.
Anyone can reach out in #meta on the Discord server to comment/discuss on any of the points reported here.
Scope: Full port from the old codebase, can’t be 1-to-1
Architecture design in progress
Motivation: C not attractive anymore for potential contributors. Showing its age. Technicalities (std::vector). Rust is fancier, more active community.
(Personal) De-risking/Viability study
Question from Pino: will this affect availability of binaries?
Homebrew Hub: developments and expanding to NES *
Gbcompo23: [...] *
Funding: how to? *
Creating the steering committee: Roles? *
Q to Sanqui: how to run these meetings? Minutes *
ISSOtm:
Difficulties finding contributors
Pan Docs, RGBDS, ASM Tutorial are lacking
AV: Homebrew Hub is OK
Quality barrier too high? Is our PR review process too much?
Possible (partial) solution = avoid nitpicking
Interesting actionable by DUO: give reviews in “steps”, avoid attacking a PR with everything all at once.
If making follow-up changes separately, be upfront about that in the initial PR to avoid the contributor feeling “the rug pulled under their feet”
Put a hard cap on the PR’s “TTL” before being merged (e.g. 3 weeks), and avoid minor changes as the deadline looms closer
How to handle contributors going silent?
“Better merge something incomplete to beg correction work, rather than drop everything done thus far”
Promoting a “Bug Day”?
Moving projects to SourceHut? *
Sanqui:
Overview of Wikidata and the possibility of contributing Homebrew Hub information to Wikidata
Add Ext ID in the game metadata schema and point it to Wikidata entry. Possibly populate others.
HHub - cartridges/published games/publishers
DUO:
Remarks and compliments on the great state of gbdev.
Promote PRs that need to be worked on, but also promote work that has been done “for cred” - Start using #feedback again.
nitro2k01:
Potentially add a long-term solution for the IRC bridge as a point/responsibility.
Someone other than @avivace needs to volunteer (=> @nitro2k01) and get SSH access and/or a webhook token to delegate the responsibility of handling the bridge
Future of the forums *
Kva64:
Social media handling — general outline and feedback request
Not only game releases/showcase content can be shared, but projects themselves may have cool milestones to share
Allow developers, maintainers, etc. to raise up things that can be promoted, so people helping with social media don’t have to “chase” those. Should those go in #showcase? Should #showcase be split?
Encourage (PSA?) developers to post in #showcase Move all showcase channels to a separate category, and rename “showcase” to “wip-showcase”? Split #releases into e.g. #releases-games and #releases-tooling
To discuss in #meta: opt-in/opt-out status of showcase channels. A suggestion: make sharing opt-out in #releases-*, and opt-in in #wip-showcase
Policy on sharing and promoting commercial projects?
DUO: do not shy away from promoting them because it’s a motivation to some; however, restrictions should be put to avoid being taken advantage of
Should the cooldown (if any) apply per project, per user...?
Need for using any newly-emerging social media platforms (e.g. Threads and Bluesky)?
Threads and Bluesky currently have very little reach, so maybe not
Other platforms (e.g. Instagram): maybe, but this may require more people to handle them
Creating a channel to coordinate them
Sylvie:
Q to ISSO: Continue improving existing C++ while Rust progresses?
C to C++ in parallel
Talk separately (and later) about whether we want to upstream the port, and if yes, the roadmap (responsibility & task distribution, etc.)
`,13)]])}var s=r(a,[[`render`,o]]);export{i as _pageData,s as default};
\ No newline at end of file
diff --git a/assets/2024-03-10-sc2-DRyFlweO.js b/assets/2024-03-10-sc2-DRyFlweO.js
new file mode 100644
index 00000000..1cfbf798
--- /dev/null
+++ b/assets/2024-03-10-sc2-DRyFlweO.js
@@ -0,0 +1 @@
+import{a as e,d as t,i as n,t as r}from"./app-oZCJRxfU.js";var i=JSON.parse(`{"path":"/meetings/2024-03-10-sc2.html","title":"Steering Committee - March 2024","lang":"en-US","frontmatter":{"sidebar":false},"git":{"contributors":[{"name":"Antonio Vivace","username":"","email":"avivace4@gmail.com","commits":1}],"changelog":[{"hash":"a3a0422fa3b8e16a67dc8a661e1fa6eae5cb0fa8","time":1792000514000,"email":"avivace4@gmail.com","author":"Antonio Vivace","message":"ci, docs: rebuild on awesome-gbdev changes, add CONTRIBUTING.md (#8)"}]},"filePathRelative":"meetings/2024-03-10-sc2.md"}`),a={name:`2024-03-10-sc2.md`};function o(r,i,a,o,s,c){return t(),n(`div`,null,[...i[0]||=[e(`
Finding some time to write down the “vision” Isso has for the Rust port (what needs to be done, how, etc.)
Prioritization, among and within…
gb-asm-tutorial (1)
Pan Docs (2)
rsgbds (RGBDS on “life support” with Sylvie maintaining) (last)
Next meeting:
Moving projects to SourceHut?
avivace
FOSDEM feedback
Fosdem25. Who wants to join and come in the “gbdev” delegation?
Isso wants to give a talk
RGBDS
“Breaking changes and how to live with them”
Working from a codebase from the 90’s
Tutorial?
kva64
Promoting usage of public domain/creative commons/”use as long as you give credit” assets among community members (open related issue on github)
How do deal with “drama” against users/companies/etc we want to shout out: consult with outreach team + use best judgment
`,22)]])}var s=r(a,[[`render`,o]]);export{i as _pageData,s as default};
\ No newline at end of file
diff --git a/assets/404.html-5z1RNE_k.js b/assets/404.html-5z1RNE_k.js
new file mode 100644
index 00000000..e1d5185b
--- /dev/null
+++ b/assets/404.html-5z1RNE_k.js
@@ -0,0 +1 @@
+import{d as e,i as t,n,t as r}from"./app-oZCJRxfU.js";var i=JSON.parse(`{"path":"/404.html","title":"","lang":"en-US","frontmatter":{"layout":"NotFound"},"git":{},"filePathRelative":null}`),a={name:`404.html.vue`};function o(r,i,a,o,s,c){return e(),t(`div`,null,[...i[0]||=[n(`p`,null,`404 Not Found`,-1)]])}var s=r(a,[[`render`,o]]);export{i as _pageData,s as default};
\ No newline at end of file
diff --git a/website/.vuepress/public/images/Incube8-Games-Logo.png b/assets/Incube8-Games-Logo-Cl6LMdxo.png
similarity index 100%
rename from website/.vuepress/public/images/Incube8-Games-Logo.png
rename to assets/Incube8-Games-Logo-Cl6LMdxo.png
diff --git a/assets/README-BW-IoVw9.js b/assets/README-BW-IoVw9.js
new file mode 100644
index 00000000..355a0313
--- /dev/null
+++ b/assets/README-BW-IoVw9.js
@@ -0,0 +1 @@
+import{a as e,d as t,f as n,h as r,i,n as a,o,s,t as c}from"./app-oZCJRxfU.js";var l={mounted(){let e=document.createElement(`script`);e.setAttribute(`src`,`https://buttons.github.io/buttons.js`),document.head.appendChild(e);let t=[`#5f98c6`,`#FCCF37`,`#EB1667`,`#A8F602`,`rgba(255, 255, 255, 0.5)`,`rgba(91, 48, 153, 0.5)`],n=t[Math.floor(Math.random()*t.length)];document.querySelector(`#box`).style.setProperty(`background-color`,n),console.log(n)}},u=JSON.parse(`{"path":"/","title":"Projects","lang":"en-US","frontmatter":{"sidebar":false,"head":[["meta",{"name":"og:title","content":"gbdev.io homepage"}],["meta",{"name":"og:description","content":"Home of the Game Boy and Game Boy Advance homebrew development scene. We are working on (open) development tools, documentation and digital preservation.\\n"}]],"editLink":false},"git":{"contributors":[{"name":"Antonio Vivace","username":"","email":"avivace4@gmail.com","commits":1}],"changelog":[{"hash":"a3a0422fa3b8e16a67dc8a661e1fa6eae5cb0fa8","time":1792000514000,"email":"avivace4@gmail.com","author":"Antonio Vivace","message":"ci, docs: rebuild on awesome-gbdev changes, add CONTRIBUTING.md (#8)"}]},"filePathRelative":"README.md"}`);function d(c,l,u,d,f,p){let m=n(`smalL`);return t(),i(`div`,null,[l[1]||=e(`
Game Boy Development community
We are a non for profit collective of passionate developers and hackers working on development tools, homebrew games, emulators, preservation and documentation for the Nintendo Game Boy handheld console, the original gray brick from 1989.
Curated list of Game Boy development resources such as tools, guides, technical documentation, tutorials, emulators, related projects and open-source ROMs. Everything you'll ever need to know and see about this console is here. If you want to code an emulator, create your own game or simply dive into the software and hardware architecture of the Game Boy, this is the place!
An automated test comparison project for Game Boy emulators. It runs a suite of accuracy test ROMs across a set of Game Boy emulators and publishes the pass/fail results as a comprehensive table, with screenshots for each test case.
A (work in progress) tutorial on how to program for the Game Boy in assembly, touching on every aspect required to make Game Boy games, via a Hello World, constructing an Arkanoid clone, and capping off by making a playable Shoot-'Em-Up.
Maintained and modernized GBDK, the Game Boy Development Kit. Now powered by an updated version of the SDCC toolchain, provides a C compiler, assembler, linker and a set of libraries.
The places where our community thrives. Here we chat, discuss, help each other and show what we are working on. We have a Discord server, an IRC channel, and more.
Play Game Boy games online from an archive of hundreds of entries! A community-led attempt to collect, archive and save every unofficial game, homebrew, demo, patch, hackrom for Game Boy produced by the community through the last 3 decades of passionate work.
Digital library of Game Boy related software, hardware and literature. Aimed to mirror and preserve old and fragmented contributions in the scene from the last three decades.
Released in 1998 as a Nintendo accessory for the original handheld console, the Game Boy Camera became an instant hit with kids. Despite technological advances, its capturing sub-megapixel charm endures, capturing the imagination of photographers who embrace its unique, low-fi aesthetic.
Events
We host coding competitions in which anyone can partecipate by creating original games, demos, homebrews tools and music for the Game Boy and compete for glory and prizes.
\xA0\xA0\xA0\xA0
`,7),s(m,null,{default:r(()=>[...l[0]||=[a(`p`,null,[o(`For Game Boy `),a(`em`,null,`Advance`),o(` development, check `),a(`a`,{href:`https://gbadev.net`,target:`_blank`,rel:`noopener noreferrer`},`gbadev.net`),o(`.`)],-1)]]),_:1})])}var f=c(l,[[`render`,d]]);export{u as _pageData,f as default};
\ No newline at end of file
diff --git a/assets/aipolicy-gQogIlCK.js b/assets/aipolicy-gQogIlCK.js
new file mode 100644
index 00000000..d0dde932
--- /dev/null
+++ b/assets/aipolicy-gQogIlCK.js
@@ -0,0 +1 @@
+import{a as e,d as t,i as n,t as r}from"./app-oZCJRxfU.js";var i=JSON.parse(`{"path":"/aipolicy.html","title":"AI Coding Assistants","lang":"en-US","frontmatter":{},"git":{"contributors":[{"name":"Antonio Vivace","username":"","email":"avivace4@gmail.com","commits":1}],"changelog":[{"hash":"a3a0422fa3b8e16a67dc8a661e1fa6eae5cb0fa8","time":1792000514000,"email":"avivace4@gmail.com","author":"Antonio Vivace","message":"ci, docs: rebuild on awesome-gbdev changes, add CONTRIBUTING.md (#8)"}]},"filePathRelative":"aipolicy.md"}`),a={name:`aipolicy.md`};function o(r,i,a,o,s,c){return t(),n(`div`,null,[...i[0]||=[e(`
This document provides guidance for AI tools and developers using AI assistance when contributing to gbdev (github.com/gbdev) projects.
This is the generic, gbdev-wide AI policy. Individual projects under the gbdev umbrella may define their own policy, which takes precedence over this document; when present, such project-specific policies can be found in the repository's CONTRIBUTING.md (except for the attribution/disclosure requirement below, which is mandatory and cannot be overridden).
Policy
LLM-assisted contributions are accepted but discouraged, provided they are disclosed as described in Attribution.
Some projects may not agree with this generic policy at all, and may reject AI-assisted contributions entirely. Even where a project's policy allows AI-assisted contributions, an individual maintainer may still decline to review a specific PR on that basis, at their discretion.
gbdev as a community does not endorse or recommend the use of generative AI assistants for software development, as it raises multiple concerns about ethics, legality, copyright, etc.
Nevertheless, gbdev acknowledges that these practices are already in use and likely here to stay. Rather than banning their use, the project chooses to place responsibility on contributors and therefore defines the following guidelines:
Contributions to gbdev projects are welcome, but should be your own work. Anything you submit is your responsibility, even if you used AI or other tool assistance to help create it. You must explain which tools you used and what they did (like citing sources for written work). You must have sufficiently evaluated and properly understood the work you intend to submit, and be able to explain and defend it.
Contributors MUST NOT submit "unattended" LLM-generated contributions - that is, prompt-generated contributions that received no further human vetting, and more broadly any submission produced with little to no meaningful human review, understanding, or involvement - unless the project has an area explicitly designated for such contributions. If no such area exists, unattended contributions are forbidden.
Contributors are expected to follow the standard gbdev contribution process and community guidelines, as well as any project-specific guidelines or rules where applicable.
gbdev is more than just code: it is an open source community built on a shared interest in Game Boy development, and that community grows largely through mentorship. Education and guidance of new contributors are part of our mission. Maintainers spend their limited time reviewing new contributors' work not only to get a given change merged, but to help those contributors become effective, self-sufficient contributors in their own right (see "Contributor Poker").
Heavily AI-assisted contributions undermine this. A new contributor submitting largely LLM-generated output places an outsized review burden on maintainers, who must scrutinize code the contributor may not fully understand, which can contribute to reviewer burnout. Just as importantly, a contributor who leans on an LLM instead of working through a project's internals does not build the understanding needed to become an effective contributor, so the time invested in reviewing and mentoring them produces a return for the project that is not only less lasting, but potentially damaging.
A Living Document
This policy reflects our current thinking and is not final. AI tooling, community norms, and the legal landscape around generative AI are all still shifting, and we may revise these guidelines as new facts, tools, and experiences warrant. An open RFC can be found here.
All contributions MUST comply with the licensing terms of the project you are contributing to. Verify that any AI-generated content does not introduce incompatible or unclear licensing. Contributors are responsible for ensuring the contribution is clean.
When AI tools have meaningfully assisted in producing a contribution, it MUST be disclosed with an Assisted-by tag in the git commit message, in the following format:
This style guide aims to formalize a style that most Game Boy ASM programmers agree on, and provide a good baseline for new programmers just starting in this field. (If that's you, welcome! 😄)
Coding style is very personal, and I won't force my views on anybody, but this is what goes for anything that I have to be able to maintain, and I'd prefer it for most other things too. Please at least consider the points made here.
Many people follow alternate style guides, and that's fine; but if you're starting to code in ASM, a clean style goes a long way to keep your code organized. Again: you don't have to do everything listed here, but please at least consider the reasons behind each bullet point.
Symbol names can contain letters, numbers, underscores ‘_’, hashes ‘#’ and at signs ‘@’. However, they must begin with either a letter, or an underscore.
However, naming conventions make code easier to read, since they help convey the different semantics between each symbol's name.
Labels use PascalCase: DrawNPCs, GetOffsetFromCamera.
Labels in RAM (VRAM, SRAM, WRAM, HRAM; you shouldn't be using Echo RAM or OAM) use the same convention but are prefixed with the initial of the RAM they're in, in lowercase: wCameraOffsetBuffer, hVBlankFlag, vTilesetTiles, sSaveFileChecksum. Rationale: to know in which memory type the label is; this is important because VRAM and SRAM have special access precautions and HRAM can (should (must)) be accessed using the ldh instruction.
Local labels use camelCase, regardless of memory type: .waitVRAM, wPlayer.xCoord.
Macro names use snake_case: wait_vram, end_struct.
Constants use CAPS_SNAKE: NB_NPCS, OVERWORLD_STATE_LOAD_MAP.
Exception: constants that are used like labels should follow the label naming conventions. For example, see hardware.inc's rXXX constants.
No magic numbers. ld a, CURSOR_SPEED is much more obvious than ld a, 5. In addition, if you ever change your mind and decide to change the cursor speed, you will only need to do so in one location (CURSOR_SPEED equ 5 → CURSOR_SPEED equ 4) instead of at every location you're using it, potentially missing some.
Unless absolutely necessary, don't force a SECTION's bank or address. This puts the burden of managing ROM space on you, instead of offloading the job to RGBLINK, which performs very well in typical cases. Exceptions:
Your ROM's entry point must be at $0100, however the jump does not have to be to $0150 (example).
RGBDS presently does not allow forcing different sections to be in the same bank. If you need to do so, the ideal fix is to merge the two sections together (either by moving the code, or using SECTION FRAGMENT), but if that option is unavailable, the only alternative is to explicitly declare them with the same BANK[] attribute. (In which case it's advisable to add an assert BANK("Section A") == BANK("Section B") line.)
If you need some alignment, prefer ALIGN[] to forcing the address. A typical example is OAM DMA; for that, prefer SECTION "Shadow OAM", WRAM0,ALIGN[8] over e.g. SECTION "Shadow OAM", WRAM0[$C000].
Allocate space for your variables using labels + ds & co instead of equ. This has several benefits:
Removing, adding, or changing the size of a variable that isn't the last one doesn't require updating every variable after it.
The size of each variable is obvious (ds 4) instead of having to be calculated from the addresses.
equ allocation is equivalent to hardcoding section addresses (see above), whereas labels are placed automatically by RGBLINK.
Labels support BANK() and many cool other features!
If a file gets too big, you should split it. Files too large are harder to read and navigate. However, the splitting should stay coherent and consistent; having to jump around files constantly is equally as hard to read and navigate.
Unless you're making a 32k ROM, put things in ROMX by default. ROM0 space is precious, and can deplete quickly; and when you run out, it's difficult to move things to ROMX.
However, if you have code in ROM bank A refer to code or data in ROM bank B, then either should probably be moved to ROM0, or both be placed in the same bank (options for that are mentioned further above). farcall is a good way to make your code really spaghetti.
Don't clear RAM at init! Good debugging emulators will warn you when you're reading uninitialized RAM (BGB has one in the option's Exceptions tab, for example), which will let you know that you forgot to initialize a variable. Clearing RAM does not fix most of these bugs, but silences the helpful warnings.
Also, a lot of the time, variables need to get initialized to values other than 0, so clearing RAM is actually counter-productive in these cases.
The difference between these and the "best practices" above is that these are more subjective, but they're still worth talking about here.
Historically, RGBDS has required label definitions to begin at "column 1" (i.e. no whitespace before them on their line). However, later versions (with full support added in 0.5.0) allow indenting labels, for example to make loops stand out like in higher-level languages. However, a lot of people don't do this, so it's up to you.
Please use the .asm (or .s) file extensions, not .z80. The GB CPU isn't a Z80, so syntax highlighters get it mostly right, but not quite. And it helps spreading the false idea that the GB CPU is a Z80. 😢
Compressing data is useful for several reasons; however, it's not necessary in a lot of cases nowadays, so you may want to only look at it after more high-priority aspects.
Avoid abusing macros. Macros tend to make code opaque and hard to read for people trying to help you, in addition to having side effects and sometimes leading to very inefficient code.
Never let the hardware draw a corrupted frame even if it's just one frame. If it's noticeable by squinting a bit, it must go.
Makefiles are bae; they speed up build time by not re-processing what hasn't changed, and they can automate a lot of tedium. Writing a good Makefile can be quite daunting, but gb-boilerplate and gb-starter-kit can help you get started faster.
`,18)]])}var s=r(a,[[`render`,o]]);export{i as _pageData,s as default};
\ No newline at end of file
diff --git a/assets/chat-DTmLSgtO.js b/assets/chat-DTmLSgtO.js
new file mode 100644
index 00000000..457629b4
--- /dev/null
+++ b/assets/chat-DTmLSgtO.js
@@ -0,0 +1 @@
+import{a as e,d as t,i as n,t as r}from"./app-oZCJRxfU.js";var i=JSON.parse(`{"path":"/chat.html","title":"Community channels","lang":"en-US","frontmatter":{"sidebar":false,"head":[["meta",{"name":"og:title","content":"gbdev chat channels"}],["meta",{"name":"og:description","content":"Join gbdev.io community channels"}]]},"git":{"contributors":[{"name":"Antonio Vivace","username":"","email":"avivace4@gmail.com","commits":1}],"changelog":[{"hash":"a3a0422fa3b8e16a67dc8a661e1fa6eae5cb0fa8","time":1792000514000,"email":"avivace4@gmail.com","author":"Antonio Vivace","message":"ci, docs: rebuild on awesome-gbdev changes, add CONTRIBUTING.md (#8)"}]},"filePathRelative":"chat.md"}`),a={name:`chat.md`};function o(r,i,a,o,s,c){return t(),n(`div`,null,[...i[0]||=[e(`
#gbdev-ot, #gbdev-casual, #gbdev-hw, #gbdev-meta, #gbdev-asm, #gbdev-gfx, #gbdev-emudev, #gbdev-romhacking and #gbdev-research are also available for discussion about specific topics.
`,3)]])}var s=r(a,[[`render`,o]]);export{i as _pageData,s as default};
\ No newline at end of file
diff --git a/assets/contributing-CvRjsUrf.js b/assets/contributing-CvRjsUrf.js
new file mode 100644
index 00000000..8d8240c6
--- /dev/null
+++ b/assets/contributing-CvRjsUrf.js
@@ -0,0 +1 @@
+import{a as e,d as t,f as n,h as r,i,n as a,o,s,t as c}from"./app-oZCJRxfU.js";var l=JSON.parse(`{"path":"/contributing.html","title":"Contribution guidelines","lang":"en-US","frontmatter":{},"git":{"contributors":[{"name":"Antonio Vivace","username":"","email":"avivace4@gmail.com","commits":1}],"changelog":[{"hash":"a3a0422fa3b8e16a67dc8a661e1fa6eae5cb0fa8","time":1792000514000,"email":"avivace4@gmail.com","author":"Antonio Vivace","message":"ci, docs: rebuild on awesome-gbdev changes, add CONTRIBUTING.md (#8)"}]},"filePathRelative":"contributing.md"}`),u={name:`contributing.md`};function d(c,l,u,d,f,p){let m=n(`RouteLink`);return t(),i(`div`,null,[l[3]||=e(`
These base guidelines (if not specified otherwise) apply to every repository under the gbdev github organisation. The specific CONTRIBUTING rules of the repository in object also apply. In case of conflicts, those last ones have the priority.
Reaching out to maintainers
If you are unsure about anything or need guidance, you can always reach out the maintainers on Discord or IRC.
You're allowed to use AI/LLM tools to help with your contribution, but you must disclose it and meet certain expectations around reviewing and understanding the work you submit.
Unattended AI-driven interactions and contributions are not allowed, unless the project has an area explicitly designated for such contributions.
`,6),a(`p`,null,[l[1]||=o(`See the full `,-1),s(m,{to:`/aipolicy.html`},{default:r(()=>[...l[0]||=[o(`AI Coding Assistants policy`,-1)]]),_:1}),l[2]||=o(`.`,-1)]),l[4]||=e(`
Provide an example to reproduce the problem in isolation. This should include no private code. The example should be self-runnable. If a snippet is too small, create a repository.
Unless the problem is visual, do not include screenshots. They are not helpful.
Please be patient. You are probably frustrated by your problem - the maintainers are likely not aware of your workflow.
Be grateful for any help you are getting and follow through. The maintainers are likely giving you support for free.
Informal conversations are great (e.g. on Discord), but once a problem, feature request etc is outlined, opening an issue formalizing it is the most important thing you can do to help this information flow.
Open an issue in the related repository. If you don't know where it's better suited, ask for help.
Include context and any information useful to understand the situation. Don't be scared about doing something wrong or not good enough, maintainers will follow up if needed.
Keep titles and commit messages brief and concise.
If the PR is not self-explanatory, add brief description of what has been done and implementation choices (if any).
Keep the work in the branch focused and directed at accomplishing only what you are describing in the PR. Ideally a PR should address a single issue (or a set of assimilable ones).
PRs require commitment and they are an investment by the (volunteer) maintainers. If you don't have time or abandon the PR after reviews have been left and additional changes are needed, other maintainers may take over to make sure the PR can move forward and the work is not lost.
Maintainers may chime in and commit to your feature branch on minor fixes (or suggestions agreed upon).
Don't be scared of opening more PRs for the same task, if they can be logically divided. The smaller the changes, the faster they can get reviewed and eventually merged.
When adding new features, think about tests and possibly updating documentation. Think about other parts of the project which may be affected by the change.
Document trade-offs, risks, implementation and design choices, if any.
Tests and pipelines must pass. If they failed because of something unrelated to the PR (e.g. a link died in the meantime), mention it in the PR thread.
If the solution is "sub-optimal" (but still better than the actual implementation) and there are clear possible improvements, mention them and try to collaborate with the contributor, but ultimately prefer merging something sub-optimal than keeping the content stale/inactive. An issue can be opened (or kept opened) to report this situation.
If there's an ongoing conversation/discussion on a PR/thread, let it continue. Don't abruptly merge unless there's a clear consensus among the core maintainers (and approvals).
Respect the requests from other maintainers for additional time if they ask for it.
For disruptive (e.g. major and/or breaking changes) PRs:
Wait for 2* maintainers approval
Every week of inactivity/staleness, ping maintainers
After 2 weeks from last approval, PR is merge-able with only 1 maintainer approval
For non-disruptive PRs:
Wait for 1 maintainer approval
If a maintainer wants to put a veto and to make the PR wait for his/her input after the mentioned period, the "veto" label can be used
*if there are at least 2 active maintainers, otherwise 1.
The single, most comprehensive technical reference to Game Boy available to the public. Here, you can help adding new content, proof reading, fixing typos, preparing updated versions of figures or port new content from old resources.
Technologies and tools: Markdown, Rust, Python, mdBook.
A community-led attempt to collect, archive and save every unofficial game, homebrew, patch, hackrom for Game Boy produced by the community through decades of passionate work.
This is the database of games powering the main Homebrew Hub project. Find a game (we have a lot to add!) and add it with a Pull Request. Or - even better - write scrapers to dump entire sources and add them to our repository!
Technologies and tools: JSON Schema, Python, Javascript, BeautifulSoup
`,36)])}var f=c(u,[[`render`,d]]);export{l as _pageData,f as default};
\ No newline at end of file
diff --git a/assets/deadcscroll-CEcT8oqT.js b/assets/deadcscroll-CEcT8oqT.js
new file mode 100644
index 00000000..4fef0247
--- /dev/null
+++ b/assets/deadcscroll-CEcT8oqT.js
@@ -0,0 +1,124 @@
+import{a as e,d as t,i as n,t as r}from"./app-oZCJRxfU.js";import"./xysine-B_k5r4-G.js";var i=JSON.parse(`{"path":"/guides/deadcscroll.html","title":"Dead C Scroll","lang":"en-US","frontmatter":{"head":[["meta",{"name":"og:title","content":"Dead C Scroll tutorial"}],["meta",{"name":"og:description","content":"An assembly tutorial for Game Boy showing how the scroll registers can be exploited to create some nice and interesting effects."}]]},"git":{"contributors":[{"name":"Antonio Vivace","username":"","email":"avivace4@gmail.com","commits":1}],"changelog":[{"hash":"a3a0422fa3b8e16a67dc8a661e1fa6eae5cb0fa8","time":1792000514000,"email":"avivace4@gmail.com","author":"Antonio Vivace","message":"ci, docs: rebuild on awesome-gbdev changes, add CONTRIBUTING.md (#8)"}]},"filePathRelative":"guides/deadcscroll.md"}`),a={name:`deadcscroll.md`};function o(r,i,a,o,s,c){return t(),n(`div`,null,[...i[0]||=[e(`
The SCY/SCX registers have a simple purpose: specify the coordinate of the screen's top-left pixel (or view, if you prefer) somewhere on the 256x256 pixel background map. This is really handy for certain kinds of games like platformers or top-down racing games (though there are LOTS of other kinds of games that benefit from this) where the view is the 'camera' and its position is set once per frame.
When you don't require scrolling, and when your cart boots, SCY/SCX is typically set to 0,0. When a screen is displayed, it appears normally even though you only set the values once. This is because as the screen draws, the PPU automatically adds the value in LY ($FF44) to the value in SCY in order to know what row of pixels to draw.
You can take advantage of how the PPU renders the screen by setting these registers as the screen draws. If you do this, you can create some interesting 'raster' effects that are presented here.
As an example, let's say you wanted to triple line 0 and show it for line 0, line 1, and line 2, and then continue with line 3. You would write to the SCY register like so:
SCY value (set once per line)
+│
+│ screen
+│ ┌───────┐
+├─$00 │Line 0 │ VRAM row $00 ($00+$00) is displayed
+├─$FF │Line 1 │ VRAM row $00 ($FF+$01) is displayed
+├─$FE │Line 2 │ VRAM row $00 ($FE+$02) is displayed
+├─$00 │Line 3 │ VRAM row $03 ($00+$03) is displayed
+├─$00 │Line 4 │ VRAM row $04 ($00+$04) is displayed
+├─... │... │ ...
+ │ │
+ └───────┘
When setting values for SCY, you need to remember that LY always (and automatically) increments, so you have to account for that in your new SCY value. You can write anything to SCX; that's not affected by the hardware so you don't have to adjust the value like you need to for SCY.
Note: The scroll registers only affect background rendering. They do not change how objects are displayed.
There are three main states that drive the display on the Game Boy: the Horizontal Blank (HBlank), the Vertical Blank (VBlank), and drawing. The HBlank starts when a line of pixels is completely drawn. There is an opportunity to do some work* before the next line of pixels starts drawing, and there is one HBlank for every line, all the way down the screen.
*The exact amount of time you have depends on several things; most notably how many objects are being drawn on that line. The PanDocs has a detailed explanation of the timing. (Indeed, read that entire document because it's great!)
When all of the lines are completely drawn, the VBlank starts. This interval is always 10 lines high so there is much more time to do some work compared to the HBlank. The VBlank is only secondary to this system though; the focus is the HBlank since we want to change the screen as it draws. The problem that needs to be solved is reliably knowing what value to set for a specific line.
As previously mentioned, there is a small amount of time that HBlanks give you to do work. This means that the handler has to be as fast as possible. On a limited system like the Game Boy, that usually equates to judicious use of table lookups and buffers.
There are two key elements to make this system very stable and very fast:
A double-buffering system that holds the data that feeds the HBlank handler
The idea of the double-buffer is that while one buffer is being used by the hardware to draw the screen, you modify (fill) values in the other. When the screen is done drawing, you switch buffers so the one you were just modifying is being used for drawing and you start modifying the other.
While the Draw Buffer (A) is used to render the screen, you change values in the Fill Buffer (B).
When the screen is done being drawn (and you know this because the VBlank interrupt would have triggered or the value in LY changed to 144), you switch the buffers.
Here, "switch buffers" means to switch the purpose of each buffer. It doesn't mean to copy buffers. Remember, we need this to be as fast as possible so to change buffers, you simply change pointers:
The size of each buffer (indeed, any buffer) depends on two things:
how many elements are needed
how much data is needed per element
We know that the buffers exist to support the HBlank handler, so the number of elements in the buffer are however many times the HBlank can trigger. We said earlier that the HBlank starts at the end of every screen line, so the number of elements is at least that many. However, remember when the HBlank starts: at the end of every line. What do we do if we need to change the 0th line (before any line has started drawing)? Well, we need to change that value before line 0 starts, which means it has to be done in the VBlank. And that means we need one more element. In short, we need the height of the screen plus one (144+1=145) elements in each buffer.
This tutorial is only concerned with the scroll registers, so it only needs to store 2 values per line: one for SCY and one for SCX. (You can store more data per line, of course, but this tutorial doesn't require it.)
In summary: each buffer is 145 2-byte elements (290 bytes), and we need two of them, so the total buffer memory size is 580 bytes.
Assume for a moment that you put the buffers physically next to each other in memory. For example, Buffer A is at $C000 and Buffer B is at $C122 (the buffer size is 290 bytes). We said earlier that in order to swap buffers, we just swap pointers, so the code that does that might look like this:
; assume the pointers are next to each other in memory
+wDrawBuffer: DS 2 ; buffer currently being drawn
+wFillBuffer: DS 2 ; buffer currently being modified
+
+; swap the contents of each pointer (28 cycles)
+ld hl,wDrawBuffer
+ld a,[hl+]
+ld b,[hl]
+ld c,a ; bc = contents of wDrawBuffer
+inc hl
+ld a,[hl+] ; a = LOW(contents of wFillBuffer)
+ld d,[hl] ; d = HIGH(contents of wFillBuffer)
+ld hl,wDrawBuffer
+ld [hl+],a
+ld [hl],d
+inc hl
+ld a,c
+ld [hl+],a
+ld [hl],b
To use a pointer, that code looks like this:
; use a pointer (8 cycles)
+ld hl,wFillBuffer
+ld a,[hl+]
+ld h,[hl]
+ld l,a ; hl = contents of wFillBuffer ($C000 or $C122)
You could certainly implement the system like this, but there is a way to gain some efficiency when swapping buffers and even with the actual pointers themselves.
Consider this: other than the memory locations, the buffers are identical. Since we're only really concerned with pointers, where the buffers reside in memory doesn't really matter. This can be exploited (and optimized!)
We can keep Buffer A at $C000. The buffer size is $122 bytes, but instead of putting Buffer B at $C122, what if we put it at $C200? This would make the pointer values $C000 and $C200. Literally a 1-bit difference. This, too, can be exploited! Both pointers end in $00 so we don't need to store those, which saves 2 bytes. This leaves us with two 1-byte 'pointers': $C0 and $C2.
To swap the pointers, literally just one bit has to be toggled:
; swap the contents of each 'pointer' (11 cycles)
+ldh a,[hFillBuffer]
+ldh [hDrawBuffer],a
+xor $02
+ldh [hFillBuffer],a
And to use a pointer, we only need to do this:
; use a 'pointer' (6 cycles)
+ldh a,[hFillBuffer]
+ld h,a
+ld l,0 ; hl = contents of hFillBuffer ($C000 or $C200)
You'll notice that the name of the pointers have changed. This is because they were moved into HRAM. (Also notice that they don't have to be next to each other in memory.) They were moved to HRAM for a couple of reasons: it allows an optimization in the swapping code (11 cycles vs 28), and it makes the use code slightly faster. There are only 2 bytes used now so that is a better candidate for moving to HRAM than 4 bytes.
In this system, code in the VBlank is responsible for two things:
swapping the pointers
setting the data for line 0
We've already seen what swapping the pointers looks like, but how is the data set for line 0? We need to emulate an HBlank handler running for "line -1" by getting the start of the new draw buffer and setting the scroll registers with the first data pair:
ldh a,[hDrawBuffer]
+ld h,a
+ld l,0
+
+; set the scroll registers
+ld a,[hl+]
+ldh [rSCY],a
+ld a,[hl+]
+ldh [rSCX],a
It's convenient that the scroll register addresses are next to each other. The data in the buffer is in the same order so as you can see in the code fragment above, this makes writing simple.
In an HBlank handler, every cycle counts! So don't do any work in there unless it's absolutely necessary. This is a good target for hyper-optimizations -- especially if you are changing VRAM (like palettes) -- so one should design around that optimization.
HBlankHandler::
+ push af
+ push hl
+
+ ; obtain the pointer to the data pair
+ ldh a,[rLY]
+ inc a
+ add a,a ; double the offset since each line uses 2 bytes
+ ld l,a
+ ldh a,[hDrawBuffer]
+ adc 0
+ ld h,a ; hl now points to somewhere in the draw buffer
+
+ ; set the scroll registers
+ ld a,[hl+]
+ ldh [rSCY],a
+ ld a,[hl+]
+ ldh [rSCX],a
+
+ pop hl
+ pop af
+ reti
Notice that we can take advantage of the fact that there is only 2 bytes per line. We can use LY directly and quickly turn it into pointer. (Thanks to rondnelson99 for pointing this out!)
And there you have it. An automatic and stable way to take advantage of the HBlank to do whatever your imagination wants to do!
All you need to do is set the fill buffer while the draw buffer is being displayed (you have an entire frame's worth of time to do this) and the system does the rest!
This effect uses a sine table to shift each line in a pleasant way. There are 3 states to this effect:
The image is stable and a progression line moves up the screen starting each line on its way
The table cycles a few times
The image stability is restored with the progression line moving up the screen
The values in the table can dramatically change the effect. For example, if the sine cycle was short enough, you could simulate a smoke effect (for example). Try it out!
Also, you could create a 'glitch' effect during a cut-scene, perhaps in a sci-fi game to simulate a slightly dirty transmission.
This effect is structured very similar to X Sine, in that there is a table of sine values driven by 3 states. The only difference is that SCY is changed instead of SCX.
This is a really good way to simulate water reflections.
This is simply a combination of the X Sine and Y Sine effects so you can see how different it looks compared to just the X or Y changing.
Instead of a full-screen image like this tutorial uses, imagine if you had a repeating image in VRAM (bigger than the screen) that looked like water ripples. This would move just like water!
This is like a flood fill effect used as an appearance transition. It's quite simple in that it repeats the lines to achieve the 'smear' effect and is perhaps more interesting than a fade in.
The specific image used in the tutorial is light along the bottom so it looks better if the screen was already light before the effect starts. You would change this to suit your image.
This is a disappearance transition and the reverse of Smear On. Due to the specific image that was used (i.e. it is light along the bottom), it looks better in this tutorial to have the effect reveal a light screen instead of dark. Again, you would change this to suit your image.
This effect simulates an image unrolling onto the screen. This might be useful for fantasy RPGs to transition to a map screen or perhaps a message written on a scroll. The image unrolls over a dark screen because the top of the image is mostly dark so it looks better to keep it dark than the contrast of using a light screen.
This effect simulates an image rolling off screen. This might be useful for fantasy RPGs to transition away from a map or scroll screen. This reveals a dark screen because the first thing you see in the roll is dark (because that's what's in VRAM below the screen). Keeping it dark made the transition more seamless.
The roll effects look complicated but the implementation is probably one of the simpler ones. The key to make this look good is the values in the table. The roll size is 32 pixels, but you can change this to whatever size you want, provided the table values support it. This SpecBas demo was used as a reference to obtain those values.
A GNU makefile is included. You will have to tailor it for your development environment but it builds cleanly with RGBDS 0.4.2. The only dependency is hardware.inc. All of the effects are shown here so you don't have to build first to see them.
To reduce dependencies, everything is in one .asm file. It's structured in a logical way and there are comments where applicable.
The effects are called "parts" by the code and each part has an Init and Process routine. The sequence is controlled by a table of Init pointers and driven by the ProcessPartTransition routine. Each Init is responsible for setting up the data for the effect (part) and to set the Process function pointer via the SetProcessFunc macro. When the effect is done, the Process routine calls the SetChangePartFlag to tell the tutorial driver to move to the next part.
There are non-effect parts present to get the effect sequence looking good when the parts are played one after the other. These are "delay" parts of various flavors:
ShowDelay: this shows the screen normally for a few seconds
LightDelay: this shows a light-colored blank screen for a few seconds
DarkDelay: this shows a dark-colored blank screen for a few seconds
The Delay parts share code because they're only present to make the ROM look nice, but the effects parts were developed in a way to be isolated from one another. This was done to make extraction easier. Because of this, you will see similar code present across several parts, for example, the various Sine effects.
One quirk you might notice when looking at VRAM is that the tile map is placed at 0,4 instead of 0,0. This was done to get the roll/unroll to handle the top of the screen correctly. The effects look best when they smoothly (dis)appear off-screen and if the image was placed at 0,0, the code to handle that would be distracting to how to implement the core of the effect.
Another topic worth mentioning is the row of light tiles that are under the image in VRAM. This was necessary to allow LightDelay to exist. Those light tiles don't have to be right under the image, that's just where it was placed for this tutorial. It could be moved well out of the way so it doesn't affect the effects that show that part of VRAM (Y Sine, Roll On, Roll Off).
If you run the ROM in BGB and have the Debug Messages window open, you will see the various parts announce themselves when they are initialized.
You can do more things than just change the scroll registers. For example, you can change the palette. Can you do this to make the roll/unroll effect look better? Here's an example of scroll register and palette changes.
This appearance effect from Sword of Sodan (Amiga) is really cool! (And you might recognize one of the opening effects if you scrub to the beginning.)
Another raster effect you could do is a 'twist' like the one in the Wired demo.
This was released for educational purposes and so is placed in the Public Domain. See LICENSE for more details.
`,117)]])}var s=r(a,[[`render`,o]]);export{i as _pageData,s as default};
\ No newline at end of file
diff --git a/assets/dma_hijacking-AwgBqFg1.js b/assets/dma_hijacking-AwgBqFg1.js
new file mode 100644
index 00000000..5d84fea9
--- /dev/null
+++ b/assets/dma_hijacking-AwgBqFg1.js
@@ -0,0 +1,51 @@
+import{a as e,d as t,i as n,n as r,t as i}from"./app-oZCJRxfU.js";var a=JSON.parse(`{"path":"/guides/dma_hijacking.html","title":"DMA Hijacking","lang":"en-US","frontmatter":{"head":[["meta",{"name":"og:title","content":"DMA Hijacking on Game Boy"}],["meta",{"name":"og:description","content":"A technique that allows you to run custom code in most GB/SGB/CGB games, provided you have an ACE exploit."}]]},"git":{"contributors":[{"name":"Antonio Vivace","username":"","email":"avivace4@gmail.com","commits":1}],"changelog":[{"hash":"a3a0422fa3b8e16a67dc8a661e1fa6eae5cb0fa8","time":1792000514000,"email":"avivace4@gmail.com","author":"Antonio Vivace","message":"ci, docs: rebuild on awesome-gbdev changes, add CONTRIBUTING.md (#8)"}]},"filePathRelative":"guides/dma_hijacking.md"}`),o={name:`dma_hijacking.md`};function s(i,a,o,s,c,l){return t(),n(`div`,null,[...a[0]||=[e(`
OAM DMA hijacking is a simple technique that allows you to run custom code in most GB/SGB/CGB games, provided you have an ACE exploit.
One would be quick to point out that if you have an ACE exploit, you can already execute custom code. So then, what is the point? It's that code ran through DMA Hijacking will be run on every game frame (for most games, at least).
If you are familiar enough with OAM, you may know about a feature called OAM DMA.
OAM DMA is a convenient feature that allows quickly updating the on-screen "objects" (often known as "sprites") quickly—which is especially useful since it typically needs to occur on every frame. However, using OAM DMA requires a small routine to be copied to HRAM and then run from there.
Interestingly, most games only copy the routine when starting up, and then execute it on every subsequent frame. But, if we modified that routine while the game is running, then the game will happily run the customized routine!
Here is the standard routine, given by Nintendo in the GB programming manual (using RGBASM syntax and a symbol from hardware.inc):
ld a, HIGH(OAMBuffer)
+ ldh [rDMA], a ; $FF46
+ ld a, 40
+DMALoop:
+ dec a
+ jr nz, DMALoop
+ ret
The simplest way to get custom code (let's call it DMAHook) executed would be to overwrite the first few bytes with a jump to DMAHook:
jp DMAHook
+ db $46 ; Leftover operand byte of \`ldh [rDMA], a\`
+ ld a, 40 ; None of this is executed
+DMALoop:
+ dec a
+ jr nz, DMALoop
+ ret
Now, overwriting the routine like this works for our purposes, but comes with a large drawback: the routine isn't doing what it is intended to anymore, and so the game's objects won't update (unless you manually copied OAM, but beware of the OAM corruption bug). Further, it's not possible to write to rDMA from DMAHook, as the write and subsequent wait loop must be executed from HRAM.
But, there is a solution.
call DMAHook
+ ldh [c], a ; A write to \`rDMA\`, set up by DMAHook
+ ld a, 40
+DMALoop:
+ dec a
+ jr nz, DMALoop
+ ret
Provided that DMAHook returns with properly set registers, this allows writing to rDMA in the single HRAM byte left by the call instruction. Here is a pattern for DMAHook :
DMAHook:
+ ;; Custom code, do whatever you want, it's VBlank time!
+ ; ...
+ ld c, LOW(rDMA) ; $46
+ ld a, HIGH(OAMBuffer)
+ ret
DMAHook can live anywhere in memory, but typically it will be in WRAM. It will be executed in the context of the VBlank interrupt, so for most games interrupts will be disabled, etc.
DMA Hijacking is also useful when combined with cartswap (swapping carts without shutting the console down, concept found by furrtek, developed by Cryo and me on the GCL forums), because it allows "transporting" ACE to other games.
General procedure:
Acquire ACE in the "source" game
Perform cartswap, insert the "victim" game
"Pseudo-initialize" the victim
Place the modified DMA handler in HRAM
Transfer control back to the victim's ROM
????
Profit!
Possible applications are checking for a button combo to trigger specific code (for example, credits warp), checking one or multiple memory addresses to detect a certain game state, etc.
Possible "attack vectors", i.e. ways of affecting the victim game, are setting certain memory addresses (like a GameShark), or even better: manipulating the stack.
Here is a video demonstration:
`,28),r(`iframe`,{width:`560`,height:`315`,src:`https://www.youtube-nocookie.com/embed/BNyDmZlbsNI`,title:`YouTube video player`,frameborder:`0`,allow:`accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture`,allowfullscreen:``},null,-1),r(`p`,null,`Manipulating the stack with this technique can not crash if the triggering game state is specific enough. I achieved text pointer manipulation in Pokémon Red this way. (This is not a ROM hack!)`,-1),r(`iframe`,{width:`560`,height:`315`,src:`https://www.youtube-nocookie.com/embed/yXy5sYZR9mk`,title:`YouTube video player`,frameborder:`0`,allow:`accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture`,allowfullscreen:``},null,-1),e(`
This new technique hinges on breaking one of any game's core assumptions: its entry point. You see, normally, the console transfers control to the game at address $0100, so any code placed there is designed to initialize all of the game's systems, in particular their memory.
However, since we have control of the CPU, we can jump to any location in the game's ROM, which allows bypassing some of said initialization. Doing so without any precautions is very likely to go haywire, though—it is important to initialize enough that the game runs, but not too much that it would end up overwriting the code we are trying to inject. This is what I call "pseudo-initialization".
Another important part is finding some free space to store the hook code in. The stack area can work surprisingly well for this, as many games appear to over-allocate (e.g. 256 bytes when the typical usage doesn't go beyond 32).
None of this has a silver bullet: the game's init code must be analyzed, and its memory usage carefully scrutinized in order to dig up enough free space for your hook.
DMA hijacking works similarly to the GameShark: that device intercepts accesses to the ROM, and when it detects that the VBlank handler is being run, it "overlays" different instructions that apply the stored codes, and jump back to the actual handler.
I encountered some games that don't transfer OAM unless a specific flag is set; I believe that it is always possible to override this limitation, by setting the flag back in the hook.
The OAM DMA routine is often placed at $FF80 in commercial games.
The patched OAM DMA routine with our hook may be modifying registers that the game expects to be preserved. This is all dependent on the target game, so no general advice can be given.
Additionally, if the hook takes too long, it may cause code expecting to run in VBlank to break. This might be solved for example by manipulating the stack and injecting an additional return address; here is an example.
jp DMAHook
+PostDMAHook:
+ ldh [c], a
+ ld a, 40
+DMALoop:
+ dec a
+ jr nz, DMALoop
+ jp hl
pop hl ; Get original return address
+ ld bc, PostHandlerHook ; Address of code that will be executed once the VBlank handler finishes
+ push bc ; Inject return address for VBlank handler
+ ld c, LOW(rDMA)
+ ld a, HIGH(OAMBuffer)
+ jp PostDMAHook
(Since the handler almost certainly performs some pops before returning, you will almost certainly need more complex stack manipulation, but that's the gist of it.)
Some games have a slightly more clever routine in HRAM, that omits the initial ld a, HIGH(OAMBuffer) saving 2 bytes of HRAM.
ldh [rDMA], a
+ ld a, 40
+DMALoop:
+ dec a
+ jr nz, DMALoop
+ ret
They can still be patched by overwriting the ld a, 40 instead, and using e.g. the b register for the loop:
call DMAHook
+ ldh [c], a ; Write to rDMA
+DMALoop:
+ dec b
+ jr nz, DMALoop
+ ret
Then DMAHook needs to return with b additionally set to 40:
DMAHook:
+ ;; Custom code, do whatever you want, it's VBlank time!
+ ; ...
+ ld bc, 40 << 8 | LOW(rDMA) ; 40 in B, $46 in C
+ ld a, HIGH(OAMBuffer)
+ ret
However, if the OAM buffer address passed to the function (in a) is not static, push af and pop af will have to be used instead of ld a, HIGH(OAMBuffer).
`,10)]])}var c=i(o,[[`render`,s]]);export{a as _pageData,c as default};
\ No newline at end of file
diff --git a/assets/donate-DtVYCwuu.js b/assets/donate-DtVYCwuu.js
new file mode 100644
index 00000000..b3c91dcf
--- /dev/null
+++ b/assets/donate-DtVYCwuu.js
@@ -0,0 +1 @@
+import{a as e,d as t,i as n,t as r}from"./app-oZCJRxfU.js";var i=JSON.parse(`{"path":"/donate.html","title":"Donations and bounties","lang":"en-US","frontmatter":{},"git":{"contributors":[{"name":"Antonio Vivace","username":"","email":"avivace4@gmail.com","commits":1}],"changelog":[{"hash":"a3a0422fa3b8e16a67dc8a661e1fa6eae5cb0fa8","time":1792000514000,"email":"avivace4@gmail.com","author":"Antonio Vivace","message":"ci, docs: rebuild on awesome-gbdev changes, add CONTRIBUTING.md (#8)"}]},"filePathRelative":"donate.md"}`),a={name:`donate.md`};function o(r,i,a,o,s,c){return t(),n(`div`,null,[...i[0]||=[e(`
You can donate on our Open Collective or on GitHub sponsors. You can send a one-time donation of any amount or join the collective as backer, donating monthly.
WHY OPENCOLLECTIVE
The gbdev organisation is an informal collective of individuals. As such, we are not able to hold any fund in our accounts. Instead, the US based non-profit Open Source Collective is our fiscal host, providing us the financial and legal infrastructure to receive donations and spend money.
It's also possible to donate specifically towards a project under our umbrella. We'll do our best to honour this preference.
Donations are subject to a 10% fee from our Open Collective host (Open Source Collective) plus a variable fee from the payment processor (Wise).
Money collected on GitHub sponsors are paid out by GitHub to our Open Collective at the end of each month.
If you have a company and you are interested in being highlighted on some of our webpages and channels, we have sponsorship tiers starting at 50$/month. Get in touch at sponsors@gbdev.io to understand if this could be for you. We are interested in hearing how our software is helping your business etc.
Sponsorships are evaluated on a case-to-case basis and only be accepted if we feel like our core values are compatible and the relationship can last in the long term with beneficial effect for both parties.
Sometimes, we may assign a bounty to an Issue. Labels such as bounty-25$, bounty-50$, bounty-100$ are used to specify the amount. If you wish to work on a issue with a bounty attached, it's a good measure to comment on the issue asking for it to be assigned to you*.
If you send a Pull Request fixing that issue and this work gets merged, you will be able to claim the corresponding bounty.
Bounties are paid out from our Open Collective: submit an expense mentioning the Issue you worked on and make sure to use your GitHub email (or the one you authored the git commit with) when creating an account on OpenCollective. Wire transfers and PayPal are supported**.
Details are generally verified in the following 24-48 hours. Payments are then processed by our Open Collective host a couple of times per week.
TAX CONSIDERATIONS
Make sure to check the tax regulations of your country and properly declare the bounties received. We're not tax consultants and we cannot help with that.
* In case of inactivity we may remove the assignment to you or let someone else who submitted a PR claim the bounty.
** Our Open Collective host uses Wise as the payment processor so the countries supported for the bank transfers depends on them.
`,22)]])}var s=r(a,[[`render`,o]]);export{i as _pageData,s as default};
\ No newline at end of file
diff --git a/assets/gbcompo-statement-DuIddDPh.js b/assets/gbcompo-statement-DuIddDPh.js
new file mode 100644
index 00000000..32f1f737
--- /dev/null
+++ b/assets/gbcompo-statement-DuIddDPh.js
@@ -0,0 +1 @@
+import{a as e,d as t,i as n,t as r}from"./app-oZCJRxfU.js";var i=JSON.parse(`{"path":"/gbcompo-statement.html","title":"Statement on the recent removal request and rule compliance in GBCompo events","lang":"en-US","frontmatter":{"head":[["meta",{"name":"og:title","content":"Statement on the recent removal request and rule compliance in GBCompo events"}]]},"git":{"contributors":[{"name":"Antonio Vivace","username":"","email":"avivace4@gmail.com","commits":1}],"changelog":[{"hash":"a3a0422fa3b8e16a67dc8a661e1fa6eae5cb0fa8","time":1792000514000,"email":"avivace4@gmail.com","author":"Antonio Vivace","message":"ci, docs: rebuild on awesome-gbdev changes, add CONTRIBUTING.md (#8)"}]},"filePathRelative":"gbcompo-statement.md"}`),a={name:`gbcompo-statement.md`};function o(r,i,a,o,s,c){return t(),n(`div`,null,[...i[0]||=[e(`
gbdev.io is a non-profit initiative that maintains, develops, and promotes open-source tools, documentation, and digital preservation projects for the Game Boy platform. Since 2021 we also host the "gbcompo" events.
Homebrew Hub is gbdev.io's institutional repository for Game Boy homebrew, dedicated to long-term digital preservation. We use it to archive and mirror all GBCompo submissions, so that the versions entered into the competition remain available and accessible over time.
GBCompo events (2021, 2023, 2025) are game development competitions organised by gbdev.io, focused on creating new free Game Boy homebrew games. Developers submit games that are judged, showcased, and actively promoted during and after each edition. Some entries later go on to have commercial success, including paid digital releases and physical editions; while we are happy about this and actively showcase it, we do not profit from it, and this is not the focus of the competition.
GBCompo events are run on a non-profit basis under a clear, shared framework. The prize pool is funded entirely by community donations and sponsors, with organisers and judges putting in around six months of unpaid work around and during each edition. No profit is made: donated and sponsored funds are redistributed to winners, with only a small portion going back into gbdev.io’s common funds to help support future events (for example, gbcompo25's first $500 in the prize pool were contributed by gbdev.io).
At the explicit request of 'allalonegamez' we have begun removing their games from Homebrew Hub. All copies of the relevant entries hosted on Homebrew Hub are being taken offline (subject only to delays such as DNS propagation, rebuilds, and caching). We are fully complying with their request to remove the content from our platform.
We were requested to take down a game for which the copy on Homebrew Hub was the last available online, for free. This game participated in the 'gbcompo23' competition and was awarded monetary prizes and promotion through gbdev.io channels.
Rule 20 of 'gbcompo23', the event where 'allalonegamez' game was submitted and won prizes says:
The submission must be available for free for the public (and not only the judges). Submission will be published and kept online for free on the competition website, while you are free to keep working on it (and eventually charge for it/make commercial usage).
(emphasis added)
We publish the jam submitted ROMs and keep them online for free on on gbdev's Homebrew Hub, after the competition window ends. Additional ZIP compilations are also prepared. kept online for free clearly means that the submitted jam version must remain publicly accessible after the competition, and not be taken down, delisted, or paywalled at a later date.
In practise though, we don't enforce a specific 'competition website'; any stable, accessible online location is acceptable to be considered in compliance with Rule 20. A request to take down the default mirror to satisfy the rule in other ways/places was acceptable.
We did not want to host a creator's work against their will, even if they had accepted their entry being kept online by submitting to our event, so we accepted a withdrawal scenario where 'allalonegamez' had removed every free copy of the jam version and we complied by removing ours.
The gbcompo23 entry in question was put offline by the author sometime between March and June 2024. This is a perfectly legitimate approach, as long as the jam-submitted versions of the games are kept online and available for free, somewhere, as per rules of the event they were submitted to.
Since the author did not entertain the idea of keeping the copy anywhere else online for free, the entry is in violation of Rule 20.
Because the entry no longer complies to the event rules, they'll be disqualified from the competition. Disqualified entries are not eligible for prizes nor leaderboards.
In order to be fair to all other participants who are respecting the rules and keeping their jam-versions available, any prizes or awards previously assigned to these entries should be reassigned to eligible participants. Any mention of a refund of prizes was made solely in this context of fairness and proper redistribution.
We want to clarify that we have no intention of taking legal action or attempting to enforce a prize refund by any means. Our reference to an 'expectation of a refund' was intended purely as a matter of fairness to all parties involved in the event, not as a threat or condition, and we recognize that our wording may have come across more harshly than intended. At no point was legal action discussed or considered, and we did not withhold or delay any part of the removal requested by 'allalonegamez'.
'allalonegamez' is now offering to refund the prize and donate it to a charity. We appreciate the offer, however we are not and have never been interested in pursuing the financial matter further so we do not have a say on that. No further action will be taken in that regard. We are in any case ready to reinstate the entry in its earned position at any time, if and when it becomes compliant with the rules again.
The GBCompo prize pool is funded by people who donate money under specific premises: free games will be released in the framework of the ruleset of the event. Participants also agree to abide by these rules as a condition for qualifying for prizes and promotion.
In this context, it would be unfair to continue to award exposure, promotion, and prizes to a participant who is not abiding by the rules, receiving prizes, exposure and promotion only to pull their entry offline and make them paid-only, while many other submitters – including those who did not win – continue to respect them and keep their jam-versions available.
Disqualification in such a case is necessary to uphold the expectations of all the other actors in the event, including donors, organisers and other participants.
We plan to revise the wording of the rules to make this expectation clearer, explicitly indicating where entries will be kept online and using more precise language about the long-term, effectively perpetual availability of the submitted jam versions.
`,35)]])}var s=r(a,[[`render`,o]]);export{i as _pageData,s as default};
\ No newline at end of file
diff --git a/assets/gbcompo21-D_bj2sOg.js b/assets/gbcompo21-D_bj2sOg.js
new file mode 100644
index 00000000..763cdf7d
--- /dev/null
+++ b/assets/gbcompo21-D_bj2sOg.js
@@ -0,0 +1 @@
+import{a as e,d as t,i as n,t as r}from"./app-oZCJRxfU.js";var i=JSON.parse(`{"path":"/gbcompo21.html","title":"Game Boy Competition 2021","lang":"en-US","frontmatter":{"head":[["meta",{"name":"og:title","content":"GB Compo 2021"}],["meta",{"name":"og:description","content":"An OG game competition! Create original games, demos, homebrews tools and music for the original Game Boy / Game Boy Color and compete for glory (and a lot of prizes)!\\n"}]]},"git":{"contributors":[{"name":"Antonio Vivace","username":"","email":"avivace4@gmail.com","commits":1}],"changelog":[{"hash":"a3a0422fa3b8e16a67dc8a661e1fa6eae5cb0fa8","time":1792000514000,"email":"avivace4@gmail.com","author":"Antonio Vivace","message":"ci, docs: rebuild on awesome-gbdev changes, add CONTRIBUTING.md (#8)"}]},"filePathRelative":"gbcompo21.md"}`),a={name:`gbcompo21.md`};function o(r,i,a,o,s,c){return t(),n(`div`,null,[...i[0]||=[e(`
Full leaderboards are available here. Top rated entries are playable here while the complete list can be found on Homebrew Hub and Itch.io. A zip file can be downloaded from GitHub.
Come join us on Discord or others for help getting started, and head over to gbdev.io for useful links & resources!
We'd love to hear from participants, share your progress on Twitter using the #gbcompo hashtag and join the conversation the on Discord #compo-chat channel!
The current prize pool is and will be split according to this document.
Entries will compete in 4 separate categories and 4 rankings will be drawn up. Based on these leaderboards, the top entries will be awarded as follows:
Best games (80% of the prize pool)
The best 5 games will get a prize.
The best 3 OSS games will get an additional prize (can overlap)
Best homebrew/tool (1) (running on a GB/GBC)
Best music cartridge (1)
Best demo (1) (demoscene style demos)
The rankings are decided according to the JUDGING criteria.
If you publish your game source code and assets on a public repository (e.g. GitHub) under an open source license (GPL, MIT, Apache, CC0...) you get 20% more of the prize as a bonus!
Money prizes are subject to fees and conversions. They may be delivered as gift cards of your choice.
Final entry "deliverable" must consist of a GB or GBC ROM file. You can also attach descriptive documents in TXT, Markdown or HTML file format (e.g. game manual).
The ROM file will be tested on real hardware (usually a DMG or a GBC) and on the BGB emulator. In case of different results, the hardware test will be the trusted one.
GBC features are allowed.
SGB features are allowed. The entries will be evaluated mainly on Game Boy (Color). You can use SGB features, but don't rely on them as main features for your entry.
Teams are allowed (award will be split).
The competition is focused on games, but you are allowed to submit homebrew tools, demoscene style demos and music cartridges. There may be special category prizes for those. There are no preferred themes.
ROM Hacks are not allowed.
NSFW content is not allowed. Please keep it safe for work and avoid anything overly edgy or distasteful.
You can submit more than one entry. They will be rated separately, but each person or team may only accept prizes on behalf of a single entry (e.g., you can't win both the 1st and 2nd place prize).
Judges can submit entries, but they are not eligible for prizes.
Only some official MBC chips (MBC1, MBC2, MBC3 and MBC5, as well as no MBC at all) are allowed. The only exceptions are:
While Nintendo only manufactured very specific MBC+ROM combinations, we will allow any power-of-two ROM size between 32 KiB and the maximum for the chosen chip. (This is simply a convenience feature, as any ROM image can be extended to fit a larger ROM chip by appending copies of itself.)
The ROM+RAM combination (i.e., without an MBC, up to 32 KiB ROM and 8 KiB SRAM), with or without battery, will be accepted for compatibility, as many people have simple flash carts that do have SRAM but don't have an MBC. (This can be trivially emulated by any MBC1, MBC3 or MBC5 configuration.)
The variants MBC30 and MBC1M are accepted, as they have been used in licensed games.
Some official MBCs added extra components to the cart: MBC3/MBC30 carts could have an RTC, and MBC5 carts could have rumble. These combinations, and only these combinations, will be accepted. In particular, this means it's impossible to have RTC and rumble in the same cart.
Entries cannot rely on extra hardware or add-on devices, they must be playable without a specific setup.
As long as you produce a GB or GBC ROM file that runs on GB/GBC, any tool to develop the entry is allowed. This includes RGBDS (ASM), GBDK (C), ZGB (C), GB Studio, ...
Source code is optional, but very much appreciated. Entries submitted with source code (public repositories on GitHub/GitLab/... are accepted) and with an open source license (See rule 15) are eligible for additional prizes. Make sure to fill the "Open Source repository" field with a valid and public repository when submitting on itch.io. See this issue for further details.
To be eligible for the OSS bonus, the code must be licensed under an "Open Source" license (any of the "Free" license listed here is accepted). Assets can be licensed under any of the CC licenses. Obfuscated code, non-reproducible builds, intentionally making the source code hard to read/re-use will exclude the entry form the OSS leaderboards. This is at discretion of the hosts and judges. We must be able to read the code and compile it into a working ROM, running a script or following istructions.
The entry must be submitted on the itch.io jam page.
Your work must be new and original. You cannot enter with something you were already working on before the start of the jam. The majority of work should be done during the jam. If in doubt, please ask us before proceeding. Examples of things that are allowed:
A brand new game using your own (or any) existing game engine.
A remake of your (or someone else's) game for a different platform, but with new code and assets.
A brand new game you barely started (e.g. you only made a title screen or a mockup of the gameplay).
Using templates, tutorial code as starting point (when they're license allow to do so, e.g. gb-boilerplate)
Asset packs for art and music are ok if the licensing permits. Be sure to make note of what is being reused and under what terms. Use of pre-made assets (vs original ones) may be factored in when judges assess points for relevant criteria (e.g. graphics, audio).
Examples of things that are not allowed:
A project you've been working on for months.
An update/patch for a game you already released publicly.
The submission must be available for free for the public (and not only the judges). Submission will be published and kept online for free on the competition website, while you are free to keep working on it (and eventually charge for it/make commercial usage).
Multiple submissions are allowed.
If your submission contains text that isn't available in English, some judges might not be able to read that text, and they will judge accordingly.
`,65)]])}var s=r(a,[[`render`,o]]);export{i as _pageData,s as default};
\ No newline at end of file
diff --git a/assets/gbcompo23-D2F_-kFn.js b/assets/gbcompo23-D2F_-kFn.js
new file mode 100644
index 00000000..208afb06
--- /dev/null
+++ b/assets/gbcompo23-D2F_-kFn.js
@@ -0,0 +1 @@
+import{a as e,d as t,i as n,t as r}from"./app-oZCJRxfU.js";var i=JSON.parse(`{"path":"/gbcompo23.html","title":"Game Boy Competition 2023","lang":"en-US","frontmatter":{"head":[["meta",{"name":"og:title","content":"GB Compo 2023"}],["meta",{"name":"og:description","content":"Join us for an epic game development event celebrating the classics! Show off your creativity and skills by crafting original games for the iconic Game Boy!\\n"}]]},"git":{"contributors":[{"name":"Antonio Vivace","username":"","email":"avivace4@gmail.com","commits":1}],"changelog":[{"hash":"a3a0422fa3b8e16a67dc8a661e1fa6eae5cb0fa8","time":1792000514000,"email":"avivace4@gmail.com","author":"Antonio Vivace","message":"ci, docs: rebuild on awesome-gbdev changes, add CONTRIBUTING.md (#8)"}]},"filePathRelative":"gbcompo23.md"}`),a={name:`gbcompo23.md`};function o(r,i,a,o,s,c){return t(),n(`div`,null,[...i[0]||=[e(`
This category is for audio-visual demonstrations that run without viewer interaction, in such a way as to demonstrate skill in manipulation of the Game Boy hardware through code, special effects, art, music, and direction. More on Wikipedia.
The submitted entry must implement / interpret the jam theme (applies to Game category only).
Multiple submissions are allowed from the same person or team. They will be rated separately, but each person or team may only accept prizes on behalf of a single entry (e.g., you can't win both the 1st and 2nd place prize).
Judges can submit entries, but the entries won't be eligible for prizes. Judges won't score the entries they submitted.
Source code is optional, but very much appreciated. Entries submitted with source code (public repositories on GitHub/GitLab/... are accepted) and with an open source license (See rule 5) are eligible for additional prizes. Make sure to fill the "Open Source repository" field with a valid and public repository when submitting on itch.io. It is NOT necessary for the source to be available during the competition period. Repositories/sources just need to be public from the time the event ends. See this issue for further details.
To be eligible for the OSS bonus, the code must be licensed under an "Open Source" license (any of the "Free" license listed here is accepted). Assets can be licensed under any of the CC licenses. Obfuscated code, non-reproducible builds, intentionally making the source code hard to read/re-use will exclude the entry form the OSS leaderboards. This is at discretion of the hosts and judges. We must be able to read the code and compile it into a working ROM, running a script or following instructions.
If your submission contains text that isn't available in English, some judges might not be able to read that, and they will judge accordingly.
Shipment of physical prizes may not be available to all countries, they are subject to shipping ability of the prize donors.
Final entry "deliverable" must consist of a GB or GBC ROM file. You can also attach descriptive documents in TXT, Markdown, PDF or HTML file format (e.g. game manual).
The ROM file will be tested on real hardware (primarily a GBC) and on the BGB emulator. In case of different results, the hardware test will be the trusted one.
GBC features are allowed, and entries may be GBC only (GBC only should be indicated in game for non-GBC hardware or on the entry's page).
SGB features are allowed. The entries will be evaluated mainly on Game Boy Color. You can use SGB features, but don't rely on them as main features for your entry.
Only some official MBC chips (MBC1, MBC2, MBC3 and MBC5, as well as no MBC at all) are allowed. The only exceptions are:
While Nintendo only manufactured very specific MBC+ROM combinations, we will allow any power-of-two ROM size between 32 KiB and the maximum for the chosen chip. (This is simply a convenience feature, as any ROM image can be extended to fit a larger ROM chip by appending copies of itself.)
The ROM+RAM combination (i.e., without an MBC, up to 32 KiB ROM and 8 KiB SRAM), with or without battery, will be accepted for compatibility, as many people have simple flash carts that do have SRAM but don't have an MBC. (This can be trivially emulated by any MBC1, MBC3 or MBC5 configuration.)
The variants MBC30 and MBC1M are accepted, as they have been used in licensed games.
Some official MBCs added extra components to the cart: MBC3/MBC30 carts could have an RTC, and MBC5 carts could have rumble. These combinations, and only these combinations, will be accepted. In particular, this means it's impossible to have RTC and rumble in the same cart.
Entries cannot rely on extra hardware or add-on devices, they must be playable without a specific setup.
As long as you produce a GB or GBC ROM file that runs on GB/GBC, any tool to develop the entry is allowed. This includes RGBDS (ASM), GBDK (C), ZGB (C), GB Studio, ...
NSFW content is not allowed. Please keep it safe for work and avoid anything overly edgy or distasteful.
The entry must be submitted on the itch.io jam page.
Your work must be new and original. You cannot enter with something you were already working on before the start of the jam. The majority of work should be done during the jam. If in doubt, please ask us before proceeding.
Examples of things that are allowed:
A brand new game using your own (or any) existing game engine.
A remake of your (or someone else's) game for a different platform, but with new code and assets.
A brand new game you barely started (e.g. you only made a title screen or a mockup of the gameplay).
Using templates, tutorial code as starting point (when they're license allow to do so, e.g. gb-boilerplate)
Asset packs for art and music and covers of other music are allowed if the licensing permits. Be sure to make note of what is being reused or re-interpreted and under what terms. Use of pre-made assets and content (vs wholly original) may be factored in when judges assess points for relevant criteria (e.g. graphics, audio).
Examples of things that are not allowed:
A project you've been working on for months.
An update/patch for a game you already released publicly.
The submission must be available for free for the public (and not only the judges). Submission will be published and kept online for free on the competition website, while you are free to keep working on it (and eventually charge for it/make commercial usage).
It is allowed to submit an entry to multiple events.
A team of (~10) judges has been selected before the start of the competition.
Every judge will be asked to compile an unranked shortlist of their favourite entries. Once the shortlists are ready from all the judges, the top 15 most selected entries will be evaluated by the judges on the following criteria:
Gameplay - How entertaining are the gameplay mechanics? How fun is the game?
Technical - How innovative is it from a technical perspective? Does it push the hardware to the limit?
Originality - How refreshing is it from a non-technical perspective? Does it have a unique design / mechanics?
Graphics - How impressive are the art, animation and visual effects?
Audio - How good are the music and sound effects?
Theme - How was the theme implemented in the gameplay, the mechanics and/or the story?
Applicability may vary depending on the category of the entry (e.g. a "Music" ROM may be evaluated only on 5).
The final leaderboard is computed by averaging the "Overall" score.
Warning
Judges are volunteers. Those details can change without prior notice, as the judging process is long and involves hundreds of entries. Detailed criteria leaderboards will be provided only for entries in the final shortlist.
`,67)]])}var s=r(a,[[`render`,o]]);export{i as _pageData,s as default};
\ No newline at end of file
diff --git a/assets/gbcompo25-DxR_4Yr3.js b/assets/gbcompo25-DxR_4Yr3.js
new file mode 100644
index 00000000..dd6d1741
--- /dev/null
+++ b/assets/gbcompo25-DxR_4Yr3.js
@@ -0,0 +1 @@
+import{a as e,d as t,i as n,t as r}from"./app-oZCJRxfU.js";var i=JSON.parse(`{"path":"/gbcompo25.html","title":"Game Boy Competition 2025","lang":"en-US","frontmatter":{"head":[["meta",{"name":"og:title","content":"GB Compo 2025"}],["meta",{"name":"og:description","content":"Join us for an epic game development event celebrating the classics! Show off your creativity and skills by crafting original games for the iconic Game Boy!\\n"}]]},"git":{"contributors":[{"name":"Antonio Vivace","username":"","email":"avivace4@gmail.com","commits":1}],"changelog":[{"hash":"a3a0422fa3b8e16a67dc8a661e1fa6eae5cb0fa8","time":1792000514000,"email":"avivace4@gmail.com","author":"Antonio Vivace","message":"ci, docs: rebuild on awesome-gbdev changes, add CONTRIBUTING.md (#8)"}]},"filePathRelative":"gbcompo25.md"}`),a={name:`gbcompo25.md`};function o(r,i,a,o,s,c){return t(),n(`div`,null,[...i[0]||=[e(`
This category is for audio-visual demonstrations that run without viewer interaction, in such a way as to demonstrate skill in manipulation of the Game Boy hardware through code, special effects, art, music, and direction. More on Wikipedia.
Multiple submissions are allowed from the same person or team. They will be rated separately, but each person or team may only accept prizes for a single entry (e.g., you can't win both the 1st and 2nd place prize).
Judges can submit entries, but the entries won't be eligible for prizes. Judges won't score the entries they submitted.
Source code is optional, but very much appreciated. Entries submitted with source code (public repositories on GitHub/GitLab/... are accepted) and with an open source license (See rule 4) are eligible for additional prizes. Make sure to fill the "Open Source repository" field with a valid and public repository when submitting on itch.io. It is NOT necessary for the source to be available during the competition period. Repositories/sources just need to be public from the time the event ends. See this issue for further details.
To be eligible for the OSS bonus, the code must be licensed under an "Open Source" license (any of the "Free" license listed here is accepted). Assets can be licensed under any of the CC licenses. Obfuscated code, non-reproducible builds, intentionally making the source code hard to read/re-use will exclude the entry form the OSS leaderboards. This is at discretion of the hosts and judges. We must be able to read the code and compile it into a working ROM, running a script or following instructions.
If your submission contains text that isn't available in English, some judges might not be able to read that, and they will judge accordingly.
Shipment of physical prizes may not be available to all countries, they are subject to shipping ability of the prize donors.
The submitted entry must implement / interpret the jam theme (applies to Game category only).
Final entry "deliverable" must consist of a GB or GBC ROM file. You can also attach descriptive documents in TXT, Markdown, PDF or HTML file format (e.g. game manual).
The ROM file will be tested on real hardware (primarily a GBC) and on a hardware accurate emulator such as BGB and SameBoy. In case of different results, the hardware test will be the trusted one.
GBC features are allowed, and entries may be GBC only (GBC only should be indicated in game for non-GBC hardware or on the entry's page).
SGB features are allowed. The entries will be evaluated mainly on Game Boy Color. You can use SGB features, but don't rely on them as main features for your entry.
Only some official MBC chips (MBC1, MBC2, MBC3 and MBC5, as well as no MBC at all) are allowed. The only exceptions are:
While Nintendo only manufactured very specific MBC+ROM combinations, we will allow any power-of-two ROM size between 32 KiB and the maximum for the chosen chip. (This is simply a convenience feature, as any ROM image can be extended to fit a larger ROM chip by appending copies of itself.)
The ROM+RAM combination (i.e., without an MBC, up to 32 KiB ROM and 8 KiB SRAM), with or without battery, will be accepted for compatibility, as many people have simple flash carts that do have SRAM but don't have an MBC. (This can be trivially emulated by any MBC1, MBC3 or MBC5 configuration.)
The variants MBC30 and MBC1M are accepted, as they have been used in licensed games.
Some official MBCs added extra components to the cart: MBC3/MBC30 carts could have an RTC, and MBC5 carts could have rumble. These combinations, and only these combinations, will be accepted. In particular, this means it's impossible to have RTC and rumble in the same cart.
Entries cannot rely on extra hardware or add-on devices, they must be playable without a specific setup.
As long as you produce a GB or GBC ROM file that runs on GB/GBC, any tool to develop the entry is allowed. This includes RGBDS (ASM), GBDK (C), ZGB (C), GB Studio, ...
NSFW content is not allowed. Please keep it safe for work and avoid anything overly edgy or distasteful.
The entry must be submitted on the itch.io jam page.
Your work must be new and original. You cannot enter with something you were already working on before the start of the jam. The majority of work should be done during the jam. If in doubt, please ask us before proceeding.
Examples of things that are allowed:
A brand new game using your own (or any) existing game engine.
A remake of your (or someone else's) game for a different platform, but with new code and assets.
A brand new game you barely started (e.g. you only made a title screen or a mockup of the gameplay).
Using templates, tutorial code as starting point (when they're license allow to do so, e.g. gb-boilerplate)
Pre-made and AI generated assets for art and music (and covers of other music) are allowed if the licensing permits. Be sure to make note of what is being reused or re-interpreted and under what terms. There is a specific field in the submission form to allow you to disclose this. Use of pre-made and AI generated assets and content (vs wholly original and human created) may be factored in when judges assess points for relevant criteria (e.g. graphics, audio).
Examples of things that are not allowed:
A project you've been working on for months.
An update/patch for a game you already released publicly.
The submission must be available for free for the public (and not only the judges). Submission will be published and kept online for free on the competition website, while you are free to keep working on it (and eventually charge for it/make commercial usage).
It is allowed to submit an entry to multiple events.
It is permitted to use the 'gbcompo25 logo' graphics to identify the entry as a submission to this competition (e.g. as an intro splash screen). Minor modifications and alterations (color palette, size, smaller resolution) to fit the medium are permitted.
Before submitting, make sure you've read the rules. Our resources are limited - low effort, non-compliant or spammy submissions may lead to exclusion from this event and future ones.
A team of (~10) judges has been selected before the start of the competition.
Every judge will be asked to compile an unranked shortlist of their favourite entries. Once the shortlists are ready from all the judges, the top 15 most selected entries will be evaluated by the judges on the following criteria:
Gameplay - How entertaining are the gameplay mechanics? How fun is the game?
Technical - How innovative is it from a technical perspective? Does it push the hardware to the limit?
Originality - How refreshing is it from a non-technical perspective? Does it have a unique design / mechanics?
Graphics - How impressive are the art, animation and visual effects?
Audio - How good are the music and sound effects?
Applicability may vary depending on the category of the entry (e.g. a "Music" ROM may be evaluated only on 5)-
The final leaderboard is computed by averaging the "Overall" score.
Warning
Judges are volunteers. Those details can change without prior notice, as the judging process is long and involves hundreds of entries. Detailed criteria leaderboards will be provided only for entries in the final shortlist.
`,52)]])}var s=r(a,[[`render`,o]]);export{i as _pageData,s as default};
\ No newline at end of file
diff --git a/assets/lyc_timing-DIlRHC0K.js b/assets/lyc_timing-DIlRHC0K.js
new file mode 100644
index 00000000..85b8b526
--- /dev/null
+++ b/assets/lyc_timing-DIlRHC0K.js
@@ -0,0 +1,208 @@
+import{a as e,c as t,d as n,f as r,g as i,h as a,i as o,l as s,m as c,n as l,o as u,p as d,r as f,s as p,t as m,u as h}from"./app-oZCJRxfU.js";import"./xysine-B_k5r4-G.js";var g=`.waitVRAM
+ ldh a, [rSTAT]
+ and STATF_BUSY ; 2
+ jr nz, .waitVRAM
+`,_=`LYC::
+ push af
+ ldh a, [rLY]
+ cp 128 - 1
+ jr z, .disableSprites
+
+ ; enable sprites
+ ldh a, [rLCDC]
+ or a, LCDCF_OBJON
+ ldh [rLCDC], a
+ pop af
+ reti
+
+.disableSprites
+ ldh a, [rLCDC]
+ and a, ~LCDCF_OBJON
+ ldh [rLCDC], a
+ pop af
+ reti
+`,v=`LYC::
+ push af
+ push hl
+ ldh a, [rLY]
+ cp 128 - 1
+ jr z, .disableSprites
+
+ ; enable sprites
+ ldh a, [rLCDC]
+ or a, LCDCF_OBJON
+ jr .finish
+
+.disableSprites
+ ldh a, [rLCDC]
+ and a, ~LCDCF_OBJON
+
+.finish
+ ld hl, rSTAT
+.waitNotBlank
+ bit STATB_BUSY, [hl]
+ jr z, .waitNotBlank
+.waitBlank
+ bit STATB_BUSY, [hl]
+ jr nz, .waitBlank
+
+ ldh [rLCDC], a
+ pop hl
+ pop af
+ reti
+`,y=` push af ; 4
+ push hl ; 8
+
+ ; obtain the pointer to the data pair
+ ldh a, [rLY] ; 11
+ inc a ; 12
+ add a, a ; 13 ; double the offset since each line uses 2 bytes
+ ld l, a ; 14
+ ldh a, [hDrawBuffer] ; 17
+ adc 0 ; 19
+ ld h, a ; 20 ; hl now points to somewhere in the draw buffer
+
+ call UnconditionalRet ; just waste 31 cycles while we wait for HBlank to maybe start
+ call UnconditionalRet
+ call UnconditionalRet
+ nop ; 51
+
+ ; now start trying to look for HBlank to exit early
+
+ ldh a, [rSTAT]
+ and STATF_BUSY
+ jr z, .setAndExit ; 58
+
+ ldh a, [rSTAT]
+ and STATF_BUSY
+ jr z, .setAndExit ; 65
+
+ ldh a, [rSTAT]
+ and STATF_BUSY
+ jr z, .setAndExit ; 72
+
+ ldh a, [rSTAT]
+ and STATF_BUSY
+ jr z, .setAndExit ; 79
+
+ nop ; waste 4 more cycles since there isn't time for another check
+ nop
+ nop
+ nop ; 83
+
+.setAndExit
+ ; set the scroll registers
+ ld a,[hl+] ; 85
+ ldh [rSCY],a ; 88
+ ld a,[hl+] ; 90
+ ldh [rSCX],a ; 93
+
+ pop hl ; 97
+ pop af ; 100
+ reti ; 104
+`,b={};function x(e){return e.getIsPending!==void 0}function S(e){if(x(e))return e;let t=!0,n=e.then(e=>(t=!1,e),e=>{throw t=!1,e});return n.getIsPending=function(){return t},n}function C(e){let t={},n=e.attributes;if(!n)return t;for(let e=n.length-1;e>=0;e--)t[n[e].name]=n[e].value;return t}function w(e){return Object.keys(e).reduce((t,n)=>(e[n]!==!1&&e[n]!==null&&e[n]!==void 0&&(t[n]=e[n]),t),{})}function T(e,t){let{class:n,style:r,...i}=C(e),{class:a,style:o,...s}=w(t);return{class:[n,a],style:[r,o],...i,...s}}var E=t({inheritAttrs:!1,__name:`InlineSvg`,props:{src:{},title:{default:void 0},transformSource:{type:Function,default:e=>e},keepDuringLoading:{type:Boolean,default:!0},uniqueIds:{type:[Boolean,String],default:!1},uniqueIdsBase:{default:``}},emits:[`loaded`,`unloaded`,`error`],setup(e,{expose:t,emit:r}){let a=e,o=r,l=d(),u=i(),p=i(),m=Math.random().toString(36).substring(2);t({svgElSource:u,request:p}),c(()=>a.src,e=>{_(e)}),_(a.src);function g(e){if(e=e.cloneNode(!0),a.uniqueIds){let t=typeof a.uniqueIds==`string`?a.uniqueIds:m;e=C(e,t,a.uniqueIdsBase)}return e=a.transformSource(e),a.title&&x(e,a.title),e.innerHTML}function _(e){b[e]||(b[e]=v(e)),u.value&&b[e].getIsPending()&&!a.keepDuringLoading&&(u.value=null,o(`unloaded`)),b[e].then(e=>{u.value=e,h(()=>{o(`loaded`,document.querySelector(`svg`))})}).catch(t=>{u.value&&(u.value=void 0,o(`unloaded`)),delete b[e],o(`error`,t)})}function v(e){return S(new Promise((t,n)=>{let r=new XMLHttpRequest;r.open(`GET`,e,!0),p.value=r,r.onload=()=>{if(r.status>=200&&r.status<400)try{let e=new DOMParser().parseFromString(r.responseText,`text/xml`).getElementsByTagName(`svg`)[0];e?t(e):n(Error(`Loaded file is not valid SVG"`))}catch(e){n(e)}else n(Error(`Error loading SVG`))},r.onerror=n,r.send()}))}let y=()=>u.value?s(`svg`,{...T(u.value,l),innerHTML:g(u.value)}):null;function x(e,t){let n=e.getElementsByTagName(`title`);if(n.length)n[0].textContent=t;else{let n=document.createElementNS(`http://www.w3.org/2000/svg`,`title`);n.textContent=t,e.insertBefore(n,e.firstChild)}}function C(e,t,n=``){let r=[`id`,`href`,`xlink:href`,`xlink:role`,`xlink:arcrole`],i=[`href`,`xlink:href`],a=(e,t)=>i.includes(e)&&(t?!t.includes(`#`):!1);return[...e.children].forEach(e=>{var i;if((i=e.attributes)!=null&&i.length){let i=Object.values(e.attributes).map(e=>{let r=/url\((.*?)\)/.exec(e.value);return r!=null&&r[1]&&(e.value=e.value.replace(r[0],`url(${n}${r[1]}_${t})`)),e});r.forEach(e=>{let n=i.find(t=>t.name===e);n&&!a(e,n.value)&&(n.value=`${n.value}_${t}`)})}return e.children.length?C(e,t,n):e}),e}return(e,t)=>(n(),f(y))}}),D=e=>e.split(/\r?\n/).map(e=>e.split(`;`)[0].trim()),O={"lyc_timing/stat_loop.asm":D(g),"lyc_timing/simple_handler.asm":D(_),"lyc_timing/ret_hblank_handler.asm":D(v),"lyc_timing/hybrid_handler.asm":D(y)},k=114,A=21,j=51,M={props:{offset:{type:Number,required:!0,validator(e){return e>=0&&e<=k}},hblankLength:{type:Number,default:A,validator(e){return e>=A&&e<=j}},asmFile:String},render(){let e=[20,k-this.$props.hblankLength,k],t=this.$props.asmFile,n;if(t&&(n=O[t],n===void 0))throw SyntaxError(`Unknown ASM file "${t}" (did you forget to register it in \`ASM_FILES\`?)`);let r=this.$slots.default(),i={},a=[],o=this.$props.offset,c=0;for(let l=0;lo3){let n=()=>`var(--ppu-mode${u(o)})`,r=t.props.rowspan-2,i=r,l=[],d=e.findIndex(e=>e>o);for(;e[d]-os(`tr`,{},[s(`td`,{class:`ppu-mode`+e.id},``+e.id),s(`td`,{},e.name)]))]),s(`table`,{},[s(`tr`,{},s(`th`,{colspan:2},`CPU operation`)),...Object.entries(i).sort(([e,t],[n,r])=>t.order-r.order).map(([e,{legend:t}])=>s(`tr`,{},[s(`td`,{class:e}),s(`td`,{},t)]))])]),s(`table`,{},[s(`thead`,{},l),s(`tfoot`,{},l),s(`tbody`,{class:`timeline`},a)])])}},N={props:{op:String,line:{type:Number,validator(e){return e>0}},immediate:Boolean,io:Boolean,taken:Boolean,class:String,legend:String,cycles:Number},info(e,t){if(e.line!==void 0){if(t===void 0)throw SyntaxError(`ASM line requested, but no ASM file was provided!`);e.instr=t[e.line-1]}e.instr!==void 0&&e.op===void 0&&(e.op=e.instr.split(/\s/)[0]);let n=(function(){switch(e.op){case`and`:return{cycles:1,instr:`and`};case`bit`:return{cycles:2,instr:`bit`,fixed:!1};case`call`:return{cycles:6,instr:`call`,class:`call`,fixed:!0};case`cp`:return{cycles:1,instr:`cp`};case`critical`:return{cycles:16,legend:`VRAM accesses`,fixed:!0};case`interrupt`:return{cycles:5,legend:`Interrupt dispatch`,fixed:!0};case`jr`:return{cycles:2,instr:`jr`,jump:!0};case`ld-imm16`:return{cycles:3,instr:`ld`,fixed:!0};case`ldh`:return{cycles:2,instr:`ldh`};case`ldhl`:return{cycles:2};case`or`:return{cycles:1,instr:`or`};case`pop`:return{cycles:3,instr:`pop`,fixed:!0};case`push`:return{cycles:4,instr:`push`,fixed:!0};case`reti`:return{cycles:4,instr:`reti`,legend:`Return from interrupt`,fixed:!0};case`set`:return{cycles:2,instr:`set`,fixed:!1};case`condensed`:let t=e.cycles;if(typeof t!=`number`||!isFinite(t))throw SyntaxError(`"Skip" CPU op requires a numeric cycle count`);return{cycles:t,class:`condensed`,class:`op`};default:throw SyntaxError(`Unknown instruction type "${e.op}"`)}})();if(e.instr&&(n.instr=e.instr),e.immediate!==void 0){if(n.fixed||n.jump)throw SyntaxError(`CPU op ${e.op} cannot be immediate!`);n.cycles++}if(e.taken!==void 0){if(!n.jump)throw SyntaxError(`CPU op ${e.op} is not a jump, it cannot be taken!`);n.cycles++}return e.legend&&(n.legend=e.legend),n.legend&&e.io===void 0&&(n.class=e.op),e.class&&(n.class=e.class),n}},P={components:{Timeline:M,CPUOp:N,InlineSvg:E}},F=JSON.parse(`{"path":"/guides/lyc_timing.html","title":"The Timing of LYC STAT Handlers","lang":"en-US","frontmatter":{"head":[["meta",{"name":"og:title","content":"Timing of LYC STAT Handlers"}],["meta",{"name":"og:description","content":"Guide on how to implement raster effects with the rLYC register."}]]},"git":{"contributors":[{"name":"Antonio Vivace","username":"","email":"avivace4@gmail.com","commits":1}],"changelog":[{"hash":"a3a0422fa3b8e16a67dc8a661e1fa6eae5cb0fa8","time":1792000514000,"email":"avivace4@gmail.com","author":"Antonio Vivace","message":"ci, docs: rebuild on awesome-gbdev changes, add CONTRIBUTING.md (#8)"}]},"filePathRelative":"guides/lyc_timing.md"}`),I={class:`hint-container warning`};function L(t,i,s,c,d,f){let m=r(`RouteLink`),h=r(`inline-svg`),g=r(`CPUOp`),_=r(`Timeline`);return n(),o(`div`,null,[i[5]||=e(`
Raster effects are probably the greatest assets that retro game consoles have. The fact that the PPU generates the image right as it is displayed allows many special effects to be created by modifying the rendering parameters while the image is being drawn. Here is an example:
However, unlike some consoles like the SNES, the Game Boy contains no hardware dedicated to raster effects, so the task falls squarely on the CPU. This causes raster FX code to interact with the rest of the program in complex ways, particularly when it comes to accessing VRAM.
In this article, we will explore different techniques for handling raster effects, and discuss their pros and cons with the help of some diagrams.
`,7),l(`div`,I,[i[3]||=l(`p`,{class:`hint-container-title`},`PRIOR KNOWLEDGE ASSUMED`,-1),l(`p`,null,[i[1]||=u(`This article is not a friendly introduction to programming raster effects, and assumes you are already comfortable with Game Boy programming. To learn more about how to achieve neat raster effects like the above, check out `,-1),p(m,{to:`/guides/deadcscroll.html`},{default:a(()=>[...i[0]||=[u(`DeadCScroll`,-1)]]),_:1}),i[2]||=u(` first, which the above GIF is actually from!`,-1)]),i[4]||=l(`p`,null,[u(`Additionally, since the operations discussed here are `),l(`em`,null,`extremely`),u(` timing-sensitive, discussions will revolve around assembly instructions. You can learn how to program for the Game Boy in assembly in `),l(`a`,{href:`https://eldred.fr/gb-asm-tutorial`,target:`_blank`,rel:`noopener noreferrer`},`GB ASM Tutorial`),u(`.`)],-1)]),i[6]||=e(`
TERMINOLOGY
We'll reference a few terms throughout this tutorial; here are brief explanations of them:
SoC: System-on-a-Chip, a single chip that includes most (or all!) components of a system. The Game Boy's functionality is almost entirely contained within a single chip, confusingly labelled "DMG-CPU" or similar. (Contrast this with, for example, the SNES, where there is one chip for the CPU, two for the PPU, and many more.)
CPU: Central Processing Unit, the part of the SoC that executes code and configures everything else.
PPU: Pixel Processing Unit, the part of the SoC that is responsible for sending pixels to the LCD and generating them.
Rasterization: the process of turning... something (for example, a collection of textured polygons; or, on the GB, tiles and tilemaps) into an array of pixels. "Raster" is sort of a contraction of that term.
Scanline: a row of pixels; it's called a "scan"-line because the lines get drawn one by one, pixel by pixel, as if the PPU was "scanning" along the screen.
Register: in general, a small piece of memory, usually linked to some hardware component.
PPU mode: The PPU can be in one of four modes at a given time, depending on what it's doing. Please refer to Pan Docs to learn what each mode corresponds to and how they are scheduled—they interact very tightly with raster effects.
Interrupt: an event that gets generated. Typically, this causes a "handler" to be called, which is a special routine dedicated to reacting to a given interrupt.
"Main thread": any code that is executed outside of interrupt handlers.
The Game Boy permanently compares the value of the LYC and LY registers. When both values are identical, the “LYC=LY” flag in the STAT register is set, and (if enabled) a STAT interrupt is requested.
So then, the outline for setting up a raster effect is as follows:
Register an interrupt by setting LYC to the desired scanline
When that scanline begins, the STAT interrupt handler will automatically be called
Perform your chosen effect by modifying PPU registers
Exit the handler with reti
ALTERNATIVES
There are other ways to perform raster FX, such as busy-waiting in the "main thread", but as this article's title suggests, we won't discuss them here.
A major pro of LYC-interrupt-based raster effects is that they can be made self-contained, and thus largely independent of whatever the "main thread" is doing. This, in turn, simplifies the mental complexity of the code (decoupling), copes better with lag frames, and more.
Many of the points brought forth later, particularly regarding cycle counting, are still relevant with these alternatives, so this is still worth reading!
These four steps sound simple enough on their own, but there are numerous caveats we will discuss. Strap in!
Most raster effects are implemented by modifying registers between scanlines. Thus, you will want to write the register either during Mode 2 (of the same scanline), or Mode 0 (of the previous one)—anything but Mode 3, really. Unfortunately, LY=LYC interrupts are requested at the beginning of a scanline, so during the very short Mode 2, leaving too little time to perform but the most basic of effects.
Writing to the register during HBlank instead implies triggering the interrupt on the scanline above the effect, as well as idling for most of the scanline. So, if I wanted to enable sprites on scanline 16, I'd write 15 to LYC.
Mode 3's length is variable, so syncing to HBlank is difficult and time-consuming.
The interrupt handler's execution may be delayed by a few cycles, which makes it difficult to reliably sync to the PPU.
If the "main thread" is itself trying to sync with the PPU (typically by polling STAT in a loop), our interrupt may throw off its timing.
A "dot" is one period of the PPU's 4 MiHz clock, i.e. 0.25 µs.
A "cycle" is the main unit of time in the CPU, which is equal to 1 µs, or 4 dots. (The Game Boy Color CPU can enter a "double-speed" mode which halves the length of cycles, but not of dots. For the sake of simplicity, we won't consider the differences it involves here.)
Each scanline takes exactly 456 dots, or 114 cycles.
Mode 2 also takes a constant amount of time (20 cycles)
HBlank's length varies wildly, and will often be nearly as long as or longer than the drawing phase.
HBlank and OAM scan are mostly interchangeable, and long as you're not writing to OAM.
The worst-case HBlank's length is not a multiple of 4 dots, so we will round down to 21 cycles.
Let's consider a simple STAT handler, which disables OBJs if called at line 128, and enables them otherwise:
LYC::
+ push af
+ ldh a, [rLY]
+ cp 128 - 1
+ jr z, .disableSprites
+
+ ; enable sprites
+ ldh a, [rLCDC]
+ or a, LCDCF_OBJON
+ ldh [rLCDC], a
+ pop af
+ reti
+
+.disableSprites
+ ldh a, [rLCDC]
+ and a, ~LCDCF_OBJON
+ ldh [rLCDC], a
+ pop af
+ reti
Tips
This is not an especially well-written STAT handler, but the actual is outside the scope of this tutorial. If that's what you're looking for, check out DeadCScroll by Blitter Object. It triggers the STAT interrupt on HBlanks rather than LYC, but the fundamentals are the same.
Note that, for simplicity's sake, DeadCScroll does not consider the problems described further below, so be wary of combining that tutorial's STAT handler unmodified with STAT-based VRAM accesses in the main thread.
Let's assume that the interrupt fires at, say, scanline 42. Equipped with the GB instruction table (see its legend at the bottom), we can plot how many cycles each operation takes, in relation with the PPU's mode:
`,6),p(_,{offset:0,asmFile:`lyc_timing/simple_handler.asm`},{default:a(()=>[p(g,{op:`interrupt`}),p(g,{line:2}),p(g,{line:3,immediate:``}),p(g,{line:4,immediate:``}),p(g,{line:5}),p(g,{line:8,immediate:``}),p(g,{line:9,immediate:``}),p(g,{line:10,immediate:``,io:``,legend:`Write to LCDC`}),p(g,{line:11}),p(g,{line:12})]),_:1}),i[8]||=e(`
The first 5 cycles do not have an instruction: indeed, calling an interrupt handler is not instantaneous, and the CPU is temporarily busy pushing the program counter (PC) to the stack, disabling interrupts, etc. Then, the actual interrupt handler begins execution.
We can immediately spot a problem: the cycle during which LCDC is written to falls in the middle of rendering! (With only a handful of exceptions, instructions that access memory do so on their very last cycle.) This is usually undesirable, and could lead to graphical glitches like an OBJ being partially cut off until we write to LCDC.
Another problem, less obvious but oh so painful, is how the interrupt handler might interact with the "main thread"'s operation.
Accessing VRAM is not possible during Mode 3. Thus, when we want to access VRAM, precautions must be taken; the most common is to use the following loop:
.waitVRAM
+ ldh a, [rSTAT]
+ and STATF_BUSY ; 2
+ jr nz, .waitVRAM
This loop checks whether [STAT] & 2 is zero, and exits when it does. Looking at documentation for STAT, we can see that the lowest 2 bits report the PPU's mode, and that [STAT] & 2 is zero for Mode 0 and Mode 1, but not Mode 2 or Mode 3. So, essentially, this loop waits for Mode 0 or Mode 1, which are both safe to write to VRAM—but it can't be that simple.
`,7),p(_,{offset:111,asmFile:`lyc_timing/stat_loop.asm`},{default:a(()=>[p(g,{line:2,immediate:``,io:``,legend:`Read from STAT`}),p(g,{line:3,immediate:``}),p(g,{line:4}),p(g,{op:`critical`}),p(g,{op:`condensed`,cycles:2})]),_:1}),i[9]||=l(`p`,null,[u(`Pictured above is the "worst case" for this loop. As you can see, on the cycle that `),l(`code`,null,`STAT`),u(` is read, the PPU is still in Mode 0; however, checking for it takes a few cycles, during which we enter Mode 2!`)],-1),i[10]||=l(`p`,null,[u(`Now, thankfully, Mode 2 is `),l(`em`,null,`also`),u(` safe for accessing VRAM—but only 16 cycles of it remain. This is why this loop is said to guarantee 16 "VRAM-safe" cycles: any access performed 17 cycles or more after it would break in this worst case.`)],-1),i[11]||=l(`p`,null,`Now, what would happen if our interrupt was requested in the middle of this?`,-1),p(_,{offset:111,asmFile:`lyc_timing/simple_handler.asm`},{default:a(()=>[p(g,{instr:`ldh a, [rSTAT]`,immediate:``,io:``,legend:`Read from STAT`}),p(g,{op:`interrupt`}),p(g,{line:2}),p(g,{line:3,immediate:``}),p(g,{line:4,immediate:``}),p(g,{line:5}),p(g,{line:8,immediate:``}),p(g,{line:9,immediate:``}),p(g,{line:10,immediate:``}),p(g,{line:11}),p(g,{line:12}),p(g,{instr:`and STATF_BUSY`,immediate:``}),p(g,{instr:`jr nz, .waitVRAM`}),p(g,{op:`critical`})]),_:1}),i[12]||=l(`p`,null,`Oh no! The main thread is now trying to access VRAM right in the middle of Mode 3! This could lead to all sorts of visual bugs.`,-1),i[13]||=l(`h3`,{id:`a-solution`,tabindex:`-1`},[l(`a`,{class:`header-anchor`,href:`#a-solution`},[l(`span`,null,`A solution?`)])],-1),i[14]||=l(`p`,null,[u(`The solution is not too complicated, at least on paper. We should be able to use the same `),l(`code`,null,`STAT`),u(`-checking loop (or at least, a variation of it) inside of the handler. It works in the main thread, so it should work here as well, right?`)],-1),i[15]||=l(`p`,null,[u(`Remember that many `),l(`code`,null,`STAT`),u(` handlers will be much more complicated than the simple example above, so let's draw a diagram with an imaginary handler that would take significantly more time:`)],-1),p(_,{offset:111},{default:a(()=>[p(g,{instr:`ldh a, [rSTAT]`,immediate:``,io:``,legend:`Read from STAT`}),p(g,{op:`interrupt`}),p(g,{op:`condensed`,cycles:92}),p(g,{instr:`ldh a, [rSTAT]`,immediate:``}),p(g,{instr:`and STATF_BUSY`,immediate:``}),p(g,{instr:`jr nz, .handlerWait`}),p(g,{instr:`(Write to LCDC)`,op:`ldhl`,immediate:``}),p(g,{instr:`pop hl`}),p(g,{instr:`pop af`}),p(g,{instr:`reti`}),p(g,{instr:`and STATF_BUSY`,immediate:``}),p(g,{instr:`jr nz, .waitVRAM`}),p(g,{op:`critical`})]),_:1}),i[16]||=e(`
Tips
All the instructions between the "Interrupt dispatch" and "Return from interrupt" blocks are the interrupt handler, the rest is in the "main thread".
The STAT loop does fix the register being written to during Mode 3; however, once again, the 16 cycles that "main thread" expects to be VRAM-safe overlap with Mode 3. The problem here is that the write, pop and reti all take some of those cycles, and the "main thread" is using the value it read from STAT during the previous scanline—but that value is now stale.
It's entirely possible to accept the loss of some of those cycles. This amounts to assuming less than the usual 16 cycles after such loops. For example, putting a STAT-polling loop just before the last pop af and reti would have these two eat up 7 cycles, so we are down to 9.
This will quickly become impractical, requiring syncing to the LCD much more often in the main thread.
A simple way to prevent those pesky handlers from throwing off our timing is to disable them, with the di instruction. Unfortunately, it can't quite be so simple, as using di for this brings its own share of problems.
The most important one is that disabling the handlers like this delays their execution! STAT handlers designed to write to hardware regs during HBlank may start doing so during rendering instead; timer interrupts won't trigger as regularly now; and so on.
Using di is valid in some cases, but typically not when STAT interrupts are involved, due to their fairly strict timing requirements.
An oddly common alternative is to perform all VRAM updates in VBlank handler. (The reason why it's common especially in early GB games is likely being a carry-over from the NES, where the lack of HBlanks essentially mandates such a setup anyway.) While this can work, such as for Metroid II, it requires significant complexity from having to keep deferring graphical updates.
This is the solution that the rest of this article will explore, as we will see that it makes the least painful compromises out of most use cases.
So, the real solution is to fully exit before the end of HBlank. There are two ways to do this. One is to wait for the Drawing phase before waiting for HBlank. This effectively catches the very start of HBlank, leaving plenty of time to exit. Here's how the earlier example might look using this method:
LYC::
+ push af
+ push hl
+ ldh a, [rLY]
+ cp 128 - 1
+ jr z, .disableSprites
+
+ ; enable sprites
+ ldh a, [rLCDC]
+ or a, LCDCF_OBJON
+ jr .finish
+
+.disableSprites
+ ldh a, [rLCDC]
+ and a, ~LCDCF_OBJON
+
+.finish
+ ld hl, rSTAT
+.waitNotBlank
+ bit STATB_BUSY, [hl]
+ jr z, .waitNotBlank
+.waitBlank
+ bit STATB_BUSY, [hl]
+ jr nz, .waitBlank
+
+ ldh [rLCDC], a
+ pop hl
+ pop af
+ reti
See how this method never interferes with VRAM accesses in the main thread, even with the worst possible timing and the shortest of HBlanks:
`,22),p(_,{offset:111,"hblank-length":21,asmFile:`lyc_timing/ret_hblank_handler.asm`},{default:a(()=>[p(g,{instr:`ldh a, [rSTAT]`,immediate:``}),p(g,{op:`interrupt`}),p(g,{line:2}),p(g,{line:3}),p(g,{line:4,immediate:``}),p(g,{line:5,immediate:``}),p(g,{line:6}),p(g,{line:9,immediate:``}),p(g,{line:10,immediate:``}),p(g,{line:11,taken:``}),p(g,{line:18,op:`ld-imm16`}),p(g,{line:20,immediate:``,io:``,legend:`STAT is tested`}),p(g,{line:21}),p(g,{line:23,immediate:``,io:``,legend:`STAT is tested`}),p(g,{line:24,taken:``}),p(g,{op:`condensed`,cycles:42}),p(g,{line:23,immediate:``,io:``,legend:`STAT is tested`}),p(g,{line:24,taken:``}),p(g,{line:23,immediate:``,io:``,legend:`STAT is tested`}),p(g,{line:24,taken:``}),p(g,{line:23,immediate:``,io:``,legend:`STAT is tested`}),p(g,{line:24}),p(g,{line:26,immediate:``}),p(g,{line:27}),p(g,{line:28}),p(g,{line:29}),p(g,{instr:`and STATF_BUSY`,immediate:``}),p(g,{instr:`jr nz, .waitVRAM`}),p(g,{op:`condensed`,cycles:16,class:`critical`,legend:`VRAM accesses`})]),_:1}),i[17]||=l(`p`,null,`Phew! This just barely works. There are only two cycles to spare! If there were multiple registers that needed updating, you might run into trouble. Normally, These really short HBlanks are the worst-case scenario that you always fear. However, in practice, HBlanks are normally much longer, often even longer than the drawing phase. Using this method, that can actually have unfortunate consequences:`,-1),p(_,{offset:111,"hblank-length":51,asmFile:`lyc_timing/ret_hblank_handler.asm`},{default:a(()=>[p(g,{instr:`ldh a, [rSTAT]`,immediate:``}),p(g,{op:`interrupt`}),p(g,{line:2}),p(g,{line:3}),p(g,{op:`condensed`,cycles:46}),p(g,{line:18,op:`ld-imm16`}),p(g,{line:20,immediate:``,io:``,legend:`STAT is tested`}),p(g,{line:21,taken:``}),p(g,{op:`condensed`,cycles:48}),p(g,{line:20,immediate:``,io:``,legend:`STAT is tested`}),p(g,{line:21}),p(g,{line:23,immediate:``,io:``,legend:`STAT is tested`}),p(g,{line:24,taken:``}),p(g,{op:`condensed`,cycles:48}),p(g,{line:23,immediate:``,io:``,legend:`STAT is tested`}),p(g,{line:24}),p(g,{line:26,immediate:``}),p(g,{line:27}),p(g,{line:28}),p(g,{line:29}),p(g,{instr:`and STATF_BUSY`,immediate:``}),p(g,{instr:`jr nz, .waitVRAM`}),p(g,{op:`condensed`,cycles:16,class:`critical`,legend:`VRAM accesses`})]),_:1}),i[18]||=e(`
This time, when all the processing was done, there was still plenty of time left in the scanline to safely exit. However, since HBlank was so long, the routine missed the check for the drawing window and wasted an entire scanline waiting for that Drawing -> HBlank transition before it exited. Not only does this waste precious CPU time, but it also limits how often raster FX can be used throughout the frame. This method still works fine though, and can be an easy approach if you use Raster FX sparingly.
I'm a bit of a perfectionist, so I usually like to strive for the absolute best method. In a perfect world, we would precisely know whether we have enough HBlank left to safely exit. There actually is a way to do that though! You just need to count exactly how long your routine takes, and make sure it always exits during HBlank. This comes with some caveats though. Most routines, if they haven't been specifically designed for this method, will take a variable amount of time. The main things you need to avoid are if statements and loops. Specifically, if statements of this form are problematic:
; test a condition here...
+
+ jr nc, .skip ; skip the next part unless Carry is set
+
+ ; do something here, only if the previous operation set Carry
+
+.skip
+ ; continue on with the program.
The problem here is that the code following this pattern may be run after a variable number of cycles have passed. If you need to use an if statement, always make it an if/else statement so that you can waste cycles in the else portion and take the same number of cycles.
So now that you're ready to count the cycles of your handler, how long do you need to make the routine? Let's look at some more diagrams to figure this out!
`,5),p(_,{offset:111},{default:a(()=>[p(g,{instr:`ldh a, [rSTAT]`,immediate:``,io:``,legend:`STAT read`}),p(g,{op:`interrupt`}),p(g,{op:`condensed`,cycles:105}),p(g,{instr:`reti`}),p(g,{instr:`and STATF_BUSY`,immediate:``}),p(g,{instr:`jr nz, .waitVRAM`}),p(g,{op:`condensed`,cycles:16,class:`critical`,legend:`VRAM accesses`})]),_:1}),i[19]||=l(`p`,null,[u(`Wow! That's a lot of cycles! Here, the routine takes exactly one scanline to complete, so the main thread does its writes at the same moment on the next scanline, with no idea what happened! If you count up all the cyan cycles, you'll see that there are 105 of them, and 109 if you count the `),l(`code`,null,`reti`),u(`. This extra time makes it possible to write to two or three registers safely, rather than just one. If you don't need all that time, you can make it shorter as well:`)],-1),p(_,{offset:107,"hblank-length":21},{default:a(()=>[p(g,{instr:`ldh a, [rSTAT]`,immediate:``,io:``,legend:`STAT read`}),p(g,{instr:`and STATF_BUSY`,immediate:``}),p(g,{instr:`jr nz, .waitVRAM`}),p(g,{op:`interrupt`}),p(g,{op:`condensed`,cycles:84}),p(g,{instr:`reti`}),p(g,{op:`condensed`,cycles:16,class:`critical`,legend:`VRAM accesses`})]),_:1}),i[20]||=l(`p`,null,[u(`This time, I put the `),l(`code`,null,`and`),u(` and `),l(`code`,null,`jr`),u(` before the interrupt, so that when it resumes, it's all ready to start writing to VRAM. This interrupt routine is 87 cycles long, including the `),l(`code`,null,`reti`),u(`. This won't often prove especially useful though, because you never take any time during HBlank to actually do any register writes. However, you could use this if your routine has a case where it realizes that nothing actually needs to be written, and you can exit earlier.`)],-1),i[21]||=l(`p`,null,[u(`From those two diagrams, you'll see that the 22 cycles of worst-case HBlank is the time you can use to write to any PPU registers, pop your registers back, and then exit with `),l(`code`,null,`reti`),u(`. These 22 cycles are cycle 88 through cycle 109, inclusive.`)],-1),i[22]||=l(`p`,null,`What if I told you that you could actually have your handler take only 86 cycles? Well, you can!`,-1),p(_,{offset:107,"hblank-length":21},{default:a(()=>[p(g,{instr:`ldh a, [rSTAT]`,immediate:``,io:``,legend:`STAT read`}),p(g,{instr:`and STATF_BUSY`,immediate:``}),p(g,{instr:`jr nz, .waitVRAM`}),p(g,{op:`interrupt`}),p(g,{op:`condensed`,cycles:83}),p(g,{instr:`reti`}),p(g,{op:`condensed`,cycles:16,class:`critical`,legend:`VRAM accesses`})]),_:1}),i[23]||=l(`p`,null,`This seems bad, since the first cycle of the red bar, where the main thread may try to access VRAM, is potentially during the Drawing phase! This is also fine though. All instructions that access memory, whether through an immediate address or using a register pair as a pointer, take multiple cycles to complete. That's because the first cycle of every instruction is used to fetch the operation code itself. The memory access that the instruction performs is always in the 2nd, 3rd or 4th cycle of the instruction. In this situation, the 2nd cycle of the VRAM-accessible time is in HBlank, so this won't actually cause any problems.`,-1),i[24]||=l(`h2`,{id:`but-wait`,tabindex:`-1`},[l(`a`,{class:`header-anchor`,href:`#but-wait`},[l(`span`,null,`But Wait!`)])],-1),i[25]||=l(`p`,null,`The interrupt latency I showed earlier doesn't actually tell the full story. Before it even starts to service the interrupt, the system waits for the current instruction to finish. This is how that might look with the longest allowable routine:`,-1),p(_,{offset:106,"hblank-length":51},{default:a(()=>[p(g,{instr:`ldh a, [rSTAT]`,immediate:``,io:``,legend:`STAT read`}),p(g,{instr:`and STATF_BUSY`,immediate:``}),p(g,{instr:`jr nz, .waitVRAM`}),p(g,{instr:`call SomeFunc`}),p(g,{op:`interrupt`}),p(g,{op:`condensed`,cycles:105}),p(g,{instr:`reti`}),p(g,{op:`condensed`,cycles:10,class:`critical`,legend:`VRAM accesses`})]),_:1}),i[26]||=e(`
Here, the first green block shows the system waiting 5 cycles for a call instruction to finish. call is the longest instruction at 6 cycles, so if the interrupt is requested just after it begins, the system will wait 5 cycles for it to complete. This seems bad, since the routine exited after the end of HBlank. However, this is actually fine! Those waiting cycles were not wasted; they were still 5 cycles of work that the main thread got done. So in the end, the main thread still gets its 20 cycles of VRAM-accessible time.
leaves more time for more complex logic in the routine
allows enough time during blanking to write to up to three registers
never takes longer than one scanline
Cons
requires all code to be constant-time
requires tedious cycle-counting
always takes close to an entire scanline, even if HBlank starts much sooner
This suggests that the double-busy-loop method is good for extremely simple LYC routines that only need to write to one register, or routines that for some reason cannot be cycle-counted. If you need more time for calculations and more time to write to those registers, you can cycle-count your routine.
But what if you could combine both these methods? Enter the Hybrid Cycle-Counted Handler™, a technique I came up with while writing this document.
The goal of this method is to combine the maximum HBlank time that cycle-counting delivers, while still exiting early when HBlank is longer. Here is an example. If you've read DeadCScroll, you'll recognise this as that tutorial's STAT Handler, modified to start at Mode 2 rather than HBlank, and be safe towards VRAM accesses in the main thread.
push af ; 4
+ push hl ; 8
+
+ ; obtain the pointer to the data pair
+ ldh a, [rLY] ; 11
+ inc a ; 12
+ add a, a ; 13 ; double the offset since each line uses 2 bytes
+ ld l, a ; 14
+ ldh a, [hDrawBuffer] ; 17
+ adc 0 ; 19
+ ld h, a ; 20 ; hl now points to somewhere in the draw buffer
+
+ call UnconditionalRet ; just waste 31 cycles while we wait for HBlank to maybe start
+ call UnconditionalRet
+ call UnconditionalRet
+ nop ; 51
+
+ ; now start trying to look for HBlank to exit early
+
+ ldh a, [rSTAT]
+ and STATF_BUSY
+ jr z, .setAndExit ; 58
+
+ ldh a, [rSTAT]
+ and STATF_BUSY
+ jr z, .setAndExit ; 65
+
+ ldh a, [rSTAT]
+ and STATF_BUSY
+ jr z, .setAndExit ; 72
+
+ ldh a, [rSTAT]
+ and STATF_BUSY
+ jr z, .setAndExit ; 79
+
+ nop ; waste 4 more cycles since there isn't time for another check
+ nop
+ nop
+ nop ; 83
+
+.setAndExit
+ ; set the scroll registers
+ ld a,[hl+] ; 85
+ ldh [rSCY],a ; 88
+ ld a,[hl+] ; 90
+ ldh [rSCX],a ; 93
+
+ pop hl ; 97
+ pop af ; 100
+ reti ; 104
Once the handler finishes its logic, the handler delays cycles until it reaches the window then HBlank might start. With a 5-cycle offset due to a call, and the longest possible HBlank, the earliest HBlank might start is cycle 54, so that's the first attempt to read STAT. It keeps checking STAT until even in the worst-case scenario, it knows that HBlank will start. Then, it uses that time to write the scroll registers and exit. This way, it can still exit early, as long as the HBlank length permits. This routine takes 104 cycles in the worst-case scenario, but may take as few as 79 if HBlank comes sooner.
The reason that the double-busy-loop method requires checking for Mode 3 but this method does not is that the double-busy-loop method is not cycle-counted, so you might be at the very end of HBlank which is problematic. Since this method is cycle-counted, you know that if HBlank has begun, you are at or near the start of it.
If we make a similar list of pros and cons for this method, this is what it might look like:
allows enough time during blanking to write to up to three registers
never takes longer than one scanline
Cons
requires all code to be constant-time
requires tedious cycle-counting
This method can work well in many circumstances, and is especially suited to frequent effects that modify multiple registers and need to exit quickly to avoid taking too much CPU time. This method can even work reasonably well when used on every scanline through the Mode 2 interrupt.
All three of these methods can generate great-looking effects, but I think the third one is an especially attractive option.
Congrats! You made it to the end of the tutorial! I bet you're tired of reading it, and I'm tired of writing it too. So thanks for reading, see you next time!
`,29)])}var R=m(P,[[`render`,L]]);export{F as _pageData,R as default};
\ No newline at end of file
diff --git a/assets/meetings-CS6rrLzv.js b/assets/meetings-CS6rrLzv.js
new file mode 100644
index 00000000..c5f21e49
--- /dev/null
+++ b/assets/meetings-CS6rrLzv.js
@@ -0,0 +1 @@
+import{d as e,f as t,h as n,i as r,n as i,o as a,s as o,t as s}from"./app-oZCJRxfU.js";var c=JSON.parse(`{"path":"/meetings.html","title":"Meetings minutes","lang":"en-US","frontmatter":{"sidebar":false},"git":{"contributors":[{"name":"Antonio Vivace","username":"","email":"avivace4@gmail.com","commits":1}],"changelog":[{"hash":"a3a0422fa3b8e16a67dc8a661e1fa6eae5cb0fa8","time":1792000514000,"email":"avivace4@gmail.com","author":"Antonio Vivace","message":"ci, docs: rebuild on awesome-gbdev changes, add CONTRIBUTING.md (#8)"}]},"filePathRelative":"meetings.md"}`),l={name:`meetings.md`};function u(s,c,l,u,d,f){let p=t(`RouteLink`);return e(),r(`div`,null,[c[2]||=i(`h1`,{id:`meetings-minutes`,tabindex:`-1`},[i(`a`,{class:`header-anchor`,href:`#meetings-minutes`},[i(`span`,null,`Meetings minutes`)])],-1),i(`ul`,null,[i(`li`,null,[o(p,{to:`/meetings/2024-03-10-sc2.html`},{default:n(()=>[...c[0]||=[a(`March 10, 2024`,-1)]]),_:1})]),i(`li`,null,[o(p,{to:`/meetings/2023-11-04-sc.html`},{default:n(()=>[...c[1]||=[a(`November 4th, 2023`,-1)]]),_:1})])])])}var d=s(l,[[`render`,u]]);export{c as _pageData,d as default};
\ No newline at end of file
diff --git a/assets/newsletter-DRJ_Z2sO.js b/assets/newsletter-DRJ_Z2sO.js
new file mode 100644
index 00000000..b80e3eb6
--- /dev/null
+++ b/assets/newsletter-DRJ_Z2sO.js
@@ -0,0 +1 @@
+import{a as e,d as t,i as n,t as r}from"./app-oZCJRxfU.js";var i=JSON.parse(`{"path":"/newsletter.html","title":"Newsletter","lang":"en-US","frontmatter":{"sidebar":false,"editLink":false},"git":{"contributors":[{"name":"Antonio Vivace","username":"","email":"avivace4@gmail.com","commits":1}],"changelog":[{"hash":"a3a0422fa3b8e16a67dc8a661e1fa6eae5cb0fa8","time":1792000514000,"email":"avivace4@gmail.com","author":"Antonio Vivace","message":"ci, docs: rebuild on awesome-gbdev changes, add CONTRIBUTING.md (#8)"}]},"filePathRelative":"newsletter.md"}`),a={name:`newsletter.md`};function o(r,i,a,o,s,c){return t(),n(`div`,null,[...i[0]||=[e(`
`,2)]])}var s=r(a,[[`render`,o]]);export{i as _pageData,s as default};
\ No newline at end of file
diff --git a/assets/privacypolicy-C30LFIF4.js b/assets/privacypolicy-C30LFIF4.js
new file mode 100644
index 00000000..b2122163
--- /dev/null
+++ b/assets/privacypolicy-C30LFIF4.js
@@ -0,0 +1 @@
+import{a as e,d as t,i as n,t as r}from"./app-oZCJRxfU.js";var i=JSON.parse(`{"path":"/privacypolicy.html","title":"Privacy Policy","lang":"en-US","frontmatter":{"sidebar":false,"editLink":false},"git":{"contributors":[{"name":"Antonio Vivace","username":"","email":"avivace4@gmail.com","commits":1}],"changelog":[{"hash":"a3a0422fa3b8e16a67dc8a661e1fa6eae5cb0fa8","time":1792000514000,"email":"avivace4@gmail.com","author":"Antonio Vivace","message":"ci, docs: rebuild on awesome-gbdev changes, add CONTRIBUTING.md (#8)"}]},"filePathRelative":"privacypolicy.md"}`),a={name:`privacypolicy.md`};function o(r,i,a,o,s,c){return t(),n(`div`,null,[...i[0]||=[e(`
We use a self hosted instance of Matomo, a free and open source web analytics application to track online visits to our websites (*.gbdev.io, *.gbadev.net), in order to better understand which audience we serve and what content is consulted.
`,5)]])}var s=r(a,[[`render`,o]]);export{i as _pageData,s as default};
\ No newline at end of file
diff --git a/assets/resources-D802ex1B.js b/assets/resources-D802ex1B.js
new file mode 100644
index 00000000..f7a0e6ab
--- /dev/null
+++ b/assets/resources-D802ex1B.js
@@ -0,0 +1 @@
+import{a as e,d as t,i as n,n as r,t as i}from"./app-oZCJRxfU.js";var a=JSON.parse(`{"path":"/resources.html","title":"Resources","lang":"en-US","frontmatter":{"title":"Resources","lang":"en-US","head":[["meta",{"name":"og:title","content":"Game Boy dev resources"}],["meta",{"name":"og:description","content":"A curated list of awesome Development resources, tools, docs, related projects and homebrews."}]]},"git":{},"filePathRelative":"resources.md"}`),o={name:`resources.md`};function s(i,a,o,s,c,l){return t(),n(`div`,null,[...a[0]||=[e(`
Awesome Game Boy Development
A curated list of awesome Game Boy (Color) Development resources, tools, docs, related projects and homebrews. Inspired by the awesome list thing.
This project is open source and community-lead. Come contribute!
`,10),r(`div`,{class:`videoWrapper`},[r(`iframe`,{id:`ytplayer`,type:`text/html`,src:`https://www.youtube.com/embed/videoseries?list=PLu3xpmdUP-GRDp8tknpXC_Y4RUQtMMqEu`,frameborder:`0`,allowfullscreen:``}),r(`br`)],-1),r(`h3`,{id:`the-ultimate-game-boy-talk`,tabindex:`-1`},[r(`a`,{class:`header-anchor`,href:`#the-ultimate-game-boy-talk`},[r(`span`,null,[r(`a`,{href:`https://media.ccc.de/v/33c3-8029-the_ultimate_game_boy_talk`,target:`_blank`,rel:`noopener noreferrer`},`The Ultimate Game Boy Talk`)])])],-1),r(`br`,null,null,-1),r(`div`,{class:`videoWrapper`},[r(`iframe`,{id:`ytplayer`,type:`text/html`,src:`https://www.youtube.com/embed/HyzD8pNlpwI`,frameborder:`0`,allowfullscreen:``}),r(`br`)],-1),e(`
Game Boy Advance development is covered by another project, the awesome-GBAdev list. GBA, however, can run GB/GBC games. It does so in a slightly different way compared to native hardware. This is covered in the Emulator Development section of this list.
This list is focused on the original (1989) Game Boy (DMG). Game Boy Color (GBC) and Super Game Boy (SGB) are very similar systems, with a few important distinctions, such as:
Different hardware specifications
Specific hardware and software features
Specific registers
Specific bugs, quirks and exploitable behaviours
If you aim to develop your software for SGB or GBC, or you want to know how it runs on the other systems, you may want to take advantage and adapt to these differences, check the Game Boy Color category and look for specific references to GBC/CGB and SGB.
Resources marked with † are developed with AI/LLM usage, resources marked with ‡ were mostly generated by AI. See AI disclosure.
Game Boy Project Report - Report of an hardware emulator (on a Terasic DE1-SoC Board) developed as final project for the CSEE4840 Embedded Systems Design course at Columbia University.
RGBDS opcodes reference - A reference of all instructions, including short descriptions, cycle and byte counts, and explanations of flag modifications.
The Game Boy Project - Provides a study on the hardware and detailed constructional information for the implementation of three 8-bit bidirectional parallel ports.
dmg-schematics - Schematics and annotated overlay for the DMG-CPU B chip, extracted from die photos, made with KiCad. Also contains Electric VLSI library with layouts for some of the cells and memories.
Dan Docs - Obscure Game Boy hardware documentation.
Edge of Emulation, a series of articles about emulating and investigating Game Boy accessories. Also available as technical documents in the GBE- emulator documentation.
Mobile Adapter GB - Internet connectivity and DLC on the Game Boy Color.
About swotGB - Notes about the development of a Game Boy emulator in JavaScript.
Game Boy Doctor - A command line tool for comparing logs from your emulator to those from a known-correct one. Useful for line-by-line debugging of Blargg's test ROMs.
DraconicGB: Creating a Game Boy Emulator - Bachelor's thesis (UOC, 2020) documenting the research, architecture and C++ implementation of a Game Boy emulator, with an overview of the hardware and of existing emulators.
GBDK - Maintained and modernized GBDK (Game Boy Development Kit) powered by an updated version of the SDCC toolchain. Provides a C compiler, assembler, linker and a set of libraries.
Turbo Rascal Syntax Error - Complete suite (IDE, compiler, programming language, resource editor) intended for developing games/demos for 8 / 16-bit line of computers, including the Game Boy and Game Boy Color.
LLVM-Z80 † - A fork of LLVM with a backend for the Game Boy CPU (SM83), supporting the latest C standards and LLVM’s excellent optimizations.
MetroBoy - A playable, circuit-level simulation of an entire Game Boy.
gbe-plus - A recently rewritten emulator that has a large effort in preserving the functions of obscure accessories (such as IR link, Mobile Network GB, Barcode Boy, GB Printer, local and online GB Serial Link Cable, ... )
Emulicious - Provides accurate emulation and includes powerful tools such as a profiler and source-level debugging for ASM and C via a VS Code debug adapter.
Game Boy Tile Data Generator - HTML5 / JS web application that will convert bitmap images to hexadecimal data appropriate for use in tile based graphical applications, specifically GB.
Harry Mulder's GB Development - Some sources and home of Game Boy Tile Designer (GBTD) and Game Boy Map Builder (GBMB) tools.
GBTiles - Converts .GBR files created with Harry Mulder's Tile Designer (GBTD) and .GBM files created with Harry Mulder's Map Builder (GBMB) to different formats for use with the Game Boy and GBDK.
bmp2cgb - Graphics converter for Game Boy Color development providing real time palette adjustments.
png2gb - CLI tool to convert image file to game boy .c array.
GB-convert - Game Boy tile conversion and map editor tool (converts to assembly).
brewtool - A collection of primitive editor/converter tools for making assets used with homebrew ROM development.
tpp1 - Definition and specification of a custom GB/GBC memory/hardware mapper, as a functional superset of MBC.
libstdgb - A C library of useful Game Boy operations (SDCC).
Tilemap GB - GIMP image editor plug-in for importing & exporting GBMB and GBTD tilemaps and tilesets (as bitmap images or .GBM/.GBR files).
Tilemap Helper - GIMP image editor plug-in for optimizing tile maps and tile sets.
Tilemap Studio - A tilemap editor for Game Boy, Color, Advance, and SNES projects. Written in C++ with FLTK.
Superfamiconv - Flexible and composable tile graphics converter supporting Super Nintendo, Game Boy, Game Boy Color, Game Boy Advance, Mega Drive and PC Engine formats.
Tessarium ‡ - Tile-first pixel art level editor with linked tile editing, shared tilesets, and live unique tile and colours-per-tile budget checks for Game Boy and Game Boy Color backgrounds.
gb asm tutorial - Step by step tutorial, building several ROMs to accompany its explanations.
hardware.inc - Standard include file containing Game Boy hardware definitions for use in RGBDS projects.
Assembly tutorial by David Pello - Good document to learn to produce working asm code for gb. Brief explanations of many important topics. Many examples with commented source code.
assemblydigest - Exploring Game Boy programming techniques:
EmmaEwert's experiments - A collection of prototype programs, mostly just toying around. Among others, a daylight effect, transparency and a weather effect.
DeadCScroll - A detailed tutorial on how to make the screen wobble, among other "raster effects"
DOTs for GBC - Making of a Game Boy Color port of the Dots game in C with GBDK, from code to a physical cartridge with box art. Includes the source code and a one-hour development commentary video.
Homebrew Hub - A community-led attempt to collect, archive and preserve every unlicensed and homebrew game released for Game Boy. Entries are playable online.
waveform-gb - Program visualizing the wave form used by the wave channel. The wave form can be edited freely and playback of the wave is updated immediately.
Evoland.gb - A port of the first level of Evoland.
Petris - A puzzle game of shapely pets for the Game Boy Color (itch.io).
Infinity - RPG developed by Affinix Software primarily between the years 1999 and 2001. The game never found a publisher and was eventually canceled. Got recently released with the full source, development tools and workflows.
matrix-rain-gb - A Matrix digital rain effect in assembler.
GBVideoPlayer - A technical demo demonstrating how the Game Boy LCD controller can be hacked to make a Game Boy Color play a full motion video in color, together with music.
GBVideoPlayer2 - The second iteration of the above demo, which increases the resolution, adds stereo PCM audio, and introduces video compression.
DMA hijacking - A simple technique that allows you to run custom code in most GB/SGB/CGB games, provided you have an ACE exploit.
Super Mario Land 2 - Memory Exploration - Video by Retro Game Mechanics Explained on how the game stores levels in memory, and what happens when Mario goes out of bounds.
Lets Build a Platformer Game! - A course designed to teach anyone how to create a platformer game using GB Studio 4+.
ArduinoBoy - Serial communication (MIDI) from an Arduino to the Game Boy for music applications such as LittleSoundDJ, Nanoloop, and mGB.
papiGB - Game Boy Classic fully functional FPGA implementation from scratch.
fpgaboy - Implementation Nintendo's Game Boy console on an FPGA.
Piglet - A LUA-driven AI that plays classic Game Boy color games using experimentation. In active development.
Ostrich - A Game Boy Sound System player written in Swift.
mGB - A Game Boy cartridge program that enables the Game Boy to act as a full MIDI supported sound module.
GBVisualizer - Demonstrating the use of two undocumented Game Boy Color registers, nicknamed PCM12 (FF76) and PCM34 (FF77), which can be used to read the current PCM amplitude of the 4 APU channels.
ArduinoGameBoy - Arduino based Game Boy cartridge reader and writer.
Nintendo's fake logos - Every cartridge has to show the authentic logo to be considered valid and be run, but obviously some companies managed to exploit the check system.
liblsdj - Utility library for interacting with the LSDj save format (.sav), song files (.lsdsng) and more.
lsdpatch - Tool for modifying samples, fonts and palettes on LSDj ROM images.
Game Boy video effects - Some little experiments using the STAT interrupt to do funny video manipulations.
The Game Boy Archive - A library of Game Boy related software, hardware and literature. Aimed to mirror and preserve old and fragmented contributions from the last three decades.
Handheld Underground - Unlicensed games, blog posts about Game Boy, home of the hhugboy emulator.
`,91)]])}var c=i(o,[[`render`,s]]);export{a as _pageData,c as default};
\ No newline at end of file
diff --git a/assets/sgb_border-DremF4EG.js b/assets/sgb_border-DremF4EG.js
new file mode 100644
index 00000000..b37a49b1
--- /dev/null
+++ b/assets/sgb_border-DremF4EG.js
@@ -0,0 +1,51 @@
+import{a as e,d as t,i as n,t as r}from"./app-oZCJRxfU.js";var i=JSON.parse(`{"path":"/guides/sgb_border.html","title":"Adding a custom SGB border","lang":"en-US","frontmatter":{},"git":{"contributors":[{"name":"Antonio Vivace","username":"","email":"avivace4@gmail.com","commits":1}],"changelog":[{"hash":"a3a0422fa3b8e16a67dc8a661e1fa6eae5cb0fa8","time":1792000514000,"email":"avivace4@gmail.com","author":"Antonio Vivace","message":"ci, docs: rebuild on awesome-gbdev changes, add CONTRIBUTING.md (#8)"}]},"filePathRelative":"guides/sgb_border.md"}`),a={name:`sgb_border.md`};function o(r,i,a,o,s,c){return t(),n(`div`,null,[...i[0]||=[e(`
Before we can do anything else, we must first specify in the header that this game is aware of SGB features. Otherwise, the SGB BIOS will ignore any packets we send, and we won't even be able to detect when the program is running on SGB.
The SGB BIOS can be "talked to" via command packets, sent bit by bit via the P1/JOYP register.
An SGB packet consists of:
a "start" pulse (P1=%xx00xxxx)
128 data pulses (P1=%xx01xxxx for "1", P1=%xx10xxxx for "0")
a "0" pulse (P1=%xx10xxxx)
You must set P1 to %xx11xxxx between each pulse.
This adds up to 16 bytes of data (LSB first). If a packet doesn't read all 16 bytes, the unused bytes are ignored.
You should wait 4 frames between each packet and the next. This gives the SGB BIOS a chance to receive a packet even if it is doing something else time-consuming.
Bulk transfer (TRN) packets tell the SGB to copy the contents of the screen to buffers in Super NES work RAM. The CHR_TRN and PCT_TRN packets are used to send data for SGB borders.
For a transfer to function properly, you must prepare VRAM and the LCD registers:
set BGP to $e4 and LCDC to $91 (screen enabled, BG uses tiles $8000-$8fff and tilemap $9800, WIN and OBJ disabled, BG enabled)
set SCX and SCY to $00
the tilemap consists of $00, $01..$13, 12 bytes padding (offscreen), $14..$27, padding, repeat until $ff (inclusive)
the data you want to send must be loaded at $8000-$8fff
You must load the data into VRAM and enable the screen before sending the TRN packet. The SGB reads TRN payloads from the screen. If rendering is off, there is nothing on the screen to read.
You must wait ~8 frames after each TRN instead of just 4. The SGB BIOS has to finish what it's doing, receive the packet, and then read the screen.
Here's how a SGB detection routine should look like:
Wait 12 or more frames for the SGB BIOS to start listening for packets.
Send a MLT_REQ packet selecting 2 or 4 players ($89, $01 or $89, $03), and wait a couple frames for the SGB to receive the packet.
Read the controller. Set P1 bit 5 to 0, then set P1 bits 5 and 4 to %11 (%xx11xxxx) to release the key matrix.
Read the low nibble (bits 3-0) of P1, and look for a value other than %1111 (which indicates player 1).
If player 1 was still found, repeat steps 3 and 4 once more, in case the next read indicates player 2.
Optionally turn off multiplayer mode.
If a non-%1111 value was found in step 4 either time, the program is running on SGB.
A routine like this may be used to detect SGB (modified from source):
SGB_Detect:
+ ; test for SGB
+ di
+ call SGB_Wait4Frames
+ call SGB_Wait4Frames
+ call SGB_Wait4Frames
+
+ ; Send MLT_REQ packet to enable multiplayer
+ ld hl, Packets.mltOn ; send MLT_REQ for 2 players
+ call SGB_SendPacket
+ call SGB_Wait4Frames
+
+ ; Detect the SGB by checking if SGB responded to MLT_REQ.
+ ; Setting P1.5 to low then high advances the selected player.
+ ; Setting P1.4 and P1.5 high causes the ICD2 to reflect the
+ ; selected player in low bits of P1.
+ ld b, 4 ; Number of attempts
+ ld c, LOW(rP1) ; Address to write
+ .loop
+ ld a, P1F_4 ; Try to advance player
+ ldh [c], a
+ ld a, P1F_4|P1F_5 ; Set P1 to return the selected player
+ ldh [c], a
+ ; In case this is DMG and not SGB, let the input lines settle
+ ; for a few cycles before reading P1 again
+ call SGB_Wait4Frames.knownRet
+
+ ldh a, [c] ; Player 1 has A.0 = 1; players 2 and 4 have A.0 = 0
+ cpl ; Invert this
+ and %00000001 ; Keep only bit 0, which is 0 for player 1 or nonzero for players 2 and 4
+ jr nz, .done
+ dec b ; Keep trying until we've cycled through all players
+ jr nz, .loop
+ .done
+ ; After this loop, A is $01 for SGB or $00 for not SGB. Remember this
+ ld [wIsSGB], a
+
+ ; (Optional) Disable multiplayer
+ ld hl, Packets.mltOff ; send MLT_REQ for 2 players
+ call nz, SGB_SendPacket
+ ret
+
+SGB_Wait4Frames:
+ ld bc, -(456 * 154 / 16 * 4)
+ .loop
+ inc c ; inner loop takes 16 T-states per iteration
+ jr nz, .loop
+ inc b
+ jr nz, .loop
+ .knownRet
+ ret
It would be a good idea to save somewhere in RAM whether the game is running on an SGB capable device or not, such that if you wish to change the border mid-gameplay, you won't have to perform SGB detection again. The sample code stores it in wIsSGB.
If you wish to only use 1 controller for the game, you will have to send another MLT_REQ to disable multiplayer ($89, $00)
SGB_Wait4Frames above uses busy waiting. Depending on the structure of your initialization code, you can change it to use vblank interrupts or di+halt instead.
--color-zero should be the color that your image for transparency, in my case it was blue.
If your image has an alpha channel, it can be set can also be set to 00000000 to use the actual transparent color; however, this may cause some issues.
-v is optional, for showing details of the conversion process
You can add a row of the transparent color at the top of the image to force superfamiconv to make it tile #0, then incbin "output.pct", 64 to leave out that row.
-P 4 sets the base palette to the 4th one, and SGB borders use SNES palettes 4, 5, and 6. as of writing this, this option only works if you built superfamiconv from source.
As stated before, the SGB border consists of tile data, picture data, and palette data. These are split across 2-3 packets:
CHR_TRN ($99) is used to send 4KiB of tile data.
since the border can use up to 8KiB of tiles, bit 0 of the second byte specifies which "half" you're sending
$99, $00 if the screen is loaded with the first 4KiB of tile data
$99, $01 if the screen is loaded with the second 4KiB of tile data
PCT_TRN ($a1) is used to send the picture and palette data. it also swaps the border, generally a good idea to send it after the tile data[^1]
assuming tiles 0-255 use VRAM from $8000 to $8fff:
the picture data must be at $8000-$873f (last 64 bytes are usually offscreen, see notes)
palette data must be at $8800-$885f
everything else is ignored
how you skip putting data at $8740-$87ff is up to you, I prefer doing separate copies, others prefer copying tilemap and palette data in one go, with the area between them padded.
[^1]: You can send a CHR_TRNup to ~60 frames after the PCT_TRN for it to apply to the current border, but not all emulators will emulate this. It's fine to just pretend CHR_TRNs must go before PCT_TRN.
You can set the first row of tiles to your transparent color to force superfamiconv to put the transparent tile as the 1st tile, however you must then exclude 64 bytes of the tilemap (incbin "border.pct" -> incbin "border.pct", 64)
SGB BIOS reserves palettes 4 through 6 for borders. If you really know what you're doing, you may be able to use palette 0 (the gameplay palette) for animated borders. You will probably have to edit the border in a tile editor such as YY-CHR, as there aren't yet any other tools for that.
When the SNES lags, scanline 225 of the SGB border will be visible! You can set the topmost row of the 29th row of tiles to black to hide this.
If this doesn't work for you, you can ask for help on the gbdev channels.
This essay gives an overview of the Game Boy's capabilities, discussing the pros and cons of the available development tools, and providing a few tips to write more efficient code.
In the past few years as retro gaming has grown in popularity, programming for older platforms has also gained traction. A popular platform is the Game Boy, both for its nostalgia and (relative) ease to program for.
Warning
This document only applies to the Game Boy and Game Boy Color. Game Boy Advance programming has little in common with Game Boy programming.
If you want to program for the GBA, which is much more C-friendly (and C++ and Rust, for that matter) than the GB and GBC, then I advise you to download devkitARM and follow the Tonc tutorial. Please note that the Game Boy Advance also functions as a Game Boy Color, so if you only have a GBA, you can use it for both GB and GBC development.
When someone wants to make their own game, one of the first problems they will encounter is picking the tools they will use. There current main options are:
RGBDS (Rednex Game Boy Development System) and the Game Boy's Assembly language (ASM)
GBDK-2020 (Game Boy Development Kit) and the C language
ZGB (an engine built on GBDK-2020) and the C language
GB Studio (a drag-and-drop game creator with scripting)
The purpose of this document is to provide some insights and help you make the better choice if you're starting a new project. I will also provide some "good practice" tips, both for C and ASM, if you have already made up your mind or are already using one of these.
The original Game Boy, codenamed the DMG, has a 1 MHz CPU [the CPU is actually clocked at 4 MHz, but every instruction takes up a multiple of 4 clocks, so it's often simplified to a 1 MHz CPU]. Given that an instruction takes approximately 2 to 3 cycles, this gives the CPU a capacity of 333,000~500,000 instructions per second. Its LCD boasts 60 fps [it's actually 59.73 fps], which rounds up to between 50,000 and 80,000 instructions per frame. Suddenly not so much, eh? It also has 8 kB of RAM, and 8 kB of video RAM ; a 160x144 px LCD (thus slightly wider than it's tall), 4 colors, and 4-channel audio.
The Super Game Boy adds a few minor things, such as a customizable screen border, and some crude color. It's also slightly faster than the DMG.
The Game Boy Color can [if you tell it to] unlock additional functionality, such as more fleshed-out color, a double-speed CPU, twice the video RAM and four times the RAM! (With caveats, obviously.)
The choice of programming language is important and can have a very large effect on a project. It determines how much work is involved, what will be possible, and how fast it will be able to run.
C will typically be used with the SDCC compiler and GBDK-2020 or ZGB, though it can also be used on its own without a framework or with a different compiler/dev kit (such as z88dk).
Strengths:
Allows for getting up and running faster than with ASM, especially when building on top of GBDK-2020 and ZGB.
The language abstractions make it relatively easy to implement ideas and algorithms.
C source debugging is available through Emulicious with the VSCode debug adapter, making it easier to understand problems if they arise.
ASM can be included in projects with C, either standalone or inline for speed critical features.
Weaknesses:
The SDCC C compiler won't always generate code that runs as fast as skilled, hand-optimized assembly. It has matured a lot in the 20 years since the original GBDK, but bugs still turns up on occasion. On a platform with a slow CPU such as the Game Boy this can be a factor.
It’s easier to write inefficient code in C without realizing it. The Game Boy's CPU is only capable of performing 8-bit addition or subtraction, or 16-bit addition. Using INT32s is quite taxing on the CPU (it needs to do two consecutive 16-bit adds, and add the carry). See the tips below to avoid such blunders.
There is overhead due to C being a stack-oriented language, whereas the Game Boy's CPU is rather built for a register-oriented strategy. This most notably makes passing function arguments a lot slower, although SDCC has some optimizations for this.
Using a GUI instead- If you don’t want to learn a programming language in order to make Game Boy games, then GB Studio is an option. See the GB Studio section for more details.
RGBDS is an actively maintained suite of programs that allow building a ROM using ASM (assembly). It contains three programs that perform different stages of the compilation, as well as a program that converts PNG images to the Game Boy's tile format. RGBDS is available for Linux, Windows and MacOS.
Strengths:
Very knowledgeable community with a lot of history.
Built in support for ROM banking.
Works quite well with BGB for debugging.
Weaknesses:
Provides a limited amount of built-in code and functionality (does not include a large API like GBDK-2020 does).
GBDK-2020 is a development kit and toolchain built around the SDCC C compiler which allows you to write programs in C and build ROMs. It includes an API for interfacing with the Game Boy. GBDK-2020 is a modernized version of the original GBDK. It's available for Linux, Windows and MacOS.
Strengths:
Flexible and extensible.
Comprehensive API that covers most hardware features.
Many sample projects and open source games are available that demonstrate how to use the API, hardware, and structure games.
C source debugging is available with Emulicious.
Weaknesses:
Takes care of some aspects of the hardware without requiring the developer to initiate them (such as OAM DMA during VBLANK), so it's not always obvious to beginners what the hardware is doing behind the scenes, or how to fix them when something goes wrong.
ROM banking may require more management in code than RGBDS.
ZGB is a small engine for the Game Boy built on top of GBDK-2020 and written in C. Strengths:
The basic graphics, sound and event structure are all pre-written, so it’s faster and easier to start writing a game.
Several open source games built with it are available as examples.
Weaknesses:
The engine just has the basics and custom code may need to be needed for common game features (such as moving platforms, etc.).
Even more of the hardware configuration and processing is taken care of behind the scenes than with GBDK, so less experienced users may have trouble when problems arise.
GB Studio is a drag-and-drop game creator for the Game Boy that does not require knowledge of programming languages. Games are built using a graphical interface to script graphics, sound and actions. It is available for Linux, Windows and MacOS.
Strengths:
Very easy for beginners to start building games right away. Everything is built-in and requires minimal knowledge and understanding of the Game Boy hardware.
Has been used to create large and extensive projects.
Very active community for help and support.
Weaknesses:
It’s games will tend to be slower than both ASM and C.
There is a limited set of commands to script with and some artificially smaller restrictions on palettes, sprites, background tiles and other hardware features (due to how GB Studio manages them).
Games may be more constrained or require workarounds to do things if they don’t easily fit within the available scripting, graphics and sound tools. (Though it is possible for advanced users to do a “engine eject” and add more functionality using C and ASM.)
Accurate emulators and debugging tools are tremendously helpful for testing and tracking down problems. The following Game Boy emulators provide excellent accuracy and include a variety of different features.
BGB has a convenient (ASM) debugger, though its minimal interface can be confusing at first. It is available for Windows only, but runs almost flawlessly with Wine.
Emulicious includes powerful tools such as a profiler and source-level debugging for ASM and C via a VS Code debug adapter. It runs on Linux, Windows, MacOS and any other operating systems that supports Java SE.
Same Boy is user friendly and has a wide range of powerful (ASM) debugging features. It runs on Windows and MacOS.
Gambatte lacks a debugger and must be compiled from source, but is packaged both in RetroArch (Linux, Windows and Mac) and BizHawk (Windows-only).
Purists prefer to also run their games on hardware, which is possible thanks to flashcarts. My personal recommendation is krikzz's carts, particularly the Everdrive GB X5.
Side note : if you are using VBA or VBA-rr, stop using them right now. These emulators are extremely inaccurate, and also contain severe security flaws. I strongly urge you to ditch these emulators and spread the word.
If your question is "What should I use for my game project ?", then you're in the right section. The first question you should ask yourself is what languages you know.
Consider starting with C and GBDK. This will introduce you to working with the hardware and is an easier starting place.
Once you've grasped C's concepts (most importantly pointers), give ASM a go. The language is simpler than it looks. Even if you don't manage to get working ASM code, it actually helps a lot (especially on such a constrained system) to know what's "under the hood". There is even an online IDE to experiment with.
For C / GBDK users, knowing ASM will help you understand what its API (which is mostly written in ASM) is doing behind the scenes and will make using emulator debuggers easier to understand.
If you don't wan't to learn a language at all, GB Studio is an alternative to C and ASM.
Consider the goals, scope and time frame of your project. If you'd like to start building right away then C and GBDK will make that easy. You'll also have growing exposure to ASM as time goes on due to working with the hardware and tracking down problems in the debugger.
On the other hand, if you'd like to expand your programming skill set and have additional time, learning to use ASM and RGBDS will provide you with a lot of knowledge about the Game Boy hardware. Once you know ASM in addition to C, you'll have a lot of flexibility in what tools you use for projects.
RGBDS with ASM is a solid option. You'll be able to get the best performance out of the hardware, and there is an experienced community available to help.
Another option is to reach out to us, and discuss the matter.
The very first thing to do in all cases is to read the docs, to grasp how the Game Boy works. In ASM, this is essential; in C, this will let you understand what a given library function does. It will also let you understand what is possible on the Game Boy, and what isn't. (You can always ask, if you have doubts.)
I also recommend looking up awesome-gbdev for resources and tutorials. There are a lot of helpful articles there, as well as helper tools.
Modules Separate your game into several "entities" that interact together. Camera, Player, NPCs, Loading zones, etc. This simplifies coding, by allowing you to reason independently on smaller units. This facilitates development and reduces the amount of bugs.
Document your functions For each function, write a comment saying what it does, what memory it touches, and what registers it affects. This will avoid conflicts, and let you optimize your code by minimizing the amount of registers you save when calling a function.
Plan before writing You should plan what register is going to be used for what within your functions before starting to write them. Your goal is to minimize the amount of register swapping. There's no general rule, so feel free to drop by and ask us, if you're in doubt.
RGBASM -E and RGBLINK -n <symfile> When you load ROM.gb or ROM.gbc in BGB, it automatically loads (if it exists) the file ROM.sym in the same folder as the ROM. This adds symbols to the debugger, which - believe me - helps a ton.
Global variables Use as many global variables as you can; the Game Boy has a lot of RAM compared to other platforms such as the NES, but is slow at using the stack. Thus, minimizing the number of local variables, especially in heavily-called functions, will reduce the time spent manipulating the stack.
Optimized code Write code as efficient as possible. Sometimes there is a readability tradeoff, so I recommend you get the comment machine gun out and put some everywhere.
By default GBDK-2020 (after v4.0.1) will use the SDCC flag --max-allocs-per-node 50000 for an increased optimization pass. You may also choose to use --opt-code-speed (optimize code generation towards fast code, possibly at the expense of codesize) or --opt-code-size (optimize code generation towards compact code, possibly at the expense of codespeed).
Inlining When performance is important avoid using functions if you can inline them, which skips passing all arguments to the stack, mostly. Macros will be your friends there. If needed you can also use inline ASM.
NEVER use recursive functions
AVOID printf printf clobbers a sizeable chunk of VRAM with unnecessary text tiles. Instead, you should sprintf to a buffer in WRAM, then put that on the screen using a custom font.
Geometry funcs Avoid the functions that draw geometry on-screen (lines, rectangles, etc.). The Game Boy isn't designed for this kind of drawing method, and you will have a hard time mixing this with, say, background art. Plus, the functions are super slow.
const (very important!) Declaring a variable that doesn't change as constgreatly reduces the amount of ROM, RAM, and CPU used. The technical reason behind that is that non-const values, especially arrays, are loaded to RAM from ROM in an extremely inefficient way. This takes up a LOT more ROM, and copies the value(s) to RAM when it's unneeded. (And the GB does not have enough RAM for that to be viable.)
Don't use MBC1 MBC1 is often assumed to be the simplest of all MBCs... but it has a quirk that adds some overhead every time ROM or SRAM bank switches are performed. MBC3 and MBC5 don't have this quirk, and don't add any complexity. Using MBC1 has no real use. (Let's not talk about MBC2, either.)
`,82)]])}var s=r(a,[[`render`,o]]);export{i as _pageData,s as default};
\ No newline at end of file
diff --git a/assets/xysine-B_k5r4-G.js b/assets/xysine-B_k5r4-G.js
new file mode 100644
index 00000000..bae4f438
--- /dev/null
+++ b/assets/xysine-B_k5r4-G.js
@@ -0,0 +1 @@
+var e=`/deadcscroll/gif/xysine.gif`;export{e as t};
\ No newline at end of file
diff --git a/chat.html b/chat.html
new file mode 100644
index 00000000..6dff36c2
--- /dev/null
+++ b/chat.html
@@ -0,0 +1,55 @@
+
+
+
+
+
+
+
+
+ Community channels | gbdev.io
+
+
+
+
+
+
#gbdev-ot, #gbdev-casual, #gbdev-hw, #gbdev-meta, #gbdev-asm, #gbdev-gfx, #gbdev-emudev, #gbdev-romhacking and #gbdev-research are also available for discussion about specific topics.
These base guidelines (if not specified otherwise) apply to every repository under the gbdev github organisation. The specific CONTRIBUTING rules of the repository in object also apply. In case of conflicts, those last ones have the priority.
Reaching out to maintainers
If you are unsure about anything or need guidance, you can always reach out the maintainers on Discord or IRC.
You're allowed to use AI/LLM tools to help with your contribution, but you must disclose it and meet certain expectations around reviewing and understanding the work you submit.
Unattended AI-driven interactions and contributions are not allowed, unless the project has an area explicitly designated for such contributions.
Provide an example to reproduce the problem in isolation. This should include no private code. The example should be self-runnable. If a snippet is too small, create a repository.
Unless the problem is visual, do not include screenshots. They are not helpful.
Please be patient. You are probably frustrated by your problem - the maintainers are likely not aware of your workflow.
Be grateful for any help you are getting and follow through. The maintainers are likely giving you support for free.
Informal conversations are great (e.g. on Discord), but once a problem, feature request etc is outlined, opening an issue formalizing it is the most important thing you can do to help this information flow.
Open an issue in the related repository. If you don't know where it's better suited, ask for help.
Include context and any information useful to understand the situation. Don't be scared about doing something wrong or not good enough, maintainers will follow up if needed.
Keep titles and commit messages brief and concise.
If the PR is not self-explanatory, add brief description of what has been done and implementation choices (if any).
Keep the work in the branch focused and directed at accomplishing only what you are describing in the PR. Ideally a PR should address a single issue (or a set of assimilable ones).
PRs require commitment and they are an investment by the (volunteer) maintainers. If you don't have time or abandon the PR after reviews have been left and additional changes are needed, other maintainers may take over to make sure the PR can move forward and the work is not lost.
Maintainers may chime in and commit to your feature branch on minor fixes (or suggestions agreed upon).
Don't be scared of opening more PRs for the same task, if they can be logically divided. The smaller the changes, the faster they can get reviewed and eventually merged.
When adding new features, think about tests and possibly updating documentation. Think about other parts of the project which may be affected by the change.
Document trade-offs, risks, implementation and design choices, if any.
Tests and pipelines must pass. If they failed because of something unrelated to the PR (e.g. a link died in the meantime), mention it in the PR thread.
If the solution is "sub-optimal" (but still better than the actual implementation) and there are clear possible improvements, mention them and try to collaborate with the contributor, but ultimately prefer merging something sub-optimal than keeping the content stale/inactive. An issue can be opened (or kept opened) to report this situation.
If there's an ongoing conversation/discussion on a PR/thread, let it continue. Don't abruptly merge unless there's a clear consensus among the core maintainers (and approvals).
Respect the requests from other maintainers for additional time if they ask for it.
For disruptive (e.g. major and/or breaking changes) PRs:
Wait for 2* maintainers approval
Every week of inactivity/staleness, ping maintainers
After 2 weeks from last approval, PR is merge-able with only 1 maintainer approval
For non-disruptive PRs:
Wait for 1 maintainer approval
If a maintainer wants to put a veto and to make the PR wait for his/her input after the mentioned period, the "veto" label can be used
*if there are at least 2 active maintainers, otherwise 1.
The single, most comprehensive technical reference to Game Boy available to the public. Here, you can help adding new content, proof reading, fixing typos, preparing updated versions of figures or port new content from old resources.
Technologies and tools: Markdown, Rust, Python, mdBook.
A community-led attempt to collect, archive and save every unofficial game, homebrew, patch, hackrom for Game Boy produced by the community through decades of passionate work.
This is the database of games powering the main Homebrew Hub project. Find a game (we have a lot to add!) and add it with a Pull Request. Or - even better - write scrapers to dump entire sources and add them to our repository!
Technologies and tools: JSON Schema, Python, Javascript, BeautifulSoup
You can donate on our Open Collective or on GitHub sponsors. You can send a one-time donation of any amount or join the collective as backer, donating monthly.
WHY OPENCOLLECTIVE
The gbdev organisation is an informal collective of individuals. As such, we are not able to hold any fund in our accounts. Instead, the US based non-profit Open Source Collective is our fiscal host, providing us the financial and legal infrastructure to receive donations and spend money.
It's also possible to donate specifically towards a project under our umbrella. We'll do our best to honour this preference.
Donations are subject to a 10% fee from our Open Collective host (Open Source Collective) plus a variable fee from the payment processor (Wise).
Money collected on GitHub sponsors are paid out by GitHub to our Open Collective at the end of each month.
If you have a company and you are interested in being highlighted on some of our webpages and channels, we have sponsorship tiers starting at 50$/month. Get in touch at sponsors@gbdev.io to understand if this could be for you. We are interested in hearing how our software is helping your business etc.
Sponsorships are evaluated on a case-to-case basis and only be accepted if we feel like our core values are compatible and the relationship can last in the long term with beneficial effect for both parties.
Sometimes, we may assign a bounty to an Issue. Labels such as bounty-25$, bounty-50$, bounty-100$ are used to specify the amount. If you wish to work on a issue with a bounty attached, it's a good measure to comment on the issue asking for it to be assigned to you*.
If you send a Pull Request fixing that issue and this work gets merged, you will be able to claim the corresponding bounty.
Bounties are paid out from our Open Collective: submit an expense mentioning the Issue you worked on and make sure to use your GitHub email (or the one you authored the git commit with) when creating an account on OpenCollective. Wire transfers and PayPal are supported**.
Details are generally verified in the following 24-48 hours. Payments are then processed by our Open Collective host a couple of times per week.
TAX CONSIDERATIONS
Make sure to check the tax regulations of your country and properly declare the bounties received. We're not tax consultants and we cannot help with that.
* In case of inactivity we may remove the assignment to you or let someone else who submitted a PR claim the bounty.
** Our Open Collective host uses Wise as the payment processor so the countries supported for the bank transfers depends on them.
+
+
+
diff --git a/website/.vuepress/public/favicons/favicon-16x16.png b/favicons/favicon-16x16.png
similarity index 100%
rename from website/.vuepress/public/favicons/favicon-16x16.png
rename to favicons/favicon-16x16.png
diff --git a/website/.vuepress/public/favicons/favicon-32x32.png b/favicons/favicon-32x32.png
similarity index 100%
rename from website/.vuepress/public/favicons/favicon-32x32.png
rename to favicons/favicon-32x32.png
diff --git a/gbcompo-statement.html b/gbcompo-statement.html
new file mode 100644
index 00000000..427cc5de
--- /dev/null
+++ b/gbcompo-statement.html
@@ -0,0 +1,55 @@
+
+
+
+
+
+
+
+
+ Statement on the recent removal request and rule compliance in GBCompo events | gbdev.io
+
+
+
+
+
+
gbdev.io is a non-profit initiative that maintains, develops, and promotes open-source tools, documentation, and digital preservation projects for the Game Boy platform. Since 2021 we also host the "gbcompo" events.
Homebrew Hub is gbdev.io's institutional repository for Game Boy homebrew, dedicated to long-term digital preservation. We use it to archive and mirror all GBCompo submissions, so that the versions entered into the competition remain available and accessible over time.
GBCompo events (2021, 2023, 2025) are game development competitions organised by gbdev.io, focused on creating new free Game Boy homebrew games. Developers submit games that are judged, showcased, and actively promoted during and after each edition. Some entries later go on to have commercial success, including paid digital releases and physical editions; while we are happy about this and actively showcase it, we do not profit from it, and this is not the focus of the competition.
GBCompo events are run on a non-profit basis under a clear, shared framework. The prize pool is funded entirely by community donations and sponsors, with organisers and judges putting in around six months of unpaid work around and during each edition. No profit is made: donated and sponsored funds are redistributed to winners, with only a small portion going back into gbdev.io’s common funds to help support future events (for example, gbcompo25's first $500 in the prize pool were contributed by gbdev.io).
At the explicit request of 'allalonegamez' we have begun removing their games from Homebrew Hub. All copies of the relevant entries hosted on Homebrew Hub are being taken offline (subject only to delays such as DNS propagation, rebuilds, and caching). We are fully complying with their request to remove the content from our platform.
We were requested to take down a game for which the copy on Homebrew Hub was the last available online, for free. This game participated in the 'gbcompo23' competition and was awarded monetary prizes and promotion through gbdev.io channels.
Rule 20 of 'gbcompo23', the event where 'allalonegamez' game was submitted and won prizes says:
The submission must be available for free for the public (and not only the judges). Submission will be published and kept online for free on the competition website, while you are free to keep working on it (and eventually charge for it/make commercial usage).
(emphasis added)
We publish the jam submitted ROMs and keep them online for free on on gbdev's Homebrew Hub, after the competition window ends. Additional ZIP compilations are also prepared. kept online for free clearly means that the submitted jam version must remain publicly accessible after the competition, and not be taken down, delisted, or paywalled at a later date.
In practise though, we don't enforce a specific 'competition website'; any stable, accessible online location is acceptable to be considered in compliance with Rule 20. A request to take down the default mirror to satisfy the rule in other ways/places was acceptable.
We did not want to host a creator's work against their will, even if they had accepted their entry being kept online by submitting to our event, so we accepted a withdrawal scenario where 'allalonegamez' had removed every free copy of the jam version and we complied by removing ours.
The gbcompo23 entry in question was put offline by the author sometime between March and June 2024. This is a perfectly legitimate approach, as long as the jam-submitted versions of the games are kept online and available for free, somewhere, as per rules of the event they were submitted to.
Since the author did not entertain the idea of keeping the copy anywhere else online for free, the entry is in violation of Rule 20.
Because the entry no longer complies to the event rules, they'll be disqualified from the competition. Disqualified entries are not eligible for prizes nor leaderboards.
In order to be fair to all other participants who are respecting the rules and keeping their jam-versions available, any prizes or awards previously assigned to these entries should be reassigned to eligible participants. Any mention of a refund of prizes was made solely in this context of fairness and proper redistribution.
We want to clarify that we have no intention of taking legal action or attempting to enforce a prize refund by any means. Our reference to an 'expectation of a refund' was intended purely as a matter of fairness to all parties involved in the event, not as a threat or condition, and we recognize that our wording may have come across more harshly than intended. At no point was legal action discussed or considered, and we did not withhold or delay any part of the removal requested by 'allalonegamez'.
'allalonegamez' is now offering to refund the prize and donate it to a charity. We appreciate the offer, however we are not and have never been interested in pursuing the financial matter further so we do not have a say on that. No further action will be taken in that regard. We are in any case ready to reinstate the entry in its earned position at any time, if and when it becomes compliant with the rules again.
The GBCompo prize pool is funded by people who donate money under specific premises: free games will be released in the framework of the ruleset of the event. Participants also agree to abide by these rules as a condition for qualifying for prizes and promotion.
In this context, it would be unfair to continue to award exposure, promotion, and prizes to a participant who is not abiding by the rules, receiving prizes, exposure and promotion only to pull their entry offline and make them paid-only, while many other submitters – including those who did not win – continue to respect them and keep their jam-versions available.
Disqualification in such a case is necessary to uphold the expectations of all the other actors in the event, including donors, organisers and other participants.
We plan to revise the wording of the rules to make this expectation clearer, explicitly indicating where entries will be kept online and using more precise language about the long-term, effectively perpetual availability of the submitted jam versions.
Full leaderboards are available here. Top rated entries are playable here while the complete list can be found on Homebrew Hub and Itch.io. A zip file can be downloaded from GitHub.
Come join us on Discord or others for help getting started, and head over to gbdev.io for useful links & resources!
We'd love to hear from participants, share your progress on Twitter using the #gbcompo hashtag and join the conversation the on Discord #compo-chat channel!
The current prize pool is and will be split according to this document.
Entries will compete in 4 separate categories and 4 rankings will be drawn up. Based on these leaderboards, the top entries will be awarded as follows:
Best games (80% of the prize pool)
The best 5 games will get a prize.
The best 3 OSS games will get an additional prize (can overlap)
Best homebrew/tool (1) (running on a GB/GBC)
Best music cartridge (1)
Best demo (1) (demoscene style demos)
The rankings are decided according to the JUDGING criteria.
If you publish your game source code and assets on a public repository (e.g. GitHub) under an open source license (GPL, MIT, Apache, CC0...) you get 20% more of the prize as a bonus!
Money prizes are subject to fees and conversions. They may be delivered as gift cards of your choice.
Final entry "deliverable" must consist of a GB or GBC ROM file. You can also attach descriptive documents in TXT, Markdown or HTML file format (e.g. game manual).
The ROM file will be tested on real hardware (usually a DMG or a GBC) and on the BGB emulator. In case of different results, the hardware test will be the trusted one.
GBC features are allowed.
SGB features are allowed. The entries will be evaluated mainly on Game Boy (Color). You can use SGB features, but don't rely on them as main features for your entry.
Teams are allowed (award will be split).
The competition is focused on games, but you are allowed to submit homebrew tools, demoscene style demos and music cartridges. There may be special category prizes for those. There are no preferred themes.
ROM Hacks are not allowed.
NSFW content is not allowed. Please keep it safe for work and avoid anything overly edgy or distasteful.
You can submit more than one entry. They will be rated separately, but each person or team may only accept prizes on behalf of a single entry (e.g., you can't win both the 1st and 2nd place prize).
Judges can submit entries, but they are not eligible for prizes.
Only some official MBC chips (MBC1, MBC2, MBC3 and MBC5, as well as no MBC at all) are allowed. The only exceptions are:
While Nintendo only manufactured very specific MBC+ROM combinations, we will allow any power-of-two ROM size between 32 KiB and the maximum for the chosen chip. (This is simply a convenience feature, as any ROM image can be extended to fit a larger ROM chip by appending copies of itself.)
The ROM+RAM combination (i.e., without an MBC, up to 32 KiB ROM and 8 KiB SRAM), with or without battery, will be accepted for compatibility, as many people have simple flash carts that do have SRAM but don't have an MBC. (This can be trivially emulated by any MBC1, MBC3 or MBC5 configuration.)
The variants MBC30 and MBC1M are accepted, as they have been used in licensed games.
Some official MBCs added extra components to the cart: MBC3/MBC30 carts could have an RTC, and MBC5 carts could have rumble. These combinations, and only these combinations, will be accepted. In particular, this means it's impossible to have RTC and rumble in the same cart.
Entries cannot rely on extra hardware or add-on devices, they must be playable without a specific setup.
As long as you produce a GB or GBC ROM file that runs on GB/GBC, any tool to develop the entry is allowed. This includes RGBDS (ASM), GBDK (C), ZGB (C), GB Studio, ...
Source code is optional, but very much appreciated. Entries submitted with source code (public repositories on GitHub/GitLab/... are accepted) and with an open source license (See rule 15) are eligible for additional prizes. Make sure to fill the "Open Source repository" field with a valid and public repository when submitting on itch.io. See this issue for further details.
To be eligible for the OSS bonus, the code must be licensed under an "Open Source" license (any of the "Free" license listed here is accepted). Assets can be licensed under any of the CC licenses. Obfuscated code, non-reproducible builds, intentionally making the source code hard to read/re-use will exclude the entry form the OSS leaderboards. This is at discretion of the hosts and judges. We must be able to read the code and compile it into a working ROM, running a script or following istructions.
The entry must be submitted on the itch.io jam page.
Your work must be new and original. You cannot enter with something you were already working on before the start of the jam. The majority of work should be done during the jam. If in doubt, please ask us before proceeding. Examples of things that are allowed:
A brand new game using your own (or any) existing game engine.
A remake of your (or someone else's) game for a different platform, but with new code and assets.
A brand new game you barely started (e.g. you only made a title screen or a mockup of the gameplay).
Using templates, tutorial code as starting point (when they're license allow to do so, e.g. gb-boilerplate)
Asset packs for art and music are ok if the licensing permits. Be sure to make note of what is being reused and under what terms. Use of pre-made assets (vs original ones) may be factored in when judges assess points for relevant criteria (e.g. graphics, audio).
Examples of things that are not allowed:
A project you've been working on for months.
An update/patch for a game you already released publicly.
The submission must be available for free for the public (and not only the judges). Submission will be published and kept online for free on the competition website, while you are free to keep working on it (and eventually charge for it/make commercial usage).
Multiple submissions are allowed.
If your submission contains text that isn't available in English, some judges might not be able to read that text, and they will judge accordingly.
This category is for audio-visual demonstrations that run without viewer interaction, in such a way as to demonstrate skill in manipulation of the Game Boy hardware through code, special effects, art, music, and direction. More on Wikipedia.
The submitted entry must implement / interpret the jam theme (applies to Game category only).
Multiple submissions are allowed from the same person or team. They will be rated separately, but each person or team may only accept prizes on behalf of a single entry (e.g., you can't win both the 1st and 2nd place prize).
Judges can submit entries, but the entries won't be eligible for prizes. Judges won't score the entries they submitted.
Source code is optional, but very much appreciated. Entries submitted with source code (public repositories on GitHub/GitLab/... are accepted) and with an open source license (See rule 5) are eligible for additional prizes. Make sure to fill the "Open Source repository" field with a valid and public repository when submitting on itch.io. It is NOT necessary for the source to be available during the competition period. Repositories/sources just need to be public from the time the event ends. See this issue for further details.
To be eligible for the OSS bonus, the code must be licensed under an "Open Source" license (any of the "Free" license listed here is accepted). Assets can be licensed under any of the CC licenses. Obfuscated code, non-reproducible builds, intentionally making the source code hard to read/re-use will exclude the entry form the OSS leaderboards. This is at discretion of the hosts and judges. We must be able to read the code and compile it into a working ROM, running a script or following instructions.
If your submission contains text that isn't available in English, some judges might not be able to read that, and they will judge accordingly.
Shipment of physical prizes may not be available to all countries, they are subject to shipping ability of the prize donors.
Final entry "deliverable" must consist of a GB or GBC ROM file. You can also attach descriptive documents in TXT, Markdown, PDF or HTML file format (e.g. game manual).
The ROM file will be tested on real hardware (primarily a GBC) and on the BGB emulator. In case of different results, the hardware test will be the trusted one.
GBC features are allowed, and entries may be GBC only (GBC only should be indicated in game for non-GBC hardware or on the entry's page).
SGB features are allowed. The entries will be evaluated mainly on Game Boy Color. You can use SGB features, but don't rely on them as main features for your entry.
Only some official MBC chips (MBC1, MBC2, MBC3 and MBC5, as well as no MBC at all) are allowed. The only exceptions are:
While Nintendo only manufactured very specific MBC+ROM combinations, we will allow any power-of-two ROM size between 32 KiB and the maximum for the chosen chip. (This is simply a convenience feature, as any ROM image can be extended to fit a larger ROM chip by appending copies of itself.)
The ROM+RAM combination (i.e., without an MBC, up to 32 KiB ROM and 8 KiB SRAM), with or without battery, will be accepted for compatibility, as many people have simple flash carts that do have SRAM but don't have an MBC. (This can be trivially emulated by any MBC1, MBC3 or MBC5 configuration.)
The variants MBC30 and MBC1M are accepted, as they have been used in licensed games.
Some official MBCs added extra components to the cart: MBC3/MBC30 carts could have an RTC, and MBC5 carts could have rumble. These combinations, and only these combinations, will be accepted. In particular, this means it's impossible to have RTC and rumble in the same cart.
Entries cannot rely on extra hardware or add-on devices, they must be playable without a specific setup.
As long as you produce a GB or GBC ROM file that runs on GB/GBC, any tool to develop the entry is allowed. This includes RGBDS (ASM), GBDK (C), ZGB (C), GB Studio, ...
NSFW content is not allowed. Please keep it safe for work and avoid anything overly edgy or distasteful.
The entry must be submitted on the itch.io jam page.
Your work must be new and original. You cannot enter with something you were already working on before the start of the jam. The majority of work should be done during the jam. If in doubt, please ask us before proceeding.
Examples of things that are allowed:
A brand new game using your own (or any) existing game engine.
A remake of your (or someone else's) game for a different platform, but with new code and assets.
A brand new game you barely started (e.g. you only made a title screen or a mockup of the gameplay).
Using templates, tutorial code as starting point (when they're license allow to do so, e.g. gb-boilerplate)
Asset packs for art and music and covers of other music are allowed if the licensing permits. Be sure to make note of what is being reused or re-interpreted and under what terms. Use of pre-made assets and content (vs wholly original) may be factored in when judges assess points for relevant criteria (e.g. graphics, audio).
Examples of things that are not allowed:
A project you've been working on for months.
An update/patch for a game you already released publicly.
The submission must be available for free for the public (and not only the judges). Submission will be published and kept online for free on the competition website, while you are free to keep working on it (and eventually charge for it/make commercial usage).
It is allowed to submit an entry to multiple events.
A team of (~10) judges has been selected before the start of the competition.
Every judge will be asked to compile an unranked shortlist of their favourite entries. Once the shortlists are ready from all the judges, the top 15 most selected entries will be evaluated by the judges on the following criteria:
Gameplay - How entertaining are the gameplay mechanics? How fun is the game?
Technical - How innovative is it from a technical perspective? Does it push the hardware to the limit?
Originality - How refreshing is it from a non-technical perspective? Does it have a unique design / mechanics?
Graphics - How impressive are the art, animation and visual effects?
Audio - How good are the music and sound effects?
Theme - How was the theme implemented in the gameplay, the mechanics and/or the story?
Applicability may vary depending on the category of the entry (e.g. a "Music" ROM may be evaluated only on 5).
The final leaderboard is computed by averaging the "Overall" score.
Warning
Judges are volunteers. Those details can change without prior notice, as the judging process is long and involves hundreds of entries. Detailed criteria leaderboards will be provided only for entries in the final shortlist.
This category is for audio-visual demonstrations that run without viewer interaction, in such a way as to demonstrate skill in manipulation of the Game Boy hardware through code, special effects, art, music, and direction. More on Wikipedia.
Multiple submissions are allowed from the same person or team. They will be rated separately, but each person or team may only accept prizes for a single entry (e.g., you can't win both the 1st and 2nd place prize).
Judges can submit entries, but the entries won't be eligible for prizes. Judges won't score the entries they submitted.
Source code is optional, but very much appreciated. Entries submitted with source code (public repositories on GitHub/GitLab/... are accepted) and with an open source license (See rule 4) are eligible for additional prizes. Make sure to fill the "Open Source repository" field with a valid and public repository when submitting on itch.io. It is NOT necessary for the source to be available during the competition period. Repositories/sources just need to be public from the time the event ends. See this issue for further details.
To be eligible for the OSS bonus, the code must be licensed under an "Open Source" license (any of the "Free" license listed here is accepted). Assets can be licensed under any of the CC licenses. Obfuscated code, non-reproducible builds, intentionally making the source code hard to read/re-use will exclude the entry form the OSS leaderboards. This is at discretion of the hosts and judges. We must be able to read the code and compile it into a working ROM, running a script or following instructions.
If your submission contains text that isn't available in English, some judges might not be able to read that, and they will judge accordingly.
Shipment of physical prizes may not be available to all countries, they are subject to shipping ability of the prize donors.
The submitted entry must implement / interpret the jam theme (applies to Game category only).
Final entry "deliverable" must consist of a GB or GBC ROM file. You can also attach descriptive documents in TXT, Markdown, PDF or HTML file format (e.g. game manual).
The ROM file will be tested on real hardware (primarily a GBC) and on a hardware accurate emulator such as BGB and SameBoy. In case of different results, the hardware test will be the trusted one.
GBC features are allowed, and entries may be GBC only (GBC only should be indicated in game for non-GBC hardware or on the entry's page).
SGB features are allowed. The entries will be evaluated mainly on Game Boy Color. You can use SGB features, but don't rely on them as main features for your entry.
Only some official MBC chips (MBC1, MBC2, MBC3 and MBC5, as well as no MBC at all) are allowed. The only exceptions are:
While Nintendo only manufactured very specific MBC+ROM combinations, we will allow any power-of-two ROM size between 32 KiB and the maximum for the chosen chip. (This is simply a convenience feature, as any ROM image can be extended to fit a larger ROM chip by appending copies of itself.)
The ROM+RAM combination (i.e., without an MBC, up to 32 KiB ROM and 8 KiB SRAM), with or without battery, will be accepted for compatibility, as many people have simple flash carts that do have SRAM but don't have an MBC. (This can be trivially emulated by any MBC1, MBC3 or MBC5 configuration.)
The variants MBC30 and MBC1M are accepted, as they have been used in licensed games.
Some official MBCs added extra components to the cart: MBC3/MBC30 carts could have an RTC, and MBC5 carts could have rumble. These combinations, and only these combinations, will be accepted. In particular, this means it's impossible to have RTC and rumble in the same cart.
Entries cannot rely on extra hardware or add-on devices, they must be playable without a specific setup.
As long as you produce a GB or GBC ROM file that runs on GB/GBC, any tool to develop the entry is allowed. This includes RGBDS (ASM), GBDK (C), ZGB (C), GB Studio, ...
NSFW content is not allowed. Please keep it safe for work and avoid anything overly edgy or distasteful.
The entry must be submitted on the itch.io jam page.
Your work must be new and original. You cannot enter with something you were already working on before the start of the jam. The majority of work should be done during the jam. If in doubt, please ask us before proceeding.
Examples of things that are allowed:
A brand new game using your own (or any) existing game engine.
A remake of your (or someone else's) game for a different platform, but with new code and assets.
A brand new game you barely started (e.g. you only made a title screen or a mockup of the gameplay).
Using templates, tutorial code as starting point (when they're license allow to do so, e.g. gb-boilerplate)
Pre-made and AI generated assets for art and music (and covers of other music) are allowed if the licensing permits. Be sure to make note of what is being reused or re-interpreted and under what terms. There is a specific field in the submission form to allow you to disclose this. Use of pre-made and AI generated assets and content (vs wholly original and human created) may be factored in when judges assess points for relevant criteria (e.g. graphics, audio).
Examples of things that are not allowed:
A project you've been working on for months.
An update/patch for a game you already released publicly.
The submission must be available for free for the public (and not only the judges). Submission will be published and kept online for free on the competition website, while you are free to keep working on it (and eventually charge for it/make commercial usage).
It is allowed to submit an entry to multiple events.
It is permitted to use the 'gbcompo25 logo' graphics to identify the entry as a submission to this competition (e.g. as an intro splash screen). Minor modifications and alterations (color palette, size, smaller resolution) to fit the medium are permitted.
Before submitting, make sure you've read the rules. Our resources are limited - low effort, non-compliant or spammy submissions may lead to exclusion from this event and future ones.
A team of (~10) judges has been selected before the start of the competition.
Every judge will be asked to compile an unranked shortlist of their favourite entries. Once the shortlists are ready from all the judges, the top 15 most selected entries will be evaluated by the judges on the following criteria:
Gameplay - How entertaining are the gameplay mechanics? How fun is the game?
Technical - How innovative is it from a technical perspective? Does it push the hardware to the limit?
Originality - How refreshing is it from a non-technical perspective? Does it have a unique design / mechanics?
Graphics - How impressive are the art, animation and visual effects?
Audio - How good are the music and sound effects?
Applicability may vary depending on the category of the entry (e.g. a "Music" ROM may be evaluated only on 5)-
The final leaderboard is computed by averaging the "Overall" score.
Warning
Judges are volunteers. Those details can change without prior notice, as the judging process is long and involves hundreds of entries. Detailed criteria leaderboards will be provided only for entries in the final shortlist.
This style guide aims to formalize a style that most Game Boy ASM programmers agree on, and provide a good baseline for new programmers just starting in this field. (If that's you, welcome! 😄)
Coding style is very personal, and I won't force my views on anybody, but this is what goes for anything that I have to be able to maintain, and I'd prefer it for most other things too. Please at least consider the points made here.
Many people follow alternate style guides, and that's fine; but if you're starting to code in ASM, a clean style goes a long way to keep your code organized. Again: you don't have to do everything listed here, but please at least consider the reasons behind each bullet point.
Symbol names can contain letters, numbers, underscores ‘_’, hashes ‘#’ and at signs ‘@’. However, they must begin with either a letter, or an underscore.
However, naming conventions make code easier to read, since they help convey the different semantics between each symbol's name.
Labels use PascalCase: DrawNPCs, GetOffsetFromCamera.
Labels in RAM (VRAM, SRAM, WRAM, HRAM; you shouldn't be using Echo RAM or OAM) use the same convention but are prefixed with the initial of the RAM they're in, in lowercase: wCameraOffsetBuffer, hVBlankFlag, vTilesetTiles, sSaveFileChecksum. Rationale: to know in which memory type the label is; this is important because VRAM and SRAM have special access precautions and HRAM can (should (must)) be accessed using the ldh instruction.
Local labels use camelCase, regardless of memory type: .waitVRAM, wPlayer.xCoord.
Macro names use snake_case: wait_vram, end_struct.
Constants use CAPS_SNAKE: NB_NPCS, OVERWORLD_STATE_LOAD_MAP.
Exception: constants that are used like labels should follow the label naming conventions. For example, see hardware.inc's rXXX constants.
No magic numbers. ld a, CURSOR_SPEED is much more obvious than ld a, 5. In addition, if you ever change your mind and decide to change the cursor speed, you will only need to do so in one location (CURSOR_SPEED equ 5 → CURSOR_SPEED equ 4) instead of at every location you're using it, potentially missing some.
Unless absolutely necessary, don't force a SECTION's bank or address. This puts the burden of managing ROM space on you, instead of offloading the job to RGBLINK, which performs very well in typical cases. Exceptions:
Your ROM's entry point must be at $0100, however the jump does not have to be to $0150 (example).
RGBDS presently does not allow forcing different sections to be in the same bank. If you need to do so, the ideal fix is to merge the two sections together (either by moving the code, or using SECTION FRAGMENT), but if that option is unavailable, the only alternative is to explicitly declare them with the same BANK[] attribute. (In which case it's advisable to add an assert BANK("Section A") == BANK("Section B") line.)
If you need some alignment, prefer ALIGN[] to forcing the address. A typical example is OAM DMA; for that, prefer SECTION "Shadow OAM", WRAM0,ALIGN[8] over e.g. SECTION "Shadow OAM", WRAM0[$C000].
Allocate space for your variables using labels + ds & co instead of equ. This has several benefits:
Removing, adding, or changing the size of a variable that isn't the last one doesn't require updating every variable after it.
The size of each variable is obvious (ds 4) instead of having to be calculated from the addresses.
equ allocation is equivalent to hardcoding section addresses (see above), whereas labels are placed automatically by RGBLINK.
Labels support BANK() and many cool other features!
If a file gets too big, you should split it. Files too large are harder to read and navigate. However, the splitting should stay coherent and consistent; having to jump around files constantly is equally as hard to read and navigate.
Unless you're making a 32k ROM, put things in ROMX by default. ROM0 space is precious, and can deplete quickly; and when you run out, it's difficult to move things to ROMX.
However, if you have code in ROM bank A refer to code or data in ROM bank B, then either should probably be moved to ROM0, or both be placed in the same bank (options for that are mentioned further above). farcall is a good way to make your code really spaghetti.
Don't clear RAM at init! Good debugging emulators will warn you when you're reading uninitialized RAM (BGB has one in the option's Exceptions tab, for example), which will let you know that you forgot to initialize a variable. Clearing RAM does not fix most of these bugs, but silences the helpful warnings.
Also, a lot of the time, variables need to get initialized to values other than 0, so clearing RAM is actually counter-productive in these cases.
The difference between these and the "best practices" above is that these are more subjective, but they're still worth talking about here.
Historically, RGBDS has required label definitions to begin at "column 1" (i.e. no whitespace before them on their line). However, later versions (with full support added in 0.5.0) allow indenting labels, for example to make loops stand out like in higher-level languages. However, a lot of people don't do this, so it's up to you.
Please use the .asm (or .s) file extensions, not .z80. The GB CPU isn't a Z80, so syntax highlighters get it mostly right, but not quite. And it helps spreading the false idea that the GB CPU is a Z80. 😢
Compressing data is useful for several reasons; however, it's not necessary in a lot of cases nowadays, so you may want to only look at it after more high-priority aspects.
Avoid abusing macros. Macros tend to make code opaque and hard to read for people trying to help you, in addition to having side effects and sometimes leading to very inefficient code.
Never let the hardware draw a corrupted frame even if it's just one frame. If it's noticeable by squinting a bit, it must go.
Makefiles are bae; they speed up build time by not re-processing what hasn't changed, and they can automate a lot of tedium. Writing a good Makefile can be quite daunting, but gb-boilerplate and gb-starter-kit can help you get started faster.
The SCY/SCX registers have a simple purpose: specify the coordinate of the screen's top-left pixel (or view, if you prefer) somewhere on the 256x256 pixel background map. This is really handy for certain kinds of games like platformers or top-down racing games (though there are LOTS of other kinds of games that benefit from this) where the view is the 'camera' and its position is set once per frame.
When you don't require scrolling, and when your cart boots, SCY/SCX is typically set to 0,0. When a screen is displayed, it appears normally even though you only set the values once. This is because as the screen draws, the PPU automatically adds the value in LY ($FF44) to the value in SCY in order to know what row of pixels to draw.
You can take advantage of how the PPU renders the screen by setting these registers as the screen draws. If you do this, you can create some interesting 'raster' effects that are presented here.
As an example, let's say you wanted to triple line 0 and show it for line 0, line 1, and line 2, and then continue with line 3. You would write to the SCY register like so:
SCY value (set once per line)
+│
+│ screen
+│ ┌───────┐
+├─$00 │Line 0 │ VRAM row $00 ($00+$00) is displayed
+├─$FF │Line 1 │ VRAM row $00 ($FF+$01) is displayed
+├─$FE │Line 2 │ VRAM row $00 ($FE+$02) is displayed
+├─$00 │Line 3 │ VRAM row $03 ($00+$03) is displayed
+├─$00 │Line 4 │ VRAM row $04 ($00+$04) is displayed
+├─... │... │ ...
+ │ │
+ └───────┘
When setting values for SCY, you need to remember that LY always (and automatically) increments, so you have to account for that in your new SCY value. You can write anything to SCX; that's not affected by the hardware so you don't have to adjust the value like you need to for SCY.
Note: The scroll registers only affect background rendering. They do not change how objects are displayed.
There are three main states that drive the display on the Game Boy: the Horizontal Blank (HBlank), the Vertical Blank (VBlank), and drawing. The HBlank starts when a line of pixels is completely drawn. There is an opportunity to do some work* before the next line of pixels starts drawing, and there is one HBlank for every line, all the way down the screen.
*The exact amount of time you have depends on several things; most notably how many objects are being drawn on that line. The PanDocs has a detailed explanation of the timing. (Indeed, read that entire document because it's great!)
When all of the lines are completely drawn, the VBlank starts. This interval is always 10 lines high so there is much more time to do some work compared to the HBlank. The VBlank is only secondary to this system though; the focus is the HBlank since we want to change the screen as it draws. The problem that needs to be solved is reliably knowing what value to set for a specific line.
As previously mentioned, there is a small amount of time that HBlanks give you to do work. This means that the handler has to be as fast as possible. On a limited system like the Game Boy, that usually equates to judicious use of table lookups and buffers.
There are two key elements to make this system very stable and very fast:
A double-buffering system that holds the data that feeds the HBlank handler
The idea of the double-buffer is that while one buffer is being used by the hardware to draw the screen, you modify (fill) values in the other. When the screen is done drawing, you switch buffers so the one you were just modifying is being used for drawing and you start modifying the other.
While the Draw Buffer (A) is used to render the screen, you change values in the Fill Buffer (B).
When the screen is done being drawn (and you know this because the VBlank interrupt would have triggered or the value in LY changed to 144), you switch the buffers.
Here, "switch buffers" means to switch the purpose of each buffer. It doesn't mean to copy buffers. Remember, we need this to be as fast as possible so to change buffers, you simply change pointers:
The size of each buffer (indeed, any buffer) depends on two things:
how many elements are needed
how much data is needed per element
We know that the buffers exist to support the HBlank handler, so the number of elements in the buffer are however many times the HBlank can trigger. We said earlier that the HBlank starts at the end of every screen line, so the number of elements is at least that many. However, remember when the HBlank starts: at the end of every line. What do we do if we need to change the 0th line (before any line has started drawing)? Well, we need to change that value before line 0 starts, which means it has to be done in the VBlank. And that means we need one more element. In short, we need the height of the screen plus one (144+1=145) elements in each buffer.
This tutorial is only concerned with the scroll registers, so it only needs to store 2 values per line: one for SCY and one for SCX. (You can store more data per line, of course, but this tutorial doesn't require it.)
In summary: each buffer is 145 2-byte elements (290 bytes), and we need two of them, so the total buffer memory size is 580 bytes.
Assume for a moment that you put the buffers physically next to each other in memory. For example, Buffer A is at $C000 and Buffer B is at $C122 (the buffer size is 290 bytes). We said earlier that in order to swap buffers, we just swap pointers, so the code that does that might look like this:
; assume the pointers are next to each other in memory
+wDrawBuffer: DS 2 ; buffer currently being drawn
+wFillBuffer: DS 2 ; buffer currently being modified
+
+; swap the contents of each pointer (28 cycles)
+ld hl,wDrawBuffer
+ld a,[hl+]
+ld b,[hl]
+ld c,a ; bc = contents of wDrawBuffer
+inc hl
+ld a,[hl+] ; a = LOW(contents of wFillBuffer)
+ld d,[hl] ; d = HIGH(contents of wFillBuffer)
+ld hl,wDrawBuffer
+ld [hl+],a
+ld [hl],d
+inc hl
+ld a,c
+ld [hl+],a
+ld [hl],b
To use a pointer, that code looks like this:
; use a pointer (8 cycles)
+ld hl,wFillBuffer
+ld a,[hl+]
+ld h,[hl]
+ld l,a ; hl = contents of wFillBuffer ($C000 or $C122)
You could certainly implement the system like this, but there is a way to gain some efficiency when swapping buffers and even with the actual pointers themselves.
Consider this: other than the memory locations, the buffers are identical. Since we're only really concerned with pointers, where the buffers reside in memory doesn't really matter. This can be exploited (and optimized!)
We can keep Buffer A at $C000. The buffer size is $122 bytes, but instead of putting Buffer B at $C122, what if we put it at $C200? This would make the pointer values $C000 and $C200. Literally a 1-bit difference. This, too, can be exploited! Both pointers end in $00 so we don't need to store those, which saves 2 bytes. This leaves us with two 1-byte 'pointers': $C0 and $C2.
To swap the pointers, literally just one bit has to be toggled:
; swap the contents of each 'pointer' (11 cycles)
+ldh a,[hFillBuffer]
+ldh [hDrawBuffer],a
+xor $02
+ldh [hFillBuffer],a
And to use a pointer, we only need to do this:
; use a 'pointer' (6 cycles)
+ldh a,[hFillBuffer]
+ld h,a
+ld l,0 ; hl = contents of hFillBuffer ($C000 or $C200)
You'll notice that the name of the pointers have changed. This is because they were moved into HRAM. (Also notice that they don't have to be next to each other in memory.) They were moved to HRAM for a couple of reasons: it allows an optimization in the swapping code (11 cycles vs 28), and it makes the use code slightly faster. There are only 2 bytes used now so that is a better candidate for moving to HRAM than 4 bytes.
In this system, code in the VBlank is responsible for two things:
swapping the pointers
setting the data for line 0
We've already seen what swapping the pointers looks like, but how is the data set for line 0? We need to emulate an HBlank handler running for "line -1" by getting the start of the new draw buffer and setting the scroll registers with the first data pair:
ldh a,[hDrawBuffer]
+ld h,a
+ld l,0
+
+; set the scroll registers
+ld a,[hl+]
+ldh [rSCY],a
+ld a,[hl+]
+ldh [rSCX],a
It's convenient that the scroll register addresses are next to each other. The data in the buffer is in the same order so as you can see in the code fragment above, this makes writing simple.
In an HBlank handler, every cycle counts! So don't do any work in there unless it's absolutely necessary. This is a good target for hyper-optimizations -- especially if you are changing VRAM (like palettes) -- so one should design around that optimization.
HBlankHandler::
+ push af
+ push hl
+
+ ; obtain the pointer to the data pair
+ ldh a,[rLY]
+ inc a
+ add a,a ; double the offset since each line uses 2 bytes
+ ld l,a
+ ldh a,[hDrawBuffer]
+ adc 0
+ ld h,a ; hl now points to somewhere in the draw buffer
+
+ ; set the scroll registers
+ ld a,[hl+]
+ ldh [rSCY],a
+ ld a,[hl+]
+ ldh [rSCX],a
+
+ pop hl
+ pop af
+ reti
Notice that we can take advantage of the fact that there is only 2 bytes per line. We can use LY directly and quickly turn it into pointer. (Thanks to rondnelson99 for pointing this out!)
And there you have it. An automatic and stable way to take advantage of the HBlank to do whatever your imagination wants to do!
All you need to do is set the fill buffer while the draw buffer is being displayed (you have an entire frame's worth of time to do this) and the system does the rest!
This effect uses a sine table to shift each line in a pleasant way. There are 3 states to this effect:
The image is stable and a progression line moves up the screen starting each line on its way
The table cycles a few times
The image stability is restored with the progression line moving up the screen
The values in the table can dramatically change the effect. For example, if the sine cycle was short enough, you could simulate a smoke effect (for example). Try it out!
Also, you could create a 'glitch' effect during a cut-scene, perhaps in a sci-fi game to simulate a slightly dirty transmission.
This effect is structured very similar to X Sine, in that there is a table of sine values driven by 3 states. The only difference is that SCY is changed instead of SCX.
This is a really good way to simulate water reflections.
This is simply a combination of the X Sine and Y Sine effects so you can see how different it looks compared to just the X or Y changing.
Instead of a full-screen image like this tutorial uses, imagine if you had a repeating image in VRAM (bigger than the screen) that looked like water ripples. This would move just like water!
This is like a flood fill effect used as an appearance transition. It's quite simple in that it repeats the lines to achieve the 'smear' effect and is perhaps more interesting than a fade in.
The specific image used in the tutorial is light along the bottom so it looks better if the screen was already light before the effect starts. You would change this to suit your image.
This is a disappearance transition and the reverse of Smear On. Due to the specific image that was used (i.e. it is light along the bottom), it looks better in this tutorial to have the effect reveal a light screen instead of dark. Again, you would change this to suit your image.
This effect simulates an image unrolling onto the screen. This might be useful for fantasy RPGs to transition to a map screen or perhaps a message written on a scroll. The image unrolls over a dark screen because the top of the image is mostly dark so it looks better to keep it dark than the contrast of using a light screen.
This effect simulates an image rolling off screen. This might be useful for fantasy RPGs to transition away from a map or scroll screen. This reveals a dark screen because the first thing you see in the roll is dark (because that's what's in VRAM below the screen). Keeping it dark made the transition more seamless.
The roll effects look complicated but the implementation is probably one of the simpler ones. The key to make this look good is the values in the table. The roll size is 32 pixels, but you can change this to whatever size you want, provided the table values support it. This SpecBas demo was used as a reference to obtain those values.
A GNU makefile is included. You will have to tailor it for your development environment but it builds cleanly with RGBDS 0.4.2. The only dependency is hardware.inc. All of the effects are shown here so you don't have to build first to see them.
To reduce dependencies, everything is in one .asm file. It's structured in a logical way and there are comments where applicable.
The effects are called "parts" by the code and each part has an Init and Process routine. The sequence is controlled by a table of Init pointers and driven by the ProcessPartTransition routine. Each Init is responsible for setting up the data for the effect (part) and to set the Process function pointer via the SetProcessFunc macro. When the effect is done, the Process routine calls the SetChangePartFlag to tell the tutorial driver to move to the next part.
There are non-effect parts present to get the effect sequence looking good when the parts are played one after the other. These are "delay" parts of various flavors:
ShowDelay: this shows the screen normally for a few seconds
LightDelay: this shows a light-colored blank screen for a few seconds
DarkDelay: this shows a dark-colored blank screen for a few seconds
The Delay parts share code because they're only present to make the ROM look nice, but the effects parts were developed in a way to be isolated from one another. This was done to make extraction easier. Because of this, you will see similar code present across several parts, for example, the various Sine effects.
One quirk you might notice when looking at VRAM is that the tile map is placed at 0,4 instead of 0,0. This was done to get the roll/unroll to handle the top of the screen correctly. The effects look best when they smoothly (dis)appear off-screen and if the image was placed at 0,0, the code to handle that would be distracting to how to implement the core of the effect.
Another topic worth mentioning is the row of light tiles that are under the image in VRAM. This was necessary to allow LightDelay to exist. Those light tiles don't have to be right under the image, that's just where it was placed for this tutorial. It could be moved well out of the way so it doesn't affect the effects that show that part of VRAM (Y Sine, Roll On, Roll Off).
If you run the ROM in BGB and have the Debug Messages window open, you will see the various parts announce themselves when they are initialized.
You can do more things than just change the scroll registers. For example, you can change the palette. Can you do this to make the roll/unroll effect look better? Here's an example of scroll register and palette changes.
This appearance effect from Sword of Sodan (Amiga) is really cool! (And you might recognize one of the opening effects if you scrub to the beginning.)
Another raster effect you could do is a 'twist' like the one in the Wired demo.
OAM DMA hijacking is a simple technique that allows you to run custom code in most GB/SGB/CGB games, provided you have an ACE exploit.
One would be quick to point out that if you have an ACE exploit, you can already execute custom code. So then, what is the point? It's that code ran through DMA Hijacking will be run on every game frame (for most games, at least).
If you are familiar enough with OAM, you may know about a feature called OAM DMA.
OAM DMA is a convenient feature that allows quickly updating the on-screen "objects" (often known as "sprites") quickly—which is especially useful since it typically needs to occur on every frame. However, using OAM DMA requires a small routine to be copied to HRAM and then run from there.
Interestingly, most games only copy the routine when starting up, and then execute it on every subsequent frame. But, if we modified that routine while the game is running, then the game will happily run the customized routine!
Here is the standard routine, given by Nintendo in the GB programming manual (using RGBASM syntax and a symbol from hardware.inc):
ld a, HIGH(OAMBuffer)
+ ldh [rDMA], a ; $FF46
+ ld a, 40
+DMALoop:
+ dec a
+ jr nz, DMALoop
+ ret
The simplest way to get custom code (let's call it DMAHook) executed would be to overwrite the first few bytes with a jump to DMAHook:
jp DMAHook
+ db $46 ; Leftover operand byte of `ldh [rDMA], a`
+ ld a, 40 ; None of this is executed
+DMALoop:
+ dec a
+ jr nz, DMALoop
+ ret
Now, overwriting the routine like this works for our purposes, but comes with a large drawback: the routine isn't doing what it is intended to anymore, and so the game's objects won't update (unless you manually copied OAM, but beware of the OAM corruption bug). Further, it's not possible to write to rDMA from DMAHook, as the write and subsequent wait loop must be executed from HRAM.
But, there is a solution.
call DMAHook
+ ldh [c], a ; A write to `rDMA`, set up by DMAHook
+ ld a, 40
+DMALoop:
+ dec a
+ jr nz, DMALoop
+ ret
Provided that DMAHook returns with properly set registers, this allows writing to rDMA in the single HRAM byte left by the call instruction. Here is a pattern for DMAHook :
DMAHook:
+ ;; Custom code, do whatever you want, it's VBlank time!
+ ; ...
+ ld c, LOW(rDMA) ; $46
+ ld a, HIGH(OAMBuffer)
+ ret
DMAHook can live anywhere in memory, but typically it will be in WRAM. It will be executed in the context of the VBlank interrupt, so for most games interrupts will be disabled, etc.
DMA Hijacking is also useful when combined with cartswap (swapping carts without shutting the console down, concept found by furrtek, developed by Cryo and me on the GCL forums), because it allows "transporting" ACE to other games.
General procedure:
Acquire ACE in the "source" game
Perform cartswap, insert the "victim" game
"Pseudo-initialize" the victim
Place the modified DMA handler in HRAM
Transfer control back to the victim's ROM
????
Profit!
Possible applications are checking for a button combo to trigger specific code (for example, credits warp), checking one or multiple memory addresses to detect a certain game state, etc.
Possible "attack vectors", i.e. ways of affecting the victim game, are setting certain memory addresses (like a GameShark), or even better: manipulating the stack.
Here is a video demonstration:
Manipulating the stack with this technique can not crash if the triggering game state is specific enough. I achieved text pointer manipulation in Pokémon Red this way. (This is not a ROM hack!)
This new technique hinges on breaking one of any game's core assumptions: its entry point. You see, normally, the console transfers control to the game at address $0100, so any code placed there is designed to initialize all of the game's systems, in particular their memory.
However, since we have control of the CPU, we can jump to any location in the game's ROM, which allows bypassing some of said initialization. Doing so without any precautions is very likely to go haywire, though—it is important to initialize enough that the game runs, but not too much that it would end up overwriting the code we are trying to inject. This is what I call "pseudo-initialization".
Another important part is finding some free space to store the hook code in. The stack area can work surprisingly well for this, as many games appear to over-allocate (e.g. 256 bytes when the typical usage doesn't go beyond 32).
None of this has a silver bullet: the game's init code must be analyzed, and its memory usage carefully scrutinized in order to dig up enough free space for your hook.
DMA hijacking works similarly to the GameShark: that device intercepts accesses to the ROM, and when it detects that the VBlank handler is being run, it "overlays" different instructions that apply the stored codes, and jump back to the actual handler.
I encountered some games that don't transfer OAM unless a specific flag is set; I believe that it is always possible to override this limitation, by setting the flag back in the hook.
The OAM DMA routine is often placed at $FF80 in commercial games.
The patched OAM DMA routine with our hook may be modifying registers that the game expects to be preserved. This is all dependent on the target game, so no general advice can be given.
Additionally, if the hook takes too long, it may cause code expecting to run in VBlank to break. This might be solved for example by manipulating the stack and injecting an additional return address; here is an example.
jp DMAHook
+PostDMAHook:
+ ldh [c], a
+ ld a, 40
+DMALoop:
+ dec a
+ jr nz, DMALoop
+ jp hl
pop hl ; Get original return address
+ ld bc, PostHandlerHook ; Address of code that will be executed once the VBlank handler finishes
+ push bc ; Inject return address for VBlank handler
+ ld c, LOW(rDMA)
+ ld a, HIGH(OAMBuffer)
+ jp PostDMAHook
(Since the handler almost certainly performs some pops before returning, you will almost certainly need more complex stack manipulation, but that's the gist of it.)
Some games have a slightly more clever routine in HRAM, that omits the initial ld a, HIGH(OAMBuffer) saving 2 bytes of HRAM.
ldh [rDMA], a
+ ld a, 40
+DMALoop:
+ dec a
+ jr nz, DMALoop
+ ret
They can still be patched by overwriting the ld a, 40 instead, and using e.g. the b register for the loop:
call DMAHook
+ ldh [c], a ; Write to rDMA
+DMALoop:
+ dec b
+ jr nz, DMALoop
+ ret
Then DMAHook needs to return with b additionally set to 40:
DMAHook:
+ ;; Custom code, do whatever you want, it's VBlank time!
+ ; ...
+ ld bc, 40 << 8 | LOW(rDMA) ; 40 in B, $46 in C
+ ld a, HIGH(OAMBuffer)
+ ret
However, if the OAM buffer address passed to the function (in a) is not static, push af and pop af will have to be used instead of ld a, HIGH(OAMBuffer).
+
+
+
diff --git a/guides/lyc_timing.html b/guides/lyc_timing.html
new file mode 100644
index 00000000..080cffe7
--- /dev/null
+++ b/guides/lyc_timing.html
@@ -0,0 +1,160 @@
+
+
+
+
+
+
+
+
+ The Timing of LYC STAT Handlers | gbdev.io
+
+
+
+
+
+
Raster effects are probably the greatest assets that retro game consoles have. The fact that the PPU generates the image right as it is displayed allows many special effects to be created by modifying the rendering parameters while the image is being drawn. Here is an example:
However, unlike some consoles like the SNES, the Game Boy contains no hardware dedicated to raster effects, so the task falls squarely on the CPU. This causes raster FX code to interact with the rest of the program in complex ways, particularly when it comes to accessing VRAM.
In this article, we will explore different techniques for handling raster effects, and discuss their pros and cons with the help of some diagrams.
PRIOR KNOWLEDGE ASSUMED
This article is not a friendly introduction to programming raster effects, and assumes you are already comfortable with Game Boy programming. To learn more about how to achieve neat raster effects like the above, check out DeadCScroll first, which the above GIF is actually from!
Additionally, since the operations discussed here are extremely timing-sensitive, discussions will revolve around assembly instructions. You can learn how to program for the Game Boy in assembly in GB ASM Tutorial.
TERMINOLOGY
We'll reference a few terms throughout this tutorial; here are brief explanations of them:
SoC: System-on-a-Chip, a single chip that includes most (or all!) components of a system. The Game Boy's functionality is almost entirely contained within a single chip, confusingly labelled "DMG-CPU" or similar. (Contrast this with, for example, the SNES, where there is one chip for the CPU, two for the PPU, and many more.)
CPU: Central Processing Unit, the part of the SoC that executes code and configures everything else.
PPU: Pixel Processing Unit, the part of the SoC that is responsible for sending pixels to the LCD and generating them.
Rasterization: the process of turning... something (for example, a collection of textured polygons; or, on the GB, tiles and tilemaps) into an array of pixels. "Raster" is sort of a contraction of that term.
Scanline: a row of pixels; it's called a "scan"-line because the lines get drawn one by one, pixel by pixel, as if the PPU was "scanning" along the screen.
Register: in general, a small piece of memory, usually linked to some hardware component.
PPU mode: The PPU can be in one of four modes at a given time, depending on what it's doing. Please refer to Pan Docs to learn what each mode corresponds to and how they are scheduled—they interact very tightly with raster effects.
Interrupt: an event that gets generated. Typically, this causes a "handler" to be called, which is a special routine dedicated to reacting to a given interrupt.
"Main thread": any code that is executed outside of interrupt handlers.
The Game Boy permanently compares the value of the LYC and LY registers. When both values are identical, the “LYC=LY” flag in the STAT register is set, and (if enabled) a STAT interrupt is requested.
So then, the outline for setting up a raster effect is as follows:
Register an interrupt by setting LYC to the desired scanline
When that scanline begins, the STAT interrupt handler will automatically be called
Perform your chosen effect by modifying PPU registers
Exit the handler with reti
ALTERNATIVES
There are other ways to perform raster FX, such as busy-waiting in the "main thread", but as this article's title suggests, we won't discuss them here.
A major pro of LYC-interrupt-based raster effects is that they can be made self-contained, and thus largely independent of whatever the "main thread" is doing. This, in turn, simplifies the mental complexity of the code (decoupling), copes better with lag frames, and more.
Many of the points brought forth later, particularly regarding cycle counting, are still relevant with these alternatives, so this is still worth reading!
These four steps sound simple enough on their own, but there are numerous caveats we will discuss. Strap in!
Most raster effects are implemented by modifying registers between scanlines. Thus, you will want to write the register either during Mode 2 (of the same scanline), or Mode 0 (of the previous one)—anything but Mode 3, really. Unfortunately, LY=LYC interrupts are requested at the beginning of a scanline, so during the very short Mode 2, leaving too little time to perform but the most basic of effects.
Writing to the register during HBlank instead implies triggering the interrupt on the scanline above the effect, as well as idling for most of the scanline. So, if I wanted to enable sprites on scanline 16, I'd write 15 to LYC.
Mode 3's length is variable, so syncing to HBlank is difficult and time-consuming.
The interrupt handler's execution may be delayed by a few cycles, which makes it difficult to reliably sync to the PPU.
If the "main thread" is itself trying to sync with the PPU (typically by polling STAT in a loop), our interrupt may throw off its timing.
A "dot" is one period of the PPU's 4 MiHz clock, i.e. 0.25 µs.
A "cycle" is the main unit of time in the CPU, which is equal to 1 µs, or 4 dots. (The Game Boy Color CPU can enter a "double-speed" mode which halves the length of cycles, but not of dots. For the sake of simplicity, we won't consider the differences it involves here.)
Each scanline takes exactly 456 dots, or 114 cycles.
Mode 2 also takes a constant amount of time (20 cycles)
HBlank's length varies wildly, and will often be nearly as long as or longer than the drawing phase.
HBlank and OAM scan are mostly interchangeable, and long as you're not writing to OAM.
The worst-case HBlank's length is not a multiple of 4 dots, so we will round down to 21 cycles.
Let's consider a simple STAT handler, which disables OBJs if called at line 128, and enables them otherwise:
LYC::
+ push af
+ ldh a, [rLY]
+ cp 128 - 1
+ jr z, .disableSprites
+
+ ; enable sprites
+ ldh a, [rLCDC]
+ or a, LCDCF_OBJON
+ ldh [rLCDC], a
+ pop af
+ reti
+
+.disableSprites
+ ldh a, [rLCDC]
+ and a, ~LCDCF_OBJON
+ ldh [rLCDC], a
+ pop af
+ reti
Tips
This is not an especially well-written STAT handler, but the actual is outside the scope of this tutorial. If that's what you're looking for, check out DeadCScroll by Blitter Object. It triggers the STAT interrupt on HBlanks rather than LYC, but the fundamentals are the same.
Note that, for simplicity's sake, DeadCScroll does not consider the problems described further below, so be wary of combining that tutorial's STAT handler unmodified with STAT-based VRAM accesses in the main thread.
Let's assume that the interrupt fires at, say, scanline 42. Equipped with the GB instruction table (see its legend at the bottom), we can plot how many cycles each operation takes, in relation with the PPU's mode:
Legend
PPU Mode
2
OAM scan
3
Drawing
0
HBlank
CPU operation
Interrupt dispatch
Write to LCDC
Return from interrupt
Scanline cycle
Instruction
Scanline cycle
Instruction
0
1
2
3
4
5
push af
6
7
8
9
ldh a, [rLY]
10
11
12
cp 128 - 1
13
14
jr z, .disableSprites
15
16
ldh a, [rLCDC]
17
18
19
or a, LCDCF_OBJON
20
21
ldh [rLCDC], a
22
23
24
pop af
25
26
27
reti
28
29
30
The first 5 cycles do not have an instruction: indeed, calling an interrupt handler is not instantaneous, and the CPU is temporarily busy pushing the program counter (PC) to the stack, disabling interrupts, etc. Then, the actual interrupt handler begins execution.
We can immediately spot a problem: the cycle during which LCDC is written to falls in the middle of rendering! (With only a handful of exceptions, instructions that access memory do so on their very last cycle.) This is usually undesirable, and could lead to graphical glitches like an OBJ being partially cut off until we write to LCDC.
Another problem, less obvious but oh so painful, is how the interrupt handler might interact with the "main thread"'s operation.
Accessing VRAM is not possible during Mode 3. Thus, when we want to access VRAM, precautions must be taken; the most common is to use the following loop:
.waitVRAM
+ ldh a, [rSTAT]
+ and STATF_BUSY ; 2
+ jr nz, .waitVRAM
This loop checks whether [STAT] & 2 is zero, and exits when it does. Looking at documentation for STAT, we can see that the lowest 2 bits report the PPU's mode, and that [STAT] & 2 is zero for Mode 0 and Mode 1, but not Mode 2 or Mode 3. So, essentially, this loop waits for Mode 0 or Mode 1, which are both safe to write to VRAM—but it can't be that simple.
Legend
PPU Mode
2
OAM scan
3
Drawing
0
HBlank
CPU operation
Read from STAT
VRAM accesses
Scanline cycle
Instruction
Scanline cycle
Instruction
111
ldh a, [rSTAT]
112
113
0
and STATF_BUSY
1
2
jr nz, .waitVRAM
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
(...)
21
Pictured above is the "worst case" for this loop. As you can see, on the cycle that STAT is read, the PPU is still in Mode 0; however, checking for it takes a few cycles, during which we enter Mode 2!
Now, thankfully, Mode 2 is also safe for accessing VRAM—but only 16 cycles of it remain. This is why this loop is said to guarantee 16 "VRAM-safe" cycles: any access performed 17 cycles or more after it would break in this worst case.
Now, what would happen if our interrupt was requested in the middle of this?
Legend
PPU Mode
2
OAM scan
3
Drawing
0
HBlank
CPU operation
Read from STAT
Interrupt dispatch
Return from interrupt
VRAM accesses
Scanline cycle
Instruction
Scanline cycle
Instruction
111
ldh a, [rSTAT]
112
113
0
1
2
3
4
5
push af
6
7
8
9
ldh a, [rLY]
10
11
12
cp 128 - 1
13
14
jr z, .disableSprites
15
16
ldh a, [rLCDC]
17
18
19
or a, LCDCF_OBJON
20
21
ldh [rLCDC], a
22
23
24
pop af
25
26
27
reti
28
29
30
31
and STATF_BUSY
32
33
jr nz, .waitVRAM
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
Oh no! The main thread is now trying to access VRAM right in the middle of Mode 3! This could lead to all sorts of visual bugs.
The solution is not too complicated, at least on paper. We should be able to use the same STAT-checking loop (or at least, a variation of it) inside of the handler. It works in the main thread, so it should work here as well, right?
Remember that many STAT handlers will be much more complicated than the simple example above, so let's draw a diagram with an imaginary handler that would take significantly more time:
Legend
PPU Mode
2
OAM scan
3
Drawing
0
HBlank
CPU operation
Read from STAT
Interrupt dispatch
Return from interrupt
VRAM accesses
Scanline cycle
Instruction
Scanline cycle
Instruction
111
ldh a, [rSTAT]
112
113
0
1
2
3
4
5
(...)
...
96
97
ldh a, [rSTAT]
98
99
100
and STATF_BUSY
101
102
jr nz, .handlerWait
103
104
(Write to LCDC)
105
106
107
pop hl
108
109
110
pop af
111
112
113
reti
0
1
2
3
and STATF_BUSY
4
5
jr nz, .waitVRAM
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Tips
All the instructions between the "Interrupt dispatch" and "Return from interrupt" blocks are the interrupt handler, the rest is in the "main thread".
The STAT loop does fix the register being written to during Mode 3; however, once again, the 16 cycles that "main thread" expects to be VRAM-safe overlap with Mode 3. The problem here is that the write, pop and reti all take some of those cycles, and the "main thread" is using the value it read from STAT during the previous scanline—but that value is now stale.
It's entirely possible to accept the loss of some of those cycles. This amounts to assuming less than the usual 16 cycles after such loops. For example, putting a STAT-polling loop just before the last pop af and reti would have these two eat up 7 cycles, so we are down to 9.
This will quickly become impractical, requiring syncing to the LCD much more often in the main thread.
A simple way to prevent those pesky handlers from throwing off our timing is to disable them, with the di instruction. Unfortunately, it can't quite be so simple, as using di for this brings its own share of problems.
The most important one is that disabling the handlers like this delays their execution! STAT handlers designed to write to hardware regs during HBlank may start doing so during rendering instead; timer interrupts won't trigger as regularly now; and so on.
Using di is valid in some cases, but typically not when STAT interrupts are involved, due to their fairly strict timing requirements.
An oddly common alternative is to perform all VRAM updates in VBlank handler. (The reason why it's common especially in early GB games is likely being a carry-over from the NES, where the lack of HBlanks essentially mandates such a setup anyway.) While this can work, such as for Metroid II, it requires significant complexity from having to keep deferring graphical updates.
This is the solution that the rest of this article will explore, as we will see that it makes the least painful compromises out of most use cases.
So, the real solution is to fully exit before the end of HBlank. There are two ways to do this. One is to wait for the Drawing phase before waiting for HBlank. This effectively catches the very start of HBlank, leaving plenty of time to exit. Here's how the earlier example might look using this method:
LYC::
+ push af
+ push hl
+ ldh a, [rLY]
+ cp 128 - 1
+ jr z, .disableSprites
+
+ ; enable sprites
+ ldh a, [rLCDC]
+ or a, LCDCF_OBJON
+ jr .finish
+
+.disableSprites
+ ldh a, [rLCDC]
+ and a, ~LCDCF_OBJON
+
+.finish
+ ld hl, rSTAT
+.waitNotBlank
+ bit STATB_BUSY, [hl]
+ jr z, .waitNotBlank
+.waitBlank
+ bit STATB_BUSY, [hl]
+ jr nz, .waitBlank
+
+ ldh [rLCDC], a
+ pop hl
+ pop af
+ reti
See how this method never interferes with VRAM accesses in the main thread, even with the worst possible timing and the shortest of HBlanks:
Legend
PPU Mode
2
OAM scan
3
Drawing
0
HBlank
CPU operation
Interrupt dispatch
STAT is tested
Return from interrupt
VRAM accesses
Scanline cycle
Instruction
Scanline cycle
Instruction
111
ldh a, [rSTAT]
112
113
0
1
2
3
4
5
push af
6
7
8
9
push hl
10
11
12
13
ldh a, [rLY]
14
15
16
cp 128 - 1
17
18
jr z, .disableSprites
19
20
ldh a, [rLCDC]
21
22
23
or a, LCDCF_OBJON
24
25
jr .finish
26
27
28
ld hl, rSTAT
29
30
31
bit STATB_BUSY, [hl]
32
33
34
jr z, .waitNotBlank
35
36
bit STATB_BUSY, [hl]
37
38
39
jr nz, .waitBlank
40
41
42
(...)
...
83
84
bit STATB_BUSY, [hl]
85
86
87
jr nz, .waitBlank
88
89
90
bit STATB_BUSY, [hl]
91
92
93
jr nz, .waitBlank
94
95
96
bit STATB_BUSY, [hl]
97
98
99
jr nz, .waitBlank
100
101
ldh [rLCDC], a
102
103
104
pop hl
105
106
107
pop af
108
109
110
reti
111
112
113
0
and STATF_BUSY
1
2
jr nz, .waitVRAM
3
4
(...)
...
19
Phew! This just barely works. There are only two cycles to spare! If there were multiple registers that needed updating, you might run into trouble. Normally, These really short HBlanks are the worst-case scenario that you always fear. However, in practice, HBlanks are normally much longer, often even longer than the drawing phase. Using this method, that can actually have unfortunate consequences:
Legend
PPU Mode
2
OAM scan
3
Drawing
0
HBlank
CPU operation
Interrupt dispatch
STAT is tested
Return from interrupt
VRAM accesses
Scanline cycle
Instruction
Scanline cycle
Instruction
111
ldh a, [rSTAT]
112
113
0
1
2
3
4
5
push af
6
7
8
9
push hl
10
11
12
13
(...)
...
58
59
ld hl, rSTAT
60
61
62
bit STATB_BUSY, [hl]
63
64
65
jr z, .waitNotBlank
66
67
68
(...)
...
1
2
bit STATB_BUSY, [hl]
3
4
5
jr z, .waitNotBlank
6
7
bit STATB_BUSY, [hl]
8
9
10
jr nz, .waitBlank
11
12
13
(...)
...
60
61
bit STATB_BUSY, [hl]
62
63
64
jr nz, .waitBlank
65
66
ldh [rLCDC], a
67
68
69
pop hl
70
71
72
pop af
73
74
75
reti
76
77
78
79
and STATF_BUSY
80
81
jr nz, .waitVRAM
82
83
(...)
...
98
This time, when all the processing was done, there was still plenty of time left in the scanline to safely exit. However, since HBlank was so long, the routine missed the check for the drawing window and wasted an entire scanline waiting for that Drawing -> HBlank transition before it exited. Not only does this waste precious CPU time, but it also limits how often raster FX can be used throughout the frame. This method still works fine though, and can be an easy approach if you use Raster FX sparingly.
I'm a bit of a perfectionist, so I usually like to strive for the absolute best method. In a perfect world, we would precisely know whether we have enough HBlank left to safely exit. There actually is a way to do that though! You just need to count exactly how long your routine takes, and make sure it always exits during HBlank. This comes with some caveats though. Most routines, if they haven't been specifically designed for this method, will take a variable amount of time. The main things you need to avoid are if statements and loops. Specifically, if statements of this form are problematic:
; test a condition here...
+
+ jr nc, .skip ; skip the next part unless Carry is set
+
+ ; do something here, only if the previous operation set Carry
+
+.skip
+ ; continue on with the program.
The problem here is that the code following this pattern may be run after a variable number of cycles have passed. If you need to use an if statement, always make it an if/else statement so that you can waste cycles in the else portion and take the same number of cycles.
So now that you're ready to count the cycles of your handler, how long do you need to make the routine? Let's look at some more diagrams to figure this out!
Legend
PPU Mode
2
OAM scan
3
Drawing
0
HBlank
CPU operation
STAT read
Interrupt dispatch
Return from interrupt
VRAM accesses
Scanline cycle
Instruction
Scanline cycle
Instruction
111
ldh a, [rSTAT]
112
113
0
1
2
3
4
5
(...)
...
109
110
reti
111
112
113
0
and STATF_BUSY
1
2
jr nz, .waitVRAM
3
4
(...)
...
19
Wow! That's a lot of cycles! Here, the routine takes exactly one scanline to complete, so the main thread does its writes at the same moment on the next scanline, with no idea what happened! If you count up all the cyan cycles, you'll see that there are 105 of them, and 109 if you count the reti. This extra time makes it possible to write to two or three registers safely, rather than just one. If you don't need all that time, you can make it shorter as well:
Legend
PPU Mode
2
OAM scan
3
Drawing
0
HBlank
CPU operation
STAT read
Interrupt dispatch
Return from interrupt
VRAM accesses
Scanline cycle
Instruction
Scanline cycle
Instruction
107
ldh a, [rSTAT]
108
109
110
and STATF_BUSY
111
112
jr nz, .waitVRAM
113
0
1
2
3
4
5
(...)
...
88
89
reti
90
91
92
93
(...)
...
108
This time, I put the and and jr before the interrupt, so that when it resumes, it's all ready to start writing to VRAM. This interrupt routine is 87 cycles long, including the reti. This won't often prove especially useful though, because you never take any time during HBlank to actually do any register writes. However, you could use this if your routine has a case where it realizes that nothing actually needs to be written, and you can exit earlier.
From those two diagrams, you'll see that the 22 cycles of worst-case HBlank is the time you can use to write to any PPU registers, pop your registers back, and then exit with reti. These 22 cycles are cycle 88 through cycle 109, inclusive.
What if I told you that you could actually have your handler take only 86 cycles? Well, you can!
Legend
PPU Mode
2
OAM scan
3
Drawing
0
HBlank
CPU operation
STAT read
Interrupt dispatch
Return from interrupt
VRAM accesses
Scanline cycle
Instruction
Scanline cycle
Instruction
107
ldh a, [rSTAT]
108
109
110
and STATF_BUSY
111
112
jr nz, .waitVRAM
113
0
1
2
3
4
5
(...)
...
87
88
reti
89
90
91
92
(...)
...
107
This seems bad, since the first cycle of the red bar, where the main thread may try to access VRAM, is potentially during the Drawing phase! This is also fine though. All instructions that access memory, whether through an immediate address or using a register pair as a pointer, take multiple cycles to complete. That's because the first cycle of every instruction is used to fetch the operation code itself. The memory access that the instruction performs is always in the 2nd, 3rd or 4th cycle of the instruction. In this situation, the 2nd cycle of the VRAM-accessible time is in HBlank, so this won't actually cause any problems.
The interrupt latency I showed earlier doesn't actually tell the full story. Before it even starts to service the interrupt, the system waits for the current instruction to finish. This is how that might look with the longest allowable routine:
Legend
PPU Mode
2
OAM scan
3
Drawing
0
HBlank
CPU operation
STAT read
Interrupt dispatch
Return from interrupt
VRAM accesses
Scanline cycle
Instruction
Scanline cycle
Instruction
106
ldh a, [rSTAT]
107
108
109
and STATF_BUSY
110
111
jr nz, .waitVRAM
112
113
call SomeFunc
0
1
2
3
4
5
6
7
8
9
10
(...)
...
0
1
reti
2
3
4
5
(...)
...
14
Here, the first green block shows the system waiting 5 cycles for a call instruction to finish. call is the longest instruction at 6 cycles, so if the interrupt is requested just after it begins, the system will wait 5 cycles for it to complete. This seems bad, since the routine exited after the end of HBlank. However, this is actually fine! Those waiting cycles were not wasted; they were still 5 cycles of work that the main thread got done. So in the end, the main thread still gets its 20 cycles of VRAM-accessible time.
leaves more time for more complex logic in the routine
allows enough time during blanking to write to up to three registers
never takes longer than one scanline
Cons
requires all code to be constant-time
requires tedious cycle-counting
always takes close to an entire scanline, even if HBlank starts much sooner
This suggests that the double-busy-loop method is good for extremely simple LYC routines that only need to write to one register, or routines that for some reason cannot be cycle-counted. If you need more time for calculations and more time to write to those registers, you can cycle-count your routine.
But what if you could combine both these methods? Enter the Hybrid Cycle-Counted Handler™, a technique I came up with while writing this document.
The goal of this method is to combine the maximum HBlank time that cycle-counting delivers, while still exiting early when HBlank is longer. Here is an example. If you've read DeadCScroll, you'll recognise this as that tutorial's STAT Handler, modified to start at Mode 2 rather than HBlank, and be safe towards VRAM accesses in the main thread.
push af ; 4
+ push hl ; 8
+
+ ; obtain the pointer to the data pair
+ ldh a, [rLY] ; 11
+ inc a ; 12
+ add a, a ; 13 ; double the offset since each line uses 2 bytes
+ ld l, a ; 14
+ ldh a, [hDrawBuffer] ; 17
+ adc 0 ; 19
+ ld h, a ; 20 ; hl now points to somewhere in the draw buffer
+
+ call UnconditionalRet ; just waste 31 cycles while we wait for HBlank to maybe start
+ call UnconditionalRet
+ call UnconditionalRet
+ nop ; 51
+
+ ; now start trying to look for HBlank to exit early
+
+ ldh a, [rSTAT]
+ and STATF_BUSY
+ jr z, .setAndExit ; 58
+
+ ldh a, [rSTAT]
+ and STATF_BUSY
+ jr z, .setAndExit ; 65
+
+ ldh a, [rSTAT]
+ and STATF_BUSY
+ jr z, .setAndExit ; 72
+
+ ldh a, [rSTAT]
+ and STATF_BUSY
+ jr z, .setAndExit ; 79
+
+ nop ; waste 4 more cycles since there isn't time for another check
+ nop
+ nop
+ nop ; 83
+
+.setAndExit
+ ; set the scroll registers
+ ld a,[hl+] ; 85
+ ldh [rSCY],a ; 88
+ ld a,[hl+] ; 90
+ ldh [rSCX],a ; 93
+
+ pop hl ; 97
+ pop af ; 100
+ reti ; 104
Once the handler finishes its logic, the handler delays cycles until it reaches the window then HBlank might start. With a 5-cycle offset due to a call, and the longest possible HBlank, the earliest HBlank might start is cycle 54, so that's the first attempt to read STAT. It keeps checking STAT until even in the worst-case scenario, it knows that HBlank will start. Then, it uses that time to write the scroll registers and exit. This way, it can still exit early, as long as the HBlank length permits. This routine takes 104 cycles in the worst-case scenario, but may take as few as 79 if HBlank comes sooner.
The reason that the double-busy-loop method requires checking for Mode 3 but this method does not is that the double-busy-loop method is not cycle-counted, so you might be at the very end of HBlank which is problematic. Since this method is cycle-counted, you know that if HBlank has begun, you are at or near the start of it.
If we make a similar list of pros and cons for this method, this is what it might look like:
allows enough time during blanking to write to up to three registers
never takes longer than one scanline
Cons
requires all code to be constant-time
requires tedious cycle-counting
This method can work well in many circumstances, and is especially suited to frequent effects that modify multiple registers and need to exit quickly to avoid taking too much CPU time. This method can even work reasonably well when used on every scanline through the Mode 2 interrupt.
All three of these methods can generate great-looking effects, but I think the third one is an especially attractive option.
Congrats! You made it to the end of the tutorial! I bet you're tired of reading it, and I'm tired of writing it too. So thanks for reading, see you next time!
Before we can do anything else, we must first specify in the header that this game is aware of SGB features. Otherwise, the SGB BIOS will ignore any packets we send, and we won't even be able to detect when the program is running on SGB.
The SGB BIOS can be "talked to" via command packets, sent bit by bit via the P1/JOYP register.
An SGB packet consists of:
a "start" pulse (P1=%xx00xxxx)
128 data pulses (P1=%xx01xxxx for "1", P1=%xx10xxxx for "0")
a "0" pulse (P1=%xx10xxxx)
You must set P1 to %xx11xxxx between each pulse.
This adds up to 16 bytes of data (LSB first). If a packet doesn't read all 16 bytes, the unused bytes are ignored.
You should wait 4 frames between each packet and the next. This gives the SGB BIOS a chance to receive a packet even if it is doing something else time-consuming.
Bulk transfer (TRN) packets tell the SGB to copy the contents of the screen to buffers in Super NES work RAM. The CHR_TRN and PCT_TRN packets are used to send data for SGB borders.
For a transfer to function properly, you must prepare VRAM and the LCD registers:
set BGP to $e4 and LCDC to $91 (screen enabled, BG uses tiles $8000-$8fff and tilemap $9800, WIN and OBJ disabled, BG enabled)
set SCX and SCY to $00
the tilemap consists of $00, $01..$13, 12 bytes padding (offscreen), $14..$27, padding, repeat until $ff (inclusive)
the data you want to send must be loaded at $8000-$8fff
You must load the data into VRAM and enable the screen before sending the TRN packet. The SGB reads TRN payloads from the screen. If rendering is off, there is nothing on the screen to read.
You must wait ~8 frames after each TRN instead of just 4. The SGB BIOS has to finish what it's doing, receive the packet, and then read the screen.
Here's how a SGB detection routine should look like:
Wait 12 or more frames for the SGB BIOS to start listening for packets.
Send a MLT_REQ packet selecting 2 or 4 players ($89, $01 or $89, $03), and wait a couple frames for the SGB to receive the packet.
Read the controller. Set P1 bit 5 to 0, then set P1 bits 5 and 4 to %11 (%xx11xxxx) to release the key matrix.
Read the low nibble (bits 3-0) of P1, and look for a value other than %1111 (which indicates player 1).
If player 1 was still found, repeat steps 3 and 4 once more, in case the next read indicates player 2.
Optionally turn off multiplayer mode.
If a non-%1111 value was found in step 4 either time, the program is running on SGB.
A routine like this may be used to detect SGB (modified from source):
SGB_Detect:
+ ; test for SGB
+ di
+ call SGB_Wait4Frames
+ call SGB_Wait4Frames
+ call SGB_Wait4Frames
+
+ ; Send MLT_REQ packet to enable multiplayer
+ ld hl, Packets.mltOn ; send MLT_REQ for 2 players
+ call SGB_SendPacket
+ call SGB_Wait4Frames
+
+ ; Detect the SGB by checking if SGB responded to MLT_REQ.
+ ; Setting P1.5 to low then high advances the selected player.
+ ; Setting P1.4 and P1.5 high causes the ICD2 to reflect the
+ ; selected player in low bits of P1.
+ ld b, 4 ; Number of attempts
+ ld c, LOW(rP1) ; Address to write
+ .loop
+ ld a, P1F_4 ; Try to advance player
+ ldh [c], a
+ ld a, P1F_4|P1F_5 ; Set P1 to return the selected player
+ ldh [c], a
+ ; In case this is DMG and not SGB, let the input lines settle
+ ; for a few cycles before reading P1 again
+ call SGB_Wait4Frames.knownRet
+
+ ldh a, [c] ; Player 1 has A.0 = 1; players 2 and 4 have A.0 = 0
+ cpl ; Invert this
+ and %00000001 ; Keep only bit 0, which is 0 for player 1 or nonzero for players 2 and 4
+ jr nz, .done
+ dec b ; Keep trying until we've cycled through all players
+ jr nz, .loop
+ .done
+ ; After this loop, A is $01 for SGB or $00 for not SGB. Remember this
+ ld [wIsSGB], a
+
+ ; (Optional) Disable multiplayer
+ ld hl, Packets.mltOff ; send MLT_REQ for 2 players
+ call nz, SGB_SendPacket
+ ret
+
+SGB_Wait4Frames:
+ ld bc, -(456 * 154 / 16 * 4)
+ .loop
+ inc c ; inner loop takes 16 T-states per iteration
+ jr nz, .loop
+ inc b
+ jr nz, .loop
+ .knownRet
+ ret
It would be a good idea to save somewhere in RAM whether the game is running on an SGB capable device or not, such that if you wish to change the border mid-gameplay, you won't have to perform SGB detection again. The sample code stores it in wIsSGB.
If you wish to only use 1 controller for the game, you will have to send another MLT_REQ to disable multiplayer ($89, $00)
SGB_Wait4Frames above uses busy waiting. Depending on the structure of your initialization code, you can change it to use vblank interrupts or di+halt instead.
--color-zero should be the color that your image for transparency, in my case it was blue.
If your image has an alpha channel, it can be set can also be set to 00000000 to use the actual transparent color; however, this may cause some issues.
-v is optional, for showing details of the conversion process
You can add a row of the transparent color at the top of the image to force superfamiconv to make it tile #0, then incbin "output.pct", 64 to leave out that row.
-P 4 sets the base palette to the 4th one, and SGB borders use SNES palettes 4, 5, and 6. as of writing this, this option only works if you built superfamiconv from source.
As stated before, the SGB border consists of tile data, picture data, and palette data. These are split across 2-3 packets:
CHR_TRN ($99) is used to send 4KiB of tile data.
since the border can use up to 8KiB of tiles, bit 0 of the second byte specifies which "half" you're sending
$99, $00 if the screen is loaded with the first 4KiB of tile data
$99, $01 if the screen is loaded with the second 4KiB of tile data
PCT_TRN ($a1) is used to send the picture and palette data. it also swaps the border, generally a good idea to send it after the tile data[^1]
assuming tiles 0-255 use VRAM from $8000 to $8fff:
the picture data must be at $8000-$873f (last 64 bytes are usually offscreen, see notes)
palette data must be at $8800-$885f
everything else is ignored
how you skip putting data at $8740-$87ff is up to you, I prefer doing separate copies, others prefer copying tilemap and palette data in one go, with the area between them padded.
[^1]: You can send a CHR_TRNup to ~60 frames after the PCT_TRN for it to apply to the current border, but not all emulators will emulate this. It's fine to just pretend CHR_TRNs must go before PCT_TRN.
You can set the first row of tiles to your transparent color to force superfamiconv to put the transparent tile as the 1st tile, however you must then exclude 64 bytes of the tilemap (incbin "border.pct" -> incbin "border.pct", 64)
SGB BIOS reserves palettes 4 through 6 for borders. If you really know what you're doing, you may be able to use palette 0 (the gameplay palette) for animated borders. You will probably have to edit the border in a tile editor such as YY-CHR, as there aren't yet any other tools for that.
When the SNES lags, scanline 225 of the SGB border will be visible! You can set the topmost row of the 29th row of tiles to black to hide this.
If this doesn't work for you, you can ask for help on the gbdev channels.
+
+
+
diff --git a/guides/tools.html b/guides/tools.html
new file mode 100644
index 00000000..85a2fa89
--- /dev/null
+++ b/guides/tools.html
@@ -0,0 +1,55 @@
+
+
+
+
+
+
+
+
+ Choosing tools for Game Boy development | gbdev.io
+
+
+
+
+
+
This essay gives an overview of the Game Boy's capabilities, discussing the pros and cons of the available development tools, and providing a few tips to write more efficient code.
In the past few years as retro gaming has grown in popularity, programming for older platforms has also gained traction. A popular platform is the Game Boy, both for its nostalgia and (relative) ease to program for.
Warning
This document only applies to the Game Boy and Game Boy Color. Game Boy Advance programming has little in common with Game Boy programming.
If you want to program for the GBA, which is much more C-friendly (and C++ and Rust, for that matter) than the GB and GBC, then I advise you to download devkitARM and follow the Tonc tutorial. Please note that the Game Boy Advance also functions as a Game Boy Color, so if you only have a GBA, you can use it for both GB and GBC development.
When someone wants to make their own game, one of the first problems they will encounter is picking the tools they will use. There current main options are:
RGBDS (Rednex Game Boy Development System) and the Game Boy's Assembly language (ASM)
GBDK-2020 (Game Boy Development Kit) and the C language
ZGB (an engine built on GBDK-2020) and the C language
GB Studio (a drag-and-drop game creator with scripting)
The purpose of this document is to provide some insights and help you make the better choice if you're starting a new project. I will also provide some "good practice" tips, both for C and ASM, if you have already made up your mind or are already using one of these.
The original Game Boy, codenamed the DMG, has a 1 MHz CPU [the CPU is actually clocked at 4 MHz, but every instruction takes up a multiple of 4 clocks, so it's often simplified to a 1 MHz CPU]. Given that an instruction takes approximately 2 to 3 cycles, this gives the CPU a capacity of 333,000~500,000 instructions per second. Its LCD boasts 60 fps [it's actually 59.73 fps], which rounds up to between 50,000 and 80,000 instructions per frame. Suddenly not so much, eh? It also has 8 kB of RAM, and 8 kB of video RAM ; a 160x144 px LCD (thus slightly wider than it's tall), 4 colors, and 4-channel audio.
The Super Game Boy adds a few minor things, such as a customizable screen border, and some crude color. It's also slightly faster than the DMG.
The Game Boy Color can [if you tell it to] unlock additional functionality, such as more fleshed-out color, a double-speed CPU, twice the video RAM and four times the RAM! (With caveats, obviously.)
The choice of programming language is important and can have a very large effect on a project. It determines how much work is involved, what will be possible, and how fast it will be able to run.
C will typically be used with the SDCC compiler and GBDK-2020 or ZGB, though it can also be used on its own without a framework or with a different compiler/dev kit (such as z88dk).
Strengths:
Allows for getting up and running faster than with ASM, especially when building on top of GBDK-2020 and ZGB.
The language abstractions make it relatively easy to implement ideas and algorithms.
C source debugging is available through Emulicious with the VSCode debug adapter, making it easier to understand problems if they arise.
ASM can be included in projects with C, either standalone or inline for speed critical features.
Weaknesses:
The SDCC C compiler won't always generate code that runs as fast as skilled, hand-optimized assembly. It has matured a lot in the 20 years since the original GBDK, but bugs still turns up on occasion. On a platform with a slow CPU such as the Game Boy this can be a factor.
It’s easier to write inefficient code in C without realizing it. The Game Boy's CPU is only capable of performing 8-bit addition or subtraction, or 16-bit addition. Using INT32s is quite taxing on the CPU (it needs to do two consecutive 16-bit adds, and add the carry). See the tips below to avoid such blunders.
There is overhead due to C being a stack-oriented language, whereas the Game Boy's CPU is rather built for a register-oriented strategy. This most notably makes passing function arguments a lot slower, although SDCC has some optimizations for this.
Using a GUI instead- If you don’t want to learn a programming language in order to make Game Boy games, then GB Studio is an option. See the GB Studio section for more details.
RGBDS is an actively maintained suite of programs that allow building a ROM using ASM (assembly). It contains three programs that perform different stages of the compilation, as well as a program that converts PNG images to the Game Boy's tile format. RGBDS is available for Linux, Windows and MacOS.
Strengths:
Very knowledgeable community with a lot of history.
Built in support for ROM banking.
Works quite well with BGB for debugging.
Weaknesses:
Provides a limited amount of built-in code and functionality (does not include a large API like GBDK-2020 does).
GBDK-2020 is a development kit and toolchain built around the SDCC C compiler which allows you to write programs in C and build ROMs. It includes an API for interfacing with the Game Boy. GBDK-2020 is a modernized version of the original GBDK. It's available for Linux, Windows and MacOS.
Strengths:
Flexible and extensible.
Comprehensive API that covers most hardware features.
Many sample projects and open source games are available that demonstrate how to use the API, hardware, and structure games.
C source debugging is available with Emulicious.
Weaknesses:
Takes care of some aspects of the hardware without requiring the developer to initiate them (such as OAM DMA during VBLANK), so it's not always obvious to beginners what the hardware is doing behind the scenes, or how to fix them when something goes wrong.
ROM banking may require more management in code than RGBDS.
ZGB is a small engine for the Game Boy built on top of GBDK-2020 and written in C. Strengths:
The basic graphics, sound and event structure are all pre-written, so it’s faster and easier to start writing a game.
Several open source games built with it are available as examples.
Weaknesses:
The engine just has the basics and custom code may need to be needed for common game features (such as moving platforms, etc.).
Even more of the hardware configuration and processing is taken care of behind the scenes than with GBDK, so less experienced users may have trouble when problems arise.
GB Studio is a drag-and-drop game creator for the Game Boy that does not require knowledge of programming languages. Games are built using a graphical interface to script graphics, sound and actions. It is available for Linux, Windows and MacOS.
Strengths:
Very easy for beginners to start building games right away. Everything is built-in and requires minimal knowledge and understanding of the Game Boy hardware.
Has been used to create large and extensive projects.
Very active community for help and support.
Weaknesses:
It’s games will tend to be slower than both ASM and C.
There is a limited set of commands to script with and some artificially smaller restrictions on palettes, sprites, background tiles and other hardware features (due to how GB Studio manages them).
Games may be more constrained or require workarounds to do things if they don’t easily fit within the available scripting, graphics and sound tools. (Though it is possible for advanced users to do a “engine eject” and add more functionality using C and ASM.)
Accurate emulators and debugging tools are tremendously helpful for testing and tracking down problems. The following Game Boy emulators provide excellent accuracy and include a variety of different features.
BGB has a convenient (ASM) debugger, though its minimal interface can be confusing at first. It is available for Windows only, but runs almost flawlessly with Wine.
Emulicious includes powerful tools such as a profiler and source-level debugging for ASM and C via a VS Code debug adapter. It runs on Linux, Windows, MacOS and any other operating systems that supports Java SE.
Same Boy is user friendly and has a wide range of powerful (ASM) debugging features. It runs on Windows and MacOS.
Gambatte lacks a debugger and must be compiled from source, but is packaged both in RetroArch (Linux, Windows and Mac) and BizHawk (Windows-only).
Purists prefer to also run their games on hardware, which is possible thanks to flashcarts. My personal recommendation is krikzz's carts, particularly the Everdrive GB X5.
Side note : if you are using VBA or VBA-rr, stop using them right now. These emulators are extremely inaccurate, and also contain severe security flaws. I strongly urge you to ditch these emulators and spread the word.
If your question is "What should I use for my game project ?", then you're in the right section. The first question you should ask yourself is what languages you know.
Consider starting with C and GBDK. This will introduce you to working with the hardware and is an easier starting place.
Once you've grasped C's concepts (most importantly pointers), give ASM a go. The language is simpler than it looks. Even if you don't manage to get working ASM code, it actually helps a lot (especially on such a constrained system) to know what's "under the hood". There is even an online IDE to experiment with.
For C / GBDK users, knowing ASM will help you understand what its API (which is mostly written in ASM) is doing behind the scenes and will make using emulator debuggers easier to understand.
If you don't wan't to learn a language at all, GB Studio is an alternative to C and ASM.
Consider the goals, scope and time frame of your project. If you'd like to start building right away then C and GBDK will make that easy. You'll also have growing exposure to ASM as time goes on due to working with the hardware and tracking down problems in the debugger.
On the other hand, if you'd like to expand your programming skill set and have additional time, learning to use ASM and RGBDS will provide you with a lot of knowledge about the Game Boy hardware. Once you know ASM in addition to C, you'll have a lot of flexibility in what tools you use for projects.
RGBDS with ASM is a solid option. You'll be able to get the best performance out of the hardware, and there is an experienced community available to help.
Another option is to reach out to us, and discuss the matter.
The very first thing to do in all cases is to read the docs, to grasp how the Game Boy works. In ASM, this is essential; in C, this will let you understand what a given library function does. It will also let you understand what is possible on the Game Boy, and what isn't. (You can always ask, if you have doubts.)
I also recommend looking up awesome-gbdev for resources and tutorials. There are a lot of helpful articles there, as well as helper tools.
Modules Separate your game into several "entities" that interact together. Camera, Player, NPCs, Loading zones, etc. This simplifies coding, by allowing you to reason independently on smaller units. This facilitates development and reduces the amount of bugs.
Document your functions For each function, write a comment saying what it does, what memory it touches, and what registers it affects. This will avoid conflicts, and let you optimize your code by minimizing the amount of registers you save when calling a function.
Plan before writing You should plan what register is going to be used for what within your functions before starting to write them. Your goal is to minimize the amount of register swapping. There's no general rule, so feel free to drop by and ask us, if you're in doubt.
RGBASM -E and RGBLINK -n <symfile> When you load ROM.gb or ROM.gbc in BGB, it automatically loads (if it exists) the file ROM.sym in the same folder as the ROM. This adds symbols to the debugger, which - believe me - helps a ton.
Global variables Use as many global variables as you can; the Game Boy has a lot of RAM compared to other platforms such as the NES, but is slow at using the stack. Thus, minimizing the number of local variables, especially in heavily-called functions, will reduce the time spent manipulating the stack.
Optimized code Write code as efficient as possible. Sometimes there is a readability tradeoff, so I recommend you get the comment machine gun out and put some everywhere.
By default GBDK-2020 (after v4.0.1) will use the SDCC flag --max-allocs-per-node 50000 for an increased optimization pass. You may also choose to use --opt-code-speed (optimize code generation towards fast code, possibly at the expense of codesize) or --opt-code-size (optimize code generation towards compact code, possibly at the expense of codespeed).
Inlining When performance is important avoid using functions if you can inline them, which skips passing all arguments to the stack, mostly. Macros will be your friends there. If needed you can also use inline ASM.
NEVER use recursive functions
AVOID printf printf clobbers a sizeable chunk of VRAM with unnecessary text tiles. Instead, you should sprintf to a buffer in WRAM, then put that on the screen using a custom font.
Geometry funcs Avoid the functions that draw geometry on-screen (lines, rectangles, etc.). The Game Boy isn't designed for this kind of drawing method, and you will have a hard time mixing this with, say, background art. Plus, the functions are super slow.
const (very important!) Declaring a variable that doesn't change as constgreatly reduces the amount of ROM, RAM, and CPU used. The technical reason behind that is that non-const values, especially arrays, are loaded to RAM from ROM in an extremely inefficient way. This takes up a LOT more ROM, and copies the value(s) to RAM when it's unneeded. (And the GB does not have enough RAM for that to be viable.)
Don't use MBC1 MBC1 is often assumed to be the simplest of all MBCs... but it has a quirk that adds some overhead every time ROM or SRAM bank switches are performed. MBC3 and MBC5 don't have this quirk, and don't add any complexity. Using MBC1 has no real use. (Let's not talk about MBC2, either.)
+
+
+
diff --git a/website/.vuepress/public/images/1ST_PRESS_V3.png b/images/1ST_PRESS_V3.png
similarity index 100%
rename from website/.vuepress/public/images/1ST_PRESS_V3.png
rename to images/1ST_PRESS_V3.png
diff --git a/website/.vuepress/public/images/1px_transp.png b/images/1px_transp.png
similarity index 100%
rename from website/.vuepress/public/images/1px_transp.png
rename to images/1px_transp.png
diff --git a/website/.vuepress/public/images/1stpress_2.png b/images/1stpress_2.png
similarity index 100%
rename from website/.vuepress/public/images/1stpress_2.png
rename to images/1stpress_2.png
diff --git a/website/.vuepress/public/images/DO_Powered_by_Badge_blue.svg b/images/DO_Powered_by_Badge_blue.svg
similarity index 100%
rename from website/.vuepress/public/images/DO_Powered_by_Badge_blue.svg
rename to images/DO_Powered_by_Badge_blue.svg
diff --git a/images/Incube8-Games-Logo.png b/images/Incube8-Games-Logo.png
new file mode 100644
index 00000000..3bc5a6ca
Binary files /dev/null and b/images/Incube8-Games-Logo.png differ
diff --git a/website/.vuepress/public/images/RBIconSmallSquare.jpg b/images/RBIconSmallSquare.jpg
similarity index 100%
rename from website/.vuepress/public/images/RBIconSmallSquare.jpg
rename to images/RBIconSmallSquare.jpg
diff --git a/website/.vuepress/public/images/banner.png b/images/banner.png
similarity index 100%
rename from website/.vuepress/public/images/banner.png
rename to images/banner.png
diff --git a/website/.vuepress/public/images/bitmap_soft.png b/images/bitmap_soft.png
similarity index 100%
rename from website/.vuepress/public/images/bitmap_soft.png
rename to images/bitmap_soft.png
diff --git a/website/.vuepress/public/images/discord.svg b/images/discord.svg
similarity index 100%
rename from website/.vuepress/public/images/discord.svg
rename to images/discord.svg
diff --git a/website/.vuepress/public/images/gbcompo25-logo.gif b/images/gbcompo25-logo.gif
similarity index 100%
rename from website/.vuepress/public/images/gbcompo25-logo.gif
rename to images/gbcompo25-logo.gif
diff --git a/website/.vuepress/public/images/gbinternals.png b/images/gbinternals.png
similarity index 100%
rename from website/.vuepress/public/images/gbinternals.png
rename to images/gbinternals.png
diff --git a/website/.vuepress/public/images/gbstudio_logo.png b/images/gbstudio_logo.png
similarity index 100%
rename from website/.vuepress/public/images/gbstudio_logo.png
rename to images/gbstudio_logo.png
diff --git a/website/.vuepress/public/images/github.svg b/images/github.svg
similarity index 100%
rename from website/.vuepress/public/images/github.svg
rename to images/github.svg
diff --git a/website/.vuepress/public/images/incube8_logo_white_bg.png b/images/incube8_logo_white_bg.png
similarity index 100%
rename from website/.vuepress/public/images/incube8_logo_white_bg.png
rename to images/incube8_logo_white_bg.png
diff --git a/website/.vuepress/public/images/instagram.svg b/images/instagram.svg
similarity index 100%
rename from website/.vuepress/public/images/instagram.svg
rename to images/instagram.svg
diff --git a/website/.vuepress/public/images/kSruwo.png b/images/kSruwo.png
similarity index 100%
rename from website/.vuepress/public/images/kSruwo.png
rename to images/kSruwo.png
diff --git a/website/.vuepress/public/images/kSruwo_alt.png b/images/kSruwo_alt.png
similarity index 100%
rename from website/.vuepress/public/images/kSruwo_alt.png
rename to images/kSruwo_alt.png
diff --git a/website/.vuepress/public/images/logo_334x.png b/images/logo_334x.png
similarity index 100%
rename from website/.vuepress/public/images/logo_334x.png
rename to images/logo_334x.png
diff --git a/website/.vuepress/public/images/mega_cat_studios_hq.png b/images/mega_cat_studios_hq.png
similarity index 100%
rename from website/.vuepress/public/images/mega_cat_studios_hq.png
rename to images/mega_cat_studios_hq.png
diff --git a/website/.vuepress/public/images/pandocs_timing.png b/images/pandocs_timing.png
similarity index 100%
rename from website/.vuepress/public/images/pandocs_timing.png
rename to images/pandocs_timing.png
diff --git a/website/.vuepress/public/images/ppu_modes_timing.svg b/images/ppu_modes_timing.svg
similarity index 100%
rename from website/.vuepress/public/images/ppu_modes_timing.svg
rename to images/ppu_modes_timing.svg
diff --git a/website/.vuepress/public/images/sponsor_catskull_50h.png b/images/sponsor_catskull_50h.png
similarity index 100%
rename from website/.vuepress/public/images/sponsor_catskull_50h.png
rename to images/sponsor_catskull_50h.png
diff --git a/website/.vuepress/public/images/sponsor_greenboygames_160x144_original.png b/images/sponsor_greenboygames_160x144_original.png
similarity index 100%
rename from website/.vuepress/public/images/sponsor_greenboygames_160x144_original.png
rename to images/sponsor_greenboygames_160x144_original.png
diff --git a/website/.vuepress/public/images/sponsor_greenboygames_160x45_wide_original.png b/images/sponsor_greenboygames_160x45_wide_original.png
similarity index 100%
rename from website/.vuepress/public/images/sponsor_greenboygames_160x45_wide_original.png
rename to images/sponsor_greenboygames_160x45_wide_original.png
diff --git a/website/.vuepress/public/images/sponsor_greenboygames_50h.png b/images/sponsor_greenboygames_50h.png
similarity index 100%
rename from website/.vuepress/public/images/sponsor_greenboygames_50h.png
rename to images/sponsor_greenboygames_50h.png
diff --git a/website/.vuepress/public/images/sponsor_greenboygames_crop_52h.png b/images/sponsor_greenboygames_crop_52h.png
similarity index 100%
rename from website/.vuepress/public/images/sponsor_greenboygames_crop_52h.png
rename to images/sponsor_greenboygames_crop_52h.png
diff --git a/website/.vuepress/public/images/sponsor_greenboygames_crop_60h.png b/images/sponsor_greenboygames_crop_60h.png
similarity index 100%
rename from website/.vuepress/public/images/sponsor_greenboygames_crop_60h.png
rename to images/sponsor_greenboygames_crop_60h.png
diff --git a/website/.vuepress/public/images/sponsor_megacat_50h.png b/images/sponsor_megacat_50h.png
similarity index 100%
rename from website/.vuepress/public/images/sponsor_megacat_50h.png
rename to images/sponsor_megacat_50h.png
diff --git a/website/.vuepress/public/images/sponsor_megacat_MCS_NoBG_80h.png b/images/sponsor_megacat_MCS_NoBG_80h.png
similarity index 100%
rename from website/.vuepress/public/images/sponsor_megacat_MCS_NoBG_80h.png
rename to images/sponsor_megacat_MCS_NoBG_80h.png
diff --git a/website/.vuepress/public/images/square.png b/images/square.png
similarity index 100%
rename from website/.vuepress/public/images/square.png
rename to images/square.png
diff --git a/website/.vuepress/public/images/twitter.svg b/images/twitter.svg
similarity index 100%
rename from website/.vuepress/public/images/twitter.svg
rename to images/twitter.svg
diff --git a/index.html b/index.html
new file mode 100644
index 00000000..78c241a5
--- /dev/null
+++ b/index.html
@@ -0,0 +1,56 @@
+
+
+
+
+
+
+
+
+ Projects | gbdev.io
+
+
+
+
+
+
We are a non for profit collective of passionate developers and hackers working on development tools, homebrew games, emulators, preservation and documentation for the Nintendo Game Boy handheld console, the original gray brick from 1989.
Curated list of Game Boy development resources such as tools, guides, technical documentation, tutorials, emulators, related projects and open-source ROMs. Everything you'll ever need to know and see about this console is here. If you want to code an emulator, create your own game or simply dive into the software and hardware architecture of the Game Boy, this is the place!
An automated test comparison project for Game Boy emulators. It runs a suite of accuracy test ROMs across a set of Game Boy emulators and publishes the pass/fail results as a comprehensive table, with screenshots for each test case.
A (work in progress) tutorial on how to program for the Game Boy in assembly, touching on every aspect required to make Game Boy games, via a Hello World, constructing an Arkanoid clone, and capping off by making a playable Shoot-'Em-Up.
Maintained and modernized GBDK, the Game Boy Development Kit. Now powered by an updated version of the SDCC toolchain, provides a C compiler, assembler, linker and a set of libraries.
The places where our community thrives. Here we chat, discuss, help each other and show what we are working on. We have a Discord server, an IRC channel, and more.
Play Game Boy games online from an archive of hundreds of entries! A community-led attempt to collect, archive and save every unofficial game, homebrew, demo, patch, hackrom for Game Boy produced by the community through the last 3 decades of passionate work.
Digital library of Game Boy related software, hardware and literature. Aimed to mirror and preserve old and fragmented contributions in the scene from the last three decades.
Released in 1998 as a Nintendo accessory for the original handheld console, the Game Boy Camera became an instant hit with kids. Despite technological advances, its capturing sub-megapixel charm endures, capturing the imagination of photographers who embrace its unique, low-fi aesthetic.
Events
We host coding competitions in which anyone can partecipate by creating original games, demos, homebrews tools and music for the Game Boy and compete for glory and prizes.
Participants were asked to fill in discussion points beforehand. The results of these discussions (decisions, suggestions, comments, etc.) are reported as subitems.
Discussion points marked * were not brought up due to lack of time.
Anyone can reach out in #meta on the Discord server to comment/discuss on any of the points reported here.
Scope: Full port from the old codebase, can’t be 1-to-1
Architecture design in progress
Motivation: C not attractive anymore for potential contributors. Showing its age. Technicalities (std::vector). Rust is fancier, more active community.
(Personal) De-risking/Viability study
Question from Pino: will this affect availability of binaries?
Homebrew Hub: developments and expanding to NES *
Gbcompo23: [...] *
Funding: how to? *
Creating the steering committee: Roles? *
Q to Sanqui: how to run these meetings? Minutes *
ISSOtm:
Difficulties finding contributors
Pan Docs, RGBDS, ASM Tutorial are lacking
AV: Homebrew Hub is OK
Quality barrier too high? Is our PR review process too much?
Possible (partial) solution = avoid nitpicking
Interesting actionable by DUO: give reviews in “steps”, avoid attacking a PR with everything all at once.
If making follow-up changes separately, be upfront about that in the initial PR to avoid the contributor feeling “the rug pulled under their feet”
Put a hard cap on the PR’s “TTL” before being merged (e.g. 3 weeks), and avoid minor changes as the deadline looms closer
How to handle contributors going silent?
“Better merge something incomplete to beg correction work, rather than drop everything done thus far”
Promoting a “Bug Day”?
Moving projects to SourceHut? *
Sanqui:
Overview of Wikidata and the possibility of contributing Homebrew Hub information to Wikidata
Add Ext ID in the game metadata schema and point it to Wikidata entry. Possibly populate others.
HHub - cartridges/published games/publishers
DUO:
Remarks and compliments on the great state of gbdev.
Promote PRs that need to be worked on, but also promote work that has been done “for cred” - Start using #feedback again.
nitro2k01:
Potentially add a long-term solution for the IRC bridge as a point/responsibility.
Someone other than @avivace needs to volunteer (=> @nitro2k01) and get SSH access and/or a webhook token to delegate the responsibility of handling the bridge
Future of the forums *
Kva64:
Social media handling — general outline and feedback request
Not only game releases/showcase content can be shared, but projects themselves may have cool milestones to share
Allow developers, maintainers, etc. to raise up things that can be promoted, so people helping with social media don’t have to “chase” those. Should those go in #showcase? Should #showcase be split?
Encourage (PSA?) developers to post in #showcase Move all showcase channels to a separate category, and rename “showcase” to “wip-showcase”? Split #releases into e.g. #releases-games and #releases-tooling
To discuss in #meta: opt-in/opt-out status of showcase channels. A suggestion: make sharing opt-out in #releases-*, and opt-in in #wip-showcase
Policy on sharing and promoting commercial projects?
DUO: do not shy away from promoting them because it’s a motivation to some; however, restrictions should be put to avoid being taken advantage of
Should the cooldown (if any) apply per project, per user...?
Need for using any newly-emerging social media platforms (e.g. Threads and Bluesky)?
Threads and Bluesky currently have very little reach, so maybe not
Other platforms (e.g. Instagram): maybe, but this may require more people to handle them
Creating a channel to coordinate them
Sylvie:
Q to ISSO: Continue improving existing C++ while Rust progresses?
C to C++ in parallel
Talk separately (and later) about whether we want to upstream the port, and if yes, the roadmap (responsibility & task distribution, etc.)
Written by avivace, on behalf of the gbdev org. Originally published on "getrevue" on the 21st of February, 2022.
Hey all and welcome to the first Issue of the new gbdev curated digest! Our idea is to prepare a collection of curated news about what is happening in our community, with a particular focus on our Open Source projects. Whatever you are interested in what’s happening the gbdev scene, new homebrew, tools, and documentation releases, find stuff where you can contribute with code or provide feedback, this newsletter is for you!
The following is a quick clarification since this is the first issue of the Gbdev Digest. Feel free to shamelessly skip it.
This newsletter is curated by the team behind the gbdev.io community, with a lot of help from members of other smaller communities and groups. We are directly involved with the development and maintaining of a lot of projects (e.g. Pan Docs, RGBDS, and Homebrew Hub), but of course not with everything that will be mentioned in this newsletter.
The gbdev initiative promotes the development of tools, documentation and homebrew games. We also push research and emulation efforts while trying to keep everything as accessible as possible to everyone, no matter the technical background. Another big commitment is the archiving one: we are working on salvaging old and unavailable content, creating a digital memory of a scene that is now three decades old.
Since our inception in 2015, open source and free software values are at the core of our commitment. We release everything we work on under OSS licenses (or in the Public Domain), maintaining a strong non-profit approach. Even if we reshare and give exposure to commercial products related to gbdev, we don’t get any cash from any of the involved companies and we don’t sponsor or endorse any particular release or publisher. No referral links are ever shared through our channels.
Our expenses are entirely sustained by voluntary contributions (for which donators don’t get any benefit) and sponsorships (such as the DigitalOcean one). Everything is tracked trasparently on our OpenCollective.
While a core team of maintainers is behind every project, we welcome any type of contribution, from feedback to participation in Request For Comments threads. Check the bottom of this page to learn how to join our community.
2021 was a huge success for gbdev. The Game Boy scene was never this alive and flourishing, with an unprecedented number of releases. From the incredible gbcompo21 (for which we are preparing a dedicated issue of this newsletter) to the number of new releases of our projects, we enjoyed the most traffic we ever saw, for the fourth year in a row.
We finished revamping a quite comprehensive guide on how to choose tools for Game Boy, giving an overview on the different available approaches to developing homebrews for the grey brick.
Homebrew Hub is a community-led attempt to collect, archive and preserve every unofficial game and homebrew released for Game Boy produced through decades of passionate work. Every entry , with its metadata and related assets is exposed through an API available to everyone.
We started a complete rewrite of the backend and the frontend software behind Homebrew Hub. Originally built with Express in 2016 the codebase started to show its age… The new backend, powered by Django, will offer a full Restful API to access all the games, assets, metadata and ROMs we have in the JSON database. The new frontend is powered by the binjgb emulator, running in the browser via WebAssembly. This will bring better emulation accuracy and more features!
The Homebrew Hub database is community maintained. Everyone can contribute adding a game or writing a scraper to mass-import games on the website.
In parallel to the rewrite, a lot of stuff is happening on the database, too:
New CI scripts are now in place to detect potentially duplicated entries, calculating checksums of assets. The pipeline will now run on Pull Requests and also validate the submitted metadata and the referenced files, to aid contributors.
The following new entries have been added: Crystal Lake, Grub Glide, Labirinth, Bannerprint and Europa Rescue (thanks to N•I•L, v666, reini1305, Exetric and godai78).
All the 20 shortlisted entries from the GB Competition 2021 were also added and are ready to play.
dag7dev worked on new scrapers, cleaned up a bunch of entries metadata and merged some duplicates. Hundreds of new homebrews are now available thanks to his work!
A new game metadata schema is in preparation, enabling game translations and specific tags for hardware support (e.g. gb-printer, gb-camera, ..).
The total number of games is now 748! Go play with them!
RGBDS is the standard toolchain for developing homebrew Game Boy programs in Assembly. First released in 1997, it’s now enjoying renewed community attention.
A new version of the RGBDS website, providing downloads, build instructions and full documentation is now work in progress.
From a statically built HTML version we are migrating to a solution based on Docusaurus, a documentation framework powered by React.
The rgbgfx program, part of the RGBDS suite, converts PNG images into the Nintendo Game Boy’s planar tile format.
A new version is being rewritten in modern C++17, enabling a series of improvements such as better error messages (more explicit), proper transparency support, explicit palette specification and proper 8×16 support.
GBDK is a C compiler, assembler, linker and set of libraries for the Z80 like Nintendo Gameboy.
Our friends over at the GBDK team worked on a big release, the first adding more targets to the toolchain: GBDK can now compile to Sega Master System/Sega Game Gear and the Analogue Pocket.
4.0.6 shipped support for the Mega Duck, too!
Other notable features added in 2021 were:
Metasprite API and conversion tool
Auto ROM bank assignment and packing
Compression API and console tool
More information can be found in the release notes.
For a long time now the main GBDK tutorials online have been mostly outdated and based on the 20 year old version GBDK but this is now changing: Larold is working on a series of tutorials where they break down the Game Boy game creation process into multiple high-level steps.
GB Studio is a quick and easy to use drag and drop retro game creator for the Game Boy. No programming required. Here are some of the highlights from the new release:
Thanks for reading! That’s all for this issue. We hope you’re having fun developing or playing games for Game Boy. As always, your contributions to our initiatives are appreciated. You’re welcome to browse around our GitHub organisation or join our Discord server to see what everyone is working on.
If you still want more gbdev, our Twitter feed provides a curated (and frequent) collection of new releases, WIP content and news about our projects.
If you have any feedback, want to send us some gbdev links or comment on any of the topics brought up in this issue (or on the newsletter itself) feel free to reach us.
This digest won’t try to respect an actual schedule but will rather go online when enough quality content is ready. On the next issue, we will continue to shed some light on the incredible accomplishments we made in 2021, such as the GB Competition 2021, showing you how some games enjoyed continued support and physical releases. We will also tell you how we managed to make Pan Docs this good and what’s new in the latest RGBDS releases.
If you’d like to send us some gbdev content you found interesting and you think would fit this newsletter, feel free to reach out.
Special thanks to bbbbbr, ISSOtm, toxa and Emi Paternostro.
We use a self hosted instance of Matomo, a free and open source web analytics application to track online visits to our websites (*.gbdev.io, *.gbadev.net), in order to better understand which audience we serve and what content is consulted.
Game Boy Advance development is covered by another project, the awesome-GBAdev list. GBA, however, can run GB/GBC games. It does so in a slightly different way compared to native hardware. This is covered in the Emulator Development section of this list.
This list is focused on the original (1989) Game Boy (DMG). Game Boy Color (GBC) and Super Game Boy (SGB) are very similar systems, with a few important distinctions, such as:
Different hardware specifications
Specific hardware and software features
Specific registers
Specific bugs, quirks and exploitable behaviours
If you aim to develop your software for SGB or GBC, or you want to know how it runs on the other systems, you may want to take advantage and adapt to these differences, check the Game Boy Color category and look for specific references to GBC/CGB and SGB.
Resources marked with † are developed with AI/LLM usage, resources marked with ‡ were mostly generated by AI. See AI disclosure.
Game Boy Project Report - Report of an hardware emulator (on a Terasic DE1-SoC Board) developed as final project for the CSEE4840 Embedded Systems Design course at Columbia University.
RGBDS opcodes reference - A reference of all instructions, including short descriptions, cycle and byte counts, and explanations of flag modifications.
The Game Boy Project - Provides a study on the hardware and detailed constructional information for the implementation of three 8-bit bidirectional parallel ports.
dmg-schematics - Schematics and annotated overlay for the DMG-CPU B chip, extracted from die photos, made with KiCad. Also contains Electric VLSI library with layouts for some of the cells and memories.
Dan Docs - Obscure Game Boy hardware documentation.
Edge of Emulation, a series of articles about emulating and investigating Game Boy accessories. Also available as technical documents in the GBE- emulator documentation.
Mobile Adapter GB - Internet connectivity and DLC on the Game Boy Color.
About swotGB - Notes about the development of a Game Boy emulator in JavaScript.
Game Boy Doctor - A command line tool for comparing logs from your emulator to those from a known-correct one. Useful for line-by-line debugging of Blargg's test ROMs.
DraconicGB: Creating a Game Boy Emulator - Bachelor's thesis (UOC, 2020) documenting the research, architecture and C++ implementation of a Game Boy emulator, with an overview of the hardware and of existing emulators.
GBDK - Maintained and modernized GBDK (Game Boy Development Kit) powered by an updated version of the SDCC toolchain. Provides a C compiler, assembler, linker and a set of libraries.
Turbo Rascal Syntax Error - Complete suite (IDE, compiler, programming language, resource editor) intended for developing games/demos for 8 / 16-bit line of computers, including the Game Boy and Game Boy Color.
LLVM-Z80 † - A fork of LLVM with a backend for the Game Boy CPU (SM83), supporting the latest C standards and LLVM’s excellent optimizations.
MetroBoy - A playable, circuit-level simulation of an entire Game Boy.
gbe-plus - A recently rewritten emulator that has a large effort in preserving the functions of obscure accessories (such as IR link, Mobile Network GB, Barcode Boy, GB Printer, local and online GB Serial Link Cable, ... )
Emulicious - Provides accurate emulation and includes powerful tools such as a profiler and source-level debugging for ASM and C via a VS Code debug adapter.
Game Boy Tile Data Generator - HTML5 / JS web application that will convert bitmap images to hexadecimal data appropriate for use in tile based graphical applications, specifically GB.
Harry Mulder's GB Development - Some sources and home of Game Boy Tile Designer (GBTD) and Game Boy Map Builder (GBMB) tools.
GBTiles - Converts .GBR files created with Harry Mulder's Tile Designer (GBTD) and .GBM files created with Harry Mulder's Map Builder (GBMB) to different formats for use with the Game Boy and GBDK.
bmp2cgb - Graphics converter for Game Boy Color development providing real time palette adjustments.
png2gb - CLI tool to convert image file to game boy .c array.
GB-convert - Game Boy tile conversion and map editor tool (converts to assembly).
brewtool - A collection of primitive editor/converter tools for making assets used with homebrew ROM development.
tpp1 - Definition and specification of a custom GB/GBC memory/hardware mapper, as a functional superset of MBC.
libstdgb - A C library of useful Game Boy operations (SDCC).
Tilemap GB - GIMP image editor plug-in for importing & exporting GBMB and GBTD tilemaps and tilesets (as bitmap images or .GBM/.GBR files).
Tilemap Helper - GIMP image editor plug-in for optimizing tile maps and tile sets.
Tilemap Studio - A tilemap editor for Game Boy, Color, Advance, and SNES projects. Written in C++ with FLTK.
Superfamiconv - Flexible and composable tile graphics converter supporting Super Nintendo, Game Boy, Game Boy Color, Game Boy Advance, Mega Drive and PC Engine formats.
Tessarium ‡ - Tile-first pixel art level editor with linked tile editing, shared tilesets, and live unique tile and colours-per-tile budget checks for Game Boy and Game Boy Color backgrounds.
gb asm tutorial - Step by step tutorial, building several ROMs to accompany its explanations.
hardware.inc - Standard include file containing Game Boy hardware definitions for use in RGBDS projects.
Assembly tutorial by David Pello - Good document to learn to produce working asm code for gb. Brief explanations of many important topics. Many examples with commented source code.
assemblydigest - Exploring Game Boy programming techniques:
EmmaEwert's experiments - A collection of prototype programs, mostly just toying around. Among others, a daylight effect, transparency and a weather effect.
DeadCScroll - A detailed tutorial on how to make the screen wobble, among other "raster effects"
DOTs for GBC - Making of a Game Boy Color port of the Dots game in C with GBDK, from code to a physical cartridge with box art. Includes the source code and a one-hour development commentary video.
Homebrew Hub - A community-led attempt to collect, archive and preserve every unlicensed and homebrew game released for Game Boy. Entries are playable online.
waveform-gb - Program visualizing the wave form used by the wave channel. The wave form can be edited freely and playback of the wave is updated immediately.
Evoland.gb - A port of the first level of Evoland.
Petris - A puzzle game of shapely pets for the Game Boy Color (itch.io).
Infinity - RPG developed by Affinix Software primarily between the years 1999 and 2001. The game never found a publisher and was eventually canceled. Got recently released with the full source, development tools and workflows.
matrix-rain-gb - A Matrix digital rain effect in assembler.
GBVideoPlayer - A technical demo demonstrating how the Game Boy LCD controller can be hacked to make a Game Boy Color play a full motion video in color, together with music.
GBVideoPlayer2 - The second iteration of the above demo, which increases the resolution, adds stereo PCM audio, and introduces video compression.
DMA hijacking - A simple technique that allows you to run custom code in most GB/SGB/CGB games, provided you have an ACE exploit.
Super Mario Land 2 - Memory Exploration - Video by Retro Game Mechanics Explained on how the game stores levels in memory, and what happens when Mario goes out of bounds.
Lets Build a Platformer Game! - A course designed to teach anyone how to create a platformer game using GB Studio 4+.
ArduinoBoy - Serial communication (MIDI) from an Arduino to the Game Boy for music applications such as LittleSoundDJ, Nanoloop, and mGB.
papiGB - Game Boy Classic fully functional FPGA implementation from scratch.
fpgaboy - Implementation Nintendo's Game Boy console on an FPGA.
Piglet - A LUA-driven AI that plays classic Game Boy color games using experimentation. In active development.
Ostrich - A Game Boy Sound System player written in Swift.
mGB - A Game Boy cartridge program that enables the Game Boy to act as a full MIDI supported sound module.
GBVisualizer - Demonstrating the use of two undocumented Game Boy Color registers, nicknamed PCM12 (FF76) and PCM34 (FF77), which can be used to read the current PCM amplitude of the 4 APU channels.
ArduinoGameBoy - Arduino based Game Boy cartridge reader and writer.
Nintendo's fake logos - Every cartridge has to show the authentic logo to be considered valid and be run, but obviously some companies managed to exploit the check system.
liblsdj - Utility library for interacting with the LSDj save format (.sav), song files (.lsdsng) and more.
lsdpatch - Tool for modifying samples, fonts and palettes on LSDj ROM images.
Game Boy video effects - Some little experiments using the STAT interrupt to do funny video manipulations.
The Game Boy Archive - A library of Game Boy related software, hardware and literature. Aimed to mirror and preserve old and fragmented contributions from the last three decades.
Handheld Underground - Unlicensed games, blog posts about Game Boy, home of the hhugboy emulator.
+
+
+
diff --git a/scripts/build-resources.mjs b/scripts/build-resources.mjs
deleted file mode 100644
index 540e4e4d..00000000
--- a/scripts/build-resources.mjs
+++ /dev/null
@@ -1,70 +0,0 @@
-// Compile website/resources.md from the README of awesome-gbdev.
-//
-// The README delimits the part of the list that goes on the website with two
-// HTML comments (see https://github.com/gbdev/awesome-gbdev/issues/203):
-//
-//
-//
-//
-// The page is scripts/resources/header.md (front matter, videos, notes and
-// anything else that's website-only) followed by that part of the README.
-// website/resources.md is not committed: it's generated before every
-// `yarn build` (run this script by hand before `yarn dev`). Edit the header
-// or the README instead.
-//
-// Usage:
-// node scripts/build-resources.mjs # fetch README.md from awesome-gbdev master
-// node scripts/build-resources.mjs # fetch it from another branch, tag or commit
-// node scripts/build-resources.mjs ./README.md # use a local checkout
-//
-// The source can also be given in the RESOURCES_MD_SRC environment variable,
-// which is how it reaches the script when it runs through `yarn build`.
-// With neither an argument nor RESOURCES_MD_SRC, README.md is fetched from
-// the master branch of awesome-gbdev on GitHub.
-
-import { readFile, writeFile } from "node:fs/promises";
-import { existsSync } from "node:fs";
-
-const REPO = "gbdev/awesome-gbdev";
-const HEADER = new URL("./resources/header.md", import.meta.url);
-const TARGET = new URL("../website/resources.md", import.meta.url);
-const BEGIN = "";
-const END = "";
-
-const source = process.argv[2] ?? process.env.RESOURCES_MD_SRC ?? "master";
-const isLocal = existsSync(source);
-
-async function loadReadme() {
- if (isLocal) return readFile(source, "utf8");
- const url = `https://raw.githubusercontent.com/${REPO}/${source}/README.md`;
- const res = await fetch(url);
- if (!res.ok) throw new Error(`GET ${url}: ${res.status} ${res.statusText}`);
- return res.text();
-}
-
-// Returns what lies strictly between the markers
-function extractBody(text) {
- const begin = text.indexOf(BEGIN);
- const end = text.indexOf(END);
- if (begin === -1 || end === -1 || end < begin) {
- throw new Error(`README.md: missing or misplaced "${BEGIN}" / "${END}" markers`);
- }
- if (text.indexOf(BEGIN, begin + 1) !== -1 || text.indexOf(END, end + 1) !== -1) {
- throw new Error("README.md: markers must appear exactly once");
- }
- return text.slice(begin + BEGIN.length, end);
-}
-
-// Links relative to the awesome-gbdev repository (e.g. EMULATORS.md) don't
-// exist on the website: point them to GitHub instead.
-function absolutizeLinks(md) {
- return md.replace(/\]\((?!https?:|mailto:|#|\/)([^)\s]+)\)/g,
- (_, path) => `](https://github.com/${REPO}/blob/master/${path})`);
-}
-
-const readme = (await loadReadme()).replace(/\r\n/g, "\n");
-const body = absolutizeLinks(extractBody(readme)).trim();
-const header = (await readFile(HEADER, "utf8")).trimEnd();
-
-await writeFile(TARGET, `${header}\n\n${body}\n`);
-console.log(`resources.md built from ${isLocal ? source : `${REPO}@${source}`}.`);
diff --git a/scripts/resources/header.md b/scripts/resources/header.md
deleted file mode 100644
index dec67db8..00000000
--- a/scripts/resources/header.md
+++ /dev/null
@@ -1,74 +0,0 @@
----
-title: Resources
-lang: en-US
-head:
- - - meta
- - name: og:title
- content: Game Boy dev resources
- - - meta
- - name: og:description
- content: A curated list of awesome Development resources, tools, docs, related projects and homebrews.
----
-
-
-
Awesome
- Game Boy Development
-
-
-
-A curated list of awesome Game Boy (Color) Development resources, tools, docs, related projects and homebrews. Inspired by the awesome list thing.
-
-
-
-This project is open source and community-lead. Come contribute!
-
-
-
-## Introduction
-
-### [The Game Boy, a hardware autopsy](https://www.youtube.com/playlist?list=PLu3xpmdUP-GRDp8tknpXC_Y4RUQtMMqEu)
-
-
-
-
-
-
-
-### [The Ultimate Game Boy Talk](https://media.ccc.de/v/33c3-8029-the_ultimate_game_boy_talk)
-
-
-
-
-
-
-
-::: note Disambiguation: GBA vs GB/GBC
-
-###### Game Boy Advance
-
-Game Boy Advance development is covered by another project, the [awesome-GBAdev](https://github.com/gbdev/awesome-gbadev) list.
-GBA, however, can run GB/GBC games. It does so in a slightly different way compared to native hardware. This is covered in the Emulator Development section of this list.
-
-###### Game Boy Color and Super Game Boy
-
-This list is focused on the original (1989) Game Boy (DMG). _Game Boy Color_ (GBC) and _Super Game Boy_ (SGB) are very similar systems, with a few important distinctions, such as:
-
-- Different hardware specifications
-- Specific hardware and software features
-- Specific registers
-- Specific bugs, quirks and exploitable behaviours
-
-If you aim to develop your software for SGB or GBC, or you want to know how it runs on the other systems, you may want to take advantage and adapt to these differences, check the [Game Boy Color](#game-boy-color) category and look for specific references to GBC/CGB and SGB.
-
-:::
-
diff --git a/to_c_or_not_to_c.html b/to_c_or_not_to_c.html
new file mode 100644
index 00000000..ce13d69a
--- /dev/null
+++ b/to_c_or_not_to_c.html
@@ -0,0 +1,55 @@
+
+
+
+
+
+
+
+
+ To C or not to C | gbdev.io
+
+
+
+
+
+
-
-We are a non for profit collective of passionate developers and hackers working on development tools, homebrew games, emulators, preservation and documentation for the Nintendo Game Boy handheld console, the original gray brick from 1989.
-The single, most comprehensive technical reference to Game Boy available to the public.
-
-
-#### [Game Boy CPU Opcode tables](https://gbdev.io/gb-opcodes/optables)
-
-
-
-Curated list of Game Boy development resources such as tools, guides, technical documentation, tutorials, emulators, related projects and open-source ROMs. Everything you'll ever need to know and see about this console is here. If you want to code an emulator, create your own game or simply dive into the software and hardware architecture of the Game Boy, this is the place!
-
-Rednex Game Boy Development System: the de-facto ASM development toolkit for the Game Boy and Game Boy Color.
-
-
-[hardware.inc](https://github.com/gbdev/hardware.inc) - Standard include file containing Game Boy hardware definitions for use in RGBDS projects.
-
-[rgbds-live](https://gbdev.io/rgbds-live/) - A live Game Boy programming environment in the browser, allowing for realtime assembly programming with RGBDS.
-
-
-
-
-An automated test comparison project for Game Boy emulators. It runs a suite of accuracy test ROMs across a set of Game Boy emulators and publishes the pass/fail results as a comprehensive table, with screenshots for each test case.
-
-A (work in progress) tutorial on how to program for the Game Boy in assembly, touching on every aspect required to make Game Boy games, via a Hello World, constructing an Arkanoid clone, and capping off by making a playable Shoot-'Em-Up.
-Maintained and modernized GBDK, the Game Boy Development Kit. Now powered by an updated version of the SDCC toolchain, provides a C compiler, assembler, linker and a set of libraries.
-
-The places where our community thrives. Here we chat, discuss, help each other and show what we are working on. We have a Discord server, an IRC channel, and more.
-
-Play Game Boy games online from an archive of hundreds of entries!
-A community-led attempt to collect, archive and save every unofficial game, homebrew, demo, patch, hackrom for Game Boy produced by the community through the last 3 decades of passionate work.
-
-Digital library of Game Boy related software, hardware and literature. Aimed to mirror and preserve old and fragmented contributions in the scene from the last three decades.
-
-Released in 1998 as a Nintendo accessory for the original handheld console, the Game Boy Camera became an instant hit with kids.
-Despite technological advances, its capturing sub-megapixel charm endures, capturing the imagination of photographers who embrace its unique, low-fi aesthetic.
-
-
Events
-We host coding competitions in which anyone can partecipate by creating original games, demos, homebrews tools and music for the Game Boy and compete for glory and prizes.
-
-
-
-
-
-
-
-
-For Game Boy _Advance_ development, check [gbadev.net](https://gbadev.net).
-
-
-
-
-
-
diff --git a/website/aipolicy.md b/website/aipolicy.md
deleted file mode 100644
index 359c8dcc..00000000
--- a/website/aipolicy.md
+++ /dev/null
@@ -1,111 +0,0 @@
-# AI Coding Assistants
-
-This document provides guidance for AI tools and developers using AI
-assistance when contributing to gbdev ([github.com/gbdev](https://github.com/gbdev)) projects.
-
-This is the **generic, gbdev-wide** AI policy. Individual projects under the gbdev umbrella may define their own policy, which takes precedence over this document; when present, such project-specific policies can be found in the repository's `CONTRIBUTING.md` (except for the attribution/disclosure requirement below, which is mandatory and cannot be overridden).
-
-::: tip Policy
-
-**LLM-assisted contributions are accepted but discouraged, provided they are disclosed
-as described in [Attribution](#attribution)**.
-
-:::
-
-
-Some projects may not agree with this generic policy at all, and may reject AI-assisted contributions entirely. Even where a project's policy allows AI-assisted contributions, an individual maintainer may still decline to review a specific PR on that basis, at their discretion.
-
-gbdev as a community does not endorse or recommend the use of generative AI
-assistants for software development, as it raises multiple concerns about ethics,
-legality, copyright, etc.
-
-Nevertheless, gbdev acknowledges that these practices are already in use and
-likely here to stay. Rather than banning their use, the project chooses to place responsibility
-on contributors and therefore defines the following guidelines:
-
-Contributions to gbdev projects are welcome, but should be your own work.
-Anything you submit is your responsibility, even if you used AI or other tool assistance to
-help create it. You must explain which tools you used and what
-they did (like citing sources for written work). You must have sufficiently evaluated and
-properly understood the work you intend to submit, and be able to explain and defend it.
-
-Contributors MUST NOT submit "unattended" LLM-generated contributions - that is,
-prompt-generated contributions that received no further human vetting, and more broadly
-any submission produced with little to no meaningful human review, understanding, or
-involvement - unless the project has an area explicitly designated for such
-contributions. If no such area exists, unattended contributions are forbidden.
-
-Contributors are expected to follow the standard gbdev contribution process and community
-guidelines, as well as any project-specific guidelines or rules where applicable.
-
-## Why This Matters
-
-gbdev is more than just code: it is an open source community built on a shared interest
-in Game Boy development, and that community grows largely through mentorship. Education and
-guidance of new contributors are part of our mission. Maintainers spend
-their limited time reviewing new contributors' work not only to get a given change
-merged, but to help those contributors become effective, self-sufficient contributors
-in their own right (see ["Contributor Poker"](https://kristoff.it/blog/contributor-poker-and-ai/)).
-
-Heavily AI-assisted contributions undermine this. A new contributor submitting
-largely LLM-generated output places an outsized review burden on maintainers, who
-must scrutinize code the contributor may not fully understand, which can contribute
-to reviewer burnout. Just as importantly, a contributor who leans on an LLM instead
-of working through a project's internals does not build the understanding needed to
-become an effective contributor, so the time invested in reviewing and mentoring
-them produces a return for the project that is not only less lasting, but potentially
-damaging.
-
-::: tip A Living Document
-
-This policy reflects our current thinking and is not final. AI tooling, community norms, and the legal landscape around generative AI are all still shifting, and we may revise these guidelines as new facts, tools, and experiences warrant. An open RFC can be found [here](https://github.com/gbdev/gbdev.github.io/issues/103).
-
-:::
-
-## Licensing and Legal Requirements
-
-All contributions MUST comply with the licensing terms of the project you are contributing to.
-Verify that any AI-generated content does not introduce incompatible or unclear licensing.
-Contributors are responsible for ensuring the contribution is clean.
-
-## Commit authorship
-
-AI agents MUST NOT author git commits.
-
-The human submitter is responsible for:
-
-* Reviewing all AI-generated code
-* Ensuring compliance with licensing requirements
-* Taking full responsibility for the contribution
-
-## Attribution
-
-When AI tools have meaningfully assisted in producing a contribution, it MUST be disclosed with an `Assisted-by` tag in the git commit message, in the following format:
-
-```
- Assisted-by: AGENT_NAME:MODEL_VERSION [TOOL1] [TOOL2]
-```
-
-Where:
-
-* `AGENT_NAME` is the name of the AI tool or framework
-* `MODEL_VERSION` is the specific model version used
-* `[TOOL1] [TOOL2]` are optional specialized analysis tools used
- (e.g., coccinelle, sparse, smatch, clang-tidy)
-
-Basic development tools (git, gcc, make, editors) should not be listed.
-
-Example:
-
-```
- Assisted-by: Claude:claude-3-opus coccinelle sparse
-```
-
-## References
-
-Adapted from [Linux Kernel - AI Coding Assistants](https://docs.kernel.org/process/coding-assistants.html),
-some proposals in [General Resolution: LLM usage in Debian](https://www.debian.org/vote/2026/vote_002) and
-[SF Conservancy - Recommendations When Using LLM-backed Generative AI Systems for FOSS Contributions](https://sfconservancy.org/llm-gen-ai/llm-backed-generative-ai-recommendations.html).
-
-August 16, 2026 - gbdev AI policy working group.
-
diff --git a/website/chat.md b/website/chat.md
deleted file mode 100644
index 5b9276dc..00000000
--- a/website/chat.md
+++ /dev/null
@@ -1,19 +0,0 @@
----
-sidebar: false
-head:
- - - meta
- - name: og:title
- content: gbdev chat channels
- - - meta
- - name: og:description
- content: Join gbdev.io community channels
----
-
-# Community channels
-
-Join our community on the following channels. Before posting, read the rules and the FAQ (usually in the welcome threads or in the signup).
-
-- [Discord Server](https://discord.gg/RjJKA8wrD4).
-- IRC: [`#gbdev` on Libera.chat](https://web.libera.chat/#gbdev) is bridged to the Discord chat.
- - `#gbdev-ot`, `#gbdev-casual`, `#gbdev-hw`, `#gbdev-meta`, `#gbdev-asm`, `#gbdev-gfx`, `#gbdev-emudev`, `#gbdev-romhacking` and `#gbdev-research` are also available for discussion about specific topics.
-
diff --git a/website/contributing.md b/website/contributing.md
deleted file mode 100644
index 13bb16fa..00000000
--- a/website/contributing.md
+++ /dev/null
@@ -1,111 +0,0 @@
-# Contribution guidelines
-
-These base guidelines (if not specified otherwise) apply to every repository under the [**gbdev**](https://github.com/gbdev/) github organisation. The specific CONTRIBUTING rules of the repository in object also apply. In case of conflicts, those last ones have the priority.
-
-::: tip Reaching out to maintainers
-
-If you are unsure about anything or need guidance, you can always reach out the maintainers on [Discord or IRC](https://gbdev.io/chat.html).
-
-:::
-
-## AI usage
-
-You're allowed to use AI/LLM tools to help with your contribution, but you must disclose it and meet certain expectations around reviewing and understanding the work you submit.
-
-Unattended AI-driven interactions and contributions are not allowed, unless the project has an area explicitly designated for such contributions.
-
-See the full [AI Coding Assistants policy](aipolicy.md).
-
-## Issues
-
-- Provide an example to reproduce the problem in isolation. This should include no private code. The example should be self-runnable. If a snippet is too small, create a repository.
-- Unless the problem is visual, do not include screenshots. They are not helpful.
-- Please be patient. You are probably frustrated by your problem - the maintainers are likely not aware of your workflow.
-- Be grateful for any help you are getting and follow through. The maintainers are likely giving you support for free.
-- Informal conversations are great (e.g. on Discord), but once a problem, feature request etc is outlined, opening an issue formalizing it is the most important thing you can do to help this information flow.
- - Open an issue in the related repository. If you don't know where it's better suited, [ask for help](https://gbdev.io/chat.html).
- - Include context and any information useful to understand the situation. Don't be scared about doing something wrong or _not good enough_, maintainers will follow up if needed.
-
-## Pull Requests
-
-- Keep titles and commit messages brief and concise.
-- If the PR is not self-explanatory, add brief description of what has been done and implementation choices (if any).
-- Keep the work in the branch focused and directed at accomplishing _only_ what you are describing in the PR. Ideally a PR should address a single issue (or a set of assimilable ones).
-- PRs require commitment and they are an investment by the (volunteer) maintainers. If you don't have time or abandon the PR after reviews have been left and additional changes are needed, other maintainers may take over to make sure the PR can move forward and the work is not lost.
-- Maintainers may chime in and commit to your feature branch on minor fixes (or suggestions agreed upon).
-- Don't be scared of opening more PRs for the same task, if they can be logically divided. The smaller the changes, the faster they can get reviewed and eventually merged.
-- When adding new features, think about tests and possibly updating documentation. Think about other parts of the project which may be affected by the change.
-- Document trade-offs, risks, implementation and design choices, if any.
-
-## Maintainers workflow
-
-- Tests and pipelines must pass. If they failed because of something unrelated to the PR (e.g. a link died in the meantime), mention it in the PR thread.
-- If the solution is "sub-optimal" (but still better than the actual implementation) and there are clear possible improvements, mention them and try to collaborate with the contributor, but ultimately prefer merging something sub-optimal than keeping the content stale/inactive. An issue can be opened (or kept opened) to report this situation.
-- If there's an ongoing conversation/discussion on a PR/thread, let it continue. Don't abruptly merge unless there's a clear consensus among the core maintainers (and approvals).
-- Respect the requests from other maintainers for additional time if they ask for it.
-
-
-For disruptive (e.g. major and/or breaking changes) PRs:
-
-- Wait for 2\* maintainers approval
-- Every week of inactivity/staleness, ping maintainers
-- After **2** weeks from last approval, PR is merge-able with only 1 maintainer approval
-
-For non-disruptive PRs:
-
-- Wait for 1 maintainer approval
-- If a maintainer wants to put a veto and to make the PR wait for his/her input after the mentioned period, the "veto" label can be used
-
-\*if there are at least 2 active maintainers, otherwise 1.
-
-## Where can I help?
-
-Here's a list of the project in most need of help:
-
-### GB ASM Tutorial
-
-If you're confident in writing assembly, we need contributors in helping us write, proofread and suggest improvements for new lessons in the tutorial.
-
-Technologies and tools: Assembly, RGBDS, Rust, mdBook.
-
-[Homepage](https://gbdev.io/gb-asm-tutorial) - [Repository](https://github.com/gbdev/gb-asm-tutorial)
-
-### Pan Docs
-
-The single, most comprehensive technical reference to Game Boy available to the public. Here, you can help adding new content, proof reading, fixing typos, preparing updated versions of figures or port new content from old resources.
-
-Technologies and tools: Markdown, Rust, Python, mdBook.
-
-[Homepage](https://gbdev.io/pandocs/) - [Repository](https://github.com/gbdev/pandocs) - [Beginner issues](https://github.com/gbdev/pandocs/issues?q=is%3Aissue+is%3Aopen+label%3AHacktoberfest)
-
-### RGBDS
-
-The de-facto standard assembly toolchain for the Nintendo Game Boy & Game Boy Color.
-
-Technologies and tools: C, C++, Assembly, Rust
-
-[Homepage](https://rgbds.gbdev.io/) - [Repository](https://github.com/gbdev/rgbds) - [Beginner issues](https://github.com/gbdev/rgbds/issues?q=is%3Aissue+is%3Aopen+label%3Ahacktoberfest)
-
-### Homebrew Hub
-
-A community-led attempt to collect, archive and save every unofficial game, homebrew, patch, hackrom for Game Boy produced by the community through decades of passionate work.
-
-Technologies and tools:
-
-- Frontend: Vue JS 3, Nuxt 3
-- Backend: Django REST
-
-[Homepage](https://hh.gbdev.io/) - [Repository (backend)](https://github.com/gbdev/homebrewhub) - [Repository (frontend)](https://github.com/gbdev/virens)
-
-### Homebrew Hub Database
-
-This is the database of games powering the main Homebrew Hub project. Find a game (we have a lot to add!) and add it with a Pull Request. Or - even better - write scrapers to dump entire sources and add them to our repository!
-
-Technologies and tools: JSON Schema, Python, Javascript, BeautifulSoup
-
-[Homepage](https://hh.gbdev.io/) - [Repository](https://github.com/gbdev/database) - [Beginner issues](https://github.com/gbdev/database/issues?q=is%3Aissue+is%3Aopen+label%3AHacktoberfest)
-
-## References
-
-[1](https://nitter.net/matteocollina/status/1359087694375174145)
-
diff --git a/website/donate.md b/website/donate.md
deleted file mode 100644
index dcb38cee..00000000
--- a/website/donate.md
+++ /dev/null
@@ -1,55 +0,0 @@
-# Donations and bounties
-
-## Donate
-
-You can donate on our [Open Collective](https://opencollective.com/gbdev) or on [GitHub sponsors](https://github.com/sponsors/gbdev). You can send a one-time donation of any amount or join the collective as backer, donating monthly.
-
-::: tip WHY OPENCOLLECTIVE
-
-The [gbdev](https://github.com/gbdev) organisation is an _informal_ collective of individuals. As such, we are not able to hold any fund in our accounts. Instead, the US based non-profit [Open Source Collective](https://www.oscollective.org/) is our [fiscal host](https://thesocialchangeagency.org/blog/what-is-fiscal-hosting/), providing us the financial and legal infrastructure to receive donations and spend money.
-
-:::
-
-It's also possible to donate specifically [towards a project](https://opencollective.com/gbdev/projects) under our umbrella. We'll do our best to honour this preference.
-
-Donations are subject to a 10% fee from our Open Collective host (Open Source Collective) plus a variable fee from the payment processor (Wise).
-
-Money collected on GitHub sponsors are paid out by GitHub to our Open Collective at the end of each month.
-
-## What do we spend the money on?
-
-We mostly use the money to:
-
-- Organize events (such as the gbcompo21) and give prizes,
-- Fund maintainers and contributors to our repositories,
-- Put bounties on issues on our repositories,
-- Donate back to some of the packages we use,
-- Pay servers and domains.
-
-Expenses are tracked [here](https://opencollective.com/gbdev).
-
-## Sponsoring
-
-If you have a company and you are interested in being highlighted on some of our webpages and channels, we have sponsorship tiers starting at 50$/month. Get in touch at sponsors@gbdev.io to understand if this could be for you. We are interested in hearing how our software is helping your business etc.
-
-Sponsorships are evaluated on a case-to-case basis and only be accepted if we feel like our core values are compatible and the relationship can last in the long term with beneficial effect for both parties.
-
-## Bounties
-
-Sometimes, we may assign a bounty to an Issue. Labels such as `bounty-25$`, `bounty-50$`, `bounty-100$` are used to specify the amount. If you wish to work on a issue with a bounty attached, it's a good measure to comment on the issue asking for it to be assigned to you\*.
-
-If you send a Pull Request fixing that issue and this work gets merged, you will be able to claim the corresponding bounty.
-
-Bounties are paid out from our Open Collective: submit an expense mentioning the Issue you worked on and make sure to use your GitHub email (or the one you authored the git commit with) when creating an account on OpenCollective. Wire transfers and PayPal are supported\*\*.
-
-Details are generally verified in the following 24-48 hours. Payments are then processed by our Open Collective host a couple of times per week.
-
-::: warning TAX CONSIDERATIONS
-
-Make sure to check the tax regulations of your country and properly declare the bounties received. We're not tax consultants and we cannot help with that.
-
-:::
-
-\* In case of inactivity we may remove the assignment to you or let someone else who submitted a PR claim the bounty.
-
-\*\* Our Open Collective host uses Wise as the payment processor so the countries supported for the bank transfers depends on them.
diff --git a/website/gbcompo-statement.md b/website/gbcompo-statement.md
deleted file mode 100644
index d3f4f467..00000000
--- a/website/gbcompo-statement.md
+++ /dev/null
@@ -1,79 +0,0 @@
----
-head:
- - - meta
- - name: og:title
- content: Statement on the recent removal request and rule compliance in GBCompo events
----
-
-# Statement on the recent removal request and rule compliance in GBCompo events
-
-This statement follows:
-
-- [When Exercising Copyrights Puts a Gamedev Under Threat (My Take on GBCOMPO 25) - allalonegamez](https://web.archive.org/web/20251208215130/https://allalonegamez.itch.io/zoryad/devlog/1135761/when-exercising-copyrights-puts-a-gamedev-under-threat-my-take-on-gbcompo-25)
-- [On the gbcompo23 dispute - asie](https://blog.asie.pl/2025/12/gbcompo-2023-dispute/)
-- [https://www.reddit.com/r/gbstudio/comments/1pg8r8q/comment/nszxqdn](https://www.reddit.com/r/gbstudio/comments/1pg8r8q/comment/nszxqdn)
-
-## 0. gbdev.io, Homebrew Hub and gbcompo events
-
-[gbdev.io](https://gbdev.io/) is a non-profit initiative that maintains, develops, and promotes open-source tools, documentation, and digital preservation projects for the Game Boy platform. Since 2021 we also host the "gbcompo" events.
-
-[Homebrew Hub](https://hh.gbdev.io/) is gbdev.io's institutional repository for Game Boy homebrew, dedicated to long-term digital preservation. We use it to archive and mirror all GBCompo submissions, so that the versions entered into the competition remain available and accessible over time.
-
-GBCompo events ([2021](https://gbdev.io/gbcompo21.html), [2023](https://gbdev.io/gbcompo23.html), [2025](https://gbdev.io/gbcompo25.html)) are game development competitions organised by gbdev.io, focused on creating new free Game Boy homebrew games. Developers submit games that are judged, showcased, and actively promoted during and after each edition. Some entries later go on to have commercial success, including paid digital releases and physical editions; while we are happy about this and actively showcase it, we do not profit from it, and this is not the focus of the competition.
-
-GBCompo events are run on a non-profit basis under a clear, shared framework. The prize pool is funded entirely by community donations and sponsors, with organisers and judges putting in around six months of unpaid work around and during each edition. No profit is made: donated and sponsored funds are redistributed to winners, with only a small portion going back into gbdev.io’s common funds to help support future events (for example, gbcompo25's first $500 in the prize pool were contributed by gbdev.io).
-
-All the financial statements are public on our [Open Collective pages](https://opencollective.com/gbdev/).
-
-The competition is centred on the creation and release of free Game Boy homebrew games, with open-source releases encouraged but not required.
-
-This information is available upfront and partecipants are reminded to read about the ruleset when they are submitting.
-
-## 1. Removal of Games from Homebrew Hub
-
-At the explicit request of 'allalonegamez' we have begun removing their games from Homebrew Hub. All copies of the relevant entries hosted on Homebrew Hub are being taken offline (subject only to delays such as DNS propagation, rebuilds, and caching). We are fully complying with their request to remove the content from our platform.
-
-We were requested to take down a game for which the copy on Homebrew Hub was the last available online, for free. This game participated in the 'gbcompo23' competition and was awarded monetary prizes and promotion through gbdev.io channels.
-
-## 2. Competition Rules and Availability of Jam Versions
-
-[Rule 20](https://gbdev.io/gbcompo23.html#general-criteria) of 'gbcompo23', the event where 'allalonegamez' game was submitted and won prizes says:
-
-> The submission must be available for free for the public (and not only the judges). Submission **will be published and kept online for free on the competition website**, while you are free to keep working on it (and eventually charge for it/make commercial usage).
-
-(emphasis added)
-
-We publish the jam submitted ROMs and keep them online for free on on gbdev's Homebrew Hub, after the competition window ends. Additional ZIP compilations are also prepared. **kept online for free** clearly means that the **submitted jam version** must remain publicly accessible after the competition, and not be taken down, delisted, or paywalled at a later date.
-
-In practise though, we don't enforce a specific 'competition website'; any stable, accessible online location is acceptable to be considered in compliance with Rule 20. A request to take down the default mirror to satisfy the rule in other ways/places was acceptable.
-
-We did not want to host a creator's work against their will, even if they had accepted their entry being kept online by submitting to our event, so we accepted a withdrawal scenario where 'allalonegamez' had removed every free copy of the jam version and we complied by removing ours.
-
-> The gbcompo23 entry in question was put offline by the author sometime between [March](https://web.archive.org/web/20240314014850/https://allalonegamez.itch.io/) and [June 2024](https://web.archive.org/web/20240625110157/https://allalonegamez.itch.io/). This is a perfectly legitimate approach, as long as the jam-submitted versions of the games are kept online and available for free, somewhere, as per rules of the event they were submitted to.
-
-
-## 3. Disqualification and Prize Eligibility
-
-Since the author did not entertain the idea of keeping the copy anywhere else online for free, the entry is in violation of Rule 20.
-
-Because the entry no longer complies to the event rules, they'll be disqualified from the competition. Disqualified entries are not eligible for prizes nor leaderboards.
-
-In order to be fair to all other participants who are respecting the rules and keeping their jam-versions available, any prizes or awards previously assigned to these entries should be reassigned to eligible participants. Any mention of a refund of prizes was made solely in this context of fairness and proper redistribution.
-
-## 4. No Legal action is being taken
-
-We want to clarify that we have no intention of taking legal action or attempting to enforce a prize refund by any means. Our reference to an 'expectation of a refund' was intended purely as a matter of fairness to all parties involved in the event, not as a threat or condition, and we recognize that our wording may have come across more harshly than intended. At no point was legal action discussed or considered, and we did not withhold or delay any part of the removal requested by 'allalonegamez'.
-
-> 'allalonegamez' is [now offering to refund the prize and donate it to a charity](https://old.reddit.com/r/gbstudio/comments/1pg8r8q/when_exercising_copyrights_puts_a_gamedev_under/nsy77rt/). We appreciate the offer, however we are not and have never been interested in pursuing the financial matter further so we do not have a say on that. No further action will be taken in that regard. We are in any case ready to reinstate the entry in its earned position at any time, if and when it becomes compliant with the rules again.
-
-## 5. Fairness to all parties involved in events
-
-The GBCompo prize pool is funded by people who donate money under specific premises: free games will be released in the framework of the ruleset of the event. Participants also agree to abide by these rules as a condition for qualifying for prizes and promotion.
-
-In this context, it would be unfair to continue to award exposure, promotion, and prizes to a participant who is not abiding by the rules, receiving prizes, exposure and promotion only to pull their entry offline and make them paid-only, while many other submitters – including those who did not win – continue to respect them and keep their jam-versions available.
-
-Disqualification in such a case is necessary to uphold the expectations of all the other actors in the event, including donors, organisers and other participants.
-
-## 6. Next steps
-
-We plan to revise the wording of the rules to make this expectation clearer, explicitly indicating where entries will be kept online and using more precise language about the long-term, effectively perpetual availability of the submitted jam versions.
diff --git a/website/gbcompo21.md b/website/gbcompo21.md
deleted file mode 100644
index 2dc07d21..00000000
--- a/website/gbcompo21.md
+++ /dev/null
@@ -1,352 +0,0 @@
----
-head:
- - - meta
- - name: og:title
- content: GB Compo 2021
- - - meta
- - name: og:description
- content: >
- An OG game competition! Create original games, demos, homebrews tools and music
- for the original Game Boy / Game Boy Color and compete for glory (and a lot of prizes)!
----
-
-# Game Boy Competition 2021
-
-
-
-
-
-> *gbcompo21* was a game jam run from July 1 to October 1 2021 and it was hosted on [itch.io/jam/gbcompo21](https://itch.io/jam/gbcompo21).
-
-Create original games, demos, homebrews tools and music for the original Game Boy (Color) and compete for glory (and up to US$ 3,829 in prizes)!
-
-
-## Results
-
-Full leaderboards are available [here](#full-results). Top rated entries are playable [here](https://hh.gbdev.io/search?tags=gbcompo21-shortlist) while the complete list can be found on [Homebrew Hub](https://hh.gbdev.io/search?tags=gbcompo21) and [Itch.io](https://itch.io/jam/gbcompo21/entries). A zip file can be downloaded from [GitHub](https://github.com/gbdev/gbcompo21/releases/tag/gbcompo21_entries).
-
-**Games**:
-
-1. [Unearthed](https://hh.gbdev.io/game/unearthed) - BenJelter
-2. [Rhythm Land](https://hh.gbdev.io/game/rhythm-land) - martendo
-3. [Shock Lobster](https://hh.gbdev.io/game/shock-lobster) - tbsp
-4. [Dango Dash](https://hh.gbdev.io/game/dango-dash) - Rebusmind
-5. [Rebound](https://hh.gbdev.io/game/rebound) - DevEd
-6. [Core Machina](https://hh.gbdev.io/game/core-machina) - Pearacidic
-7. [Sushi Nights](https://hh.gbdev.io/game/sushi-nights) - Zalo
-8. [MARLA and the Elemental Rings](https://hh.gbdev.io/game/marla-and-the-elemental-rings-demo) - Geek_Joystick
-9. [Porklike GB](https://hh.gbdev.io/game/porklike-gb) - binji
-10. [GB Corp](https://hh.gbdev.io/game/gb-corp) - Dr. Ludos
-11. [Dawn Will Come](https://hh.gbdev.io/game/dawn-will-come) - eishiya
-12. [\](https://hh.gbdev.io/game/corrib75) - gearfo
-13. [El Dueloroso](https://hh.gbdev.io/game/el-dueloroso) - Adrián JG
-14. [Glory Hunter](https://hh.gbdev.io/game/glory-hunters) - 2think design studio
-15. [Renegade rush](https://hh.gbdev.io/game/renegade-rush) - quinnp
-16. [Fix My Heart](https://hh.gbdev.io/game/fix-my-heart) - Filipe Bianchin
-17. [GBCspelunky](https://hh.gbdev.io/game/gbcspelunky) - sukus
-
-Winning entry for the **Music** category: [Zilogized](https://hh.gbdev.io/game/zilogized) - Kabcorp
-
-Winning entry for the **Tools** category: [Brainfox](https://hh.gbdev.io/game/brainfox) - Yprit
-
-
-## Sponsors
-
-::: warning
-Donations to the prize pool are now __closed__, no more are being accepted.
-:::
-
-The entire prize pool is community-funded. Donate or sponsor the competition through [GitHub sponsors](https://github.com/sponsors/avivace?editing=true&frequency=one-time).
-
-
-
-
-
-
-
-
-
-
-
-### Donations from Individuals
-
-[Toxa](https://github.com/untoxa),
-[Carles Castillo](https://github.com/Nicolastriplec),
-[Dave VanEe](https://github.com/tbsp),
-[Chris Maltby](https://github.com/chrismaltby),
-[William Bettridge-Radford](https://github.com/willbr),
-[Josh Frisby](https://github.com/oshf),
-[Sanqui](https://github.com/Sanqui)
-
-### Partners
-
-
-
-
-
-## Community
-
-Come join us on Discord or others for help getting started, and head over to gbdev.io for useful links & resources!
-
-We'd love to hear from participants, share your progress on Twitter using the `#gbcompo` hashtag and join the conversation the on Discord `#compo-chat` channel!
-
-## Resources
-
-Here a curated list tools and documentaiton to get you started.
-
-- General links
- - [Pan Docs](https://gbdev.io/pandocs), Game Boy technical documentation
- - [Awesome Game Boy Development list of resources](https://gbdev.io/list)
- - Especially relevant is the ["Software Development"](https://gbdev.io/list.html#software-development) paragraph
- - [How to choose between C and ASM for GB?](https://gbdev.io/guides/tools.html#languages)
-- [RGBDS](https://rgbds.gbdev.io/) (ASM language)
- - [GB ASM Tutorial](https://eldred.fr/gb-asm-tutorial)
-- [GBDK](https://github.com/gbdk-2020/gbdk-2020/) (C language)
- - [API docs: Getting Started](https://gbdk-2020.github.io/gbdk-2020/docs/api/docs_getting_started.html)
- - [Examples](https://github.com/mrombout/gbdk_playground)
- - [Docs, links and tools](https://gbdk-2020.github.io/gbdk-2020/docs/api/docs_links_and_tools.html)
- - [ZGB: a game engine using GBDK](https://github.com/Zal0/ZGB)
- - [Dedicated Discord]()
-- [GBStudio](https://www.gbstudio.dev/) (no coding required)
- - [Resources to get started](https://gbstudiocentral.com/resources/)
- - [Dedicated Discord](https://discord.gg/knRryZWGcm)
-
-
-## Prize pool
-
-The current prize pool is  and will be split according to [this document](https://docs.google.com/spreadsheets/d/196vIZGLbSk1ewzRnZc7Dy_LJdPQpfJUg-udT4iyBVOg).
-
-Entries will compete in 4 separate categories and 4 rankings will be drawn up. Based on these leaderboards, the top entries will be awarded as follows:
-
-- Best games (80% of the prize pool)
- - The best 5 games will get a prize.
- - The best 3 OSS games will get an additional prize (can overlap)
-- Best homebrew/tool (1) (running on a GB/GBC)
-- Best music cartridge (1)
-- Best demo (1) (demoscene style demos)
-
-The rankings are decided according to the [JUDGING](#JUDGING) criteria.
-
-### Open Source Bonus
-
-If you publish your game source code and assets on a public repository (e.g. GitHub) under [an open source license](https://choosealicense.com/) (GPL, MIT, Apache, CC0...) you get **20%** more of the prize as a bonus!
-
-*Money prizes are subject to fees and conversions. They may be delivered as gift cards of your choice.*
-
-## Rules
-
-1. Final entry "deliverable" must consist of a GB or GBC ROM file. You can also attach descriptive documents in TXT, Markdown or HTML file format (e.g. game manual).
-2. The ROM file will be tested on real hardware (usually a DMG or a GBC) and on the BGB emulator. In case of different results, the hardware test will be the trusted one.
-3. GBC features are allowed.
-4. SGB features are allowed. The entries will be evaluated mainly on Game Boy (Color). You can use SGB features, but don't rely on them as main features for your entry.
-5. Teams are allowed (award will be split).
-6. The competition is focused on *games*, but you are allowed to submit *homebrew tools*, *demoscene style demos* and *music cartridges*. There may be special category prizes for those. There are no preferred themes.
-7. ROM Hacks are not allowed.
-8. NSFW content is not allowed. Please keep it safe for work and avoid anything overly edgy or distasteful.
-9. You can submit more than one entry. They will be rated separately, but each person or team may only accept prizes on behalf of a single entry (e.g., you can't win both the 1st and 2nd place prize).
-10. Judges can submit entries, but they are not eligible for prizes.
-11. Only some official MBC chips (MBC1, MBC2, MBC3 and MBC5, as well as no MBC at all) are allowed. The only exceptions are:
- 1. While Nintendo only manufactured very specific MBC+ROM combinations, we will allow any power-of-two ROM size between 32 KiB and the maximum for the chosen chip. (This is simply a convenience feature, as any ROM image can be extended to fit a larger ROM chip by appending copies of itself.)
- 2. The ROM+RAM combination (i.e., without an MBC, up to 32 KiB ROM and 8 KiB SRAM), with or without battery, will be accepted for compatibility, as many people have simple flash carts that do have SRAM but don't have an MBC. (This can be trivially emulated by any MBC1, MBC3 or MBC5 configuration.)
- 3. The variants MBC30 and MBC1M are accepted, as they have been used in licensed games.
- 4. Some official MBCs added extra components to the cart: MBC3/MBC30 carts could have an RTC, and MBC5 carts could have rumble. These combinations, and only these combinations, will be accepted. In particular, this means it's impossible to have RTC and rumble in the same cart.
-
-12. Entries cannot rely on extra hardware or add-on devices, they must be playable without a specific setup.
-13. As long as you produce a GB or GBC ROM file that runs on GB/GBC, any tool to develop the entry is allowed. This includes RGBDS (ASM), GBDK (C), ZGB (C), GB Studio, ...
-14. Source code is optional, but very much appreciated. Entries submitted with source code (public repositories on GitHub/GitLab/... are accepted) and with an open source license (See rule 15) are eligible for **additional** prizes. Make sure to fill the "Open Source repository" field with a valid and public repository when submitting on itch.io. See [this issue](https://github.com/gbdev/gbcompo21/issues/3) for further details.
-15. To be eligible for the OSS bonus, the code must be licensed under an "Open Source" license (any of the "Free" license listed [here](https://www.gnu.org/licenses/license-list.en.html) is accepted). Assets can be licensed under any of the CC licenses. Obfuscated code, non-reproducible builds, intentionally making the source code hard to read/re-use will exclude the entry form the OSS leaderboards. This is at discretion of the hosts and judges. We must be able to read the code and compile it into a working ROM, running a script or following istructions.
-16. The entry must be submitted on the itch.io jam page.
-17. Your work must be new and original. You cannot enter with something you were already working on before the start of the jam. The majority of work should be done during the jam. If in doubt, please ask us before proceeding.
- Examples of things that are **allowed:**
- - A brand new game using your own (or any) existing game engine.
- - A remake of your (or someone else's) game for a different platform, but with new code and assets.
- - A brand new game you barely started (e.g. you only made a title screen or a mockup of the gameplay).
- - Using templates, tutorial code as starting point (when they're license allow to do so, e.g. [gb-boilerplate](https://github.com/ISSOtm/gb-boilerplate))
- - Asset packs for art and music are ok if the licensing permits. Be sure to make note of what is being reused and under what terms. Use of pre-made assets (vs original ones) may be factored in when judges assess points for relevant criteria (e.g. graphics, audio).
-
-
- Examples of things that are **not allowed:**
- - A project you've been working on for months.
- - An update/patch for a game you already released publicly.
-18. The submission must be available for free for the public (and not only the judges). Submission will be published and kept online for free on the competition website, while you are free to keep working on it (and eventually charge for it/make commercial usage).
-19. Multiple submissions are allowed.
-20. If your submission contains text that isn't available in English, some judges might not be able to read that text, and they will judge accordingly.
-
-## Judging
-
-Judges will evaluate submissions using the following criteria:
-
-1. **Gameplay** - How entertaining are the gameplay mechanics? How fun is the game?
-2. **Technical** - How innovative is it from a technical perspective? Does it push the hardware to the limit?
-3. **Originality** - How refreshing is it from a non-technical perspective? Does it have a unique design / mechanics?
-4. **Graphics** - How impressive are the art, animation and visual effects?
-5. **Audio** - How good are the music and sound effects?
-
-Criteria applicability:
-
-- Games are ranked according to all the listed criteria
-- demos on 2,3,4,5
-- Music entries on 5
-- Homebrew/tools entries on 2 and 1 (with gameplay being UX)
-
-The leaderboards are computed by averaging the "Overall" score.
-
-## FAQ
-
-Something not clear? Open an issue or join the discussion on Discord.
-
-## Credits
-
-Special thanks to:
-
-- The organizers, from all over the gbdev community: [avivace](https://github.com/avivace), [Proximity Sound](https://proximitysound.com), [untoxa](https://github.com/untoxa), [bbbbbr](https://github.com/bbbbbr), [Sanqui](https://github.com/Sanqui), [aaaaaa123456789](https://github.com/aaaaaa123456789), [ISSOtm](https://github.com/ISSOtm)
-- [Optix](https://github.com/Hacktix), for the awesome logo and the other graphical assets
-- Judges: ISSOtm, avivace, Sanqui, Asobi.tech, untoxa, Veund, duodreamer
-
-## Full results
-
-### Music
-
-**Winner**: Zilogized by Kabcorp
-
-### Tools
-
-**Winner**: BrainFox by Yprit
-
-### Overall
-
-Sorted by "Overall" score, only the shortlisted entries were ranked by judges.
-
-|Name |Gameplay |Technical |Originality |Graphics |Audio |**Overall** |
-|-----------------------------|------------------|------------------|------------------|------------------|------------------|------------------|
-|Unearthed |4.29 |3.86 |4.86 |4.43 |3.29 |4.15 |
-|Rhythm Land |3.71 |3.71 |4.14 |3.86 |4.57 |4.0 |
-|Shock Lobster |4.0 |4.29 |4.71 |3.71 |3.14 |3.97 |
-|Dango Dash |3.86 |3.29 |3.86 |4.0 |4.57 |3.92 |
-|Rebound |4.0 |3.86 |3.57 |3.71 |4.29 |3.89 |
-|Core Machina |3.14 |3.43 |4.0 |4.57 |4.14 |3.86 |
-|Sushi Nights |3.86 |3.86 |4.0 |3.71 |3.71 |3.83 |
-|MARLA and the Elemental Rings|3.29 |3.86 |3.43 |4.0 |3.86 |3.69 |
-|Porklike GB |4.0 |4.0 |3.14 |3.57 |3.71 |3.68 |
-|GB Corp. |3.14 |3.86 |4.57 |3.14 |3.29 |3.6 |
-|Dawn Will Come |2.71 |3.14 |4.29 |4.29 |3.43 |3.57 |
-|\ |3.71 |3.86 |3.14 |3.71 |3.29 |3.54 |
-|El Dueloroso |3.29 |3.57 |3.86 |3.0 |3.29 |3.4 |
-|Glory Hunters |3.43 |3.14 |3.43 |3.57 |3.43 |3.4 |
-|Renegade rush |3.57 |3.57 |3.29 |3.0 |3.29 |3.34 |
-|Fix My Heart |3.71 |2.86 |3.57 |2.71 |2.71 |3.11 |
-|GBCspelunky |3.0 |3.71 |2.43 |3.71 |2.57 |3.08 |
-
-### Gameplay
-
-|Name |Gameplay|
-|-----------------------------|--------|
-|Unearthed |4.29 |
-|Rebound |4.0 |
-|Porklike GB |4.0 |
-|Shock Lobster |4.0 |
-|Dango Dash |3.86 |
-|Sushi Nights |3.86 |
-|Rhythm Land |3.71 |
-|\ |3.71 |
-|Fix My Heart |3.71 |
-|Renegade rush |3.57 |
-|Glory Hunters |3.43 |
-|MARLA and the Elemental Rings|3.29 |
-|El Dueloroso |3.29 |
-|Core Machina |3.14 |
-|GB Corp. |3.14 |
-|GBCspelunky |3.0 |
-|Dawn Will Come |2.71 |
-
-
-### Technical
-
-|Name |Technical|
-|-----------------------------|---------|
-|Shock Lobster |4.29 |
-|Porklike GB |4.0 |
-|Sushi Nights |3.86 |
-|\ |3.86 |
-|GB Corp. |3.86 |
-|MARLA and the Elemental Rings|3.86 |
-|Unearthed |3.86 |
-|Rebound |3.86 |
-|Rhythm Land |3.71 |
-|GBCspelunky |3.71 |
-|El Dueloroso |3.57 |
-|Renegade rush |3.57 |
-|Core Machina |3.43 |
-|Dango Dash |3.29 |
-|Dawn Will Come |3.14 |
-|Glory Hunters |3.14 |
-|Fix My Heart |2.86 |
-
-
-### Originality
-
-|Name |Originality|
-|-----------------------------|-----------|
-|Unearthed |4.86 |
-|Shock Lobster |4.71 |
-|GB Corp. |4.57 |
-|Dawn Will Come |4.29 |
-|Rhythm Land |4.14 |
-|Core Machina |4.0 |
-|Sushi Nights |4.0 |
-|Dango Dash |3.86 |
-|El Dueloroso |3.86 |
-|Rebound |3.57 |
-|Fix My Heart |3.57 |
-|MARLA and the Elemental Rings|3.43 |
-|Glory Hunters |3.43 |
-|Renegade rush |3.29 |
-|Porklike GB |3.14 |
-|\ |3.14 |
-|GBCspelunky |2.43 |
-
-### Graphics
-
-|Name |Graphics|
-|-----------------------------|--------|
-|Core Machina |4.57 |
-|Unearthed |4.43 |
-|Dawn Will Come |4.29 |
-|Dango Dash |4.0 |
-|MARLA and the Elemental Rings|4.0 |
-|Rhythm Land |3.86 |
-|\ |3.71 |
-|GBCspelunky |3.71 |
-|Rebound |3.71 |
-|Shock Lobster |3.71 |
-|Sushi Nights |3.71 |
-|Glory Hunters |3.57 |
-|Porklike GB |3.57 |
-|GB Corp. |3.14 |
-|El Dueloroso |3.0 |
-|Renegade rush |3.0 |
-|Fix My Heart |2.71 |
-
-
-### Audio
-
-|Name |Audio|
-|-----------------------------|-----|
-|Dango Dash |4.57 |
-|Rhythm Land |4.57 |
-|Rebound |4.29 |
-|Core Machina |4.14 |
-|MARLA and the Elemental Rings|3.86 |
-|Porklike GB |3.71 |
-|Sushi Nights |3.71 |
-|Glory Hunters |3.43 |
-|Dawn Will Come |3.43 |
-|El Dueloroso |3.29 |
-|Renegade rush |3.29 |
-|Unearthed |3.29 |
-|\ |3.29 |
-|GB Corp. |3.29 |
-|Shock Lobster |3.14 |
-|Fix My Heart |2.71 |
-|GBCspelunky |2.57 |
-
diff --git a/website/gbcompo23.md b/website/gbcompo23.md
deleted file mode 100644
index 2cbbe759..00000000
--- a/website/gbcompo23.md
+++ /dev/null
@@ -1,349 +0,0 @@
----
-head:
- - - meta
- - name: og:title
- content: GB Compo 2023
- - - meta
- - name: og:description
- content: >
- Join us for an epic game development event celebrating the classics! Show off your creativity and skills by crafting original games for the iconic Game Boy!
----
-
-# Game Boy Competition 2023
-
-
-
-
-
-
-> *gbcompo23* was a game jam run from **June 15th** to **September 15**, 2023 and it was hosted on [itch.io/jam/gbcompo23](https://itch.io/jam/gbcompo23).
-
-
-#### Join us for an epic game development event celebrating the classics! Show off your creativity and skills by crafting original games, demos, homebrew tools or music specifically designed for the legendary Game Boy. Get ready to battle it out for fame, honor, and an incredible array of prizes that await the best entries.
-
-
-
-Theme: _You are the monster_.
-
-## Prizes and Categories
-
-The total prize pool of will be split according to [this document](https://docs.google.com/spreadsheets/d/e/2PACX-1vRTOM0XElTJJGF97GyxzL393SBuPAjW3woxYZo5Fs_X_tC8Yxq_hojVNw74XGeOFYHu0_5bwS3NyQLZ/pubhtml). Anybody can contribute by [donating](https://opencollective.com/gbdev/projects/gbcompo23).
-
-An entry can compete in one and only one of the following leaderboards:
-
-- Games (80% of the prize pool)
- - The best **5** games will get a prize.
- - The best **3** OSS games will get an **additional** prize (can overlap)
- - Examples: [Dango Dash](https://hh.gbdev.io/game/dango-dash), [Core Machina](https://hh.gbdev.io/game/core-machina), [Unearthed](https://hh.gbdev.io/game/unearthed)
-- Music cartridges (**1 prize**)
- - Examples: [Zilogized](https://hh.gbdev.io/game/zilogized), [Freebie-gbs-2018](https://hh.gbdev.io/game/freebie-gbs-2018)
-- Demoscene style (**1 prize**)
- - This category is for audio-visual demonstrations that run without viewer interaction, in such a way as to demonstrate skill in manipulation of the Game Boy hardware through code, special effects, art, music, and direction. More on [Wikipedia](https://en.wikipedia.org/wiki/Demoscene).
- - Examples: [Back to Color](https://hh.gbdev.io/game/back-to-color), [Is that a demo in your pocket?](https://hh.gbdev.io/game/is-that-a-demo-in-your-pocket), [Cute Demo CGB](https://hh.gbdev.io/game/cute-demo-cgb).
-- Tool (**1 prize**)
- - Examples: [Bannerprinter](https://hh.gbdev.io/game/bannerprinter), [On-Board GB Tile Creator](https://hh.gbdev.io/game/obgbtc), [StopWatch](https://hh.gbdev.io/game/stopwatch-version-1).
-
-The rankings are decided according to the [judging](#judging) criteria.
-
-## Community
-
-Come join us on Discord for help getting started (or find a team), and head over to gbdev.io for a curated list of links and development resources!
-
-We'd love to hear from participants, share your progress using the `#gbcompo23` hashtag and join the conversation on the Discord `#gbcompo23` channel!
-
-We're also on [Mastodon](https://mastodon.social/@gbdev).
-
-## Resources
-
-Here is a curated list tools and documentation to get you started.
-
-- General links
- - [The Game Boy, a hardware autopsy](https://www.youtube.com/playlist?list=PLu3xpmdUP-GRDp8tknpXC_Y4RUQtMMqEu) (Video)
- - [The Ultimate Game Boy Talk](https://media.ccc.de/v/33c3-8029-the_ultimate_game_boy_talk) (Video)
- - [Pan Docs](https://gbdev.io/pandocs), Game Boy technical documentation
- - [Awesome Game Boy Development list of resources](https://gbdev.io/resources.html)
- - Especially relevant is the ["Software Development"](https://gbdev.io/resources.html#software-development) section
- - [How to choose between C and ASM for GB?](https://gbdev.io/guides/tools.html#languages)
-- [RGBDS](https://rgbds.gbdev.io/) (ASM language)
- - [GB ASM Tutorial](https://eldred.fr/gb-asm-tutorial)
-- [GBDK](https://github.com/gbdk-2020/gbdk-2020/) (C language)
- - [API docs: Getting Started](https://gbdk-2020.github.io/gbdk-2020/docs/api/docs_getting_started.html)
- - [Examples](https://github.com/mrombout/gbdk_playground)
- - [Docs, links and tools](https://gbdk-2020.github.io/gbdk-2020/docs/api/docs_links_and_tools.html)
- - [ZGB: a game engine using GBDK](https://github.com/Zal0/ZGB)
- - [Dedicated Discord](https://discord.gg/XCbjCvqnUY)
- - ["How-to make a Gameboy Game"](https://laroldsjubilantjunkyard.com/tutorials/how-to-make-a-gameboy-game/) tutorial series
-- [GBStudio](https://www.gbstudio.dev/) (no coding required)
- - [Resources to get started](https://gbstudiocentral.com/resources/)
- - [Dedicated Discord](https://discord.gg/knRryZWGcm)
-- [GBSDK](https://github.com/daid/gbsdk) (C + RGBDS ASM, without a standard library)
- - [Documentation](https://daid.github.io/gbsdk/)
- - [Source for a game](https://github.com/QuinnPainter/CrossConnect) and [another game](https://github.com/QuinnPainter/Wyrmhole) built with GBSDK
-
-## Sponsors
-
-- Main sponsor: [Incube8Games](https://incube8games.com/)
-- Gold sponsors: [FerranteCrafts](https://ferrantecrafts.com/), [Macho Nacho](https://macho-nacho.com/)
-- Silver sponsors: [Bitmap Soft](https://www.bitmapsoft.co.uk/), [Broke Studio](https://www.brokestudio.fr/), [Yastuna Games](https://yastuna-games.com/en/)
-- Bronze sponsors: [insideGadgets](https://shop.insidegadgets.com/)
-
-Interested in sponsoring? Shoot us a [DM](https://twitter.com/gbdev0).
-
-## Rules
-
-### Prizes and Judging
-
-1. The submitted entry must implement / interpret the jam theme (applies to Game category only).
-2. Multiple submissions are allowed from the same person or team. They will be rated separately, but each person or team may only accept prizes on behalf of a single entry (e.g., you can't win both the 1st and 2nd place prize).
-3. Judges can submit entries, but the entries won't be eligible for prizes. Judges won't score the entries they submitted.
-4. Source code is optional, but very much appreciated. Entries submitted with source code (public repositories on GitHub/GitLab/... are accepted) and with an open source license (See rule 5) are eligible for **additional** prizes. Make sure to fill the "Open Source repository" field with a valid and public repository when submitting on itch.io. It is NOT necessary for the source to be available *during* the competition period. Repositories/sources just need to be public from the time the event ends. See [this issue](https://github.com/gbdev/gbcompo21/issues/3) for further details.
-5. To be eligible for the OSS bonus, the code must be licensed under an "Open Source" license (any of the "Free" license listed [here](https://www.gnu.org/licenses/license-list.en.html) is accepted). Assets can be licensed under any of the CC licenses. Obfuscated code, non-reproducible builds, intentionally making the source code hard to read/re-use will exclude the entry form the OSS leaderboards. This is at discretion of the hosts and judges. We must be able to read the code and compile it into a working ROM, running a script or following instructions.
-7. If your submission contains text that isn't available in English, some judges might not be able to read that, and they will judge accordingly.
-8. Shipment of physical prizes may not be available to all countries, they are subject to shipping ability of the prize donors.
-
-### Hardware Criteria
-
-8. Final entry "deliverable" must consist of a GB or GBC ROM file. You can also attach descriptive documents in TXT, Markdown, PDF or HTML file format (e.g. game manual).
-9. The ROM file will be tested on real hardware (primarily a GBC) and on the BGB emulator. In case of different results, the hardware test will be the trusted one.
-10. GBC features are allowed, and entries may be GBC only (GBC only should be indicated in game for non-GBC hardware or on the entry's page).
-11. SGB features are allowed. The entries will be evaluated mainly on Game Boy Color. You can use SGB features, but don't rely on them as main features for your entry.
-12. Only some official MBC chips (MBC1, MBC2, MBC3 and MBC5, as well as no MBC at all) are allowed. The only exceptions are:
- 1. While Nintendo only manufactured very specific MBC+ROM combinations, we will allow any power-of-two ROM size between 32 KiB and the maximum for the chosen chip. (This is simply a convenience feature, as any ROM image can be extended to fit a larger ROM chip by appending copies of itself.)
- 2. The ROM+RAM combination (i.e., without an MBC, up to 32 KiB ROM and 8 KiB SRAM), with or without battery, will be accepted for compatibility, as many people have simple flash carts that do have SRAM but don't have an MBC. (This can be trivially emulated by any MBC1, MBC3 or MBC5 configuration.)
- 3. The variants MBC30 and MBC1M are accepted, as they have been used in licensed games.
- 4. Some official MBCs added extra components to the cart: MBC3/MBC30 carts could have an RTC, and MBC5 carts could have rumble. These combinations, and only these combinations, will be accepted. In particular, this means it's impossible to have RTC and rumble in the same cart.
-13. Entries cannot rely on extra hardware or add-on devices, they must be playable without a specific setup.
-14. As long as you produce a GB or GBC ROM file that runs on GB/GBC, any tool to develop the entry is allowed. This includes RGBDS (ASM), GBDK (C), ZGB (C), GB Studio, ...
-
-### General Criteria
-
-15. Teams are allowed (award will be split).
-16. ROM Hacks are not allowed.
-17. NSFW content is not allowed. Please keep it safe for work and avoid anything overly edgy or distasteful.
-18. The entry must be submitted on the itch.io jam page.
-19. Your work must be new and original. You cannot enter with something you were already working on before the start of the jam. The majority of work should be done during the jam. If in doubt, please ask us before proceeding.
-
- Examples of things that are **allowed:**
-
- - A brand new game using your own (or any) existing game engine.
- - A remake of your (or someone else's) game for a different platform, but with new code and assets.
- - A brand new game you barely started (e.g. you only made a title screen or a mockup of the gameplay).
- - Using templates, tutorial code as starting point (when they're license allow to do so, e.g. [gb-boilerplate](https://github.com/ISSOtm/gb-boilerplate))
- - Asset packs for art and music and covers of other music are allowed if the licensing permits. Be sure to make note of what is being reused or re-interpreted and under what terms. Use of pre-made assets and content (vs wholly original) may be factored in when judges assess points for relevant criteria (e.g. graphics, audio).
-
- Examples of things that are **not allowed:**
-
- - A project you've been working on for months.
- - An update/patch for a game you already released publicly.
-
-20. The submission must be available for free for the public (and not only the judges). Submission will be published and kept online for free on the competition website, while you are free to keep working on it (and eventually charge for it/make commercial usage).
-21. It is allowed to submit an entry to multiple events.
-
-## Judging
-
-A team of (~10) judges has been selected before the start of the competition.
-
-Every judge will be asked to compile an unranked shortlist of their favourite entries. Once the shortlists are ready from all the judges, the top 15 most selected entries will be evaluated by the judges on the following criteria:
-
-1. **Gameplay** - How entertaining are the gameplay mechanics? How fun is the game?
-2. **Technical** - How innovative is it from a technical perspective? Does it push the hardware to the limit?
-3. **Originality** - How refreshing is it from a non-technical perspective? Does it have a unique design / mechanics?
-4. **Graphics** - How impressive are the art, animation and visual effects?
-5. **Audio** - How good are the music and sound effects?
-6. **Theme** - How was the theme implemented in the gameplay, the mechanics and/or the story?
-
-Applicability may vary depending on the category of the entry (e.g. a "Music" ROM may be evaluated only on 5).
-
-The final leaderboard is computed by averaging the "Overall" score.
-
-::: warning
-Judges are volunteers. Those details can change without prior notice, as the judging process is long and involves hundreds of entries. Detailed criteria leaderboards will be provided only for entries in the final shortlist.
-:::
-
-## Acknowledgements
-
-Special thanks to everyone who helped organising and running this event:
-
-**Donators:** Anonymous Donor
-
-**Partners:** gbdev.io, LaroldsJubilantJunkyard, RetroBreak, GB Studio Central
-
-**Organisers:** avivace, Tronimal, duodreamer, bbbbbr, ISSOtm, RetroBreak, LaroldsJubilantJunkyard, ProximitySound, Hacktix
-
-**Judges:** avivace, ISSOtm, Duo, Tronimal, Ferrante Crafts, NickWestwood, Veund, Toxa
-
-## Past editions
-
-- [GB Competition 2021](https://itch.io/jam/gbcompo21)
-
-
-## Results
-
-You can find [all submissions in Homebrew Hub](https://hh.gbdev.io/search?tags=event:gbcompo23) and in [this GitHub repository](https://github.com/gbdev/gbcompo23/).
-
-### Music
-
-1. [8bit Sunset (GameBoy Music)](https://hh.gbdev.io/game/kabcorp__8bit-sunset) - Kabcorp
-2. [Gb Compo 23 – Music Cart – Tune In!](https://hh.gbdev.io/game/beatscribe__gb-compo-23-music-cart-tune-in) - beatscribe
-3. [Monkeys on Mars](https://hh.gbdev.io/game/chavezfunktion__monkeys-on-mars) - Chavez.funktion
-4. shared by
- * [The Infernodome – Original GB SongROM!](https://hh.gbdev.io/game/emperorjub__infernodome-song-rom) - EmperorJub
- * [No time for art sorry (Music Cart)](https://hh.gbdev.io/game/incognitiouwu__no-time-for-art-sorry-music-cart) - incognitio
- * [Re: Cycle](https://hh.gbdev.io/game/sloopygoop__re-cycle) - sloopygoop
-
-### Demoscene
-
-1. [Did Somebody Say Demo?](https://hh.gbdev.io/game/vl2m-studio__dssd) - VL2MSTUDIO
-2. [DDDDDEEMO(N)](https://hh.gbdev.io/game/lillie-chippie__dddddeemon) - Lillie_chippie
-
-### Tools
-
-1. [Monster Orc-arina](https://hh.gbdev.io/game/pearacidic__monster-orc-arina-a-game-boy-tool) - Pearacidic
-2. [Dungeon Master](https://hh.gbdev.io/game/joaobapt__dungeon-master) - joaobapt
-3. [WeekPlanner](https://hh.gbdev.io/game/creativags__weekplanner) - CreativaGS
-4. shared by
- * [Iron Cor – Stainless](https://hh.gbdev.io/game/elvies__iron-cor-stainless) - Elvies
- * [Nyan note](https://hh.gbdev.io/game/djarky__nyan-note) - Arky750
- * [witch sound tracker](https://hh.gbdev.io/game/djarky__witch-sound-tracker) - Arky750
- * [SGB SOU_TRN demo](https://hh.gbdev.io/game/cloudscomputing__sgb-sou-trn-demo) - cloudscomputing
-
-### Games
-
-#### Overall
-
-|# |Name |Overall|Gameplay|Technical|Originality|Graphics|Audio|Theme|
-|--:|-----------------------------------------------------------------------------------|-------|--------|---------|-----------|--------|-----|-----|
-|1 |[Hermano](https://hh.gbdev.io/game/patmorita__hermano-game-boy) † § |4.0 |4.17 |4.0 |3.5 |4.33 |3.83 |4.17 |
-|2 |[Feed IT Souls](https://hh.gbdev.io/game/gumpyfunction__feed-it-souls) † |3.92 |4.17 |3.5 |3.83 |3.83 |3.67 |4.5 |
-|3 |[Enceladus](https://hh.gbdev.io/game/mrpapshmir__enceladus) † |3.75 |3.5 |3.0 |3.83 |3.67 |3.67 |4.83 |
-|5 |[NUNYA](https://hh.gbdev.io/game/bard-0__nunya) † |3.58 |3.33 |3.0 |3.17 |4.33 |3.67 |4.0 |
-|6 |[Ghost of the Arcade](https://hh.gbdev.io/game/playinstinct__ghost-of-the-arcade) |3.42 |3.33 |3.5 |3.33 |3.67 |3.33 |3.33 |
-|7 |[Kaiju Kai Kai](https://hh.gbdev.io/game/sunnychowtheguy__kaijukaikai) |3.33 |3.0 |2.67 |3.5 |3.5 |2.83 |4.5 |
-|8 |[Chantey (Prologue)](https://hh.gbdev.io/game/gortyncode__chantey-prologue) |3.25 |2.83 |3.0 |3.67 |3.83 |4.5 |1.67 |
-|9 |[Slime Trials](https://hh.gbdev.io/game/canight__slime-trials) § |3.06 |3.83 |3.17 |3.67 |2.33 |2.83 |2.5 |
-|10 |[Hidden Gems](https://hh.gbdev.io/game/rebusmind__hidden-gems) |3.06 |4.0 |3.0 |4.5 |2.67 |3.0 |1.17 |
-|11 |[Abducted](https://hh.gbdev.io/game/grimmrobegames__abducted) § |2.94 |2.83 |3.17 |3.83 |3.83 |1.17 |2.83 |
-|12 |[Slayer the Hawk](https://hh.gbdev.io/game/mindbleach__slayer-the-hawk) |2.86 |2.67 |3.5 |4.17 |2.67 |1.5 |2.67 |
-|13 |[EXTERMINATOR](https://hh.gbdev.io/game/glitchytsp__exterminator) |2.83 |2.67 |2.67 |3.17 |3.0 |2.83 |2.67 |
-|14 |[Imperium Strike Force](https://hh.gbdev.io/game/orlando64__imperium-strike-force) |2.69 |3.17 |2.83 |2.33 |3.17 |2.67 |2.0 |
-|15 |[Lightseeker](https://hh.gbdev.io/game/zanipixels__lightseeker) |2.67 |2.17 |2.17 |3.0 |4.0 |3.17 |1.5 |
-
-Tiebreaks (4-5, 9-10) were explicitly resolved by judges for this leaderboard.
-
-Entries marked with † are awarded the "Best Games" prizes and ones with § get the "Best Open Source Games".
-
-#### Gameplay
-
-|# |Name |Score|
-|---------------------:|-----|-----|
-|1 | Feed IT Souls | 4.17|
-|2 | Hermano | 4.17|
-|3 | Hidden Gems | 4.0 |
-|4 | Slime Trials | 3.83|
-|5 | Enceladus | 3.5 |
-|6 | Ghost of the Arcade | 3.33|
-|7 | NUNYA | 3.33|
-|9 | Imperium Strike Force | 3.17|
-|10 | Kaiju Kai Kai | 3.0 |
-|11 | Abducted | 2.83|
-|12 | Chantey (Prologue) | 2.83|
-|13 | EXTERMINATOR | 2.67|
-|14 | Slayer the Hawk | 2.67|
-|15 | Lightseeker | 2.17|
-
-#### Technical
-
-|# |Name |Score|
-|---------------------:|-----|-----|
-|1 | Hermano | 4.0 |
-|2 | Feed IT Souls | 3.5 |
-|3 | Ghost of the Arcade | 3.5 |
-|4 | Slayer the Hawk | 3.5 |
-|5 | Slime Trials | 3.17|
-|6 | Abducted | 3.17|
-|7 | Enceladus | 3.0 |
-|8 | NUNYA | 3.0 |
-|10 | Hidden Gems | 3.0 |
-|11 | Chantey (Prologue) | 3.0 |
-|12 | Imperium Strike Force | 2.83|
-|13 | Kaiju Kai Kai | 2.67|
-|14 | EXTERMINATOR | 2.67|
-|15 | Lightseeker | 2.17|
-
-#### Originality
-
-|# |Name |Score|
-|---------------------:|-----|-----|
-|1 | Hidden Gems | 4.5 |
-|2 | Slayer the Hawk | 4.17|
-|3 | Enceladus | 3.83|
-|4 | Feed IT Souls | 3.83|
-|5 | Abducted | 3.83|
-|6 | Slime Trials | 3.67|
-|7 | Chantey (Prologue) | 3.67|
-|8 | Hermano | 3.5 |
-|9 | Kaiju Kai Kai | 3.5 |
-|10 | Ghost of the Arcade | 3.33|
-|12 | NUNYA | 3.17|
-|13 | EXTERMINATOR | 3.17|
-|14 | Lightseeker | 3.0 |
-|15 | Imperium Strike Force | 2.33|
-
-
-#### Graphics
-
-|# |Name |Score|
-|---------------------:|-----|-----|
-|1 | Hermano | 4.33|
-|2 | NUNYA | 4.33|
-|3 | Lightseeker | 4.0 |
-|4 | Feed IT Souls | 3.83|
-|5 | Abducted | 3.83|
-|6 | Chantey (Prologue) | 3.83|
-|7 | Enceladus | 3.67|
-|8 | Ghost of the Arcade | 3.67|
-|9 | Kaiju Kai Kai | 3.5 |
-|11 | Imperium Strike Force | 3.17|
-|12 | EXTERMINATOR | 3.0 |
-|13 | Hidden Gems | 2.67|
-|14 | Slayer the Hawk | 2.67|
-|15 | Slime Trials | 2.33|
-
-#### Audio Ranking
-
-|# |Name |Score|
-|---------------------:|-----|-----|
-|1 | Chantey (Prologue) | 4.5 |
-|3 | Hermano | 3.83|
-|4 | Enceladus | 3.67|
-|5 | Feed IT Souls | 3.67|
-|6 | NUNYA | 3.67|
-|7 | Ghost of the Arcade | 3.33|
-|8 | Lightseeker | 3.17|
-|9 | Hidden Gems | 3.0 |
-|10 | Slime Trials | 2.83|
-|11 | Kaiju Kai Kai | 2.83|
-|12 | EXTERMINATOR | 2.83|
-|13 | Imperium Strike Force | 2.67|
-|14 | Slayer the Hawk | 1.5 |
-|15 | Abducted | 1.17|
-
-#### Theme
-|# |Name |Score|
-|---------------------:|-----|-----|
-|1 | Enceladus | 4.83|
-|3 | Feed IT Souls | 4.5 |
-|4 | Kaiju Kai Kai | 4.5 |
-|5 | Hermano | 4.17|
-|6 | NUNYA | 4.0 |
-|7 | Ghost of the Arcade | 3.33|
-|8 | Abducted | 2.83|
-|9 | EXTERMINATOR | 2.67|
-|10 | Slayer the Hawk | 2.67|
-|11 | Slime Trials | 2.5 |
-|12 | Imperium Strike Force | 2.0 |
-|13 | Chantey (Prologue) | 1.67|
-|14 | Lightseeker | 1.5 |
-|15 | Hidden Gems | 1.17|
-
-
diff --git a/website/gbcompo25.md b/website/gbcompo25.md
deleted file mode 100644
index 53e38268..00000000
--- a/website/gbcompo25.md
+++ /dev/null
@@ -1,213 +0,0 @@
----
-head:
- - - meta
- - name: og:title
- content: GB Compo 2025
- - - meta
- - name: og:description
- content: >
- Join us for an epic game development event celebrating the classics! Show off your creativity and skills by crafting original games for the iconic Game Boy!
----
-
-# Game Boy Competition 2025
-
-
-
-
-
-> *gbcompo25* was a game jam run from **June 1st** to **September 1st**, 2025 and it was hosted on [itch.io/jam/gbcompo25](https://itch.io/jam/gbcompo25).
-
-#### Join us for an epic game development event celebrating the classics! Show off your creativity and skills by crafting original games, demos, homebrew tools or music specifically designed for the legendary Game Boy. Get ready to battle it out for fame, honor, and an incredible array of prizes that await the best entries.
-
-## Prizes and Categories
-
-The total prize pool of will be split according to [this document](https://docs.google.com/spreadsheets/d/e/2PACX-1vRTOM0XElTJJGF97GyxzL393SBuPAjW3woxYZo5Fs_X_tC8Yxq_hojVNw74XGeOFYHu0_5bwS3NyQLZ/pubhtml). Anybody can contribute by [donating](https://opencollective.com/gbdev/projects/gbcompo25).
-
-An entry can compete in one and only one of the following leaderboards:
-
-- Games (80% of the prize pool)
- - The best **5** games will get a prize.
- - The best **3** OSS games will get an **additional** prize (can overlap)
- - Examples: [Hermano](https://hh.gbdev.io/game/patmorita__hermano-game-boy), [Feed IT Souls](https://hh.gbdev.io/game/gumpyfunction__feed-it-souls), [Enceladus](https://hh.gbdev.io/game/mrpapshmir__enceladus)
-- Music cartridges (**1 prize**)
- - Examples: [8bit Sunset](https://hh.gbdev.io/game/kabcorp__8bit-sunset), [Tune In!](https://hh.gbdev.io/game/beatscribe__gb-compo-23-music-cart-tune-in)
-- Demoscene style (**1 prize**)
- - This category is for audio-visual demonstrations that run without viewer interaction, in such a way as to demonstrate skill in manipulation of the Game Boy hardware through code, special effects, art, music, and direction. More on [Wikipedia](https://en.wikipedia.org/wiki/Demoscene).
- - Examples: [Back to Color](https://hh.gbdev.io/game/back-to-color), [Is that a demo in your pocket?](https://hh.gbdev.io/game/is-that-a-demo-in-your-pocket), [Cute Demo CGB](https://hh.gbdev.io/game/cute-demo-cgb).
-- Tool (**1 prize**)
- - Examples: [Monster Orcarina](https://hh.gbdev.io/game/pearacidic__monster-orc-arina-a-game-boy-tool), [On-Board GB Tile Creator](https://hh.gbdev.io/game/obgbtc), [Witch Sound Tracker](https://hh.gbdev.io/game/djarky__witch-sound-tracker).
-
-The rankings are decided according to the [judging](#judging) criteria.
-
-## Community
-
-Come join us on Discord for help getting started (or find a team), and head over to gbdev.io for a curated list of links and development resources!
-
-We'd love to hear from participants, share your progress using the `#gbcompo25` hashtag and join the conversation on the Discord `#gbcompo25` channel!
-
-We're also on [BlueSky](https://bsky.app/profile/gbdev.io) and [Mastodon](https://mastodon.social/@gbdev).
-
-## Resources
-
-Here is a curated list tools and documentation to get you started.
-
-- General links
- - [The Game Boy, a hardware autopsy](https://www.youtube.com/playlist?list=PLu3xpmdUP-GRDp8tknpXC_Y4RUQtMMqEu) (Video)
- - [The Ultimate Game Boy Talk](https://media.ccc.de/v/33c3-8029-the_ultimate_game_boy_talk) (Video)
- - [Pan Docs](https://gbdev.io/pandocs), Game Boy technical documentation
- - [Awesome Game Boy Development list of resources](https://gbdev.io/resources.html)
- - Especially relevant is the ["Software Development"](https://gbdev.io/resources.html#software-development) section
- - [How to choose between C and ASM for GB?](https://gbdev.io/guides/tools.html#languages)
-- [RGBDS](https://rgbds.gbdev.io/) (ASM language)
- - [GB ASM Tutorial](https://gbdev.io/gb-asm-tutorial/index)
-- [GBDK](https://github.com/gbdk-2020/gbdk-2020/) (C language)
- - [API docs: Getting Started](https://gbdk-2020.github.io/gbdk-2020/docs/api/docs_getting_started.html)
- - [Examples](https://github.com/mrombout/gbdk_playground)
- - [Docs, links and tools](https://gbdk-2020.github.io/gbdk-2020/docs/api/docs_links_and_tools.html)
- - [ZGB: a game engine using GBDK](https://github.com/Zal0/ZGB) / [CrossZGB](https://github.com/gbdk-2020/CrossZGB) (an updated fork of ZGB)
- - [Dedicated Discord](https://discord.gg/XCbjCvqnUY)
- - ["How-to make a Gameboy Game"](https://laroldsretrogameyard.com/category/tutorials/gb/) tutorial series
-- [GBStudio](https://www.gbstudio.dev/) (no coding required)
- - [Resources to get started](https://gbstudiocentral.com/resources/)
- - [Dedicated Discord](https://discord.gg/knRryZWGcm)
-- [GBSDK](https://github.com/daid/gbsdk) (C + RGBDS ASM, without a standard library)
- - [Documentation](https://daid.github.io/gbsdk/)
- - [Source for a game](https://github.com/QuinnPainter/CrossConnect) and [another game](https://github.com/QuinnPainter/Wyrmhole) built with GBSDK
-
-## Sponsors
-
-
-
-
-
-## Rules
-
-### JAM THEME: None (do whatever you want!)
-
-### Prizes and Judging
-
-1. Multiple submissions are allowed from the same person or team. They will be rated separately, but each person or team may only accept prizes for a single entry (e.g., you can't win both the 1st and 2nd place prize).
-2. Judges can submit entries, but the entries won't be eligible for prizes. Judges won't score the entries they submitted.
-3. Source code is optional, but very much appreciated. Entries submitted with source code (public repositories on GitHub/GitLab/... are accepted) and with an open source license (See rule 4) are eligible for **additional** prizes. Make sure to fill the "Open Source repository" field with a valid and public repository when submitting on itch.io. It is NOT necessary for the source to be available *during* the competition period. Repositories/sources just need to be public from the time the event ends. See [this issue](https://github.com/gbdev/gbcompo21/issues/3) for further details.
-4. To be eligible for the OSS bonus, the code must be licensed under an "Open Source" license (any of the "Free" license listed [here](https://www.gnu.org/licenses/license-list.en.html) is accepted). Assets can be licensed under any of the CC licenses. Obfuscated code, non-reproducible builds, intentionally making the source code hard to read/re-use will exclude the entry form the OSS leaderboards. This is at discretion of the hosts and judges. We must be able to read the code and compile it into a working ROM, running a script or following instructions.
-5. If your submission contains text that isn't available in English, some judges might not be able to read that, and they will judge accordingly.
-6. Shipment of physical prizes may not be available to all countries, they are subject to shipping ability of the prize donors.
-7. The submitted entry must implement / interpret the jam theme (applies to Game category only).
-
-### Hardware Criteria
-
-8. Final entry "deliverable" must consist of a GB or GBC ROM file. You can also attach descriptive documents in TXT, Markdown, PDF or HTML file format (e.g. game manual).
-9. The ROM file will be tested on real hardware (primarily a GBC) and on a hardware accurate emulator such as BGB and SameBoy. In case of different results, the hardware test will be the trusted one.
-10. GBC features are allowed, and entries may be GBC only (GBC only should be indicated in game for non-GBC hardware or on the entry's page).
-11. SGB features are allowed. The entries will be evaluated mainly on Game Boy Color. You can use SGB features, but don't rely on them as main features for your entry.
-12. Only some official MBC chips (MBC1, MBC2, MBC3 and MBC5, as well as no MBC at all) are allowed. The only exceptions are:
- 1. While Nintendo only manufactured very specific MBC+ROM combinations, we will allow any power-of-two ROM size between 32 KiB and the maximum for the chosen chip. (This is simply a convenience feature, as any ROM image can be extended to fit a larger ROM chip by appending copies of itself.)
- 2. The ROM+RAM combination (i.e., without an MBC, up to 32 KiB ROM and 8 KiB SRAM), with or without battery, will be accepted for compatibility, as many people have simple flash carts that do have SRAM but don't have an MBC. (This can be trivially emulated by any MBC1, MBC3 or MBC5 configuration.)
- 3. The variants MBC30 and MBC1M are accepted, as they have been used in licensed games.
- 4. Some official MBCs added extra components to the cart: MBC3/MBC30 carts could have an RTC, and MBC5 carts could have rumble. These combinations, and only these combinations, will be accepted. In particular, this means it's impossible to have RTC and rumble in the same cart.
-13. Entries cannot rely on extra hardware or add-on devices, they must be playable without a specific setup.
-14. As long as you produce a GB or GBC ROM file that runs on GB/GBC, any tool to develop the entry is allowed. This includes RGBDS (ASM), GBDK (C), ZGB (C), GB Studio, ...
-
-### General Criteria
-
-15. Teams are allowed (prizes will be split).
-16. ROM Hacks are not allowed.
-17. NSFW content is not allowed. Please keep it safe for work and avoid anything overly edgy or distasteful.
-18. The entry must be submitted on the itch.io jam page.
-19. Your work must be new and original. You cannot enter with something you were already working on before the start of the jam. The majority of work should be done during the jam. If in doubt, please ask us before proceeding.
-
- Examples of things that are **allowed:**
-
- - A brand new game using your own (or any) existing game engine.
- - A remake of your (or someone else's) game for a different platform, but with new code and assets.
- - A brand new game you barely started (e.g. you only made a title screen or a mockup of the gameplay).
- - Using templates, tutorial code as starting point (when they're license allow to do so, e.g. [gb-boilerplate](https://github.com/ISSOtm/gb-boilerplate))
- - Pre-made and AI generated assets for art and music (and covers of other music) are allowed if the licensing permits. Be sure to make note of what is being reused or re-interpreted and under what terms. There is a specific field in the submission form to allow you to disclose this. Use of pre-made and AI generated assets and content (vs wholly original and human created) may be factored in when judges assess points for relevant criteria (e.g. graphics, audio).
-
- Examples of things that are **not allowed:**
-
- - A project you've been working on for months.
- - An update/patch for a game you already released publicly.
-
-20. The submission must be available for free for the public (and not only the judges). Submission will be published and kept online for free on the competition website, while you are free to keep working on it (and eventually charge for it/make commercial usage).
-21. It is allowed to submit an entry to multiple events.
-22. It is permitted to use the 'gbcompo25 logo' graphics to identify the entry as a submission to this competition (e.g. as an intro splash screen). Minor modifications and alterations (color palette, size, smaller resolution) to fit the medium are permitted.
-23. Before submitting, make sure you've read the rules. Our resources are limited - low effort, non-compliant or spammy submissions may lead to exclusion from this event and future ones.
-
-## Judging
-
-A team of (~10) judges has been selected before the start of the competition.
-
-Every judge will be asked to compile an unranked shortlist of their favourite entries. Once the shortlists are ready from all the judges, the top 15 most selected entries will be evaluated by the judges on the following criteria:
-
-1. **Gameplay** - How entertaining are the gameplay mechanics? How fun is the game?
-2. **Technical** - How innovative is it from a technical perspective? Does it push the hardware to the limit?
-3. **Originality** - How refreshing is it from a non-technical perspective? Does it have a unique design / mechanics?
-4. **Graphics** - How impressive are the art, animation and visual effects?
-5. **Audio** - How good are the music and sound effects?
-
-Applicability may vary depending on the category of the entry (e.g. a "Music" ROM may be evaluated only on 5)-
-
-The final leaderboard is computed by averaging the "Overall" score.
-
-::: warning
-Judges are volunteers. Those details can change without prior notice, as the judging process is long and involves hundreds of entries. Detailed criteria leaderboards will be provided only for entries in the final shortlist.
-
-:::
-
-## Acknowledgements
-
-Special thanks to everyone who helped organising and running this event:
-
-**Partners**: gbdev.io, LaroldsJubilantJunkyard
-
-**Organisers**: avivace, bbbbbr
-
-**Judges**: Maithonis, DUO, Fei, Veund, David Couzelis, Sanqui, j0nas, RetroBreak, Ferrante
-
-
-## Results
-
-### Games
-
-| # | Name | Overall | Gameplay | Technical | Originality | Graphics | Audio |
-|----:|:---------------------------|----------:|-----------:|------------:|--------------:|-----------:|--------:|
-| 1 | **OrmBlok** † | 3.84 | 4.00 | 3.67 | 4.22 | 3.22 | 4.11 |
-| 2 | **Fortune & Glory** † § | 3.82 | 4.22 | 3.22 | 3.22 | 4.78 | 3.67 |
-| 3 | **Potion Panic** † § | 3.49 | 3.33 | 3.89 | 3.33 | 3.78 | 3.11 |
-| 4 | **DropBlox!** † | 3.47 | 3.89 | 3.67 | 4.11 | 2.67 | 3.00 |
-| 5 | **Game of Life Kuzushi** † | 3.42 | 3.67 | 3.78 | 4.11 | 2.44 | 3.11 |
-| 6 | Sheep Split | 3.40 | 3.67 | 2.56 | 3.78 | 3.56 | 3.44 |
-| 7 | PocketWare | 3.36 | 3.44 | 3.33 | 2.78 | 3.67 | 3.56 |
-| 8 | Cosmo Command | 3.33 | 3.00 | 2.67 | 3.11 | 4.22 | 3.67 |
-| 9 | CLEAVING CALIBER EX | 3.22 | 3.33 | 3.11 | 3.56 | 4.11 | 2.00 |
-| 10 | FUN VIDEO STORE | 3.16 | 2.44 | 2.44 | 3.56 | 4.00 | 3.33 |
-| 10 | The Sweeping Wizard | 3.16 | 3.56 | 3.11 | 3.56 | 2.56 | 3.00 |
-| 10 | Abyss | 3.16 | 3.00 | 2.22 | 3.44 | 4.11 | 3.00 |
-| 14 | Ningami 2 | 3.11 | 3.78 | 3.00 | 3.22 | 2.67 | 2.89 |
-| 15 | Mount Crymore | 3.00 | 3.89 | 2.67 | 2.22 | 2.89 | 3.33 |
-| 16 | Peasant Republic | 2.98 | 2.89 | 2.67 | 2.89 | 3.22 | 3.22 |
-| 17 | PuzzleBoy: Logic Quest | 2.13 | 3.00 | 3.00 | 1.44 | 2.00 | 1.22 |
-
-Entries marked with **†** are awarded the "Best Games" prizes and the ones with **§** get the "Best Open Source Games".
-
-### Tools
-
-1. **Tomato-do** †
-2. GBC Pip-Boy
-3. GB Tile Editor
-4. Still Got the Blues
-
-### Music
-
-1. **Neon Nova** †
-2. **DMG Keygen** †
-
-### Demoscene
-
-No prize was awarded in the demoscene category due to lack of qualifying entries.
-
-## Past editions
-
-- [GB Competition 2021](https://itch.io/jam/gbcompo21)
-- [GB Competition 2023](https://itch.io/jam/gbcompo23)
diff --git a/website/guides/asmstyle.md b/website/guides/asmstyle.md
deleted file mode 100644
index a0a375f8..00000000
--- a/website/guides/asmstyle.md
+++ /dev/null
@@ -1,85 +0,0 @@
----
-head:
- - - meta
- - name: og:title
- content: GB ASM style guide
- - - meta
- - name: og:description
- content: This style guide aims to formalize a style that most Game Boy ASM programmers agree on, and provide a good baseline for new programmers just starting in this field.
----
-
-# Game Boy ASM style guide
-
-Written by [ISSOtm](https://github.com/ISSOtm/)
-
----
-
-This style guide aims to formalize a style that most Game Boy ASM programmers agree on, and provide a good baseline for new programmers just starting in this field. (If that's you, welcome! :D)
-
-To quote the [Linux kernel style guide](https://github.com/torvalds/linux/blob/master/Documentation/process/coding-style.rst):
-
-> Coding style is very personal, and I won't **force** my views on anybody, but this is what goes for anything that I have to be able to maintain, and I'd prefer it for most other things too. Please at least consider the points made here.
-
-Many people follow alternate style guides, and that's fine; but if you're starting to code in ASM, a clean style goes a long way to keep your code organized. Again: you don't have to do everything listed here, but please at least consider the reasons behind each bullet point.
-
-Oh, by the way, you're free to [contribute to this document](https://github.com/gbdev/gbdev.github.io) and/or [chat with us about it](https://gbdev.io/chat)!
-
-## Naming
-
-[RGBASM accepts a lot of symbol names](https://rgbds.gbdev.io/docs/v0.4.2/rgbasm.5#SYMBOLS):
-
-> Symbol names can contain letters, numbers, underscores ‘_’, hashes ‘#’ and at signs ‘@’. However, they must begin with either a letter, or an underscore.
-
-However, naming conventions make code easier to read, since they help convey the different semantics between each symbol's name.
-
-- Labels use PascalCase: `DrawNPCs`, `GetOffsetFromCamera`.
-- Labels in RAM (VRAM, SRAM, WRAM, HRAM; you shouldn't be using Echo RAM or OAM) use the same convention but are prefixed with the initial of the RAM they're in, in lowercase: `wCameraOffsetBuffer`, `hVBlankFlag`, `vTilesetTiles`, `sSaveFileChecksum`. *Rationale: to know in which memory type the label is; this is important because VRAM and SRAM have special access precautions and HRAM can (should (must)) be accessed using the `ldh` instruction.*
-- Local labels use camelCase, regardless of memory type: `.waitVRAM`, `wPlayer.xCoord`.
-- Macro names use snake_case: `wait_vram`, `end_struct`.
-- Constants use CAPS_SNAKE: `NB_NPCS`, `OVERWORLD_STATE_LOAD_MAP`.
-
- Exception: constants that are used like labels should follow the label naming conventions. For example, see [hardware.inc](https://github.com/gbdev/hardware.inc/blob/master/hardware.inc)'s `rXXX` constants.
-
-## Best practices
-
-- Avoid hardcoding things. This means:
- * No magic numbers. `ld a, CURSOR_SPEED` is much more obvious than `ld a, 5`. In addition, if you ever change your mind and decide to change the cursor speed, you will only need to do so in one location (`CURSOR_SPEED equ 5` → `CURSOR_SPEED equ 4`) instead of at every location you're using it, potentially missing some.
- * Unless **absolutely necessary**, don't [force a `SECTION`'s bank](https://rgbds.gbdev.io/docs/v0.4.2/rgbasm.5#BANK) or address. This puts the burden of managing ROM space on you, instead of offloading the job to RGBLINK, which performs very well in typical cases. Exceptions:
- - Your ROM's entry point [must be at $0100](https://gbdev.io/pandocs/#_0100-0103-entry-point), however the jump does not have to be to $0150 ([example](https://github.com/GreenAndEievui/vuibui-engine/blob/206fd814e67da2cebbeca7d011a5537fef22a29c/src/main.asm#L6)).
- - [`rst` vectors and interrupt handlers](https://gbdev.io/pandocs/#jump-vectors-in-first-rom-bank) obviously need to be at the corresponding locations.
- - [RGBDS presently does not allow forcing different sections to be in the same bank](https://github.com/gbdev/rgbds/issues/244). If you need to do so, the ideal fix is to merge the two sections together (either by moving the code, or using [`SECTION FRAGMENT`](https://rgbds.gbdev.io/docs/v0.4.2/rgbasm.5#Section_Fragments)), but if that option is unavailable, the only alternative is to explicitly declare them with the same `BANK[]` attribute. (In which case it's advisable to add an `assert BANK("Section A") == BANK("Section B")` line.)
-
- If you need some alignment, prefer [`ALIGN[]`](https://rgbds.gbdev.io/docs/v0.4.2/rgbasm.5#ALIGN) to forcing the address. A typical example is [OAM DMA](https://gbdev.io/pandocs/#lcd-oam-dma-transfers); for that, prefer `SECTION "Shadow OAM", WRAM0,ALIGN[8]` over e.g. `SECTION "Shadow OAM", WRAM0[$C000]`.
-
-- Allocate space for your variables using [labels](https://rgbds.gbdev.io/docs/v0.4.2/rgbasm.5#SYMBOLS) + [`ds` & co](https://rgbds.gbdev.io/docs/v0.4.2/rgbasm.5#Declaring_variables_in_a_RAM_section) instead of [`equ`](https://rgbds.gbdev.io/docs/v0.4.2/rgbasm.5#EQU). This has several benefits:
- * Removing, adding, or changing the size of a variable that isn't the last one doesn't require updating every variable after it.
- * The size of each variable is obvious (`ds 4`) instead of having to be calculated from the addresses.
- * `equ` allocation is equivalent to hardcoding section addresses (see above), whereas labels are placed automatically by RGBLINK.
- * Labels support [`BANK()`](https://rgbds.gbdev.io/docs/v0.4.2/rgbasm.5#Other_functions) and many cool other features!
- * Labels are output in [`map` and `sym`](https://rgbds.gbdev.io/docs/v0.4.2/rgblink.1#m) files.
-
-- If a file gets too big, you should split it. Files too large are harder to read and navigate. However, the splitting should stay coherent and consistent; having to jump around files constantly is equally as hard to read and navigate.
-
-- [Unless you're making a 32k ROM](https://gbdev.io/pandocs/#no-mbc), put things in [`ROMX`](https://rgbds.gbdev.io/docs/v0.4.2/rgbasm.5#ROMX) by default. `ROM0` space is precious, and can deplete quickly; and when you run out, it's difficult to move things to ROMX.
-
- However, if you have code in ROM bank A refer to code or data in ROM bank B, then either should probably be moved to ROM0, or both be placed in the same bank (options for that are mentioned further above). [`farcall`](https://github.com/pret/pokecrystal/blob/35219230960f0dc85c0cb6a5723877b247609e46/macros/rst.asm#L1-L5) is a good way to make your code really [spaghetti](https://en.wikipedia.org/wiki/Spaghetti_code).
-
-- Don't clear RAM at init! Good debugging emulators will warn you when you're reading uninitialized RAM ([BGB](https://bgb.bircd.org) has one in the option's Exceptions tab, for example), which will let you know that you forgot to initialize a variable. Clearing RAM does not fix most of these bugs, but silences the helpful warnings.
-
- Also, a lot of the time, variables need to get initialized to values other than 0, so clearing RAM is actually counter-productive in these cases.
-
-## Recommendations
-
-The difference between these and the "best practices" above is that these are more subjective, but they're still worth talking about here.
-
-- Historically, RGBDS has required label definitions to begin at "column 1" (i.e. no whitespace before them on their line). However, later versions (with full support added in 0.5.0) allow [indenting labels](https://github.com/pinobatch/libbet/blob/cabe48bc4042338b9975cb32c2dbd0ee6640f31e/src/main.z80#L206-L231), for example to make loops stand out like in higher-level languages. However, [a lot of people don't do this](https://github.com/gbdev/DeadCScroll/blob/9834372eb0d56e8b9a8cdcaae4b8aecb6d402266/DeadCScroll.asm#L410-L422), so it's up to you.
-
-- Please use the `.asm` (or `.s`) file extensions, not `.z80`. The GB CPU isn't a Z80, so syntax highlighters get it *mostly* right, but not quite. And it helps spreading the false idea that the GB CPU is a Z80. :,(
-
-- Compressing data is useful for several reasons; however, it's not necessary in a lot of cases nowadays, so you may want to only look at it after more high-priority aspects.
-
-- Avoid abusing macros. Macros tend to make code opaque and hard to read for people trying to help you, in addition to having side effects and sometimes leading to very inefficient code.
-
-- Never let the hardware draw a corrupted frame even if it's just one frame. If it's noticeable by squinting a bit, it must go.
-
-- [Makefiles are bae](https://www.gnu.org/software/make/manual/html_node/); they speed up build time by not re-processing what hasn't changed, and they can automate a lot of tedium. Writing a good Makefile can be quite daunting, but [gb-boilerplate](https://github.com/ISSOtm/gb-boilerplate) and [gb-starter-kit](https://github.com/ISSOtm/gb-starter-kit) can help you get started faster.
diff --git a/website/guides/deadcscroll.md b/website/guides/deadcscroll.md
deleted file mode 100644
index 5d930e00..00000000
--- a/website/guides/deadcscroll.md
+++ /dev/null
@@ -1,380 +0,0 @@
----
-head:
- - - meta
- - name: og:title
- content: Dead C Scroll tutorial
- - - meta
- - name: og:description
- content: An assembly tutorial for Game Boy showing how the scroll registers can be exploited to create some nice and interesting effects.
----
-
-# Dead C Scroll
-
-Written by Bob.
-
----
-
-An assembly tutorial for Game Boy showing how the scroll registers can be exploited to create some nice and interesting effects.
-
-Files related to this tutorial can be found [here](https://github.com/gbdev/gbdev.github.io/tree/dev/website/.vuepress/public/deadcscroll).
-
-## Introducing the registers
-
-### SCY ($FF42)/SCX ($FF43)
-
-The `SCY`/`SCX` registers have a simple purpose: specify the coordinate of the screen's top-left pixel (or view, if you prefer) somewhere on the 256x256 pixel background map. This is really handy for certain kinds of games like platformers or top-down racing games (though there are LOTS of other kinds of games that benefit from this) where the view is the 'camera' and its position is set once per frame.
-
-When you don't require scrolling, and when your cart boots, `SCY`/`SCX` is typically set to 0,0. When a screen is displayed, it appears normally even though you only set the values once. This is because as the screen draws, the PPU automatically adds the value in `LY` ($FF44) to the value in `SCY` in order to know what row of pixels to draw.
-
-```:no-line-numbers
-SCY value (set once)
-│
-│ screen
-│ ┌───────┐
-└─$00 │Line 0 │ VRAM row $00 ($00+$00) is displayed
- │Line 1 │ VRAM row $01 ($00+$01) is displayed
- │Line 2 │ VRAM row $02 ($00+$02) is displayed
- │Line 3 │ VRAM row $03 ($00+$03) is displayed
- │Line 4 │ VRAM row $04 ($00+$04) is displayed
- │... │ ...
- │ │
- └───────┘
-```
-If `SCY` = $20 (for example):
-```:no-line-numbers
-SCY value (set once)
-│
-│ screen
-│ ┌───────┐
-└─$20 │Line 0 │ VRAM row $20 ($20+$00) is displayed
- │Line 1 │ VRAM row $21 ($20+$01) is displayed
- │Line 2 │ VRAM row $22 ($20+$02) is displayed
- │Line 3 │ VRAM row $23 ($20+$03) is displayed
- │Line 4 │ VRAM row $24 ($20+$04) is displayed
- │... │ ...
- │ │
- └───────┘
-```
-
-You can take advantage of how the PPU renders the screen by setting these registers *as the screen draws*. If you do this, you can create some interesting 'raster' effects that are presented here.
-
-As an example, let's say you wanted to triple line 0 and show it for line 0, line 1, and line 2, and then continue with line 3. You would write to the `SCY` register like so:
-```:no-line-numbers
-SCY value (set once per line)
-│
-│ screen
-│ ┌───────┐
-├─$00 │Line 0 │ VRAM row $00 ($00+$00) is displayed
-├─$FF │Line 1 │ VRAM row $00 ($FF+$01) is displayed
-├─$FE │Line 2 │ VRAM row $00 ($FE+$02) is displayed
-├─$00 │Line 3 │ VRAM row $03 ($00+$03) is displayed
-├─$00 │Line 4 │ VRAM row $04 ($00+$04) is displayed
-├─... │... │ ...
- │ │
- └───────┘
-```
-
-When setting values for `SCY`, you need to remember that `LY` always (and automatically) increments, so you have to account for that in your new `SCY` value. You can write anything to `SCX`; that's not affected by the hardware so you don't have to adjust the value like you need to for `SCY`.
-
-> Note: The scroll registers only affect background rendering. They do not change how objects are displayed.
-
-## Implementation
-
-There are three main states that drive the display on the Game Boy: the Horizontal Blank (HBlank), the Vertical Blank (VBlank), and drawing. The HBlank starts when a line of pixels is completely drawn. There is an opportunity to do some work\* before the next line of pixels starts drawing, and there is one HBlank for every line, all the way down the screen.
-
-> \**The exact amount of time you have depends on several things; most notably how many objects are being drawn on that line. The [PanDocs](https://gbdev.io/pandocs/#pixel-fifo) has a detailed explanation of the timing. (Indeed, read that entire document because it's great!)*
-
-When all of the lines are completely drawn, the VBlank starts. This interval is always 10 lines high so there is much more time to do some work compared to the HBlank. The VBlank is only secondary to this system though; the focus is the HBlank since we want to change the screen as it draws. The problem that needs to be solved is reliably knowing what value to set for a specific line.
-
-As previously mentioned, there is a small amount of time that HBlanks give you to do work. This means that the handler has to be as fast as possible. On a limited system like the Game Boy, that usually equates to judicious use of table lookups and buffers.
-
-There are two key elements to make this system very stable and very fast:
-1. A double-buffering system that holds the data that feeds the HBlank handler
-2. How the buffers are arranged
-
-### Double Buffering
-
-The idea of the double-buffer is that while one buffer is being used by the hardware to draw the screen, you modify (fill) values in the other. When the screen is done drawing, you switch buffers so the one you were just modifying is being used for drawing and you start modifying the other.
-
-While the Draw Buffer (A) is used to render the screen, you change values in the Fill Buffer (B).
-```:no-line-numbers
-┌───────┐ ┌───────┐
-│Draw │ │Fill │
-│Buffer │ │Buffer │
-│ │ │ │
-│ │ │ │
-│ │ │ │
-│ │ │ │
-│A │ │B │
-└───────┘ └───────┘
-```
-
-When the screen is done being drawn (and you know this because the VBlank interrupt would have triggered or the value in `LY` changed to 144), you switch the buffers.
-```:no-line-numbers
-┌───────┐ ┌───────┐
-│Fill │ │Draw │
-│Buffer │ │Buffer │
-│ │ │ │
-│ │ │ │
-│ │ │ │
-│ │ │ │
-│A │ │B │
-└───────┘ └───────┘
-```
-
-Here, "switch buffers" means to switch the *purpose* of each buffer. It doesn't mean to copy buffers. Remember, we need this to be as fast as possible so to change buffers, you simply change pointers:
-```:no-line-numbers
-Draw-->┌───────┐ Fill-->┌───────┐
-Ptr │Buffer │ Ptr │Buffer │
- │A │ │B │
- │ │ │ │
- │ │ │ │
- │ │ │ │
- │ │ │ │
- │ │ │ │
- └───────┘ └───────┘
-```
-Becomes:
-```:no-line-numbers
-Fill-->┌───────┐ Draw-->┌───────┐
-Ptr │Buffer │ Ptr │Buffer │
- │A │ │B │
- │ │ │ │
- │ │ │ │
- │ │ │ │
- │ │ │ │
- │ │ │ │
- └───────┘ └───────┘
-```
-
-#### Buffer Size
-
-The size of each buffer (indeed, any buffer) depends on two things:
-- how many elements are needed
-- how much data is needed per element
-
-We know that the buffers exist to support the HBlank handler, so the number of elements in the buffer are however many times the HBlank can trigger. We said earlier that the HBlank starts at the end of every screen line, so the number of elements is at least that many. However, remember *when* the HBlank starts: at the *end* of every line. What do we do if we need to change the 0th line (before *any* line has started drawing)? Well, we need to change that value *before* line 0 starts, which means it has to be done in the VBlank. And **that** means we need one more element. In short, we need the height of the screen plus one (144+1=145) elements in each buffer.
-
-This tutorial is only concerned with the scroll registers, so it only needs to store 2 values per line: one for `SCY` and one for `SCX`. (You can store more data per line, of course, but this tutorial doesn't require it.)
-
-In summary: each buffer is 145 2-byte elements (290 bytes), and we need two of them, so the total buffer memory size is 580 bytes.
-
-#### Location in Memory
-
-Assume for a moment that you put the buffers physically next to each other in memory. For example, Buffer A is at `$C000` and Buffer B is at `$C122` (the buffer size is 290 bytes). We said earlier that in order to swap buffers, we just swap pointers, so the code that does that might look like this:
-
-```asm
-; assume the pointers are next to each other in memory
-wDrawBuffer: DS 2 ; buffer currently being drawn
-wFillBuffer: DS 2 ; buffer currently being modified
-
-; swap the contents of each pointer (28 cycles)
-ld hl,wDrawBuffer
-ld a,[hl+]
-ld b,[hl]
-ld c,a ; bc = contents of wDrawBuffer
-inc hl
-ld a,[hl+] ; a = LOW(contents of wFillBuffer)
-ld d,[hl] ; d = HIGH(contents of wFillBuffer)
-ld hl,wDrawBuffer
-ld [hl+],a
-ld [hl],d
-inc hl
-ld a,c
-ld [hl+],a
-ld [hl],b
-```
-To use a pointer, that code looks like this:
-```asm
-; use a pointer (8 cycles)
-ld hl,wFillBuffer
-ld a,[hl+]
-ld h,[hl]
-ld l,a ; hl = contents of wFillBuffer ($C000 or $C122)
-```
-
-You could certainly implement the system like this, but there is a way to gain some efficiency when swapping buffers and even with the actual pointers themselves.
-
-Consider this: other than the memory locations, the buffers are identical. Since we're only really concerned with pointers, *where* the buffers reside in memory doesn't really matter. This can be exploited (and optimized!)
-
-We can keep Buffer A at `$C000`. The buffer size is `$122` bytes, but instead of putting Buffer B at `$C122`, what if we put it at `$C200`? This would make the pointer values `$C000` and `$C200`. Literally a 1-bit difference. This, too, can be exploited! Both pointers end in `$00` so we don't need to store those, which saves 2 bytes. This leaves us with two 1-byte 'pointers': `$C0` and `$C2`.
-
-To swap the pointers, literally just one bit has to be toggled:
-```asm
-; swap the contents of each 'pointer' (11 cycles)
-ldh a,[hFillBuffer]
-ldh [hDrawBuffer],a
-xor $02
-ldh [hFillBuffer],a
-```
-And to use a pointer, we only need to do this:
-```asm
-; use a 'pointer' (6 cycles)
-ldh a,[hFillBuffer]
-ld h,a
-ld l,0 ; hl = contents of hFillBuffer ($C000 or $C200)
-```
-
-You'll notice that the name of the pointers have changed. This is because they were moved into HRAM. (Also notice that they don't have to be next to each other in memory.) They were moved to HRAM for a couple of reasons: it allows an optimization in the swapping code (11 cycles vs 28), and it makes the use code slightly faster. There are only 2 bytes used now so that is a better candidate for moving to HRAM than 4 bytes.
-
-### VBlank
-
-In this system, code in the VBlank is responsible for two things:
-- swapping the pointers
-- setting the data for line 0
-
-We've already seen what swapping the pointers looks like, but how is the data set for line 0? We need to emulate an HBlank handler running for "line -1" by getting the start of the new draw buffer and setting the scroll registers with the first data pair:
-
-```asm
-ldh a,[hDrawBuffer]
-ld h,a
-ld l,0
-
-; set the scroll registers
-ld a,[hl+]
-ldh [rSCY],a
-ld a,[hl+]
-ldh [rSCX],a
-```
-It's convenient that the scroll register addresses are next to each other. The data in the buffer is in the same order so as you can see in the code fragment above, this makes writing simple.
-
-### HBlank Handler
-
-In an HBlank handler, **every cycle counts**! So don't do any work in there unless it's absolutely necessary. This is a good target for hyper-optimizations -- especially if you are changing VRAM (like palettes) -- so one should design around that optimization.
-
-```asm
-HBlankHandler::
- push af
- push hl
-
- ; obtain the pointer to the data pair
- ldh a,[rLY]
- inc a
- add a,a ; double the offset since each line uses 2 bytes
- ld l,a
- ldh a,[hDrawBuffer]
- adc 0
- ld h,a ; hl now points to somewhere in the draw buffer
-
- ; set the scroll registers
- ld a,[hl+]
- ldh [rSCY],a
- ld a,[hl+]
- ldh [rSCX],a
-
- pop hl
- pop af
- reti
-```
-
-Notice that we can take advantage of the fact that there is only 2 bytes per line. We can use `LY` directly and quickly turn it into pointer. (Thanks to rondnelson99 for pointing this out!)
-
-### Use the fill buffer
-
-And there you have it. An automatic and stable way to take advantage of the HBlank to do whatever your imagination wants to do!
-
-All you need to do is set the fill buffer while the draw buffer is being displayed (you have an entire frame's worth of time to do this) and the system does the rest!
-
-## Effects
-
-### X (Horizontal) Sine
-
-This effect uses a sine table to shift each line in a pleasant way. There are 3 states to this effect:
-- The image is stable and a progression line moves up the screen starting each line on its way
-- The table cycles a few times
-- The image stability is restored with the progression line moving up the screen
-
-The values in the table can dramatically change the effect. For example, if the sine cycle was short enough, you could simulate a smoke effect (for example). Try it out!
-
-Also, you could create a 'glitch' effect during a cut-scene, perhaps in a sci-fi game to simulate a slightly dirty transmission.
-
-
-
-### Y (Vertical) Sine
-
-This effect is structured very similar to X Sine, in that there is a table of sine values driven by 3 states. The only difference is that `SCY` is changed instead of `SCX`.
-
-This is a really good way to simulate water reflections.
-
-
-
-### X and Y Sine
-
-This is simply a combination of the X Sine and Y Sine effects so you can see how different it looks compared to just the X or Y changing.
-
-Instead of a full-screen image like this tutorial uses, imagine if you had a repeating image in VRAM (bigger than the screen) that looked like water ripples. This would move just like water!
-
-
-
-### Smear On
-
-This is like a flood fill effect used as an appearance transition. It's quite simple in that it repeats the lines to achieve the 'smear' effect and is perhaps more interesting than a fade in.
-
-The specific image used in the tutorial is light along the bottom so it looks better if the screen was already light before the effect starts. You would change this to suit your image.
-
-
-
-### Smear Off
-
-This is a disappearance transition and the reverse of Smear On. Due to the specific image that was used (i.e. it is light along the bottom), it looks better in this tutorial to have the effect reveal a light screen instead of dark. Again, you would change this to suit your image.
-
-
-
-### Roll On
-
-This effect simulates an image unrolling onto the screen. This might be useful for fantasy RPGs to transition to a map screen or perhaps a message written on a scroll. The image unrolls over a dark screen because the top of the image is mostly dark so it looks better to keep it dark than the contrast of using a light screen.
-
-
-
-### Roll Off
-
-This effect simulates an image rolling off screen. This might be useful for fantasy RPGs to transition away from a map or scroll screen. This reveals a dark screen because the first thing you see in the roll is dark (because that's what's in VRAM below the screen). Keeping it dark made the transition more seamless.
-
-
-
-The roll effects look complicated but the implementation is probably one of the simpler ones. The key to make this look good is the values in the table. The roll size is 32 pixels, but you can change this to whatever size you want, provided the table values support it. This [SpecBas demo](https://www.youtube.com/watch?v=j04TKI9WKfo) was used as a reference to obtain those values.
-
-## How to build
-
-A GNU makefile is included. You will have to tailor it for your development environment but it builds cleanly with [RGBDS](https://github.com/gbdev/rgbds) 0.4.2. The only dependency is [`hardware.inc`](https://github.com/gbdev/hardware.inc). All of the effects are shown [here](https://github.com/BlitterObjectBob/ScrollexY#effects) so you don't have to build first to see them.
-
-## Notes about the code
-
-To reduce dependencies, everything is in one .asm file. It's structured in a logical way and there are comments where applicable.
-
-The effects are called "parts" by the code and each part has an `Init` and `Process` routine. The sequence is controlled by a table of `Init` pointers and driven by the `ProcessPartTransition` routine. Each `Init` is responsible for setting up the data for the effect (part) and to set the `Process` function pointer via the `SetProcessFunc` macro. When the effect is done, the `Process` routine calls the `SetChangePartFlag` to tell the tutorial driver to move to the next part.
-
-There are non-effect parts present to get the effect sequence looking good when the parts are played one after the other. These are "delay" parts of various flavors:
-- `ShowDelay`: this shows the screen normally for a few seconds
-- `LightDelay`: this shows a light-colored blank screen for a few seconds
-- `DarkDelay`: this shows a dark-colored blank screen for a few seconds
-
-The `Delay` parts share code because they're only present to make the ROM look nice, but the effects parts were developed in a way to be isolated from one another. This was done to make extraction easier. Because of this, you will see similar code present across several parts, for example, the various Sine effects.
-
-One quirk you might notice when looking at VRAM is that the tile map is placed at 0,4 instead of 0,0. This was done to get the roll/unroll to handle the top of the screen correctly. The effects look best when they smoothly (dis)appear off-screen and if the image was placed at 0,0, the code to handle that would be distracting to how to implement the core of the effect.
-
-Another topic worth mentioning is the row of light tiles that are under the image in VRAM. This was necessary to allow `LightDelay` to exist. Those light tiles don't *have* to be right under the image, that's just where it was placed for this tutorial. It could be moved well out of the way so it doesn't affect the effects that show that part of VRAM (Y Sine, Roll On, Roll Off).
-
-If you run the ROM in [BGB](https://bgb.bircd.org/) and have the Debug Messages window open, you will see the various parts announce themselves when they are initialized.
-
-## Exercises for the reader
-
-You can do more things than just change the scroll registers. For example, you can change the palette. Can you do this to make the roll/unroll effect look better? Here's an [example of scroll register and palette changes](https://www.youtube.com/watch?v=_-GTCao5cxs).
-
-This [appearance effect](https://www.youtube.com/watch?v=leTk0uRnE_g&t=91s) from Sword of Sodan (Amiga) is really cool! (And you might recognize one of the opening effects if you scrub to the beginning.)
-
-Another raster effect you could do is a 'twist' like the one in the [Wired demo](https://www.youtube.com/watch?v=WlMl8XKCb1Y&t=63s).
-
-You can use this system to make a racing game similar to [F-1 World Grand Prix II](https://www.youtube.com/watch?v=yvbQD2pbJes) or [Wacky Races](https://www.youtube.com/watch?v=1kXiU_odMMM&t=110s). How might you achieve this?
-
-## PRs are welcome!
-
-Other effects can be done, such as flipping the entire image about the X axis to look like its tumbling. What other effects can you create?
-
-## Acknowledgements
-
-Thanks go to Baŝto for use of the [Dead Boy](https://opengameart.org/content/dead-boy) image and [ISSOtm](https://github.com/ISSOtm) for peer review!
-
-## License
-
-This was released for educational purposes and so is placed in the Public Domain. See [LICENSE](https://github.com/gbdev/gbdev.github.io/blob/dev/list/.vuepress/public/deadcscroll/LICENSE) for more details.
diff --git a/website/guides/dma_hijacking.md b/website/guides/dma_hijacking.md
deleted file mode 100644
index 951b6c57..00000000
--- a/website/guides/dma_hijacking.md
+++ /dev/null
@@ -1,197 +0,0 @@
----
-head:
- - - meta
- - name: og:title
- content: DMA Hijacking on Game Boy
- - - meta
- - name: og:description
- content: A technique that allows you to run custom code in most GB/SGB/CGB games, provided you have an ACE exploit.
----
-
-# DMA Hijacking
-
-Written by [ISSOtm](https://github.com/ISSOtm).
-
-::: tip TARGET AUDIENCE
-
-Unlike most resources here, this guide is not very useful to developers or even ROM hackers, but rather to glitch-hunters and exploit developers.
-
-:::
-
-## What is it?
-
-*OAM DMA hijacking* is a simple technique that allows you to run custom code in most GB/SGB/CGB games, provided you have an ACE exploit.
-
-One would be quick to point out that if you have an ACE exploit, you can already execute custom code.
-So then, what is the point?
-It's that code ran through DMA Hijacking will be run *on every game frame* (for most games, at least).
-
-## How is it done?
-
-If you are familiar enough with [OAM](https://gbdev.io/pandocs/OAM), you may know about a feature called *OAM DMA*.
-
-[OAM DMA](https://gbdev.io/pandocs/OAM_DMA_Transfer) is a convenient feature that allows quickly updating the on-screen ["objects"](https://gbdev.io/pandocs/Rendering#objects) (often known as "sprites") quickly—which is especially useful since it typically needs to occur on every frame.
-However, using OAM DMA requires a small routine to be copied to HRAM and then run from there.
-
-Interestingly, most games only copy the routine when starting up, and then execute it on every subsequent frame.
-But, *if we modified that routine while the game is running*, then the game will happily run the customized routine!
-
-### Patching the code
-
-Here is the standard routine, given by Nintendo in the GB programming manual (using [RGBASM syntax](https://rgbds.gbdev.io/docs/rgbasm.5) and a symbol from [`hardware.inc`](https://github.com/gbdev/hardware.inc)):
-
-```asm
- ld a, HIGH(OAMBuffer)
- ldh [rDMA], a ; $FF46
- ld a, 40
-DMALoop:
- dec a
- jr nz, DMALoop
- ret
-```
-
-The simplest way to get custom code (let's call it `DMAHook`) executed would be to overwrite the first few bytes with a jump to `DMAHook`:
-
-```asm{1}
- jp DMAHook
- db $46 ; Leftover operand byte of `ldh [rDMA], a`
- ld a, 40 ; None of this is executed
-DMALoop:
- dec a
- jr nz, DMALoop
- ret
-```
-
-Now, overwriting the routine like this works for our purposes, but comes with a large drawback: the routine isn't doing what it is intended to anymore, and so the game's objects won't update (unless you manually copied OAM, but beware of [the OAM corruption bug](https://gbdev.io/pandocs/OAM_Corruption_Bug)).
-Further, it's not possible to write to `rDMA` from `DMAHook`, as the write and subsequent wait loop **must** be executed from HRAM.
-
-But, there is a solution.
-
-```asm{1-2}
- call DMAHook
- ldh [c], a ; A write to `rDMA`, set up by DMAHook
- ld a, 40
-DMALoop:
- dec a
- jr nz, DMALoop
- ret
-```
-
-Provided that `DMAHook` returns with properly set registers, this allows writing to `rDMA` in the single HRAM byte left by the `call` instruction.
-Here is a pattern for DMAHook :
-
-```asm
-DMAHook:
- ;; Custom code, do whatever you want, it's VBlank time!
- ; ...
- ld c, LOW(rDMA) ; $46
- ld a, HIGH(OAMBuffer)
- ret
-```
-
-`DMAHook` can live anywhere in memory, but typically it will be in WRAM.
-It will be executed in the context of the VBlank interrupt, so for most games interrupts will be disabled, etc.
-
-## With Cartswap
-
-DMA Hijacking is also useful when combined with [cartswap](https://gist.github.com/ISSOtm/3008fd73ec66cb56f1caecfcc8b6fb6f) (swapping carts without shutting the console down, concept found by furrtek, developed by Cryo and me on the GCL forums), because it allows "transporting" ACE to other games.
-
-General procedure:
-
-1. Acquire ACE in the "source" game
-1. Perform cartswap, insert the "victim" game
-1. "Pseudo-initialize" the victim
-1. Place the modified DMA handler in HRAM
-1. Transfer control back to the victim's ROM
-1. ????
-1. Profit!
-
-Possible applications are checking for a button combo to trigger specific code (for example, credits warp), checking one or multiple memory addresses to detect a certain game state, etc.
-
-Possible "attack vectors", i.e. ways of affecting the victim game, are setting certain memory addresses (like a GameShark), or even better: manipulating the stack.
-
-Here is a video demonstration:
-
-
-Manipulating the stack with this technique can not crash if the triggering game state is specific enough.
-I achieved text pointer manipulation in Pokémon Red this way.
-(This is not a ROM hack!)
-
-
-### Details
-
-This new technique hinges on breaking one of any game's core assumptions: its entry point.
-You see, normally, [the console transfers control to the game at address $0100](https://gbdev.io/pandocs/The_Cartridge_Header#0100-0103---entry-point), so any code placed there is designed to initialize all of the game's systems, in particular their memory.
-
-However, since we have control of the CPU, we can jump to any location in the game's ROM, which allows bypassing some of said initialization.
-Doing so without any precautions is very likely to go haywire, though—it is important to initialize *enough* that the game runs, but not *too much* that it would end up overwriting the code we are trying to inject.
-This is what I call "**pseudo-initialization**".
-
-Another important part is finding some free space to store the hook code in.
-The stack area can work surprisingly well for this, as many games appear to over-allocate (e.g. 256 bytes when the typical usage doesn't go beyond 32).
-
-None of this has a silver bullet: the game's init code must be analyzed, and its memory usage carefully scrutinized in order to dig up enough free space for your hook.
-
-## Trivia
-
-DMA hijacking works similarly to the GameShark: that device intercepts accesses to the ROM, and when it detects that the VBlank handler is being run, it "overlays" different instructions that apply the stored codes, and jump back to the actual handler.
-
-And, why yes, it is possible to use DMA hijacking to emulate GameShark codes!
-[Here is a proof-of-concept in Pokémon Red](http://gbdev.gg8.se/forums/viewtopic.php?id=430).
-
-## Notes
-
-- I encountered some games that don't transfer OAM unless a specific flag is set; I believe that it is always possible to override this limitation, by setting the flag back in the hook.
-- The OAM DMA routine is often placed at $FF80 in commercial games.
-- The patched OAM DMA routine with our hook may be modifying registers that the game expects to be preserved.
- This is all dependent on the target game, so no general advice can be given.
-
- Additionally, if the hook takes too long, it may cause code expecting to run in VBlank to break.
- This might be solved for example by manipulating the stack and injecting an additional return address; here is an example.
- ```asm
- jp DMAHook
- PostDMAHook:
- ldh [c], a
- ld a, 40
- DMALoop:
- dec a
- jr nz, DMALoop
- jp hl
- ```
- ```asm
- pop hl ; Get original return address
- ld bc, PostHandlerHook ; Address of code that will be executed once the VBlank handler finishes
- push bc ; Inject return address for VBlank handler
- ld c, LOW(rDMA)
- ld a, HIGH(OAMBuffer)
- jp PostDMAHook
- ```
- (Since the handler almost certainly performs some `pop`s before returning, you will almost certainly need more complex stack manipulation, but that's the gist of it.)
-- Some games have a slightly more clever routine in HRAM, that omits the initial `ld a, HIGH(OAMBuffer)` saving 2 bytes of HRAM.
- ```asm
- ldh [rDMA], a
- ld a, 40
- DMALoop:
- dec a
- jr nz, DMALoop
- ret
- ```
- They can still be patched by overwriting the `ld a, 40` instead, and using e.g. the `b` register for the loop:
- ```asm{1-2,4}
- call DMAHook
- ldh [c], a ; Write to rDMA
- DMALoop:
- dec b
- jr nz, DMALoop
- ret
- ```
- Then `DMAHook` needs to return with `b` additionally set to 40:
- ```asm{4-5}
- DMAHook:
- ;; Custom code, do whatever you want, it's VBlank time!
- ; ...
- ld bc, 40 << 8 | LOW(rDMA) ; 40 in B, $46 in C
- ld a, HIGH(OAMBuffer)
- ret
- ```
- However, if the OAM buffer address passed to the function (in `a`) is not static, `push af` and `pop af` will have to be used instead of `ld a, HIGH(OAMBuffer)`.
diff --git a/website/guides/lyc_timing.md b/website/guides/lyc_timing.md
deleted file mode 100644
index bccea9b4..00000000
--- a/website/guides/lyc_timing.md
+++ /dev/null
@@ -1,914 +0,0 @@
----
-head:
- - - meta
- - name: og:title
- content: Timing of LYC STAT Handlers
- - - meta
- - name: og:description
- content: Guide on how to implement raster effects with the rLYC register.
----
-
-# The Timing of LYC STAT Handlers
-
-Written by [Ron Nelson](https://github.com/rondnelson99/) and [ISSOtm](https://eldred.fr)
-
----
-
-Raster effects are probably the greatest assets that retro game consoles have.
-The fact that the PPU generates the image right as it is displayed allows many special effects to be created by modifying the rendering parameters while the image is being drawn.
-Here is an example:
-
-
-
-
-
-
-
-However, unlike some consoles like the SNES, the Game Boy contains no hardware dedicated to raster effects, so the task falls squarely on the CPU.
-This causes raster FX code to interact with the rest of the program in complex ways, particularly when it comes to [accessing VRAM](https://gbdev.io/pandocs/Accessing_VRAM_and_OAM).
-
-In this article, we will explore different techniques for handling raster effects, and discuss their pros and cons with the help of some diagrams.
-
-::: warning PRIOR KNOWLEDGE ASSUMED
-
-This article is not a friendly introduction to programming raster effects, and assumes you are already comfortable with Game Boy programming.
-To learn more about how to achieve neat raster effects like the above, check out [DeadCScroll](deadcscroll.md) first, which the above GIF is actually from!
-
-Additionally, since the operations discussed here are *extremely* timing-sensitive, discussions will revolve around assembly instructions.
-You can learn how to program for the Game Boy in assembly in [GB ASM Tutorial](https://eldred.fr/gb-asm-tutorial).
-
-:::
-
-::: tip TERMINOLOGY
-
-We'll reference a few terms throughout this tutorial; here are brief explanations of them:
-
-- **SoC**: [System-on-a-Chip](https://en.wikipedia.org/wiki/System_on_a_chip), a single chip that includes most (or all!) components of a system. The Game Boy's functionality is almost entirely contained within a single chip, confusingly labelled "DMG-CPU" or similar. (Contrast this with, for example, the SNES, where there is one chip for the CPU, two for the PPU, and many more.)
-- **CPU**: Central Processing Unit, the part of the SoC that executes code and configures everything else.
-- **PPU**: Pixel Processing Unit, the part of the SoC that is responsible for sending pixels to the LCD and generating them.
-- **Rasterization**: the process of turning... something (for example, a collection of textured polygons; or, on the GB, tiles and tilemaps) into an array of pixels. "Raster" is sort of a contraction of that term.
-- **Scanline**: a row of pixels; it's called a "scan"-line because the lines get drawn one by one, pixel by pixel, as if the PPU was "scanning" along the screen.
-- **Register**: in general, a small piece of memory, usually linked to some hardware component.
-- **PPU mode**: The PPU can be in one of four modes at a given time, depending on what it's doing. [Please refer to Pan Docs](https://gbdev.io/pandocs/STAT) to learn what each mode corresponds to and how they are scheduled—they interact very tightly with raster effects.
-- **Interrupt**: an event that gets generated. Typically, this causes a "handler" to be `call`ed, which is a special routine dedicated to reacting to a given interrupt.
-- **"Main thread"**: any code that is executed outside of interrupt handlers.
-
-:::
-
-## Introduction
-
-The easiest way to implement raster effects is to use the `LYC` register with the STAT interrupt.
-
-Here is what the Pan Docs have to say about this register's simple function:
-
-> [**FF45 - LYC (LY Compare) (R/W)**](https://gbdev.io/pandocs/Scrolling#ff45---lyc-ly-compare-rw)
->
-> The Game Boy permanently compares the value of the LYC and LY registers.
-> When both values are identical, the “LYC=LY” flag in the STAT register is set, and (if enabled) a STAT interrupt is requested.
-
-So then, the outline for setting up a raster effect is as follows:
-
-1. Register an interrupt by setting `LYC` to the desired scanline
-2. When that scanline begins, the STAT interrupt handler will automatically be called
-3. Perform your chosen effect by modifying PPU registers
-4. Exit the handler with `reti`
-
-::: tip ALTERNATIVES
-
-There are other ways to perform raster FX, such as busy-waiting in the "main thread", but as this article's title suggests, we won't discuss them here.
-
-A major pro of `LYC`-interrupt-based raster effects is that they can be made self-contained, and thus largely independent of whatever the "main thread" is doing.
-This, in turn, simplifies the mental complexity of the code (decoupling), copes better with lag frames, and more.
-
-Many of the points brought forth later, particularly regarding cycle counting, are still relevant with these alternatives, so this is still worth reading!
-
-:::
-
-These four steps sound simple enough on their own, but there are numerous caveats we will discuss.
-Strap in!
-
-- Most raster effects are implemented by modifying registers *between* scanlines.
- Thus, you will want to write the register either during Mode 2 (of the same scanline), or Mode 0 (of the previous one)—anything but Mode 3, really.
- Unfortunately, `LY=LYC` interrupts are requested at the beginning of a scanline, so during the very short Mode 2, leaving too little time to perform but the most basic of effects.
-- Writing to the register during HBlank instead implies triggering the interrupt on the scanline *above* the effect, as well as idling for most of the scanline. So, if I wanted to enable sprites on scanline 16, I'd write 15 to `LYC`.
-- Mode 3's length is variable, so syncing to HBlank is difficult and time-consuming.
-- The interrupt handler's execution may be delayed by a few cycles, which makes it difficult to reliably sync to the PPU.
-- If the "main thread" is itself trying to sync with the PPU (typically by polling `STAT` in a loop), our interrupt may throw off its timing.
-
-Sounds good?
-Then let's get started!
-
-## Timing
-
-First, let's look at the timing of the rendering itself, [courtesy of the Pan Docs](https://gbdev.io/pandocs/pixel_fifo):
-
-
-
-Here are some key points:
-- A "dot" is one period of the PPU's 4 MiHz clock, i.e. 0.25 µs.
-- A "cycle" is the main unit of time in the CPU, which is equal to 1 µs, or 4 dots. (The Game Boy Color CPU can enter a "double-speed" mode which halves the length of cycles, but not of dots. For the sake of simplicity, we won't consider the differences it involves here.)
-- Each scanline takes exactly 456 dots, or 114 cycles.
-- Mode 2 also takes a constant amount of time (20 cycles)
-- HBlank's length varies wildly, and will often be nearly as long as or longer than the drawing phase.
-- HBlank and OAM scan are mostly interchangeable, and long as you're not writing to OAM.
-- The worst-case HBlank's length is not a multiple of 4 dots, so we will round down to 21 cycles.
-
-Let's consider a simple STAT handler, which disables OBJs if called at line 128, and enables them otherwise:
-
-@[code asm](lyc_timing/simple_handler.asm)
-
-::: tip
-
-This is not an especially well-written `STAT` handler, but the actual is outside the scope of this tutorial.
-If that's what you're looking for, check out [DeadCScroll](deadcscroll) by Blitter Object.
-It triggers the STAT interrupt on HBlanks rather than `LYC`, but the fundamentals are the same.
-
-Note that, for simplicity's sake, DeadCScroll does not consider the problems described further below, so be wary of combining that tutorial's STAT handler unmodified with `STAT`-based VRAM accesses in the main thread.
-
-:::
-
-Let's assume that the interrupt fires at, say, scanline 42.
-Equipped with [the GB instruction table](https://gbdev.io/gb-opcodes/optables) (see its legend at the bottom), we can plot how many cycles each operation takes, in relation with the PPU's mode:
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-The first 5 cycles do not have an instruction: indeed, calling an interrupt handler is not instantaneous, and the CPU is temporarily busy pushing the program counter (`PC`) to the stack, disabling interrupts, etc.
-Then, the actual interrupt handler begins execution.
-
-We can immediately spot a problem: the cycle during which `LCDC` is written to falls in the middle of rendering!
-(With only a handful of exceptions, instructions that access memory do so on their very last cycle.)
-This is usually undesirable, and could lead to graphical glitches like an OBJ being partially cut off until we write to `LCDC`.
-
-Another problem, less obvious but oh so painful, is how the interrupt handler might interact with the "main thread"'s operation.
-
-### The VRAM access [race condition](https://en.wikipedia.org/wiki/Race_condition)
-
-Accessing VRAM [is not possible during Mode 3](https://gbdev.io/pandocs/Accessing_VRAM_and_OAM).
-Thus, when we want to access VRAM, precautions must be taken; the most common is to use the following loop:
-
-@[code asm](lyc_timing/stat_loop.asm)
-
-This loop checks whether `[STAT] & 2` is zero, and exits when it does.
-Looking at [documentation for `STAT`](https://gbdev.io/pandocs/STAT#ff41---stat-lcd-status-rw), we can see that the lowest 2 bits report the PPU's mode, and that `[STAT] & 2` is zero for Mode 0 and Mode 1, but not Mode 2 or Mode 3.
-So, essentially, this loop waits for Mode 0 or Mode 1, which are both safe to write to VRAM—but it can't be that simple.
-
-
-
-
-
-
-
-
-
-Pictured above is the "worst case" for this loop.
-As you can see, on the cycle that `STAT` is read, the PPU is still in Mode 0; however, checking for it takes a few cycles, during which we enter Mode 2!
-
-Now, thankfully, Mode 2 is *also* safe for accessing VRAM—but only 16 cycles of it remain.
-This is why this loop is said to guarantee 16 "VRAM-safe" cycles: any access performed 17 cycles or more after it would break in this worst case.
-
-Now, what would happen if our interrupt was requested in the middle of this?
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-Oh no!
-The main thread is now trying to access VRAM right in the middle of Mode 3!
-This could lead to all sorts of visual bugs.
-
-### A solution?
-
-The solution is not too complicated, at least on paper.
-We should be able to use the same `STAT`-checking loop (or at least, a variation of it) inside of the handler.
-It works in the main thread, so it should work here as well, right?
-
-Remember that many `STAT` handlers will be much more complicated than the simple example above, so let's draw a diagram with an imaginary handler that would take significantly more time:
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-::: tip
-
-All the instructions between the "Interrupt dispatch" and "Return from interrupt" blocks are the interrupt handler, the rest is in the "main thread".
-
-:::
-
-The `STAT` loop does fix the register being written to during Mode 3; however, once again, the 16 cycles that "main thread" expects to be VRAM-safe overlap with Mode 3.
-The problem here is that the write, `pop` and `reti` all take some of those cycles, and the "main thread" is using the value it read from `STAT` during the previous scanline—but that value is now stale.
-
-### Possible fixes
-
-Using what we have learned so far, we can boil down the problem to three factors:
-
-1. Our handler can trigger in the middle of this sequence of events
-2. Our handler preserves the stale value read from `STAT` earlier
-3. Our handler returns during a time where accessing VRAM is unsafe
-
-It would be enough to get rid of *any* of these, so let's enumerate our options.
-
-#### Dealing with it
-
-It's entirely possible to accept the loss of some of those cycles.
-This amounts to assuming less than the usual 16 cycles after such loops.
-For example, putting a `STAT`-polling loop just before the last `pop af` and `reti` would have these two eat up 7 cycles, so we are down to 9.
-
-This will quickly become impractical, requiring syncing to the LCD much more often in the main thread.
-
-#### Handler timing
-
-A simple way to prevent those pesky handlers from throwing off our timing is to disable them, with the `di` instruction.
-Unfortunately, it can't quite be so simple, as using `di` for this brings its own share of problems.
-
-The most important one is that disabling the handlers like this *delays* their execution!
-`STAT` handlers designed to write to hardware regs during HBlank may start doing so during rendering instead; timer interrupts won't trigger as regularly now; and so on.
-
-Using `di` is valid in some cases, but typically not when `STAT` interrupts are involved, due to their fairly strict timing requirements.
-
-An oddly common alternative is to perform all VRAM updates in VBlank handler.
-(The reason why it's common especially in early GB games is likely being a carry-over from the NES, where the lack of HBlanks essentially mandates such a setup anyway.)
-While this can work, such as for *Metroid II*, it requires significant complexity from having to keep deferring graphical updates.
-
-#### Stale `STAT` read
-
-There is not much that can be done about this one.
-The interrupt handler *must* preserve registers, and ...
-
-TOCTTOU
-
-#### Return timing
-
-This is the solution that the rest of this article will explore, as we will see that it makes the least painful compromises out of most use cases.
-
-So, the real solution is to fully exit before the end of HBlank.
-There are two ways to do this.
-One is to wait for the Drawing phase before waiting for HBlank.
-This effectively catches the very start of HBlank, leaving plenty of time to exit.
-Here's how the earlier example might look using this method:
-
-@[code asm](lyc_timing/ret_hblank_handler.asm)
-
-See how this method never interferes with VRAM accesses in the main thread, even with the worst possible timing and the shortest of HBlanks:
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-Phew! This just barely works.
-There are only two cycles to spare! If there were multiple registers that needed updating, you might run into trouble.
-Normally, These really short HBlanks are the worst-case scenario that you always fear.
-However, in practice, HBlanks are normally much longer, often even longer than the drawing phase.
-Using this method, that can actually have unfortunate consequences:
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-This time, when all the processing was done, there was still plenty of time left in the scanline to safely exit.
-However, since HBlank was so long, the routine missed the check for the drawing window and wasted an entire scanline waiting for that Drawing -> HBlank transition before it exited.
-Not only does this waste precious CPU time, but it also limits how often raster FX can be used throughout the frame.
-This method still works fine though, and can be an easy approach if you use Raster FX sparingly.
-
-I'm a bit of a perfectionist, so I usually like to strive for the absolute best method.
-In a perfect world, we would precisely know whether we have enough HBlank left to safely exit.
-There actually is a way to do that though! You just need to count exactly how long your routine takes, and make sure it always exits during HBlank.
-This comes with some caveats though.
-Most routines, if they haven't been specifically designed for this method, will take a variable amount of time.
-The main things you need to avoid are `if` statements and loops.
-Specifically, if statements of this form are problematic:
-
-```asm
- ; test a condition here...
-
- jr nc, .skip ; skip the next part unless Carry is set
-
- ; do something here, only if the previous operation set Carry
-
-.skip
- ; continue on with the program.
-```
-
-The problem here is that the code following this pattern may be run after a variable number of cycles have passed.
-If you need to use an if statement, always make it an if/else statement so that you can waste cycles in the `else` portion and take the same number of cycles.
-
-So now that you're ready to count the cycles of your handler, how long do you need to make the routine? Let's look at some more diagrams to figure this out!
-
-
-
-
-
-
-
-
-
-
-
-Wow! That's a lot of cycles! Here, the routine takes exactly one scanline to complete, so the main thread does its writes at the same moment on the next scanline, with no idea what happened! If you count up all the cyan cycles, you'll see that there are 105 of them, and 109 if you count the `reti`.
-This extra time makes it possible to write to two or three registers safely, rather than just one.
-If you don't need all that time, you can make it shorter as well:
-
-
-
-
-
-
-
-
-
-
-
-This time, I put the `and` and `jr` before the interrupt, so that when it resumes, it's all ready to start writing to VRAM.
-This interrupt routine is 87 cycles long, including the `reti`.
-This won't often prove especially useful though, because you never take any time during HBlank to actually do any register writes.
-However, you could use this if your routine has a case where it realizes that nothing actually needs to be written, and you can exit earlier.
-
-From those two diagrams, you'll see that the 22 cycles of worst-case HBlank is the time you can use to write to any PPU registers, pop your registers back, and then exit with `reti`.
-These 22 cycles are cycle 88 through cycle 109, inclusive.
-
-What if I told you that you could actually have your handler take only 86 cycles? Well, you can!
-
-
-
-
-
-
-
-
-
-
-
-This seems bad, since the first cycle of the red bar, where the main thread may try to access VRAM, is potentially during the Drawing phase! This is also fine though.
-All instructions that access memory, whether through an immediate address or using a register pair as a pointer, take multiple cycles to complete.
-That's because the first cycle of every instruction is used to fetch the operation code itself.
-The memory access that the instruction performs is always in the 2nd, 3rd or 4th cycle of the instruction.
-In this situation, the 2nd cycle of the VRAM-accessible time is in HBlank, so this won't actually cause any problems.
-
-## But Wait!
-
-The interrupt latency I showed earlier doesn't actually tell the full story.
-Before it even starts to service the interrupt, the system waits for the current instruction to finish.
-This is how that might look with the longest allowable routine:
-
-
-
-
-
-
-
-
-
-
-
-
-Here, the first green block shows the system waiting 5 cycles for a `call` instruction to finish. `call` is the longest instruction at 6 cycles, so if the interrupt is requested just after it begins, the system will wait 5 cycles for it to complete.
-This seems bad, since the routine exited after the end of HBlank.
-However, this is actually fine! Those waiting cycles were not wasted; they were still 5 cycles of work that the main thread got done.
-So in the end, the main thread still gets its 20 cycles of VRAM-accessible time.
-
-## Pros and Cons
-
-Thus far, I have presented two very different methods for making safe `LYC` handlers, and each have their pros and cons.
-
-## Double-Busy-Loop
-
-**Pros**
-- does not require all code to be constant-time
-- does not require tedious cycle-counting
-- may exit very early if the routine finishes quickly
-
-**Cons**
-- does not provide enough HBlank time to safely write multiple registers
-- if the routine takes too long, it may miss mode 3 and waste an entire scanline before exiting
-
-## Cycle-counting
-
-**Pros**
-- leaves more time for more complex logic in the routine
-- allows enough time during blanking to write to up to three registers
-- never takes longer than one scanline
-
-**Cons**
-- requires all code to be constant-time
-- requires tedious cycle-counting
-- always takes close to an entire scanline, even if HBlank starts much sooner
-
-This suggests that the double-busy-loop method is good for extremely simple `LYC` routines that only need to write to one register, or routines that for some reason cannot be cycle-counted.
-If you need more time for calculations and more time to write to those registers, you can cycle-count your routine.
-
-But what if you could combine both these methods? Enter the **Hybrid Cycle-Counted Handler™**, a technique I came up with while writing this document.
-
-## Combining Approaches
-
-The goal of this method is to combine the maximum HBlank time that cycle-counting delivers, while still exiting early when HBlank is longer.
-Here is an example.
-If you've read [DeadCScroll](deadcscroll), you'll recognise this as that tutorial's `STAT` Handler, modified to start at Mode 2 rather than HBlank, and be safe towards VRAM accesses in the main thread.
-
-@[code asm](lyc_timing/hybrid_handler.asm)
-
-Once the handler finishes its logic, the handler delays cycles until it reaches the window then HBlank might start.
-With a 5-cycle offset due to a `call`, and the longest possible HBlank, the earliest HBlank might start is cycle 54, so that's the first attempt to read `STAT`.
-It keeps checking `STAT` until even in the worst-case scenario, it knows that HBlank will start.
-Then, it uses that time to write the scroll registers and exit.
-This way, it can still exit early, as long as the HBlank length permits.
-This routine takes 104 cycles in the worst-case scenario, but may take as few as 79 if HBlank comes sooner.
-
-The reason that the double-busy-loop method requires checking for Mode 3 but this method does not is that the double-busy-loop method is not cycle-counted, so you might be at the very end of HBlank which is problematic.
-Since this method is cycle-counted, you know that if HBlank has begun, you are at or near the start of it.
-
-If we make a similar list of pros and cons for this method, this is what it might look like:
-
-## Hybrid cycle-counting
-
-**Pros**
-- may exit very early if HBlank is longer
-- allows enough time during blanking to write to up to three registers
-- never takes longer than one scanline
-
-**Cons**
-- requires all code to be constant-time
-- requires tedious cycle-counting
-
-This method can work well in many circumstances, and is especially suited to frequent effects that modify multiple registers and need to exit quickly to avoid taking too much CPU time.
-This method can even work reasonably well when used on every scanline through the Mode 2 interrupt.
-
-All three of these methods can generate great-looking effects, but I think the third one is an especially attractive option.
-
-Congrats! You made it to the end of the tutorial! I bet you're tired of reading it, and I'm tired of writing it too.
-So thanks for reading, see you next time!
-
-
-
diff --git a/website/guides/lyc_timing/hybrid_handler.asm b/website/guides/lyc_timing/hybrid_handler.asm
deleted file mode 100644
index 7fc1dbfb..00000000
--- a/website/guides/lyc_timing/hybrid_handler.asm
+++ /dev/null
@@ -1,50 +0,0 @@
- push af ; 4
- push hl ; 8
-
- ; obtain the pointer to the data pair
- ldh a, [rLY] ; 11
- inc a ; 12
- add a, a ; 13 ; double the offset since each line uses 2 bytes
- ld l, a ; 14
- ldh a, [hDrawBuffer] ; 17
- adc 0 ; 19
- ld h, a ; 20 ; hl now points to somewhere in the draw buffer
-
- call UnconditionalRet ; just waste 31 cycles while we wait for HBlank to maybe start
- call UnconditionalRet
- call UnconditionalRet
- nop ; 51
-
- ; now start trying to look for HBlank to exit early
-
- ldh a, [rSTAT]
- and STATF_BUSY
- jr z, .setAndExit ; 58
-
- ldh a, [rSTAT]
- and STATF_BUSY
- jr z, .setAndExit ; 65
-
- ldh a, [rSTAT]
- and STATF_BUSY
- jr z, .setAndExit ; 72
-
- ldh a, [rSTAT]
- and STATF_BUSY
- jr z, .setAndExit ; 79
-
- nop ; waste 4 more cycles since there isn't time for another check
- nop
- nop
- nop ; 83
-
-.setAndExit
- ; set the scroll registers
- ld a,[hl+] ; 85
- ldh [rSCY],a ; 88
- ld a,[hl+] ; 90
- ldh [rSCX],a ; 93
-
- pop hl ; 97
- pop af ; 100
- reti ; 104
diff --git a/website/guides/lyc_timing/ret_hblank_handler.asm b/website/guides/lyc_timing/ret_hblank_handler.asm
deleted file mode 100644
index c041f0b1..00000000
--- a/website/guides/lyc_timing/ret_hblank_handler.asm
+++ /dev/null
@@ -1,29 +0,0 @@
-LYC::
- push af
- push hl
- ldh a, [rLY]
- cp 128 - 1
- jr z, .disableSprites
-
- ; enable sprites
- ldh a, [rLCDC]
- or a, LCDCF_OBJON
- jr .finish
-
-.disableSprites
- ldh a, [rLCDC]
- and a, ~LCDCF_OBJON
-
-.finish
- ld hl, rSTAT
-.waitNotBlank
- bit STATB_BUSY, [hl]
- jr z, .waitNotBlank
-.waitBlank
- bit STATB_BUSY, [hl]
- jr nz, .waitBlank
-
- ldh [rLCDC], a
- pop hl
- pop af
- reti
diff --git a/website/guides/lyc_timing/simple_handler.asm b/website/guides/lyc_timing/simple_handler.asm
deleted file mode 100644
index 30655de1..00000000
--- a/website/guides/lyc_timing/simple_handler.asm
+++ /dev/null
@@ -1,19 +0,0 @@
-LYC::
- push af
- ldh a, [rLY]
- cp 128 - 1
- jr z, .disableSprites
-
- ; enable sprites
- ldh a, [rLCDC]
- or a, LCDCF_OBJON
- ldh [rLCDC], a
- pop af
- reti
-
-.disableSprites
- ldh a, [rLCDC]
- and a, ~LCDCF_OBJON
- ldh [rLCDC], a
- pop af
- reti
diff --git a/website/guides/lyc_timing/stat_loop.asm b/website/guides/lyc_timing/stat_loop.asm
deleted file mode 100644
index 3695ec79..00000000
--- a/website/guides/lyc_timing/stat_loop.asm
+++ /dev/null
@@ -1,4 +0,0 @@
-.waitVRAM
- ldh a, [rSTAT]
- and STATF_BUSY ; 2
- jr nz, .waitVRAM
diff --git a/website/guides/sgb_border.md b/website/guides/sgb_border.md
deleted file mode 100644
index 5b85198b..00000000
--- a/website/guides/sgb_border.md
+++ /dev/null
@@ -1,195 +0,0 @@
-# Adding a custom SGB border
-
-This document aims to help developers of DMG-compatible homebrew with adding Super Game Boy borders.
-
-We will see how to:
-
-- Detect whether the program is running on a SGB
-- Transfer the border's tiles
-- Transfer and display the border's tilemap and palettes
-
-
-
-Written by **sylvie (zlago)**, idea (and minor help) by [valentina (coffee bat)](https://coffeebat.neocities.org/), reviews and improvements by [ISSOtm](https://eldred.fr), [avivace](https://github.com/avivace), and [PinoBatch](https://github.com/pinobatch).
-
-
-
-## Enabling SGB features
-
-Before we can do anything else, we must first specify in the header that this game is aware of SGB features.
-Otherwise, the SGB BIOS will ignore any packets we send, and we won't even be able to detect when the program is running on SGB.
-
-To enable SGB features:
-
-- The [SGB flag] must be set to `$03`;
-- The [old licensee code] must be set to `$33`.
-
-[SGB flag]: https://gbdev.io/pandocs/The_Cartridge_Header#0146--sgb-flag
-[old licensee code]: https://gbdev.io/pandocs/The_Cartridge_Header#014b--old-licensee-code
-
-This can be achieved by passing the `--sgb-compatible` and `--old-licensee 0x33` flags to [`rgbfix`](https://rgbds.gbdev.io/docs/rgbfix.1).
-
-## Packets
-
-The SGB BIOS can be "talked to" via *command packets*, sent bit by bit via the `P1`/`JOYP` register.
-
-An SGB packet consists of:
-
-- a "start" pulse (`P1`=`%xx00xxxx`)
-- 128 data pulses (`P1`=`%xx01xxxx` for "1", `P1`=`%xx10xxxx` for "0")
-- a "0" pulse (`P1`=`%xx10xxxx`)
-
-You must set `P1` to `%xx11xxxx` between each pulse.
-
-This adds up to 16 bytes of data (LSB first). If a packet doesn't read all 16 bytes, the unused bytes are ignored.
-
-**You should wait 4 frames between each packet and the next.** This gives the SGB BIOS a chance to receive a packet even if it is doing something else time-consuming.
-
-For an example of such routine, see the related Pan Docs entry: [SGB Command Packet on Pandocs](https://gbdev.io/pandocs/SGB_Command_Packet.html).
-
-This guide glosses over a minor detail, as certain packets can be (albeit unccomon) more than 16 bytes.
-
-## TRN
-
-Bulk transfer (TRN) packets tell the SGB to copy the contents of the screen to buffers in Super NES work RAM. The `CHR_TRN` and `PCT_TRN` packets are used to send data for SGB borders.
-
-For a transfer to function properly, you must prepare VRAM and the LCD registers:
-
-1. set `BGP` to `$e4` and `LCDC` to `$91` (screen enabled, BG uses tiles `$8000`-`$8fff` and tilemap `$9800`, WIN and OBJ disabled, BG enabled)
-2. set `SCX` and `SCY` to `$00`
-3. the tilemap consists of `$00`, `$01`..`$13`, 12 bytes padding (offscreen), `$14`..`$27`, padding, repeat until `$ff` (inclusive)
-4. the data you want to send must be loaded at `$8000`-`$8fff`
-
-You can do 1, 2 and 3 via [this snippet](https://github.com/gb-archive/snek-gbc/blob/main/code/init.sm83#L208-L230)
-
-- **You must load the data into VRAM and enable the screen before sending the TRN packet.** The SGB reads TRN payloads from the screen. If rendering is off, there is nothing on the screen to read.
-- **You must wait ~8 frames after each TRN** instead of just 4. The SGB BIOS has to finish what it's doing, receive the packet, and then read the screen.
-
-## Detecting SGB
-
-Here's how a SGB detection routine should look like:
-
-1. Wait 12 or more frames for the SGB BIOS to start listening for packets.
-2. Send a `MLT_REQ` packet selecting 2 or 4 players (`$89, $01` or `$89, $03`), and wait a couple frames for the SGB to receive the packet.
-3. Read the controller. Set `P1` bit 5 to 0, then set `P1` bits 5 and 4 to `%11` (`%xx11xxxx`) to release the key matrix.
-4. Read the low nibble (bits 3-0) of `P1`, and look for a value other than `%1111` (which indicates player 1).
-5. If player 1 was still found, repeat steps 3 and 4 once more, in case the next read indicates player 2.
-6. Optionally turn off multiplayer mode.
-
-If a non-`%1111` value was found in step 4 either time, the program is running on SGB.
-
-A routine like this may be used to detect SGB (modified from [source](https://github.com/gb-archive/snek-gbc/blob/main/code/init.sm83#L167-L196)):
-
-```sm83asm
-SGB_Detect:
- ; test for SGB
- di
- call SGB_Wait4Frames
- call SGB_Wait4Frames
- call SGB_Wait4Frames
-
- ; Send MLT_REQ packet to enable multiplayer
- ld hl, Packets.mltOn ; send MLT_REQ for 2 players
- call SGB_SendPacket
- call SGB_Wait4Frames
-
- ; Detect the SGB by checking if SGB responded to MLT_REQ.
- ; Setting P1.5 to low then high advances the selected player.
- ; Setting P1.4 and P1.5 high causes the ICD2 to reflect the
- ; selected player in low bits of P1.
- ld b, 4 ; Number of attempts
- ld c, LOW(rP1) ; Address to write
- .loop
- ld a, P1F_4 ; Try to advance player
- ldh [c], a
- ld a, P1F_4|P1F_5 ; Set P1 to return the selected player
- ldh [c], a
- ; In case this is DMG and not SGB, let the input lines settle
- ; for a few cycles before reading P1 again
- call SGB_Wait4Frames.knownRet
-
- ldh a, [c] ; Player 1 has A.0 = 1; players 2 and 4 have A.0 = 0
- cpl ; Invert this
- and %00000001 ; Keep only bit 0, which is 0 for player 1 or nonzero for players 2 and 4
- jr nz, .done
- dec b ; Keep trying until we've cycled through all players
- jr nz, .loop
- .done
- ; After this loop, A is $01 for SGB or $00 for not SGB. Remember this
- ld [wIsSGB], a
-
- ; (Optional) Disable multiplayer
- ld hl, Packets.mltOff ; send MLT_REQ for 2 players
- call nz, SGB_SendPacket
- ret
-
-SGB_Wait4Frames:
- ld bc, -(456 * 154 / 16 * 4)
- .loop
- inc c ; inner loop takes 16 T-states per iteration
- jr nz, .loop
- inc b
- jr nz, .loop
- .knownRet
- ret
-```
-
-### SGB detection notes
-
-- It would be a good idea to save somewhere in RAM whether the game is running on an SGB capable device or not, such that if you wish to change the border mid-gameplay, you won't have to perform SGB detection again. The sample code stores it in `wIsSGB`.
-- **If you wish to only use 1 controller for the game, you will have to send another `MLT_REQ` to disable multiplayer** (`$89, $00`)
-- `SGB_Wait4Frames` above uses busy waiting. Depending on the structure of your initialization code, you can change it to use vblank interrupts or `di`+`halt` instead.
-
-## Border limitations
-
-An SGB border has:
-
-- 255 tiles + 1 transparent tile (preferably tile #0)
-- 3 palettes of 15 (+ 1 transparent) colors, (up to 45 solid colors total)
-- a 256×224-pixel tilemap (there's a bit more to this, see [notes](#notes))
-
-## Converting borders
-
-With a recent version of [superfamiconv](https://github.com/Optiroc/SuperFamiconv):
-
-```sh
-superfamiconv -v -i input.png -p output.pal -t output.4bpp -P 4 -m output.pct -M snes --color-zero 0000ff -B 4
-```
-
-- `--color-zero` should be the color that your image for transparency, in my case it was blue.
- * If your image has an alpha channel, it can be set can also be set to `00000000` to use the actual transparent color; however, this may cause some issues.
-- `-v` is optional, for showing details of the conversion process
-- You can add a row of the transparent color at the top of the image to force superfamiconv to make it tile #0, then `incbin "output.pct", 64` to leave out that row.
-- `-P 4` sets the base palette to the 4th one, and **SGB borders use SNES palettes 4, 5, and 6.** as of writing this, this option only works if you built superfamiconv from source.
-
-## Uploading borders
-
-As stated before, the SGB border consists of tile data, picture data, and palette data. These are split across 2-3 packets:
-
-- `CHR_TRN` (`$99`) is used to send 4KiB of tile data.
-
- * since the border can use up to 8KiB of tiles, bit 0 of the second byte specifies which "half" you're sending
- - `$99, $00` if the screen is loaded with the first 4KiB of tile data
- - `$99, $01` if the screen is loaded with the second 4KiB of tile data
-
-- `PCT_TRN` (`$a1`) is used to send the picture and palette data. it also swaps the border, generally a good idea to send it after the tile data[^1]
-
- * assuming tiles `0`-`255` use VRAM from `$8000` to `$8fff`:
- - the picture data must be at `$8000`-`$873f` (last 64 bytes are _usually_ offscreen, see [notes](#notes))
- - palette data must be at `$8800`-`$885f`
- - everything else is ignored
- * how you skip putting data at `$8740`-`$87ff` is up to you, I prefer doing separate copies, others prefer copying tilemap and palette data in one go, with the area between them padded.
-
-See also the related Pan Docs entry: [SGB Command Border](https://gbdev.io/pandocs/SGB_Command_Border.html).
-
-[^1]: You can send a `CHR_TRN` [up to \~60 frames](https://github.com/pinobatch/little-things-gb/blob/b11b554d73c48a0f54fee0df31e59eb83806fcb4/sgbears/docs/long_story.md) after the `PCT_TRN` for it to apply to the current border, but not all emulators will emulate this. It's fine to just pretend `CHR_TRN`s must go before `PCT_TRN`.
-
-## Notes
-
-1. You can set the first row of tiles to your transparent color to force superfamiconv to put the transparent tile as the 1st tile, however you must then exclude 64 bytes of the tilemap (`incbin "border.pct"` -> `incbin "border.pct", 64`)
-
-2. SGB BIOS reserves palettes 4 through 6 for borders. If you really know what you're doing, you may be able to use palette 0 (the gameplay palette) for animated borders. You will probably have to edit the border in a tile editor such as YY-CHR, as there aren't yet any other tools for that.
-
-3. When the SNES lags, scanline 225 of the SGB border will be visible! You can set the topmost row of the 29th row of tiles to black to hide this.
-
-4. If this doesn't work for you, you can ask for help on the [gbdev](https://gbdev.io/chat.html) channels.
diff --git a/website/guides/tools.md b/website/guides/tools.md
deleted file mode 100644
index 5ec52ad0..00000000
--- a/website/guides/tools.md
+++ /dev/null
@@ -1,237 +0,0 @@
----
-head:
- - - meta
- - name: og:title
- content: Choosing tools for Game Boy development
- - - meta
- - name: og:description
- content: Overview of the available tools for Game Boy homebrew development.
----
-
-
-# Choosing tools for Game Boy development
-
-
-This essay gives an overview of the Game Boy's capabilities, discussing the pros and cons of the available development tools, and providing a few tips to write more efficient code.
-
-Written by [ISSOtm](https://github.com/ISSOtm/) with help from [tobiasvl](https://github.com/tobiasvl), some updates by [bbbbbr](https://github.com/bbbbbr).
-
----
-
-
-In the past few years as retro gaming has grown in popularity, programming for older platforms has also gained traction. A popular platform is the Game Boy, both for its nostalgia and (relative) ease to program for.
-
-::: warning
-This document only applies to the Game Boy and Game Boy Color. Game Boy Advance programming has little in common with Game Boy programming.
-
-If you want to program for the GBA, which is much more C-friendly (and C++ and Rust, for that matter) than the GB and GBC, then I advise you to download devkitARM and follow the [Tonc](https://gbadev.net/tonc/) tutorial. Please note that the Game Boy Advance also functions as a Game Boy Color, so if you only have a GBA, you can use it for both GB and GBC development.
-:::
-
-When someone wants to make their own game, one of the first problems they will encounter is picking the *tools* they will use. There current main options are:
-- RGBDS (Rednex Game Boy Development System) and the Game Boy's Assembly language (ASM)
-- GBDK-2020 (Game Boy Development Kit) and the C language
-- ZGB (an engine built on GBDK-2020) and the C language
-- GB Studio (a drag-and-drop game creator with scripting)
-
-The purpose of this document is to provide some insights and help you make the better choice if you're starting a new project. I will also provide some "good practice" tips, both for C and ASM, if you have already made up your mind or are already using one of these.
-
-
-
-# Overview
-
-The original Game Boy, codenamed the DMG, has a 1 MHz CPU \[the CPU is actually clocked at 4 MHz, but every instruction takes up a multiple of 4 clocks, so it's often simplified to a 1 MHz CPU\]. Given that an instruction takes approximately 2 to 3 cycles, this gives the CPU a capacity of 333,000~500,000 instructions per second.
-Its LCD boasts 60 fps \[it's actually 59.73 fps\], which rounds up to between 50,000 and 80,000 instructions per frame. Suddenly not so much, eh?
-It also has 8 kB of RAM, and 8 kB of video RAM ; a 160x144 px LCD (thus slightly wider than it's tall), 4 colors, and 4-channel audio.
-
-The Super Game Boy adds a few minor things, such as a customizable screen border, and some crude color. It's also slightly faster than the DMG.
-
-The Game Boy Color *can* \[if you tell it to\] unlock additional functionality, such as more fleshed-out color, a double-speed CPU, twice the video RAM and *four times* the RAM! (With caveats, obviously.)
-
-
-# Languages
-
-The choice of programming language is important and can have a very large effect on a project. It determines how much work is involved, what will be possible, and how fast it will be able to run.
-
-### Assembly (ASM)
-Most games and programs for the Game Boy written in ASM will use RGBDS or WLA-DX.
-
-Strengths:
-* Not too difficult to learn.
-* Extremely powerful and flexible.
-* When well written it allows for maximum speed and efficiency on the limited resources of the Game Boy hardware.
-
-Weaknesses:
-* It takes a special kind of work to write optimized ASM code.
-* It's quite verbose and sometimes tedious.
-* Will require more time and learning to get up and running when compared with C.
-* Code may not be easily shared with ports of a game on other platforms.
-
-
-### C
-C will typically be used with the SDCC compiler and GBDK-2020 or ZGB, though it can also be used on its own without a framework or with a different compiler/dev kit (such as [z88dk](https://github.com/z88dk/)).
-
-Strengths:
-* Allows for getting up and running faster than with ASM, especially when building on top of GBDK-2020 and ZGB.
-* The language abstractions make it relatively easy to implement ideas and algorithms.
-* C source debugging is available through Emulicious with the VSCode debug adapter, making it easier to understand problems if they arise.
-* ASM can be included in projects with C, either standalone or inline for speed critical features.
-
-Weaknesses:
-* The SDCC C compiler won't always generate code that runs as fast as skilled, hand-optimized assembly. It has matured a lot in the 20 years since the original GBDK, but bugs still turns up on occasion. On a platform with a slow CPU such as the Game Boy this can be a factor.
-* It’s easier to write inefficient code in C without realizing it. The Game Boy's CPU is only capable of performing 8-bit addition or subtraction, or 16-bit addition. Using `INT32`s is quite taxing on the CPU (it needs to do two consecutive 16-bit adds, and add the carry). See the tips below to avoid such blunders.
-* There is overhead due to C being a stack-oriented language, whereas the Game Boy's CPU is rather built for a register-oriented strategy. This most notably makes passing function arguments a lot slower, although SDCC has some optimizations for this.
-
-
-### Non-Programming Language option
-Using a GUI instead- If you don’t want to learn a programming language in order to make Game Boy games, then GB Studio is an option. See the [GB Studio](#gb-studio) section for more details.
-
-
-# Development Platforms
-
-### [RGBDS](http://github.com/rednex/rgbds) with ASM
-
-RGBDS is an actively maintained suite of programs that allow building a ROM using ASM (assembly). It contains three programs that perform different stages of the compilation, as well as a program that converts PNG images to the Game Boy's tile format. RGBDS is available for Linux, Windows and MacOS.
-
-Strengths:
-* Very knowledgeable community with a lot of history.
-* Built in support for ROM banking.
-* Works quite well with BGB for debugging.
-
-Weaknesses:
-* Provides a limited amount of built-in code and functionality (does not include a large API like GBDK-2020 does).
-
-### [WLA-DX](https://github.com/vhelin/wla-dx) with ASM
-WLA-DX is also sometimes used when writing in ASM, mostly due to its better struct support than RGBDS.
-
-
-### [GBDK-2020](https://github.com/Zal0/gbdk-2020) with C
-GBDK-2020 is a development kit and toolchain built around the SDCC C compiler which allows you to write programs in C and build ROMs. It includes an API for interfacing with the Game Boy. GBDK-2020 is a modernized version of the original [GBDK](http://gbdk.sourceforge.net). It's available for Linux, Windows and MacOS.
-
-Strengths:
-* Flexible and extensible.
-* Comprehensive API that covers most hardware features.
-* Many sample projects and open source games are available that demonstrate how to use the API, hardware, and structure games.
-* C source debugging is available with Emulicious.
-
-Weaknesses:
-* Takes care of some aspects of the hardware without requiring the developer to initiate them (such as OAM DMA during VBLANK), so it's not always obvious to beginners what the hardware is doing behind the scenes, or how to fix them when something goes wrong.
-* ROM banking may require more management in code than RGBDS.
-
-
-### [ZGB](https://github.com/Zal0/ZGB/) with C & GBDK-2020
-ZGB is a small engine for the Game Boy built on top of GBDK-2020 and written in C.
-Strengths:
-* The basic graphics, sound and event structure are all pre-written, so it’s faster and easier to start writing a game.
-* Several open source games built with it are available as examples.
-
-Weaknesses:
-* The engine just has the basics and custom code may need to be needed for common game features (such as moving platforms, etc.).
-* Even more of the hardware configuration and processing is taken care of behind the scenes than with GBDK, so less experienced users may have trouble when problems arise.
-
-
-### [GB Studio](https://www.gbstudio.dev/)
-
-GB Studio is a drag-and-drop game creator for the Game Boy that does not require knowledge of programming languages. Games are built using a graphical interface to script graphics, sound and actions. It is available for Linux, Windows and MacOS.
-
-Strengths:
-* Very easy for beginners to start building games right away. Everything is built-in and requires minimal knowledge and understanding of the Game Boy hardware.
-* Has been used to create large and extensive projects.
-* Very active community for help and support.
-
-Weaknesses:
-* It’s games will tend to be slower than both ASM and C.
-* There is a limited set of commands to script with and some artificially smaller restrictions on palettes, sprites, background tiles and other hardware features (due to how GB Studio manages them).
-* Games may be more constrained or require workarounds to do things if they don’t easily fit within the available scripting, graphics and sound tools. (Though it is possible for advanced users to do a “engine eject” and add more functionality using C and ASM.)
-
-
-
-# Emulators and debuging tools
-Accurate emulators and debugging tools are tremendously helpful for testing and tracking down problems. The following Game Boy emulators provide excellent accuracy and include a variety of different features.
-
-* [BGB](http://bgb.bircd.org) has a convenient (ASM) debugger, though its minimal interface can be confusing at first. It is available for Windows only, but runs almost flawlessly with Wine.
-
-* [Emulicious](https://emulicious.net/) includes powerful tools such as a profiler and source-level debugging for ASM and C via a [VS Code debug adapter](https://marketplace.visualstudio.com/items?itemName=emulicious.emulicious-debugger). It runs on Linux, Windows, MacOS and any other operating systems that supports Java SE.
-
-* [Same Boy](https://sameboy.github.io/features/) is user friendly and has a wide range of powerful (ASM) debugging features. It runs on Windows and MacOS.
-
-* [Gambatte](http://github.com/sinamas/gambatte) lacks a debugger and must be compiled from source, but is packaged both in [RetroArch](http://retroarch.com) (Linux, Windows and Mac) and [BizHawk](http://tasvideos.org/BizHawk.html) (Windows-only).
-
-
-* Purists prefer to also run their games on hardware, which is possible thanks to flashcarts. My personal recommendation is [krikzz's carts](http://krikzz.com/store/), particularly the [Everdrive GB X5](https://krikzz.com/store/home/47-everdrive-gb.html).
-
-Side note : if you are using VBA or VBA-rr, **stop using them right now**. These emulators are extremely inaccurate, and also contain **severe security flaws**. I strongly urge you to ditch these emulators and spread the word.
-
-
-
-
-# Summary
-
-If your question is "*What should I use for my game project ?*", then you're in the right section. The first question you should ask yourself is what languages you know.
-
-### If you don't know ASM, C or C++
-Consider starting with C and GBDK. This will introduce you to working with the hardware and is an easier starting place.
-
-Once you've grasped C's concepts (most importantly pointers), give ASM a go. The language is simpler than it looks. Even if you don't manage to get working ASM code, it actually helps a lot (especially on such a constrained system) to know what's "under the hood". There is even an [online IDE](https://gbdev.io/rgbds-live/) to experiment with.
-
-For C / GBDK users, knowing ASM will help you understand what its API (which is mostly written in ASM) is doing behind the scenes and will make using emulator debuggers easier to understand.
-
-If you don't wan't to learn a language at all, [GB Studio](#gb-studio) is an alternative to C and ASM.
-
-### If you know C but not ASM
-Consider the goals, scope and time frame of your project. If you'd like to start building right away then C and GBDK will make that easy. You'll also have growing exposure to ASM as time goes on due to working with the hardware and tracking down problems in the debugger.
-
-On the other hand, if you'd like to expand your programming skill set and have additional time, learning to use ASM and RGBDS will provide you with a lot of knowledge about the Game Boy hardware. Once you know ASM in addition to C, you'll have a lot of flexibility in what tools you use for projects.
-
-
-### If you know ASM
-RGBDS with ASM is a solid option. You'll be able to get the best performance out of the hardware, and there is an experienced community available to help.
-
-Another option is to [reach out to us](#community-and-help), and discuss the matter.
-
-
-# Tips For Better Code
-
-The *very first thing* to do **in all cases** is to [read the docs](https://gbdev.io/pandocs/), to grasp how the Game Boy works. In ASM, this is essential; in C, this will let you understand what a given library function does. It will also let you understand what is possible on the Game Boy, and what isn't. (You can always ask, if you have doubts.)
-
-I also recommend looking up [awesome-gbdev](https://gbdev.io/resources.html) for resources and tutorials. There are a lot of helpful articles there, as well as helper tools.
-
-
-## ASM Help
-
-- *Modules*
- Separate your game into several "entities" that interact together. Camera, Player, NPCs, Loading zones, etc. This simplifies coding, by allowing you to reason independently on smaller units. This facilitates development and reduces the amount of bugs.
-- *Document your functions*
- For each function, write a comment saying what it does, what memory it touches, and what registers it affects. This will avoid conflicts, and let you optimize your code by minimizing the amount of registers you save when calling a function.
-- *Plan before writing*
- You should plan what register is going to be used for what within your functions *before starting to write them*. Your goal is to minimize the amount of register swapping. There's no general rule, so feel free to drop by and ask us, if you're in doubt.
-- *RGBASM `-E` and RGBLINK `-n `*
- When you load `ROM.gb` or `ROM.gbc` in BGB, it automatically loads (if it exists) the file `ROM.sym` in the same folder as the ROM. This adds symbols to the debugger, which - believe me - helps *a ton*.
-
-
-## Optimizing For GBDK
-
-- *Global variables*
- Use as many global variables as you can; the Game Boy has a lot of RAM compared to other platforms such as the NES, but is slow at using the stack. Thus, minimizing the number of local variables, especially in heavily-called functions, will reduce the time spent manipulating the stack.
-- *Optimized code*
- Write code as efficient as possible. Sometimes there is a readability tradeoff, so I recommend you get the comment machine gun out and put some everywhere.
-- By default GBDK-2020 (after v4.0.1) will use the SDCC flag `--max-allocs-per-node 50000` for an increased optimization pass. You may also choose to use --opt-code-speed (optimize code generation towards fast code, possibly at the expense of codesize) or --opt-code-size (optimize code generation towards compact code, possibly at the expense of codespeed).
-- *Inlining*
- When performance is important avoid using functions if you can inline them, which skips passing all arguments to the stack, mostly. Macros will be your friends there. If needed you can also use inline ASM.
-- **NEVER use recursive functions**
-- **AVOID printf**
- `printf` clobbers a sizeable chunk of VRAM with unnecessary text tiles. Instead, you should `sprintf` to a buffer in WRAM, then put that on the screen using a custom font.
-- *Geometry funcs*
- Avoid the functions that draw geometry on-screen (lines, rectangles, etc.). The Game Boy isn't designed for this kind of drawing method, and you will have a hard time mixing this with, say, background art. Plus, the functions are super slow.
-- `const` (very important!)
- Declaring a variable that doesn't change as `const` **greatly** reduces the amount of ROM, RAM, and CPU used.
- The technical reason behind that is that non-`const` values, *especially* arrays, are loaded to RAM from ROM in an *extremely* inefficient way. This takes up a LOT more ROM, and copies the value(s) to RAM when it's unneeded. (And the GB does not have enough RAM for that to be viable.)
-- *Don't use MBC1*
- MBC1 is often assumed to be the simplest of all MBCs... but it has a quirk that adds some overhead every time ROM or SRAM bank switches are performed. MBC3 and MBC5 don't have this quirk, and don't add any complexity. Using MBC1 has no real use. (Let's not talk about MBC2, either.)
-
-
-# Community And Help
-
-If you want to get help from the community, go:
-- To the historical IRC channel, #gbdev on [EFNet](http://efnet.net) \[if you don't have an IRC client, you can use the "Webchat login" box, just enter a username\].
-- To the more recent [gbdev Discord server](https://gbdev.io/chat.html) or [GBDK/ZGB](https://github.com/Zal0/gbdk-2020#discord-servers) specific server.
-- And to the [GBDev forums](http://gbdev.gg8.se/forums)!
diff --git a/website/meetings.md b/website/meetings.md
deleted file mode 100644
index 907cf283..00000000
--- a/website/meetings.md
+++ /dev/null
@@ -1,8 +0,0 @@
----
-sidebar: false
----
-
-# Meetings minutes
-
-- [March 10, 2024](./meetings/2024-03-10-sc2.html)
-- [November 4th, 2023](./meetings/2023-11-04-sc.html)
diff --git a/website/meetings/2023-11-04-sc.md b/website/meetings/2023-11-04-sc.md
deleted file mode 100644
index 6118dfc5..00000000
--- a/website/meetings/2023-11-04-sc.md
+++ /dev/null
@@ -1,97 +0,0 @@
----
-sidebar: false
----
-
-# Steering Committee - November 2023
-
-Written by avivace.
-
-**November 4th, 2023**. 15:00 — 17:30 UTC. Discord VC.
-
-##### Invited
-
-Moderators, Staff, Experts, Outreach, RGBDS maintainers.
-
-##### Partecipants
-
-PinoBatch, avivace, ISSO, Calindro, Duo, nitro2k01, QuangDX, superdisk, kva64, Sylvie, Sanqui
-
-
-##### About those meetings
-
-Participants were asked to fill in discussion points beforehand. The results of these discussions (decisions, suggestions, comments, etc.) are reported as subitems.
-
-Discussion points marked \* were not brought up due to lack of time.
-
-Anyone can reach out in `#meta` on the Discord server to comment/discuss on any of the points reported here.
-
-##### Minutes
-
-- avivace:
-
- - Q to ISSO: RGBDS Rust port plans?
- - Scope: Full port from the old codebase, can’t be 1-to-1
- - Architecture design in progress
- - Motivation: C not attractive anymore for potential contributors. Showing its age. Technicalities (std::vector). Rust is fancier, more active community.
- - (Personal) De-risking/Viability study
- - Question from Pino: will this affect availability of binaries?
- - Homebrew Hub: developments and expanding to NES \*
- - Gbcompo23: \[...\] \*
- - Funding: how to? \*
- - Creating the steering committee: Roles? \*
- - Q to Sanqui: how to run these meetings? Minutes \*
-
-- ISSOtm:
-
- - Difficulties finding contributors
- - Pan Docs, RGBDS, ASM Tutorial are lacking
- - AV: Homebrew Hub is OK
- - Quality barrier too high? Is our PR review process too much?
- - Possible (partial) solution = avoid nitpicking
- - Interesting actionable by DUO: give reviews in “steps”, avoid attacking a PR with everything all at once.
- - If making follow-up changes separately, be upfront about that in the initial PR to avoid the contributor feeling “the rug pulled under their feet”
- - Put a hard cap on the PR’s “TTL” before being merged (e.g. 3 weeks), and avoid minor changes as the deadline looms closer
- - How to handle contributors going silent?
- - “Better merge something incomplete to beg correction work, rather than drop everything done thus far”
- - Promoting a “Bug Day”?
- - Moving projects to SourceHut? \*
-
-- Sanqui:
-
- - Overview of Wikidata and the possibility of contributing Homebrew Hub information to Wikidata
- - Add Ext ID in the game metadata schema and point it to Wikidata entry. Possibly populate others.
- - HHub - cartridges/published games/publishers
-
-- DUO:
-
- - Remarks and compliments on the great state of gbdev.
- - Promote PRs that need to be worked on, but also promote work that _has_ been done “for cred” - Start using #feedback again.
-
-- nitro2k01:
-
- - Potentially add a long-term solution for the IRC bridge as a point/responsibility.
- - Someone other than @avivace needs to volunteer (=> @nitro2k01) and get SSH access and/or a webhook token to delegate the responsibility of handling the bridge
- - Future of the forums \*
-
-- Kva64:
-
- - Social media handling — general outline and feedback request
- - Not only game releases/showcase content can be shared, but projects themselves may have cool milestones to share
- - Allow developers, maintainers, etc. to raise up things that can be promoted, so people helping with social media don’t have to “chase” those. Should those go in #showcase? Should #showcase be split?
- - Encourage (PSA?) developers to post in #showcase
- Move all showcase channels to a separate category, and rename “showcase” to “wip-showcase”?
- Split #releases into e.g. #releases-games and #releases-tooling
- - To discuss in #meta: opt-in/opt-out status of showcase channels. A suggestion: make sharing opt-out in #releases-\*, and opt-in in #wip-showcase
- - Policy on sharing and promoting commercial projects?
- - DUO: do not shy away from promoting them because it’s a motivation to some; however, restrictions should be put to avoid being taken advantage of
- - Should the cooldown (if any) apply per project, per user...?
- - Need for using any newly-emerging social media platforms (e.g. Threads and Bluesky)?
- - Threads and Bluesky currently have very little reach, so maybe not
- - Other platforms (e.g. Instagram): maybe, but this may require more people to handle them
- - Creating a channel to coordinate them
-
-- Sylvie:
- - Q to ISSO: Continue improving existing C++ while Rust progresses?
- - C to C++ in parallel
- - Talk separately (and later) about whether we want to upstream the port, and if yes, the roadmap (responsibility & task distribution, etc.)
-
diff --git a/website/meetings/2024-03-10-sc2.md b/website/meetings/2024-03-10-sc2.md
deleted file mode 100644
index 01620f01..00000000
--- a/website/meetings/2024-03-10-sc2.md
+++ /dev/null
@@ -1,83 +0,0 @@
----
-sidebar: false
----
-
-# Steering Committee - March 2024
-
-Minutes curated by Sanqui and avivace
-
-
----
-
-**Participants**: avivace, Sanqui, Calindro, superdisk, ISSOtm, nitro, Pino, asie, tobias, DUO, Sylvie, kva
-
-**Where**: Discord gbdev, `#sc-meeting` voice channel
-
-**When**: March 10th 2024, 6:30 PM - 8:00 PM CET (UTC +1)
-
-[Last meeting minutes](https://gbdev.io/meetings/2023-11-04-sc.html)
-
-Discussion points are ordered by priority.
-
-
----
-
-### Updates
-
-avivace:
-- Creation of a new Outreach issue tracker / board to coordinate social media posting etc.
-- Asking for help (and funding) gb-asm-tutorial
-- matomo statistics shows growing interest in gb-asm-tutorial
-- Unblocking gb-asm-tutorial
-
----
-
-### Discussion
-
-
-nitro2k01
-
-- Future of the forums
- - Problems
- - Not mobile friendly
- - People don’t want to make another account -> offer login using Discord or GitHub
- - When/if migrating to a new system, what do with old posts?
- - Migrate to new? (lose URLs)
- - Set old in read-only
- - Threads on Discord <-> Forum ?
- - Options
- - Leave it the way it is
- - Could switch forum systems
- - Discourse is very popular, but JS heavy
- - Migrate to nesdev as they would be willing to help
- - Migrate to [https://forum.gbadev.net/](https://forum.gbadev.net/), which uses Flaskbb
- - Recycling logins may make the forum more attractive (e.g. nesdev or gbadev may have users overlap)
-
-
-ISSOtm:
-
-- RGBDS (and other projects?): onboarding friction ([https://matklad.github.io/2021/02/06/ARCHITECTURE.md.html](https://matklad.github.io/2021/02/06/ARCHITECTURE.md.html) for inspiration)
- - Projects are complex and “unapproachable”
- - Adding examples to CONTRIBUTING.md?
- - Which projects don’t have CONTRIBUTING.md?
- - Write down what is expected of a maintainer!
- - Finding some time to write down the “vision” Isso has for the Rust port (what needs to be done, how, etc.)
-- Prioritization, among and within…
- - gb-asm-tutorial (1)
- - Pan Docs (2)
- - rsgbds (RGBDS on “life support” with Sylvie maintaining) (last)
-- Next meeting:
- - Moving projects to SourceHut?
-
-avivace
-- FOSDEM feedback
- - Fosdem25. Who wants to join and come in the “gbdev” delegation?
- - Isso wants to give a talk
- - RGBDS
- - “Breaking changes and how to live with them”
- - Working from a codebase from the 90’s
- - Tutorial?
-
-kva64
-- Promoting usage of public domain/creative commons/”use as long as you give credit” assets among community members (open related issue on github)
-- How do deal with “drama” against users/companies/etc we want to shout out: consult with outreach team + use best judgment
diff --git a/website/newsletter.md b/website/newsletter.md
deleted file mode 100644
index 908dd721..00000000
--- a/website/newsletter.md
+++ /dev/null
@@ -1,10 +0,0 @@
----
-sidebar: false
-editLink: false
----
-
-# Newsletter
-
-- [gbdev newsletter Issue #1 - 2021](/newsletter/1)
-- [State of GBDev - March 2021](https://eldred.fr/blog/gbdev-status)
-- [gbdev updates - November 2020](https://avivace.com/posts/gbdev-updates-nov2020/)
diff --git a/website/newsletter/1.md b/website/newsletter/1.md
deleted file mode 100644
index ca409bcd..00000000
--- a/website/newsletter/1.md
+++ /dev/null
@@ -1,144 +0,0 @@
-# The Gbdev Digest #1 - 2021 Recap
-
-
-
-Written by avivace, on behalf of the gbdev org. Originally published on "getrevue" on the 21st of February, 2022.
-
-
-
-Hey all and welcome to the first Issue of the new gbdev curated digest!
-Our idea is to prepare a collection of curated news about what is happening in our community, with a particular focus on our Open Source projects.
-Whatever you are interested in what’s happening the gbdev scene, new homebrew, tools, and documentation releases, find stuff where you can contribute with code or provide feedback, this newsletter is for you!
-
-## About us and our commitment to OSS
-
-The following is a quick clarification since this is the first issue of the Gbdev Digest. Feel free to shamelessly skip it.
->This newsletter is curated by the team behind the gbdev.io community, with a lot of help from members of other smaller communities and groups. We are directly involved with the development and maintaining of a lot of projects (e.g. Pan Docs, RGBDS, and Homebrew Hub), but of course not with everything that will be mentioned in this newsletter.
-
-The gbdev initiative promotes the development of tools, documentation and homebrew games. We also push research and emulation efforts while trying to keep everything as accessible as possible to everyone, no matter the technical background.
-Another big commitment is the archiving one: we are working on salvaging old and unavailable content, creating a digital memory of a scene that is now three decades old.
-
-> Since our inception in 2015, open source and free software values are at the core of our commitment. We release everything we work on under OSS licenses (or in the Public Domain), maintaining a strong non-profit approach. Even if we reshare and give exposure to commercial products related to gbdev, we don’t get any cash from any of the involved companies and we don’t sponsor or endorse any particular release or publisher. No referral links are ever shared through our channels.
->
-> Our expenses are entirely sustained by voluntary contributions (for which donators don’t get any benefit) and sponsorships (such as the DigitalOcean one). Everything is tracked trasparently on our OpenCollective.
->
-> While a core team of maintainers is behind every project, we welcome any type of contribution, from feedback to participation in Request For Comments threads. Check the bottom of this page to learn how to join our community.
-
-## Best year yet, for the 4th year in a row
-
-2021 was a huge success for gbdev.
-The Game Boy scene was never this alive and flourishing, with an unprecedented number of releases.
-From the incredible gbcompo21 (for which we are preparing a dedicated issue of this newsletter) to the number of new releases of our projects, we enjoyed the most traffic we ever saw, for the fourth year in a row.
-
-## New guide on choosing development tools
-
-We finished revamping a quite comprehensive guide on how to choose tools for Game Boy, giving an overview on the different available approaches to developing homebrews for the grey brick.
-
-https://gbdev.io/guides/tools.html
-
-## Homebrew Hub 2.0
-
->Homebrew Hub is a community-led attempt to collect, archive and preserve every unofficial game and homebrew released for Game Boy produced through decades of passionate work. Every entry , with its metadata and related assets is exposed through an API available to everyone.
-
-We started a complete rewrite of the backend and the frontend software behind Homebrew Hub. Originally built with Express in 2016 the codebase started to show its age…
-The new backend, powered by Django, will offer a full Restful API to access all the games, assets, metadata and ROMs we have in the JSON database.
-The new frontend is powered by the binjgb emulator, running in the browser via WebAssembly. This will bring better emulation accuracy and more features!
-
-## New games on the Homebrew Hub database
-
-> The Homebrew Hub database is community maintained. Everyone can contribute adding a game or writing a scraper to mass-import games on the website.
-
-In parallel to the rewrite, a lot of stuff is happening on the database, too:
-
-New CI scripts are now in place to detect potentially duplicated entries, calculating checksums of assets. The pipeline will now run on Pull Requests and also validate the submitted metadata and the referenced files, to aid contributors.
-
-The following new entries have been added: Crystal Lake, Grub Glide, Labirinth, Bannerprint and Europa Rescue (thanks to N•I•L, v666, reini1305, Exetric and godai78).
-
-All the 20 shortlisted entries from the GB Competition 2021 were also added and are ready to play.
-
-
-dag7dev worked on new scrapers, cleaned up a bunch of entries metadata and merged some duplicates. Hundreds of new homebrews are now available thanks to his work!
-
-A new game metadata schema is in preparation, enabling game translations and specific tags for hardware support (e.g. gb-printer, gb-camera, ..).
-
-The total number of games is now 748! Go play with them!
-
-## New RGBDS website and documentation
-
->RGBDS is the standard toolchain for developing homebrew Game Boy programs in Assembly. First released in 1997, it’s now enjoying renewed community attention.
-
-A new version of the RGBDS website, providing downloads, build instructions and full documentation is now work in progress.
-
-From a statically built HTML version we are migrating to a solution based on Docusaurus, a documentation framework powered by React.
-
-## RGBGFX rewrite incoming
-
-> The rgbgfx program, part of the RGBDS suite, converts PNG images into the Nintendo Game Boy’s planar tile format.
-
-A new version is being rewritten in modern C++17, enabling a series of improvements such as better error messages (more explicit), proper transparency support, explicit palette specification and proper 8×16 support.
-
-## GBDK 4.0.5 and 4.0.6
-
-> GBDK is a C compiler, assembler, linker and set of libraries for the Z80 like Nintendo Gameboy.
-
-Our friends over at the GBDK team worked on a big release, the first adding more targets to the toolchain: GBDK can now compile to Sega Master System/Sega Game Gear and the Analogue Pocket.
-
-4.0.6 shipped support for the Mega Duck, too!
-
-
-Other notable features added in 2021 were:
-
-- Metasprite API and conversion tool
-- Auto ROM bank assignment and packing
-- Compression API and console tool
-
-More information can be found in the release notes.
-
-## GBDK Tutorials
-
-For a long time now the main GBDK tutorials online have been mostly outdated and based on the 20 year old version GBDK but this is now changing: Larold is working on a series of tutorials where they break down the Game Boy game creation process into multiple high-level steps.
-
-https://laroldsjubilantjunkyard.com/tutorials/
-
-## GB Studio 3 is out
-
-GB Studio is a quick and easy to use drag and drop retro game creator for the Game Boy. No programming required.
-Here are some of the highlights from the new release:
-
-- Improved UI for adding Events
-- Math Expressions
-- Large Sprites
-- Sprite Editor
-- Animation States
-- New Music Engine (hUGETracker)
-- Music Editor
-- Multiple Save Slots
-- Parallax Scrolling
-- Super Game Boy Borders
-- Multiple Fonts
-
-You can learn more from the [dedicated article](https://gbstudiocentral.com/news/gb-studio-3-0-released) on GB Studio Central.
-
-## Contributions to other (non-gbdev) projects
-
-mdbook is the wonderful Rust tool we use to render Pan Docs. Apart from overloading it with custom features, we also contribute upstream.
-
-ISSOtm recently prepared a PR to add a feature for generating sitemaps, which we hope will get some attention from the upstream maintainers soon.
-
-https://github.com/rust-lang/mdBook/pull/1618
-
-## Wrapping up and feedback
-Thanks for reading! That’s all for this issue. We hope you’re having fun developing or playing games for Game Boy.
-As always, your contributions to our initiatives are appreciated. You’re welcome to browse around our GitHub organisation or join our Discord server to see what everyone is working on.
-
-If you still want more gbdev, our Twitter feed provides a curated (and frequent) collection of new releases, WIP content and news about our projects.
-
-If you have any feedback, want to send us some gbdev links or comment on any of the topics brought up in this issue (or on the newsletter itself) feel free to reach us.
-
-## Next Issue
-
-This digest won’t try to respect an actual schedule but will rather go online when enough quality content is ready. On the next issue, we will continue to shed some light on the incredible accomplishments we made in 2021, such as the GB Competition 2021, showing you how some games enjoyed continued support and physical releases. We will also tell you how we managed to make Pan Docs this good and what’s new in the latest RGBDS releases.
-
-If you’d like to send us some gbdev content you found interesting and you think would fit this newsletter, feel free to reach out.
-
-Special thanks to bbbbbr, ISSOtm, toxa and Emi Paternostro.
diff --git a/website/privacypolicy.md b/website/privacypolicy.md
deleted file mode 100644
index a0691d36..00000000
--- a/website/privacypolicy.md
+++ /dev/null
@@ -1,18 +0,0 @@
----
-sidebar: false
-editLink: false
----
-
-# Privacy Policy
-
-We use a self hosted instance of [Matomo](https://en.wikipedia.org/wiki/Matomo_(software)), a free and open source web analytics application to track online visits to our websites (`*.gbdev.io`, `*.gbadev.net`), in order to better understand which audience we serve and what content is consulted.
-
-### How
-
-- Visitors' IP addresses are **anonymized**. The last byte is masked and fully qualified addressess **never** reach our analytics software.
-- User IDs are replaced by **pseudonyms** to avoid directly storing and displaying personally identifiable information.
-- We ignore any existing tracking cookie and our tracking **DOES NOT use any cookie**. (See also: [What are cookies?](https://www.cookiesandyou.com/))
-- We honor your browser "I do not want to be tracked" preference.
-- Data is aggregated and reports are compiled. Raw logs are regularly cleaned up.
-
-You can opt out by clicking [here](https://stats.gbdev.io/index.php?module=CoreAdminHome&action=optOut&language=en).
diff --git a/website/to_c_or_not_to_c.md b/website/to_c_or_not_to_c.md
deleted file mode 100644
index b2918a8a..00000000
--- a/website/to_c_or_not_to_c.md
+++ /dev/null
@@ -1,20 +0,0 @@
----
-sidebar: false
-head:
- - - meta
- - http-equiv: refresh
- content: 10; url=https://gbdev.io/guides/tools.html
- - - link
- - rel: canonical
- href: https://gbdev.io/guides/tools.html
----
-
-# To C or not to C
-
-This guide has been moved to
-"[Choosing tools for Game Boy development](guides/tools.html)".
-
-Please update your bookmarks and ask other website operators
-to update inbound links.
-
-In 10 seconds, you'll be redirected.
diff --git a/yarn.lock b/yarn.lock
deleted file mode 100644
index 217151b6..00000000
--- a/yarn.lock
+++ /dev/null
@@ -1,2464 +0,0 @@
-# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
-# yarn lockfile v1
-
-
-"@babel/helper-string-parser@^7.29.7":
- version "7.29.7"
- resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz#7f0871d99824d23137d60f86fcf6130fd5a1b51f"
- integrity sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==
-
-"@babel/helper-validator-identifier@^7.29.7":
- version "7.29.7"
- resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz#bd87084ced0c796ec46bda492de6e83d29e89fc2"
- integrity sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==
-
-"@babel/parser@^7.28.5", "@babel/parser@^7.29.2", "@babel/parser@^7.29.8":
- version "7.29.9"
- resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.29.9.tgz#e7ee0a24f752c5296c6455855ccb72662e67ea96"
- integrity sha512-CjXrNHTnvqBVqHgdBysY3vk2T8tpJHb5/RMeHJBTyVa9xgugCB0CJTx/3oO8RV2QRQP391RWpB7D6hLjm8V9uA==
- dependencies:
- "@babel/types" "^7.29.8"
-
-"@babel/types@^7.29.0", "@babel/types@^7.29.8":
- version "7.29.8"
- resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.29.8.tgz#1229eef31d85156d70fa3f4cd859376d0eaf6863"
- integrity sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==
- dependencies:
- "@babel/helper-string-parser" "^7.29.7"
- "@babel/helper-validator-identifier" "^7.29.7"
-
-"@bufbuild/protobuf@^2.0.0":
- version "2.2.5"
- resolved "https://registry.yarnpkg.com/@bufbuild/protobuf/-/protobuf-2.2.5.tgz#8e82c0af292113b4a89f8b658c71c4636c8d2e36"
- integrity sha512-/g5EzJifw5GF8aren8wZ/G5oMuPoGeS6MQD3ca8ddcvdXR5UELUfdTZITCGNhNXynY/AYl3Z4plmxdj/tRl/hQ==
-
-"@jridgewell/gen-mapping@^0.3.5":
- version "0.3.13"
- resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz#6342a19f44347518c93e43b1ac69deb3c4656a1f"
- integrity sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==
- dependencies:
- "@jridgewell/sourcemap-codec" "^1.5.0"
- "@jridgewell/trace-mapping" "^0.3.24"
-
-"@jridgewell/remapping@^2.3.5":
- version "2.3.5"
- resolved "https://registry.yarnpkg.com/@jridgewell/remapping/-/remapping-2.3.5.tgz#375c476d1972947851ba1e15ae8f123047445aa1"
- integrity sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==
- dependencies:
- "@jridgewell/gen-mapping" "^0.3.5"
- "@jridgewell/trace-mapping" "^0.3.24"
-
-"@jridgewell/resolve-uri@^3.1.0":
- version "3.1.2"
- resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6"
- integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==
-
-"@jridgewell/sourcemap-codec@^1.4.14", "@jridgewell/sourcemap-codec@^1.5.5":
- version "1.6.0"
- resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.6.0.tgz#f4c663e862f06dc98ca4d453862c46902789a18d"
- integrity sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==
-
-"@jridgewell/sourcemap-codec@^1.5.0":
- version "1.5.0"
- resolved "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz"
- integrity sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==
-
-"@jridgewell/trace-mapping@^0.3.24":
- version "0.3.31"
- resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz#db15d6781c931f3a251a3dac39501c98a6082fd0"
- integrity sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==
- dependencies:
- "@jridgewell/resolve-uri" "^3.1.0"
- "@jridgewell/sourcemap-codec" "^1.4.14"
-
-"@mdit-vue/plugin-component@^3.0.2":
- version "3.0.2"
- resolved "https://registry.yarnpkg.com/@mdit-vue/plugin-component/-/plugin-component-3.0.2.tgz#bf26d37a770811943a38a9758605e918e523ff4d"
- integrity sha512-Fu53MajrZMOAjOIPGMTdTXgHLgGU9KwTqKtYc6WNYtFZNKw04euSfJ/zFg8eBY/2MlciVngkF7Gyc2IL7e8Bsw==
- dependencies:
- "@types/markdown-it" "^14.1.2"
- markdown-it "^14.1.0"
-
-"@mdit-vue/plugin-frontmatter@^3.0.2":
- version "3.0.2"
- resolved "https://registry.yarnpkg.com/@mdit-vue/plugin-frontmatter/-/plugin-frontmatter-3.0.2.tgz#2f0b73e8b103b6aa79253e143407ce9299f839ff"
- integrity sha512-QKKgIva31YtqHgSAz7S7hRcL7cHXiqdog4wxTfxeQCHo+9IP4Oi5/r1Y5E93nTPccpadDWzAwr3A0F+kAEnsVQ==
- dependencies:
- "@mdit-vue/types" "3.0.2"
- "@types/markdown-it" "^14.1.2"
- gray-matter "^4.0.3"
- markdown-it "^14.1.0"
-
-"@mdit-vue/plugin-headers@^3.0.2":
- version "3.0.2"
- resolved "https://registry.yarnpkg.com/@mdit-vue/plugin-headers/-/plugin-headers-3.0.2.tgz#17f41ce4f461ff3d3d52f72fc697ce5b00ab3a00"
- integrity sha512-Z3PpDdwBTO5jlW2r617tQibkwtCc5unTnj/Ew1SCxTQaXjtKgwP9WngdSN+xxriISHoNOYzwpoUw/1CW8ntibA==
- dependencies:
- "@mdit-vue/shared" "3.0.2"
- "@mdit-vue/types" "3.0.2"
- "@types/markdown-it" "^14.1.2"
- markdown-it "^14.1.0"
-
-"@mdit-vue/plugin-sfc@^3.0.2":
- version "3.0.2"
- resolved "https://registry.yarnpkg.com/@mdit-vue/plugin-sfc/-/plugin-sfc-3.0.2.tgz#80084da4a19d387e75048ea54dc4c030609255e7"
- integrity sha512-dhxIrCGu5Nd4Cgo9JJHLjdNy2lMEv+LpimetBHDSeEEJxJBC4TPN0Cljn+3/nV1uJdGyw33UZA86PGdgt1LsoA==
- dependencies:
- "@mdit-vue/types" "3.0.2"
- "@types/markdown-it" "^14.1.2"
- markdown-it "^14.1.0"
-
-"@mdit-vue/plugin-title@^3.0.2":
- version "3.0.2"
- resolved "https://registry.yarnpkg.com/@mdit-vue/plugin-title/-/plugin-title-3.0.2.tgz#265f942794466105a680a327c69503e43e34630d"
- integrity sha512-KTDP7s68eKTwy4iYp5UauQuVJf+tDMdJZMO6K4feWYS8TX95ItmcxyX7RprfBWLTUwNXBYOifsL6CkIGlWcNjA==
- dependencies:
- "@mdit-vue/shared" "3.0.2"
- "@mdit-vue/types" "3.0.2"
- "@types/markdown-it" "^14.1.2"
- markdown-it "^14.1.0"
-
-"@mdit-vue/plugin-toc@^3.0.2":
- version "3.0.2"
- resolved "https://registry.yarnpkg.com/@mdit-vue/plugin-toc/-/plugin-toc-3.0.2.tgz#a768b3b22e1045669463ea5b3f44b6421da3ae2a"
- integrity sha512-Dz0dURjD5wR4nBxFMiqb0BTGRAOkCE60byIemqLqnkF6ORKKJ8h5aLF5J5ssbLO87hwu81IikHiaXvqoiEneoQ==
- dependencies:
- "@mdit-vue/shared" "3.0.2"
- "@mdit-vue/types" "3.0.2"
- "@types/markdown-it" "^14.1.2"
- markdown-it "^14.1.0"
-
-"@mdit-vue/shared@3.0.2", "@mdit-vue/shared@^3.0.2":
- version "3.0.2"
- resolved "https://registry.yarnpkg.com/@mdit-vue/shared/-/shared-3.0.2.tgz#dbe3ab5bad165c0b9cdba62dbd3c3a8116e2997b"
- integrity sha512-anFGls154h0iVzUt5O43EaqYvPwzfUxQ34QpNQsUQML7pbEJMhcgkRNvYw9hZBspab+/TP45agdPw5joh6/BBA==
- dependencies:
- "@mdit-vue/types" "3.0.2"
- "@types/markdown-it" "^14.1.2"
- markdown-it "^14.1.0"
-
-"@mdit-vue/types@3.0.2", "@mdit-vue/types@^3.0.2":
- version "3.0.2"
- resolved "https://registry.yarnpkg.com/@mdit-vue/types/-/types-3.0.2.tgz#bcc569c5435ecb38b750a58ce69e555cf0021120"
- integrity sha512-00aAZ0F0NLik6I6Yba2emGbHLxv+QYrPH00qQ5dFKXlAo1Ll2RHDXwY7nN2WAfrx2pP+WrvSRFTGFCNGdzBDHw==
-
-"@mdit/helper@1.1.2":
- version "1.1.2"
- resolved "https://registry.yarnpkg.com/@mdit/helper/-/helper-1.1.2.tgz#5610af5b86a35dd312abdba2f40733cbd1c11cb7"
- integrity sha512-xZywQ7aXrmkRCuGvFXguXVKeiQ7kHCxPkjtR+nK/n31YiSPPi/Rmb/ll+CYt0LdyeXR0Y1I16da7gxDT4/VXWg==
-
-"@mdit/plugin-alert@^2.0.1":
- version "2.0.2"
- resolved "https://registry.yarnpkg.com/@mdit/plugin-alert/-/plugin-alert-2.0.2.tgz#3f34375a15bd7bc6e715b6acd2089cb4e7b554fc"
- integrity sha512-0vjhfT58lKE5BDkrdpXdsX4jzRq2zDhlVqQNcgAOAatjG+Tq7k8eCQZBle+10lzNegqpWRgszpu2BSS4df7luw==
- dependencies:
- "@mdit/helper" "1.1.2"
-
-"@mdit/plugin-container@^2.0.1":
- version "2.0.2"
- resolved "https://registry.yarnpkg.com/@mdit/plugin-container/-/plugin-container-2.0.2.tgz#e888fdd960d396d599905050bd34bc544960474e"
- integrity sha512-hccAp7qxFVlv2vTFetTU9Ay6I3jGKyJJkdXR57zgUAWpJnW9Hp+Ig9nC2p19e/FNnBoBJLLiL8NWZQj2shEoXA==
- dependencies:
- "@mdit/helper" "1.1.2"
-
-"@mdit/plugin-tab@^2.0.1":
- version "2.0.2"
- resolved "https://registry.yarnpkg.com/@mdit/plugin-tab/-/plugin-tab-2.0.2.tgz#c1721dc8f60eb46edc22d645e066b764cffceb5c"
- integrity sha512-bdRa1cd6Gfe0ZVUCXBZHEVcCwWz2Pa8NrFc1ucMbsHF1AEfZapPFHkDTM0NggriV5oguTiKVJFbq3Qrvz9STNA==
- dependencies:
- "@mdit/helper" "1.1.2"
-
-"@oxc-project/types@=0.151.0":
- version "0.151.0"
- resolved "https://registry.yarnpkg.com/@oxc-project/types/-/types-0.151.0.tgz#37206ed3fdebb8c4c5fc49ae20d53d9b4eed2bfe"
- integrity sha512-J1yXrIlNDZVzE3ada310xeAw7nH8yCAyLPuUIsjKatFPmfn5bS1oW+cM+QsGOtVWd5nhSpbwZWx/rue+r5Z+PA==
-
-"@pkgr/core@^0.3.6":
- version "0.3.6"
- resolved "https://registry.yarnpkg.com/@pkgr/core/-/core-0.3.6.tgz#3569708bd4be4d8870ba32bf1c456dac81600d97"
- integrity sha512-SEeaJLb3qBNF/OaXnaR1NmmBbFYk1zC0ZH/52fATcRPLFg/p791YrcyFFy44Bo9sLaGuSuLp5Q6axbb/O+v/RA==
-
-"@rolldown/binding-android-arm-eabi@1.2.11":
- version "1.2.11"
- resolved "https://registry.yarnpkg.com/@rolldown/binding-android-arm-eabi/-/binding-android-arm-eabi-1.2.11.tgz#898b1c0c0c4de1ffb5997050a705f04cc102b4eb"
- integrity sha512-A5kXfGKvKWWZE0TtPrfsvT+q4Y5d1QG8gGUzpYjGydM+fARM9MuX90PrXYXe0XbsDVgyxxNzHo6giCj90bsFNw==
-
-"@rolldown/binding-android-arm64@1.2.11":
- version "1.2.11"
- resolved "https://registry.yarnpkg.com/@rolldown/binding-android-arm64/-/binding-android-arm64-1.2.11.tgz#bf8ee88baaecd1747326d6c1d05ebdad483edc34"
- integrity sha512-z6cTycz+iJ4PVkuL4HHW4DfTfoeU/2nqYYuSOrTmH7yHK5Y0LCOnA03V4ZNxavyVaU1oOqUgIg2klN/s+USGOA==
-
-"@rolldown/binding-darwin-arm64@1.2.11":
- version "1.2.11"
- resolved "https://registry.yarnpkg.com/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.2.11.tgz#4ebc103da1e0d6b24d815c1fb1d6af73245c1b1f"
- integrity sha512-jShvqNtP6vDC6/A5JOAzbVV+DkgHqhl/ScVCJEbt+TUY6QYz7YnXcrg3sLtFBniro0f/Ld50ZwCWA6f7KYD1nQ==
-
-"@rolldown/binding-darwin-x64@1.2.11":
- version "1.2.11"
- resolved "https://registry.yarnpkg.com/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.2.11.tgz#ab669b448eb2cd2366a7ff01a84579a0f854f5f0"
- integrity sha512-f2i2xiNWq1Z1l2++q2fuhZRdLAT3aqxD6vRNm1RAxpUoBcdqNB3C0s1Bt+K+PbEx2F5F4gQp6hqKkphCY/xF9w==
-
-"@rolldown/binding-freebsd-x64@1.2.11":
- version "1.2.11"
- resolved "https://registry.yarnpkg.com/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.2.11.tgz#74c26ec877c293c47c8958ab58e07e57f9382070"
- integrity sha512-4Ir5FSOKIAMr4r0kExpt1s3bMgzJU3rA45AYOHtQpls0oNeqcYBKrWMlckrYH4KCfGLfkfn1tN1dmZPMVsdXow==
-
-"@rolldown/binding-linux-arm-gnueabihf@1.2.11":
- version "1.2.11"
- resolved "https://registry.yarnpkg.com/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.2.11.tgz#cbfe844867d7dbce1655c9b593be05bcaffb501d"
- integrity sha512-/gnRDM+39BROzAN/k1OZjDPnDMcZxB/0EUxKjONO5yVkNEvlsoMDrxGNKgZi/ttFriS2gwlDNzB65pvNbFOXIQ==
-
-"@rolldown/binding-linux-arm64-gnu@1.2.11":
- version "1.2.11"
- resolved "https://registry.yarnpkg.com/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.2.11.tgz#2028ebac4a3f56b26ecd02f8850c74447087cbb2"
- integrity sha512-PFaK8HwvAHbaKbBcDNQihjMKYvFnA5hiENx/l5tphTDz1E0WFp32l0A7aq7lyUwGsRw/xSrNIy/gIK4thrSCrw==
-
-"@rolldown/binding-linux-arm64-musl@1.2.11":
- version "1.2.11"
- resolved "https://registry.yarnpkg.com/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.2.11.tgz#a143325f96d5d5040dff50e292374f7467e7c82c"
- integrity sha512-AskzJUIKRLPxkruR1wLKewGbOw+EYfU/9lOrBFj4AFrEA8hPpKFnODWNu2WLaNs0QNkEb9QIJufmVZZIL/bJlg==
-
-"@rolldown/binding-linux-ppc64-gnu@1.2.11":
- version "1.2.11"
- resolved "https://registry.yarnpkg.com/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.2.11.tgz#b38937ad8b4821682e3671ebfb609cb0e3ecb935"
- integrity sha512-qlUGAheh2yh8afH7QBgx0PrRHN85hKnNd78x8MeMhXivuevgd8vgf6/CstOzmNKY/lLTHvNTrPy98cLnAugzJw==
-
-"@rolldown/binding-linux-s390x-gnu@1.2.11":
- version "1.2.11"
- resolved "https://registry.yarnpkg.com/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.2.11.tgz#6be2645a1ad89185e8d03f69116798c94eae19ec"
- integrity sha512-secpEad+0vCbSfn8upFySkDskv+bGPk3THSDS9Y89yc4rb4kzqHp8Dmyd9BkQW4SnhNXBZCl/6CrO//hZahNJQ==
-
-"@rolldown/binding-linux-x64-gnu@1.2.11":
- version "1.2.11"
- resolved "https://registry.yarnpkg.com/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.2.11.tgz#fbc9a6a6c3b3db00e35573c67894bfbaa3128040"
- integrity sha512-mOVBT3dPpkWm8XBWPmU4bf+U6dYDLeMo/9ojUmis4N0L5uu10qra5vOyngZ7/PSdoE4G9KvRt4bloRxNjLas7A==
-
-"@rolldown/binding-linux-x64-musl@1.2.11":
- version "1.2.11"
- resolved "https://registry.yarnpkg.com/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.2.11.tgz#f3f07a30c03d1d3a673b21fd4eaba84979aa4fd4"
- integrity sha512-Is78i9A8Ui4SqcxUwFJ9uMmjDn58IbVTjFWYdQestFEgeuEmHMLGNriXnVJKkwG2YiZjw8cP0zCTyDMdDGtOOg==
-
-"@rolldown/binding-openharmony-arm64@1.2.11":
- version "1.2.11"
- resolved "https://registry.yarnpkg.com/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.2.11.tgz#c3848c61dd3b15edde9c98dfa8d50edb356ec71f"
- integrity sha512-dUCXneZ87INUMyQ0D+C0HrEBNUPNXHaPmU5GTjyKTJEiussw9Kaj5Ln8UztPe4epV/ffvgNBEadksdYhmW6xJA==
-
-"@rolldown/binding-win32-arm64-msvc@1.2.11":
- version "1.2.11"
- resolved "https://registry.yarnpkg.com/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.2.11.tgz#99d9f7eb53b26f09212d646e9f50708a2f14414f"
- integrity sha512-jByxb6qfd+bH1xUd0qnfFnb17i9sWBPY2tOavJ0l3tdr3OTu+Kvtm8cd/JV5nFt657b1VqGltxg9olOEfofXWw==
-
-"@rolldown/binding-win32-x64-msvc@1.2.11":
- version "1.2.11"
- resolved "https://registry.yarnpkg.com/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.2.11.tgz#c7bf663043c01021f63122a8499e9aca36408467"
- integrity sha512-/PzKqzAJ03i19oy2ItPvyvaVjOjBCNnfaJs8yvUdGBKmiESgnrJSQ2awd81QzFbbnAmu7YO9ZnJrDCb9VSJPRA==
-
-"@rolldown/pluginutils@^1.0.0", "@rolldown/pluginutils@^1.0.1":
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz#e3fcee093fbb5ce765e1ad088ff4de2889f6f9be"
- integrity sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==
-
-"@shikijs/core@4.4.3":
- version "4.4.3"
- resolved "https://registry.yarnpkg.com/@shikijs/core/-/core-4.4.3.tgz#00a942fa45ad0e4146ac6dbbac32b8b704b42e3f"
- integrity sha512-QCR4q2ZO/ILJEuwiBMel4wdcTDb1JGwfjKTxPDF6x8ixOaluPrVqIn06C99AcRPhmYlBR56d/Fb+GN58GzExpg==
- dependencies:
- "@shikijs/primitive" "4.4.3"
- "@shikijs/types" "4.4.3"
- "@shikijs/vscode-textmate" "^10.0.2"
- "@types/hast" "^3.0.5"
- hast-util-to-html "^9.0.5"
-
-"@shikijs/engine-javascript@4.4.3":
- version "4.4.3"
- resolved "https://registry.yarnpkg.com/@shikijs/engine-javascript/-/engine-javascript-4.4.3.tgz#42dbdc18ec2f86003624674839a8f090cdd7cb62"
- integrity sha512-FbOjFJp9VLdo1Wevs10BBtVxiTWwNLqZh5Gkhjgda/ioL15YOgeSl9n+6XMa3qRlPQzfhFNe641SrynFHYG0nQ==
- dependencies:
- "@shikijs/types" "4.4.3"
- "@shikijs/vscode-textmate" "^10.0.2"
- oniguruma-to-es "^4.3.6"
-
-"@shikijs/engine-oniguruma@4.4.3":
- version "4.4.3"
- resolved "https://registry.yarnpkg.com/@shikijs/engine-oniguruma/-/engine-oniguruma-4.4.3.tgz#a1754f9f42e0f35a55cda9a977599041a2ad5b07"
- integrity sha512-EcOQkxdxGQrc1Row/cC2c96/v1dbZqGnEVu1qTuT/MJmp6+cXCvQussowVmCv5Tqr3KuY3c7IbM6HTW3LJ1k9w==
- dependencies:
- "@shikijs/types" "4.4.3"
- "@shikijs/vscode-textmate" "^10.0.2"
-
-"@shikijs/langs@4.4.3":
- version "4.4.3"
- resolved "https://registry.yarnpkg.com/@shikijs/langs/-/langs-4.4.3.tgz#113282396f119dbba8d3b5e86668258fa8df6e7b"
- integrity sha512-ePic0yfAJGOF83D5wBHK/00EjK65oahBYxFk5epgq33WRv7X9UuxLEV8PtR0szC0z8dl7INIpIodB99JRFlR+A==
- dependencies:
- "@shikijs/types" "4.4.3"
-
-"@shikijs/primitive@4.4.3":
- version "4.4.3"
- resolved "https://registry.yarnpkg.com/@shikijs/primitive/-/primitive-4.4.3.tgz#86490cea63b3e2c56b8d9163046e010258844d81"
- integrity sha512-m0wBeLDQDeIxRdUmrCPdQqfuUamDwRL5isCfYbguKD6NiaKpVbsv+3J81DyIKgNW5h4WAIIr8T4EkgQrBBxvaQ==
- dependencies:
- "@shikijs/types" "4.4.3"
- "@shikijs/vscode-textmate" "^10.0.2"
- "@types/hast" "^3.0.5"
-
-"@shikijs/themes@4.4.3":
- version "4.4.3"
- resolved "https://registry.yarnpkg.com/@shikijs/themes/-/themes-4.4.3.tgz#8310a78261f4cf742663e07e2028df046a02bd72"
- integrity sha512-w8UHjeUnIR965KMWJHUPXOc2mNJUnK3vpVLYLvw5IYU2mnTTJ89E24OrJDBNiJDQ0qzb0tc4l7mrIXx5cFeIyw==
- dependencies:
- "@shikijs/types" "4.4.3"
-
-"@shikijs/transformers@^4.4.3":
- version "4.4.3"
- resolved "https://registry.yarnpkg.com/@shikijs/transformers/-/transformers-4.4.3.tgz#18869d95b0e2656fa7ae98350e09408c57585018"
- integrity sha512-oJSARV6NaWd+rnNJbtnpAdj3Zg0ZVyzsnMgb3vi3HA+35y8lBWUCpOnWsmyiXZIikY+x1BDqrQUgmxfzWh7Jvw==
- dependencies:
- "@shikijs/core" "4.4.3"
- "@shikijs/types" "4.4.3"
-
-"@shikijs/types@4.4.3":
- version "4.4.3"
- resolved "https://registry.yarnpkg.com/@shikijs/types/-/types-4.4.3.tgz#019aff19f0cbfb21642c59f6f8432ced74e27b45"
- integrity sha512-UEJxmRR++MAGR6hugn0vgVS2W/6lWAts84FFSrnlH9sP0LNol7E5+NQ792pH8liWUhyMyjhTgSUH3k7iD7tc5g==
- dependencies:
- "@shikijs/vscode-textmate" "^10.0.2"
- "@types/hast" "^3.0.5"
-
-"@shikijs/vscode-textmate@^10.0.2":
- version "10.0.2"
- resolved "https://registry.npmjs.org/@shikijs/vscode-textmate/-/vscode-textmate-10.0.2.tgz"
- integrity sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==
-
-"@types/debug@^4.1.13":
- version "4.1.13"
- resolved "https://registry.yarnpkg.com/@types/debug/-/debug-4.1.13.tgz#22d1cc9d542d3593caea764f974306ab36286ee7"
- integrity sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==
- dependencies:
- "@types/ms" "*"
-
-"@types/fs-extra@^11.0.4":
- version "11.0.4"
- resolved "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-11.0.4.tgz"
- integrity sha512-yTbItCNreRooED33qjunPthRcSjERP1r4MqCZc7wv0u2sUkzTFp45tgUfS5+r7FrZPdmCCNflLhVSP/o+SemsQ==
- dependencies:
- "@types/jsonfile" "*"
- "@types/node" "*"
-
-"@types/hash-sum@^1.0.2":
- version "1.0.2"
- resolved "https://registry.npmjs.org/@types/hash-sum/-/hash-sum-1.0.2.tgz"
- integrity sha512-UP28RddqY8xcU0SCEp9YKutQICXpaAq9N8U2klqF5hegGha7KzTOL8EdhIIV3bOSGBzjEpN9bU/d+nNZBdJYVw==
-
-"@types/hast@^3.0.0":
- version "3.0.4"
- resolved "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz"
- integrity sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==
- dependencies:
- "@types/unist" "*"
-
-"@types/hast@^3.0.5":
- version "3.0.5"
- resolved "https://registry.yarnpkg.com/@types/hast/-/hast-3.0.5.tgz#48020de4c0e63492f4ca9db42068c108f68b7f8f"
- integrity sha512-rp/ezSWaD1m44dPKICGhiskI13nVr7qTloFwDa/IYkhhf5nzwP+zIQcIJh3WIFSBOy/H1PzB40jPjMDksN4F+g==
- dependencies:
- "@types/unist" "*"
-
-"@types/jsonfile@*":
- version "6.1.4"
- resolved "https://registry.npmjs.org/@types/jsonfile/-/jsonfile-6.1.4.tgz"
- integrity sha512-D5qGUYwjvnNNextdU59/+fI+spnwtTFmyQP0h+PfIOSkNfpU6AOICUOkm4i0OnSk+NyjdPJrxCDro0sJsWlRpQ==
- dependencies:
- "@types/node" "*"
-
-"@types/linkify-it@^5":
- version "5.0.0"
- resolved "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-5.0.0.tgz"
- integrity sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==
-
-"@types/markdown-it-emoji@^3.0.1":
- version "3.0.1"
- resolved "https://registry.npmjs.org/@types/markdown-it-emoji/-/markdown-it-emoji-3.0.1.tgz"
- integrity sha512-cz1j8R35XivBqq9mwnsrP2fsz2yicLhB8+PDtuVkKOExwEdsVBNI+ROL3sbhtR5occRZ66vT0QnwFZCqdjf3pA==
- dependencies:
- "@types/markdown-it" "^14"
-
-"@types/markdown-it@^14", "@types/markdown-it@^14.1.2":
- version "14.1.2"
- resolved "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.1.2.tgz"
- integrity sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==
- dependencies:
- "@types/linkify-it" "^5"
- "@types/mdurl" "^2"
-
-"@types/markdown-it@^14.2.0":
- version "14.2.0"
- resolved "https://registry.yarnpkg.com/@types/markdown-it/-/markdown-it-14.2.0.tgz#4074873f98fbc791131e31d7446b12dfa3a31df9"
- integrity sha512-NoQ2yGlLWj4wpxMs+TYmRKk3thDrQ97agr7sFqfLsAlvoS8SNQuTrlObhFqG9iugdTtgOE9jpJ6FNM4ZGsa5xQ==
- dependencies:
- "@types/linkify-it" "^5"
- "@types/mdurl" "^2"
-
-"@types/mdast@^4.0.0":
- version "4.0.4"
- resolved "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz"
- integrity sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==
- dependencies:
- "@types/unist" "*"
-
-"@types/mdurl@^2":
- version "2.0.0"
- resolved "https://registry.npmjs.org/@types/mdurl/-/mdurl-2.0.0.tgz"
- integrity sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==
-
-"@types/ms@*":
- version "2.1.0"
- resolved "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz"
- integrity sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==
-
-"@types/node@*":
- version "22.13.16"
- resolved "https://registry.npmjs.org/@types/node/-/node-22.13.16.tgz"
- integrity sha512-15tM+qA4Ypml/N7kyRdvfRjBQT2RL461uF1Bldn06K0Nzn1lY3nAPgHlsVrJxdZ9WhZiW0Fmc1lOYMtDsAuB3w==
- dependencies:
- undici-types "~6.20.0"
-
-"@types/node@^24.9.2":
- version "24.19.0"
- resolved "https://registry.yarnpkg.com/@types/node/-/node-24.19.0.tgz#becd0549e091f749520b8db8a446ec1559c88a0b"
- integrity sha512-zY+5tKxXdhGh1PYI0ac+7juvEu4OI6vWtVVoj5i2m42jxAY1U+zHGt6QCyOFwykdP62sM3MJ9stoYYUw5aCWew==
- dependencies:
- undici-types ">=7.24.0 <7.24.7"
-
-"@types/picomatch@^4.0.3":
- version "4.0.3"
- resolved "https://registry.yarnpkg.com/@types/picomatch/-/picomatch-4.0.3.tgz#d92bbf24166478c5457c8bb9ba41a7f49d55e094"
- integrity sha512-iG0T6+nYJ9FAPmx9SsUlnwcq1ZVRuCXcVEvWnntoPlrOpwtSTKNDC9uVAxTsC3PUvJ+99n4RpAcNgBbHX3JSnQ==
-
-"@types/sax@^1.2.1":
- version "1.2.7"
- resolved "https://registry.npmjs.org/@types/sax/-/sax-1.2.7.tgz"
- integrity sha512-rO73L89PJxeYM3s3pPPjiPgVVcymqU490g0YO5n5By0k2Erzj6tay/4lr1CHAAU4JyOWd1rpQ8bCf6cZfHU96A==
- dependencies:
- "@types/node" "*"
-
-"@types/unist@*", "@types/unist@^3.0.0":
- version "3.0.3"
- resolved "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz"
- integrity sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==
-
-"@types/web-bluetooth@^0.0.21":
- version "0.0.21"
- resolved "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.21.tgz"
- integrity sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA==
-
-"@ungap/structured-clone@^1.0.0":
- version "1.3.0"
- resolved "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz"
- integrity sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==
-
-"@vitejs/plugin-vue@^6.0.8":
- version "6.0.9"
- resolved "https://registry.yarnpkg.com/@vitejs/plugin-vue/-/plugin-vue-6.0.9.tgz#d6a6d288dd307d086dbf8d3fcccb8c378c473a45"
- integrity sha512-rD/MORlhaZMlXWW0rEn4FB4wMinWC0z/D7Ye160S5+Rs1mC8ZDAdcDp4SjKfUhG5t8q1ktcPVw4xuTkPRlzrSA==
- dependencies:
- "@rolldown/pluginutils" "^1.0.1"
-
-"@vue-macros/common@^3.1.3":
- version "3.1.4"
- resolved "https://registry.yarnpkg.com/@vue-macros/common/-/common-3.1.4.tgz#cb56f0a84bdbfacbfa640ac34d9f805667adac8c"
- integrity sha512-/5Fv+6DgIcM9ajY05ZmKBv+LMX1M9A0X+IUwDRVdt67ciw8OV9bvG2r34p3RiEadlsQybjhKPRKNXDC8Bp23cw==
- dependencies:
- "@vue/compiler-sfc" "^3.5.22"
- ast-kit "^2.1.2"
- local-pkg "^1.1.2"
- magic-string-ast "^1.0.2"
- unplugin-utils "^0.3.0"
-
-"@vue/compiler-core@3.5.43":
- version "3.5.43"
- resolved "https://registry.yarnpkg.com/@vue/compiler-core/-/compiler-core-3.5.43.tgz#ff792a8bb20d29fe2a9f013d5735d39c7808c9a5"
- integrity sha512-zdiLhnbe1QQqgDT8xZMpNmyqZ3qlI+/Q/FHQco57Kwl/b05HhCzN6eVGN9QU9rbga4CrS0H5SYY8VGHZCt/1Hg==
- dependencies:
- "@babel/parser" "^7.29.8"
- "@vue/shared" "3.5.43"
- entities "^7.0.1"
- estree-walker "^2.0.2"
- source-map-js "^1.2.1"
-
-"@vue/compiler-dom@3.5.43":
- version "3.5.43"
- resolved "https://registry.yarnpkg.com/@vue/compiler-dom/-/compiler-dom-3.5.43.tgz#1bec85b4c3078544b278524e521311a6202d673f"
- integrity sha512-PEZoAk3NQmsn/ejMzSOCyTYqwGqczrWm70PuhBKjjv1+TCoQAaO/zOqNwjV+honlNstT5ILxtc+8r8UUfj+iEQ==
- dependencies:
- "@vue/compiler-core" "3.5.43"
- "@vue/shared" "3.5.43"
-
-"@vue/compiler-sfc@3.5.43", "@vue/compiler-sfc@^3.5.22":
- version "3.5.43"
- resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-3.5.43.tgz#19c097f398abacfee9627646075a919d2f57a6ae"
- integrity sha512-FCbrG3XNCRl+js3huuKx4IVHBLTvMkJhVepjbxSPu1gn4yWLaYtGNQdjJGZaMytXB6qb76qQDDmDSLy/vkmleQ==
- dependencies:
- "@babel/parser" "^7.29.8"
- "@vue/compiler-core" "3.5.43"
- "@vue/compiler-dom" "3.5.43"
- "@vue/compiler-ssr" "3.5.43"
- "@vue/shared" "3.5.43"
- estree-walker "^2.0.2"
- magic-string "^0.30.21"
- postcss "^8.5.28"
- source-map-js "^1.2.1"
-
-"@vue/compiler-ssr@3.5.43":
- version "3.5.43"
- resolved "https://registry.yarnpkg.com/@vue/compiler-ssr/-/compiler-ssr-3.5.43.tgz#aacd1d047545d51d5cb0eca9aa3f490507bf7040"
- integrity sha512-GF62orf7KiJX9RqrHNGrYBudsQGD0OhJ5nDs90O8UiDuS40+YMYomiXu6w6EuvtXuRDc3MSNis3EaaSKAVSWpg==
- dependencies:
- "@vue/compiler-dom" "3.5.43"
- "@vue/shared" "3.5.43"
-
-"@vue/devtools-api@^8.1.5", "@vue/devtools-api@^8.2.1":
- version "8.2.1"
- resolved "https://registry.yarnpkg.com/@vue/devtools-api/-/devtools-api-8.2.1.tgz#9d95de2b908aa80b9957d737ddca51790e70c3b5"
- integrity sha512-6u4vXBlIBAC1wMplIZgpyPn7uh/s4Bf6F5bMzvLv+EdJ0aHs/+4B7Ygv864EStQSjRbsRzTko/kUG1A1IejQ3A==
- dependencies:
- "@vue/devtools-kit" "^8.2.1"
-
-"@vue/devtools-kit@^8.1.5", "@vue/devtools-kit@^8.2.1":
- version "8.2.1"
- resolved "https://registry.yarnpkg.com/@vue/devtools-kit/-/devtools-kit-8.2.1.tgz#ad45babb12c51931d32d1b67ffaebc251f550ce9"
- integrity sha512-FIGIuq3AWReEpbAHY/cRGeHDfI0qOb8OCQ3YjbEAX04uaxIDbGc9rhkbVcG7rnfHPXE3RsU5KrWOu9V/okd8AQ==
- dependencies:
- "@vue/devtools-shared" "^8.2.1"
- birpc "^2.6.1"
- hookable "^5.5.3"
- perfect-debounce "^2.0.0"
-
-"@vue/devtools-shared@^8.2.1":
- version "8.2.1"
- resolved "https://registry.yarnpkg.com/@vue/devtools-shared/-/devtools-shared-8.2.1.tgz#26524e9e12fd205bcd5477cf3b5e9a17b876aeac"
- integrity sha512-Fkac7lUdGReh6pVOi3AYPRGe82LQqRmAfThW7RRligOAP0ZA/Z1z9XLHDM9dv34pV2HRc79DK8uKPeG2fLnA/g==
-
-"@vue/reactivity@3.5.43":
- version "3.5.43"
- resolved "https://registry.yarnpkg.com/@vue/reactivity/-/reactivity-3.5.43.tgz#26441233103e834616959a032d8fabd4524fd089"
- integrity sha512-G/c9GyOZNI2jVaaS6OX1EF1SSFSv7H0ERqNTl4+DTFMlZmB5eVAB53aLQNam/7NL2NPtaDD7RdVrzf8uJzMuOA==
- dependencies:
- "@vue/shared" "3.5.43"
-
-"@vue/runtime-core@3.5.43":
- version "3.5.43"
- resolved "https://registry.yarnpkg.com/@vue/runtime-core/-/runtime-core-3.5.43.tgz#eca01c87c32047d32f3639c1a9206d368abe0c46"
- integrity sha512-hU6U6VnVhBGQDpvlnnDlIB8ZGJBiOcgk2lh/0InltHiz3D8oSkluvuvY+do1G2H3+udeKFsmaBlgVYP7gXQzEw==
- dependencies:
- "@vue/reactivity" "3.5.43"
- "@vue/shared" "3.5.43"
-
-"@vue/runtime-dom@3.5.43":
- version "3.5.43"
- resolved "https://registry.yarnpkg.com/@vue/runtime-dom/-/runtime-dom-3.5.43.tgz#c40a898bd1ce11697741b9a079414a803413028a"
- integrity sha512-Bb2Jc0YjjJdMt1SJmb9b2L/IWd3I8lIT9x9eS/xvvP9CiVgna0ffua74xKRmt4/uSJ+0r4iN8ex1jrqkhQGWQw==
- dependencies:
- "@vue/reactivity" "3.5.43"
- "@vue/runtime-core" "3.5.43"
- "@vue/shared" "3.5.43"
- csstype "^3.2.3"
-
-"@vue/server-renderer@3.5.43":
- version "3.5.43"
- resolved "https://registry.yarnpkg.com/@vue/server-renderer/-/server-renderer-3.5.43.tgz#ae2b84b832821e39c3355705e1c269f5a7d29b3c"
- integrity sha512-l2Ygjv9NehV94PSBxNWsAHC0j/eIIKbn92mBuWXAPGLnn6HfJ6MH5ubsd+Nk0YoZ5FRuxWI1P2VSoh+dbPQhCQ==
- dependencies:
- "@vue/compiler-ssr" "3.5.43"
- "@vue/runtime-dom" "3.5.43"
- "@vue/shared" "3.5.43"
-
-"@vue/shared@3.5.43", "@vue/shared@^3.5.43":
- version "3.5.43"
- resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.5.43.tgz#22c11ecf1304bfc6f6b745f3b4ba54c410074338"
- integrity sha512-uksS7YGMR5NZyr4JNq0Rp+QyLns0ueaz20KwzIPW9R0LH1Vnt4E+XUM29PNseEbf1www2gOuhuDi5AKOIXag9Q==
-
-"@vuepress/bundler-vite@^2.0.0-rc.31":
- version "2.0.0-rc.31"
- resolved "https://registry.yarnpkg.com/@vuepress/bundler-vite/-/bundler-vite-2.0.0-rc.31.tgz#d20c83f9173ff570744114e40c109bb99bb68c1b"
- integrity sha512-4ptpbmDvAf9KvME7xY4OhYT4M/fAyk4qnP97jl5+kdRkOcv/XsRW2WKhP2lC8ckr9hMwVoRdBRE2/qgrtX+iDQ==
- dependencies:
- "@vitejs/plugin-vue" "^6.0.8"
- "@vuepress/bundlerutils" "2.0.0-rc.31"
- "@vuepress/client" "2.0.0-rc.31"
- "@vuepress/core" "2.0.0-rc.31"
- "@vuepress/shared" "2.0.0-rc.31"
- "@vuepress/utils" "2.0.0-rc.31"
- autoprefixer "^10.5.4"
- connect-history-api-fallback "^2.0.0"
- postcss "^8.5.22"
- postcss-load-config "^6.0.1"
- rolldown "^1.2.0"
- vite "^8.1.5"
- vue "^3.5.40"
- vue-router "^5.2.0"
-
-"@vuepress/bundlerutils@2.0.0-rc.31":
- version "2.0.0-rc.31"
- resolved "https://registry.yarnpkg.com/@vuepress/bundlerutils/-/bundlerutils-2.0.0-rc.31.tgz#4cb20b158906f61e5fd3b41913573149e87f2dab"
- integrity sha512-CSk4gR0fvJ419ocipH3cbk/xt0IFK92XwqzFX3N7BzPVFlWE74iaBfT3VUFQHQN2vYPwMMou4wlEdnK4PS4cvQ==
- dependencies:
- "@vuepress/client" "2.0.0-rc.31"
- "@vuepress/core" "2.0.0-rc.31"
- "@vuepress/markdown" "2.0.0-rc.31"
- "@vuepress/shared" "2.0.0-rc.31"
- "@vuepress/utils" "2.0.0-rc.31"
- vue "^3.5.40"
- vue-router "^5.2.0"
-
-"@vuepress/cli@2.0.0-rc.31":
- version "2.0.0-rc.31"
- resolved "https://registry.yarnpkg.com/@vuepress/cli/-/cli-2.0.0-rc.31.tgz#6f76cf1061ee10f26f6690b7703b32bd6c256edd"
- integrity sha512-X75edJjhacYCzrYSOhXvlakh+/1fm2eCGJcrcZqKGfeZXcKhDrF154E7meLqfEkiHDb3zPCg+C2wAs1m6ujoxQ==
- dependencies:
- "@vuepress/core" "2.0.0-rc.31"
- "@vuepress/shared" "2.0.0-rc.31"
- "@vuepress/utils" "2.0.0-rc.31"
- cac "^7.0.0"
- chokidar "^5.0.0"
- envinfo "^7.21.0"
- rolldown "^1.2.0"
-
-"@vuepress/client@2.0.0-rc.31":
- version "2.0.0-rc.31"
- resolved "https://registry.yarnpkg.com/@vuepress/client/-/client-2.0.0-rc.31.tgz#93b636a21cddc3582759462a9109129a851d2e4a"
- integrity sha512-F1mVj2NCblUwweGjfsmfLiC7GrO0YN5rnej14nMSGfHP3ch1FHpZeACiiRk1L0MolpBd7OYskx5rYyh7vY0tCg==
- dependencies:
- "@vue/devtools-api" "^8.1.5"
- "@vue/devtools-kit" "^8.1.5"
- "@vuepress/shared" "2.0.0-rc.31"
- vue "^3.5.40"
- vue-router "^5.2.0"
-
-"@vuepress/core@2.0.0-rc.31":
- version "2.0.0-rc.31"
- resolved "https://registry.yarnpkg.com/@vuepress/core/-/core-2.0.0-rc.31.tgz#654e571b57c3cc0a3dddb7988d26ff2477035def"
- integrity sha512-b01o8GkukVSZwQYn0DZirEr5ghiAiX2m8yNUeptOJ6nJgLyPGO02zmGlmERCxR9qsfHnTsc5UcbjJpmOr/cFiQ==
- dependencies:
- "@vuepress/client" "2.0.0-rc.31"
- "@vuepress/markdown" "2.0.0-rc.31"
- "@vuepress/shared" "2.0.0-rc.31"
- "@vuepress/utils" "2.0.0-rc.31"
- vue "^3.5.40"
-
-"@vuepress/helper@2.0.0-rc.135":
- version "2.0.0-rc.135"
- resolved "https://registry.yarnpkg.com/@vuepress/helper/-/helper-2.0.0-rc.135.tgz#89ecf625720049dcf3d9237573c21a9cfd71c88e"
- integrity sha512-L66c0swf64/OCPFJ+9EmO/FhLcqxcWiZVxwLBgf53+/AKxjIq9oYT+U+nqjV4oqFDun14szJoDh0AjbOHKFySg==
- dependencies:
- "@vue/shared" "^3.5.43"
- "@vueuse/core" "^15.0.0"
- cheerio "^1.2.0"
- fflate "^0.8.3"
- gray-matter "^4.0.3"
- vue "^3.5.43"
-
-"@vuepress/highlighter-helper@2.0.0-rc.135":
- version "2.0.0-rc.135"
- resolved "https://registry.yarnpkg.com/@vuepress/highlighter-helper/-/highlighter-helper-2.0.0-rc.135.tgz#5b42ed4e3d34ab3f3c86c086237c9b6efc8da511"
- integrity sha512-7UbBJ0cgeGfW5NSRLyLnBUJ3pGBxv65his0esOdAZly77FoVckF1rdQWQoVCayyH9Si6ZOyYBe/DwVgJPxu6IQ==
-
-"@vuepress/markdown@2.0.0-rc.31":
- version "2.0.0-rc.31"
- resolved "https://registry.yarnpkg.com/@vuepress/markdown/-/markdown-2.0.0-rc.31.tgz#b9d3a81cf09f0357a3dcf253f4f733f05aa5946a"
- integrity sha512-W4UdVwYob3sZ794BFd53dolhXVOma0JzN15k6CwmmhOy/3wWuIZ8pV3YQsxxyAQJ8ZxQWGZ4Wf5u/w7G/1/IkQ==
- dependencies:
- "@mdit-vue/plugin-component" "^3.0.2"
- "@mdit-vue/plugin-frontmatter" "^3.0.2"
- "@mdit-vue/plugin-headers" "^3.0.2"
- "@mdit-vue/plugin-sfc" "^3.0.2"
- "@mdit-vue/plugin-title" "^3.0.2"
- "@mdit-vue/plugin-toc" "^3.0.2"
- "@mdit-vue/shared" "^3.0.2"
- "@mdit-vue/types" "^3.0.2"
- "@types/markdown-it" "^14.1.2"
- "@types/markdown-it-emoji" "^3.0.1"
- "@vuepress/shared" "2.0.0-rc.31"
- "@vuepress/utils" "2.0.0-rc.31"
- markdown-it "^14.3.0"
- markdown-it-anchor "^9.2.1"
- markdown-it-emoji "^3.1.0"
- mdurl "^2.1.0"
-
-"@vuepress/plugin-active-header-links@2.0.0-rc.135":
- version "2.0.0-rc.135"
- resolved "https://registry.yarnpkg.com/@vuepress/plugin-active-header-links/-/plugin-active-header-links-2.0.0-rc.135.tgz#70e153436689fb9f510d34402c07c39ed53a46a3"
- integrity sha512-8ceSSmDW3Wwws+axQ4XcBMiJ2a8BfnnkuAsbYeTKeeoDc2K/OjyJ0Ib7OoM1eugJY6+mAtvDcotGwYGl4boYaQ==
- dependencies:
- "@vueuse/core" "^15.0.0"
- vue "^3.5.43"
-
-"@vuepress/plugin-back-to-top@2.0.0-rc.135":
- version "2.0.0-rc.135"
- resolved "https://registry.yarnpkg.com/@vuepress/plugin-back-to-top/-/plugin-back-to-top-2.0.0-rc.135.tgz#93b7c0f9fcceba74fab78c3cd5b679f7063154d9"
- integrity sha512-rhbFcrqUwl9v6HzFLE9Wc0ZH0nc/gRUtRpPWg2De5aY3jkykKsiQUK6ZnNGgJoTnbdfjO/FktteNm1ffUWf4ow==
- dependencies:
- "@vuepress/helper" "2.0.0-rc.135"
- "@vueuse/core" "^15.0.0"
- vue "^3.5.43"
-
-"@vuepress/plugin-copy-code@2.0.0-rc.135":
- version "2.0.0-rc.135"
- resolved "https://registry.yarnpkg.com/@vuepress/plugin-copy-code/-/plugin-copy-code-2.0.0-rc.135.tgz#c013a3b96a84b00b8d58160c7cfd9caff657b8f5"
- integrity sha512-WL+Ll5l0hJhzq4Jg+SvuqBYzOSecDmgK/z/QVaACtWvBPXYUhGbCJe5iV+5r7ZGPrgGNh1NDRKVilP4FBmDIKg==
- dependencies:
- "@vuepress/helper" "2.0.0-rc.135"
- "@vueuse/core" "^15.0.0"
- vue "^3.5.43"
-
-"@vuepress/plugin-git@2.0.0-rc.135":
- version "2.0.0-rc.135"
- resolved "https://registry.yarnpkg.com/@vuepress/plugin-git/-/plugin-git-2.0.0-rc.135.tgz#2770879af1ae49e303a90846ea07eb39f3b9cb2e"
- integrity sha512-fGfRtmmO68p6Zcr3Asbrvac5tB6r3qKqG+iYPxfoqTjneaz7Ojx/Md209WY6FCHAFo5Z6IIhIZjT5ioMY7yY6Q==
- dependencies:
- "@vuepress/helper" "2.0.0-rc.135"
- "@vueuse/core" "^15.0.0"
- rehype-parse "^9.0.1"
- rehype-sanitize "^6.0.0"
- rehype-stringify "^10.0.1"
- unified "^11.0.5"
- vue "^3.5.43"
-
-"@vuepress/plugin-links-check@2.0.0-rc.135":
- version "2.0.0-rc.135"
- resolved "https://registry.yarnpkg.com/@vuepress/plugin-links-check/-/plugin-links-check-2.0.0-rc.135.tgz#bbf5535d3874a2960a55b90466debe0437d95e4c"
- integrity sha512-CkM4CZuTrj4nQErZExutdhuXGwrDtpu4QvIZ1BJCBvfNTXlYKKf30z1o+gnZriU+5qG4a1xHHex2Dob2kw0GBg==
- dependencies:
- "@vuepress/helper" "2.0.0-rc.135"
-
-"@vuepress/plugin-markdown-hint@2.0.0-rc.135":
- version "2.0.0-rc.135"
- resolved "https://registry.yarnpkg.com/@vuepress/plugin-markdown-hint/-/plugin-markdown-hint-2.0.0-rc.135.tgz#1cef75347d74ea653d5d56093fe4869288051e3b"
- integrity sha512-mnSmb9ksx4QE1ChjFapEDTuEhfRXPDbDUtxy795nd7X0FANihv9qaQDEXhcZ5cMBG0KCSvZF6DTTPKvYD1Mlxw==
- dependencies:
- "@mdit/plugin-alert" "^2.0.1"
- "@mdit/plugin-container" "^2.0.1"
- "@types/markdown-it" "^14.2.0"
- "@vuepress/helper" "2.0.0-rc.135"
- "@vueuse/core" "^15.0.0"
-
-"@vuepress/plugin-markdown-tab@2.0.0-rc.135":
- version "2.0.0-rc.135"
- resolved "https://registry.yarnpkg.com/@vuepress/plugin-markdown-tab/-/plugin-markdown-tab-2.0.0-rc.135.tgz#9dfcdebd43b514a439f15e10f4d1c8dac9bfb9ec"
- integrity sha512-ngNUb7rD4yNDjHASMg+OH2scE5e7Lq75R02K+XeOkCWrAC+NO/DoMrsytsFclFUTOlTxYRFBAO3UwUZESt40Lw==
- dependencies:
- "@mdit/plugin-tab" "^2.0.1"
- "@types/markdown-it" "^14.2.0"
- "@vuepress/helper" "2.0.0-rc.135"
- "@vueuse/core" "^15.0.0"
- vue "^3.5.43"
-
-"@vuepress/plugin-medium-zoom@2.0.0-rc.135":
- version "2.0.0-rc.135"
- resolved "https://registry.yarnpkg.com/@vuepress/plugin-medium-zoom/-/plugin-medium-zoom-2.0.0-rc.135.tgz#32acc9abb322a8e9610b6e5f8d59f6d38e41faee"
- integrity sha512-69REzvPxslyPY8FIMbmPFeQ9yQtdR6lmArj6WOqjjrsyhPrJRtYIlTmCAlbgmmrmTZo14ShXCegF0aMFhQIHAg==
- dependencies:
- "@vuepress/helper" "2.0.0-rc.135"
- medium-zoom "^1.1.0"
- vue "^3.5.43"
-
-"@vuepress/plugin-nprogress@2.0.0-rc.135":
- version "2.0.0-rc.135"
- resolved "https://registry.yarnpkg.com/@vuepress/plugin-nprogress/-/plugin-nprogress-2.0.0-rc.135.tgz#c5926519b8fc07890d435992b28875707d5da9d9"
- integrity sha512-I9MRFGbwLXjwlHOMDsiz7mz9RMvirg+1YXR6qdHJrtC8Ck8HMFsDASBVwj1qR1uWIy1kSrOmz2aeC1wxqTChTg==
- dependencies:
- "@vuepress/helper" "2.0.0-rc.135"
- vue "^3.5.43"
-
-"@vuepress/plugin-palette@2.0.0-rc.135":
- version "2.0.0-rc.135"
- resolved "https://registry.yarnpkg.com/@vuepress/plugin-palette/-/plugin-palette-2.0.0-rc.135.tgz#a351b94e2a373d600f8ff6bb7b39c58c9780c835"
- integrity sha512-TXBjZxKZEfqGyzs8lZCrM006DYn7txRdBTE6tcchH90X709hjOyRGAwJzUxiCpCll/UJ7DfJjQ2YOGeC54pmmw==
- dependencies:
- "@vuepress/helper" "2.0.0-rc.135"
- chokidar "^5.0.0"
-
-"@vuepress/plugin-prismjs@2.0.0-rc.136":
- version "2.0.0-rc.136"
- resolved "https://registry.yarnpkg.com/@vuepress/plugin-prismjs/-/plugin-prismjs-2.0.0-rc.136.tgz#39500d4c2b78748d4ae375639d24d6551117fc25"
- integrity sha512-94FVyAMlQKLn4K7+Eodyg5XXf68IZcNNTq2R/nmGrreZNkRhVR0pODua0o7qMym0KB458ZTTCZO8sS5HLWFo6g==
- dependencies:
- "@vuepress/helper" "2.0.0-rc.135"
- "@vuepress/highlighter-helper" "2.0.0-rc.135"
- prismjs "^1.30.0"
-
-"@vuepress/plugin-seo@2.0.0-rc.135":
- version "2.0.0-rc.135"
- resolved "https://registry.yarnpkg.com/@vuepress/plugin-seo/-/plugin-seo-2.0.0-rc.135.tgz#40293e7d39d59de6c99dcc03d41dee4bfc528758"
- integrity sha512-jEot3JZJWN/GXjpe27sqCsWIntDSl6ei9v+Pm9SwxnrtoCLaU63Skz5eXRsHdTd946kDMBnW4s5x24HSGOOejA==
- dependencies:
- "@vuepress/helper" "2.0.0-rc.135"
-
-"@vuepress/plugin-shiki@^2.0.0-rc.136":
- version "2.0.0-rc.136"
- resolved "https://registry.yarnpkg.com/@vuepress/plugin-shiki/-/plugin-shiki-2.0.0-rc.136.tgz#3d6c1842a535b2a49d83a81a1930585bff36a69f"
- integrity sha512-oEmsly+BWZBmpg/jk6u1ETCUtJx+j3Enz3Bd67LEp5L9bEnqiEQk11UDyR7uWdCa1whXKohLpgEG0iu5dgsWng==
- dependencies:
- "@shikijs/transformers" "^4.4.3"
- "@vuepress/helper" "2.0.0-rc.135"
- "@vuepress/highlighter-helper" "2.0.0-rc.135"
- nanoid "^6.0.1"
- shiki "^4.4.3"
- synckit "^0.11.13"
-
-"@vuepress/plugin-sitemap@2.0.0-rc.135":
- version "2.0.0-rc.135"
- resolved "https://registry.yarnpkg.com/@vuepress/plugin-sitemap/-/plugin-sitemap-2.0.0-rc.135.tgz#6b03605a362525da73cf8576770260aeec6dbfae"
- integrity sha512-edHHU3GI4vHFcSTIOf+RxBj5B5x+adN1njdii7i66/jV9BNKS4vPQNm1CFN1dKzvMjletYEnumbqDuBx8evkNg==
- dependencies:
- "@vuepress/helper" "2.0.0-rc.135"
- sitemap "^9.0.1"
-
-"@vuepress/plugin-theme-data@2.0.0-rc.135":
- version "2.0.0-rc.135"
- resolved "https://registry.yarnpkg.com/@vuepress/plugin-theme-data/-/plugin-theme-data-2.0.0-rc.135.tgz#f71d7f23b5b6120c8a58aace42138c5ce59c9f8b"
- integrity sha512-0r2eDFDQLzwOIqH+Um//iEAV273T9RmUj2Az47noUsW+aeL4oePnu0av3WCobpc0YhxQ1RdJElQH+P6IVYsNpg==
- dependencies:
- "@vue/devtools-api" "^8.2.1"
- vue "^3.5.43"
-
-"@vuepress/shared@2.0.0-rc.31":
- version "2.0.0-rc.31"
- resolved "https://registry.yarnpkg.com/@vuepress/shared/-/shared-2.0.0-rc.31.tgz#4048f3a512565ce0fa5705a06fa9a2c04ac66da5"
- integrity sha512-FCBYbDrRd8rbRdj7osw2liS+gGsx7eyYktEm0efJlZavPh5O9nqC6e1ryUc0Mjzo04ZCgS+8EY3B+NHa5W2t8A==
- dependencies:
- "@mdit-vue/types" "^3.0.2"
-
-"@vuepress/theme-default@^2.0.0-rc.136":
- version "2.0.0-rc.136"
- resolved "https://registry.yarnpkg.com/@vuepress/theme-default/-/theme-default-2.0.0-rc.136.tgz#782e16ffc955c648f51bc5211cad366c6a3f8137"
- integrity sha512-gq6MrctzX5he4QHM+QQKt15yLuZHQ8dab6Vfue18JG1PbA+LWzl6dsBMpc2pqTvQlHriZD9eVfzdOODcQsGRTw==
- dependencies:
- "@vuepress/helper" "2.0.0-rc.135"
- "@vuepress/plugin-active-header-links" "2.0.0-rc.135"
- "@vuepress/plugin-back-to-top" "2.0.0-rc.135"
- "@vuepress/plugin-copy-code" "2.0.0-rc.135"
- "@vuepress/plugin-git" "2.0.0-rc.135"
- "@vuepress/plugin-links-check" "2.0.0-rc.135"
- "@vuepress/plugin-markdown-hint" "2.0.0-rc.135"
- "@vuepress/plugin-markdown-tab" "2.0.0-rc.135"
- "@vuepress/plugin-medium-zoom" "2.0.0-rc.135"
- "@vuepress/plugin-nprogress" "2.0.0-rc.135"
- "@vuepress/plugin-palette" "2.0.0-rc.135"
- "@vuepress/plugin-prismjs" "2.0.0-rc.136"
- "@vuepress/plugin-seo" "2.0.0-rc.135"
- "@vuepress/plugin-sitemap" "2.0.0-rc.135"
- "@vuepress/plugin-theme-data" "2.0.0-rc.135"
- "@vueuse/core" "^15.0.0"
- vue "^3.5.43"
-
-"@vuepress/utils@2.0.0-rc.31":
- version "2.0.0-rc.31"
- resolved "https://registry.yarnpkg.com/@vuepress/utils/-/utils-2.0.0-rc.31.tgz#8155c2592e089f1d51c12bc2a91e9a0381cf1508"
- integrity sha512-UeJYobsP2JuGjGNqh9Tk/iLhV9O0FQLj1ClFRxjG0gl/KUAASxofOODjOFFEsWv4FnkxNEUwejxO6cNqW1zOWg==
- dependencies:
- "@types/debug" "^4.1.13"
- "@types/fs-extra" "^11.0.4"
- "@types/hash-sum" "^1.0.2"
- "@types/picomatch" "^4.0.3"
- "@vuepress/shared" "2.0.0-rc.31"
- debug "^4.4.3"
- fs-extra "^11.3.6"
- hash-sum "^2.0.0"
- ora "^9.4.1"
- picocolors "^1.1.1"
- picomatch "^4.0.5"
- tinyglobby "^0.2.17"
- upath "^3.0.8"
-
-"@vueuse/core@^15.0.0":
- version "15.0.0"
- resolved "https://registry.yarnpkg.com/@vueuse/core/-/core-15.0.0.tgz#d4ca881cbee5ba653bda1a388ce441e8eed4e61d"
- integrity sha512-Zv67h5mrk7YHDJsRObx+DXaCBz6RKBUpxqLesJ+3xZNPG6yuQs7ja7kg3veFrrouYLWAqydQ7CwGEbLVxB43Ng==
- dependencies:
- "@types/web-bluetooth" "^0.0.21"
- "@vueuse/metadata" "15.0.0"
- "@vueuse/shared" "15.0.0"
-
-"@vueuse/metadata@15.0.0":
- version "15.0.0"
- resolved "https://registry.yarnpkg.com/@vueuse/metadata/-/metadata-15.0.0.tgz#c143e77d6e5b8f59ec345e1a524824381d33bd7b"
- integrity sha512-sfWE5LADAKJ0KnjtGliMdSN8Ij/L7uP6rXIZlgPloAI4zvvefbw6H5Lt7KJA6RqQOYgVqWF5Guj50HHMIzygBg==
-
-"@vueuse/shared@15.0.0":
- version "15.0.0"
- resolved "https://registry.yarnpkg.com/@vueuse/shared/-/shared-15.0.0.tgz#9e9332e1345c08601f67f1657e0e82597b42cdef"
- integrity sha512-vbNXhGrbV53xPOMSyOzMHQRvhMgZf7oNDw2RChUetVxwTIEkInQ1joZ9JbT0G3z2n2AJNjvmCWeM9ySYrnvogA==
-
-acorn@^8.16.0:
- version "8.18.0"
- resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.18.0.tgz#4faf01b2d6d326bfeed97aea1f52220b5f4c1940"
- integrity sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ==
-
-ansi-regex@^6.2.2:
- version "6.4.0"
- resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.4.0.tgz#69093d6436d30dbb9b04fe7bdcbcfe16c697d8a1"
- integrity sha512-KzTVk2tCWAHtYrvvvaP8bJKJq2pVinhLcGEQdtLIYPbmNGNyYe8QwNaTUYQp2J7/vIsUKt5QCqAfUkYyG9DkOw==
-
-arg@^5.0.0:
- version "5.0.2"
- resolved "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz"
- integrity sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==
-
-argparse@^1.0.7:
- version "1.0.10"
- resolved "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz"
- integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==
- dependencies:
- sprintf-js "~1.0.2"
-
-argparse@^2.0.1:
- version "2.0.1"
- resolved "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz"
- integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==
-
-ast-kit@^2.1.2, ast-kit@^2.2.0:
- version "2.2.0"
- resolved "https://registry.yarnpkg.com/ast-kit/-/ast-kit-2.2.0.tgz#6d9a298acefef5bdfc5a0fa51d94d1334ef2e671"
- integrity sha512-m1Q/RaVOnTp9JxPX+F+Zn7IcLYMzM8kZofDImfsKZd8MbR+ikdOzTeztStWqfrqIxZnYWryyI9ePm3NGjnZgGw==
- dependencies:
- "@babel/parser" "^7.28.5"
- pathe "^2.0.3"
-
-ast-walker-scope@^0.9.0:
- version "0.9.0"
- resolved "https://registry.yarnpkg.com/ast-walker-scope/-/ast-walker-scope-0.9.0.tgz#3bdcba80c488dd4c7fc1de055d645a0f95dff8fe"
- integrity sha512-IJdzo2vLiElBxKzwS36VsCue/62d6IdWjnPB2v3nuPKeWGynp6FF/CYoLa5i/3jXH/z97ZDdsXz6abpgM6w07A==
- dependencies:
- "@babel/parser" "^7.29.2"
- "@babel/types" "^7.29.0"
- ast-kit "^2.2.0"
-
-autoprefixer@^10.5.4:
- version "10.6.1"
- resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.6.1.tgz#437cea1a0b96cedc4065c05327b728b1594faf27"
- integrity sha512-cL1Qz6ADZhcEbny/8HPfe99J6HhNoYtpX2LFLIbhgGE7Q1hlQVkYFdetDN7Id3KiQxhDrHwzlHr/YQCnZ8+xSA==
- dependencies:
- browserslist "^4.28.9"
- caniuse-lite "^1.0.30001810"
- fraction.js "^5.3.4"
- picocolors "^1.1.1"
- postcss-value-parser "^4.2.0"
-
-bail@^2.0.0:
- version "2.0.2"
- resolved "https://registry.yarnpkg.com/bail/-/bail-2.0.2.tgz#d26f5cd8fe5d6f832a31517b9f7c356040ba6d5d"
- integrity sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==
-
-baseline-browser-mapping@^2.11.25:
- version "2.11.26"
- resolved "https://registry.yarnpkg.com/baseline-browser-mapping/-/baseline-browser-mapping-2.11.26.tgz#01bf6774efb3fbb9de20afc6b923ab85c7d230a6"
- integrity sha512-GLQdD3y6UF8iVuMJl5fHgE4jdn/ua7n+toKfLgNlg3BqQtOZjpy68T8Tup8/wGWZCDlm7KMg7tPb4MPn7oN0TQ==
-
-birpc@^2.6.1:
- version "2.9.0"
- resolved "https://registry.yarnpkg.com/birpc/-/birpc-2.9.0.tgz#b59550897e4cd96a223e2a6c1475b572236ed145"
- integrity sha512-KrayHS5pBi69Xi9JmvoqrIgYGDkD6mcSe/i6YKi3w5kekCLzrX4+nawcXqrj2tIp50Kw/mT/s3p+GVK0A0sKxw==
-
-boolbase@^1.0.0:
- version "1.0.0"
- resolved "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz"
- integrity sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==
-
-browserslist@^4.28.9:
- version "4.29.1"
- resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.29.1.tgz#f723071987a4f094177d94e01f6dc6e7493b972c"
- integrity sha512-AUdjuRyCNGUYtqpqfTmWyM4fXay8yIQhmLnvYe/THMGfT9B/34X7xQd3ifKxwyNPPpowVBjLb+64BN9Rn1mizw==
- dependencies:
- baseline-browser-mapping "^2.11.25"
- caniuse-lite "^1.0.30001810"
- electron-to-chromium "^1.5.438"
- node-releases "^2.0.57"
- update-browserslist-db "^1.3.3"
-
-buffer-builder@^0.2.0:
- version "0.2.0"
- resolved "https://registry.yarnpkg.com/buffer-builder/-/buffer-builder-0.2.0.tgz#3322cd307d8296dab1f604618593b261a3fade8f"
- integrity sha512-7VPMEPuYznPSoR21NE1zvd2Xna6c/CloiZCfcMXR1Jny6PjX0N4Nsa38zcBFo/FMK+BlA+FLKbJCQ0i2yxp+Xg==
-
-cac@^7.0.0:
- version "7.0.0"
- resolved "https://registry.yarnpkg.com/cac/-/cac-7.0.0.tgz#7dda83da2268f75f840ab89ac3bcc36c120a78da"
- integrity sha512-tixWYgm5ZoOD+3g6UTea91eow5z6AAHaho3g0V9CNSNb45gM8SmflpAc+GRd1InC4AqN/07Unrgp56Y94N9hJQ==
-
-caniuse-lite@^1.0.30001810:
- version "1.0.30001812"
- resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001812.tgz#55b32ed819d120b384149a4f9fd59d179ce008f9"
- integrity sha512-qN+QNNBr93TCmFrmte0bBCjSDMuRvt78VlHT99qIGPszm4QsqCX8lnyWUFkHi8B7ZBAPq8SH+UqyXNy/odMdng==
-
-ccount@^2.0.0:
- version "2.0.1"
- resolved "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz"
- integrity sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==
-
-chalk@^5.6.2:
- version "5.6.2"
- resolved "https://registry.yarnpkg.com/chalk/-/chalk-5.6.2.tgz#b1238b6e23ea337af71c7f8a295db5af0c158aea"
- integrity sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==
-
-character-entities-html4@^2.0.0:
- version "2.1.0"
- resolved "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz"
- integrity sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==
-
-character-entities-legacy@^3.0.0:
- version "3.0.0"
- resolved "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz"
- integrity sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==
-
-cheerio-select@^2.1.0:
- version "2.1.0"
- resolved "https://registry.npmjs.org/cheerio-select/-/cheerio-select-2.1.0.tgz"
- integrity sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==
- dependencies:
- boolbase "^1.0.0"
- css-select "^5.1.0"
- css-what "^6.1.0"
- domelementtype "^2.3.0"
- domhandler "^5.0.3"
- domutils "^3.0.1"
-
-cheerio@^1.2.0:
- version "1.2.0"
- resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-1.2.0.tgz#f23b777c49021ead7475dcf3390d3535a7f896d6"
- integrity sha512-WDrybc/gKFpTYQutKIK6UvfcuxijIZfMfXaYm8NMsPQxSYvf+13fXUJ4rztGGbJcBQ/GF55gvrZ0Bc0bj/mqvg==
- dependencies:
- cheerio-select "^2.1.0"
- dom-serializer "^2.0.0"
- domhandler "^5.0.3"
- domutils "^3.2.2"
- encoding-sniffer "^0.2.1"
- htmlparser2 "^10.1.0"
- parse5 "^7.3.0"
- parse5-htmlparser2-tree-adapter "^7.1.0"
- parse5-parser-stream "^7.1.2"
- undici "^7.19.0"
- whatwg-mimetype "^4.0.0"
-
-chokidar@^5.0.0:
- version "5.0.0"
- resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-5.0.0.tgz#949c126a9238a80792be9a0265934f098af369a5"
- integrity sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==
- dependencies:
- readdirp "^5.0.0"
-
-cli-cursor@^5.0.0:
- version "5.0.0"
- resolved "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz"
- integrity sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==
- dependencies:
- restore-cursor "^5.0.0"
-
-cli-spinners@^3.2.0:
- version "3.4.0"
- resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-3.4.0.tgz#1f11f6d48c4e5bc6849fcb4efa0dc98f9e7299ea"
- integrity sha512-bXfOC4QcT1tKXGorxL3wbJm6XJPDqEnij2gQ2m7ESQuE+/z9YFIWnl/5RpTiKWbMq3EVKR4fRLJGn6DVfu0mpw==
-
-colorjs.io@^0.5.0:
- version "0.5.2"
- resolved "https://registry.yarnpkg.com/colorjs.io/-/colorjs.io-0.5.2.tgz#63b20139b007591ebc3359932bef84628eb3fcef"
- integrity sha512-twmVoizEW7ylZSN32OgKdXRmo1qg+wT5/6C3xu5b9QsWzSFAhHLn2xd8ro0diCsKfCj1RdaTP/nrcW+vAoQPIw==
-
-comma-separated-tokens@^2.0.0:
- version "2.0.3"
- resolved "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz"
- integrity sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==
-
-confbox@^0.1.8:
- version "0.1.8"
- resolved "https://registry.yarnpkg.com/confbox/-/confbox-0.1.8.tgz#820d73d3b3c82d9bd910652c5d4d599ef8ff8b06"
- integrity sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==
-
-confbox@^0.2.4:
- version "0.2.4"
- resolved "https://registry.yarnpkg.com/confbox/-/confbox-0.2.4.tgz#592e7be71f882a4a874e3c88f0ac1ef6f7da1ce5"
- integrity sha512-ysOGlgTFbN2/Y6Cg3Iye8YKulHw+R2fNXHrgSmXISQdMnomY6eNDprVdW9R5xBguEqI954+S6709UyiO7B+6OQ==
-
-confbox@^0.3.1:
- version "0.3.1"
- resolved "https://registry.yarnpkg.com/confbox/-/confbox-0.3.1.tgz#79aa91893791a202fb349fb367e7320423f7c76e"
- integrity sha512-cKUSoKa8YxFZZSmraVi7onONx3amu77ngK3kGpsYHDH7drPwCRkQE1RYMPlLRrMtnciRj274XNRxcHxnKmDSnA==
-
-connect-history-api-fallback@^2.0.0:
- version "2.0.0"
- resolved "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz"
- integrity sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==
-
-css-select@^5.1.0:
- version "5.1.0"
- resolved "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz"
- integrity sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==
- dependencies:
- boolbase "^1.0.0"
- css-what "^6.1.0"
- domhandler "^5.0.2"
- domutils "^3.0.1"
- nth-check "^2.0.1"
-
-css-what@^6.1.0:
- version "6.1.0"
- resolved "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz"
- integrity sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==
-
-csstype@^3.2.3:
- version "3.2.3"
- resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.2.3.tgz#ec48c0f3e993e50648c86da559e2610995cf989a"
- integrity sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==
-
-debug@^4.4.3:
- version "4.4.3"
- resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.3.tgz#c6ae432d9bd9662582fce08709b038c58e9e3d6a"
- integrity sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==
- dependencies:
- ms "^2.1.3"
-
-dequal@^2.0.0:
- version "2.0.3"
- resolved "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz"
- integrity sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==
-
-detect-libc@^2.0.3:
- version "2.1.2"
- resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.1.2.tgz#689c5dcdc1900ef5583a4cb9f6d7b473742074ad"
- integrity sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==
-
-devlop@^1.0.0, devlop@^1.1.0:
- version "1.1.0"
- resolved "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz"
- integrity sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==
- dependencies:
- dequal "^2.0.0"
-
-dom-serializer@^2.0.0:
- version "2.0.0"
- resolved "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz"
- integrity sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==
- dependencies:
- domelementtype "^2.3.0"
- domhandler "^5.0.2"
- entities "^4.2.0"
-
-domelementtype@^2.3.0:
- version "2.3.0"
- resolved "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz"
- integrity sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==
-
-domhandler@^5.0.2, domhandler@^5.0.3:
- version "5.0.3"
- resolved "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz"
- integrity sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==
- dependencies:
- domelementtype "^2.3.0"
-
-domutils@^3.0.1, domutils@^3.2.2:
- version "3.2.2"
- resolved "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz"
- integrity sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==
- dependencies:
- dom-serializer "^2.0.0"
- domelementtype "^2.3.0"
- domhandler "^5.0.3"
-
-electron-to-chromium@^1.5.438:
- version "1.5.439"
- resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.439.tgz#d14ad21f157ee83b77d1e46fc031776eec9013c9"
- integrity sha512-qu6QIPXhsb+CRcAiTMNjR4A1y/7tCYKkKjr5CZXRVih6qkDf79peZ2BpEU3qoDBNCRrcy3Mra3X9nG5oruuA7Q==
-
-encoding-sniffer@^0.2.1:
- version "0.2.1"
- resolved "https://registry.yarnpkg.com/encoding-sniffer/-/encoding-sniffer-0.2.1.tgz#396ec97ac22ce5a037ba44af1992ac9d46a7b819"
- integrity sha512-5gvq20T6vfpekVtqrYQsSCFZ1wEg5+wW0/QaZMWkFr6BqD3NfKs0rLCx4rrVlSWJeZb5NBJgVLswK/w2MWU+Gw==
- dependencies:
- iconv-lite "^0.6.3"
- whatwg-encoding "^3.1.1"
-
-entities@^4.2.0, entities@^4.4.0, entities@^4.5.0:
- version "4.5.0"
- resolved "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz"
- integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==
-
-entities@^6.0.0:
- version "6.0.1"
- resolved "https://registry.yarnpkg.com/entities/-/entities-6.0.1.tgz#c28c34a43379ca7f61d074130b2f5f7020a30694"
- integrity sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==
-
-entities@^7.0.1:
- version "7.0.1"
- resolved "https://registry.yarnpkg.com/entities/-/entities-7.0.1.tgz#26e8a88889db63417dcb9a1e79a3f1bc92b5976b"
- integrity sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==
-
-envinfo@^7.21.0:
- version "7.21.0"
- resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.21.0.tgz#04a251be79f92548541f37d13c8b6f22940c3bae"
- integrity sha512-Lw7I8Zp5YKHFCXL7+Dz95g4CcbMEpgvqZNNq3AmlT5XAV6CgAAk6gyAMqn2zjw08K9BHfcNuKrMiCPLByGafow==
-
-escalade@^3.2.0:
- version "3.2.0"
- resolved "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz"
- integrity sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==
-
-esprima@^4.0.0:
- version "4.0.1"
- resolved "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz"
- integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==
-
-estree-walker@^2.0.2:
- version "2.0.2"
- resolved "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz"
- integrity sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==
-
-exsolve@^1.1.1:
- version "1.1.1"
- resolved "https://registry.yarnpkg.com/exsolve/-/exsolve-1.1.1.tgz#c055418255459b6ecde4e59de0060a3e97bc7572"
- integrity sha512-9U/jZUgjnSGyntRr6y5Muu1MJcwFl6kPu7k8qLF0IMNfLqvw0NZ4nnVDq0RVoZ0RvCyumib4Ez3KYrVfilrw+g==
-
-extend-shallow@^2.0.1:
- version "2.0.1"
- resolved "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz"
- integrity sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==
- dependencies:
- is-extendable "^0.1.0"
-
-extend@^3.0.0:
- version "3.0.2"
- resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa"
- integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==
-
-fdir@^6.5.0:
- version "6.5.0"
- resolved "https://registry.yarnpkg.com/fdir/-/fdir-6.5.0.tgz#ed2ab967a331ade62f18d077dae192684d50d350"
- integrity sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==
-
-fflate@^0.8.3:
- version "0.8.3"
- resolved "https://registry.yarnpkg.com/fflate/-/fflate-0.8.3.tgz#bc27d8eb30343d4d512abb03480202ce65d825fc"
- integrity sha512-tbZNuJrLwGUp3zshBtdy4W+ORxZuIh8a5ilyIEQDC5rY1f3U20JMry0Ll3WBzU58EZKsEuJFXhb5gwv8CsPvgA==
-
-fraction.js@^5.3.4:
- version "5.3.4"
- resolved "https://registry.yarnpkg.com/fraction.js/-/fraction.js-5.3.4.tgz#8c0fcc6a9908262df4ed197427bdeef563e0699a"
- integrity sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==
-
-fs-extra@^11.3.6:
- version "11.4.1"
- resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-11.4.1.tgz#6a01ca80008eacf3ca1ffde5ef79088f68203a5a"
- integrity sha512-KYAb4c9BJQI6QqGKthV68OHe0badztdXJWKo0WtBA9IuCFPTKvE5ZdUBglP833aMjhaSPNO4A5j/EkzZtGlKjA==
- dependencies:
- graceful-fs "^4.2.0"
- jsonfile "^6.0.1"
- universalify "^2.0.0"
-
-fsevents@~2.3.3:
- version "2.3.3"
- resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6"
- integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==
-
-get-east-asian-width@^1.5.0:
- version "1.7.0"
- resolved "https://registry.yarnpkg.com/get-east-asian-width/-/get-east-asian-width-1.7.0.tgz#7ee99f0bd5ab7c622002deeabba232923726d1d4"
- integrity sha512-XjH1AECxf0giL2V1aU8vKyRR2ppRUb5c0EvT7zuJTokQ74bNo52zOtghqdWIqrhUD79fo3x0WfKZdOqxF6LG1Q==
-
-graceful-fs@^4.1.6, graceful-fs@^4.2.0:
- version "4.2.11"
- resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz"
- integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==
-
-gray-matter@^4.0.3:
- version "4.0.3"
- resolved "https://registry.npmjs.org/gray-matter/-/gray-matter-4.0.3.tgz"
- integrity sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q==
- dependencies:
- js-yaml "^3.13.1"
- kind-of "^6.0.2"
- section-matter "^1.0.0"
- strip-bom-string "^1.0.0"
-
-has-flag@^4.0.0:
- version "4.0.0"
- resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b"
- integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==
-
-hash-sum@^2.0.0:
- version "2.0.0"
- resolved "https://registry.npmjs.org/hash-sum/-/hash-sum-2.0.0.tgz"
- integrity sha512-WdZTbAByD+pHfl/g9QSsBIIwy8IT+EsPiKDs0KNX+zSHhdDLFKdZu0BQHljvO+0QI/BasbMSUa8wYNCZTvhslg==
-
-hast-util-from-html@^2.0.0:
- version "2.0.3"
- resolved "https://registry.yarnpkg.com/hast-util-from-html/-/hast-util-from-html-2.0.3.tgz#485c74785358beb80c4ba6346299311ac4c49c82"
- integrity sha512-CUSRHXyKjzHov8yKsQjGOElXy/3EKpyX56ELnkHH34vDVw1N1XSQ1ZcAvTyAPtGqLTuKP/uxM+aLkSPqF/EtMw==
- dependencies:
- "@types/hast" "^3.0.0"
- devlop "^1.1.0"
- hast-util-from-parse5 "^8.0.0"
- parse5 "^7.0.0"
- vfile "^6.0.0"
- vfile-message "^4.0.0"
-
-hast-util-from-parse5@^8.0.0:
- version "8.0.3"
- resolved "https://registry.yarnpkg.com/hast-util-from-parse5/-/hast-util-from-parse5-8.0.3.tgz#830a35022fff28c3fea3697a98c2f4cc6b835a2e"
- integrity sha512-3kxEVkEKt0zvcZ3hCRYI8rqrgwtlIOFMWkbclACvjlDw8Li9S2hk/d51OI0nr/gIpdMHNepwgOKqZ/sy0Clpyg==
- dependencies:
- "@types/hast" "^3.0.0"
- "@types/unist" "^3.0.0"
- devlop "^1.0.0"
- hastscript "^9.0.0"
- property-information "^7.0.0"
- vfile "^6.0.0"
- vfile-location "^5.0.0"
- web-namespaces "^2.0.0"
-
-hast-util-parse-selector@^4.0.0:
- version "4.0.0"
- resolved "https://registry.yarnpkg.com/hast-util-parse-selector/-/hast-util-parse-selector-4.0.0.tgz#352879fa86e25616036037dd8931fb5f34cb4a27"
- integrity sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==
- dependencies:
- "@types/hast" "^3.0.0"
-
-hast-util-sanitize@^5.0.0:
- version "5.0.2"
- resolved "https://registry.yarnpkg.com/hast-util-sanitize/-/hast-util-sanitize-5.0.2.tgz#edb260d94e5bba2030eb9375790a8753e5bf391f"
- integrity sha512-3yTWghByc50aGS7JlGhk61SPenfE/p1oaFeNwkOOyrscaOkMGrcW9+Cy/QAIOBpZxP1yqDIzFMR0+Np0i0+usg==
- dependencies:
- "@types/hast" "^3.0.0"
- "@ungap/structured-clone" "^1.0.0"
- unist-util-position "^5.0.0"
-
-hast-util-to-html@^9.0.0, hast-util-to-html@^9.0.5:
- version "9.0.5"
- resolved "https://registry.npmjs.org/hast-util-to-html/-/hast-util-to-html-9.0.5.tgz"
- integrity sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==
- dependencies:
- "@types/hast" "^3.0.0"
- "@types/unist" "^3.0.0"
- ccount "^2.0.0"
- comma-separated-tokens "^2.0.0"
- hast-util-whitespace "^3.0.0"
- html-void-elements "^3.0.0"
- mdast-util-to-hast "^13.0.0"
- property-information "^7.0.0"
- space-separated-tokens "^2.0.0"
- stringify-entities "^4.0.0"
- zwitch "^2.0.4"
-
-hast-util-whitespace@^3.0.0:
- version "3.0.0"
- resolved "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz"
- integrity sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==
- dependencies:
- "@types/hast" "^3.0.0"
-
-hastscript@^9.0.0:
- version "9.0.1"
- resolved "https://registry.yarnpkg.com/hastscript/-/hastscript-9.0.1.tgz#dbc84bef6051d40084342c229c451cd9dc567dff"
- integrity sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w==
- dependencies:
- "@types/hast" "^3.0.0"
- comma-separated-tokens "^2.0.0"
- hast-util-parse-selector "^4.0.0"
- property-information "^7.0.0"
- space-separated-tokens "^2.0.0"
-
-hookable@^5.5.3:
- version "5.5.3"
- resolved "https://registry.npmjs.org/hookable/-/hookable-5.5.3.tgz"
- integrity sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==
-
-html-void-elements@^3.0.0:
- version "3.0.0"
- resolved "https://registry.npmjs.org/html-void-elements/-/html-void-elements-3.0.0.tgz"
- integrity sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==
-
-htmlparser2@^10.1.0:
- version "10.1.0"
- resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-10.1.0.tgz#fe3f2e12c73b6e462d4e10395db9c1119e4d6ae4"
- integrity sha512-VTZkM9GWRAtEpveh7MSF6SjjrpNVNNVJfFup7xTY3UpFtm67foy9HDVXneLtFVt4pMz5kZtgNcvCniNFb1hlEQ==
- dependencies:
- domelementtype "^2.3.0"
- domhandler "^5.0.3"
- domutils "^3.2.2"
- entities "^7.0.1"
-
-iconv-lite@0.6.3, iconv-lite@^0.6.3:
- version "0.6.3"
- resolved "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz"
- integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==
- dependencies:
- safer-buffer ">= 2.1.2 < 3.0.0"
-
-immutable@^5.0.2:
- version "5.1.1"
- resolved "https://registry.yarnpkg.com/immutable/-/immutable-5.1.1.tgz#d4cb552686f34b076b3dcf23c4384c04424d8354"
- integrity sha512-3jatXi9ObIsPGr3N5hGw/vWWcTkq6hUYhpQz4k0wLC+owqWi/LiugIw9x0EdNZ2yGedKN/HzePiBvaJRXa0Ujg==
-
-is-extendable@^0.1.0:
- version "0.1.1"
- resolved "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz"
- integrity sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==
-
-is-interactive@^2.0.0:
- version "2.0.0"
- resolved "https://registry.npmjs.org/is-interactive/-/is-interactive-2.0.0.tgz"
- integrity sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==
-
-is-plain-obj@^4.0.0:
- version "4.1.0"
- resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-4.1.0.tgz#d65025edec3657ce032fd7db63c97883eaed71f0"
- integrity sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==
-
-is-unicode-supported@^2.0.0, is-unicode-supported@^2.1.0:
- version "2.1.0"
- resolved "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz"
- integrity sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==
-
-js-yaml@^3.13.1:
- version "3.14.1"
- resolved "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz"
- integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==
- dependencies:
- argparse "^1.0.7"
- esprima "^4.0.0"
-
-jsonfile@^6.0.1:
- version "6.1.0"
- resolved "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz"
- integrity sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==
- dependencies:
- universalify "^2.0.0"
- optionalDependencies:
- graceful-fs "^4.1.6"
-
-kind-of@^6.0.0, kind-of@^6.0.2:
- version "6.0.3"
- resolved "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz"
- integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==
-
-lightningcss-android-arm64@1.33.0:
- version "1.33.0"
- resolved "https://registry.yarnpkg.com/lightningcss-android-arm64/-/lightningcss-android-arm64-1.33.0.tgz#9a6841f88ae50fc83502903892b41af41bc2b907"
- integrity sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==
-
-lightningcss-darwin-arm64@1.33.0:
- version "1.33.0"
- resolved "https://registry.yarnpkg.com/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.33.0.tgz#c0f2c31c0bfd19fa4dd3f18e957a1f1a152097d6"
- integrity sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==
-
-lightningcss-darwin-x64@1.33.0:
- version "1.33.0"
- resolved "https://registry.yarnpkg.com/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.33.0.tgz#cb0705965acb538c6683949ce6925fb3cdf7c361"
- integrity sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==
-
-lightningcss-freebsd-x64@1.33.0:
- version "1.33.0"
- resolved "https://registry.yarnpkg.com/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.33.0.tgz#763538828b26bab2680dadafcc84ee78b0eb502b"
- integrity sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==
-
-lightningcss-linux-arm-gnueabihf@1.33.0:
- version "1.33.0"
- resolved "https://registry.yarnpkg.com/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.33.0.tgz#6862e3176a331aedbdec1ed352b4d7d0dd0784de"
- integrity sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==
-
-lightningcss-linux-arm64-gnu@1.33.0:
- version "1.33.0"
- resolved "https://registry.yarnpkg.com/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.33.0.tgz#c6a3a2ed15141daf6bdc2628930f8e39bdf473aa"
- integrity sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==
-
-lightningcss-linux-arm64-musl@1.33.0:
- version "1.33.0"
- resolved "https://registry.yarnpkg.com/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.33.0.tgz#7fa1334971fc82845f9827df6ef8a0b20914bac6"
- integrity sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==
-
-lightningcss-linux-x64-gnu@1.33.0:
- version "1.33.0"
- resolved "https://registry.yarnpkg.com/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.33.0.tgz#8b927862ea8c2bbc6831a46509244b50d9936e55"
- integrity sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==
-
-lightningcss-linux-x64-musl@1.33.0:
- version "1.33.0"
- resolved "https://registry.yarnpkg.com/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.33.0.tgz#0c525bb077dfd94404c059cfe42dad797e96aeaf"
- integrity sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==
-
-lightningcss-win32-arm64-msvc@1.33.0:
- version "1.33.0"
- resolved "https://registry.yarnpkg.com/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.33.0.tgz#850ee1103dac989cfab50e3ac22d1a69e394e63d"
- integrity sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==
-
-lightningcss-win32-x64-msvc@1.33.0:
- version "1.33.0"
- resolved "https://registry.yarnpkg.com/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.33.0.tgz#e343ae152eed3609dc6e11949d1a3bf39a1c946f"
- integrity sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==
-
-lightningcss@^1.33.0:
- version "1.33.0"
- resolved "https://registry.yarnpkg.com/lightningcss/-/lightningcss-1.33.0.tgz#c08867d71a79385c6e190214fd72fef3e5f95f0b"
- integrity sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==
- dependencies:
- detect-libc "^2.0.3"
- optionalDependencies:
- lightningcss-android-arm64 "1.33.0"
- lightningcss-darwin-arm64 "1.33.0"
- lightningcss-darwin-x64 "1.33.0"
- lightningcss-freebsd-x64 "1.33.0"
- lightningcss-linux-arm-gnueabihf "1.33.0"
- lightningcss-linux-arm64-gnu "1.33.0"
- lightningcss-linux-arm64-musl "1.33.0"
- lightningcss-linux-x64-gnu "1.33.0"
- lightningcss-linux-x64-musl "1.33.0"
- lightningcss-win32-arm64-msvc "1.33.0"
- lightningcss-win32-x64-msvc "1.33.0"
-
-lilconfig@^3.1.1:
- version "3.1.3"
- resolved "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz"
- integrity sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==
-
-linkify-it@^5.0.0:
- version "5.0.0"
- resolved "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz"
- integrity sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==
- dependencies:
- uc.micro "^2.0.0"
-
-linkify-it@^5.0.2:
- version "5.0.2"
- resolved "https://registry.yarnpkg.com/linkify-it/-/linkify-it-5.0.2.tgz#d3be0a693af3da9df3883f1e346a0e97461a8c19"
- integrity sha512-ONTm2jCMAVZjgQa/Fy1kScXsuOoF5NPTsoFBdE1KVIZ2vAh/r9+Bqo+0jINCBYnavTPQZz38QzFTme79ENoN3Q==
- dependencies:
- uc.micro "^2.0.0"
-
-local-pkg@^1.1.2, local-pkg@^1.2.1:
- version "1.2.1"
- resolved "https://registry.yarnpkg.com/local-pkg/-/local-pkg-1.2.1.tgz#9628389399851d78f3b50c9236eddb02f0c31b2b"
- integrity sha512-++gUqRDEvcnN6Zhqrr+y/CkVEHhlrR96vZn3nZZPYzMcBUyBtTKzB9NadClFIsIVSsu+3i9tfk/erqy9kAmt7Q==
- dependencies:
- mlly "^1.7.4"
- pkg-types "^2.3.0"
- quansync "^0.2.11"
-
-log-symbols@^7.0.1:
- version "7.0.1"
- resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-7.0.1.tgz#f52e68037d96f589fc572ff2193dc424d48c195b"
- integrity sha512-ja1E3yCr9i/0hmBVaM0bfwDjnGy8I/s6PP4DFp+yP+a+mrHO4Rm7DtmnqROTUkHIkqffC84YY7AeqX6oFk0WFg==
- dependencies:
- is-unicode-supported "^2.0.0"
- yoctocolors "^2.1.1"
-
-magic-string-ast@^1.0.2:
- version "1.0.3"
- resolved "https://registry.yarnpkg.com/magic-string-ast/-/magic-string-ast-1.0.3.tgz#51ef7832fd5c70a0188fb94627caa3b8c74ff9bf"
- integrity sha512-CvkkH1i81zl7mmb94DsRiFeG9V2fR2JeuK8yDgS8oiZSFa++wWLEgZ5ufEOyLHbvSbD1gTRKv9NdX69Rnvr9JA==
- dependencies:
- magic-string "^0.30.19"
-
-magic-string@^0.30.19, magic-string@^0.30.21:
- version "0.30.21"
- resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.30.21.tgz#56763ec09a0fa8091df27879fd94d19078c00d91"
- integrity sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==
- dependencies:
- "@jridgewell/sourcemap-codec" "^1.5.5"
-
-markdown-it-anchor@^9.2.1:
- version "9.2.1"
- resolved "https://registry.yarnpkg.com/markdown-it-anchor/-/markdown-it-anchor-9.2.1.tgz#0df1665838d1003f234feed6e864ede30734c203"
- integrity sha512-p6APiLJDFAW2GEvaavDvhIBn7jrX2jLv77NkBGgNacFTurbORYc4pyYySg/mI6mpR6cHQuAtzKtmqgQr4K8dsQ==
-
-markdown-it-emoji@^3.1.0:
- version "3.1.0"
- resolved "https://registry.yarnpkg.com/markdown-it-emoji/-/markdown-it-emoji-3.1.0.tgz#67d9a601b6b2255e7e94d445e4e52e2165c31346"
- integrity sha512-NhmMEH2ywduD4Nty1E8uB5NqfLhAT1VR0dyvoJyStKOqCzbZmVdn/+8wj7zpDsb/fLBikpCPsWwxqKlvMmbz4g==
-
-markdown-it@^14.1.0:
- version "14.1.0"
- resolved "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz"
- integrity sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==
- dependencies:
- argparse "^2.0.1"
- entities "^4.4.0"
- linkify-it "^5.0.0"
- mdurl "^2.0.0"
- punycode.js "^2.3.1"
- uc.micro "^2.1.0"
-
-markdown-it@^14.3.0:
- version "14.3.2"
- resolved "https://registry.yarnpkg.com/markdown-it/-/markdown-it-14.3.2.tgz#eb41b5855120836603c33c1be4c92f58001f8a26"
- integrity sha512-sHHjZ5fJKlgrG4qns2YwVcdNep35h5fERrfkD2YNsb9UFk0UIHarbiTaHKVMlPuWAoiilyK8Fv/jAm11slsY7Q==
- dependencies:
- argparse "^2.0.1"
- entities "^4.5.0"
- linkify-it "^5.0.2"
- mdurl "^2.0.0"
- punycode.js "^2.3.1"
- uc.micro "^2.1.0"
-
-mdast-util-to-hast@^13.0.0:
- version "13.2.0"
- resolved "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.0.tgz"
- integrity sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA==
- dependencies:
- "@types/hast" "^3.0.0"
- "@types/mdast" "^4.0.0"
- "@ungap/structured-clone" "^1.0.0"
- devlop "^1.0.0"
- micromark-util-sanitize-uri "^2.0.0"
- trim-lines "^3.0.0"
- unist-util-position "^5.0.0"
- unist-util-visit "^5.0.0"
- vfile "^6.0.0"
-
-mdurl@^2.0.0:
- version "2.0.0"
- resolved "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz"
- integrity sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==
-
-mdurl@^2.1.0:
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/mdurl/-/mdurl-2.1.0.tgz#d711d3f7bce7f22c487c91be78545f356fa96573"
- integrity sha512-1+HBaOx0zi/dQWht8rNv9MYf9qqpqL/kxI0hXImU6Y547zM6Sni8BQibt7ifgMcYtQg41ao3Ivd6cnSM86inpg==
-
-medium-zoom@^1.1.0:
- version "1.1.0"
- resolved "https://registry.npmjs.org/medium-zoom/-/medium-zoom-1.1.0.tgz"
- integrity sha512-ewyDsp7k4InCUp3jRmwHBRFGyjBimKps/AJLjRSox+2q/2H4p/PNpQf+pwONWlJiOudkBXtbdmVbFjqyybfTmQ==
-
-micromark-util-character@^2.0.0:
- version "2.1.1"
- resolved "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz"
- integrity sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==
- dependencies:
- micromark-util-symbol "^2.0.0"
- micromark-util-types "^2.0.0"
-
-micromark-util-encode@^2.0.0:
- version "2.0.1"
- resolved "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz"
- integrity sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==
-
-micromark-util-sanitize-uri@^2.0.0:
- version "2.0.1"
- resolved "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz"
- integrity sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==
- dependencies:
- micromark-util-character "^2.0.0"
- micromark-util-encode "^2.0.0"
- micromark-util-symbol "^2.0.0"
-
-micromark-util-symbol@^2.0.0:
- version "2.0.1"
- resolved "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz"
- integrity sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==
-
-micromark-util-types@^2.0.0:
- version "2.0.2"
- resolved "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz"
- integrity sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==
-
-mimic-function@^5.0.0:
- version "5.0.1"
- resolved "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz"
- integrity sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==
-
-mlly@^1.7.4, mlly@^1.8.2:
- version "1.8.2"
- resolved "https://registry.yarnpkg.com/mlly/-/mlly-1.8.2.tgz#e7f7919a82d13b174405613117249a3f449d78bb"
- integrity sha512-d+ObxMQFmbt10sretNDytwt85VrbkhhUA/JBGm1MPaWJ65Cl4wOgLaB1NYvJSZ0Ef03MMEU/0xpPMXUIQ29UfA==
- dependencies:
- acorn "^8.16.0"
- pathe "^2.0.3"
- pkg-types "^1.3.1"
- ufo "^1.6.3"
-
-ms@^2.1.3:
- version "2.1.3"
- resolved "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz"
- integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
-
-muggle-string@^0.4.1:
- version "0.4.1"
- resolved "https://registry.yarnpkg.com/muggle-string/-/muggle-string-0.4.1.tgz#3b366bd43b32f809dc20659534dd30e7c8a0d328"
- integrity sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==
-
-nanoid@^3.3.18:
- version "3.3.19"
- resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.19.tgz#336d4aa4bcd4fb24d2cddede7ffeae40bec03f0a"
- integrity sha512-Y2tUNy4ouw6tq5oDSKeQYGOyhkUBhNOcGV/02KC+6kd9eDGqdZd++mjMiIDilrBYvjEnCYvVtsuHCuP+okSfug==
-
-nanoid@^6.0.1:
- version "6.0.1"
- resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-6.0.1.tgz#a04a2304f05b620c600ae8464d94c325ceeebd35"
- integrity sha512-3wVS3i51pE2pi1k5FFL/95BGfVS0kSsvDVuGXHOtxox/TywUmtgq+3qiTOTbs9J7KfHaXPiN171k/A6dBnaXFw==
-
-node-releases@^2.0.57:
- version "2.0.57"
- resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.57.tgz#47827d34b86a98b4b7afacc1222878c286b6ef2d"
- integrity sha512-kQK9LGGFiHtrWiNhZtA7Qbw17AQz+dmsEKODRIVTXA9+e5MS/2gZEBhYJt13GrAz5/IOZKddH/0Z3TP/Zgo+yw==
-
-nostics@^1.1.4:
- version "1.3.0"
- resolved "https://registry.yarnpkg.com/nostics/-/nostics-1.3.0.tgz#e1d538463acb0207f9a0ef9ec551b075930559c4"
- integrity sha512-wzRsrJwNlFbqIRQt8aCeL2WE8USip/uuvGSOZV1D02CxX8ABTMbw8IywKjH7yWHETMXbWJI4OBpihg9rmz2Vyg==
-
-nth-check@^2.0.1:
- version "2.1.1"
- resolved "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz"
- integrity sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==
- dependencies:
- boolbase "^1.0.0"
-
-onetime@^7.0.0:
- version "7.0.0"
- resolved "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz"
- integrity sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==
- dependencies:
- mimic-function "^5.0.0"
-
-oniguruma-parser@^0.12.2:
- version "0.12.2"
- resolved "https://registry.yarnpkg.com/oniguruma-parser/-/oniguruma-parser-0.12.2.tgz#e27ca446f7fcf0969662a3ab9b4f43176d62b139"
- integrity sha512-6HVa5oIrgMC6aA6WF6XyyqbhRPJrKR02L20+2+zpDtO5QAzGHAUGw5TKQvwi5vctNnRHkJYmjAhRVQF2EKdTQw==
-
-oniguruma-to-es@^4.3.6:
- version "4.3.6"
- resolved "https://registry.yarnpkg.com/oniguruma-to-es/-/oniguruma-to-es-4.3.6.tgz#43e640280241b0d687a314e7a641d476407a1c4d"
- integrity sha512-csuQ9x3Yr0cEIs/Zgx/OEt9iBw9vqIunAPQkx19R/fiMq2oGVTgcMqO/V3Ybqefr1TBvosI6jU539ksaBULJyA==
- dependencies:
- oniguruma-parser "^0.12.2"
- regex "^6.1.0"
- regex-recursion "^6.0.2"
-
-ora@^9.4.1:
- version "9.4.1"
- resolved "https://registry.yarnpkg.com/ora/-/ora-9.4.1.tgz#ba7644f3c7c92a8f830fbc48ca770b9eaf4bc55c"
- integrity sha512-6VlU9MLXbjVQD04AZCMX28hVtA5bUoadvUqO76MUCVA0ilwJbMiHsITRPfyVm6p/BC0Av/BXMujx39WCe1LEqw==
- dependencies:
- chalk "^5.6.2"
- cli-cursor "^5.0.0"
- cli-spinners "^3.2.0"
- is-interactive "^2.0.0"
- is-unicode-supported "^2.1.0"
- log-symbols "^7.0.1"
- stdin-discarder "^0.3.2"
- string-width "^8.1.0"
-
-parse5-htmlparser2-tree-adapter@^7.1.0:
- version "7.1.0"
- resolved "https://registry.yarnpkg.com/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.1.0.tgz#b5a806548ed893a43e24ccb42fbb78069311e81b"
- integrity sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g==
- dependencies:
- domhandler "^5.0.3"
- parse5 "^7.0.0"
-
-parse5-parser-stream@^7.1.2:
- version "7.1.2"
- resolved "https://registry.npmjs.org/parse5-parser-stream/-/parse5-parser-stream-7.1.2.tgz"
- integrity sha512-JyeQc9iwFLn5TbvvqACIF/VXG6abODeB3Fwmv/TGdLk2LfbWkaySGY72at4+Ty7EkPZj854u4CrICqNk2qIbow==
- dependencies:
- parse5 "^7.0.0"
-
-parse5@^7.0.0:
- version "7.2.1"
- resolved "https://registry.npmjs.org/parse5/-/parse5-7.2.1.tgz"
- integrity sha512-BuBYQYlv1ckiPdQi/ohiivi9Sagc9JG+Ozs0r7b/0iK3sKmrb0b9FdWdBbOdx6hBCM/F9Ir82ofnBhtZOjCRPQ==
- dependencies:
- entities "^4.5.0"
-
-parse5@^7.3.0:
- version "7.3.0"
- resolved "https://registry.yarnpkg.com/parse5/-/parse5-7.3.0.tgz#d7e224fa72399c7a175099f45fc2ad024b05ec05"
- integrity sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==
- dependencies:
- entities "^6.0.0"
-
-pathe@^2.0.1, pathe@^2.0.3:
- version "2.0.3"
- resolved "https://registry.yarnpkg.com/pathe/-/pathe-2.0.3.tgz#3ecbec55421685b70a9da872b2cff3e1cbed1716"
- integrity sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==
-
-perfect-debounce@^2.0.0:
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/perfect-debounce/-/perfect-debounce-2.1.0.tgz#e7078e38f231cb191855c3136a4423aef725d261"
- integrity sha512-LjgdTytVFXeUgtHZr9WYViYSM/g8MkcTPYDlPa3cDqMirHjKiSZPYd6DoL7pK8AJQr+uWkQvCjHNdiMqsrJs+g==
-
-picocolors@^1.1.1:
- version "1.1.1"
- resolved "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz"
- integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==
-
-picomatch@^4.0.4, picomatch@^4.0.5, picomatch@^4.0.7:
- version "4.0.7"
- resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-4.0.7.tgz#6313360034ccb36b3dc61ecbdff78121f90fe21f"
- integrity sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==
-
-pkg-types@^1.3.1:
- version "1.3.1"
- resolved "https://registry.yarnpkg.com/pkg-types/-/pkg-types-1.3.1.tgz#bd7cc70881192777eef5326c19deb46e890917df"
- integrity sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==
- dependencies:
- confbox "^0.1.8"
- mlly "^1.7.4"
- pathe "^2.0.1"
-
-pkg-types@^2.3.0:
- version "2.3.3"
- resolved "https://registry.yarnpkg.com/pkg-types/-/pkg-types-2.3.3.tgz#7fa477b1050acf906fdb8896bb2accd52c45e3d5"
- integrity sha512-j/lCFdcppV0JxWpCEITdbDltBxPP6cHT+yNJ6Go2OgoSA9518X847X9z0p6LtA4Nc16+eQzCZjRrWanTGvHJ5w==
- dependencies:
- confbox "^0.3.1"
- exsolve "^1.1.1"
- pathe "^2.0.3"
-
-postcss-load-config@^6.0.1:
- version "6.0.1"
- resolved "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-6.0.1.tgz"
- integrity sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==
- dependencies:
- lilconfig "^3.1.1"
-
-postcss-value-parser@^4.2.0:
- version "4.2.0"
- resolved "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz"
- integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==
-
-postcss@^8.5.22, postcss@^8.5.28:
- version "8.5.28"
- resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.5.28.tgz#da4563a99a06e62d6c1cd1acae363224bcaed6e9"
- integrity sha512-RRuzqDtt5Y9h3quz5hWhK+TPnsmVs6WwSU6LkJMeY4HstUEDuYTG8UJSdawMRzmzAtV+KEoG8N3Qg2qLy5vM/A==
- dependencies:
- nanoid "^3.3.18"
- picocolors "^1.1.1"
- source-map-js "^1.2.1"
-
-prismjs@^1.30.0:
- version "1.30.0"
- resolved "https://registry.npmjs.org/prismjs/-/prismjs-1.30.0.tgz"
- integrity sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==
-
-property-information@^7.0.0:
- version "7.0.0"
- resolved "https://registry.npmjs.org/property-information/-/property-information-7.0.0.tgz"
- integrity sha512-7D/qOz/+Y4X/rzSB6jKxKUsQnphO046ei8qxG59mtM3RG3DHgTK81HrxrmoDVINJb8NKT5ZsRbwHvQ6B68Iyhg==
-
-punycode.js@^2.3.1:
- version "2.3.1"
- resolved "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz"
- integrity sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==
-
-quansync@^0.2.11:
- version "0.2.11"
- resolved "https://registry.yarnpkg.com/quansync/-/quansync-0.2.11.tgz#f9c3adda2e1272e4f8cf3f1457b04cbdb4ee692a"
- integrity sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA==
-
-readdirp@^5.0.0:
- version "5.1.1"
- resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-5.1.1.tgz#520bca06f9d1ae1b96cc0800dbe84b983d19422c"
- integrity sha512-Kko+Y5XQ6fM+Ce3dq3m9YGxnacYZYl9cA1wZjaF3Vbry2L3i1qVg8+CAgNPsXRArPMUMCaOR7oa9Nqntc43JKA==
-
-regex-recursion@^6.0.2:
- version "6.0.2"
- resolved "https://registry.npmjs.org/regex-recursion/-/regex-recursion-6.0.2.tgz"
- integrity sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==
- dependencies:
- regex-utilities "^2.3.0"
-
-regex-utilities@^2.3.0:
- version "2.3.0"
- resolved "https://registry.npmjs.org/regex-utilities/-/regex-utilities-2.3.0.tgz"
- integrity sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==
-
-regex@^6.1.0:
- version "6.1.0"
- resolved "https://registry.yarnpkg.com/regex/-/regex-6.1.0.tgz#d7ce98f8ee32da7497c13f6601fca2bc4a6a7803"
- integrity sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg==
- dependencies:
- regex-utilities "^2.3.0"
-
-rehype-parse@^9.0.1:
- version "9.0.1"
- resolved "https://registry.yarnpkg.com/rehype-parse/-/rehype-parse-9.0.1.tgz#9993bda129acc64c417a9d3654a7be38b2a94c20"
- integrity sha512-ksCzCD0Fgfh7trPDxr2rSylbwq9iYDkSn8TCDmEJ49ljEUBxDVCzCHv7QNzZOfODanX4+bWQ4WZqLCRWYLfhag==
- dependencies:
- "@types/hast" "^3.0.0"
- hast-util-from-html "^2.0.0"
- unified "^11.0.0"
-
-rehype-sanitize@^6.0.0:
- version "6.0.0"
- resolved "https://registry.yarnpkg.com/rehype-sanitize/-/rehype-sanitize-6.0.0.tgz#16e95f4a67a69cbf0f79e113c8e0df48203db73c"
- integrity sha512-CsnhKNsyI8Tub6L4sm5ZFsme4puGfc6pYylvXo1AeqaGbjOYyzNv3qZPwvs0oMJ39eryyeOdmxwUIo94IpEhqg==
- dependencies:
- "@types/hast" "^3.0.0"
- hast-util-sanitize "^5.0.0"
-
-rehype-stringify@^10.0.1:
- version "10.0.1"
- resolved "https://registry.yarnpkg.com/rehype-stringify/-/rehype-stringify-10.0.1.tgz#2ec1ebc56c6aba07905d3b4470bdf0f684f30b75"
- integrity sha512-k9ecfXHmIPuFVI61B9DeLPN0qFHfawM6RsuX48hoqlaKSF61RskNjSm1lI8PhBEM0MRdLxVVm4WmTqJQccH9mA==
- dependencies:
- "@types/hast" "^3.0.0"
- hast-util-to-html "^9.0.0"
- unified "^11.0.0"
-
-restore-cursor@^5.0.0:
- version "5.1.0"
- resolved "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz"
- integrity sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==
- dependencies:
- onetime "^7.0.0"
- signal-exit "^4.1.0"
-
-rolldown@^1.2.0, rolldown@~1.2.9:
- version "1.2.11"
- resolved "https://registry.yarnpkg.com/rolldown/-/rolldown-1.2.11.tgz#89c4e07d9098b88fe0b023de59d63a6216e9285c"
- integrity sha512-qpSwIyz0jHQq5qXBTNxFmE6664rJ7O+4TvPFOiOaBSrz8IOHc1koKKSqTM2H6u1UG1+TveuC6vaDHKXFOvb1Kw==
- dependencies:
- "@oxc-project/types" "=0.151.0"
- "@rolldown/pluginutils" "^1.0.0"
- optionalDependencies:
- "@rolldown/binding-android-arm-eabi" "1.2.11"
- "@rolldown/binding-android-arm64" "1.2.11"
- "@rolldown/binding-darwin-arm64" "1.2.11"
- "@rolldown/binding-darwin-x64" "1.2.11"
- "@rolldown/binding-freebsd-x64" "1.2.11"
- "@rolldown/binding-linux-arm-gnueabihf" "1.2.11"
- "@rolldown/binding-linux-arm64-gnu" "1.2.11"
- "@rolldown/binding-linux-arm64-musl" "1.2.11"
- "@rolldown/binding-linux-ppc64-gnu" "1.2.11"
- "@rolldown/binding-linux-s390x-gnu" "1.2.11"
- "@rolldown/binding-linux-x64-gnu" "1.2.11"
- "@rolldown/binding-linux-x64-musl" "1.2.11"
- "@rolldown/binding-openharmony-arm64" "1.2.11"
- "@rolldown/binding-win32-arm64-msvc" "1.2.11"
- "@rolldown/binding-win32-x64-msvc" "1.2.11"
-
-rxjs@^7.4.0:
- version "7.8.2"
- resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.2.tgz#955bc473ed8af11a002a2be52071bf475638607b"
- integrity sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==
- dependencies:
- tslib "^2.1.0"
-
-"safer-buffer@>= 2.1.2 < 3.0.0":
- version "2.1.2"
- resolved "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz"
- integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
-
-sass-embedded-android-arm64@1.86.1:
- version "1.86.1"
- resolved "https://registry.yarnpkg.com/sass-embedded-android-arm64/-/sass-embedded-android-arm64-1.86.1.tgz#d7b4bd145592e30dc2b9e5ae46c109349574b113"
- integrity sha512-SMY79YhNfq/gdz8MHqwEsnf/IjSnQFAmSEGDDv0vjL0yy9VZC/zhsxpsho8vbFEvTSEGFFlkGgPdzDuoozRrOg==
-
-sass-embedded-android-arm@1.86.1:
- version "1.86.1"
- resolved "https://registry.yarnpkg.com/sass-embedded-android-arm/-/sass-embedded-android-arm-1.86.1.tgz#6a94c9bf80fc869476ef9974f14885a6cc0c465b"
- integrity sha512-bcmKB67uCb9znune+QsE6cWIiKAHE9P+24/9vDPHwwN3BmmH1B/4mznNKKakdYMuxpgbeLrPcEScHEpQbdrIpA==
-
-sass-embedded-android-ia32@1.86.1:
- version "1.86.1"
- resolved "https://registry.yarnpkg.com/sass-embedded-android-ia32/-/sass-embedded-android-ia32-1.86.1.tgz#e2cf595adb5707a3464e9bee5a00bae4409c4a7a"
- integrity sha512-AX6I5qS8GbgcbBJ1o3uKVI5/7tq6evg/BO/wa0XaNqnzP4i/PojBaGh7EcZrg/spl//SfpS55eA18a0/AOi71w==
-
-sass-embedded-android-riscv64@1.86.1:
- version "1.86.1"
- resolved "https://registry.yarnpkg.com/sass-embedded-android-riscv64/-/sass-embedded-android-riscv64-1.86.1.tgz#c60ddffe6fc56da85e2dcff23220131737402d3e"
- integrity sha512-Af6ZzRTRfIfx6KICJZ19je6OjOXhxo+v6z/lf/SXm5/1EaHGpGC5xIw4ivtj4nNINNoqkykfIDCjpzm1qWEPPQ==
-
-sass-embedded-android-x64@1.86.1:
- version "1.86.1"
- resolved "https://registry.yarnpkg.com/sass-embedded-android-x64/-/sass-embedded-android-x64-1.86.1.tgz#88c2b44b3d4b92102c0b7a21b1ec753332046b1c"
- integrity sha512-GW47z1AH8gXB7IG6EUbC5aDBDtiITeP5nUfEenE6vaaN0H17mBjIwSnEcKPPA1IdxzDpj+4bE/SGfiF0W/At4g==
-
-sass-embedded-darwin-arm64@1.86.1:
- version "1.86.1"
- resolved "https://registry.yarnpkg.com/sass-embedded-darwin-arm64/-/sass-embedded-darwin-arm64-1.86.1.tgz#4af0f75dffec1c418b2eee97cfe72498403746c3"
- integrity sha512-grBnDW5Rg+mEmZM7I9hJySS4MMXDwLMd+RyegQnr+SIJ3WA807Cw830+raALxgDY+UKKKhVEoq3FgbTo40Awgw==
-
-sass-embedded-darwin-x64@1.86.1:
- version "1.86.1"
- resolved "https://registry.yarnpkg.com/sass-embedded-darwin-x64/-/sass-embedded-darwin-x64-1.86.1.tgz#1e02df4f003fd7520fda247b49ec734c19a94bec"
- integrity sha512-XxSCMcmeADNouiJAr8G1oRnEhkivHKVLV5DRpfFnUK5FqtFCuSk3K18I+xIfpQDeZnjRL3t2VjsmEJuFiBYV8w==
-
-sass-embedded-linux-arm64@1.86.1:
- version "1.86.1"
- resolved "https://registry.yarnpkg.com/sass-embedded-linux-arm64/-/sass-embedded-linux-arm64-1.86.1.tgz#79514d4b4e2c7decbc1df37e583de65912cd95ce"
- integrity sha512-zchms0BtaOrkvfvjRnl1PDWK931DxAeYEY2yKQceO/0OFtcBz1r480Kh/RjIffTNreJqIr9Mx4wFdP+icKwLpg==
-
-sass-embedded-linux-arm@1.86.1:
- version "1.86.1"
- resolved "https://registry.yarnpkg.com/sass-embedded-linux-arm/-/sass-embedded-linux-arm-1.86.1.tgz#c221ad6ff4c7dae10b2212098e6dac4f32d9cae9"
- integrity sha512-Z57ZUcWPuoOHpnl3TiUf/x9wWF2dFtkjdv7hZQpFXYwK5eudHFeBErK6KNCos6jkif1KyeFELXT/HWOznitU/w==
-
-sass-embedded-linux-ia32@1.86.1:
- version "1.86.1"
- resolved "https://registry.yarnpkg.com/sass-embedded-linux-ia32/-/sass-embedded-linux-ia32-1.86.1.tgz#5b19ce4e98a0fffbf91fff889d22626b7d8c3df8"
- integrity sha512-WHntVnCgpiJPCmTeQrn5rtl1zJdd693TwpNGAFPzKD4FILPcVBKtWutl7COL6bKe/mKTf9OW0t6GBJ6mav2hAA==
-
-sass-embedded-linux-musl-arm64@1.86.1:
- version "1.86.1"
- resolved "https://registry.yarnpkg.com/sass-embedded-linux-musl-arm64/-/sass-embedded-linux-musl-arm64-1.86.1.tgz#484dd74117101d5801f86542bc13832e0d905a41"
- integrity sha512-CwuHMRWSJFByHpgqcVtCSt29dMWhr0lpUTjaBCh9xOl0Oyz89dIqOxA0aMq+XU+thaDtOziJtMIfW6l35ZeykQ==
-
-sass-embedded-linux-musl-arm@1.86.1:
- version "1.86.1"
- resolved "https://registry.yarnpkg.com/sass-embedded-linux-musl-arm/-/sass-embedded-linux-musl-arm-1.86.1.tgz#deff5105fdc85078f9e178bf288e64f242df17da"
- integrity sha512-DlPpyp3bIL8YMtxR22hkWBtuZY6ch3KAmQvqIONippPv96WTHi1iq5jclbE1YXpDtI8Wcus0x6apoDSKq8o95g==
-
-sass-embedded-linux-musl-ia32@1.86.1:
- version "1.86.1"
- resolved "https://registry.yarnpkg.com/sass-embedded-linux-musl-ia32/-/sass-embedded-linux-musl-ia32-1.86.1.tgz#76a0ca58d434c8442d70817206dd34096e6d22e1"
- integrity sha512-yjvVpAW1YS0VQNnIUtZTf0IrRDMa0wRjFWUtsLthVIxuXyjLy44+YULlfduxqcZe3rvI4+EqT7GorvviWo9NfQ==
-
-sass-embedded-linux-musl-riscv64@1.86.1:
- version "1.86.1"
- resolved "https://registry.yarnpkg.com/sass-embedded-linux-musl-riscv64/-/sass-embedded-linux-musl-riscv64-1.86.1.tgz#831cc4fbd4760623404d9faadd6b8862f9acb9b3"
- integrity sha512-0zCUOMwX/hwPV1zimxM46dq/MdATSqbw6G646DwQ3/2V2Db1t9lfXBZqSavx8p/cqRp1JYTUPbJQV1gT4J7NYw==
-
-sass-embedded-linux-musl-x64@1.86.1:
- version "1.86.1"
- resolved "https://registry.yarnpkg.com/sass-embedded-linux-musl-x64/-/sass-embedded-linux-musl-x64-1.86.1.tgz#bc08cc0ca4a103a9651790a044c53ad07b562c21"
- integrity sha512-8KJ6kEj1N16V9E0g5PDSd4aVe1LwcVKROJcVqnzTKPMa/4j2VuNWep7D81OYchdQMm9Egn1RqV0jCwm0b2aSHQ==
-
-sass-embedded-linux-riscv64@1.86.1:
- version "1.86.1"
- resolved "https://registry.yarnpkg.com/sass-embedded-linux-riscv64/-/sass-embedded-linux-riscv64-1.86.1.tgz#cb01ea36889421af2fd2b38d047be22d32c6c7cf"
- integrity sha512-rNJ1EfIkQpvBfMS1fBdyb+Gsji4yK0AwsV1T7NEcy21yDxDt7mdCgkAJiaN9qf7UEXuCuueQoed7WZoDaSpjww==
-
-sass-embedded-linux-x64@1.86.1:
- version "1.86.1"
- resolved "https://registry.yarnpkg.com/sass-embedded-linux-x64/-/sass-embedded-linux-x64-1.86.1.tgz#120c6040d3ed3e4e4e81f85a4b0f2b35502b40c6"
- integrity sha512-DGCdUoYRRUKzRZz/q7plbB5Nean2+Uk4CqKF4RWAU0v1tHnDKKWmYfETryhWdB2WJM8QSn7O8qRebe6FCobB5g==
-
-sass-embedded-win32-arm64@1.86.1:
- version "1.86.1"
- resolved "https://registry.yarnpkg.com/sass-embedded-win32-arm64/-/sass-embedded-win32-arm64-1.86.1.tgz#b9418381463d6503bc5ad8a21872a8cc6b18cfe2"
- integrity sha512-qRLZR3yLuk/3y64YhcltkwGclhPoK6EdiLP1e5SVw5+kughcs+mNUZ3rdvSAmCSA4vDv+XOiOjRpjxmpeon95Q==
-
-sass-embedded-win32-ia32@1.86.1:
- version "1.86.1"
- resolved "https://registry.yarnpkg.com/sass-embedded-win32-ia32/-/sass-embedded-win32-ia32-1.86.1.tgz#6b0fe65c34434136ce32fe19ef87095ccc676ec9"
- integrity sha512-o860a7/YGHZnGeY3l/e6yt3+ZMeDdDHmthTaKnw2wpJNEq0nmytYLTJQmjWPxEMz7O8AQ0LtcbDDrhivSog+KQ==
-
-sass-embedded-win32-x64@1.86.1:
- version "1.86.1"
- resolved "https://registry.yarnpkg.com/sass-embedded-win32-x64/-/sass-embedded-win32-x64-1.86.1.tgz#ac9d7f8ac3e6b50ca0113ad61eaed9de15e4b8fc"
- integrity sha512-7Z3wsVKfseJodmv689dDEV/JrXJH5TAclWNvHrEYW5BtoViOTU2pIDxRgLYzdKU9teIw5g6R0nJZb9M105oIKA==
-
-sass-embedded@^1.86.1:
- version "1.86.1"
- resolved "https://registry.yarnpkg.com/sass-embedded/-/sass-embedded-1.86.1.tgz#970e7221ae95e2706907fff4975ac33b949c26e2"
- integrity sha512-LMJvytHh7lIUtmjGCqpM4cRdIDvPllLJKznNIK4L7EZJ77BLeUFoOSRXEOHq4G4gqy5CVhHUKlHslzCANkDOhQ==
- dependencies:
- "@bufbuild/protobuf" "^2.0.0"
- buffer-builder "^0.2.0"
- colorjs.io "^0.5.0"
- immutable "^5.0.2"
- rxjs "^7.4.0"
- supports-color "^8.1.1"
- sync-child-process "^1.0.2"
- varint "^6.0.0"
- optionalDependencies:
- sass-embedded-android-arm "1.86.1"
- sass-embedded-android-arm64 "1.86.1"
- sass-embedded-android-ia32 "1.86.1"
- sass-embedded-android-riscv64 "1.86.1"
- sass-embedded-android-x64 "1.86.1"
- sass-embedded-darwin-arm64 "1.86.1"
- sass-embedded-darwin-x64 "1.86.1"
- sass-embedded-linux-arm "1.86.1"
- sass-embedded-linux-arm64 "1.86.1"
- sass-embedded-linux-ia32 "1.86.1"
- sass-embedded-linux-musl-arm "1.86.1"
- sass-embedded-linux-musl-arm64 "1.86.1"
- sass-embedded-linux-musl-ia32 "1.86.1"
- sass-embedded-linux-musl-riscv64 "1.86.1"
- sass-embedded-linux-musl-x64 "1.86.1"
- sass-embedded-linux-riscv64 "1.86.1"
- sass-embedded-linux-x64 "1.86.1"
- sass-embedded-win32-arm64 "1.86.1"
- sass-embedded-win32-ia32 "1.86.1"
- sass-embedded-win32-x64 "1.86.1"
-
-sax@^1.4.1:
- version "1.6.1"
- resolved "https://registry.yarnpkg.com/sax/-/sax-1.6.1.tgz#4c23cf608c0b693ab54b4b5888e92cfe977b9843"
- integrity sha512-42tBVwLWnaQvW5zc4HbZrTuWccECCZfBi92FDuwtqxasH+JbPB3/FOKb1m222K42R4WxuxzzMsTswfzgtSu64Q==
-
-scule@^1.3.0:
- version "1.3.0"
- resolved "https://registry.yarnpkg.com/scule/-/scule-1.3.0.tgz#6efbd22fd0bb801bdcc585c89266a7d2daa8fbd3"
- integrity sha512-6FtHJEvt+pVMIB9IBY+IcCJ6Z5f1iQnytgyfKMhDKgmzYG+TeH/wx1y3l27rshSbLiSanrR9ffZDrEsmjlQF2g==
-
-section-matter@^1.0.0:
- version "1.0.0"
- resolved "https://registry.npmjs.org/section-matter/-/section-matter-1.0.0.tgz"
- integrity sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA==
- dependencies:
- extend-shallow "^2.0.1"
- kind-of "^6.0.0"
-
-shiki@^4.4.3:
- version "4.4.3"
- resolved "https://registry.yarnpkg.com/shiki/-/shiki-4.4.3.tgz#31fb41c5c82435779a0b5a9b92a3b0377b061e15"
- integrity sha512-Mb/GvXPHBAXdgGIcnfU5L3ldpn1XcxrGkPHwqgRx17/I2XRfqlFKk2vGkHWINn1kdXvzJZeuO3is6I9KLPFm0g==
- dependencies:
- "@shikijs/core" "4.4.3"
- "@shikijs/engine-javascript" "4.4.3"
- "@shikijs/engine-oniguruma" "4.4.3"
- "@shikijs/langs" "4.4.3"
- "@shikijs/themes" "4.4.3"
- "@shikijs/types" "4.4.3"
- "@shikijs/vscode-textmate" "^10.0.2"
- "@types/hast" "^3.0.5"
-
-signal-exit@^4.1.0:
- version "4.1.0"
- resolved "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz"
- integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==
-
-sitemap@^9.0.1:
- version "9.0.1"
- resolved "https://registry.yarnpkg.com/sitemap/-/sitemap-9.0.1.tgz#33e9b09e2177eb896e05b16da4219f53919842f6"
- integrity sha512-S6hzjGJSG3d6if0YoF5kTyeRJvia6FSTBroE5fQ0bu1QNxyJqhhinfUsXi9fH3MgtXODWvwo2BDyQSnhPQ88uQ==
- dependencies:
- "@types/node" "^24.9.2"
- "@types/sax" "^1.2.1"
- arg "^5.0.0"
- sax "^1.4.1"
-
-source-map-js@^1.2.1:
- version "1.2.1"
- resolved "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz"
- integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==
-
-space-separated-tokens@^2.0.0:
- version "2.0.2"
- resolved "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz"
- integrity sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==
-
-sprintf-js@~1.0.2:
- version "1.0.3"
- resolved "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz"
- integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==
-
-stdin-discarder@^0.3.2:
- version "0.3.2"
- resolved "https://registry.yarnpkg.com/stdin-discarder/-/stdin-discarder-0.3.2.tgz#998ab3b9a988661e6036a9bfdc96f43649e8e83e"
- integrity sha512-eCPu1qRxPVkl5605OTWF8Wz40b4Mf45NY5LQmVPQ599knfs5QhASUm9GbJ5BDMDOXgrnh0wyEdvzmL//YMlw0A==
-
-string-width@^8.1.0:
- version "8.3.0"
- resolved "https://registry.yarnpkg.com/string-width/-/string-width-8.3.0.tgz#f1e85ca6d276ed9226c979c3651fb7abf548fbbe"
- integrity sha512-ZbmZM0JCihQN91dWnxoipT2KOEyHqEyfRXUyjuRhW8b/xnqPDoq4gWEVApTVa9db2wN8mmoikgFBbjh71+cGeQ==
- dependencies:
- get-east-asian-width "^1.5.0"
- strip-ansi "^7.1.2"
-
-stringify-entities@^4.0.0:
- version "4.0.4"
- resolved "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz"
- integrity sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==
- dependencies:
- character-entities-html4 "^2.0.0"
- character-entities-legacy "^3.0.0"
-
-strip-ansi@^7.1.2:
- version "7.2.0"
- resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.2.0.tgz#d22a269522836a627af8d04b5c3fd2c7fa3e32e3"
- integrity sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==
- dependencies:
- ansi-regex "^6.2.2"
-
-strip-bom-string@^1.0.0:
- version "1.0.0"
- resolved "https://registry.npmjs.org/strip-bom-string/-/strip-bom-string-1.0.0.tgz"
- integrity sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g==
-
-supports-color@^8.1.1:
- version "8.1.1"
- resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c"
- integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==
- dependencies:
- has-flag "^4.0.0"
-
-sync-child-process@^1.0.2:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/sync-child-process/-/sync-child-process-1.0.2.tgz#45e7c72e756d1243e80b547ea2e17957ab9e367f"
- integrity sha512-8lD+t2KrrScJ/7KXCSyfhT3/hRq78rC0wBFqNJXv3mZyn6hW2ypM05JmlSvtqRbeq6jqA94oHbxAr2vYsJ8vDA==
- dependencies:
- sync-message-port "^1.0.0"
-
-sync-message-port@^1.0.0:
- version "1.1.3"
- resolved "https://registry.yarnpkg.com/sync-message-port/-/sync-message-port-1.1.3.tgz#6055c565ee8c81d2f9ee5aae7db757e6d9088c0c"
- integrity sha512-GTt8rSKje5FilG+wEdfCkOcLL7LWqpMlr2c3LRuKt/YXxcJ52aGSbGBAdI4L3aaqfrBt6y711El53ItyH1NWzg==
-
-synckit@^0.11.13:
- version "0.11.13"
- resolved "https://registry.yarnpkg.com/synckit/-/synckit-0.11.13.tgz#062a5ea57d81befc35892f8254de5c567e97c80a"
- integrity sha512-eNRKgb3z66Yp3D2CixVujOUvXLFUTij/zVnV8KRyvFdQwpz7I5DS8UfRkTeLzb64u+dkzDSdelE24izu+zSSUg==
- dependencies:
- "@pkgr/core" "^0.3.6"
-
-tinyglobby@^0.2.17:
- version "0.2.17"
- resolved "https://registry.yarnpkg.com/tinyglobby/-/tinyglobby-0.2.17.tgz#562a9a6c9eb2b3b123d39719f9af5bb44fcd7631"
- integrity sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==
- dependencies:
- fdir "^6.5.0"
- picomatch "^4.0.4"
-
-trim-lines@^3.0.0:
- version "3.0.1"
- resolved "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz"
- integrity sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==
-
-trough@^2.0.0:
- version "2.2.0"
- resolved "https://registry.yarnpkg.com/trough/-/trough-2.2.0.tgz#94a60bd6bd375c152c1df911a4b11d5b0256f50f"
- integrity sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==
-
-tslib@^2.1.0:
- version "2.8.1"
- resolved "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz"
- integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==
-
-uc.micro@^2.0.0, uc.micro@^2.1.0:
- version "2.1.0"
- resolved "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz"
- integrity sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==
-
-ufo@^1.6.3:
- version "1.6.4"
- resolved "https://registry.yarnpkg.com/ufo/-/ufo-1.6.4.tgz#7a8fb875fcc6382d2c7d0b3692738b0500a92467"
- integrity sha512-JFNbkD1Svwe0KvGi8GOeLcP4kAWQ609twvCdcHxq1oSL8svv39ZuSvajcD8B+5D0eL4+s1Is2D/O6KN3qcTeRA==
-
-"undici-types@>=7.24.0 <7.24.7":
- version "7.24.6"
- resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-7.24.6.tgz#61275b485d7fd4e9d269c7cf04ec2873c9cc0f91"
- integrity sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==
-
-undici-types@~6.20.0:
- version "6.20.0"
- resolved "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz"
- integrity sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==
-
-undici@^7.19.0:
- version "7.30.0"
- resolved "https://registry.yarnpkg.com/undici/-/undici-7.30.0.tgz#64a947266eb7e21dbeac8b99f8720feef308bbe0"
- integrity sha512-dkrQXeHSaoamnItlYbmzG0wFYrM0ZwDxCIg0A7aKjTyyhh9svRzCNFEzV+Vm05/yehjCzjDZ31KXfGEjYSztDQ==
-
-unified@^11.0.0, unified@^11.0.5:
- version "11.0.5"
- resolved "https://registry.yarnpkg.com/unified/-/unified-11.0.5.tgz#f66677610a5c0a9ee90cab2b8d4d66037026d9e1"
- integrity sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==
- dependencies:
- "@types/unist" "^3.0.0"
- bail "^2.0.0"
- devlop "^1.0.0"
- extend "^3.0.0"
- is-plain-obj "^4.0.0"
- trough "^2.0.0"
- vfile "^6.0.0"
-
-unist-util-is@^6.0.0:
- version "6.0.0"
- resolved "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.0.tgz"
- integrity sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==
- dependencies:
- "@types/unist" "^3.0.0"
-
-unist-util-position@^5.0.0:
- version "5.0.0"
- resolved "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz"
- integrity sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==
- dependencies:
- "@types/unist" "^3.0.0"
-
-unist-util-stringify-position@^4.0.0:
- version "4.0.0"
- resolved "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz"
- integrity sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==
- dependencies:
- "@types/unist" "^3.0.0"
-
-unist-util-visit-parents@^6.0.0:
- version "6.0.1"
- resolved "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz"
- integrity sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==
- dependencies:
- "@types/unist" "^3.0.0"
- unist-util-is "^6.0.0"
-
-unist-util-visit@^5.0.0:
- version "5.0.0"
- resolved "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.0.0.tgz"
- integrity sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==
- dependencies:
- "@types/unist" "^3.0.0"
- unist-util-is "^6.0.0"
- unist-util-visit-parents "^6.0.0"
-
-universalify@^2.0.0:
- version "2.0.1"
- resolved "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz"
- integrity sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==
-
-unplugin-utils@^0.3.0, unplugin-utils@^0.3.2:
- version "0.3.2"
- resolved "https://registry.yarnpkg.com/unplugin-utils/-/unplugin-utils-0.3.2.tgz#22f6856408880b0b2d7dd974084faf94094613ef"
- integrity sha512-xVToRh2CTmLk2HnEG7ac4rl1MJTT3RFkpS8B++/SnB0kXvuaavD+n3m/vrzyWQOdJNSZQACnbz01pnppbwV5BA==
- dependencies:
- pathe "^2.0.3"
- picomatch "^4.0.4"
-
-unplugin@^3.3.0:
- version "3.4.0"
- resolved "https://registry.yarnpkg.com/unplugin/-/unplugin-3.4.0.tgz#c3e19efdb040222f1e767ec07233410995e29275"
- integrity sha512-9skdIFlCsPdFV7wUfZxNsFInlW+7nJmGu2gkTu0OUhF56aXGsHab9x52/QhdJ4lC7ZDPWTxbiC4ANqVlsuaW3w==
- dependencies:
- "@jridgewell/remapping" "^2.3.5"
- picomatch "^4.0.7"
- webpack-virtual-modules "^0.6.2"
-
-upath@^3.0.8:
- version "3.0.8"
- resolved "https://registry.yarnpkg.com/upath/-/upath-3.0.8.tgz#4c072fbbe04ef2d88d9ec1923457497bc46dc545"
- integrity sha512-YAsrLMIlhfSCm9rga5TZsJ1mXgahs7N0qOTokzU8mFz35hUrYMvVkaEPefI3rEb/vkAWAOj1Km/qdije9RC1kQ==
-
-update-browserslist-db@^1.3.3:
- version "1.3.3"
- resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.3.3.tgz#197e21fb2561fa89f8b94fad605a2b296a18993a"
- integrity sha512-pJ2sYawQS0R/WI928Gj5GlPhTGzbMelq0+4INtSYNDV9ErKJcX6xjGWkoG/VnB3dpUm00zALaqkrUD77pO5TDQ==
- dependencies:
- escalade "^3.2.0"
- picocolors "^1.1.1"
-
-varint@^6.0.0:
- version "6.0.0"
- resolved "https://registry.yarnpkg.com/varint/-/varint-6.0.0.tgz#9881eb0ce8feaea6512439d19ddf84bf551661d0"
- integrity sha512-cXEIW6cfr15lFv563k4GuVuW/fiwjknytD37jIOLSdSWuOI6WnO/oKwmP2FQTU2l01LP8/M5TSAJpzUaGe3uWg==
-
-vfile-location@^5.0.0:
- version "5.0.3"
- resolved "https://registry.yarnpkg.com/vfile-location/-/vfile-location-5.0.3.tgz#cb9eacd20f2b6426d19451e0eafa3d0a846225c3"
- integrity sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg==
- dependencies:
- "@types/unist" "^3.0.0"
- vfile "^6.0.0"
-
-vfile-message@^4.0.0:
- version "4.0.2"
- resolved "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.2.tgz"
- integrity sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==
- dependencies:
- "@types/unist" "^3.0.0"
- unist-util-stringify-position "^4.0.0"
-
-vfile@^6.0.0:
- version "6.0.3"
- resolved "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz"
- integrity sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==
- dependencies:
- "@types/unist" "^3.0.0"
- vfile-message "^4.0.0"
-
-vite@^8.1.5:
- version "8.3.1"
- resolved "https://registry.yarnpkg.com/vite/-/vite-8.3.1.tgz#aa1099a4ccaf104a8c32492951497fa1781d9fce"
- integrity sha512-/bvH9E9tmCXRGp2uXY3WbOldqpTwFkbha/8ANaEQ6VkxhH60KyqLwgZq6lG2y+4uT55x9+9eUHMpQ7uGnOCKjA==
- dependencies:
- lightningcss "^1.33.0"
- picomatch "^4.0.7"
- postcss "^8.5.28"
- rolldown "~1.2.9"
- tinyglobby "^0.2.17"
- optionalDependencies:
- fsevents "~2.3.3"
-
-vue-inline-svg@^4.0.0:
- version "4.0.0"
- resolved "https://registry.npmjs.org/vue-inline-svg/-/vue-inline-svg-4.0.0.tgz"
- integrity sha512-6kFnPZXQ5jPgumqhDGyJbpqtR5/hlkF4euXxMPai2tcnd6n7zYrEUhZPjE49blf3dDjdoslCu1oyk4EqX/KwgA==
-
-vue-router@^5.2.0:
- version "5.3.1"
- resolved "https://registry.yarnpkg.com/vue-router/-/vue-router-5.3.1.tgz#9152b9011803e9012f0c134763c6482516bbb13c"
- integrity sha512-GDBZzgmILxA/kFnkFbJjQZdZ2QQbngnIMMuoUcjhZIfH1RGMaPjPwX5ASnV38qamuA9uhO0RDjSBHTDNG2uXyQ==
- dependencies:
- "@vue-macros/common" "^3.1.3"
- "@vue/devtools-api" "^8.1.5"
- ast-walker-scope "^0.9.0"
- chokidar "^5.0.0"
- confbox "^0.2.4"
- local-pkg "^1.2.1"
- magic-string "^0.30.21"
- mlly "^1.8.2"
- muggle-string "^0.4.1"
- nostics "^1.1.4"
- pathe "^2.0.3"
- picomatch "^4.0.5"
- scule "^1.3.0"
- tinyglobby "^0.2.17"
- unplugin "^3.3.0"
- unplugin-utils "^0.3.2"
-
-vue@^3.5.40, vue@^3.5.43:
- version "3.5.43"
- resolved "https://registry.yarnpkg.com/vue/-/vue-3.5.43.tgz#bab91368e7b9aad9b89dd92e866b120081deb094"
- integrity sha512-o5qZoksdnjIKvW1srZ3ab7pcDNYAerBjRe54D0LBLfRdCYFrSgBHVXokMas35czQc0//lmx4/tuY4ZNQ+Rf2Ng==
- dependencies:
- "@vue/compiler-dom" "3.5.43"
- "@vue/compiler-sfc" "3.5.43"
- "@vue/runtime-dom" "3.5.43"
- "@vue/server-renderer" "3.5.43"
- "@vue/shared" "3.5.43"
-
-vuepress@^2.0.0-rc.31:
- version "2.0.0-rc.31"
- resolved "https://registry.yarnpkg.com/vuepress/-/vuepress-2.0.0-rc.31.tgz#74c39ba9d1124845a45279b86990aff9d49fc9df"
- integrity sha512-IcJ4E5iOL4aoERm5d+W4j6rJeH/gxDx+NGxvLYvEVPInmv2p90Mjj4VFjFoxqlWc7lIwwFNIVYpPI6dw0miH4Q==
- dependencies:
- "@vuepress/cli" "2.0.0-rc.31"
- "@vuepress/client" "2.0.0-rc.31"
- "@vuepress/core" "2.0.0-rc.31"
- "@vuepress/markdown" "2.0.0-rc.31"
- "@vuepress/shared" "2.0.0-rc.31"
- "@vuepress/utils" "2.0.0-rc.31"
- vue "^3.5.40"
-
-web-namespaces@^2.0.0:
- version "2.0.1"
- resolved "https://registry.yarnpkg.com/web-namespaces/-/web-namespaces-2.0.1.tgz#1010ff7c650eccb2592cebeeaf9a1b253fd40692"
- integrity sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==
-
-webpack-virtual-modules@^0.6.2:
- version "0.6.2"
- resolved "https://registry.yarnpkg.com/webpack-virtual-modules/-/webpack-virtual-modules-0.6.2.tgz#057faa9065c8acf48f24cb57ac0e77739ab9a7e8"
- integrity sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==
-
-whatwg-encoding@^3.1.1:
- version "3.1.1"
- resolved "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz"
- integrity sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==
- dependencies:
- iconv-lite "0.6.3"
-
-whatwg-mimetype@^4.0.0:
- version "4.0.0"
- resolved "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz"
- integrity sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==
-
-yoctocolors@^2.1.1:
- version "2.2.0"
- resolved "https://registry.yarnpkg.com/yoctocolors/-/yoctocolors-2.2.0.tgz#c4b68ee477f3982c33e4de24a5aa4a354be506d3"
- integrity sha512-xYqdZFUK/VYazNl/oCDYN+3WloWQwMfZxBoiNt6qNyk+xfOdi598muWE42rNZFp1kNOiqW936q5RhUdnpqElSg==
-
-zwitch@^2.0.4:
- version "2.0.4"
- resolved "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz"
- integrity sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==