├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── cgb └── picture.asm ├── data ├── font_8x8.chr ├── gbcyus.atr ├── gbcyus.bmp ├── gbcyus.chr ├── gbcyus.map ├── gbcyus.pal ├── gbhorror.bmp ├── gbhorror.chr ├── gbhorror.map ├── hlgbmcp.bmp ├── hlgbmcp.chr ├── hlgbmcp_bg.map ├── hlgbmcp_win.map ├── opti.bmp ├── opti.chr ├── opti.map ├── tsodki1.bmp ├── tsodki1.chr ├── tsodki1.map ├── tsodki2.bmp ├── tsodki2.chr └── tsodki2.map ├── dmg ├── bg_scroll_x_y.asm ├── hello.asm ├── joypad.asm ├── large_picture.asm ├── meta_sprite.asm ├── picture.asm ├── score_bcd.asm ├── score_hex.asm ├── sprite.asm ├── sprite_collision.asm ├── timer_clock.asm └── window.asm ├── inc └── hardware.inc └── make.cmd /.gitignore: -------------------------------------------------------------------------------- 1 | *.gb 2 | *.gbc -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | {one line to give the program's name and a brief idea of what it does.} 635 | Copyright (C) {year} {name of author} 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | {project} Copyright (C) {year} {fullname} 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . 675 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | AS := rgbasm 2 | ASFLAGS := -i inc/ -i data/ -o 3 | LD := rgblink 4 | LDFLAGS1 := -d -o 5 | LDFLAGS2 := -o 6 | FX := rgbfix 7 | FXFLAGS1 := -p 0 -r 0 -t DMG_EXAMPLE -v 8 | FXFLAGS2 := -C -p 0 -r 0 -t CGB_EXAMPLE -v 9 | 10 | dmg_src := $(wildcard dmg/*.asm) 11 | cgb_src := $(wildcard cgb/*.asm) 12 | dmg_rom := $(dmg_src:.asm=.gb) 13 | cgb_rom := $(cgb_src:.asm=.gbc) 14 | 15 | all: $(dmg_rom) $(cgb_rom) 16 | 17 | %.gb: %.o 18 | $(LD) $(LDFLAGS1) $@ $< 19 | $(FX) $(FXFLAGS1) $@ 20 | 21 | %.gbc: %.o 22 | $(LD) $(LDFLAGS2) $@ $< 23 | $(FX) $(FXFLAGS2) $@ 24 | 25 | %.o: %.asm 26 | $(AS) $(ASFLAGS) $@ $< 27 | 28 | clean: 29 | rm -f dmg/*.o 30 | rm -f dmg/*.gb 31 | rm -f cgb/*.o 32 | rm -f cgb/*.gbc 33 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### Notice 2 | Starting October 12th, 2023 GitHub is enforcing mandatory [two-factor authentication](https://github.blog/2023-03-09-raising-the-bar-for-software-security-github-2fa-begins-march-13/) on my account. 3 | I'm not going to comply and move all my activity to GitLab instead. 4 | Any future updates / releases will be available at: [https://gitlab.com/gitendo/helloworld](https://gitlab.com/gitendo/helloworld) 5 | Thanks and see you there! 6 | ___ 7 | 8 | # Hello Game Boy! 9 | This repo started as simple "Hello world!" for Gameboy (DMG) written in assembly language. Few stars later I thought I could start adding some other examples as well, including Game Boy Color (CGB). So here it is - new folder structure, makefiles for Windows / Linux and content that will gradually follow. Everything commented and ready to assemble and link with [RGBASM](https://github.com/rednex/rgbds). Currently it contains: 10 | 11 | ``` 12 | [DMG] 13 | - Hello world 14 | - Display picture composed of 242 unique tiles 15 | - Display picture composed of 355 unique tiles 16 | - Background scroll (clockwise) 17 | - Reading joypad state 18 | - Window 19 | - Single, d-pad moveable sprite 20 | - Meta sprite 21 | - 8x8 sprite collision detection 22 | - Game score in Binary Coded Decimal 23 | - Game score in hexadecimal 24 | - ClockBoy - timer based clock 25 | 26 | [CGB] 27 | - Display picture composed of 247 unique tiles, 8 palettes 28 | ``` 29 | On Windows make sure RGBDS binaries are added to path - here's [how to](https://www.computerhope.com/issues/ch000549.htm) in case you don't know. Otherwise you'd need to change path to files being included and use these commands for each source file: 30 | 31 | ``` 32 | rgbasm.exe -o hello.o hello.s 33 | rgblink.exe -d -o hello.gb hello.o 34 | rgbfix.exe -p 0 -r 0 -v hello.gb 35 | ``` 36 | -------------------------------------------------------------------------------- /cgb/picture.asm: -------------------------------------------------------------------------------- 1 | ; display color picture composed of 247 unique tiles, 8 palettes by tmk @ https://github.com/gitendo/ 2 | ; Yus Bird goes Gameboy Color pixeled by ptoing @ http://pixeljoint.com/pixelart/55124.htm 3 | 4 | INCLUDE "hardware.inc" ; system defines 5 | 6 | SECTION "Start",ROM0[$100] ; start vector, followed by header data applied by rgbfix.exe 7 | nop 8 | jp start 9 | 10 | SECTION "Example",ROM0[$150] ; code starts here 11 | 12 | start: 13 | di ; disable interrupts 14 | ld sp,$E000 ; setup stack 15 | 16 | .wait_vbl ; wait for vblank to properly disable lcd 17 | ld a,[rLY] 18 | cp $90 19 | jr nz,.wait_vbl 20 | 21 | xor a ; reset important registers 22 | ld [rIF],a 23 | ld [rLCDC],a 24 | ld [rSTAT],a 25 | ld [rSCX],a 26 | ld [rSCY],a 27 | ld [rLYC],a 28 | ld [rIE],a 29 | ld [rVBK],a 30 | ld [rSVBK],a 31 | ld [rRP],a 32 | 33 | ld hl,_RAM ; clear ram (fill with a which is 0 here) 34 | ld bc,$2000-2 ; watch out for stack ;) 35 | call fill 36 | 37 | ld hl,_HRAM ; clear hram 38 | ld c,$80 ; a = 0, b = 0 here, so let's save a byte and 4 cycles (ld c,$80 - 2/8 vs ld bc,$80 - 3/12) 39 | call fill 40 | ; no point in clearing vram, we'll overwrite it with picture data anyway 41 | ; lcdc is already disabled so we have 'easy' access to vram 42 | 43 | ld hl,picture_chr ; picture data 44 | ld de,_VRAM ; place it between $8000-8FFF (tiles are numbered here from 0 to 255) 45 | ld bc,3952 ; gbhorror.chr file size 46 | call copy 47 | 48 | ld hl,picture_map ; picture map (160x144px padded = 32*18) 49 | ld de,_SCRN0 ; place it at $9800 50 | ld bc,576 ; gbcyus.map file size 51 | call copy 52 | 53 | ld a,1 ; switch to vram bank 1 54 | ld [rVBK],a ; this is where we place attribute map 55 | 56 | ld hl,picture_atr ; picture attributes 57 | ld de,_SCRN0 ; place it at $9800 just like map 58 | ld bc,576 ; gbcyus.atr file size 59 | call copy 60 | 61 | xor a ; switch back to vram bank 0 62 | ld [rVBK],a 63 | 64 | ld hl,picture_pal ; picture palette 65 | ld b,64 ; gbcyus.pal file size 66 | ; 1 palette has 4 colors, 1 color takes 2 bytes, so 8 palettes = 64 bytes 67 | call set_bg_pal 68 | 69 | 70 | ld a,LCDCF_ON | LCDCF_BG8000 | LCDCF_BG9800 | LCDCF_OBJ8 | LCDCF_OBJOFF | LCDCF_WINOFF | LCDCF_BGON 71 | ; lcd setup: tiles at $8000, map at $9800, 8x8 sprites (disabled), no window, etc. 72 | ld [rLCDC],a ; enable lcd 73 | 74 | .the_end 75 | halt ; save battery 76 | ; nop ; nop after halt is mandatory but rgbasm takes care of it :) 77 | jr .the_end ; endless loop 78 | 79 | 80 | ;------------------------------------------------------------------------------- 81 | copy: 82 | ;------------------------------------------------------------------------------- 83 | ; hl - source address 84 | ; de - destination 85 | ; bc - size 86 | 87 | inc b 88 | inc c 89 | jr .skip 90 | .copy 91 | ld a,[hl+] 92 | ld [de],a 93 | inc de 94 | .skip 95 | dec c 96 | jr nz,.copy 97 | dec b 98 | jr nz,.copy 99 | ret 100 | 101 | 102 | ;------------------------------------------------------------------------------- 103 | fill: 104 | ;------------------------------------------------------------------------------- 105 | ; a - byte to fill with 106 | ; hl - destination address 107 | ; bc - size of area to fill 108 | 109 | inc b 110 | inc c 111 | jr .skip 112 | .fill 113 | ld [hl+],a 114 | .skip 115 | dec c 116 | jr nz,.fill 117 | dec b 118 | jr nz,.fill 119 | ret 120 | 121 | 122 | ;------------------------------------------------------------------------------- 123 | set_bg_pal: 124 | ;------------------------------------------------------------------------------- 125 | ld a,%10000000 ; bit 7 - enable palette auto increment 126 | ; bits 5,4,3 - palette number (0-7) 127 | ; bits 2,1 - color number (0-3) 128 | ld [rBCPS],a ; we start from color #0 in palette #0 and let the hardware to auto increment those values while we copy palette data 129 | .copy 130 | ld a,[hl+] ; this is really basic = slow way of doing things 131 | ldh [rBCPD],a 132 | dec b 133 | jr nz,.copy 134 | ret 135 | 136 | ;------------------------------------------------------------------------------- 137 | 138 | picture_chr: ; bmp2cgb -e0 gbcyus.bmp 139 | INCBIN "gbcyus.chr" 140 | picture_map: 141 | INCBIN "gbcyus.map" 142 | picture_atr: 143 | INCBIN "gbcyus.atr" 144 | picture_pal: 145 | INCBIN "gbcyus.pal" 146 | -------------------------------------------------------------------------------- /data/font_8x8.chr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gitendo/helloworld/17cae56b11435ae815d52b5d809c0e7cdd53963a/data/font_8x8.chr -------------------------------------------------------------------------------- /data/gbcyus.atr: -------------------------------------------------------------------------------- 1 | # -------------------------------------------------------------------------------- /data/gbcyus.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gitendo/helloworld/17cae56b11435ae815d52b5d809c0e7cdd53963a/data/gbcyus.bmp -------------------------------------------------------------------------------- /data/gbcyus.chr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gitendo/helloworld/17cae56b11435ae815d52b5d809c0e7cdd53963a/data/gbcyus.chr -------------------------------------------------------------------------------- /data/gbcyus.map: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gitendo/helloworld/17cae56b11435ae815d52b5d809c0e7cdd53963a/data/gbcyus.map -------------------------------------------------------------------------------- /data/gbcyus.pal: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gitendo/helloworld/17cae56b11435ae815d52b5d809c0e7cdd53963a/data/gbcyus.pal -------------------------------------------------------------------------------- /data/gbhorror.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gitendo/helloworld/17cae56b11435ae815d52b5d809c0e7cdd53963a/data/gbhorror.bmp -------------------------------------------------------------------------------- /data/gbhorror.chr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gitendo/helloworld/17cae56b11435ae815d52b5d809c0e7cdd53963a/data/gbhorror.chr -------------------------------------------------------------------------------- /data/gbhorror.map: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gitendo/helloworld/17cae56b11435ae815d52b5d809c0e7cdd53963a/data/gbhorror.map -------------------------------------------------------------------------------- /data/hlgbmcp.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gitendo/helloworld/17cae56b11435ae815d52b5d809c0e7cdd53963a/data/hlgbmcp.bmp -------------------------------------------------------------------------------- /data/hlgbmcp.chr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gitendo/helloworld/17cae56b11435ae815d52b5d809c0e7cdd53963a/data/hlgbmcp.chr -------------------------------------------------------------------------------- /data/hlgbmcp_bg.map: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gitendo/helloworld/17cae56b11435ae815d52b5d809c0e7cdd53963a/data/hlgbmcp_bg.map -------------------------------------------------------------------------------- /data/hlgbmcp_win.map: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gitendo/helloworld/17cae56b11435ae815d52b5d809c0e7cdd53963a/data/hlgbmcp_win.map -------------------------------------------------------------------------------- /data/opti.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gitendo/helloworld/17cae56b11435ae815d52b5d809c0e7cdd53963a/data/opti.bmp -------------------------------------------------------------------------------- /data/opti.chr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gitendo/helloworld/17cae56b11435ae815d52b5d809c0e7cdd53963a/data/opti.chr -------------------------------------------------------------------------------- /data/opti.map: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gitendo/helloworld/17cae56b11435ae815d52b5d809c0e7cdd53963a/data/opti.map -------------------------------------------------------------------------------- /data/tsodki1.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gitendo/helloworld/17cae56b11435ae815d52b5d809c0e7cdd53963a/data/tsodki1.bmp -------------------------------------------------------------------------------- /data/tsodki1.chr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gitendo/helloworld/17cae56b11435ae815d52b5d809c0e7cdd53963a/data/tsodki1.chr -------------------------------------------------------------------------------- /data/tsodki1.map: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gitendo/helloworld/17cae56b11435ae815d52b5d809c0e7cdd53963a/data/tsodki1.map -------------------------------------------------------------------------------- /data/tsodki2.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gitendo/helloworld/17cae56b11435ae815d52b5d809c0e7cdd53963a/data/tsodki2.bmp -------------------------------------------------------------------------------- /data/tsodki2.chr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gitendo/helloworld/17cae56b11435ae815d52b5d809c0e7cdd53963a/data/tsodki2.chr -------------------------------------------------------------------------------- /data/tsodki2.map: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gitendo/helloworld/17cae56b11435ae815d52b5d809c0e7cdd53963a/data/tsodki2.map -------------------------------------------------------------------------------- /dmg/bg_scroll_x_y.asm: -------------------------------------------------------------------------------- 1 | ; ----------------------------------------------------------------------------- 2 | ; Example: Background scroll (clockwise) 3 | ; ----------------------------------------------------------------------------- 4 | ; Optical Illusion pixeled by Phexion @ http://pixeljoint.com/pixelart/81815.htm 5 | ; More examples by tmk @ https://github.com/gitendo/helloworld 6 | ; ----------------------------------------------------------------------------- 7 | 8 | INCLUDE "hardware.inc" ; system defines 9 | 10 | SECTION "V-Blank",ROM0[$0040] ; vblank interrupt handler 11 | jp vblank 12 | 13 | SECTION "Start",ROM0[$100] ; start vector, followed by header data applied by rgbfix.exe 14 | nop 15 | jp start 16 | 17 | SECTION "Example",ROM0[$150] ; code starts here 18 | 19 | start: 20 | di ; disable interrupts 21 | ld sp,$E000 ; setup stack 22 | 23 | .wait_vbl ; wait for vblank to properly disable lcd 24 | ld a,[rLY] 25 | cp $90 26 | jr nz,.wait_vbl 27 | 28 | xor a ; reset important registers 29 | ld [rIF],a 30 | ld [rLCDC],a 31 | ld [rSTAT],a 32 | ld [rSCX],a 33 | ld [rSCY],a 34 | ld [rLYC],a 35 | ld [rIE],a 36 | 37 | ld hl,_RAM ; clear ram (fill with a which is 0 here) 38 | ld bc,$2000-2 ; watch out for stack ;) 39 | call fill 40 | 41 | ld hl,_HRAM ; clear hram 42 | ld c,$80 ; a = 0, b = 0 here, so let's save a byte and 4 cycles (ld c,$80 - 2/8 vs ld bc,$80 - 3/12) 43 | call fill 44 | ; no point in clearing vram, we'll overwrite it with picture data anyway 45 | ; lcdc is already disabled so we have 'easy' access to vram 46 | 47 | ld hl,picture_chr ; picture data 48 | ld de,_VRAM ; place it between $8000-8FFF (tiles are numbered here from 0 to 255) 49 | ld bc,3872 ; opti.chr file size 50 | call copy 51 | 52 | ld hl,picture_map ; picture map (256x256px = 32*32) takes whole _SCRN0 53 | ld de,_SCRN0 ; place it at $9800 54 | ld bc,3872 ; opti.map file size 55 | call copy 56 | 57 | ld a,%00011011 ; bits: 7-6 = 1st color, 5-4 = 2nd, 3-2 = 3rd and 1-0 = 4th color 58 | ; color values: 00 - light, 01 - gray, 10 - dark gray, 11 - dark 59 | ld [rBGP],a ; bg palette 60 | ld [rOBP0],a ; obj palettes (not used in this example) 61 | ld [rOBP1],a 62 | 63 | ld a,IEF_VBLANK ; vblank interrupt 64 | ld [rIE],a ; setup 65 | 66 | ld a,LCDCF_ON | LCDCF_BG8000 | LCDCF_BG9800 | LCDCF_OBJ8 | LCDCF_OBJOFF | LCDCF_WINOFF | LCDCF_BGON 67 | ; lcd setup: tiles at $8000, map at $9800, 8x8 sprites (disabled), no window, etc. 68 | ld [rLCDC],a ; enable lcd 69 | 70 | ei ; enable interrupts 71 | 72 | .the_end 73 | halt ; save battery 74 | ; nop ; nop after halt is mandatory but rgbasm takes care of it :) 75 | jr .the_end ; endless loop 76 | 77 | 78 | vblank: 79 | ldh a,[delay] ; fetch delay value, it's 0 after hram initialization 80 | xor 1 ; (0 xor 1 = 1) then (1 xor 1 = 0) - this makes code bellow to be called every second frame 81 | ldh [delay],a ; store delay value 82 | and a ; check if a = 0 83 | jr z,.scroll ; execute scroll part if so 84 | reti 85 | 86 | .scroll 87 | ldh a,[direction] ; first load direction value 88 | .right 89 | cp 0 ; move right if it's 0 90 | jr nz,.down ; not 'right', check another direction 91 | ldh a,[rSCX] ; increase scroll x 92 | inc a 93 | ldh [rSCX],a 94 | cp 96 ; boundary (256 - 160) 95 | jr nz,.r_done ; we haven't reached it yet 96 | ld a,1 ; boundary reached, change direction to 'down' 97 | ldh [direction],a 98 | .r_done 99 | reti 100 | 101 | .down 102 | cp 1 ; move down if it's 1 103 | jr nz,.left ; not 'down', check another direction 104 | ldh a,[rSCY] ; increase scroll y 105 | inc a 106 | ldh [rSCY],a 107 | cp 112 ; boundary (256 - 144) 108 | jr nz,.d_done ; we haven't reached it yet 109 | ld a,2 ; boundary reached, change direction to 'left' 110 | ldh [direction],a 111 | .d_done 112 | reti 113 | 114 | .left 115 | cp 2 ; move left if it's 2 116 | jr nz,.up ; not 'left', check another direction 117 | ldh a,[rSCX] ; decrease scroll x 118 | dec a 119 | ldh [rSCX],a 120 | and a ; let's see if we reached starting point = 0 121 | jr nz,.l_done ; nope 122 | ld a,3 ; true, change direction to 'up' 123 | ldh [direction],a 124 | .l_done 125 | 126 | reti 127 | 128 | .up ; no point in checking direction here sinc it's last possibility 129 | ldh a,[rSCY] ; decrease scroll y 130 | dec a 131 | ldh [rSCY],a 132 | and a ; let's see if we reached starting point = 0 133 | jr nz,.u_done ; nope 134 | xor a ; true, change direction to 'right' 135 | ldh [direction],a 136 | .u_done 137 | reti 138 | 139 | 140 | ;------------------------------------------------------------------------------- 141 | copy: 142 | ;------------------------------------------------------------------------------- 143 | ; hl - source address 144 | ; de - destination 145 | ; bc - size 146 | 147 | inc b 148 | inc c 149 | jr .skip 150 | .copy 151 | ld a,[hl+] 152 | ld [de],a 153 | inc de 154 | .skip 155 | dec c 156 | jr nz,.copy 157 | dec b 158 | jr nz,.copy 159 | ret 160 | 161 | 162 | ;------------------------------------------------------------------------------- 163 | fill: 164 | ;------------------------------------------------------------------------------- 165 | ; a - byte to fill with 166 | ; hl - destination address 167 | ; bc - size of area to fill 168 | 169 | inc b 170 | inc c 171 | jr .skip 172 | .fill 173 | ld [hl+],a 174 | .skip 175 | dec c 176 | jr nz,.fill 177 | dec b 178 | jr nz,.fill 179 | ret 180 | 181 | ;------------------------------------------------------------------------------- 182 | 183 | picture_chr: ; bmp2cgb -x -y -z opti.bmp 184 | INCBIN "opti.chr" 185 | picture_map: 186 | INCBIN "opti.map" 187 | 188 | 189 | SECTION "Variables",HRAM 190 | 191 | delay: 192 | ds 1 193 | direction: 194 | ds 1 195 | -------------------------------------------------------------------------------- /dmg/hello.asm: -------------------------------------------------------------------------------- 1 | ; ----------------------------------------------------------------------------- 2 | ; Example: Hello world - DMG ver. 3 | ; ----------------------------------------------------------------------------- 4 | ; Font comes from ZX Spectrum - https://en.wikipedia.org/wiki/ZX_Spectrum_character_set 5 | ; More examples by tmk @ https://github.com/gitendo/helloworld 6 | ; ----------------------------------------------------------------------------- 7 | 8 | INCLUDE "hardware.inc" ; system defines 9 | 10 | SECTION "Start",ROM0[$100] ; start vector, followed by header data applied by rgbfix.exe 11 | nop 12 | jp start 13 | 14 | SECTION "Example",ROM0[$150] ; code starts here 15 | 16 | start: 17 | di ; disable interrupts 18 | ld sp,$E000 ; setup stack 19 | 20 | .wait_vbl ; wait for vblank to properly disable lcd 21 | ld a,[rLY] 22 | cp $90 23 | jr nz,.wait_vbl 24 | 25 | xor a 26 | ld [rIF],a ; reset important registers 27 | ld [rLCDC],a 28 | ld [rSTAT],a 29 | ld [rSCX],a 30 | ld [rSCY],a 31 | ld [rLYC],a 32 | ld [rIE],a 33 | 34 | ld hl,_RAM ; clear ram (fill with a which is 0 here) 35 | ld bc,$2000-2 ; watch out for stack ;) 36 | call fill 37 | 38 | ld hl,_HRAM ; clear hram 39 | ld c,$80 ; a = 0, b = 0 here, so let's save a byte and 4 cycles (ld c,$80 - 2/8 vs ld bc,$80 - 3/12) 40 | call fill 41 | 42 | ld hl,_VRAM ; clear vram 43 | ld b,$18 ; a = 0, bc should be $1800; c = 0 here, so.. 44 | call fill 45 | 46 | ld a,$20 ; ascii code for 'space' character 47 | 48 | ; no need to setup hl since _SCRN0 ($9800) and _SCRN1 ($9C00) are part of _VRAM, just continue 49 | 50 | ld b,8 ; bc should be $800 (_SCRN0/1 are 32*32 bytes); c = 0 here, so.. 51 | call fill 52 | 53 | ld a,%10010011 ; bits: 7-6 = 1st color, 5-4 = 2nd, 3-2 = 3rd and 1-0 = 4th color 54 | ; color values: 00 - light, 01 - gray, 10 - dark gray, 11 - dark 55 | ld [rBGP],a ; bg palette 56 | ld [rOBP0],a ; obj palettes (not used in this example) 57 | ld [rOBP1],a 58 | 59 | ld hl,font ; font data 60 | ld de,_VRAM+$200 ; place it here to get ascii mapping ('space' code is $20, tile size $10) 61 | ld bc,1776 ; font_8x8.chr file size 62 | call copy 63 | 64 | ld hl,text ; hello message 65 | ld de,_SCRN0+$100 ; center it a bit 66 | ld c,text_end-text ; b = 0, our string = 18 chars, so.. 67 | call copy ; lcdc is disabled so you have 'easy' access to vram 68 | 69 | ld a,LCDCF_ON | LCDCF_BG8000 | LCDCF_BG9800 | LCDCF_OBJ8 | LCDCF_OBJOFF | LCDCF_WINOFF | LCDCF_BGON 70 | ; lcd setup: tiles at $8000, map at $9800, 8x8 sprites (disabled), no window, etc. 71 | ld [rLCDC],a ; enable lcd 72 | 73 | .the_end 74 | halt ; save battery 75 | ; nop ; nop after halt is mandatory but rgbasm takes care of it :) 76 | jr .the_end ; endless loop 77 | 78 | ;------------------------------------------------------------------------------- 79 | copy: 80 | ;------------------------------------------------------------------------------- 81 | ; hl - source address 82 | ; de - destination 83 | ; bc - size 84 | 85 | inc b 86 | inc c 87 | jr .skip 88 | .copy 89 | ld a,[hl+] 90 | ld [de],a 91 | inc de 92 | .skip 93 | dec c 94 | jr nz,.copy 95 | dec b 96 | jr nz,.copy 97 | ret 98 | 99 | ;------------------------------------------------------------------------------- 100 | fill: 101 | ;------------------------------------------------------------------------------- 102 | ; a - byte to fill with 103 | ; hl - destination address 104 | ; bc - size of area to fill 105 | 106 | inc b 107 | inc c 108 | jr .skip 109 | .fill 110 | ld [hl+],a 111 | .skip 112 | dec c 113 | jr nz,.fill 114 | dec b 115 | jr nz,.fill 116 | ret 117 | 118 | ;------------------------------------------------------------------------------- 119 | 120 | font: 121 | INCBIN "font_8x8.chr" ; converted with https://github.com/gitendo/bmp2cgb 122 | 123 | text: 124 | DB " Hello 8-bit world! " 125 | text_end: -------------------------------------------------------------------------------- /dmg/joypad.asm: -------------------------------------------------------------------------------- 1 | ; ----------------------------------------------------------------------------- 2 | ; Example: Reading joypad state 3 | ; ----------------------------------------------------------------------------- 4 | ; Font comes from ZX Spectrum - https://en.wikipedia.org/wiki/ZX_Spectrum_character_set 5 | ; More examples by tmk @ https://github.com/gitendo/helloworld 6 | ; ----------------------------------------------------------------------------- 7 | 8 | INCLUDE "hardware.inc" ; system defines 9 | 10 | SECTION "VBL",ROM0[$0040] ; vblank interrupt handler 11 | jp vbl 12 | 13 | SECTION "Start",ROM0[$100] ; start vector, followed by header data applied by rgbfix.exe 14 | nop 15 | jp start 16 | 17 | SECTION "Example",ROM0[$150] ; code starts here 18 | 19 | start: 20 | di ; disable interrupts 21 | ld sp,$E000 ; setup stack 22 | 23 | .wait_vbl ; wait for vblank to properly disable lcd 24 | ld a,[rLY] 25 | cp $90 26 | jr nz,.wait_vbl 27 | 28 | xor a 29 | ld [rIF],a ; reset important registers 30 | ld [rLCDC],a 31 | ld [rSTAT],a 32 | ld [rSCX],a 33 | ld [rSCY],a 34 | ld [rLYC],a 35 | ld [rIE],a 36 | 37 | ld hl,_RAM ; clear ram (fill with a which is 0 here) 38 | ld bc,$2000-2 ; watch out for stack ;) 39 | call fill 40 | 41 | ld hl,_HRAM ; clear hram 42 | ld c,$80 ; a = 0, b = 0 here, so let's save a byte and 4 cycles (ld c,$80 - 2/8 vs ld bc,$80 - 3/12) 43 | call fill 44 | 45 | ld hl,_VRAM ; clear vram, lcdc is disabled so you have 'easy' access 46 | ld b,$18 ; a = 0, bc should be $1800; c = 0 here, so.. 47 | call fill 48 | 49 | ld a,$20 ; ascii code for 'space' character 50 | 51 | ; no need to setup hl since _SCRN0 ($9800) and _SCRN1 ($9C00) are part of _VRAM, just continue 52 | 53 | ld b,8 ; bc should be $800 (_SCRN0/1 are 32*32 bytes); c = 0 here, so.. 54 | call fill 55 | 56 | ld a,%10010011 ; bits: 7-6 = 1st color, 5-4 = 2nd, 3-2 = 3rd and 1-0 = 4th color 57 | ; color values: 00 - light, 01 - gray, 10 - dark gray, 11 - dark 58 | ld [rBGP],a ; bg palette 59 | ld [rOBP0],a ; obj palettes (not used in this example) 60 | ld [rOBP1],a 61 | 62 | ld hl,font ; font data 63 | ld de,_VRAM+$200 ; place it here to get ascii mapping ('space' code is $20, tile size $10) 64 | ld bc,1776 ; font_8x8.chr file size 65 | call copy 66 | 67 | ld hl,text ; menu text 68 | ld de,_SCRN0+$60 ; center it a bit 69 | ld b,11 70 | call copy_text 71 | 72 | ld a,IEF_VBLANK ; vblank interrupt 73 | ld [rIE],a ; setup 74 | 75 | ld a,LCDCF_ON | LCDCF_BG8000 | LCDCF_BG9800 | LCDCF_OBJ8 | LCDCF_OBJOFF | LCDCF_WINOFF | LCDCF_BGON 76 | ; lcd setup: tiles at $8000, map at $9800, 8x8 sprites (disabled), no window, etc. 77 | ld [rLCDC],a ; enable lcd 78 | 79 | ei ; enable interrupts 80 | 81 | .loop 82 | call parse_input ; read joypad and update inputs array that holds individual keys status 83 | halt ; save battery 84 | ; nop ; nop after halt is mandatory but rgbasm takes care of it :) 85 | 86 | 87 | jr .loop ; endless loop 88 | 89 | 90 | vbl: ; update screen 91 | ld hl,_SCRN0+$C5 ; this points exactly at vram character between square brackets in 'Down' entry 92 | ld bc,32 ; next line, hl + bc will point to another entry 93 | 94 | ldh a,[btn_dn] ; use ldh instead of ld to copy value from hram, it's one byte shorter and 8 cycles faster 95 | ld [hl],a ; since it's vblank we have easy access to vram, update 1st entry 96 | add hl,bc ; and go to another 97 | 98 | ldh a,[btn_up] ; repeat 7 times ... 99 | ld [hl],a 100 | add hl,bc 101 | 102 | ldh a,[btn_lt] 103 | ld [hl],a 104 | add hl,bc 105 | 106 | ldh a,[btn_rt] 107 | ld [hl],a 108 | add hl,bc 109 | 110 | ldh a,[btn_st] 111 | ld [hl],a 112 | add hl,bc 113 | 114 | ldh a,[btn_sl] 115 | ld [hl],a 116 | add hl,bc 117 | 118 | ldh a,[btn_b] 119 | ld [hl],a 120 | add hl,bc 121 | 122 | ldh a,[btn_a] 123 | ld [hl],a 124 | 125 | reti 126 | 127 | 128 | ;------------------------------------------------------------------------------- 129 | parse_input: 130 | ;------------------------------------------------------------------------------- 131 | 132 | ld a,"-" ; button not pressed, you could write $2D instead 133 | ld hl,inputs ; 8 byte array that holds individual keys status 134 | ld c,8 135 | .clear 136 | ld [hl+],a ; mark all keys as not pressed 137 | dec c 138 | jr nz,.clear 139 | 140 | call read_keys ; read joypad 141 | 142 | ld a,"+" ; button pressed, you could write $2B instead 143 | dec l ; hl points here to next byte after inputs array, move it back to point on btn_a 144 | .btn_a 145 | bit 0,b ; is button a pressed ? (bit must be 1) 146 | jr z,.btn_b ; no, check other key (apparently it's 0) 147 | ldh [btn_a],a ; it is, mark it as + 148 | .btn_b 149 | bit 1,b ; ... 150 | jr z,.select 151 | ldh [btn_b],a 152 | .select 153 | bit 2,b 154 | jr z,.start 155 | ldh [btn_sl],a 156 | .start 157 | bit 3,b 158 | jr z,.right 159 | ldh [btn_st],a 160 | .right 161 | bit 4,b 162 | jr z,.left 163 | ldh [btn_rt],a 164 | .left 165 | bit 5,b 166 | jr z,.up 167 | ldh [btn_lt],a 168 | .up 169 | bit 6,b 170 | jr z,.down 171 | ldh [btn_up],a 172 | .down 173 | bit 7,b 174 | ret z 175 | ldh [btn_dn],a 176 | 177 | ret 178 | 179 | 180 | ;------------------------------------------------------------------------------- 181 | copy: 182 | ;------------------------------------------------------------------------------- 183 | ; hl - source address 184 | ; de - destination 185 | ; bc - size 186 | 187 | inc b 188 | inc c 189 | jr .skip 190 | .copy 191 | ld a,[hl+] 192 | ld [de],a 193 | inc de 194 | .skip 195 | dec c 196 | jr nz,.copy 197 | dec b 198 | jr nz,.copy 199 | ret 200 | 201 | 202 | ;------------------------------------------------------------------------------- 203 | copy_text: 204 | ;------------------------------------------------------------------------------- 205 | ; hl - text to display 206 | ; de - _SCRN0 or _SCRN1 207 | ; b - rows 208 | ; c - columns 209 | 210 | .next_row 211 | ld c,20 212 | .row 213 | ld a,[hl+] ; fetch one byte from text array and increase hl to point to another one 214 | ld [de],a ; store it at _SCRN0 215 | inc de ; unfortunately there's no [de+] 216 | dec c ; one byte done 217 | jr nz,.row ; next byte, copy untill c=0 218 | 219 | ld a,e ; our row = 20 which is what you can see on the screen 220 | add a,12 ; the part you don't see = 12, so we need to add it 221 | jr nc,.skip ; to make sure the next row is copied at right offset 222 | inc d ; nc flag is set when a+12 > 255 223 | .skip 224 | ld e,a 225 | 226 | dec b ; next row, copy untill b=0 227 | jr nz,.next_row 228 | ret 229 | 230 | 231 | ;------------------------------------------------------------------------------- 232 | fill: 233 | ;------------------------------------------------------------------------------- 234 | ; a - byte to fill with 235 | ; hl - destination address 236 | ; bc - size of area to fill 237 | 238 | inc b 239 | inc c 240 | jr .skip 241 | .fill 242 | ld [hl+],a 243 | .skip 244 | dec c 245 | jr nz,.fill 246 | dec b 247 | jr nz,.fill 248 | ret 249 | 250 | 251 | ;------------------------------------------------------------------------------- 252 | read_keys: 253 | ;------------------------------------------------------------------------------- 254 | ; this function returns two different values in b and c registers: 255 | ; b - returns raw state (pressing key triggers given action continuously as long as it's pressed - it does not prevent bouncing) 256 | ; c - returns debounced state (pressing key triggers given action only once - key must be released and pressed again) 257 | 258 | ld a,$20 ; read P15 - returns a, b, select, start 259 | ldh [rP1],a 260 | ldh a,[rP1] ; mandatory 261 | ldh a,[rP1] 262 | cpl ; rP1 returns not pressed keys as 1 and pressed as 0, invert it to make result more readable 263 | and $0f ; lower nibble has a, b, select, start state 264 | swap a 265 | ld b,a 266 | 267 | ld a,$10 ; read P14 - returns up, down, left, right 268 | ldh [rP1],a 269 | ldh a,[rP1] ; mandatory 270 | ldh a,[rP1] 271 | ldh a,[rP1] 272 | ldh a,[rP1] 273 | ldh a,[rP1] 274 | ldh a,[rP1] 275 | cpl ; rP1 returns not pressed keys as 1 and pressed as 0, invert it to make result more readable 276 | and $0f ; lower nibble has up, down, left, right state 277 | or b ; combine P15 and P14 states in one byte 278 | ld b,a ; store it 279 | 280 | ldh a,[previous] ; this is when important part begins, load previous P15 & P14 state 281 | xor b ; result will be 0 if it's the same as current read 282 | and b ; keep buttons that were pressed during this read only 283 | ldh [current],a ; store final result in variable and register 284 | ld c,a 285 | ld a,b ; current P15 & P14 state will be previous in next read 286 | ldh [previous],a 287 | 288 | ld a,$30 ; reset rP1 289 | ldh [rP1],a 290 | 291 | ret 292 | 293 | 294 | ;------------------------------------------------------------------------------- 295 | 296 | font: 297 | INCBIN "font_8x8.chr" ; converted with https://github.com/gitendo/bmp2cgb 298 | 299 | text: 300 | DB " Joypad state: " 301 | DB " " 302 | DB " " 303 | DB " [-] - Down " 304 | DB " [-] - Up " 305 | DB " [-] - Left " 306 | DB " [-] - Right " 307 | DB " [-] - Start " 308 | DB " [-] - Select " 309 | DB " [-] - B " 310 | DB " [-] - A " 311 | 312 | ;------------------------------------------------------------------------------- 313 | 314 | 315 | SECTION "Variables",HRAM 316 | 317 | current: DS 1 ; usually you read keys state and store it into variable for further processing 318 | previous: DS 1 ; this is previous keys state used by debouncing part of read_keys function 319 | inputs: ; array of buttons 320 | btn_dn: DS 1 321 | btn_up: DS 1 322 | btn_lt: DS 1 323 | btn_rt: DS 1 324 | btn_st: DS 1 325 | btn_sl: DS 1 326 | btn_b: DS 1 327 | btn_a: DS 1 -------------------------------------------------------------------------------- /dmg/large_picture.asm: -------------------------------------------------------------------------------- 1 | ; ----------------------------------------------------------------------------- 2 | ; Example: Display picture composed of 355 unique tiles 3 | ; ----------------------------------------------------------------------------- 4 | ; [gameboy demake] the secret of donkey kong island pixeled by tomic @ http://pixeljoint.com/pixelart/28278.htm 5 | ; More examples by tmk @ https://github.com/gitendo/helloworld 6 | ; ----------------------------------------------------------------------------- 7 | 8 | INCLUDE "hardware.inc" ; system defines 9 | 10 | SECTION "VBL",ROM0[$0040] ; vblank interrupt handler 11 | jp vbl 12 | 13 | SECTION "LCDC",ROM0[$0048] ; lcdc interrupt handler 14 | jp lcdc 15 | 16 | 17 | SECTION "Start",ROM0[$100] ; start vector, followed by header data applied by rgbfix.exe 18 | nop 19 | jp start 20 | 21 | SECTION "Example",ROM0[$150] ; code starts here 22 | 23 | start: 24 | di ; disable interrupts 25 | ld sp,$E000 ; setup stack 26 | 27 | .wait_vbl ; wait for vblank to properly disable lcd 28 | ld a,[rLY] 29 | cp $90 30 | jr nz,.wait_vbl 31 | 32 | xor a ; reset important registers 33 | ld [rIF],a 34 | ld [rLCDC],a 35 | ld [rSTAT],a 36 | ld [rSCX],a 37 | ld [rSCY],a 38 | ld [rLYC],a 39 | ld [rIE],a 40 | 41 | ld hl,_RAM ; clear ram (fill with a which is 0 here) 42 | ld bc,$2000-2 ; watch out for stack ;) 43 | call fill 44 | 45 | ld hl,_HRAM ; clear hram 46 | ld c,$80 ; a = 0, b = 0 here, so let's save a byte and 4 cycles (ld c,$80 - 2/8 vs ld bc,$80 - 3/12) 47 | call fill 48 | ; no point in clearing vram, we'll overwrite it with picture data anyway 49 | ; lcdc is already disabled so we have 'easy' access to vram 50 | 51 | ld hl,picture_top_chr ; upper part takes 255 tiles 52 | ld de,_VRAM ; place it between $8000-8FFF (tiles are numbered here from 0 to 255) 53 | ld bc,4080 ; tsodki1.chr file size 54 | call copy 55 | 56 | ld hl,picture_bottom_chr ; bottom part takes 100 tiles 57 | ld de,_VRAM+$1000 ; place it between $8800-97FF (tiles are numbered here from -128 to 127) 58 | ld bc,1600 ; tsodki2.chr file size 59 | call copy 60 | 61 | ld hl,picture_top_map ; picture's map upper part (padded to 32 columns, so we can easily copy it) 62 | ld de,_SCRN0 ; tile map at $9800 63 | ld bc,416 ; tsodki1.map file size 64 | call copy 65 | 66 | ld hl,picture_bottom_map ; picture's map bottom part (also padded) 67 | ; de = _SCRN0+416, so we continue right after upper part 68 | ld bc,160 ; tsodki2.map file size 69 | call copy 70 | 71 | ld a,%00011011 ; bits: 7-6 = 1st color, 5-4 = 2nd, 3-2 = 3rd and 1-0 = 4th color 72 | ; color values: 00 - light, 01 - gray, 10 - dark gray, 11 - dark 73 | ld [rBGP],a ; bg palette 74 | ld [rOBP0],a ; obj palettes (not used in this example) 75 | ld [rOBP1],a 76 | 77 | ld a,104 ; this is where upper part of picture ends and bottom starts, we'll switch map base here 78 | ld [rLYC],a ; line at which lcdc interrupt will be fired 79 | ld a,STATF_LYC ; important! 80 | ld [rSTAT],a ; bit 6 needs to be set to make lcdc interrupt work 81 | 82 | ld a,IEF_VBLANK | IEF_LCDC ; vblank and lcdc interrupts 83 | ld [rIE],a ; setup 84 | 85 | ld a,LCDCF_ON | LCDCF_BG8000 | LCDCF_BG9800 | LCDCF_OBJ8 | LCDCF_OBJOFF | LCDCF_WINOFF | LCDCF_BGON 86 | ; lcd setup: tiles at $8000, map at $9800, 8x8 sprites (disabled), no window, etc. 87 | ld [rLCDC],a ; enable lcd 88 | 89 | ei ; enable interrupts 90 | 91 | .the_end 92 | halt ; save battery 93 | ; nop ; nop after halt is mandatory but rgbasm takes care of it :) 94 | jr .the_end ; endless loop 95 | 96 | 97 | lcdc: ; lcdc interrupt - executed every frame when LY=104 (end of picture's upper part) 98 | ld hl,rLCDC ; contains lcd setup 99 | res 4,[hl] ; change map location to $9C00, currently it's $9800 100 | reti 101 | 102 | vbl: ; vblank interrupt - executed every frame when LY=144 103 | ld hl,rLCDC ; contains lcd setup 104 | set 4,[hl] ; restore map location to $9800, currently it's $9C00 105 | reti ; return from interrupt 106 | 107 | 108 | ;------------------------------------------------------------------------------- 109 | copy: 110 | ;------------------------------------------------------------------------------- 111 | ; hl - source address 112 | ; de - destination 113 | ; bc - size 114 | 115 | inc b 116 | inc c 117 | jr .skip 118 | .copy 119 | ld a,[hl+] 120 | ld [de],a 121 | inc de 122 | .skip 123 | dec c 124 | jr nz,.copy 125 | dec b 126 | jr nz,.copy 127 | ret 128 | 129 | ;------------------------------------------------------------------------------- 130 | fill: 131 | ;------------------------------------------------------------------------------- 132 | ; a - byte to fill with 133 | ; hl - destination address 134 | ; bc - size of area to fill 135 | 136 | inc b 137 | inc c 138 | jr .skip 139 | .fill 140 | ld [hl+],a 141 | .skip 142 | dec c 143 | jr nz,.fill 144 | dec b 145 | jr nz,.fill 146 | ret 147 | 148 | ;------------------------------------------------------------------------------- 149 | 150 | picture_top_chr: ; bmp2cgb -e255 tsodki1.bmp (there's no x,y,x/y duplicates) 151 | INCBIN "tsodki1.chr" 152 | picture_top_map: 153 | INCBIN "tsodki1.map" 154 | picture_bottom_chr: ; bmp2cgb -e255 tsodki1.bmp (there's no x,y,x/y duplicates) 155 | INCBIN "tsodki2.chr" 156 | picture_bottom_map: 157 | INCBIN "tsodki2.map" 158 | -------------------------------------------------------------------------------- /dmg/meta_sprite.asm: -------------------------------------------------------------------------------- 1 | ; ----------------------------------------------------------------------------- 2 | ; Example: Meta sprite 3 | ; ----------------------------------------------------------------------------- 4 | ; Knight pixelled by Stratto @ http://pixeljoint.com/pixelart/52412.htm 5 | ; More examples by tmk @ https://github.com/gitendo/helloworld 6 | ; ----------------------------------------------------------------------------- 7 | 8 | INCLUDE "hardware.inc" ; system defines 9 | 10 | SECTION "VBL",ROM0[$0040] ; vblank interrupt handler 11 | jp vbl 12 | 13 | SECTION "Start",ROM0[$100] ; start vector, followed by header data applied by rgbfix.exe 14 | nop 15 | jp start 16 | 17 | SECTION "Example",ROM0[$150] ; code starts here 18 | 19 | start: 20 | di ; disable interrupts 21 | ld sp,$E000 ; setup stack 22 | 23 | .wait_vbl ; wait for vblank to properly disable lcd 24 | ld a,[rLY] 25 | cp $90 26 | jr nz,.wait_vbl 27 | 28 | xor a 29 | ld [rIF],a ; reset important registers 30 | ld [rLCDC],a 31 | ld [rSTAT],a 32 | ld [rSCX],a 33 | ld [rSCY],a 34 | ld [rLYC],a 35 | ld [rIE],a 36 | 37 | ld hl,_RAM ; clear ram (fill with a which is 0 here) 38 | ld bc,$2000-2 ; watch out for stack ;) 39 | call fill 40 | 41 | ld hl,_HRAM ; clear hram 42 | ld c,$80 ; a = 0, b = 0 here, so let's save a byte and 4 cycles (ld c,$80 - 2/8 vs ld bc,$80 - 3/12) 43 | call fill 44 | 45 | ld hl,_VRAM ; clear vram 46 | ld b,$18 ; a = 0, bc should be $1800; c = 0 here, so.. 47 | call fill 48 | 49 | ld a,$10 ; tile number to fill the _SCRN0 with - 16 tiles is used for meta sprite, rest is empty 50 | 51 | ; no need to setup hl since _SCRN0 ($9800) and _SCRN1 ($9C00) are part of _VRAM, just continue 52 | 53 | ld b,8 ; bc should be $800 (_SCRN0/1 are 32*32 bytes); c = 0 here, so.. 54 | call fill 55 | 56 | ld a,%00011110 ; bits: 7-6 = 1st color, 5-4 = 2nd, 3-2 = 3rd and 1-0 = 4th color 57 | ; color values: 00 - light, 01 - gray, 10 - dark gray, 11 - dark 58 | ld [rBGP],a ; bg palette 59 | ld [rOBP0],a ; obj palettes 60 | ld [rOBP1],a 61 | 62 | ld hl,knight ; tiles used as sprites 63 | ld de,_VRAM 64 | ld bc,256 ; tiles size 65 | call copy 66 | 67 | ld c,$80 ; dma sub will be copied to _HRAM, at $FF80 68 | ld b,dma_sub_end-dma_sub_start ; size of dma sub, which is 10 bytes 69 | ld hl,dma_sub_start ; dma sub code 70 | .copy 71 | ld a,[hl+] 72 | ld [c],a 73 | inc c 74 | dec b 75 | jr nz,.copy 76 | 77 | ld hl,knight_oam_start ; precalculated part of OAM table that contains our meta sprite 78 | ld de,_RAM ; OAM table mirror is located at $C000 79 | ld bc,knight_oam_end-knight_oam_start 80 | call copy 81 | 82 | ld a,IEF_VBLANK ; setup vblank interrupt 83 | ld [rIE],a 84 | 85 | ld a,LCDCF_ON | LCDCF_BG8000 | LCDCF_BG9800 | LCDCF_OBJ16 | LCDCF_OBJON | LCDCF_WINOFF | LCDCF_BGON 86 | ; lcd setup: tiles at $8000, map at $9800, 8x16 sprites (enabled), no window, etc. 87 | ld [rLCDC],a ; enable lcd 88 | 89 | ei ; enable interrupts 90 | 91 | .the_end 92 | halt ; save battery 93 | ; nop ; nop after halt is mandatory but rgbasm takes care of it :) 94 | jr .the_end ; endless loop 95 | 96 | 97 | vbl: 98 | call $FF80 99 | reti 100 | 101 | ;------------------------------------------------------------------------------- 102 | copy: 103 | ;------------------------------------------------------------------------------- 104 | ; hl - source address 105 | ; de - destination 106 | ; bc - size 107 | 108 | inc b 109 | inc c 110 | jr .skip 111 | .copy 112 | ld a,[hl+] 113 | ld [de],a 114 | inc de 115 | .skip 116 | dec c 117 | jr nz,.copy 118 | dec b 119 | jr nz,.copy 120 | ret 121 | 122 | ;------------------------------------------------------------------------------- 123 | fill: 124 | ;------------------------------------------------------------------------------- 125 | ; a - byte to fill with 126 | ; hl - destination address 127 | ; bc - size of area to fill 128 | 129 | inc b 130 | inc c 131 | jr .skip 132 | .fill 133 | ld [hl+],a 134 | .skip 135 | dec c 136 | jr nz,.fill 137 | dec b 138 | jr nz,.fill 139 | ret 140 | 141 | 142 | ;------------------------------------------------------------------------------- 143 | 144 | dma_sub_start: 145 | db $3E,$C0 ; ld a,$C0 ; OAM table mirror in RAM at $C000 146 | db $E0,$46 ; ld [rDMA],a 147 | db $3E,$28 ; ld a,40 ; delay = 160 cycles 148 | ;.copy 149 | db $3D ; dec a 150 | db $20,$FD ; jr nz,.copy 151 | db $C9 ; ret 152 | dma_sub_end: 153 | 154 | ;------------------------------------------------------------------------------- 155 | 156 | ; knight gfx is 32x32 px, so when using 8x8 sprites tile order wouldn't need to be shuffled, they'd be stored one after another: 157 | 158 | ; 0 1 2 3 159 | ; 4 5 6 7 160 | ; 8 9 a b 161 | ; c d e f 162 | 163 | ; but for 8x16 sprites we need to do place the tiles like this: 164 | 165 | ; 0 4 1 5 166 | ; 2 6 3 7 167 | ; 8 C 9 D 168 | ; A E B F 169 | 170 | knight: 171 | db $00,$00,$00,$00,$1C,$00,$12,$0C,$09,$06,$09,$06,$08,$07,$04,$03 ; 0 172 | db $04,$03,$02,$01,$01,$00,$00,$00,$03,$00,$04,$03,$08,$07,$10,$0F ; 4 173 | db $00,$00,$07,$00,$18,$07,$26,$1F,$46,$3F,$9E,$7F,$9F,$7F,$1F,$FF ; 1 174 | db $1E,$F1,$1F,$F0,$9F,$7E,$9F,$7E,$9F,$7E,$DF,$BF,$FF,$C0,$FF,$D0 ; 5 175 | db $00,$00,$00,$00,$C1,$00,$63,$C1,$77,$E3,$7F,$F3,$FF,$F7,$F7,$FE ; 2 176 | db $DF,$BE,$DE,$3C,$BC,$70,$B8,$70,$AE,$70,$F1,$EE,$F9,$06,$F8,$17 ; 6 177 | db $00,$00,$00,$00,$C0,$00,$C0,$80,$80,$00,$80,$00,$80,$00,$00,$00 ; 3 178 | db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$30,$00,$D8,$30 ; 7 179 | db $10,$0F,$11,$0F,$0F,$00,$0F,$00,$0B,$07,$0B,$07,$0B,$07,$0B,$07 ; 8 180 | db $0B,$06,$0F,$00,$08,$07,$08,$07,$07,$00,$00,$00,$00,$00,$00,$00 ; c 181 | db $DF,$AF,$DF,$AE,$D6,$2D,$CC,$33,$D9,$37,$B3,$4E,$A7,$5C,$CE,$3B ; 9 182 | db $9E,$77,$FB,$2C,$FF,$80,$FC,$00,$98,$60,$18,$60,$78,$00,$98,$60 ; d 183 | db $F8,$07,$FF,$C8,$7F,$C8,$EF,$98,$CC,$33,$DC,$E3,$FF,$18,$77,$F8 ; a 184 | db $77,$F8,$D7,$38,$FF,$00,$7E,$01,$C8,$37,$CF,$30,$F0,$00,$C8,$30 ; e 185 | db $8C,$78,$76,$8C,$FA,$04,$FB,$06,$7B,$86,$F9,$06,$52,$EC,$C4,$F8 ; b 186 | db $08,$F0,$90,$60,$20,$C0,$40,$80,$80,$00,$00,$00,$00,$00,$00,$00 ; f 187 | 188 | x equ 72 ; sprite x coordinate 189 | y equ 72 ; sprite y xoordinate 190 | 191 | ; 7 6 5 4 3 2 1 0 192 | ; y, x, chr, atr (priority, v-flip, h-flip, dmg pal, bank, palette, palette, palette) 193 | 194 | knight_oam_start: ; knight gfx is 32x32 px (16 tiles) but we use 8x16 sprites, so we need 8 of them 195 | db y+00, x+00, $00, %00000000 ; 1st row 196 | db y+00, x+08, $02, %00000000 197 | db y+00, x+16, $04, %00000000 198 | db y+00, x+24, $06, %00000000 199 | db y+16, x+00, $08, %00000000 ; 2nd row 200 | db y+16, x+08, $0a, %00000000 201 | db y+16, x+16, $0c, %00000000 202 | db y+16, x+24, $0e, %00000000 203 | knight_oam_end: 204 | -------------------------------------------------------------------------------- /dmg/picture.asm: -------------------------------------------------------------------------------- 1 | ; ----------------------------------------------------------------------------- 2 | ; Example: Display picture composed of 242 unique tiles 3 | ; ----------------------------------------------------------------------------- 4 | ; Gameboy Horror pixeled by vassink @ http://pixeljoint.com/pixelart/61800.htm 5 | ; More examples by tmk @ https://github.com/gitendo/helloworld 6 | ; ----------------------------------------------------------------------------- 7 | 8 | INCLUDE "hardware.inc" ; system defines 9 | 10 | SECTION "Start",ROM0[$100] ; start vector, followed by header data applied by rgbfix.exe 11 | nop 12 | jp start 13 | 14 | SECTION "Example",ROM0[$150] ; code starts here 15 | 16 | start: 17 | di ; disable interrupts 18 | ld sp,$E000 ; setup stack 19 | 20 | .wait_vbl ; wait for vblank to properly disable lcd 21 | ld a,[rLY] 22 | cp $90 23 | jr nz,.wait_vbl 24 | 25 | xor a ; reset important registers 26 | ld [rIF],a 27 | ld [rLCDC],a 28 | ld [rSTAT],a 29 | ld [rSCX],a 30 | ld [rSCY],a 31 | ld [rLYC],a 32 | ld [rIE],a 33 | 34 | ld hl,_RAM ; clear ram (fill with a which is 0 here) 35 | ld bc,$2000-2 ; watch out for stack ;) 36 | call fill 37 | 38 | ld hl,_HRAM ; clear hram 39 | ld c,$80 ; a = 0, b = 0 here, so let's save a byte and 4 cycles (ld c,$80 - 2/8 vs ld bc,$80 - 3/12) 40 | call fill 41 | ; no point in clearing vram, we'll overwrite it with picture data anyway 42 | ; lcdc is already disabled so we have 'easy' access to vram 43 | 44 | ld hl,picture_chr ; picture data 45 | ld de,_VRAM ; place it between $8000-8FFF (tiles are numbered here from 0 to 255) 46 | ld bc,3872 ; gbhorror.chr file size 47 | call copy 48 | 49 | ld hl,picture_map ; picture map (not padded, 160x144px = 20*18) 50 | ld de,_SCRN0 ; place it at $9800 51 | call copy_map ; should have used bmp2cgb with -e option :) 52 | 53 | ld a,%00011011 ; bits: 7-6 = 1st color, 5-4 = 2nd, 3-2 = 3rd and 1-0 = 4th color 54 | ; color values: 00 - light, 01 - gray, 10 - dark gray, 11 - dark 55 | ld [rBGP],a ; bg palette 56 | ld [rOBP0],a ; obj palettes (not used in this example) 57 | ld [rOBP1],a 58 | 59 | ld a,LCDCF_ON | LCDCF_BG8000 | LCDCF_BG9800 | LCDCF_OBJ8 | LCDCF_OBJOFF | LCDCF_WINOFF | LCDCF_BGON 60 | ; lcd setup: tiles at $8000, map at $9800, 8x8 sprites (disabled), no window, etc. 61 | ld [rLCDC],a ; enable lcd 62 | 63 | .the_end 64 | halt ; save battery 65 | ; nop ; nop after halt is mandatory but rgbasm takes care of it :) 66 | jr .the_end ; endless loop 67 | 68 | 69 | ;------------------------------------------------------------------------------- 70 | copy: 71 | ;------------------------------------------------------------------------------- 72 | ; hl - source address 73 | ; de - destination 74 | ; bc - size 75 | 76 | inc b 77 | inc c 78 | jr .skip 79 | .copy 80 | ld a,[hl+] 81 | ld [de],a 82 | inc de 83 | .skip 84 | dec c 85 | jr nz,.copy 86 | dec b 87 | jr nz,.copy 88 | ret 89 | 90 | 91 | ;------------------------------------------------------------------------------- 92 | copy_map: 93 | ;------------------------------------------------------------------------------- 94 | ; hl - map data 95 | ; de - _SCRN0 or _SCRN1 96 | ; b - rows 97 | ; c - columns 98 | ; picture is 160x144px, 99 | ld b,18 ; 144/8 = 18 rows 100 | .next_row 101 | ld c,20 ; 160/8 = 20 tiles each 102 | .row 103 | ld a,[hl+] ; fetch one byte from rom and increase hl to point to another one 104 | ld [de],a ; store it at _SCRN0 105 | inc de ; unfortunately there's no [de+] 106 | dec c ; one byte done 107 | jr nz,.row ; next byte, copy untill c=0 108 | 109 | ld a,e ; our row = 20 which is what you can see on the screen 110 | add a,12 ; the part you don't see is 12, so we need to add it 111 | jr nc,.skip ; to make sure the next row is copied at right offset 112 | inc d ; nc flag is set when a+12 > 255 113 | .skip 114 | ld e,a 115 | 116 | dec b ; next row, copy untill b=0 117 | jr nz,.next_row 118 | ret 119 | 120 | 121 | ;------------------------------------------------------------------------------- 122 | fill: 123 | ;------------------------------------------------------------------------------- 124 | ; a - byte to fill with 125 | ; hl - destination address 126 | ; bc - size of area to fill 127 | 128 | inc b 129 | inc c 130 | jr .skip 131 | .fill 132 | ld [hl+],a 133 | .skip 134 | dec c 135 | jr nz,.fill 136 | dec b 137 | jr nz,.fill 138 | ret 139 | 140 | ;------------------------------------------------------------------------------- 141 | 142 | picture_chr: ; bmp2cgb -x -y -z gbhorror.bmp 143 | INCBIN "gbhorror.chr" 144 | picture_map: 145 | INCBIN "gbhorror.map" 146 | -------------------------------------------------------------------------------- /dmg/score_bcd.asm: -------------------------------------------------------------------------------- 1 | ; ----------------------------------------------------------------------------- 2 | ; Example: Game score in Binary Coded Decimal 3 | ; ----------------------------------------------------------------------------- 4 | ; This is alternative approach to score_hex.asm. If you've never heard of BCD 5 | ; here's some reading: https://ehaskins.com/2018-01-30%20Z80%20DAA/ 6 | ; Font comes from ZX Spectrum - https://en.wikipedia.org/wiki/ZX_Spectrum_character_set 7 | ; More examples by tmk @ https://github.com/gitendo/helloworld 8 | ; ----------------------------------------------------------------------------- 9 | 10 | INCLUDE "hardware.inc" ; system defines 11 | 12 | SECTION "VBL",ROM0[$0040] ; vblank interrupt handler 13 | jp vbl 14 | 15 | SECTION "Start",ROM0[$100] ; start vector, followed by header data applied by rgbfix.exe 16 | nop 17 | jp start 18 | 19 | SECTION "Example",ROM0[$150] ; code starts here 20 | 21 | start: 22 | di ; disable interrupts 23 | ld sp,$E000 ; setup stack 24 | 25 | .wait_vbl ; wait for vblank to properly disable lcd 26 | ld a,[rLY] 27 | cp $90 28 | jr nz,.wait_vbl 29 | 30 | xor a 31 | ld [rIF],a ; reset important registers 32 | ld [rLCDC],a 33 | ld [rSTAT],a 34 | ld [rSCX],a 35 | ld [rSCY],a 36 | ld [rLYC],a 37 | ld [rIE],a 38 | 39 | ld hl,_RAM ; clear ram (fill with a which is 0 here) 40 | ld bc,$2000-2 ; watch out for stack ;) 41 | call fill 42 | 43 | ld hl,_HRAM ; clear hram 44 | ld c,$80 ; a = 0, b = 0 here, so let's save a byte and 4 cycles (ld c,$80 - 2/8 vs ld bc,$80 - 3/12) 45 | call fill 46 | 47 | ld hl,_VRAM ; clear vram, lcdc is disabled so you have 'easy' access 48 | ld b,$18 ; a = 0, bc should be $1800; c = 0 here, so.. 49 | call fill 50 | 51 | ld a,$20 ; ascii code for 'space' character 52 | 53 | ; no need to setup hl since _SCRN0 ($9800) and _SCRN1 ($9C00) are part of _VRAM, just continue 54 | 55 | ld b,8 ; bc should be $800 (_SCRN0/1 are 32*32 bytes); c = 0 here, so.. 56 | call fill 57 | 58 | ld a,%10010011 ; bits: 7-6 = 1st color, 5-4 = 2nd, 3-2 = 3rd and 1-0 = 4th color 59 | ; color values: 00 - light, 01 - gray, 10 - dark gray, 11 - dark 60 | ld [rBGP],a ; bg palette 61 | ld [rOBP0],a ; obj palettes (not used in this example) 62 | ld [rOBP1],a 63 | 64 | ld hl,font ; font data 65 | ld de,_VRAM+$200 ; place it here to get ascii mapping ('space' code is $20, tile size $10) 66 | ld bc,1776 ; font_8x8.chr file size 67 | call copy 68 | 69 | ld hl,text ; menu text 70 | ld de,_SCRN0+$A0 ; center it a bit 71 | ld b,8 ; it has 8 lines 72 | call copy_text 73 | 74 | ld a,IEF_VBLANK ; vblank interrupt 75 | ld [rIE],a ; setup 76 | 77 | ld a,LCDCF_ON | LCDCF_BG8000 | LCDCF_BG9800 | LCDCF_OBJ8 | LCDCF_OBJOFF | LCDCF_WINOFF | LCDCF_BGON 78 | ; lcd setup: tiles at $8000, map at $9800, 8x8 sprites (disabled), no window, etc. 79 | ld [rLCDC],a ; enable lcd 80 | 81 | ei ; enable interrupts 82 | 83 | .loop 84 | call parse_input ; read joypad and update inputs array that holds individual keys status 85 | halt ; save battery 86 | ; nop ; nop after halt is mandatory but rgbasm takes care of it :) 87 | jr .loop ; endless loop 88 | 89 | 90 | vbl: ; update screen 91 | ld hl,score 92 | ld de,_SCRN0+$E7 ; this points exactly at map coordinate where score string is stored 93 | ld c,3 ; score takes 3 bytes, each byte holds 2 digits 94 | .copy ; copy from hram to vram without waiting for access since it's vblank 95 | ld a,[hl+] ; get byte 96 | ld b,a ; store it for further processing 97 | and a,$F0 ; leave upper nible, remove lower one 98 | swap a ; swap nibbles with places, lower is upper now and upper is lower 99 | or $30 ; upper nibble is 3, lower keeps its value - we have valid number in ascii notation now 100 | ld [de],a ; put it on screen - map actually 101 | inc de ; next map entry 102 | ld a,b ; restore byte and repeat process above to lower nibble 103 | and a,$0F 104 | or $30 105 | ld [de],a 106 | inc de 107 | dec c ; repeat 3 times 108 | jr nz,.copy 109 | 110 | reti 111 | 112 | 113 | ;------------------------------------------------------------------------------- 114 | parse_input: 115 | ;------------------------------------------------------------------------------- 116 | 117 | call read_keys ; read joypad 118 | 119 | .btn_a 120 | bit 0,c ; is button a pressed ? (bit must be 1) 121 | jr z,.btn_b ; no, check other key (apparently it's 0) 122 | ld bc,1 ; 1 - thousands and hundreds 123 | ld a,$30 ; increase score by 3000 124 | call increase_score ; read explanation there 125 | ret 126 | 127 | .btn_b 128 | bit 1,c ; ... 129 | jr z,.right 130 | ld bc,1 131 | ld a,$30 ; decrease score by 3000 132 | call decrease_score 133 | ret 134 | 135 | .right 136 | bit 4,b ; b has no debounce check, counter will be increasing while button is pressed 137 | jr z,.left 138 | ld bc,2 ; 2 - tens and ones 139 | ld a,$01 ; increase score by 1 140 | call increase_score 141 | ret 142 | 143 | .left 144 | bit 5,b 145 | jr z,.up 146 | ld bc,2 147 | ld a,$01 ; decrease score by 1 148 | call decrease_score 149 | ret 150 | 151 | .up 152 | bit 6,c 153 | jr z,.down 154 | ld bc,2 ; 2 - tens and ones 155 | ld a,$20 ; increase score by 20 156 | call increase_score 157 | ret 158 | 159 | .down 160 | bit 7,c 161 | jr z,.done 162 | ld bc,2 163 | ld a,$20 ; decrease score by 20 164 | call decrease_score 165 | 166 | .done 167 | ret 168 | 169 | 170 | ;------------------------------------------------------------------------------- 171 | copy: 172 | ;------------------------------------------------------------------------------- 173 | ; hl - source address 174 | ; de - destination 175 | ; bc - size 176 | 177 | inc b 178 | inc c 179 | jr .skip 180 | .copy 181 | ld a,[hl+] 182 | ld [de],a 183 | inc de 184 | .skip 185 | dec c 186 | jr nz,.copy 187 | dec b 188 | jr nz,.copy 189 | ret 190 | 191 | 192 | ;------------------------------------------------------------------------------- 193 | copy_text: 194 | ;------------------------------------------------------------------------------- 195 | ; hl - text to display 196 | ; de - _SCRN0 or _SCRN1 197 | ; b - rows 198 | ; c - columns 199 | 200 | .next_row 201 | ld c,20 202 | .row 203 | ld a,[hl+] ; fetch one byte from text array and increase hl to point to another one 204 | ld [de],a ; store it at _SCRN0 205 | inc de ; unfortunately there's no [de+] 206 | dec c ; one byte done 207 | jr nz,.row ; next byte, copy untill c=0 208 | 209 | ld a,e ; our row = 20 which is what you can see on the screen 210 | add a,12 ; the part you don't see = 12, so we need to add it 211 | jr nc,.skip ; to make sure the next row is copied at right offset 212 | inc d ; nc flag is set when a+12 > 255 213 | .skip 214 | ld e,a 215 | 216 | dec b ; next row, copy untill b=0 217 | jr nz,.next_row 218 | ret 219 | 220 | 221 | ;------------------------------------------------------------------------------- 222 | fill: 223 | ;------------------------------------------------------------------------------- 224 | ; a - byte to fill with 225 | ; hl - destination address 226 | ; bc - size of area to fill 227 | 228 | inc b 229 | inc c 230 | jr .skip 231 | .fill 232 | ld [hl+],a 233 | .skip 234 | dec c 235 | jr nz,.fill 236 | dec b 237 | jr nz,.fill 238 | ret 239 | 240 | 241 | ;------------------------------------------------------------------------------- 242 | read_keys: 243 | ;------------------------------------------------------------------------------- 244 | ; this function returns two different values in b and c registers: 245 | ; b - returns raw state (pressing key triggers given action continuously as long as it's pressed - it does not prevent bouncing) 246 | ; c - returns debounced state (pressing key triggers given action only once - key must be released and pressed again) 247 | 248 | ld a,$20 ; read P15 - returns a, b, select, start 249 | ldh [rP1],a 250 | ldh a,[rP1] ; mandatory 251 | ldh a,[rP1] 252 | cpl ; rP1 returns not pressed keys as 1 and pressed as 0, invert it to make result more readable 253 | and $0f ; lower nibble has a, b, select, start state 254 | swap a 255 | ld b,a 256 | 257 | ld a,$10 ; read P14 - returns up, down, left, right 258 | ldh [rP1],a 259 | ldh a,[rP1] ; mandatory 260 | ldh a,[rP1] 261 | ldh a,[rP1] 262 | ldh a,[rP1] 263 | ldh a,[rP1] 264 | ldh a,[rP1] 265 | cpl ; rP1 returns not pressed keys as 1 and pressed as 0, invert it to make result more readable 266 | and $0f ; lower nibble has up, down, left, right state 267 | or b ; combine P15 and P14 states in one byte 268 | ld b,a ; store it 269 | 270 | ldh a,[previous] ; this is when important part begins, load previous P15 & P14 state 271 | xor b ; result will be 0 if it's the same as current read 272 | and b ; keep buttons that were pressed during this read only 273 | ldh [current],a ; store final result in variable and register 274 | ld c,a 275 | ld a,b ; current P15 & P14 state will be previous in next read 276 | ldh [previous],a 277 | 278 | ld a,$30 ; reset rP1 279 | ldh [rP1],a 280 | 281 | ret 282 | 283 | 284 | ;------------------------------------------------------------------------------- 285 | increase_score: 286 | ;------------------------------------------------------------------------------- 287 | ; score consists of 3 bytes, each byte holds two digits in bcd format ie. 00 - 99 288 | 289 | ; bc - points to a pair of digits in score 290 | ; 0 - hundreds of thousands and tens of thousands, 1 - thousands and hundreds, 2 - tens and ones 291 | 292 | ; a - value of register (upper/lower byte) decides which digit gets increased, use bcd values only ie. $01, $20, $09, etc. 293 | 294 | ; hl - score offset in ram 295 | 296 | ld hl,score ; get offset 297 | add hl,bc ; move to a pair of digits to be updated 298 | inc c ; recycle pointer to a counter of pair of digits left 299 | 300 | add a,[hl] ; add selected pair of digits to value 301 | daa ; keep result as bcd 302 | ld [hl-],a ; store it and move to another pair of digits 303 | ret nc ; carry flag is set when overflow occurs (ie. $99 + $20 = $19), we need to update other pairs then 304 | 305 | .recurse 306 | dec c ; counter keeps track of pair of digits we're dealing with 307 | ret z 308 | ld a,b ; b = 0 here, we save 1 byte and 4 cycles by not using ld a,0 309 | adc a,[hl] ; carry flag is set so 1 will be added to adjacent pair of digits 310 | daa ; keep result as bcd 311 | ld [hl-],a ; store it 312 | call c,.recurse ; if there's overflow go to another pair of digits (update the rest of the score) 313 | ret 314 | 315 | ;------------------------------------------------------------------------------- 316 | decrease_score: 317 | ;------------------------------------------------------------------------------- 318 | ; clone of increase_score that does subtraction, look at the comments above 319 | 320 | ld hl,score 321 | add hl,bc 322 | inc c 323 | 324 | ld b,a 325 | ld a,[hl] 326 | sub a,b 327 | daa 328 | ld [hl-],a 329 | ret nc 330 | ld b,0 331 | 332 | .recurse 333 | dec c 334 | ret z 335 | ld a,[hl] 336 | sbc a,b 337 | daa 338 | ld [hl-],a 339 | call c,.recurse 340 | ret 341 | 342 | 343 | ;------------------------------------------------------------------------------- 344 | 345 | font: 346 | INCBIN "font_8x8.chr" ; converted with https://github.com/gitendo/bmp2cgb 347 | 348 | text: 349 | DB " Score: " 350 | DB " " 351 | DB " 00000 " 352 | DB " " 353 | DB " Press UP or DOWN, " 354 | DB " LEFT or RIGHT, " 355 | DB " A or B " 356 | DB " to update score. " 357 | 358 | ;------------------------------------------------------------------------------- 359 | 360 | 361 | SECTION "Variables",HRAM 362 | 363 | score: DS 3 ; score in bcd format, ranges from 000000 to 999999, should be enough for most games ;) 364 | current: DS 1 ; usually you read keys state and store it into variable for further processing 365 | previous: DS 1 ; this is previous keys state used by debouncing part of read_keys function 366 | -------------------------------------------------------------------------------- /dmg/score_hex.asm: -------------------------------------------------------------------------------- 1 | ; ----------------------------------------------------------------------------- 2 | ; Example: Game score in hexadecimal 3 | ; ----------------------------------------------------------------------------- 4 | ; Font comes from ZX Spectrum - https://en.wikipedia.org/wiki/ZX_Spectrum_character_set 5 | ; More examples by tmk @ https://github.com/gitendo/helloworld 6 | ; ----------------------------------------------------------------------------- 7 | 8 | INCLUDE "hardware.inc" ; system defines 9 | 10 | SECTION "VBL",ROM0[$0040] ; vblank interrupt handler 11 | jp vbl 12 | 13 | SECTION "Start",ROM0[$100] ; start vector, followed by header data applied by rgbfix.exe 14 | nop 15 | jp start 16 | 17 | SECTION "Example",ROM0[$150] ; code starts here 18 | 19 | start: 20 | di ; disable interrupts 21 | ld sp,$E000 ; setup stack 22 | 23 | .wait_vbl ; wait for vblank to properly disable lcd 24 | ld a,[rLY] 25 | cp $90 26 | jr nz,.wait_vbl 27 | 28 | xor a 29 | ld [rIF],a ; reset important registers 30 | ld [rLCDC],a 31 | ld [rSTAT],a 32 | ld [rSCX],a 33 | ld [rSCY],a 34 | ld [rLYC],a 35 | ld [rIE],a 36 | 37 | ld hl,_RAM ; clear ram (fill with a which is 0 here) 38 | ld bc,$2000-2 ; watch out for stack ;) 39 | call fill 40 | 41 | ld hl,_HRAM ; clear hram 42 | ld c,$80 ; a = 0, b = 0 here, so let's save a byte and 4 cycles (ld c,$80 - 2/8 vs ld bc,$80 - 3/12) 43 | call fill 44 | 45 | ld hl,_VRAM ; clear vram, lcdc is disabled so you have 'easy' access 46 | ld b,$18 ; a = 0, bc should be $1800; c = 0 here, so.. 47 | call fill 48 | 49 | ld a,$20 ; ascii code for 'space' character 50 | 51 | ; no need to setup hl since _SCRN0 ($9800) and _SCRN1 ($9C00) are part of _VRAM, just continue 52 | 53 | ld b,8 ; bc should be $800 (_SCRN0/1 are 32*32 bytes); c = 0 here, so.. 54 | call fill 55 | 56 | ld a,%10010011 ; bits: 7-6 = 1st color, 5-4 = 2nd, 3-2 = 3rd and 1-0 = 4th color 57 | ; color values: 00 - light, 01 - gray, 10 - dark gray, 11 - dark 58 | ld [rBGP],a ; bg palette 59 | ld [rOBP0],a ; obj palettes (not used in this example) 60 | ld [rOBP1],a 61 | 62 | ld hl,font ; font data 63 | ld de,_VRAM+$200 ; place it here to get ascii mapping ('space' code is $20, tile size $10) 64 | ld bc,1776 ; font_8x8.chr file size 65 | call copy 66 | 67 | ld hl,text ; menu text 68 | ld de,_SCRN0+$A0 ; center it a bit 69 | ld b,6 ; it has 6 lines 70 | call copy_text 71 | 72 | ld a,IEF_VBLANK ; vblank interrupt 73 | ld [rIE],a ; setup 74 | 75 | ld a,LCDCF_ON | LCDCF_BG8000 | LCDCF_BG9800 | LCDCF_OBJ8 | LCDCF_OBJOFF | LCDCF_WINOFF | LCDCF_BGON 76 | ; lcd setup: tiles at $8000, map at $9800, 8x8 sprites (disabled), no window, etc. 77 | ld [rLCDC],a ; enable lcd 78 | 79 | ei ; enable interrupts 80 | 81 | .loop 82 | call parse_input ; read joypad and update inputs array that holds individual keys status 83 | halt ; save battery 84 | ; nop ; nop after halt is mandatory but rgbasm takes care of it :) 85 | jr .loop ; endless loop 86 | 87 | 88 | vbl: ; update screen 89 | ld hl,ascii_score 90 | ld de,_SCRN0+$E7 ; this points exactly at map coordinate where score string is stored 91 | ld c,5 ; score string length 92 | .copy ; copy from hram to vram without waiting for access since it's vblank 93 | ld a,[hl+] 94 | ld [de],a 95 | inc de 96 | dec c 97 | jr nz,.copy 98 | 99 | 100 | reti 101 | 102 | 103 | ;------------------------------------------------------------------------------- 104 | parse_input: 105 | ;------------------------------------------------------------------------------- 106 | ldh a,[hex_score_h] ; score high byte 107 | ld h,a 108 | ldh a,[hex_score_l] ; score low byte 109 | ld l,a 110 | 111 | call read_keys ; read joypad 112 | 113 | .up 114 | bit 6,b ; joypad state in b has no debounce check (reads constantly pressed buttons) as opposite to c 115 | jr z,.down ; up not pressed, check down 116 | inc hl ; increase score 117 | .down 118 | bit 7,b 119 | jr z,.done ; down not pressed 120 | dec hl ; decrease score 121 | .done 122 | ld a,h ; store current score value in ram 123 | ldh [hex_score_h],a 124 | ld a,l 125 | ldh [hex_score_l],a 126 | 127 | ld c,LOW(ascii_score) ; HRAM offset at which decimal ascii string representing score will be stored 128 | call update_score 129 | 130 | ret 131 | 132 | 133 | ;------------------------------------------------------------------------------- 134 | copy: 135 | ;------------------------------------------------------------------------------- 136 | ; hl - source address 137 | ; de - destination 138 | ; bc - size 139 | 140 | inc b 141 | inc c 142 | jr .skip 143 | .copy 144 | ld a,[hl+] 145 | ld [de],a 146 | inc de 147 | .skip 148 | dec c 149 | jr nz,.copy 150 | dec b 151 | jr nz,.copy 152 | ret 153 | 154 | 155 | ;------------------------------------------------------------------------------- 156 | copy_text: 157 | ;------------------------------------------------------------------------------- 158 | ; hl - text to display 159 | ; de - _SCRN0 or _SCRN1 160 | ; b - rows 161 | ; c - columns 162 | 163 | .next_row 164 | ld c,20 165 | .row 166 | ld a,[hl+] ; fetch one byte from text array and increase hl to point to another one 167 | ld [de],a ; store it at _SCRN0 168 | inc de ; unfortunately there's no [de+] 169 | dec c ; one byte done 170 | jr nz,.row ; next byte, copy untill c=0 171 | 172 | ld a,e ; our row = 20 which is what you can see on the screen 173 | add a,12 ; the part you don't see = 12, so we need to add it 174 | jr nc,.skip ; to make sure the next row is copied at right offset 175 | inc d ; nc flag is set when a+12 > 255 176 | .skip 177 | ld e,a 178 | 179 | dec b ; next row, copy untill b=0 180 | jr nz,.next_row 181 | ret 182 | 183 | 184 | ;------------------------------------------------------------------------------- 185 | fill: 186 | ;------------------------------------------------------------------------------- 187 | ; a - byte to fill with 188 | ; hl - destination address 189 | ; bc - size of area to fill 190 | 191 | inc b 192 | inc c 193 | jr .skip 194 | .fill 195 | ld [hl+],a 196 | .skip 197 | dec c 198 | jr nz,.fill 199 | dec b 200 | jr nz,.fill 201 | ret 202 | 203 | 204 | ;------------------------------------------------------------------------------- 205 | read_keys: 206 | ;------------------------------------------------------------------------------- 207 | ; this function returns two different values in b and c registers: 208 | ; b - returns raw state (pressing key triggers given action continuously as long as it's pressed - it does not prevent bouncing) 209 | ; c - returns debounced state (pressing key triggers given action only once - key must be released and pressed again) 210 | 211 | ld a,$20 ; read P15 - returns a, b, select, start 212 | ldh [rP1],a 213 | ldh a,[rP1] ; mandatory 214 | ldh a,[rP1] 215 | cpl ; rP1 returns not pressed keys as 1 and pressed as 0, invert it to make result more readable 216 | and $0f ; lower nibble has a, b, select, start state 217 | swap a 218 | ld b,a 219 | 220 | ld a,$10 ; read P14 - returns up, down, left, right 221 | ldh [rP1],a 222 | ldh a,[rP1] ; mandatory 223 | ldh a,[rP1] 224 | ldh a,[rP1] 225 | ldh a,[rP1] 226 | ldh a,[rP1] 227 | ldh a,[rP1] 228 | cpl ; rP1 returns not pressed keys as 1 and pressed as 0, invert it to make result more readable 229 | and $0f ; lower nibble has up, down, left, right state 230 | or b ; combine P15 and P14 states in one byte 231 | ld b,a ; store it 232 | 233 | ldh a,[previous] ; this is when important part begins, load previous P15 & P14 state 234 | xor b ; result will be 0 if it's the same as current read 235 | and b ; keep buttons that were pressed during this read only 236 | ldh [current],a ; store final result in variable and register 237 | ld c,a 238 | ld a,b ; current P15 & P14 state will be previous in next read 239 | ldh [previous],a 240 | 241 | ld a,$30 ; reset rP1 242 | ldh [rP1],a 243 | 244 | ret 245 | 246 | 247 | ;------------------------------------------------------------------------------- 248 | update_score: 249 | ;------------------------------------------------------------------------------- 250 | ; found it on Milos "baze" Bazelides webpage and adjusted for gb cpu 251 | ; hl - contains score value, it's 16 bits so maximum value is 65535 252 | ; c - offset in HRAM that stores hl as decimal ascii string 253 | 254 | ld de,-10000 ; count tens of thousands 255 | call .hex2ascii 256 | ld de,-1000 ; count tousands 257 | call .hex2ascii 258 | ld de,-100 ; count hundreds 259 | call .hex2ascii 260 | ld e,-10 ; count tens 261 | call .hex2ascii 262 | ld e,d ; count ones 263 | 264 | .hex2ascii 265 | ld a,$2F ; preload a with $2F = $30 - 1, $30 is "0" in ascii notation 266 | ; code below is executed at least once so we get value in $30 - $39 range 267 | .count 268 | inc a ; increase counter 269 | add hl,de ; adding de to our score value might cause overflow, c flag is set then 270 | jr c,.count ; repeat the loop untill there's no overflow 271 | 272 | ld [c],a ; a contains number of repeats as ascii decimal value, store it 273 | inc c ; next number 274 | 275 | ld a,l ; restore last "overflow" value, hl = hl - de 276 | sub a,e 277 | ld l,a 278 | ld a,h 279 | sbc a,d 280 | ld h,a 281 | 282 | ret 283 | 284 | 285 | ;------------------------------------------------------------------------------- 286 | 287 | font: 288 | INCBIN "font_8x8.chr" ; converted with https://github.com/gitendo/bmp2cgb 289 | 290 | text: 291 | DB " Score: " 292 | DB " " 293 | DB " 00000 " 294 | DB " " 295 | DB " Press UP or DOWN " 296 | DB " to update score. " 297 | 298 | ;------------------------------------------------------------------------------- 299 | 300 | 301 | SECTION "Variables",HRAM 302 | 303 | hex_score_l: DS 1 ; 16 bit number that contains score value (0-65535) 304 | hex_score_h: DS 1 305 | ascii_score: DS 5 ; ascii string that contains hex score in decimal format 306 | current: DS 1 ; usually you read keys state and store it into variable for further processing 307 | previous: DS 1 ; this is previous keys state used by debouncing part of read_keys function 308 | -------------------------------------------------------------------------------- /dmg/sprite.asm: -------------------------------------------------------------------------------- 1 | ; ----------------------------------------------------------------------------- 2 | ; Example: Single, d-pad moveable sprite 3 | ; ----------------------------------------------------------------------------- 4 | ; Font comes from ZX Spectrum - https://en.wikipedia.org/wiki/ZX_Spectrum_character_set 5 | ; More examples by tmk @ https://github.com/gitendo/helloworld 6 | ; ----------------------------------------------------------------------------- 7 | 8 | INCLUDE "hardware.inc" ; system defines 9 | 10 | SECTION "VBL",ROM0[$0040] ; vblank interrupt handler 11 | jp vbl 12 | 13 | SECTION "Start",ROM0[$100] ; start vector, followed by header data applied by rgbfix.exe 14 | nop 15 | jp start 16 | 17 | SECTION "Example",ROM0[$150] ; code starts here 18 | 19 | start: 20 | di ; disable interrupts 21 | ld sp,$E000 ; setup stack 22 | 23 | .wait_vbl ; wait for vblank to properly disable lcd 24 | ld a,[rLY] 25 | cp $90 26 | jr nz,.wait_vbl 27 | 28 | xor a 29 | ld [rIF],a ; reset important registers 30 | ld [rLCDC],a 31 | ld [rSTAT],a 32 | ld [rSCX],a 33 | ld [rSCY],a 34 | ld [rLYC],a 35 | ld [rIE],a 36 | 37 | ld hl,_RAM ; clear ram (fill with a which is 0 here) 38 | ld bc,$2000-2 ; watch out for stack ;) 39 | call fill 40 | 41 | ld hl,_HRAM ; clear hram 42 | ld c,$80 ; a = 0, b = 0 here, so let's save a byte and 4 cycles (ld c,$80 - 2/8 vs ld bc,$80 - 3/12) 43 | call fill 44 | 45 | ld hl,_VRAM ; clear vram 46 | ld b,$18 ; a = 0, bc should be $1800; c = 0 here, so.. 47 | call fill 48 | 49 | ld a,2 ; tile number to fill the _SCRN0 with - tiles 0, 1 are used for sprite, rest is empty 50 | 51 | ; no need to setup hl since _SCRN0 ($9800) and _SCRN1 ($9C00) are part of _VRAM, just continue 52 | 53 | ld b,8 ; bc should be $800 (_SCRN0/1 are 32*32 bytes); c = 0 here, so.. 54 | call fill 55 | 56 | ld a,%01101100 ; bits: 7-6 = 1st color, 5-4 = 2nd, 3-2 = 3rd and 1-0 = 4th color 57 | ; color values: 00 - light, 01 - gray, 10 - dark gray, 11 - dark 58 | ld [rBGP],a ; bg palette 59 | ld [rOBP0],a ; obj palettes 60 | ld [rOBP1],a 61 | 62 | ld hl,heart ; tiles used as sprites 63 | ld de,_VRAM 64 | ld bc,32 ; two tiles, 16 bytes each 65 | call copy 66 | 67 | ld c,$80 ; dma sub will be copied to _HRAM, at $FF80 68 | ld b,dma_sub_end-dma_sub_start ; size of dma sub, which is 10 bytes 69 | ld hl,dma_sub_start ; dma sub code to be copied 70 | .copy 71 | ld a,[hl+] 72 | ld [c],a 73 | inc c 74 | dec b 75 | jr nz,.copy 76 | 77 | ld a,80 ; OAM table mirror is located at $C000, let's set up 1st sprite 78 | ld [$C000],a ; y coordinate 79 | ld a,84 80 | ld [$C001],a ; x coordinate 81 | ; sprite will use tile 0 and no attributes, we already zeroed whole RAM so no need to do it again 82 | 83 | ld a,IEF_VBLANK ; set up vblank interrupt 84 | ld [rIE],a 85 | 86 | ld a,LCDCF_ON | LCDCF_BG8000 | LCDCF_BG9800 | LCDCF_OBJ8 | LCDCF_OBJON | LCDCF_WINOFF | LCDCF_BGON 87 | ; lcd setup: tiles at $8000, map at $9800, 8x8 sprites (enabled), no window, etc. 88 | ld [rLCDC],a ; enable lcd 89 | 90 | ei ; enable interrupts 91 | 92 | .loop 93 | halt ; save battery 94 | ; nop ; nop after halt is mandatory but rgbasm takes care of it :) 95 | 96 | call read_keys ; read joypad 97 | ld hl,$C000 98 | .btn_a 99 | bit 0,b ; is button a pressed ? (bit must be 1) 100 | jr z,.btn_b ; no, check other key (apparently it's 0) 101 | ld l,2 ; hl = $C002 which is 1st sprite in OAM table mirror, located at $C000 102 | xor a ; change sprite to tile 0 103 | ld [hl],a 104 | .btn_b 105 | bit 1,b ; ... 106 | jr z,.right 107 | ld l,2 108 | ld a,1 ; change sprite to tile 1 109 | ld [hl],a 110 | .right 111 | bit 4,b 112 | jr z,.left 113 | ld l,1 ; x coordinate 114 | inc [hl] ; increase 115 | .left 116 | bit 5,b 117 | jr z,.up 118 | ld l,1 ; x coordinate 119 | dec [hl] ; decrease 120 | .up 121 | bit 6,b 122 | jr z,.down 123 | ld l,0 ; y coordinate 124 | dec [hl] ; decrease 125 | .down 126 | bit 7,b 127 | jr z,.loop 128 | ld l,0 ; y coordinate 129 | inc [hl] ; increase 130 | 131 | jr .loop ; endless loop 132 | 133 | 134 | vbl: 135 | call $FF80 ; copy OAM mirror table using DMA 136 | reti 137 | 138 | ;------------------------------------------------------------------------------- 139 | read_keys: 140 | ;------------------------------------------------------------------------------- 141 | ; this function returns two different values in b and c registers: 142 | ; b - returns raw state (pressing key triggers given action continuously as long as it's pressed - it does not prevent bouncing) 143 | ; c - returns debounced state (pressing key triggers given action only once - key must be released and pressed again) 144 | 145 | ld a,$20 ; read P15 - returns a, b, select, start 146 | ldh [rP1],a 147 | ldh a,[rP1] ; mandatory 148 | ldh a,[rP1] 149 | cpl ; rP1 returns not pressed keys as 1 and pressed as 0, invert it to make result more readable 150 | and $0f ; lower nibble has a, b, select, start state 151 | swap a 152 | ld b,a 153 | 154 | ld a,$10 ; read P14 - returns up, down, left, right 155 | ldh [rP1],a 156 | ldh a,[rP1] ; mandatory 157 | ldh a,[rP1] 158 | ldh a,[rP1] 159 | ldh a,[rP1] 160 | ldh a,[rP1] 161 | ldh a,[rP1] 162 | cpl ; rP1 returns not pressed keys as 1 and pressed as 0, invert it to make result more readable 163 | and $0f ; lower nibble has up, down, left, right state 164 | or b ; combine P15 and P14 states in one byte 165 | ld b,a ; store it 166 | 167 | ldh a,[previous] ; this is when important part begins, load previous P15 & P14 state 168 | xor b ; result will be 0 if it's the same as current read 169 | and b ; keep buttons that were pressed during this read only 170 | ldh [current],a ; store final result in variable and register 171 | ld c,a 172 | ld a,b ; current P15 & P14 state will be previous in next read 173 | ldh [previous],a 174 | 175 | ld a,$30 ; reset rP1 176 | ldh [rP1],a 177 | 178 | ret 179 | 180 | 181 | ;------------------------------------------------------------------------------- 182 | copy: 183 | ;------------------------------------------------------------------------------- 184 | ; hl - source address 185 | ; de - destination 186 | ; bc - size 187 | 188 | inc b 189 | inc c 190 | jr .skip 191 | .copy 192 | ld a,[hl+] 193 | ld [de],a 194 | inc de 195 | .skip 196 | dec c 197 | jr nz,.copy 198 | dec b 199 | jr nz,.copy 200 | ret 201 | 202 | ;------------------------------------------------------------------------------- 203 | fill: 204 | ;------------------------------------------------------------------------------- 205 | ; a - byte to fill with 206 | ; hl - destination address 207 | ; bc - size of area to fill 208 | 209 | inc b 210 | inc c 211 | jr .skip 212 | .fill 213 | ld [hl+],a 214 | .skip 215 | dec c 216 | jr nz,.fill 217 | dec b 218 | jr nz,.fill 219 | ret 220 | 221 | 222 | ;------------------------------------------------------------------------------- 223 | dma_sub_start: 224 | ;------------------------------------------------------------------------------- 225 | db $3E,$C0 ; ld a,$C0 ; OAM table mirror in RAM at $C000 (high byte) 226 | db $E0,$46 ; ld [rDMA],a 227 | db $3E,$28 ; ld a,40 ; delay = 160 cycles 228 | ;.copy 229 | db $3D ; dec a 230 | db $20,$FD ; jr nz,.copy 231 | db $C9 ; ret 232 | dma_sub_end: 233 | 234 | ;------------------------------------------------------------------------------- 235 | 236 | heart: 237 | db $00,$6C,$6C,$92,$3C,$82,$7C,$C2,$7C,$82,$38,$44,$10,$28,$00,$10 ; 1st heart tile 238 | db $6C,$6C,$FE,$FE,$FE,$FE,$FE,$FE,$FE,$FE,$7C,$7C,$38,$38,$10,$10 ; 2nd heart tile 239 | 240 | 241 | ;------------------------------------------------------------------------------- 242 | 243 | 244 | SECTION "Variables",HRAM[$FF8A] ; $FF80 - $FF89 is taken by dma_sub function 245 | 246 | current: DS 1 ; usually you read keys state and store it into variable for further processing 247 | previous: DS 1 ; this is previous keys state used by debouncing part of read_keys function 248 | -------------------------------------------------------------------------------- /dmg/sprite_collision.asm: -------------------------------------------------------------------------------- 1 | ; ----------------------------------------------------------------------------- 2 | ; Example: Sprite collision example 3 | ; ----------------------------------------------------------------------------- 4 | ; More examples by tmk @ https://github.com/gitendo/helloworld 5 | ; ----------------------------------------------------------------------------- 6 | 7 | INCLUDE "hardware.inc" ; system defines 8 | 9 | SECTION "VBL",ROM0[$0040] ; vblank interrupt handler 10 | jp vbl 11 | 12 | SECTION "Start",ROM0[$100] ; start vector, followed by header data applied by rgbfix.exe 13 | nop 14 | jp start 15 | 16 | SECTION "Example",ROM0[$150] ; code starts here 17 | 18 | OAM_MIRROR equ $C000 ; oam table mirror will be stored in RAM at $C000 19 | 20 | start: 21 | di ; disable interrupts 22 | ld sp,$E000 ; setup stack 23 | 24 | .wait_vbl ; wait for vblank to properly disable lcd 25 | ld a,[rLY] 26 | cp $90 27 | jr nz,.wait_vbl 28 | 29 | xor a 30 | ld [rIF],a ; reset important registers 31 | ld [rLCDC],a 32 | ld [rSTAT],a 33 | ld [rSCX],a 34 | ld [rSCY],a 35 | ld [rLYC],a 36 | ld [rIE],a 37 | 38 | ld hl,_RAM ; clear ram (fill with a which is 0 here) 39 | ld bc,$2000-2 ; watch out for stack ;) 40 | call fill 41 | 42 | ld hl,_HRAM ; clear hram 43 | ld c,$80 ; a = 0, b = 0 here, so let's save a byte and 4 cycles (ld c,$80 - 2/8 vs ld bc,$80 - 3/12) 44 | call fill 45 | 46 | ld hl,_VRAM ; clear vram 47 | ld b,$18 ; a = 0, bc should be $1800; c = 0 here, so.. 48 | call fill 49 | 50 | ld a,2 ; tile number to fill the _SCRN0 with - tiles 0, 1 are used for sprite, rest is empty 51 | 52 | ; no need to setup hl since _SCRN0 ($9800) and _SCRN1 ($9C00) are part of _VRAM, just continue 53 | 54 | ld b,8 ; bc should be $800 (_SCRN0/1 are 32*32 bytes); c = 0 here, so.. 55 | call fill 56 | 57 | ld a,%01101100 ; bits: 7-6 = 1st color, 5-4 = 2nd, 3-2 = 3rd and 1-0 = 4th color 58 | ; color values: 00 - light, 01 - gray, 10 - dark gray, 11 - dark 59 | ld [rBGP],a ; bg palette 60 | ld [rOBP0],a ; obj palette for "enemy" sprites 61 | ld a,%00111001 ; and inverted for "hero" sprite 62 | ld [rOBP1],a 63 | 64 | ld hl,heart ; tiles used as sprites 65 | ld de,_VRAM 66 | ld bc,32 ; two tiles, 16 bytes each 67 | call copy 68 | 69 | ld c,$80 ; dma sub will be copied to _HRAM, at $FF80 70 | ld b,dma_sub_end-dma_sub_start ; size of dma sub, which is 10 bytes 71 | ld hl,dma_sub_start ; dma sub code to be copied 72 | .copy 73 | ld a,[hl+] 74 | ld [c],a 75 | inc c 76 | dec b 77 | jr nz,.copy 78 | 79 | ld hl,oam_stub ; predefined values for 4 sprites used in this example 80 | ld de,OAM_MIRROR ; sprites table in RAM 81 | ld bc,16 ; 4 sprites, each entry needs 4 bytes 82 | call copy 83 | 84 | ld hl,dir_stub ; predefined values for moving/speed directions (y, x) for each sprite 85 | ld de,directions ; proper table in RAM 86 | ld bc,4*2 87 | call copy 88 | 89 | ld a,IEF_VBLANK ; set up vblank interrupt 90 | ld [rIE],a 91 | 92 | ld a,LCDCF_ON | LCDCF_BG8000 | LCDCF_BG9800 | LCDCF_OBJ8 | LCDCF_OBJON | LCDCF_WINOFF | LCDCF_BGON 93 | ; lcd setup: tiles at $8000, map at $9800, 8x8 sprites (enabled), no window, etc. 94 | ld [rLCDC],a ; enable lcd 95 | 96 | ei ; enable interrupts 97 | 98 | .loop 99 | halt ; save battery 100 | ; nop ; nop after halt is mandatory but rgbasm takes care of it :) 101 | 102 | ldh a,[frame] ; make sure functions below are executed every second frame 103 | and a ; this is to faciliate seeing when collision happens and can be omitted 104 | jr nz,.loop 105 | 106 | call move_sprites ; move sprites around 107 | call check_boundaries ; make sure they stay on the screen 108 | call check_collision ; check if they collide and animate when that happens 109 | 110 | jr .loop ; endless loop 111 | 112 | 113 | ;------------------------------------------------------------------------------- 114 | vbl: 115 | ;------------------------------------------------------------------------------- 116 | push af 117 | call $FF80 ; copy OAM_MIRROR table using DMA 118 | ldh a,[frame] ; vbl is executed 60 times per second 119 | xor 1 ; xoring frame variable causes it to have value of 1 every second frame 120 | ld [frame],a ; this is used in main loop to slow sprites update - 30 fps instead of 60 fps 121 | pop af 122 | reti 123 | 124 | 125 | ;------------------------------------------------------------------------------- 126 | move_sprites: 127 | ;------------------------------------------------------------------------------- 128 | ld de,OAM_MIRROR ; sprites table 129 | ld hl,directions ; movement directions (y, x) 130 | ld c,4 ; number of sprites to move 131 | .move 132 | ld a,[de] ; get sprite y position 133 | add a,[hl] ; depending on movement direction value increase or decrease it to move sprite up or down 134 | ld [de],a ; store updated y position 135 | inc e ; move to sprite x position 136 | inc l ; move to x movement direction 137 | ld a,[de] ; get sprite x position 138 | add a,[hl] ; depending on movement direction value increase or decrease it to move sprite left or right 139 | ld [de],a ; store updated x position 140 | ld a,3 ; move to another sprite y position 141 | add a,e 142 | ld e,a 143 | inc l ; move to another y movement direction 144 | dec c ; do another sprite 145 | jr nz,.move 146 | ret 147 | 148 | 149 | ;------------------------------------------------------------------------------- 150 | check_boundaries: 151 | ;------------------------------------------------------------------------------- 152 | ld hl,OAM_MIRROR ; sprites table 153 | ld de,directions ; movement directions (y, x) for each sprite 154 | ld c,4 ; number of sprites to check 155 | .check_top 156 | ld a,[hl+] ; get sprite y position 157 | cp 16 ; top boundary 158 | jr nz,.check_bottom ; not reached yet 159 | ld a,1 ; top reached, change direction to positive number to move sprite down 160 | ld [de],a ; update y direction 161 | jr .check_left ; no reason to check bottom boundary, do left 162 | .check_bottom 163 | cp 152 ; bottom boundary 164 | jr nz,.check_left ; not reached yet 165 | ld a,-1 ; bottom reached, change direction to negative number to move sprite up 166 | ld [de],a ; update y direction 167 | .check_left 168 | inc e ; move to x movement direction 169 | ld a,[hl+] ; get sprite x position 170 | cp 8 ; left boundary 171 | jr nz,.check_right ; not reached yet 172 | ld a,1 ; left reached, change direction to positive number to move sprite right 173 | ld [de],a ; update x direction 174 | jr .next ; no reason to check right boundary, do next 175 | .check_right 176 | cp 160 ; right boundary 177 | jr nz,.next ; not reached yet 178 | ld a,-1 ; right reached, change direction to negative number to move sprite left 179 | ld [de],a ; update x direction 180 | .next 181 | inc e ; move to y direction (next pair) 182 | inc l ; move to next sprite (skip tile and attribute bytes) 183 | inc l 184 | dec c ; do another sprite 185 | jr nz,.check_top 186 | ret 187 | 188 | 189 | ;------------------------------------------------------------------------------- 190 | check_collision: 191 | ;------------------------------------------------------------------------------- 192 | ld hl,OAM_MIRROR ; sprites table 193 | ld c,3 ; 1 "hero" sprite and 3 "enemy" sprites, for sake of simplicity we don't check collision between "enemy" sprites 194 | 195 | ld a,[hl+] ; get "hero" sprite y position 196 | and $F8 ; strip 3 lower bits to get row where sprite is located (sprites in this example are 8x8 px) 197 | ld d,a ; store it in d 198 | ld a,[hl+] ; get "hero" sprite x position 199 | and $F8 ; strip 3 lower bits to get column where sprite is located 200 | ld e,a ; store it in e 201 | inc l ; move to next sprite 202 | inc l 203 | .next 204 | ld b,0 ; b holds tile number (0 - normal tile, 1 - collision tile) to perform animation during collision 205 | ld a,[hl+] ; get "enemy" sprite y position 206 | and $F8 ; strip 3 lower bits to get row where sprite is located 207 | cp d ; compare with "hero" sprite y position 208 | jr nz,.l1 ; skip if not equal 209 | ld a,[hl+] ; get "enemy" sprite x position 210 | and $F8 ; strip 3 lower bits to get column where sprite is located 211 | cp e ; compare with "hero" sprite x position 212 | jr nz,.l2 ; skip if not equal 213 | inc b ; collision detected 214 | jr .l2 ; update tile 215 | .l1 216 | inc l ; move to tile byte of "enemy" sprite 217 | .l2 218 | ld a,b ; 0 - normal tile, 1 - collision tile 219 | ld [hl+],a ; update "enemy" sprite tile 220 | inc l ; move to next sprite data 221 | dec c ; check another sprite 222 | jr nz,.next 223 | ret 224 | 225 | 226 | ;------------------------------------------------------------------------------- 227 | copy: 228 | ;------------------------------------------------------------------------------- 229 | ; hl - source address 230 | ; de - destination 231 | ; bc - size 232 | 233 | inc b 234 | inc c 235 | jr .skip 236 | .copy 237 | ld a,[hl+] 238 | ld [de],a 239 | inc de 240 | .skip 241 | dec c 242 | jr nz,.copy 243 | dec b 244 | jr nz,.copy 245 | ret 246 | 247 | ;------------------------------------------------------------------------------- 248 | fill: 249 | ;------------------------------------------------------------------------------- 250 | ; a - byte to fill with 251 | ; hl - destination address 252 | ; bc - size of area to fill 253 | 254 | inc b 255 | inc c 256 | jr .skip 257 | .fill 258 | ld [hl+],a 259 | .skip 260 | dec c 261 | jr nz,.fill 262 | dec b 263 | jr nz,.fill 264 | ret 265 | 266 | 267 | ;------------------------------------------------------------------------------- 268 | dma_sub_start: 269 | ;------------------------------------------------------------------------------- 270 | ld a,$C0 ; OAM table mirror in RAM at $C000 (high byte) 271 | ld [rDMA],a 272 | ld a,40 ; delay = 160 cycles 273 | .copy 274 | dec a 275 | jr nz,.copy 276 | ret 277 | dma_sub_end: 278 | 279 | ;------------------------------------------------------------------------------- 280 | 281 | 282 | oam_stub: ; let's setup 4 sprites 283 | db 84,84,0,16 284 | db 84,84,0,0 285 | db 84,84,0,0 286 | db 84,84,0,0 287 | 288 | dir_stub: ; (y, x) -1 = move up / left, 1 = move down / right 289 | db -1, -1 290 | db -1, 1 291 | db 1, -1 292 | db 1, 1 293 | 294 | heart: 295 | db $00,$6C,$6C,$92,$3C,$82,$7C,$C2,$7C,$82,$38,$44,$10,$28,$00,$10 ; 1st heart tile 296 | db $6C,$6C,$FE,$FE,$FE,$FE,$FE,$FE,$FE,$FE,$7C,$7C,$38,$38,$10,$10 ; 2nd heart tile 297 | 298 | 299 | ;------------------------------------------------------------------------------- 300 | 301 | SECTION "Variables",HRAM[$FF8A] ; $FF80 - $FF89 is taken by dma_sub function 302 | 303 | directions: 304 | ds 4*2 ; dir_stub will be copied here 305 | frame: 306 | ds 1 307 | -------------------------------------------------------------------------------- /dmg/timer_clock.asm: -------------------------------------------------------------------------------- 1 | ; ----------------------------------------------------------------------------- 2 | ; Example: ClockBoy - timer based clock 3 | ; ----------------------------------------------------------------------------- 4 | ; Turn your GameBoy into clock by utilizing timer interrupt! 5 | ; Font comes from ZX Spectrum - https://en.wikipedia.org/wiki/ZX_Spectrum_character_set 6 | ; More examples by tmk @ https://github.com/gitendo/helloworld 7 | ; ----------------------------------------------------------------------------- 8 | 9 | INCLUDE "hardware.inc" ; system defines 10 | 11 | SECTION "VBL",ROM0[$0040] ; vblank interrupt handler 12 | jp vbl 13 | 14 | SECTION "TMR",ROM0[$0050] ; timer interrupt handler 15 | jp tmr 16 | 17 | SECTION "Start",ROM0[$100] ; start vector, followed by header data applied by rgbfix.exe 18 | nop 19 | jp start 20 | 21 | SECTION "Example",ROM0[$150] ; code starts here 22 | 23 | start: 24 | di ; disable interrupts 25 | ld sp,$E000 ; setup stack 26 | 27 | .wait_vbl ; wait for vblank to properly disable lcd 28 | ld a,[rLY] 29 | cp $90 30 | jr nz,.wait_vbl 31 | 32 | xor a 33 | ld [rIF],a ; reset important registers 34 | ld [rLCDC],a 35 | ld [rSTAT],a 36 | ld [rSCX],a 37 | ld [rSCY],a 38 | ld [rLYC],a 39 | ld [rIE],a 40 | 41 | ld hl,_RAM ; clear ram (fill with a which is 0 here) 42 | ld bc,$2000-2 ; watch out for stack ;) 43 | call fill 44 | 45 | ld hl,_HRAM ; clear hram 46 | ld c,$80 ; a = 0, b = 0 here, so let's save a byte and 4 cycles (ld c,$80 - 2/8 vs ld bc,$80 - 3/12) 47 | call fill 48 | 49 | ld hl,_VRAM ; clear vram, lcdc is disabled so you have 'easy' access 50 | ld b,$18 ; a = 0, bc should be $1800; c = 0 here, so.. 51 | call fill 52 | 53 | ld a,$20 ; ascii code for 'space' character 54 | 55 | ; no need to setup hl since _SCRN0 ($9800) and _SCRN1 ($9C00) are part of _VRAM, just continue 56 | 57 | ld b,8 ; bc should be $800 (_SCRN0/1 are 32*32 bytes); c = 0 here, so.. 58 | call fill 59 | 60 | ld a,%10010011 ; bits: 7-6 = 1st color, 5-4 = 2nd, 3-2 = 3rd and 1-0 = 4th color 61 | ; color values: 00 - light, 01 - gray, 10 - dark gray, 11 - dark 62 | ld [rBGP],a ; bg palette 63 | ld [rOBP0],a ; obj palettes (not used in this example) 64 | ld [rOBP1],a 65 | 66 | ld hl,font ; font data 67 | ld de,_VRAM+$200 ; place it here to get ascii mapping ('space' code is $20, tile size $10) 68 | ld bc,1776 ; font_8x8.chr file size 69 | call copy 70 | 71 | ld hl,text ; clock menu text 72 | ld de,_SCRN0+$80 ; center it a bit 73 | ld b,9 ; it has 9 lines 74 | call copy_text 75 | 76 | ld a,$12 ; set clock to 12:00:00:000 77 | ld [hours],a 78 | ; things are going to be more complicated here :) 79 | ; timer consists of 3 registers: 80 | ; - rTIMA (timer counter) which is automatically incremented at given intervals and when it overflows interrupt is generated 81 | ; - rTMA (timer modulo), when rTIMA overflows rTMA value is reloaded into rTIMA so it always starts counting from that value 82 | ; - rTAC (timer controller), enables / disables timer and allows to select frequency rTIMA is incremented with 83 | ; - there're 4 options: 4096 Hz, 16384 Hz, 65536 Hz and 262144 Hz but since Hz doesn't tell us much so we convert 84 | ; them to count up pulses by dividing gameboy clock with them, ie. 4194304 Hz / 4096 Hz = 1024 85 | ; 86 | ; we know that gameboy clock speed is 4194304 Hz, 1 Hz means one cycle per second, so it does 4194304 clock cycles per second 87 | ; our equation looks like this: 1024 * 256 (maximum rTMA value) * x = 4194304 88 | ; so x = 16 therfore with timer frequency of 4096 Hz our interrupt will be triggered 16 times per second 89 | ; we could also use some other rTMA value to trigger it more often or just change frequency, that really depends on scenario 90 | 91 | xor a ; proper way of setting up timer 92 | ld [rTMA],a ; we go for 256 increments after which interrupt is called 93 | ld a,TACF_4KHZ ; 1st set up frequency / count up pulses 94 | ld [rTAC],a 95 | or TACF_START ; then enable timer 96 | ld [rTAC],a 97 | 98 | ld a,IEF_VBLANK | IEF_TIMER ; use vblank and timer interrupts 99 | ld [rIE],a ; set up 100 | 101 | ld a,LCDCF_ON | LCDCF_BG8000 | LCDCF_BG9800 | LCDCF_OBJ8 | LCDCF_OBJOFF | LCDCF_WINOFF | LCDCF_BGON 102 | ; lcd setup: tiles at $8000, map at $9800, 8x8 sprites (disabled), no window, etc. 103 | ld [rLCDC],a ; enable lcd 104 | 105 | ei ; enable interrupts 106 | 107 | .loop 108 | halt ; save battery 109 | ; nop ; nop after halt is mandatory but rgbasm takes care of it :) 110 | call parse_input ; read joypad and handle pause mode 111 | jr .loop ; endless loop 112 | 113 | 114 | ;------------------------------------------------------------------------------- 115 | vbl: ; update screen 116 | ;------------------------------------------------------------------------------- 117 | 118 | push af ; make sure to preserve original values when there's other code in main loop 119 | push bc ; without it glitches are bound to happen 120 | push de 121 | push hl 122 | 123 | ld hl,time 124 | ld de,_SCRN0+$E4 ; this points exactly at map coordinate where clock digits are stored 125 | ld c,4 ; clock takes 4 bytes and 1 nibble, each byte holds 2 digits 126 | .copy ; copy from hram to vram without waiting for access since it's vblank 127 | ld a,[hl+] ; get byte 128 | ld b,a ; store it for further processing 129 | and a,$F0 ; leave upper nible, remove lower one 130 | swap a ; swap nibbles with places, lower is upper now and upper is lower 131 | or $30 ; upper nibble is 3, lower keeps its value - we have valid number in ascii notation now 132 | ld [de],a ; put it on the screen - map actually 133 | inc de ; next map entry 134 | ld a,b ; restore byte and repeat process above to lower nibble 135 | and a,$0F 136 | or $30 137 | ld [de],a 138 | inc de 139 | inc de 140 | dec c ; repeat 3 times 141 | jr nz,.copy 142 | dec de ; next map entry 143 | ld a,[hl] ; here's last nibble that contains single milliseconds, lower one is not needed 144 | and a,$F0 ; process as above 145 | swap a 146 | or $30 147 | ld [de],a 148 | 149 | pop hl ; restore original values and return 150 | pop de 151 | pop bc 152 | pop af 153 | reti 154 | 155 | ;------------------------------------------------------------------------------- 156 | tmr: ; timer (i leave it unrolled so it's hopefully easier to read / understand) 157 | ;------------------------------------------------------------------------------- 158 | 159 | push af ; make sure to preserve original values since timer will be called also when main loop code is executed 160 | push bc ; without it glitches are bound to happen 161 | push hl 162 | 163 | ld a,$25 ; interrupt is executed 16 times per second, 1000 ms / 16 = 62,5 ms - this is decimal point value so we need to _be clever_ here 164 | ld hl,milliseconds+1 ; we use 3 nibbles for integers and 1 nibble for fractional part to store milliseconds, 4 nibbles = 2 bytes in total ($0625) 165 | add a,[hl] ; let's add lower byte ($25) 166 | daa ; convert to bcd format 167 | ld [hl-],a ; store and move hl to upper byte 168 | ld b,a ; keep result in b 169 | ld a,$06 ; now the same with upper byte ($06) 170 | adc a,[hl] ; add with carry here to increase upper byte when lower overflows 171 | daa ; convert to bcd format 172 | ld [hl-],a ; store and move hl to seconds 173 | cp b ; a = b when 1000 milliseconds have passed, they both will be 0 then (0 -> 62,5 -> 125 -> 187,5 -> (...) -> 937,5 -> 0) 174 | jr nz,.done ; no need to update seconds yet 175 | 176 | ld a,[hl] ; load seconds 177 | inc a ; increase by 1 178 | daa ; convert to bcd format 179 | ld [hl],a ; and store 180 | cp $60 ; 60 seconds passed? 181 | jr nz,.done ; not yet 182 | xor a ; reset seconds 183 | ld [hl-],a ; update and move hl to minutes 184 | 185 | ld a,[hl] ; same as above but minutes this time, a lot of duplicated code here that could be fit into smaller procedure 186 | inc a 187 | daa 188 | ld [hl],a 189 | cp $60 190 | jr nz,.done 191 | xor a 192 | ld [hl-],a 193 | 194 | ld a,[hl] ; and again, hours this time 195 | inc a 196 | daa 197 | ld [hl],a 198 | cp $24 ; 24h ftw! 199 | jr nz,.done 200 | xor a 201 | ld [hl],a 202 | 203 | .done 204 | pop hl ; restore original values and return 205 | pop bc 206 | pop af 207 | reti 208 | 209 | 210 | ;------------------------------------------------------------------------------- 211 | parse_input: 212 | ;------------------------------------------------------------------------------- 213 | 214 | call read_keys ; read joypad 215 | 216 | .start 217 | bit 3,c ; key code is in c, see if start was pressed 218 | jr z,.done ; not pressed 219 | 220 | ld a,[rIE] ; get interrupt setup 221 | xor IEF_TIMER ; turn timer on / off 222 | ld [rIE],a ; update interrupt setup 223 | 224 | .done 225 | ret 226 | 227 | 228 | ;------------------------------------------------------------------------------- 229 | copy: 230 | ;------------------------------------------------------------------------------- 231 | ; hl - source address 232 | ; de - destination 233 | ; bc - size 234 | 235 | inc b 236 | inc c 237 | jr .skip 238 | .copy 239 | ld a,[hl+] 240 | ld [de],a 241 | inc de 242 | .skip 243 | dec c 244 | jr nz,.copy 245 | dec b 246 | jr nz,.copy 247 | ret 248 | 249 | 250 | ;------------------------------------------------------------------------------- 251 | copy_text: 252 | ;------------------------------------------------------------------------------- 253 | ; hl - text to display 254 | ; de - _SCRN0 or _SCRN1 255 | ; b - rows 256 | ; c - columns 257 | 258 | .next_row 259 | ld c,20 260 | .row 261 | ld a,[hl+] ; fetch one byte from text array and increase hl to point to another one 262 | ld [de],a ; store it at _SCRN0 263 | inc de ; unfortunately there's no [de+] 264 | dec c ; one byte done 265 | jr nz,.row ; next byte, copy untill c=0 266 | 267 | ld a,e ; our row = 20 which is what you can see on the screen 268 | add a,12 ; the part you don't see = 12, so we need to add it 269 | jr nc,.skip ; to make sure the next row is copied at right offset 270 | inc d ; nc flag is set when a+12 > 255 271 | .skip 272 | ld e,a 273 | 274 | dec b ; next row, copy untill b=0 275 | jr nz,.next_row 276 | ret 277 | 278 | 279 | ;------------------------------------------------------------------------------- 280 | fill: 281 | ;------------------------------------------------------------------------------- 282 | ; a - byte to fill with 283 | ; hl - destination address 284 | ; bc - size of area to fill 285 | 286 | inc b 287 | inc c 288 | jr .skip 289 | .fill 290 | ld [hl+],a 291 | .skip 292 | dec c 293 | jr nz,.fill 294 | dec b 295 | jr nz,.fill 296 | ret 297 | 298 | 299 | ;------------------------------------------------------------------------------- 300 | read_keys: 301 | ;------------------------------------------------------------------------------- 302 | ; this function returns two different values in b and c registers: 303 | ; b - returns raw state (pressing key triggers given action continuously as long as it's pressed - it does not prevent bouncing) 304 | ; c - returns debounced state (pressing key triggers given action only once - key must be released and pressed again) 305 | 306 | ld a,$20 ; read P15 - returns a, b, select, start 307 | ldh [rP1],a 308 | ldh a,[rP1] ; mandatory 309 | ldh a,[rP1] 310 | cpl ; rP1 returns not pressed keys as 1 and pressed as 0, invert it to make result more readable 311 | and $0f ; lower nibble has a, b, select, start state 312 | swap a 313 | ld b,a 314 | 315 | ld a,$10 ; read P14 - returns up, down, left, right 316 | ldh [rP1],a 317 | ldh a,[rP1] ; mandatory 318 | ldh a,[rP1] 319 | ldh a,[rP1] 320 | ldh a,[rP1] 321 | ldh a,[rP1] 322 | ldh a,[rP1] 323 | cpl ; rP1 returns not pressed keys as 1 and pressed as 0, invert it to make result more readable 324 | and $0f ; lower nibble has up, down, left, right state 325 | or b ; combine P15 and P14 states in one byte 326 | ld b,a ; store it 327 | 328 | ldh a,[previous] ; this is when important part begins, load previous P15 & P14 state 329 | xor b ; result will be 0 if it's the same as current read 330 | and b ; keep buttons that were pressed during this read only 331 | ldh [current],a ; store final result in variable and register 332 | ld c,a 333 | ld a,b ; current P15 & P14 state will be previous in next read 334 | ldh [previous],a 335 | 336 | ld a,$30 ; reset rP1 337 | ldh [rP1],a 338 | 339 | ret 340 | 341 | ;------------------------------------------------------------------------------- 342 | 343 | font: 344 | INCBIN "font_8x8.chr" ; converted with https://github.com/gitendo/bmp2cgb 345 | 346 | text: 347 | DB " ClockBoy " 348 | DB " " 349 | DB " " 350 | DB " 00:00:00:000 " 351 | DB " " 352 | DB " " 353 | DB " Press Start to " 354 | DB " pause the timer. " 355 | DB " " 356 | 357 | ;------------------------------------------------------------------------------- 358 | 359 | SECTION "Variables",HRAM 360 | 361 | time: 362 | hours: DS 1 ; time in bcd format 363 | minutes: DS 1 364 | seconds: DS 1 365 | milliseconds: DS 2 366 | current: DS 1 ; usually you read keys state and store it into variable for further processing 367 | previous: DS 1 ; this is previous keys state used by debouncing part of read_keys function 368 | -------------------------------------------------------------------------------- /dmg/window.asm: -------------------------------------------------------------------------------- 1 | ; ----------------------------------------------------------------------------- 2 | ; Example: Window 3 | ; ----------------------------------------------------------------------------- 4 | ; [gameboy demake] pick up that can! pixeled by b236 @ http://pixeljoint.com/pixelart/129407.htm 5 | ; Press select to enable/disable window and use d-pad to change its coordinates. 6 | ; More examples by tmk @ https://github.com/gitendo/helloworld 7 | ; ----------------------------------------------------------------------------- 8 | 9 | INCLUDE "hardware.inc" ; system defines 10 | 11 | SECTION "VBL",ROM0[$0040] ; vblank interrupt handler 12 | jp vbl 13 | 14 | SECTION "Start",ROM0[$100] ; start vector, followed by header data applied by rgbfix.exe 15 | nop 16 | jp start 17 | 18 | SECTION "Example",ROM0[$150] ; code starts here 19 | 20 | start: 21 | di ; disable interrupts 22 | ld sp,$E000 ; setup stack 23 | 24 | .wait_vbl ; wait for vblank to properly disable lcd 25 | ld a,[rLY] 26 | cp $90 27 | jr nz,.wait_vbl 28 | 29 | xor a ; reset important registers 30 | ld [rIF],a 31 | ld [rLCDC],a 32 | ld [rSTAT],a 33 | ld [rSCX],a 34 | ld [rSCY],a 35 | ld [rWX],a 36 | ld [rWY],a 37 | ld [rIE],a 38 | 39 | ld hl,_RAM ; clear ram (fill with a which is 0 here) 40 | ld bc,$2000-2 ; watch out for stack ;) 41 | call fill 42 | 43 | ld hl,_HRAM ; clear hram 44 | ld c,$80 ; a = 0, b = 0 here, so let's save a byte and 4 cycles (ld c,$80 - 2/8 vs ld bc,$80 - 3/12) 45 | call fill 46 | ; no point in clearing vram, we'll overwrite it with picture data anyway 47 | ; lcdc is already disabled so we have 'easy' access to vram 48 | 49 | ld hl,tiles ; picture tiles 50 | ld de,_VRAM ; place it between $8000-8FFF (tiles are numbered here from 0 to 255) 51 | ld bc,3040 ; hlgbmcp.chr file size 52 | call copy 53 | 54 | ld hl,bg_map ; main picture map (padded to 32 columns, so we can easily copy it) 55 | ld de,_SCRN0 ; store it at $9800 56 | ld bc,576 ; hlgbmcp_bg.map file size 57 | call copy 58 | 59 | ld hl,window_map ; dialog window map (also padded) 60 | ld de,_SCRN1 ; store it at $9C00 61 | ld bc,576 ; hlgbmcp_win.map file size 62 | call copy 63 | 64 | ld a,%00011011 ; bits: 7-6 = 1st color, 5-4 = 2nd, 3-2 = 3rd and 1-0 = 4th color 65 | ; color values: 00 - light, 01 - gray, 10 - dark gray, 11 - dark 66 | ld [rBGP],a ; bg palette 67 | ld [rOBP0],a ; obj palettes (not used in this example) 68 | ld [rOBP1],a 69 | 70 | ld a,7 ; window x coordinate 71 | ldh [x],a ; 72 | ld a,112 ; window y coordinate 73 | ldh [y],a ; 74 | 75 | ld a,IEF_VBLANK ; vblank interrupt 76 | ld [rIE],a ; setup 77 | 78 | ld a,LCDCF_ON | LCDCF_BG8000 | LCDCF_BG9800 | LCDCF_WIN9C00 | LCDCF_OBJ8 | LCDCF_OBJOFF | LCDCF_WINOFF | LCDCF_BGON 79 | ; lcd setup: tiles at $8000, map at $9800, 8x8 sprites (disabled), window (disabled), etc. 80 | ld [rLCDC],a ; enable lcd 81 | 82 | ei ; enable interrupts 83 | 84 | .the_end 85 | halt ; save battery 86 | ; nop ; nop after halt is mandatory but rgbasm takes care of it :) 87 | call read_keys ; read joypad 88 | jr .the_end ; endless loop 89 | 90 | 91 | vbl: ; vblank interrupt - executed every frame when LY=144 92 | 93 | ; please notice that b still contains [previous] key state 94 | ; and c holds [current] one which is debounce free, 95 | ; there're no other functions in main loop so we don't have to reload them 96 | .select 97 | bit 2,c ; check if select was pressed 98 | jr z,.right 99 | ldh a,[rLCDC] ; contains lcd setup 100 | xor LCDCF_WINON ; if window is enabled it will be disabled and otherwise 101 | ldh [rLCDC],a ; update lcd with window status 102 | .right 103 | bit 4,b 104 | jr z,.left 105 | ld hl,x ; increase window x coordinate 106 | inc [hl] 107 | .left 108 | bit 5,b 109 | jr z,.up 110 | ld hl,x ; decrease window x coordinate 111 | dec [hl] 112 | .up 113 | bit 6,b 114 | jr z,.down 115 | ld hl,y ; decrease window y coordinate 116 | dec [hl] 117 | .down 118 | bit 7,b 119 | jr z,.quit 120 | ld hl,y ; increase window y coordinate 121 | inc [hl] 122 | .quit 123 | ldh a,[x] ; rWX: 0 - 166 is valid 124 | ld [rWX],a 125 | ldh a,[y] ; rWY: 0 - 143 is valid 126 | ld [rWY],a 127 | ; rWX=7, rWY=0 locates the window at top left corner, completly covering background 128 | reti ; return from interrupt 129 | 130 | 131 | ;------------------------------------------------------------------------------- 132 | copy: 133 | ;------------------------------------------------------------------------------- 134 | ; hl - source address 135 | ; de - destination 136 | ; bc - size 137 | 138 | inc b 139 | inc c 140 | jr .skip 141 | .copy 142 | ld a,[hl+] 143 | ld [de],a 144 | inc de 145 | .skip 146 | dec c 147 | jr nz,.copy 148 | dec b 149 | jr nz,.copy 150 | ret 151 | 152 | ;------------------------------------------------------------------------------- 153 | fill: 154 | ;------------------------------------------------------------------------------- 155 | ; a - byte to fill with 156 | ; hl - destination address 157 | ; bc - size of area to fill 158 | 159 | inc b 160 | inc c 161 | jr .skip 162 | .fill 163 | ld [hl+],a 164 | .skip 165 | dec c 166 | jr nz,.fill 167 | dec b 168 | jr nz,.fill 169 | ret 170 | 171 | ;------------------------------------------------------------------------------- 172 | read_keys: 173 | ;------------------------------------------------------------------------------- 174 | ; this function returns two different values in b and c registers: 175 | ; b - returns raw state (pressing key triggers given action continuously as long as it's pressed - it does not prevent bouncing) 176 | ; c - returns debounced state (pressing key triggers given action only once - key must be released and pressed again) 177 | 178 | ld a,$20 ; read P15 - returns a, b, select, start 179 | ldh [rP1],a 180 | ldh a,[rP1] ; mandatory 181 | ldh a,[rP1] 182 | cpl ; rP1 returns not pressed keys as 1 and pressed as 0, invert it to make result more readable 183 | and $0f ; lower nibble has a, b, select, start state 184 | swap a 185 | ld b,a 186 | 187 | ld a,$10 ; read P14 - returns up, down, left, right 188 | ldh [rP1],a 189 | ldh a,[rP1] ; mandatory 190 | ldh a,[rP1] 191 | ldh a,[rP1] 192 | ldh a,[rP1] 193 | ldh a,[rP1] 194 | ldh a,[rP1] 195 | cpl ; rP1 returns not pressed keys as 1 and pressed as 0, invert it to make result more readable 196 | and $0f ; lower nibble has up, down, left, right state 197 | or b ; combine P15 and P14 states in one byte 198 | ld b,a ; store it 199 | 200 | ldh a,[previous] ; this is when important part begins, load previous P15 & P14 state 201 | xor b ; result will be 0 if it's the same as current read 202 | and b ; keep buttons that were pressed during this read only 203 | ldh [current],a ; store final result in variable and register 204 | ld c,a 205 | ld a,b ; current P15 & P14 state will be previous in next read 206 | ldh [previous],a 207 | 208 | ld a,$30 ; reset rP1 209 | ldh [rP1],a 210 | 211 | ret 212 | 213 | 214 | ;------------------------------------------------------------------------------- 215 | 216 | tiles: ; bmp2cgb -x -y -z -e26 hlgbmcp.bmp 217 | INCBIN "hlgbmcp.chr" 218 | bg_map: 219 | INCBIN "hlgbmcp_bg.map" 220 | window_map: 221 | INCBIN "hlgbmcp_win.map" 222 | 223 | ;------------------------------------------------------------------------------- 224 | 225 | SECTION "Variables",HRAM 226 | 227 | current: DS 1 ; usually you read keys state and store it into variable for further processing 228 | previous: DS 1 ; this is previous keys state used by debouncing part of read_keys function 229 | x DS 1 230 | y DS 1 -------------------------------------------------------------------------------- /inc/hardware.inc: -------------------------------------------------------------------------------- 1 | ;* 2 | ;* Gameboy Hardware definitions 3 | ;* 4 | ;* Based on Jones' hardware.inc 5 | ;* And based on Carsten Sorensen's ideas. 6 | ;* 7 | ;* Rev 1.1 - 15-Jul-97 : Added define check 8 | ;* Rev 1.2 - 18-Jul-97 : Added revision check macro 9 | ;* Rev 1.3 - 19-Jul-97 : Modified for RGBASM V1.05 10 | ;* Rev 1.4 - 27-Jul-97 : Modified for new subroutine prefixes 11 | ;* Rev 1.5 - 15-Aug-97 : Added _HRAM, PAD, CART defines 12 | ;* : and Nintendo Logo 13 | ;* Rev 1.6 - 30-Nov-97 : Added rDIV, rTIMA, rTMA, & rTAC 14 | ;* Rev 1.7 - 31-Jan-98 : Added _SCRN0, _SCRN1 15 | ;* Rev 1.8 - 15-Feb-98 : Added rSB, rSC 16 | ;* Rev 1.9 - 16-Feb-98 : Converted I/O registers to $FFXX format 17 | ;* Rev 2.0 - : Added GBC registers 18 | ;* Rev 2.1 - : Added MBC5 & cart RAM enable/disable defines 19 | ;* Rev 2.2 : Fixed NR42,NR43, & NR44 equates 20 | ;* Rev 2.3 - : Fixed incorrect _HRAM equate 21 | ;* Rev 2.4 - : Completed CART defines 22 | ;* Rev 2.5 - : Fixed bug in CART defines 23 | 24 | ; If all of these are already defined, don't do it again. 25 | 26 | IF !DEF(HARDWARE_INC) 27 | HARDWARE_INC SET 1 28 | 29 | rev_Check_hardware_inc: MACRO 30 | ;NOTE: REVISION NUMBER CHANGES MUST BE ADDED 31 | ;TO SECOND PARAMETER IN FOLLOWING LINE. 32 | IF \1 > 2.2 ;PUT REVISION NUMBER HERE 33 | WARN "Version \1 or later of 'hardware.inc' is required." 34 | ENDC 35 | ENDM 36 | 37 | _HW EQU $FF00 38 | 39 | _VRAM EQU $8000 ; $8000->$A000 40 | _SCRN0 EQU $9800 ; $9800->$9BFF 41 | _SCRN1 EQU $9C00 ; $9C00->$9FFF 42 | _RAM EQU $C000 ; $C000->$E000 43 | _HRAM EQU $FF80 ; $FF80->$FFFE 44 | _OAMRAM EQU $FE00 ; $FE00->$FE9F 45 | _AUD3WAVERAM EQU $FF30 ; $FF30->$FF3F 46 | 47 | ; *** MBC5 Equates *** 48 | 49 | rRAMG EQU $0000 ; $0000->$1fff 50 | rROMB0 EQU $2000 ; $2000->$2fff 51 | rROMB1 EQU $3000 ; $3000->$3fff 52 | rRAMB EQU $4000 ; $4000->$5fff 53 | 54 | 55 | ; -- 56 | ; -- OAM flags 57 | ; -- 58 | 59 | OAMF_PRI EQU %10000000 ; Priority 60 | OAMF_YFLIP EQU %01000000 ; Y flip 61 | OAMF_XFLIP EQU %00100000 ; X flip 62 | OAMF_PAL0 EQU %00000000 ; Palette number; 0,1 63 | OAMF_PAL1 EQU %00010000 ; Palette number; 0,1 64 | 65 | 66 | ;*************************************************************************** 67 | ;* 68 | ;* Custom registers 69 | ;* 70 | ;*************************************************************************** 71 | 72 | ; -- 73 | ; -- P1 ($FF00) 74 | ; -- Register for reading joy pad info. (R/W) 75 | ; -- 76 | rP1 EQU $FF00 77 | 78 | P1F_5 EQU %00100000 ; P15 out port 79 | P1F_4 EQU %00010000 ; P14 out port 80 | P1F_3 EQU %00001000 ; P13 in port 81 | P1F_2 EQU %00000100 ; P12 in port 82 | P1F_1 EQU %00000010 ; P11 in port 83 | P1F_0 EQU %00000001 ; P10 in port 84 | 85 | ; -- 86 | ; -- SB ($FF01) 87 | ; -- Serial Transfer Data (R/W) 88 | ; -- 89 | rSB EQU $FF01 90 | 91 | ; -- 92 | ; -- SC ($FF02) 93 | ; -- Serial I/O Control (R/W) 94 | ; -- 95 | rSC EQU $FF02 96 | 97 | ; -- 98 | ; -- DIV ($FF04) 99 | ; -- Divider register (R/W) 100 | ; -- 101 | rDIV EQU $FF04 102 | 103 | 104 | ; -- 105 | ; -- TIMA ($FF05) 106 | ; -- Timer counter (R/W) 107 | ; -- 108 | rTIMA EQU $FF05 109 | 110 | 111 | ; -- 112 | ; -- TMA ($FF06) 113 | ; -- Timer modulo (R/W) 114 | ; -- 115 | rTMA EQU $FF06 116 | 117 | 118 | ; -- 119 | ; -- TAC ($FF07) 120 | ; -- Timer control (R/W) 121 | ; -- 122 | rTAC EQU $FF07 123 | 124 | TACF_START EQU %00000100 125 | TACF_STOP EQU %00000000 126 | TACF_4KHZ EQU %00000000 127 | TACF_16KHZ EQU %00000011 128 | TACF_65KHZ EQU %00000010 129 | TACF_262KHZ EQU %00000001 130 | 131 | ; -- 132 | ; -- IF ($FF0F) 133 | ; -- Interrupt Flag (R/W) 134 | ; -- 135 | rIF EQU $FF0F 136 | 137 | ; -- 138 | ; -- LCDC ($FF40) 139 | ; -- LCD Control (R/W) 140 | ; -- 141 | rLCDC EQU $FF40 142 | 143 | LCDCF_OFF EQU %00000000 ; LCD Control Operation 144 | LCDCF_ON EQU %10000000 ; LCD Control Operation 145 | LCDCF_WIN9800 EQU %00000000 ; Window Tile Map Display Select 146 | LCDCF_WIN9C00 EQU %01000000 ; Window Tile Map Display Select 147 | LCDCF_WINOFF EQU %00000000 ; Window Display 148 | LCDCF_WINON EQU %00100000 ; Window Display 149 | LCDCF_BG8800 EQU %00000000 ; BG & Window Tile Data Select 150 | LCDCF_BG8000 EQU %00010000 ; BG & Window Tile Data Select 151 | LCDCF_BG9800 EQU %00000000 ; BG Tile Map Display Select 152 | LCDCF_BG9C00 EQU %00001000 ; BG Tile Map Display Select 153 | LCDCF_OBJ8 EQU %00000000 ; OBJ Construction 154 | LCDCF_OBJ16 EQU %00000100 ; OBJ Construction 155 | LCDCF_OBJOFF EQU %00000000 ; OBJ Display 156 | LCDCF_OBJON EQU %00000010 ; OBJ Display 157 | LCDCF_BGOFF EQU %00000000 ; BG Display 158 | LCDCF_BGON EQU %00000001 ; BG Display 159 | ; "Window Character Data Select" follows BG 160 | 161 | 162 | ; -- 163 | ; -- STAT ($FF41) 164 | ; -- LCDC Status (R/W) 165 | ; -- 166 | rSTAT EQU $FF41 167 | 168 | STATF_LYC EQU %01000000 ; LYCEQULY Coincidence (Selectable) 169 | STATF_MODE10 EQU %00100000 ; Mode 10 170 | STATF_MODE01 EQU %00010000 ; Mode 01 (V-Blank) 171 | STATF_MODE00 EQU %00001000 ; Mode 00 (H-Blank) 172 | STATF_LYCF EQU %00000100 ; Coincidence Flag 173 | STATF_HB EQU %00000000 ; H-Blank 174 | STATF_VB EQU %00000001 ; V-Blank 175 | STATF_OAM EQU %00000010 ; OAM-RAM is used by system 176 | STATF_LCD EQU %00000011 ; Both OAM and VRAM used by system 177 | STATF_BUSY EQU %00000010 ; When set, VRAM access is unsafe 178 | 179 | 180 | ; -- 181 | ; -- SCY ($FF42) 182 | ; -- Scroll Y (R/W) 183 | ; -- 184 | rSCY EQU $FF42 185 | 186 | 187 | ; -- 188 | ; -- SCY ($FF43) 189 | ; -- Scroll X (R/W) 190 | ; -- 191 | rSCX EQU $FF43 192 | 193 | 194 | ; -- 195 | ; -- LY ($FF44) 196 | ; -- LCDC Y-Coordinate (R) 197 | ; -- 198 | ; -- Values range from 0->153. 144->153 is the VBlank period. 199 | ; -- 200 | rLY EQU $FF44 201 | 202 | 203 | ; -- 204 | ; -- LYC ($FF45) 205 | ; -- LY Compare (R/W) 206 | ; -- 207 | ; -- When LYEQUEQULYC, STATF_LYCF will be set in STAT 208 | ; -- 209 | rLYC EQU $FF45 210 | 211 | 212 | ; -- 213 | ; -- DMA ($FF46) 214 | ; -- DMA Transfer and Start Address (W) 215 | ; -- 216 | rDMA EQU $FF46 217 | 218 | 219 | ; -- 220 | ; -- BGP ($FF47) 221 | ; -- BG Palette Data (W) 222 | ; -- 223 | ; -- Bit 7-6 - Intensity for %11 224 | ; -- Bit 5-4 - Intensity for %10 225 | ; -- Bit 3-2 - Intensity for %01 226 | ; -- Bit 1-0 - Intensity for %00 227 | ; -- 228 | rBGP EQU $FF47 229 | 230 | 231 | ; -- 232 | ; -- OBP0 ($FF48) 233 | ; -- Object Palette 0 Data (W) 234 | ; -- 235 | ; -- See BGP for info 236 | ; -- 237 | rOBP0 EQU $FF48 238 | 239 | 240 | ; -- 241 | ; -- OBP1 ($FF49) 242 | ; -- Object Palette 1 Data (W) 243 | ; -- 244 | ; -- See BGP for info 245 | ; -- 246 | rOBP1 EQU $FF49 247 | 248 | 249 | ; -- 250 | ; -- WY ($FF4A) 251 | ; -- Window Y Position (R/W) 252 | ; -- 253 | ; -- 0 SO2 ON/OFF (Vin??) 385 | ; -- Bit 6-4 - SO2 output level (volume) (# 0-7) 386 | ; -- Bit 3 - Vin->SO1 ON/OFF (Vin??) 387 | ; -- Bit 2-0 - SO1 output level (volume) (# 0-7) 388 | ; -- 389 | rNR50 EQU $FF24 390 | rAUDVOL EQU rNR50 391 | 392 | 393 | ; -- 394 | ; -- AUDTERM/NR51 ($FF25) 395 | ; -- Selection of Sound output terminal (R/W) 396 | ; -- 397 | ; -- Bit 7 - Output sound 4 to SO2 terminal 398 | ; -- Bit 6 - Output sound 3 to SO2 terminal 399 | ; -- Bit 5 - Output sound 2 to SO2 terminal 400 | ; -- Bit 4 - Output sound 1 to SO2 terminal 401 | ; -- Bit 3 - Output sound 4 to SO1 terminal 402 | ; -- Bit 2 - Output sound 3 to SO1 terminal 403 | ; -- Bit 1 - Output sound 2 to SO1 terminal 404 | ; -- Bit 0 - Output sound 0 to SO1 terminal 405 | ; -- 406 | rNR51 EQU $FF25 407 | rAUDTERM EQU rNR51 408 | 409 | 410 | ; -- 411 | ; -- AUDENA/NR52 ($FF26) 412 | ; -- Sound on/off (R/W) 413 | ; -- 414 | ; -- Bit 7 - All sound on/off (sets all audio regs to 0!) 415 | ; -- Bit 3 - Sound 4 ON flag (doesn't work!) 416 | ; -- Bit 2 - Sound 3 ON flag (doesn't work!) 417 | ; -- Bit 1 - Sound 2 ON flag (doesn't work!) 418 | ; -- Bit 0 - Sound 1 ON flag (doesn't work!) 419 | ; -- 420 | rNR52 EQU $FF26 421 | rAUDENA EQU rNR52 422 | 423 | 424 | ;*************************************************************************** 425 | ;* 426 | ;* SoundChannel #1 registers 427 | ;* 428 | ;*************************************************************************** 429 | 430 | ; -- 431 | ; -- AUD1SWEEP/NR10 ($FF10) 432 | ; -- Sweep register (R/W) 433 | ; -- 434 | ; -- Bit 6-4 - Sweep Time 435 | ; -- Bit 3 - Sweep Increase/Decrease 436 | ; -- 0: Addition (frequency increases???) 437 | ; -- 1: Subtraction (frequency increases???) 438 | ; -- Bit 2-0 - Number of sweep shift (# 0-7) 439 | ; -- Sweep Time: (n*7.8ms) 440 | ; -- 441 | rNR10 EQU $FF10 442 | rAUD1SWEEP EQU rNR10 443 | 444 | 445 | ; -- 446 | ; -- AUD1LEN/NR11 ($FF11) 447 | ; -- Sound length/Wave pattern duty (R/W) 448 | ; -- 449 | ; -- Bit 7-6 - Wave Pattern Duty (00:12.5% 01:25% 10:50% 11:75%) 450 | ; -- Bit 5-0 - Sound length data (# 0-63) 451 | ; -- 452 | rNR11 EQU $FF11 453 | rAUD1LEN EQU rNR11 454 | 455 | 456 | ; -- 457 | ; -- AUD1ENV/NR12 ($FF12) 458 | ; -- Envelope (R/W) 459 | ; -- 460 | ; -- Bit 7-4 - Initial value of envelope 461 | ; -- Bit 3 - Envelope UP/DOWN 462 | ; -- 0: Decrease 463 | ; -- 1: Range of increase 464 | ; -- Bit 2-0 - Number of envelope sweep (# 0-7) 465 | ; -- 466 | rNR12 EQU $FF12 467 | rAUD1ENV EQU rNR12 468 | 469 | 470 | ; -- 471 | ; -- AUD1LOW/NR13 ($FF13) 472 | ; -- Frequency lo (W) 473 | ; -- 474 | rNR13 EQU $FF13 475 | rAUD1LOW EQU rNR13 476 | 477 | 478 | ; -- 479 | ; -- AUD1HIGH/NR14 ($FF14) 480 | ; -- Frequency hi (W) 481 | ; -- 482 | ; -- Bit 7 - Initial (when set, sound restarts) 483 | ; -- Bit 6 - Counter/consecutive selection 484 | ; -- Bit 2-0 - Frequency's higher 3 bits 485 | ; -- 486 | rNR14 EQU $FF14 487 | rAUD1HIGH EQU rNR14 488 | 489 | 490 | ;*************************************************************************** 491 | ;* 492 | ;* SoundChannel #2 registers 493 | ;* 494 | ;*************************************************************************** 495 | 496 | ; -- 497 | ; -- AUD2LEN/NR21 ($FF16) 498 | ; -- Sound Length; Wave Pattern Duty (R/W) 499 | ; -- 500 | ; -- see AUD1LEN for info 501 | ; -- 502 | rNR21 EQU $FF16 503 | rAUD2LEN EQU rNR21 504 | 505 | 506 | ; -- 507 | ; -- AUD2ENV/NR22 ($FF17) 508 | ; -- Envelope (R/W) 509 | ; -- 510 | ; -- see AUD1ENV for info 511 | ; -- 512 | rNR22 EQU $FF17 513 | rAUD2ENV EQU rNR22 514 | 515 | 516 | ; -- 517 | ; -- AUD2LOW/NR23 ($FF18) 518 | ; -- Frequency lo (W) 519 | ; -- 520 | rNR23 EQU $FF18 521 | rAUD2LOW EQU rNR23 522 | 523 | 524 | ; -- 525 | ; -- AUD2HIGH/NR24 ($FF19) 526 | ; -- Frequency hi (W) 527 | ; -- 528 | ; -- see AUD1HIGH for info 529 | ; -- 530 | rNR24 EQU $FF19 531 | rAUD2HIGH EQU rNR24 532 | 533 | 534 | ;*************************************************************************** 535 | ;* 536 | ;* SoundChannel #3 registers 537 | ;* 538 | ;*************************************************************************** 539 | 540 | ; -- 541 | ; -- AUD3ENA/NR30 ($FF1A) 542 | ; -- Sound on/off (R/W) 543 | ; -- 544 | ; -- Bit 7 - Sound ON/OFF (1EQUON,0EQUOFF) 545 | ; -- 546 | rNR30 EQU $FF1A 547 | rAUD3ENA EQU rNR30 548 | 549 | 550 | ; -- 551 | ; -- AUD3LEN/NR31 ($FF1B) 552 | ; -- Sound length (R/W) 553 | ; -- 554 | ; -- Bit 7-0 - Sound length 555 | ; -- 556 | rNR31 EQU $FF1B 557 | rAUD3LEN EQU rNR31 558 | 559 | 560 | ; -- 561 | ; -- AUD3LEVEL/NR32 ($FF1C) 562 | ; -- Select output level 563 | ; -- 564 | ; -- Bit 6-5 - Select output level 565 | ; -- 00: 0/1 (mute) 566 | ; -- 01: 1/1 567 | ; -- 10: 1/2 568 | ; -- 11: 1/4 569 | ; -- 570 | rNR32 EQU $FF1C 571 | rAUD3LEVEL EQU rNR32 572 | 573 | 574 | ; -- 575 | ; -- AUD3LOW/NR33 ($FF1D) 576 | ; -- Frequency lo (W) 577 | ; -- 578 | ; -- see AUD1LOW for info 579 | ; -- 580 | rNR33 EQU $FF1D 581 | rAUD3LOW EQU rNR33 582 | 583 | 584 | ; -- 585 | ; -- AUD3HIGH/NR34 ($FF1E) 586 | ; -- Frequency hi (W) 587 | ; -- 588 | ; -- see AUD1HIGH for info 589 | ; -- 590 | rNR34 EQU $FF1E 591 | rAUD3HIGH EQU rNR34 592 | 593 | 594 | ; -- 595 | ; -- AUD4LEN/NR41 ($FF20) 596 | ; -- Sound length (R/W) 597 | ; -- 598 | ; -- Bit 5-0 - Sound length data (# 0-63) 599 | ; -- 600 | rNR41 EQU $FF20 601 | rAUD4LEN EQU rNR41 602 | 603 | 604 | ; -- 605 | ; -- AUD4ENV/NR42 ($FF21) 606 | ; -- Envelope (R/W) 607 | ; -- 608 | ; -- see AUD1ENV for info 609 | ; -- 610 | rNR42 EQU $FF21 611 | rAUD4ENV EQU rNR42 612 | 613 | 614 | ; -- 615 | ; -- AUD4POLY/NR43 ($FF22) 616 | ; -- Polynomial counter (R/W) 617 | ; -- 618 | ; -- Bit 7-4 - Selection of the shift clock frequency of the (scf) 619 | ; -- polynomial counter (0000-1101) 620 | ; -- freqEQUdrf*1/2^scf (not sure) 621 | ; -- Bit 3 - Selection of the polynomial counter's step 622 | ; -- 0: 15 steps 623 | ; -- 1: 7 steps 624 | ; -- Bit 2-0 - Selection of the dividing ratio of frequencies (drf) 625 | ; -- 000: f/4 001: f/8 010: f/16 011: f/24 626 | ; -- 100: f/32 101: f/40 110: f/48 111: f/56 (fEQU4.194304 Mhz) 627 | ; -- 628 | rNR43 EQU $FF22 629 | rAUD4POLY EQU rNR43 630 | 631 | 632 | ; -- 633 | ; -- AUD4GO/NR44 ($FF23) 634 | ; -- (has wrong name and value (ff30) in Dr.Pan's doc!) 635 | ; -- 636 | ; -- Bit 7 - Inital 637 | ; -- Bit 6 - Counter/consecutive selection 638 | ; -- 639 | rNR44 EQU $FF23 640 | rAUD4GO EQU rNR44 ; silly name! 641 | 642 | ;*************************************************************************** 643 | ;* 644 | ;* Cart related 645 | ;* 646 | ;*************************************************************************** 647 | 648 | CART_ROM EQU 0 ; ROM 649 | CART_ROM_MBC1 EQU 1 ; ROM+MBC1 650 | CART_ROM_MBC1_RAM EQU 2 ; ROM+MBC1+RAM 651 | CART_ROM_MBC1_RAM_BAT EQU 3 ; ROM+MBC1+RAM+BATTERY 652 | CART_ROM_MBC2 EQU 5 ; ROM+MBC2 653 | CART_ROM_MBC2_BAT EQU 6 ; ROM+MBC2+BATTERY 654 | CART_ROM_RAM EQU 8 ; ROM+RAM 655 | CART_ROM_RAM_BAT EQU 9 ; ROM+RAM+BATTERY 656 | CART_ROM_MMM EQU $b ; ROM+MMM01 657 | CART_ROM_RAM_MMM EQU $c ; ROM+RAM+MMM01 658 | CART_ROM_RAM_MMM_BAT EQU $d ; ROM+RAM+MMM01+BATTERY 659 | CART_ROM_MBC3_TIM_BAT EQU $f ; ROM+MBC3+TIMER+BATTERY 660 | CART_ROM_MBC3_TIM_RAM_BAT EQU $10 ; ROM+MBC3+TIMER+RAM+BATTERY 661 | CART_ROM_MBC3 EQU $11 ; ROM+MBC3 662 | CART_ROM_MBC3_RAM EQU $12 ; ROM+MBC3+RAM 663 | CART_ROM_MBC3_RAM_BAT EQU $13 ; ROM+MBC3+RAM+BATTERY 664 | CART_ROM_MBC4 EQU $15 ; ROM+MBC4 665 | CART_ROM_MBC4_RAM EQU $16 ; ROM+MBC4+RAM 666 | CART_ROM_MBC4_RAM_BAT EQU $17 ; ROM+MBC4+RAM+BATTERY 667 | CART_ROM_MBC5 EQU $19 ; ROM+MBC5 668 | CART_ROM_MBC5_RAM EQU $1a ; ROM+MBC5+RAM 669 | CART_ROM_MBC5_RAM_BAT EQU $1b ; ROM+MBC5+RAM+BATTERY 670 | CART_ROM_MBC5_RUM EQU $1c ; ROM+MBC5+RUMBLE 671 | CART_ROM_MBC5_RUM_RAM EQU $1d ; ROM+MBC5+RUMBLE+RAM 672 | CART_ROM_MBC5_RUM_RAM_BAT EQU $1e ; ROM+MBC5+RUMBLE+RAM+BATTERY 673 | CART_CAMERA EQU $fc ; Camera ROM 674 | CART_TAMA5 EQU $fd ; Bandai Tama 5 675 | CART_HuC3 EQU $fe ; HuC3 676 | CART_HuC1_RAM_BAT EQU $ff ; HuC1+RAM+BATTERY 677 | 678 | CART_ROM_256K EQU 0 679 | CART_ROM_512K EQU 1 680 | CART_ROM_1M EQU 2 681 | CART_ROM_2M EQU 3 682 | CART_ROM_4M EQU 4 683 | CART_ROM_8M EQU 5 684 | CART_ROM_16M EQU 6 685 | 686 | CART_RAM_NONE EQU 0 687 | CART_RAM_16K EQU 1 688 | CART_RAM_64K EQU 2 689 | CART_RAM_256K EQU 3 690 | 691 | CART_RAM_ENABLE EQU $0a 692 | CART_RAM_DISABLE EQU $00 693 | 694 | ;*************************************************************************** 695 | ;* 696 | ;* Keypad related 697 | ;* 698 | ;*************************************************************************** 699 | 700 | PADF_DOWN EQU $80 701 | PADF_UP EQU $40 702 | PADF_LEFT EQU $20 703 | PADF_RIGHT EQU $10 704 | PADF_START EQU $08 705 | PADF_SELECT EQU $04 706 | PADF_B EQU $02 707 | PADF_A EQU $01 708 | 709 | PADB_DOWN EQU $7 710 | PADB_UP EQU $6 711 | PADB_LEFT EQU $5 712 | PADB_RIGHT EQU $4 713 | PADB_START EQU $3 714 | PADB_SELECT EQU $2 715 | PADB_B EQU $1 716 | PADB_A EQU $0 717 | 718 | ;*************************************************************************** 719 | ;* 720 | ;* Screen related 721 | ;* 722 | ;*************************************************************************** 723 | 724 | SCRN_X EQU 160 ; Width of screen in pixels 725 | SCRN_Y EQU 144 ; Height of screen in pixels 726 | SCRN_X_B EQU 20 ; Width of screen in bytes 727 | SCRN_Y_B EQU 18 ; Height of screen in bytes 728 | 729 | SCRN_VX EQU 256 ; Virtual width of screen in pixels 730 | SCRN_VY EQU 256 ; Virtual height of screen in pixels 731 | SCRN_VX_B EQU 32 ; Virtual width of screen in bytes 732 | SCRN_VY_B EQU 32 ; Virtual height of screen in bytes 733 | 734 | NINTENDO_LOGO: MACRO 735 | ;* 736 | ;* Nintendo scrolling logo 737 | ;* (Code won't work on a real GameBoy) 738 | ;* (if next six lines are altered.) 739 | DB $CE,$ED,$66,$66,$CC,$0D,$00,$0B,$03,$73,$00,$83,$00,$0C,$00,$0D 740 | DB $00,$08,$11,$1F,$88,$89,$00,$0E,$DC,$CC,$6E,$E6,$DD,$DD,$D9,$99 741 | DB $BB,$BB,$67,$63,$6E,$0E,$EC,$CC,$DD,$DC,$99,$9F,$BB,$B9,$33,$3E 742 | ENDM 743 | 744 | ENDC ;HARDWARE_INC -------------------------------------------------------------------------------- /make.cmd: -------------------------------------------------------------------------------- 1 | @REM RGBDS Makefile for Windows, written by tmk - https://github.com/gitendo 2 | 3 | @CD dmg 4 | @FOR /F "delims==" %%i in ('dir /b /on *.asm') DO @( 5 | rgbasm -i ..\inc\ -i ..\data\ -o %%~ni.o %%i 6 | @IF ERRORLEVEL 1 ( 7 | ECHO Failed to assemble %%~i 8 | GOTO :eof 9 | ) 10 | rgblink -d -o %%~ni.gb %%~ni.o 11 | @IF ERRORLEVEL 1 ( 12 | ECHO Failed to link %%~ni.o 13 | GOTO :eof 14 | ) 15 | @DEL %%~ni.o 16 | rgbfix -p 0 -r 0 -t DMG_EXAMPLE -v %%~ni.gb 17 | @IF ERRORLEVEL 1 ( 18 | ECHO Failed to fix %%~ni.gb 19 | GOTO :eof 20 | ) 21 | ) 22 | 23 | @CD ../cgb 24 | @FOR /F "delims==" %%i in ('dir /b /on *.asm') DO @( 25 | rgbasm -i ..\inc\ -i ..\data\ -o %%~ni.o %%i 26 | @IF ERRORLEVEL 1 ( 27 | ECHO Failed to assemble %%~i 28 | GOTO :eof 29 | ) 30 | rgblink -o %%~ni.gbc %%~ni.o 31 | @IF ERRORLEVEL 1 ( 32 | ECHO Failed to link %%~ni.o 33 | GOTO :eof 34 | ) 35 | @DEL %%~ni.o 36 | rgbfix -C -p 0 -r 0 -t CGB_EXAMPLE -v %%~ni.gbc 37 | @IF ERRORLEVEL 1 ( 38 | ECHO Failed to fix %%~ni.gbc 39 | GOTO :eof 40 | ) 41 | ) 42 | 43 | 44 | @ECHO Build successful! --------------------------------------------------------------------------------