diff --git a/build.xml b/build.xml new file mode 100644 index 0000000..3d2cfc8 --- /dev/null +++ b/build.xml @@ -0,0 +1,73 @@ + + + + + + + + + + + Builds, tests, and runs the project halfnes. + + + diff --git a/lib/OpenAL32.dll b/lib/OpenAL32.dll new file mode 100644 index 0000000..b6f6f65 Binary files /dev/null and b/lib/OpenAL32.dll differ diff --git a/lib/OpenAL64.dll b/lib/OpenAL64.dll new file mode 100644 index 0000000..d835da5 Binary files /dev/null and b/lib/OpenAL64.dll differ diff --git a/lib/jinput-dx8.dll b/lib/jinput-dx8.dll new file mode 100644 index 0000000..e930dcb Binary files /dev/null and b/lib/jinput-dx8.dll differ diff --git a/lib/jinput-dx8_64.dll b/lib/jinput-dx8_64.dll new file mode 100644 index 0000000..86413ed Binary files /dev/null and b/lib/jinput-dx8_64.dll differ diff --git a/lib/jinput-raw.dll b/lib/jinput-raw.dll new file mode 100644 index 0000000..b312e96 Binary files /dev/null and b/lib/jinput-raw.dll differ diff --git a/lib/jinput-raw_64.dll b/lib/jinput-raw_64.dll new file mode 100644 index 0000000..ef512e6 Binary files /dev/null and b/lib/jinput-raw_64.dll differ diff --git a/lib/jinput-wintab.dll b/lib/jinput-wintab.dll new file mode 100644 index 0000000..0a6b8bb Binary files /dev/null and b/lib/jinput-wintab.dll differ diff --git a/lib/libjinput-linux.so b/lib/libjinput-linux.so new file mode 100644 index 0000000..dba9e59 Binary files /dev/null and b/lib/libjinput-linux.so differ diff --git a/lib/libjinput-linux64.so b/lib/libjinput-linux64.so new file mode 100644 index 0000000..04840b6 Binary files /dev/null and b/lib/libjinput-linux64.so differ diff --git a/lib/libjinput-osx.jnilib b/lib/libjinput-osx.jnilib new file mode 100644 index 0000000..1cf6dbb Binary files /dev/null and b/lib/libjinput-osx.jnilib differ diff --git a/lib/liblwjgl.jnilib b/lib/liblwjgl.jnilib new file mode 100644 index 0000000..3cca853 Binary files /dev/null and b/lib/liblwjgl.jnilib differ diff --git a/lib/liblwjgl.so b/lib/liblwjgl.so new file mode 100644 index 0000000..9f7a0ed Binary files /dev/null and b/lib/liblwjgl.so differ diff --git a/lib/liblwjgl64.so b/lib/liblwjgl64.so new file mode 100644 index 0000000..97a2e2e Binary files /dev/null and b/lib/liblwjgl64.so differ diff --git a/lib/libopenal.so b/lib/libopenal.so new file mode 100644 index 0000000..f2bf0ac Binary files /dev/null and b/lib/libopenal.so differ diff --git a/lib/libopenal64.so b/lib/libopenal64.so new file mode 100644 index 0000000..361d137 Binary files /dev/null and b/lib/libopenal64.so differ diff --git a/lib/libs/LICENSES/commons-io-LICENSE.txt b/lib/libs/LICENSES/commons-io-LICENSE.txt new file mode 100644 index 0000000..6b0b127 --- /dev/null +++ b/lib/libs/LICENSES/commons-io-LICENSE.txt @@ -0,0 +1,203 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + diff --git a/lib/libs/LICENSES/concurrent-LICENSE.txt b/lib/libs/LICENSES/concurrent-LICENSE.txt new file mode 100644 index 0000000..239e5f7 --- /dev/null +++ b/lib/libs/LICENSES/concurrent-LICENSE.txt @@ -0,0 +1,11 @@ +The concurrent library is developed by Doug Lea on http://gee.cs.oswego.edu/dl/classes/EDU/oswego/cs/dl/util/concurrent/intro.html + +Here are some Release Notes: +All classes are released to the public domain and may be used for any purpose whatsoever without permission +or acknowledgment. Portions of the CopyOnWriteArrayList and ConcurrentReaderHashMap classes are adapted from +Sun JDK source code. These are copyright of Sun Microsystems, Inc, and are used with their kind permission, +as described in this license. + +You don't need a license to use the concurrent library, here is an answer for such question from Doug Lea: +Do I need a license to use it? Can I get one? + No! \ No newline at end of file diff --git a/lib/libs/LICENSES/guava-io-LICENSE.txt b/lib/libs/LICENSES/guava-io-LICENSE.txt new file mode 100644 index 0000000..6b0b127 --- /dev/null +++ b/lib/libs/LICENSES/guava-io-LICENSE.txt @@ -0,0 +1,203 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + diff --git a/lib/libs/LICENSES/junit-LICENSE b/lib/libs/LICENSES/junit-LICENSE new file mode 100644 index 0000000..b0c1af0 --- /dev/null +++ b/lib/libs/LICENSES/junit-LICENSE @@ -0,0 +1,215 @@ +Common Public License - v 1.0 + +THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS COMMON PUBLIC +LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM +CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. + +1. DEFINITIONS + +"Contribution" means: + +a) in the case of the initial Contributor, the initial code and +documentation distributed under this Agreement, and +b) in the case of each subsequent Contributor: + +i) changes to the Program, and + +ii) additions to the Program; + +where such changes and/or additions to the Program originate from and are +distributed by that particular Contributor. A Contribution 'originates' from a +Contributor if it was added to the Program by such Contributor itself or anyone +acting on such Contributor's behalf. Contributions do not include additions to +the Program which: (i) are separate modules of software distributed in +conjunction with the Program under their own license agreement, and (ii) are +not derivative works of the Program. + +"Contributor" means any person or entity that distributes the Program. + +"Licensed Patents " mean patent claims licensable by a Contributor which are +necessarily infringed by the use or sale of its Contribution alone or when +combined with the Program. + +"Program" means the Contributions distributed in accordance with this Agreement. + +"Recipient" means anyone who receives the Program under this Agreement, +including all Contributors. + +2. GRANT OF RIGHTS + +a) Subject to the terms of this Agreement, each Contributor hereby grants +Recipient a non-exclusive, worldwide, royalty-free copyright license to +reproduce, prepare derivative works of, publicly display, publicly perform, +distribute and sublicense the Contribution of such Contributor, if any, and +such derivative works, in source code and object code form. + +b) Subject to the terms of this Agreement, each Contributor hereby grants +Recipient a non-exclusive, worldwide, royalty-free patent license under +Licensed Patents to make, use, sell, offer to sell, import and otherwise +transfer the Contribution of such Contributor, if any, in source code and +object code form. This patent license shall apply to the combination of the +Contribution and the Program if, at the time the Contribution is added by the +Contributor, such addition of the Contribution causes such combination to be +covered by the Licensed Patents. The patent license shall not apply to any +other combinations which include the Contribution. No hardware per se is +licensed hereunder. + +c) Recipient understands that although each Contributor grants the +licenses to its Contributions set forth herein, no assurances are provided by +any Contributor that the Program does not infringe the patent or other +intellectual property rights of any other entity. Each Contributor disclaims +any liability to Recipient for claims brought by any other entity based on +infringement of intellectual property rights or otherwise. As a condition to +exercising the rights and licenses granted hereunder, each Recipient hereby +assumes sole responsibility to secure any other intellectual property rights +needed, if any. For example, if a third party patent license is required to +allow Recipient to distribute the Program, it is Recipient's responsibility to +acquire that license before distributing the Program. + +d) Each Contributor represents that to its knowledge it has sufficient +copyright rights in its Contribution, if any, to grant the copyright license +set forth in this Agreement. + +3. REQUIREMENTS + +A Contributor may choose to distribute the Program in object code form under +its own license agreement, provided that: + +a) it complies with the terms and conditions of this Agreement; and + +b) its license agreement: + +i) effectively disclaims on behalf of all Contributors all warranties and +conditions, express and implied, including warranties or conditions of title +and non-infringement, and implied warranties or conditions of merchantability +and fitness for a particular purpose; + +ii) effectively excludes on behalf of all Contributors all liability for +damages, including direct, indirect, special, incidental and consequential +damages, such as lost profits; + +iii) states that any provisions which differ from this Agreement are +offered by that Contributor alone and not by any other party; and + +iv) states that source code for the Program is available from such +Contributor, and informs licensees how to obtain it in a reasonable manner on +or through a medium customarily used for software exchange. + +When the Program is made available in source code form: + +a) it must be made available under this Agreement; and + +b) a copy of this Agreement must be included with each copy of the +Program. + +Contributors may not remove or alter any copyright notices contained within the +Program. + +Each Contributor must identify itself as the originator of its Contribution, if +any, in a manner that reasonably allows subsequent Recipients to identify the +originator of the Contribution. + +4. COMMERCIAL DISTRIBUTION + +Commercial distributors of software may accept certain responsibilities with +respect to end users, business partners and the like. While this license is +intended to facilitate the commercial use of the Program, the Contributor who +includes the Program in a commercial product offering should do so in a manner +which does not create potential liability for other Contributors. Therefore, if +a Contributor includes the Program in a commercial product offering, such +Contributor ("Commercial Contributor") hereby agrees to defend and indemnify +every other Contributor ("Indemnified Contributor") against any losses, damages +and costs (collectively "Losses") arising from claims, lawsuits and other legal +actions brought by a third party against the Indemnified Contributor to the +extent caused by the acts or omissions of such Commercial Contributor in +connection with its distribution of the Program in a commercial product +offering. The obligations in this section do not apply to any claims or Losses +relating to any actual or alleged intellectual property infringement. In order +to qualify, an Indemnified Contributor must: a) promptly notify the Commercial +Contributor in writing of such claim, and b) allow the Commercial Contributor +to control, and cooperate with the Commercial Contributor in, the defense and +any related settlement negotiations. The Indemnified Contributor may +participate in any such claim at its own expense. + +For example, a Contributor might include the Program in a commercial product +offering, Product X. That Contributor is then a Commercial Contributor. If that +Commercial Contributor then makes performance claims, or offers warranties +related to Product X, those performance claims and warranties are such +Commercial Contributor's responsibility alone. Under this section, the +Commercial Contributor would have to defend claims against the other +Contributors related to those performance claims and warranties, and if a court +requires any other Contributor to pay any damages as a result, the Commercial +Contributor must pay those damages. + +5. NO WARRANTY + +EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON AN +"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR +IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, +NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each +Recipient is solely responsible for determining the appropriateness of using +and distributing the Program and assumes all risks associated with its exercise +of rights under this Agreement, including but not limited to the risks and +costs of program errors, compliance with applicable laws, damage to or loss of +data, programs or equipment, and unavailability or interruption of operations. + +6. DISCLAIMER OF LIABILITY + +EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY +CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST +PROFITS), 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 OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS +GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +7. GENERAL + +If any provision of this Agreement is invalid or unenforceable under applicable +law, it shall not affect the validity or enforceability of the remainder of the +terms of this Agreement, and without further action by the parties hereto, such +provision shall be reformed to the minimum extent necessary to make such +provision valid and enforceable. + +If Recipient institutes patent litigation against a Contributor with respect to +a patent applicable to software (including a cross-claim or counterclaim in a +lawsuit), then any patent licenses granted by that Contributor to such +Recipient under this Agreement shall terminate as of the date such litigation +is filed. In addition, if Recipient institutes patent litigation against any +entity (including a cross-claim or counterclaim in a lawsuit) alleging that the +Program itself (excluding combinations of the Program with other software or +hardware) infringes such Recipient's patent(s), then such Recipient's rights +granted under Section 2(b) shall terminate as of the date such litigation is +filed. + +All Recipient's rights under this Agreement shall terminate if it fails to +comply with any of the material terms or conditions of this Agreement and does +not cure such failure in a reasonable period of time after becoming aware of +such noncompliance. If all Recipient's rights under this Agreement terminate, +Recipient agrees to cease use and distribution of the Program as soon as +reasonably practicable. However, Recipient's obligations under this Agreement +and any licenses granted by Recipient relating to the Program shall continue +and survive. + +Everyone is permitted to copy and distribute copies of this Agreement, but in +order to avoid inconsistency the Agreement is copyrighted and may only be +modified in the following manner. The Agreement Steward reserves the right to +publish new versions (including revisions) of this Agreement from time to time. +No one other than the Agreement Steward has the right to modify this Agreement. +IBM is the initial Agreement Steward. IBM may assign the responsibility to +serve as the Agreement Steward to a suitable separate entity. Each new version +of the Agreement will be given a distinguishing version number. The Program +(including Contributions) may always be distributed subject to the version of +the Agreement under which it was received. In addition, after a new version of +the Agreement is published, Contributor may elect to distribute the Program +(including its Contributions) under the new version. Except as expressly stated +in Sections 2(a) and 2(b) above, Recipient receives no rights or licenses to +the intellectual property of any Contributor under this Agreement, whether +expressly, by implication, estoppel or otherwise. All rights in the Program not +expressly granted under this Agreement are reserved. + +This Agreement is governed by the laws of the State of New York and the +intellectual property laws of the United States of America. No party to this +Agreement will bring a legal action under this Agreement more than one year +after the cause of action arose. Each party waives its rights to a jury trial +in any resulting litigation. diff --git a/lib/libs/LICENSES/poi-LICENSE.txt b/lib/libs/LICENSES/poi-LICENSE.txt new file mode 100644 index 0000000..6b0b127 --- /dev/null +++ b/lib/libs/LICENSES/poi-LICENSE.txt @@ -0,0 +1,203 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + diff --git a/lib/libs/LICENSES/xstream-LICENSE.txt b/lib/libs/LICENSES/xstream-LICENSE.txt new file mode 100644 index 0000000..b8ef4c6 --- /dev/null +++ b/lib/libs/LICENSES/xstream-LICENSE.txt @@ -0,0 +1,26 @@ +Copyright (c) 2003-2006, Joe Walnes +Copyright (c) 2006-2009, XStream Committers +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this list of +conditions and the following disclaimer. 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. + +Neither the name of XStream nor the names of its contributors may be used to endorse +or promote products derived from this software without specific prior written +permission. + +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 OWNER 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. \ No newline at end of file diff --git a/lib/lwjgl.dll b/lib/lwjgl.dll new file mode 100644 index 0000000..ff9a1f8 Binary files /dev/null and b/lib/lwjgl.dll differ diff --git a/lib/lwjgl64.dll b/lib/lwjgl64.dll new file mode 100644 index 0000000..776de5c Binary files /dev/null and b/lib/lwjgl64.dll differ diff --git a/lib/nblibraries.properties b/lib/nblibraries.properties new file mode 100644 index 0000000..0e92743 --- /dev/null +++ b/lib/nblibraries.properties @@ -0,0 +1,18 @@ +libs.JInput.classpath=\ + ${base}/jinput.jar +libs.JInput.displayName=JInput +libs.LWJGL.classpath=\ + ${base}/lwjgl.jar +libs.LWJGL.displayName=LWJGL +libs.LWJGL.javadoc=\ + ${base}/javadoc/ +libs.LWJGL.src=\ + ${base}/src/;\ + ${base}/src/generated/;\ + ${base}/src/java/ +libs.The_Happy_Library_test.classpath=\ + ${base}/org.happy.library-1.3.jar:\ + ${base}/libs/:\ + ${base}/org.happy.library-1.3.jar:\ + ${base}/org.happy.library-1.3.jar +libs.The_Happy_Library_test.displayName=The Happy Library test diff --git a/lib/openal.dylib b/lib/openal.dylib new file mode 100644 index 0000000..cb8e458 Binary files /dev/null and b/lib/openal.dylib differ diff --git a/manifest.mf b/manifest.mf new file mode 100644 index 0000000..328e8e5 --- /dev/null +++ b/manifest.mf @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +X-COMMENT: Main-Class will be added automatically by build + diff --git a/nbproject/build-impl.xml b/nbproject/build-impl.xml new file mode 100644 index 0000000..16da5e3 --- /dev/null +++ b/nbproject/build-impl.xml @@ -0,0 +1,1397 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set src.dir + Must set build.dir + Must set dist.dir + Must set build.classes.dir + Must set dist.javadoc.dir + Must set build.test.classes.dir + Must set build.test.results.dir + Must set build.classes.excludes + Must set dist.jar + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set javac.includes + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + No tests executed. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set JVM to use for profiling in profiler.info.jvm + Must set profiler agent JVM arguments in profiler.info.jvmargs.agent + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select some files in the IDE or set javac.includes + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + To run this application from the command line without Ant, try: + + java -jar "${dist.jar.resolved}" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set run.class + + + + Must select one file in the IDE or set run.class + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set debug.class + + + + + Must select one file in the IDE or set debug.class + + + + + Must set fix.includes + + + + + + + + + + This target only works when run from inside the NetBeans IDE. + + + + + + + + + Must select one file in the IDE or set profile.class + This target only works when run from inside the NetBeans IDE. + + + + + + + + + This target only works when run from inside the NetBeans IDE. + + + + + + + + + + + + + This target only works when run from inside the NetBeans IDE. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set run.class + + + + + + Must select some files in the IDE or set test.includes + + + + + Must select one file in the IDE or set run.class + + + + + Must select one file in the IDE or set applet.url + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select some files in the IDE or set javac.includes + + + + + + + + + + + + + + + + + + Some tests failed; see details above. + + + + + + + + + Must select some files in the IDE or set test.includes + + + + Some tests failed; see details above. + + + + Must select some files in the IDE or set test.class + Must select some method in the IDE or set test.method + + + + Some tests failed; see details above. + + + + + Must select one file in the IDE or set test.class + + + + Must select one file in the IDE or set test.class + Must select some method in the IDE or set test.method + + + + + + + + + + + + + + Must select one file in the IDE or set applet.url + + + + + + + + + Must select one file in the IDE or set applet.url + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/nbproject/configs/Raspberry_Pi.properties b/nbproject/configs/Raspberry_Pi.properties new file mode 100644 index 0000000..e335255 --- /dev/null +++ b/nbproject/configs/Raspberry_Pi.properties @@ -0,0 +1,8 @@ +$label=Raspberry Pi +$target.debug=debug-remote +$target.profile=profile-remote +$target.run=run-remote +compile.on.save.unsupported.remote.platform=true +debug-transport=dt_socket +platform.runtime=Gluon_JavaFX_2 +run.jvmargs=-Djava.library.path=/usr/lib/jni -Xms256m diff --git a/nbproject/genfiles.properties b/nbproject/genfiles.properties new file mode 100644 index 0000000..78b675a --- /dev/null +++ b/nbproject/genfiles.properties @@ -0,0 +1,8 @@ +build.xml.data.CRC32=53808b6e +build.xml.script.CRC32=7256fdc0 +build.xml.stylesheet.CRC32=8064a381@1.75.2.48 +# This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml. +# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you. +nbproject/build-impl.xml.data.CRC32=53808b6e +nbproject/build-impl.xml.script.CRC32=9c512202 +nbproject/build-impl.xml.stylesheet.CRC32=876e7a8f@1.75.2.48 diff --git a/nbproject/project.properties b/nbproject/project.properties new file mode 100644 index 0000000..467cd2e --- /dev/null +++ b/nbproject/project.properties @@ -0,0 +1,89 @@ +annotation.processing.enabled=true +annotation.processing.enabled.in.editor=false +annotation.processing.processors.list= +annotation.processing.run.all.processors=true +annotation.processing.source.output=${build.generated.sources.dir}/ap-source-output +application.title=halfnes +application.vendor=sjc +build.classes.dir=${build.dir}/classes +build.classes.excludes=**/*.java,**/*.form +# This directory is removed when the project is cleaned: +build.dir=build +build.generated.dir=${build.dir}/generated +build.generated.sources.dir=${build.dir}/generated-sources +# Only compile against the classpath explicitly listed here: +build.sysclasspath=ignore +build.test.classes.dir=${build.dir}/test/classes +build.test.results.dir=${build.dir}/test/results +# Uncomment to specify the preferred debugger connection transport: +#debug.transport=dt_socket +debug.classpath=\ + ${run.classpath} +debug.test.classpath=\ + ${run.test.classpath} +# Files in build.classes.dir which should be excluded from distribution jar +dist.archive.excludes= +# This directory is removed when the project is cleaned: +dist.dir=dist +dist.jar=${dist.dir}/halfnes.jar +dist.javadoc.dir=${dist.dir}/javadoc +endorsed.classpath= +excludes= +file.reference.halfnes-src=src +file.reference.jinput-20100502.jar=lib/jinput-20100502.jar +file.reference.jinput.jar=lib/jinput.jar +file.reference.jutils-20100502.jar=lib/jutils-20100502.jar +file.reference.jutils.jar=lib/jutils.jar +file.reference.org.happy.library-1.3.jar=lib/org.happy.library-1.3.jar +file.reference.pi4j-core.jar=lib/pi4j-core.jar +includes=** +jar.compress=false +javac.classpath=\ + ${file.reference.jinput.jar}:\ + ${file.reference.org.happy.library-1.3.jar}:\ + ${file.reference.jinput-20100502.jar}:\ + ${file.reference.jutils-20100502.jar}:\ + ${file.reference.jutils.jar}:\ + ${file.reference.pi4j-core.jar} +# Space-separated list of extra javac options +javac.compilerargs= +javac.deprecation=false +javac.processorpath=\ + ${javac.classpath} +javac.source=1.8 +javac.target=1.8 +javac.test.classpath=\ + ${javac.classpath}:\ + ${build.classes.dir} +javac.test.processorpath=\ + ${javac.test.classpath} +javadoc.additionalparam= +javadoc.author=false +javadoc.encoding=${source.encoding} +javadoc.noindex=false +javadoc.nonavbar=false +javadoc.notree=false +javadoc.private=false +javadoc.reference.pi4j-core.jar=lib/pi4j-core-javadoc.jar +javadoc.splitindex=true +javadoc.use=true +javadoc.version=false +javadoc.windowtitle= +main.class=com.grapeshot.halfnes.JavaFXNES +manifest.file=manifest.mf +meta.inf.dir=${src.dir}/META-INF +mkdist.disabled=false +platform.active=default_platform +run.classpath=\ + ${javac.classpath}:\ + ${build.classes.dir} +# Space-separated list of JVM arguments used when running the project. +# You may also define separate properties like run-sys-prop.name=value instead of -Dname=value. +# To set system properties for unit tests define test-sys-prop.name=value: +run.jvmargs=-Djava.library.path=./lib -XX:UseSSE=3 -Xms256m +run.test.classpath=\ + ${javac.test.classpath}:\ + ${build.test.classes.dir} +source.encoding=UTF-8 +source.reference.pi4j-core.jar=lib/pi4j-core-sources.jar +src.dir=${file.reference.halfnes-src} diff --git a/nbproject/project.xml b/nbproject/project.xml new file mode 100644 index 0000000..a307441 --- /dev/null +++ b/nbproject/project.xml @@ -0,0 +1,16 @@ + + + org.netbeans.modules.java.j2seproject + + + + + + halfnes + + + + + + + diff --git a/nbproject/remote-platform-impl.xml b/nbproject/remote-platform-impl.xml new file mode 100644 index 0000000..83d1041 --- /dev/null +++ b/nbproject/remote-platform-impl.xml @@ -0,0 +1,213 @@ + + + + + + + + + + Runtime platform (platform.runtime) must be set. + + + + + + + + + Must set remote.platform.home.dir + Must set remote.platform.work.dir + Must set remote.platform.host + Must set remote.platform.port + Must set remote.platform.user + Must set remote.platform.auth + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set remote.platform.keyfile + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set remote.platform.keyfile + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/com/grapeshot/halfnes/APU.java b/src/com/grapeshot/halfnes/APU.java index cac8679..aebc88c 100644 --- a/src/com/grapeshot/halfnes/APU.java +++ b/src/com/grapeshot/halfnes/APU.java @@ -10,7 +10,7 @@ public class APU { public int samplerate = 1; private final Timer[] timers = {new SquareTimer(8, 2), new SquareTimer(8, 2), new TriangleTimer(), new NoiseTimer()}; - private double cyclespersample; + private int cyclespersample; public final NES nes; CPU cpu; CPURAM cpuram; @@ -45,7 +45,7 @@ public APU(final NES nes, final CPU cpu, final CPURAM cpuram) { public final synchronized void setParameters() { soundFiltering = PrefsSingleton.get().getBoolean("soundFiltering", true); samplerate = PrefsSingleton.get().getInt("sampleRate", 44100); - cyclespersample = 1789773.0 / samplerate; + cyclespersample = 1789773 / samplerate; if (ai != null) { ai.destroy(); } @@ -71,12 +71,12 @@ public final int read(final int addr) { //for future ref: NEED to put those ternary operators in parentheses! //otherwise order of operations does the wrong thing. final int returnval = ((lengthctr[0] > 0) ? 1 : 0) - + ((lengthctr[1] > 0) ? 2 : 0) - + ((lengthctr[2] > 0) ? 4 : 0) - + ((lengthctr[3] > 0) ? 8 : 0) - + ((dmcsamplesleft > 0) ? 16 : 0) - + (statusframeint ? 64 : 0) - + (statusdmcint ? 128 : 0); + | ((lengthctr[1] > 0) ? 2 : 0) + | ((lengthctr[2] > 0) ? 4 : 0) + | ((lengthctr[3] > 0) ? 8 : 0) + | ((dmcsamplesleft > 0) ? 16 : 0) + | (statusframeint ? 64 : 0) + | (statusdmcint ? 128 : 0); if (statusframeint) { //System.err.println("Frame interrupt ack at " + cpu.cycles); --cpu.interrupt; @@ -127,22 +127,22 @@ public final void write(final int reg, final int data) { switch (reg) { case 0x0: //length counter 1 halt - lenctrHalt[0] = utils.getbit(data, 5); + lenctrHalt[0] = (data & utils.BIT5) != 0; // pulse 1 duty cycle timers[0].setduty(dutylookup[data >> 6]); // and envelope - envConstVolume[0] = utils.getbit(data, 4); + envConstVolume[0] = (data & utils.BIT4) != 0; envelopeValue[0] = data & 15; //setvolumes(); break; case 0x1: //pulse 1 sweep setup //sweep enabled - sweepenable[0] = utils.getbit(data, 7); + sweepenable[0] = (data & utils.BIT7) != 0; //sweep divider period sweepperiod[0] = (data >> 4) & 7; //sweep negate flag - sweepnegate[0] = utils.getbit(data, 3); + sweepnegate[0] = (data & utils.BIT3) != 0; //sweep shift count sweepshift[0] = (data & 7); sweepreload[0] = true; @@ -164,22 +164,22 @@ public final void write(final int reg, final int data) { break; case 0x4: //length counter 2 halt - lenctrHalt[1] = utils.getbit(data, 5); + lenctrHalt[1] = (data & utils.BIT5) != 0; // pulse 2 duty cycle timers[1].setduty(dutylookup[data >> 6]); // and envelope - envConstVolume[1] = utils.getbit(data, 4); + envConstVolume[1] = (data & utils.BIT4) != 0; envelopeValue[1] = data & 15; //setvolumes(); break; case 0x5: //pulse 2 sweep setup //sweep enabled - sweepenable[1] = utils.getbit(data, 7); + sweepenable[1] = (data & utils.BIT7) != 0; //sweep divider period sweepperiod[1] = (data >> 4) & 7; //sweep negate flag - sweepnegate[1] = utils.getbit(data, 3); + sweepnegate[1] = (data & utils.BIT3) != 0; //sweep shift count sweepshift[1] = (data & 7); sweepreload[1] = true; @@ -202,7 +202,7 @@ public final void write(final int reg, final int data) { //triangle linear counter load linctrreload = data & 0x7f; //and length counter halt - lenctrHalt[2] = utils.getbit(data, 7); + lenctrHalt[2] = (data & utils.BIT7) != 0; break; case 0x9: break; @@ -221,15 +221,15 @@ public final void write(final int reg, final int data) { break; case 0xC: //noise halt and envelope - lenctrHalt[3] = utils.getbit(data, 5); - envConstVolume[3] = utils.getbit(data, 4); + lenctrHalt[3] = (data & utils.BIT5) != 0; + envConstVolume[3] = (data & utils.BIT4) != 0; envelopeValue[3] = data & 0xf; //setvolumes(); break; case 0xD: break; case 0xE: - timers[3].setduty(utils.getbit(data, 7) ? 6 : 1); + timers[3].setduty((data & utils.BIT7) != 0 ? 6 : 1); timers[3].setperiod(noiseperiod[data & 15]); break; case 0xF: @@ -240,8 +240,8 @@ public final void write(final int reg, final int data) { envelopeStartFlag[3] = true; break; case 0x10: - dmcirq = utils.getbit(data, 7); - dmcloop = utils.getbit(data, 6); + dmcirq = (data & utils.BIT7) != 0; + dmcloop = (data & utils.BIT6) != 0; dmcrate = dmcperiods[data & 0xf]; if (!dmcirq && statusdmcint) { --cpu.interrupt; @@ -278,7 +278,7 @@ public final void write(final int reg, final int data) { lengthctr[i] = 0; } } - if (utils.getbit(data, 4)) { + if ((data & utils.BIT4) != 0) { if (dmcsamplesleft == 0) { restartdmc(); } @@ -293,13 +293,13 @@ public final void write(final int reg, final int data) { break; case 0x16: // latch controller 1 + 2 - nes.getcontroller1().output(utils.getbit(data, 0)); - nes.getcontroller2().output(utils.getbit(data, 0)); + nes.getcontroller1().output((data & utils.BIT0) != 0); + nes.getcontroller2().output((data & utils.BIT0) != 0); break; case 0x17: - ctrmode = utils.getbit(data, 7) ? 5 : 4; + ctrmode = (data & utils.BIT7) != 0 ? 5 : 4; //System.err.println("reset " + ctrmode + ' ' + cpu.cycles); - apuintflag = utils.getbit(data, 6); + apuintflag = (data & utils.BIT6) != 0; //set is no interrupt, clear is an interrupt framectr = 0; framectrdiv = 7456 + 8; @@ -322,7 +322,7 @@ public final void write(final int reg, final int data) { } private int framectrdiv = 7456; - public final synchronized void updateto(final int cpucycle) { + public final void updateto(final int cpucycle) { //still have to run this even if sound is disabled, some games rely on DMC IRQ etc. if (soundFiltering) { //linear sampling code @@ -350,7 +350,7 @@ public final synchronized void updateto(final int cpucycle) { } accum += getOutputLevel(); - if ((apucycle % cyclespersample) < 1) { + if ((apucycle % cyclespersample) == 0) { //not quite right - there's a non-integer # cycles per sample. ai.outputSample(lowpass_filter(highpass_filter((int) (accum / remainder)))); remainder = 0; @@ -367,7 +367,7 @@ public final synchronized void updateto(final int cpucycle) { framectrdiv = 7456; clockframecounter(); } - if ((apucycle % cyclespersample) < 1) { + if ((apucycle % cyclespersample) == 0) { //not quite right - there's a non-integer # cycles per sample. timers[0].clock(remainder); timers[1].clock(remainder); @@ -393,8 +393,7 @@ private int getOutputLevel() { int vol; vol = square_lookup[volume[0] * timers[0].getval() + volume[1] * timers[1].getval()]; - vol += tnd_lookup[ - 3 * timers[2].getval() + vol += tnd_lookup[3 * timers[2].getval() + 2 * volume[3] * timers[3].getval() + dmcvalue]; if (!expnSound.isEmpty()) { @@ -422,10 +421,12 @@ private int lowpass_filter(int sample) { return lpaccum; } - public final void finishframe() { + public final void finishframe(boolean flush) { updateto(29781); apucycle = 0; - ai.flushFrame(nes.isFrameLimiterOn()); + if (flush) { + ai.flushFrame(nes.isFrameLimiterOn()); + } } private boolean apuintflag = true, statusdmcint = false, statusframeint = false; private int framectr = 0, ctrmode = 4; @@ -493,7 +494,7 @@ private void clockdmc() { } } if (!dmcsilence) { - dmcvalue += (utils.getbit(dmcshiftregister, 0) ? 2 : -2); + dmcvalue += ((dmcshiftregister & utils.BIT0) != 0 ? 2 : -2); //DMC output register doesn't wrap around if (dmcvalue > 0x7f) { dmcvalue = 0x7f; @@ -503,7 +504,6 @@ private void clockdmc() { } dmcshiftregister >>= 1; --dmcbitsleft; - } } } diff --git a/src/com/grapeshot/halfnes/CPU.java b/src/com/grapeshot/halfnes/CPU.java index 5b7a4e9..f2f375c 100644 --- a/src/com/grapeshot/halfnes/CPU.java +++ b/src/com/grapeshot/halfnes/CPU.java @@ -1,6 +1,7 @@ package com.grapeshot.halfnes; //HalfNES, Copyright Andrew Hoffman, October 2010 +import static com.grapeshot.halfnes.CPURAM.unsafe; import java.io.File; import java.io.FileWriter; import java.io.IOException; @@ -90,7 +91,7 @@ public void stealcycles(int cyclestosteal) { } public final void runcycle(final int scanline, final int pixel) { - ram.read(0x4000); //attempt to sync the APU every cycle and make dmc irqs work properly, which they still don't. Feh. +// ram.read(0x4000); //attempt to sync the APU every cycle and make dmc irqs work properly, which they still don't. Feh. ++clocks; if (ram.apu.sprdma_count > 0) { @@ -131,21 +132,53 @@ public final void runcycle(final int scanline, final int pixel) { } pb = 0; final int instr = ram.read(PC++); - if (logging) { - try { - //note: this *might* trigger side effects if logging while executing - //code from i/o registers. So don't do that. - w.write(utils.hex(PC - 1) + " " + utils.hex(instr) - + String.format(" %-14s ", opcodes[instr].replaceFirst("%1", utils.hex(ram.read(PC))).replaceFirst("%2", utils.hex(ram.read(PC + 1))).replaceFirst("%3", utils.hex(PC + (byte) (ram.read(PC)) + 1))) - + status() + " CYC:" + pixel + " SL:" + scanline + "\n"); - if (cycles == 0) { - w.flush(); - } - } catch (IOException e) { - System.err.println("Cannot write to debug log"); - } +// if (logging) { +// try { +// //note: this *might* trigger side effects if logging while executing +// //code from i/o registers. So don't do that. +// w.write(utils.hex(PC - 1) + " " + utils.hex(instr) +// + String.format(" %-14s ", opcodes[instr].replaceFirst("%1", utils.hex(ram.read(PC))).replaceFirst("%2", utils.hex(ram.read(PC + 1))).replaceFirst("%3", utils.hex(PC + (byte) (ram.read(PC)) + 1))) +// + status() + " CYC:" + pixel + " SL:" + scanline + "\n"); +// if (cycles == 0) { +// w.flush(); +// } +// } catch (IOException e) { +// System.err.println("Cannot write to debug log"); +// } +// } + switch (instr) { + case 0xa5: + lda(zpg()); + cycles += 3; + break; + case 0xD0: + branch(!zeroFlag); + cycles += 2 + pb; + break; + // JMP + case 0x4c: + PC = abs(); + cycles += 3; + break; + case 0xe8: + X++; + X &= 0xFF; + setflags(X); + cycles += 2; + break; + case 0x10: + branch(!negativeFlag); + cycles += 2 + pb; + break; + default: + processInstruction(instr); } + pb = 0; + PC &= 0xffff; + } + // less frequent instructions broken out to improve inlining + private void processInstruction(final int instr) { switch (instr) { // ADC case 0x69: @@ -180,7 +213,7 @@ public final void runcycle(final int scanline, final int pixel) { adc(indY(dummy.ONCARRY)); cycles += 5 + pb; break; - // AHX (unofficial) + // AHX (unofficial) case 0x93: ahx(indY(dummy.ALWAYS)); cycles += 6; @@ -189,12 +222,12 @@ public final void runcycle(final int scanline, final int pixel) { ahx(abs(Y, dummy.ALWAYS)); cycles += 5; break; - // ALR (unofficial) + // ALR (unofficial) case 0x4b: alr(imm()); cycles += 2; break; - // ANC (unofficial) + // ANC (unofficial) case 0x0b: anc(imm()); cycles += 2; @@ -203,7 +236,7 @@ public final void runcycle(final int scanline, final int pixel) { anc(imm()); cycles += 2; break; - // AND + // AND case 0x29: and(imm()); cycles += 2; @@ -236,12 +269,12 @@ public final void runcycle(final int scanline, final int pixel) { and(indY(dummy.ONCARRY)); cycles += 5 + pb; break; - // ARR (unofficial) + // ARR (unofficial) case 0x6b: arr(imm()); cycles += 2; break; - // ASL + // ASL case 0x0A: aslA(); cycles += 2; @@ -262,12 +295,12 @@ public final void runcycle(final int scanline, final int pixel) { asl(abs(X, dummy.ALWAYS)); cycles += 7; break; - // AXS (unofficial) + // AXS (unofficial) case 0xcb: axs(imm()); cycles += 2; break; - // BIT + // BIT case 0x24: bit(zpg()); cycles += 3; @@ -276,11 +309,7 @@ public final void runcycle(final int scanline, final int pixel) { bit(abs()); cycles += 4; break; - // Branches: every branch uses rel. addressing - case 0x10: - branch(!negativeFlag); - cycles += 2 + pb; - break; + // Branches: every branch uses rel. addressing case 0x30: branch(negativeFlag); cycles += 2 + pb; @@ -301,21 +330,17 @@ public final void runcycle(final int scanline, final int pixel) { branch(carryFlag); cycles += 2 + pb; break; - case 0xD0: - branch(!zeroFlag); - cycles += 2 + pb; - break; case 0xF0: branch(zeroFlag); cycles += 2 + pb; break; - // BRK + // BRK case 0x00: //System.err.println("Hey! A break!"); breakinterrupt(); cycles += 7; break; - // CMP + // CMP case 0xc9: cmp(A, imm()); cycles += 2; @@ -348,7 +373,7 @@ public final void runcycle(final int scanline, final int pixel) { cmp(A, indY(dummy.ONCARRY)); cycles += 5 + pb; break; - // CPX + // CPX case 0xe0: cmp(X, imm()); cycles += 2; @@ -361,7 +386,7 @@ public final void runcycle(final int scanline, final int pixel) { cmp(X, abs()); cycles += 4; break; - // CPY + // CPY case 0xc0: cmp(Y, imm()); cycles += 2; @@ -374,7 +399,7 @@ public final void runcycle(final int scanline, final int pixel) { cmp(Y, abs()); cycles += 4; break; - // DEC + // DEC case 0xc6: dec(zpg()); cycles += 5; @@ -391,7 +416,7 @@ public final void runcycle(final int scanline, final int pixel) { dec(abs(X, dummy.ALWAYS)); cycles += 7; break; - // DCP (unofficial) + // DCP (unofficial) case 0xc3: dcp(A, indX()); cycles += 8; @@ -420,7 +445,7 @@ public final void runcycle(final int scanline, final int pixel) { dcp(A, abs(X, dummy.ALWAYS)); cycles += 7; break; - // EOR + // EOR case 0x49: eor(imm()); cycles += 2; @@ -453,7 +478,7 @@ public final void runcycle(final int scanline, final int pixel) { eor(indY(dummy.ONCARRY)); cycles += 5 + pb; break; - // Flag set/clear + // Flag set/clear case 0x18: carryFlag = false; cycles += 2; @@ -487,7 +512,7 @@ public final void runcycle(final int scanline, final int pixel) { decimalModeFlag = true; cycles += 2; break;// do anything on NES - // INC + // INC case 0xe6: inc(zpg()); cycles += 5; @@ -504,7 +529,7 @@ public final void runcycle(final int scanline, final int pixel) { inc(abs(X, dummy.ALWAYS)); cycles += 7; break; - // ISC (unofficial) + // ISC (unofficial) case 0xe3: isc(indX()); cycles += 8; @@ -533,21 +558,16 @@ public final void runcycle(final int scanline, final int pixel) { isc(abs(X, dummy.ALWAYS)); cycles += 7; break; - // JMP - case 0x4c: - PC = abs(); - cycles += 3; - break; case 0x6c: PC = ind(); cycles += 5; break; - // JSR + // JSR case 0x20: jsr(abs()); cycles += 6; break; - // KIL (unofficial) + // KIL (unofficial) case 0x02: case 0x12: case 0x22: @@ -563,12 +583,12 @@ public final void runcycle(final int scanline, final int pixel) { System.err.println("KIL - CPU locked"); ram.apu.nes.runEmulation = false; break; - // LAS (unofficial) + // LAS (unofficial) case 0xbb: las(abs(Y, dummy.ONCARRY)); cycles += 4 + pb; break; - // LAX (unofficial) + // LAX (unofficial) case 0xa3: lax(indX()); cycles += 6; @@ -597,15 +617,11 @@ public final void runcycle(final int scanline, final int pixel) { lax(abs(Y, dummy.ONCARRY)); cycles += 4 + pb; break; - // LDA + // LDA case 0xa9: lda(imm()); cycles += 2; break; - case 0xa5: - lda(zpg()); - cycles += 3; - break; case 0xb5: lda(zpg(X)); cycles += 4; @@ -630,7 +646,7 @@ public final void runcycle(final int scanline, final int pixel) { lda(indY(dummy.ONCARRY)); cycles += 5 + pb; break; - // LDX + // LDX case 0xa2: ldx(imm()); cycles += 2; @@ -651,7 +667,7 @@ public final void runcycle(final int scanline, final int pixel) { ldx(abs(Y, dummy.ONCARRY)); cycles += 4 + pb; break; - // LDY + // LDY case 0xa0: ldy(imm()); cycles += 2; @@ -672,7 +688,7 @@ public final void runcycle(final int scanline, final int pixel) { ldy(abs(X, dummy.ONCARRY)); cycles += 4 + pb; break; - // LSR + // LSR case 0x4a: lsrA(); cycles += 2; @@ -693,7 +709,7 @@ public final void runcycle(final int scanline, final int pixel) { lsr(abs(X, dummy.ALWAYS)); cycles += 7; break; - // NOP + // NOP case 0x1a: case 0x3a: case 0x5a: @@ -739,7 +755,7 @@ public final void runcycle(final int scanline, final int pixel) { abs(X, dummy.ONCARRY); cycles += 4 + pb; break; - // ORA + // ORA case 0x09: ora(imm()); cycles += 2; @@ -772,7 +788,7 @@ public final void runcycle(final int scanline, final int pixel) { ora(indY(dummy.ONCARRY)); cycles += 5 + pb; break; - // Register instrs. + // Register instrs. case 0xAA: X = A; cycles += 2; @@ -789,12 +805,6 @@ public final void runcycle(final int scanline, final int pixel) { setflags(X); cycles += 2; break; - case 0xe8: - X++; - X &= 0xFF; - setflags(X); - cycles += 2; - break; case 0xa8: Y = A; cycles += 2; @@ -817,7 +827,7 @@ public final void runcycle(final int scanline, final int pixel) { setflags(Y); cycles += 2; break; - // RLA (unofficial) + // RLA (unofficial) case 0x23: rla(indX()); cycles += 8; @@ -846,7 +856,7 @@ public final void runcycle(final int scanline, final int pixel) { rla(abs(X, dummy.ALWAYS)); cycles += 7; break; - // ROL + // ROL case 0x2a: rolA(); cycles += 2; @@ -867,7 +877,7 @@ public final void runcycle(final int scanline, final int pixel) { rol(abs(X, dummy.ALWAYS)); cycles += 7; break; - // ROR + // ROR case 0x6a: rorA(); cycles += 2; @@ -888,7 +898,7 @@ public final void runcycle(final int scanline, final int pixel) { ror(abs(X, dummy.ALWAYS)); cycles += 7; break; - // RRA (unofficial) + // RRA (unofficial) case 0x63: rra(indX()); cycles += 8; @@ -917,17 +927,17 @@ public final void runcycle(final int scanline, final int pixel) { rra(abs(X, dummy.ALWAYS)); cycles += 7; break; - // RTI + // RTI case 0x40: rti(); cycles += 6; break; - // RTS + // RTS case 0x60: rts(); cycles += 6; break; - // SAX (unofficial) + // SAX (unofficial) case 0x83: sax(indX()); cycles += 6; @@ -944,7 +954,7 @@ public final void runcycle(final int scanline, final int pixel) { sax(abs()); cycles += 4; break; - // SBC + // SBC case 0xE1: sbc(indX()); cycles += 6; @@ -981,17 +991,17 @@ public final void runcycle(final int scanline, final int pixel) { sbc(abs(X, dummy.ONCARRY)); cycles += 4 + pb; break; - // SHX (unofficial) + // SHX (unofficial) case 0x9e: shx(abs(Y, dummy.ALWAYS)); cycles += 5; break; - // SHY (unofficial) + // SHY (unofficial) case 0x9c: shy(abs(X, dummy.ALWAYS)); cycles += 5; break; - // SLO (unofficial) + // SLO (unofficial) case 0x03: slo(indX()); cycles += 8; @@ -1020,7 +1030,7 @@ public final void runcycle(final int scanline, final int pixel) { slo(abs(X, dummy.ALWAYS)); cycles += 7; break; - // SRE (unofficial) + // SRE (unofficial) case 0x43: sre(indX()); cycles += 8; @@ -1049,7 +1059,7 @@ public final void runcycle(final int scanline, final int pixel) { sre(abs(X, dummy.ALWAYS)); cycles += 7; break; - // STA + // STA case 0x85: sta(zpg()); cycles += 3; @@ -1078,7 +1088,7 @@ public final void runcycle(final int scanline, final int pixel) { sta(indY(dummy.ALWAYS)); cycles += 6; break; - // Stack instructions + // Stack instructions case 0x9A: S = X; cycles += 2; @@ -1111,7 +1121,7 @@ public final void runcycle(final int scanline, final int pixel) { bytetoflags(pop()); cycles += 4; break; - // STX + // STX case 0x86: stx(zpg()); cycles += 3; @@ -1124,7 +1134,7 @@ public final void runcycle(final int scanline, final int pixel) { stx(abs()); cycles += 4; break; - // STY + // STY case 0x84: sty(zpg()); cycles += 3; @@ -1137,12 +1147,12 @@ public final void runcycle(final int scanline, final int pixel) { sty(abs()); cycles += 4; break; - // TAS (unofficial) + // TAS (unofficial) case 0x9b: tas(abs(Y, dummy.ALWAYS)); cycles += 5; break; - // XAA (unofficial) + // XAA (unofficial) case 0x8b: xaa(imm()); cycles += 2; @@ -1153,8 +1163,6 @@ public final void runcycle(final int scanline, final int pixel) { + utils.hex(PC - 1)); break; } - pb = 0; - PC &= 0xffff; } private void delayInterrupt() { @@ -1166,7 +1174,7 @@ private void rol(final int addr) { int data = (ram.read(addr)); ram.write(addr, data); //dummy write data = (data << 1) | (carryFlag ? 1 : 0); - carryFlag = utils.getbit(data, 8); + carryFlag = (data & utils.BIT8) != 0; data &= 0xFF; setflags(data); ram.write(addr, data); @@ -1174,7 +1182,7 @@ private void rol(final int addr) { private void rolA() { A = A << 1 | (carryFlag ? 1 : 0); - carryFlag = utils.getbit(A, 8); + carryFlag = (A & utils.BIT8) != 0; A &= 0xFF; setflags(A); } @@ -1183,7 +1191,7 @@ private void ror(final int addr) { int data = ram.read(addr); ram.write(addr, data); //dummy write final boolean tmp = carryFlag; - carryFlag = utils.getbit(data, 0); + carryFlag = (data & utils.BIT0) != 0; data >>= 1; data &= 0x7F; data |= (tmp ? 0x80 : 0); @@ -1193,7 +1201,7 @@ private void ror(final int addr) { private void rorA() { final boolean tmp = carryFlag; - carryFlag = utils.getbit(A, 0); + carryFlag = (A & utils.BIT0) != 0; A >>= 1; A &= 0x7F; A |= (tmp ? 128 : 0); @@ -1261,7 +1269,7 @@ private void breakinterrupt() { private void lsr(final int addr) { int data = ram.read(addr); ram.write(addr, data); //dummy write - carryFlag = utils.getbit(data, 0); + carryFlag = (data & utils.BIT0) != 0; data >>= 1; data &= 0x7F; ram.write(addr, data); @@ -1269,7 +1277,7 @@ private void lsr(final int addr) { } private void lsrA() { - carryFlag = utils.getbit(A, 0); + carryFlag = (A & utils.BIT0) != 0; A >>= 1; A &= 0x7F; setflags(A); @@ -1291,8 +1299,8 @@ private void ora(final int addr) { private void bit(final int addr) { final int data = ram.read(addr); zeroFlag = ((data & A) == 0); - negativeFlag = utils.getbit(data, 7); - overflowFlag = utils.getbit(data, 6); + negativeFlag = (data & utils.BIT7) != 0; + overflowFlag = (data & utils.BIT6) != 0; } private void jsr(final int addr) { @@ -1422,7 +1430,7 @@ private void and(final int addr) { private void asl(final int addr) { int data = ram.read(addr); ram.write(addr, data); //dummy write - carryFlag = utils.getbit(data, 7); + carryFlag = (data & utils.BIT7) != 0; data = data << 1; data &= 0xff; setflags(data); @@ -1430,7 +1438,7 @@ private void asl(final int addr) { } private void aslA() { - carryFlag = utils.getbit(A, 7); + carryFlag = (A & utils.BIT7) != 0; A <<= 1; A &= 0xff; setflags(A); @@ -1440,7 +1448,7 @@ private void aslA() { private void cmp(final int regval, final int addr) { final int result = regval - ram.read(addr); if (result < 0) { - negativeFlag = utils.getbit(result, 7); + negativeFlag = (result & utils.BIT7) != 0; carryFlag = false; zeroFlag = false; } else if (result == 0) { @@ -1448,7 +1456,7 @@ private void cmp(final int regval, final int addr) { carryFlag = true; zeroFlag = true; } else { - negativeFlag = utils.getbit(result, 7); + negativeFlag = (result & utils.BIT7) != 0; carryFlag = true; zeroFlag = false; } @@ -1471,7 +1479,7 @@ private void ldy(final int addr) { private void setflags(final int result) { zeroFlag = (result == 0); - negativeFlag = utils.getbit(result, 7); + negativeFlag = (result & utils.BIT7) != 0; } private void sta(final int addr) { @@ -1511,8 +1519,8 @@ private void arr(final int addr) { A = (((ram.read(addr) & A) >> 1) | (carryFlag ? 0x80 : 0x00)); setflags(A); - carryFlag = utils.getbit(A, 6); - overflowFlag = carryFlag ^ utils.getbit(A, 5); + carryFlag = (A & utils.BIT6) != 0; + overflowFlag = carryFlag ^ (A & utils.BIT5) != 0; } private void axs(final int addr) { @@ -1604,32 +1612,32 @@ private void xaa(int addr) { // Functions for memory address types; each returns the _memory_address_ for // the next fn - protected final int imm() { + private final int imm() { return PC++; } - protected final int zpg() { + private final int zpg() { // zero page mode return ram.read(PC++); } - protected final int zpg(final int reg) { + private final int zpg(final int reg) { // zero page added to register (modulus page boundary) return (ram.read(PC++) + reg) & 0xff; } - protected final int rel() { + private final int rel() { // returns actual value of PC, not memory location to look at // because only branches use this return ((byte) ram.read(PC++)) + PC; } - protected final int abs() { + private final int abs() { // absolute mode return ram.read(PC++) + (ram.read(PC++) << 8); } - protected final int abs(final int reg, final dummy dummy) { + private final int abs(final int reg, final dummy dummy) { // absolute plus value from reg final int addr = (ram.read(PC++) | (ram.read(PC++) << 8)); @@ -1649,7 +1657,7 @@ protected final int abs(final int reg, final dummy dummy) { return (addr + reg) & 0xffff; } - protected final int ind() { + private final int ind() { // weird mode. only used by jmp final int readloc = abs(); return ram.read(readloc) @@ -1659,7 +1667,7 @@ protected final int ind() { //is taken from first byte on the page, not first byte on NEXT page. } - protected final int indX() { + private final int indX() { // indirect mode final int arg = ram.read(PC++); return ram.read((arg + X) & 0xff) @@ -1667,7 +1675,7 @@ protected final int indX() { // doesn't suffer from the same bug as jump indirect } - protected final int indY(final dummy dummy) { + private final int indY(final dummy dummy) { final int arg = ram.read(PC++); final int addr = (ram.read((arg) & 0xff) | (ram.read((arg + 1) & 0xff) << 8)); diff --git a/src/com/grapeshot/halfnes/CPURAM.java b/src/com/grapeshot/halfnes/CPURAM.java index 59b42de..34d4f78 100644 --- a/src/com/grapeshot/halfnes/CPURAM.java +++ b/src/com/grapeshot/halfnes/CPURAM.java @@ -6,8 +6,12 @@ import com.grapeshot.halfnes.cheats.Patch; import com.grapeshot.halfnes.mappers.Mapper; +import java.lang.reflect.Field; +import java.security.AccessController; +import java.security.PrivilegedAction; import java.util.Arrays; import java.util.HashMap; +import sun.misc.Unsafe; /** * @@ -15,10 +19,34 @@ * * */ -public class CPURAM { +public final class CPURAM { + + static final Unsafe unsafe; + static final int INTEGER_ARRAY_BASE_OFFSET; + + static { + unsafe = (Unsafe) AccessController.doPrivileged( + new PrivilegedAction() { + @Override + public Object run() { + try { + Field f = Unsafe.class.getDeclaredField("theUnsafe"); + f.setAccessible(true); + return f.get(null); + } catch (Exception e) { + throw new Error(); + } + } + }); + + INTEGER_ARRAY_BASE_OFFSET = unsafe.arrayBaseOffset(int[].class); + if (unsafe.arrayIndexScale(int[].class) != 4) { + throw new AssertionError(); + } + } private boolean hasprgram = true; - private final int[] wram = new int[2048]; + final int[] wram = new int[2048]; Mapper mapper; public APU apu; PPU ppu; //need these to call their write handlers from here. @@ -30,26 +58,27 @@ public CPURAM(final Mapper mappy) { Arrays.fill(wram, 0xff); } - public final int read(final int addr) { - if (patches != null) { - int retval = _read(addr); - Patch p = patches.get(addr); - if (p != null) { - if (p.getAddress() == addr && p.matchesData(retval)) { - return p.getData(); - } - } - return retval; - } else { - return _read(addr); - } - } +// public final int read(final int addr) { +// if (patches != null) { +// int retval = _read(addr); +// Patch p = patches.get(addr); +// if (p != null) { +// if (p.getAddress() == addr && p.matchesData(retval)) { +// return p.getData(); +// } +// } +// return retval; +// } else { +// return _read(addr); +// } +// } - public final int _read(final int addr) { + public final int read(final int addr) { if (addr > 0x4018) { return mapper.cartRead(addr); } else if (addr <= 0x1fff) { return wram[addr & 0x7FF]; +// return unsafe.getInt(wram, INTEGER_ARRAY_BASE_OFFSET + (addr & 0x7FF) * 4); } else if (addr <= 0x3fff) { // 8 byte ppu regs; mirrored lots return ppu.read(addr & 7); diff --git a/src/com/grapeshot/halfnes/JavaFXNES.java b/src/com/grapeshot/halfnes/JavaFXNES.java new file mode 100644 index 0000000..6f9c7ce --- /dev/null +++ b/src/com/grapeshot/halfnes/JavaFXNES.java @@ -0,0 +1,159 @@ +package com.grapeshot.halfnes; + +import com.grapeshot.halfnes.ui.ControllerImpl; +import com.grapeshot.halfnes.ui.GUIInterface; +import com.grapeshot.halfnes.ui.OnScreenMenu; +import com.grapeshot.halfnes.video.NesColors; +import java.nio.ByteBuffer; +import java.util.List; +import javafx.application.Application; +import javafx.application.Platform; +import javafx.geometry.Insets; +import javafx.geometry.Rectangle2D; +import javafx.scene.Group; +import javafx.scene.Scene; +import javafx.scene.canvas.Canvas; +import javafx.scene.image.PixelWriter; +import javafx.scene.image.WritablePixelFormat; +import javafx.scene.input.KeyCode; +import javafx.scene.input.KeyCombination; +import javafx.scene.paint.Color; +import javafx.scene.transform.Scale; +import javafx.stage.Screen; +import javafx.stage.Stage; + +/** + * @author Stephen Chin + */ +public class JavaFXNES extends Application implements GUIInterface { + + + // Set the overscan insets to match your config + // And make sure your framebuffer is set to: + // * screen.width + overscan.right + // * screen.height + overscan.bottom +// private static final Insets overscan = new Insets(0, 0, 0, 0); + private static final Insets overscan = new Insets(-59, 160, 150, 0); + private static final Insets extraOverscan = new Insets(8, 0, 8, 0); + + private NES nes; + private Canvas gameCanvas; + private Stage stage; + private OnScreenMenu menu; + + @Override + public void start(Stage stage) throws Exception { + this.stage = stage; + Rectangle2D bounds = Screen.getPrimary().getBounds(); + gameCanvas = new Canvas(256, 240); + stage.addEventHandler(javafx.stage.WindowEvent.WINDOW_CLOSE_REQUEST, e -> { + nes.quit(); + }); + menu = new OnScreenMenu(this); + menu.setPadding(extraOverscan); + menu.setPrefWidth(256); + menu.setPrefHeight(240); + Group root = new Group(gameCanvas, menu); + Scene scene = new Scene(root, bounds.getWidth(), bounds.getHeight(), Color.BLACK); + stage.setScene(scene); + stage.setFullScreen(true); + stage.setFullScreenExitKeyCombination(KeyCombination.NO_MATCH); + stage.addEventHandler(javafx.scene.input.KeyEvent.KEY_PRESSED, e -> { + if (e.getCode().equals(KeyCode.ESCAPE)) { + menu.show(); + } + }); + root.setLayoutX(overscan.getRight() - overscan.getLeft() - extraOverscan.getLeft() * bounds.getWidth() / 256); + root.setLayoutY(overscan.getBottom() - overscan.getTop() - extraOverscan.getTop() * bounds.getHeight() / 240); + root.getTransforms().add(new Scale( + (bounds.getWidth() - (overscan.getRight() - overscan.getLeft())) / (256 - extraOverscan.getLeft() - extraOverscan.getRight()), + (bounds.getHeight() - (overscan.getBottom() - overscan.getTop())) / (240 - extraOverscan.getTop() - extraOverscan.getBottom()))); + nes = new NES(this); + ControllerImpl padController1 = new ControllerImpl(scene, 0); + ControllerImpl padController2 = new ControllerImpl(scene, 1); + padController1.startEventQueue(); + padController2.startEventQueue(); + nes.setControllers(padController1, padController2); + final List params = getParameters().getRaw(); + new Thread(() -> { + if (params.isEmpty()) { + nes.run(); + } else { + nes.run(params.get(0)); + } + }, "Game Thread").start(); + } + + public static void main(String[] args) { + launch(args); + } + + @Override + public NES getNes() { + return nes; + } + + @Override + public void setNES(NES nes) { + this.nes = nes; + } + + final byte[] buffer = new byte[256 * 240 * 4]; + final WritablePixelFormat format = WritablePixelFormat.getByteBgraPreInstance(); + + private final long[] frametimes = new long[60]; + private int frametimeptr = 0; + private double fps; + + @Override + public void setFrame(int[] nespixels, int[] bgcolor, boolean dotcrawl) { + Platform.runLater(() -> { + frametimes[frametimeptr] = nes.getFrameTime(); + ++frametimeptr; + frametimeptr %= frametimes.length; + + if (frametimeptr == 0) { + long averageframes = 0; + for (long l : frametimes) { + averageframes += l; + } + averageframes /= frametimes.length; + fps = 1E9 / averageframes; + stage.setTitle(String.format("HalfNES %s, %2.2f fps", + // + ((nes.frameskip > 0) ? " frameskip " + nes.frameskip : ""), + NES.VERSION, + // nes.getCurrentRomName(), + fps)); + } + PixelWriter writer = gameCanvas.getGraphicsContext2D().getPixelWriter(); + for (int i = 0; i < nespixels.length; i++) { + byte[] colbytes = NesColors.colbytes[(nespixels[i] & 0x1c0) >> 6][nespixels[i] & 0x3f]; + System.arraycopy(colbytes, 0, buffer, i * 4, 3); + } + writer.setPixels(0, 0, 256, 240, format, buffer, 0, 256 * 4); + }); + } + + @Override + public void messageBox(String message) { + System.out.println("message = " + message); + } + + @Override + public void run() { + Platform.runLater(() -> { + stage.show(); + menu.show(); + }); + } + + @Override + public void render() { + // whatever... + } + + @Override + public void loadROMs(String path) { + menu.loadROMs(path); + } +} diff --git a/src/com/grapeshot/halfnes/NES.java b/src/com/grapeshot/halfnes/NES.java index 56e8760..5b6d017 100644 --- a/src/com/grapeshot/halfnes/NES.java +++ b/src/com/grapeshot/halfnes/NES.java @@ -5,14 +5,15 @@ import com.grapeshot.halfnes.mappers.BadMapperException; import com.grapeshot.halfnes.mappers.Mapper; -import java.util.prefs.Preferences; - /** * * @author Andrew Hoffman */ public class NES { + public final static int frameskip = 0; + public final static int soundskip = 3; + private Mapper mapper; private APU apu; private CPU cpu; @@ -25,29 +26,22 @@ public class NES { public long frameStartTime, framecount, frameDoneTime; private boolean frameLimiterOn = true; private String curRomPath, curRomName; - private final GUIInterface gui = new GUIImpl(this); + private final GUIInterface gui; private final FrameLimiterInterface limiter = new FrameLimiterImpl(this); // Pro Action Replay device private ActionReplay actionReplay; - - public NES() { - try { - java.awt.EventQueue.invokeAndWait(gui); - } catch (InterruptedException e) { - System.err.println("Could not initialize GUI. Exiting."); - System.exit(-1); - } catch (java.lang.reflect.InvocationTargetException f) { - System.err.println(f.getCause().toString()); - //not sure how this could happen (thrown if run method causes exception) - System.exit(-1); - } + + public NES(GUIInterface gui) { + this.gui = gui; + gui.setNES(this); + gui.run(); } public void run(final String romtoload) { Thread.currentThread().setPriority(Thread.NORM_PRIORITY + 1); //set thread priority higher than the interface thread curRomPath = romtoload; - loadROM(romtoload); + gui.loadROMs(romtoload); run(); } @@ -76,17 +70,9 @@ public void run() { } }; - private synchronized void runframe() { - //the main method sequencing everything that has to happen in the nes each frame - //loops unrolled a bit to avoid some conditionals every cycle - for (int scanline = 0; scanline <= 240; ++scanline) { - runLine(scanline); - } - - //run for scanlines of vblank - for (int scanline = 241; scanline < 262; ++scanline) { - runLine(scanline); - } + private void runframe() { + final boolean draw = framecount % (frameskip + 1) == 0; + ppu.draw(draw); //do end of frame stuff dontSleep = apu.bufferHasLessThan(1000); @@ -94,25 +80,22 @@ private synchronized void runframe() { //this is to prevent the emulator from getting stuck sleeping too much //on a slow system or when the audio buffer runs dry. - apu.finishframe(); + apu.finishframe(framecount % (soundskip + 1) == 0); cpu.modcycles(); //run cpu, ppu for active drawing time //render the frame - ppu.renderFrame(gui); + if (draw) { + ppu.renderFrame(gui); + } if ((framecount & 2047) == 0) { //save sram every 30 seconds or so saveSRAM(true); } ++framecount; - //System.err.println(framecount); } - private void runLine(int scanline) { - //System.err.println(scanline); - ppu.clockLine(scanline); - } public void setControllers(ControllerInterface controller1, ControllerInterface controller2) { this.controller1 = controller1; diff --git a/src/com/grapeshot/halfnes/PPU.java b/src/com/grapeshot/halfnes/PPU.java index d96660e..3c7f410 100644 --- a/src/com/grapeshot/halfnes/PPU.java +++ b/src/com/grapeshot/halfnes/PPU.java @@ -8,7 +8,7 @@ import java.awt.image.BufferedImage; import java.util.Arrays; -public class PPU { +public final class PPU { public Mapper mapper; private int oamaddr, sprite0x, readbuffer = 0; @@ -16,7 +16,7 @@ public class PPU { private int loopyT = 0x0;//temp pointer private int loopyX = 0;//fine x scroll private int scanline = 0; - private int cycles = 0; + private boolean firstcycle; private int framecount = 0; private int div = 2; private final int[] OAM = new int[256], spriteshiftregH = new int[8], @@ -24,11 +24,12 @@ public class PPU { spritepals = new int[8], bitmap = new int[240 * 256]; private final boolean[] spritebgflags = new boolean[8]; private boolean sprite0hit = false, even = true, bgpattern = true, sprpattern = false; - private final int[] ppuregs = new int[0x8]; + private int ppureg0, ppureg1, ppureg2, ppureg3; public final int[] pal = //power-up pallette verified by Blargg's power_up_palette test {0x09, 0x01, 0x00, 0x01, 0x00, 0x02, 0x02, 0x0D, 0x08, 0x10, 0x08, 0x24, 0x00, 0x00, //palette *might* be different on every NES 0x04, 0x2C, 0x09, 0x01, 0x34, 0x03, 0x00, 0x04, 0x00, 0x14, 0x08, 0x3A, 0x00, 0x02, //but some emulators (nesemu1, BizHawk, RockNES, MyNes) 0x00, 0x20, 0x2C, 0x08}; //use it anyway + public final int[][] packedpal = new int[8][4]; private DebugUI debuggui; private int vraminc = 1; private final static boolean PPUDEBUG = PrefsSingleton.get().getBoolean("ntView", false); @@ -39,7 +40,6 @@ public class PPU { public PPU(final Mapper mapper) { this.mapper = mapper; Arrays.fill(OAM, 0xff); - Arrays.fill(ppuregs, 0x00); if (PPUDEBUG) { nametableView = new BufferedImage(512, 480, BufferedImage.TYPE_INT_BGR); debuggui = new DebugUI(512, 480); @@ -60,8 +60,8 @@ public final int read(final int regnum) { case 2: even = true; if (scanline == 241) { - if (cycles == 1) {//suppress NMI flag if it was just turned on this same cycle - setvblankflag(false); + if (firstcycle) {//suppress NMI flag if it was just turned on this same cycle + clearvblankflag(); } //OK, uncommenting this makes blargg's NMI suppression test //work but breaks Antarctic Adventure. @@ -73,14 +73,12 @@ public final int read(final int regnum) { // mapper.cpu.nmiNext = false; // } } - final int tmp = (ppuregs[2] & ~0x1f) + (openbus & 0x1f); - setvblankflag(false); - openbus = tmp; - break; + final int tmp = (ppureg2 & ~0x1f) + (openbus & 0x1f); + clearvblankflag(); + return openbus = tmp; case 4: // reading this is NOT reliable but some games do it anyways - openbus = OAM[oamaddr]; - break; + return openbus = OAM[oamaddr]; case 7: // PPUDATA // correct behavior. read is delayed by one @@ -89,21 +87,18 @@ public final int read(final int regnum) { final int temp = readbuffer; readbuffer = mapper.ppuRead(loopyV & 0x3fff); loopyV += vraminc; - openbus = temp; - break; + return openbus = temp; } else { readbuffer = mapper.ppuRead((loopyV & 0x3fff) - 0x1000); final int temp = mapper.ppuRead(loopyV); loopyV += vraminc; - openbus = temp; - break; + return openbus = temp; } // and don't increment on read default: return openbus; // last value written to ppu } - return openbus; } /** @@ -119,15 +114,15 @@ public final void write(final int regnum, final int data) { openbus = data; switch (regnum) { case 0: - ppuregs[0] = data; - vraminc = (getbit(data, 2) ? 32 : 1); + ppureg0 = data; + vraminc = ((data & BIT2) != 0 ? 32 : 1); //set 2 bits of vram address (nametable select) loopyT &= ~0xc00; loopyT += (data & 3) << 10; - //ppuregs[1] ^= 0xe0; //DEBUG: why does SMB write $2000 in midframe? + //ppureg1 ^= 0xe0; //DEBUG: why does SMB write $2000 in midframe? break; case 1: - ppuregs[1] = data; + ppureg1 = data; break; case 3: // PPUOAMADDR (2003) @@ -198,7 +193,7 @@ public final void write(final int regnum, final int data) { * @return true */ private boolean ppuIsOn() { - return getbit(ppuregs[1], 3) || getbit(ppuregs[1], 4); + return (ppureg1 & (BIT3 | BIT4)) != 0; } /** @@ -213,69 +208,59 @@ public final boolean mmc3CounterClocking() { } /** - * Runs the PPU emulation for one NES scan line. + * Runs the PPU emulation for the entire screen. * * @param scanline */ - public final void clockLine(int scanline) { - //skip a PPU clock on line 0 of odd frames when rendering is on - int skip = (scanline == 0 - && getbit(ppuregs[1], 3) - && !getbit(framecount, 1)) ? 1 : 0; - for (cycles = skip; cycles < 341; ++cycles) { - clock(); - } - } - - /** - * runs the emulation for one PPU clock cycle. - */ - public final void clock() { - //cycle based ppu stuff will go here - if (cycles == 1) { - drawLine(); - } - //handle sprite 0 - if (sprite0hit && sprite0x == (cycles + 1)) { - sprite0hit = false; - ppuregs[2] |= 0x40; - } - //handle vblank on / off - if (scanline < 240 || scanline == 261) { - //on all rendering lines - if (cycles >= 257 && cycles <= 341) { - //clear the oam address from pxls 257-341 continuously - ppuregs[3] = 0; - } - if (scanline == 261 && cycles == 0) { - // turn off vblank, sprite 0, sprite overflow flags - setvblankflag(false); - ppuregs[2] &= 0x9F; - } - } else if (scanline == 241 && cycles == 1) { - setvblankflag(true); - } - //handle nmi - if (vblankflag && getbit(ppuregs[0], 7)) { - //pull NMI line on when conditions are right - mapper.cpu.setNMI(true); - } else { - mapper.cpu.setNMI(false); - } + public final void draw(boolean render) { + firstcycle = true; + for (scanline = 0; scanline < 262; ++scanline) { + //skip a PPU clock on line 0 of odd frames when rendering is on + final int skip = scanline == 0 + && (ppureg1 & BIT3) != 0 + && (framecount & BIT1) == 0 ? 1 : 0; + drawLine(render); + for (int cycle = skip; cycle < 341; ++cycle) { + //handle sprite 0 + if (sprite0hit && sprite0x == (cycle + 1)) { + sprite0hit = false; + ppureg2 |= 0x40; + } + //handle vblank on / off + if (scanline < 240 || scanline == 261) { + //on all rendering lines + if (cycle >= 257 && cycle <= 341) { + //clear the oam address from pxls 257-341 continuously + ppureg3 = 0; + } + if (scanline == 261 && cycle == 0) { + // turn off vblank, sprite 0, sprite overflow flags + clearvblankflag(); + ppureg2 &= 0x9F; + } + } else if (scanline == 241 && cycle == 1) { + markvblankflag(); + } + //handle nmi + if (vblankflag && (ppureg0 & utils.BIT7) != 0) { + //pull NMI line on when conditions are right + mapper.cpu.setNMI(true); + } else { + mapper.cpu.setNMI(false); + } - //clock CPU, once every 3 ppu cycles - div = (div + 1) % 3; - if (div == 0) { - mapper.cpu.runcycle(scanline, cycles); - } - if (cycles == 257) { - mapper.notifyscanline(scanline); - } else if (cycles == 340) { - scanline = (scanline + 1) % 262; - if (scanline == 0) { - ++framecount; + //clock CPU, once every 3 ppu cycle + if (++div == 3) { + div = 0; + mapper.cpu.runcycle(scanline, cycle); + } + if (cycle == 257) { + mapper.notifyscanline(scanline); + } + firstcycle = false; } } + ++framecount; } int bgcolor; @@ -284,7 +269,7 @@ public final void clock() { /** * Causes the PPU to draw one line of video. */ - private void drawLine() { + private void drawLine(boolean render) { //System.err.println("SCANLINE " + scanline); //this contains probably more magic numbers than the rest of the program combined. //TODO: define some static bitmasks to manipulate the address through, instead @@ -294,38 +279,43 @@ private void drawLine() { if (scanline >= 240) { return; } - bgpattern = getbit(ppuregs[0], 4); - sprpattern = getbit(ppuregs[0], 3); final int bufferoffset = scanline << 8; - //TODO: Simplify Logic - bgcolor = pal[0] + 256; //plus 256 is to give indication it IS the bgcolor - //because bg color is special - bgcolors[scanline] = pal[0]; - if (getbit(ppuregs[1], 3)) { //if background is on, draw a line of that - drawBG(bufferoffset); - } else { - //rendering is off, so draw either the background color OR - //if the PPU address points to the palette, draw that color instead. - bgcolor = ((loopyV > 0x3f00 && loopyV < 0x3fff) ? mapper.ppuRead(loopyV) : pal[0]); - Arrays.fill(bitmap, bufferoffset, bufferoffset + 256, bgcolor); + if (render) { + bgpattern = (ppureg0 & utils.BIT4) != 0; + sprpattern = (ppureg0 & utils.BIT3) != 0; + //TODO: Simplify Logic + bgcolor = pal[0] + 256; //plus 256 is to give indication it IS the bgcolor + //because bg color is special + bgcolors[scanline] = pal[0]; + computePackedpal(); + if ((ppureg1 & utils.BIT3) != 0) { //if background is on, draw a line of that + drawBG(bufferoffset); + } else { + //rendering is off, so draw either the background color OR + //if the PPU address points to the palette, draw that color instead. + bgcolor = ((loopyV > 0x3f00 && loopyV < 0x3fff) ? mapper.ppuRead(loopyV) : pal[0]); + Arrays.fill(bitmap, bufferoffset, bufferoffset + 256, bgcolor); + } } //draw sprites on top of whatever we had - drawSprites(); + drawSprites(render); //evaluate sprites for NEXT scanline (as long as either background or sprites are enabled) if (ppuIsOn()) { - evalSprites(); + evalSprites(render); } - //deal with the grayscale flag - if (getbit(ppuregs[1], 0)) { + if (render) { + //deal with the grayscale flag + if ((ppureg1 & utils.BIT0) != 0) { + for (int i = bufferoffset; i < (bufferoffset + 256); ++i) { + bitmap[i] &= 0x30; + } + } + //handle color emphasis + final int emph = (ppureg1 & 0xe0) << 1; for (int i = bufferoffset; i < (bufferoffset + 256); ++i) { - bitmap[i] &= 0x30; + bitmap[i] = bitmap[i] & 0x3f | emph; } } - //handle color emphasis - final int emph = (ppuregs[1] & 0xe0) << 1; - for (int i = bufferoffset; i < (bufferoffset + 256); ++i) { - bitmap[i] = bitmap[i] & 0x3f | emph; - } } /** @@ -335,7 +325,6 @@ private void drawLine() { * multiple of 256) */ private void drawBG(final int bufferoffset) { - //System.err.println(" BG ON!"); // if bg is on, draw tiles. if (scanline == 0) { //update whole scroll @@ -350,9 +339,14 @@ private void drawBG(final int bufferoffset) { int ntoffset = (loopyV & 0xc00) | 0x2000; int attroffset = ntoffset + 0x3c0; boolean horizWrap = false; - for (int tilenum = 0; tilenum < 33; ++tilenum) { + int xpos = -loopyX; + final int ntcheckoff = (((loopyV & 0x1f) << 3) + loopyX); + final int bgpatternoff = (bgpattern ? 0x1000 : 0); + final int numtiles = loopyX == 0 ? 32 : 33; + final int loopyVOff = (loopyV & 0x7000) >> 12; + for (int tilenum = 0; tilenum < numtiles; ++tilenum, xpos += 8) { //for each tile in row - if ((tilenum * 8 + (((loopyV & 0x1f) << 3) + loopyX)) > 255 + if ((tilenum * 8 + ntcheckoff) > 255 && !horizWrap) { //if scrolling off the side of the nametable, bump address to next nametable ntoffset ^= 0x400; @@ -360,20 +354,20 @@ private void drawBG(final int bufferoffset) { attroffset ^= 0x400; horizWrap = true; } - //get palette number from attribute table byte final int tileaddr = mapper.ppuRead(ntoffset + (loopyV & 0x3ff) + tilenum) * 16 - + (bgpattern ? 0x1000 : 0); + + bgpatternoff; + //get palette number from attribute table byte final int palettenum = getAttribute(attroffset, (loopyV + tilenum) & 0x1f, (((ntoffset + loopyV + tilenum) & 0x3e0) >> 5)); - final int[] tile = getTile(tileaddr, palettenum * 4, - (loopyV & 0x7000) >> 12); + final int[] tile = getTile(tileaddr, palettenum, loopyVOff); //now put inna buffer - final int xpos = tilenum * 8 - loopyX; - for (int pxl = 0; pxl < 8; ++pxl) { - if ((pxl + xpos) < 256 && (pxl + xpos) >= 0) { //it's not off the screen - bitmap[pxl + xpos + bufferoffset] = tile[pxl]; - } + if (tilenum == 0 && loopyX > 0) { + System.arraycopy(tile, loopyX, bitmap, xpos + bufferoffset + loopyX, 8 - loopyX); + } else if (tilenum == 32 && loopyX > 0) { + System.arraycopy(tile, 0, bitmap, xpos + bufferoffset, loopyX); + } else { + System.arraycopy(tile, 0, bitmap, xpos + bufferoffset, 8); } } //increment loopy_v to next row of tiles @@ -396,7 +390,7 @@ private void drawBG(final int bufferoffset) { //attroffset += 0x7c0; } //hide leftmost 8 pixels if that flag is on - if (!getbit(ppuregs[1], 1)) { + if ((ppureg1 & BIT1) == 0) { for (int i = 0; i < 8; ++i) { bitmap[i + bufferoffset] = bgcolor; } @@ -408,13 +402,13 @@ private void drawBG(final int bufferoffset) { /** * evaluates PPU sprites for the NEXT scanline */ - private void evalSprites() { + private void evalSprites(boolean render) { sprite0here = false; - bgpattern = getbit(ppuregs[0], 4); - sprpattern = getbit(ppuregs[0], 3); + bgpattern = (ppureg0 & BIT4) != 0; + sprpattern = (ppureg0 & BIT3) != 0; int ypos, offset, tilefetched; found = 0; - final boolean spritesize = getbit(ppuregs[0], 5); + final boolean spritesize = (ppureg0 & BIT5) != 0; //primary evaluation //need to emulate behavior when OAM address is set to nonzero here for (int spritestart = 0; spritestart < 255; spritestart += 4) { @@ -434,17 +428,17 @@ private void evalSprites() { if (found >= 8) { //if more than 8 sprites, set overflow bit and STOP looking //todo: add "no sprite limit" option back - ppuregs[2] |= 0x20; + ppureg2 |= 0x20; break; //also the real PPU does strange stuff on sprite overflow. } else { //set up ye sprite for rendering final int oamextra = OAM[spritestart + 2]; //bg flag - spritebgflags[found] = getbit(oamextra, 5); + spritebgflags[found] = (oamextra & BIT5) != 0; //x value spriteXlatch[found] = OAM[spritestart + 3]; spritepals[found] = ((oamextra & 3) + 4) * 4; - if (getbit(oamextra, 7)) { + if ((oamextra & BIT7) != 0) { //if sprite is flipped vertically, reverse the offset offset = (spritesize ? 15 : 7) - offset; } @@ -463,7 +457,7 @@ private void evalSprites() { } tilefetched += offset; //now load up the shift registers for said sprite - final boolean hflip = getbit(oamextra, 6); + final boolean hflip = (oamextra & BIT6) != 0; if (!hflip) { spriteshiftregL[found] = reverseByte(mapper.ppuRead(tilefetched)); spriteshiftregH[found] = reverseByte(mapper.ppuRead(tilefetched + 8)); @@ -476,22 +470,22 @@ private void evalSprites() { } for (int i = found; i < 8; ++i) { //fill unused sprite registers with zeros - spriteshiftregL[found] = 0; - spriteshiftregH[found] = 0; + spriteshiftregL[i] = 0; + spriteshiftregH[i] = 0; } } /** * draws appropriate lines of the sprites selected by sprite evaluation */ - private void drawSprites() { + private void drawSprites(boolean render) { //profiler doesn't see how slow it is though. fix that! if (found == 0) { //no sprites to draw on line. return; } final int bufferoffset = scanline << 8; - final int startdraw = getbit(ppuregs[1], 2) ? 0 : 8;//sprite left 8 pixels clip + final int startdraw = (ppureg1 & BIT2) != 0 ? 0 : 8;//sprite left 8 pixels clip for (int x = 0; x < 256; ++x) { sprpxl = 0; index = 7; @@ -507,7 +501,7 @@ private void drawSprites() { spriteshiftregL[y] >>= 1; } } - if (sprpxl == 0 || x < startdraw || !getbit(ppuregs[1], 4)) { + if (sprpxl == 0 || x < startdraw || (ppureg1 & BIT4) == 0) { //no opaque sprite pixel here continue; } @@ -517,10 +511,10 @@ private void drawSprites() { //sprite 0 hit! sprite0hit = true; sprite0x = x; - // ppuregs[1] |= 1;//debug + // ppureg1 |= 1;//debug } //now, FINALLY, drawing. - if (!spritebgflags[index] || (bitmap[bufferoffset + x] == bgcolor)) { + if ((!spritebgflags[index] || (bitmap[bufferoffset + x] == bgcolor)) && render) { bitmap[bufferoffset + x] = pal[spritepals[index] + sprpxl]; } } @@ -538,14 +532,14 @@ private void drawSprites() { */ private int getAttribute(final int ntstart, final int tileX, final int tileY) { final int base = mapper.ppuRead(ntstart + (tileX >> 2) + 8 * (tileY >> 2)); - if (getbit(tileY, 1)) { - if (getbit(tileX, 1)) { + if ((tileY & utils.BIT1) != 0) { + if ((tileX & utils.BIT1) != 0) { return (base >> 6) & 3; } else { return (base >> 4) & 3; } } else { - if (getbit(tileX, 1)) { + if ((tileX & utils.BIT1) != 0) { return (base >> 2) & 3; } else { return base & 3; @@ -660,30 +654,49 @@ public final void renderFrame(GUIInterface gui) { * @param off PPU base address * @return int array with 8 NES color numbers */ - private int[] getTile(final int tileptr, final int paletteindex, final int off) { - tilepal[0] = bgcolor; - System.arraycopy(pal, paletteindex + 1, tilepal, 1, 3); - // per line of tile ( 1 byte) - int linelowbits = mapper.ppuRead(off + tileptr); - int linehighbits = mapper.ppuRead(off + tileptr + 8); - for (int j = 7; j >= 0; --j) { - // per pixel(1 bit) - tiledata[j] = tilepal[((linehighbits & 1) << 1) + (linelowbits & 1)]; - linehighbits >>= 1; - linelowbits >>= 1; - } + private int[] getTile(final int tileptr, final int palettenum, final int off) { + final int[] tilepal = packedpal[palettenum]; + final int linelowbits = mapper.ppuRead(off + tileptr); + final int linehighbits = mapper.ppuRead(off + tileptr + 8) << 1; + tiledata[7] = tilepal[((linehighbits & 0x2 | linelowbits & 0x1)) & 0xFF]; + tiledata[6] = tilepal[((linehighbits & 0x4 | linelowbits & 0x2) >> 1) & 0xFF]; + tiledata[5] = tilepal[((linehighbits & 0x8 | linelowbits & 0x4) >> 2) & 0xFF]; + tiledata[4] = tilepal[((linehighbits & 0x10 | linelowbits & 0x8) >> 3) & 0xFF]; + tiledata[3] = tilepal[((linehighbits & 0x20 | linelowbits & 0x10) >> 4) & 0xFF]; + tiledata[2] = tilepal[((linehighbits & 0x40 | linelowbits & 0x20) >> 5) & 0xFF]; + tiledata[1] = tilepal[((linehighbits & 0x80 | linelowbits & 0x40) >> 6) & 0xFF]; + tiledata[0] = tilepal[((linehighbits & 0x100 | linelowbits & 0x80) >> 7) & 0xFF]; return tiledata; } private boolean vblankflag = false; /** - * Sets both the internal PPU vblank flag and the one visible to the NES + * Marks both the internal PPU vblank flag and the one visible to the NES + * + * @param b value of the flag + */ + private void markvblankflag() { + vblankflag = true; + ppureg2 |= BIT7; + } + + /** + * Clears both the internal PPU vblank flag and the one visible to the NES * * @param b value of the flag */ - private void setvblankflag(boolean b) { - vblankflag = b; - ppuregs[2] = setbit(ppuregs[2], 7, b); + private void clearvblankflag() { + vblankflag = false; + ppureg2 &= ~BIT7; + } + + private void computePackedpal() { + for (int i=0; i<8; i++) { + packedpal[i][0] = bgcolor; + packedpal[i][1] = pal[i*4 + 1]; + packedpal[i][2] = pal[i*4 + 2]; + packedpal[i][3] = pal[i*4 + 3]; + } } } diff --git a/src/com/grapeshot/halfnes/ROMLoader.java b/src/com/grapeshot/halfnes/ROMLoader.java index e586a33..7b829df 100644 --- a/src/com/grapeshot/halfnes/ROMLoader.java +++ b/src/com/grapeshot/halfnes/ROMLoader.java @@ -49,12 +49,12 @@ public void parseHeader() throws BadMapperException { //basically multicarts over 2mb won't ever work without a DB } chrsize = 8192 * header[5]; - scrolltype = utils.getbit(header[6], 3) + scrolltype = (header[6] & utils.BIT3) != 0 ? Mapper.MirrorType.FOUR_SCREEN_MIRROR - : utils.getbit(header[6], 0) + : (header[6] & utils.BIT0) != 0 ? Mapper.MirrorType.V_MIRROR : Mapper.MirrorType.H_MIRROR; - savesram = utils.getbit(header[6], 1); + savesram = (header[6] & utils.BIT1) != 0; mappertype = (header[6] >> 4); if (header[11] + header[12] + header[13] + header[14] + header[15] == 0) {// fix for DiskDude diff --git a/src/com/grapeshot/halfnes/audio/MMC5SoundChip.java b/src/com/grapeshot/halfnes/audio/MMC5SoundChip.java index 1a606e9..50b24e3 100644 --- a/src/com/grapeshot/halfnes/audio/MMC5SoundChip.java +++ b/src/com/grapeshot/halfnes/audio/MMC5SoundChip.java @@ -36,11 +36,11 @@ public void write(int register, int data) { switch (register) { case 0x0: //length counter 1 halt - lenctrHalt[0] = utils.getbit(data, 5); + lenctrHalt[0] = (data & utils.BIT5) != 0; // pulse 1 duty cycle timers[0].setduty(dutylookup[data >> 6]); // and envelope - envConstVolume[0] = utils.getbit(data, 4); + envConstVolume[0] = (data & utils.BIT4) != 0; envelopeValue[0] = data & 15; //setvolumes(); break; @@ -65,11 +65,11 @@ public void write(int register, int data) { break; case 0x4: //length counter 2 halt - lenctrHalt[1] = utils.getbit(data, 5); + lenctrHalt[1] = (data & utils.BIT5) != 0; // pulse 2 duty cycle timers[1].setduty(dutylookup[data >> 6]); // and envelope - envConstVolume[1] = utils.getbit(data, 4); + envConstVolume[1] = (data & utils.BIT4) != 0; envelopeValue[1] = data & 15; //setvolumes(); break; @@ -91,12 +91,12 @@ public void write(int register, int data) { envelopeStartFlag[1] = true; break; case 0x10: - pcmMode = utils.getbit(data, 0); + pcmMode = (data & utils.BIT0) != 0; //true = read mode, false = write mode //read mode watches ALL reads in first 8k of PRG ROM //and writes to dpcm reg //(no way to implement w/o refactors) - pcmIRQen = utils.getbit(data, 7); + pcmIRQen = (data & utils.BIT7) != 0; if (pcmIRQen || pcmMode) { System.err.println("Implement the MMC5 PCM IRQ, something's using it!"); } diff --git a/src/com/grapeshot/halfnes/audio/Namco163SoundChip.java b/src/com/grapeshot/halfnes/audio/Namco163SoundChip.java index 2fd3416..b5a391e 100644 --- a/src/com/grapeshot/halfnes/audio/Namco163SoundChip.java +++ b/src/com/grapeshot/halfnes/audio/Namco163SoundChip.java @@ -74,7 +74,7 @@ private void clock_channel(final int ch) { private int getWavefromRAM(final int addr) { final int b = registers[(addr) >> 1]; - int r = utils.getbit(addr, 0) ? b >> 4 : b & 0xf; + int r = (addr & utils.BIT0) != 0 ? b >> 4 : b & 0xf; return r; } diff --git a/src/com/grapeshot/halfnes/audio/NoiseTimer.java b/src/com/grapeshot/halfnes/audio/NoiseTimer.java index ea4fba3..621e6ab 100644 --- a/src/com/grapeshot/halfnes/audio/NoiseTimer.java +++ b/src/com/grapeshot/halfnes/audio/NoiseTimer.java @@ -32,11 +32,14 @@ public void setduty(int duty) { @Override public final void clock() { - --divider; - while (divider <= 0) { - divider += period + periodadd; - position = ++position % values.length; + ++divider; + // note: stay away from negative division to avoid rounding problems + int periods = (divider + period + periodadd) / (period + periodadd); + if (periods < 0) { + periods = 0; // can happen if period or periodadd were made smaller } + position = (position + periods) % values.length; + divider -= (period + periodadd) * periods; } @Override @@ -51,11 +54,14 @@ public final void reset() { @Override public final void clock(final int cycles) { - divider -= cycles; - while (divider <= 0) { - divider += period + periodadd; - position = ++position % values.length; + divider += cycles; + // note: stay away from negative division to avoid rounding problems + int periods = (divider + period + periodadd) / (period + periodadd); + if (periods < 0) { + periods = 0; // can happen if period or periodadd were made smaller } + position = (position + periods) % values.length; + divider -= (period + periodadd) * periods; } @Override @@ -68,7 +74,7 @@ public static int[] genvalues(int whichbit, int seed) { for (int i = 0; i < tehsuck.length; ++i) { seed = (seed >> 1) | ((utils.getbit(seed, whichbit) - ^ utils.getbit(seed, 0)) + ^ (seed & utils.BIT0) != 0) ? 16384 : 0); tehsuck[i] = seed; } diff --git a/src/com/grapeshot/halfnes/audio/SquareTimer.java b/src/com/grapeshot/halfnes/audio/SquareTimer.java index c60951b..57eea47 100644 --- a/src/com/grapeshot/halfnes/audio/SquareTimer.java +++ b/src/com/grapeshot/halfnes/audio/SquareTimer.java @@ -18,22 +18,28 @@ public final void clock() { if (period + periodadd <= 0) { return; } - --divider; - while (divider <= 0) { - divider += period + periodadd; - position = ++position % values.length; + ++divider; + // note: stay away from negative division to avoid rounding problems + int periods = (divider + period + periodadd) / (period + periodadd); + if (periods < 0) { + periods = 0; // can happen if period or periodadd were made smaller } + position = (position + periods) % values.length; + divider -= (period + periodadd) * periods; } public final void clock(final int cycles) { if (period < 8) { return; } - divider -= cycles; - while (divider <= 0) { - divider += period + periodadd; - position = ++position % values.length; + divider += cycles; + // note: stay away from negative division to avoid rounding problems + int periods = (divider + period + periodadd) / (period + periodadd); + if (periods < 0) { + periods = 0; // can happen if period or periodadd were made smaller } + position = (position + periods) % values.length; + divider -= (period + periodadd) * periods; } public SquareTimer(final int ctrlen, final int periodadd) { diff --git a/src/com/grapeshot/halfnes/audio/Sunsoft5BSoundChip.java b/src/com/grapeshot/halfnes/audio/Sunsoft5BSoundChip.java index 9a1eed2..72aa944 100644 --- a/src/com/grapeshot/halfnes/audio/Sunsoft5BSoundChip.java +++ b/src/com/grapeshot/halfnes/audio/Sunsoft5BSoundChip.java @@ -52,15 +52,15 @@ public final void write(final int register, final int data) { break; case 8: volume[0] = data & 0xf; - useenvelope[0] = utils.getbit(data, 4); + useenvelope[0] = (data & utils.BIT4) != 0; break; case 9: volume[1] = data & 0xf; - useenvelope[1] = utils.getbit(data, 4); + useenvelope[1] = (data & utils.BIT4) != 0; break; case 10: volume[2] = data & 0xf; - useenvelope[2] = utils.getbit(data, 4); + useenvelope[2] = (data & utils.BIT4) != 0; break; case 13: enval = 15; diff --git a/src/com/grapeshot/halfnes/audio/SwingAudioImpl.java b/src/com/grapeshot/halfnes/audio/SwingAudioImpl.java index 0a73895..8480df0 100644 --- a/src/com/grapeshot/halfnes/audio/SwingAudioImpl.java +++ b/src/com/grapeshot/halfnes/audio/SwingAudioImpl.java @@ -26,7 +26,7 @@ public SwingAudioImpl(final NES nes, final int samplerate) { outputvol = (float) (PrefsSingleton.get().getInt("outputvol", 13107) / 16384.); if (soundEnable) { final int samplesperframe = (int) Math.ceil((samplerate * 2) / 60.); - audiobuf = new byte[samplesperframe * 2]; + audiobuf = new byte[samplesperframe * 15]; try { AudioFormat af = new AudioFormat( samplerate, @@ -37,7 +37,7 @@ public SwingAudioImpl(final NES nes, final int samplerate) { //(works everywhere, afaict, but macs need 44100 sample rate) ); sdl = AudioSystem.getSourceDataLine(af); - sdl.open(af, samplesperframe * 8); //create 4 frame audio buffer + sdl.open(af, samplesperframe * 15); //create 15 frame audio buffer sdl.start(); } catch (LineUnavailableException a) { System.err.println(a); @@ -53,24 +53,12 @@ public SwingAudioImpl(final NES nes, final int samplerate) { public final void flushFrame(final boolean waitIfBufferFull) { if (soundEnable) { - -// if (sdl.available() == sdl.getBufferSize()) { -// System.err.println("Audio is underrun"); -// } - if (sdl.available() < bufptr) { -// System.err.println("Audio is blocking"); - if (waitIfBufferFull) { - - //write to audio buffer and don't worry if it blocks - sdl.write(audiobuf, 0, bufptr); - } - //else don't bother to write if the buffer is full - } else { + if (sdl.available() >= bufptr || waitIfBufferFull) { + //write to audio buffer and don't worry if it blocks sdl.write(audiobuf, 0, bufptr); } } bufptr = 0; - } int dckiller = 0; diff --git a/src/com/grapeshot/halfnes/audio/TriangleTimer.java b/src/com/grapeshot/halfnes/audio/TriangleTimer.java index 4652122..6c5d59f 100644 --- a/src/com/grapeshot/halfnes/audio/TriangleTimer.java +++ b/src/com/grapeshot/halfnes/audio/TriangleTimer.java @@ -14,7 +14,7 @@ public class TriangleTimer extends Timer { private final static int periodadd = 1; private final static int[] triangle = {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,}; + 5, 4, 3, 2, 1, 0}; public TriangleTimer() { period = 0; @@ -31,26 +31,28 @@ public final void clock() { if (period == 0) { return; } - --divider; - while (divider <= 0) { - divider += period + periodadd; - position = (++position & 31); + ++divider; + // note: stay away from negative division to avoid rounding problems + int periods = (divider + period + periodadd) / (period + periodadd); + if (periods < 0) { + periods = 0; // can happen if period or periodadd were made smaller } + position = (position + periods) & 0x1F; + divider -= (period + periodadd) * periods; } public final void clock(final int cycles) { if (period == 0) { return; } - divider -= cycles; - while (divider <= 0) { - divider += period + periodadd; - ++position; - position &= 31; - if (position > 31) { - System.err.println("how"); - } + divider += cycles; + // note: stay away from negative division to avoid rounding problems + int periods = (divider + period + periodadd) / (period + periodadd); + if (periods < 0) { + periods = 0; // can happen if period or periodadd were made smaller } + position = (position + periods) & 0x1F; + divider -= (period + periodadd) * periods; } @Override diff --git a/src/com/grapeshot/halfnes/audio/VRC6SoundChip.java b/src/com/grapeshot/halfnes/audio/VRC6SoundChip.java index 9b8adc8..13f6def 100644 --- a/src/com/grapeshot/halfnes/audio/VRC6SoundChip.java +++ b/src/com/grapeshot/halfnes/audio/VRC6SoundChip.java @@ -29,25 +29,25 @@ public final void write(final int register, final int data) { case 0x9000: volume[0] = data & 0xf; //duty cycle is between 12.5% and 50% unless last bit set and then it's forced to 100% - timers[0].setduty(utils.getbit(data, 7) ? 16 : (((data >> 4) & 7) + 1)); + timers[0].setduty((data & utils.BIT7) != 0 ? 16 : (((data >> 4) & 7) + 1)); break; case 0x9001: timers[0].setperiod((timers[0].getperiod() & 0xf00) + data); break; case 0x9002: timers[0].setperiod((timers[0].getperiod() & 0xff) + ((data & 0xf) << 8)); - enable[0] = utils.getbit(data, 7); + enable[0] = (data & utils.BIT7) != 0; break; case 0xa000: volume[1] = data & 0xf; - timers[1].setduty(utils.getbit(data, 7) ? 16 : (((data >> 4) & 7) + 1)); + timers[1].setduty((data & utils.BIT7) != 0 ? 16 : (((data >> 4) & 7) + 1)); break; case 0xa001: timers[1].setperiod((timers[1].getperiod() & 0xf00) + data); break; case 0xa002: timers[1].setperiod((timers[1].getperiod() & 0xff) + ((data & 0xf) << 8)); - enable[1] = utils.getbit(data, 7); + enable[1] = (data & utils.BIT7) != 0; break; case 0xb000: //saw accumulator @@ -60,7 +60,7 @@ public final void write(final int register, final int data) { case 0xb002: sawdivider &= 0xff; sawdivider += ((data & 0xf) << 8); - enable[2] = utils.getbit(data, 7); + enable[2] = (data & utils.BIT7) != 0; break; } } diff --git a/src/com/grapeshot/halfnes/audio/VRC7SoundChip.java b/src/com/grapeshot/halfnes/audio/VRC7SoundChip.java index 8d3c797..b09be47 100644 --- a/src/com/grapeshot/halfnes/audio/VRC7SoundChip.java +++ b/src/com/grapeshot/halfnes/audio/VRC7SoundChip.java @@ -186,14 +186,14 @@ public final void write(int register, int data) { int m = register - 0x20; octave[m] = (data >> 1) & 7; freq[m] = (freq[m] & 0xff) | ((data & 1) << 8); - if (getbit(data, 4) && !key[m]) { + if ((data & BIT4) != 0 && !key[m]) { //when note is keyed on carenv_state[m] = adsr.CUTOFF; modenv_state[m] = adsr.CUTOFF; // printarray(key); } - key[m] = getbit(data, 4); - sust[m] = getbit(data, 5); + key[m] = (data & BIT4) != 0; + sust[m] = (data & BIT5) != 0; break; case 0x30: case 0x31: @@ -256,7 +256,7 @@ private void operate() { //invaluable info: http://gendev.spritesmind.net/forum/viewtopic.php?t=386 //http://www.smspower.org/maxim/Documents/YM2413ApplicationManual //http://forums.nesdev.com/viewtopic.php?f=3&t=9102 - final double modVibrato = getbit(inst[0], 6) ? vib[fmctr] * (1 << octave[ch]) : 0; + final double modVibrato = (inst[0] & BIT6) != 0 ? vib[fmctr] * (1 << octave[ch]) : 0; final double modFreqMultiplier = multbl[inst[0] & 0xf]; final int modFeedback = (fb == 7) ? 0 : (mod[ch] + oldmodout[ch]) >> (2 + fb); //no i don't know why it adds the last 2 old outputs but MAME @@ -265,20 +265,20 @@ private void operate() { //each of these values is an attenuation value final int modVol = (inst[2] & 0x3f) * 32;//modulator vol final int modEnvelope = ((int) modenv_vol[ch]) << 2; - final int modAM = getbit(inst[0], 7) ? am[amctr] : 0; - final boolean modRectify = getbit(inst[3], 3); + final int modAM = (inst[0] & BIT7) != 0 ? am[amctr] : 0; + final boolean modRectify = (inst[3] & BIT3) != 0; //calculate modulator operator value mod[ch] = operator(mod_f, (int) (modVol + modEnvelope + modks + modAM), modRectify) << 2; oldmodout[ch] = mod[ch]; //now repeat most of that for the carrier - final double carVibrato = getbit(inst[1], 6) ? vib[fmctr] * (freq[ch] << octave[ch]) / 512. : 0; + final double carVibrato = (inst[1] & BIT6) != 0 ? vib[fmctr] * (freq[ch] << octave[ch]) / 512. : 0; final double carFreqMultiplier = multbl[inst[1] & 0xf]; final int carFeedback = (mod[ch] + oldmodout[ch]) >> 1; //inaccurately named final int car_f = carFeedback + (int) (carVibrato + carFreqMultiplier * phase[ch]); final int carVol = vol[ch] * 128; //4 bits for carrier vol not 6 final int carEnvelope = ((int) carenv_vol[ch]) << 2; - final int carAM = getbit(inst[1], 7) ? am[amctr] : 0; - final boolean carRectify = getbit(inst[3], 4); + final int carAM = (inst[1] & BIT7) != 0 ? am[amctr] : 0; + final boolean carRectify = (inst[3] & BIT4) != 0; out[ch] = operator(car_f, (int) (carVol + carEnvelope + carks + carAM), carRectify) << 3; outputSample(); } @@ -343,7 +343,7 @@ public final int getval() { final private static int MAXVOL = 0; private void setenvelope(final int[] instrument, final adsr[] state, final double[] vol, final int ch, final boolean isCarrier) { - final boolean keyscaleRate = getbit(instrument[(isCarrier ? 1 : 0)], 4); + final boolean keyscaleRate = (instrument[(isCarrier ? 1 : 0)] & BIT4) != 0; final int ksrShift = keyscaleRate ? octave[ch] << 1 : octave[ch] >> 1; //^ the key scaling bit (java should really have unions, this is such a mess) /*TODO: fix all of this. Most of these constants were calculated from a @@ -412,7 +412,7 @@ private void setenvelope(final int[] instrument, final adsr[] state, final doubl vol[ch] += .005; } } else if (vol[ch] < ZEROVOL) { - if (getbit(instrument[isCarrier ? 1 : 0], 5)) { + if ((instrument[isCarrier ? 1 : 0] & BIT5) != 0) { //sustain on, don't decay until keyed if (!key[ch]) { state[ch] = adsr.SUSTRELEASE; diff --git a/src/com/grapeshot/halfnes/cheats/ActionReplay.java b/src/com/grapeshot/halfnes/cheats/ActionReplay.java index 42541ef..db98961 100644 --- a/src/com/grapeshot/halfnes/cheats/ActionReplay.java +++ b/src/com/grapeshot/halfnes/cheats/ActionReplay.java @@ -53,7 +53,7 @@ public void addMemoryPatch(Patch patch) { * Patches the memory with Pro Action Replay codes. */ public void applyPatches() { - cpuram.setPatches(patches); + cpuram.setPatches(patches.isEmpty() ? null : patches); } /** diff --git a/src/com/grapeshot/halfnes/halfNES.java b/src/com/grapeshot/halfnes/halfNES.java index 3395d17..82a7a08 100644 --- a/src/com/grapeshot/halfnes/halfNES.java +++ b/src/com/grapeshot/halfnes/halfNES.java @@ -1,6 +1,7 @@ package com.grapeshot.halfnes; //HalfNES, Copyright Andrew Hoffman, October 2010 +import com.grapeshot.halfnes.ui.GUIImpl; import java.io.*; import javax.swing.*; @@ -11,14 +12,14 @@ public class halfNES { public static void main(String[] args) throws IOException { try { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); - } catch (Exception e) { - System.err.println("Could not set system look and feel. Meh."); + } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException e) { + System.err.println("Could not set system look and feel. Meh: " + e); } - com.grapeshot.halfnes.NES thing = new com.grapeshot.halfnes.NES(); + NES nes = new NES(new GUIImpl()); if (args == null || args.length < 1 || args[0] == null) { - thing.run(); + nes.run(); } else { - thing.run(args[0]); + nes.run(args[0]); } } diff --git a/src/com/grapeshot/halfnes/mappers/Action52Mapper.java b/src/com/grapeshot/halfnes/mappers/Action52Mapper.java index 5ab690a..64626fc 100644 --- a/src/com/grapeshot/halfnes/mappers/Action52Mapper.java +++ b/src/com/grapeshot/halfnes/mappers/Action52Mapper.java @@ -30,10 +30,10 @@ public final void cartWrite(final int addr, final int data) { ram[addr & 3] = data & 0xf; } else if (addr >= 0x8000) { chrpage = ((addr & 0xf) << 2) + (data & 3); - prgmode = utils.getbit(addr, 5); + prgmode = (addr & utils.BIT5) != 0; prgpage = (addr >> 6) & 0x1f; prgchip = (addr >> 11) & 3; - setmirroring((utils.getbit(addr, 13)) ? MirrorType.H_MIRROR : MirrorType.V_MIRROR); + setmirroring((addr & utils.BIT13) != 0 ? MirrorType.H_MIRROR : MirrorType.V_MIRROR); for (int i = 0; i < 8; ++i) { chr_map[i] = (1024 * (chrpage * 8 + i)) % chrsize; } diff --git a/src/com/grapeshot/halfnes/mappers/AfterburnerMapper.java b/src/com/grapeshot/halfnes/mappers/AfterburnerMapper.java index a4a7423..3c34058 100644 --- a/src/com/grapeshot/halfnes/mappers/AfterburnerMapper.java +++ b/src/com/grapeshot/halfnes/mappers/AfterburnerMapper.java @@ -53,8 +53,8 @@ public final void cartWrite(int addr, int data) { } else if (addr <= 0xdfff) { romnt2 = data | 0x80; } else if (addr <= 0xefff) { - useromnt = utils.getbit(data, 4); - setmirroring(utils.getbit(data, 0) ? MirrorType.H_MIRROR : MirrorType.V_MIRROR); + useromnt = (data & utils.BIT4) != 0; + setmirroring((data & utils.BIT0) != 0 ? MirrorType.H_MIRROR : MirrorType.V_MIRROR); } else if (addr <= 0xffff) { bank = data & 0xf; //remap PRG bank (1st bank switchable, 2nd bank mapped to LAST bank) diff --git a/src/com/grapeshot/halfnes/mappers/AnromMapper.java b/src/com/grapeshot/halfnes/mappers/AnromMapper.java index d512d9d..d632cd8 100644 --- a/src/com/grapeshot/halfnes/mappers/AnromMapper.java +++ b/src/com/grapeshot/halfnes/mappers/AnromMapper.java @@ -29,7 +29,7 @@ public final void cartWrite(final int addr, final int data) { for (int i = 0; i < 32; ++i) { prg_map[i] = (1024 * (i + (32 * (data & 15)))) & (prgsize - 1); } - setmirroring(utils.getbit(data, 4) ? MirrorType.SS_MIRROR1 : MirrorType.SS_MIRROR0); + setmirroring((data & utils.BIT4) != 0 ? MirrorType.SS_MIRROR1 : MirrorType.SS_MIRROR0); } } diff --git a/src/com/grapeshot/halfnes/mappers/CaltronMapper.java b/src/com/grapeshot/halfnes/mappers/CaltronMapper.java index ffcd770..084579f 100644 --- a/src/com/grapeshot/halfnes/mappers/CaltronMapper.java +++ b/src/com/grapeshot/halfnes/mappers/CaltronMapper.java @@ -28,7 +28,7 @@ public final void cartWrite(final int addr, final int data) { prg_map[i] = (1024 * (i + 32 * (addr & 7))) & (prgsize - 1); } - setmirroring(utils.getbit(addr, 5) ? MirrorType.H_MIRROR : MirrorType.V_MIRROR); + setmirroring((addr & utils.BIT5) != 0 ? MirrorType.H_MIRROR : MirrorType.V_MIRROR); } else if (addr >= 0x8000 && addr <= 0xFFFF) { if ((reg & 4) != 0) { //remap CHR bank diff --git a/src/com/grapeshot/halfnes/mappers/CodemastersMapper.java b/src/com/grapeshot/halfnes/mappers/CodemastersMapper.java index 20bf244..7dc9ec7 100644 --- a/src/com/grapeshot/halfnes/mappers/CodemastersMapper.java +++ b/src/com/grapeshot/halfnes/mappers/CodemastersMapper.java @@ -41,7 +41,7 @@ public final void cartWrite(int addr, int data) { if (crc == 0x1BC686A8) { //fire hawk is only game with mapper controlled mirroring //micro machines glitches hard if this is on - setmirroring((utils.getbit(data, 4) ? MirrorType.SS_MIRROR1 : MirrorType.SS_MIRROR0)); + setmirroring(((data & utils.BIT4) != 0 ? MirrorType.SS_MIRROR1 : MirrorType.SS_MIRROR0)); } } else { bank = data & 0xf; diff --git a/src/com/grapeshot/halfnes/mappers/FME7Mapper.java b/src/com/grapeshot/halfnes/mappers/FME7Mapper.java index f5ccaa9..d5d5835 100644 --- a/src/com/grapeshot/halfnes/mappers/FME7Mapper.java +++ b/src/com/grapeshot/halfnes/mappers/FME7Mapper.java @@ -93,8 +93,8 @@ public final void cartWrite(final int addr, final int data) { setbanks(); break; case 8: - ramEnable = utils.getbit(data, 7); - ramSelect = utils.getbit(data, 6); + ramEnable = (data & utils.BIT7) != 0; + ramSelect = (data & utils.BIT6) != 0; prgbanks[0] = data & 0x3f; setbanks(); break; @@ -123,8 +123,8 @@ public final void cartWrite(final int addr, final int data) { } case 0xd: //irq - let's put this in and hope it works - irqclock = utils.getbit(data, 7); - irqenabled = utils.getbit(data, 0); + irqclock = (data & utils.BIT7) != 0; + irqenabled = (data & utils.BIT0) != 0; if (!irqenabled) { interrupted = false; --cpu.interrupt; diff --git a/src/com/grapeshot/halfnes/mappers/IremH3001Mapper.java b/src/com/grapeshot/halfnes/mappers/IremH3001Mapper.java index 9f8a112..99c51fb 100644 --- a/src/com/grapeshot/halfnes/mappers/IremH3001Mapper.java +++ b/src/com/grapeshot/halfnes/mappers/IremH3001Mapper.java @@ -32,9 +32,9 @@ public final void cartWrite(int addr, int data) { prg_map[i] = (1024 * (i + (data * 8))) & (prgsize - 1); } } else if (addr == 0x9001) { //Mirroring - setmirroring(utils.getbit(data, 7) ? MirrorType.H_MIRROR : MirrorType.V_MIRROR); + setmirroring((data & utils.BIT7) != 0 ? MirrorType.H_MIRROR : MirrorType.V_MIRROR); } else if (addr == 0x9003) { //IRQ Enable - irqenable = utils.getbit(data, 7); + irqenable = (data & utils.BIT7) != 0; if (interrupted) { --cpu.interrupt; interrupted = false; diff --git a/src/com/grapeshot/halfnes/mappers/MMC1Mapper.java b/src/com/grapeshot/halfnes/mappers/MMC1Mapper.java index 50a7a3c..6974267 100644 --- a/src/com/grapeshot/halfnes/mappers/MMC1Mapper.java +++ b/src/com/grapeshot/halfnes/mappers/MMC1Mapper.java @@ -43,7 +43,7 @@ public final void cartWrite(final int addr, final int data) { //and this is extremely ugly/likely to break //but is needed to prevent the Bill+Ted fix from breaking Dr Mario intro. cpucycleprev = cpu.clocks; - if (utils.getbit(data, 7)) { + if ((data & utils.BIT7) != 0) { // reset shift register mmc1shift = 0; mmc1latch = 0; @@ -85,7 +85,7 @@ public final void cartWrite(final int addr, final int data) { //SOROM boards use the high bit of CHR to switch between 1st and last //256k of the PRG ROM mmc1chr0 &= 0xf; - soromlatch = utils.getbit(mmc1shift, 4); + soromlatch = (mmc1shift & utils.BIT4) != 0; } } else if (addr >= 0xc000 && addr <= 0xdfff) { // mmc1chr1 @@ -107,7 +107,7 @@ public final void cartWrite(final int addr, final int data) { private void setbanks() { // chr bank 0 - if (utils.getbit(mmc1ctrl, 4)) { + if ((mmc1ctrl & utils.BIT4) != 0) { // 4k bank mode for (int i = 0; i < 4; ++i) { chr_map[i] = (1024 * (i + 4 * mmc1chr0)) % chrsize; @@ -123,14 +123,14 @@ private void setbanks() { } // prg bank - if (!utils.getbit(mmc1ctrl, 3)) { + if ((mmc1ctrl & utils.BIT3) == 0) { // 32k switch // ignore low bank bit for (int i = 0; i < 32; ++i) { prg_map[i] = (1024 * i + 32768 * (mmc1prg >> 1)) % prgsize; } - } else if (!utils.getbit(mmc1ctrl, 2)) { + } else if ((mmc1ctrl & utils.BIT2) == 0) { // fix 1st bank, 16k switch 2nd bank for (int i = 0; i < 16; ++i) { prg_map[i] = (1024 * i); diff --git a/src/com/grapeshot/halfnes/mappers/MMC2Mapper.java b/src/com/grapeshot/halfnes/mappers/MMC2Mapper.java index 566c1cc..5b4e887 100644 --- a/src/com/grapeshot/halfnes/mappers/MMC2Mapper.java +++ b/src/com/grapeshot/halfnes/mappers/MMC2Mapper.java @@ -56,14 +56,14 @@ public final void cartWrite(int addr, int data) { chrbankR2 = data & 0x1f; setupPPUBanks(); } else if (addr >= 0xf000 && addr <= 0xffff) { - setmirroring((utils.getbit(data, 0)) ? MirrorType.H_MIRROR : MirrorType.V_MIRROR); + setmirroring(((data & utils.BIT0) != 0) ? MirrorType.H_MIRROR : MirrorType.V_MIRROR); } } @Override public int ppuRead(final int addr) { int retval = super.ppuRead(addr); - if (utils.getbit(addr, 3)) { + if ((addr & utils.BIT3) != 0) { //latch fires after 2nd read from pattern table //A3 will be on for 2nd read b/c it's tile low bytes switch (addr >> 4) { diff --git a/src/com/grapeshot/halfnes/mappers/MMC3Mapper.java b/src/com/grapeshot/halfnes/mappers/MMC3Mapper.java index 3de7cea..4ce46e7 100644 --- a/src/com/grapeshot/halfnes/mappers/MMC3Mapper.java +++ b/src/com/grapeshot/halfnes/mappers/MMC3Mapper.java @@ -46,7 +46,7 @@ public final void cartWrite(int addr, int data) { } //bankswitches here //different register for even/odd writes - if (utils.getbit(addr, 0)) { + if ((addr & utils.BIT0) != 0) { //odd registers if ((addr >= 0x8000) && (addr <= 0x9fff)) { //bank change @@ -65,7 +65,7 @@ public final void cartWrite(int addr, int data) { } } else if ((addr >= 0xA000) && (addr <= 0xbfff)) { //prg ram write protect - //cpuram.setPrgRAMEnable(!utils.getbit(data, 7)); + //cpuram.setPrgRAMEnable((data & utils.BIT7) == 0); } else if ((addr >= 0xc000) && (addr <= 0xdfff)) { //any value here reloads irq counter irqreload = true; @@ -79,10 +79,10 @@ public final void cartWrite(int addr, int data) { if ((addr >= 0x8000) && (addr <= 0x9fff)) { //bank select whichbank = data & 7; - prgconfig = utils.getbit(data, 6); + prgconfig = (data & utils.BIT6) != 0; //if bit is false, 8000-9fff swappable and c000-dfff fixed to 2nd to last bank //if bit is true, c000-dfff swappable and 8000-9fff fixed to 2nd to last bank - chrconfig = utils.getbit(data, 7); + chrconfig = (data & utils.BIT7) != 0; //if false: 2 2k banks @ 0000-0fff, 4 1k banks in 1000-1fff //if true: 4 1k banks @ 0000-0fff, 2 2k banks @ 1000-1fff setupchr(); @@ -90,7 +90,7 @@ public final void cartWrite(int addr, int data) { } else if ((addr >= 0xA000) && (addr <= 0xbfff)) { //mirroring setup if (scrolltype != MirrorType.FOUR_SCREEN_MIRROR) { - setmirroring(utils.getbit(data, 0) ? MirrorType.H_MIRROR : MirrorType.V_MIRROR); + setmirroring((data & utils.BIT0) != 0 ? MirrorType.H_MIRROR : MirrorType.V_MIRROR); } } else if ((addr >= 0xc000) && (addr <= 0xdfff)) { //value written here used to reload irq counter _@ end of scanline_ diff --git a/src/com/grapeshot/halfnes/mappers/MMC4Mapper.java b/src/com/grapeshot/halfnes/mappers/MMC4Mapper.java index f5fcefa..ef8fa34 100644 --- a/src/com/grapeshot/halfnes/mappers/MMC4Mapper.java +++ b/src/com/grapeshot/halfnes/mappers/MMC4Mapper.java @@ -47,14 +47,14 @@ public final void cartWrite(int addr, int data) { chrbankR2 = data & 0x1f; setupPPUBanks(); } else if (addr >= 0xf000 && addr <= 0xffff) { - setmirroring((utils.getbit(data, 0)) ? MirrorType.H_MIRROR : MirrorType.V_MIRROR); + setmirroring(((data & utils.BIT0) != 0) ? MirrorType.H_MIRROR : MirrorType.V_MIRROR); } } @Override public int ppuRead(final int addr) { int retval = super.ppuRead(addr); - if (utils.getbit(addr, 3)) { + if ((addr & utils.BIT3) != 0) { //latch fires after 2nd read from pattern table //A3 will be on for 2nd read b/c it's tile low bytes switch (addr >> 4) { diff --git a/src/com/grapeshot/halfnes/mappers/MMC5Mapper.java b/src/com/grapeshot/halfnes/mappers/MMC5Mapper.java index f1e830d..3d5d4e0 100644 --- a/src/com/grapeshot/halfnes/mappers/MMC5Mapper.java +++ b/src/com/grapeshot/halfnes/mappers/MMC5Mapper.java @@ -114,19 +114,19 @@ public final void cartWrite(final int addr, final int data) { case 0x5114: //prg reg 1 prgregs[0] = data & 0x7f; - romHere[0] = utils.getbit(data, 7); + romHere[0] = (data & utils.BIT7) != 0; setupPRG(); break; case 0x5115: //prg reg 2 prgregs[1] = data & 0x7f; - romHere[1] = utils.getbit(data, 7); + romHere[1] = (data & utils.BIT7) != 0; setupPRG(); break; case 0x5116: //prg reg 3 prgregs[2] = data & 0x7f; - romHere[2] = utils.getbit(data, 7); + romHere[2] = (data & utils.BIT7) != 0; setupPRG(); break; case 0x5117: @@ -190,7 +190,7 @@ public final void cartWrite(final int addr, final int data) { break; case 0x5200: //splitscreen control - if (utils.getbit(data, 7)) { + if ((data & utils.BIT7) != 0) { System.err.println("Split screen mode not supported yet"); } break; @@ -204,7 +204,7 @@ public final void cartWrite(final int addr, final int data) { break; case 0x5204: //irq control - scanctrEnable = utils.getbit(data, 7); + scanctrEnable = (data & utils.BIT7) != 0; break; case 0x5205: multiplier1 = data; diff --git a/src/com/grapeshot/halfnes/mappers/Mapper.java b/src/com/grapeshot/halfnes/mappers/Mapper.java index 3208b4f..332cb00 100644 --- a/src/com/grapeshot/halfnes/mappers/Mapper.java +++ b/src/com/grapeshot/halfnes/mappers/Mapper.java @@ -2,11 +2,39 @@ //HalfNES, Copyright Andrew Hoffman, October 2010 import com.grapeshot.halfnes.*; +import java.lang.reflect.Field; +import java.security.AccessController; +import java.security.PrivilegedAction; import java.util.Arrays; import java.util.zip.CRC32; +import sun.misc.Unsafe; public abstract class Mapper { + static final Unsafe unsafe; + static final int INTEGER_ARRAY_BASE_OFFSET; + + static { + unsafe = (Unsafe) AccessController.doPrivileged( + new PrivilegedAction() { + @Override + public Object run() { + try { + Field f = Unsafe.class.getDeclaredField("theUnsafe"); + f.setAccessible(true); + return f.get(null); + } catch (Exception e) { + throw new Error(); + } + } + }); + + INTEGER_ARRAY_BASE_OFFSET = unsafe.arrayBaseOffset(int[].class); + if (unsafe.arrayIndexScale(int[].class) != 4) { + throw new AssertionError(); + } + } + protected ROMLoader loader; protected int mappertype = 0, prgsize = 0, prgoff = 0, chroff = 0, chrsize = 0; public CPU cpu; @@ -110,8 +138,10 @@ public int cartRead(final int addr) { // but some mappers have different so override for those if (addr >= 0x8000) { return prg[prg_map[((addr & 0x7fff)) >> 10] + (addr & 1023)]; +// return unsafe.getInt(prg, INTEGER_ARRAY_BASE_OFFSET + (addr - 0x8000) * 4); } else if (addr >= 0x6000 && hasprgram) { return prgram[addr & 0x1fff]; +// return unsafe.getInt(prgram, INTEGER_ARRAY_BASE_OFFSET + (addr & 0x1fff) * 4); } return addr >> 8; //open bus } diff --git a/src/com/grapeshot/halfnes/mappers/Mapper112.java b/src/com/grapeshot/halfnes/mappers/Mapper112.java index 7370700..01a6a8c 100644 --- a/src/com/grapeshot/halfnes/mappers/Mapper112.java +++ b/src/com/grapeshot/halfnes/mappers/Mapper112.java @@ -48,7 +48,7 @@ public final void cartWrite(int addr, int data) { //bank select whichbank = data & 7; } else if (addr == 0xE000) { - setmirroring(utils.getbit(data, 0) ? MirrorType.H_MIRROR : MirrorType.V_MIRROR); + setmirroring((data & utils.BIT0) != 0 ? MirrorType.H_MIRROR : MirrorType.V_MIRROR); } } diff --git a/src/com/grapeshot/halfnes/mappers/Mapper119.java b/src/com/grapeshot/halfnes/mappers/Mapper119.java index 3f788ef..0915eff 100644 --- a/src/com/grapeshot/halfnes/mappers/Mapper119.java +++ b/src/com/grapeshot/halfnes/mappers/Mapper119.java @@ -78,7 +78,7 @@ public final void cartWrite(int addr, int data) { } //bankswitches here //different register for even/odd writes - if (utils.getbit(addr, 0)) { + if ((addr & utils.BIT0) != 0) { //odd registers if ((addr >= 0x8000) && (addr <= 0x9fff)) { //bank change @@ -97,7 +97,7 @@ public final void cartWrite(int addr, int data) { } //} else if ((addr >= 0xA000) && (addr <= 0xbfff)) { //prg ram write protect - //cpuram.setPrgRAMEnable(!utils.getbit(data, 7)); + //cpuram.setPrgRAMEnable((data & utils.BIT7) == 0); } else if ((addr >= 0xc000) && (addr <= 0xdfff)) { //any value here reloads irq counter irqreload = true; @@ -111,10 +111,10 @@ public final void cartWrite(int addr, int data) { if ((addr >= 0x8000) && (addr <= 0x9fff)) { //bank select whichbank = data & 7; - prgconfig = utils.getbit(data, 6); + prgconfig = (data & utils.BIT6) != 0; //if bit is false, 8000-9fff swappable and c000-dfff fixed to 2nd to last bank //if bit is true, c000-dfff swappable and 8000-9fff fixed to 2nd to last bank - chrconfig = utils.getbit(data, 7); + chrconfig = (data & utils.BIT7) != 0; //if false: 2 2k banks @ 0000-0fff, 4 1k banks in 1000-1fff //if true: 4 1k banks @ 0000-0fff, 2 2k banks @ 1000-1fff setupchr(); @@ -122,7 +122,7 @@ public final void cartWrite(int addr, int data) { } else if ((addr >= 0xA000) && (addr <= 0xbfff)) { //mirroring setup if (scrolltype != MirrorType.FOUR_SCREEN_MIRROR) { - setmirroring(utils.getbit(data, 0) ? MirrorType.H_MIRROR : MirrorType.V_MIRROR); + setmirroring((data & utils.BIT0) != 0 ? MirrorType.H_MIRROR : MirrorType.V_MIRROR); } } else if ((addr >= 0xc000) && (addr <= 0xdfff)) { //value written here used to reload irq counter _@ end of scanline_ diff --git a/src/com/grapeshot/halfnes/mappers/Mapper15.java b/src/com/grapeshot/halfnes/mappers/Mapper15.java index 59c8f85..3a7614f 100644 --- a/src/com/grapeshot/halfnes/mappers/Mapper15.java +++ b/src/com/grapeshot/halfnes/mappers/Mapper15.java @@ -25,7 +25,7 @@ public final void cartWrite(int addr, int data) { int prgbank = (data << 1) & 0xFE; int prgflip = data >> 7; - setmirroring(utils.getbit(data, 6) ? MirrorType.H_MIRROR : MirrorType.V_MIRROR); + setmirroring((data & utils.BIT6) != 0 ? MirrorType.H_MIRROR : MirrorType.V_MIRROR); switch (addr & 0xFFF) { diff --git a/src/com/grapeshot/halfnes/mappers/Mapper152.java b/src/com/grapeshot/halfnes/mappers/Mapper152.java index a4fa226..54f653f 100644 --- a/src/com/grapeshot/halfnes/mappers/Mapper152.java +++ b/src/com/grapeshot/halfnes/mappers/Mapper152.java @@ -39,6 +39,6 @@ public final void cartWrite(final int addr, final int data) { prg_map[i] = (1024 * (i + 16 * prgselect)) & (prgsize - 1); } - setmirroring(utils.getbit(data, 7) ? MirrorType.SS_MIRROR1 : MirrorType.SS_MIRROR0); + setmirroring((data & utils.BIT7) != 0 ? MirrorType.SS_MIRROR1 : MirrorType.SS_MIRROR0); } } diff --git a/src/com/grapeshot/halfnes/mappers/Mapper182.java b/src/com/grapeshot/halfnes/mappers/Mapper182.java index 4a08558..335c9e8 100644 --- a/src/com/grapeshot/halfnes/mappers/Mapper182.java +++ b/src/com/grapeshot/halfnes/mappers/Mapper182.java @@ -39,14 +39,14 @@ public final void cartWrite(int addr, int data) { } //bankswitches here //different register for even/odd writes - if (utils.getbit(addr, 0)) { + if ((addr & utils.BIT0) != 0) { //odd registers if ((addr >= 0x8000) && (addr <= 0x9fff)) { //mirroring setup - setmirroring(utils.getbit(data, 0) ? MirrorType.H_MIRROR : MirrorType.V_MIRROR); + setmirroring((data & utils.BIT0) != 0 ? MirrorType.H_MIRROR : MirrorType.V_MIRROR); } else if ((addr >= 0xA000) && (addr <= 0xBFFF)) { //prg ram write protect - //cpuram.setPrgRAMEnable(!utils.getbit(data, 7)); + //cpuram.setPrgRAMEnable((data & utils.BIT7) == 0); } else if ((addr >= 0xC000) && (addr <= 0xDFFF)) { //any value here reloads irq counter _@ end of scanline_ irqreload = true; @@ -60,10 +60,10 @@ public final void cartWrite(int addr, int data) { if ((addr >= 0xA000) && (addr <= 0xBFFF)) { //bank select whichbank = data & 7; - prgconfig = utils.getbit(data, 4); + prgconfig = (data & utils.BIT4) != 0; //if bit is false, 8000-9fff swappable and c000-dfff fixed to 2nd to last bank //if bit is true, c000-dfff swappable and 8000-9fff fixed to 2nd to last bank - chrconfig = utils.getbit(data, 5); + chrconfig = (data & utils.BIT5) != 0; //if false: 2 2k banks @ 0000-0fff, 4 1k banks in 1000-1fff //if true: 4 1k banks @ 0000-0fff, 2 2k banks @ 1000-1fff setupchr(); diff --git a/src/com/grapeshot/halfnes/mappers/Mapper200.java b/src/com/grapeshot/halfnes/mappers/Mapper200.java index 5a68ce9..feda936 100644 --- a/src/com/grapeshot/halfnes/mappers/Mapper200.java +++ b/src/com/grapeshot/halfnes/mappers/Mapper200.java @@ -36,7 +36,7 @@ public final void cartWrite(final int addr, final int data) { int reg = addr & 7; - setmirroring(utils.getbit(data, 3) ? MirrorType.H_MIRROR : MirrorType.V_MIRROR); + setmirroring((data & utils.BIT3) != 0 ? MirrorType.H_MIRROR : MirrorType.V_MIRROR); //remap CHR bank for (int i = 0; i < 8; ++i) { diff --git a/src/com/grapeshot/halfnes/mappers/Mapper212.java b/src/com/grapeshot/halfnes/mappers/Mapper212.java index cbc30c8..d67584a 100644 --- a/src/com/grapeshot/halfnes/mappers/Mapper212.java +++ b/src/com/grapeshot/halfnes/mappers/Mapper212.java @@ -39,6 +39,6 @@ public final void cartWrite(final int addr, final int data) { for (int i = 0; i < 8; ++i) { chr_map[i] = (1024 * (i + 8 * addr)) & (chrsize - 1); } - setmirroring(utils.getbit(addr, 4) ? MirrorType.H_MIRROR : MirrorType.V_MIRROR); + setmirroring((addr & utils.BIT4) != 0 ? MirrorType.H_MIRROR : MirrorType.V_MIRROR); } } diff --git a/src/com/grapeshot/halfnes/mappers/Mapper225.java b/src/com/grapeshot/halfnes/mappers/Mapper225.java index ad3dad2..d356d04 100644 --- a/src/com/grapeshot/halfnes/mappers/Mapper225.java +++ b/src/com/grapeshot/halfnes/mappers/Mapper225.java @@ -23,7 +23,7 @@ public final void cartWrite(final int addr, final int data) { return; } - setmirroring(utils.getbit(addr, 0xD) ? MirrorType.H_MIRROR : MirrorType.V_MIRROR); + setmirroring((addr & utils.BIT13) != 0 ? MirrorType.H_MIRROR : MirrorType.V_MIRROR); int bank = addr >> 7 & 0x1F; diff --git a/src/com/grapeshot/halfnes/mappers/Mapper226.java b/src/com/grapeshot/halfnes/mappers/Mapper226.java index a135dd1..4d19580 100644 --- a/src/com/grapeshot/halfnes/mappers/Mapper226.java +++ b/src/com/grapeshot/halfnes/mappers/Mapper226.java @@ -29,7 +29,7 @@ public final void cartWrite(final int addr, final int data) { int bank = ((reg[0] >> 1 & 0x0F) | (reg[0] >> 3 & 0x10) | (reg[1] << 5 & 0x20)); - setmirroring(utils.getbit(reg[0], 6) ? MirrorType.V_MIRROR : MirrorType.H_MIRROR); + setmirroring((reg[0] & utils.BIT6) != 0 ? MirrorType.V_MIRROR : MirrorType.H_MIRROR); if ((reg[0] & 0x20) != 0) { bank = (bank << 1) | (reg[0] & 1); diff --git a/src/com/grapeshot/halfnes/mappers/Mapper229.java b/src/com/grapeshot/halfnes/mappers/Mapper229.java index 979cc99..e972381 100644 --- a/src/com/grapeshot/halfnes/mappers/Mapper229.java +++ b/src/com/grapeshot/halfnes/mappers/Mapper229.java @@ -37,6 +37,6 @@ public final void cartWrite(final int addr, final int data) { prg_map[i + 16] = (1024 * (i + 16 * bank)) & (prgsize - 1); } - setmirroring(utils.getbit(addr, 5) ? MirrorType.H_MIRROR : MirrorType.V_MIRROR); + setmirroring((addr & utils.BIT5) != 0 ? MirrorType.H_MIRROR : MirrorType.V_MIRROR); } } diff --git a/src/com/grapeshot/halfnes/mappers/Mapper231.java b/src/com/grapeshot/halfnes/mappers/Mapper231.java index 201e556..1036853 100644 --- a/src/com/grapeshot/halfnes/mappers/Mapper231.java +++ b/src/com/grapeshot/halfnes/mappers/Mapper231.java @@ -23,7 +23,7 @@ public final void cartWrite(final int addr, final int data) { return; } - setmirroring(utils.getbit(addr, 7) ? MirrorType.H_MIRROR : MirrorType.V_MIRROR); + setmirroring((addr & utils.BIT7) != 0 ? MirrorType.H_MIRROR : MirrorType.V_MIRROR); int prg = addr & 0x1E; diff --git a/src/com/grapeshot/halfnes/mappers/Mapper242.java b/src/com/grapeshot/halfnes/mappers/Mapper242.java index fbff082..788dcc4 100644 --- a/src/com/grapeshot/halfnes/mappers/Mapper242.java +++ b/src/com/grapeshot/halfnes/mappers/Mapper242.java @@ -28,6 +28,6 @@ public final void cartWrite(final int addr, final int data) { prg_map[i] = (1024 * (i + 32 * (addr >> 3))) & (prgsize - 1); } - setmirroring(utils.getbit(addr, 1) ? MirrorType.H_MIRROR : MirrorType.V_MIRROR); + setmirroring((addr & utils.BIT1) != 0 ? MirrorType.H_MIRROR : MirrorType.V_MIRROR); } } diff --git a/src/com/grapeshot/halfnes/mappers/Mapper33.java b/src/com/grapeshot/halfnes/mappers/Mapper33.java index 903b977..39be964 100644 --- a/src/com/grapeshot/halfnes/mappers/Mapper33.java +++ b/src/com/grapeshot/halfnes/mappers/Mapper33.java @@ -32,7 +32,7 @@ public final void cartWrite(final int addr, final int data) { switch (addr & 3) { case 0: prgbank0 = data; - setmirroring(utils.getbit(data, 6) ? MirrorType.H_MIRROR : MirrorType.V_MIRROR); + setmirroring((data & utils.BIT6) != 0 ? MirrorType.H_MIRROR : MirrorType.V_MIRROR); setbanks(); break; case 1: diff --git a/src/com/grapeshot/halfnes/mappers/Mapper47.java b/src/com/grapeshot/halfnes/mappers/Mapper47.java index e4b6955..828a835 100644 --- a/src/com/grapeshot/halfnes/mappers/Mapper47.java +++ b/src/com/grapeshot/halfnes/mappers/Mapper47.java @@ -61,7 +61,7 @@ public void cartWrite(int addr, int data) { } } //different register for even/odd writes - if (utils.getbit(addr, 0)) { + if ((addr & utils.BIT0) != 0) { //odd registers if ((addr >= 0x8000) && (addr <= 0x9fff)) { //bank change @@ -80,7 +80,7 @@ public void cartWrite(int addr, int data) { } } else if ((addr >= 0xA000) && (addr <= 0xbfff)) { //prg ram write protect - //cpuram.setPrgRAMEnable(!utils.getbit(data, 7)); + //cpuram.setPrgRAMEnable((data & utils.BIT7) == 0); } else if ((addr >= 0xc000) && (addr <= 0xdfff)) { //any value here reloads irq counter irqreload = true; @@ -94,10 +94,10 @@ public void cartWrite(int addr, int data) { if ((addr >= 0x8000) && (addr <= 0x9fff)) { //bank select whichbank = data & 7; - prgconfig = utils.getbit(data, 6); + prgconfig = (data & utils.BIT6) != 0; //if bit is false, 8000-9fff swappable and c000-dfff fixed to 2nd to last bank //if bit is true, c000-dfff swappable and 8000-9fff fixed to 2nd to last bank - chrconfig = utils.getbit(data, 7); + chrconfig = (data & utils.BIT7) != 0; //if false: 2 2k banks @ 0000-0fff, 4 1k banks in 1000-1fff //if true: 4 1k banks @ 0000-0fff, 2 2k banks @ 1000-1fff setupchr(); @@ -105,7 +105,7 @@ public void cartWrite(int addr, int data) { } else if ((addr >= 0xA000) && (addr <= 0xbfff)) { //mirroring setup if (scrolltype != MirrorType.FOUR_SCREEN_MIRROR) { - setmirroring(utils.getbit(data, 0) ? MirrorType.H_MIRROR : MirrorType.V_MIRROR); + setmirroring((data & utils.BIT0) != 0 ? MirrorType.H_MIRROR : MirrorType.V_MIRROR); } } else if ((addr >= 0xc000) && (addr <= 0xdfff)) { //value written here used to reload irq counter _@ end of scanline_ diff --git a/src/com/grapeshot/halfnes/mappers/Mapper48.java b/src/com/grapeshot/halfnes/mappers/Mapper48.java index a409e5e..1860405 100644 --- a/src/com/grapeshot/halfnes/mappers/Mapper48.java +++ b/src/com/grapeshot/halfnes/mappers/Mapper48.java @@ -101,7 +101,7 @@ public final void cartWrite(final int addr, final int data) { } else if (addr <= 0xFFFF) { switch (addr & 3) { case 0: - setmirroring(utils.getbit(data, 6) ? MirrorType.H_MIRROR : MirrorType.V_MIRROR); + setmirroring((data & utils.BIT6) != 0 ? MirrorType.H_MIRROR : MirrorType.V_MIRROR); break; } } diff --git a/src/com/grapeshot/halfnes/mappers/Mapper58.java b/src/com/grapeshot/halfnes/mappers/Mapper58.java index 70d46ca..ee60bab 100644 --- a/src/com/grapeshot/halfnes/mappers/Mapper58.java +++ b/src/com/grapeshot/halfnes/mappers/Mapper58.java @@ -28,7 +28,7 @@ public final void cartWrite(final int addr, final int data) { return; } - setmirroring(utils.getbit(addr, 7) ? MirrorType.H_MIRROR : MirrorType.V_MIRROR); + setmirroring((addr & utils.BIT7) != 0 ? MirrorType.H_MIRROR : MirrorType.V_MIRROR); //remap CHR bank for (int i = 0; i < 8; ++i) { diff --git a/src/com/grapeshot/halfnes/mappers/Mapper61.java b/src/com/grapeshot/halfnes/mappers/Mapper61.java index 8d885dc..fe74613 100644 --- a/src/com/grapeshot/halfnes/mappers/Mapper61.java +++ b/src/com/grapeshot/halfnes/mappers/Mapper61.java @@ -46,6 +46,6 @@ public final void cartWrite(final int addr, final int data) { } } - setmirroring(utils.getbit(addr, 7) ? MirrorType.H_MIRROR : MirrorType.V_MIRROR); + setmirroring((addr & utils.BIT7) != 0 ? MirrorType.H_MIRROR : MirrorType.V_MIRROR); } } \ No newline at end of file diff --git a/src/com/grapeshot/halfnes/mappers/Mapper62.java b/src/com/grapeshot/halfnes/mappers/Mapper62.java index 4e7e54f..84a072a 100644 --- a/src/com/grapeshot/halfnes/mappers/Mapper62.java +++ b/src/com/grapeshot/halfnes/mappers/Mapper62.java @@ -25,7 +25,7 @@ public final void cartWrite(final int addr, final int data) { super.cartWrite(addr, data); return; } - prg_mode = utils.getbit(addr, 5); + prg_mode = (addr & utils.BIT5) != 0; prgselect = (addr & 0x40) | ((addr >> 8) & 0x3F); chrselect = (addr << 2) | (data & 3); @@ -47,6 +47,6 @@ public final void cartWrite(final int addr, final int data) { } } - setmirroring(utils.getbit(addr, 7) ? MirrorType.H_MIRROR : MirrorType.V_MIRROR); + setmirroring((addr & utils.BIT7) != 0 ? MirrorType.H_MIRROR : MirrorType.V_MIRROR); } } diff --git a/src/com/grapeshot/halfnes/mappers/Mapper78.java b/src/com/grapeshot/halfnes/mappers/Mapper78.java index 438fd82..6564c95 100644 --- a/src/com/grapeshot/halfnes/mappers/Mapper78.java +++ b/src/com/grapeshot/halfnes/mappers/Mapper78.java @@ -35,10 +35,10 @@ public final void cartWrite(final int addr, final int data) { int chrselect = (data >> 4) & 0xf; if (crc == 0x42392440) //Cosmo Carrier { - setmirroring(utils.getbit(data, 3) ? MirrorType.SS_MIRROR1 : MirrorType.SS_MIRROR0); + setmirroring((data & utils.BIT3) != 0 ? MirrorType.SS_MIRROR1 : MirrorType.SS_MIRROR0); } else //Holy Diver { - setmirroring(utils.getbit(data, 3) ? MirrorType.V_MIRROR : MirrorType.H_MIRROR); + setmirroring((data & utils.BIT3) != 0 ? MirrorType.V_MIRROR : MirrorType.H_MIRROR); } //remap CHR bank diff --git a/src/com/grapeshot/halfnes/mappers/NINA_003_006_Mapper.java b/src/com/grapeshot/halfnes/mappers/NINA_003_006_Mapper.java index 4fad73e..4905771 100644 --- a/src/com/grapeshot/halfnes/mappers/NINA_003_006_Mapper.java +++ b/src/com/grapeshot/halfnes/mappers/NINA_003_006_Mapper.java @@ -46,7 +46,7 @@ public final void cartWrite(final int addr, final int data) { } if (m113) { - setmirroring(utils.getbit(data, 7) ? MirrorType.V_MIRROR : MirrorType.H_MIRROR); + setmirroring((data & utils.BIT7) != 0 ? MirrorType.V_MIRROR : MirrorType.H_MIRROR); //remap CHR bank for (int i = 0; i < 8; ++i) { diff --git a/src/com/grapeshot/halfnes/mappers/NSFMapper.java b/src/com/grapeshot/halfnes/mappers/NSFMapper.java index 31d1a39..7bc770f 100644 --- a/src/com/grapeshot/halfnes/mappers/NSFMapper.java +++ b/src/com/grapeshot/halfnes/mappers/NSFMapper.java @@ -159,7 +159,7 @@ public void reset() { @Override public void cartWrite(final int addr, final int data) { if (n163 && addr == 0xF800) { - n163autoincrement = utils.getbit(data, 7); + n163autoincrement = (data & utils.BIT7) != 0; n163soundAddr = data & 0x7f; } else if (n163 && addr == 0x4800) { n163Audio.write(n163soundAddr, data); @@ -371,7 +371,7 @@ private void setBanks() { } private void setSoundChip() { - if (utils.getbit(sndchip, 0)) { + if ((sndchip & utils.BIT0) != 0) { //VRC6 audio vrc6 = true; vrc6Audio = new VRC6SoundChip(); @@ -379,7 +379,7 @@ private void setSoundChip() { cpuram.apu.addExpnSound(vrc6Audio); } } - if (utils.getbit(sndchip, 1)) { + if ((sndchip & utils.BIT1) != 0) { //VRC7 audio vrc7 = true; vrc7Audio = new VRC7SoundChip(); @@ -387,10 +387,10 @@ private void setSoundChip() { cpuram.apu.addExpnSound(vrc7Audio); } } - if (utils.getbit(sndchip, 2)) { + if ((sndchip & utils.BIT2) != 0) { //FDS audio, not yet implemented } - if (utils.getbit(sndchip, 3)) { + if ((sndchip & utils.BIT3) != 0) { //MMC5 audio mmc5 = true; mmc5Audio = new MMC5SoundChip(); @@ -398,7 +398,7 @@ private void setSoundChip() { cpuram.apu.addExpnSound(mmc5Audio); } } - if (utils.getbit(sndchip, 4)) { + if ((sndchip & utils.BIT4) != 0) { //Namco 163 audio n163 = true; n163Audio = new Namco163SoundChip(); @@ -406,7 +406,7 @@ private void setSoundChip() { cpuram.apu.addExpnSound(n163Audio); } } - if (utils.getbit(sndchip, 5)) { + if ((sndchip & utils.BIT5) != 0) { //Sunsoft 5B audio s5b = true; s5bAudio = new Sunsoft5BSoundChip(); diff --git a/src/com/grapeshot/halfnes/mappers/NamcoMapper.java b/src/com/grapeshot/halfnes/mappers/NamcoMapper.java index aeff7d2..d51ce48 100644 --- a/src/com/grapeshot/halfnes/mappers/NamcoMapper.java +++ b/src/com/grapeshot/halfnes/mappers/NamcoMapper.java @@ -87,7 +87,7 @@ public final void cartWrite(final int addr, final int data) { //irq counter high 7 bits irqcounter &= 0xff; irqcounter |= ((data & 0x7f) << 8); - irqenable = utils.getbit(data, 7); + irqenable = (data & utils.BIT7) != 0; irqack(); //and bit 7 is irq enable } else if (addr <= 0xbfff) { @@ -104,28 +104,28 @@ public final void cartWrite(final int addr, final int data) { //i hope it doesnt try to write while it's chr rom nt0 = Arrays.copyOfRange(chr, (data * 1024), (data + 1) * 1024); } else { - nt0 = (utils.getbit(data, 0) ? pput1 : pput0); + nt0 = ((data & utils.BIT0) != 0 ? pput1 : pput0); } } else if (addr <= 0xc8ff) { //nametable select 2 if (data < 0xe0) { nt1 = Arrays.copyOfRange(chr, (data * 1024), (data + 1) * 1024); } else { - nt1 = (utils.getbit(data, 0) ? pput1 : pput0); + nt1 = ((data & utils.BIT0) != 0 ? pput1 : pput0); } } else if (addr <= 0xd7ff) { //nametable select 3 if (data < 0xe0) { nt2 = Arrays.copyOfRange(chr, (data * 1024), (data + 1) * 1024); } else { - nt2 = (utils.getbit(data, 0) ? pput1 : pput0); + nt2 = ((data & utils.BIT0) != 0 ? pput1 : pput0); } } else if (addr <= 0xdfff) { //nametable select 4 if (data < 0xe0) { nt3 = Arrays.copyOfRange(chr, (data * 1024), (data + 1) * 1024); } else { - nt3 = (utils.getbit(data, 0) ? pput1 : pput0); + nt3 = ((data & utils.BIT0) != 0 ? pput1 : pput0); } } else if (addr <= 0xe7ff) { //prg select 1 (1st 6 bits) and mirroring @@ -137,8 +137,8 @@ public final void cartWrite(final int addr, final int data) { for (int i = 0; i < 8; ++i) { prg_map[i + 8] = (1024 * (i + 8 * (data & 63))) % prgsize; } - chrramenable0 = !utils.getbit(data, 6); - chrramenable1 = !utils.getbit(data, 7); + chrramenable0 = (data & utils.BIT6) == 0; + chrramenable1 = (data & utils.BIT7) == 0; } else if (addr <= 0xf7ff) { for (int i = 0; i < 8; ++i) { prg_map[i + 16] = (1024 * (i + 8 * (data & 63))) % prgsize; @@ -147,7 +147,7 @@ public final void cartWrite(final int addr, final int data) { } else if (addr <= 0xffff) { //write protect for prg ram on namco 163 //also sound address port (7 bits) - autoincrement = utils.getbit(data, 7); + autoincrement = (data & utils.BIT7) != 0; soundAddr = data & 0x7f; } } diff --git a/src/com/grapeshot/halfnes/mappers/Namcot34x3Mapper.java b/src/com/grapeshot/halfnes/mappers/Namcot34x3Mapper.java index b94714c..8553f00 100644 --- a/src/com/grapeshot/halfnes/mappers/Namcot34x3Mapper.java +++ b/src/com/grapeshot/halfnes/mappers/Namcot34x3Mapper.java @@ -58,7 +58,7 @@ public final void cartWrite(int addr, int data) { } if (mirroring) { - setmirroring(utils.getbit(data, 6) ? MirrorType.SS_MIRROR1 : MirrorType.SS_MIRROR0); + setmirroring((data & utils.BIT6) != 0 ? MirrorType.SS_MIRROR1 : MirrorType.SS_MIRROR0); } } diff --git a/src/com/grapeshot/halfnes/mappers/Sunsoft02Mapper.java b/src/com/grapeshot/halfnes/mappers/Sunsoft02Mapper.java index 4f569eb..f0900ea 100644 --- a/src/com/grapeshot/halfnes/mappers/Sunsoft02Mapper.java +++ b/src/com/grapeshot/halfnes/mappers/Sunsoft02Mapper.java @@ -40,7 +40,7 @@ public final void cartWrite(final int addr, final int data) { prgselect = (data >> 4) & 15; } else { prgselect = (data >> 4) & 7; - setmirroring(utils.getbit(data, 3) ? MirrorType.SS_MIRROR1 : MirrorType.SS_MIRROR0); + setmirroring((data & utils.BIT3) != 0 ? MirrorType.SS_MIRROR1 : MirrorType.SS_MIRROR0); int chrselect = ((data & 7) | (data >> 7) * 8); diff --git a/src/com/grapeshot/halfnes/mappers/Sunsoft03Mapper.java b/src/com/grapeshot/halfnes/mappers/Sunsoft03Mapper.java index b173d99..56d7fab 100644 --- a/src/com/grapeshot/halfnes/mappers/Sunsoft03Mapper.java +++ b/src/com/grapeshot/halfnes/mappers/Sunsoft03Mapper.java @@ -56,7 +56,7 @@ public final void cartWrite(final int addr, final int data) { --cpu.interrupt; interrupted = false; } - irqenable = utils.getbit(data, 4); + irqenable = (data & utils.BIT4) != 0; irqtoggle = false; } else if (addr >= 0xE800 && addr <= 0xEFFF) { switch (data & 3) { diff --git a/src/com/grapeshot/halfnes/mappers/TengenRamboMapper.java b/src/com/grapeshot/halfnes/mappers/TengenRamboMapper.java index 12b0fc2..2ca430b 100644 --- a/src/com/grapeshot/halfnes/mappers/TengenRamboMapper.java +++ b/src/com/grapeshot/halfnes/mappers/TengenRamboMapper.java @@ -53,7 +53,7 @@ public final void cartWrite(int addr, int data) { } //bankswitches here //different register for even/odd writes - if (utils.getbit(addr, 0)) { + if ((addr & utils.BIT0) != 0) { //odd registers if ((addr >= 0x8000) && (addr <= 0x9fff)) { //bank change @@ -89,11 +89,11 @@ public final void cartWrite(int addr, int data) { } } else if ((addr >= 0xA000) && (addr <= 0xbfff)) { //prg ram write protect - //cpuram.setPrgRAMEnable(!utils.getbit(data, 7)); + //cpuram.setPrgRAMEnable((data & utils.BIT7) == 0); } else if ((addr >= 0xc000) && (addr <= 0xdfff)) { //any value here reloads irq counter irqreload = true; - irqmode = utils.getbit(data, 0); + irqmode = (data & utils.BIT0) != 0; } else if ((addr >= 0xe000) && (addr <= 0xffff)) { //iany value here enables interrupts irqenable = true; @@ -103,11 +103,11 @@ public final void cartWrite(int addr, int data) { if ((addr >= 0x8000) && (addr <= 0x9fff)) { //bank select whichbank = data & 0xf; - chrmode1k = utils.getbit(data, 5); - prgconfig = utils.getbit(data, 6); + chrmode1k = (data & utils.BIT5) != 0; + prgconfig = (data & utils.BIT6) != 0; //if bit is false, 8000-9fff swappable and c000-dfff fixed to 2nd to last bank //if bit is true, c000-dfff swappable and 8000-9fff fixed to 2nd to last bank - chrconfig = utils.getbit(data, 7); + chrconfig = (data & utils.BIT7) != 0; //if false: 2 2k banks @ 0000-0fff, 4 1k banks in 1000-1fff //if true: 4 1k banks @ 0000-0fff, 2 2k banks @ 1000-1fff setupchr(); @@ -115,7 +115,7 @@ public final void cartWrite(int addr, int data) { } else if ((addr >= 0xA000) && (addr <= 0xbfff)) { //mirroring setup if (scrolltype != MirrorType.FOUR_SCREEN_MIRROR) { - setmirroring(utils.getbit(data, 0) ? MirrorType.H_MIRROR : MirrorType.V_MIRROR); + setmirroring((data & utils.BIT0) != 0 ? MirrorType.H_MIRROR : MirrorType.V_MIRROR); } } else if ((addr >= 0xc000) && (addr <= 0xdfff)) { //value written here used to reload irq counter _@ end of scanline_ diff --git a/src/com/grapeshot/halfnes/mappers/VRC1Mapper.java b/src/com/grapeshot/halfnes/mappers/VRC1Mapper.java index 137ca13..ef23478 100644 --- a/src/com/grapeshot/halfnes/mappers/VRC1Mapper.java +++ b/src/com/grapeshot/halfnes/mappers/VRC1Mapper.java @@ -38,7 +38,7 @@ public final void cartWrite(int addr, int data) { setbanks(); break; case 0x9: - setmirroring(utils.getbit(data, 0) ? MirrorType.H_MIRROR : MirrorType.V_MIRROR); + setmirroring((data & utils.BIT0) != 0 ? MirrorType.H_MIRROR : MirrorType.V_MIRROR); chrbank[0] = (chrbank[0] & 0xf) | ((data << 3) & 0x10); chrbank[1] = (chrbank[1] & 0xf) | ((data << 2) & 0x10); setbanks(); diff --git a/src/com/grapeshot/halfnes/mappers/VRC2Mapper.java b/src/com/grapeshot/halfnes/mappers/VRC2Mapper.java index 1d8367d..dc446cf 100644 --- a/src/com/grapeshot/halfnes/mappers/VRC2Mapper.java +++ b/src/com/grapeshot/halfnes/mappers/VRC2Mapper.java @@ -28,8 +28,8 @@ public final void cartWrite(int addr, int data) { super.cartWrite(addr, data); return; } - boolean bit0 = utils.getbit(addr, 1); - boolean bit1 = utils.getbit(addr, 0); + boolean bit0 = (addr & utils.BIT1) != 0; + boolean bit1 = (addr & utils.BIT0) != 0; switch (addr >> 12) { case 0x8: prgbank0 = data & 0xf; diff --git a/src/com/grapeshot/halfnes/mappers/VRC3Mapper.java b/src/com/grapeshot/halfnes/mappers/VRC3Mapper.java index 9dc01db..ed05766 100644 --- a/src/com/grapeshot/halfnes/mappers/VRC3Mapper.java +++ b/src/com/grapeshot/halfnes/mappers/VRC3Mapper.java @@ -46,10 +46,10 @@ public final void cartWrite(int addr, int data) { irqreload = (irqreload & 0x0FFF) | (data & 0xF) << 12; break; case 0xC: //IRQ Control - irqmode = utils.getbit(data, 2); - irqackenable = utils.getbit(data, 0); + irqmode = (data & utils.BIT2) != 0; + irqackenable = (data & utils.BIT0) != 0; - irqenable = utils.getbit(data, 1); + irqenable = (data & utils.BIT1) != 0; if (irqenable) { if (irqmode) { irqctr &= 0xFF00; diff --git a/src/com/grapeshot/halfnes/mappers/VRC4Mapper.java b/src/com/grapeshot/halfnes/mappers/VRC4Mapper.java index 4152e03..2ccd880 100644 --- a/src/com/grapeshot/halfnes/mappers/VRC4Mapper.java +++ b/src/com/grapeshot/halfnes/mappers/VRC4Mapper.java @@ -81,7 +81,7 @@ public final void cartWrite(int addr, int data) { break; } } else { - prgmode = utils.getbit(data, 1); + prgmode = (data & utils.BIT1) != 0; } break; case 0xa: @@ -117,9 +117,9 @@ public final void cartWrite(int addr, int data) { // System.err.println("reload set to " + irqreload); } else { if (!bit0) { - irqack = utils.getbit(data, 0); - irqenable = utils.getbit(data, 1); - irqmode = utils.getbit(data, 2); + irqack = (data & utils.BIT0) != 0; + irqenable = (data & utils.BIT1) != 0; + irqmode = (data & utils.BIT2) != 0; if (irqenable) { irqcounter = irqreload; } diff --git a/src/com/grapeshot/halfnes/mappers/VRC6Mapper.java b/src/com/grapeshot/halfnes/mappers/VRC6Mapper.java index e77599b..6a20ff4 100644 --- a/src/com/grapeshot/halfnes/mappers/VRC6Mapper.java +++ b/src/com/grapeshot/halfnes/mappers/VRC6Mapper.java @@ -113,9 +113,9 @@ public final void cartWrite(final int addr, final int data) { if (!bit0) { irqreload = data; } else { - irqack = utils.getbit(data, 0); - irqenable = utils.getbit(data, 1); - irqmode = utils.getbit(data, 2); + irqack = (data & utils.BIT0) != 0; + irqenable = (data & utils.BIT1) != 0; + irqmode = (data & utils.BIT2) != 0; if (irqenable) { irqcounter = irqreload; } diff --git a/src/com/grapeshot/halfnes/mappers/VRC7Mapper.java b/src/com/grapeshot/halfnes/mappers/VRC7Mapper.java index a3316b1..95d2893 100644 --- a/src/com/grapeshot/halfnes/mappers/VRC7Mapper.java +++ b/src/com/grapeshot/halfnes/mappers/VRC7Mapper.java @@ -41,8 +41,8 @@ public final void cartWrite(final int addr, final int data) { notifyscanline(999); } - final boolean bit0 = utils.getbit(addr, 4) | utils.getbit(addr, 3); - final boolean bit1 = utils.getbit(addr, 5); + final boolean bit0 = (addr & (utils.BIT4 | utils.BIT3)) != 0; + final boolean bit1 = (addr & utils.BIT5) != 0; switch (addr >> 12) { case 0x8: if (bit0) { @@ -119,9 +119,9 @@ public final void cartWrite(final int addr, final int data) { firedinterrupt = false; } else { //irq control - irqack = utils.getbit(data, 0); - irqenable = utils.getbit(data, 1); - irqmode = utils.getbit(data, 2); + irqack = (data & utils.BIT0) != 0; + irqenable = (data & utils.BIT1) != 0; + irqmode = (data & utils.BIT2) != 0; if (irqenable) { irqcounter = irqreload; } diff --git a/src/com/grapeshot/halfnes/ui/ControllerImpl.java b/src/com/grapeshot/halfnes/ui/ControllerImpl.java index 1e92998..c8c1ac9 100644 --- a/src/com/grapeshot/halfnes/ui/ControllerImpl.java +++ b/src/com/grapeshot/halfnes/ui/ControllerImpl.java @@ -14,6 +14,7 @@ import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.prefs.Preferences; +import javafx.scene.Scene; import net.java.games.input.Component; import net.java.games.input.Controller; @@ -28,58 +29,75 @@ */ public class ControllerImpl implements ControllerInterface, KeyListener { - private final java.awt.Component parent; + private java.awt.Component parent; private Controller gameController; private Component[] buttons; private final ScheduledExecutorService thread = Executors.newSingleThreadScheduledExecutor(); private int latchbyte = 0, controllerbyte = 0, prevbyte = 0, outbyte = 0, gamepadbyte = 0; private final HashMap m = new HashMap(10); private final int controllernum; + private GpioControllerImpl gpioController; public ControllerImpl(final java.awt.Component parent, final int controllernum) { - if (parent == null) { - throw new NullPointerException("parent"); - } + this(controllernum); + this.parent = parent; + parent.addKeyListener(this); + } + + public ControllerImpl(final Scene scene, final int controllernum) { + this(controllernum); + scene.addEventHandler(javafx.scene.input.KeyEvent.KEY_PRESSED, e -> { + pressKey(e.getCode().impl_getCode()); + }); + scene.addEventHandler(javafx.scene.input.KeyEvent.KEY_RELEASED, e -> { + releaseKey(e.getCode().impl_getCode()); + }); + } + + public ControllerImpl(final int controllernum) { if ((controllernum != 0) && (controllernum != 1)) { throw new IllegalArgumentException("controllerNum must be 0 or 1"); } this.controllernum = controllernum; setButtons(); - this.parent = parent; - parent.addKeyListener(this); } @Override - public void keyPressed(final KeyEvent arg0) { + public void keyPressed(final KeyEvent keyEvent) { + pressKey(keyEvent.getKeyCode()); + } + + private void pressKey(int keyCode) { //enable the byte of whatever is found prevbyte = controllerbyte; - final int kepressed = arg0.getKeyCode(); - if (!m.containsKey(kepressed)) { + if (!m.containsKey(keyCode)) { return; } //enable the corresponding bit to the key - controllerbyte |= m.get(kepressed); + controllerbyte |= m.get(keyCode); //special case: if up and down are pressed at once, use whichever was pressed previously - if (getbit(controllerbyte, 4) && getbit(controllerbyte, 5)) { + if ((controllerbyte & (BIT4 | BIT5)) == (BIT4 | BIT5)) { controllerbyte &= ~(BIT4 | BIT5); controllerbyte |= (prevbyte & ~(BIT4 | BIT5)); } //same for left and right - if (getbit(controllerbyte, 6) && getbit(controllerbyte, 7)) { + if ((controllerbyte & (BIT6 | BIT7)) == (BIT6 | BIT7)) { controllerbyte &= ~(BIT6 | BIT7); controllerbyte |= (prevbyte & ~(BIT6 | BIT7)); } - } @Override - public void keyReleased(final KeyEvent arg0) { + public void keyReleased(final KeyEvent keyEvent) { + releaseKey(keyEvent.getKeyCode()); + } + + private void releaseKey(int keyCode) { prevbyte = controllerbyte; - final int kepressed = arg0.getKeyCode(); - if (!m.containsKey(kepressed)) { + if (!m.containsKey(keyCode)) { return; } - controllerbyte &= ~m.get(kepressed); + controllerbyte &= ~m.get(keyCode); } public int getbyte() { @@ -91,14 +109,29 @@ public void keyTyped(final KeyEvent arg0) { // TODO Auto-generated method stub } + @Override public void strobe() { //shifts a byte out outbyte = latchbyte & 1; latchbyte = ((latchbyte >> 1) | 0x100); } + @Override public void output(final boolean state) { + if (gpioController == null) { latchbyte = gamepadbyte | controllerbyte; + } else { + latchbyte = gamepadbyte | controllerbyte | gpioController.getByte(); + } + } + + @Override + public int peekOutput() { + if (gpioController == null) { + return gamepadbyte | controllerbyte; + } else { + return gamepadbyte | controllerbyte | gpioController.getByte(); + } } /** @@ -275,6 +308,11 @@ public final void setButtons() { m.put(prefs.getInt("keyB1", KeyEvent.VK_Z), BIT1); m.put(prefs.getInt("keySelect1", KeyEvent.VK_SHIFT), BIT2); m.put(prefs.getInt("keyStart1", KeyEvent.VK_ENTER), BIT3); + try { + gpioController = new GpioControllerImpl(); + } catch (java.lang.UnsatisfiedLinkError e) { + // No GPIO on this platform, skip it... + } break; case 1: default: diff --git a/src/com/grapeshot/halfnes/ui/ControllerInterface.java b/src/com/grapeshot/halfnes/ui/ControllerInterface.java index 6076d32..6f8e0a1 100644 --- a/src/com/grapeshot/halfnes/ui/ControllerInterface.java +++ b/src/com/grapeshot/halfnes/ui/ControllerInterface.java @@ -15,4 +15,6 @@ public interface ControllerInterface { public void output(final boolean state); public int getbyte(); + + public int peekOutput(); } diff --git a/src/com/grapeshot/halfnes/ui/GUIImpl.java b/src/com/grapeshot/halfnes/ui/GUIImpl.java index a2b0d28..5824d2d 100644 --- a/src/com/grapeshot/halfnes/ui/GUIImpl.java +++ b/src/com/grapeshot/halfnes/ui/GUIImpl.java @@ -32,7 +32,7 @@ public class GUIImpl extends JFrame implements GUIInterface { private Canvas canvas; private BufferStrategy buffer; - private final NES nes; + private NES nes; private static final long serialVersionUID = 6411494245530679723L; private final AL listener = new AL(); private int screenScaleFactor; @@ -44,16 +44,25 @@ public class GUIImpl extends JFrame implements GUIInterface { private Renderer renderer; private final ControllerImpl padController1, padController2; - public GUIImpl(NES nes) { - this.nes = nes; + public GUIImpl() { screenScaleFactor = PrefsSingleton.get().getInt("screenScaling", 2); padController1 = new ControllerImpl(this, 0); padController2 = new ControllerImpl(this, 1); - nes.setControllers(padController1, padController2); padController1.startEventQueue(); padController2.startEventQueue(); } + @Override + public NES getNes() { + return nes; + } + + @Override + public void setNES(NES nes) { + this.nes = nes; + nes.setControllers(padController1, padController2); + } + public synchronized void setRenderOptions() { if (canvas != null) { this.remove(canvas); @@ -80,6 +89,10 @@ public synchronized void setRenderOptions() { @Override public synchronized void run() { + SwingUtilities.invokeLater(this::runOnEventThread); + } + + public synchronized void runOnEventThread() { //construct window this.setTitle("HalfNES " + NES.VERSION); this.setResizable(false); @@ -95,7 +108,10 @@ public synchronized void run() { PrefsSingleton.get().getInt("windowY", 0)); this.addWindowListener(listener); this.setDefaultCloseOperation(DISPOSE_ON_CLOSE); - + + // todo: create options for these + hideCursor(); + toggleFullScreen(); this.setVisible(true); // Create BackBuffer @@ -120,7 +136,7 @@ public boolean importData(final TransferHandler.TransferSupport support) { try { //holy typecasting batman (this interface predates generics) File toload = (File) ((java.util.List) t.getTransferData(DataFlavor.javaFileListFlavor)).get(0); - loadROM(toload.getCanonicalPath()); + loadROMs(toload.getCanonicalPath()); } catch (UnsupportedFlavorException e) { return false; } catch (IOException e) { @@ -132,6 +148,16 @@ public boolean importData(final TransferHandler.TransferSupport support) { this.setTransferHandler(handler); } + private void hideCursor() throws IndexOutOfBoundsException, HeadlessException { + // Transparent 16 x 16 pixel cursor image. + BufferedImage cursorImg = new BufferedImage(16, 16, BufferedImage.TYPE_INT_ARGB); + // Create a new blank cursor. + Cursor blankCursor = Toolkit.getDefaultToolkit().createCustomCursor( + cursorImg, new Point(0, 0), "blank cursor"); + // Set the blank cursor to the JFrame. + getContentPane().setCursor(blankCursor); + } + public void buildMenus() { JMenuBar menus = new JMenuBar(); JMenu file = new JMenu("File"); @@ -240,18 +266,20 @@ public void loadROM() { fileDialog.setVisible(true); if (fileDialog.getFile() != null) { PrefsSingleton.get().put("filePath", fileDialog.getDirectory()); - loadROM(fileDialog.getDirectory() + fileDialog.getFile()); + loadROMs(fileDialog.getDirectory() + fileDialog.getFile()); } if (wasInFullScreen) { toggleFullScreen(); } } - private void loadROM(String path) { + @Override + public void loadROMs(String path) { if (path.endsWith(".zip") || path.endsWith(".ZIP")) { try { loadRomFromZip(path); } catch (IOException ex) { + ex.printStackTrace(); this.messageBox("Could not load file:\nFile does not exist or is not a valid NES game.\n" + ex.getMessage()); } } else { @@ -315,13 +343,15 @@ private File extractRomFromZip(String zipName, String romName) throws IOExceptio //note: here's the bug, when it saves the temp file if it's in a folder //in the zip it's trying to put it in the same folder outside the zip - final File outputFile = new File(new File(zipName).getParent() + final File outputFile = new File(new File(zipName).getCanonicalFile().getParent() + File.separator + FileUtils.stripExtension(new File(zipName).getName()) + " - " + romName); if (outputFile.exists()) { - this.messageBox("Cannot extract file. File " + outputFile.getCanonicalPath() + " already exists."); - zipStream.close(); - return null; + if (!outputFile.delete()) { + this.messageBox("Cannot extract file. File " + outputFile.getCanonicalPath() + " already exists."); + zipStream.close(); + return null; + } } final byte[] buf = new byte[4096]; final FileOutputStream fos = new FileOutputStream(outputFile); @@ -368,7 +398,6 @@ public void messageBox(final String message) { int bgcolor; BufferedImage frame; double fps; - int frameskip = 0; @Override public final synchronized void setFrame(final int[] nextframe, final int[] bgcolors, boolean dotcrawl) { @@ -388,16 +417,14 @@ public final synchronized void setFrame(final int[] nextframe, final int[] bgcol } averageframes /= frametimes.length; fps = 1E9 / averageframes; - this.setTitle(String.format("HalfNES %s - %s, %2.2f fps" - + ((frameskip > 0) ? " frameskip " + frameskip : ""), + this.setTitle(String.format("HalfNES %s - %s, %2.2f fps", +// + ((nes.frameskip > 0) ? " frameskip " + nes.frameskip : ""), NES.VERSION, nes.getCurrentRomName(), fps)); } - if (nes.framecount % (frameskip + 1) == 0) { - frame = renderer.render(nextframe, bgcolors, dotcrawl); - render(); - } + frame = renderer.render(nextframe, bgcolors, dotcrawl); + render(); } @Override diff --git a/src/com/grapeshot/halfnes/ui/GUIInterface.java b/src/com/grapeshot/halfnes/ui/GUIInterface.java index 57d432b..5f11f33 100644 --- a/src/com/grapeshot/halfnes/ui/GUIInterface.java +++ b/src/com/grapeshot/halfnes/ui/GUIInterface.java @@ -4,12 +4,18 @@ */ package com.grapeshot.halfnes.ui; +import com.grapeshot.halfnes.NES; + /** * * @author Andrew */ public interface GUIInterface extends Runnable { + public NES getNes(); + + public void setNES(NES nes); + public void setFrame(int[] frame, int[] bgcolor, boolean dotcrawl); //Frame is now a 256x240 array with NES color numbers from 0-3F //plus the state of the 3 color emphasis bits in bits 7,8,9 @@ -20,4 +26,6 @@ public interface GUIInterface extends Runnable { public void run(); public void render(); + + public void loadROMs(String path); } diff --git a/src/com/grapeshot/halfnes/ui/GpioControllerImpl.java b/src/com/grapeshot/halfnes/ui/GpioControllerImpl.java new file mode 100644 index 0000000..45d8113 --- /dev/null +++ b/src/com/grapeshot/halfnes/ui/GpioControllerImpl.java @@ -0,0 +1,91 @@ +package com.grapeshot.halfnes.ui; + +import static com.grapeshot.halfnes.utils.*; +import com.pi4j.io.gpio.GpioController; +import com.pi4j.io.gpio.GpioFactory; +import com.pi4j.io.gpio.GpioPinDigitalInput; +import com.pi4j.io.gpio.Pin; +import com.pi4j.io.gpio.PinPullResistance; +import com.pi4j.io.gpio.RaspiPin; +import com.pi4j.io.gpio.event.GpioPinListenerDigital; + +/** + * @author Stephen Chin + */ +class GpioControllerImpl { + + private static final Pin[] BUTTON_PINS = { + RaspiPin.GPIO_02, // GPIO 27 + RaspiPin.GPIO_03, // GPIO 22 + RaspiPin.GPIO_04, // GPIO 23 + RaspiPin.GPIO_05, // GPIO 24 + RaspiPin.GPIO_06, // GPIO 25 + RaspiPin.GPIO_25, // GPIO 26 + }; + + private static final int[] BUTTON_BIT = { + BIT1, // B + BIT0, // A + BIT6, // left + BIT5, // down + BIT7, // right + BIT4, // up + }; + + private static final int SELECT = BIT2; + private static final int MAGIC_SELECT = BIT6 | BIT7; + private static final int START = BIT3; + private static final int MAGIC_START = BIT4 | BIT5; + private static final int MENU = BIT8; + private static final int MAGIC_MENU = START | SELECT; + private static final int MAGIC_MENU_DECOMPOSE = MAGIC_START | MAGIC_SELECT; + + private final GpioController gpio; + private int gpiobyte = 0; + private final GpioPinDigitalInput[] buttons = new GpioPinDigitalInput[BUTTON_PINS.length]; + + public GpioControllerImpl() { + gpio = GpioFactory.getInstance(); + for (int i = 0; i < BUTTON_PINS.length; i++) { + final int bx = i; + buttons[bx] = gpio.provisionDigitalInputPin(BUTTON_PINS[bx], PinPullResistance.PULL_UP); + buttons[bx].setDebounce(20); + buttons[bx].addListener((GpioPinListenerDigital) event -> { + if (event.getState().isLow()) { // press + gpiobyte |= BUTTON_BIT[bx]; + if ((gpiobyte & MAGIC_START) == MAGIC_START) { + gpiobyte &= ~MAGIC_START; + gpiobyte |= START; + } + if ((gpiobyte & MAGIC_SELECT) == MAGIC_SELECT) { + gpiobyte &= ~MAGIC_SELECT; + gpiobyte |= SELECT; + } + if ((gpiobyte & MAGIC_MENU) == MAGIC_MENU) { + gpiobyte &= ~MAGIC_MENU; + gpiobyte |= MENU; + } + } else { // release + if ((gpiobyte & MENU) != 0 && (BUTTON_BIT[bx] & MAGIC_MENU_DECOMPOSE) != 0) { + gpiobyte &= ~MENU; + gpiobyte |= MAGIC_MENU; + } + if ((gpiobyte & START) != 0 && (BUTTON_BIT[bx] & MAGIC_START) != 0) { + gpiobyte &= ~START; + gpiobyte |= MAGIC_START; + } + if ((gpiobyte & SELECT) != 0 && (BUTTON_BIT[bx] & MAGIC_SELECT) != 0) { + gpiobyte &= ~SELECT; + gpiobyte |= MAGIC_SELECT; + } + gpiobyte &= ~BUTTON_BIT[bx]; + } + }); + } + } + + int getByte() { + return gpiobyte; + } + +} diff --git a/src/com/grapeshot/halfnes/ui/OnScreenMenu.java b/src/com/grapeshot/halfnes/ui/OnScreenMenu.java new file mode 100644 index 0000000..d5ea448 --- /dev/null +++ b/src/com/grapeshot/halfnes/ui/OnScreenMenu.java @@ -0,0 +1,315 @@ +package com.grapeshot.halfnes.ui; + +import com.grapeshot.halfnes.FileUtils; +import static com.grapeshot.halfnes.utils.BIT8; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; +import java.util.zip.ZipInputStream; +import javafx.application.Platform; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import javafx.scene.control.ListView; +import javafx.scene.input.KeyCode; +import javafx.scene.input.KeyEvent; +import javafx.scene.layout.StackPane; + +/** + * @author Stephen Chin + */ +public class OnScreenMenu extends StackPane { + + private GUIInterface gui; + private ListView menu; + private ListView gameMenu; + private final ObservableList menuItems = FXCollections.observableArrayList( + new MenuAction("Resume", this::resume), + new MenuAction("Load Game", this::loadGame), + new MenuAction("Reset", this::reset), + new MenuAction("Exit", this::exit), + new MenuAction("Power Off", this::powerOff)); + private final ObservableList games = FXCollections.observableArrayList( + new MenuAction("Back", () -> gameMenu.setVisible(false))); + + public OnScreenMenu(GUIInterface gui) { + this.gui = gui; + menu = new ListView<>(menuItems); + gameMenu = new ListView(games); + addMenuListeners(menu); + addMenuListeners(gameMenu); + getChildren().addAll(menu, gameMenu); + gameMenu.setVisible(false); + setVisible(false); + } + + private void addMenuListeners(ListView menu) { + menu.addEventHandler(javafx.scene.input.KeyEvent.KEY_RELEASED, e -> { + if (e.getCode().equals(KeyCode.ENTER) || e.getCode().equals(KeyCode.SPACE)) { + menu.getSelectionModel().getSelectedItem().run(); + } + }); + menu.addEventHandler(javafx.scene.input.MouseEvent.MOUSE_CLICKED, e -> { + if (e.getClickCount() == 2) { + menu.getSelectionModel().getSelectedItem().run(); + } + }); + } + + public void show() { + gui.getNes().pause(); + setVisible(true); + final ControllerInterface controller = gui.getNes().getcontroller1(); + new Thread(() -> { + boolean wasB = true, wasA = true, wasStart = true; + long lastDown = 0, lastUp = 0; + while (isVisible()) { + controller.output(true); + controller.strobe(); + boolean a = controller.getbyte() == 0x1; + controller.strobe(); + boolean b = controller.getbyte() == 0x1; + controller.strobe(); + boolean select = controller.getbyte() == 0x1; + controller.strobe(); + boolean start = controller.getbyte() == 0x1; + controller.strobe(); + boolean up = controller.getbyte() == 0x1; + controller.strobe(); + boolean down = controller.getbyte() == 0x1; + ListView activeMenu = gameMenu.isVisible() ? gameMenu : menu; + if ((a && !wasA) || (start && !wasStart)) { + Platform.runLater(() -> { + MenuAction item = activeMenu.getSelectionModel().getSelectedItem(); + if (item != null) { + item.run(); + } + }); + } + if (b && !wasB) { + if (gameMenu.isVisible()) { + Platform.runLater(() -> gameMenu.setVisible(false)); + } else { + Platform.runLater(this::hide); + } + } + if ((select || down) && System.currentTimeMillis() - lastDown > 100) { + Platform.runLater(() -> activeMenu.fireEvent(new KeyEvent(KeyEvent.KEY_PRESSED, null, null, KeyCode.DOWN, false, false, false, false))); + lastDown = System.currentTimeMillis(); + } + if (up && System.currentTimeMillis() - lastUp > 100) { + Platform.runLater(() -> activeMenu.fireEvent(new KeyEvent(KeyEvent.KEY_PRESSED, null, null, KeyCode.UP, false, false, false, false))); + lastUp = System.currentTimeMillis(); + } + wasA = a; + wasB = b; + wasStart = start; + try { + Thread.sleep(5); + } catch (InterruptedException ex) { + return; + } + } + }).start(); + } + + private void hide() { + setVisible(false); + final ControllerInterface controller = gui.getNes().getcontroller1(); + new Thread(() -> { + while (!isVisible()) { + if ((controller.peekOutput() & BIT8) != 0) { + Platform.runLater(this::show); + } + try { + Thread.sleep(50); + } catch (InterruptedException ex) { + return; + } + } + }).start(); + } + + public void loadROMs(String path) { + if (path.toLowerCase().endsWith(".zip")) { + try { + loadRomFromZip(path); + } catch (IOException ex) { + gui.messageBox("Could not load file:\nFile does not exist or is not a valid NES game.\n" + ex.getMessage()); + } + } else { + games.add(new GameAction(new File(path))); + runGame(path); + } + } + + private void loadRomFromZip(String zipName) throws IOException { + listRomsInZip(zipName).stream().map(romName -> new GameAction(zipName, romName)).forEach(games::add); + if (games.size() == 2) { + games.get(1).run(); + } else if (games.size() > 2) { + Platform.runLater(() -> { + loadGame(); + }); + } + } + + private List listRomsInZip(String zipName) throws IOException { + final List romNames; + try (ZipFile zipFile = new ZipFile(zipName)) { + final Enumeration zipEntries = zipFile.entries(); + romNames = new ArrayList<>(); + while (zipEntries.hasMoreElements()) { + final ZipEntry entry = zipEntries.nextElement(); + if (!entry.isDirectory() && (entry.getName().endsWith(".nes") + || entry.getName().endsWith(".fds") + || entry.getName().endsWith(".nsf"))) { + romNames.add(entry.getName()); + } + } + } + if (romNames.isEmpty()) { + throw new IOException("No NES games found in ZIP file."); + } + return romNames; + } + + private File extractRomFromZip(String zipName, String romName) throws IOException { + final File outputFile; + final FileOutputStream fos; + try (ZipInputStream zipStream = new ZipInputStream(new FileInputStream(zipName))) { + ZipEntry entry; + do { + entry = zipStream.getNextEntry(); + } while ((entry != null) && (!entry.getName().equals(romName))); + if (entry == null) { + zipStream.close(); + throw new IOException("Cannot find file " + romName + " inside archive " + zipName); + } + //name temp. extracted file after parent zip and file inside + //note: here's the bug, when it saves the temp file if it's in a folder + //in the zip it's trying to put it in the same folder outside the zip + outputFile = new File(new File(zipName).getCanonicalFile().getParent() + + File.separator + FileUtils.stripExtension(new File(zipName).getName()) + + " - " + romName); + if (outputFile.exists()) { + if (!outputFile.delete()) { + gui.messageBox("Cannot extract file. File " + outputFile.getCanonicalPath() + " already exists."); + zipStream.close(); + return null; + } + } + final byte[] buf = new byte[4096]; + fos = new FileOutputStream(outputFile); + int numBytes; + while ((numBytes = zipStream.read(buf, 0, buf.length)) != -1) { + fos.write(buf, 0, numBytes); + } + } + fos.close(); + return outputFile; + } + + private void resume() { + gui.getNes().resume(); + hide(); + } + + private void loadGame() { + gameMenu.setVisible(true); + gameMenu.requestFocus(); + } + + private void runGame(String path) { + gui.getNes().loadROM(path); + Platform.runLater(() -> { + gameMenu.setVisible(false); + hide(); + }); + } + + private void reset() { + gui.getNes().reset(); + hide(); + } + + private void exit() { + gui.getNes().quit(); + Platform.exit(); + } + + private void powerOff() { + try { + Runtime.getRuntime().exec("sudo shutdown -h now"); + } catch (IOException ex) { + Logger.getLogger(OnScreenMenu.class.getName()).log(Level.SEVERE, null, ex); + } + } + + class MenuAction { + + String name; + Runnable action; + + MenuAction() { + } + + MenuAction(String name, Runnable action) { + this.name = name; + this.action = action; + } + + public void run() { + action.run(); + } + + @Override + public String toString() { + return name; + } + } + + class GameAction extends MenuAction { + + GameAction(File game) { + name = game.getName(); + action = () -> { + try { + gui.getNes().loadROM(game.getCanonicalPath()); + Platform.runLater(() -> { + gameMenu.setVisible(false); + menu.setVisible(false); + }); + } catch (IOException e) { + gui.messageBox(e.getMessage()); + } + }; + } + + GameAction(final String zipName, final String romName) { + if (romName.toLowerCase().endsWith(".nes")) { + name = romName.substring(0, romName.length() - 4); + } else { + name = romName; + } + action = () -> { + try { + final File extractedFile = extractRomFromZip(zipName, romName); + if (extractedFile != null) { + extractedFile.deleteOnExit(); + } + runGame(extractedFile.getCanonicalPath()); + } catch (IOException e) { + gui.messageBox(e.getMessage()); + } + }; + } + } +} diff --git a/src/com/grapeshot/halfnes/ui/PreferencesDialog.java b/src/com/grapeshot/halfnes/ui/PreferencesDialog.java index 3a59aea..4c3bb6a 100644 --- a/src/com/grapeshot/halfnes/ui/PreferencesDialog.java +++ b/src/com/grapeshot/halfnes/ui/PreferencesDialog.java @@ -37,7 +37,7 @@ public PreferencesDialog(java.awt.Frame parent) { //set all of the text boxes //aand the sound enable checkbox. jCheckSoundEnable.setSelected(prefs.getBoolean("soundEnable", true)); - jCheckSoundFiltering.setSelected(prefs.getBoolean("soundFiltering", true)); + jCheckSoundFiltering.setSelected(prefs.getBoolean("soundFiltering", false)); jCheckMaintainAspect.setSelected(prefs.getBoolean("maintainAspect", true)); jCheckBoxNTSC.setSelected(prefs.getBoolean("TVEmulation", false)); jCheckBoxNTView.setSelected(prefs.getBoolean("ntView", false)); diff --git a/src/com/grapeshot/halfnes/video/AltNTSCRenderer.java b/src/com/grapeshot/halfnes/video/AltNTSCRenderer.java index 40432d4..8c86209 100644 --- a/src/com/grapeshot/halfnes/video/AltNTSCRenderer.java +++ b/src/com/grapeshot/halfnes/video/AltNTSCRenderer.java @@ -71,9 +71,9 @@ private void ntsc_render(int pixel) { for (int i = 0; i < SAMPLESPERPIXEL; ++i, ++phase) { double signal = inColorPhase(color, phase) ? high : low; if (emphasis != 0) { - if ((utils.getbit(emphasis, 0) && inColorPhase(0, phase)) - || (utils.getbit(emphasis, 1) && inColorPhase(4, phase)) - || (utils.getbit(emphasis, 2) && inColorPhase(8, phase))) { + if (((emphasis & utils.BIT0) != 0 && inColorPhase(0, phase)) + || ((emphasis & utils.BIT1) != 0 && inColorPhase(4, phase)) + || ((emphasis & utils.BIT2) != 0 && inColorPhase(8, phase))) { signal *= attenuation; } } diff --git a/src/com/grapeshot/halfnes/video/NesColors.java b/src/com/grapeshot/halfnes/video/NesColors.java index 46e7af9..c75b814 100644 --- a/src/com/grapeshot/halfnes/video/NesColors.java +++ b/src/com/grapeshot/halfnes/video/NesColors.java @@ -12,6 +12,7 @@ public class NesColors { private final static double att = 0.7; public final static int[][] col = GetNESColors(); + public final static byte[][][] colbytes = NESColorsToBytes(col); private static int[][] GetNESColors() { //just or's all the colors with opaque alpha and does the color emphasis calcs @@ -67,6 +68,21 @@ private static int[][] GetNESColors() { return colors; } + + private static byte[][][] NESColorsToBytes(int[][] col) { + byte[][][] colbytes = new byte[col.length][][]; + for (int i=0; i> 16) & 0xff; } diff --git a/src/com/grapeshot/halfnes/video/RGBRenderer.java b/src/com/grapeshot/halfnes/video/RGBRenderer.java index ff12559..826b019 100644 --- a/src/com/grapeshot/halfnes/video/RGBRenderer.java +++ b/src/com/grapeshot/halfnes/video/RGBRenderer.java @@ -17,7 +17,6 @@ public class RGBRenderer extends Renderer { @Override public BufferedImage render(int[] nespixels, int[] bgcolors, boolean dotcrawl) { //and now replace the nes color numbers with rgb colors (respecting color emph bits) - nespixels = nespixels.clone(); for (int i = 0; i < nespixels.length; ++i) { nespixels[i] = NesColors.col[(nespixels[i] & 0x1c0) >> 6][nespixels[i] & 0x3f]; } diff --git a/src/com/grapeshot/halfnes/video/Renderer.java b/src/com/grapeshot/halfnes/video/Renderer.java index 78c3201..029359d 100644 --- a/src/com/grapeshot/halfnes/video/Renderer.java +++ b/src/com/grapeshot/halfnes/video/Renderer.java @@ -4,8 +4,13 @@ */ package com.grapeshot.halfnes.video; +import java.awt.GraphicsConfiguration; +import java.awt.GraphicsEnvironment; import java.awt.image.BufferedImage; +import java.awt.image.ColorModel; import java.awt.image.DataBufferInt; +import java.awt.image.DirectColorModel; +import java.awt.image.Raster; import java.awt.image.WritableRaster; /** @@ -17,10 +22,15 @@ public abstract class Renderer { public abstract BufferedImage render(int[] nespixels, int[] bgcolors, boolean dotcrawl); public static BufferedImage getImageFromArray(final int[] bitmap, final int offset, final int width, final int height) { - final BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB_PRE); - final WritableRaster raster = image.getRaster(); - final int[] pixels = ((DataBufferInt) raster.getDataBuffer()).getData(); - System.arraycopy(bitmap, offset, pixels, 0, width * height); - return image; + GraphicsConfiguration gc = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration(); + DataBufferInt dataBuffer = new DataBufferInt(bitmap, width * height, offset); + int[] bandmasks = new int[3]; + bandmasks[0] = 0x00ff0000; // Red + bandmasks[1] = 0x0000ff00; // Green + bandmasks[2] = 0x000000ff; // Blue + final ColorModel colorModel = new DirectColorModel(24, bandmasks[0], bandmasks[1], bandmasks[2], 0); + final WritableRaster raster = Raster.createPackedRaster(dataBuffer, width, height, width, bandmasks, null); + final BufferedImage bufferedImage = new BufferedImage(colorModel, raster, false, null); + return bufferedImage; } }