├── .gitignore ├── .travis.yml ├── AUTHORS ├── BUGS ├── COPYING ├── ChangeLog ├── INSTALL ├── Makefile.am ├── NEWS ├── README ├── README.md ├── THANKS ├── bootstrap ├── configure.ac ├── debian ├── changelog ├── compat ├── control ├── copyright ├── rules └── source │ ├── format │ └── include-binaries ├── doc ├── Makefile.am ├── chip8.texi └── fdl.texi ├── examples ├── 15PUZZLE ├── BLINKY ├── BLITZ ├── BRIX ├── CONNECT4 ├── GUESS ├── HIDDEN ├── INVADERS ├── KALEID ├── MAZE ├── MERLIN ├── MISSILE ├── PONG ├── PONG2 ├── PUZZLE ├── SYZYGY ├── TANK ├── TETRIS ├── TICTAC ├── UFO ├── VBRIX ├── VERS └── WIPEOFF ├── src ├── Makefile.am ├── chip8 │ ├── Makefile.am │ ├── chip8.1 │ ├── chip8.c │ ├── libsdl.c │ └── libsdl.h └── lib8 │ ├── Makefile.am │ ├── cpu.c │ └── cpu.h └── tests ├── .gitignore ├── Makefile.am ├── opchip.c ├── opschip.c ├── screen.c └── test.c /.gitignore: -------------------------------------------------------------------------------- 1 | # Autoconf things 2 | /aclocal.m4 3 | /ar-lib 4 | /autom4te.cache/ 5 | /compile 6 | /depcomp 7 | /missing 8 | /install-sh 9 | /stamp-h1 10 | /test-driver 11 | 12 | # Autotools things 13 | /config.cache 14 | /config.h.in 15 | /config.h 16 | /config.log 17 | /config.status 18 | /configure 19 | 20 | # Makefiles are generated by Autotools 21 | **/Makefile 22 | **/Makefile.in 23 | 24 | # Output files 25 | **/*.a 26 | **/*.o 27 | **/.deps/ 28 | 29 | # Coverage files 30 | **/*.gcno 31 | **/*.gcda 32 | 33 | # Generated targets 34 | src/chip8/chip8 35 | 36 | # Eclipse CDT 37 | .autotools 38 | .cproject 39 | .project 40 | .settings/ 41 | 42 | # Texinfo files 43 | doc/texinfo.tex 44 | doc/version.texi 45 | doc/stamp-vti 46 | doc/mdate-sh 47 | doc/chip8.info 48 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # TODO: Migrate to container based builds 2 | 3 | sudo: required 4 | dist: trusty 5 | branches: 6 | only: 7 | - master 8 | - devel 9 | 10 | language: c 11 | compiler: gcc 12 | 13 | before_install: 14 | - sudo apt-get -qq update 15 | - sudo apt-get install -y libsdl2-dev check texinfo 16 | - pip install --user cpp-coveralls 17 | before_script: ./bootstrap 18 | script: "./configure --enable-gcov && make && make check" 19 | after_success: 20 | - coveralls --exclude tests --gcov-options '\-lp' 21 | after_failure: "cat tests/test-suite.log" 22 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | chip8 is a CHIP-8 emulator developed as part of a bigger project. 2 | 3 | Main developer and maintainer: 4 | Dani Rodríguez - 5 | 6 | -------------------------------------------------------------------------------- /BUGS: -------------------------------------------------------------------------------- 1 | Reporting bugs.- 2 | 3 | If you find a bug on the emulator, you can report it at the issue 4 | tracker for this project, which is located on GitHub, at 5 | 6 | http://github.com/danirod/chip8 7 | 8 | Please, only report bugs that can be reproduced. Report the steps 9 | required to reproduce the bug, as well as the environment (operating 10 | system, processor) you are running the emulator in. If you are 11 | able to tell which ROM you were playing when the bug popped out 12 | that would be better too. 13 | 14 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 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 | 635 | Copyright (C) 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 | Copyright (C) 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 | -------------------------------------------------------------------------------- /ChangeLog: -------------------------------------------------------------------------------- 1 | 2016-03-26 Dani Rodríguez 2 | 3 | * src/libsdl.c: fixed package not building under Windows and 4 | MacOS X because name conflict between our sdl.h and SDL2's 5 | SDL.h (non-case sensitive file systems). 6 | * src/chip8.c: doesn't crash when the PC has no sound device, 7 | emulator will just run muted. (Bug#19) 8 | * src/chip8.c: added --mute flag. 9 | * Makefile.am: extra files are now copied to datadir. 10 | * debian/: added Debian control files for Debian packaging. 11 | 12 | 2016-03-24 Dani Rodríguez 13 | 14 | * Version 0.1.2 released. 15 | * src/sdl.h: fixed SDL.h header file inclusion. 16 | 17 | 2016-03-02 Dani Rodríguez 18 | 19 | * src/cpu.c: fix failing opcodes. 20 | 21 | 2016-02-25 Dani Rodríguez 22 | 23 | * tests/: add support for unit testing with Check. 24 | * src/cpu.c: organize CPU library in this file. 25 | * src/sdl.c: organize SDL2 code in this file. 26 | * src/chip8.c: organize client code in this file. 27 | * src/chip8.c: added support for --version flag. 28 | 29 | 2015-03-27 Dani Rodríguez 30 | 31 | * src/chip8.c: Add support for getopt_long for passing options 32 | to the program. 33 | 34 | 2015-03-14 Dani Rodríguez 35 | 36 | * src/chip8.c: Read hexadecimal files (ASCII files containing 37 | only hexadecimal characters describing machine code for a 38 | ROM). 39 | 40 | 2015-03-13 Dani Rodríguez 41 | 42 | * src/sound.c: Add support for sound and beeping. 43 | 44 | 2015-03-12 Hugo Martin 45 | 46 | * Version 0.1.1 released. 47 | * src/sdl.c: Drop SDL_WINDOW_OPENGL so that SDL can decide 48 | the best graphics backend based on the user platform. 49 | * src/sdl.c: Remove unrequired SDL_Surface. 50 | 51 | 2015-03-11 Dani Rodríguez 52 | 53 | * src/chip8.c: Fix bug where return status from SDL 54 | and context initialization functions was not checked for 55 | errors. (Bug#2) 56 | * src/sdl.c: Rename error tags to exception tags to make 57 | them sound more pleasant. 58 | 59 | 2015-03-10 Dani Rodríguez 60 | 61 | * src/sdl.c: Fix bug where SDL surface was freed twice, 62 | second time failing. (Bug#3) 63 | 64 | 2015-03-09 Dani Rodríguez 65 | 66 | * Version 0.1.0 released. 67 | * src/Makefile.in: Code was splitted to multiple files. 68 | 69 | 2015-03-07 Dani Rodríguez 70 | 71 | * chip8.c: Implement DRW opcode. This opcode will now plot 72 | pixels to the emulator screen, managed by an SDL window. 73 | * chip8.c: Implement opcodes related to keyboard. CHIP-8 74 | keyboard has 16 keys and they have been mapped to 75 | 1234/QWER/ASDF/ZXCV. 76 | 77 | 2015-03-03 Dani Rodríguez 78 | 79 | Implement CHIP-8 opcodes exception keyboard input and screen 80 | output. The emulator now supports every opcode related to ALU 81 | and memory. 82 | * configure.ac: SDL 2.0 is now required to build the project. 83 | 84 | 2015-02-27 Dani Rodríguez 85 | 86 | Define CPU data structure and implement the main loop with 87 | placeholders that print to stdout each opcode dissassembled. 88 | * configure.ac: Create project structure. 89 | -------------------------------------------------------------------------------- /INSTALL: -------------------------------------------------------------------------------- 1 | chip8 emulator: Installation instructions 2 | Copyright (C) 2015 Dani Rodríguez 3 | 4 | In order to compile this software, download the sources from the project 5 | web site and extract them into a software folder. Source code packages 6 | can be downloaded from the releases directory. These packages already 7 | contains a `configure` script generated by the GNU Autotools System. 8 | 9 | As an alternative, you may download the sources by cloning the Git 10 | repository for this project. However, these sources cannot be directly 11 | compiled, as the `configure` script has to be manually created. See below 12 | for more information. 13 | 14 | Compiling the project. 15 | 16 | To compile this software, cd to the folder where you have extracted 17 | your source code and run the following commands using your terminal 18 | 19 | ./configure 20 | make 21 | 22 | Alternatively, you may prefer to build this package on a separate 23 | directory from the directory where your sources are located. In that 24 | case you must invoke `configure` script as 25 | 26 | $PATH_TO_SOURCES/configure 27 | make 28 | 29 | Where $PATH_TO_SOURCES is the destination where the package sources 30 | are located. For instance, if you are compiling the project from a 31 | subfolder you may invoke `configure` as `../configure`. 32 | 33 | Installing the project 34 | 35 | After successfully running `make`, run `make install`. This will 36 | copy generated output by `make` into the preferred destination. By 37 | default they will be installed to /usr/local. You can change this 38 | directory by invoking `configure` as 39 | 40 | ./configure --prefix=$FOLDER 41 | 42 | where $FOLDER is the folder where you want to install the program 43 | to. Please note that binaries are not directly installed to $FOLDER 44 | but to $FOLDER/bin. For instance, to install the package into your 45 | home directory, run 46 | 47 | ./configure --prefix=$HOME 48 | 49 | The emulator will be installed at ~/bin/chip8. 50 | 51 | Uninstalling the project 52 | 53 | If you haven't still delete files generated by ./configure script, 54 | you can uninstall this software by running 55 | 56 | make uninstall 57 | 58 | If you have already deleted the output files generated by ./configure 59 | or even the entire sources distribution, you must invoke again 60 | `./configure` using the same prefix than the one you provided when 61 | installing the software. 62 | 63 | Compiling on Windows 64 | 65 | This project can be build on Windows provided you have a compatible 66 | system. MinGW has been tested and it works. You must install into your 67 | MSYS system SDL2 if you want the code to compile. Cygwin hasn't been 68 | tested although it is expected to work. 69 | 70 | To successfully compile the emulator on Windows you must provide 71 | some linker flags when compiling it. You must either execute the 72 | following commands: 73 | 74 | LDFLAGS='-lmingw32 -lSDL2main -lSDL2' ./configure 75 | make 76 | 77 | Or the following 78 | 79 | ./configure 80 | LDFLAGS='-lmingw32 -lSDL2main -lSDL2' make 81 | 82 | Generating compile script from bootstrap 83 | 84 | If you download a source code distribution from the releases page 85 | you may get a tarball with a `configure` script that generates a 86 | valid Makefile to compile the project using make. 87 | 88 | If you instead clone or pull the Git repository, you won't get 89 | that `configure` script since it's generated by GNU Autotools. 90 | You must run the bootstrap script first: 91 | 92 | ./bootstrap 93 | ./configure 94 | make 95 | 96 | Please note that `bootstrap` requires GNU Autotools installed to work. 97 | You must have GNU Autoconf and GNU Autoheader installed and working. 98 | The bootstrap script will execute `autoreconf`, which automatically 99 | executes every program from the GNU Autotools suite that is required 100 | to generate the configuration scripts. 101 | 102 | 103 | 104 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | # Subdirectories 2 | SUBDIRS = src doc . 3 | if HAVE_CHECK 4 | SUBDIRS += tests 5 | endif 6 | 7 | # Copy extra distribution files 8 | docdir = $(datadir)/doc/$(PACKAGE) 9 | doc_DATA = ChangeLog NEWS README COPYING 10 | 11 | # Copy public domain examples 12 | examples = examples/* 13 | examplesdir = $(datadir)/doc/@PACKAGE@/roms 14 | examples_DATA = $(examples) 15 | 16 | # Extra files that should come with dist (like ROMs) 17 | EXTRA_DIST = $(examples) debian/* 18 | -------------------------------------------------------------------------------- /NEWS: -------------------------------------------------------------------------------- 1 | CHIP-8 news - History of user visible changes 2 | Copyright (C) 2015-2016 Dani Rodríguez 3 | 4 | This file is about relevant changes for CHIP-8 users. Developers: Check 5 | ChangeLog for an overview on relevant changes about the source code. 6 | 7 | Version 0.1.3 - 2016-03-28 8 | * Fixed: emulator won't crash if the PC has no sound device. 9 | * Added: --mute flag was added, forcing emulator to not buzz. 10 | * Added: support for DPKG distributions using a .deb file. 11 | 12 | Version 0.1.2 - 2016-03-24 13 | * Added sound support. 14 | * Added more flags: --version and --usage. 15 | * Fixed a few bugs on some opcodes. 16 | 17 | Version 0.1.1 - 2015-03-12 18 | * Emulator doesn't crash now on exit on Linux and MacOS X (issue #3) 19 | * Emulator now uses the default system graphics backend. For example, might 20 | choose DirectX on Windows. 21 | 22 | Version 0.1.0 - 2015-03-09 23 | * First release with initial emulation capabilities. 24 | * All the opcodes for a standard CHIP-8 machine have been implemented. 25 | * Graphical capabilities for the CHIP-8 emulator are provided using SDL2. 26 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | chip8 is a CHIP-8 emulator developed in C using the SDL2 multimedia library. It 2 | emulates a standard CHIP-8 machine and implements all the opcodes that the 3 | specification provides. 4 | 5 | To emulate a binary ROM just provide the file as an argument: 6 | 7 | $ chip8 ~/roms/TETRIS.bin 8 | 9 | You can have more information by reading the software manual at the following 10 | locations: 11 | 12 | * http://www.danirod.es/chip8/docs/current/manual/ (online manual). 13 | * http://www.danirod.es/chip8/docs/current/chip8.pdf (PDF manual). 14 | 15 | In order to compile this project you will need to have SDL 2.0 headers and 16 | libraries in your machine. Head to www.libsdl.org to get those in case you 17 | still haven’t got them or get them using your package manager if your operating 18 | system has any. 19 | 20 | After installing SDL 2.0 you can download the software distribution and install 21 | it via the following commands: 22 | 23 | autoreconf --install # Only if you are using the Git repository 24 | ./configure 25 | make 26 | make install 27 | make check # Optional: to test the emulator -- libcheck is required 28 | 29 | This program is released under the terms of the GNU General Public License v3. 30 | See the COPYING file for the entire distribution conditions. 31 | 32 | CHIP-8: A multiplatform CHIP-8 emulator done in SDL 33 | Copyright © 2015-2016 Dani Rodríguez 34 | 35 | This program is free software: you can redistribute it and/or modify 36 | it under the terms of the GNU General Public License as published by 37 | the Free Software Foundation, either version 3 of the License, or 38 | (at your option) any later version. 39 | 40 | This program is distributed in the hope that it will be useful, 41 | but WITHOUT ANY WARRANTY; without even the implied warranty of 42 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 43 | GNU General Public License for more details. 44 | 45 | You should have received a copy of the GNU General Public License 46 | along with this program. If not, see . 47 | See COPYING for the entire contents of the license. 48 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CHIP-8 [![Build Status](https://travis-ci.org/danirod/chip8.svg)](https://travis-ci.org/danirod/chip8) [![Coverage Status](https://coveralls.io/repos/github/danirod/chip8/badge.svg?branch=devel)](https://coveralls.io/github/danirod/chip8?branch=devel) [![GitHub tag](https://img.shields.io/github/tag/danirod/chip8.svg)](https://github.com/danirod/chip8/releases/latest) [![GitHub license](https://img.shields.io/badge/license-GPL3-blue.svg)](http://www.gnu.org/licenses/gpl-3.0.html) 2 | 3 | chip8 is a CHIP-8 emulator developed in C using the SDL2 multimedia library. It emulates a standard CHIP-8 machine and implements all the opcodes that the specification provides. 4 | 5 | ## Usage 6 | 7 | To emulate a binary ROM just provide the file as an argument: 8 | 9 | ```sh 10 | $ chip8 ~/roms/TETRIS.bin 11 | ``` 12 | 13 | You can have more information by reading the [software manual](http://www.danirod.es/chip8/docs/current/manual/). [Download as PDF](http://www.danirod.es/chip8/docs/current/chip8.pdf). 14 | 15 | ## Building from sources 16 | 17 | In order to compile this project you will need to have SDL 2.0 headers and 18 | libraries in your machine. Head to www.libsdl.org to get those in case 19 | you still haven't got them or get them using your package manager if your 20 | operating system has any. 21 | 22 | After installing SDL 2.0 you can download the software distribution and install it via the following commands: 23 | 24 | ```sh 25 | autoreconf --install # Only if you are using the Git repository 26 | ./configure 27 | make 28 | make install 29 | make check # Optional: to test the emulator -- libcheck is required 30 | ``` 31 | 32 | ## Screenshots 33 | 34 | GNU/Linux: 35 | 36 | ![CHIP-8 Emulator on GNU/Linux](http://www.danirod.es/chip8/screenshots/linux.png) 37 | 38 | Apple® MacOS® X: 39 | 40 | ![CHIP-8 Emulator on MacOS X](http://www.danirod.es/chip8/screenshots/mac.png) 41 | 42 | 43 | Microsoft® Windows®: 44 | 45 | ![CHIP-8 Emulator on Windows](http://www.danirod.es/chip8/screenshots/windows.png) 46 | 47 | ## License 48 | 49 | CHIP-8: A multiplatform CHIP-8 emulator done in SDL 50 | Copyright © 2015-2016 Dani Rodríguez 51 | 52 | This program is free software: you can redistribute it and/or modify 53 | it under the terms of the GNU General Public License as published by 54 | the Free Software Foundation, either version 3 of the License, or 55 | (at your option) any later version. 56 | 57 | This program is distributed in the hope that it will be useful, 58 | but WITHOUT ANY WARRANTY; without even the implied warranty of 59 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 60 | GNU General Public License for more details. 61 | 62 | You should have received a copy of the GNU General Public License 63 | along with this program. If not, see . 64 | 65 | See COPYING for the entire contents of the license. 66 | -------------------------------------------------------------------------------- /THANKS: -------------------------------------------------------------------------------- 1 | chip8 Thanks file 2 | 3 | This emulator is developed and maintained by the authors described in 4 | AUTHORS file. However, a few people have contributed with small patches 5 | to the project or reported problems in the past. To get information 6 | about contributors, please check out the Contributors page at the 7 | GitHub repository: 8 | 9 | https://github.com/danirod/chip8/graphs/contributors 10 | 11 | Aditionally, since this project was built as part of a streaming series on 12 | my YouTube channel, I would like to thank to the audience that has been 13 | following this project since its birth. A few of the people that have been 14 | hanging out in the YouTube chat during the Season 1: aitor killer, ArcRoaR, 15 | Iovictor29, J D Gambin, JoacoGamer100, Luis Perez, Marcos Sanchess, Matías, 16 | nexus0709, Omega36R, Sergio CM, Skillath, zhexirox. Thanks to all of you. 17 | -------------------------------------------------------------------------------- /bootstrap: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Bootstrap script for CHIP-8 Project. 4 | # You need to run this file to create ./configure and Makefile.in files. 5 | # Those files are autogenerated and thus should not be saved. 6 | 7 | autoreconf --install 8 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | # configure.ac 2 | 3 | AC_INIT([chip8], [0.2.0], [danirod@outlook.com]) 4 | AM_INIT_AUTOMAKE([foreign -Wall -Werror]) 5 | 6 | # Check programs 7 | AM_PROG_AR 8 | AC_PROG_CC 9 | AC_PROG_RANLIB 10 | 11 | # Coverages 12 | AC_ARG_ENABLE(gcov, ([--enable-gcov, "Enables gcov"])) 13 | AS_IF([test "x$enable_gcov" = "xyes"], CFLAGS="$CFLAGS -g -O0 -fprofile-arcs -ftest-coverage") 14 | 15 | # Check libraries 16 | AC_CHECK_LIB([m], [sinf], [], [AC_MSG_ERROR(["** ERROR: Math library not found **"])]) 17 | # Check header files 18 | 19 | # Check typedefs, structures and so 20 | 21 | # Check library functions 22 | AM_PATH_SDL2([2.0.0], :, AC_MSG_ERROR(["** ERROR: SDL 2.0 is required **"])) 23 | PKG_CHECK_MODULES([CHECK], [check >= 0.9.6], [has_check=1], [has_check=0]) 24 | AM_CONDITIONAL([HAVE_CHECK], [test "x$has_check" = "x1"]) 25 | 26 | # Output 27 | AC_CONFIG_HEADERS([config.h]) 28 | AC_CONFIG_FILES([ 29 | Makefile 30 | src/Makefile 31 | src/lib8/Makefile 32 | src/chip8/Makefile 33 | doc/Makefile 34 | tests/Makefile 35 | ]) 36 | AC_OUTPUT 37 | -------------------------------------------------------------------------------- /debian/changelog: -------------------------------------------------------------------------------- 1 | chip8 (0.1.3-1) unstable; urgency=low 2 | 3 | * Initial release. 4 | 5 | -- Dani Rodríguez Sat, 26 Mar 2016 17:00:41 +0100 6 | -------------------------------------------------------------------------------- /debian/compat: -------------------------------------------------------------------------------- 1 | 9 2 | -------------------------------------------------------------------------------- /debian/control: -------------------------------------------------------------------------------- 1 | Source: chip8 2 | Section: games 3 | Priority: optional 4 | Maintainer: Dani Rodríguez 5 | Build-Depends: debhelper (>= 8.0.0), autotools-dev, check, libsdl2-dev (>= 2.0.0) 6 | Standards-Version: 3.9.4 7 | Homepage: http://github.com/danirod/chip8 8 | Vcs-Git: git://github.com/danirod/chip8.git 9 | Vcs-Browser: http://github.com/danirod/chip8 10 | 11 | Package: chip8 12 | Architecture: any 13 | Depends: ${shlibs:Depends}, ${misc:Depends}, libsdl2-2.0-0 (>= 2.0.0) 14 | Description: Multiplatform CHIP-8 emulator 15 | chip8 is a CHIP-8 emulator using the SDL2 emulation library. 16 | It is able to emulate CHIP-8 ROMs in binary and hexadecimal 17 | format. CHIP-8 is under development and not all features 18 | have been implemented. 19 | 20 | -------------------------------------------------------------------------------- /debian/copyright: -------------------------------------------------------------------------------- 1 | Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ 2 | Upstream-Name: chip8 3 | Source: http://github.com/danirod/chip8 4 | 5 | Files: * 6 | Copyright: 2015-2016 Dani Rodríguez 7 | License: GPL-3+ 8 | This program is free software: you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation, either version 3 of the License, or 11 | (at your option) any later version. 12 | . 13 | This program is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | . 18 | You should have received a copy of the GNU General Public License 19 | along with this program. If not, see . 20 | -------------------------------------------------------------------------------- /debian/rules: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | # -*- makefile -*- 3 | 4 | # Uncomment this to turn on verbose mode. 5 | #export DH_VERBOSE=1 6 | 7 | %: 8 | dh $@ --with autotools-dev 9 | -------------------------------------------------------------------------------- /debian/source/format: -------------------------------------------------------------------------------- 1 | 3.0 (quilt) 2 | -------------------------------------------------------------------------------- /debian/source/include-binaries: -------------------------------------------------------------------------------- 1 | examples/15PUZZLE 2 | examples/BLINKY 3 | examples/BLITZ 4 | examples/BRIX 5 | examples/CONNECT4 6 | examples/GUESS 7 | examples/HIDDEN 8 | examples/INVADERS 9 | examples/KALEID 10 | examples/MAZE 11 | examples/MERLIN 12 | examples/MISSILE 13 | examples/PONG 14 | examples/PONG2 15 | examples/PUZZLE 16 | examples/SYZYGY 17 | examples/TANK 18 | examples/TETRIS 19 | examples/TICTAC 20 | examples/UFO 21 | examples/VBRIX 22 | examples/VERS 23 | examples/WIPEOFF 24 | -------------------------------------------------------------------------------- /doc/Makefile.am: -------------------------------------------------------------------------------- 1 | # Info sources 2 | info_TEXINFOS = chip8.texi 3 | 4 | -------------------------------------------------------------------------------- /doc/chip8.texi: -------------------------------------------------------------------------------- 1 | \input texinfo 2 | @settitle CHIP-8 3 | @setfilename chip8.info 4 | @documentencoding ISO-8859-1 5 | 6 | @include version.texi 7 | 8 | @c This file is part of CHIP-8: a virtual machine emulator for CHIP-8. 9 | @c Copyright (C) 2015-2016 Dani Rodr@'iguez 10 | @c This is the manual for CHIP-8 made using texinfo. 11 | 12 | @copying 13 | Copyright @copyright{} 2015-2016 Dani Rodr@'iguez 14 | 15 | Permission is granted to copy, distribute and/or modify this document under the 16 | terms of the GNU Free Documentation License, Version 1.3 or any later version 17 | published by the Free Software Foundation; with no Invariant Sections, no 18 | Front-Cover Texts, and no Back-Cover Texts. A copy of the license is included 19 | in the section entitled "GNU Free Documentation License". 20 | @end copying 21 | 22 | @c Print title page 23 | @titlepage 24 | @title CHIP-8 25 | @subtitle Package manual 26 | @subtitle version @value{VERSION} 27 | @subtitle @value{UPDATED-MONTH} 28 | @author Dani Rodr@'iguez 29 | 30 | @c Print copyright information 31 | @page 32 | @vskip 0pt plus 1fill 33 | This is the user manual for version @value{VERSION} of CHIP-8 software package. 34 | To check for updates, please visit @uref{http://github.com/danirod/chip8}. 35 | 36 | @insertcopying 37 | @end titlepage 38 | 39 | @c Print table of contents 40 | @contents 41 | 42 | @c Greeting on non TeX version. 43 | @ifnottex 44 | @node Top 45 | @top CHIP-8 Package manual 46 | This is the user manual for version @value{VERSION} of CHIP-8 software package. 47 | To check for updates, please visit @uref{http://github.com/danirod/chip8}. 48 | @end ifnottex 49 | 50 | @menu 51 | * Introduction:: An introduction to CHIP-8 52 | * Emulator usage:: How to use the emulator 53 | * Emulated systems:: Platforms that can be executed 54 | * Compatible ROM formats:: ROM formats supported by the emulator. 55 | * Hacking the emulator:: How to modify the source code. 56 | * GNU Free Documentation License:: 57 | @end menu 58 | 59 | @node Introduction 60 | @chapter Introduction 61 | 62 | Welcome to @dfn{chip8}, my implementation of an emulator compatible with the 63 | CHIP-8 virtual machine. This software manual will bring you information about 64 | the emulator, how to run programs on the CHIP-8, how to use the emulator and 65 | some information for those interested on the insides of the CHIP-8 virtual 66 | machine and the format used by the ROMs that can be run from CHIP-8. 67 | 68 | @menu 69 | * Main features:: Some of the features of this emulator 70 | * Reporting bugs:: How to report bugs that you might find 71 | * Some history:: Some history on the CHIP-8 machine 72 | @end menu 73 | 74 | @node Main features 75 | @section Main features 76 | 77 | This implementation for the CHIP-8 virtual machine has, at this moment, the 78 | following features: 79 | 80 | @itemize @bullet 81 | @item Compatible with CHIP-8 and SUPERCHIP specifications. 82 | @item Modular, lightweight and fast. 83 | @item Multiplatform. Not only runs on GNU/Linux but also can be compiled on 84 | Microsoft@registeredsymbol{} Windows@registeredsymbol{} and 85 | Apple@registeredsymbol{} MacOS@registeredsymbol{} X. 86 | @item Virtually compatible with any machine by using the SDL2 library. 87 | @end itemize 88 | 89 | At this stage the emulator has not been finished. There are a few features 90 | that have not been implemented yet. There is a particular interest in adding 91 | the following features: 92 | 93 | @itemize @bullet 94 | @item Support for the MEGACHIP specification. 95 | @item User defined settings. 96 | @item Development tools for writing ROMs. 97 | @end itemize 98 | 99 | @node Reporting bugs 100 | @section Reporting bugs 101 | 102 | This tool has not been finished yet, and therefore there might be issues that 103 | haven't been reported or addressed yet. If during the execution of a program 104 | you find some bug that would like to report for having it fixed, you can report 105 | bugs to our issue tracker, at the canonical repository for our project. The 106 | URL is @uref{https://www.github.com/danirod/chip8}. 107 | 108 | Please, provide as much information as you can, including the operating system 109 | you are using, the version of the emulation software and if you can tell us 110 | which ROM were you playing, it would be better. 111 | 112 | @node Some history 113 | @section Some history 114 | 115 | CHIP-8 is an interpreted programming language and the specification for a 116 | virtual machine. As is, CHIP-8 is not a cmoputer, and there are no computers 117 | in the wild using this architecture, because it has limited resources. Instead 118 | there are computers with their own hardware, that have a virtual machine that 119 | allows the computer to decode and execute CHIP-8 programs. 120 | 121 | One of the first computers in add support for CHIP-8 as the COSMAC VIP, sold by 122 | RCA in the late 70s. This computer had a processor made too by RCA, with a 123 | speed of about 1.77 MHz and 2 kB of RAM memory. Inside the ROM, the operating 124 | system had the CHIP-8 interpreter that allowed the system to run games using 125 | the CHIP-8 language. 126 | 127 | However, there are more machines that have added support for CHIP-8, specially 128 | during the late 80s and early 90s, with the expansion of small computers such 129 | as graphical calculators. In fact, during this period and thanks to the first 130 | Internet communities that came around that time, many hobbyists could extend 131 | the original CHIP-8 specification, by making some changes that would make them 132 | run better on their machines, and sometimes even improving the original CHIP-8 133 | system, with features such as a bigger screen, more memory and even color. 134 | 135 | Thanks to being easy to understand, CHIP-8 is one of the greatest platforms 136 | for those people who want to start working in the world of emulators 137 | development, learning an easy architecture before starting bigger projects. 138 | 139 | @node Emulator usage 140 | @chapter Emulator usage 141 | 142 | @menu 143 | * Installing the emulator:: How to install the emulator 144 | * Running the emulator:: How to run the emulator 145 | * Using the emulator:: How to use the emulator 146 | * Stopping the emulation:: How to stop the emulator 147 | @end menu 148 | 149 | @node Installing the emulator 150 | @section Installing the emulator 151 | 152 | The main procedure for installing the emulator is to download the source 153 | code distribution, compiling it and installing it. Of course, there are 154 | aditional ways for obtaining the emulator, such as getting it from a binary 155 | distribution, for example, a @emph{.deb} file for Debian based systems or 156 | an @emph{.exe} application for Microsoft@registeredsymbol{} 157 | Windows@registeredsymbol{}. 158 | 159 | This emulator only depends on SDL2, because it is the multimedia library used 160 | for displaying the emulator output, reading keys from the keyboard and making 161 | the speaker buzz. After the dependencies are met, this CHIP-8 emulator can be 162 | installed by downloading the software release, extracting it somewhere, and 163 | then running @command{./configure && make} to compile it. 164 | 165 | Once compiled, it can be installed using @command{make install}. By default 166 | on a standard GNU/Linux system they would be installed into 167 | @file{/usr/local/bin}, @file{/usr/local/share/doc/chip8}, etc. On 168 | Apple@registeredsymbol{} MacOS@registeredsymbol{} X systems, these locations 169 | will be used as well. On Microsoft@registeredsymbol{} 170 | Windows@registeredsymbol{}, the location might actually change depending on 171 | how you build the software. 172 | 173 | If you are interested in checking that the software actually works as intended 174 | you can use the test suite provided with the source distribution. In order to 175 | run the tests you'll need to have @dfn{check} installed on your computer. 176 | Install the dependencies, and then after running @command{./configure && make} 177 | on the emulator package, run @command{make check} to run the test suite. 178 | After the execution, you will get a report on the test status. 179 | 180 | @node Running the emulator 181 | @section Running the emulator 182 | 183 | After the emulator has been downloaded it can be executed using the command 184 | @command{chip8 @var{file}}, where @var{file} is the path to a binary file 185 | that has the encoded data for a program that can run on the CHIP-8 virtual 186 | machine. 187 | 188 | As an example, to run the ROM @file{WIPEOFF.BIN} using the emulator, the 189 | following command can be used: 190 | 191 | @example 192 | $ chip8 WIPEOFF.BIN 193 | @end example 194 | 195 | assuming the game is on the current working directory. Some games can make use 196 | of the buzzer that the CHIP-8 computer has. The buzzer will sound through the 197 | speakers. If you want to avoid this behaviour you can run the game muted using 198 | the @command{--mute} option: 199 | 200 | @example 201 | $ chip8 --mute WIPEOFF.BIN 202 | @end example 203 | 204 | There are actually two kinds of ROMs: binary ROMs and hexadecimal ROMs. A 205 | binary ROM only has binary data and it is the most lightweight and fast way 206 | for running a ROM because the data can be placed in memory and executed 207 | instantaneously. This data can come from a dump for an actual CHIP-8 game or 208 | a game made for the CHIP-8 platform in any other way 209 | 210 | However, to make easier to people to create their own ROMs, there is an 211 | aditional way for running these ROMs and is using hexadecimal files. These 212 | files are plain text files only having the characters @code{0-9}, @code{A-F} 213 | and @code{a-f} encoded as either US-ASCII, UTF-8, Windows-1252 or a similar 214 | human readable text-enconding and can be modified using any kind of text 215 | editor that supports plain text. This format is slower to load since the 216 | file has to be converted to binary but it makes playing around faster. 217 | 218 | @node Using the emulator 219 | @section Using the emulator 220 | 221 | Once the emulator opens, it automatically starts executing the ROM contents. 222 | It is possible to control the emulator using the emulated keyboard. The CHIP-8 223 | specification sets a 16 keys keyboard using a 4x4 table having the following 224 | layout: 225 | 226 | @verbatim 227 | [1] [2] [3] [C] 228 | [4] [5] [6] [D] 229 | [7] [8] [9] [E] 230 | [A] [0] [B] [F] 231 | @end verbatim 232 | 233 | Using the current settings, that aren't modifiable by the user yet --although 234 | that is hoped for future releases, the assigned keys for the keyboard are 235 | mapped to the following keys on a traditional keyboard: 236 | 237 | @verbatim 238 | [1] [2] [3] [4] 239 | [Q] [W] [E] [R] 240 | [A] [S] [D] [F] 241 | [Z] [X] [C] [V] 242 | @end verbatim 243 | 244 | Which means that if you want to send the @kbd{5} key to your game you should 245 | press the @kbd{W} key in the emulator. Pressing @kbd{F} will trigger @kbd{E} 246 | key and similar. 247 | 248 | During the execution of a CHIP-8 ROM, some games may play sounds on the buzzer. 249 | As explained in @ref{Running the emulator}, this behaviour can be changed by 250 | using the option @option{--mute} when running the game. 251 | 252 | @node Stopping the emulation 253 | @section Stopping the emulation 254 | 255 | At this moment there is no way for pausing, resuming, resetting or stopping 256 | the emulation in this CHIP-8 emulator. The only way for stopping it is by 257 | exiting the emulator, something that you can do by pressing the Close button 258 | on the emulator window. This depends on your window manager or operating 259 | system. 260 | 261 | 262 | @node Emulated systems 263 | @chapter Emulated systems 264 | 265 | This CHIP-8 emulator is compatible with other kinds of systems. CHIP-8 is based 266 | in the origina lspecification written for the first computers using CHIP-8 267 | virtual machines inside, such as the COSMAC VIP. However, through the years, 268 | hobbyists interested in CHIP-8 emulation have developed emulators for other 269 | kinds of machines such as calculators or PCs, and they have extended the 270 | original specification for adding support to new features if the host hardware 271 | allowed so. 272 | 273 | In particular, this emulator is at the moment compatible with CHIP-8 and 274 | SUPERCHIP ROMs. 275 | 276 | @menu 277 | * CHIP-8 platform:: About the original CHIP-8 specification. 278 | * SUPERCHIP platform:: The SUPERCHIP extended specification. 279 | * MEGACHIP platform:: The MEGACHIP extended specification. 280 | @end menu 281 | 282 | @node CHIP-8 platform 283 | @section CHIP-8 platform 284 | 285 | This is the original platform for the CHIP-8 virtual machine. It is a simple 286 | architecture that has the following hardware resources. 287 | 288 | @itemize @bullet 289 | @item 4 kB of RAM memory. 290 | @item A register bank made of 16 registers, each one being 8 bits. These 291 | registers are refered to as @code{V0}, @code{V1}, ..., @code{V9}, 292 | @code{VA}, @code{VB}, @code{VC}, @code{VD}, @code{VE} and @code{VF}. 293 | @item A stack that can hold 16 16-bit values. 294 | @item A stack pointer for pointing to the next free location in the stack. 295 | As an example, if the stack pointer holds the value 5, that means 296 | that the next value pushed to the stack will be in the position 5. 297 | Pushing a value to the stack will increase the value of the stack pointer 298 | by 1 to make it point to the next free location. Popping something will 299 | decrease the value by 1, and then whatever is pointed by it, will be 300 | transfered to the target destination. 301 | @item A special register named I that is used by some operations such as 302 | painting to the screen. 303 | @end itemize 304 | 305 | The CHIP-8 screen is a rasterized 64 x 32 pixels display. It is monochrome 306 | and up to 2048 pixels can be represented. Usually these pixels will be 307 | black and white, but there is no official statement on this, which means 308 | that is possible for some systems to use different colors, such as green 309 | over black. 310 | 311 | CHIP-8 also has a 16 keys keyboard, as indicated in @ref{Using the emulator}. 312 | It also has a buzzer that can play sounds on a particular and single frequency. 313 | 314 | Talking about the interpreter, the CHIP-8 specification sets 34 instructions 315 | that can manipulate the data in memory, registers and stack, and interact with 316 | other IO devices such as sending images to the screen, reading keys from the 317 | keyboard or making the speaker buzz. 318 | 319 | 320 | @node SUPERCHIP platform 321 | @section SUPERCHIP platform 322 | 323 | SUPERCHIP is an extension over the original CHIP-8 specification that 324 | appeared in the early 90s in order to run CHIP-8 games in some graphical 325 | calculators. Thanks to the new hardware capabilities of the machine, this 326 | specification adds the following features to CHIP-8: 327 | 328 | @itemize @bullet 329 | @item A new graphical mode. By default the emulator runs in compatibility 330 | mode in order to play old games that do not support the new mode, but 331 | a new mode can be enabled that has a 128x64 monochrome display, with up 332 | to 4 times more pixels to display. 333 | @item Instructions for scrolling the screen into different directions, making 334 | faster some operations that rely on this, such as letting the user see 335 | more parts of a big screen. 336 | @item Interaction with the underlying operating system that runs the 337 | SUPERCHIP emulator. For the first time, it is possible to stop the 338 | emulation without having to close the emulator. Plus, it is possible to 339 | receive input from the operating system and send output to the operating 340 | system using a new register bank called @emph{R}, whose values can be 341 | provided before running the game, and requested after finishing the game. 342 | @end itemize 343 | 344 | There are 10 new instructions that cna be used in the programs. SUPERCHIP is 345 | still compatible with the original instruction set because these new opcodes 346 | are mapped to opcodes that would trigger on a crash on regular CHIP-8 347 | emulators. A classic ROM can still work because the original instruction 348 | set is, of course, provided. 349 | 350 | 351 | @node MEGACHIP platform 352 | @section MEGACHIP platform 353 | 354 | @strong{This mode is not implemented yet in this emulator, although their 355 | features makes this platform very appealing}. 356 | 357 | MEGACHIP is a new format born after the improvement of personal computers. 358 | Old limitations have no place on regular devices we have today, and therefore 359 | we can have better multimedia capabilities for running games. 360 | 361 | MEGACHIP adds support for the following features, that should be designed to 362 | be backwards compatible with CHIP-8 and SUPERCHIP formats: 363 | 364 | @itemize @bullet 365 | @item 256x192 raster resolution. 366 | @item For the first time, multiple colors using an indexed 256 color palette. 367 | @item Up to 32 MB of RAM. 368 | @item 8 bit digital sound. 369 | @end itemize 370 | 371 | There are 11 new instructions that are of course mapped to opcodes that neither 372 | CHIP-8 nor SUPERCHIP uses. Therefore, it is possible to run old CHIP-8 and 373 | SUPERCHIP games without side effects. 374 | 375 | 376 | @node Compatible ROM formats 377 | @chapter Compatible ROM formats 378 | 379 | In order to run the programs, it is required to have a ROM containing the 380 | instructions for the program. These instructions are executed by the virtual 381 | machine when the emulator starts. 382 | 383 | This CHIP-8 distribution comes with a set of public domain ROMs that can be 384 | executed in order to test the features of the emulator. These ROMs are in the 385 | @file{examples/} directory for the software distribution package, and once 386 | installed they are placed in @file{/usr/local/share/doc/chip8/roms}, although 387 | the exact location may change depending on where is the package installed in. 388 | 389 | The user can also create new ROMs using software tools or manually writing 390 | machine code into a binary file or an hexadecimal file. These files have to be 391 | provided as parameters when starting the emulator, as explained in 392 | @ref{Running the emulator}. 393 | 394 | However, there are two ways of storing the program instructions inside CHIP-8 395 | ROMs. 396 | 397 | @menu 398 | * Binary ROM files:: Binary files having only instructions. 399 | * Hexadecimal ROM files:: Text files having hexadecimal characters. 400 | * Corrupt ROM files:: What happens when a broken ROM is loaded. 401 | @end menu 402 | 403 | 404 | @node Binary ROM files 405 | @section Binary ROM files 406 | 407 | A binary ROM file is a file where every byte is a byte that must be placed 408 | in the RAM memory for the emulator when the game starts. In other words, 409 | the actual opcodes are encoded. Every 2 bytes in the ROM file can be translated 410 | to an actual instruction that the machine must run. (Or to some data such as 411 | an sprite). 412 | 413 | This is the fastest way for starting ROMs because no transformation is needed. 414 | As the emulator starts, the contents of the file are loaded in RAM memory 415 | if they fit, and the game is started. However, they need special software, 416 | such as hexadecimal editors, in order to see the contents, because they are 417 | binary and cannot be opened with regular text editors. 418 | 419 | As an example, let's suppose that we have a binary ROM with the following 420 | contents, that we might have got using an hexadecimal file tool such as 421 | @command{hexdump}: 422 | 423 | @example 424 | $ hexdump examples/PONG 425 | 0000 6a 02 6b 0c 6c 3f 6d 0c a2 ea da b6 dc d6 6e 00 426 | 0010 22 d4 66 03 68 02 60 60 f0 15 f0 07 30 00 12 1a 427 | @end example 428 | 429 | (more lines have been omitted for brevity) 430 | 431 | When this ROM is loaded into memory, the first instruction that will be 432 | executed will be @code{6A02}, as these are the first two bytes in the file. 433 | Next, the instruction @code{6B0C} will be executed, and then @code{6C3F}, 434 | and so. 435 | 436 | Binary format is the most preferred way when working with dumps coming from 437 | external sources such as actual chips containing programs for old computers 438 | using the CHIP-8 format, because they represent the program as it was. 439 | 440 | 441 | @node Hexadecimal ROM files 442 | @section Hexadecimal ROM files 443 | 444 | An hexadecimal ROM file has the hexadecimal characters that are required for 445 | running a ROM file, organized in a text file that can be edited by the user 446 | in order to build their own programs and games. 447 | 448 | It is a file that makes use of a human readable character encoding such as 449 | US-ASCII, UTF-8 or Windows-1252 and that contains only characters in the ranges 450 | @emph{"0"} to @emph{"9"}, @emph{"A"} to @emph{"F"} and @emph{"a"} to @emph{"f}. 451 | These ranges would equal to the US-ASCII codes @code{0x30}-@code{0x39}, 452 | @code{0x41}-@code{0x46} and @code{0x61}-@code{0x66}. 453 | 454 | The contents for an hexadecimal ROM file can be inspected using regular text 455 | editors, as an example: 456 | 457 | @example 458 | $ cat pong.hex 459 | 6A026B0C6C3F6D0CA2EADAB6DCD66E0022D46603 460 | 68026060F015F0073000121AC717770869FFA2F0 461 | D671A2EADAB6DCD66001E0A17BFE6004E0A17B02 462 | 601F8B02DAB6600CE0A17DFE600DE0A17D02601F 463 | 8D02DCD6A2F0D67186848794603F8602611F8712 464 | 46021278463F1282471F69FF47006901D671122A 465 | @end example 466 | 467 | When an hexadecimal ROM file is given to the emulator, it must be processed. 468 | The hexadecimal ROM is converted to binary using the following method: 469 | 470 | @itemize @bullet 471 | @item It ignores any whitespace or line break. 472 | @item It reads the characters 2 by 2. 473 | @item For every pair of characters that should represent hexadecimal characters 474 | (meaning that they should match the regular expresion 475 | @code{([0-9A-Fa-f]@{2@})}, the following operation is made: 476 | @code{(L, R) -> (L << 4) | R}, where L is the left hexadecimal character 477 | once converted to binary and R is the right hexadecimal character once 478 | converted to binary. This conversion is made by transforming every 479 | character to the 4-bit digit they represent (@code{0x0} to @code{0xF}). 480 | @end itemize 481 | 482 | Although it has not been implemented yet, the following operations are 483 | planned: 484 | 485 | @itemize @bullet 486 | @item Ignore also tabs, in order to ignore any kind of whitespace. 487 | @item Allow comments in hexadecimal ROM files to make it easier to people to 488 | know what code snippets should do. 489 | @end itemize 490 | 491 | 492 | @node Corrupt ROM files 493 | @section Corrupt ROM files 494 | 495 | This emulator has not been tested against fuzz testing, which means that it is 496 | not possible to know at this moment what should happen and how should the 497 | emulator behave when ROMs having corrupt instructions are execute. 498 | 499 | Fuzz testing is made by giving a random input to a program. As an example, 500 | randomly generating about 100 to 200 bytes of instructions, either in binary 501 | or hexadecimal way, and loading the contents in order to check what happens. 502 | 503 | Even though it has not been tested, the expected behaviour should be: 504 | 505 | @itemize @bullet 506 | @item Glitches on the screen. 507 | @item Weird behaviour of the program. 508 | @item Keyboard delays where they shouldn't be. 509 | @item Sound, if available, playing through the speaker. 510 | @end itemize 511 | 512 | Anyway, it is not expected that a corrupt ROM could harm the host machine in 513 | anyway, becuase none of the operations execcuted by the program interact with 514 | the underlying operating system in a way that could have side effects. On 515 | the other side, all the operations that work with memory buffers make enough 516 | tests to minimize the risk of a buffer overflow error. 517 | 518 | Despite all of that, this is something that hasn't been further tested. 519 | As stated by the license terms of this sfotware, available in the COPYING file, 520 | the emulator is provided AS IS, with no extra warranties. 521 | 522 | 523 | @node Hacking the emulator 524 | @appendix Hacking the emulator 525 | 526 | By downloading the software package including source code, any user can change 527 | how the emulator works to make it behave in a different way for their personal 528 | interest, as stated in the license terms for the software package. 529 | 530 | Some hints are given on the source code to make it easier to advanced people 531 | to modify the program. They should be specially useful for people that wants 532 | to contribute to the project by making their contributions public by patching 533 | the upstream source code via a Pull Request. 534 | 535 | @menu 536 | * Basic source code guidelines:: The most easy rules to follow. 537 | * Style guides:: Style guides when writing code for this project. 538 | * Opcodes table:: Clarification on how the opcodes table is made. 539 | @end menu 540 | 541 | 542 | @node Basic source code guidelines 543 | @section Basic source code guidelines 544 | 545 | This program is made using the C programming language. Although any port to 546 | a different programming language is good to have as a side project, this is 547 | the official language for this project. Therefore, any patch that attempts 548 | to change that is not welcome. The project is good using C; I don't want to 549 | change to C++, Python or whatever funky language is the trendiest at this 550 | moment. 551 | 552 | This program uses GNU Autotools for the build tool. Although GNU Autotools 553 | is not loved by everyone, this is the official tool for this project. So, 554 | any attempt to change the build tool to CMake, Gradle or regular Makefiles 555 | is not welcome. 556 | 557 | 558 | @node Style guides 559 | @section Style guides 560 | 561 | Although this is not an official GNU project, I make use of most of their 562 | style guidelines. Any contributor that wants to make public changes to the 563 | project must keep the following rules in mind. 564 | 565 | @itemize @bullet 566 | @item Line width is limited to 80 characters. 567 | @item Indent uses 4 spaces. No tabs, no 8 spaces, no 2 spaces. 568 | @item Functions should be defined by keeping the datatype in a different 569 | line than the function name and parameters. Check the source code for 570 | an example. 571 | @item Parameters and variables should have a easy to understand name. Some 572 | short names are allowed when by context is clear, such as using @code{P}, 573 | @code{X}, @code{Y} or @code{K} for variables related to an opcode, because 574 | these are the names given to the opcode structures. Long names when they 575 | are not required is not pretty. 576 | @item Comments should be added when neccesary. Don't comment every line, but 577 | don't add long chunks of code with no clear explanation of what they do. 578 | @item Long functions should be split in multiple small functions to make 579 | the development easier, plus to make testing easier whenever there are 580 | functions that should be tested. 581 | @end itemize 582 | 583 | 584 | @node Opcodes table 585 | @section Opcodes table 586 | 587 | In order to implement the opcodes, the following structure has been made. 588 | 589 | There is a common definition for every opcode function in the code, declared 590 | as a type named @code{opcode_table_t}, located as a private @emph{typedef} 591 | in the file @file{cpu.c} at the @dfn{lib8} subproject. 592 | 593 | After that, 16 functions are declared, every one compatible with that typedef. 594 | Their names are @code{nibble_0}, @code{nibble_1}, @code{nibble_2}... These 595 | functions have been declared as private functions using the same parameters: 596 | 597 | @verbatim 598 | static void 599 | nibble_0(struct machine_t* cpu, word opcode); 600 | 601 | static void 602 | nibble_1(struct machine_t* cpu, word opcode); 603 | 604 | static void 605 | nibble_2(struct machine_t* cpu, word opcode); 606 | @end verbatim 607 | 608 | Every one of these functions executes opcodes with a P value matching the one 609 | in the function name. As an example, the opcode @code{6104} would be executed 610 | by @code{nibble_6}, since @strong{P(6104) = 6}. 611 | 612 | Then, there is an array pointing to every opcode function named 613 | @code{nibbles[]}. These functions are sorted by P value, so that 614 | @code{nibbles[6]} will return a pointer to the function @code{nibble_6}, 615 | and @code{nibbles[0xB]} will return a pointer to the function 616 | @code{nibble_B}. 617 | 618 | Thanks to this indirection, the processing logic for an opcode is easier 619 | since no big switch is required on code. Using this indirection, it is 620 | more straightforward to know which function should process each opcode. 621 | Although, some functions such as @code{nibble_6} will make use of switch 622 | anyway since there are multiple opcodes sharing the same P value. 623 | 624 | 625 | @node GNU Free Documentation License 626 | @appendix GNU Free Documentation License 627 | @include fdl.texi 628 | 629 | 630 | @bye 631 | -------------------------------------------------------------------------------- /doc/fdl.texi: -------------------------------------------------------------------------------- 1 | @c The GNU Free Documentation License. 2 | @center Version 1.3, 3 November 2008 3 | 4 | @c This file is intended to be included within another document, 5 | @c hence no sectioning command or @node. 6 | 7 | @display 8 | Copyright @copyright{} 2000, 2001, 2002, 2007, 2008 Free Software Foundation, Inc. 9 | @uref{http://fsf.org/} 10 | 11 | Everyone is permitted to copy and distribute verbatim copies 12 | of this license document, but changing it is not allowed. 13 | @end display 14 | 15 | @enumerate 0 16 | @item 17 | PREAMBLE 18 | 19 | The purpose of this License is to make a manual, textbook, or other 20 | functional and useful document @dfn{free} in the sense of freedom: to 21 | assure everyone the effective freedom to copy and redistribute it, 22 | with or without modifying it, either commercially or noncommercially. 23 | Secondarily, this License preserves for the author and publisher a way 24 | to get credit for their work, while not being considered responsible 25 | for modifications made by others. 26 | 27 | This License is a kind of ``copyleft'', which means that derivative 28 | works of the document must themselves be free in the same sense. It 29 | complements the GNU General Public License, which is a copyleft 30 | license designed for free software. 31 | 32 | We have designed this License in order to use it for manuals for free 33 | software, because free software needs free documentation: a free 34 | program should come with manuals providing the same freedoms that the 35 | software does. But this License is not limited to software manuals; 36 | it can be used for any textual work, regardless of subject matter or 37 | whether it is published as a printed book. We recommend this License 38 | principally for works whose purpose is instruction or reference. 39 | 40 | @item 41 | APPLICABILITY AND DEFINITIONS 42 | 43 | This License applies to any manual or other work, in any medium, that 44 | contains a notice placed by the copyright holder saying it can be 45 | distributed under the terms of this License. Such a notice grants a 46 | world-wide, royalty-free license, unlimited in duration, to use that 47 | work under the conditions stated herein. The ``Document'', below, 48 | refers to any such manual or work. Any member of the public is a 49 | licensee, and is addressed as ``you''. You accept the license if you 50 | copy, modify or distribute the work in a way requiring permission 51 | under copyright law. 52 | 53 | A ``Modified Version'' of the Document means any work containing the 54 | Document or a portion of it, either copied verbatim, or with 55 | modifications and/or translated into another language. 56 | 57 | A ``Secondary Section'' is a named appendix or a front-matter section 58 | of the Document that deals exclusively with the relationship of the 59 | publishers or authors of the Document to the Document's overall 60 | subject (or to related matters) and contains nothing that could fall 61 | directly within that overall subject. (Thus, if the Document is in 62 | part a textbook of mathematics, a Secondary Section may not explain 63 | any mathematics.) The relationship could be a matter of historical 64 | connection with the subject or with related matters, or of legal, 65 | commercial, philosophical, ethical or political position regarding 66 | them. 67 | 68 | The ``Invariant Sections'' are certain Secondary Sections whose titles 69 | are designated, as being those of Invariant Sections, in the notice 70 | that says that the Document is released under this License. If a 71 | section does not fit the above definition of Secondary then it is not 72 | allowed to be designated as Invariant. The Document may contain zero 73 | Invariant Sections. If the Document does not identify any Invariant 74 | Sections then there are none. 75 | 76 | The ``Cover Texts'' are certain short passages of text that are listed, 77 | as Front-Cover Texts or Back-Cover Texts, in the notice that says that 78 | the Document is released under this License. A Front-Cover Text may 79 | be at most 5 words, and a Back-Cover Text may be at most 25 words. 80 | 81 | A ``Transparent'' copy of the Document means a machine-readable copy, 82 | represented in a format whose specification is available to the 83 | general public, that is suitable for revising the document 84 | straightforwardly with generic text editors or (for images composed of 85 | pixels) generic paint programs or (for drawings) some widely available 86 | drawing editor, and that is suitable for input to text formatters or 87 | for automatic translation to a variety of formats suitable for input 88 | to text formatters. A copy made in an otherwise Transparent file 89 | format whose markup, or absence of markup, has been arranged to thwart 90 | or discourage subsequent modification by readers is not Transparent. 91 | An image format is not Transparent if used for any substantial amount 92 | of text. A copy that is not ``Transparent'' is called ``Opaque''. 93 | 94 | Examples of suitable formats for Transparent copies include plain 95 | @sc{ascii} without markup, Texinfo input format, La@TeX{} input 96 | format, @acronym{SGML} or @acronym{XML} using a publicly available 97 | @acronym{DTD}, and standard-conforming simple @acronym{HTML}, 98 | PostScript or @acronym{PDF} designed for human modification. Examples 99 | of transparent image formats include @acronym{PNG}, @acronym{XCF} and 100 | @acronym{JPG}. Opaque formats include proprietary formats that can be 101 | read and edited only by proprietary word processors, @acronym{SGML} or 102 | @acronym{XML} for which the @acronym{DTD} and/or processing tools are 103 | not generally available, and the machine-generated @acronym{HTML}, 104 | PostScript or @acronym{PDF} produced by some word processors for 105 | output purposes only. 106 | 107 | The ``Title Page'' means, for a printed book, the title page itself, 108 | plus such following pages as are needed to hold, legibly, the material 109 | this License requires to appear in the title page. For works in 110 | formats which do not have any title page as such, ``Title Page'' means 111 | the text near the most prominent appearance of the work's title, 112 | preceding the beginning of the body of the text. 113 | 114 | The ``publisher'' means any person or entity that distributes copies 115 | of the Document to the public. 116 | 117 | A section ``Entitled XYZ'' means a named subunit of the Document whose 118 | title either is precisely XYZ or contains XYZ in parentheses following 119 | text that translates XYZ in another language. (Here XYZ stands for a 120 | specific section name mentioned below, such as ``Acknowledgements'', 121 | ``Dedications'', ``Endorsements'', or ``History''.) To ``Preserve the Title'' 122 | of such a section when you modify the Document means that it remains a 123 | section ``Entitled XYZ'' according to this definition. 124 | 125 | The Document may include Warranty Disclaimers next to the notice which 126 | states that this License applies to the Document. These Warranty 127 | Disclaimers are considered to be included by reference in this 128 | License, but only as regards disclaiming warranties: any other 129 | implication that these Warranty Disclaimers may have is void and has 130 | no effect on the meaning of this License. 131 | 132 | @item 133 | VERBATIM COPYING 134 | 135 | You may copy and distribute the Document in any medium, either 136 | commercially or noncommercially, provided that this License, the 137 | copyright notices, and the license notice saying this License applies 138 | to the Document are reproduced in all copies, and that you add no other 139 | conditions whatsoever to those of this License. You may not use 140 | technical measures to obstruct or control the reading or further 141 | copying of the copies you make or distribute. However, you may accept 142 | compensation in exchange for copies. If you distribute a large enough 143 | number of copies you must also follow the conditions in section 3. 144 | 145 | You may also lend copies, under the same conditions stated above, and 146 | you may publicly display copies. 147 | 148 | @item 149 | COPYING IN QUANTITY 150 | 151 | If you publish printed copies (or copies in media that commonly have 152 | printed covers) of the Document, numbering more than 100, and the 153 | Document's license notice requires Cover Texts, you must enclose the 154 | copies in covers that carry, clearly and legibly, all these Cover 155 | Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on 156 | the back cover. Both covers must also clearly and legibly identify 157 | you as the publisher of these copies. The front cover must present 158 | the full title with all words of the title equally prominent and 159 | visible. You may add other material on the covers in addition. 160 | Copying with changes limited to the covers, as long as they preserve 161 | the title of the Document and satisfy these conditions, can be treated 162 | as verbatim copying in other respects. 163 | 164 | If the required texts for either cover are too voluminous to fit 165 | legibly, you should put the first ones listed (as many as fit 166 | reasonably) on the actual cover, and continue the rest onto adjacent 167 | pages. 168 | 169 | If you publish or distribute Opaque copies of the Document numbering 170 | more than 100, you must either include a machine-readable Transparent 171 | copy along with each Opaque copy, or state in or with each Opaque copy 172 | a computer-network location from which the general network-using 173 | public has access to download using public-standard network protocols 174 | a complete Transparent copy of the Document, free of added material. 175 | If you use the latter option, you must take reasonably prudent steps, 176 | when you begin distribution of Opaque copies in quantity, to ensure 177 | that this Transparent copy will remain thus accessible at the stated 178 | location until at least one year after the last time you distribute an 179 | Opaque copy (directly or through your agents or retailers) of that 180 | edition to the public. 181 | 182 | It is requested, but not required, that you contact the authors of the 183 | Document well before redistributing any large number of copies, to give 184 | them a chance to provide you with an updated version of the Document. 185 | 186 | @item 187 | MODIFICATIONS 188 | 189 | You may copy and distribute a Modified Version of the Document under 190 | the conditions of sections 2 and 3 above, provided that you release 191 | the Modified Version under precisely this License, with the Modified 192 | Version filling the role of the Document, thus licensing distribution 193 | and modification of the Modified Version to whoever possesses a copy 194 | of it. In addition, you must do these things in the Modified Version: 195 | 196 | @enumerate A 197 | @item 198 | Use in the Title Page (and on the covers, if any) a title distinct 199 | from that of the Document, and from those of previous versions 200 | (which should, if there were any, be listed in the History section 201 | of the Document). You may use the same title as a previous version 202 | if the original publisher of that version gives permission. 203 | 204 | @item 205 | List on the Title Page, as authors, one or more persons or entities 206 | responsible for authorship of the modifications in the Modified 207 | Version, together with at least five of the principal authors of the 208 | Document (all of its principal authors, if it has fewer than five), 209 | unless they release you from this requirement. 210 | 211 | @item 212 | State on the Title page the name of the publisher of the 213 | Modified Version, as the publisher. 214 | 215 | @item 216 | Preserve all the copyright notices of the Document. 217 | 218 | @item 219 | Add an appropriate copyright notice for your modifications 220 | adjacent to the other copyright notices. 221 | 222 | @item 223 | Include, immediately after the copyright notices, a license notice 224 | giving the public permission to use the Modified Version under the 225 | terms of this License, in the form shown in the Addendum below. 226 | 227 | @item 228 | Preserve in that license notice the full lists of Invariant Sections 229 | and required Cover Texts given in the Document's license notice. 230 | 231 | @item 232 | Include an unaltered copy of this License. 233 | 234 | @item 235 | Preserve the section Entitled ``History'', Preserve its Title, and add 236 | to it an item stating at least the title, year, new authors, and 237 | publisher of the Modified Version as given on the Title Page. If 238 | there is no section Entitled ``History'' in the Document, create one 239 | stating the title, year, authors, and publisher of the Document as 240 | given on its Title Page, then add an item describing the Modified 241 | Version as stated in the previous sentence. 242 | 243 | @item 244 | Preserve the network location, if any, given in the Document for 245 | public access to a Transparent copy of the Document, and likewise 246 | the network locations given in the Document for previous versions 247 | it was based on. These may be placed in the ``History'' section. 248 | You may omit a network location for a work that was published at 249 | least four years before the Document itself, or if the original 250 | publisher of the version it refers to gives permission. 251 | 252 | @item 253 | For any section Entitled ``Acknowledgements'' or ``Dedications'', Preserve 254 | the Title of the section, and preserve in the section all the 255 | substance and tone of each of the contributor acknowledgements and/or 256 | dedications given therein. 257 | 258 | @item 259 | Preserve all the Invariant Sections of the Document, 260 | unaltered in their text and in their titles. Section numbers 261 | or the equivalent are not considered part of the section titles. 262 | 263 | @item 264 | Delete any section Entitled ``Endorsements''. Such a section 265 | may not be included in the Modified Version. 266 | 267 | @item 268 | Do not retitle any existing section to be Entitled ``Endorsements'' or 269 | to conflict in title with any Invariant Section. 270 | 271 | @item 272 | Preserve any Warranty Disclaimers. 273 | @end enumerate 274 | 275 | If the Modified Version includes new front-matter sections or 276 | appendices that qualify as Secondary Sections and contain no material 277 | copied from the Document, you may at your option designate some or all 278 | of these sections as invariant. To do this, add their titles to the 279 | list of Invariant Sections in the Modified Version's license notice. 280 | These titles must be distinct from any other section titles. 281 | 282 | You may add a section Entitled ``Endorsements'', provided it contains 283 | nothing but endorsements of your Modified Version by various 284 | parties---for example, statements of peer review or that the text has 285 | been approved by an organization as the authoritative definition of a 286 | standard. 287 | 288 | You may add a passage of up to five words as a Front-Cover Text, and a 289 | passage of up to 25 words as a Back-Cover Text, to the end of the list 290 | of Cover Texts in the Modified Version. Only one passage of 291 | Front-Cover Text and one of Back-Cover Text may be added by (or 292 | through arrangements made by) any one entity. If the Document already 293 | includes a cover text for the same cover, previously added by you or 294 | by arrangement made by the same entity you are acting on behalf of, 295 | you may not add another; but you may replace the old one, on explicit 296 | permission from the previous publisher that added the old one. 297 | 298 | The author(s) and publisher(s) of the Document do not by this License 299 | give permission to use their names for publicity for or to assert or 300 | imply endorsement of any Modified Version. 301 | 302 | @item 303 | COMBINING DOCUMENTS 304 | 305 | You may combine the Document with other documents released under this 306 | License, under the terms defined in section 4 above for modified 307 | versions, provided that you include in the combination all of the 308 | Invariant Sections of all of the original documents, unmodified, and 309 | list them all as Invariant Sections of your combined work in its 310 | license notice, and that you preserve all their Warranty Disclaimers. 311 | 312 | The combined work need only contain one copy of this License, and 313 | multiple identical Invariant Sections may be replaced with a single 314 | copy. If there are multiple Invariant Sections with the same name but 315 | different contents, make the title of each such section unique by 316 | adding at the end of it, in parentheses, the name of the original 317 | author or publisher of that section if known, or else a unique number. 318 | Make the same adjustment to the section titles in the list of 319 | Invariant Sections in the license notice of the combined work. 320 | 321 | In the combination, you must combine any sections Entitled ``History'' 322 | in the various original documents, forming one section Entitled 323 | ``History''; likewise combine any sections Entitled ``Acknowledgements'', 324 | and any sections Entitled ``Dedications''. You must delete all 325 | sections Entitled ``Endorsements.'' 326 | 327 | @item 328 | COLLECTIONS OF DOCUMENTS 329 | 330 | You may make a collection consisting of the Document and other documents 331 | released under this License, and replace the individual copies of this 332 | License in the various documents with a single copy that is included in 333 | the collection, provided that you follow the rules of this License for 334 | verbatim copying of each of the documents in all other respects. 335 | 336 | You may extract a single document from such a collection, and distribute 337 | it individually under this License, provided you insert a copy of this 338 | License into the extracted document, and follow this License in all 339 | other respects regarding verbatim copying of that document. 340 | 341 | @item 342 | AGGREGATION WITH INDEPENDENT WORKS 343 | 344 | A compilation of the Document or its derivatives with other separate 345 | and independent documents or works, in or on a volume of a storage or 346 | distribution medium, is called an ``aggregate'' if the copyright 347 | resulting from the compilation is not used to limit the legal rights 348 | of the compilation's users beyond what the individual works permit. 349 | When the Document is included in an aggregate, this License does not 350 | apply to the other works in the aggregate which are not themselves 351 | derivative works of the Document. 352 | 353 | If the Cover Text requirement of section 3 is applicable to these 354 | copies of the Document, then if the Document is less than one half of 355 | the entire aggregate, the Document's Cover Texts may be placed on 356 | covers that bracket the Document within the aggregate, or the 357 | electronic equivalent of covers if the Document is in electronic form. 358 | Otherwise they must appear on printed covers that bracket the whole 359 | aggregate. 360 | 361 | @item 362 | TRANSLATION 363 | 364 | Translation is considered a kind of modification, so you may 365 | distribute translations of the Document under the terms of section 4. 366 | Replacing Invariant Sections with translations requires special 367 | permission from their copyright holders, but you may include 368 | translations of some or all Invariant Sections in addition to the 369 | original versions of these Invariant Sections. You may include a 370 | translation of this License, and all the license notices in the 371 | Document, and any Warranty Disclaimers, provided that you also include 372 | the original English version of this License and the original versions 373 | of those notices and disclaimers. In case of a disagreement between 374 | the translation and the original version of this License or a notice 375 | or disclaimer, the original version will prevail. 376 | 377 | If a section in the Document is Entitled ``Acknowledgements'', 378 | ``Dedications'', or ``History'', the requirement (section 4) to Preserve 379 | its Title (section 1) will typically require changing the actual 380 | title. 381 | 382 | @item 383 | TERMINATION 384 | 385 | You may not copy, modify, sublicense, or distribute the Document 386 | except as expressly provided under this License. Any attempt 387 | otherwise to copy, modify, sublicense, or distribute it is void, and 388 | will automatically terminate your rights under this License. 389 | 390 | However, if you cease all violation of this License, then your license 391 | from a particular copyright holder is reinstated (a) provisionally, 392 | unless and until the copyright holder explicitly and finally 393 | terminates your license, and (b) permanently, if the copyright holder 394 | fails to notify you of the violation by some reasonable means prior to 395 | 60 days after the cessation. 396 | 397 | Moreover, your license from a particular copyright holder is 398 | reinstated permanently if the copyright holder notifies you of the 399 | violation by some reasonable means, this is the first time you have 400 | received notice of violation of this License (for any work) from that 401 | copyright holder, and you cure the violation prior to 30 days after 402 | your receipt of the notice. 403 | 404 | Termination of your rights under this section does not terminate the 405 | licenses of parties who have received copies or rights from you under 406 | this License. If your rights have been terminated and not permanently 407 | reinstated, receipt of a copy of some or all of the same material does 408 | not give you any rights to use it. 409 | 410 | @item 411 | FUTURE REVISIONS OF THIS LICENSE 412 | 413 | The Free Software Foundation may publish new, revised versions 414 | of the GNU Free Documentation License from time to time. Such new 415 | versions will be similar in spirit to the present version, but may 416 | differ in detail to address new problems or concerns. See 417 | @uref{http://www.gnu.org/copyleft/}. 418 | 419 | Each version of the License is given a distinguishing version number. 420 | If the Document specifies that a particular numbered version of this 421 | License ``or any later version'' applies to it, you have the option of 422 | following the terms and conditions either of that specified version or 423 | of any later version that has been published (not as a draft) by the 424 | Free Software Foundation. If the Document does not specify a version 425 | number of this License, you may choose any version ever published (not 426 | as a draft) by the Free Software Foundation. If the Document 427 | specifies that a proxy can decide which future versions of this 428 | License can be used, that proxy's public statement of acceptance of a 429 | version permanently authorizes you to choose that version for the 430 | Document. 431 | 432 | @item 433 | RELICENSING 434 | 435 | ``Massive Multiauthor Collaboration Site'' (or ``MMC Site'') means any 436 | World Wide Web server that publishes copyrightable works and also 437 | provides prominent facilities for anybody to edit those works. A 438 | public wiki that anybody can edit is an example of such a server. A 439 | ``Massive Multiauthor Collaboration'' (or ``MMC'') contained in the 440 | site means any set of copyrightable works thus published on the MMC 441 | site. 442 | 443 | ``CC-BY-SA'' means the Creative Commons Attribution-Share Alike 3.0 444 | license published by Creative Commons Corporation, a not-for-profit 445 | corporation with a principal place of business in San Francisco, 446 | California, as well as future copyleft versions of that license 447 | published by that same organization. 448 | 449 | ``Incorporate'' means to publish or republish a Document, in whole or 450 | in part, as part of another Document. 451 | 452 | An MMC is ``eligible for relicensing'' if it is licensed under this 453 | License, and if all works that were first published under this License 454 | somewhere other than this MMC, and subsequently incorporated in whole 455 | or in part into the MMC, (1) had no cover texts or invariant sections, 456 | and (2) were thus incorporated prior to November 1, 2008. 457 | 458 | The operator of an MMC Site may republish an MMC contained in the site 459 | under CC-BY-SA on the same site at any time before August 1, 2009, 460 | provided the MMC is eligible for relicensing. 461 | 462 | @end enumerate 463 | 464 | @page 465 | @heading ADDENDUM: How to use this License for your documents 466 | 467 | To use this License in a document you have written, include a copy of 468 | the License in the document and put the following copyright and 469 | license notices just after the title page: 470 | 471 | @smallexample 472 | @group 473 | Copyright (C) @var{year} @var{your name}. 474 | Permission is granted to copy, distribute and/or modify this document 475 | under the terms of the GNU Free Documentation License, Version 1.3 476 | or any later version published by the Free Software Foundation; 477 | with no Invariant Sections, no Front-Cover Texts, and no Back-Cover 478 | Texts. A copy of the license is included in the section entitled ``GNU 479 | Free Documentation License''. 480 | @end group 481 | @end smallexample 482 | 483 | If you have Invariant Sections, Front-Cover Texts and Back-Cover Texts, 484 | replace the ``with@dots{}Texts.'' line with this: 485 | 486 | @smallexample 487 | @group 488 | with the Invariant Sections being @var{list their titles}, with 489 | the Front-Cover Texts being @var{list}, and with the Back-Cover Texts 490 | being @var{list}. 491 | @end group 492 | @end smallexample 493 | 494 | If you have Invariant Sections without Cover Texts, or some other 495 | combination of the three, merge those two alternatives to suit the 496 | situation. 497 | 498 | If your document contains nontrivial examples of program code, we 499 | recommend releasing these examples in parallel under your choice of 500 | free software license, such as the GNU General Public License, 501 | to permit their use in free software. 502 | 503 | @c Local Variables: 504 | @c ispell-local-pdict: "ispell-dict" 505 | @c End: 506 | 507 | -------------------------------------------------------------------------------- /examples/15PUZZLE: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danirod/chip8/1fc16795d99f7249d017c4e3c3639b54a28d8caf/examples/15PUZZLE -------------------------------------------------------------------------------- /examples/BLINKY: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danirod/chip8/1fc16795d99f7249d017c4e3c3639b54a28d8caf/examples/BLINKY -------------------------------------------------------------------------------- /examples/BLITZ: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danirod/chip8/1fc16795d99f7249d017c4e3c3639b54a28d8caf/examples/BLITZ -------------------------------------------------------------------------------- /examples/BRIX: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danirod/chip8/1fc16795d99f7249d017c4e3c3639b54a28d8caf/examples/BRIX -------------------------------------------------------------------------------- /examples/CONNECT4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danirod/chip8/1fc16795d99f7249d017c4e3c3639b54a28d8caf/examples/CONNECT4 -------------------------------------------------------------------------------- /examples/GUESS: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danirod/chip8/1fc16795d99f7249d017c4e3c3639b54a28d8caf/examples/GUESS -------------------------------------------------------------------------------- /examples/HIDDEN: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danirod/chip8/1fc16795d99f7249d017c4e3c3639b54a28d8caf/examples/HIDDEN -------------------------------------------------------------------------------- /examples/INVADERS: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danirod/chip8/1fc16795d99f7249d017c4e3c3639b54a28d8caf/examples/INVADERS -------------------------------------------------------------------------------- /examples/KALEID: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danirod/chip8/1fc16795d99f7249d017c4e3c3639b54a28d8caf/examples/KALEID -------------------------------------------------------------------------------- /examples/MAZE: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danirod/chip8/1fc16795d99f7249d017c4e3c3639b54a28d8caf/examples/MAZE -------------------------------------------------------------------------------- /examples/MERLIN: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danirod/chip8/1fc16795d99f7249d017c4e3c3639b54a28d8caf/examples/MERLIN -------------------------------------------------------------------------------- /examples/MISSILE: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danirod/chip8/1fc16795d99f7249d017c4e3c3639b54a28d8caf/examples/MISSILE -------------------------------------------------------------------------------- /examples/PONG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danirod/chip8/1fc16795d99f7249d017c4e3c3639b54a28d8caf/examples/PONG -------------------------------------------------------------------------------- /examples/PONG2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danirod/chip8/1fc16795d99f7249d017c4e3c3639b54a28d8caf/examples/PONG2 -------------------------------------------------------------------------------- /examples/PUZZLE: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danirod/chip8/1fc16795d99f7249d017c4e3c3639b54a28d8caf/examples/PUZZLE -------------------------------------------------------------------------------- /examples/SYZYGY: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danirod/chip8/1fc16795d99f7249d017c4e3c3639b54a28d8caf/examples/SYZYGY -------------------------------------------------------------------------------- /examples/TANK: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danirod/chip8/1fc16795d99f7249d017c4e3c3639b54a28d8caf/examples/TANK -------------------------------------------------------------------------------- /examples/TETRIS: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danirod/chip8/1fc16795d99f7249d017c4e3c3639b54a28d8caf/examples/TETRIS -------------------------------------------------------------------------------- /examples/TICTAC: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danirod/chip8/1fc16795d99f7249d017c4e3c3639b54a28d8caf/examples/TICTAC -------------------------------------------------------------------------------- /examples/UFO: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danirod/chip8/1fc16795d99f7249d017c4e3c3639b54a28d8caf/examples/UFO -------------------------------------------------------------------------------- /examples/VBRIX: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danirod/chip8/1fc16795d99f7249d017c4e3c3639b54a28d8caf/examples/VBRIX -------------------------------------------------------------------------------- /examples/VERS: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danirod/chip8/1fc16795d99f7249d017c4e3c3639b54a28d8caf/examples/VERS -------------------------------------------------------------------------------- /examples/WIPEOFF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danirod/chip8/1fc16795d99f7249d017c4e3c3639b54a28d8caf/examples/WIPEOFF -------------------------------------------------------------------------------- /src/Makefile.am: -------------------------------------------------------------------------------- 1 | SUBDIRS = lib8 chip8 -------------------------------------------------------------------------------- /src/chip8/Makefile.am: -------------------------------------------------------------------------------- 1 | # This Makefile builds the CHIP-8 emulator. 2 | 3 | bin_PROGRAMS = chip8 4 | chip8_SOURCES = chip8.c libsdl.c libsdl.h 5 | chip8_CFLAGS = -I$(top_srcdir)/src @SDL_CFLAGS@ -std=c99 -Wall 6 | chip8_LDADD = $(top_srcdir)/src/lib8/lib8.a @SDL_LIBS@ 7 | dist_man_MANS = chip8.1 8 | -------------------------------------------------------------------------------- /src/chip8/chip8.1: -------------------------------------------------------------------------------- 1 | .TH chip8 6 2 | 3 | .SH NAME 4 | chip8 \- CHIP-8 emulator 5 | 6 | .SH SYNOPSIS 7 | .B chip8 8 | [\fB\-h\fR | \fB\-\-help\fR] 9 | [\fB\-v\fR | \fB\-\-version\fR] 10 | [\fB\-\-hex\fR] 11 | [\fB\-\-mute\fR] 12 | .IR file ... 13 | 14 | .SH DESCRIPTION 15 | .B chip8 16 | is another implementation of the CHIP-8 interpreted language and virtual 17 | machine. It is designed as a modular application, is small, lightweight, 18 | fast and multiplatform. 19 | 20 | When the application is started, the ROM data contained in the file 21 | .IR file 22 | is loaded and then executed by the emulated virtual machine. 23 | 24 | .SH OPTIONS 25 | .TP 26 | .B \-h ", " \-\-help 27 | Shows the help message listing possible flags for the program. 28 | 29 | .TP 30 | .B \-v ", " \-\-version 31 | Shows the installed version of the emulator. 32 | 33 | .TP 34 | .B \-\-hex 35 | If provided, will consider the given file as an 36 | .B hexadecimal file 37 | instead of a binary file. An 38 | .B hexadecimal file 39 | is only composed of hexadecimal characters encoded in a human encoding format 40 | such as US-ASCII or UTF-8, as an example the digits 0-9 and the letters A-F. 41 | The emulator won't distinct between uppercase A-F and lowercase a-f although 42 | the former is preferred. See below on section 43 | .B ROMs 44 | for more information on that. 45 | 46 | .TP 47 | .B \-\-mute 48 | If provided, the emulator won't make any sound, which is useful for people 49 | who don't want to play beeper sounds. 50 | 51 | .SH ROMs 52 | This emulator is compatible with CHIP-8 and SCHIP ROMs. A ROM is a file that 53 | contains the opcodes that the virtual machine will run. There are two types of 54 | ROMs: 55 | .B binary ROMs 56 | and 57 | .B hexadecimal ROMs\fR. 58 | 59 | By default ROM files are expected to be binary ROMs. 60 | .B Binary ROMs 61 | contains the program encoded as binary data. This means that every two bytes 62 | in the ROM file will be mapped to a particular opcode executed by the virtual 63 | machine (or to data used by some opcodes such as sprites). This is the default 64 | behaviour since it produces the smallest possible files, the fastest to load 65 | and the easiest to obtain (probably coming from a dump for an actual ROM chip). 66 | 67 | On the other side, 68 | .B hexadecimal ROMs 69 | are actually 70 | .B text files 71 | encoded as ASCII or UTF-8 where every character represents an hexadecimal 72 | character such as 0-9 or A-F. Note that because the file is encoded as ASCII 73 | or UTF-8 and because the characters are only hexadecimal to the human, the 74 | actual bytes stored in the file should always be in the range 0x30-0x39 (for 75 | \fI0\fR\-\fI9\fR characters), 0x41-0x46 (for \fIA\fR\-\fIF\fR characters) or 76 | 0x61-0x66 (for \fIa\fR\-\fIf\fR characters). When the ROM is loaded, each two 77 | ASCII characters found in the file are converted to a single byte by 78 | translating the hexadecimal representation into actual bits. As an example, if 79 | the sequence \fI6F\fR is read (0x36 0x46), the emulator translates it to the 80 | byte 0x6F in the virtual memory. Needless to say, this method requires more 81 | time to start up the emulation since the ROM needs to be translated to binary, 82 | although it will probably be the easiest to use for newcomers. 83 | 84 | .SH BUGS 85 | In case you find a bug, you can file it at our official issue tracker. The 86 | canonical URL for our issue tracker is 87 | <\fIhttp://github.com/danirod/chip8/issues\fR>. 88 | 89 | .SH AUTHORS 90 | This package is developed and maintained by 91 | Dani Rodriguez (<\fIdanirod@outlook.com\fR>). 92 | 93 | .SH COPYRIGHT 94 | Copyright (C) 2015-2016 Dani Rodriguez 95 | -------------------------------------------------------------------------------- /src/chip8/chip8.c: -------------------------------------------------------------------------------- 1 | /* 2 | * chip8 is a CHIP-8 emulator done in C 3 | * Copyright (C) 2015-2016 Dani Rodríguez 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #include 20 | #include "libsdl.h" 21 | #include 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | /* Flag set by '--hex' */ 30 | static int use_hexloader; 31 | 32 | /* Flag set by '--mute' */ 33 | static int use_mute; 34 | 35 | /* Flag used by '--debug' */ 36 | static int use_debug; 37 | 38 | /* Opcodes to execute per frame. */ 39 | static int speed = 16; 40 | 41 | /* getopt parameter structure. */ 42 | static struct option long_options[] = { 43 | { "help", no_argument, 0, 'h' }, 44 | { "version", no_argument, 0, 'v' }, 45 | { "hex", no_argument, &use_hexloader, 1 }, 46 | { "mute", no_argument, &use_mute, 1 }, 47 | { "debug", no_argument, &use_debug, 1 }, 48 | { "speed", required_argument, 0, 's' }, 49 | { 0, 0, 0, 0 } 50 | }; 51 | 52 | /** 53 | * Print usage. In case you use bad arguments, this will be printed. 54 | * @param name how is the program named, usually argv[0]. 55 | */ 56 | static void 57 | usage(const char* name) 58 | { 59 | /* How many characters has Usage: %s? */ 60 | int pad = strnlen(name, 10) + 7; // 7 = "Usage: " 61 | 62 | printf("Usage: %s [-h | --help] [-v | --version]\n", name); 63 | printf("%*c [--hex] [--mute] \n", pad, ' '); 64 | } 65 | 66 | static char 67 | hex_to_bin(char hex) 68 | { 69 | if (hex >= '0' && hex <= '9') 70 | return hex - '0'; 71 | hex &= 0xDF; 72 | if (hex >= 'A' && hex <= 'F') 73 | return 10 + (hex - 'A'); 74 | return -1; 75 | } 76 | 77 | /** 78 | * Load a hex file. 79 | * 80 | * @param file file path. 81 | * @param machine data structure to load the HEX into. 82 | */ 83 | static int 84 | load_hex(const char* file, struct machine_t* machine) 85 | { 86 | FILE* fp = fopen(file, "rb"); 87 | if (fp == NULL) { 88 | fprintf(stderr, "Cannot open ROM file.\n"); 89 | return 1; 90 | } 91 | 92 | // Use the fseek/ftell/fseek trick to retrieve file size. 93 | fseek(fp, 0, SEEK_END); 94 | int length = ftell(fp); 95 | fseek(fp, 0, SEEK_SET); 96 | 97 | // Create a temporal buffer where to store the data. 98 | char* hexfile = malloc(length); 99 | if (hexfile == NULL) { 100 | return 1; 101 | } 102 | fread(hexfile, length, 1, fp); 103 | fclose(fp); 104 | 105 | int mempos = 0x200; 106 | if (length & 0x01) length--; 107 | for (int i = 0; i < length; i += 2) 108 | { 109 | char hi = hex_to_bin(hexfile[i]); 110 | char lo = hex_to_bin(hexfile[i + 1]); 111 | if (hi == -1 || lo == -1) { 112 | free(hexfile); 113 | return 1; 114 | } 115 | 116 | machine->mem[mempos++] = hi << 4 | lo; 117 | if (mempos > 0xFFF) 118 | break; 119 | } 120 | 121 | free(hexfile); 122 | return 0; 123 | } 124 | 125 | /** 126 | * Load a ROM into a machine. This function will open a file and load its 127 | * contents into the memory from the provided machine data structure. 128 | * In compliance with the specification, ROM data will start at 0x200. 129 | * 130 | * @param file file path. 131 | * @param machine machine data structure to load the ROM into. 132 | */ 133 | static int 134 | load_rom(const char* file, struct machine_t* machine) 135 | { 136 | FILE* fp = fopen(file, "rb"); 137 | if (fp == NULL) { 138 | fprintf(stderr, "Cannot open ROM file.\n"); 139 | return 1; 140 | } 141 | 142 | // Use the fseek/ftell/fseek trick to retrieve file size. 143 | fseek(fp, 0, SEEK_END); 144 | int length = ftell(fp); 145 | fseek(fp, 0, SEEK_SET); 146 | 147 | // Check the length of the rom. Must be as much 3584 bytes long, which 148 | // is 4096 - 512. Since first 512 bytes of memory are reserved, program 149 | // code can only allocate up to 3584 bytes. Must check for bounds in 150 | // order to avoid buffer overflows. 151 | if (length > 3584) { 152 | fprintf(stderr, "ROM too large.\n"); 153 | return 1; 154 | } 155 | 156 | // Everything is OK, read the ROM. 157 | fread(machine->mem + 0x200, length, 1, fp); 158 | fclose(fp); 159 | return 0; 160 | } 161 | 162 | static int 163 | load_data(char* file, struct machine_t* mac) 164 | { 165 | if (use_hexloader == 0) { 166 | return load_rom(file, mac); 167 | } else { 168 | return load_hex(file, mac); 169 | } 170 | } 171 | 172 | int 173 | main(int argc, char** argv) 174 | { 175 | struct machine_t mac; 176 | 177 | /* Parse parameters */ 178 | int indexptr, c; 179 | while ((c = getopt_long(argc, argv, "hs:v", long_options, &indexptr)) 180 | != -1) { 181 | switch (c) { 182 | case 'h': 183 | usage(argv[0]); 184 | exit(0); 185 | case 's': 186 | if (optarg) { 187 | speed = atoi(optarg); 188 | } 189 | if (speed <= 0) { 190 | fprintf( 191 | stderr, 192 | "Invalid speed value: must be a positive number\n"); 193 | exit(1); 194 | } 195 | break; 196 | case 'v': 197 | printf("%s\n", PACKAGE_STRING); 198 | exit(0); 199 | break; 200 | case 0: 201 | /* A long option is being processed, probably --hex. */ 202 | break; 203 | default: 204 | exit(1); 205 | } 206 | } 207 | 208 | /* 209 | * optind should have the index of next parameter in argv. It should be 210 | * the name of the file to read. Therefore, should complain if file is not 211 | * given. 212 | */ 213 | if (optind >= argc) { 214 | fprintf(stderr, "%1$s: no file given. '%1$s -h' for help.\n", argv[0]); 215 | exit(1); 216 | } 217 | 218 | printf("CHIP-8 emulator\n"); 219 | printf("Speed emulation: %d\n", speed); 220 | 221 | /* Initialize SDL Context. */ 222 | if (init_context()) { 223 | fprintf(stderr, "Error initializing SDL graphical context:\n"); 224 | fprintf(stderr, "%s\n", SDL_GetError()); 225 | return 1; 226 | } 227 | if (!try_enable_sound()) { 228 | fprintf(stderr, "Couldn't enable sound.\n"); 229 | use_mute = 1; 230 | } 231 | 232 | /* Init emulator. */ 233 | srand(time(NULL)); 234 | if (use_debug) { 235 | set_debug_mode(1); 236 | } 237 | init_machine(&mac); 238 | mac.keydown = &is_key_down; 239 | if (!use_mute) { 240 | mac.speaker = &update_speaker; 241 | } 242 | load_data(argv[optind], &mac); 243 | 244 | int last_ticks = SDL_GetTicks(); 245 | int last_delta = 0; 246 | while (!is_close_requested()) { 247 | /* Update timers. */ 248 | last_delta = SDL_GetTicks() - last_ticks; 249 | last_ticks = SDL_GetTicks(); 250 | 251 | /* Update computer. */ 252 | update_time(&mac, last_delta); 253 | for (int i = 0; i < speed; i++) { 254 | step_machine(&mac); 255 | } 256 | 257 | /* Render computer. */ 258 | render_display(&mac); 259 | 260 | /* 261 | * To render at 60 Hz, you must render a frame each 16.6 ms. 262 | * If it took less than 16.6 ms, you can afford sleep some 263 | * time. 1 ms will always be slept to keep the CPU cold. 264 | */ 265 | int render_time = SDL_GetTicks() - last_ticks; 266 | if (render_time < 16) { 267 | /* We can sleep :) */ 268 | SDL_Delay(16 - render_time); 269 | } else { 270 | /* We should not sleep, but we will do because the CPU will 271 | * otherwise explode. */ 272 | SDL_Delay(1); 273 | } 274 | } 275 | 276 | /* Dispose SDL context. */ 277 | destroy_context(); 278 | 279 | return 0; 280 | } 281 | -------------------------------------------------------------------------------- /src/chip8/libsdl.c: -------------------------------------------------------------------------------- 1 | /* 2 | * chip8 is a CHIP-8 emulator done in C 3 | * Copyright (C) 2015-2016 Dani Rodríguez 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | 20 | #include "libsdl.h" 21 | 22 | #include 23 | #include 24 | 25 | /** 26 | * This array maps a SDL scancode to the CHIP-8 key that serves as index 27 | * for the array. For instance, pressing key keys[5] on your keyboard will 28 | * make your CHIP-8 understand as if you pressed the [5] key. Remember that 29 | * CHIP-8 uses a 16-key keyboard with keys labeled 0..F. 30 | */ 31 | char keys[] = { 32 | SDL_SCANCODE_X, // 0 33 | SDL_SCANCODE_1, // 1 34 | SDL_SCANCODE_2, // 2 35 | SDL_SCANCODE_3, // 3 36 | SDL_SCANCODE_Q, // 4 37 | SDL_SCANCODE_W, // 5 38 | SDL_SCANCODE_E, // 6 39 | SDL_SCANCODE_A, // 7 40 | SDL_SCANCODE_S, // 8 41 | SDL_SCANCODE_D, // 9 42 | SDL_SCANCODE_Z, // A 43 | SDL_SCANCODE_C, // B 44 | SDL_SCANCODE_4, // C 45 | SDL_SCANCODE_R, // D 46 | SDL_SCANCODE_F, // E 47 | SDL_SCANCODE_V // F 48 | }; 49 | 50 | /** 51 | * This is a private structure used for holding information about audio. 52 | * I need to create the structure becuase the feeding function for audio 53 | * in SDL only allows one single parameter to be provided via user data. 54 | * This little trick lets me pass more than one variable. 55 | */ 56 | struct audiodata_t 57 | { 58 | float tone_pos; 59 | float tone_inc; 60 | }; 61 | 62 | static SDL_Window* window = NULL; 63 | 64 | static SDL_Renderer* renderer = NULL; 65 | 66 | static SDL_Texture* texture = NULL; 67 | 68 | static SDL_AudioDeviceID device = 0; 69 | 70 | static SDL_AudioSpec* spec = NULL; 71 | 72 | /** 73 | * This is the function that generates the beep noise heard in the emulator. 74 | * It generates RAW PCM values that are written to the stream. This is fast 75 | * and has no dependencies on external files. 76 | */ 77 | static void 78 | feed(void* udata, Uint8* stream, int len) 79 | { 80 | struct audiodata_t* audio = (struct audiodata_t *) udata; 81 | for (int i = 0; i < len; i++) { 82 | stream[i] = sinf(audio->tone_pos) + 127; 83 | audio->tone_pos += audio->tone_inc; 84 | } 85 | } 86 | 87 | /** 88 | * Generate an audiospec data structure ready to be used by SDL Audio. 89 | * It would be a nice idea if the sampling frequency and buffer size could 90 | * be provided as an input instead of being hardcoded, though. 91 | */ 92 | static SDL_AudioSpec* 93 | init_audiospec(void) 94 | { 95 | /* Initialize user data structure. */ 96 | struct audiodata_t* audio = malloc(sizeof(struct audiodata_t)); 97 | audio->tone_pos = 0; 98 | audio->tone_inc = 2 * 3.14159 * 1000 / 44100; 99 | 100 | /* Set up the audiospec data structure required by SDL. */ 101 | spec = (SDL_AudioSpec *) malloc(sizeof(SDL_AudioSpec)); 102 | spec->freq = 44100; 103 | spec->format = AUDIO_U8; 104 | spec->channels = 1; 105 | spec->samples = 4096; 106 | spec->callback = *feed; 107 | spec->userdata = audio; 108 | return spec; 109 | } 110 | 111 | static void 112 | clean_up() 113 | { 114 | if (device != 0) { 115 | SDL_CloseAudioDevice(device); 116 | device = 0; 117 | } 118 | if (spec != NULL) { 119 | free(spec->userdata); 120 | free(spec); 121 | spec = NULL; 122 | } 123 | if (texture != NULL) { 124 | SDL_DestroyTexture(texture); 125 | texture = NULL; 126 | } 127 | if (renderer != NULL) { 128 | SDL_DestroyRenderer(renderer); 129 | renderer = NULL; 130 | } 131 | if (window != NULL) { 132 | SDL_DestroyWindow(window); 133 | window = NULL; 134 | } 135 | SDL_Quit(); 136 | } 137 | 138 | #define TEXTURE_PIXEL(x, y) (128 * (y) + (x)) 139 | 140 | static void 141 | expand_screen(char* from, Uint32* to, int use_hdpi) 142 | { 143 | if (use_hdpi) { 144 | for (int i = 0; i < 8192; i++) 145 | to[i] = from[i] ? -1 : 0; 146 | } else { 147 | int x = 0, y = 0; 148 | for (int i = 0; i < 2048; i++) { 149 | Uint32 val = from[i] ? -1 : 0; 150 | to[TEXTURE_PIXEL(2 * x + 0, 2 * y + 0)] = val; 151 | to[TEXTURE_PIXEL(2 * x + 1, 2 * y + 0)] = val; 152 | to[TEXTURE_PIXEL(2 * x + 0, 2 * y + 1)] = val; 153 | to[TEXTURE_PIXEL(2 * x + 1, 2 * y + 1)] = val; 154 | if (++x == 64) { 155 | x = 0; 156 | y++; 157 | } 158 | } 159 | } 160 | } 161 | 162 | int 163 | init_context() 164 | { 165 | if (SDL_Init(SDL_INIT_EVERYTHING)) { 166 | return 1; 167 | } 168 | window = SDL_CreateWindow("CHIP-8 Emulator", 169 | SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 170 | 640, 320, SDL_WINDOW_SHOWN); 171 | if (window == NULL) { 172 | clean_up(); 173 | return 1; 174 | } 175 | renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED); 176 | if (renderer == NULL) { 177 | clean_up(); 178 | return 1; 179 | } 180 | texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, 181 | SDL_TEXTUREACCESS_STREAMING, 128, 64); 182 | if (texture == NULL) { 183 | clean_up(); 184 | return 1; 185 | } 186 | return 0; 187 | } 188 | 189 | int 190 | try_enable_sound() 191 | { 192 | spec = init_audiospec(); 193 | device = SDL_OpenAudioDevice(NULL, 0, spec, 194 | NULL, SDL_AUDIO_ALLOW_FORMAT_CHANGE); 195 | return (device != 0); 196 | } 197 | 198 | void 199 | destroy_context() 200 | { 201 | clean_up(); 202 | } 203 | 204 | int 205 | is_close_requested() 206 | { 207 | SDL_Event ev; 208 | while (SDL_PollEvent(&ev)) { 209 | if (ev.type == SDL_QUIT) { 210 | return 1; 211 | } 212 | } 213 | return 0; 214 | } 215 | 216 | void 217 | render_display(struct machine_t* machine) 218 | { 219 | void* pixels; 220 | int pitch; 221 | 222 | /* Update SDL Texture with current data in CPU. */ 223 | SDL_LockTexture(texture, NULL, &pixels, &pitch); 224 | expand_screen(machine->screen, (Uint32 *) pixels, machine->esm); 225 | SDL_UnlockTexture(texture); 226 | 227 | /* Render the texture. */ 228 | SDL_RenderClear(renderer); 229 | SDL_RenderCopy(renderer, texture, NULL, NULL); 230 | SDL_RenderPresent(renderer); 231 | } 232 | 233 | /** 234 | * Checks if a given key is pressed. This function acceps a CHIP-8 key in 235 | * range 0-F. It will check using SDL if the PC keyboard mapped to that 236 | * CHIP-8 key is acutally being pressed or not. 237 | * 238 | * @param key CHIP-8 key to be checked. 239 | * @return 0 if that key is not down; != 0 if that key IS down. 240 | */ 241 | int 242 | is_key_down(char key) 243 | { 244 | const Uint8* sdl_keys; // SDL key array information 245 | Uint8 real_key; // Mapped SDL scancode for the given key 246 | if (key < 0 || key > 15) return 0; // check those bounds. 247 | 248 | sdl_keys = SDL_GetKeyboardState(NULL); 249 | real_key = keys[(int) key]; 250 | return sdl_keys[real_key]; 251 | } 252 | 253 | void 254 | update_speaker(int enabled) 255 | { 256 | if (enabled) { 257 | SDL_PauseAudioDevice(device, 0); 258 | } else { 259 | SDL_PauseAudioDevice(device, 1); 260 | } 261 | } 262 | -------------------------------------------------------------------------------- /src/chip8/libsdl.h: -------------------------------------------------------------------------------- 1 | /* 2 | * chip8 is a CHIP-8 emulator done in C 3 | * Copyright (C) 2015-2016 Dani Rodríguez 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #ifndef LIBSDL_H_ 20 | #define LIBSDL_H_ 21 | 22 | #include 23 | 24 | #include 25 | 26 | int init_context(); 27 | 28 | int try_enable_sound(); 29 | 30 | void destroy_context(); 31 | 32 | void render_display(struct machine_t* cpu); 33 | 34 | int is_close_requested(); 35 | 36 | int is_key_down(char); 37 | 38 | void update_speaker(int); 39 | 40 | #endif // LIBSDL_H_ 41 | -------------------------------------------------------------------------------- /src/lib8/Makefile.am: -------------------------------------------------------------------------------- 1 | # This Makefile builds lib8. 2 | 3 | noinst_LIBRARIES = lib8.a 4 | lib8_a_SOURCES = cpu.c cpu.h 5 | lib8_a_CFLAGS = -std=c99 -Wall -------------------------------------------------------------------------------- /src/lib8/cpu.c: -------------------------------------------------------------------------------- 1 | /* 2 | * chip8 is a CHIP-8 emulator done in C 3 | * Copyright (C) 2015-2016 Dani Rodríguez 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #include "cpu.h" 20 | #include 21 | #include 22 | #include 23 | 24 | #define OPCODE_NNN(opcode) (opcode & 0xFFF) 25 | #define OPCODE_KK(opcode) (opcode & 0xFF) 26 | #define OPCODE_N(opcode) (opcode & 0xF) 27 | #define OPCODE_X(opcode) ((opcode >> 8) & 0xF) 28 | #define OPCODE_Y(opcode) ((opcode >> 4) & 0xF) 29 | #define OPCODE_P(opcode) (opcode >> 12) 30 | 31 | static int is_debug = 0; 32 | 33 | static void 34 | log(const char* msg) 35 | { 36 | if (is_debug) { 37 | printf("MESSAGE: %s\n", msg); 38 | } 39 | } 40 | 41 | void 42 | set_debug_mode(int debug_mode) { 43 | is_debug = debug_mode; 44 | } 45 | 46 | /** 47 | * These are the bitmaps for the sprites that represent numbers. 48 | * This array should be memcopied to memory address 0x050. LD F, Vx 49 | * instruction sets I register to the memory address of a provided 50 | * number. 51 | */ 52 | static char hexcodes[] = { 53 | 0xF0, 0x90, 0x90, 0x90, 0xF0, // 0 54 | 0x20, 0x60, 0x20, 0x20, 0x70, // 1 55 | 0xF0, 0x10, 0xF0, 0x80, 0xF0, // 2 56 | 0xF0, 0x10, 0xF0, 0x10, 0xF0, // 3 57 | 0x90, 0x90, 0xF0, 0x10, 0x10, // 4 58 | 0xF0, 0x80, 0xF0, 0x10, 0xF0, // 5 59 | 0xF0, 0x80, 0xF0, 0x90, 0xF0, // 6 60 | 0xF0, 0x10, 0x20, 0x40, 0x40, // 7 61 | 0xF0, 0x90, 0xF0, 0x90, 0xF0, // 8 62 | 0xF0, 0x90, 0xF0, 0x10, 0xF0, // 9 63 | 0xF0, 0x90, 0xF0, 0x90, 0x90, // A 64 | 0xE0, 0x90, 0xE0, 0x90, 0xE0, // B 65 | 0xF0, 0x80, 0x80, 0x80, 0xF0, // C 66 | 0xE0, 0x90, 0x90, 0x90, 0xE0, // D 67 | 0xF0, 0x80, 0xF0, 0x80, 0xF0, // E 68 | 0xF0, 0x80, 0xF0, 0x80, 0x80 // F 69 | }; 70 | 71 | static int global_delta; 72 | 73 | typedef void (*opcode_table_t) (struct machine_t* cpu, word opcode); 74 | 75 | static void 76 | nibble_0(struct machine_t* cpu, word opcode) 77 | { 78 | if ((opcode & 0xFFF0) == 0x00c0) { 79 | /* 00CN: SCD - Scroll down. */ 80 | int rowsiz = cpu->esm ? 128 : 64; 81 | int colsiz = cpu->esm ? 64 : 32; 82 | int n = OPCODE_N(opcode); 83 | int start_row = 0, last_row = colsiz - n - 1; 84 | for (int row = last_row; row >= start_row; row--) { 85 | for (int x = 0; x < rowsiz; x++) { 86 | int from = row * rowsiz + x; 87 | int to = (row + n) * rowsiz + x; 88 | cpu->screen[to] = cpu->screen[from]; 89 | } 90 | } 91 | } else if (opcode == 0x00e0) { 92 | /* 00E0: CLS - Clear the screen. */ 93 | memset(cpu->screen, 0, 2048); 94 | } else if (opcode == 0x00ee) { 95 | /* 00EE: RET - Return from subroutine. */ 96 | if (cpu->sp > 0) 97 | cpu->pc = cpu->stack[(int) --cpu->sp]; 98 | /* TODO: Should throw an error on stack underflow. */ 99 | } else if (opcode == 0x00fb) { 100 | /* 00FB: SCR - Scroll 4 pixels to the right. */ 101 | int rowsiz = cpu->esm ? 128 : 64; 102 | int colsiz = cpu->esm ? 64 : 32; 103 | int start_col = 0, last_col = rowsiz - 4 - 1; 104 | for (int col = last_col; col >= start_col; col--) { 105 | for (int y = 0; y < colsiz; y++) { 106 | int from = y * rowsiz + col; 107 | int to = y * rowsiz + (4 + col); 108 | cpu->screen[to] = cpu->screen[from]; 109 | } 110 | } 111 | } else if (opcode == 0x00fc) { 112 | /* 00FC: SCL - Scroll 4 pixels to the left. */ 113 | int rowsiz = cpu->esm ? 128 : 64; 114 | int colsiz = cpu->esm ? 64 : 32; 115 | int start_col = 4, last_col = rowsiz - 1; 116 | for (int col = start_col; col <= last_col; col++) { 117 | for (int y = 0; y < colsiz; y++) { 118 | int from = y * rowsiz + col; 119 | int to = y * rowsiz + (col - 4); 120 | cpu->screen[to] = cpu->screen[from]; 121 | } 122 | } 123 | } else if (opcode == 0x00fd) { 124 | /* 00FD: EXIT - Stop emulator. */ 125 | cpu->exit = 1; 126 | } else if (opcode == 0x00fe) { 127 | /* 00FE: LOW - Disable extended screen mode. */ 128 | cpu->esm = 0; 129 | } else if (opcode == 0x00ff) { 130 | /* 00FF: HIGH - Enable extended scren mode. */ 131 | cpu->esm = 1; 132 | } 133 | } 134 | 135 | static void 136 | nibble_1(struct machine_t* cpu, word opcode) 137 | { 138 | /* 1NNN: JMP - Jump to address location NNN. */ 139 | cpu->pc = OPCODE_NNN(opcode); 140 | } 141 | 142 | static void 143 | nibble_2(struct machine_t* cpu, word opcode) 144 | { 145 | /* 2NNN: CALL - Call subroutine starting at address NNN. */ 146 | if (cpu->sp < 16) { 147 | cpu->stack[(int) cpu->sp++] = cpu->pc; 148 | cpu->pc = OPCODE_NNN(opcode); 149 | } 150 | /* TODO: Should throw an error on stack overflow. */ 151 | } 152 | 153 | static void 154 | nibble_3(struct machine_t* cpu, word opcode) 155 | { 156 | /* 3XKK: SE: Skip next instruction if V[X] = KK. */ 157 | if (cpu->v[OPCODE_X(opcode)] == OPCODE_KK(opcode)) 158 | cpu->pc = (cpu->pc + 2) & 0xfff; 159 | } 160 | 161 | static void 162 | nibble_4(struct machine_t* cpu, word opcode) 163 | { 164 | /* 4XKK: SNE - Skip next instruction if V[X] != KK. */ 165 | if (cpu->v[OPCODE_X(opcode)] != OPCODE_KK(opcode)) 166 | cpu->pc = (cpu->pc + 2) & 0xfff; 167 | } 168 | 169 | static void 170 | nibble_5(struct machine_t* cpu, word opcode) 171 | { 172 | /* 5XY0: SE - Skip next instruction if V[X] == V[Y]. */ 173 | if (cpu->v[OPCODE_X(opcode)] == cpu->v[OPCODE_Y(opcode)]) 174 | cpu->pc = (cpu->pc + 2) & 0xfff; 175 | } 176 | 177 | static void 178 | nibble_6(struct machine_t* cpu, word opcode) 179 | { 180 | /* 6XKK: LD - Set V[X] = KK. */ 181 | cpu->v[OPCODE_X(opcode)] = OPCODE_KK(opcode); 182 | } 183 | 184 | static void 185 | nibble_7(struct machine_t* cpu, word opcode) 186 | { 187 | /* 7XKK: ADD - Add KK to V[X]. */ 188 | cpu->v[OPCODE_X(opcode)] += OPCODE_KK(opcode); 189 | } 190 | 191 | static void 192 | nibble_8(struct machine_t* cpu, word opcode) 193 | { 194 | /* All these opcodes work with X and most of them with Y, worth it. */ 195 | byte x = OPCODE_X(opcode), y = OPCODE_Y(opcode); 196 | switch (OPCODE_N(opcode)) 197 | { 198 | case 0: 199 | /* 8XY0: LD - Set V[X] = V[Y]. */ 200 | cpu->v[x] = cpu->v[y]; 201 | break; 202 | case 1: 203 | /* 8XY1: OR - Set V[X] |= V[Y]. */ 204 | cpu->v[x] |= cpu->v[y]; 205 | break; 206 | case 2: 207 | /* 8XY2: AND - Set V[X] &= V[Y]. */ 208 | cpu->v[x] &= cpu->v[y]; 209 | break; 210 | case 3: 211 | /* 8XY3: XOR - Set V[X] ^= V[Y]. */ 212 | cpu->v[x] ^= cpu->v[y]; 213 | break; 214 | case 4: 215 | /* 8XY4: ADD - Set V[X] += V[Y], V[15] is carry flag. */ 216 | cpu->v[0xf] = cpu->v[x] > ((cpu->v[x] + cpu->v[y]) & 0xFF); 217 | cpu->v[x] += cpu->v[y]; 218 | break; 219 | case 5: 220 | /* 8XY5: SUB - Set V[X] -= V[Y], V[15] is borrow flag. */ 221 | cpu->v[0xf] = (cpu->v[x] > cpu->v[y]); 222 | cpu->v[x] -= cpu->v[y]; 223 | break; 224 | case 6: 225 | /* 8X06: SHR - Shifts right V[X], LSB bit goes to V[15]. */ 226 | cpu->v[0xf] = (cpu->v[x] & 1); 227 | cpu->v[x] >>= 1; 228 | break; 229 | case 7: 230 | /* 8XY7: SUBN X, Y - Set V[X] = V[Y] - V[X], V[16] is borrow. */ 231 | cpu->v[0xF] = (cpu->v[y] > cpu->v[x]); 232 | cpu->v[x] = cpu->v[y] - cpu->v[x]; 233 | break; 234 | case 0xE: 235 | /* 8X0E: SHL - Shifts left V[X], MSB bit goes to V[15]. */ 236 | cpu->v[0xF] = ((cpu->v[x] & 0x80) != 0); 237 | cpu->v[x] <<= 1; 238 | break; 239 | } 240 | } 241 | 242 | static void 243 | nibble_9(struct machine_t* cpu, word opcode) 244 | { 245 | /* 9XY0: SNE - Skip next instruction if V[X] != V[Y]. */ 246 | if (cpu->v[OPCODE_X(opcode)] != cpu->v[OPCODE_Y(opcode)]) 247 | cpu->pc = (cpu->pc + 2) & 0xFFF; 248 | } 249 | 250 | static void 251 | nibble_A(struct machine_t* cpu, word opcode) 252 | { 253 | /* ANNN: LD - Set I to NNN. */ 254 | cpu->i = OPCODE_NNN(opcode); 255 | } 256 | 257 | static void 258 | nibble_B(struct machine_t* cpu, word opcode) 259 | { 260 | /* BNNN: JP - Jump to memory address (V[0] + NNN). */ 261 | cpu->pc = (cpu->v[0] + OPCODE_NNN(opcode)) & 0xFFF; 262 | } 263 | 264 | static void 265 | nibble_C(struct machine_t* cpu, word opcode) 266 | { 267 | /* CXKK: RND - Put a random value, bitmasked against KK in V[X]. */ 268 | cpu->v[OPCODE_X(opcode)] = rand() & OPCODE_KK(opcode); 269 | } 270 | 271 | static void 272 | nibble_D(struct machine_t* cpu, word opcode) 273 | { 274 | /* DXYN: DRW - Draw a sprite on the screen at location V[X], V[Y]. */ 275 | byte x = OPCODE_X(opcode), y = OPCODE_Y(opcode); 276 | cpu->v[15] = 0; 277 | if (cpu->esm && OPCODE_N(opcode) == 0) { 278 | for (int j = 0; j < 16; j++) { 279 | // Sprite to plot on this line. 280 | byte hi = cpu->mem[(cpu->i + 2 * j) & ADDRESS_MASK]; 281 | byte lo = cpu->mem[(cpu->i + 2 * j + 1) & ADDRESS_MASK]; 282 | word sprite = hi << 8 | lo; 283 | for (int i = 0; i < 16; i++) { 284 | // Where to plot at. 285 | int px = (cpu->v[x] + i) & 127; 286 | int py = (cpu->v[y] + j) & 63; 287 | int pos = 128 * py + px; 288 | // What to plot. 289 | int pixel = (sprite & (1 << (15-i))) != 0; 290 | cpu->v[15] |= (cpu->screen[pos] & pixel); 291 | cpu->screen[pos] ^= pixel; 292 | } 293 | } 294 | } else for (int j = 0; j < OPCODE_N(opcode); j++) { 295 | byte sprite = cpu->mem[(cpu->i + j) & ADDRESS_MASK]; 296 | for (int i = 0; i < 8; i++) { 297 | // Where to plot at. 298 | int px = (cpu->v[x] + i) & (cpu->esm ? 127 : 63); 299 | int py = (cpu->v[y] + j) & (cpu->esm ? 63 : 31); 300 | int pos = (cpu->esm ? 128 : 64) * py + px; 301 | // What to plot. 302 | int pixel = (sprite & (1 << (7-i))) != 0; 303 | cpu->v[15] |= (cpu->screen[pos] & pixel); 304 | cpu->screen[pos] ^= pixel; 305 | } 306 | } 307 | } 308 | 309 | static void 310 | nibble_E(struct machine_t* cpu, word opcode) 311 | { 312 | char key = cpu->v[OPCODE_X(opcode)]; 313 | if (OPCODE_KK(opcode) == 0x9E) { 314 | /* EX9E: SKP - Skip next instruction if key V[X] is down. */ 315 | if (cpu->keydown && cpu->keydown(key & 0xF)) 316 | cpu->pc = (cpu->pc + 2) & 0xFFF; 317 | } else if (OPCODE_KK(opcode) == 0xA1) { 318 | /* EXA1: SKNP - Skip next instruction if key V[X] is not down. */ 319 | if (cpu->keydown && !cpu->keydown(key & 0xF)) 320 | cpu->pc = (cpu->pc + 2) & 0xFFF; 321 | } 322 | } 323 | 324 | static void 325 | nibble_F(struct machine_t* cpu, word opcode) 326 | { 327 | switch (OPCODE_KK(opcode)) { 328 | case 0x07: 329 | /* FX07: LD - Set V[X] to DT. */ 330 | cpu->v[OPCODE_X(opcode)] = cpu->dt; 331 | break; 332 | case 0x0A: 333 | /* FX0A: LD - Wait for a keypress, then store the key in V[X]. */ 334 | cpu->wait_key = OPCODE_X(opcode); 335 | break; 336 | case 0x15: 337 | /* FX15: LD - Set DT to V[X]. */ 338 | cpu->dt = cpu->v[OPCODE_X(opcode)]; 339 | break; 340 | case 0x18: 341 | /* FX18: LD - Set ST to V[X]. */ 342 | cpu->st = cpu->v[OPCODE_X(opcode)]; 343 | break; 344 | case 0x1E: 345 | /* FX1E: ADD - Add V[X] to I. */ 346 | cpu->i += cpu->v[OPCODE_X(opcode)]; 347 | break; 348 | case 0x29: 349 | /* FX29: LD - Set I to the address location for the sprite. */ 350 | cpu->i = 0x50 + (cpu->v[OPCODE_X(opcode)] & 0xF) * 5; 351 | break; 352 | case 0x30: 353 | /* FX30: LD H, F - Load a 10 byte font glyph. */ 354 | cpu->i = 0x8200 + (cpu->v[OPCODE_X(opcode)] & 0xF) * 10; 355 | break; 356 | case 0x33: 357 | /* FX33: Represent V[X] as BCD in I, I+1, I+2. */ 358 | cpu->mem[(cpu->i + 2) & ADDRESS_MASK] = cpu->v[OPCODE_X(opcode)] % 10; 359 | cpu->mem[(cpu->i + 1) & ADDRESS_MASK] = (cpu->v[OPCODE_X(opcode)] / 10) % 10; 360 | cpu->mem[cpu->i & ADDRESS_MASK] = cpu->v[OPCODE_X(opcode)] / 100; 361 | break; 362 | case 0x55: 363 | /* FX55: LD - Save registers V[0] to V[x] starting at I. */ 364 | for (int reg = 0; reg <= OPCODE_X(opcode); reg++) { 365 | cpu->mem[(cpu->i + reg) & ADDRESS_MASK] = cpu->v[reg]; 366 | } 367 | break; 368 | case 0x65: 369 | /* FX65: LD - Load registers V[0] to V[x] from I. */ 370 | for (int reg = 0; reg <= OPCODE_X(opcode); reg++) { 371 | cpu->v[reg] = cpu->mem[(cpu->i + reg) & ADDRESS_MASK]; 372 | } 373 | break; 374 | case 0x75: 375 | /* FX75: LD R, V - Store V[0]..V[X] in R registers. */ 376 | // FIXME: Should check that X <= 7. 377 | for (int reg = 0; reg <= OPCODE_X(opcode); reg++) { 378 | cpu->r[reg] = cpu->v[reg]; 379 | } 380 | break; 381 | case 0x85: 382 | /* FX85: LD V, R - Load V[0]..V[X] in R registers. */ 383 | // FIXME: Should check that X <= 7. 384 | for (int reg = 0; reg <= OPCODE_X(opcode); reg++) { 385 | cpu->v[reg] = cpu->r[reg]; 386 | } 387 | break; 388 | } 389 | } 390 | 391 | /** 392 | * This is the handler table. There are 16 handlers in the following array, 393 | * each one covering a subset of the opcodes for the CHIP-8. During opcode 394 | * fetching, the most significant nibble (most significant hex char) is taken 395 | * out as a value in range [0, 15]. The handler from this array whose index 396 | * matches the value of that nibble is executed. The handler should execute 397 | * opcodes starting by that value. 398 | */ 399 | static opcode_table_t nibbles[16] = { 400 | &nibble_0, &nibble_1, &nibble_2, &nibble_3, 401 | &nibble_4, &nibble_5, &nibble_6, &nibble_7, 402 | &nibble_8, &nibble_9, &nibble_A, &nibble_B, 403 | &nibble_C, &nibble_D, &nibble_E, &nibble_F 404 | }; 405 | 406 | void 407 | init_machine(struct machine_t* machine) 408 | { 409 | memset(machine, 0x00, sizeof(struct machine_t)); 410 | memcpy(machine->mem + 0x50, hexcodes, 80); 411 | machine->pc = 0x200; 412 | machine->wait_key = -1; 413 | global_delta = 0; 414 | log("Debug mode is enabled"); 415 | log("Machine has been initialized"); 416 | } 417 | 418 | void 419 | step_machine(struct machine_t* cpu) 420 | { 421 | if (cpu->exit) 422 | return; 423 | 424 | /* Are we waiting for a key press? */ 425 | if (cpu->wait_key != -1 && cpu->keydown) { 426 | for (int i = 0; i < 16; i++) { 427 | int status = cpu->keydown(i); 428 | if (status) { 429 | /* Key was down. Restore system. */ 430 | cpu->v[(int) cpu->wait_key] = i; 431 | cpu->wait_key = -1; 432 | break; 433 | } 434 | } 435 | /* Test again. If we are still waiting for a key, don't fetch. */ 436 | if (cpu->wait_key != -1) { 437 | return; 438 | } 439 | } 440 | 441 | /* Fetch next opcode. */ 442 | word opcode = (cpu->mem[cpu->pc & ADDRESS_MASK] << 8) 443 | | cpu->mem[(cpu->pc + 1) & ADDRESS_MASK]; 444 | cpu->pc = (cpu->pc + 2) & 0xFFF; 445 | 446 | if (is_debug) { 447 | printf("Executing opcode 0x%x...\n", opcode); 448 | } 449 | 450 | /* Execute the corresponding handler from the nibble table. */ 451 | nibbles[OPCODE_P(opcode)](cpu, opcode); 452 | } 453 | 454 | void 455 | update_time(struct machine_t* cpu, int delta) 456 | { 457 | global_delta += delta; 458 | while (global_delta > (1000 / 60)) { 459 | global_delta -= (1000 / 60); 460 | if (cpu->dt > 0) { 461 | cpu->dt--; 462 | } 463 | if (cpu->st > 0) { 464 | if (--cpu->st == 0 && cpu->speaker) { 465 | /* Disable speaker buzz. */ 466 | cpu->speaker(0); 467 | } else if (cpu->speaker) { 468 | /* Enable speaker buzz. */ 469 | cpu->speaker(1); 470 | } 471 | } 472 | } 473 | } 474 | 475 | void 476 | screen_fill_column(struct machine_t* cpu, int column) 477 | { 478 | int rowsiz = cpu->esm ? 128 : 64; 479 | int limit = cpu->esm ? 64 : 32; 480 | for (int y = 0; y < limit; y++) { 481 | cpu->screen[rowsiz * y + column] = 1; 482 | } 483 | } 484 | 485 | void 486 | screen_clear_column(struct machine_t* cpu, int column) 487 | { 488 | int rowsiz = cpu->esm ? 128 : 64; 489 | int limit = cpu->esm ? 64 : 32; 490 | for (int y = 0; y < limit; y++) { 491 | cpu->screen[rowsiz * y + column] = 0; 492 | } 493 | } 494 | 495 | void 496 | screen_fill_row(struct machine_t* cpu, int row) 497 | { 498 | int limit = cpu->esm ? 128 : 64; 499 | int rowsiz = limit; 500 | for (int x = 0; x < limit; x++) { 501 | cpu->screen[rowsiz * row + x] = 1; 502 | } 503 | } 504 | 505 | void 506 | screen_clear_row(struct machine_t* cpu, int row) 507 | { 508 | int limit = cpu->esm ? 128 : 64; 509 | int rowsiz = limit; 510 | for (int x = 0; x < limit; x++) { 511 | cpu->screen[rowsiz * row + x] = 0; 512 | } 513 | } 514 | 515 | int 516 | screen_get_pixel(struct machine_t* cpu, int row, int column) 517 | { 518 | int rowsiz = cpu->esm ? 128 : 64; 519 | return cpu->screen[rowsiz * row + column] != 0; 520 | } 521 | 522 | void 523 | screen_set_pixel(struct machine_t* cpu, int row, int column) 524 | { 525 | int rowsiz = cpu->esm ? 128 : 64; 526 | cpu->screen[rowsiz * row + column] = 1; 527 | } 528 | 529 | void 530 | screen_clear_pixel(struct machine_t* cpu, int row, int column) 531 | { 532 | int rowsiz = cpu->esm ? 128 : 64; 533 | cpu->screen[rowsiz * row + column] = 0; 534 | } 535 | -------------------------------------------------------------------------------- /src/lib8/cpu.h: -------------------------------------------------------------------------------- 1 | /* 2 | * chip8 is a CHIP-8 emulator done in C 3 | * Copyright (C) 2015-2016 Dani Rodríguez 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #ifndef CPU_H_ 20 | #define CPU_H_ 21 | 22 | #include 23 | 24 | #define MEMSIZ 4096 // How much memory can handle the CHIP-8 25 | 26 | /** 27 | * Type definition for a byte value. Bytes are unsigned 8-bit variables. 28 | * They are widely used on the CHIP-8 since the memory and registers are 29 | * byte-sized. 30 | */ 31 | typedef uint8_t byte; 32 | 33 | /** 34 | * Type definition for a word value. Words are unsigned 16-bit variables. 35 | * Words are used for decoding opcodes, since a opcode is 16-bit long. 36 | */ 37 | typedef uint16_t word; 38 | 39 | /** 40 | * Type definition for refering to addresses. Addresses are 12-bit value. 41 | * However there is no such type on C, thus a 16-bit value is used and only 42 | * the 12 least significant bits are used. 43 | * 44 | * Address should be used whenever a memory address is being manipulated. 45 | * They shouldn't be used on any other situation. 46 | */ 47 | typedef uint16_t address; 48 | 49 | /** 50 | * This is the maximum amount of memory addressable by the machine. 51 | * It should be used as a bitmask when overflows could possibly happen 52 | * due to memory address management. 53 | */ 54 | #define ADDRESS_MASK 0xFFF 55 | 56 | typedef int (*keyboard_poller_t)(char); 57 | 58 | typedef void (*speaker_handler_t)(int); 59 | 60 | /** 61 | * Main data structure for holding information and state about processor. 62 | * Memory, stack, and register set is all defined here. 63 | */ 64 | struct machine_t 65 | { 66 | byte mem[MEMSIZ]; // Memory is allocated as a buffer 67 | address pc; // Program Counter 68 | 69 | address stack[16]; // Stack can hold 16 16-bit values 70 | char sp; // Stack Pointer: points to next free cell 71 | 72 | byte v[16]; // 16 general purpose registers 73 | address i; // Special I register 74 | byte dt, st; // Timers 75 | 76 | char screen[8192]; // Screen bitmap 77 | char wait_key; // Key the CHIP-8 is idle waiting for. 78 | 79 | keyboard_poller_t keydown; // Keyboard poller 80 | speaker_handler_t speaker; // Speaker handler 81 | 82 | int exit; // Should close the game. 83 | int esm; // Is in Extended Screen Mode? 84 | byte r[8]; // R register set. 85 | }; 86 | 87 | /** 88 | * Initializes to cero a machine data structure. This function should be 89 | * called when the program is starting up to make sure that the machine 90 | * data structure is getting initialized. It also can be called everytime 91 | * the user wants the machine to be reinitialized, such as a reboot. 92 | * 93 | * @param machine machine data structure that wants to be initialized. 94 | */ 95 | void init_machine(struct machine_t* cpu); 96 | 97 | /** 98 | * Step the machine. This method will fetch an instruction from memory 99 | * and execute it. After invoking this method, the state of the provided 100 | * machine is modified according to the executed instruction. 101 | * @param cpu reference pointer to the machine to step. 102 | */ 103 | void step_machine(struct machine_t* cpu); 104 | 105 | /** 106 | * Updates subsystems that depend on time. Several parts of the CHIP-8 107 | * depend on a timer. Examples are the DT and ST countdown registers, whose 108 | * values must countdown at a rate of 60 times per second. This function 109 | * should be called regularly so that the systems are updated. 110 | * @param delta amount of milliseconds since last call to function. 111 | */ 112 | void update_time(struct machine_t* cpu, int delta); 113 | 114 | void screen_fill_column(struct machine_t* cpu, int column); 115 | 116 | void screen_clear_column(struct machine_t* cpu, int column); 117 | 118 | void screen_fill_row(struct machine_t* cpu, int row); 119 | 120 | void screen_clear_row(struct machine_t* cpu, int row); 121 | 122 | int screen_get_pixel(struct machine_t* cpu, int row, int column); 123 | 124 | void screen_set_pixel(struct machine_t* cpu, int row, int column); 125 | 126 | void screen_clear_pixel(struct machine_t* cpu, int row, int column); 127 | 128 | void set_debug_mode(int mode); 129 | 130 | #endif // CPU_H_ 131 | -------------------------------------------------------------------------------- /tests/.gitignore: -------------------------------------------------------------------------------- 1 | chip8_test 2 | chip8_test.log 3 | chip8_test.trs 4 | test-suite.log 5 | -------------------------------------------------------------------------------- /tests/Makefile.am: -------------------------------------------------------------------------------- 1 | TESTS = chip8_test 2 | check_PROGRAMS = chip8_test 3 | chip8_test_SOURCES = test.c opchip.c opschip.c screen.c 4 | chip8_test_CFLAGS = -std=c99 -Wall @CHECK_CFLAGS@ -I$(top_srcdir)/src 5 | chip8_test_LDADD = @CHECK_LIBS@ $(top_srcdir)/src/lib8/lib8.a 6 | -------------------------------------------------------------------------------- /tests/opchip.c: -------------------------------------------------------------------------------- 1 | /* 2 | * chip8 is a CHIP-8 emulator done in C 3 | * Copyright (C) 2015-2016 Dani Rodríguez 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | /* 20 | * File: tests/opchip.c 21 | * Description: Unit test related to CHIP-8 Opcodes. 22 | */ 23 | 24 | #include 25 | #include 26 | #include 27 | 28 | struct machine_t cpu; 29 | 30 | static void 31 | setup_cpu(void) 32 | { 33 | init_machine(&cpu); 34 | } 35 | 36 | static TCase* 37 | setup_tcase(char* name) 38 | { 39 | TCase* tcase = tcase_create(name); 40 | tcase_add_checked_fixture(tcase, setup_cpu, NULL); 41 | return tcase; 42 | } 43 | 44 | static void 45 | put_opcode(word opcode, address pos) 46 | { 47 | cpu.mem[pos] = opcode >> 8; 48 | cpu.mem[pos + 1] = opcode & 0xFF; 49 | } 50 | 51 | /* Should test that upon execution of CLS the screen is cleant. */ 52 | START_TEST(test_cls) 53 | { 54 | memset(cpu.screen, 0x55, 2048); 55 | put_opcode(0x00E0, 0x00); 56 | cpu.pc = 0x00; 57 | step_machine(&cpu); 58 | for (int i = 0; i < 2048; i++) { 59 | ck_assert_int_eq(0, cpu.screen[i]); 60 | } 61 | } 62 | END_TEST 63 | 64 | static TCase* 65 | tcase_cls() 66 | { 67 | TCase* tcase = setup_tcase("CLS"); 68 | tcase_add_test(tcase, test_cls); 69 | return tcase; 70 | } 71 | 72 | START_TEST(test_rts_normal) 73 | { 74 | cpu.stack[(int) cpu.sp++] = 0x123; 75 | cpu.stack[(int) cpu.sp++] = 0x234; 76 | cpu.pc = 0x345; 77 | ck_assert_int_eq(2, cpu.sp); /* Check I did the math OK. */ 78 | put_opcode(0x00EE, 0x345); 79 | step_machine(&cpu); 80 | ck_assert_int_eq(0x234, cpu.pc); 81 | ck_assert_int_eq(1, cpu.sp); 82 | } 83 | END_TEST 84 | 85 | static TCase* 86 | tcase_rts() 87 | { 88 | TCase* tcase = setup_tcase("RTS"); 89 | tcase_add_test(tcase, test_rts_normal); 90 | return tcase; 91 | } 92 | 93 | START_TEST(test_jmp) 94 | { 95 | cpu.pc = 0; 96 | put_opcode(0x1123, 0); 97 | step_machine(&cpu); 98 | ck_assert_int_eq(0x123, cpu.pc); 99 | } 100 | END_TEST 101 | 102 | static TCase* 103 | tcase_jmp() 104 | { 105 | TCase* tcase = setup_tcase("JMP"); 106 | tcase_add_test(tcase, test_jmp); 107 | return tcase; 108 | } 109 | 110 | START_TEST(test_jsr_normal) 111 | { 112 | cpu.pc = 0x55; 113 | cpu.sp = 0; 114 | put_opcode(0x2123, 0x55); 115 | step_machine(&cpu); 116 | ck_assert_int_eq(1, cpu.sp); 117 | ck_assert_int_eq(0x123, cpu.pc); 118 | ck_assert_int_eq(0x57, cpu.stack[0]); 119 | } 120 | END_TEST 121 | 122 | static TCase* 123 | tcase_jsr() 124 | { 125 | TCase* tcase = setup_tcase("JSR"); 126 | tcase_add_test(tcase, test_jsr_normal); 127 | return tcase; 128 | } 129 | 130 | START_TEST(test_se_eq) 131 | { 132 | cpu.v[4] = 0x55; 133 | cpu.pc = 0x00; 134 | put_opcode(0x3455, 0x00); 135 | step_machine(&cpu); 136 | ck_assert_int_eq(4, cpu.pc); 137 | } 138 | END_TEST 139 | 140 | START_TEST(test_se_ne) 141 | { 142 | cpu.v[4] = 0x54; 143 | cpu.pc = 0x00; 144 | put_opcode(0x3455, 0x00); 145 | step_machine(&cpu); 146 | ck_assert_int_eq(2, cpu.pc); 147 | } 148 | END_TEST 149 | 150 | static TCase* 151 | tcase_se() 152 | { 153 | TCase* tcase = setup_tcase("SE"); 154 | tcase_add_test(tcase, test_se_eq); 155 | tcase_add_test(tcase, test_se_ne); 156 | return tcase; 157 | } 158 | 159 | START_TEST(test_sne_eq) 160 | { 161 | cpu.v[4] = 0x55; 162 | cpu.pc = 0x00; 163 | put_opcode(0x4455, 0x00); 164 | step_machine(&cpu); 165 | ck_assert_int_eq(2, cpu.pc); 166 | } 167 | END_TEST 168 | 169 | START_TEST(test_sne_ne) 170 | { 171 | cpu.v[4] = 0x54; 172 | cpu.pc = 0x00; 173 | put_opcode(0x4455, 0x00); 174 | step_machine(&cpu); 175 | ck_assert_int_eq(4, cpu.pc); 176 | } 177 | END_TEST 178 | 179 | static TCase* 180 | tcase_sne() 181 | { 182 | TCase* tcase = setup_tcase("SNE"); 183 | tcase_add_test(tcase, test_sne_eq); 184 | tcase_add_test(tcase, test_sne_ne); 185 | return tcase; 186 | } 187 | 188 | START_TEST(test_sexy_eq) 189 | { 190 | cpu.v[4] = 0x55; 191 | cpu.v[5] = 0x55; 192 | cpu.pc = 0x00; 193 | put_opcode(0x5450, 0x00); 194 | step_machine(&cpu); 195 | ck_assert_int_eq(4, cpu.pc); 196 | } 197 | END_TEST 198 | 199 | START_TEST(test_sexy_ne) 200 | { 201 | cpu.v[4] = 0x54; 202 | cpu.v[5] = 0x55; 203 | cpu.pc = 0x00; 204 | put_opcode(0x5450, 0x00); 205 | step_machine(&cpu); 206 | ck_assert_int_eq(2, cpu.pc); 207 | } 208 | END_TEST 209 | 210 | static TCase* 211 | tcase_sexy() 212 | { 213 | TCase* tcase = setup_tcase("SEXY"); 214 | tcase_add_test(tcase, test_sexy_eq); 215 | tcase_add_test(tcase, test_sexy_ne); 216 | return tcase; 217 | } 218 | 219 | START_TEST(test_ld) 220 | { 221 | cpu.v[5] = 0x12; 222 | cpu.pc = 0; 223 | put_opcode(0x6534, 0); 224 | step_machine(&cpu); 225 | ck_assert_int_eq(0x34, cpu.v[5]); 226 | } 227 | END_TEST 228 | 229 | static TCase* 230 | tcase_ld() 231 | { 232 | TCase* tcase = setup_tcase("LD"); 233 | tcase_add_test(tcase, test_ld); 234 | return tcase; 235 | } 236 | 237 | START_TEST(test_add) 238 | { 239 | cpu.v[5] = 0x12; 240 | cpu.pc = 0; 241 | put_opcode(0x7534, 0); 242 | step_machine(&cpu); 243 | ck_assert_int_eq(0x46, cpu.v[5]); 244 | } 245 | END_TEST 246 | 247 | static TCase* 248 | tcase_add() 249 | { 250 | TCase* tcase = setup_tcase("ADD"); 251 | tcase_add_test(tcase, test_add); 252 | return tcase; 253 | } 254 | 255 | START_TEST(test_ldxy) 256 | { 257 | cpu.v[4] = 0x33; 258 | cpu.v[5] = 0x55; 259 | cpu.pc = 0; 260 | put_opcode(0x8450, 0); 261 | step_machine(&cpu); 262 | ck_assert_int_eq(0x55, cpu.v[4]); 263 | } 264 | END_TEST 265 | 266 | static TCase* 267 | tcase_ldxy() 268 | { 269 | TCase *tcase = setup_tcase("LDXY"); 270 | tcase_add_test(tcase, test_ldxy); 271 | return tcase; 272 | } 273 | 274 | START_TEST(test_orxy) 275 | { 276 | cpu.v[4] = 0x40; 277 | cpu.v[5] = 0x04; 278 | cpu.pc = 0; 279 | put_opcode(0x8451, 0); 280 | step_machine(&cpu); 281 | ck_assert_int_eq(0x44, cpu.v[4]); 282 | } 283 | END_TEST 284 | 285 | static TCase* 286 | tcase_orxy() 287 | { 288 | TCase* tcase = setup_tcase("ORXY"); 289 | tcase_add_test(tcase, test_orxy); 290 | return tcase; 291 | } 292 | 293 | START_TEST(test_andxy) 294 | { 295 | cpu.v[4] = 0x40; 296 | cpu.v[5] = 0x04; 297 | cpu.pc = 0; 298 | put_opcode(0x8452, 0); 299 | step_machine(&cpu); 300 | ck_assert_int_eq(0x00, cpu.v[4]); 301 | } 302 | END_TEST 303 | 304 | static TCase* 305 | tcase_andxy() 306 | { 307 | TCase* tcase = setup_tcase("ANDXY"); 308 | tcase_add_test(tcase, test_andxy); 309 | return tcase; 310 | } 311 | 312 | START_TEST(test_xorxy) 313 | { 314 | cpu.v[4] = 0x55; 315 | cpu.v[5] = 0xAA; 316 | cpu.pc = 0; 317 | put_opcode(0x8453, 0); 318 | step_machine(&cpu); 319 | ck_assert_int_eq(0xFF, cpu.v[4]); 320 | } 321 | END_TEST 322 | 323 | static TCase* 324 | tcase_xorxy() 325 | { 326 | TCase* tcase = setup_tcase("XORXY"); 327 | tcase_add_test(tcase, test_xorxy); 328 | return tcase; 329 | } 330 | 331 | START_TEST(test_addxy_nocarry) 332 | { 333 | cpu.v[4] = 0x12; 334 | cpu.v[5] = 0x34; 335 | cpu.pc = 0; 336 | put_opcode(0x8454, 0); 337 | step_machine(&cpu); 338 | ck_assert_int_eq(0x46, cpu.v[4]); 339 | ck_assert_int_eq(0, cpu.v[0xf]); 340 | } 341 | END_TEST 342 | 343 | START_TEST(test_addxy_carry) 344 | { 345 | cpu.v[4] = 0xF0; 346 | cpu.v[5] = 0xF0; 347 | cpu.pc = 0; 348 | put_opcode(0x8454, 0); 349 | step_machine(&cpu); 350 | ck_assert_int_eq(0xE0, cpu.v[4]); 351 | ck_assert_int_eq(1, cpu.v[0xf]); 352 | } 353 | END_TEST 354 | 355 | static TCase* 356 | tcase_addxy() 357 | { 358 | TCase* tcase = setup_tcase("ADDXY"); 359 | tcase_add_test(tcase, test_addxy_nocarry); 360 | tcase_add_test(tcase, test_addxy_carry); 361 | return tcase; 362 | } 363 | 364 | START_TEST(test_subxy_noborrow) 365 | { 366 | cpu.v[4] = 0x46; 367 | cpu.v[5] = 0x34; 368 | cpu.pc = 0; 369 | put_opcode(0x8455, 0); 370 | step_machine(&cpu); 371 | ck_assert_int_eq(0x12, cpu.v[4]); 372 | ck_assert_int_eq(1, cpu.v[0xf]); 373 | } 374 | END_TEST 375 | 376 | START_TEST(test_subxy_borrow) 377 | { 378 | cpu.v[4] = 0x30; 379 | cpu.v[5] = 0x40; 380 | cpu.pc = 0; 381 | put_opcode(0x8455, 0); 382 | step_machine(&cpu); 383 | ck_assert_int_eq(0xF0, cpu.v[4]); 384 | ck_assert_int_eq(0, cpu.v[0xf]); 385 | } 386 | END_TEST 387 | 388 | static TCase* 389 | tcase_subxy() 390 | { 391 | TCase* tcase = setup_tcase("SUBXY"); 392 | tcase_add_test(tcase, test_subxy_noborrow); 393 | tcase_add_test(tcase, test_subxy_borrow); 394 | return tcase; 395 | } 396 | 397 | START_TEST(test_shr_1) 398 | { 399 | cpu.v[4] = 0x45; 400 | put_opcode(0x8406, 0); 401 | cpu.pc = 0x00; 402 | step_machine(&cpu); 403 | ck_assert_int_eq(0x22, cpu.v[4]); 404 | ck_assert_int_eq(1, cpu.v[0xf]); 405 | } 406 | END_TEST 407 | 408 | START_TEST(test_shr_0) 409 | { 410 | cpu.v[4] = 0x44; 411 | put_opcode(0x8406, 0); 412 | cpu.pc = 0x00; 413 | step_machine(&cpu); 414 | ck_assert_int_eq(0x22, cpu.v[4]); 415 | ck_assert_int_eq(0, cpu.v[0xf]); 416 | } 417 | END_TEST 418 | 419 | static TCase* 420 | tcase_shr() 421 | { 422 | TCase* tcase = setup_tcase("SHR"); 423 | tcase_add_test(tcase, test_shr_0); 424 | tcase_add_test(tcase, test_shr_1); 425 | return tcase; 426 | } 427 | 428 | START_TEST(test_subnxy_noborrow) 429 | { 430 | cpu.v[4] = 0x34; 431 | cpu.v[5] = 0x46; 432 | cpu.pc = 0; 433 | put_opcode(0x8457, 0); 434 | step_machine(&cpu); 435 | ck_assert_int_eq(0x12, cpu.v[4]); 436 | ck_assert_int_eq(1, cpu.v[0xf]); 437 | } 438 | END_TEST 439 | 440 | START_TEST(test_subnxy_borrow) 441 | { 442 | cpu.v[4] = 0x40; 443 | cpu.v[5] = 0x30; 444 | cpu.pc = 0; 445 | put_opcode(0x8457, 0); 446 | step_machine(&cpu); 447 | ck_assert_int_eq(0xF0, cpu.v[4]); 448 | ck_assert_int_eq(0, cpu.v[0xf]); 449 | } 450 | END_TEST 451 | 452 | static TCase* 453 | tcase_subnxy() 454 | { 455 | TCase* tcase = setup_tcase("SUBN"); 456 | tcase_add_test(tcase, test_subnxy_noborrow); 457 | tcase_add_test(tcase, test_subnxy_borrow); 458 | return tcase; 459 | } 460 | 461 | START_TEST(test_shl_0) 462 | { 463 | cpu.v[4] = 0x08; 464 | cpu.pc = 0; 465 | put_opcode(0x840E, 0); 466 | step_machine(&cpu); 467 | ck_assert_int_eq(0x10, cpu.v[4]); 468 | ck_assert_int_eq(0, cpu.v[0xf]); 469 | } 470 | END_TEST 471 | 472 | START_TEST(test_shl_1) 473 | { 474 | cpu.v[4] = 0xC8; 475 | cpu.pc = 0; 476 | put_opcode(0x840E, 0); 477 | step_machine(&cpu); 478 | ck_assert_int_eq(0x90, cpu.v[4]); 479 | ck_assert_int_eq(1, cpu.v[0xf]); 480 | } 481 | END_TEST 482 | 483 | static TCase* 484 | tcase_shl() 485 | { 486 | TCase* tcase = setup_tcase("SHL"); 487 | tcase_add_test(tcase, test_shl_0); 488 | tcase_add_test(tcase, test_shl_1); 489 | return tcase; 490 | } 491 | 492 | START_TEST(test_snexy_eq) 493 | { 494 | cpu.v[4] = 0x55; 495 | cpu.v[5] = 0x55; 496 | cpu.pc = 0; 497 | put_opcode(0x9450, 0); 498 | step_machine(&cpu); 499 | ck_assert_int_eq(2, cpu.pc); 500 | } 501 | END_TEST 502 | 503 | START_TEST(test_snexy_ne) 504 | { 505 | cpu.v[4] = 0x55; 506 | cpu.v[5] = 0x56; 507 | cpu.pc = 0; 508 | put_opcode(0x9450, 0); 509 | step_machine(&cpu); 510 | ck_assert_int_eq(4, cpu.pc); 511 | } 512 | END_TEST 513 | 514 | static TCase* 515 | tcase_snexy() 516 | { 517 | TCase* tcase = setup_tcase("SNEXY"); 518 | tcase_add_test(tcase, test_snexy_eq); 519 | tcase_add_test(tcase, test_snexy_ne); 520 | return tcase; 521 | } 522 | 523 | START_TEST(test_ldi) 524 | { 525 | cpu.i = 0; 526 | cpu.pc = 0; 527 | put_opcode(0xA123, 0); 528 | step_machine(&cpu); 529 | ck_assert_int_eq(0x123, cpu.i); 530 | } 531 | END_TEST 532 | 533 | static TCase* 534 | tcase_ldi() 535 | { 536 | TCase* tcase = setup_tcase("LDI"); 537 | tcase_add_test(tcase, test_ldi); 538 | return tcase; 539 | } 540 | 541 | START_TEST(test_jp) 542 | { 543 | cpu.v[0] = 0x55; 544 | cpu.pc = 0; 545 | put_opcode(0xB123, 0); 546 | step_machine(&cpu); 547 | ck_assert_int_eq(0x178, cpu.pc); 548 | } 549 | END_TEST 550 | 551 | static TCase* 552 | tcase_jp() 553 | { 554 | TCase* tcase = setup_tcase("JP"); 555 | tcase_add_test(tcase, test_jp); 556 | return tcase; 557 | } 558 | 559 | static int 560 | mock_poller(char key) 561 | { 562 | return key == 2; 563 | } 564 | 565 | START_TEST(test_skp) 566 | { 567 | cpu.keydown = &mock_poller; 568 | put_opcode(0xE09E, 0); 569 | for (char key = 0; key < 16; key++) { 570 | cpu.pc = 0; 571 | cpu.v[0] = key; 572 | step_machine(&cpu); 573 | if (key == 2) { 574 | ck_assert_int_eq(4, cpu.pc); 575 | } else { 576 | ck_assert_int_eq(2, cpu.pc); 577 | } 578 | } 579 | } 580 | END_TEST 581 | 582 | static TCase* 583 | tcase_skp() 584 | { 585 | TCase* tcase = setup_tcase("SKP"); 586 | tcase_add_test(tcase, test_skp); 587 | return tcase; 588 | } 589 | 590 | START_TEST(test_sknp) 591 | { 592 | cpu.keydown = &mock_poller; 593 | put_opcode(0xE0A1, 0); 594 | for (char key = 0; key < 16; key++) { 595 | cpu.pc = 0; 596 | cpu.v[0] = key; 597 | step_machine(&cpu); 598 | if (key != 2) { 599 | ck_assert_int_eq(4, cpu.pc); 600 | } else { 601 | ck_assert_int_eq(2, cpu.pc); 602 | } 603 | } 604 | } 605 | END_TEST 606 | 607 | static TCase* 608 | tcase_sknp() 609 | { 610 | TCase* tcase = setup_tcase("SKNP"); 611 | tcase_add_test(tcase, test_sknp); 612 | return tcase; 613 | } 614 | 615 | START_TEST(test_lddt_in) 616 | { 617 | cpu.dt = 0x55; 618 | cpu.v[0] = 0; 619 | put_opcode(0xF007, 0); 620 | cpu.pc = 0x00; 621 | step_machine(&cpu); 622 | ck_assert_int_eq(0x55, cpu.v[0]); 623 | } 624 | END_TEST 625 | 626 | START_TEST(test_lddt_out) 627 | { 628 | put_opcode(0xF015, 0); 629 | cpu.pc = 0x00; 630 | cpu.v[0] = 0x55; 631 | cpu.dt = 0; 632 | step_machine(&cpu); 633 | ck_assert_int_eq(0x55, cpu.dt); 634 | } 635 | END_TEST 636 | 637 | static TCase* 638 | tcase_lddt() 639 | { 640 | TCase* tcase = setup_tcase("LDDT"); 641 | tcase_add_test(tcase, test_lddt_out); 642 | tcase_add_test(tcase, test_lddt_in); 643 | return tcase; 644 | } 645 | 646 | START_TEST(test_ldk) 647 | { 648 | cpu.keydown = &mock_poller; 649 | cpu.v[0] = 0xFF; 650 | put_opcode(0xF00A, 0); 651 | cpu.pc = 0x00; 652 | step_machine(&cpu); 653 | /* Since this opcode should wait, we have to iterate a new step. */ 654 | step_machine(&cpu); 655 | ck_assert_int_eq(2, cpu.v[0]); 656 | } 657 | END_TEST 658 | 659 | static TCase* 660 | tcase_ldk() 661 | { 662 | TCase* tcase = setup_tcase("LDK"); 663 | tcase_add_test(tcase, test_ldk); 664 | return tcase; 665 | } 666 | 667 | START_TEST(test_ldst) 668 | { 669 | put_opcode(0xF018, 0); 670 | cpu.v[0] = 0x55; 671 | cpu.st = 0; 672 | cpu.pc = 0x00; 673 | step_machine(&cpu); 674 | ck_assert_int_eq(0x55, cpu.st); 675 | } 676 | END_TEST 677 | 678 | static TCase* 679 | tcase_ldst() 680 | { 681 | TCase* tcase = setup_tcase("LDST"); 682 | tcase_add_test(tcase, test_ldst); 683 | return tcase; 684 | } 685 | 686 | START_TEST(test_addi) 687 | { 688 | put_opcode(0xF01E, 0); 689 | cpu.v[0] = 0x30; 690 | cpu.i = 0x400; 691 | cpu.pc = 0x00; 692 | step_machine(&cpu); 693 | ck_assert_int_eq(0x430, cpu.i); 694 | } 695 | END_TEST 696 | 697 | static TCase* 698 | tcase_addi() 699 | { 700 | TCase* tcase = setup_tcase("ADDI"); 701 | tcase_add_test(tcase, test_addi); 702 | return tcase; 703 | } 704 | 705 | START_TEST(test_ldf) 706 | { 707 | cpu.v[0] = 3; 708 | cpu.i = 0; 709 | cpu.pc = 0x00; 710 | put_opcode(0xF029, 0); 711 | step_machine(&cpu); 712 | ck_assert_int_eq(0x50 + 15, cpu.i); 713 | } 714 | END_TEST 715 | 716 | static TCase* 717 | tcase_ldf() 718 | { 719 | TCase* tcase = setup_tcase("LDF"); 720 | tcase_add_test(tcase, test_ldf); 721 | return tcase; 722 | } 723 | 724 | START_TEST(test_ldb) 725 | { 726 | cpu.v[0] = 123; 727 | cpu.i = 0x400; 728 | cpu.pc = 0x00; 729 | put_opcode(0xF033, 0); 730 | step_machine(&cpu); 731 | ck_assert_int_eq(1, cpu.mem[0x400]); 732 | ck_assert_int_eq(2, cpu.mem[0x401]); 733 | ck_assert_int_eq(3, cpu.mem[0x402]); 734 | } 735 | END_TEST 736 | 737 | static TCase* 738 | tcase_ldb() 739 | { 740 | TCase* tcase = setup_tcase("LDB"); 741 | tcase_add_test(tcase, test_ldb); 742 | return tcase; 743 | } 744 | 745 | START_TEST(test_ldix_out) 746 | { 747 | for (int i = 0; i < 16; i++) { 748 | cpu.v[i] = 0x80 + i; 749 | cpu.mem[0x400 + i] = 0xFF; 750 | } 751 | put_opcode(0xFF55, 0); 752 | cpu.i = 0x400; 753 | cpu.pc = 0x00; 754 | step_machine(&cpu); 755 | for (int i = 0; i < 16; i++) { 756 | ck_assert_int_eq(0x80 + i, cpu.mem[0x400 + i]); 757 | } 758 | } 759 | END_TEST 760 | 761 | START_TEST(test_ldix_in) 762 | { 763 | for (int i = 0; i < 16; i++) { 764 | cpu.mem[0x400 + i] = 0x80 + i; 765 | cpu.v[i] = 0xFF; 766 | } 767 | put_opcode(0xFF65, 0); 768 | cpu.i = 0x400; 769 | cpu.pc = 0x00; 770 | step_machine(&cpu); 771 | for (int i = 0; i < 16; i++) { 772 | ck_assert_int_eq(0x80 + i, cpu.v[i]); 773 | } 774 | } 775 | END_TEST 776 | 777 | static TCase* 778 | tcase_ldix() 779 | { 780 | TCase* tcase = setup_tcase("LDIX"); 781 | tcase_add_test(tcase, test_ldix_in); 782 | tcase_add_test(tcase, test_ldix_out); 783 | return tcase; 784 | } 785 | 786 | Suite* 787 | create_chip8_opcodes_suite() 788 | { 789 | Suite* suite = suite_create("CHIP-8 Opcodes"); 790 | suite_add_tcase(suite, tcase_cls()); 791 | suite_add_tcase(suite, tcase_rts()); 792 | suite_add_tcase(suite, tcase_jmp()); 793 | suite_add_tcase(suite, tcase_jsr()); 794 | suite_add_tcase(suite, tcase_se()); 795 | suite_add_tcase(suite, tcase_sne()); 796 | suite_add_tcase(suite, tcase_sexy()); 797 | suite_add_tcase(suite, tcase_ld()); 798 | suite_add_tcase(suite, tcase_add()); 799 | suite_add_tcase(suite, tcase_ldxy()); 800 | suite_add_tcase(suite, tcase_orxy()); 801 | suite_add_tcase(suite, tcase_andxy()); 802 | suite_add_tcase(suite, tcase_xorxy()); 803 | suite_add_tcase(suite, tcase_addxy()); 804 | suite_add_tcase(suite, tcase_subxy()); 805 | suite_add_tcase(suite, tcase_shr()); 806 | suite_add_tcase(suite, tcase_subnxy()); 807 | suite_add_tcase(suite, tcase_shl()); 808 | suite_add_tcase(suite, tcase_snexy()); 809 | suite_add_tcase(suite, tcase_ldi()); 810 | suite_add_tcase(suite, tcase_jp()); 811 | suite_add_tcase(suite, tcase_skp()); 812 | suite_add_tcase(suite, tcase_sknp()); 813 | suite_add_tcase(suite, tcase_lddt()); 814 | suite_add_tcase(suite, tcase_ldk()); 815 | suite_add_tcase(suite, tcase_ldst()); 816 | suite_add_tcase(suite, tcase_addi()); 817 | suite_add_tcase(suite, tcase_ldf()); 818 | suite_add_tcase(suite, tcase_ldb()); 819 | suite_add_tcase(suite, tcase_ldix()); 820 | return suite; 821 | } 822 | -------------------------------------------------------------------------------- /tests/opschip.c: -------------------------------------------------------------------------------- 1 | /* 2 | * chip8 is a CHIP-8 emulator done in C 3 | * Copyright (C) 2015-2016 Dani Rodríguez 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | /* 20 | * File: tests/opschip.c 21 | * Description: Unit test related to SUPER-CHIP Opcodes. 22 | */ 23 | 24 | #include 25 | #include 26 | #include 27 | 28 | struct machine_t cpu; 29 | 30 | static void 31 | setup_cpu(void) 32 | { 33 | init_machine(&cpu); 34 | } 35 | 36 | static TCase* 37 | setup_tcase(char* name) 38 | { 39 | TCase* tcase = tcase_create(name); 40 | tcase_add_checked_fixture(tcase, setup_cpu, NULL); 41 | return tcase; 42 | } 43 | 44 | static void 45 | put_opcode(word opcode, address pos) 46 | { 47 | cpu.mem[pos] = opcode >> 8; 48 | cpu.mem[pos + 1] = opcode & 0xFF; 49 | } 50 | 51 | /* Executing SCD should scroll the screen N pixels down. */ 52 | START_TEST(test_scd_esm_off) 53 | { 54 | /* Clear the screen, but put an horizontal line on Y = 0. */ 55 | cpu.esm = 0; 56 | memset(cpu.screen, 0, 2048); 57 | screen_fill_row(&cpu, 0); 58 | 59 | /* Execute SCD 4. */ 60 | cpu.pc = 0x200; 61 | put_opcode(0x00C4, 0x200); 62 | step_machine(&cpu); 63 | 64 | /* Test execution. */ 65 | ck_assert_int_eq(0x202, cpu.pc); 66 | for (int row = 0; row < 32; row++) { 67 | for (int col = 0; col < 64; col++) { 68 | /* White row on Y = 0 should still be there too!. */ 69 | if (row == 0 || row == 4) { 70 | ck_assert_int_ne(0, screen_get_pixel(&cpu, row, col)); 71 | } else { 72 | ck_assert_int_eq(0, screen_get_pixel(&cpu, row, col)); 73 | } 74 | } 75 | } 76 | } 77 | END_TEST 78 | 79 | START_TEST(test_scd_esm_on) 80 | { 81 | /* Clear the screen, put an horizontal line on Y = 0. */ 82 | cpu.esm = 1; 83 | memset(cpu.screen, 0, 8192); 84 | screen_fill_row(&cpu, 0); 85 | 86 | /* Execute SCD 4. */ 87 | cpu.pc = 0x200; 88 | put_opcode(0x00C4, 0x200); 89 | step_machine(&cpu); 90 | 91 | /* Test execution. */ 92 | ck_assert_int_eq(0x202, cpu.pc); 93 | for (int row = 0; row < 64; row++) { 94 | for (int col = 0; col < 128; col++) { 95 | /* Again, there should still be a line on Y = 0!. */ 96 | if (row == 0 || row == 4) { 97 | ck_assert_int_ne(0, screen_get_pixel(&cpu, row, col)); 98 | } else { 99 | ck_assert_int_eq(0, screen_get_pixel(&cpu, row, col)); 100 | } 101 | } 102 | } 103 | } 104 | END_TEST 105 | 106 | static TCase* 107 | tcase_scd() 108 | { 109 | TCase* tcase = setup_tcase("SCD"); 110 | tcase_add_test(tcase, test_scd_esm_off); 111 | tcase_add_test(tcase, test_scd_esm_on); 112 | return tcase; 113 | } 114 | 115 | /* Executing SCR should scroll the screen 4 pixels to the right. */ 116 | START_TEST(test_scr_esm_off) 117 | { 118 | /* Clear the screen and put a vertical line on X = 0. */ 119 | cpu.esm = 0; 120 | memset(cpu.screen, 0, 2048); 121 | screen_fill_column(&cpu, 0); 122 | 123 | /* Execute SCR. */ 124 | cpu.pc = 0x200; 125 | put_opcode(0x00FB, 0x200); 126 | step_machine(&cpu); 127 | 128 | /* Test execution. */ 129 | ck_assert_int_eq(0x202, cpu.pc); 130 | for (int row = 0; row < 32; row++) { 131 | for (int col = 0; col < 64; col++) { 132 | /* Since we are scrolling right, line at X = 0 should be there. */ 133 | if (col == 0 || col == 4) { 134 | ck_assert_int_ne(0, screen_get_pixel(&cpu, row, col)); 135 | } else { 136 | ck_assert_int_eq(0, screen_get_pixel(&cpu, row, col)); 137 | } 138 | } 139 | } 140 | } 141 | END_TEST 142 | 143 | START_TEST(test_scr_esm_on) 144 | { 145 | /* Clear screen, put vertical line on X = 0. */ 146 | cpu.esm = 1; 147 | memset(cpu.screen, 0, 8192); 148 | screen_fill_column(&cpu, 0); 149 | 150 | /* Execute SCR. */ 151 | cpu.pc = 0x200; 152 | put_opcode(0x00FB, 0x200); 153 | step_machine(&cpu); 154 | 155 | /* Test execution. */ 156 | ck_assert_int_eq(0x202, cpu.pc); 157 | for (int row = 0; row < 64; row++) { 158 | for (int col = 0; col < 128; col++) { 159 | /* Since we scroll right, line at X = 0 should be there too. */ 160 | if (col == 0 || col == 4) { 161 | ck_assert_int_ne(0, screen_get_pixel(&cpu, row, col)); 162 | } else { 163 | ck_assert_int_eq(0, screen_get_pixel(&cpu, row, col)); 164 | } 165 | } 166 | } 167 | } 168 | END_TEST 169 | 170 | static TCase* 171 | tcase_scr() 172 | { 173 | TCase* tcase = setup_tcase("SCR"); 174 | tcase_add_test(tcase, test_scr_esm_off); 175 | tcase_add_test(tcase, test_scr_esm_on); 176 | return tcase; 177 | } 178 | 179 | /* Executing SCL should scroll the screen 4 pixels to the left. */ 180 | START_TEST(test_scl_esm_off) 181 | { 182 | /* Clear the screen and put a vertical line on X = 0. */ 183 | memset(cpu.screen, 0, 2048); 184 | screen_fill_column(&cpu, 4); 185 | 186 | /* Execute SCL. */ 187 | cpu.pc = 0x200; 188 | put_opcode(0x00FC, 0x200); 189 | step_machine(&cpu); 190 | 191 | /* Test execution. */ 192 | ck_assert_int_eq(0x202, cpu.pc); 193 | for (int row = 0; row < 32; row++) { 194 | for (int col = 0; col < 64; col++) { 195 | /* There is no line at X = 4 because is overwritten by X = 8. */ 196 | if (col == 0) { 197 | ck_assert_int_ne(0, screen_get_pixel(&cpu, row, col)); 198 | } else { 199 | ck_assert_int_eq(0, screen_get_pixel(&cpu, row, col)); 200 | } 201 | } 202 | } 203 | } 204 | END_TEST 205 | 206 | START_TEST(test_scl_esm_on) 207 | { 208 | /* Clear thes creen and put a vertical line on X = 4. */ 209 | cpu.esm = 1; 210 | memset(cpu.screen, 0, 8192); 211 | screen_fill_column(&cpu, 4); 212 | 213 | /* Execute SCL. */ 214 | cpu.pc = 0x200; 215 | put_opcode(0x00FC, 0x200); 216 | step_machine(&cpu); 217 | 218 | /* Test execution. */ 219 | ck_assert_int_eq(0x202, cpu.pc); 220 | for (int row = 0; row < 64; row++) { 221 | for (int col = 0; col < 128; col++) { 222 | /* There is no line at X = 4 because is overwritten by X = 8. */ 223 | if (col == 0) { 224 | ck_assert_int_ne(0, screen_get_pixel(&cpu, row, col)); 225 | } else { 226 | ck_assert_int_eq(0, screen_get_pixel(&cpu, row, col)); 227 | } 228 | } 229 | } 230 | } 231 | END_TEST 232 | 233 | static TCase* 234 | tcase_scl() 235 | { 236 | TCase* tcase = setup_tcase("SCL"); 237 | tcase_add_test(tcase, test_scl_esm_off); 238 | tcase_add_test(tcase, test_scl_esm_on); 239 | return tcase; 240 | } 241 | 242 | /* Executing EXIT should set the exit flag to true. */ 243 | START_TEST(test_exit) 244 | { 245 | cpu.exit = 0; 246 | cpu.pc = 0x200; 247 | put_opcode(0x00FD, 0x200); 248 | step_machine(&cpu); 249 | ck_assert_int_eq(0x202, cpu.pc); 250 | ck_assert_int_ne(0, cpu.exit); 251 | } 252 | END_TEST 253 | 254 | static TCase* 255 | tcase_exit() 256 | { 257 | TCase* tcase = setup_tcase("EXIT"); 258 | tcase_add_test(tcase, test_exit); 259 | return tcase; 260 | } 261 | 262 | /* Executing LOW should disable extended screen mode. */ 263 | START_TEST(test_low) 264 | { 265 | cpu.esm = 1; 266 | cpu.pc = 0x200; 267 | put_opcode(0x00FE, 0x200); 268 | step_machine(&cpu); 269 | ck_assert_int_eq(0x202, cpu.pc); 270 | ck_assert_int_eq(0, cpu.esm); 271 | } 272 | END_TEST 273 | 274 | static TCase* 275 | tcase_low() 276 | { 277 | TCase* tcase = setup_tcase("LOW"); 278 | tcase_add_test(tcase, test_low); 279 | return tcase; 280 | } 281 | 282 | /* Executing HIGH should enable extended screen mode. */ 283 | START_TEST(test_high) 284 | { 285 | cpu.esm = 0; 286 | cpu.pc = 0x200; 287 | put_opcode(0x00FF, 0x200); 288 | step_machine(&cpu); 289 | ck_assert_int_eq(0x202, cpu.pc); 290 | ck_assert_int_ne(0, cpu.esm); 291 | } 292 | END_TEST 293 | 294 | static TCase* 295 | tcase_high() 296 | { 297 | TCase* tcase = setup_tcase("HIGH"); 298 | tcase_add_test(tcase, test_high); 299 | return tcase; 300 | } 301 | 302 | /* Executing DRAW with extended mode should render a 16x16 sprite. */ 303 | START_TEST(test_draw_esm) 304 | { 305 | /* Set up sprite. */ 306 | for (int i = 0; i < 32; i++) { 307 | cpu.mem[0x800 + i] = 0xFF; 308 | } 309 | 310 | /* Set up machine. */ 311 | cpu.esm = 1; 312 | memset(cpu.screen, 0, 8192); 313 | cpu.i = 0x800; 314 | put_opcode(0xD110, 0x200); 315 | step_machine(&cpu); 316 | 317 | /* Check that the sprite is drawn. */ 318 | ck_assert_int_eq(0x202, cpu.pc); 319 | for (int row = 0; row < 64; row++) { 320 | for (int col = 0; col < 128; col++) { 321 | if (row < 16 && col < 16) { 322 | ck_assert_int_ne(0, screen_get_pixel(&cpu, row, col)); 323 | } else { 324 | ck_assert_int_eq(0, screen_get_pixel(&cpu, row, col)); 325 | } 326 | } 327 | } 328 | } 329 | END_TEST 330 | 331 | static TCase* 332 | tcase_draw_esm() 333 | { 334 | TCase* tcase = setup_tcase("DRW ESM"); 335 | tcase_add_test(tcase, test_draw_esm); 336 | return tcase; 337 | } 338 | 339 | START_TEST(test_ld_hf) 340 | { 341 | cpu.esm = 1; 342 | put_opcode(0xF030, 0x200); 343 | for (int r = 0; r < 16; r++) { 344 | cpu.v[0] = r; 345 | cpu.pc = 0x200; 346 | step_machine(&cpu); 347 | ck_assert_int_eq(0x8200 + r * 10, cpu.i); 348 | } 349 | } 350 | END_TEST 351 | 352 | static TCase* 353 | tcase_ld_hf() 354 | { 355 | TCase* tcase = setup_tcase("LD HF"); 356 | tcase_add_test(tcase, test_ld_hf); 357 | return tcase; 358 | } 359 | 360 | START_TEST(test_ld_r_v) 361 | { 362 | for (int rg = 0; rg < 8; rg++) { 363 | cpu.v[rg] = rg * 3; 364 | cpu.r[rg] = 0xFF; 365 | } 366 | cpu.pc = 0x200; 367 | put_opcode(0xF775, 0x200); 368 | step_machine(&cpu); 369 | for (int rg = 0; rg < 8; rg++) { 370 | ck_assert_int_eq(rg * 3, cpu.r[rg]); 371 | } 372 | } 373 | END_TEST 374 | 375 | START_TEST(test_ld_r_v_partial) 376 | { 377 | for (int rg = 0; rg < 8; rg++) { 378 | cpu.v[rg] = rg * 3; 379 | cpu.r[rg] = 0xFF; 380 | } 381 | cpu.pc = 0x200; 382 | put_opcode(0xF475, 0x200); 383 | step_machine(&cpu); 384 | for (int rg = 0; rg < 8; rg++) { 385 | if (rg <= 4) { 386 | ck_assert_int_eq(rg * 3, cpu.r[rg]); 387 | } else { 388 | ck_assert_int_eq(0xFF, cpu.r[rg]); 389 | } 390 | } 391 | } 392 | END_TEST 393 | 394 | static TCase* 395 | tcase_ld_r_v() 396 | { 397 | TCase* tcase = setup_tcase("LD R, V"); 398 | tcase_add_test(tcase, test_ld_r_v); 399 | tcase_add_test(tcase, test_ld_r_v_partial); 400 | return tcase; 401 | } 402 | 403 | START_TEST(test_ld_v_r) 404 | { 405 | for (int rg = 0; rg < 8; rg++) { 406 | cpu.r[rg] = rg * 3; 407 | cpu.v[rg] = 0xFF; 408 | } 409 | cpu.pc = 0x200; 410 | put_opcode(0xF785, 0x200); 411 | step_machine(&cpu); 412 | for (int rg = 0; rg < 8; rg++) { 413 | ck_assert_int_eq(rg * 3, cpu.v[rg]); 414 | } 415 | } 416 | END_TEST 417 | 418 | START_TEST(test_ld_v_r_partial) 419 | { 420 | for (int rg = 0; rg < 8; rg++) { 421 | cpu.r[rg] = rg * 3; 422 | cpu.v[rg] = 0xFF; 423 | } 424 | cpu.pc = 0x200; 425 | put_opcode(0xF485, 0x200); 426 | step_machine(&cpu); 427 | for (int rg = 0; rg < 8; rg++) { 428 | if (rg <= 4) { 429 | ck_assert_int_eq(rg * 3, cpu.v[rg]); 430 | } else { 431 | ck_assert_int_eq(0xFF, cpu.v[rg]); 432 | } 433 | } 434 | } 435 | END_TEST 436 | 437 | static TCase* 438 | tcase_ld_v_r() 439 | { 440 | TCase* tcase = setup_tcase("LD V, R"); 441 | tcase_add_test(tcase, test_ld_v_r); 442 | tcase_add_test(tcase, test_ld_v_r_partial); 443 | return tcase; 444 | } 445 | 446 | Suite* 447 | create_superchip_opcodes_suite() 448 | { 449 | Suite* suite = suite_create("SCHIP Opcodes"); 450 | suite_add_tcase(suite, tcase_scd()); 451 | suite_add_tcase(suite, tcase_scr()); 452 | suite_add_tcase(suite, tcase_scl()); 453 | suite_add_tcase(suite, tcase_exit()); 454 | suite_add_tcase(suite, tcase_high()); 455 | suite_add_tcase(suite, tcase_low()); 456 | suite_add_tcase(suite, tcase_draw_esm()); 457 | suite_add_tcase(suite, tcase_ld_hf()); 458 | suite_add_tcase(suite, tcase_ld_r_v()); 459 | suite_add_tcase(suite, tcase_ld_v_r()); 460 | return suite; 461 | } 462 | 463 | -------------------------------------------------------------------------------- /tests/screen.c: -------------------------------------------------------------------------------- 1 | /* 2 | * chip8 is a CHIP-8 emulator done in C 3 | * Copyright (C) 2015-2016 Dani Rodríguez 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | /* 20 | * File: tests/screen.c 21 | * Description: Unit test related to screen management. 22 | */ 23 | 24 | #include 25 | #include 26 | #include 27 | 28 | struct machine_t cpu; 29 | 30 | static void 31 | setup_cpu(void) 32 | { 33 | init_machine(&cpu); 34 | } 35 | 36 | static TCase* 37 | setup_tcase(char* name) 38 | { 39 | TCase* tcase = tcase_create(name); 40 | tcase_add_checked_fixture(tcase, setup_cpu, NULL); 41 | return tcase; 42 | } 43 | 44 | START_TEST(test_screen_fill_column) 45 | { 46 | cpu.esm = 0; 47 | memset(cpu.screen, 0, sizeof (cpu.screen)); 48 | screen_fill_column(&cpu, 4); 49 | for (int y = 0; y < 32; y++) { 50 | for (int x = 0; x < 64; x++) { 51 | if (x == 4) { 52 | ck_assert_int_ne(0, cpu.screen[64 * y + x]); 53 | } else { 54 | ck_assert_int_eq(0, cpu.screen[64 * y + x]); 55 | } 56 | } 57 | } 58 | } 59 | END_TEST 60 | 61 | START_TEST(test_screen_clear_column) 62 | { 63 | cpu.esm = 0; 64 | memset(cpu.screen, 1, sizeof (cpu.screen)); 65 | screen_clear_column(&cpu, 8); 66 | for (int y = 0; y < 32; y++) { 67 | for (int x = 0; x < 64; x++) { 68 | if (x == 8) { 69 | ck_assert_int_eq(0, cpu.screen[64 * y + x]); 70 | } else { 71 | ck_assert_int_ne(0, cpu.screen[64 * y + x]); 72 | } 73 | } 74 | } 75 | } 76 | END_TEST 77 | 78 | START_TEST(test_screen_fill_row) 79 | { 80 | cpu.esm = 0; 81 | memset(cpu.screen, 0, sizeof(cpu.screen)); 82 | screen_fill_row(&cpu, 4); 83 | for (int y = 0; y < 32; y++) { 84 | for (int x = 0; x < 64; x++) { 85 | if (y == 4) { 86 | ck_assert_int_ne(0, cpu.screen[64 * y + x]); 87 | } else { 88 | ck_assert_int_eq(0, cpu.screen[64 * y + x]); 89 | } 90 | } 91 | } 92 | } 93 | END_TEST 94 | 95 | START_TEST(test_screen_clear_row) 96 | { 97 | cpu.esm = 0; 98 | memset(cpu.screen, 1, sizeof(cpu.screen)); 99 | screen_clear_row(&cpu, 6); 100 | for (int y = 0; y < 32; y++) { 101 | for (int x = 0; x < 64; x++) { 102 | if (y == 6) { 103 | ck_assert_int_eq(0, cpu.screen[64 * y + x]); 104 | } else { 105 | ck_assert_int_ne(0, cpu.screen[64 * y + x]); 106 | } 107 | } 108 | } 109 | } 110 | END_TEST 111 | 112 | START_TEST(test_screen_get_pixel) 113 | { 114 | cpu.esm = 0; 115 | memset(cpu.screen, 0, sizeof (cpu.screen)); 116 | cpu.screen[64 * 10 + 10] = 1; 117 | cpu.screen[64 * 20 + 20] = 1; 118 | for (int y = 0; y < 32; y++) { 119 | for (int x = 0; x < 64; x++) { 120 | if (x == 10 && y == 10) { 121 | ck_assert_int_ne(0, screen_get_pixel(&cpu, x, y)); 122 | } else if (x == 20 && y == 20) { 123 | ck_assert_int_ne(0, screen_get_pixel(&cpu, x, y)); 124 | } else { 125 | ck_assert_int_eq(0, screen_get_pixel(&cpu, x, y)); 126 | } 127 | } 128 | } 129 | } 130 | END_TEST 131 | 132 | START_TEST(test_screen_set_pixel) 133 | { 134 | cpu.esm = 0; 135 | memset(cpu.screen, 0, sizeof(cpu.screen)); 136 | screen_set_pixel(&cpu, 10, 10); 137 | screen_set_pixel(&cpu, 20, 20); 138 | for (int y = 0; y < 32; y++) { 139 | for (int x = 0; x < 64; x++) { 140 | if (x == 10 && y == 10) { 141 | ck_assert_int_ne(0, cpu.screen[64 * y + x]); 142 | } else if (x == 20 && y == 20) { 143 | ck_assert_int_ne(0, cpu.screen[64 * y + x]); 144 | } else { 145 | ck_assert_int_eq(0, cpu.screen[64 * y + x]); 146 | } 147 | } 148 | } 149 | } 150 | END_TEST 151 | 152 | START_TEST(test_screen_clear_pixel) 153 | { 154 | cpu.esm = 0; 155 | memset(cpu.screen, 0, sizeof (cpu.screen)); 156 | cpu.screen[64 * 10 + 10] = 1; 157 | cpu.screen[64 * 20 + 20] = 1; 158 | screen_clear_pixel(&cpu, 10, 10); 159 | screen_clear_pixel(&cpu, 20, 20); 160 | ck_assert_int_eq(0, cpu.screen[64 * 10 + 10]); 161 | ck_assert_int_eq(0, cpu.screen[64 * 20 + 20]); 162 | } 163 | END_TEST 164 | 165 | static TCase* 166 | tcase_screen_fill_column() 167 | { 168 | TCase* tcase = setup_tcase("screen_fill_column()"); 169 | tcase_add_test(tcase, test_screen_fill_column); 170 | return tcase; 171 | } 172 | 173 | static TCase* 174 | tcase_screen_clear_column() 175 | { 176 | TCase* tcase = setup_tcase("screen_clear_column()"); 177 | tcase_add_test(tcase, test_screen_clear_column); 178 | return tcase; 179 | } 180 | 181 | static TCase* 182 | tcase_screen_fill_row() 183 | { 184 | TCase* tcase = setup_tcase("screen_fill_row()"); 185 | tcase_add_test(tcase, test_screen_fill_row); 186 | return tcase; 187 | } 188 | 189 | static TCase* 190 | tcase_screen_clear_row() 191 | { 192 | TCase* tcase = setup_tcase("screen_clear_row()"); 193 | tcase_add_test(tcase, test_screen_clear_row); 194 | return tcase; 195 | } 196 | 197 | static TCase* 198 | tcase_screen_get_pixel() 199 | { 200 | TCase* tcase = setup_tcase("screen_get_pixel()"); 201 | tcase_add_test(tcase, test_screen_get_pixel); 202 | return tcase; 203 | } 204 | 205 | static TCase* 206 | tcase_screen_set_pixel() 207 | { 208 | TCase* tcase = setup_tcase("screen_set_pixel()"); 209 | tcase_add_test(tcase, test_screen_set_pixel); 210 | return tcase; 211 | } 212 | 213 | static TCase* 214 | tcase_screen_clear_pixel() 215 | { 216 | TCase* tcase = setup_tcase("screen_clear_pixel()"); 217 | tcase_add_test(tcase, test_screen_clear_pixel); 218 | return tcase; 219 | } 220 | 221 | Suite* 222 | create_screen_suite() 223 | { 224 | Suite* suite = suite_create("Screen management"); 225 | suite_add_tcase(suite, tcase_screen_fill_column()); 226 | suite_add_tcase(suite, tcase_screen_clear_column()); 227 | suite_add_tcase(suite, tcase_screen_fill_row()); 228 | suite_add_tcase(suite, tcase_screen_clear_row()); 229 | suite_add_tcase(suite, tcase_screen_get_pixel()); 230 | suite_add_tcase(suite, tcase_screen_set_pixel()); 231 | suite_add_tcase(suite, tcase_screen_clear_pixel()); 232 | return suite; 233 | } 234 | -------------------------------------------------------------------------------- /tests/test.c: -------------------------------------------------------------------------------- 1 | /* 2 | * chip8 is a CHIP-8 emulator done in C 3 | * Copyright (C) 2015-2016 Dani Rodríguez 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #include 20 | 21 | extern Suite* 22 | create_chip8_opcodes_suite(); 23 | 24 | extern Suite* 25 | create_superchip_opcodes_suite(); 26 | 27 | extern Suite* 28 | create_screen_suite(); 29 | 30 | int main(int argc, char** argv) 31 | { 32 | SRunner* runner = srunner_create(create_chip8_opcodes_suite()); 33 | srunner_add_suite(runner, create_superchip_opcodes_suite()); 34 | srunner_add_suite(runner, create_screen_suite()); 35 | srunner_run_all(runner, CK_VERBOSE); 36 | int failed = srunner_ntests_failed(runner); 37 | srunner_free(runner); 38 | return (failed == 0) ? 0 : 1; 39 | } 40 | --------------------------------------------------------------------------------