├── .gitignore ├── CREDITS.txt ├── LICENSE-Paulscode IBXM Library.txt ├── LICENSE-Paulscode SoundSystem CodecIBXM.txt ├── LICENSE.txt ├── README.md ├── build.gradle ├── forge-1.12.2-14.23.5.2768-changelog.txt ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── src └── main ├── java ├── deltatwoforce │ └── mcemu │ │ ├── MCEmu.java │ │ ├── MCEmuBlocks.java │ │ ├── MCEmuItems.java │ │ └── nes │ │ ├── BlockNESConsole.java │ │ ├── BlockTelevision.java │ │ ├── ItemNESCartridge.java │ │ ├── MCEmuNESRenderer.java │ │ ├── TelevisionSpecialRenderer.java │ │ └── TelevisionTileEntity.java └── jp │ └── tanakh │ └── bjne │ └── nes │ ├── Apu.java │ ├── Cpu.java │ ├── Mapper.java │ ├── MapperAdapter.java │ ├── MapperMaker.java │ ├── Mbc.java │ ├── Nes.java │ ├── Ppu.java │ ├── Regs.java │ ├── Renderer.java │ ├── Rom.java │ └── mapper │ ├── CNROM.java │ ├── MMC1.java │ ├── MMC3.java │ ├── NullMapper.java │ ├── UNROM.java │ └── VRC6.java └── resources ├── assets └── mcemu │ ├── blockstates │ ├── nesconsole.json │ └── television.json │ ├── lang │ └── en_us.lang │ ├── models │ ├── block │ │ ├── nesconsole.json │ │ └── television.json │ └── item │ │ ├── nescartridge.json │ │ ├── nesconsole.json │ │ └── television.json │ └── textures │ ├── blocks │ ├── nesback.png │ ├── nesbottom.png │ ├── nesfront.png │ ├── nesside.png │ └── nestop.png │ └── items │ └── cart1.png ├── mcmod.info └── pack.mcmeta /.gitignore: -------------------------------------------------------------------------------- 1 | # eclipse 2 | bin 3 | *.launch 4 | .settings 5 | .metadata 6 | .classpath 7 | .project 8 | 9 | # idea 10 | out 11 | *.ipr 12 | *.iws 13 | *.iml 14 | .idea 15 | 16 | # gradle 17 | build 18 | .gradle 19 | 20 | # other 21 | eclipse 22 | run 23 | -------------------------------------------------------------------------------- /CREDITS.txt: -------------------------------------------------------------------------------- 1 | Minecraft Forge: Credits/Thank You 2 | 3 | Forge is a set of tools and modifications to the Minecraft base game code to assist 4 | mod developers in creating new and exciting content. It has been in development for 5 | several years now, but I would like to take this time thank a few people who have 6 | helped it along it's way. 7 | 8 | First, the people who originally created the Forge projects way back in Minecraft 9 | alpha. Eloraam of RedPower, and SpaceToad of Buildcraft, without their acceptiance 10 | of me taking over the project, who knows what Minecraft modding would be today. 11 | 12 | Secondly, someone who has worked with me, and developed some of the core features 13 | that allow modding to he as functional, and as simple as it is, cpw. For developing 14 | FML, which stabelized the client and server modding ecosystem. As well as the base 15 | loading system that allows us to modify Minecraft's code as elegently as possible. 16 | 17 | Mezz, who has stepped up as the issue and pull request manager. Helping to keep me 18 | sane as well as guiding the community into creating better additions to Forge. 19 | 20 | Searge, Bspks, Fesh0r, ProfMobious, and all the rest over on the MCP team {of which 21 | I am a part}. For creating some of the core tools needed to make Minecraft modding 22 | both possible, and as stable as can be. 23 | On that note, here is some specific information of the MCP data we use: 24 | * Minecraft Coder Pack (MCP) * 25 | Forge Mod Loader and Minecraft Forge have permission to distribute and automatically 26 | download components of MCP and distribute MCP data files. This permission is not 27 | transitive and others wishing to redistribute the Minecraft Forge source independently 28 | should seek permission of MCP or remove the MCP data files and request their users 29 | to download MCP separately. 30 | 31 | And lastly, the countless community members who have spent time submitting bug reports, 32 | pull requests, and just helping out the community in general. Thank you. 33 | 34 | --LexManos 35 | 36 | ========================================================================= 37 | 38 | This is Forge Mod Loader. 39 | 40 | You can find the source code at all times at https://github.com/MinecraftForge/MinecraftForge/tree/1.12.x/src/main/java/net/minecraftforge/fml 41 | 42 | This minecraft mod is a clean open source implementation of a mod loader for minecraft servers 43 | and minecraft clients. 44 | 45 | The code is authored by cpw. 46 | 47 | It began by partially implementing an API defined by the client side ModLoader, authored by Risugami. 48 | http://www.minecraftforum.net/topic/75440- 49 | This support has been dropped as of Minecraft release 1.7, as Risugami no longer maintains ModLoader. 50 | 51 | It also contains suggestions and hints and generous helpings of code from LexManos, author of MinecraftForge. 52 | http://www.minecraftforge.net/ 53 | 54 | Additionally, it contains an implementation of topological sort based on that 55 | published at http://keithschwarz.com/interesting/code/?dir=topological-sort 56 | 57 | It also contains code from the Maven project for performing versioned dependency 58 | resolution. http://maven.apache.org/ 59 | 60 | It also contains a partial repackaging of the javaxdelta library from http://sourceforge.net/projects/javaxdelta/ 61 | with credit to it's authors. 62 | 63 | Forge Mod Loader downloads components from the Minecraft Coder Pack 64 | (http://mcp.ocean-labs.de/index.php/Main_Page) with kind permission from the MCP team. 65 | 66 | -------------------------------------------------------------------------------- /LICENSE-Paulscode IBXM Library.txt: -------------------------------------------------------------------------------- 1 | IBXM is copyright (c) 2007, Martin Cameron, and is licensed under the BSD License. 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 5 | 6 | Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 7 | Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 8 | Neither the name of mumart nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 9 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 10 | 11 | -------------------------------------------------------------------------------- /LICENSE-Paulscode SoundSystem CodecIBXM.txt: -------------------------------------------------------------------------------- 1 | SoundSystem CodecIBXM Class License: 2 | 3 | You are free to use this class for any purpose, commercial or otherwise. 4 | You may modify this class or source code, and distribute it any way you 5 | like, provided the following conditions are met: 6 | 7 | 1) You may not falsely claim to be the author of this class or any 8 | unmodified portion of it. 9 | 2) You may not copyright this class or a modified version of it and then 10 | sue me for copyright infringement. 11 | 3) If you modify the source code, you must clearly document the changes 12 | made before redistributing the modified source code, so other users know 13 | it is not the original code. 14 | 4) You are not required to give me credit for this class in any derived 15 | work, but if you do, you must also mention my website: 16 | http://www.paulscode.com 17 | 5) I the author will not be responsible for any damages (physical, 18 | financial, or otherwise) caused by the use if this class or any 19 | portion of it. 20 | 6) I the author do not guarantee, warrant, or make any representations, 21 | either expressed or implied, regarding the use of this class or any 22 | portion of it. 23 | 24 | Author: Paul Lamb 25 | http://www.paulscode.com 26 | 27 | 28 | This software is based on or using the IBXM library available from 29 | http://www.geocities.com/sunet2000/ 30 | 31 | 32 | IBXM is copyright (c) 2007, Martin Cameron, and is licensed under the BSD License. 33 | All rights reserved. 34 | 35 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 36 | 37 | Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 38 | Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 39 | Neither the name of mumart nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 40 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 41 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Unless noted below, Minecraft Forge, Forge Mod Loader, and all 2 | parts herein are licensed under the terms of the LGPL 2.1 found 3 | here http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt and 4 | copied below. 5 | 6 | Homepage: http://MinecraftForge.net/ 7 | http://github.com/MinecraftForge/MinecraftForge 8 | 9 | 10 | A note on authorship: 11 | All source artifacts are property of their original author, with 12 | the exclusion of the contents of the patches directory and others 13 | copied from it from time to time. Authorship of the contents of 14 | the patches directory is retained by the Minecraft Forge project. 15 | This is because the patches are partially machine generated 16 | artifacts, and are changed heavily due to the way forge works. 17 | Individual attribution within them is impossible. 18 | 19 | Consent: 20 | All contributions to Forge must consent to the release of any 21 | patch content to the Forge project. 22 | 23 | A note on infectivity: 24 | The LGPL is chosen specifically so that projects may depend on Forge 25 | features without being infected with its license. That is the 26 | purpose of the LGPL. Mods and others using this code via ordinary 27 | Java mechanics for referencing libraries are specifically not bound 28 | by Forge's license for the Mod code. 29 | 30 | 31 | === MCP Data === 32 | This software includes data from the Minecraft Coder Pack (MCP), with kind permission 33 | from them. The license to MCP data is not transitive - distribution of this data by 34 | third parties requires independent licensing from the MCP team. This data is not 35 | redistributable without permission from the MCP team. 36 | 37 | === Sharing === 38 | I grant permission for some parts of FML to be redistributed outside the terms of the LGPL, for the benefit of 39 | the minecraft modding community. All contributions to these parts should be licensed under the same additional grant. 40 | 41 | -- Runtime patcher -- 42 | License is granted to redistribute the runtime patcher code (src/main/java/net/minecraftforge/fml/common/patcher 43 | and subdirectories) under any alternative open source license as classified by the OSI (http://opensource.org/licenses) 44 | 45 | -- ASM transformers -- 46 | License is granted to redistribute the ASM transformer code (src/main/java/net/minecraftforge/common/asm/ and subdirectories) 47 | under any alternative open source license as classified by the OSI (http://opensource.org/licenses) 48 | 49 | ========================================================================= 50 | This software includes portions from the Apache Maven project at 51 | http://maven.apache.org/ specifically the ComparableVersion.java code. It is 52 | included based on guidelines at 53 | http://www.softwarefreedom.org/resources/2007/gpl-non-gpl-collaboration.html 54 | with notices intact. The only change is a non-functional change of package name. 55 | 56 | This software contains a partial repackaging of javaxdelta, a BSD licensed program for generating 57 | binary differences and applying them, sourced from the subversion at http://sourceforge.net/projects/javaxdelta/ 58 | authored by genman, heikok, pivot. 59 | The only changes are to replace some Trove collection types with standard Java collections, and repackaged. 60 | 61 | This software contains potions of Paulscodee IBXM library, a BSD liceensed library for 62 | loading and playing IBXM formated auto. No modifications havee beeen made. The associated 63 | licenses can be found along side this one, or at 64 | https://github.com/MinecraftForge/MinecraftForge/blob/1.12.x/LICENSE-Paulscode%20IBXM%20Library.txt 65 | https://github.com/MinecraftForge/MinecraftForge/blob/1.12.x/LICENSE-Paulscode%20SoundSystem%20CodecIBXM.txt 66 | ========================================================================= 67 | 68 | 69 | GNU LESSER GENERAL PUBLIC LICENSE 70 | Version 2.1, February 1999 71 | 72 | Copyright (C) 1991, 1999 Free Software Foundation, Inc. 73 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 74 | Everyone is permitted to copy and distribute verbatim copies 75 | of this license document, but changing it is not allowed. 76 | 77 | [This is the first released version of the Lesser GPL. It also counts 78 | as the successor of the GNU Library Public License, version 2, hence 79 | the version number 2.1.] 80 | 81 | Preamble 82 | 83 | The licenses for most software are designed to take away your 84 | freedom to share and change it. By contrast, the GNU General Public 85 | Licenses are intended to guarantee your freedom to share and change 86 | free software--to make sure the software is free for all its users. 87 | 88 | This license, the Lesser General Public License, applies to some 89 | specially designated software packages--typically libraries--of the 90 | Free Software Foundation and other authors who decide to use it. You 91 | can use it too, but we suggest you first think carefully about whether 92 | this license or the ordinary General Public License is the better 93 | strategy to use in any particular case, based on the explanations below. 94 | 95 | When we speak of free software, we are referring to freedom of use, 96 | not price. Our General Public Licenses are designed to make sure that 97 | you have the freedom to distribute copies of free software (and charge 98 | for this service if you wish); that you receive source code or can get 99 | it if you want it; that you can change the software and use pieces of 100 | it in new free programs; and that you are informed that you can do 101 | these things. 102 | 103 | To protect your rights, we need to make restrictions that forbid 104 | distributors to deny you these rights or to ask you to surrender these 105 | rights. These restrictions translate to certain responsibilities for 106 | you if you distribute copies of the library or if you modify it. 107 | 108 | For example, if you distribute copies of the library, whether gratis 109 | or for a fee, you must give the recipients all the rights that we gave 110 | you. You must make sure that they, too, receive or can get the source 111 | code. If you link other code with the library, you must provide 112 | complete object files to the recipients, so that they can relink them 113 | with the library after making changes to the library and recompiling 114 | it. And you must show them these terms so they know their rights. 115 | 116 | We protect your rights with a two-step method: (1) we copyright the 117 | library, and (2) we offer you this license, which gives you legal 118 | permission to copy, distribute and/or modify the library. 119 | 120 | To protect each distributor, we want to make it very clear that 121 | there is no warranty for the free library. Also, if the library is 122 | modified by someone else and passed on, the recipients should know 123 | that what they have is not the original version, so that the original 124 | author's reputation will not be affected by problems that might be 125 | introduced by others. 126 | 127 | Finally, software patents pose a constant threat to the existence of 128 | any free program. We wish to make sure that a company cannot 129 | effectively restrict the users of a free program by obtaining a 130 | restrictive license from a patent holder. Therefore, we insist that 131 | any patent license obtained for a version of the library must be 132 | consistent with the full freedom of use specified in this license. 133 | 134 | Most GNU software, including some libraries, is covered by the 135 | ordinary GNU General Public License. This license, the GNU Lesser 136 | General Public License, applies to certain designated libraries, and 137 | is quite different from the ordinary General Public License. We use 138 | this license for certain libraries in order to permit linking those 139 | libraries into non-free programs. 140 | 141 | When a program is linked with a library, whether statically or using 142 | a shared library, the combination of the two is legally speaking a 143 | combined work, a derivative of the original library. The ordinary 144 | General Public License therefore permits such linking only if the 145 | entire combination fits its criteria of freedom. The Lesser General 146 | Public License permits more lax criteria for linking other code with 147 | the library. 148 | 149 | We call this license the "Lesser" General Public License because it 150 | does Less to protect the user's freedom than the ordinary General 151 | Public License. It also provides other free software developers Less 152 | of an advantage over competing non-free programs. These disadvantages 153 | are the reason we use the ordinary General Public License for many 154 | libraries. However, the Lesser license provides advantages in certain 155 | special circumstances. 156 | 157 | For example, on rare occasions, there may be a special need to 158 | encourage the widest possible use of a certain library, so that it becomes 159 | a de-facto standard. To achieve this, non-free programs must be 160 | allowed to use the library. A more frequent case is that a free 161 | library does the same job as widely used non-free libraries. In this 162 | case, there is little to gain by limiting the free library to free 163 | software only, so we use the Lesser General Public License. 164 | 165 | In other cases, permission to use a particular library in non-free 166 | programs enables a greater number of people to use a large body of 167 | free software. For example, permission to use the GNU C Library in 168 | non-free programs enables many more people to use the whole GNU 169 | operating system, as well as its variant, the GNU/Linux operating 170 | system. 171 | 172 | Although the Lesser General Public License is Less protective of the 173 | users' freedom, it does ensure that the user of a program that is 174 | linked with the Library has the freedom and the wherewithal to run 175 | that program using a modified version of the Library. 176 | 177 | The precise terms and conditions for copying, distribution and 178 | modification follow. Pay close attention to the difference between a 179 | "work based on the library" and a "work that uses the library". The 180 | former contains code derived from the library, whereas the latter must 181 | be combined with the library in order to run. 182 | 183 | GNU LESSER GENERAL PUBLIC LICENSE 184 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 185 | 186 | 0. This License Agreement applies to any software library or other 187 | program which contains a notice placed by the copyright holder or 188 | other authorized party saying it may be distributed under the terms of 189 | this Lesser General Public License (also called "this License"). 190 | Each licensee is addressed as "you". 191 | 192 | A "library" means a collection of software functions and/or data 193 | prepared so as to be conveniently linked with application programs 194 | (which use some of those functions and data) to form executables. 195 | 196 | The "Library", below, refers to any such software library or work 197 | which has been distributed under these terms. A "work based on the 198 | Library" means either the Library or any derivative work under 199 | copyright law: that is to say, a work containing the Library or a 200 | portion of it, either verbatim or with modifications and/or translated 201 | straightforwardly into another language. (Hereinafter, translation is 202 | included without limitation in the term "modification".) 203 | 204 | "Source code" for a work means the preferred form of the work for 205 | making modifications to it. For a library, complete source code means 206 | all the source code for all modules it contains, plus any associated 207 | interface definition files, plus the scripts used to control compilation 208 | and installation of the library. 209 | 210 | Activities other than copying, distribution and modification are not 211 | covered by this License; they are outside its scope. The act of 212 | running a program using the Library is not restricted, and output from 213 | such a program is covered only if its contents constitute a work based 214 | on the Library (independent of the use of the Library in a tool for 215 | writing it). Whether that is true depends on what the Library does 216 | and what the program that uses the Library does. 217 | 218 | 1. You may copy and distribute verbatim copies of the Library's 219 | complete source code as you receive it, in any medium, provided that 220 | you conspicuously and appropriately publish on each copy an 221 | appropriate copyright notice and disclaimer of warranty; keep intact 222 | all the notices that refer to this License and to the absence of any 223 | warranty; and distribute a copy of this License along with the 224 | Library. 225 | 226 | You may charge a fee for the physical act of transferring a copy, 227 | and you may at your option offer warranty protection in exchange for a 228 | fee. 229 | 230 | 2. You may modify your copy or copies of the Library or any portion 231 | of it, thus forming a work based on the Library, and copy and 232 | distribute such modifications or work under the terms of Section 1 233 | above, provided that you also meet all of these conditions: 234 | 235 | a) The modified work must itself be a software library. 236 | 237 | b) You must cause the files modified to carry prominent notices 238 | stating that you changed the files and the date of any change. 239 | 240 | c) You must cause the whole of the work to be licensed at no 241 | charge to all third parties under the terms of this License. 242 | 243 | d) If a facility in the modified Library refers to a function or a 244 | table of data to be supplied by an application program that uses 245 | the facility, other than as an argument passed when the facility 246 | is invoked, then you must make a good faith effort to ensure that, 247 | in the event an application does not supply such function or 248 | table, the facility still operates, and performs whatever part of 249 | its purpose remains meaningful. 250 | 251 | (For example, a function in a library to compute square roots has 252 | a purpose that is entirely well-defined independent of the 253 | application. Therefore, Subsection 2d requires that any 254 | application-supplied function or table used by this function must 255 | be optional: if the application does not supply it, the square 256 | root function must still compute square roots.) 257 | 258 | These requirements apply to the modified work as a whole. If 259 | identifiable sections of that work are not derived from the Library, 260 | and can be reasonably considered independent and separate works in 261 | themselves, then this License, and its terms, do not apply to those 262 | sections when you distribute them as separate works. But when you 263 | distribute the same sections as part of a whole which is a work based 264 | on the Library, the distribution of the whole must be on the terms of 265 | this License, whose permissions for other licensees extend to the 266 | entire whole, and thus to each and every part regardless of who wrote 267 | it. 268 | 269 | Thus, it is not the intent of this section to claim rights or contest 270 | your rights to work written entirely by you; rather, the intent is to 271 | exercise the right to control the distribution of derivative or 272 | collective works based on the Library. 273 | 274 | In addition, mere aggregation of another work not based on the Library 275 | with the Library (or with a work based on the Library) on a volume of 276 | a storage or distribution medium does not bring the other work under 277 | the scope of this License. 278 | 279 | 3. You may opt to apply the terms of the ordinary GNU General Public 280 | License instead of this License to a given copy of the Library. To do 281 | this, you must alter all the notices that refer to this License, so 282 | that they refer to the ordinary GNU General Public License, version 2, 283 | instead of to this License. (If a newer version than version 2 of the 284 | ordinary GNU General Public License has appeared, then you can specify 285 | that version instead if you wish.) Do not make any other change in 286 | these notices. 287 | 288 | Once this change is made in a given copy, it is irreversible for 289 | that copy, so the ordinary GNU General Public License applies to all 290 | subsequent copies and derivative works made from that copy. 291 | 292 | This option is useful when you wish to copy part of the code of 293 | the Library into a program that is not a library. 294 | 295 | 4. You may copy and distribute the Library (or a portion or 296 | derivative of it, under Section 2) in object code or executable form 297 | under the terms of Sections 1 and 2 above provided that you accompany 298 | it with the complete corresponding machine-readable source code, which 299 | must be distributed under the terms of Sections 1 and 2 above on a 300 | medium customarily used for software interchange. 301 | 302 | If distribution of object code is made by offering access to copy 303 | from a designated place, then offering equivalent access to copy the 304 | source code from the same place satisfies the requirement to 305 | distribute the source code, even though third parties are not 306 | compelled to copy the source along with the object code. 307 | 308 | 5. A program that contains no derivative of any portion of the 309 | Library, but is designed to work with the Library by being compiled or 310 | linked with it, is called a "work that uses the Library". Such a 311 | work, in isolation, is not a derivative work of the Library, and 312 | therefore falls outside the scope of this License. 313 | 314 | However, linking a "work that uses the Library" with the Library 315 | creates an executable that is a derivative of the Library (because it 316 | contains portions of the Library), rather than a "work that uses the 317 | library". The executable is therefore covered by this License. 318 | Section 6 states terms for distribution of such executables. 319 | 320 | When a "work that uses the Library" uses material from a header file 321 | that is part of the Library, the object code for the work may be a 322 | derivative work of the Library even though the source code is not. 323 | Whether this is true is especially significant if the work can be 324 | linked without the Library, or if the work is itself a library. The 325 | threshold for this to be true is not precisely defined by law. 326 | 327 | If such an object file uses only numerical parameters, data 328 | structure layouts and accessors, and small macros and small inline 329 | functions (ten lines or less in length), then the use of the object 330 | file is unrestricted, regardless of whether it is legally a derivative 331 | work. (Executables containing this object code plus portions of the 332 | Library will still fall under Section 6.) 333 | 334 | Otherwise, if the work is a derivative of the Library, you may 335 | distribute the object code for the work under the terms of Section 6. 336 | Any executables containing that work also fall under Section 6, 337 | whether or not they are linked directly with the Library itself. 338 | 339 | 6. As an exception to the Sections above, you may also combine or 340 | link a "work that uses the Library" with the Library to produce a 341 | work containing portions of the Library, and distribute that work 342 | under terms of your choice, provided that the terms permit 343 | modification of the work for the customer's own use and reverse 344 | engineering for debugging such modifications. 345 | 346 | You must give prominent notice with each copy of the work that the 347 | Library is used in it and that the Library and its use are covered by 348 | this License. You must supply a copy of this License. If the work 349 | during execution displays copyright notices, you must include the 350 | copyright notice for the Library among them, as well as a reference 351 | directing the user to the copy of this License. Also, you must do one 352 | of these things: 353 | 354 | a) Accompany the work with the complete corresponding 355 | machine-readable source code for the Library including whatever 356 | changes were used in the work (which must be distributed under 357 | Sections 1 and 2 above); and, if the work is an executable linked 358 | with the Library, with the complete machine-readable "work that 359 | uses the Library", as object code and/or source code, so that the 360 | user can modify the Library and then relink to produce a modified 361 | executable containing the modified Library. (It is understood 362 | that the user who changes the contents of definitions files in the 363 | Library will not necessarily be able to recompile the application 364 | to use the modified definitions.) 365 | 366 | b) Use a suitable shared library mechanism for linking with the 367 | Library. A suitable mechanism is one that (1) uses at run time a 368 | copy of the library already present on the user's computer system, 369 | rather than copying library functions into the executable, and (2) 370 | will operate properly with a modified version of the library, if 371 | the user installs one, as long as the modified version is 372 | interface-compatible with the version that the work was made with. 373 | 374 | c) Accompany the work with a written offer, valid for at 375 | least three years, to give the same user the materials 376 | specified in Subsection 6a, above, for a charge no more 377 | than the cost of performing this distribution. 378 | 379 | d) If distribution of the work is made by offering access to copy 380 | from a designated place, offer equivalent access to copy the above 381 | specified materials from the same place. 382 | 383 | e) Verify that the user has already received a copy of these 384 | materials or that you have already sent this user a copy. 385 | 386 | For an executable, the required form of the "work that uses the 387 | Library" must include any data and utility programs needed for 388 | reproducing the executable from it. However, as a special exception, 389 | the materials to be distributed need not include anything that is 390 | normally distributed (in either source or binary form) with the major 391 | components (compiler, kernel, and so on) of the operating system on 392 | which the executable runs, unless that component itself accompanies 393 | the executable. 394 | 395 | It may happen that this requirement contradicts the license 396 | restrictions of other proprietary libraries that do not normally 397 | accompany the operating system. Such a contradiction means you cannot 398 | use both them and the Library together in an executable that you 399 | distribute. 400 | 401 | 7. You may place library facilities that are a work based on the 402 | Library side-by-side in a single library together with other library 403 | facilities not covered by this License, and distribute such a combined 404 | library, provided that the separate distribution of the work based on 405 | the Library and of the other library facilities is otherwise 406 | permitted, and provided that you do these two things: 407 | 408 | a) Accompany the combined library with a copy of the same work 409 | based on the Library, uncombined with any other library 410 | facilities. This must be distributed under the terms of the 411 | Sections above. 412 | 413 | b) Give prominent notice with the combined library of the fact 414 | that part of it is a work based on the Library, and explaining 415 | where to find the accompanying uncombined form of the same work. 416 | 417 | 8. You may not copy, modify, sublicense, link with, or distribute 418 | the Library except as expressly provided under this License. Any 419 | attempt otherwise to copy, modify, sublicense, link with, or 420 | distribute the Library is void, and will automatically terminate your 421 | rights under this License. However, parties who have received copies, 422 | or rights, from you under this License will not have their licenses 423 | terminated so long as such parties remain in full compliance. 424 | 425 | 9. You are not required to accept this License, since you have not 426 | signed it. However, nothing else grants you permission to modify or 427 | distribute the Library or its derivative works. These actions are 428 | prohibited by law if you do not accept this License. Therefore, by 429 | modifying or distributing the Library (or any work based on the 430 | Library), you indicate your acceptance of this License to do so, and 431 | all its terms and conditions for copying, distributing or modifying 432 | the Library or works based on it. 433 | 434 | 10. Each time you redistribute the Library (or any work based on the 435 | Library), the recipient automatically receives a license from the 436 | original licensor to copy, distribute, link with or modify the Library 437 | subject to these terms and conditions. You may not impose any further 438 | restrictions on the recipients' exercise of the rights granted herein. 439 | You are not responsible for enforcing compliance by third parties with 440 | this License. 441 | 442 | 11. If, as a consequence of a court judgment or allegation of patent 443 | infringement or for any other reason (not limited to patent issues), 444 | conditions are imposed on you (whether by court order, agreement or 445 | otherwise) that contradict the conditions of this License, they do not 446 | excuse you from the conditions of this License. If you cannot 447 | distribute so as to satisfy simultaneously your obligations under this 448 | License and any other pertinent obligations, then as a consequence you 449 | may not distribute the Library at all. For example, if a patent 450 | license would not permit royalty-free redistribution of the Library by 451 | all those who receive copies directly or indirectly through you, then 452 | the only way you could satisfy both it and this License would be to 453 | refrain entirely from distribution of the Library. 454 | 455 | If any portion of this section is held invalid or unenforceable under any 456 | particular circumstance, the balance of the section is intended to apply, 457 | and the section as a whole is intended to apply in other circumstances. 458 | 459 | It is not the purpose of this section to induce you to infringe any 460 | patents or other property right claims or to contest validity of any 461 | such claims; this section has the sole purpose of protecting the 462 | integrity of the free software distribution system which is 463 | implemented by public license practices. Many people have made 464 | generous contributions to the wide range of software distributed 465 | through that system in reliance on consistent application of that 466 | system; it is up to the author/donor to decide if he or she is willing 467 | to distribute software through any other system and a licensee cannot 468 | impose that choice. 469 | 470 | This section is intended to make thoroughly clear what is believed to 471 | be a consequence of the rest of this License. 472 | 473 | 12. If the distribution and/or use of the Library is restricted in 474 | certain countries either by patents or by copyrighted interfaces, the 475 | original copyright holder who places the Library under this License may add 476 | an explicit geographical distribution limitation excluding those countries, 477 | so that distribution is permitted only in or among countries not thus 478 | excluded. In such case, this License incorporates the limitation as if 479 | written in the body of this License. 480 | 481 | 13. The Free Software Foundation may publish revised and/or new 482 | versions of the Lesser General Public License from time to time. 483 | Such new versions will be similar in spirit to the present version, 484 | but may differ in detail to address new problems or concerns. 485 | 486 | Each version is given a distinguishing version number. If the Library 487 | specifies a version number of this License which applies to it and 488 | "any later version", you have the option of following the terms and 489 | conditions either of that version or of any later version published by 490 | the Free Software Foundation. If the Library does not specify a 491 | license version number, you may choose any version ever published by 492 | the Free Software Foundation. 493 | 494 | 14. If you wish to incorporate parts of the Library into other free 495 | programs whose distribution conditions are incompatible with these, 496 | write to the author to ask for permission. For software which is 497 | copyrighted by the Free Software Foundation, write to the Free 498 | Software Foundation; we sometimes make exceptions for this. Our 499 | decision will be guided by the two goals of preserving the free status 500 | of all derivatives of our free software and of promoting the sharing 501 | and reuse of software generally. 502 | 503 | NO WARRANTY 504 | 505 | 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO 506 | WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. 507 | EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR 508 | OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY 509 | KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE 510 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 511 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE 512 | LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME 513 | THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 514 | 515 | 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN 516 | WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY 517 | AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU 518 | FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR 519 | CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE 520 | LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING 521 | RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A 522 | FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF 523 | SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH 524 | DAMAGES. 525 | 526 | END OF TERMS AND CONDITIONS 527 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MCEmu 2 | Run emulators in Minecraft! 3 | ## Installation 4 | First, you need [Forge for Minecraft 1.12.2](https://files.minecraftforge.net/maven/net/minecraftforge/forge/index_1.12.2.html). (Download the recommended version) 5 | 6 | Then you need to put the .jar into your mods folder. After opening the game with the mod installed once, you will see a new folder in your minecraft folder called "roms". Go into "roms" and put your NES roms into the "nes" folder. After you restart the game, you will see the roms as cartridges in the "MCEmu: NES" creative category. 7 | ## Building 8 | ### Linux 9 | To build, run `./gradlew build` in the root directory of the project. 10 | ### Windows 11 | To build, run `gradlew.bat build` using cmd in the root directory of the project. 12 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | jcenter() 4 | maven { url = "http://files.minecraftforge.net/maven" } 5 | } 6 | dependencies { 7 | classpath 'net.minecraftforge.gradle:ForgeGradle:2.3-SNAPSHOT' 8 | } 9 | } 10 | apply plugin: 'net.minecraftforge.gradle.forge' 11 | //Only edit below this line, the above code adds and enables the necessary things for Forge to be setup. 12 | 13 | 14 | version = "1.0" 15 | group = "deltatwoforce.mcemu" // http://maven.apache.org/guides/mini/guide-naming-conventions.html 16 | archivesBaseName = "mcemu" 17 | 18 | sourceCompatibility = targetCompatibility = '1.8' // Need this here so eclipse task generates correctly. 19 | compileJava { 20 | sourceCompatibility = targetCompatibility = '1.8' 21 | } 22 | 23 | minecraft { 24 | version = "1.12.2-14.23.5.2768" 25 | runDir = "run" 26 | 27 | // the mappings can be changed at any time, and must be in the following format. 28 | // snapshot_YYYYMMDD snapshot are built nightly. 29 | // stable_# stables are built at the discretion of the MCP team. 30 | // Use non-default mappings at your own risk. they may not always work. 31 | // simply re-run your setup task after changing the mappings to update your workspace. 32 | mappings = "snapshot_20171003" 33 | // makeObfSourceJar = false // an Srg named sources jar is made by default. uncomment this to disable. 34 | } 35 | 36 | dependencies { 37 | // you may put jars on which you depend on in ./libs 38 | // or you may define them like so.. 39 | //compile "some.group:artifact:version:classifier" 40 | //compile "some.group:artifact:version" 41 | 42 | // real examples 43 | //compile 'com.mod-buildcraft:buildcraft:6.0.8:dev' // adds buildcraft to the dev env 44 | //compile 'com.googlecode.efficient-java-matrix-library:ejml:0.24' // adds ejml to the dev env 45 | 46 | // the 'provided' configuration is for optional dependencies that exist at compile-time but might not at runtime. 47 | //provided 'com.mod-buildcraft:buildcraft:6.0.8:dev' 48 | 49 | // the deobf configurations: 'deobfCompile' and 'deobfProvided' are the same as the normal compile and provided, 50 | // except that these dependencies get remapped to your current MCP mappings 51 | //deobfCompile 'com.mod-buildcraft:buildcraft:6.0.8:dev' 52 | //deobfProvided 'com.mod-buildcraft:buildcraft:6.0.8:dev' 53 | 54 | // for more info... 55 | // http://www.gradle.org/docs/current/userguide/artifact_dependencies_tutorial.html 56 | // http://www.gradle.org/docs/current/userguide/dependency_management.html 57 | 58 | } 59 | 60 | processResources { 61 | // this will ensure that this task is redone when the versions change. 62 | inputs.property "version", project.version 63 | inputs.property "mcversion", project.minecraft.version 64 | 65 | // replace stuff in mcmod.info, nothing else 66 | from(sourceSets.main.resources.srcDirs) { 67 | include 'mcmod.info' 68 | 69 | // replace version and mcversion 70 | expand 'version':project.version, 'mcversion':project.minecraft.version 71 | } 72 | 73 | // copy everything else except the mcmod.info 74 | from(sourceSets.main.resources.srcDirs) { 75 | exclude 'mcmod.info' 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /forge-1.12.2-14.23.5.2768-changelog.txt: -------------------------------------------------------------------------------- 1 | Changelog: 2 | Build 2768: 3 | LexManos: Bump version number for RB. 4 | 5 | Build 2767: 6 | LexManos: 7 | Change biome spawn list entries to use factory method where possible 8 | (#5075) 9 | LexManos: Prevent some texture loading errors from crashing the game (#5121) 10 | LexManos: Patch PotionHelper to use registry delegates (#5142) 11 | LexManos: Add a notification event for handling game rule changes (#5152) 12 | 13 | Build 2766: 14 | LexManos: 15 | Change universal bucket support to use fluid names instead of instances 16 | (#5031) 17 | 18 | Build 2765: 19 | LexManos: Fix NPE on clientside entities constructed with null world (#5170) 20 | 21 | Build 2764: 22 | tterrag: Fix patches from #5160 running on the client and causing stutter 23 | 24 | Build 2763: 25 | LexManos: 26 | Class transformer optimizations (#5159) 27 | * Filter packages for deobf transformation 28 | * Only serialize transformed class with TerminalTransformer if bytecode 29 | changed 30 | 31 | Build 2762: 32 | github: Update github stale so issues can be Assigned 33 | 34 | Build 2761: 35 | LexManos: 36 | Fix MC-136995 - Chunk loading and unloading issue with entities placed 37 | in exact positions. (#5160) 38 | Scatter gun patches to improve entity tracking and position tracking. 39 | Provided by Aikar through the Paper project, this commit of patches 40 | combines the following patches: 41 | 42 | https://github.com/PaperMC/Paper/blob/fd1bd5223a461b6d98280bb8f2d67280a30dd24a/Spigot-Server-Patches/0306-Mark-chunk-dirty-anytime-entities-change-to-guarante.patch 43 | 44 | https://github.com/PaperMC/Paper/blob/fd1bd5223a461b6d98280bb8f2d67280a30dd24a/Spigot-Server-Patches/0315-Always-process-chunk-registration-after-moving.patch 45 | 46 | https://github.com/PaperMC/Paper/blob/fd1bd5223a461b6d98280bb8f2d67280a30dd24a/Spigot-Server-Patches/0335-Ensure-chunks-are-always-loaded-on-hard-position-set.patch 47 | 48 | https://github.com/PaperMC/Paper/blob/fd1bd5223a461b6d98280bb8f2d67280a30dd24a/Spigot-Server-Patches/0378-Sync-Player-Position-to-Vehicles.patch 49 | 50 | Build 2760: 51 | LexManos: Fix --mods and --modListFile arguments not making it past LaunchWrapper. 52 | 53 | Build 2759: 54 | LexManos: Remove BlamingTransformer (#5115) 55 | 56 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Sets default memory used for gradle commands. Can be overridden by user or command line properties. 2 | # This is required to provide enough memory for the Minecraft decompilation process. 3 | org.gradle.jvmargs=-Xmx3G 4 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Delta2Force/MCEmu/ba4f598971b8597e88ec8734b8ef836da3cfe9a4/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Mon Sep 14 12:28:28 PDT 2015 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.14-bin.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # For Cygwin, ensure paths are in UNIX format before anything is touched. 46 | if $cygwin ; then 47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 48 | fi 49 | 50 | # Attempt to set APP_HOME 51 | # Resolve links: $0 may be a link 52 | PRG="$0" 53 | # Need this for relative symlinks. 54 | while [ -h "$PRG" ] ; do 55 | ls=`ls -ld "$PRG"` 56 | link=`expr "$ls" : '.*-> \(.*\)$'` 57 | if expr "$link" : '/.*' > /dev/null; then 58 | PRG="$link" 59 | else 60 | PRG=`dirname "$PRG"`"/$link" 61 | fi 62 | done 63 | SAVED="`pwd`" 64 | cd "`dirname \"$PRG\"`/" >&- 65 | APP_HOME="`pwd -P`" 66 | cd "$SAVED" >&- 67 | 68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 69 | 70 | # Determine the Java command to use to start the JVM. 71 | if [ -n "$JAVA_HOME" ] ; then 72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 73 | # IBM's JDK on AIX uses strange locations for the executables 74 | JAVACMD="$JAVA_HOME/jre/sh/java" 75 | else 76 | JAVACMD="$JAVA_HOME/bin/java" 77 | fi 78 | if [ ! -x "$JAVACMD" ] ; then 79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 80 | 81 | Please set the JAVA_HOME variable in your environment to match the 82 | location of your Java installation." 83 | fi 84 | else 85 | JAVACMD="java" 86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 87 | 88 | Please set the JAVA_HOME variable in your environment to match the 89 | location of your Java installation." 90 | fi 91 | 92 | # Increase the maximum file descriptors if we can. 93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 94 | MAX_FD_LIMIT=`ulimit -H -n` 95 | if [ $? -eq 0 ] ; then 96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 97 | MAX_FD="$MAX_FD_LIMIT" 98 | fi 99 | ulimit -n $MAX_FD 100 | if [ $? -ne 0 ] ; then 101 | warn "Could not set maximum file descriptor limit: $MAX_FD" 102 | fi 103 | else 104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 105 | fi 106 | fi 107 | 108 | # For Darwin, add options to specify how the application appears in the dock 109 | if $darwin; then 110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 111 | fi 112 | 113 | # For Cygwin, switch paths to Windows format before running java 114 | if $cygwin ; then 115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 158 | function splitJvmOpts() { 159 | JVM_OPTS=("$@") 160 | } 161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 163 | 164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 165 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /src/main/java/deltatwoforce/mcemu/MCEmu.java: -------------------------------------------------------------------------------- 1 | package deltatwoforce.mcemu; 2 | 3 | import net.minecraft.block.Block; 4 | import net.minecraft.client.renderer.block.model.ModelResourceLocation; 5 | import net.minecraft.client.settings.KeyBinding; 6 | import net.minecraft.creativetab.CreativeTabs; 7 | import net.minecraft.init.Blocks; 8 | import net.minecraft.init.Items; 9 | import net.minecraft.item.Item; 10 | import net.minecraft.item.ItemStack; 11 | import net.minecraft.tileentity.TileEntity; 12 | import net.minecraft.util.ResourceLocation; 13 | import net.minecraftforge.client.event.ModelBakeEvent; 14 | import net.minecraftforge.client.event.ModelRegistryEvent; 15 | import net.minecraftforge.client.model.IModel; 16 | import net.minecraftforge.client.model.ModelLoader; 17 | import net.minecraftforge.event.RegistryEvent; 18 | import net.minecraftforge.fml.client.registry.ClientRegistry; 19 | import net.minecraftforge.fml.common.Mod; 20 | import net.minecraftforge.fml.common.Mod.EventHandler; 21 | import net.minecraftforge.fml.common.event.FMLInitializationEvent; 22 | import net.minecraftforge.fml.common.event.FMLPreInitializationEvent; 23 | import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; 24 | import net.minecraftforge.fml.common.gameevent.InputEvent.KeyInputEvent; 25 | import net.minecraftforge.fml.common.registry.GameRegistry; 26 | import net.minecraftforge.fml.relauncher.Side; 27 | import net.minecraftforge.fml.relauncher.SideOnly; 28 | 29 | import java.awt.Color; 30 | import java.awt.Graphics2D; 31 | import java.awt.image.BufferedImage; 32 | import java.io.File; 33 | import java.io.IOException; 34 | import java.net.MalformedURLException; 35 | import java.net.URL; 36 | import java.util.ArrayList; 37 | 38 | import javax.imageio.ImageIO; 39 | import javax.sound.sampled.LineUnavailableException; 40 | 41 | import org.apache.logging.log4j.Logger; 42 | import org.lwjgl.input.Keyboard; 43 | 44 | import deltatwoforce.mcemu.nes.ItemNESCartridge; 45 | import deltatwoforce.mcemu.nes.MCEmuNESRenderer; 46 | import deltatwoforce.mcemu.nes.TelevisionSpecialRenderer; 47 | import deltatwoforce.mcemu.nes.TelevisionTileEntity; 48 | import jp.tanakh.bjne.nes.Nes; 49 | 50 | @Mod(modid = MCEmu.MODID, name = MCEmu.NAME, version = MCEmu.VERSION) 51 | @Mod.EventBusSubscriber(modid=MCEmu.MODID) 52 | public class MCEmu 53 | { 54 | public static final String MODID = "mcemu"; 55 | public static final String NAME = "MCEmu"; 56 | public static final String VERSION = "1.0"; 57 | 58 | public static int INDEX = 1; 59 | 60 | public static final CreativeTabs tabNES = (new CreativeTabs("mcemu.nes") { 61 | @Override 62 | public ItemStack getTabIconItem() { 63 | return new ItemStack(MCEmuItems.nesconsole); 64 | } 65 | }); 66 | 67 | public static final KeyBinding P1NES_LEFT = new KeyBinding("NES: P1 Left", Keyboard.KEY_LEFT, "NES"); 68 | public static final KeyBinding P1NES_RIGHT = new KeyBinding("NES: P1 Right", Keyboard.KEY_RIGHT, "NES"); 69 | public static final KeyBinding P1NES_UP = new KeyBinding("NES: P1 Up", Keyboard.KEY_UP, "NES"); 70 | public static final KeyBinding P1NES_DOWN = new KeyBinding("NES: P1 Down", Keyboard.KEY_DOWN, "NES"); 71 | public static final KeyBinding P1NES_A = new KeyBinding("NES: P1 A", Keyboard.KEY_RSHIFT, "NES"); 72 | public static final KeyBinding P1NES_B = new KeyBinding("NES: P1 B", Keyboard.KEY_RCONTROL, "NES"); 73 | public static final KeyBinding P1NES_START = new KeyBinding("NES: P1 Start", Keyboard.KEY_RETURN, "NES"); 74 | public static final KeyBinding P1NES_SELECT = new KeyBinding("NES: P1 Select", Keyboard.KEY_BACK, "NES"); 75 | 76 | public static final KeyBinding P2NES_LEFT = new KeyBinding("NES: P2 Left", Keyboard.KEY_NUMPAD1, "NES"); 77 | public static final KeyBinding P2NES_RIGHT = new KeyBinding("NES: P2 Right", Keyboard.KEY_NUMPAD3, "NES"); 78 | public static final KeyBinding P2NES_UP = new KeyBinding("NES: P2 Up", Keyboard.KEY_NUMPAD5, "NES"); 79 | public static final KeyBinding P2NES_DOWN = new KeyBinding("NES: P2 Down", Keyboard.KEY_NUMPAD2, "NES"); 80 | public static final KeyBinding P2NES_A = new KeyBinding("NES: P2 A", Keyboard.KEY_NUMPADENTER, "NES"); 81 | public static final KeyBinding P2NES_B = new KeyBinding("NES: P2 B", Keyboard.KEY_NUMPAD0, "NES"); 82 | public static final KeyBinding P2NES_START = new KeyBinding("NES: P2 Start", Keyboard.KEY_NUMPAD7, "NES"); 83 | public static final KeyBinding P2NES_SELECT = new KeyBinding("NES: P2 Select", Keyboard.KEY_NUMPAD9, "NES"); 84 | 85 | public static final KeyBinding[] bindings = {P1NES_LEFT, P1NES_RIGHT, P1NES_UP, P1NES_DOWN, P1NES_A, P1NES_B, P1NES_START, P1NES_SELECT, 86 | P2NES_LEFT, P2NES_RIGHT, P2NES_UP, P2NES_DOWN, P2NES_A, P2NES_B, P2NES_START, P2NES_SELECT}; 87 | 88 | public static MCEmuNESRenderer NESRenderer; 89 | public static Nes NES_INSTANCE; 90 | public static BufferedImage BUFIMG; 91 | public static ItemStack CURRENTLYPLAYING; 92 | public static boolean running = false; 93 | static { 94 | BUFIMG = new BufferedImage(256, 240, BufferedImage.TYPE_INT_ARGB); 95 | Graphics2D g = BUFIMG.createGraphics(); 96 | g.setColor(Color.BLACK); 97 | g.fillRect(0, 0, MCEmu.BUFIMG.getWidth(), MCEmu.BUFIMG.getHeight()); 98 | g.setColor(Color.WHITE); 99 | g.drawString("No cartridge inserted", 8, 20); 100 | g.drawString("Get cartridges from your creative menu", 8, 40); 101 | g.drawString("or put roms into the folder if you haven't.", 8, 56); 102 | g.drawString("ROM Folder: /.minecraft/roms/nes/", 8, 76); 103 | g.setFont(g.getFont().deriveFont(64f)); 104 | g.drawString("MCEmu", 8, 222); 105 | } 106 | public static Thread NES_THREAD = new Thread(new Runnable() { 107 | @Override 108 | public void run() { 109 | for (;;) { 110 | if (NES_INSTANCE == null) 111 | continue; 112 | 113 | /*long start = System.nanoTime(); 114 | NES_INSTANCE.execFrame(); 115 | 116 | for (;;) { 117 | long elapsed = System.nanoTime() - start; 118 | long wait = (long) (1.0 / 60 - elapsed / 1e-9); 119 | try { 120 | if (wait > 0) 121 | Thread.sleep(wait); 122 | } catch (InterruptedException e) { 123 | } 124 | break; 125 | }*/ 126 | if(running) { 127 | NES_INSTANCE.execFrame(); 128 | } 129 | try { 130 | Thread.sleep(1000/60); 131 | } catch (InterruptedException e) { 132 | e.printStackTrace(); 133 | } 134 | } 135 | } 136 | }); 137 | 138 | private static Logger logger; 139 | 140 | @EventHandler 141 | public void preInit(FMLPreInitializationEvent event) 142 | { 143 | try { 144 | NESRenderer = new MCEmuNESRenderer(); 145 | } catch (LineUnavailableException e) { 146 | e.printStackTrace(); 147 | } 148 | 149 | logger = event.getModLog(); 150 | File nesroms = new File(event.getModConfigurationDirectory().getParentFile(), "roms/nes"); 151 | nesroms.mkdirs(); 152 | logger.info("Loading NES roms from " + nesroms.getPath() + "..."); 153 | MCEmuItems.cartidges = new ArrayList(); 154 | for(File f : nesroms.listFiles()) { 155 | MCEmuItems.cartidges.add(new ItemNESCartridge(f)); 156 | logger.info("Loaded " + f.getName() + "..."); 157 | } 158 | logger.info("Loaded " + MCEmuItems.cartidges.size() + " NES roms!"); 159 | 160 | GameRegistry.registerTileEntity(TelevisionTileEntity.class, new ResourceLocation(MODID, "televisionTileEntity")); 161 | } 162 | 163 | @SideOnly(Side.CLIENT) 164 | @EventHandler 165 | public void init(FMLInitializationEvent event) 166 | { 167 | for(KeyBinding binding : bindings) { 168 | ClientRegistry.registerKeyBinding(binding); 169 | } 170 | ClientRegistry.bindTileEntitySpecialRenderer(TelevisionTileEntity.class, new TelevisionSpecialRenderer()); 171 | } 172 | 173 | @SubscribeEvent 174 | public static void registerBlocks(RegistryEvent.Register event) { 175 | event.getRegistry().register(MCEmuBlocks.nesconsole); 176 | event.getRegistry().register(MCEmuBlocks.television); 177 | } 178 | 179 | @SubscribeEvent 180 | public static void registerItems(RegistryEvent.Register event) { 181 | event.getRegistry().register(MCEmuItems.nesconsole); 182 | event.getRegistry().register(MCEmuItems.television); 183 | for(ItemNESCartridge inc : MCEmuItems.cartidges) { 184 | event.getRegistry().register(inc); 185 | } 186 | } 187 | 188 | @SideOnly(Side.CLIENT) 189 | @SubscribeEvent 190 | public static void registerKeyBindings(ModelRegistryEvent event) { 191 | ModelLoader.setCustomModelResourceLocation(MCEmuItems.nesconsole, 0, new ModelResourceLocation(MODID + ":nesconsole")); 192 | ModelLoader.setCustomModelResourceLocation(MCEmuItems.television, 0, new ModelResourceLocation(MODID + ":television")); 193 | for(ItemNESCartridge inc : MCEmuItems.cartidges) { 194 | ModelLoader.setCustomModelResourceLocation(inc, 0, new ModelResourceLocation(MODID + ":nescartridge")); 195 | } 196 | } 197 | 198 | @SideOnly(Side.CLIENT) 199 | @SubscribeEvent 200 | public static void onKeyEvent(KeyInputEvent event) { 201 | for(KeyBinding kb : bindings) { 202 | NESRenderer.onKey(kb.getKeyCode(), kb.isPressed()); 203 | } 204 | } 205 | } 206 | -------------------------------------------------------------------------------- /src/main/java/deltatwoforce/mcemu/MCEmuBlocks.java: -------------------------------------------------------------------------------- 1 | package deltatwoforce.mcemu; 2 | 3 | import deltatwoforce.mcemu.nes.BlockNESConsole; 4 | import deltatwoforce.mcemu.nes.BlockTelevision; 5 | import net.minecraftforge.fml.common.registry.GameRegistry.ObjectHolder; 6 | 7 | @ObjectHolder(MCEmu.MODID) 8 | public class MCEmuBlocks { 9 | public static final BlockNESConsole nesconsole = new BlockNESConsole(); 10 | public static final BlockTelevision television = new BlockTelevision(); 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/deltatwoforce/mcemu/MCEmuItems.java: -------------------------------------------------------------------------------- 1 | package deltatwoforce.mcemu; 2 | 3 | import java.util.ArrayList; 4 | 5 | import deltatwoforce.mcemu.nes.BlockNESConsole; 6 | import deltatwoforce.mcemu.nes.ItemNESCartridge; 7 | import net.minecraft.item.Item; 8 | import net.minecraft.item.ItemBlock; 9 | import net.minecraftforge.fml.common.registry.GameRegistry.ObjectHolder; 10 | 11 | @ObjectHolder(MCEmu.MODID) 12 | public class MCEmuItems { 13 | public static final Item nesconsole = new ItemBlock(MCEmuBlocks.nesconsole).setRegistryName(MCEmuBlocks.nesconsole.getRegistryName()); 14 | public static final Item television = new ItemBlock(MCEmuBlocks.television).setRegistryName(MCEmuBlocks.television.getRegistryName()); 15 | 16 | public static ArrayList cartidges; 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/deltatwoforce/mcemu/nes/BlockNESConsole.java: -------------------------------------------------------------------------------- 1 | package deltatwoforce.mcemu.nes; 2 | 3 | import java.awt.Color; 4 | import java.awt.Graphics2D; 5 | import java.io.IOException; 6 | 7 | import deltatwoforce.mcemu.MCEmu; 8 | import jp.tanakh.bjne.nes.Nes; 9 | import net.minecraft.block.Block; 10 | import net.minecraft.block.material.Material; 11 | import net.minecraft.block.state.IBlockState; 12 | import net.minecraft.entity.item.EntityItem; 13 | import net.minecraft.entity.player.EntityPlayer; 14 | import net.minecraft.item.ItemStack; 15 | import net.minecraft.util.BlockRenderLayer; 16 | import net.minecraft.util.EnumFacing; 17 | import net.minecraft.util.EnumHand; 18 | import net.minecraft.util.math.AxisAlignedBB; 19 | import net.minecraft.util.math.BlockPos; 20 | import net.minecraft.world.IBlockAccess; 21 | import net.minecraft.world.World; 22 | 23 | public class BlockNESConsole extends Block{ 24 | private static final AxisAlignedBB AABB = new AxisAlignedBB(0.0625, 0, 0.0625*3, 0.0625*15, 0.0625*5, 0.0625*13); 25 | 26 | public BlockNESConsole() { 27 | super(Material.IRON); 28 | setRegistryName(MCEmu.MODID, "nesconsole"); 29 | setUnlocalizedName("nesconsole"); 30 | setCreativeTab(MCEmu.tabNES); 31 | } 32 | 33 | @Override 34 | public boolean onBlockActivated(World worldIn, BlockPos pos, IBlockState state, EntityPlayer playerIn, 35 | EnumHand hand, EnumFacing facing, float hitX, float hitY, float hitZ) { 36 | 37 | if(!worldIn.isRemote) { 38 | ItemStack is; 39 | is = playerIn.getHeldItemMainhand(); 40 | 41 | if(is.getItem() instanceof ItemNESCartridge) { 42 | if(MCEmu.running) { 43 | MCEmu.running = false; 44 | } 45 | MCEmu.CURRENTLYPLAYING = new ItemStack(is.getItem()); 46 | MCEmu.NES_INSTANCE = new Nes(MCEmu.NESRenderer); 47 | try { 48 | MCEmu.NES_INSTANCE.load(((ItemNESCartridge) is.getItem()).rom.getPath()); 49 | } catch (IOException e) { 50 | e.printStackTrace(); 51 | } 52 | MCEmu.running = true; 53 | if(!MCEmu.NES_THREAD.isAlive()) { 54 | MCEmu.NES_THREAD.start(); 55 | } 56 | }else { 57 | if(MCEmu.running) { 58 | MCEmu.running = false; 59 | Graphics2D g2d = MCEmu.BUFIMG.createGraphics(); 60 | g2d.setColor(Color.BLACK); 61 | g2d.fillRect(0, 0, MCEmu.BUFIMG.getWidth(), MCEmu.BUFIMG.getHeight()); 62 | g2d.setColor(Color.WHITE); 63 | g2d.drawString("No cartridge inserted", 8, 20); 64 | g2d.drawString("Get cartridges from your creative menu", 8, 40); 65 | g2d.drawString("or put roms into the folder if you haven't.", 8, 56); 66 | g2d.drawString("ROM Folder: /.minecraft/roms/nes/", 8, 76); 67 | g2d.setFont(g2d.getFont().deriveFont(64f)); 68 | g2d.drawString("MCEmu", 8, 222); 69 | } 70 | } 71 | } 72 | 73 | return super.onBlockActivated(worldIn, pos, state, playerIn, hand, facing, hitX, hitY, hitZ); 74 | } 75 | 76 | @Override 77 | public boolean isFullCube(IBlockState state) { 78 | return false; 79 | } 80 | 81 | @Override 82 | public boolean isOpaqueCube(IBlockState state) { 83 | return false; 84 | } 85 | 86 | @Override 87 | public BlockRenderLayer getBlockLayer() { 88 | return BlockRenderLayer.TRANSLUCENT; 89 | } 90 | 91 | public static AxisAlignedBB getAabb() { 92 | return AABB; 93 | } 94 | 95 | @Override 96 | public AxisAlignedBB getBoundingBox(IBlockState state, IBlockAccess source, BlockPos pos) { 97 | return AABB; 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/main/java/deltatwoforce/mcemu/nes/BlockTelevision.java: -------------------------------------------------------------------------------- 1 | package deltatwoforce.mcemu.nes; 2 | 3 | import deltatwoforce.mcemu.MCEmu; 4 | import net.minecraft.block.Block; 5 | import net.minecraft.block.material.Material; 6 | import net.minecraft.block.state.IBlockState; 7 | import net.minecraft.tileentity.TileEntity; 8 | import net.minecraft.world.World; 9 | 10 | public class BlockTelevision extends Block{ 11 | public BlockTelevision() { 12 | super(Material.WOOD); 13 | setRegistryName(MCEmu.MODID, "television"); 14 | setUnlocalizedName("television"); 15 | setCreativeTab(MCEmu.tabNES); 16 | } 17 | 18 | @Override 19 | public boolean hasTileEntity(IBlockState state) { 20 | return true; 21 | } 22 | 23 | @Override 24 | public TileEntity createTileEntity(World world, IBlockState state) { 25 | return new TelevisionTileEntity(); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/deltatwoforce/mcemu/nes/ItemNESCartridge.java: -------------------------------------------------------------------------------- 1 | package deltatwoforce.mcemu.nes; 2 | 3 | import java.io.File; 4 | import java.util.List; 5 | 6 | import deltatwoforce.mcemu.MCEmu; 7 | import net.minecraft.client.util.ITooltipFlag; 8 | import net.minecraft.item.Item; 9 | import net.minecraft.item.ItemStack; 10 | import net.minecraft.world.World; 11 | 12 | public class ItemNESCartridge extends Item{ 13 | private String name; 14 | public File rom; 15 | 16 | public ItemNESCartridge(File rom) { 17 | name = rom.getName(); 18 | int i = name.lastIndexOf('.'); 19 | if(i>0) { 20 | name = name.replace(name.substring(i), ""); 21 | } 22 | String smol = rom.getName().toLowerCase().replace(" ", ""); 23 | setRegistryName(MCEmu.MODID, "nescartridge->" + smol); 24 | setUnlocalizedName("nescartridge"); 25 | setCreativeTab(MCEmu.tabNES); 26 | 27 | this.rom = rom; 28 | } 29 | 30 | @Override 31 | public String getItemStackDisplayName(ItemStack stack) { 32 | return name; 33 | } 34 | 35 | @Override 36 | public void addInformation(ItemStack stack, World worldIn, List tooltip, ITooltipFlag flagIn) { 37 | tooltip.add("A ROM File for the NES."); 38 | super.addInformation(stack, worldIn, tooltip, flagIn); 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/deltatwoforce/mcemu/nes/MCEmuNESRenderer.java: -------------------------------------------------------------------------------- 1 | package deltatwoforce.mcemu.nes; 2 | 3 | import java.awt.Frame; 4 | import java.awt.Graphics; 5 | import java.awt.event.KeyAdapter; 6 | import java.awt.event.KeyEvent; 7 | import java.awt.image.BufferedImage; 8 | import java.awt.image.DataBufferByte; 9 | import java.util.HashMap; 10 | 11 | import javax.sound.sampled.AudioFormat; 12 | import javax.sound.sampled.AudioSystem; 13 | import javax.sound.sampled.DataLine; 14 | import javax.sound.sampled.LineUnavailableException; 15 | import javax.sound.sampled.SourceDataLine; 16 | 17 | import com.google.common.collect.ImmutableMap; 18 | 19 | import deltatwoforce.mcemu.MCEmu; 20 | import jp.tanakh.bjne.nes.Renderer; 21 | import jp.tanakh.bjne.nes.Renderer.InputInfo; 22 | import jp.tanakh.bjne.nes.Renderer.ScreenInfo; 23 | import jp.tanakh.bjne.nes.Renderer.SoundInfo; 24 | import net.minecraft.client.Minecraft; 25 | import net.minecraft.client.renderer.texture.DynamicTexture; 26 | import net.minecraft.util.ResourceLocation; 27 | import net.minecraftforge.client.model.IModel; 28 | import net.minecraftforge.client.model.ModelLoader; 29 | import net.minecraftforge.client.model.ModelLoaderRegistry; 30 | import net.minecraftforge.fml.client.FMLClientHandler; 31 | import net.minecraftforge.fml.common.registry.GameRegistry; 32 | 33 | public class MCEmuNESRenderer implements Renderer{ 34 | private static final int SCREEN_WIDTH = 256; 35 | private static final int SCREEN_HEIGHT = 240; 36 | 37 | private static final int SAMPLE_RATE = 48000; 38 | private static final int BPS = 16; 39 | private static final int CHANNELS = 2; 40 | private static final int BUFFER_FRAMES = 2; 41 | 42 | private static final int FPS = 60; 43 | private static final int SAMPLES_PER_FRAME = SAMPLE_RATE / FPS; 44 | 45 | private ScreenInfo scri = new ScreenInfo(); 46 | private SoundInfo sndi = new SoundInfo(); 47 | private InputInfo inpi = new InputInfo(); 48 | 49 | private BufferedImage image = new BufferedImage(SCREEN_WIDTH, 50 | SCREEN_HEIGHT, BufferedImage.TYPE_3BYTE_BGR); 51 | 52 | private int lineBufferSize; 53 | 54 | public MCEmuNESRenderer() throws LineUnavailableException { 55 | AudioFormat format = new AudioFormat(SAMPLE_RATE, BPS, CHANNELS, true, 56 | false); 57 | DataLine.Info info = new DataLine.Info(SourceDataLine.class, format); 58 | 59 | int bufSamples = SAMPLES_PER_FRAME; 60 | 61 | sndi.bps = 16; 62 | sndi.buf = new byte[bufSamples * (BPS / 8) * CHANNELS]; 63 | sndi.ch = 2; 64 | sndi.freq = SAMPLE_RATE; 65 | sndi.sample = bufSamples; 66 | 67 | inpi.buf = new int[16]; 68 | } 69 | 70 | @Override 71 | public void outputMessage(String msg) { 72 | System.out.println(msg); 73 | } 74 | 75 | @Override 76 | public ScreenInfo requestScreen(int width, int height) { 77 | if (!(scri.width == width && scri.height == height)) { 78 | scri.width = width; 79 | scri.height = height; 80 | scri.buf = new byte[3 * width * height]; 81 | scri.pitch = 3 * width; 82 | scri.bpp = 24; 83 | } 84 | return scri; 85 | } 86 | 87 | @Override 88 | public void outputScreen(ScreenInfo info) { 89 | byte[] bgr = ((DataBufferByte) image.getRaster().getDataBuffer()) 90 | .getData(); 91 | 92 | for (int i = 0; i < SCREEN_WIDTH * SCREEN_HEIGHT; i++) { 93 | bgr[i * 3] = info.buf[i * 3 + 2]; 94 | bgr[i * 3 + 1] = info.buf[i * 3 + 1]; 95 | bgr[i * 3 + 2] = info.buf[i * 3 + 0]; 96 | } 97 | 98 | MCEmu.BUFIMG = image; 99 | } 100 | 101 | @Override 102 | public SoundInfo requestSound() { 103 | if (getSoundBufferState() <= 0) 104 | return sndi; 105 | else 106 | return null; 107 | } 108 | 109 | @Override 110 | public void outputSound(SoundInfo info) { 111 | //line.write(info.buf, 0, info.sample * (info.bps / 8) * info.ch); 112 | } 113 | 114 | public int getSoundBufferState() { 115 | /*int rest = (lineBufferSize - line.available()) / (sndi.bps / 8) 116 | / sndi.ch; 117 | if (rest < SAMPLES_PER_FRAME * BUFFER_FRAMES) 118 | return -1; 119 | if (rest == SAMPLES_PER_FRAME * BUFFER_FRAMES) 120 | return 0*/ 121 | return 1; 122 | } 123 | 124 | static final int[][] keyDef = { 125 | { MCEmu.P1NES_A.getKeyCode(), MCEmu.P1NES_B.getKeyCode(), MCEmu.P1NES_SELECT.getKeyCode(), 126 | MCEmu.P1NES_START.getKeyCode(), MCEmu.P1NES_UP.getKeyCode(), MCEmu.P1NES_DOWN.getKeyCode(), 127 | MCEmu.P1NES_LEFT.getKeyCode(), MCEmu.P1NES_RIGHT.getKeyCode(), }, 128 | { MCEmu.P2NES_A.getKeyCode(), MCEmu.P2NES_B.getKeyCode(), MCEmu.P2NES_SELECT.getKeyCode(), 129 | MCEmu.P2NES_START.getKeyCode(), MCEmu.P2NES_UP.getKeyCode(), MCEmu.P2NES_DOWN.getKeyCode(), 130 | MCEmu.P2NES_LEFT.getKeyCode(), MCEmu.P2NES_RIGHT.getKeyCode(), } }; 131 | 132 | public void onKey(int keyCode, boolean press) { 133 | for (int i = 0; i < 2; i++) 134 | for (int j = 0; j < 8; j++) 135 | if (keyCode == keyDef[i][j]) 136 | inpi.buf[i * 8 + j] = (press ? 1 : 0); 137 | } 138 | 139 | @Override 140 | public InputInfo requestInput(int padCount, int buttonCount) { 141 | return inpi; 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /src/main/java/deltatwoforce/mcemu/nes/TelevisionSpecialRenderer.java: -------------------------------------------------------------------------------- 1 | package deltatwoforce.mcemu.nes; 2 | 3 | import net.minecraft.client.renderer.GlStateManager; 4 | import net.minecraft.client.renderer.RenderHelper; 5 | import net.minecraft.client.renderer.tileentity.TileEntitySpecialRenderer; 6 | 7 | import static org.lwjgl.opengl.GL11.*; 8 | 9 | import java.awt.Color; 10 | import java.awt.Graphics; 11 | import java.awt.image.BufferedImage; 12 | import java.awt.image.DataBuffer; 13 | import java.awt.image.DataBufferByte; 14 | import java.awt.image.Raster; 15 | import java.awt.image.WritableRaster; 16 | import java.nio.ByteBuffer; 17 | import java.nio.ByteOrder; 18 | 19 | import org.lwjgl.BufferUtils; 20 | import org.lwjgl.opengl.GL12; 21 | 22 | import deltatwoforce.mcemu.MCEmu; 23 | 24 | public class TelevisionSpecialRenderer extends TileEntitySpecialRenderer{ 25 | public int lastTex = -1; 26 | 27 | @Override 28 | public void render(TelevisionTileEntity te, double x, double y, double z, float partialTicks, int destroyStage, float alpha) { 29 | //super.render(te, x, y, z, partialTicks, destroyStage, alpha); 30 | 31 | if(lastTex != -1) { 32 | glDeleteTextures(lastTex); 33 | } 34 | 35 | RenderHelper.disableStandardItemLighting(); 36 | setLightmapDisabled(true); 37 | glEnable(GL_TEXTURE_2D); 38 | glDisable(GL_CULL_FACE); 39 | glDisable(GL_BLEND); 40 | 41 | glPushMatrix(); 42 | glTranslated(x+0.5, y+0.5, z-(0.0625*-9)); 43 | glScaled(0.5-(0.0625), 0.5-(0.0625), 1); 44 | glRotated(180, 1, 0, 0); 45 | 46 | int tex = convertImageData(MCEmu.BUFIMG); 47 | glBindTexture(GL_TEXTURE_2D, tex); 48 | glBegin(GL_QUADS); 49 | int sw=1,sh=1; 50 | glColor4f(1.f, 1.f, 1.f, 1.f); glTexCoord2f(0.f, 1.f); glVertex3f( sw, sh, 0.505f); 51 | glColor4f(1.f, 1.f, 1.f, 1.f); glTexCoord2f(1.f, 1.f); glVertex3f(-sw, sh, 0.505f); 52 | glColor4f(1.f, 1.f, 1.f, 1.f); glTexCoord2f(1.f, 0.f); glVertex3f(-sw, -sh, 0.505f); 53 | glColor4f(1.f, 1.f, 1.f, 1.f); glTexCoord2f(0.f, 0.f); glVertex3f( sw, -sh, 0.505f); 54 | glEnd(); 55 | GlStateManager.bindTexture(0); 56 | glPopMatrix(); 57 | 58 | lastTex = tex; 59 | 60 | RenderHelper.enableStandardItemLighting(); 61 | setLightmapDisabled(false); 62 | glEnable(GL_CULL_FACE); 63 | } 64 | 65 | private int convertImageData(BufferedImage image) { 66 | int[] pixels = new int[image.getWidth() * image.getHeight()]; 67 | image.getRGB(0, 0, image.getWidth(), image.getHeight(), pixels, 0, image.getWidth()); 68 | 69 | ByteBuffer buffer = BufferUtils.createByteBuffer(image.getWidth() * image.getHeight() * 4); //4 for RGBA, 3 for RGB 70 | 71 | for(int y = 0; y < image.getHeight(); y++){ 72 | for(int x = 0; x < image.getWidth(); x++){ 73 | int pixel = pixels[y * image.getWidth() + x]; 74 | buffer.put((byte) ((pixel >> 16) & 0xFF)); // Red component 75 | buffer.put((byte) ((pixel >> 8) & 0xFF)); // Green component 76 | buffer.put((byte) (pixel & 0xFF)); // Blue component 77 | buffer.put((byte) ((pixel >> 24) & 0xFF)); // Alpha component. Only for RGBA 78 | } 79 | } 80 | 81 | buffer.flip(); 82 | 83 | int textureID = glGenTextures(); //Generate texture ID 84 | glBindTexture(GL_TEXTURE_2D, textureID); //Bind texture ID 85 | 86 | //Setup wrap mode 87 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL12.GL_CLAMP_TO_EDGE); 88 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL12.GL_CLAMP_TO_EDGE); 89 | 90 | //Setup texture scaling filtering 91 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 92 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 93 | 94 | //Send texel data to OpenGL 95 | glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, image.getWidth(), image.getHeight(), 0, GL_RGBA, GL_UNSIGNED_BYTE, buffer); 96 | 97 | //Return the texture ID so we can bind it later again 98 | return textureID; 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/main/java/deltatwoforce/mcemu/nes/TelevisionTileEntity.java: -------------------------------------------------------------------------------- 1 | package deltatwoforce.mcemu.nes; 2 | 3 | import net.minecraft.tileentity.TileEntity; 4 | 5 | public class TelevisionTileEntity extends TileEntity{ } 6 | -------------------------------------------------------------------------------- /src/main/java/jp/tanakh/bjne/nes/Apu.java: -------------------------------------------------------------------------------- 1 | package jp.tanakh.bjne.nes; 2 | 3 | import java.util.Arrays; 4 | import java.util.LinkedList; 5 | import java.util.Queue; 6 | 7 | import jp.tanakh.bjne.nes.Renderer.SoundInfo; 8 | 9 | public class Apu { 10 | public Apu(Nes n) { 11 | this.nes = n; 12 | reset(); 13 | } 14 | 15 | public void reset() { 16 | ch = new ChState[4]; 17 | ch[0] = new ChState(); 18 | ch[1] = new ChState(); 19 | ch[2] = new ChState(); 20 | ch[3] = new ChState(); 21 | 22 | sch = new ChState[4]; 23 | sch[0] = new ChState(); 24 | sch[1] = new ChState(); 25 | sch[2] = new ChState(); 26 | sch[3] = new ChState(); 27 | 28 | dmc = new DmcState(); 29 | 30 | ch[3].shiftRegister = 1; 31 | befClock = befSync = 0; 32 | } 33 | 34 | public byte read(short adr) { 35 | if (adr == 0x4015) { 36 | sync(); 37 | return (byte) ((sch[0].length == 0 ? 0 : 1) | ((sch[1].length == 0 ? 0 : 1) << 1) | ((sch[2].length == 0 ? 0 : 1) << 2) 38 | | ((sch[3].length == 0 ? 0 : 1) << 3) | ((sdmc.enable ? 1 : 0) << 4) | ((sdmc.irq ? 1 : 0) << 7)); 39 | } 40 | return (byte) 0xA0; 41 | } 42 | 43 | public void write(short adr, byte dat) { 44 | // delay writing data for generating sound 45 | writeQueue.add(new WriteDat(nes.getCpu().getMasterClock(), adr, dat)); 46 | while (writeQueue.size() > 1000) { 47 | WriteDat wd = writeQueue.remove(); 48 | doWrite(ch, dmc, wd.adr, wd.dat); 49 | } 50 | // process for status register 51 | sync(); 52 | doWrite(sch, sdmc, adr, dat); 53 | } 54 | 55 | public void genAudio(SoundInfo info) { 56 | double cpuClock = nes.getCpu().getFrequency(); 57 | 58 | long curClock = nes.getCpu().getMasterClock(); 59 | int sample = info.sample; 60 | 61 | byte[] buf = info.buf; 62 | int span = info.ch * (info.bps / 8); 63 | 64 | double incClk = ((double) (curClock - befClock)) / sample; // executed 65 | double sampleClk = cpuClock / info.freq; // CPU clocks per sample 66 | 67 | Arrays.fill(buf, (byte) 0x00); 68 | 69 | if (nes.getMapper() != null) // external APU 70 | nes.getMapper().audio(info); 71 | 72 | for (int i = 0; i < sample; i++) { 73 | //long pos = (curClock - befClock) * i / sample + befClock; 74 | long pos = (long) (befClock + sampleClk * i); 75 | while (!writeQueue.isEmpty() && writeQueue.peek().clk <= pos) { 76 | WriteDat wd = writeQueue.remove(); 77 | doWrite(ch, dmc, wd.adr, wd.dat); 78 | } 79 | 80 | double v = 0; 81 | 82 | for (int j = 0; j < 4; j++) { 83 | ChState cc = ch[j]; 84 | 85 | boolean pause = false; 86 | if (!cc.enable) 87 | continue; 88 | if (cc.length == 0) 89 | pause = true; 90 | 91 | // length counter 92 | if (cc.lengthEnable) { 93 | double length_clk = cpuClock / 60.0; 94 | cc.lengthClk += incClk; 95 | while (cc.lengthClk > length_clk) { 96 | cc.lengthClk -= length_clk; 97 | if (cc.length > 0) 98 | cc.length--; 99 | } 100 | } 101 | // linear counter 102 | if (j == TRI) { 103 | if (cc.counterStart != 0) 104 | cc.linearCounter = cc.linearLatch; 105 | else { 106 | double linear_clk = cpuClock / 240.0; 107 | cc.linearClk += incClk; 108 | while (cc.linearClk > linear_clk) { 109 | cc.linearClk -= linear_clk; 110 | if (cc.linearCounter > 0) 111 | cc.linearCounter--; 112 | } 113 | } 114 | if (!cc.holdnote && cc.linearCounter != 0) 115 | cc.counterStart = 0; 116 | 117 | if (cc.linearCounter == 0) 118 | pause = true; 119 | } 120 | 121 | // envelope 122 | int vol = 16; 123 | if (j != TRI) { 124 | if (cc.envelopeEnable) { 125 | double decay_clk = cpuClock / (240.0 / (cc.envelopeRate + 1)); 126 | cc.envelopeClk += incClk; 127 | while (cc.envelopeClk > decay_clk) { 128 | cc.envelopeClk -= decay_clk; 129 | if (cc.volume > 0) 130 | cc.volume--; 131 | else { 132 | if (!cc.lengthEnable) // loop 133 | cc.volume = 0xf; 134 | else 135 | cc.volume = 0; 136 | } 137 | } 138 | } 139 | vol = cc.volume; 140 | } 141 | 142 | // sweep 143 | if ((j == SQ1 || j == SQ2) && cc.sweepEnable && !cc.sweepPausing) { 144 | double sweep_clk = cpuClock / (120.0 / (cc.sweepRate + 1)); 145 | cc.sweepClk += incClk; 146 | while (cc.sweepClk > sweep_clk) { 147 | cc.sweepClk -= sweep_clk; 148 | if (cc.sweepShift != 0 && cc.length != 0) { 149 | if (!cc.sweepMode) // increase 150 | cc.waveLength += cc.waveLength >> cc.sweepShift; 151 | else 152 | // decrease 153 | cc.waveLength += ~(cc.waveLength >> cc.sweepShift); 154 | if (cc.waveLength < 0x008) 155 | cc.sweepPausing = true; 156 | if ((cc.waveLength & ~0x7FF) != 0) 157 | cc.sweepPausing = true; 158 | cc.waveLength &= 0x7FF; 159 | } 160 | } 161 | } 162 | 163 | pause |= cc.sweepPausing; 164 | pause |= cc.waveLength == 0; 165 | if (pause) 166 | continue; 167 | 168 | // generate wave 169 | double t = ((j == SQ1 || j == SQ2) ? sqProduce(cc, sampleClk) : (j == TRI) ? triProduce(cc, sampleClk) : (j == NOI) ? noiProduce(cc, sampleClk) 170 | : 0); 171 | 172 | v += t * vol / 16; 173 | } 174 | 175 | v += dmcProduce(sampleClk); 176 | 177 | if (info.bps == 8) { 178 | buf[i * span + 0] += (byte) (v * 30); 179 | if (info.ch == 2) 180 | buf[i * span + 1] += (byte) (v * 30); 181 | } else { 182 | { 183 | short b = (short) ((buf[i * span + 0] & 0xff) | (buf[i * span + 1] << 8)); 184 | short w = (short) Math.min(32767, Math.max(-32767, b + v * 8000)); 185 | buf[i * span + 0] = (byte) (w & 0xff); 186 | buf[i * span + 1] = (byte) (w >> 8); 187 | } 188 | if (info.ch == 2) { 189 | short b = (short) ((buf[i * span + 2] & 0xff) | (buf[i * span + 3] << 8)); 190 | short w = (short) Math.min(32767, Math.max(-32767, b + v * 8000)); 191 | buf[i * span + 2] = (byte) (w & 0xff); 192 | buf[i * span + 3] = (byte) (w >> 8); 193 | } 194 | } 195 | 196 | } 197 | befClock = curClock; 198 | 199 | } 200 | 201 | public void sync() { 202 | double cpuClock = nes.getCpu().getFrequency(); 203 | long cur = nes.getCpu().getMasterClock(); 204 | int adv_clock = (int) (cur - befSync); 205 | 206 | // update 4 channels 207 | for (int j = 0; j < 4; j++) { 208 | ChState cc = sch[j]; 209 | // length counter 210 | if (cc.enable && cc.lengthEnable) { 211 | double length_clk = cpuClock / 60.0; 212 | cc.lengthClk += adv_clock; 213 | int dec = (int) (cc.lengthClk / length_clk); 214 | cc.lengthClk -= length_clk * dec; 215 | cc.length = Math.max(0, cc.length - dec); 216 | } 217 | } 218 | // update DMC 219 | if (sdmc.enable) { 220 | sdmc.clk += adv_clock; 221 | int dec = (int) (sdmc.clk / sdmc.waveLength); 222 | sdmc.clk -= dec * sdmc.waveLength; 223 | 224 | int rest = sdmc.shiftCount + sdmc.length * 8 - dec; 225 | if (rest <= 0) { // end playback 226 | if ((sdmc.playbackMode & 1) != 0) { // loop 227 | sdmc.length = rest / 8; 228 | while (sdmc.length < 0) 229 | sdmc.length += sdmc.lengthLatch; 230 | sdmc.shiftCount = 0; 231 | } else { 232 | sdmc.enable = false; 233 | if (sdmc.playbackMode == 2) { // IRQ occur 234 | sdmc.irq = true; 235 | nes.getCpu().setIrq(true); 236 | } 237 | } 238 | } else { 239 | sdmc.length = rest / 8; 240 | sdmc.shiftCount = rest % 8; 241 | } 242 | } 243 | 244 | befSync = cur; 245 | } 246 | 247 | private class ChState { 248 | boolean enable; 249 | 250 | int waveLength; 251 | 252 | boolean lengthEnable; 253 | int length; 254 | double lengthClk; 255 | 256 | int volume; 257 | int envelopeRate; 258 | boolean envelopeEnable; 259 | double envelopeClk; 260 | 261 | boolean sweepEnable; 262 | int sweepRate; 263 | boolean sweepMode; 264 | int sweepShift; 265 | double sweepClk; 266 | boolean sweepPausing; 267 | 268 | int duty; 269 | 270 | int linearLatch; 271 | int linearCounter; 272 | boolean holdnote; 273 | int counterStart; 274 | double linearClk; 275 | 276 | boolean randomType; 277 | 278 | int step; 279 | double stepClk; 280 | int shiftRegister; 281 | } 282 | 283 | private ChState[] ch = null; 284 | private ChState[] sch = null; 285 | 286 | private class DmcState { 287 | boolean enable; 288 | boolean irq; 289 | 290 | int playbackMode; 291 | int waveLength; 292 | double clk; 293 | 294 | int counter; 295 | int length; 296 | int lengthLatch; 297 | short adr; 298 | short adrLatch; 299 | int shiftReg; 300 | int shiftCount; 301 | int dacLsb; 302 | }; 303 | 304 | private DmcState dmc = new DmcState(); 305 | private DmcState sdmc = new DmcState(); 306 | 307 | static final int SQ1 = 0; 308 | static final int SQ2 = 1; 309 | static final int TRI = 2; 310 | static final int NOI = 3; 311 | static final int DMC = 4; 312 | 313 | final static int convTable[] = { 0x002, 0x004, 0x008, 0x010, 0x020, 0x030, 0x040, 0x050, 0x065, 0x07F, 0x0BE, 0x0FE, 0x17D, 0x1FC, 0x3F9, 0x7F2, }; 314 | 315 | final static int lengthTbl[] = { 0x05, 0x06, 0x0A, 0x0C, 0x14, 0x18, 0x28, 0x30, 0x50, 0x60, 0x1E, 0x24, 0x07, 0x08, 0x0E, 0x10, }; 316 | 317 | final static int dacTable[] = { 0xD60, 0xBE0, 0xAA0, 0xA00, 0x8F0, 0x7F0, 0x710, 0x6B0, 0x5F0, 0x500, 0x470, 0x400, 0x350, 0x2A0, 0x240, 0x1B0, }; 318 | 319 | void doWrite(ChState[] ch, DmcState dmc, short adr, byte bdat) { 320 | int cn = (adr & 0x1f) / 4; 321 | ChState cc = null; 322 | if (cn < 4) 323 | cc = ch[cn]; 324 | 325 | int dat = bdat & 0xff; 326 | 327 | switch (adr) { 328 | case 0x4000: 329 | case 0x4004: 330 | case 0x400C: 331 | cc.envelopeEnable = (dat & 0x10) == 0; 332 | if (cc.envelopeEnable) { 333 | cc.volume = 0xf; 334 | cc.envelopeRate = dat & 0xf; 335 | } else 336 | cc.volume = dat & 0xf; 337 | cc.lengthEnable = (dat & 0x20) == 0; 338 | cc.duty = dat >> 6; 339 | cc.envelopeClk = 0; 340 | break; 341 | case 0x4008: 342 | cc.linearLatch = dat & 0x7f; 343 | cc.holdnote = (dat & 0x80) != 0; 344 | break; 345 | 346 | case 0x4001: 347 | case 0x4005: 348 | cc.sweepShift = dat & 7; 349 | cc.sweepMode = (dat & 0x8) != 0; 350 | cc.sweepRate = (dat >> 4) & 7; 351 | cc.sweepEnable = (dat & 0x80) != 0; 352 | cc.sweepClk = 0; 353 | cc.sweepPausing = false; 354 | break; 355 | case 0x4009: 356 | case 0x400D: // unused 357 | break; 358 | 359 | case 0x4002: 360 | case 0x4006: 361 | case 0x400A: 362 | cc.waveLength = (cc.waveLength & ~0xff) | dat; 363 | break; 364 | case 0x400E: { 365 | cc.waveLength = convTable[dat & 0xf] - 1; 366 | cc.randomType = (dat & 0x80) != 0; 367 | break; 368 | } 369 | case 0x4003: 370 | case 0x4007: 371 | case 0x400B: 372 | case 0x400F: { 373 | if (cn != 3) 374 | cc.waveLength = (cc.waveLength & 0xff) | ((dat & 0x7) << 8); 375 | if ((dat & 0x8) == 0) 376 | cc.length = lengthTbl[dat >> 4]; 377 | else 378 | cc.length = (dat >> 4) == 0 ? 0x7f : (dat >> 4); 379 | if (cn == 2) 380 | cc.counterStart = 1; 381 | 382 | if (cc.envelopeEnable) { 383 | cc.volume = 0xf; 384 | cc.envelopeClk = 0; 385 | } 386 | break; 387 | } 388 | 389 | case 0x4010: { 390 | dmc.playbackMode = dat >> 6; 391 | dmc.waveLength = dacTable[dat & 0xf] / 8; 392 | if ((dat >> 7) == 0) 393 | dmc.irq = false; 394 | break; 395 | } 396 | case 0x4011: 397 | dmc.dacLsb = dat & 1; 398 | dmc.counter = (dat >> 1) & 0x3f; 399 | break; 400 | case 0x4012: 401 | dmc.adrLatch = (short) ((dat << 6) | 0xC000); 402 | break; 403 | case 0x4013: 404 | dmc.lengthLatch = (dat << 4) + 1; 405 | break; 406 | 407 | case 0x4015: 408 | ch[0].enable = (dat & 1) != 0; 409 | if (!ch[0].enable) 410 | ch[0].length = 0; 411 | ch[1].enable = (dat & 2) != 0; 412 | if (!ch[1].enable) 413 | ch[1].length = 0; 414 | ch[2].enable = (dat & 4) != 0; 415 | if (!ch[2].enable) 416 | ch[2].length = 0; 417 | ch[3].enable = (dat & 8) != 0; 418 | if (!ch[3].enable) 419 | ch[3].length = 0; 420 | 421 | if ((dat & 0x10) != 0) { 422 | if (!dmc.enable) { 423 | dmc.adr = dmc.adrLatch; 424 | dmc.length = dmc.lengthLatch; 425 | dmc.shiftCount = 0; 426 | } 427 | dmc.enable = true; 428 | } else 429 | dmc.enable = false; 430 | dmc.irq = false; 431 | break; 432 | } 433 | } 434 | 435 | final static int sqWav[][] = { { 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, { 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, 436 | { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1 }, }; 437 | 438 | double sqProduce(ChState cc, double clk) { 439 | cc.stepClk += clk; 440 | double ret = (0.5 - sqWav[cc.duty][cc.step]); 441 | double term = cc.waveLength + 1; 442 | if (cc.stepClk >= term) { 443 | int t = (int) (cc.stepClk / term); 444 | cc.stepClk -= term * t; 445 | cc.step = (cc.step + t) % 16; 446 | } 447 | return ret; 448 | } 449 | 450 | final static int triWav[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, }; 451 | 452 | double triProduce(ChState cc, double clk) { 453 | cc.stepClk += clk; 454 | double ret = (triWav[cc.step] / 16.0 - 0.5); 455 | double term = cc.waveLength + 1; 456 | if (cc.stepClk >= term) { 457 | int t = (int) (cc.stepClk / term); 458 | cc.stepClk -= term * t; 459 | cc.step = (cc.step + t) % 32; 460 | } 461 | return ret; 462 | } 463 | 464 | double noiProduce(ChState cc, double clk) { 465 | cc.stepClk += clk; 466 | double ret = 0.5 - (cc.shiftRegister >> 14); 467 | double term = cc.waveLength + 1; 468 | 469 | while (cc.stepClk >= term) { 470 | cc.stepClk -= term; 471 | int t = cc.shiftRegister; 472 | if (cc.randomType) 473 | cc.shiftRegister = ((t << 1) | (((t >> 14) ^ (t >> 8)) & 1)) & 0x7fff; 474 | else 475 | cc.shiftRegister = ((t << 1) | (((t >> 14) ^ (t >> 13)) & 1)) & 0x7fff; 476 | } 477 | return ret; 478 | } 479 | 480 | double dmcProduce(double clk) { 481 | if (!dmc.enable) 482 | return ((((dmc.counter << 1) | dmc.dacLsb) - 64) / 32.0); 483 | 484 | dmc.clk += clk; 485 | while (dmc.clk > dmc.waveLength) { 486 | dmc.clk -= dmc.waveLength; 487 | if (dmc.shiftCount == 0) { 488 | if (dmc.length == 0) { // is end? 489 | if ((dmc.playbackMode & 1) != 0) { // loop mode 490 | dmc.adr = dmc.adrLatch; 491 | dmc.length = dmc.lengthLatch; 492 | } else { 493 | dmc.enable = false; 494 | if (dmc.playbackMode == 2) { // occur IRQ 495 | dmc.irq = true; 496 | // nes.getCpu().setIrq(true); // 497 | // actually, IRQ occurs at sync() 498 | } 499 | return ((((dmc.counter << 1) | dmc.dacLsb) - 64) / 32.0); 500 | } 501 | } 502 | dmc.shiftCount = 8; 503 | dmc.shiftReg = nes.getMbc().read(dmc.adr); 504 | if (dmc.adr == 0xFFFF) 505 | dmc.adr = (short) 0x8000; 506 | else 507 | dmc.adr++; 508 | dmc.length--; 509 | } 510 | 511 | int b = dmc.shiftReg & 1; 512 | if (b == 0 && dmc.counter != 0) // decrement 513 | dmc.counter--; 514 | if (b == 1 && dmc.counter != 0x3F) 515 | dmc.counter++; 516 | dmc.counter &= 0x3f; 517 | dmc.shiftCount--; 518 | dmc.shiftReg >>= 1; 519 | } 520 | return ((((dmc.counter << 1) | dmc.dacLsb) - 64) / 32.0); 521 | } 522 | 523 | private class WriteDat { 524 | WriteDat(long clk, short adr, byte dat) { 525 | this.clk = clk; 526 | this.adr = adr; 527 | this.dat = dat; 528 | } 529 | 530 | long clk; 531 | short adr; 532 | byte dat; 533 | }; 534 | 535 | private Queue writeQueue = new LinkedList(); 536 | private long befClock; 537 | private long befSync; 538 | 539 | private Nes nes; 540 | } 541 | -------------------------------------------------------------------------------- /src/main/java/jp/tanakh/bjne/nes/Cpu.java: -------------------------------------------------------------------------------- 1 | package jp.tanakh.bjne.nes; 2 | 3 | public class Cpu { 4 | public Cpu(Nes n) { 5 | nes = n; 6 | logging = false; 7 | } 8 | 9 | public void reset() { 10 | mbc = nes.getMbc(); 11 | 12 | regPC = read16((short) 0xFFFC); 13 | regA = 0x00; 14 | regX = 0x00; 15 | regY = 0x00; 16 | regS = (byte) 0xFF; 17 | 18 | oprPC = 0; 19 | 20 | nFlag = vFlag = zFlag = cFlag = 0; // reset for Reproducibility 21 | bFlag = 1; 22 | dFlag = 0; 23 | iFlag = 1; 24 | 25 | rest = 0; 26 | mclock = 0; 27 | nmiLine = irqLine = resetLine = false; 28 | } 29 | 30 | private short imm() { 31 | return regPC++; 32 | } 33 | 34 | // TODO: penalty of carry 35 | private short abs() { 36 | regPC += 2; 37 | return read16(oprPC); 38 | } 39 | 40 | // private short abxi(){ regPC+=2; return 41 | // read16((short)(read16(oprPC)+(regX&0xff))); } 42 | private short abx() { 43 | regPC += 2; 44 | return (short) (read16(oprPC) + (regX & 0xff)); 45 | } 46 | 47 | private short aby() { 48 | regPC += 2; 49 | return (short) (read16(oprPC) + (regY & 0xff)); 50 | } 51 | 52 | private short absi() { 53 | regPC += 2; 54 | return read16(read16(oprPC)); 55 | } 56 | 57 | private short zp() { 58 | return (short) (read8(regPC++) & 0xff); 59 | } 60 | 61 | private short zpxi() { 62 | return read16((short) ((read8(regPC++) + (regX & 0xff)) & 0xff)); 63 | } 64 | 65 | private short zpx() { 66 | return (short) ((read8(regPC++) + (regX & 0xff)) & 0xff); 67 | } 68 | 69 | private short zpy() { 70 | return (short) ((read8(regPC++) + (regY & 0xff)) & 0xff); 71 | } 72 | 73 | // private short zpi(){ return read16((short)(read8(regPC++)&0xff)); } 74 | private short zpiy() { 75 | return (short) (read16((short) (read8(regPC++) & 0xff)) + (regY & 0xff)); 76 | } 77 | 78 | private void push8(byte dat) { 79 | write8((short) (0x100 | ((regS--) & 0xff)), dat); 80 | } 81 | 82 | private byte pop8() { 83 | return read8((short) (0x100 | ((++regS) & 0xff))); 84 | } 85 | 86 | private void push16(short dat) { 87 | write16((short) (0x100 | ((regS - 1) & 0xff)), dat); 88 | regS -= 2; 89 | } 90 | 91 | private short pop16() { 92 | regS += 2; 93 | return read16((short) (0x100 | ((regS - 1) & 0xff))); 94 | } 95 | 96 | private byte bindFlags() { 97 | return (byte) ((nFlag << 7) | (vFlag << 6) | 0x20 | (bFlag << 4) 98 | | (dFlag << 3) | (iFlag << 2) | (zFlag << 1) | cFlag); 99 | } 100 | 101 | private void unbindFlags(byte dat) { 102 | nFlag = (byte) ((dat >> 7) & 1); 103 | vFlag = (byte) ((dat >> 6) & 1); 104 | bFlag = (byte) ((dat >> 4) & 1); 105 | dFlag = (byte) ((dat >> 3) & 1); 106 | iFlag = (byte) ((dat >> 2) & 1); 107 | zFlag = (byte) ((dat >> 1) & 1); 108 | cFlag = (byte) (dat & 1); 109 | } 110 | 111 | // ops 112 | 113 | // TODO : decimal support 114 | private void adc(int cycle, short adr) { 115 | byte s = read8(adr); 116 | int t = (regA & 0xff) + (s & 0xff) + (cFlag & 0xff); 117 | cFlag = (byte) (t >> 8); 118 | zFlag = (byte) ((t & 0xff) == 0 ? 1 : 0); 119 | nFlag = (byte) ((t >> 7) & 1); 120 | vFlag = (byte) ((((regA ^ s) & 0x80) == 0 && ((regA ^ t) & 0x80) != 0) ? 1 121 | : 0); 122 | regA = (byte) (t & 0xff); 123 | rest -= cycle; 124 | } 125 | 126 | // TODO : decimal support 127 | private void sbc(int cycle, short adr) { 128 | byte s = read8(adr); 129 | int t = (regA & 0xff) - (s & 0xff) - (cFlag != 0 ? 0 : 1); 130 | cFlag = (byte) ((t & 0xff00) == 0 ? 1 : 0); 131 | zFlag = (byte) ((t & 0xff) == 0 ? 1 : 0); 132 | nFlag = (byte) ((t >> 7) & 1); 133 | vFlag = (byte) ((((regA ^ s) & 0x80) != 0) 134 | && (((regA ^ t) & 0x80) != 0) ? 1 : 0); 135 | regA = (byte) t; 136 | rest -= cycle; 137 | } 138 | 139 | private void cmp(int cycle, byte reg, short adr) { 140 | short t = (short) ((reg & 0xff) - (read8(adr) & 0xff)); 141 | cFlag = (byte) ((t & 0xff00) == 0 ? 1 : 0); 142 | zFlag = (byte) ((t & 0xff) == 0 ? 1 : 0); 143 | nFlag = (byte) ((t >> 7) & 1); 144 | rest -= cycle; 145 | } 146 | 147 | private void and(int cycle, short adr) { 148 | regA &= read8(adr); 149 | nFlag = (byte) ((regA >> 7) & 1); 150 | zFlag = (byte) (regA == 0 ? 1 : 0); 151 | rest -= cycle; 152 | } 153 | 154 | private void ora(int cycle, short adr) { 155 | regA |= read8(adr); 156 | nFlag = (byte) ((regA >> 7) & 1); 157 | zFlag = (byte) (regA == 0 ? 1 : 0); 158 | rest -= cycle; 159 | } 160 | 161 | private void eor(int cycle, short adr) { 162 | regA ^= read8(adr); 163 | nFlag = (byte) ((regA >> 7) & 1); 164 | zFlag = (byte) (regA == 0 ? 1 : 0); 165 | rest -= cycle; 166 | } 167 | 168 | private void bit(int cycle, short adr) { 169 | byte t = read8(adr); 170 | nFlag = (byte) ((t >> 7) & 1); 171 | vFlag = (byte) ((t >> 6) & 1); 172 | zFlag = (byte) ((regA & t) == 0 ? 1 : 0); 173 | rest -= cycle; 174 | } 175 | 176 | private void loadA(int cycle, short adr) { 177 | regA = read8(adr); 178 | nFlag = (byte) ((regA >> 7) & 1); 179 | zFlag = (byte) (regA == 0 ? 1 : 0); 180 | rest -= cycle; 181 | } 182 | 183 | private void loadX(int cycle, short adr) { 184 | regX = read8(adr); 185 | nFlag = (byte) ((regX >> 7) & 1); 186 | zFlag = (byte) (regX == 0 ? 1 : 0); 187 | rest -= cycle; 188 | } 189 | 190 | private void loadY(int cycle, short adr) { 191 | regY = read8(adr); 192 | nFlag = (byte) ((regY >> 7) & 1); 193 | zFlag = (byte) (regY == 0 ? 1 : 0); 194 | rest -= cycle; 195 | } 196 | 197 | private void store(int cycle, byte reg, short adr) { 198 | write8(adr, reg); 199 | rest -= cycle; 200 | } 201 | 202 | private void movA(int cycle, byte src) { 203 | regA = src; 204 | nFlag = (byte) ((src >> 7) & 1); 205 | zFlag = (byte) (src == 0 ? 1 : 0); 206 | rest -= cycle; 207 | } 208 | 209 | private void movX(int cycle, byte src) { 210 | regX = src; 211 | nFlag = (byte) ((src >> 7) & 1); 212 | zFlag = (byte) (src == 0 ? 1 : 0); 213 | rest -= cycle; 214 | } 215 | 216 | private void movY(int cycle, byte src) { 217 | regY = src; 218 | nFlag = (byte) ((src >> 7) & 1); 219 | zFlag = (byte) (src == 0 ? 1 : 0); 220 | rest -= cycle; 221 | } 222 | 223 | private byte asli(byte arg) { 224 | cFlag = (byte) ((arg >> 7) & 1); 225 | arg <<= 1; 226 | nFlag = (byte) ((arg >> 7) & 1); 227 | zFlag = (byte) (arg == 0 ? 1 : 0); 228 | return arg; 229 | } 230 | 231 | private byte lsri(byte arg) { 232 | cFlag = (byte) (arg & 1); 233 | arg = (byte) ((arg & 0xff) >> 1); 234 | nFlag = (byte) ((arg >> 7) & 1); 235 | zFlag = (byte) (arg == 0 ? 1 : 0); 236 | return arg; 237 | } 238 | 239 | private byte roli(byte arg) { 240 | byte u = arg; 241 | arg = (byte) ((arg << 1) | cFlag); 242 | cFlag = (byte) ((u >> 7) & 1); 243 | nFlag = (byte) ((arg >> 7) & 1); 244 | zFlag = (byte) (arg == 0 ? 1 : 0); 245 | return arg; 246 | } 247 | 248 | private byte rori(byte arg) { 249 | byte u = arg; 250 | arg = (byte) (((arg & 0xff) >> 1) | (cFlag << 7)); 251 | cFlag = (byte) (u & 1); 252 | nFlag = (byte) ((arg >> 7) & 1); 253 | zFlag = (byte) (arg == 0 ? 1 : 0); 254 | return arg; 255 | } 256 | 257 | private byte inci(byte arg) { 258 | arg++; 259 | nFlag = (byte) ((arg >> 7) & 1); 260 | zFlag = (byte) (arg == 0 ? 1 : 0); 261 | return arg; 262 | } 263 | 264 | private byte deci(byte arg) { 265 | arg--; 266 | nFlag = (byte) ((arg >> 7) & 1); 267 | zFlag = (byte) (arg == 0 ? 1 : 0); 268 | return arg; 269 | } 270 | 271 | private void asla(int cycle) { 272 | regA = asli(regA); 273 | rest -= cycle; 274 | } 275 | 276 | private void asl(int cycle, short adr) { 277 | write8(adr, asli(read8(adr))); 278 | rest -= cycle; 279 | } 280 | 281 | private void lsra(int cycle) { 282 | regA = lsri(regA); 283 | rest -= cycle; 284 | } 285 | 286 | private void lsr(int cycle, short adr) { 287 | write8(adr, lsri(read8(adr))); 288 | rest -= cycle; 289 | } 290 | 291 | private void rola(int cycle) { 292 | regA = roli(regA); 293 | rest -= cycle; 294 | } 295 | 296 | private void rol(int cycle, short adr) { 297 | write8(adr, roli(read8(adr))); 298 | rest -= cycle; 299 | } 300 | 301 | private void rora(int cycle) { 302 | regA = rori(regA); 303 | rest -= cycle; 304 | } 305 | 306 | private void ror(int cycle, short adr) { 307 | write8(adr, rori(read8(adr))); 308 | rest -= cycle; 309 | } 310 | 311 | private void incX(int cycle) { 312 | regX = inci(regX); 313 | rest -= cycle; 314 | } 315 | 316 | private void incY(int cycle) { 317 | regY = inci(regY); 318 | rest -= cycle; 319 | } 320 | 321 | private void inc(int cycle, short adr) { 322 | write8(adr, inci(read8(adr))); 323 | rest -= cycle; 324 | } 325 | 326 | private void decX(int cycle) { 327 | regX = deci(regX); 328 | rest -= cycle; 329 | } 330 | 331 | private void decY(int cycle) { 332 | regY = deci(regY); 333 | rest -= cycle; 334 | } 335 | 336 | private void dec(int cycle, short adr) { 337 | write8(adr, deci(read8(adr))); 338 | rest -= cycle; 339 | } 340 | 341 | private void bra(int cycle, boolean cond) { 342 | byte rel = read8(imm()); 343 | rest -= cycle; 344 | if (cond) { 345 | rest -= (regPC & 0xff00) == ((regPC + rel) & 0xff00) ? 1 : 2; 346 | regPC += rel; 347 | } 348 | } 349 | 350 | public void exec(int clk) { 351 | rest += clk; 352 | mclock += clk; 353 | 354 | do { 355 | if (iFlag == 0) { // check IRQs 356 | if (resetLine) { 357 | execIrq(IrqType.RESET); 358 | resetLine = false; 359 | } else if (irqLine) { 360 | execIrq(IrqType.IRQ); 361 | irqLine = false; 362 | } 363 | } 364 | 365 | if (logging) 366 | log(); 367 | 368 | byte opc = read8(regPC++); 369 | oprPC = regPC; 370 | 371 | switch (opc & 0xff) { 372 | /* ALU */ 373 | case 0x69: 374 | adc(2, imm()); 375 | break; 376 | case 0x65: 377 | adc(3, zp()); 378 | break; 379 | case 0x75: 380 | adc(4, zpx()); 381 | break; 382 | case 0x6D: 383 | adc(4, abs()); 384 | break; 385 | case 0x7D: 386 | adc(4, abx()); 387 | break; 388 | case 0x79: 389 | adc(4, aby()); 390 | break; 391 | case 0x61: 392 | adc(6, zpxi()); 393 | break; 394 | case 0x71: 395 | adc(5, zpiy()); 396 | break; 397 | 398 | case 0xE9: 399 | sbc(2, imm()); 400 | break; 401 | case 0xE5: 402 | sbc(3, zp()); 403 | break; 404 | case 0xF5: 405 | sbc(4, zpx()); 406 | break; 407 | case 0xED: 408 | sbc(4, abs()); 409 | break; 410 | case 0xFD: 411 | sbc(4, abx()); 412 | break; 413 | case 0xF9: 414 | sbc(4, aby()); 415 | break; 416 | case 0xE1: 417 | sbc(6, zpxi()); 418 | break; 419 | case 0xF1: 420 | sbc(5, zpiy()); 421 | break; 422 | 423 | case 0xC9: 424 | cmp(2, regA, imm()); 425 | break; 426 | case 0xC5: 427 | cmp(3, regA, zp()); 428 | break; 429 | case 0xD5: 430 | cmp(4, regA, zpx()); 431 | break; 432 | case 0xCD: 433 | cmp(4, regA, abs()); 434 | break; 435 | case 0xDD: 436 | cmp(4, regA, abx()); 437 | break; 438 | case 0xD9: 439 | cmp(4, regA, aby()); 440 | break; 441 | case 0xC1: 442 | cmp(6, regA, zpxi()); 443 | break; 444 | case 0xD1: 445 | cmp(5, regA, zpiy()); 446 | break; 447 | 448 | case 0xE0: 449 | cmp(2, regX, imm()); 450 | break; 451 | case 0xE4: 452 | cmp(2, regX, zp()); 453 | break; 454 | case 0xEC: 455 | cmp(3, regX, abs()); 456 | break; 457 | 458 | case 0xC0: 459 | cmp(2, regY, imm()); 460 | break; 461 | case 0xC4: 462 | cmp(2, regY, zp()); 463 | break; 464 | case 0xCC: 465 | cmp(3, regY, abs()); 466 | break; 467 | 468 | case 0x29: 469 | and(2, imm()); 470 | break; 471 | case 0x25: 472 | and(3, zp()); 473 | break; 474 | case 0x35: 475 | and(4, zpx()); 476 | break; 477 | case 0x2D: 478 | and(4, abs()); 479 | break; 480 | case 0x3D: 481 | and(4, abx()); 482 | break; 483 | case 0x39: 484 | and(4, aby()); 485 | break; 486 | case 0x21: 487 | and(6, zpxi()); 488 | break; 489 | case 0x31: 490 | and(5, zpiy()); 491 | break; 492 | 493 | case 0x09: 494 | ora(2, imm()); 495 | break; 496 | case 0x05: 497 | ora(3, zp()); 498 | break; 499 | case 0x15: 500 | ora(4, zpx()); 501 | break; 502 | case 0x0D: 503 | ora(4, abs()); 504 | break; 505 | case 0x1D: 506 | ora(4, abx()); 507 | break; 508 | case 0x19: 509 | ora(4, aby()); 510 | break; 511 | case 0x01: 512 | ora(6, zpxi()); 513 | break; 514 | case 0x11: 515 | ora(5, zpiy()); 516 | break; 517 | 518 | case 0x49: 519 | eor(2, imm()); 520 | break; 521 | case 0x45: 522 | eor(3, zp()); 523 | break; 524 | case 0x55: 525 | eor(4, zpx()); 526 | break; 527 | case 0x4D: 528 | eor(4, abs()); 529 | break; 530 | case 0x5D: 531 | eor(4, abx()); 532 | break; 533 | case 0x59: 534 | eor(4, aby()); 535 | break; 536 | case 0x41: 537 | eor(6, zpxi()); 538 | break; 539 | case 0x51: 540 | eor(5, zpiy()); 541 | break; 542 | 543 | case 0x24: 544 | bit(3, zp()); 545 | break; 546 | case 0x2C: 547 | bit(4, abs()); 548 | break; 549 | 550 | /* laod / store */ 551 | case 0xA9: 552 | loadA(2, imm()); 553 | break; 554 | case 0xA5: 555 | loadA(3, zp()); 556 | break; 557 | case 0xB5: 558 | loadA(4, zpx()); 559 | break; 560 | case 0xAD: 561 | loadA(4, abs()); 562 | break; 563 | case 0xBD: 564 | loadA(4, abx()); 565 | break; 566 | case 0xB9: 567 | loadA(4, aby()); 568 | break; 569 | case 0xA1: 570 | loadA(6, zpxi()); 571 | break; 572 | case 0xB1: 573 | loadA(5, zpiy()); 574 | break; 575 | 576 | case 0xA2: 577 | loadX(2, imm()); 578 | break; 579 | case 0xA6: 580 | loadX(3, zp()); 581 | break; 582 | case 0xB6: 583 | loadX(4, zpy()); 584 | break; 585 | case 0xAE: 586 | loadX(4, abs()); 587 | break; 588 | case 0xBE: 589 | loadX(4, aby()); 590 | break; 591 | 592 | case 0xA0: 593 | loadY(2, imm()); 594 | break; 595 | case 0xA4: 596 | loadY(3, zp()); 597 | break; 598 | case 0xB4: 599 | loadY(4, zpx()); 600 | break; 601 | case 0xAC: 602 | loadY(4, abs()); 603 | break; 604 | case 0xBC: 605 | loadY(4, abx()); 606 | break; 607 | 608 | case 0x85: 609 | store(3, regA, zp()); 610 | break; 611 | case 0x95: 612 | store(4, regA, zpx()); 613 | break; 614 | case 0x8D: 615 | store(4, regA, abs()); 616 | break; 617 | case 0x9D: 618 | store(5, regA, abx()); 619 | break; 620 | case 0x99: 621 | store(5, regA, aby()); 622 | break; 623 | case 0x81: 624 | store(6, regA, zpxi()); 625 | break; 626 | case 0x91: 627 | store(6, regA, zpiy()); 628 | break; 629 | 630 | case 0x86: 631 | store(3, regX, zp()); 632 | break; 633 | case 0x96: 634 | store(4, regX, zpy()); 635 | break; 636 | case 0x8E: 637 | store(4, regX, abs()); 638 | break; 639 | 640 | case 0x84: 641 | store(3, regY, zp()); 642 | break; 643 | case 0x94: 644 | store(4, regY, zpx()); 645 | break; 646 | case 0x8C: 647 | store(4, regY, abs()); 648 | break; 649 | 650 | /* transfer */ 651 | case 0xAA: 652 | movX(2, regA); 653 | break; // TAX 654 | case 0xA8: 655 | movY(2, regA); 656 | break; // TAY 657 | case 0x8A: 658 | movA(2, regX); 659 | break; // TXA 660 | case 0x98: 661 | movA(2, regY); 662 | break; // TYA 663 | case 0xBA: 664 | movX(2, regS); 665 | break; // TSX 666 | case 0x9A: 667 | regS = regX; 668 | rest -= 2; 669 | break; // TXS 670 | 671 | /* shift */ 672 | case 0x0A: 673 | asla(2); 674 | break; 675 | case 0x06: 676 | asl(5, zp()); 677 | break; 678 | case 0x16: 679 | asl(6, zpx()); 680 | break; 681 | case 0x0E: 682 | asl(6, abs()); 683 | break; 684 | case 0x1E: 685 | asl(7, abx()); 686 | break; 687 | 688 | case 0x4A: 689 | lsra(2); 690 | break; 691 | case 0x46: 692 | lsr(5, zp()); 693 | break; 694 | case 0x56: 695 | lsr(6, zpx()); 696 | break; 697 | case 0x4E: 698 | lsr(6, abs()); 699 | break; 700 | case 0x5E: 701 | lsr(7, abx()); 702 | break; 703 | 704 | case 0x2A: 705 | rola(2); 706 | break; 707 | case 0x26: 708 | rol(5, zp()); 709 | break; 710 | case 0x36: 711 | rol(6, zpx()); 712 | break; 713 | case 0x2E: 714 | rol(6, abs()); 715 | break; 716 | case 0x3E: 717 | rol(7, abx()); 718 | break; 719 | 720 | case 0x6A: 721 | rora(2); 722 | break; 723 | case 0x66: 724 | ror(5, zp()); 725 | break; 726 | case 0x76: 727 | ror(6, zpx()); 728 | break; 729 | case 0x6E: 730 | ror(6, abs()); 731 | break; 732 | case 0x7E: 733 | ror(7, abx()); 734 | break; 735 | 736 | case 0xE6: 737 | inc(5, zp()); 738 | break; 739 | case 0xF6: 740 | inc(6, zpx()); 741 | break; 742 | case 0xEE: 743 | inc(6, abs()); 744 | break; 745 | case 0xFE: 746 | inc(7, abx()); 747 | break; 748 | case 0xE8: 749 | incX(2); 750 | break; 751 | case 0xC8: 752 | incY(2); 753 | break; 754 | 755 | case 0xC6: 756 | dec(5, zp()); 757 | break; 758 | case 0xD6: 759 | dec(6, zpx()); 760 | break; 761 | case 0xCE: 762 | dec(6, abs()); 763 | break; 764 | case 0xDE: 765 | dec(7, abx()); 766 | break; 767 | case 0xCA: 768 | decX(2); 769 | break; 770 | case 0x88: 771 | decY(2); 772 | break; 773 | 774 | /* branch */ 775 | case 0x90: 776 | bra(2, cFlag == 0); 777 | break; // BCC 778 | case 0xB0: 779 | bra(2, cFlag != 0); 780 | break; // BCS 781 | case 0xD0: 782 | bra(2, zFlag == 0); 783 | break; // BNE 784 | case 0xF0: 785 | bra(2, zFlag != 0); 786 | break; // BEQ 787 | case 0x10: 788 | bra(2, nFlag == 0); 789 | break; // BPL 790 | case 0x30: 791 | bra(2, nFlag != 0); 792 | break; // BMI 793 | case 0x50: 794 | bra(2, vFlag == 0); 795 | break; // BVC 796 | case 0x70: 797 | bra(2, vFlag != 0); 798 | break; // BVS 799 | 800 | /* jump / call / return */ 801 | case 0x4C: 802 | regPC = abs(); 803 | rest -= 3; 804 | break; // JMP abs 805 | case 0x6C: 806 | regPC = absi(); 807 | rest -= 5; 808 | break; // JMP (abs) 809 | 810 | case 0x20: 811 | push16((short) (regPC + 1)); 812 | regPC = abs(); 813 | rest -= 6; 814 | break; // JSR 815 | 816 | case 0x60: 817 | regPC = (short) (pop16() + 1); 818 | rest -= 6; 819 | break; // RTS 820 | case 0x40: 821 | unbindFlags(pop8()); 822 | regPC = pop16(); 823 | rest -= 6; 824 | break; // RTI 825 | 826 | /* flag */ 827 | case 0x38: 828 | cFlag = 1; 829 | rest -= 2; 830 | break; // SEC 831 | case 0xF8: 832 | dFlag = 1; 833 | rest -= 2; 834 | break; // SED 835 | case 0x78: 836 | iFlag = 1; 837 | rest -= 2; 838 | break; // SEI 839 | 840 | case 0x18: 841 | cFlag = 0; 842 | rest -= 2; 843 | break; // CLC 844 | case 0xD8: 845 | dFlag = 0; 846 | rest -= 2; 847 | break; // CLD 848 | case 0x58: 849 | iFlag = 0; 850 | rest -= 2; 851 | break; // CLI 852 | case 0xB8: 853 | vFlag = 0; 854 | rest -= 2; 855 | break; // CLV 856 | 857 | /* stack */ 858 | case 0x48: 859 | push8(regA); 860 | rest -= 3; 861 | break; // PHA 862 | case 0x08: 863 | push8(bindFlags()); 864 | rest -= 3; 865 | break; // PHP 866 | case 0x68: 867 | regA = pop8(); 868 | nFlag = (byte) ((regA >> 7) & 1); 869 | zFlag = (byte) (regA == 0 ? 1 : 0); 870 | rest -= 4; 871 | break; // PLA 872 | case 0x28: 873 | unbindFlags(pop8()); 874 | rest -= 4; 875 | break; // PLP 876 | 877 | /* others */ 878 | case 0x00: // BRK 879 | bFlag = 1; 880 | regPC++; 881 | execIrq(IrqType.IRQ); 882 | break; 883 | 884 | case 0xEA: 885 | rest -= 2; 886 | break; // NOP 887 | 888 | default: 889 | System.out.printf("undefined opcode: %02X\n", opc & 0xff); 890 | break; 891 | } 892 | } while (rest > 0); 893 | } 894 | 895 | public void setLogging(boolean b) { 896 | logging = b; 897 | } 898 | 899 | public void setNmi(boolean b) { 900 | // edge sensitive 901 | if (!nmiLine && b) 902 | execIrq(IrqType.NMI); 903 | nmiLine = b; 904 | } 905 | 906 | public void setIrq(boolean b) { 907 | irqLine = b; 908 | } 909 | 910 | public void setReset(boolean b) { 911 | resetLine = b; 912 | } 913 | 914 | public long getMasterClock() { 915 | return mclock - rest; 916 | } 917 | 918 | public double getFrequency() { 919 | return 3579545.0 / 2; 920 | } 921 | 922 | private byte read8(short adr) { 923 | return mbc.read(adr); 924 | } 925 | 926 | private short read16(short adr) { 927 | return (short) ((mbc.read(adr) & 0xff) | ((mbc.read((short) (adr + 1)) & 0xff) << 8)); 928 | } 929 | 930 | private void write8(short adr, byte dat) { 931 | mbc.write(adr, (byte) dat); 932 | } 933 | 934 | private void write16(short adr, short dat) { 935 | mbc.write(adr, (byte) dat); 936 | mbc.write((short) (adr + 1), (byte) (dat >> 8)); 937 | } 938 | 939 | private enum IrqType { 940 | NMI, IRQ, RESET, 941 | } 942 | 943 | private void execIrq(IrqType it) { 944 | if (logging) 945 | System.out.printf("%s occured!\n", it == IrqType.RESET ? "RESET" 946 | : it == IrqType.NMI ? "NMI" : "IRQ"); 947 | 948 | short vect = (short) ((it == IrqType.RESET) ? 0xFFFC 949 | : (it == IrqType.NMI) ? 0xFFFA : (it == IrqType.IRQ) ? 0xFFFE 950 | : 0xFFFE); 951 | 952 | push16(regPC); 953 | push8(bindFlags()); 954 | iFlag = 1; 955 | regPC = read16(vect); 956 | rest -= 7; 957 | } 958 | 959 | private int cnt = 0; 960 | 961 | private void log() { 962 | byte opc = read8(regPC); 963 | short opr = read16((short) (regPC + 1)); 964 | String s = disasm(regPC, opc, opr); 965 | 966 | System.out 967 | .printf( 968 | "%06d: %04d: %04X %-13s : A:%02X X:%02X Y:%02X S:%02X P:%c%c%c%c%c%c%c\n", 969 | cnt++, rest, regPC & 0xffff, s, regA & 0xff, 970 | regX & 0xff, regY & 0xff, regS & 0xff, nFlag != 0 ? 'N' 971 | : 'n', vFlag != 0 ? 'V' : 'v', bFlag != 0 ? 'B' 972 | : 'b', dFlag != 0 ? 'D' : 'd', iFlag != 0 ? 'I' 973 | : 'i', zFlag != 0 ? 'Z' : 'z', cFlag != 0 ? 'C' 974 | : 'c'); 975 | } 976 | 977 | static final String[] mne = { "BRK", "ORA", "UNK", "UNK", "UNK", "ORA", 978 | "ASL", "UNK", "PHP", "ORA", "ASL", "UNK", "UNK", "ORA", "ASL", 979 | "UNK", "BPL", "ORA", "UNK", "UNK", "UNK", "ORA", "ASL", "UNK", 980 | "CLC", "ORA", "UNK", "UNK", "UNK", "ORA", "ASL", "UNK", "JSR", 981 | "AND", "UNK", "UNK", "BIT", "AND", "ROL", "UNK", "PLP", "AND", 982 | "ROL", "UNK", "BIT", "AND", "ROL", "UNK", "BMI", "AND", "UNK", 983 | "UNK", "UNK", "AND", "ROL", "UNK", "SEC", "AND", "UNK", "UNK", 984 | "UNK", "AND", "ROL", "UNK", "RTI", "EOR", "UNK", "UNK", "UNK", 985 | "EOR", "LSR", "UNK", "PHA", "EOR", "LSR", "UNK", "JMP", "EOR", 986 | "LSR", "UNK", "BVC", "EOR", "UNK", "UNK", "UNK", "EOR", "LSR", 987 | "UNK", "CLI", "EOR", "UNK", "UNK", "UNK", "EOR", "LSR", "UNK", 988 | "RTS", "ADC", "UNK", "UNK", "UNK", "ADC", "ROR", "UNK", "PLA", 989 | "ADC", "ROR", "UNK", "JMP", "ADC", "ROR", "UNK", "BVS", "ADC", 990 | "UNK", "UNK", "UNK", "ADC", "ROR", "UNK", "SEI", "ADC", "UNK", 991 | "UNK", "UNK", "ADC", "ROR", "UNK", "UNK", "STA", "UNK", "UNK", 992 | "STY", "STA", "STX", "UNK", "DEY", "UNK", "TXA", "UNK", "STY", 993 | "STA", "STX", "UNK", "BCC", "STA", "UNK", "UNK", "STY", "STA", 994 | "STX", "UNK", "TYA", "STA", "TXS", "UNK", "UNK", "STA", "UNK", 995 | "UNK", "LDY", "LDA", "LDX", "UNK", "LDY", "LDA", "LDX", "UNK", 996 | "TAY", "LDA", "TAX", "UNK", "LDY", "LDA", "LDX", "UNK", "BCS", 997 | "LDA", "UNK", "UNK", "LDY", "LDA", "LDX", "UNK", "CLV", "LDA", 998 | "TSX", "UNK", "LDY", "LDA", "LDX", "UNK", "CPY", "CMP", "UNK", 999 | "UNK", "CPY", "CMP", "DEC", "UNK", "INY", "CMP", "DEX", "UNK", 1000 | "CPY", "CMP", "DEC", "UNK", "BNE", "CMP", "UNK", "UNK", "UNK", 1001 | "CMP", "DEC", "UNK", "CLD", "CMP", "UNK", "UNK", "UNK", "CMP", 1002 | "DEC", "UNK", "CPX", "SBC", "UNK", "UNK", "CPX", "SBC", "INC", 1003 | "UNK", "INX", "SBC", "NOP", "UNK", "CPX", "SBC", "INC", "UNK", 1004 | "BEQ", "SBC", "UNK", "UNK", "UNK", "SBC", "INC", "UNK", "SED", 1005 | "SBC", "UNK", "UNK", "UNK", "SBC", "INC", "UNK", }; 1006 | 1007 | static final int adr[] = { 0, 12, 0, 0, 0, 8, 8, 0, 0, 1, 2, 0, 0, 3, 3, 0, 1008 | 14, 13, 0, 0, 0, 9, 9, 0, 0, 5, 0, 0, 0, 4, 4, 0, 3, 12, 0, 0, 8, 1009 | 8, 8, 0, 0, 1, 2, 0, 3, 3, 3, 0, 14, 13, 0, 0, 0, 9, 9, 0, 0, 5, 0, 1010 | 0, 0, 4, 4, 0, 0, 12, 0, 0, 0, 8, 8, 0, 0, 1, 2, 0, 3, 3, 3, 0, 14, 1011 | 13, 0, 0, 0, 9, 9, 0, 0, 5, 0, 0, 0, 4, 4, 0, 0, 12, 0, 0, 0, 8, 8, 1012 | 0, 0, 1, 2, 0, 7, 3, 3, 0, 14, 13, 0, 0, 0, 9, 9, 0, 0, 5, 0, 0, 0, 1013 | 4, 4, 0, 0, 12, 0, 0, 8, 8, 8, 0, 0, 0, 0, 0, 3, 3, 3, 0, 14, 13, 1014 | 0, 0, 9, 9, 10, 0, 0, 5, 0, 0, 0, 4, 0, 0, 1, 12, 1, 0, 8, 8, 8, 0, 1015 | 0, 1, 0, 0, 3, 3, 3, 0, 14, 13, 0, 0, 9, 9, 10, 0, 0, 5, 0, 0, 4, 1016 | 4, 5, 0, 1, 12, 0, 0, 8, 8, 8, 0, 0, 1, 0, 0, 3, 3, 3, 0, 14, 13, 1017 | 0, 0, 0, 9, 9, 0, 0, 5, 0, 0, 0, 4, 4, 0, 1, 12, 0, 0, 8, 8, 8, 0, 1018 | 0, 1, 0, 0, 3, 3, 3, 0, 14, 13, 0, 0, 0, 9, 9, 0, 0, 5, 0, 0, 0, 4, 1019 | 4, 0, }; 1020 | 1021 | private String disasm(short pc, byte opc, short opr) { 1022 | String op = mne[opc & 0xff]; 1023 | switch (adr[opc & 0xff]) { 1024 | case 0: // Implied 1025 | return op; 1026 | case 1: // Immediate #$xx 1027 | return String.format("%s #$%02X", op, opr & 0xff); 1028 | case 2: // Accumerate 1029 | return String.format("%s A", op); 1030 | case 3: // Absolute $xxxx 1031 | return String.format("%s $%04X", op, opr & 0xffff); 1032 | case 4: // Absolute X $xxxx,X 1033 | return String.format("%s $%04X,X", op, opr & 0xffff); 1034 | case 5: // Absolute Y $xxxx,Y 1035 | return String.format("%s $%04X,Y", op, opr & 0xffff); 1036 | case 6: // Absolute X indirected ($xxxx,X) 1037 | return String.format("%s ($%04X,X)", op, opr & 0xffff); 1038 | case 7: // Absolute indirected 1039 | return String.format("%s ($%04X)", op, opr & 0xffff); 1040 | case 8: // Zero page $xx 1041 | return String.format("%s $%02X", op, opr & 0xff); 1042 | case 9: // Zero page indexed X $xx,X 1043 | return String.format("%s $%02X,X", op, opr & 0xff); 1044 | case 10: // Zero page indexed Y $xx,Y 1045 | return String.format("%s $%02X,Y", op, opr & 0xff); 1046 | case 11: // Zero page indirected ($xx) 1047 | return String.format("%s ($%02X)", op, opr & 0xff); 1048 | case 12: // Zero page indexed indirected ($xx,X) 1049 | return String.format("%s ($%02X,X)", op, opr & 0xff); 1050 | case 13: // Zero page indirected indexed ($xx),Y 1051 | return String.format("%s ($%02X),Y", op, opr & 0xff); 1052 | case 14: // Relative 1053 | return String.format("%s $%04X", op, 1054 | (pc + (byte) (opr & 0xff) + 2) & 0xffff); 1055 | default: 1056 | return ""; 1057 | } 1058 | } 1059 | 1060 | private byte regA, regX, regY, regS; 1061 | private short regPC; 1062 | private byte cFlag, zFlag, iFlag, dFlag, bFlag, vFlag, nFlag; 1063 | 1064 | private short oprPC; 1065 | 1066 | private int rest; 1067 | private long mclock; 1068 | private boolean nmiLine, irqLine, resetLine; 1069 | 1070 | private boolean logging; 1071 | 1072 | private Nes nes; 1073 | private Mbc mbc; 1074 | } 1075 | -------------------------------------------------------------------------------- /src/main/java/jp/tanakh/bjne/nes/Mapper.java: -------------------------------------------------------------------------------- 1 | package jp.tanakh.bjne.nes; 2 | 3 | import jp.tanakh.bjne.nes.Renderer.SoundInfo; 4 | 5 | public interface Mapper { 6 | int mapperNo(); 7 | 8 | void reset(); 9 | 10 | void write(short adr, byte dat); 11 | 12 | void hblank(int line); 13 | 14 | void audio(SoundInfo info); 15 | 16 | // void serialize(StateData sd){} 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/jp/tanakh/bjne/nes/MapperAdapter.java: -------------------------------------------------------------------------------- 1 | package jp.tanakh.bjne.nes; 2 | 3 | import jp.tanakh.bjne.nes.Renderer.SoundInfo; 4 | 5 | public abstract class MapperAdapter implements Mapper { 6 | 7 | @Override 8 | public void audio(SoundInfo info) { 9 | } 10 | 11 | @Override 12 | public void hblank(int line) { 13 | } 14 | 15 | @Override 16 | public void reset() { 17 | } 18 | 19 | @Override 20 | public void write(short adr, byte dat) { 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/jp/tanakh/bjne/nes/MapperMaker.java: -------------------------------------------------------------------------------- 1 | package jp.tanakh.bjne.nes; 2 | 3 | import java.io.File; 4 | import java.io.IOException; 5 | import java.lang.reflect.Constructor; 6 | import java.net.JarURLConnection; 7 | import java.net.URL; 8 | import java.util.ArrayList; 9 | import java.util.Enumeration; 10 | import java.util.jar.JarEntry; 11 | import java.util.jar.JarFile; 12 | 13 | public class MapperMaker { 14 | @SuppressWarnings("unchecked") 15 | public static Mapper makeMapper(int num, Nes n) { 16 | Class[] classes; 17 | try { 18 | classes = getClasses("jp.tanakh.bjne.nes.mapper"); 19 | if (classes == null) 20 | return null; 21 | 22 | for (Class c : classes) { 23 | try { 24 | Class mc = (Class) c; 25 | Constructor ctor = mc.getConstructor(Nes.class); 26 | Mapper ret = ctor.newInstance(n); 27 | if (ret.mapperNo() == num) 28 | return ret; 29 | } catch (Exception e) { 30 | } 31 | } 32 | 33 | } catch (Exception e) { 34 | return null; 35 | } 36 | return null; 37 | } 38 | 39 | public static Class[] getClasses(String pckgname) 40 | throws ClassNotFoundException, IOException { 41 | ClassLoader cld = Thread.currentThread().getContextClassLoader(); 42 | if (cld == null) 43 | return null; 44 | String path = pckgname.replace('.', '/'); 45 | URL resource = cld.getResource(path); 46 | if (resource == null) 47 | return null; 48 | 49 | ArrayList> classes = new ArrayList>(); 50 | 51 | if (resource.getProtocol() == "jar") { 52 | JarURLConnection conn = (JarURLConnection) resource 53 | .openConnection(); 54 | JarFile jar = conn.getJarFile(); 55 | for (Enumeration en = jar.entries(); en.hasMoreElements();) { 56 | JarEntry entry = en.nextElement(); 57 | String entryName = entry.getName(); 58 | if (!entryName.matches(path + "/.*.class")) 59 | continue; 60 | String className = entryName.replaceAll("/", ".").replace( 61 | ".class", ""); 62 | classes.add(Class.forName(className)); 63 | } 64 | } else { 65 | File directory = new File(resource.getFile()); 66 | if (!directory.exists()) 67 | return null; 68 | 69 | String[] files = directory.list(); 70 | for (int i = 0; i < files.length; i++) 71 | if (files[i].endsWith(".class")) 72 | classes.add(Class.forName(pckgname + '.' 73 | + files[i].replace(".class", ""))); 74 | } 75 | 76 | Class[] ret = new Class[classes.size()]; 77 | classes.toArray(ret); 78 | return ret; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/main/java/jp/tanakh/bjne/nes/Mbc.java: -------------------------------------------------------------------------------- 1 | package jp.tanakh.bjne.nes; 2 | 3 | import java.util.Arrays; 4 | 5 | public class Mbc { 6 | public Mbc(Nes n) { 7 | nes = n; 8 | rom = vrom = null; 9 | isVram = false; 10 | } 11 | 12 | public void reset() { 13 | Arrays.fill(ram, (byte) 0x00); 14 | 15 | rom = nes.getRom().getRom(); 16 | vrom = nes.getRom().getChr(); 17 | sram = nes.getRom().getSram(); 18 | vram = nes.getRom().getVram(); 19 | 20 | for (int i = 0; i < 4; i++) 21 | mapRom(i, i); 22 | if (vrom != null) 23 | for (int i = 0; i < 8; i++) 24 | mapVrom(i, i); 25 | else 26 | for (int i = 0; i < 8; i++) 27 | mapVram(i, i); 28 | 29 | sramEnabled = true; 30 | } 31 | 32 | public void mapRom(int page, int val) { 33 | romPage[page] = (val % (nes.getRom().romSize() * 2)) * 0x2000; 34 | } 35 | 36 | public void mapVrom(int page, int val) { 37 | if (vrom != null) { 38 | chrPage[page] = (val % (nes.getRom().chrSize() * 8)) * 0x0400; 39 | isVram = false; 40 | } 41 | } 42 | 43 | public void mapVram(int page, int val) { 44 | // TODO : VRAM size 45 | chrPage[page] = (val % 8) * 0x400; 46 | isVram = true; 47 | } 48 | 49 | public byte read(short adr) { 50 | switch ((adr & 0xffff) >> 11) { 51 | case 0x00: 52 | case 0x01: 53 | case 0x02: 54 | case 0x03: // 0x0000 - 0x1FFF 55 | return ram[adr & 0x7ff]; 56 | case 0x04: 57 | case 0x05: 58 | case 0x06: 59 | case 0x07: // 0x2000 - 0x3FFF 60 | return nes.getRegs().read(adr); 61 | case 0x08: 62 | case 0x09: 63 | case 0x0A: 64 | case 0x0B: // 0x4000 - 0x5FFF 65 | if (adr < 0x4020) 66 | return nes.getRegs().read(adr); 67 | return 0; // TODO : Expanision ROM 68 | case 0x0C: 69 | case 0x0D: 70 | case 0x0E: 71 | case 0x0F: // 0x6000 - 0x7FFF 72 | if (sramEnabled) 73 | return sram[adr & 0x1fff]; 74 | else 75 | return 0x00; 76 | case 0x10: 77 | case 0x11: 78 | case 0x12: 79 | case 0x13: // 0x8000 - 0x9FFF 80 | return rom[romPage[0] + (adr & 0x1fff)]; 81 | case 0x14: 82 | case 0x15: 83 | case 0x16: 84 | case 0x17: // 0xA000 - 0xBFFF 85 | return rom[romPage[1] + (adr & 0x1fff)]; 86 | case 0x18: 87 | case 0x19: 88 | case 0x1A: 89 | case 0x1B: // 0xC000?0xDFFF 90 | return rom[romPage[2] + (adr & 0x1fff)]; 91 | case 0x1C: 92 | case 0x1D: 93 | case 0x1E: 94 | case 0x1F: // 0xE000?0xFFFF 95 | return rom[romPage[3] + (adr & 0x1fff)]; 96 | } 97 | return 0x00; 98 | } 99 | 100 | public void write(short adr, byte dat) { 101 | switch ((adr & 0xffff) >> 11) { 102 | case 0x00: 103 | case 0x01: 104 | case 0x02: 105 | case 0x03: // 0x0000 - 0x1FFF 106 | ram[adr & 0x7ff] = dat; 107 | break; 108 | case 0x04: 109 | case 0x05: 110 | case 0x06: 111 | case 0x07: // 0x2000 - 0x3FFF 112 | nes.getRegs().write(adr, dat); 113 | break; 114 | case 0x08: 115 | case 0x09: 116 | case 0x0A: 117 | case 0x0B: // 0x4000 - 0x5FFF 118 | if (adr < 0x4020) 119 | nes.getRegs().write(adr, dat); 120 | else if (nes.getMapper() != null) 121 | nes.getMapper().write(adr, dat); 122 | break; 123 | case 0x0C: 124 | case 0x0D: 125 | case 0x0E: 126 | case 0x0F: // 0x6000 - 0x7FFF 127 | if (sramEnabled) 128 | sram[adr & 0x1fff] = dat; 129 | else if (nes.getMapper() != null) 130 | nes.getMapper().write(adr, dat); 131 | break; 132 | 133 | case 0x10: 134 | case 0x11: 135 | case 0x12: 136 | case 0x13: // 0x8000 - 0xFFFF 137 | case 0x14: 138 | case 0x15: 139 | case 0x16: 140 | case 0x17: 141 | case 0x18: 142 | case 0x19: 143 | case 0x1A: 144 | case 0x1B: 145 | case 0x1C: 146 | case 0x1D: 147 | case 0x1E: 148 | case 0x1F: 149 | if (nes.getMapper() != null) 150 | nes.getMapper().write(adr, dat); 151 | break; 152 | } 153 | } 154 | 155 | public byte readChrRom(short adr) { 156 | if (isVram) 157 | return vram[chrPage[(adr >> 10) & 7] + (adr & 0x03ff)]; 158 | else 159 | return vrom[chrPage[(adr >> 10) & 7] + (adr & 0x03ff)]; 160 | } 161 | 162 | public void writeChrRom(short adr, byte dat) { 163 | if (isVram) 164 | vram[chrPage[(adr >> 10) & 7] + (adr & 0x03ff)] = dat; 165 | else 166 | vrom[chrPage[(adr >> 10) & 7] + (adr & 0x03ff)] = dat; 167 | } 168 | 169 | private byte[] rom, vrom, sram, vram; 170 | private byte[] ram = new byte[0x800]; 171 | 172 | // byte[][] romPage = new byte[4][]; 173 | // byte[][] chrPage = new byte[8][]; 174 | private int[] romPage = new int[4]; 175 | private int[] chrPage = new int[8]; 176 | 177 | private boolean isVram; 178 | private boolean sramEnabled; 179 | 180 | private Nes nes; 181 | } 182 | -------------------------------------------------------------------------------- /src/main/java/jp/tanakh/bjne/nes/Nes.java: -------------------------------------------------------------------------------- 1 | package jp.tanakh.bjne.nes; 2 | 3 | import java.io.IOException; 4 | 5 | public class Nes { 6 | public Nes(Renderer r) { 7 | renderer = r; 8 | cpu = new Cpu(this); 9 | apu = new Apu(this); 10 | ppu = new Ppu(this); 11 | mbc = new Mbc(this); 12 | regs = new Regs(this); 13 | rom = new Rom(this); 14 | mapper = null; 15 | } 16 | 17 | public void load(String fname) throws IOException { 18 | rom.load(fname); 19 | mapper = MapperMaker.makeMapper(rom.mapperNo(), this); 20 | if (mapper == null) 21 | throw new IOException(String.format("unsupported mapper: #%d", rom.mapperNo())); 22 | reset(); 23 | } 24 | 25 | public boolean checkMapper() { 26 | return mapper != null; 27 | } 28 | 29 | public void saveSram(String fname) { 30 | rom.saveSram(fname); 31 | } 32 | 33 | public void loadSram(String fname) { 34 | rom.loadSram(fname); 35 | } 36 | 37 | public void saveState(String fname) { 38 | // TODO 39 | } 40 | 41 | public void loadState(String fname) { 42 | // TODO 43 | } 44 | 45 | public void reset() { 46 | // reset rom & mbc first 47 | rom.reset(); 48 | mbc.reset(); 49 | 50 | // reset mapper 51 | mapper.reset(); 52 | 53 | // reset rest 54 | cpu.reset(); 55 | apu.reset(); 56 | ppu.reset(); 57 | regs.reset(); 58 | 59 | System.out.println("Reset virtual machine ..."); 60 | } 61 | 62 | public void execFrame() { 63 | // CPU clock is 1.7897725MHz 64 | // 1789772.5 / 60 / 262 = 113.85... 65 | // 114 cycles per line? 66 | // 1789772.5 / 262 / 114 = 59.922 fps ? 67 | 68 | Renderer.ScreenInfo scri = renderer.requestScreen(256, 240); 69 | Renderer.SoundInfo sndi = renderer.requestSound(); 70 | Renderer.InputInfo inpi = renderer.requestInput(2, 8); 71 | 72 | if (sndi != null) { 73 | apu.genAudio(sndi); 74 | renderer.outputSound(sndi); 75 | } 76 | if (inpi != null) 77 | regs.setInput(inpi.buf); 78 | 79 | regs.setVBlank(false, true); 80 | regs.startFrame(); 81 | for (int i = 0; i < 240; i++) { 82 | if (mapper != null) 83 | mapper.hblank(i); 84 | regs.startScanline(); 85 | if (scri != null) 86 | ppu.render(i, scri); 87 | ppu.spriteCheck(i); 88 | apu.sync(); 89 | cpu.exec(114); 90 | regs.endScanline(); 91 | } 92 | 93 | if ((regs.getFrameIrq() & 0xC0) == 0) 94 | cpu.setIrq(true); 95 | 96 | for (int i = 240; i < 262; i++) { 97 | if (mapper != null) 98 | mapper.hblank(i); 99 | apu.sync(); 100 | if (i == 241) { 101 | regs.setVBlank(true, false); 102 | cpu.exec(0); // one extra op will execute after VBLANK 103 | regs.setVBlank(regs.getIsVBlank(), true); 104 | cpu.exec(114); 105 | } else 106 | cpu.exec(114); 107 | } 108 | 109 | if (scri != null) 110 | renderer.outputScreen(scri); 111 | } 112 | 113 | public Rom getRom() { 114 | return rom; 115 | } 116 | 117 | public Cpu getCpu() { 118 | return cpu; 119 | } 120 | 121 | public Ppu getPpu() { 122 | return ppu; 123 | } 124 | 125 | public Apu getApu() { 126 | return apu; 127 | } 128 | 129 | public Mbc getMbc() { 130 | return mbc; 131 | } 132 | 133 | public Regs getRegs() { 134 | return regs; 135 | } 136 | 137 | public Mapper getMapper() { 138 | return mapper; 139 | } 140 | 141 | private Rom rom; 142 | private Cpu cpu; 143 | private Ppu ppu; 144 | private Apu apu; 145 | private Mbc mbc; 146 | private Regs regs; 147 | private Mapper mapper; 148 | 149 | private Renderer renderer; 150 | } 151 | -------------------------------------------------------------------------------- /src/main/java/jp/tanakh/bjne/nes/Ppu.java: -------------------------------------------------------------------------------- 1 | package jp.tanakh.bjne.nes; 2 | 3 | import java.util.Arrays; 4 | 5 | public class Ppu { 6 | public Ppu(Nes n) { 7 | nes = n; 8 | initPalette(); 9 | } 10 | 11 | public void reset() { 12 | Arrays.fill(sprram, (byte) 0x00); 13 | Arrays.fill(nameTable[0], (byte) 0x00); 14 | Arrays.fill(nameTable[1], (byte) 0x00); 15 | Arrays.fill(nameTable[2], (byte) 0x00); 16 | Arrays.fill(nameTable[3], (byte) 0x00); 17 | Arrays.fill(palette, (byte) 0x00); 18 | 19 | if (nes.getRom().isFourScreen()) 20 | setMirroring(MirrorType.FOUR_SCREEN); 21 | else if (nes.getRom().mirror() == Rom.MirrorType.HORIZONTAL) 22 | setMirroring(MirrorType.HOLIZONTAL); 23 | else if (nes.getRom().mirror() == Rom.MirrorType.VERTICAL) 24 | setMirroring(MirrorType.VERTICAL); 25 | else 26 | setMirroring(MirrorType.FOUR_SCREEN); 27 | } 28 | 29 | public void setMirroring(MirrorType mt) { 30 | switch (mt) { 31 | case HOLIZONTAL: 32 | setMirroring(0, 0, 1, 1); 33 | break; 34 | case VERTICAL: 35 | setMirroring(0, 1, 0, 1); 36 | break; 37 | case FOUR_SCREEN: 38 | setMirroring(0, 1, 2, 3); 39 | break; 40 | case SINGLE_SCREEN: 41 | setMirroring(0, 0, 0, 0); 42 | break; 43 | } 44 | } 45 | 46 | public void setMirroring(int m0, int m1, int m2, int m3) { 47 | namePage[0] = nameTable[m0]; 48 | namePage[1] = nameTable[m1]; 49 | namePage[2] = nameTable[m2]; 50 | namePage[3] = nameTable[m3]; 51 | } 52 | 53 | public void render(int line, Renderer.ScreenInfo scri) { 54 | Arrays.fill(buf, palette[0]); 55 | 56 | if (nes.getRegs().getBgVisible()) 57 | renderBG(line, buf); 58 | if (nes.getRegs().getSpriteVisible()) 59 | renderSPR(line, buf); 60 | 61 | if (scri.bpp == 24 || scri.bpp == 32) { // full color 62 | int inc = scri.bpp / 8; 63 | int dest = scri.pitch * line; 64 | for (int i = 0; i < 256; i++) { 65 | int c = nesPalette24[buf[i + 8] & 0x3f]; 66 | scri.buf[dest + 0] = (byte) (c & 0xff); 67 | scri.buf[dest + 1] = (byte) ((c >> 8) & 0xff); 68 | scri.buf[dest + 2] = (byte) ((c >> 16) & 0xff); 69 | dest += inc; 70 | } 71 | } else if (scri.bpp == 16) { // high color 72 | int dest = scri.pitch * line; 73 | for (int i = 0; i < 256; i++) { 74 | short c = nesPalette16[buf[i + 8] & 0x3f]; 75 | scri.buf[dest + 0] = (byte) (c & 0xff); 76 | scri.buf[dest + 1] = (byte) ((c >> 8) & 0xff); 77 | dest += 2; 78 | } 79 | } else { 80 | // not supported 81 | } 82 | } 83 | 84 | public byte[] getSpriteRam() { 85 | return sprram; 86 | } 87 | 88 | public byte[][] getNameTable() { 89 | return nameTable; 90 | } 91 | 92 | public byte[][] getNamePage() { 93 | return namePage; 94 | } 95 | 96 | public byte[] getPalette() { 97 | return palette; 98 | } 99 | 100 | public enum MirrorType { 101 | HOLIZONTAL, VERTICAL, FOUR_SCREEN, SINGLE_SCREEN, 102 | } 103 | 104 | public void serialize() { 105 | // TODO 106 | } 107 | 108 | // ----- 109 | 110 | private void initPalette() { 111 | int[][] palDat = new int[][] { { 0x75, 0x75, 0x75 }, 112 | { 0x27, 0x1B, 0x8F }, { 0x00, 0x00, 0xAB }, 113 | { 0x47, 0x00, 0x9F }, { 0x8F, 0x00, 0x77 }, 114 | { 0xAB, 0x00, 0x13 }, { 0xA7, 0x00, 0x00 }, 115 | { 0x7F, 0x0B, 0x00 }, { 0x43, 0x2F, 0x00 }, 116 | { 0x00, 0x47, 0x00 }, { 0x00, 0x51, 0x00 }, 117 | { 0x00, 0x3F, 0x17 }, { 0x1B, 0x3F, 0x5F }, 118 | { 0x00, 0x00, 0x00 }, { 0x05, 0x05, 0x05 }, 119 | { 0x05, 0x05, 0x05 }, 120 | 121 | { 0xBC, 0xBC, 0xBC }, { 0x00, 0x73, 0xEF }, 122 | { 0x23, 0x3B, 0xEF }, { 0x83, 0x00, 0xF3 }, 123 | { 0xBF, 0x00, 0xBF }, { 0xE7, 0x00, 0x5B }, 124 | { 0xDB, 0x2B, 0x00 }, { 0xCB, 0x4F, 0x0F }, 125 | { 0x8B, 0x73, 0x00 }, { 0x00, 0x97, 0x00 }, 126 | { 0x00, 0xAB, 0x00 }, { 0x00, 0x93, 0x3B }, 127 | { 0x00, 0x83, 0x8B }, { 0x11, 0x11, 0x11 }, 128 | { 0x09, 0x09, 0x09 }, { 0x09, 0x09, 0x09 }, 129 | 130 | { 0xFF, 0xFF, 0xFF }, { 0x3F, 0xBF, 0xFF }, 131 | { 0x5F, 0x97, 0xFF }, { 0xA7, 0x8B, 0xFD }, 132 | { 0xF7, 0x7B, 0xFF }, { 0xFF, 0x77, 0xB7 }, 133 | { 0xFF, 0x77, 0x63 }, { 0xFF, 0x9B, 0x3B }, 134 | { 0xF3, 0xBF, 0x3F }, { 0x83, 0xD3, 0x13 }, 135 | { 0x4F, 0xDF, 0x4B }, { 0x58, 0xF8, 0x98 }, 136 | { 0x00, 0xEB, 0xDB }, { 0x66, 0x66, 0x66 }, 137 | { 0x0D, 0x0D, 0x0D }, { 0x0D, 0x0D, 0x0D }, 138 | 139 | { 0xFF, 0xFF, 0xFF }, { 0xAB, 0xE7, 0xFF }, 140 | { 0xC7, 0xD7, 0xFF }, { 0xD7, 0xCB, 0xFF }, 141 | { 0xFF, 0xC7, 0xFF }, { 0xFF, 0xC7, 0xDB }, 142 | { 0xFF, 0xBF, 0xB3 }, { 0xFF, 0xDB, 0xAB }, 143 | { 0xFF, 0xE7, 0xA3 }, { 0xE3, 0xFF, 0xA3 }, 144 | { 0xAB, 0xF3, 0xBF }, { 0xB3, 0xFF, 0xCF }, 145 | { 0x9F, 0xFF, 0xF3 }, { 0xDD, 0xDD, 0xDD }, 146 | { 0x11, 0x11, 0x11 }, { 0x11, 0x11, 0x11 } }; 147 | 148 | for (int i = 0; i < 0x40; i++) 149 | nesPalette24[i] = palDat[i][0] | (palDat[i][1] << 8) 150 | | (palDat[i][2] << 16); 151 | } 152 | 153 | private byte readNameTable(short adr) { 154 | return namePage[((adr) >> 10) & 3][(adr) & 0x3ff]; 155 | } 156 | 157 | private byte readPatTable(short adr) { 158 | return nes.getMbc().readChrRom(adr); 159 | } 160 | 161 | private void renderBG(int line, byte[] buf) { 162 | Regs r = nes.getRegs(); 163 | int x_ofs = r.getPpuAdrX() & 7; 164 | int y_ofs = (r.getPpuAdrV() >> 12) & 7; 165 | short name_adr = (short) (r.getPpuAdrV() & 0xfff); 166 | short pat_adr = (short) (r.getBgPatAdr() ? 0x1000 : 0x0000); 167 | 168 | int ix = -x_ofs; 169 | for (int i = 0; i < 33; i++, ix += 8) { 170 | byte tile = readNameTable((short) (0x2000 + name_adr)); 171 | 172 | byte l = readPatTable((short) (pat_adr + (tile & 0xff) * 16 + y_ofs)); 173 | byte u = readPatTable((short) (pat_adr + (tile & 0xff) * 16 + y_ofs + 8)); 174 | 175 | int tx = name_adr & 0x1f, ty = (name_adr >> 5) & 0x1f; 176 | short attr_adr = (short) ((name_adr & 0xC00) + 0x3C0 177 | + ((ty & ~3) << 1) + (tx >> 2)); 178 | int aofs = ((ty & 2) == 0 ? 0 : 4) + ((tx & 2) == 0 ? 0 : 2); 179 | int attr = ((readNameTable((short) (0x2000 + attr_adr)) >> aofs) & 3) << 2; 180 | 181 | for (int j = 7; j >= 0; j--) { 182 | int t = ((l & 1) | (u << 1)) & 3; 183 | if (t != 0) 184 | buf[8 + ix + j] = (byte) (0x40 | palette[t | attr]); 185 | l >>= 1; 186 | u >>= 1; 187 | } 188 | 189 | if ((name_adr & 0x1f) == 0x1f) 190 | name_adr = (short) ((name_adr & ~0x1f) ^ 0x400); 191 | else 192 | name_adr++; 193 | } 194 | } 195 | 196 | private void renderSPR(int line, byte[] buf) { 197 | int spr_height = nes.getRegs().getSpriteSize() ? 16 : 8; 198 | int pat_adr = nes.getRegs().getSpritePatAdr() ? 0x1000 : 0x0000; 199 | 200 | for (int i = 0; i < 64; i++) { 201 | int spr_y = (sprram[i * 4 + 0] & 0xff) + 1; 202 | int attr = sprram[i * 4 + 2] & 0xff; 203 | 204 | if (!(line >= spr_y && line < spr_y + spr_height)) 205 | continue; 206 | 207 | boolean is_bg = ((attr >> 5) & 1) != 0; 208 | int y_ofs = line - spr_y; 209 | int tile_index = sprram[i * 4 + 1] & 0xff; 210 | int spr_x = sprram[i * 4 + 3] & 0xff; 211 | int upper = (attr & 3) << 2; 212 | 213 | boolean h_flip = (attr & 0x40) == 0; 214 | int sx = h_flip ? 7 : 0, ex = h_flip ? -1 : 8, ix = h_flip ? -1 : 1; 215 | 216 | if ((attr & 0x80) != 0) 217 | y_ofs = spr_height - 1 - y_ofs; 218 | 219 | short tile_adr; 220 | if (spr_height == 16) 221 | tile_adr = (short) ((tile_index & ~1) * 16 222 | + ((tile_index & 1) * 0x1000) + (y_ofs >= 8 ? 16 : 0) + (y_ofs & 7)); 223 | else 224 | tile_adr = (short) (pat_adr + tile_index * 16 + y_ofs); 225 | 226 | byte l = readPatTable(tile_adr); 227 | byte u = readPatTable((short) (tile_adr + 8)); 228 | 229 | for (int x = sx; x != ex; x += ix) { 230 | int lower = (l & 1) | ((u & 1) << 1); 231 | if (lower != 0 && (buf[8 + spr_x + x] & 0x80) == 0) { 232 | if (!is_bg || (buf[8 + spr_x + x] & 0x40) == 0) 233 | buf[8 + spr_x + x] = palette[0x10 | upper | lower]; 234 | buf[8 + spr_x + x] |= 0x80; 235 | } 236 | l >>= 1; 237 | u >>= 1; 238 | } 239 | } 240 | } 241 | 242 | public void spriteCheck(int line) { 243 | if (nes.getRegs().getSpriteVisible()) { 244 | int spr_y = (sprram[0] & 0xff) + 1; 245 | int spr_height = nes.getRegs().getSpriteSize() ? 16 : 8; 246 | int pat_adr = nes.getRegs().getSpritePatAdr() ? 0x1000 : 0x0000; 247 | 248 | if (line >= spr_y && line < spr_y + spr_height) { 249 | int y_ofs = line - spr_y; 250 | int tile_index = sprram[1] & 0xff; 251 | if ((sprram[2] & 0x80) != 0) 252 | y_ofs = spr_height - 1 - y_ofs; 253 | short tile_adr; 254 | if (spr_height == 16) 255 | tile_adr = (short) ((tile_index & ~1) * 16 256 | + ((tile_index & 1) * 0x1000) 257 | + (y_ofs >= 8 ? 16 : 0) + (y_ofs & 7)); 258 | else 259 | tile_adr = (short) (pat_adr + tile_index * 16 + y_ofs); 260 | byte l = readPatTable(tile_adr); 261 | byte u = readPatTable((short) (tile_adr + 8)); 262 | if (l != 0 || u != 0) 263 | nes.getRegs().setSprite0Occur(true); 264 | } 265 | } 266 | } 267 | 268 | private byte[] sprram = new byte[0x100]; 269 | private byte[][] nameTable = new byte[4][0x400]; 270 | private byte[][] namePage = new byte[4][]; 271 | private byte[] palette = new byte[0x20]; 272 | 273 | private int[] nesPalette24 = new int[0x40]; 274 | private short[] nesPalette16 = new short[0x40]; 275 | 276 | private byte[] buf = new byte[256 + 16]; 277 | 278 | Nes nes; 279 | } 280 | -------------------------------------------------------------------------------- /src/main/java/jp/tanakh/bjne/nes/Regs.java: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Delta2Force/MCEmu/ba4f598971b8597e88ec8734b8ef836da3cfe9a4/src/main/java/jp/tanakh/bjne/nes/Regs.java -------------------------------------------------------------------------------- /src/main/java/jp/tanakh/bjne/nes/Renderer.java: -------------------------------------------------------------------------------- 1 | package jp.tanakh.bjne.nes; 2 | 3 | public interface Renderer { 4 | public class ScreenInfo { 5 | public byte[] buf; 6 | public int width; 7 | public int height; 8 | public int pitch; 9 | public int bpp; 10 | } 11 | 12 | public class SoundInfo { 13 | public byte[] buf; 14 | public int freq; 15 | public int bps; 16 | public int ch; 17 | public int sample; 18 | } 19 | 20 | public class InputInfo { 21 | public int[] buf; 22 | } 23 | 24 | public ScreenInfo requestScreen(int width, int height); 25 | 26 | public SoundInfo requestSound(); 27 | 28 | public InputInfo requestInput(int padCount, int buttonCount); 29 | 30 | public void outputScreen(ScreenInfo info); 31 | 32 | public void outputSound(SoundInfo info); 33 | 34 | public void outputMessage(String msg); 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/jp/tanakh/bjne/nes/Rom.java: -------------------------------------------------------------------------------- 1 | package jp.tanakh.bjne.nes; 2 | 3 | import java.io.FileInputStream; 4 | import java.io.IOException; 5 | 6 | public class Rom { 7 | public Rom(Nes n) { 8 | } 9 | 10 | public void reset() { 11 | 12 | } 13 | 14 | public void release() { 15 | romDat = null; 16 | chrDat = null; 17 | sram = null; 18 | vram = null; 19 | } 20 | 21 | public void load(String fname) throws IOException { 22 | release(); 23 | 24 | FileInputStream is = new FileInputStream(fname); 25 | byte[] dat = new byte[is.available()]; 26 | is.read(dat, 0, dat.length); 27 | is.close(); 28 | 29 | if (!(dat[0] == 'N' && dat[1] == 'E' && dat[2] == 'S' && dat[3] == '\u001A')) 30 | throw new IOException("rom signature is invalid"); 31 | 32 | prgPageCnt = dat[4] & 0xff; 33 | chrPageCnt = dat[5] & 0xff; 34 | 35 | mirroring = (dat[6] & 1) != 0 ? MirrorType.VERTICAL 36 | : MirrorType.HORIZONTAL; 37 | sramEnable = (dat[6] & 2) != 0; 38 | trainerEnable = (dat[6] & 4) != 0; 39 | fourScreen = (dat[6] & 8) != 0; 40 | 41 | mapper = ((dat[6] & 0xff) >> 4) | (dat[7] & 0xf0); 42 | 43 | int romSize = 0x4000 * prgPageCnt; 44 | int chrSize = 0x2000 * chrPageCnt; 45 | 46 | romDat = new byte[romSize]; 47 | if (chrSize != 0) 48 | chrDat = new byte[chrSize]; 49 | sram = new byte[0x2000]; 50 | vram = new byte[0x2000]; 51 | 52 | if (romSize > 0) 53 | System.arraycopy(dat, 16, romDat, 0, romSize); 54 | if (chrSize > 0) 55 | System.arraycopy(dat, 16 + romSize, chrDat, 0, chrSize); 56 | 57 | System.out.printf("Cartridge information:\n"); 58 | System.out.printf("%d KB rom, %d KB vrom\n", romSize / 1024, 59 | chrSize / 1024); 60 | System.out.printf("mapper #%d\n", mapper); 61 | System.out.printf("%s mirroring\n", 62 | mirroring == MirrorType.VERTICAL ? "vertical" : "holizontal"); 63 | System.out.printf("sram : %s\n", sramEnable ? "Y" : "N"); 64 | System.out.printf("trainer : %s\n", trainerEnable ? "Y" : "N"); 65 | System.out.printf("four screen : %s\n", fourScreen ? "Y" : "N"); 66 | } 67 | 68 | public void saveSram(String fname) { 69 | // TODO 70 | } 71 | 72 | public void loadSram(String fname) { 73 | // TODO 74 | } 75 | 76 | public byte[] getRom() { 77 | return romDat; 78 | } 79 | 80 | public byte[] getChr() { 81 | return chrDat; 82 | } 83 | 84 | public byte[] getSram() { 85 | return sram; 86 | } 87 | 88 | public byte[] getVram() { 89 | return vram; 90 | } 91 | 92 | public int romSize() { 93 | return prgPageCnt; 94 | } 95 | 96 | public int chrSize() { 97 | return chrPageCnt; 98 | } 99 | 100 | public int mapperNo() { 101 | return mapper; 102 | } 103 | 104 | public boolean hasSram() { 105 | return sramEnable; 106 | } 107 | 108 | public boolean hasTrainer() { 109 | return trainerEnable; 110 | } 111 | 112 | public boolean isFourScreen() { 113 | return fourScreen; 114 | } 115 | 116 | public enum MirrorType { 117 | HORIZONTAL, VERTICAL, 118 | } 119 | 120 | public MirrorType mirror() { 121 | return mirroring; 122 | } 123 | 124 | private int prgPageCnt, chrPageCnt; 125 | private MirrorType mirroring; 126 | private boolean sramEnable, trainerEnable; 127 | private boolean fourScreen; 128 | private int mapper; 129 | private byte[] romDat, chrDat, sram, vram; 130 | } 131 | -------------------------------------------------------------------------------- /src/main/java/jp/tanakh/bjne/nes/mapper/CNROM.java: -------------------------------------------------------------------------------- 1 | package jp.tanakh.bjne.nes.mapper; 2 | 3 | import jp.tanakh.bjne.nes.MapperAdapter; 4 | import jp.tanakh.bjne.nes.Nes; 5 | 6 | public class CNROM extends MapperAdapter { 7 | public CNROM(Nes n) { 8 | nes = n; 9 | reset(); 10 | } 11 | 12 | @Override 13 | public int mapperNo() { 14 | return 3; 15 | } 16 | 17 | @Override 18 | public void reset() { 19 | for (int i = 0; i < 4; i++) 20 | nes.getMbc().mapRom(i, i); 21 | for (int i = 0; i < 8; i++) 22 | nes.getMbc().mapVrom(i, i); 23 | } 24 | 25 | @Override 26 | public void write(short adr, byte dat) { 27 | for (int i = 0; i < 8; i++) 28 | nes.getMbc().mapVrom(i, (dat & 0xff) * 8 + i); 29 | } 30 | 31 | private Nes nes; 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/jp/tanakh/bjne/nes/mapper/MMC1.java: -------------------------------------------------------------------------------- 1 | package jp.tanakh.bjne.nes.mapper; 2 | 3 | import jp.tanakh.bjne.nes.MapperAdapter; 4 | import jp.tanakh.bjne.nes.Nes; 5 | import jp.tanakh.bjne.nes.Ppu; 6 | 7 | public class MMC1 extends MapperAdapter { 8 | public MMC1(Nes n) { 9 | nes = n; 10 | reset(); 11 | } 12 | 13 | @Override 14 | public int mapperNo() { 15 | return 1; 16 | } 17 | 18 | @Override 19 | public void reset() { 20 | romSize = nes.getRom().romSize(); 21 | sizeInKB = romSize * 16; 22 | 23 | cnt = buf = 0; 24 | mirror = false; 25 | oneScreen = false; 26 | switchArea = true; 27 | switchSize = true; 28 | vromSwitchSize = false; 29 | swapBase = 0; 30 | vromPage[0] = vromPage[1] = 1; 31 | romPage[0] = 0; 32 | romPage[1] = romSize - 1; 33 | 34 | setBank(); 35 | } 36 | 37 | private static boolean _bit(int x, int n) { 38 | return ((x >> n) & 1) != 0; 39 | } 40 | 41 | @Override 42 | public void write(short sadr, byte sdat){ 43 | int adr = sadr & 0xffff; 44 | int dat = sdat & 0xff; 45 | 46 | if ((dat&0x80)!=0){ 47 | cnt=0;buf=0; 48 | return; 49 | } 50 | buf|=((dat&1)<=0x8000&&adr<=0x9FFF){ // reg0 57 | mirror =_bit(dat,0); 58 | oneScreen =_bit(dat,1); 59 | switchArea =_bit(dat,2); 60 | switchSize =_bit(dat,3); 61 | vromSwitchSize=_bit(dat,4); 62 | 63 | setMirroring(); 64 | } 65 | else if (adr>=0xA000&&adr<=0xBFFF){ // reg1 66 | if (sizeInKB==512){ 67 | swapBase=_bit(dat,4)?16:0; 68 | setBank(); 69 | } 70 | else if (sizeInKB==1024){ 71 | } 72 | 73 | vromPage[0]=dat&0xf; 74 | if (vromSwitchSize){ 75 | nes.getMbc().mapVrom(0,vromPage[0]*4); 76 | nes.getMbc().mapVrom(1,vromPage[0]*4+1); 77 | nes.getMbc().mapVrom(2,vromPage[0]*4+2); 78 | nes.getMbc().mapVrom(3,vromPage[0]*4+3); 79 | } 80 | else{ 81 | nes.getMbc().mapVrom(0,vromPage[0]*4); 82 | nes.getMbc().mapVrom(1,vromPage[0]*4+1); 83 | nes.getMbc().mapVrom(2,vromPage[0]*4+2); 84 | nes.getMbc().mapVrom(3,vromPage[0]*4+3); 85 | nes.getMbc().mapVrom(4,vromPage[0]*4+4); 86 | nes.getMbc().mapVrom(5,vromPage[0]*4+5); 87 | nes.getMbc().mapVrom(6,vromPage[0]*4+6); 88 | nes.getMbc().mapVrom(7,vromPage[0]*4+7); 89 | } 90 | } 91 | else if (adr>=0xC000&&adr<=0xDFFF){ // reg2 92 | vromPage[1]=dat&0xf; 93 | if (vromSwitchSize){ 94 | nes.getMbc().mapVrom(4,vromPage[1]*4); 95 | nes.getMbc().mapVrom(5,vromPage[1]*4+1); 96 | nes.getMbc().mapVrom(6,vromPage[1]*4+2); 97 | nes.getMbc().mapVrom(7,vromPage[1]*4+3); 98 | } 99 | 100 | if (sizeInKB==1024){ 101 | } 102 | } 103 | else if (adr>=0xE000){ // reg3 104 | if (switchSize){ // 16K 105 | if (switchArea){ 106 | romPage[0]=dat&0xf; 107 | romPage[1]=(romSize-1)&0xf; 108 | } 109 | else{ 110 | romPage[0]=0; 111 | romPage[1]=dat&0xf; 112 | } 113 | } 114 | else{ // 32K 115 | romPage[0]=dat&0xe; 116 | romPage[1]=(dat&0xe)|1; 117 | } 118 | setBank(); 119 | } 120 | } 121 | 122 | private void setBank() { 123 | nes.getMbc().mapRom(0, (swapBase | romPage[0]) * 2); 124 | nes.getMbc().mapRom(1, (swapBase | romPage[0]) * 2 + 1); 125 | nes.getMbc().mapRom(2, (swapBase | romPage[1]) * 2); 126 | nes.getMbc().mapRom(3, (swapBase | romPage[1]) * 2 + 1); 127 | } 128 | 129 | private void setMirroring() { 130 | if (oneScreen) 131 | nes.getPpu().setMirroring( 132 | mirror ? Ppu.MirrorType.HOLIZONTAL 133 | : Ppu.MirrorType.VERTICAL); 134 | else if (mirror) 135 | nes.getPpu().setMirroring(1, 1, 1, 1); 136 | else 137 | nes.getPpu().setMirroring(0, 0, 0, 0); 138 | } 139 | 140 | int romSize, sizeInKB; 141 | int cnt, buf; 142 | boolean mirror, oneScreen, switchArea, switchSize, vromSwitchSize; 143 | int swapBase; 144 | int[] vromPage = new int[2]; 145 | int[] romPage = new int[2]; 146 | 147 | private Nes nes; 148 | } 149 | -------------------------------------------------------------------------------- /src/main/java/jp/tanakh/bjne/nes/mapper/MMC3.java: -------------------------------------------------------------------------------- 1 | package jp.tanakh.bjne.nes.mapper; 2 | 3 | import jp.tanakh.bjne.nes.MapperAdapter; 4 | import jp.tanakh.bjne.nes.Nes; 5 | import jp.tanakh.bjne.nes.Ppu; 6 | 7 | /** 8 | * Mapper 4: MMC3 9 | */ 10 | public class MMC3 extends MapperAdapter { 11 | public MMC3(Nes n) { 12 | nes = n; 13 | } 14 | 15 | @Override 16 | public int mapperNo() { 17 | return 4; 18 | } 19 | 20 | @Override 21 | public void reset() { 22 | romSize = nes.getRom().romSize(); 23 | prgPage[0] = 0; 24 | prgPage[1] = 1; 25 | chrPage[0] = 0; 26 | chrPage[1] = 1; 27 | chrPage[2] = 2; 28 | chrPage[3] = 3; 29 | chrPage[4] = 4; 30 | chrPage[5] = 5; 31 | chrPage[6] = 6; 32 | chrPage[7] = 7; 33 | 34 | prgSwap = false; 35 | chrSwap = false; 36 | 37 | setRom(); 38 | setVrom(); 39 | } 40 | 41 | private static boolean _bit(int x, int n) { 42 | return ((x >> n) & 1) != 0; 43 | } 44 | 45 | @Override 46 | public void write(short adr, byte bdat) { 47 | int dat = bdat & 0xff; 48 | switch (adr & 0xE001) { 49 | case 0x8000: 50 | cmd = dat & 7; 51 | prgSwap = _bit(dat, 6); 52 | chrSwap = _bit(dat, 7); 53 | break; 54 | 55 | case 0x8001: 56 | switch (cmd) { 57 | case 0: // Select 2 1K VROM pages at PPU $0000 58 | chrPage[0] = dat & 0xfe; 59 | chrPage[1] = (dat & 0xfe) + 1; 60 | setVrom(); 61 | break; 62 | case 1: // Select 2 1K VROM pages at PPU $0800 63 | chrPage[2] = dat & 0xfe; 64 | chrPage[3] = (dat & 0xfe) + 1; 65 | setVrom(); 66 | break; 67 | case 2: // Select 1K VROM pages at PPU $1000 68 | chrPage[4] = dat; 69 | setVrom(); 70 | break; 71 | case 3: // Select 1K VROM pages at PPU $1400 72 | chrPage[5] = dat; 73 | setVrom(); 74 | break; 75 | case 4: // Select 1K VROM pages at PPU $1800 76 | chrPage[6] = dat; 77 | setVrom(); 78 | break; 79 | case 5: // Select 1K VROM pages at PPU $1C00 80 | chrPage[7] = dat; 81 | setVrom(); 82 | break; 83 | case 6: // Select first switchable ROM page 84 | prgPage[0] = dat; 85 | setRom(); 86 | break; 87 | case 7: // Select second switchable ROM page 88 | prgPage[1] = dat; 89 | setRom(); 90 | break; 91 | } 92 | break; 93 | 94 | case 0xA000: 95 | if (!nes.getRom().isFourScreen()) 96 | nes.getPpu().setMirroring( 97 | (dat & 1) != 0 ? Ppu.MirrorType.HOLIZONTAL 98 | : Ppu.MirrorType.VERTICAL); 99 | break; 100 | case 0xA001: 101 | if ((dat & 0x80) != 0) 102 | ; // enable SRAM 103 | else 104 | ; // disable SRAM 105 | break; 106 | 107 | case 0xC000: 108 | irqCounter = dat; 109 | break; 110 | case 0xC001: 111 | irqLatch = dat; 112 | break; 113 | 114 | case 0xE000: 115 | irqEnable = false; 116 | irqCounter = irqLatch; 117 | break; 118 | case 0xE001: 119 | irqEnable = true; 120 | break; 121 | } 122 | } 123 | 124 | @Override 125 | public void hblank(int line) { 126 | if (irqEnable && line >= 0 && line < 239 && nes.getRegs().drawEnabled()) { 127 | 128 | if ((irqCounter--) == 0) { 129 | irqCounter = irqLatch; 130 | nes.getCpu().setIrq(true); 131 | } 132 | } 133 | } 134 | 135 | private void setRom() { 136 | if (prgSwap) { 137 | nes.getMbc().mapRom(0, (romSize - 1) * 2); 138 | nes.getMbc().mapRom(1, prgPage[1]); 139 | nes.getMbc().mapRom(2, prgPage[0]); 140 | nes.getMbc().mapRom(3, (romSize - 1) * 2 + 1); 141 | } else { 142 | nes.getMbc().mapRom(0, prgPage[0]); 143 | nes.getMbc().mapRom(1, prgPage[1]); 144 | nes.getMbc().mapRom(2, (romSize - 1) * 2); 145 | nes.getMbc().mapRom(3, (romSize - 1) * 2 + 1); 146 | } 147 | } 148 | 149 | private void setVrom() { 150 | for (int i = 0; i < 8; i++) 151 | nes.getMbc().mapVrom((i + (chrSwap ? 4 : 0)) % 8, chrPage[i]); 152 | } 153 | 154 | private int romSize; 155 | 156 | private int cmd; 157 | private boolean prgSwap, chrSwap; 158 | 159 | private int irqCounter, irqLatch; 160 | private boolean irqEnable; 161 | 162 | private int[] prgPage = new int[2]; 163 | private int[] chrPage = new int[8]; 164 | 165 | private Nes nes; 166 | } 167 | -------------------------------------------------------------------------------- /src/main/java/jp/tanakh/bjne/nes/mapper/NullMapper.java: -------------------------------------------------------------------------------- 1 | package jp.tanakh.bjne.nes.mapper; 2 | 3 | import jp.tanakh.bjne.nes.MapperAdapter; 4 | import jp.tanakh.bjne.nes.Nes; 5 | 6 | public class NullMapper extends MapperAdapter { 7 | public NullMapper(Nes n){ 8 | } 9 | 10 | @Override 11 | public int mapperNo() { 12 | return 0; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/jp/tanakh/bjne/nes/mapper/UNROM.java: -------------------------------------------------------------------------------- 1 | package jp.tanakh.bjne.nes.mapper; 2 | 3 | import jp.tanakh.bjne.nes.MapperAdapter; 4 | import jp.tanakh.bjne.nes.Nes; 5 | 6 | public class UNROM extends MapperAdapter { 7 | public UNROM(Nes n) { 8 | nes = n; 9 | reset(); 10 | } 11 | 12 | @Override 13 | public int mapperNo() { 14 | return 2; 15 | } 16 | 17 | @Override 18 | public void reset() { 19 | int romSize = nes.getRom().romSize(); 20 | nes.getMbc().mapRom(0, 0); 21 | nes.getMbc().mapRom(1, 1); 22 | nes.getMbc().mapRom(2, (romSize - 1) * 2); 23 | nes.getMbc().mapRom(3, (romSize - 1) * 2 + 1); 24 | } 25 | 26 | @Override 27 | public void write(short adr, byte dat) { 28 | nes.getMbc().mapRom(0, dat * 2); 29 | nes.getMbc().mapRom(1, dat * 2 + 1); 30 | } 31 | 32 | private Nes nes; 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/jp/tanakh/bjne/nes/mapper/VRC6.java: -------------------------------------------------------------------------------- 1 | package jp.tanakh.bjne.nes.mapper; 2 | 3 | import java.util.LinkedList; 4 | import java.util.Queue; 5 | 6 | import jp.tanakh.bjne.nes.MapperAdapter; 7 | import jp.tanakh.bjne.nes.Nes; 8 | import jp.tanakh.bjne.nes.Ppu; 9 | import jp.tanakh.bjne.nes.Renderer.SoundInfo; 10 | 11 | public class VRC6 extends MapperAdapter { 12 | public VRC6(Nes n) { 13 | nes = n; 14 | } 15 | 16 | @Override 17 | public int mapperNo() { 18 | return 24; 19 | } 20 | 21 | @Override 22 | public void reset() { 23 | int romSize = nes.getRom().romSize(); 24 | 25 | nes.getMbc().mapRom(0, 0); 26 | nes.getMbc().mapRom(1, 1); 27 | nes.getMbc().mapRom(2, (romSize - 1) * 2); 28 | nes.getMbc().mapRom(3, (romSize - 1) * 2 + 1); 29 | 30 | irqCount = 0; 31 | irqEnable = 0; 32 | irqLatch = 0; 33 | befClk = 0; 34 | 35 | sq[0] = new SqState(); 36 | sq[1] = new SqState(); 37 | saw = new SawState(); 38 | 39 | writeQueue = new LinkedList(); 40 | } 41 | 42 | @Override 43 | public void write(short sadr, byte sdat) { 44 | int adr = sadr & 0xffff; 45 | int dat = sdat & 0xff; 46 | 47 | switch (adr & 0xF003) { 48 | case 0x8000: // Select 16K ROM bank at $8000 49 | nes.getMbc().mapRom(0, dat * 2); 50 | nes.getMbc().mapRom(1, dat * 2 + 1); 51 | break; 52 | case 0xC000: // Select 8K ROM bank at $C000 53 | nes.getMbc().mapRom(2, dat); 54 | break; 55 | 56 | case 0xD000: // Select 1K VROM bank at PPU $0000 57 | nes.getMbc().mapVrom(0, dat); 58 | break; 59 | case 0xD001: // Select 1K VROM bank at PPU $0400 60 | nes.getMbc().mapVrom(1, dat); 61 | break; 62 | case 0xD002: // Select 1K VROM bank at PPU $0800 63 | nes.getMbc().mapVrom(2, dat); 64 | break; 65 | case 0xD003: // Select 1K VROM bank at PPU $0C00 66 | nes.getMbc().mapVrom(3, dat); 67 | break; 68 | case 0xE000: // Select 1K VROM bank at PPU $1000 69 | nes.getMbc().mapVrom(4, dat); 70 | break; 71 | case 0xE001: // Select 1K VROM bank at PPU $1400 72 | nes.getMbc().mapVrom(5, dat); 73 | break; 74 | case 0xE002: // Select 1K VROM bank at PPU $1800 75 | nes.getMbc().mapVrom(6, dat); 76 | break; 77 | case 0xE003: // Select 1K VROM bank at PPU $1C00 78 | nes.getMbc().mapVrom(7, dat); 79 | break; 80 | 81 | case 0xB003: 82 | switch ((dat >> 2) & 3) { 83 | case 0: // Horizontal mirroring 84 | nes.getPpu().setMirroring(Ppu.MirrorType.VERTICAL); 85 | break; 86 | case 1: // Vertical mirroring 87 | nes.getPpu().setMirroring(Ppu.MirrorType.HOLIZONTAL); 88 | break; 89 | case 2: // Mirror page from $2000 90 | nes.getPpu().setMirroring(0, 0, 0, 0); 91 | break; 92 | case 3: // Mirror page from $2400 93 | nes.getPpu().setMirroring(1, 1, 1, 1); 94 | break; 95 | } 96 | break; 97 | 98 | case 0xF000: 99 | irqLatch = dat; 100 | break; 101 | case 0xF001: 102 | irqEnable = dat & 3; 103 | if ((irqEnable & 2) != 0) 104 | irqCount = irqLatch; 105 | break; 106 | case 0xF002: 107 | if ((irqEnable & 1) != 0) 108 | irqEnable |= 2; 109 | else 110 | irqEnable &= 1; 111 | break; 112 | 113 | case 0x9000: 114 | case 0x9001: 115 | case 0x9002: 116 | case 0xA000: 117 | case 0xA001: 118 | case 0xA002: 119 | case 0xB000: 120 | case 0xB001: 121 | case 0xB002: 122 | writeQueue.add(new WriteDat(nes.getCpu().getMasterClock(), sadr, 123 | sdat)); 124 | while (writeQueue.size() > 1000) { 125 | WriteDat wd = writeQueue.remove(); 126 | sndWrite(wd.adr, wd.dat); 127 | } 128 | break; 129 | } 130 | } 131 | 132 | private void sndWrite(short sadr, byte sdat) { 133 | int adr = sadr & 0xffff; 134 | int dat = sdat & 0xff; 135 | switch (adr & 0xF003) { 136 | case 0x9000: 137 | case 0xA000: 138 | sq[(adr >> 12) - 9].duty = ((dat >> 4) & 7); 139 | sq[(adr >> 12) - 9].volume = dat & 0xF; 140 | sq[(adr >> 12) - 9].gate = (dat >> 7) != 0; 141 | break; 142 | case 0x9001: 143 | case 0xA001: 144 | sq[(adr >> 12) - 9].freq = (sq[(adr >> 12) - 9].freq & ~0xFF) | dat; 145 | break; 146 | case 0x9002: 147 | case 0xA002: 148 | sq[(adr >> 12) - 9].freq = (sq[(adr >> 12) - 9].freq & 0xFF) 149 | | ((dat & 0xF) << 8); 150 | sq[(adr >> 12) - 9].enable = (dat >> 7) != 0; 151 | break; 152 | 153 | case 0xB000: 154 | saw.phase = dat & 0x3F; 155 | break; 156 | case 0xB001: 157 | saw.freq = (saw.freq & ~0xFF) | dat; 158 | break; 159 | case 0xB002: 160 | saw.freq = (saw.freq & 0xFF) | ((dat & 0xF) << 8); 161 | saw.enable = (dat >> 7) != 0; 162 | break; 163 | } 164 | } 165 | 166 | @Override 167 | public void hblank(int line) { 168 | if ((irqEnable & 2) != 0) { 169 | if (irqCount >= 0xFF) { 170 | nes.getCpu().setIrq(true); 171 | irqCount = irqLatch; 172 | } else 173 | irqCount++; 174 | } 175 | } 176 | 177 | @Override 178 | public void audio(SoundInfo info) { 179 | double cpuClk = nes.getCpu().getFrequency(); 180 | double sampleClk = cpuClk / info.freq; 181 | long curClk = nes.getCpu().getMasterClock(); 182 | int span = info.bps / 8 * info.ch; 183 | 184 | for (int i = 0; i < info.sample; i++) { 185 | // long cur = (curClk - befClk) * i / info.sample + befClk; 186 | long cur = (long) (befClk + sampleClk * i); 187 | while (!writeQueue.isEmpty() && cur >= writeQueue.peek().clk) { 188 | WriteDat wd = writeQueue.remove(); 189 | sndWrite(wd.adr, wd.dat); 190 | } 191 | 192 | double d = (sqProduce(sq[0], sampleClk) 193 | + sqProduce(sq[1], sampleClk) + sawProduce(sampleClk)) / 32.0; 194 | 195 | if (info.bps == 16) { 196 | short v = (short) (d * 8000); 197 | info.buf[i * span + 0] = (byte) (v & 0xff); 198 | info.buf[i * span + 1] = (byte) (v >> 8); 199 | if (info.ch == 2) { 200 | info.buf[i * span + 2] = (byte) (v & 0xff); 201 | info.buf[i * span + 3] = (byte) (v >> 8); 202 | } 203 | } else if (info.bps == 8) { 204 | info.buf[i * span + 0] = (byte) (d * 30); 205 | if (info.ch == 2) { 206 | info.buf[i * span + 1] = (byte) (d * 30); 207 | } 208 | } 209 | } 210 | 211 | befClk = curClk; 212 | } 213 | 214 | private int irqLatch, irqCount, irqEnable; 215 | 216 | private class SqState { 217 | int duty, volume; 218 | boolean gate; 219 | int freq; 220 | boolean enable; 221 | 222 | int step; 223 | double clk; 224 | } 225 | 226 | private SqState[] sq = new SqState[2]; 227 | 228 | class SawState { 229 | int phase; 230 | int freq; 231 | boolean enable; 232 | 233 | int step; 234 | double clk; 235 | } 236 | 237 | private SawState saw; 238 | 239 | private int sqProduce(SqState sq, double clk) { 240 | if (!sq.enable) 241 | return 0; 242 | if (sq.gate) 243 | return sq.volume; 244 | sq.clk += clk; 245 | int adv = (int) (sq.clk / (sq.freq + 1)); 246 | sq.clk -= (sq.freq + 1) * adv; 247 | sq.step = ((sq.step + adv) % 16); 248 | return ((sq.step > sq.duty) ? 1 : 0) * sq.volume; 249 | } 250 | 251 | private int sawProduce(double clk) { 252 | if (!saw.enable) 253 | return 0; 254 | saw.clk += clk; 255 | int adv = (int) (saw.clk / (saw.freq + 1)); 256 | saw.clk -= (saw.freq + 1) * adv; 257 | saw.step = ((saw.step + adv) % 7); 258 | return ((saw.step * saw.phase) >> 3) & 0x1f; 259 | } 260 | 261 | private class WriteDat { 262 | WriteDat(long c, short a, byte d) { 263 | clk = c; 264 | adr = a; 265 | dat = d; 266 | } 267 | 268 | long clk; 269 | short adr; 270 | byte dat; 271 | }; 272 | 273 | private Queue writeQueue; 274 | 275 | private long befClk; 276 | 277 | private Nes nes; 278 | } 279 | -------------------------------------------------------------------------------- /src/main/resources/assets/mcemu/blockstates/nesconsole.json: -------------------------------------------------------------------------------- 1 | { 2 | "forge_marker": 1, 3 | "defaults": { 4 | "model": "mcemu:nesconsole" 5 | }, 6 | "variants": { 7 | "normal": {"model": "mcemu:nesconsole"}, 8 | "inventory": {"model": "mcemu:nesconsole"} 9 | } 10 | } -------------------------------------------------------------------------------- /src/main/resources/assets/mcemu/blockstates/television.json: -------------------------------------------------------------------------------- 1 | { 2 | "forge_marker": 1, 3 | "defaults": { 4 | "model": "mcemu:television" 5 | }, 6 | "variants": { 7 | "normal": {"model": "mcemu:television"}, 8 | "inventory": {"model": "mcemu:television"} 9 | } 10 | } -------------------------------------------------------------------------------- /src/main/resources/assets/mcemu/lang/en_us.lang: -------------------------------------------------------------------------------- 1 | tile.nesconsole.name=Nintendo Entertainment System 2 | itemGroup.mcemu.nes=MCEmu: NES 3 | tile.television.name=Television -------------------------------------------------------------------------------- /src/main/resources/assets/mcemu/models/block/nesconsole.json: -------------------------------------------------------------------------------- 1 | { 2 | "credit": "Made with Blockbench", 3 | "textures": { 4 | "0": "mcemu:blocks/nesfront", 5 | "1": "mcemu:blocks/nesside", 6 | "2": "mcemu:blocks/nesback", 7 | "4": "mcemu:blocks/nesbottom", 8 | "5": "mcemu:blocks/nestop", 9 | "particle": "mcemu:blocks/nesfront" 10 | }, 11 | "elements": [ 12 | { 13 | "from": [1, 0, 3], 14 | "to": [15, 5, 13], 15 | "faces": { 16 | "north": {"uv": [0, 0, 14, 5], "texture": "#0"}, 17 | "east": {"uv": [0, 0, 14, 5], "texture": "#1"}, 18 | "south": {"uv": [0, 0, 14, 5], "texture": "#2"}, 19 | "west": {"uv": [0, 0, 14, 5], "texture": "#1"}, 20 | "up": {"uv": [0, 0, 14, 10], "texture": "#5"}, 21 | "down": {"uv": [0, 0, 14, 5], "texture": "#4"} 22 | } 23 | } 24 | ], 25 | "display": { 26 | "gui": { 27 | "rotation": [ 30, 225, 0 ], 28 | "translation": [ 0, 0, 0], 29 | "scale":[ 0.625, 0.625, 0.625 ] 30 | }, 31 | "ground": { 32 | "rotation": [ 0, 0, 0 ], 33 | "translation": [ 0, 3, 0], 34 | "scale":[ 0.25, 0.25, 0.25 ] 35 | }, 36 | "fixed": { 37 | "rotation": [ 0, 0, 0 ], 38 | "translation": [ 0, 0, 0], 39 | "scale":[ 0.5, 0.5, 0.5 ] 40 | }, 41 | "thirdperson_righthand": { 42 | "rotation": [ 75, 45, 0 ], 43 | "translation": [ 0, 2.5, 0], 44 | "scale": [ 0.375, 0.375, 0.375 ] 45 | }, 46 | "firstperson_righthand": { 47 | "rotation": [ 0, 45, 0 ], 48 | "translation": [ 0, 0, 0 ], 49 | "scale": [ 0.40, 0.40, 0.40 ] 50 | }, 51 | "firstperson_lefthand": { 52 | "rotation": [ 0, 225, 0 ], 53 | "translation": [ 0, 0, 0 ], 54 | "scale": [ 0.40, 0.40, 0.40 ] 55 | } 56 | } 57 | } -------------------------------------------------------------------------------- /src/main/resources/assets/mcemu/models/block/television.json: -------------------------------------------------------------------------------- 1 | { 2 | "credit": "Made with Blockbench", 3 | "textures": { 4 | "0": "blocks/planks_big_oak", 5 | "1": "blocks/iron_block", 6 | "2": "blocks/hardened_clay_stained_black", 7 | "particle": "blocks/planks_big_oak" 8 | }, 9 | "elements": [ 10 | { 11 | "from": [0, 0, 1], 12 | "to": [16, 16, 16], 13 | "faces": { 14 | "north": {"uv": [0, 0, 16, 16], "texture": "#2"}, 15 | "east": {"uv": [0, 0, 15, 16], "texture": "#0"}, 16 | "south": {"uv": [0, 0, 16, 16], "texture": "#0"}, 17 | "west": {"uv": [0, 0, 15, 16], "texture": "#0"}, 18 | "up": {"uv": [0, 0, 16, 15], "texture": "#0"}, 19 | "down": {"uv": [0, 0, 16, 15], "texture": "#0"} 20 | } 21 | }, 22 | { 23 | "from": [0, 0, 0], 24 | "to": [16, 1, 1], 25 | "faces": { 26 | "north": {"uv": [0, 0, 16, 1], "texture": "#0"}, 27 | "east": {"uv": [0, 0, 1, 1], "texture": "#0"}, 28 | "south": {"uv": [0, 0, 16, 1], "texture": "#missing"}, 29 | "west": {"uv": [0, 0, 1, 1], "texture": "#0"}, 30 | "up": {"uv": [0, 0, 16, 1], "texture": "#0"}, 31 | "down": {"uv": [0, 0, 16, 1], "texture": "#0"} 32 | } 33 | }, 34 | { 35 | "from": [0, 15, 0], 36 | "to": [16, 16, 1], 37 | "faces": { 38 | "north": {"uv": [0, 0, 16, 1], "texture": "#0"}, 39 | "east": {"uv": [0, 0, 1, 1], "texture": "#0"}, 40 | "south": {"uv": [0, 0, 16, 1], "texture": "#missing"}, 41 | "west": {"uv": [0, 0, 1, 1], "texture": "#0"}, 42 | "up": {"uv": [0, 0, 16, 1], "texture": "#0"}, 43 | "down": {"uv": [0, 0, 16, 1], "texture": "#0"} 44 | } 45 | }, 46 | { 47 | "from": [15, 1, 0], 48 | "to": [16, 15, 1], 49 | "faces": { 50 | "north": {"uv": [0, 0, 14, 1], "rotation": 90, "texture": "#0"}, 51 | "east": {"uv": [0, 0, 14, 1], "rotation": 270, "texture": "#0"}, 52 | "south": {"uv": [0, 0, 14, 1], "rotation": 270, "texture": "#missing"}, 53 | "west": {"uv": [0, 0, 14, 1], "rotation": 270, "texture": "#0"}, 54 | "up": {"uv": [0, 0, 1, 1], "rotation": 270, "texture": "#missing"}, 55 | "down": {"uv": [0, 0, 1, 1], "rotation": 270, "texture": "#missing"} 56 | } 57 | }, 58 | { 59 | "from": [0, 1, 0], 60 | "to": [1, 15, 1], 61 | "faces": { 62 | "north": {"uv": [0, 0, 14, 1], "rotation": 90, "texture": "#0"}, 63 | "east": {"uv": [0, 0, 14, 1], "rotation": 270, "texture": "#0"}, 64 | "south": {"uv": [0, 0, 14, 1], "rotation": 270, "texture": "#missing"}, 65 | "west": {"uv": [0, 0, 14, 1], "rotation": 270, "texture": "#0"}, 66 | "up": {"uv": [0, 0, 1, 1], "rotation": 270, "texture": "#missing"}, 67 | "down": {"uv": [0, 0, 1, 1], "rotation": 270, "texture": "#missing"} 68 | } 69 | }, 70 | { 71 | "from": [5, 15, 7], 72 | "to": [6, 21, 8], 73 | "rotation": {"angle": -22.5, "axis": "z", "origin": [8, 8, 8]}, 74 | "faces": { 75 | "north": {"uv": [0, 0, 1, 6], "texture": "#1"}, 76 | "east": {"uv": [0, 0, 1, 6], "texture": "#1"}, 77 | "south": {"uv": [0, 0, 1, 6], "texture": "#1"}, 78 | "west": {"uv": [0, 0, 1, 6], "texture": "#1"}, 79 | "up": {"uv": [0, 0, 1, 1], "texture": "#1"}, 80 | "down": {"uv": [0, 0, 1, 1], "texture": "#missing"} 81 | } 82 | }, 83 | { 84 | "from": [10, 13, 7], 85 | "to": [11, 19, 8], 86 | "rotation": {"angle": 22.5, "axis": "z", "origin": [8, 8, 8]}, 87 | "faces": { 88 | "north": {"uv": [0, 0, 1, 6], "texture": "#1"}, 89 | "east": {"uv": [0, 0, 1, 6], "texture": "#1"}, 90 | "south": {"uv": [0, 0, 1, 6], "texture": "#1"}, 91 | "west": {"uv": [0, 0, 1, 6], "texture": "#1"}, 92 | "up": {"uv": [0, 0, 1, 1], "texture": "#1"}, 93 | "down": {"uv": [0, 0, 1, 1], "texture": "#missing"} 94 | } 95 | } 96 | ], 97 | "display": { 98 | "gui": { 99 | "rotation": [ 30, 225, 0 ], 100 | "translation": [ 0, 0, 0], 101 | "scale":[ 0.625, 0.625, 0.625 ] 102 | }, 103 | "ground": { 104 | "rotation": [ 0, 0, 0 ], 105 | "translation": [ 0, 3, 0], 106 | "scale":[ 0.25, 0.25, 0.25 ] 107 | }, 108 | "fixed": { 109 | "rotation": [ 0, 0, 0 ], 110 | "translation": [ 0, 0, 0], 111 | "scale":[ 0.5, 0.5, 0.5 ] 112 | }, 113 | "thirdperson_righthand": { 114 | "rotation": [ 75, 45, 0 ], 115 | "translation": [ 0, 2.5, 0], 116 | "scale": [ 0.375, 0.375, 0.375 ] 117 | }, 118 | "firstperson_righthand": { 119 | "rotation": [ 0, 45, 0 ], 120 | "translation": [ 0, 0, 0 ], 121 | "scale": [ 0.40, 0.40, 0.40 ] 122 | }, 123 | "firstperson_lefthand": { 124 | "rotation": [ 0, 225, 0 ], 125 | "translation": [ 0, 0, 0 ], 126 | "scale": [ 0.40, 0.40, 0.40 ] 127 | } 128 | } 129 | } -------------------------------------------------------------------------------- /src/main/resources/assets/mcemu/models/item/nescartridge.json: -------------------------------------------------------------------------------- 1 | { 2 | "credit": "Made with Blockbench", 3 | "textures": { 4 | "0": "mcemu:items/cart1", 5 | "particle": "mcemu:items/cart1" 6 | }, 7 | "elements": [ 8 | { 9 | "from": [5, 0, 7], 10 | "to": [11, 2, 9], 11 | "faces": { 12 | "north": {"uv": [1, 1, 7, 3], "texture": "#0"}, 13 | "east": {"uv": [0, 0, 2, 2], "texture": "#0"}, 14 | "south": {"uv": [4, 0, 10, 2], "texture": "#0"}, 15 | "west": {"uv": [0, 0, 2, 2], "texture": "#0"}, 16 | "up": {"uv": [0, 0, 6, 2], "texture": "#missing"}, 17 | "down": {"uv": [4, 0, 10, 2], "texture": "#0"} 18 | } 19 | }, 20 | { 21 | "from": [4, 2, 7], 22 | "to": [12, 9, 9], 23 | "faces": { 24 | "north": {"uv": [0, 0, 8, 7], "texture": "#0"}, 25 | "east": {"uv": [0, 0, 2, 7], "texture": "#0"}, 26 | "south": {"uv": [4, 0, 12, 7], "texture": "#0"}, 27 | "west": {"uv": [0, 0, 2, 7], "texture": "#0"}, 28 | "up": {"uv": [0, 0, 8, 2], "texture": "#0"}, 29 | "down": {"uv": [0, 0, 8, 2], "texture": "#0"} 30 | } 31 | }, 32 | { 33 | "from": [8, 9, 8], 34 | "to": [10, 11, 9], 35 | "faces": { 36 | "north": {"uv": [0, 0, 2, 2], "texture": "#0"}, 37 | "east": {"uv": [0, 0, 1, 2], "texture": "#missing"}, 38 | "south": {"uv": [0, 0, 2, 2], "texture": "#0"}, 39 | "west": {"uv": [0, 0, 1, 2], "texture": "#missing"}, 40 | "up": {"uv": [0, 0, 2, 1], "texture": "#0"}, 41 | "down": {"uv": [0, 0, 2, 1], "texture": "#missing"} 42 | } 43 | }, 44 | { 45 | "from": [4, 9, 7], 46 | "to": [8, 11, 9], 47 | "faces": { 48 | "north": {"uv": [4, 0, 8, 2], "texture": "#0"}, 49 | "east": {"uv": [0, 0, 2, 2], "texture": "#0"}, 50 | "south": {"uv": [4, 0, 8, 2], "texture": "#0"}, 51 | "west": {"uv": [0, 0, 2, 2], "texture": "#0"}, 52 | "up": {"uv": [4, 0, 8, 2], "texture": "#0"}, 53 | "down": {"uv": [0, 0, 4, 2], "texture": "#missing"} 54 | } 55 | }, 56 | { 57 | "from": [10, 9, 7], 58 | "to": [12, 11, 9], 59 | "faces": { 60 | "north": {"uv": [0, 0, 2, 2], "texture": "#0"}, 61 | "east": {"uv": [0, 0, 2, 2], "texture": "#0"}, 62 | "south": {"uv": [0, 0, 2, 2], "texture": "#0"}, 63 | "west": {"uv": [0, 0, 2, 2], "texture": "#0"}, 64 | "up": {"uv": [0, 0, 2, 2], "texture": "#0"}, 65 | "down": {"uv": [0, 0, 2, 2], "texture": "#missing"} 66 | } 67 | } 68 | ], 69 | "display": { 70 | "thirdperson_righthand": { 71 | "rotation": [75, 45, 0], 72 | "translation": [0, 2.5, 0], 73 | "scale": [0.8, 0.8, 0.8] 74 | }, 75 | "firstperson_righthand": { 76 | "rotation": [0, 123, 0], 77 | "translation": [0, 6.25, 0], 78 | "scale": [0.8, 0.8, 0.8] 79 | }, 80 | "firstperson_lefthand": { 81 | "rotation": [0, 123, 0], 82 | "translation": [0, 6.25, 0], 83 | "scale": [0.8, 0.8, 0.8] 84 | }, 85 | "ground": { 86 | "translation": [0, 3, 0], 87 | "scale": [0.8, 0.8, 0.8] 88 | }, 89 | "gui": { 90 | "rotation": [30, 225, 0], 91 | "scale": [0.625, 0.625, 0.625] 92 | }, 93 | "fixed": { 94 | "translation": [0, 3.75, -0.5], 95 | "scale": [1.5, 1.5, 1.5] 96 | } 97 | }, 98 | "groups": [ 99 | { 100 | "name": "nescartridge", 101 | "origin": [8, 8, 8], 102 | "children": [0, 1, 2, 3, 4] 103 | } 104 | ] 105 | } -------------------------------------------------------------------------------- /src/main/resources/assets/mcemu/models/item/nesconsole.json: -------------------------------------------------------------------------------- 1 | { 2 | "parent":"mcemu:block/nesconsole" 3 | } -------------------------------------------------------------------------------- /src/main/resources/assets/mcemu/models/item/television.json: -------------------------------------------------------------------------------- 1 | { 2 | "parent":"mcemu:block/television" 3 | } -------------------------------------------------------------------------------- /src/main/resources/assets/mcemu/textures/blocks/nesback.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Delta2Force/MCEmu/ba4f598971b8597e88ec8734b8ef836da3cfe9a4/src/main/resources/assets/mcemu/textures/blocks/nesback.png -------------------------------------------------------------------------------- /src/main/resources/assets/mcemu/textures/blocks/nesbottom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Delta2Force/MCEmu/ba4f598971b8597e88ec8734b8ef836da3cfe9a4/src/main/resources/assets/mcemu/textures/blocks/nesbottom.png -------------------------------------------------------------------------------- /src/main/resources/assets/mcemu/textures/blocks/nesfront.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Delta2Force/MCEmu/ba4f598971b8597e88ec8734b8ef836da3cfe9a4/src/main/resources/assets/mcemu/textures/blocks/nesfront.png -------------------------------------------------------------------------------- /src/main/resources/assets/mcemu/textures/blocks/nesside.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Delta2Force/MCEmu/ba4f598971b8597e88ec8734b8ef836da3cfe9a4/src/main/resources/assets/mcemu/textures/blocks/nesside.png -------------------------------------------------------------------------------- /src/main/resources/assets/mcemu/textures/blocks/nestop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Delta2Force/MCEmu/ba4f598971b8597e88ec8734b8ef836da3cfe9a4/src/main/resources/assets/mcemu/textures/blocks/nestop.png -------------------------------------------------------------------------------- /src/main/resources/assets/mcemu/textures/items/cart1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Delta2Force/MCEmu/ba4f598971b8597e88ec8734b8ef836da3cfe9a4/src/main/resources/assets/mcemu/textures/items/cart1.png -------------------------------------------------------------------------------- /src/main/resources/mcmod.info: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "modid": "mcemu", 4 | "name": "MCEmu", 5 | "description": "Run emulators in Minecraft!", 6 | "version": "${version}", 7 | "mcversion": "${mcversion}", 8 | "url": "", 9 | "updateUrl": "", 10 | "authorList": ["DeltaTwoForce"], 11 | "credits": "https://github.com/tanakh/bjne-java [NES EMULATOR]", 12 | "logoFile": "", 13 | "screenshots": [], 14 | "dependencies": [] 15 | } 16 | ] 17 | -------------------------------------------------------------------------------- /src/main/resources/pack.mcmeta: -------------------------------------------------------------------------------- 1 | { 2 | "pack": { 3 | "description": "examplemod resources", 4 | "pack_format": 3, 5 | "_comment": "A pack_format of 3 should be used starting with Minecraft 1.11. All resources, including language files, should be lowercase (eg: en_us.lang). A pack_format of 2 will load your mod resources with LegacyV2Adapter, which requires language files to have uppercase letters (eg: en_US.lang)." 6 | } 7 | } 8 | --------------------------------------------------------------------------------