diff --git a/jsnes/.eslintrc.json b/jsnes/.eslintrc.json new file mode 100644 index 0000000..c23c3ee --- /dev/null +++ b/jsnes/.eslintrc.json @@ -0,0 +1,13 @@ +{ + "env": { + "browser": true, + "node": true, + "es6": true, + "commonjs": true + }, + "extends": ["eslint:recommended", "prettier"], + "rules": { + "eqeqeq": ["error", "always"], + "no-alert": "error" + } +} diff --git a/jsnes/.github/dependabot.yml b/jsnes/.github/dependabot.yml new file mode 100644 index 0000000..6c6f9f3 --- /dev/null +++ b/jsnes/.github/dependabot.yml @@ -0,0 +1,12 @@ +version: 2 +updates: +- package-ecosystem: npm + directory: "/" + schedule: + interval: weekly + open-pull-requests-limit: 10 + ignore: + - dependency-name: webpack + versions: + - "< 5" + - ">= 4.0.a" diff --git a/jsnes/.github/workflows/ci.yaml b/jsnes/.github/workflows/ci.yaml new file mode 100644 index 0000000..c57b025 --- /dev/null +++ b/jsnes/.github/workflows/ci.yaml @@ -0,0 +1,18 @@ +name: CI + +on: [push, pull_request] + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Use Node.js + uses: actions/setup-node@v2 + with: + node-version: '12.x' + - run: yarn + - run: yarn build + - run: yarn test diff --git a/jsnes/.gitignore b/jsnes/.gitignore new file mode 100644 index 0000000..a3e69d1 --- /dev/null +++ b/jsnes/.gitignore @@ -0,0 +1,6 @@ +._* +.DS_Store +/dist +/local-roms +/node_modules +/tmp diff --git a/jsnes/.gitrepo b/jsnes/.gitrepo new file mode 100644 index 0000000..0f8417a --- /dev/null +++ b/jsnes/.gitrepo @@ -0,0 +1,12 @@ +; DO NOT EDIT (unless you know what you are doing) +; +; This subdirectory is a git "subrepo", and this file is maintained by the +; git-subrepo command. See https://github.com/ingydotnet/git-subrepo#readme +; +[subrepo] + remote = https://github.com/bfirsh/jsnes.git + branch = master + commit = d8021d0336cb5c1cf924cd660ecf816bec15c11a + parent = 1c64fa74360920926657bd4670473f68a7e5f74d + method = merge + cmdver = 0.4.9 diff --git a/jsnes/.npmignore b/jsnes/.npmignore new file mode 100644 index 0000000..57311f2 --- /dev/null +++ b/jsnes/.npmignore @@ -0,0 +1,2 @@ +/local-roms +/tmp diff --git a/jsnes/AUTHORS.md b/jsnes/AUTHORS.md new file mode 100644 index 0000000..5629812 --- /dev/null +++ b/jsnes/AUTHORS.md @@ -0,0 +1,13 @@ +Authors +======= + + * Ben Firshman + +Thanks to: + + * Jamie Sanders for vNES, the Java emulator that JSNES owes so much to. + * Matt Westcott for JSSpeccy, the original inspiration for JSNES. + * Connor Dunn for a patch that dramatically increased performance on Chrome. + * Jens Lindstrom for some optimisations. + * Rafal Chlodnicki for an Opera fix. + * Ecin Krispie for fixing player 2 controls. diff --git a/jsnes/LICENSE b/jsnes/LICENSE new file mode 100644 index 0000000..13c5c83 --- /dev/null +++ b/jsnes/LICENSE @@ -0,0 +1,191 @@ + + 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 + + Copyright 2020 Ben Firshman + + 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/jsnes/README.md b/jsnes/README.md new file mode 100644 index 0000000..480b666 --- /dev/null +++ b/jsnes/README.md @@ -0,0 +1,92 @@ +# JSNES + +A JavaScript NES emulator. + +It's a library that works in both the browser and Node.js. The browser UI is available at [https://github.com/bfirsh/jsnes-web](https://github.com/bfirsh/jsnes-web). + +## Installation + +For Node.js or Webpack: + + $ npm install jsnes + +(Or `yarn add jsnes`.) + +In the browser, you can use [unpkg](https://unpkg.com): + +```html + +``` + +## Usage + +```javascript +// Initialize and set up outputs +var nes = new jsnes.NES({ + onFrame: function(frameBuffer) { + // ... write frameBuffer to screen + }, + onAudioSample: function(left, right) { + // ... play audio sample + } +}); + +// Read ROM data from disk (using Node.js APIs, for the sake of this example) +const fs = require('fs'); +var romData = fs.readFileSync('path/to/rom.nes', {encoding: 'binary'}); + +// Load ROM data as a string or byte array +nes.loadROM(romData); + +// Run frames at 60 fps, or as fast as you can. +// You are responsible for reliable timing as best you can on your platform. +nes.frame(); +nes.frame(); +// ... + +// Hook up whatever input device you have to the controller. +nes.buttonDown(1, jsnes.Controller.BUTTON_A); +nes.frame(); +nes.buttonUp(1, jsnes.Controller.BUTTON_A); +nes.frame(); +// ... +``` + +## Build + +To build a distribution: + + $ yarn run build + +This will create `dist/jsnes.min.js`. + +## Running tests + + $ yarn test + +## Embedding JSNES in a web page + +You can use JSNES to embed a playable version of a ROM in a web page. This is handy if you are a homebrew ROM developer and want to put a playable version of your ROM on its web page. + +The best implementation is [jsnes-web](https://github.com/bfirsh/jsnes-web) but unfortunately it is not trivial to reuse the code. You'll have to copy and paste the code from that repository, the use the [``](https://github.com/bfirsh/jsnes-web/blob/master/src/Emulator.js) React component. [Here is a usage example.](https://github.com/bfirsh/jsnes-web/blob/d3c35eec11986412626cbd08668dbac700e08751/src/RunPage.js#L119-L125). + +A project for potential contributors (hello!): jsnes-web should be reusable and on NPM! It just needs compiling and bundling. + +A more basic example is in the `example/` directory of this repository. Unfortunately this is known to be flawed, and doesn't do timing and sound as well as jsnes-web. + +## Formatting code + +All code must conform to [Prettier](https://prettier.io/) formatting. The test suite won't pass unless it does. + +To automatically format all your code, run: + + $ yarn run format + +## Maintainers + +- [Ben Firshman](http://github.com/bfirsh) +- [Ben Jones](https://github.com/BenShelton) +- [Stephen Hicks](https://github.com/shicks) +- [Alison Saia](https://github.com/allie) + +JSNES is based on [James Sanders' vNES](https://github.com/bfirsh/vNES), and owes an awful lot to it. It also wouldn't have happened without [Matt Wescott's JSSpeccy](http://jsspeccy.zxdemo.org/), which sparked the original idea. (Ben, circa 2008: "Hmm, I wonder what else could run in a browser?!") diff --git a/jsnes/example/InterglacticTransmissing.nes b/jsnes/example/InterglacticTransmissing.nes new file mode 100644 index 0000000..414d928 Binary files /dev/null and b/jsnes/example/InterglacticTransmissing.nes differ diff --git a/jsnes/example/README.md b/jsnes/example/README.md new file mode 100644 index 0000000..250620d --- /dev/null +++ b/jsnes/example/README.md @@ -0,0 +1,3 @@ +An example app to demonstrate a simple way to embed JSNES. + +ROM is by @slembcke: https://github.com/slembcke/InterglacticTransmissing diff --git a/jsnes/example/nes-embed.html b/jsnes/example/nes-embed.html new file mode 100644 index 0000000..7f2ef1f --- /dev/null +++ b/jsnes/example/nes-embed.html @@ -0,0 +1,18 @@ + + + + + + Embedding Example + + + + + + +
+ +
+

DPad: Arrow keys
Start: Return, Select: Tab
A Button: A, B Button: S

+ + diff --git a/jsnes/example/nes-embed.js b/jsnes/example/nes-embed.js new file mode 100644 index 0000000..27aabd5 --- /dev/null +++ b/jsnes/example/nes-embed.js @@ -0,0 +1,132 @@ +var SCREEN_WIDTH = 256; +var SCREEN_HEIGHT = 240; +var FRAMEBUFFER_SIZE = SCREEN_WIDTH*SCREEN_HEIGHT; + +var canvas_ctx, image; +var framebuffer_u8, framebuffer_u32; + +var AUDIO_BUFFERING = 512; +var SAMPLE_COUNT = 4*1024; +var SAMPLE_MASK = SAMPLE_COUNT - 1; +var audio_samples_L = new Float32Array(SAMPLE_COUNT); +var audio_samples_R = new Float32Array(SAMPLE_COUNT); +var audio_write_cursor = 0, audio_read_cursor = 0; + +var nes = new jsnes.NES({ + onFrame: function(framebuffer_24){ + for(var i = 0; i < FRAMEBUFFER_SIZE; i++) framebuffer_u32[i] = 0xFF000000 | framebuffer_24[i]; + }, + onAudioSample: function(l, r){ + audio_samples_L[audio_write_cursor] = l; + audio_samples_R[audio_write_cursor] = r; + audio_write_cursor = (audio_write_cursor + 1) & SAMPLE_MASK; + }, +}); + +function onAnimationFrame(){ + window.requestAnimationFrame(onAnimationFrame); + + image.data.set(framebuffer_u8); + canvas_ctx.putImageData(image, 0, 0); +} + +function audio_remain(){ + return (audio_write_cursor - audio_read_cursor) & SAMPLE_MASK; +} + +function audio_callback(event){ + var dst = event.outputBuffer; + var len = dst.length; + + // Attempt to avoid buffer underruns. + if(audio_remain() < AUDIO_BUFFERING) nes.frame(); + + var dst_l = dst.getChannelData(0); + var dst_r = dst.getChannelData(1); + for(var i = 0; i < len; i++){ + var src_idx = (audio_read_cursor + i) & SAMPLE_MASK; + dst_l[i] = audio_samples_L[src_idx]; + dst_r[i] = audio_samples_R[src_idx]; + } + + audio_read_cursor = (audio_read_cursor + len) & SAMPLE_MASK; +} + +function keyboard(callback, event){ + var player = 1; + switch(event.keyCode){ + case 38: // UP + callback(player, jsnes.Controller.BUTTON_UP); break; + case 40: // Down + callback(player, jsnes.Controller.BUTTON_DOWN); break; + case 37: // Left + callback(player, jsnes.Controller.BUTTON_LEFT); break; + case 39: // Right + callback(player, jsnes.Controller.BUTTON_RIGHT); break; + case 65: // 'a' - qwerty, dvorak + case 81: // 'q' - azerty + callback(player, jsnes.Controller.BUTTON_A); break; + case 83: // 's' - qwerty, azerty + case 79: // 'o' - dvorak + callback(player, jsnes.Controller.BUTTON_B); break; + case 9: // Tab + callback(player, jsnes.Controller.BUTTON_SELECT); break; + case 13: // Return + callback(player, jsnes.Controller.BUTTON_START); break; + default: break; + } +} + +function nes_init(canvas_id){ + var canvas = document.getElementById(canvas_id); + canvas_ctx = canvas.getContext("2d"); + image = canvas_ctx.getImageData(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); + + canvas_ctx.fillStyle = "black"; + canvas_ctx.fillRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); + + // Allocate framebuffer array. + var buffer = new ArrayBuffer(image.data.length); + framebuffer_u8 = new Uint8ClampedArray(buffer); + framebuffer_u32 = new Uint32Array(buffer); + + // Setup audio. + var audio_ctx = new window.AudioContext(); + var script_processor = audio_ctx.createScriptProcessor(AUDIO_BUFFERING, 0, 2); + script_processor.onaudioprocess = audio_callback; + script_processor.connect(audio_ctx.destination); +} + +function nes_boot(rom_data){ + nes.loadROM(rom_data); + window.requestAnimationFrame(onAnimationFrame); +} + +function nes_load_data(canvas_id, rom_data){ + nes_init(canvas_id); + nes_boot(rom_data); +} + +function nes_load_url(canvas_id, path){ + nes_init(canvas_id); + + var req = new XMLHttpRequest(); + req.open("GET", path); + req.overrideMimeType("text/plain; charset=x-user-defined"); + req.onerror = () => console.log(`Error loading ${path}: ${req.statusText}`); + + req.onload = function() { + if (this.status === 200) { + nes_boot(this.responseText); + } else if (this.status === 0) { + // Aborted, so ignore error + } else { + req.onerror(); + } + }; + + req.send(); +} + +document.addEventListener('keydown', (event) => {keyboard(nes.buttonDown, event)}); +document.addEventListener('keyup', (event) => {keyboard(nes.buttonUp, event)}); diff --git a/jsnes/package.json b/jsnes/package.json new file mode 100644 index 0000000..3393639 --- /dev/null +++ b/jsnes/package.json @@ -0,0 +1,32 @@ +{ + "name": "jsnes", + "version": "1.2.1", + "description": "A JavaScript NES emulator", + "homepage": "https://github.com/bfirsh/jsnes", + "author": "Ben Firshman (https://fir.sh)", + "main": "src/index.js", + "repository": { + "type": "git", + "url": "git://github.com/bfirsh/jsnes.git" + }, + "license": "Apache-2.0", + "scripts": { + "build": "webpack", + "test": "prettier-check src/**/*.js && mocha ./test/*.spec.js", + "test:watch": "mocha -w ./test/*.spec.js", + "prepublish": "npm run build", + "format": "prettier --write src/**/*.js" + }, + "devDependencies": { + "chai": "^4.1.2", + "eslint": "^6.8.0", + "eslint-config-prettier": "^6.10.1", + "eslint-loader": "^2.0.0", + "mocha": "^9.1.1", + "prettier": "^2.0.5", + "prettier-check": "^2.0.0", + "sinon": "^9.0.1", + "uglifyjs-webpack-plugin": "^1.3.0", + "webpack": "^3.9.1" + } +} diff --git a/jsnes/roms/croom/README.html b/jsnes/roms/croom/README.html new file mode 100644 index 0000000..79b7989 --- /dev/null +++ b/jsnes/roms/croom/README.html @@ -0,0 +1,75 @@ + +Concentration Room + + +
+

Concentration Room

+ + +
+ +

Overview

+ +

+An accident at the biochemical lab has released a neurotoxin, +and you've been quarantined after exposure. Maintain your +sanity by playing a card-matching game. +

+The table is littered with 10, 20, 36, 52, or 72 face-down cards. +Flip two cards, and if they show the same emblem, you keep them. +If they don't, flip them back. +

+ +

System Requirements

+

+Concentration Room is designed for your Nintendo Entertainment System. This version is an NROM-128 (16 KiB PRG, 8 KiB CHR), and it has been tested on a PowerPak. It also works in PC-based emulators such as Nestopia and FCE Ultra. +

+ +

Modes

+
+
1 Player Story
+Play solitaire to start to work the toxin out of your system. Then defeat other contaminated technicians and children one on one. +
1 Player Solitaire
+Select a difficulty level, then try to clear the table without having to turn back more than 99 non-matching pairs. +
2 Players
+Two players take turns turning over cards. They can pass one controller back and forth or use one controller each. If a pair doesn't match, the other player presses the A and B Buttons and takes a turn. The first player to take half the pairs wins. +
Vs. CPU
+Like 2 Players, except the second player is controlled by the NES. +
+ +

FAQ (Fully Anticipated Questions)

+
+
How long have you been working on this?
+This is actually my third try. The logo and the earliest background sketch date back to 2000. It got held up because I lacked artistic skill on the 16x16 pixel canvas. The second try in 2007 finalized the appearance of the game, and I did some work on the "emblem designer" that will show up in a future release. In late November 2009, I discovered Dian Shi Mali, a gambling simulator for the Famicom (Asian version of the NES) that also uses 16x16 pixel emblems. After a few hours of pushing Start to rich, I was inspired to create a set of 36 emblems. By then, I was ready to code most of the game in spare time during December 2009. +
Why are you still making games that don't scroll? You're better than that, as I saw in the President video.
+I saw it as something simple that I could finish fairly quickly in order to push falling block games off the front page of my web site. +
GameTek already made two other Concentration games on the NES. Why did you make this one?
+The controls in I Can Remember nor Classic Concentration are clunky. Neither of them features a full 72-card deck. And of course, they're not free software. +
In vs. modes, why end the game at half the cards matched instead of one more than half?
+Pairs early in a game require more skill to clear, and the last pair requires absolutely no skill. For example, a 20-card game tied at 4-4 will always end up 6-4. And at 5-3, the player in the lead likely got more early matches. So if we award no points for the last pair, the first player to reach half always wins. +
What's that font?
+The font in the game's logo is called Wasted Collection. The font in Multiboot Menu was based on it. The monospace font for menu text originally appeared in the "Who's Cuter" demo and is based on Apple Chicago by Susan Kare. (Another fun font is on this page.) +
Are you a Nazi?
+No, and that's why this game is called Concentration Room, not Concentration Camp. +
+

Legal

+

+Copyright © 2010 Damian Yerrick <croom@pineight.com> +

+Copying and distribution of this file, with or without modification, are permitted in any medium without royalty provided the copyright notice and this notice are preserved. This file is offered as-is, without any warranty. +

+The accompanying program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License, version 3 or later. As a special exception, you may copy and distribute exact copies of the program, as published by Damian Yerrick, in iNES or UNIF executable form without source code. +

+This product is not sponsored or endorsed by Nintendo, Ravensburger, Hasbro, Mattel, Quaker Oats, NBC Universal, GameTek, or Apple. +

+
+ diff --git a/jsnes/roms/croom/croom.nes b/jsnes/roms/croom/croom.nes new file mode 100644 index 0000000..716d3cc Binary files /dev/null and b/jsnes/roms/croom/croom.nes differ diff --git a/jsnes/roms/lj65/README.txt b/jsnes/roms/lj65/README.txt new file mode 100644 index 0000000..aaf1804 --- /dev/null +++ b/jsnes/roms/lj65/README.txt @@ -0,0 +1,314 @@ + _ _ __ ___ +| | (_) / / / __| +| | _ / /_ | /__ +| | | | | _ \ |___ \ +| |_ | | | (_) | .___) | + \__|_| | \___/ \___/ + |__/ + +LJ65 +an NES game +by Damian Yerrick + +See the legal section below. + +_____________________________________________________________________ +Introduction + +LJ65 is an action puzzle game for NES comparable to the popular +game Tetris(R), except distributed as free software and with more +responsive movement controls. + +_____________________________________________________________________ +Installing + +LJ65 is designed to run on Nintendo Entertainment System (called +Family Computer in Japan) and accurate NES emulators. It is +distributed as source code and an iNES format binary, using mapper +0 (NROM). Separate binaries for NTSC and PAL systems are provided. + +This program has been tested on NES using a PowerPak. It also works +on the current versions of Nintendulator, Nestopia, and FCE Ultra. +(Do not use the outdated Nesticle emulator anymore.) + +To run LJ65 on an NES without buying a PowerPak, you'll need to +solder together an NES cartridge with at least 16 KB of PRG space +and 4 KB of CHR space. A modded NROM-128 or CNROM board should be +fine. Chris Covell has put together instructions on how to replace +NES Game Paks' mask ROM chips with writable EEPROMs. +http://www.zyx.com/chrisc/solarwarscart.html + +To build LJ65 from source code, you will need + * CC65 (from http://www.cc65.org/ but you don't need the + non-free C compiler) + * GNU Make and Coreutils (included with most Linux distributions; + Windows users can use MSYS from http://www.devkitpro.org/) + +Modify the makefile to point to where you have CC65 installed. +Then run make. (Windows users can run mk.bat instead, which runs +make in the correct folder.) On a desktop PC from late 2000 with +a Pentium III 866 MHz, recompiling the whole thing takes about one +second. To build some data conversion tools, you'll need a GNU C +compiler such as MinGW; I have included Windows binaries of the +conversion tools for those who want to quickly get into hacking +on LJ65. + +_____________________________________________________________________ +Game controls + +Title screen: + Start: Show playfields. +Game over: + A+B: Join game. +Menu: + Control Pad up, down: Move cursor. + Control Pad left, right: Change option at cursor. + A: Start game. +Game: + Control Pad left, right, down: Move piece. + Control Pad up: Move piece to floor. + Control Pad up, down once landed: Lock piece into place. + A: Rotate piece clockwise. + B: Rotate piece anticlockwise. + Start: Pause game. + +_____________________________________________________________________ +Play + +At first, press Start to skip past each of the informational screens. +Then press Start at the title screen to display the playfields. +At this point, either player can press the A and B buttons at the +same time to begin playing. + +The pieces in LJ65 are called tetrominoes. (The word comes from +tetra-, a Greek prefix meaning four, and -omino, as in domino or +pentomino.) Each of the seven tetrominoes is made of four square +blocks and named after a letter of the Latin alphabet that it +resembles: + _ _ ___ ___ _ ___ + _______ | |___ ___| | | | _| _| _| |_ |_ |_ +|_______| |_____| |_____| |___| |___| |_____| |___| + I J L O S T Z + +When you start the game, a tetromino will begin to fall slowly into +the bin. You can move it with the Control Pad and rotate it with +the A or B button. + +The goal of LJ65 is to make complete horizontal lines by +packing the pieces into the bin with no holes. If you complete +a line, everything above it will move down a row. If you complete +more than one line with a piece, you get more points. + +As you play, the pieces will gradually fall faster, making the game +more difficult. At some point, the pieces will fall so fast that +they appear immediately at the bottom row of the playfield. If you +fill the bin to the top, to the point where more pieces cannot enter, +you "top out" and the game ends. + +If you have an overhang in the blocks, you can slide another +piece under it by holding Left or Right as the new piece passes +by the overhang: + _ + | | + _| | + |___| + _ _ _ _ _ + _| | => _| | | | => _| | | +| _| | _|_| | | _| | +|_| |_| |___| |_|___| + +Or in some cases, you can rotate pieces into very tight spaces: + _ + _| | + |_ | + |_| + _ ___ _ _ ___ _ ___ +| | |_ | => | |_| |_ | => | |___|_ | +| |_ _| | | |_ |_| | | |_ _| | +|___| |___| |___|_|___| |___|_|___| + +_____________________________________________________________________ +Rotation systems + +LJ65 supports two rotation systems, which it calls "Center" and +"Bottom". Center implements rules more familiar to Western players, +while Bottom pleases fans of the Japanese arcade tradition. + +In Center, pieces start out with their flat side down, and they +rotate around the center of an imaginary 3x3 or 4x4 cell bounding +box. If this is blocked, try one square to the right, one square to +the left, and finally one square up. +Up locks a piece into place immediately, and down waits for another +press of up or down before locking the piece. +After a piece locks, the next one comes out immediately, but after +the pieces have sped up enough, the next piece waits a bit. +Colors match the so-called Guideline: I is turquoise. + +. []. . []. . . . . []. . [][] . []. . . . []. . +[][][] . [][] [][][] [][]. [][]. . [][] . [][] [][]. +. . . . []. . []. . []. . . . . . [] [][]. . []. +Figure: T and S rotation in Center + +In Bottom, the J, L, S, T, and Z pieces start out with their flat +side up, and they rotate to stay in contact with the bottom of an +imaginary 3x3 cell box. S and Z pieces also keep a block in the +bottom center of this box. If this is blocked by a wall or a block +outside the piece's central column, then try one square to the right, +one square to the left, and finally (in the case of T) one square up. +Down locks on contact, and up waits for another press of up or down +to lock. After a piece locks, the next one waits a bit to come out. +Colors match those from a game with a monkey: I is red. + +. . . . []. . . . . []. . . . []. . . . . []. . +[][][] [][]. . []. . [][] . [][] [][]. . [][] [][]. +. []. . []. [][][] . []. [][]. . []. [][]. . []. +Figure: T and S rotation in Bottom + +_____________________________________________________________________ +Scoring + +Use up or down on the Control Pad to drop pieces, and you'll get +one point per row that the piece moves down. + +You also get points for clearing lines. Clearing more lines +with a single piece is worth more points: + +SINGLE (1 line with any piece) 1 * 1 * 100 = 100 points +DOUBLE (2 lines with any piece) 2 * 2 * 100 = 400 points +TRIPLE (3 lines with I, J, or L) 3 * 3 * 100 = 900 points +HOME RUN (4 lines with I only) 4 * 4 * 100 = 1600 points + +Making lines with consecutive pieces is called a combo and is +worth even more points. In general, the score for a line clear +is the number of lines cleared with this piece, times the number +of lines cleared so far in this combo, times 100. For example, +a double-triple-single combo is worth a total of 2300 points: + +2 lines 2 * 2 * 100 = 200 points +3 lines 3 * 5 * 100 = 1500 points +1 line 1 * 6 * 100 = 600 points + +When you start clearing lines, the game shows how many lines you +made in this combo. If you leave a 2-block-wide hole at the side +of the bin, you might manage to make a combo of 12 lines or more. +But then you have to weigh this against keeping your stack low +and earning more drop bonus. + +There are some grandmasters who can get millions of points in +some puzzle games. There exists a known corner case in this +game's score computation, and scoring is expected to fail beyond +6,553,000 points. + +If two players are playing, and you have GARBAGE turned on in the +menu, and you complete more than one line with a piece, the other +player's field rises by one or more rows: + +DOUBLE: 1 line +TRIPLE: 2 lines +HOME RUN: 4 lines + +This is not affected by combos. + +_____________________________________________________________________ +Keypress codes + +Some of the lesser-used features of the game are hidden so that +players interested in the most common features don't become confused. + +At title screen: + * B + Left hides the ghost piece. + +_____________________________________________________________________ +Questions + +Q: Isn't this a copy of Tetris? + +Yes, in part, but we don't believe it infringes Tetris Holding's +copyright. It was developed by people who had not read the source +code of Tetris. We disagree with Tetris Holding's claim of broad +patent-like rights over the game. Any similarity between LJ65 and +Tetris is a consequence of common methods of operation, which are +excluded from U.S. copyright (17 USC 102(b)). + +Q: Where's (feature that has appeared in another game)? + +If it's mentioned in the "future" list at the bottom of CHANGES.txt, +I know about it, and you may see some of those issues resolved in +the next version. Otherwise, I'd be glad to take suggestions, +provided that they aren't "network play with no lag" or "make the +game just like that Japanese game I saw on YouTube". + +Q: Why aren't the blocks square on my TV? + +In NTSC, a square pixel is 7/24 of a color subcarrier period wide +in 480i mode or 7/12 of a period in the so-called "240p" mode. +But like the video chipsets in most 8-bit and 16-bit computing +platforms, the NES PPU generates pixels that are not square: +8/12 of a period instead of 7/12. Games for PC, Apple II, or any +other platform with frame buffer video could correct for this by +drawing differently sized tiles, but games for NES are limited to +an 8x8 pixel tile grid. PAL video and widescreen televisions make +the problem even more pronounced. + +Q: Why do some pieces change color subtly when they land? + +The NES's tile size is 8x8 pixels, but the "attribute table" +assigns palettes to 16x16 pixel areas, or clusters of 2x2 tiles. +Only three colors plus the backdrop color can appear in each +color area. So the game approximates the color of each piece as a +combination of blue, orange, and green throughout the screen. + +The MMC5 mapper has ExGrafix, which allows 8x8 pixel color areas. +But the only source of MMC5 hardware is used copies of Castlevania +III: Dracula's Curse and Koei's war sims, unlike the discrete mapper +boards that retrousb.com sells. + +Q: Who is the fellow on How to Play, and where are his legs? + +Who are you, and where is your tail? ;-) + +_____________________________________________________________________ +Credits + +Program and graphics by Damian Yerrick +Original game design by Alexey Pajitnov +NES assembler toolchain by Ullrich von Bassewitz +NES emulators by Xodnizel, Martin Freij, and Quietust +NES documentation by contributors to http://nesdevwiki.org/ + +Music: + TEMP is "Tetris New Melody (OCRemoved)" by Am.Fm.GM + K.231 is "Leck mich im Arsch" by Wolfgang A. Mozart + +_____________________________________________________________________ +Legal + +Copyright (c) 2009 Damian Yerrick + +This manual is under the following license: + + This work is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any + damages arising from the use of this work. + + Permission is granted to anyone to use this work for any + purpose, including commercial applications, and to alter it and + redistribute it freely, subject to the following restrictions: + + 1. The origin of this work must not be misrepresented; you + must not claim that you wrote the original work. If you use + this work in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + 2. Altered source versions must be plainly marked as such, + and must not be misrepresented as being the original work. + 3. This notice may not be removed or altered from any + source distribution. + + The term "source" refers to the preferred form of a work for making + changes to it. + +The LJ65 software described by this manual is distributed under +the GNU General Public License, version 2 or later, with ABSOLUTELY +NO WARRANTY. See GPL.txt for details. + +LJ65 is not a Tetris product and is not endorsed by Tetris Holding. diff --git a/jsnes/roms/lj65/lj65.nes b/jsnes/roms/lj65/lj65.nes new file mode 100644 index 0000000..1304d36 Binary files /dev/null and b/jsnes/roms/lj65/lj65.nes differ diff --git a/jsnes/src/controller.js b/jsnes/src/controller.js new file mode 100644 index 0000000..4459728 --- /dev/null +++ b/jsnes/src/controller.js @@ -0,0 +1,27 @@ +var Controller = function () { + this.state = new Array(8); + for (var i = 0; i < this.state.length; i++) { + this.state[i] = 0x40; + } +}; + +Controller.BUTTON_A = 0; +Controller.BUTTON_B = 1; +Controller.BUTTON_SELECT = 2; +Controller.BUTTON_START = 3; +Controller.BUTTON_UP = 4; +Controller.BUTTON_DOWN = 5; +Controller.BUTTON_LEFT = 6; +Controller.BUTTON_RIGHT = 7; + +Controller.prototype = { + buttonDown: function (key) { + this.state[key] = 0x41; + }, + + buttonUp: function (key) { + this.state[key] = 0x40; + }, +}; + +module.exports = Controller; diff --git a/jsnes/src/cpu.js b/jsnes/src/cpu.js new file mode 100644 index 0000000..4a0a40c --- /dev/null +++ b/jsnes/src/cpu.js @@ -0,0 +1,2024 @@ +var utils = require("./utils"); + +var CPU = function (nes) { + this.nes = nes; + + // Keep Chrome happy + this.mem = null; + this.REG_ACC = null; + this.REG_X = null; + this.REG_Y = null; + this.REG_SP = null; + this.REG_PC = null; + this.REG_PC_NEW = null; + this.REG_STATUS = null; + this.F_CARRY = null; + this.F_DECIMAL = null; + this.F_INTERRUPT = null; + this.F_INTERRUPT_NEW = null; + this.F_OVERFLOW = null; + this.F_SIGN = null; + this.F_ZERO = null; + this.F_NOTUSED = null; + this.F_NOTUSED_NEW = null; + this.F_BRK = null; + this.F_BRK_NEW = null; + this.opdata = null; + this.cyclesToHalt = null; + this.crash = null; + this.irqRequested = null; + this.irqType = null; + + this.reset(); +}; + +CPU.prototype = { + // IRQ Types + IRQ_NORMAL: 0, + IRQ_NMI: 1, + IRQ_RESET: 2, + + reset: function () { + // Main memory + this.mem = new Array(0x10000); + + for (var i = 0; i < 0x2000; i++) { + this.mem[i] = 0xff; + } + for (var p = 0; p < 4; p++) { + var j = p * 0x800; + this.mem[j + 0x008] = 0xf7; + this.mem[j + 0x009] = 0xef; + this.mem[j + 0x00a] = 0xdf; + this.mem[j + 0x00f] = 0xbf; + } + for (var k = 0x2001; k < this.mem.length; k++) { + this.mem[k] = 0; + } + + // CPU Registers: + this.REG_ACC = 0; + this.REG_X = 0; + this.REG_Y = 0; + // Reset Stack pointer: + this.REG_SP = 0x01ff; + // Reset Program counter: + this.REG_PC = 0x8000 - 1; + this.REG_PC_NEW = 0x8000 - 1; + // Reset Status register: + this.REG_STATUS = 0x28; + + this.setStatus(0x28); + + // Set flags: + this.F_CARRY = 0; + this.F_DECIMAL = 0; + this.F_INTERRUPT = 1; + this.F_INTERRUPT_NEW = 1; + this.F_OVERFLOW = 0; + this.F_SIGN = 0; + this.F_ZERO = 1; + + this.F_NOTUSED = 1; + this.F_NOTUSED_NEW = 1; + this.F_BRK = 1; + this.F_BRK_NEW = 1; + + this.opdata = new OpData().opdata; + this.cyclesToHalt = 0; + + // Reset crash flag: + this.crash = false; + + // Interrupt notification: + this.irqRequested = false; + this.irqType = null; + }, + + // Emulates a single CPU instruction, returns the number of cycles + emulate: function () { + var temp; + var add; + + // Check interrupts: + if (this.irqRequested) { + temp = + this.F_CARRY | + ((this.F_ZERO === 0 ? 1 : 0) << 1) | + (this.F_INTERRUPT << 2) | + (this.F_DECIMAL << 3) | + (this.F_BRK << 4) | + (this.F_NOTUSED << 5) | + (this.F_OVERFLOW << 6) | + (this.F_SIGN << 7); + + this.REG_PC_NEW = this.REG_PC; + this.F_INTERRUPT_NEW = this.F_INTERRUPT; + switch (this.irqType) { + case 0: { + // Normal IRQ: + if (this.F_INTERRUPT !== 0) { + // console.log("Interrupt was masked."); + break; + } + this.doIrq(temp); + // console.log("Did normal IRQ. I="+this.F_INTERRUPT); + break; + } + case 1: { + // NMI: + this.doNonMaskableInterrupt(temp); + break; + } + case 2: { + // Reset: + this.doResetInterrupt(); + break; + } + } + + this.REG_PC = this.REG_PC_NEW; + this.F_INTERRUPT = this.F_INTERRUPT_NEW; + this.F_BRK = this.F_BRK_NEW; + this.irqRequested = false; + } + + var opinf = this.opdata[this.nes.mmap.load(this.REG_PC + 1)]; + var cycleCount = opinf >> 24; + var cycleAdd = 0; + + // Find address mode: + var addrMode = (opinf >> 8) & 0xff; + + // Increment PC by number of op bytes: + var opaddr = this.REG_PC; + this.REG_PC += (opinf >> 16) & 0xff; + + var addr = 0; + switch (addrMode) { + case 0: { + // Zero Page mode. Use the address given after the opcode, + // but without high byte. + addr = this.load(opaddr + 2); + break; + } + case 1: { + // Relative mode. + addr = this.load(opaddr + 2); + if (addr < 0x80) { + addr += this.REG_PC; + } else { + addr += this.REG_PC - 256; + } + break; + } + case 2: { + // Ignore. Address is implied in instruction. + break; + } + case 3: { + // Absolute mode. Use the two bytes following the opcode as + // an address. + addr = this.load16bit(opaddr + 2); + break; + } + case 4: { + // Accumulator mode. The address is in the accumulator + // register. + addr = this.REG_ACC; + break; + } + case 5: { + // Immediate mode. The value is given after the opcode. + addr = this.REG_PC; + break; + } + case 6: { + // Zero Page Indexed mode, X as index. Use the address given + // after the opcode, then add the + // X register to it to get the final address. + addr = (this.load(opaddr + 2) + this.REG_X) & 0xff; + break; + } + case 7: { + // Zero Page Indexed mode, Y as index. Use the address given + // after the opcode, then add the + // Y register to it to get the final address. + addr = (this.load(opaddr + 2) + this.REG_Y) & 0xff; + break; + } + case 8: { + // Absolute Indexed Mode, X as index. Same as zero page + // indexed, but with the high byte. + addr = this.load16bit(opaddr + 2); + if ((addr & 0xff00) !== ((addr + this.REG_X) & 0xff00)) { + cycleAdd = 1; + } + addr += this.REG_X; + break; + } + case 9: { + // Absolute Indexed Mode, Y as index. Same as zero page + // indexed, but with the high byte. + addr = this.load16bit(opaddr + 2); + if ((addr & 0xff00) !== ((addr + this.REG_Y) & 0xff00)) { + cycleAdd = 1; + } + addr += this.REG_Y; + break; + } + case 10: { + // Pre-indexed Indirect mode. Find the 16-bit address + // starting at the given location plus + // the current X register. The value is the contents of that + // address. + addr = this.load(opaddr + 2); + if ((addr & 0xff00) !== ((addr + this.REG_X) & 0xff00)) { + cycleAdd = 1; + } + addr += this.REG_X; + addr &= 0xff; + addr = this.load16bit(addr); + break; + } + case 11: { + // Post-indexed Indirect mode. Find the 16-bit address + // contained in the given location + // (and the one following). Add to that address the contents + // of the Y register. Fetch the value + // stored at that adress. + addr = this.load16bit(this.load(opaddr + 2)); + if ((addr & 0xff00) !== ((addr + this.REG_Y) & 0xff00)) { + cycleAdd = 1; + } + addr += this.REG_Y; + break; + } + case 12: { + // Indirect Absolute mode. Find the 16-bit address contained + // at the given location. + addr = this.load16bit(opaddr + 2); // Find op + if (addr < 0x1fff) { + addr = + this.mem[addr] + + (this.mem[(addr & 0xff00) | (((addr & 0xff) + 1) & 0xff)] << 8); // Read from address given in op + } else { + addr = + this.nes.mmap.load(addr) + + (this.nes.mmap.load( + (addr & 0xff00) | (((addr & 0xff) + 1) & 0xff) + ) << + 8); + } + break; + } + } + // Wrap around for addresses above 0xFFFF: + addr &= 0xffff; + + // ---------------------------------------------------------------------------------------------------- + // Decode & execute instruction: + // ---------------------------------------------------------------------------------------------------- + + // This should be compiled to a jump table. + switch (opinf & 0xff) { + case 0: { + // ******* + // * ADC * + // ******* + + // Add with carry. + temp = this.REG_ACC + this.load(addr) + this.F_CARRY; + + if ( + ((this.REG_ACC ^ this.load(addr)) & 0x80) === 0 && + ((this.REG_ACC ^ temp) & 0x80) !== 0 + ) { + this.F_OVERFLOW = 1; + } else { + this.F_OVERFLOW = 0; + } + this.F_CARRY = temp > 255 ? 1 : 0; + this.F_SIGN = (temp >> 7) & 1; + this.F_ZERO = temp & 0xff; + this.REG_ACC = temp & 255; + cycleCount += cycleAdd; + break; + } + case 1: { + // ******* + // * AND * + // ******* + + // AND memory with accumulator. + this.REG_ACC = this.REG_ACC & this.load(addr); + this.F_SIGN = (this.REG_ACC >> 7) & 1; + this.F_ZERO = this.REG_ACC; + if (addrMode !== 11) cycleCount += cycleAdd; // PostIdxInd = 11 + break; + } + case 2: { + // ******* + // * ASL * + // ******* + + // Shift left one bit + if (addrMode === 4) { + // ADDR_ACC = 4 + + this.F_CARRY = (this.REG_ACC >> 7) & 1; + this.REG_ACC = (this.REG_ACC << 1) & 255; + this.F_SIGN = (this.REG_ACC >> 7) & 1; + this.F_ZERO = this.REG_ACC; + } else { + temp = this.load(addr); + this.F_CARRY = (temp >> 7) & 1; + temp = (temp << 1) & 255; + this.F_SIGN = (temp >> 7) & 1; + this.F_ZERO = temp; + this.write(addr, temp); + } + break; + } + case 3: { + // ******* + // * BCC * + // ******* + + // Branch on carry clear + if (this.F_CARRY === 0) { + cycleCount += (opaddr & 0xff00) !== (addr & 0xff00) ? 2 : 1; + this.REG_PC = addr; + } + break; + } + case 4: { + // ******* + // * BCS * + // ******* + + // Branch on carry set + if (this.F_CARRY === 1) { + cycleCount += (opaddr & 0xff00) !== (addr & 0xff00) ? 2 : 1; + this.REG_PC = addr; + } + break; + } + case 5: { + // ******* + // * BEQ * + // ******* + + // Branch on zero + if (this.F_ZERO === 0) { + cycleCount += (opaddr & 0xff00) !== (addr & 0xff00) ? 2 : 1; + this.REG_PC = addr; + } + break; + } + case 6: { + // ******* + // * BIT * + // ******* + + temp = this.load(addr); + this.F_SIGN = (temp >> 7) & 1; + this.F_OVERFLOW = (temp >> 6) & 1; + temp &= this.REG_ACC; + this.F_ZERO = temp; + break; + } + case 7: { + // ******* + // * BMI * + // ******* + + // Branch on negative result + if (this.F_SIGN === 1) { + cycleCount++; + this.REG_PC = addr; + } + break; + } + case 8: { + // ******* + // * BNE * + // ******* + + // Branch on not zero + if (this.F_ZERO !== 0) { + cycleCount += (opaddr & 0xff00) !== (addr & 0xff00) ? 2 : 1; + this.REG_PC = addr; + } + break; + } + case 9: { + // ******* + // * BPL * + // ******* + + // Branch on positive result + if (this.F_SIGN === 0) { + cycleCount += (opaddr & 0xff00) !== (addr & 0xff00) ? 2 : 1; + this.REG_PC = addr; + } + break; + } + case 10: { + // ******* + // * BRK * + // ******* + + this.REG_PC += 2; + this.push((this.REG_PC >> 8) & 255); + this.push(this.REG_PC & 255); + this.F_BRK = 1; + + this.push( + this.F_CARRY | + ((this.F_ZERO === 0 ? 1 : 0) << 1) | + (this.F_INTERRUPT << 2) | + (this.F_DECIMAL << 3) | + (this.F_BRK << 4) | + (this.F_NOTUSED << 5) | + (this.F_OVERFLOW << 6) | + (this.F_SIGN << 7) + ); + + this.F_INTERRUPT = 1; + //this.REG_PC = load(0xFFFE) | (load(0xFFFF) << 8); + this.REG_PC = this.load16bit(0xfffe); + this.REG_PC--; + break; + } + case 11: { + // ******* + // * BVC * + // ******* + + // Branch on overflow clear + if (this.F_OVERFLOW === 0) { + cycleCount += (opaddr & 0xff00) !== (addr & 0xff00) ? 2 : 1; + this.REG_PC = addr; + } + break; + } + case 12: { + // ******* + // * BVS * + // ******* + + // Branch on overflow set + if (this.F_OVERFLOW === 1) { + cycleCount += (opaddr & 0xff00) !== (addr & 0xff00) ? 2 : 1; + this.REG_PC = addr; + } + break; + } + case 13: { + // ******* + // * CLC * + // ******* + + // Clear carry flag + this.F_CARRY = 0; + break; + } + case 14: { + // ******* + // * CLD * + // ******* + + // Clear decimal flag + this.F_DECIMAL = 0; + break; + } + case 15: { + // ******* + // * CLI * + // ******* + + // Clear interrupt flag + this.F_INTERRUPT = 0; + break; + } + case 16: { + // ******* + // * CLV * + // ******* + + // Clear overflow flag + this.F_OVERFLOW = 0; + break; + } + case 17: { + // ******* + // * CMP * + // ******* + + // Compare memory and accumulator: + temp = this.REG_ACC - this.load(addr); + this.F_CARRY = temp >= 0 ? 1 : 0; + this.F_SIGN = (temp >> 7) & 1; + this.F_ZERO = temp & 0xff; + cycleCount += cycleAdd; + break; + } + case 18: { + // ******* + // * CPX * + // ******* + + // Compare memory and index X: + temp = this.REG_X - this.load(addr); + this.F_CARRY = temp >= 0 ? 1 : 0; + this.F_SIGN = (temp >> 7) & 1; + this.F_ZERO = temp & 0xff; + break; + } + case 19: { + // ******* + // * CPY * + // ******* + + // Compare memory and index Y: + temp = this.REG_Y - this.load(addr); + this.F_CARRY = temp >= 0 ? 1 : 0; + this.F_SIGN = (temp >> 7) & 1; + this.F_ZERO = temp & 0xff; + break; + } + case 20: { + // ******* + // * DEC * + // ******* + + // Decrement memory by one: + temp = (this.load(addr) - 1) & 0xff; + this.F_SIGN = (temp >> 7) & 1; + this.F_ZERO = temp; + this.write(addr, temp); + break; + } + case 21: { + // ******* + // * DEX * + // ******* + + // Decrement index X by one: + this.REG_X = (this.REG_X - 1) & 0xff; + this.F_SIGN = (this.REG_X >> 7) & 1; + this.F_ZERO = this.REG_X; + break; + } + case 22: { + // ******* + // * DEY * + // ******* + + // Decrement index Y by one: + this.REG_Y = (this.REG_Y - 1) & 0xff; + this.F_SIGN = (this.REG_Y >> 7) & 1; + this.F_ZERO = this.REG_Y; + break; + } + case 23: { + // ******* + // * EOR * + // ******* + + // XOR Memory with accumulator, store in accumulator: + this.REG_ACC = (this.load(addr) ^ this.REG_ACC) & 0xff; + this.F_SIGN = (this.REG_ACC >> 7) & 1; + this.F_ZERO = this.REG_ACC; + cycleCount += cycleAdd; + break; + } + case 24: { + // ******* + // * INC * + // ******* + + // Increment memory by one: + temp = (this.load(addr) + 1) & 0xff; + this.F_SIGN = (temp >> 7) & 1; + this.F_ZERO = temp; + this.write(addr, temp & 0xff); + break; + } + case 25: { + // ******* + // * INX * + // ******* + + // Increment index X by one: + this.REG_X = (this.REG_X + 1) & 0xff; + this.F_SIGN = (this.REG_X >> 7) & 1; + this.F_ZERO = this.REG_X; + break; + } + case 26: { + // ******* + // * INY * + // ******* + + // Increment index Y by one: + this.REG_Y++; + this.REG_Y &= 0xff; + this.F_SIGN = (this.REG_Y >> 7) & 1; + this.F_ZERO = this.REG_Y; + break; + } + case 27: { + // ******* + // * JMP * + // ******* + + // Jump to new location: + this.REG_PC = addr - 1; + break; + } + case 28: { + // ******* + // * JSR * + // ******* + + // Jump to new location, saving return address. + // Push return address on stack: + this.push((this.REG_PC >> 8) & 255); + this.push(this.REG_PC & 255); + this.REG_PC = addr - 1; + break; + } + case 29: { + // ******* + // * LDA * + // ******* + + // Load accumulator with memory: + this.REG_ACC = this.load(addr); + this.F_SIGN = (this.REG_ACC >> 7) & 1; + this.F_ZERO = this.REG_ACC; + cycleCount += cycleAdd; + break; + } + case 30: { + // ******* + // * LDX * + // ******* + + // Load index X with memory: + this.REG_X = this.load(addr); + this.F_SIGN = (this.REG_X >> 7) & 1; + this.F_ZERO = this.REG_X; + cycleCount += cycleAdd; + break; + } + case 31: { + // ******* + // * LDY * + // ******* + + // Load index Y with memory: + this.REG_Y = this.load(addr); + this.F_SIGN = (this.REG_Y >> 7) & 1; + this.F_ZERO = this.REG_Y; + cycleCount += cycleAdd; + break; + } + case 32: { + // ******* + // * LSR * + // ******* + + // Shift right one bit: + if (addrMode === 4) { + // ADDR_ACC + + temp = this.REG_ACC & 0xff; + this.F_CARRY = temp & 1; + temp >>= 1; + this.REG_ACC = temp; + } else { + temp = this.load(addr) & 0xff; + this.F_CARRY = temp & 1; + temp >>= 1; + this.write(addr, temp); + } + this.F_SIGN = 0; + this.F_ZERO = temp; + break; + } + case 33: { + // ******* + // * NOP * + // ******* + + // No OPeration. + // Ignore. + break; + } + case 34: { + // ******* + // * ORA * + // ******* + + // OR memory with accumulator, store in accumulator. + temp = (this.load(addr) | this.REG_ACC) & 255; + this.F_SIGN = (temp >> 7) & 1; + this.F_ZERO = temp; + this.REG_ACC = temp; + if (addrMode !== 11) cycleCount += cycleAdd; // PostIdxInd = 11 + break; + } + case 35: { + // ******* + // * PHA * + // ******* + + // Push accumulator on stack + this.push(this.REG_ACC); + break; + } + case 36: { + // ******* + // * PHP * + // ******* + + // Push processor status on stack + this.F_BRK = 1; + this.push( + this.F_CARRY | + ((this.F_ZERO === 0 ? 1 : 0) << 1) | + (this.F_INTERRUPT << 2) | + (this.F_DECIMAL << 3) | + (this.F_BRK << 4) | + (this.F_NOTUSED << 5) | + (this.F_OVERFLOW << 6) | + (this.F_SIGN << 7) + ); + break; + } + case 37: { + // ******* + // * PLA * + // ******* + + // Pull accumulator from stack + this.REG_ACC = this.pull(); + this.F_SIGN = (this.REG_ACC >> 7) & 1; + this.F_ZERO = this.REG_ACC; + break; + } + case 38: { + // ******* + // * PLP * + // ******* + + // Pull processor status from stack + temp = this.pull(); + this.F_CARRY = temp & 1; + this.F_ZERO = ((temp >> 1) & 1) === 1 ? 0 : 1; + this.F_INTERRUPT = (temp >> 2) & 1; + this.F_DECIMAL = (temp >> 3) & 1; + this.F_BRK = (temp >> 4) & 1; + this.F_NOTUSED = (temp >> 5) & 1; + this.F_OVERFLOW = (temp >> 6) & 1; + this.F_SIGN = (temp >> 7) & 1; + + this.F_NOTUSED = 1; + break; + } + case 39: { + // ******* + // * ROL * + // ******* + + // Rotate one bit left + if (addrMode === 4) { + // ADDR_ACC = 4 + + temp = this.REG_ACC; + add = this.F_CARRY; + this.F_CARRY = (temp >> 7) & 1; + temp = ((temp << 1) & 0xff) + add; + this.REG_ACC = temp; + } else { + temp = this.load(addr); + add = this.F_CARRY; + this.F_CARRY = (temp >> 7) & 1; + temp = ((temp << 1) & 0xff) + add; + this.write(addr, temp); + } + this.F_SIGN = (temp >> 7) & 1; + this.F_ZERO = temp; + break; + } + case 40: { + // ******* + // * ROR * + // ******* + + // Rotate one bit right + if (addrMode === 4) { + // ADDR_ACC = 4 + + add = this.F_CARRY << 7; + this.F_CARRY = this.REG_ACC & 1; + temp = (this.REG_ACC >> 1) + add; + this.REG_ACC = temp; + } else { + temp = this.load(addr); + add = this.F_CARRY << 7; + this.F_CARRY = temp & 1; + temp = (temp >> 1) + add; + this.write(addr, temp); + } + this.F_SIGN = (temp >> 7) & 1; + this.F_ZERO = temp; + break; + } + case 41: { + // ******* + // * RTI * + // ******* + + // Return from interrupt. Pull status and PC from stack. + + temp = this.pull(); + this.F_CARRY = temp & 1; + this.F_ZERO = ((temp >> 1) & 1) === 0 ? 1 : 0; + this.F_INTERRUPT = (temp >> 2) & 1; + this.F_DECIMAL = (temp >> 3) & 1; + this.F_BRK = (temp >> 4) & 1; + this.F_NOTUSED = (temp >> 5) & 1; + this.F_OVERFLOW = (temp >> 6) & 1; + this.F_SIGN = (temp >> 7) & 1; + + this.REG_PC = this.pull(); + this.REG_PC += this.pull() << 8; + if (this.REG_PC === 0xffff) { + return; + } + this.REG_PC--; + this.F_NOTUSED = 1; + break; + } + case 42: { + // ******* + // * RTS * + // ******* + + // Return from subroutine. Pull PC from stack. + + this.REG_PC = this.pull(); + this.REG_PC += this.pull() << 8; + + if (this.REG_PC === 0xffff) { + return; // return from NSF play routine: + } + break; + } + case 43: { + // ******* + // * SBC * + // ******* + + temp = this.REG_ACC - this.load(addr) - (1 - this.F_CARRY); + this.F_SIGN = (temp >> 7) & 1; + this.F_ZERO = temp & 0xff; + if ( + ((this.REG_ACC ^ temp) & 0x80) !== 0 && + ((this.REG_ACC ^ this.load(addr)) & 0x80) !== 0 + ) { + this.F_OVERFLOW = 1; + } else { + this.F_OVERFLOW = 0; + } + this.F_CARRY = temp < 0 ? 0 : 1; + this.REG_ACC = temp & 0xff; + if (addrMode !== 11) cycleCount += cycleAdd; // PostIdxInd = 11 + break; + } + case 44: { + // ******* + // * SEC * + // ******* + + // Set carry flag + this.F_CARRY = 1; + break; + } + case 45: { + // ******* + // * SED * + // ******* + + // Set decimal mode + this.F_DECIMAL = 1; + break; + } + case 46: { + // ******* + // * SEI * + // ******* + + // Set interrupt disable status + this.F_INTERRUPT = 1; + break; + } + case 47: { + // ******* + // * STA * + // ******* + + // Store accumulator in memory + this.write(addr, this.REG_ACC); + break; + } + case 48: { + // ******* + // * STX * + // ******* + + // Store index X in memory + this.write(addr, this.REG_X); + break; + } + case 49: { + // ******* + // * STY * + // ******* + + // Store index Y in memory: + this.write(addr, this.REG_Y); + break; + } + case 50: { + // ******* + // * TAX * + // ******* + + // Transfer accumulator to index X: + this.REG_X = this.REG_ACC; + this.F_SIGN = (this.REG_ACC >> 7) & 1; + this.F_ZERO = this.REG_ACC; + break; + } + case 51: { + // ******* + // * TAY * + // ******* + + // Transfer accumulator to index Y: + this.REG_Y = this.REG_ACC; + this.F_SIGN = (this.REG_ACC >> 7) & 1; + this.F_ZERO = this.REG_ACC; + break; + } + case 52: { + // ******* + // * TSX * + // ******* + + // Transfer stack pointer to index X: + this.REG_X = this.REG_SP - 0x0100; + this.F_SIGN = (this.REG_SP >> 7) & 1; + this.F_ZERO = this.REG_X; + break; + } + case 53: { + // ******* + // * TXA * + // ******* + + // Transfer index X to accumulator: + this.REG_ACC = this.REG_X; + this.F_SIGN = (this.REG_X >> 7) & 1; + this.F_ZERO = this.REG_X; + break; + } + case 54: { + // ******* + // * TXS * + // ******* + + // Transfer index X to stack pointer: + this.REG_SP = this.REG_X + 0x0100; + this.stackWrap(); + break; + } + case 55: { + // ******* + // * TYA * + // ******* + + // Transfer index Y to accumulator: + this.REG_ACC = this.REG_Y; + this.F_SIGN = (this.REG_Y >> 7) & 1; + this.F_ZERO = this.REG_Y; + break; + } + case 56: { + // ******* + // * ALR * + // ******* + + // Shift right one bit after ANDing: + temp = this.REG_ACC & this.load(addr); + this.F_CARRY = temp & 1; + this.REG_ACC = this.F_ZERO = temp >> 1; + this.F_SIGN = 0; + break; + } + case 57: { + // ******* + // * ANC * + // ******* + + // AND accumulator, setting carry to bit 7 result. + this.REG_ACC = this.F_ZERO = this.REG_ACC & this.load(addr); + this.F_CARRY = this.F_SIGN = (this.REG_ACC >> 7) & 1; + break; + } + case 58: { + // ******* + // * ARR * + // ******* + + // Rotate right one bit after ANDing: + temp = this.REG_ACC & this.load(addr); + this.REG_ACC = this.F_ZERO = (temp >> 1) + (this.F_CARRY << 7); + this.F_SIGN = this.F_CARRY; + this.F_CARRY = (temp >> 7) & 1; + this.F_OVERFLOW = ((temp >> 7) ^ (temp >> 6)) & 1; + break; + } + case 59: { + // ******* + // * AXS * + // ******* + + // Set X to (X AND A) - value. + temp = (this.REG_X & this.REG_ACC) - this.load(addr); + this.F_SIGN = (temp >> 7) & 1; + this.F_ZERO = temp & 0xff; + if ( + ((this.REG_X ^ temp) & 0x80) !== 0 && + ((this.REG_X ^ this.load(addr)) & 0x80) !== 0 + ) { + this.F_OVERFLOW = 1; + } else { + this.F_OVERFLOW = 0; + } + this.F_CARRY = temp < 0 ? 0 : 1; + this.REG_X = temp & 0xff; + break; + } + case 60: { + // ******* + // * LAX * + // ******* + + // Load A and X with memory: + this.REG_ACC = this.REG_X = this.F_ZERO = this.load(addr); + this.F_SIGN = (this.REG_ACC >> 7) & 1; + cycleCount += cycleAdd; + break; + } + case 61: { + // ******* + // * SAX * + // ******* + + // Store A AND X in memory: + this.write(addr, this.REG_ACC & this.REG_X); + break; + } + case 62: { + // ******* + // * DCP * + // ******* + + // Decrement memory by one: + temp = (this.load(addr) - 1) & 0xff; + this.write(addr, temp); + + // Then compare with the accumulator: + temp = this.REG_ACC - temp; + this.F_CARRY = temp >= 0 ? 1 : 0; + this.F_SIGN = (temp >> 7) & 1; + this.F_ZERO = temp & 0xff; + if (addrMode !== 11) cycleCount += cycleAdd; // PostIdxInd = 11 + break; + } + case 63: { + // ******* + // * ISC * + // ******* + + // Increment memory by one: + temp = (this.load(addr) + 1) & 0xff; + this.write(addr, temp); + + // Then subtract from the accumulator: + temp = this.REG_ACC - temp - (1 - this.F_CARRY); + this.F_SIGN = (temp >> 7) & 1; + this.F_ZERO = temp & 0xff; + if ( + ((this.REG_ACC ^ temp) & 0x80) !== 0 && + ((this.REG_ACC ^ this.load(addr)) & 0x80) !== 0 + ) { + this.F_OVERFLOW = 1; + } else { + this.F_OVERFLOW = 0; + } + this.F_CARRY = temp < 0 ? 0 : 1; + this.REG_ACC = temp & 0xff; + if (addrMode !== 11) cycleCount += cycleAdd; // PostIdxInd = 11 + break; + } + case 64: { + // ******* + // * RLA * + // ******* + + // Rotate one bit left + temp = this.load(addr); + add = this.F_CARRY; + this.F_CARRY = (temp >> 7) & 1; + temp = ((temp << 1) & 0xff) + add; + this.write(addr, temp); + + // Then AND with the accumulator. + this.REG_ACC = this.REG_ACC & temp; + this.F_SIGN = (this.REG_ACC >> 7) & 1; + this.F_ZERO = this.REG_ACC; + if (addrMode !== 11) cycleCount += cycleAdd; // PostIdxInd = 11 + break; + } + case 65: { + // ******* + // * RRA * + // ******* + + // Rotate one bit right + temp = this.load(addr); + add = this.F_CARRY << 7; + this.F_CARRY = temp & 1; + temp = (temp >> 1) + add; + this.write(addr, temp); + + // Then add to the accumulator + temp = this.REG_ACC + this.load(addr) + this.F_CARRY; + + if ( + ((this.REG_ACC ^ this.load(addr)) & 0x80) === 0 && + ((this.REG_ACC ^ temp) & 0x80) !== 0 + ) { + this.F_OVERFLOW = 1; + } else { + this.F_OVERFLOW = 0; + } + this.F_CARRY = temp > 255 ? 1 : 0; + this.F_SIGN = (temp >> 7) & 1; + this.F_ZERO = temp & 0xff; + this.REG_ACC = temp & 255; + if (addrMode !== 11) cycleCount += cycleAdd; // PostIdxInd = 11 + break; + } + case 66: { + // ******* + // * SLO * + // ******* + + // Shift one bit left + temp = this.load(addr); + this.F_CARRY = (temp >> 7) & 1; + temp = (temp << 1) & 255; + this.write(addr, temp); + + // Then OR with the accumulator. + this.REG_ACC = this.REG_ACC | temp; + this.F_SIGN = (this.REG_ACC >> 7) & 1; + this.F_ZERO = this.REG_ACC; + if (addrMode !== 11) cycleCount += cycleAdd; // PostIdxInd = 11 + break; + } + case 67: { + // ******* + // * SRE * + // ******* + + // Shift one bit right + temp = this.load(addr) & 0xff; + this.F_CARRY = temp & 1; + temp >>= 1; + this.write(addr, temp); + + // Then XOR with the accumulator. + this.REG_ACC = this.REG_ACC ^ temp; + this.F_SIGN = (this.REG_ACC >> 7) & 1; + this.F_ZERO = this.REG_ACC; + if (addrMode !== 11) cycleCount += cycleAdd; // PostIdxInd = 11 + break; + } + case 68: { + // ******* + // * SKB * + // ******* + + // Do nothing + break; + } + case 69: { + // ******* + // * IGN * + // ******* + + // Do nothing but load. + // TODO: Properly implement the double-reads. + this.load(addr); + if (addrMode !== 11) cycleCount += cycleAdd; // PostIdxInd = 11 + break; + } + + default: { + // ******* + // * ??? * + // ******* + + this.nes.stop(); + this.nes.crashMessage = + "Game crashed, invalid opcode at address $" + opaddr.toString(16); + break; + } + } // end of switch + + return cycleCount; + }, + + load: function (addr) { + if (addr < 0x2000) { + return this.mem[addr & 0x7ff]; + } else { + return this.nes.mmap.load(addr); + } + }, + + load16bit: function (addr) { + if (addr < 0x1fff) { + return this.mem[addr & 0x7ff] | (this.mem[(addr + 1) & 0x7ff] << 8); + } else { + return this.nes.mmap.load(addr) | (this.nes.mmap.load(addr + 1) << 8); + } + }, + + write: function (addr, val) { + if (addr < 0x2000) { + this.mem[addr & 0x7ff] = val; + } else { + this.nes.mmap.write(addr, val); + } + }, + + requestIrq: function (type) { + if (this.irqRequested) { + if (type === this.IRQ_NORMAL) { + return; + } + // console.log("too fast irqs. type="+type); + } + this.irqRequested = true; + this.irqType = type; + }, + + push: function (value) { + this.nes.mmap.write(this.REG_SP, value); + this.REG_SP--; + this.REG_SP = 0x0100 | (this.REG_SP & 0xff); + }, + + stackWrap: function () { + this.REG_SP = 0x0100 | (this.REG_SP & 0xff); + }, + + pull: function () { + this.REG_SP++; + this.REG_SP = 0x0100 | (this.REG_SP & 0xff); + return this.nes.mmap.load(this.REG_SP); + }, + + pageCrossed: function (addr1, addr2) { + return (addr1 & 0xff00) !== (addr2 & 0xff00); + }, + + haltCycles: function (cycles) { + this.cyclesToHalt += cycles; + }, + + doNonMaskableInterrupt: function (status) { + if ((this.nes.mmap.load(0x2000) & 128) !== 0) { + // Check whether VBlank Interrupts are enabled + + this.REG_PC_NEW++; + this.push((this.REG_PC_NEW >> 8) & 0xff); + this.push(this.REG_PC_NEW & 0xff); + //this.F_INTERRUPT_NEW = 1; + this.push(status); + + this.REG_PC_NEW = + this.nes.mmap.load(0xfffa) | (this.nes.mmap.load(0xfffb) << 8); + this.REG_PC_NEW--; + } + }, + + doResetInterrupt: function () { + this.REG_PC_NEW = + this.nes.mmap.load(0xfffc) | (this.nes.mmap.load(0xfffd) << 8); + this.REG_PC_NEW--; + }, + + doIrq: function (status) { + this.REG_PC_NEW++; + this.push((this.REG_PC_NEW >> 8) & 0xff); + this.push(this.REG_PC_NEW & 0xff); + this.push(status); + this.F_INTERRUPT_NEW = 1; + this.F_BRK_NEW = 0; + + this.REG_PC_NEW = + this.nes.mmap.load(0xfffe) | (this.nes.mmap.load(0xffff) << 8); + this.REG_PC_NEW--; + }, + + getStatus: function () { + return ( + this.F_CARRY | + (this.F_ZERO << 1) | + (this.F_INTERRUPT << 2) | + (this.F_DECIMAL << 3) | + (this.F_BRK << 4) | + (this.F_NOTUSED << 5) | + (this.F_OVERFLOW << 6) | + (this.F_SIGN << 7) + ); + }, + + setStatus: function (st) { + this.F_CARRY = st & 1; + this.F_ZERO = (st >> 1) & 1; + this.F_INTERRUPT = (st >> 2) & 1; + this.F_DECIMAL = (st >> 3) & 1; + this.F_BRK = (st >> 4) & 1; + this.F_NOTUSED = (st >> 5) & 1; + this.F_OVERFLOW = (st >> 6) & 1; + this.F_SIGN = (st >> 7) & 1; + }, + + JSON_PROPERTIES: [ + "mem", + "cyclesToHalt", + "irqRequested", + "irqType", + // Registers + "REG_ACC", + "REG_X", + "REG_Y", + "REG_SP", + "REG_PC", + "REG_PC_NEW", + "REG_STATUS", + // Status + "F_CARRY", + "F_DECIMAL", + "F_INTERRUPT", + "F_INTERRUPT_NEW", + "F_OVERFLOW", + "F_SIGN", + "F_ZERO", + "F_NOTUSED", + "F_NOTUSED_NEW", + "F_BRK", + "F_BRK_NEW", + ], + + toJSON: function () { + return utils.toJSON(this); + }, + + fromJSON: function (s) { + utils.fromJSON(this, s); + }, +}; + +// Generates and provides an array of details about instructions +var OpData = function () { + this.opdata = new Array(256); + + // Set all to invalid instruction (to detect crashes): + for (var i = 0; i < 256; i++) this.opdata[i] = 0xff; + + // Now fill in all valid opcodes: + + // ADC: + this.setOp(this.INS_ADC, 0x69, this.ADDR_IMM, 2, 2); + this.setOp(this.INS_ADC, 0x65, this.ADDR_ZP, 2, 3); + this.setOp(this.INS_ADC, 0x75, this.ADDR_ZPX, 2, 4); + this.setOp(this.INS_ADC, 0x6d, this.ADDR_ABS, 3, 4); + this.setOp(this.INS_ADC, 0x7d, this.ADDR_ABSX, 3, 4); + this.setOp(this.INS_ADC, 0x79, this.ADDR_ABSY, 3, 4); + this.setOp(this.INS_ADC, 0x61, this.ADDR_PREIDXIND, 2, 6); + this.setOp(this.INS_ADC, 0x71, this.ADDR_POSTIDXIND, 2, 5); + + // AND: + this.setOp(this.INS_AND, 0x29, this.ADDR_IMM, 2, 2); + this.setOp(this.INS_AND, 0x25, this.ADDR_ZP, 2, 3); + this.setOp(this.INS_AND, 0x35, this.ADDR_ZPX, 2, 4); + this.setOp(this.INS_AND, 0x2d, this.ADDR_ABS, 3, 4); + this.setOp(this.INS_AND, 0x3d, this.ADDR_ABSX, 3, 4); + this.setOp(this.INS_AND, 0x39, this.ADDR_ABSY, 3, 4); + this.setOp(this.INS_AND, 0x21, this.ADDR_PREIDXIND, 2, 6); + this.setOp(this.INS_AND, 0x31, this.ADDR_POSTIDXIND, 2, 5); + + // ASL: + this.setOp(this.INS_ASL, 0x0a, this.ADDR_ACC, 1, 2); + this.setOp(this.INS_ASL, 0x06, this.ADDR_ZP, 2, 5); + this.setOp(this.INS_ASL, 0x16, this.ADDR_ZPX, 2, 6); + this.setOp(this.INS_ASL, 0x0e, this.ADDR_ABS, 3, 6); + this.setOp(this.INS_ASL, 0x1e, this.ADDR_ABSX, 3, 7); + + // BCC: + this.setOp(this.INS_BCC, 0x90, this.ADDR_REL, 2, 2); + + // BCS: + this.setOp(this.INS_BCS, 0xb0, this.ADDR_REL, 2, 2); + + // BEQ: + this.setOp(this.INS_BEQ, 0xf0, this.ADDR_REL, 2, 2); + + // BIT: + this.setOp(this.INS_BIT, 0x24, this.ADDR_ZP, 2, 3); + this.setOp(this.INS_BIT, 0x2c, this.ADDR_ABS, 3, 4); + + // BMI: + this.setOp(this.INS_BMI, 0x30, this.ADDR_REL, 2, 2); + + // BNE: + this.setOp(this.INS_BNE, 0xd0, this.ADDR_REL, 2, 2); + + // BPL: + this.setOp(this.INS_BPL, 0x10, this.ADDR_REL, 2, 2); + + // BRK: + this.setOp(this.INS_BRK, 0x00, this.ADDR_IMP, 1, 7); + + // BVC: + this.setOp(this.INS_BVC, 0x50, this.ADDR_REL, 2, 2); + + // BVS: + this.setOp(this.INS_BVS, 0x70, this.ADDR_REL, 2, 2); + + // CLC: + this.setOp(this.INS_CLC, 0x18, this.ADDR_IMP, 1, 2); + + // CLD: + this.setOp(this.INS_CLD, 0xd8, this.ADDR_IMP, 1, 2); + + // CLI: + this.setOp(this.INS_CLI, 0x58, this.ADDR_IMP, 1, 2); + + // CLV: + this.setOp(this.INS_CLV, 0xb8, this.ADDR_IMP, 1, 2); + + // CMP: + this.setOp(this.INS_CMP, 0xc9, this.ADDR_IMM, 2, 2); + this.setOp(this.INS_CMP, 0xc5, this.ADDR_ZP, 2, 3); + this.setOp(this.INS_CMP, 0xd5, this.ADDR_ZPX, 2, 4); + this.setOp(this.INS_CMP, 0xcd, this.ADDR_ABS, 3, 4); + this.setOp(this.INS_CMP, 0xdd, this.ADDR_ABSX, 3, 4); + this.setOp(this.INS_CMP, 0xd9, this.ADDR_ABSY, 3, 4); + this.setOp(this.INS_CMP, 0xc1, this.ADDR_PREIDXIND, 2, 6); + this.setOp(this.INS_CMP, 0xd1, this.ADDR_POSTIDXIND, 2, 5); + + // CPX: + this.setOp(this.INS_CPX, 0xe0, this.ADDR_IMM, 2, 2); + this.setOp(this.INS_CPX, 0xe4, this.ADDR_ZP, 2, 3); + this.setOp(this.INS_CPX, 0xec, this.ADDR_ABS, 3, 4); + + // CPY: + this.setOp(this.INS_CPY, 0xc0, this.ADDR_IMM, 2, 2); + this.setOp(this.INS_CPY, 0xc4, this.ADDR_ZP, 2, 3); + this.setOp(this.INS_CPY, 0xcc, this.ADDR_ABS, 3, 4); + + // DEC: + this.setOp(this.INS_DEC, 0xc6, this.ADDR_ZP, 2, 5); + this.setOp(this.INS_DEC, 0xd6, this.ADDR_ZPX, 2, 6); + this.setOp(this.INS_DEC, 0xce, this.ADDR_ABS, 3, 6); + this.setOp(this.INS_DEC, 0xde, this.ADDR_ABSX, 3, 7); + + // DEX: + this.setOp(this.INS_DEX, 0xca, this.ADDR_IMP, 1, 2); + + // DEY: + this.setOp(this.INS_DEY, 0x88, this.ADDR_IMP, 1, 2); + + // EOR: + this.setOp(this.INS_EOR, 0x49, this.ADDR_IMM, 2, 2); + this.setOp(this.INS_EOR, 0x45, this.ADDR_ZP, 2, 3); + this.setOp(this.INS_EOR, 0x55, this.ADDR_ZPX, 2, 4); + this.setOp(this.INS_EOR, 0x4d, this.ADDR_ABS, 3, 4); + this.setOp(this.INS_EOR, 0x5d, this.ADDR_ABSX, 3, 4); + this.setOp(this.INS_EOR, 0x59, this.ADDR_ABSY, 3, 4); + this.setOp(this.INS_EOR, 0x41, this.ADDR_PREIDXIND, 2, 6); + this.setOp(this.INS_EOR, 0x51, this.ADDR_POSTIDXIND, 2, 5); + + // INC: + this.setOp(this.INS_INC, 0xe6, this.ADDR_ZP, 2, 5); + this.setOp(this.INS_INC, 0xf6, this.ADDR_ZPX, 2, 6); + this.setOp(this.INS_INC, 0xee, this.ADDR_ABS, 3, 6); + this.setOp(this.INS_INC, 0xfe, this.ADDR_ABSX, 3, 7); + + // INX: + this.setOp(this.INS_INX, 0xe8, this.ADDR_IMP, 1, 2); + + // INY: + this.setOp(this.INS_INY, 0xc8, this.ADDR_IMP, 1, 2); + + // JMP: + this.setOp(this.INS_JMP, 0x4c, this.ADDR_ABS, 3, 3); + this.setOp(this.INS_JMP, 0x6c, this.ADDR_INDABS, 3, 5); + + // JSR: + this.setOp(this.INS_JSR, 0x20, this.ADDR_ABS, 3, 6); + + // LDA: + this.setOp(this.INS_LDA, 0xa9, this.ADDR_IMM, 2, 2); + this.setOp(this.INS_LDA, 0xa5, this.ADDR_ZP, 2, 3); + this.setOp(this.INS_LDA, 0xb5, this.ADDR_ZPX, 2, 4); + this.setOp(this.INS_LDA, 0xad, this.ADDR_ABS, 3, 4); + this.setOp(this.INS_LDA, 0xbd, this.ADDR_ABSX, 3, 4); + this.setOp(this.INS_LDA, 0xb9, this.ADDR_ABSY, 3, 4); + this.setOp(this.INS_LDA, 0xa1, this.ADDR_PREIDXIND, 2, 6); + this.setOp(this.INS_LDA, 0xb1, this.ADDR_POSTIDXIND, 2, 5); + + // LDX: + this.setOp(this.INS_LDX, 0xa2, this.ADDR_IMM, 2, 2); + this.setOp(this.INS_LDX, 0xa6, this.ADDR_ZP, 2, 3); + this.setOp(this.INS_LDX, 0xb6, this.ADDR_ZPY, 2, 4); + this.setOp(this.INS_LDX, 0xae, this.ADDR_ABS, 3, 4); + this.setOp(this.INS_LDX, 0xbe, this.ADDR_ABSY, 3, 4); + + // LDY: + this.setOp(this.INS_LDY, 0xa0, this.ADDR_IMM, 2, 2); + this.setOp(this.INS_LDY, 0xa4, this.ADDR_ZP, 2, 3); + this.setOp(this.INS_LDY, 0xb4, this.ADDR_ZPX, 2, 4); + this.setOp(this.INS_LDY, 0xac, this.ADDR_ABS, 3, 4); + this.setOp(this.INS_LDY, 0xbc, this.ADDR_ABSX, 3, 4); + + // LSR: + this.setOp(this.INS_LSR, 0x4a, this.ADDR_ACC, 1, 2); + this.setOp(this.INS_LSR, 0x46, this.ADDR_ZP, 2, 5); + this.setOp(this.INS_LSR, 0x56, this.ADDR_ZPX, 2, 6); + this.setOp(this.INS_LSR, 0x4e, this.ADDR_ABS, 3, 6); + this.setOp(this.INS_LSR, 0x5e, this.ADDR_ABSX, 3, 7); + + // NOP: + this.setOp(this.INS_NOP, 0x1a, this.ADDR_IMP, 1, 2); + this.setOp(this.INS_NOP, 0x3a, this.ADDR_IMP, 1, 2); + this.setOp(this.INS_NOP, 0x5a, this.ADDR_IMP, 1, 2); + this.setOp(this.INS_NOP, 0x7a, this.ADDR_IMP, 1, 2); + this.setOp(this.INS_NOP, 0xda, this.ADDR_IMP, 1, 2); + this.setOp(this.INS_NOP, 0xea, this.ADDR_IMP, 1, 2); + this.setOp(this.INS_NOP, 0xfa, this.ADDR_IMP, 1, 2); + + // ORA: + this.setOp(this.INS_ORA, 0x09, this.ADDR_IMM, 2, 2); + this.setOp(this.INS_ORA, 0x05, this.ADDR_ZP, 2, 3); + this.setOp(this.INS_ORA, 0x15, this.ADDR_ZPX, 2, 4); + this.setOp(this.INS_ORA, 0x0d, this.ADDR_ABS, 3, 4); + this.setOp(this.INS_ORA, 0x1d, this.ADDR_ABSX, 3, 4); + this.setOp(this.INS_ORA, 0x19, this.ADDR_ABSY, 3, 4); + this.setOp(this.INS_ORA, 0x01, this.ADDR_PREIDXIND, 2, 6); + this.setOp(this.INS_ORA, 0x11, this.ADDR_POSTIDXIND, 2, 5); + + // PHA: + this.setOp(this.INS_PHA, 0x48, this.ADDR_IMP, 1, 3); + + // PHP: + this.setOp(this.INS_PHP, 0x08, this.ADDR_IMP, 1, 3); + + // PLA: + this.setOp(this.INS_PLA, 0x68, this.ADDR_IMP, 1, 4); + + // PLP: + this.setOp(this.INS_PLP, 0x28, this.ADDR_IMP, 1, 4); + + // ROL: + this.setOp(this.INS_ROL, 0x2a, this.ADDR_ACC, 1, 2); + this.setOp(this.INS_ROL, 0x26, this.ADDR_ZP, 2, 5); + this.setOp(this.INS_ROL, 0x36, this.ADDR_ZPX, 2, 6); + this.setOp(this.INS_ROL, 0x2e, this.ADDR_ABS, 3, 6); + this.setOp(this.INS_ROL, 0x3e, this.ADDR_ABSX, 3, 7); + + // ROR: + this.setOp(this.INS_ROR, 0x6a, this.ADDR_ACC, 1, 2); + this.setOp(this.INS_ROR, 0x66, this.ADDR_ZP, 2, 5); + this.setOp(this.INS_ROR, 0x76, this.ADDR_ZPX, 2, 6); + this.setOp(this.INS_ROR, 0x6e, this.ADDR_ABS, 3, 6); + this.setOp(this.INS_ROR, 0x7e, this.ADDR_ABSX, 3, 7); + + // RTI: + this.setOp(this.INS_RTI, 0x40, this.ADDR_IMP, 1, 6); + + // RTS: + this.setOp(this.INS_RTS, 0x60, this.ADDR_IMP, 1, 6); + + // SBC: + this.setOp(this.INS_SBC, 0xe9, this.ADDR_IMM, 2, 2); + this.setOp(this.INS_SBC, 0xe5, this.ADDR_ZP, 2, 3); + this.setOp(this.INS_SBC, 0xf5, this.ADDR_ZPX, 2, 4); + this.setOp(this.INS_SBC, 0xed, this.ADDR_ABS, 3, 4); + this.setOp(this.INS_SBC, 0xfd, this.ADDR_ABSX, 3, 4); + this.setOp(this.INS_SBC, 0xf9, this.ADDR_ABSY, 3, 4); + this.setOp(this.INS_SBC, 0xe1, this.ADDR_PREIDXIND, 2, 6); + this.setOp(this.INS_SBC, 0xf1, this.ADDR_POSTIDXIND, 2, 5); + + // SEC: + this.setOp(this.INS_SEC, 0x38, this.ADDR_IMP, 1, 2); + + // SED: + this.setOp(this.INS_SED, 0xf8, this.ADDR_IMP, 1, 2); + + // SEI: + this.setOp(this.INS_SEI, 0x78, this.ADDR_IMP, 1, 2); + + // STA: + this.setOp(this.INS_STA, 0x85, this.ADDR_ZP, 2, 3); + this.setOp(this.INS_STA, 0x95, this.ADDR_ZPX, 2, 4); + this.setOp(this.INS_STA, 0x8d, this.ADDR_ABS, 3, 4); + this.setOp(this.INS_STA, 0x9d, this.ADDR_ABSX, 3, 5); + this.setOp(this.INS_STA, 0x99, this.ADDR_ABSY, 3, 5); + this.setOp(this.INS_STA, 0x81, this.ADDR_PREIDXIND, 2, 6); + this.setOp(this.INS_STA, 0x91, this.ADDR_POSTIDXIND, 2, 6); + + // STX: + this.setOp(this.INS_STX, 0x86, this.ADDR_ZP, 2, 3); + this.setOp(this.INS_STX, 0x96, this.ADDR_ZPY, 2, 4); + this.setOp(this.INS_STX, 0x8e, this.ADDR_ABS, 3, 4); + + // STY: + this.setOp(this.INS_STY, 0x84, this.ADDR_ZP, 2, 3); + this.setOp(this.INS_STY, 0x94, this.ADDR_ZPX, 2, 4); + this.setOp(this.INS_STY, 0x8c, this.ADDR_ABS, 3, 4); + + // TAX: + this.setOp(this.INS_TAX, 0xaa, this.ADDR_IMP, 1, 2); + + // TAY: + this.setOp(this.INS_TAY, 0xa8, this.ADDR_IMP, 1, 2); + + // TSX: + this.setOp(this.INS_TSX, 0xba, this.ADDR_IMP, 1, 2); + + // TXA: + this.setOp(this.INS_TXA, 0x8a, this.ADDR_IMP, 1, 2); + + // TXS: + this.setOp(this.INS_TXS, 0x9a, this.ADDR_IMP, 1, 2); + + // TYA: + this.setOp(this.INS_TYA, 0x98, this.ADDR_IMP, 1, 2); + + // ALR: + this.setOp(this.INS_ALR, 0x4b, this.ADDR_IMM, 2, 2); + + // ANC: + this.setOp(this.INS_ANC, 0x0b, this.ADDR_IMM, 2, 2); + this.setOp(this.INS_ANC, 0x2b, this.ADDR_IMM, 2, 2); + + // ARR: + this.setOp(this.INS_ARR, 0x6b, this.ADDR_IMM, 2, 2); + + // AXS: + this.setOp(this.INS_AXS, 0xcb, this.ADDR_IMM, 2, 2); + + // LAX: + this.setOp(this.INS_LAX, 0xa3, this.ADDR_PREIDXIND, 2, 6); + this.setOp(this.INS_LAX, 0xa7, this.ADDR_ZP, 2, 3); + this.setOp(this.INS_LAX, 0xaf, this.ADDR_ABS, 3, 4); + this.setOp(this.INS_LAX, 0xb3, this.ADDR_POSTIDXIND, 2, 5); + this.setOp(this.INS_LAX, 0xb7, this.ADDR_ZPY, 2, 4); + this.setOp(this.INS_LAX, 0xbf, this.ADDR_ABSY, 3, 4); + + // SAX: + this.setOp(this.INS_SAX, 0x83, this.ADDR_PREIDXIND, 2, 6); + this.setOp(this.INS_SAX, 0x87, this.ADDR_ZP, 2, 3); + this.setOp(this.INS_SAX, 0x8f, this.ADDR_ABS, 3, 4); + this.setOp(this.INS_SAX, 0x97, this.ADDR_ZPY, 2, 4); + + // DCP: + this.setOp(this.INS_DCP, 0xc3, this.ADDR_PREIDXIND, 2, 8); + this.setOp(this.INS_DCP, 0xc7, this.ADDR_ZP, 2, 5); + this.setOp(this.INS_DCP, 0xcf, this.ADDR_ABS, 3, 6); + this.setOp(this.INS_DCP, 0xd3, this.ADDR_POSTIDXIND, 2, 8); + this.setOp(this.INS_DCP, 0xd7, this.ADDR_ZPX, 2, 6); + this.setOp(this.INS_DCP, 0xdb, this.ADDR_ABSY, 3, 7); + this.setOp(this.INS_DCP, 0xdf, this.ADDR_ABSX, 3, 7); + + // ISC: + this.setOp(this.INS_ISC, 0xe3, this.ADDR_PREIDXIND, 2, 8); + this.setOp(this.INS_ISC, 0xe7, this.ADDR_ZP, 2, 5); + this.setOp(this.INS_ISC, 0xef, this.ADDR_ABS, 3, 6); + this.setOp(this.INS_ISC, 0xf3, this.ADDR_POSTIDXIND, 2, 8); + this.setOp(this.INS_ISC, 0xf7, this.ADDR_ZPX, 2, 6); + this.setOp(this.INS_ISC, 0xfb, this.ADDR_ABSY, 3, 7); + this.setOp(this.INS_ISC, 0xff, this.ADDR_ABSX, 3, 7); + + // RLA: + this.setOp(this.INS_RLA, 0x23, this.ADDR_PREIDXIND, 2, 8); + this.setOp(this.INS_RLA, 0x27, this.ADDR_ZP, 2, 5); + this.setOp(this.INS_RLA, 0x2f, this.ADDR_ABS, 3, 6); + this.setOp(this.INS_RLA, 0x33, this.ADDR_POSTIDXIND, 2, 8); + this.setOp(this.INS_RLA, 0x37, this.ADDR_ZPX, 2, 6); + this.setOp(this.INS_RLA, 0x3b, this.ADDR_ABSY, 3, 7); + this.setOp(this.INS_RLA, 0x3f, this.ADDR_ABSX, 3, 7); + + // RRA: + this.setOp(this.INS_RRA, 0x63, this.ADDR_PREIDXIND, 2, 8); + this.setOp(this.INS_RRA, 0x67, this.ADDR_ZP, 2, 5); + this.setOp(this.INS_RRA, 0x6f, this.ADDR_ABS, 3, 6); + this.setOp(this.INS_RRA, 0x73, this.ADDR_POSTIDXIND, 2, 8); + this.setOp(this.INS_RRA, 0x77, this.ADDR_ZPX, 2, 6); + this.setOp(this.INS_RRA, 0x7b, this.ADDR_ABSY, 3, 7); + this.setOp(this.INS_RRA, 0x7f, this.ADDR_ABSX, 3, 7); + + // SLO: + this.setOp(this.INS_SLO, 0x03, this.ADDR_PREIDXIND, 2, 8); + this.setOp(this.INS_SLO, 0x07, this.ADDR_ZP, 2, 5); + this.setOp(this.INS_SLO, 0x0f, this.ADDR_ABS, 3, 6); + this.setOp(this.INS_SLO, 0x13, this.ADDR_POSTIDXIND, 2, 8); + this.setOp(this.INS_SLO, 0x17, this.ADDR_ZPX, 2, 6); + this.setOp(this.INS_SLO, 0x1b, this.ADDR_ABSY, 3, 7); + this.setOp(this.INS_SLO, 0x1f, this.ADDR_ABSX, 3, 7); + + // SRE: + this.setOp(this.INS_SRE, 0x43, this.ADDR_PREIDXIND, 2, 8); + this.setOp(this.INS_SRE, 0x47, this.ADDR_ZP, 2, 5); + this.setOp(this.INS_SRE, 0x4f, this.ADDR_ABS, 3, 6); + this.setOp(this.INS_SRE, 0x53, this.ADDR_POSTIDXIND, 2, 8); + this.setOp(this.INS_SRE, 0x57, this.ADDR_ZPX, 2, 6); + this.setOp(this.INS_SRE, 0x5b, this.ADDR_ABSY, 3, 7); + this.setOp(this.INS_SRE, 0x5f, this.ADDR_ABSX, 3, 7); + + // SKB: + this.setOp(this.INS_SKB, 0x80, this.ADDR_IMM, 2, 2); + this.setOp(this.INS_SKB, 0x82, this.ADDR_IMM, 2, 2); + this.setOp(this.INS_SKB, 0x89, this.ADDR_IMM, 2, 2); + this.setOp(this.INS_SKB, 0xc2, this.ADDR_IMM, 2, 2); + this.setOp(this.INS_SKB, 0xe2, this.ADDR_IMM, 2, 2); + + // SKB: + this.setOp(this.INS_IGN, 0x0c, this.ADDR_ABS, 3, 4); + this.setOp(this.INS_IGN, 0x1c, this.ADDR_ABSX, 3, 4); + this.setOp(this.INS_IGN, 0x3c, this.ADDR_ABSX, 3, 4); + this.setOp(this.INS_IGN, 0x5c, this.ADDR_ABSX, 3, 4); + this.setOp(this.INS_IGN, 0x7c, this.ADDR_ABSX, 3, 4); + this.setOp(this.INS_IGN, 0xdc, this.ADDR_ABSX, 3, 4); + this.setOp(this.INS_IGN, 0xfc, this.ADDR_ABSX, 3, 4); + this.setOp(this.INS_IGN, 0x04, this.ADDR_ZP, 2, 3); + this.setOp(this.INS_IGN, 0x44, this.ADDR_ZP, 2, 3); + this.setOp(this.INS_IGN, 0x64, this.ADDR_ZP, 2, 3); + this.setOp(this.INS_IGN, 0x14, this.ADDR_ZPX, 2, 4); + this.setOp(this.INS_IGN, 0x34, this.ADDR_ZPX, 2, 4); + this.setOp(this.INS_IGN, 0x54, this.ADDR_ZPX, 2, 4); + this.setOp(this.INS_IGN, 0x74, this.ADDR_ZPX, 2, 4); + this.setOp(this.INS_IGN, 0xd4, this.ADDR_ZPX, 2, 4); + this.setOp(this.INS_IGN, 0xf4, this.ADDR_ZPX, 2, 4); + + // prettier-ignore + this.cycTable = new Array( + /*0x00*/ 7,6,2,8,3,3,5,5,3,2,2,2,4,4,6,6, + /*0x10*/ 2,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7, + /*0x20*/ 6,6,2,8,3,3,5,5,4,2,2,2,4,4,6,6, + /*0x30*/ 2,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7, + /*0x40*/ 6,6,2,8,3,3,5,5,3,2,2,2,3,4,6,6, + /*0x50*/ 2,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7, + /*0x60*/ 6,6,2,8,3,3,5,5,4,2,2,2,5,4,6,6, + /*0x70*/ 2,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7, + /*0x80*/ 2,6,2,6,3,3,3,3,2,2,2,2,4,4,4,4, + /*0x90*/ 2,6,2,6,4,4,4,4,2,5,2,5,5,5,5,5, + /*0xA0*/ 2,6,2,6,3,3,3,3,2,2,2,2,4,4,4,4, + /*0xB0*/ 2,5,2,5,4,4,4,4,2,4,2,4,4,4,4,4, + /*0xC0*/ 2,6,2,8,3,3,5,5,2,2,2,2,4,4,6,6, + /*0xD0*/ 2,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7, + /*0xE0*/ 2,6,3,8,3,3,5,5,2,2,2,2,4,4,6,6, + /*0xF0*/ 2,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7 + ); + + this.instname = new Array(70); + + // Instruction Names: + this.instname[0] = "ADC"; + this.instname[1] = "AND"; + this.instname[2] = "ASL"; + this.instname[3] = "BCC"; + this.instname[4] = "BCS"; + this.instname[5] = "BEQ"; + this.instname[6] = "BIT"; + this.instname[7] = "BMI"; + this.instname[8] = "BNE"; + this.instname[9] = "BPL"; + this.instname[10] = "BRK"; + this.instname[11] = "BVC"; + this.instname[12] = "BVS"; + this.instname[13] = "CLC"; + this.instname[14] = "CLD"; + this.instname[15] = "CLI"; + this.instname[16] = "CLV"; + this.instname[17] = "CMP"; + this.instname[18] = "CPX"; + this.instname[19] = "CPY"; + this.instname[20] = "DEC"; + this.instname[21] = "DEX"; + this.instname[22] = "DEY"; + this.instname[23] = "EOR"; + this.instname[24] = "INC"; + this.instname[25] = "INX"; + this.instname[26] = "INY"; + this.instname[27] = "JMP"; + this.instname[28] = "JSR"; + this.instname[29] = "LDA"; + this.instname[30] = "LDX"; + this.instname[31] = "LDY"; + this.instname[32] = "LSR"; + this.instname[33] = "NOP"; + this.instname[34] = "ORA"; + this.instname[35] = "PHA"; + this.instname[36] = "PHP"; + this.instname[37] = "PLA"; + this.instname[38] = "PLP"; + this.instname[39] = "ROL"; + this.instname[40] = "ROR"; + this.instname[41] = "RTI"; + this.instname[42] = "RTS"; + this.instname[43] = "SBC"; + this.instname[44] = "SEC"; + this.instname[45] = "SED"; + this.instname[46] = "SEI"; + this.instname[47] = "STA"; + this.instname[48] = "STX"; + this.instname[49] = "STY"; + this.instname[50] = "TAX"; + this.instname[51] = "TAY"; + this.instname[52] = "TSX"; + this.instname[53] = "TXA"; + this.instname[54] = "TXS"; + this.instname[55] = "TYA"; + this.instname[56] = "ALR"; + this.instname[57] = "ANC"; + this.instname[58] = "ARR"; + this.instname[59] = "AXS"; + this.instname[60] = "LAX"; + this.instname[61] = "SAX"; + this.instname[62] = "DCP"; + this.instname[63] = "ISC"; + this.instname[64] = "RLA"; + this.instname[65] = "RRA"; + this.instname[66] = "SLO"; + this.instname[67] = "SRE"; + this.instname[68] = "SKB"; + this.instname[69] = "IGN"; + + this.addrDesc = new Array( + "Zero Page ", + "Relative ", + "Implied ", + "Absolute ", + "Accumulator ", + "Immediate ", + "Zero Page,X ", + "Zero Page,Y ", + "Absolute,X ", + "Absolute,Y ", + "Preindexed Indirect ", + "Postindexed Indirect", + "Indirect Absolute " + ); +}; + +OpData.prototype = { + INS_ADC: 0, + INS_AND: 1, + INS_ASL: 2, + + INS_BCC: 3, + INS_BCS: 4, + INS_BEQ: 5, + INS_BIT: 6, + INS_BMI: 7, + INS_BNE: 8, + INS_BPL: 9, + INS_BRK: 10, + INS_BVC: 11, + INS_BVS: 12, + + INS_CLC: 13, + INS_CLD: 14, + INS_CLI: 15, + INS_CLV: 16, + INS_CMP: 17, + INS_CPX: 18, + INS_CPY: 19, + + INS_DEC: 20, + INS_DEX: 21, + INS_DEY: 22, + + INS_EOR: 23, + + INS_INC: 24, + INS_INX: 25, + INS_INY: 26, + + INS_JMP: 27, + INS_JSR: 28, + + INS_LDA: 29, + INS_LDX: 30, + INS_LDY: 31, + INS_LSR: 32, + + INS_NOP: 33, + + INS_ORA: 34, + + INS_PHA: 35, + INS_PHP: 36, + INS_PLA: 37, + INS_PLP: 38, + + INS_ROL: 39, + INS_ROR: 40, + INS_RTI: 41, + INS_RTS: 42, + + INS_SBC: 43, + INS_SEC: 44, + INS_SED: 45, + INS_SEI: 46, + INS_STA: 47, + INS_STX: 48, + INS_STY: 49, + + INS_TAX: 50, + INS_TAY: 51, + INS_TSX: 52, + INS_TXA: 53, + INS_TXS: 54, + INS_TYA: 55, + + INS_ALR: 56, + INS_ANC: 57, + INS_ARR: 58, + INS_AXS: 59, + INS_LAX: 60, + INS_SAX: 61, + INS_DCP: 62, + INS_ISC: 63, + INS_RLA: 64, + INS_RRA: 65, + INS_SLO: 66, + INS_SRE: 67, + INS_SKB: 68, + INS_IGN: 69, + + INS_DUMMY: 70, // dummy instruction used for 'halting' the processor some cycles + + // -------------------------------- // + + // Addressing modes: + ADDR_ZP: 0, + ADDR_REL: 1, + ADDR_IMP: 2, + ADDR_ABS: 3, + ADDR_ACC: 4, + ADDR_IMM: 5, + ADDR_ZPX: 6, + ADDR_ZPY: 7, + ADDR_ABSX: 8, + ADDR_ABSY: 9, + ADDR_PREIDXIND: 10, + ADDR_POSTIDXIND: 11, + ADDR_INDABS: 12, + + setOp: function (inst, op, addr, size, cycles) { + this.opdata[op] = + (inst & 0xff) | + ((addr & 0xff) << 8) | + ((size & 0xff) << 16) | + ((cycles & 0xff) << 24); + }, +}; + +module.exports = CPU; diff --git a/jsnes/src/index.js b/jsnes/src/index.js new file mode 100644 index 0000000..b69b1ff --- /dev/null +++ b/jsnes/src/index.js @@ -0,0 +1,4 @@ +module.exports = { + Controller: require("./controller"), + NES: require("./nes"), +}; diff --git a/jsnes/src/mappers.js b/jsnes/src/mappers.js new file mode 100644 index 0000000..866f87a --- /dev/null +++ b/jsnes/src/mappers.js @@ -0,0 +1,1518 @@ +var utils = require("./utils"); + +var Mappers = {}; + +Mappers[0] = function (nes) { + this.nes = nes; +}; + +Mappers[0].prototype = { + reset: function () { + this.joy1StrobeState = 0; + this.joy2StrobeState = 0; + this.joypadLastWrite = 0; + + this.zapperFired = false; + this.zapperX = null; + this.zapperY = null; + }, + + write: function (address, value) { + if (address < 0x2000) { + // Mirroring of RAM: + this.nes.cpu.mem[address & 0x7ff] = value; + } else if (address > 0x4017) { + this.nes.cpu.mem[address] = value; + if (address >= 0x6000 && address < 0x8000) { + // Write to persistent RAM + this.nes.opts.onBatteryRamWrite(address, value); + } + } else if (address > 0x2007 && address < 0x4000) { + this.regWrite(0x2000 + (address & 0x7), value); + } else { + this.regWrite(address, value); + } + }, + + writelow: function (address, value) { + if (address < 0x2000) { + // Mirroring of RAM: + this.nes.cpu.mem[address & 0x7ff] = value; + } else if (address > 0x4017) { + this.nes.cpu.mem[address] = value; + } else if (address > 0x2007 && address < 0x4000) { + this.regWrite(0x2000 + (address & 0x7), value); + } else { + this.regWrite(address, value); + } + }, + + load: function (address) { + // Wrap around: + address &= 0xffff; + + // Check address range: + if (address > 0x4017) { + // ROM: + return this.nes.cpu.mem[address]; + } else if (address >= 0x2000) { + // I/O Ports. + return this.regLoad(address); + } else { + // RAM (mirrored) + return this.nes.cpu.mem[address & 0x7ff]; + } + }, + + regLoad: function (address) { + switch ( + address >> 12 // use fourth nibble (0xF000) + ) { + case 0: + break; + + case 1: + break; + + case 2: + // Fall through to case 3 + case 3: + // PPU Registers + switch (address & 0x7) { + case 0x0: + // 0x2000: + // PPU Control Register 1. + // (the value is stored both + // in main memory and in the + // PPU as flags): + // (not in the real NES) + return this.nes.cpu.mem[0x2000]; + + case 0x1: + // 0x2001: + // PPU Control Register 2. + // (the value is stored both + // in main memory and in the + // PPU as flags): + // (not in the real NES) + return this.nes.cpu.mem[0x2001]; + + case 0x2: + // 0x2002: + // PPU Status Register. + // The value is stored in + // main memory in addition + // to as flags in the PPU. + // (not in the real NES) + return this.nes.ppu.readStatusRegister(); + + case 0x3: + return 0; + + case 0x4: + // 0x2004: + // Sprite Memory read. + return this.nes.ppu.sramLoad(); + case 0x5: + return 0; + + case 0x6: + return 0; + + case 0x7: + // 0x2007: + // VRAM read: + return this.nes.ppu.vramLoad(); + } + break; + case 4: + // Sound+Joypad registers + switch (address - 0x4015) { + case 0: + // 0x4015: + // Sound channel enable, DMC Status + return this.nes.papu.readReg(address); + + case 1: + // 0x4016: + // Joystick 1 + Strobe + return this.joy1Read(); + + case 2: + // 0x4017: + // Joystick 2 + Strobe + // https://wiki.nesdev.com/w/index.php/Zapper + var w; + + if ( + this.zapperX !== null && + this.zapperY !== null && + this.nes.ppu.isPixelWhite(this.zapperX, this.zapperY) + ) { + w = 0; + } else { + w = 0x1 << 3; + } + + if (this.zapperFired) { + w |= 0x1 << 4; + } + return (this.joy2Read() | w) & 0xffff; + } + break; + } + return 0; + }, + + regWrite: function (address, value) { + switch (address) { + case 0x2000: + // PPU Control register 1 + this.nes.cpu.mem[address] = value; + this.nes.ppu.updateControlReg1(value); + break; + + case 0x2001: + // PPU Control register 2 + this.nes.cpu.mem[address] = value; + this.nes.ppu.updateControlReg2(value); + break; + + case 0x2003: + // Set Sprite RAM address: + this.nes.ppu.writeSRAMAddress(value); + break; + + case 0x2004: + // Write to Sprite RAM: + this.nes.ppu.sramWrite(value); + break; + + case 0x2005: + // Screen Scroll offsets: + this.nes.ppu.scrollWrite(value); + break; + + case 0x2006: + // Set VRAM address: + this.nes.ppu.writeVRAMAddress(value); + break; + + case 0x2007: + // Write to VRAM: + this.nes.ppu.vramWrite(value); + break; + + case 0x4014: + // Sprite Memory DMA Access + this.nes.ppu.sramDMA(value); + break; + + case 0x4015: + // Sound Channel Switch, DMC Status + this.nes.papu.writeReg(address, value); + break; + + case 0x4016: + // Joystick 1 + Strobe + if ((value & 1) === 0 && (this.joypadLastWrite & 1) === 1) { + this.joy1StrobeState = 0; + this.joy2StrobeState = 0; + } + this.joypadLastWrite = value; + break; + + case 0x4017: + // Sound channel frame sequencer: + this.nes.papu.writeReg(address, value); + break; + + default: + // Sound registers + // console.log("write to sound reg"); + if (address >= 0x4000 && address <= 0x4017) { + this.nes.papu.writeReg(address, value); + } + } + }, + + joy1Read: function () { + var ret; + + switch (this.joy1StrobeState) { + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + ret = this.nes.controllers[1].state[this.joy1StrobeState]; + break; + case 8: + case 9: + case 10: + case 11: + case 12: + case 13: + case 14: + case 15: + case 16: + case 17: + case 18: + ret = 0; + break; + case 19: + ret = 1; + break; + default: + ret = 0; + } + + this.joy1StrobeState++; + if (this.joy1StrobeState === 24) { + this.joy1StrobeState = 0; + } + + return ret; + }, + + joy2Read: function () { + var ret; + + switch (this.joy2StrobeState) { + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + ret = this.nes.controllers[2].state[this.joy2StrobeState]; + break; + case 8: + case 9: + case 10: + case 11: + case 12: + case 13: + case 14: + case 15: + case 16: + case 17: + case 18: + ret = 0; + break; + case 19: + ret = 1; + break; + default: + ret = 0; + } + + this.joy2StrobeState++; + if (this.joy2StrobeState === 24) { + this.joy2StrobeState = 0; + } + + return ret; + }, + + loadROM: function () { + if (!this.nes.rom.valid || this.nes.rom.romCount < 1) { + throw new Error("NoMapper: Invalid ROM! Unable to load."); + } + + // Load ROM into memory: + this.loadPRGROM(); + + // Load CHR-ROM: + this.loadCHRROM(); + + // Load Battery RAM (if present): + this.loadBatteryRam(); + + // Reset IRQ: + //nes.getCpu().doResetInterrupt(); + this.nes.cpu.requestIrq(this.nes.cpu.IRQ_RESET); + }, + + loadPRGROM: function () { + if (this.nes.rom.romCount > 1) { + // Load the two first banks into memory. + this.loadRomBank(0, 0x8000); + this.loadRomBank(1, 0xc000); + } else { + // Load the one bank into both memory locations: + this.loadRomBank(0, 0x8000); + this.loadRomBank(0, 0xc000); + } + }, + + loadCHRROM: function () { + // console.log("Loading CHR ROM.."); + if (this.nes.rom.vromCount > 0) { + if (this.nes.rom.vromCount === 1) { + this.loadVromBank(0, 0x0000); + this.loadVromBank(0, 0x1000); + } else { + this.loadVromBank(0, 0x0000); + this.loadVromBank(1, 0x1000); + } + } else { + //System.out.println("There aren't any CHR-ROM banks.."); + } + }, + + loadBatteryRam: function () { + if (this.nes.rom.batteryRam) { + var ram = this.nes.rom.batteryRam; + if (ram !== null && ram.length === 0x2000) { + // Load Battery RAM into memory: + utils.copyArrayElements(ram, 0, this.nes.cpu.mem, 0x6000, 0x2000); + } + } + }, + + loadRomBank: function (bank, address) { + // Loads a ROM bank into the specified address. + bank %= this.nes.rom.romCount; + //var data = this.nes.rom.rom[bank]; + //cpuMem.write(address,data,data.length); + utils.copyArrayElements( + this.nes.rom.rom[bank], + 0, + this.nes.cpu.mem, + address, + 16384 + ); + }, + + loadVromBank: function (bank, address) { + if (this.nes.rom.vromCount === 0) { + return; + } + this.nes.ppu.triggerRendering(); + + utils.copyArrayElements( + this.nes.rom.vrom[bank % this.nes.rom.vromCount], + 0, + this.nes.ppu.vramMem, + address, + 4096 + ); + + var vromTile = this.nes.rom.vromTile[bank % this.nes.rom.vromCount]; + utils.copyArrayElements( + vromTile, + 0, + this.nes.ppu.ptTile, + address >> 4, + 256 + ); + }, + + load32kRomBank: function (bank, address) { + this.loadRomBank((bank * 2) % this.nes.rom.romCount, address); + this.loadRomBank((bank * 2 + 1) % this.nes.rom.romCount, address + 16384); + }, + + load8kVromBank: function (bank4kStart, address) { + if (this.nes.rom.vromCount === 0) { + return; + } + this.nes.ppu.triggerRendering(); + + this.loadVromBank(bank4kStart % this.nes.rom.vromCount, address); + this.loadVromBank( + (bank4kStart + 1) % this.nes.rom.vromCount, + address + 4096 + ); + }, + + load1kVromBank: function (bank1k, address) { + if (this.nes.rom.vromCount === 0) { + return; + } + this.nes.ppu.triggerRendering(); + + var bank4k = Math.floor(bank1k / 4) % this.nes.rom.vromCount; + var bankoffset = (bank1k % 4) * 1024; + utils.copyArrayElements( + this.nes.rom.vrom[bank4k], + bankoffset, + this.nes.ppu.vramMem, + address, + 1024 + ); + + // Update tiles: + var vromTile = this.nes.rom.vromTile[bank4k]; + var baseIndex = address >> 4; + for (var i = 0; i < 64; i++) { + this.nes.ppu.ptTile[baseIndex + i] = vromTile[(bank1k % 4 << 6) + i]; + } + }, + + load2kVromBank: function (bank2k, address) { + if (this.nes.rom.vromCount === 0) { + return; + } + this.nes.ppu.triggerRendering(); + + var bank4k = Math.floor(bank2k / 2) % this.nes.rom.vromCount; + var bankoffset = (bank2k % 2) * 2048; + utils.copyArrayElements( + this.nes.rom.vrom[bank4k], + bankoffset, + this.nes.ppu.vramMem, + address, + 2048 + ); + + // Update tiles: + var vromTile = this.nes.rom.vromTile[bank4k]; + var baseIndex = address >> 4; + for (var i = 0; i < 128; i++) { + this.nes.ppu.ptTile[baseIndex + i] = vromTile[(bank2k % 2 << 7) + i]; + } + }, + + load8kRomBank: function (bank8k, address) { + var bank16k = Math.floor(bank8k / 2) % this.nes.rom.romCount; + var offset = (bank8k % 2) * 8192; + + //this.nes.cpu.mem.write(address,this.nes.rom.rom[bank16k],offset,8192); + utils.copyArrayElements( + this.nes.rom.rom[bank16k], + offset, + this.nes.cpu.mem, + address, + 8192 + ); + }, + + clockIrqCounter: function () { + // Does nothing. This is used by the MMC3 mapper. + }, + + // eslint-disable-next-line no-unused-vars + latchAccess: function (address) { + // Does nothing. This is used by MMC2. + }, + + toJSON: function () { + return { + joy1StrobeState: this.joy1StrobeState, + joy2StrobeState: this.joy2StrobeState, + joypadLastWrite: this.joypadLastWrite, + }; + }, + + fromJSON: function (s) { + this.joy1StrobeState = s.joy1StrobeState; + this.joy2StrobeState = s.joy2StrobeState; + this.joypadLastWrite = s.joypadLastWrite; + }, +}; + +Mappers[1] = function (nes) { + this.nes = nes; +}; + +Mappers[1].prototype = new Mappers[0](); + +Mappers[1].prototype.reset = function () { + Mappers[0].prototype.reset.apply(this); + + // 5-bit buffer: + this.regBuffer = 0; + this.regBufferCounter = 0; + + // Register 0: + this.mirroring = 0; + this.oneScreenMirroring = 0; + this.prgSwitchingArea = 1; + this.prgSwitchingSize = 1; + this.vromSwitchingSize = 0; + + // Register 1: + this.romSelectionReg0 = 0; + + // Register 2: + this.romSelectionReg1 = 0; + + // Register 3: + this.romBankSelect = 0; +}; + +Mappers[1].prototype.write = function (address, value) { + // Writes to addresses other than MMC registers are handled by NoMapper. + if (address < 0x8000) { + Mappers[0].prototype.write.apply(this, arguments); + return; + } + + // See what should be done with the written value: + if ((value & 128) !== 0) { + // Reset buffering: + this.regBufferCounter = 0; + this.regBuffer = 0; + + // Reset register: + if (this.getRegNumber(address) === 0) { + this.prgSwitchingArea = 1; + this.prgSwitchingSize = 1; + } + } else { + // Continue buffering: + //regBuffer = (regBuffer & (0xFF-(1<> 2) & 1; + + // PRG Switching Size: + this.prgSwitchingSize = (value >> 3) & 1; + + // VROM Switching Size: + this.vromSwitchingSize = (value >> 4) & 1; + + break; + + case 1: + // ROM selection: + this.romSelectionReg0 = (value >> 4) & 1; + + // Check whether the cart has VROM: + if (this.nes.rom.vromCount > 0) { + // Select VROM bank at 0x0000: + if (this.vromSwitchingSize === 0) { + // Swap 8kB VROM: + if (this.romSelectionReg0 === 0) { + this.load8kVromBank(value & 0xf, 0x0000); + } else { + this.load8kVromBank( + Math.floor(this.nes.rom.vromCount / 2) + (value & 0xf), + 0x0000 + ); + } + } else { + // Swap 4kB VROM: + if (this.romSelectionReg0 === 0) { + this.loadVromBank(value & 0xf, 0x0000); + } else { + this.loadVromBank( + Math.floor(this.nes.rom.vromCount / 2) + (value & 0xf), + 0x0000 + ); + } + } + } + + break; + + case 2: + // ROM selection: + this.romSelectionReg1 = (value >> 4) & 1; + + // Check whether the cart has VROM: + if (this.nes.rom.vromCount > 0) { + // Select VROM bank at 0x1000: + if (this.vromSwitchingSize === 1) { + // Swap 4kB of VROM: + if (this.romSelectionReg1 === 0) { + this.loadVromBank(value & 0xf, 0x1000); + } else { + this.loadVromBank( + Math.floor(this.nes.rom.vromCount / 2) + (value & 0xf), + 0x1000 + ); + } + } + } + break; + + default: + // Select ROM bank: + // ------------------------- + tmp = value & 0xf; + var bank; + var baseBank = 0; + + if (this.nes.rom.romCount >= 32) { + // 1024 kB cart + if (this.vromSwitchingSize === 0) { + if (this.romSelectionReg0 === 1) { + baseBank = 16; + } + } else { + baseBank = + (this.romSelectionReg0 | (this.romSelectionReg1 << 1)) << 3; + } + } else if (this.nes.rom.romCount >= 16) { + // 512 kB cart + if (this.romSelectionReg0 === 1) { + baseBank = 8; + } + } + + if (this.prgSwitchingSize === 0) { + // 32kB + bank = baseBank + (value & 0xf); + this.load32kRomBank(bank, 0x8000); + } else { + // 16kB + bank = baseBank * 2 + (value & 0xf); + if (this.prgSwitchingArea === 0) { + this.loadRomBank(bank, 0xc000); + } else { + this.loadRomBank(bank, 0x8000); + } + } + } +}; + +// Returns the register number from the address written to: +Mappers[1].prototype.getRegNumber = function (address) { + if (address >= 0x8000 && address <= 0x9fff) { + return 0; + } else if (address >= 0xa000 && address <= 0xbfff) { + return 1; + } else if (address >= 0xc000 && address <= 0xdfff) { + return 2; + } else { + return 3; + } +}; + +Mappers[1].prototype.loadROM = function () { + if (!this.nes.rom.valid) { + throw new Error("MMC1: Invalid ROM! Unable to load."); + } + + // Load PRG-ROM: + this.loadRomBank(0, 0x8000); // First ROM bank.. + this.loadRomBank(this.nes.rom.romCount - 1, 0xc000); // ..and last ROM bank. + + // Load CHR-ROM: + this.loadCHRROM(); + + // Load Battery RAM (if present): + this.loadBatteryRam(); + + // Do Reset-Interrupt: + this.nes.cpu.requestIrq(this.nes.cpu.IRQ_RESET); +}; + +// eslint-disable-next-line no-unused-vars +Mappers[1].prototype.switchLowHighPrgRom = function (oldSetting) { + // not yet. +}; + +Mappers[1].prototype.switch16to32 = function () { + // not yet. +}; + +Mappers[1].prototype.switch32to16 = function () { + // not yet. +}; + +Mappers[1].prototype.toJSON = function () { + var s = Mappers[0].prototype.toJSON.apply(this); + s.mirroring = this.mirroring; + s.oneScreenMirroring = this.oneScreenMirroring; + s.prgSwitchingArea = this.prgSwitchingArea; + s.prgSwitchingSize = this.prgSwitchingSize; + s.vromSwitchingSize = this.vromSwitchingSize; + s.romSelectionReg0 = this.romSelectionReg0; + s.romSelectionReg1 = this.romSelectionReg1; + s.romBankSelect = this.romBankSelect; + s.regBuffer = this.regBuffer; + s.regBufferCounter = this.regBufferCounter; + return s; +}; + +Mappers[1].prototype.fromJSON = function (s) { + Mappers[0].prototype.fromJSON.apply(this, arguments); + this.mirroring = s.mirroring; + this.oneScreenMirroring = s.oneScreenMirroring; + this.prgSwitchingArea = s.prgSwitchingArea; + this.prgSwitchingSize = s.prgSwitchingSize; + this.vromSwitchingSize = s.vromSwitchingSize; + this.romSelectionReg0 = s.romSelectionReg0; + this.romSelectionReg1 = s.romSelectionReg1; + this.romBankSelect = s.romBankSelect; + this.regBuffer = s.regBuffer; + this.regBufferCounter = s.regBufferCounter; +}; + +Mappers[2] = function (nes) { + this.nes = nes; +}; + +Mappers[2].prototype = new Mappers[0](); + +Mappers[2].prototype.write = function (address, value) { + // Writes to addresses other than MMC registers are handled by NoMapper. + if (address < 0x8000) { + Mappers[0].prototype.write.apply(this, arguments); + return; + } else { + // This is a ROM bank select command. + // Swap in the given ROM bank at 0x8000: + this.loadRomBank(value, 0x8000); + } +}; + +Mappers[2].prototype.loadROM = function () { + if (!this.nes.rom.valid) { + throw new Error("UNROM: Invalid ROM! Unable to load."); + } + + // Load PRG-ROM: + this.loadRomBank(0, 0x8000); + this.loadRomBank(this.nes.rom.romCount - 1, 0xc000); + + // Load CHR-ROM: + this.loadCHRROM(); + + // Do Reset-Interrupt: + this.nes.cpu.requestIrq(this.nes.cpu.IRQ_RESET); +}; + +/** + * Mapper 003 (CNROM) + * + * @constructor + * @example Solomon's Key, Arkanoid, Arkista's Ring, Bump 'n' Jump, Cybernoid + * @description http://wiki.nesdev.com/w/index.php/INES_Mapper_003 + */ +Mappers[3] = function (nes) { + this.nes = nes; +}; + +Mappers[3].prototype = new Mappers[0](); + +Mappers[3].prototype.write = function (address, value) { + // Writes to addresses other than MMC registers are handled by NoMapper. + if (address < 0x8000) { + Mappers[0].prototype.write.apply(this, arguments); + return; + } else { + // This is a ROM bank select command. + // Swap in the given ROM bank at 0x8000: + // This is a VROM bank select command. + // Swap in the given VROM bank at 0x0000: + var bank = (value % (this.nes.rom.vromCount / 2)) * 2; + this.loadVromBank(bank, 0x0000); + this.loadVromBank(bank + 1, 0x1000); + this.load8kVromBank(value * 2, 0x0000); + } +}; + +Mappers[4] = function (nes) { + this.nes = nes; + + this.CMD_SEL_2_1K_VROM_0000 = 0; + this.CMD_SEL_2_1K_VROM_0800 = 1; + this.CMD_SEL_1K_VROM_1000 = 2; + this.CMD_SEL_1K_VROM_1400 = 3; + this.CMD_SEL_1K_VROM_1800 = 4; + this.CMD_SEL_1K_VROM_1C00 = 5; + this.CMD_SEL_ROM_PAGE1 = 6; + this.CMD_SEL_ROM_PAGE2 = 7; + + this.command = null; + this.prgAddressSelect = null; + this.chrAddressSelect = null; + this.pageNumber = null; + this.irqCounter = null; + this.irqLatchValue = null; + this.irqEnable = null; + this.prgAddressChanged = false; +}; + +Mappers[4].prototype = new Mappers[0](); + +Mappers[4].prototype.write = function (address, value) { + // Writes to addresses other than MMC registers are handled by NoMapper. + if (address < 0x8000) { + Mappers[0].prototype.write.apply(this, arguments); + return; + } + + switch (address) { + case 0x8000: + // Command/Address Select register + this.command = value & 7; + var tmp = (value >> 6) & 1; + if (tmp !== this.prgAddressSelect) { + this.prgAddressChanged = true; + } + this.prgAddressSelect = tmp; + this.chrAddressSelect = (value >> 7) & 1; + break; + + case 0x8001: + // Page number for command + this.executeCommand(this.command, value); + break; + + case 0xa000: + // Mirroring select + if ((value & 1) !== 0) { + this.nes.ppu.setMirroring(this.nes.rom.HORIZONTAL_MIRRORING); + } else { + this.nes.ppu.setMirroring(this.nes.rom.VERTICAL_MIRRORING); + } + break; + + case 0xa001: + // SaveRAM Toggle + // TODO + //nes.getRom().setSaveState((value&1)!=0); + break; + + case 0xc000: + // IRQ Counter register + this.irqCounter = value; + //nes.ppu.mapperIrqCounter = 0; + break; + + case 0xc001: + // IRQ Latch register + this.irqLatchValue = value; + break; + + case 0xe000: + // IRQ Control Reg 0 (disable) + //irqCounter = irqLatchValue; + this.irqEnable = 0; + break; + + case 0xe001: + // IRQ Control Reg 1 (enable) + this.irqEnable = 1; + break; + + default: + // Not a MMC3 register. + // The game has probably crashed, + // since it tries to write to ROM.. + // IGNORE. + } +}; + +Mappers[4].prototype.executeCommand = function (cmd, arg) { + switch (cmd) { + case this.CMD_SEL_2_1K_VROM_0000: + // Select 2 1KB VROM pages at 0x0000: + if (this.chrAddressSelect === 0) { + this.load1kVromBank(arg, 0x0000); + this.load1kVromBank(arg + 1, 0x0400); + } else { + this.load1kVromBank(arg, 0x1000); + this.load1kVromBank(arg + 1, 0x1400); + } + break; + + case this.CMD_SEL_2_1K_VROM_0800: + // Select 2 1KB VROM pages at 0x0800: + if (this.chrAddressSelect === 0) { + this.load1kVromBank(arg, 0x0800); + this.load1kVromBank(arg + 1, 0x0c00); + } else { + this.load1kVromBank(arg, 0x1800); + this.load1kVromBank(arg + 1, 0x1c00); + } + break; + + case this.CMD_SEL_1K_VROM_1000: + // Select 1K VROM Page at 0x1000: + if (this.chrAddressSelect === 0) { + this.load1kVromBank(arg, 0x1000); + } else { + this.load1kVromBank(arg, 0x0000); + } + break; + + case this.CMD_SEL_1K_VROM_1400: + // Select 1K VROM Page at 0x1400: + if (this.chrAddressSelect === 0) { + this.load1kVromBank(arg, 0x1400); + } else { + this.load1kVromBank(arg, 0x0400); + } + break; + + case this.CMD_SEL_1K_VROM_1800: + // Select 1K VROM Page at 0x1800: + if (this.chrAddressSelect === 0) { + this.load1kVromBank(arg, 0x1800); + } else { + this.load1kVromBank(arg, 0x0800); + } + break; + + case this.CMD_SEL_1K_VROM_1C00: + // Select 1K VROM Page at 0x1C00: + if (this.chrAddressSelect === 0) { + this.load1kVromBank(arg, 0x1c00); + } else { + this.load1kVromBank(arg, 0x0c00); + } + break; + + case this.CMD_SEL_ROM_PAGE1: + if (this.prgAddressChanged) { + // Load the two hardwired banks: + if (this.prgAddressSelect === 0) { + this.load8kRomBank((this.nes.rom.romCount - 1) * 2, 0xc000); + } else { + this.load8kRomBank((this.nes.rom.romCount - 1) * 2, 0x8000); + } + this.prgAddressChanged = false; + } + + // Select first switchable ROM page: + if (this.prgAddressSelect === 0) { + this.load8kRomBank(arg, 0x8000); + } else { + this.load8kRomBank(arg, 0xc000); + } + break; + + case this.CMD_SEL_ROM_PAGE2: + // Select second switchable ROM page: + this.load8kRomBank(arg, 0xa000); + + // hardwire appropriate bank: + if (this.prgAddressChanged) { + // Load the two hardwired banks: + if (this.prgAddressSelect === 0) { + this.load8kRomBank((this.nes.rom.romCount - 1) * 2, 0xc000); + } else { + this.load8kRomBank((this.nes.rom.romCount - 1) * 2, 0x8000); + } + this.prgAddressChanged = false; + } + } +}; + +Mappers[4].prototype.loadROM = function () { + if (!this.nes.rom.valid) { + throw new Error("MMC3: Invalid ROM! Unable to load."); + } + + // Load hardwired PRG banks (0xC000 and 0xE000): + this.load8kRomBank((this.nes.rom.romCount - 1) * 2, 0xc000); + this.load8kRomBank((this.nes.rom.romCount - 1) * 2 + 1, 0xe000); + + // Load swappable PRG banks (0x8000 and 0xA000): + this.load8kRomBank(0, 0x8000); + this.load8kRomBank(1, 0xa000); + + // Load CHR-ROM: + this.loadCHRROM(); + + // Load Battery RAM (if present): + this.loadBatteryRam(); + + // Do Reset-Interrupt: + this.nes.cpu.requestIrq(this.nes.cpu.IRQ_RESET); +}; + +Mappers[4].prototype.clockIrqCounter = function () { + if (this.irqEnable === 1) { + this.irqCounter--; + if (this.irqCounter < 0) { + // Trigger IRQ: + //nes.getCpu().doIrq(); + this.nes.cpu.requestIrq(this.nes.cpu.IRQ_NORMAL); + this.irqCounter = this.irqLatchValue; + } + } +}; + +Mappers[4].prototype.toJSON = function () { + var s = Mappers[0].prototype.toJSON.apply(this); + s.command = this.command; + s.prgAddressSelect = this.prgAddressSelect; + s.chrAddressSelect = this.chrAddressSelect; + s.pageNumber = this.pageNumber; + s.irqCounter = this.irqCounter; + s.irqLatchValue = this.irqLatchValue; + s.irqEnable = this.irqEnable; + s.prgAddressChanged = this.prgAddressChanged; + return s; +}; + +Mappers[4].prototype.fromJSON = function (s) { + Mappers[0].prototype.fromJSON.apply(this, arguments); + this.command = s.command; + this.prgAddressSelect = s.prgAddressSelect; + this.chrAddressSelect = s.chrAddressSelect; + this.pageNumber = s.pageNumber; + this.irqCounter = s.irqCounter; + this.irqLatchValue = s.irqLatchValue; + this.irqEnable = s.irqEnable; + this.prgAddressChanged = s.prgAddressChanged; +}; + +/** + * Mapper005 (MMC5,ExROM) + * + * @example Castlevania 3, Just Breed, Uncharted Waters, Romance of the 3 Kingdoms 2, Laser Invasion, Metal Slader Glory, Uchuu Keibitai SDF, Shin 4 Nin Uchi Mahjong - Yakuman Tengoku + * @description http://wiki.nesdev.com/w/index.php/INES_Mapper_005 + * @constructor + */ +Mappers[5] = function (nes) { + this.nes = nes; +}; + +Mappers[5].prototype = new Mappers[0](); + +Mappers[5].prototype.write = function (address, value) { + // Writes to addresses other than MMC registers are handled by NoMapper. + if (address < 0x8000) { + Mappers[0].prototype.write.apply(this, arguments); + } else { + this.load8kVromBank(value, 0x0000); + } +}; + +Mappers[5].prototype.write = function (address, value) { + // Writes to addresses other than MMC registers are handled by NoMapper. + if (address < 0x5000) { + Mappers[0].prototype.write.apply(this, arguments); + return; + } + + switch (address) { + case 0x5100: + this.prg_size = value & 3; + break; + case 0x5101: + this.chr_size = value & 3; + break; + case 0x5102: + this.sram_we_a = value & 3; + break; + case 0x5103: + this.sram_we_b = value & 3; + break; + case 0x5104: + this.graphic_mode = value & 3; + break; + case 0x5105: + this.nametable_mode = value; + this.nametable_type[0] = value & 3; + this.load1kVromBank(value & 3, 0x2000); + value >>= 2; + this.nametable_type[1] = value & 3; + this.load1kVromBank(value & 3, 0x2400); + value >>= 2; + this.nametable_type[2] = value & 3; + this.load1kVromBank(value & 3, 0x2800); + value >>= 2; + this.nametable_type[3] = value & 3; + this.load1kVromBank(value & 3, 0x2c00); + break; + case 0x5106: + this.fill_chr = value; + break; + case 0x5107: + this.fill_pal = value & 3; + break; + case 0x5113: + this.SetBank_SRAM(3, value & 3); + break; + case 0x5114: + case 0x5115: + case 0x5116: + case 0x5117: + this.SetBank_CPU(address, value); + break; + case 0x5120: + case 0x5121: + case 0x5122: + case 0x5123: + case 0x5124: + case 0x5125: + case 0x5126: + case 0x5127: + this.chr_mode = 0; + this.chr_page[0][address & 7] = value; + this.SetBank_PPU(); + break; + case 0x5128: + case 0x5129: + case 0x512a: + case 0x512b: + this.chr_mode = 1; + this.chr_page[1][(address & 3) + 0] = value; + this.chr_page[1][(address & 3) + 4] = value; + this.SetBank_PPU(); + break; + case 0x5200: + this.split_control = value; + break; + case 0x5201: + this.split_scroll = value; + break; + case 0x5202: + this.split_page = value & 0x3f; + break; + case 0x5203: + this.irq_line = value; + this.nes.cpu.ClearIRQ(); + break; + case 0x5204: + this.irq_enable = value; + this.nes.cpu.ClearIRQ(); + break; + case 0x5205: + this.mult_a = value; + break; + case 0x5206: + this.mult_b = value; + break; + default: + if (address >= 0x5000 && address <= 0x5015) { + this.nes.papu.exWrite(address, value); + } else if (address >= 0x5c00 && address <= 0x5fff) { + if (this.graphic_mode === 2) { + // ExRAM + // vram write + } else if (this.graphic_mode !== 3) { + // Split,ExGraphic + if (this.irq_status & 0x40) { + // vram write + } else { + // vram write + } + } + } else if (address >= 0x6000 && address <= 0x7fff) { + if (this.sram_we_a === 2 && this.sram_we_b === 1) { + // additional ram write + } + } + break; + } +}; + +Mappers[5].prototype.loadROM = function () { + if (!this.nes.rom.valid) { + throw new Error("UNROM: Invalid ROM! Unable to load."); + } + + // Load PRG-ROM: + this.load8kRomBank(this.nes.rom.romCount * 2 - 1, 0x8000); + this.load8kRomBank(this.nes.rom.romCount * 2 - 1, 0xa000); + this.load8kRomBank(this.nes.rom.romCount * 2 - 1, 0xc000); + this.load8kRomBank(this.nes.rom.romCount * 2 - 1, 0xe000); + + // Load CHR-ROM: + this.loadCHRROM(); + + // Do Reset-Interrupt: + this.nes.cpu.requestIrq(this.nes.cpu.IRQ_RESET); +}; + +/** + * Mapper007 (AxROM) + * @example Battletoads, Time Lord, Marble Madness + * @description http://wiki.nesdev.com/w/index.php/INES_Mapper_007 + * @constructor + */ +Mappers[7] = function (nes) { + this.nes = nes; +}; + +Mappers[7].prototype = new Mappers[0](); + +Mappers[7].prototype.write = function (address, value) { + // Writes to addresses other than MMC registers are handled by NoMapper. + if (address < 0x8000) { + Mappers[0].prototype.write.apply(this, arguments); + } else { + this.load32kRomBank(value & 0x7, 0x8000); + if (value & 0x10) { + this.nes.ppu.setMirroring(this.nes.rom.SINGLESCREEN_MIRRORING2); + } else { + this.nes.ppu.setMirroring(this.nes.rom.SINGLESCREEN_MIRRORING); + } + } +}; + +Mappers[7].prototype.loadROM = function () { + if (!this.nes.rom.valid) { + throw new Error("AOROM: Invalid ROM! Unable to load."); + } + + // Load PRG-ROM: + this.loadPRGROM(); + + // Load CHR-ROM: + this.loadCHRROM(); + + // Do Reset-Interrupt: + this.nes.cpu.requestIrq(this.nes.cpu.IRQ_RESET); +}; + +/** + * Mapper 011 (Color Dreams) + * + * @description http://wiki.nesdev.com/w/index.php/Color_Dreams + * @example Crystal Mines, Metal Fighter + * @constructor + */ +Mappers[11] = function (nes) { + this.nes = nes; +}; + +Mappers[11].prototype = new Mappers[0](); + +Mappers[11].prototype.write = function (address, value) { + if (address < 0x8000) { + Mappers[0].prototype.write.apply(this, arguments); + return; + } else { + // Swap in the given PRG-ROM bank: + var prgbank1 = ((value & 0xf) * 2) % this.nes.rom.romCount; + var prgbank2 = ((value & 0xf) * 2 + 1) % this.nes.rom.romCount; + + this.loadRomBank(prgbank1, 0x8000); + this.loadRomBank(prgbank2, 0xc000); + + if (this.nes.rom.vromCount > 0) { + // Swap in the given VROM bank at 0x0000: + var bank = ((value >> 4) * 2) % this.nes.rom.vromCount; + this.loadVromBank(bank, 0x0000); + this.loadVromBank(bank + 1, 0x1000); + } + } +}; + +/** + * Mapper 034 (BNROM, NINA-01) + * + * @description http://wiki.nesdev.com/w/index.php/INES_Mapper_034 + * @example Darkseed, Mashou, Mission Impossible 2 + * @constructor + */ +Mappers[34] = function (nes) { + this.nes = nes; +}; + +Mappers[34].prototype = new Mappers[0](); + +Mappers[34].prototype.write = function (address, value) { + if (address < 0x8000) { + Mappers[0].prototype.write.apply(this, arguments); + return; + } else { + this.load32kRomBank(value, 0x8000); + } +}; + +/** + * Mapper 038 + * + * @description http://wiki.nesdev.com/w/index.php/INES_Mapper_038 + * @example Crime Busters + * @constructor + */ +Mappers[38] = function (nes) { + this.nes = nes; +}; + +Mappers[38].prototype = new Mappers[0](); + +Mappers[38].prototype.write = function (address, value) { + if (address < 0x7000 || address > 0x7fff) { + Mappers[0].prototype.write.apply(this, arguments); + return; + } else { + // Swap in the given PRG-ROM bank at 0x8000: + this.load32kRomBank(value & 3, 0x8000); + + // Swap in the given VROM bank at 0x0000: + this.load8kVromBank(((value >> 2) & 3) * 2, 0x0000); + } +}; + +/** + * Mapper 066 (GxROM) + * + * @description http://wiki.nesdev.com/w/index.php/INES_Mapper_066 + * @example Doraemon, Dragon Power, Gumshoe, Thunder & Lightning, + * Super Mario Bros. + Duck Hunt + * @constructor + */ +Mappers[66] = function (nes) { + this.nes = nes; +}; + +Mappers[66].prototype = new Mappers[0](); + +Mappers[66].prototype.write = function (address, value) { + if (address < 0x8000) { + Mappers[0].prototype.write.apply(this, arguments); + return; + } else { + // Swap in the given PRG-ROM bank at 0x8000: + this.load32kRomBank((value >> 4) & 3, 0x8000); + + // Swap in the given VROM bank at 0x0000: + this.load8kVromBank((value & 3) * 2, 0x0000); + } +}; + +/** + * Mapper 094 (UN1ROM) + * + * @description http://wiki.nesdev.com/w/index.php/INES_Mapper_094 + * @example Senjou no Ookami + * @constructor + */ +Mappers[94] = function (nes) { + this.nes = nes; +}; + +Mappers[94].prototype = new Mappers[0](); + +Mappers[94].prototype.write = function (address, value) { + // Writes to addresses other than MMC registers are handled by NoMapper. + if (address < 0x8000) { + Mappers[0].prototype.write.apply(this, arguments); + return; + } else { + // This is a ROM bank select command. + // Swap in the given ROM bank at 0x8000: + this.loadRomBank(value >> 2, 0x8000); + } +}; + +Mappers[94].prototype.loadROM = function () { + if (!this.nes.rom.valid) { + throw new Error("UN1ROM: Invalid ROM! Unable to load."); + } + + // Load PRG-ROM: + this.loadRomBank(0, 0x8000); + this.loadRomBank(this.nes.rom.romCount - 1, 0xc000); + + // Load CHR-ROM: + this.loadCHRROM(); + + // Do Reset-Interrupt: + this.nes.cpu.requestIrq(this.nes.cpu.IRQ_RESET); +}; + +/** + * Mapper 140 + * + * @description http://wiki.nesdev.com/w/index.php/INES_Mapper_140 + * @example Bio Senshi Dan - Increaser Tono Tatakai + * @constructor + */ +Mappers[140] = function (nes) { + this.nes = nes; +}; + +Mappers[140].prototype = new Mappers[0](); + +Mappers[140].prototype.write = function (address, value) { + if (address < 0x6000 || address > 0x7fff) { + Mappers[0].prototype.write.apply(this, arguments); + return; + } else { + // Swap in the given PRG-ROM bank at 0x8000: + this.load32kRomBank((value >> 4) & 3, 0x8000); + + // Swap in the given VROM bank at 0x0000: + this.load8kVromBank((value & 0xf) * 2, 0x0000); + } +}; + +/** + * Mapper 180 + * + * @description http://wiki.nesdev.com/w/index.php/INES_Mapper_180 + * @example Crazy Climber + * @constructor + */ +Mappers[180] = function (nes) { + this.nes = nes; +}; + +Mappers[180].prototype = new Mappers[0](); + +Mappers[180].prototype.write = function (address, value) { + // Writes to addresses other than MMC registers are handled by NoMapper. + if (address < 0x8000) { + Mappers[0].prototype.write.apply(this, arguments); + return; + } else { + // This is a ROM bank select command. + // Swap in the given ROM bank at 0xc000: + this.loadRomBank(value, 0xc000); + } +}; + +Mappers[180].prototype.loadROM = function () { + if (!this.nes.rom.valid) { + throw new Error("Mapper 180: Invalid ROM! Unable to load."); + } + + // Load PRG-ROM: + this.loadRomBank(0, 0x8000); + this.loadRomBank(this.nes.rom.romCount - 1, 0xc000); + + // Load CHR-ROM: + this.loadCHRROM(); + + // Do Reset-Interrupt: + this.nes.cpu.requestIrq(this.nes.cpu.IRQ_RESET); +}; + +module.exports = Mappers; diff --git a/jsnes/src/nes.js b/jsnes/src/nes.js new file mode 100644 index 0000000..1cdd30c --- /dev/null +++ b/jsnes/src/nes.js @@ -0,0 +1,222 @@ +var CPU = require("./cpu"); +var Controller = require("./controller"); +var PPU = require("./ppu"); +var PAPU = require("./papu"); +var ROM = require("./rom"); + +var NES = function (opts) { + this.opts = { + onFrame: function () {}, + onAudioSample: null, + onStatusUpdate: function () {}, + onBatteryRamWrite: function () {}, + + // FIXME: not actually used except for in PAPU + preferredFrameRate: 60, + + emulateSound: true, + sampleRate: 48000, // Sound sample rate in hz + }; + if (typeof opts !== "undefined") { + var key; + for (key in this.opts) { + if (typeof opts[key] !== "undefined") { + this.opts[key] = opts[key]; + } + } + } + + this.frameTime = 1000 / this.opts.preferredFrameRate; + + this.ui = { + writeFrame: this.opts.onFrame, + updateStatus: this.opts.onStatusUpdate, + }; + this.cpu = new CPU(this); + this.ppu = new PPU(this); + this.papu = new PAPU(this); + this.mmap = null; // set in loadROM() + this.controllers = { + 1: new Controller(), + 2: new Controller(), + }; + + this.ui.updateStatus("Ready to load a ROM."); + + this.frame = this.frame.bind(this); + this.buttonDown = this.buttonDown.bind(this); + this.buttonUp = this.buttonUp.bind(this); + this.zapperMove = this.zapperMove.bind(this); + this.zapperFireDown = this.zapperFireDown.bind(this); + this.zapperFireUp = this.zapperFireUp.bind(this); +}; + +NES.prototype = { + fpsFrameCount: 0, + romData: null, + break: false, + + // Set break to true to stop frame loop. + stop: function () { + this.break = true; + }, + + // Resets the system + reset: function () { + if (this.mmap !== null) { + this.mmap.reset(); + } + + this.cpu.reset(); + this.ppu.reset(); + this.papu.reset(); + + this.lastFpsTime = null; + this.fpsFrameCount = 0; + + this.break = false; + }, + + frame: function () { + this.ppu.startFrame(); + var cycles = 0; + var emulateSound = this.opts.emulateSound; + var cpu = this.cpu; + var ppu = this.ppu; + var papu = this.papu; + FRAMELOOP: for (;;) { + if (this.break) break; + if (cpu.cyclesToHalt === 0) { + // Execute a CPU instruction + cycles = cpu.emulate(); + if (emulateSound) { + papu.clockFrameCounter(cycles); + } + cycles *= 3; + } else { + if (cpu.cyclesToHalt > 8) { + cycles = 24; + if (emulateSound) { + papu.clockFrameCounter(8); + } + cpu.cyclesToHalt -= 8; + } else { + cycles = cpu.cyclesToHalt * 3; + if (emulateSound) { + papu.clockFrameCounter(cpu.cyclesToHalt); + } + cpu.cyclesToHalt = 0; + } + } + + for (; cycles > 0; cycles--) { + if ( + ppu.curX === ppu.spr0HitX && + ppu.f_spVisibility === 1 && + ppu.scanline - 21 === ppu.spr0HitY + ) { + // Set sprite 0 hit flag: + ppu.setStatusFlag(ppu.STATUS_SPRITE0HIT, true); + } + + if (ppu.requestEndFrame) { + ppu.nmiCounter--; + if (ppu.nmiCounter === 0) { + ppu.requestEndFrame = false; + ppu.startVBlank(); + break FRAMELOOP; + } + } + + ppu.curX++; + if (ppu.curX === 341) { + ppu.curX = 0; + ppu.endScanline(); + } + } + } + this.fpsFrameCount++; + }, + + buttonDown: function (controller, button) { + this.controllers[controller].buttonDown(button); + }, + + buttonUp: function (controller, button) { + this.controllers[controller].buttonUp(button); + }, + + zapperMove: function (x, y) { + if (!this.mmap) return; + this.mmap.zapperX = x; + this.mmap.zapperY = y; + }, + + zapperFireDown: function () { + if (!this.mmap) return; + this.mmap.zapperFired = true; + }, + + zapperFireUp: function () { + if (!this.mmap) return; + this.mmap.zapperFired = false; + }, + + getFPS: function () { + var now = +new Date(); + var fps = null; + if (this.lastFpsTime) { + fps = this.fpsFrameCount / ((now - this.lastFpsTime) / 1000); + } + this.fpsFrameCount = 0; + this.lastFpsTime = now; + return fps; + }, + + reloadROM: function () { + if (this.romData !== null) { + this.loadROM(this.romData); + } + }, + + // Loads a ROM file into the CPU and PPU. + // The ROM file is validated first. + loadROM: function (data) { + // Load ROM file: + this.rom = new ROM(this); + this.rom.load(data); + + this.reset(); + this.mmap = this.rom.createMapper(); + this.mmap.loadROM(); + this.ppu.setMirroring(this.rom.getMirroringType()); + this.romData = data; + }, + + setFramerate: function (rate) { + this.opts.preferredFrameRate = rate; + this.frameTime = 1000 / rate; + this.papu.setSampleRate(this.opts.sampleRate, false); + }, + + toJSON: function () { + return { + // romData: this.romData, + cpu: this.cpu.toJSON(), + mmap: this.mmap.toJSON(), + ppu: this.ppu.toJSON(), + papu: this.papu.toJSON(), + }; + }, + + fromJSON: function (s) { + this.reset(); + // this.romData = s.romData; + this.cpu.fromJSON(s.cpu); + this.mmap.fromJSON(s.mmap); + this.ppu.fromJSON(s.ppu); + this.papu.fromJSON(s.papu); + }, +}; + +module.exports = NES; diff --git a/jsnes/src/papu.js b/jsnes/src/papu.js new file mode 100644 index 0000000..2579eb8 --- /dev/null +++ b/jsnes/src/papu.js @@ -0,0 +1,1559 @@ +var utils = require("./utils"); + +var CPU_FREQ_NTSC = 1789772.5; //1789772.72727272d; +// var CPU_FREQ_PAL = 1773447.4; + +var PAPU = function (nes) { + this.nes = nes; + + this.square1 = new ChannelSquare(this, true); + this.square2 = new ChannelSquare(this, false); + this.triangle = new ChannelTriangle(this); + this.noise = new ChannelNoise(this); + this.dmc = new ChannelDM(this); + + this.frameIrqCounter = null; + this.frameIrqCounterMax = 4; + this.initCounter = 2048; + this.channelEnableValue = null; + + this.sampleRate = 44100; + + this.lengthLookup = null; + this.dmcFreqLookup = null; + this.noiseWavelengthLookup = null; + this.square_table = null; + this.tnd_table = null; + + this.frameIrqEnabled = false; + this.frameIrqActive = null; + this.frameClockNow = null; + this.startedPlaying = false; + this.recordOutput = false; + this.initingHardware = false; + + this.masterFrameCounter = null; + this.derivedFrameCounter = null; + this.countSequence = null; + this.sampleTimer = null; + this.frameTime = null; + this.sampleTimerMax = null; + this.sampleCount = null; + this.triValue = 0; + + this.smpSquare1 = null; + this.smpSquare2 = null; + this.smpTriangle = null; + this.smpDmc = null; + this.accCount = null; + + // DC removal vars: + this.prevSampleL = 0; + this.prevSampleR = 0; + this.smpAccumL = 0; + this.smpAccumR = 0; + + // DAC range: + this.dacRange = 0; + this.dcValue = 0; + + // Master volume: + this.masterVolume = 256; + + // Stereo positioning: + this.stereoPosLSquare1 = null; + this.stereoPosLSquare2 = null; + this.stereoPosLTriangle = null; + this.stereoPosLNoise = null; + this.stereoPosLDMC = null; + this.stereoPosRSquare1 = null; + this.stereoPosRSquare2 = null; + this.stereoPosRTriangle = null; + this.stereoPosRNoise = null; + this.stereoPosRDMC = null; + + this.extraCycles = null; + + this.maxSample = null; + this.minSample = null; + + // Panning: + this.panning = [80, 170, 100, 150, 128]; + this.setPanning(this.panning); + + // Initialize lookup tables: + this.initLengthLookup(); + this.initDmcFrequencyLookup(); + this.initNoiseWavelengthLookup(); + this.initDACtables(); + + // Init sound registers: + for (var i = 0; i < 0x14; i++) { + if (i === 0x10) { + this.writeReg(0x4010, 0x10); + } else { + this.writeReg(0x4000 + i, 0); + } + } + + this.reset(); +}; + +PAPU.prototype = { + reset: function () { + this.sampleRate = this.nes.opts.sampleRate; + this.sampleTimerMax = Math.floor( + (1024.0 * CPU_FREQ_NTSC * this.nes.opts.preferredFrameRate) / + (this.sampleRate * 60.0) + ); + + this.frameTime = Math.floor( + (14915.0 * this.nes.opts.preferredFrameRate) / 60.0 + ); + + this.sampleTimer = 0; + + this.updateChannelEnable(0); + this.masterFrameCounter = 0; + this.derivedFrameCounter = 0; + this.countSequence = 0; + this.sampleCount = 0; + this.initCounter = 2048; + this.frameIrqEnabled = false; + this.initingHardware = false; + + this.resetCounter(); + + this.square1.reset(); + this.square2.reset(); + this.triangle.reset(); + this.noise.reset(); + this.dmc.reset(); + + this.accCount = 0; + this.smpSquare1 = 0; + this.smpSquare2 = 0; + this.smpTriangle = 0; + this.smpDmc = 0; + + this.frameIrqEnabled = false; + this.frameIrqCounterMax = 4; + + this.channelEnableValue = 0xff; + this.startedPlaying = false; + this.prevSampleL = 0; + this.prevSampleR = 0; + this.smpAccumL = 0; + this.smpAccumR = 0; + + this.maxSample = -500000; + this.minSample = 500000; + }, + + // eslint-disable-next-line no-unused-vars + readReg: function (address) { + // Read 0x4015: + var tmp = 0; + tmp |= this.square1.getLengthStatus(); + tmp |= this.square2.getLengthStatus() << 1; + tmp |= this.triangle.getLengthStatus() << 2; + tmp |= this.noise.getLengthStatus() << 3; + tmp |= this.dmc.getLengthStatus() << 4; + tmp |= (this.frameIrqActive && this.frameIrqEnabled ? 1 : 0) << 6; + tmp |= this.dmc.getIrqStatus() << 7; + + this.frameIrqActive = false; + this.dmc.irqGenerated = false; + + return tmp & 0xffff; + }, + + writeReg: function (address, value) { + if (address >= 0x4000 && address < 0x4004) { + // Square Wave 1 Control + this.square1.writeReg(address, value); + // console.log("Square Write"); + } else if (address >= 0x4004 && address < 0x4008) { + // Square 2 Control + this.square2.writeReg(address, value); + } else if (address >= 0x4008 && address < 0x400c) { + // Triangle Control + this.triangle.writeReg(address, value); + } else if (address >= 0x400c && address <= 0x400f) { + // Noise Control + this.noise.writeReg(address, value); + } else if (address === 0x4010) { + // DMC Play mode & DMA frequency + this.dmc.writeReg(address, value); + } else if (address === 0x4011) { + // DMC Delta Counter + this.dmc.writeReg(address, value); + } else if (address === 0x4012) { + // DMC Play code starting address + this.dmc.writeReg(address, value); + } else if (address === 0x4013) { + // DMC Play code length + this.dmc.writeReg(address, value); + } else if (address === 0x4015) { + // Channel enable + this.updateChannelEnable(value); + + if (value !== 0 && this.initCounter > 0) { + // Start hardware initialization + this.initingHardware = true; + } + + // DMC/IRQ Status + this.dmc.writeReg(address, value); + } else if (address === 0x4017) { + // Frame counter control + this.countSequence = (value >> 7) & 1; + this.masterFrameCounter = 0; + this.frameIrqActive = false; + + if (((value >> 6) & 0x1) === 0) { + this.frameIrqEnabled = true; + } else { + this.frameIrqEnabled = false; + } + + if (this.countSequence === 0) { + // NTSC: + this.frameIrqCounterMax = 4; + this.derivedFrameCounter = 4; + } else { + // PAL: + this.frameIrqCounterMax = 5; + this.derivedFrameCounter = 0; + this.frameCounterTick(); + } + } + }, + + resetCounter: function () { + if (this.countSequence === 0) { + this.derivedFrameCounter = 4; + } else { + this.derivedFrameCounter = 0; + } + }, + + // Updates channel enable status. + // This is done on writes to the + // channel enable register (0x4015), + // and when the user enables/disables channels + // in the GUI. + updateChannelEnable: function (value) { + this.channelEnableValue = value & 0xffff; + this.square1.setEnabled((value & 1) !== 0); + this.square2.setEnabled((value & 2) !== 0); + this.triangle.setEnabled((value & 4) !== 0); + this.noise.setEnabled((value & 8) !== 0); + this.dmc.setEnabled((value & 16) !== 0); + }, + + // Clocks the frame counter. It should be clocked at + // twice the cpu speed, so the cycles will be + // divided by 2 for those counters that are + // clocked at cpu speed. + clockFrameCounter: function (nCycles) { + if (this.initCounter > 0) { + if (this.initingHardware) { + this.initCounter -= nCycles; + if (this.initCounter <= 0) { + this.initingHardware = false; + } + return; + } + } + + // Don't process ticks beyond next sampling: + nCycles += this.extraCycles; + var maxCycles = this.sampleTimerMax - this.sampleTimer; + if (nCycles << 10 > maxCycles) { + this.extraCycles = ((nCycles << 10) - maxCycles) >> 10; + nCycles -= this.extraCycles; + } else { + this.extraCycles = 0; + } + + var dmc = this.dmc; + var triangle = this.triangle; + var square1 = this.square1; + var square2 = this.square2; + var noise = this.noise; + + // Clock DMC: + if (dmc.isEnabled) { + dmc.shiftCounter -= nCycles << 3; + while (dmc.shiftCounter <= 0 && dmc.dmaFrequency > 0) { + dmc.shiftCounter += dmc.dmaFrequency; + dmc.clockDmc(); + } + } + + // Clock Triangle channel Prog timer: + if (triangle.progTimerMax > 0) { + triangle.progTimerCount -= nCycles; + while (triangle.progTimerCount <= 0) { + triangle.progTimerCount += triangle.progTimerMax + 1; + if (triangle.linearCounter > 0 && triangle.lengthCounter > 0) { + triangle.triangleCounter++; + triangle.triangleCounter &= 0x1f; + + if (triangle.isEnabled) { + if (triangle.triangleCounter >= 0x10) { + // Normal value. + triangle.sampleValue = triangle.triangleCounter & 0xf; + } else { + // Inverted value. + triangle.sampleValue = 0xf - (triangle.triangleCounter & 0xf); + } + triangle.sampleValue <<= 4; + } + } + } + } + + // Clock Square channel 1 Prog timer: + square1.progTimerCount -= nCycles; + if (square1.progTimerCount <= 0) { + square1.progTimerCount += (square1.progTimerMax + 1) << 1; + + square1.squareCounter++; + square1.squareCounter &= 0x7; + square1.updateSampleValue(); + } + + // Clock Square channel 2 Prog timer: + square2.progTimerCount -= nCycles; + if (square2.progTimerCount <= 0) { + square2.progTimerCount += (square2.progTimerMax + 1) << 1; + + square2.squareCounter++; + square2.squareCounter &= 0x7; + square2.updateSampleValue(); + } + + // Clock noise channel Prog timer: + var acc_c = nCycles; + if (noise.progTimerCount - acc_c > 0) { + // Do all cycles at once: + noise.progTimerCount -= acc_c; + noise.accCount += acc_c; + noise.accValue += acc_c * noise.sampleValue; + } else { + // Slow-step: + while (acc_c-- > 0) { + if (--noise.progTimerCount <= 0 && noise.progTimerMax > 0) { + // Update noise shift register: + noise.shiftReg <<= 1; + noise.tmp = + ((noise.shiftReg << (noise.randomMode === 0 ? 1 : 6)) ^ + noise.shiftReg) & + 0x8000; + if (noise.tmp !== 0) { + // Sample value must be 0. + noise.shiftReg |= 0x01; + noise.randomBit = 0; + noise.sampleValue = 0; + } else { + // Find sample value: + noise.randomBit = 1; + if (noise.isEnabled && noise.lengthCounter > 0) { + noise.sampleValue = noise.masterVolume; + } else { + noise.sampleValue = 0; + } + } + + noise.progTimerCount += noise.progTimerMax; + } + + noise.accValue += noise.sampleValue; + noise.accCount++; + } + } + + // Frame IRQ handling: + if (this.frameIrqEnabled && this.frameIrqActive) { + this.nes.cpu.requestIrq(this.nes.cpu.IRQ_NORMAL); + } + + // Clock frame counter at double CPU speed: + this.masterFrameCounter += nCycles << 1; + if (this.masterFrameCounter >= this.frameTime) { + // 240Hz tick: + this.masterFrameCounter -= this.frameTime; + this.frameCounterTick(); + } + + // Accumulate sample value: + this.accSample(nCycles); + + // Clock sample timer: + this.sampleTimer += nCycles << 10; + if (this.sampleTimer >= this.sampleTimerMax) { + // Sample channels: + this.sample(); + this.sampleTimer -= this.sampleTimerMax; + } + }, + + accSample: function (cycles) { + // Special treatment for triangle channel - need to interpolate. + if (this.triangle.sampleCondition) { + this.triValue = Math.floor( + (this.triangle.progTimerCount << 4) / (this.triangle.progTimerMax + 1) + ); + if (this.triValue > 16) { + this.triValue = 16; + } + if (this.triangle.triangleCounter >= 16) { + this.triValue = 16 - this.triValue; + } + + // Add non-interpolated sample value: + this.triValue += this.triangle.sampleValue; + } + + // Now sample normally: + if (cycles === 2) { + this.smpTriangle += this.triValue << 1; + this.smpDmc += this.dmc.sample << 1; + this.smpSquare1 += this.square1.sampleValue << 1; + this.smpSquare2 += this.square2.sampleValue << 1; + this.accCount += 2; + } else if (cycles === 4) { + this.smpTriangle += this.triValue << 2; + this.smpDmc += this.dmc.sample << 2; + this.smpSquare1 += this.square1.sampleValue << 2; + this.smpSquare2 += this.square2.sampleValue << 2; + this.accCount += 4; + } else { + this.smpTriangle += cycles * this.triValue; + this.smpDmc += cycles * this.dmc.sample; + this.smpSquare1 += cycles * this.square1.sampleValue; + this.smpSquare2 += cycles * this.square2.sampleValue; + this.accCount += cycles; + } + }, + + frameCounterTick: function () { + this.derivedFrameCounter++; + if (this.derivedFrameCounter >= this.frameIrqCounterMax) { + this.derivedFrameCounter = 0; + } + + if (this.derivedFrameCounter === 1 || this.derivedFrameCounter === 3) { + // Clock length & sweep: + this.triangle.clockLengthCounter(); + this.square1.clockLengthCounter(); + this.square2.clockLengthCounter(); + this.noise.clockLengthCounter(); + this.square1.clockSweep(); + this.square2.clockSweep(); + } + + if (this.derivedFrameCounter >= 0 && this.derivedFrameCounter < 4) { + // Clock linear & decay: + this.square1.clockEnvDecay(); + this.square2.clockEnvDecay(); + this.noise.clockEnvDecay(); + this.triangle.clockLinearCounter(); + } + + if (this.derivedFrameCounter === 3 && this.countSequence === 0) { + // Enable IRQ: + this.frameIrqActive = true; + } + + // End of 240Hz tick + }, + + // Samples the channels, mixes the output together, then writes to buffer. + sample: function () { + var sq_index, tnd_index; + + if (this.accCount > 0) { + this.smpSquare1 <<= 4; + this.smpSquare1 = Math.floor(this.smpSquare1 / this.accCount); + + this.smpSquare2 <<= 4; + this.smpSquare2 = Math.floor(this.smpSquare2 / this.accCount); + + this.smpTriangle = Math.floor(this.smpTriangle / this.accCount); + + this.smpDmc <<= 4; + this.smpDmc = Math.floor(this.smpDmc / this.accCount); + + this.accCount = 0; + } else { + this.smpSquare1 = this.square1.sampleValue << 4; + this.smpSquare2 = this.square2.sampleValue << 4; + this.smpTriangle = this.triangle.sampleValue; + this.smpDmc = this.dmc.sample << 4; + } + + var smpNoise = Math.floor((this.noise.accValue << 4) / this.noise.accCount); + this.noise.accValue = smpNoise >> 4; + this.noise.accCount = 1; + + // Stereo sound. + + // Left channel: + sq_index = + (this.smpSquare1 * this.stereoPosLSquare1 + + this.smpSquare2 * this.stereoPosLSquare2) >> + 8; + tnd_index = + (3 * this.smpTriangle * this.stereoPosLTriangle + + (smpNoise << 1) * this.stereoPosLNoise + + this.smpDmc * this.stereoPosLDMC) >> + 8; + if (sq_index >= this.square_table.length) { + sq_index = this.square_table.length - 1; + } + if (tnd_index >= this.tnd_table.length) { + tnd_index = this.tnd_table.length - 1; + } + var sampleValueL = + this.square_table[sq_index] + this.tnd_table[tnd_index] - this.dcValue; + + // Right channel: + sq_index = + (this.smpSquare1 * this.stereoPosRSquare1 + + this.smpSquare2 * this.stereoPosRSquare2) >> + 8; + tnd_index = + (3 * this.smpTriangle * this.stereoPosRTriangle + + (smpNoise << 1) * this.stereoPosRNoise + + this.smpDmc * this.stereoPosRDMC) >> + 8; + if (sq_index >= this.square_table.length) { + sq_index = this.square_table.length - 1; + } + if (tnd_index >= this.tnd_table.length) { + tnd_index = this.tnd_table.length - 1; + } + var sampleValueR = + this.square_table[sq_index] + this.tnd_table[tnd_index] - this.dcValue; + + // Remove DC from left channel: + var smpDiffL = sampleValueL - this.prevSampleL; + this.prevSampleL += smpDiffL; + this.smpAccumL += smpDiffL - (this.smpAccumL >> 10); + sampleValueL = this.smpAccumL; + + // Remove DC from right channel: + var smpDiffR = sampleValueR - this.prevSampleR; + this.prevSampleR += smpDiffR; + this.smpAccumR += smpDiffR - (this.smpAccumR >> 10); + sampleValueR = this.smpAccumR; + + // Write: + if (sampleValueL > this.maxSample) { + this.maxSample = sampleValueL; + } + if (sampleValueL < this.minSample) { + this.minSample = sampleValueL; + } + + if (this.nes.opts.onAudioSample) { + this.nes.opts.onAudioSample(sampleValueL / 32768, sampleValueR / 32768); + } + + // Reset sampled values: + this.smpSquare1 = 0; + this.smpSquare2 = 0; + this.smpTriangle = 0; + this.smpDmc = 0; + }, + + getLengthMax: function (value) { + return this.lengthLookup[value >> 3]; + }, + + getDmcFrequency: function (value) { + if (value >= 0 && value < 0x10) { + return this.dmcFreqLookup[value]; + } + return 0; + }, + + getNoiseWaveLength: function (value) { + if (value >= 0 && value < 0x10) { + return this.noiseWavelengthLookup[value]; + } + return 0; + }, + + setPanning: function (pos) { + for (var i = 0; i < 5; i++) { + this.panning[i] = pos[i]; + } + this.updateStereoPos(); + }, + + setMasterVolume: function (value) { + if (value < 0) { + value = 0; + } + if (value > 256) { + value = 256; + } + this.masterVolume = value; + this.updateStereoPos(); + }, + + updateStereoPos: function () { + this.stereoPosLSquare1 = (this.panning[0] * this.masterVolume) >> 8; + this.stereoPosLSquare2 = (this.panning[1] * this.masterVolume) >> 8; + this.stereoPosLTriangle = (this.panning[2] * this.masterVolume) >> 8; + this.stereoPosLNoise = (this.panning[3] * this.masterVolume) >> 8; + this.stereoPosLDMC = (this.panning[4] * this.masterVolume) >> 8; + + this.stereoPosRSquare1 = this.masterVolume - this.stereoPosLSquare1; + this.stereoPosRSquare2 = this.masterVolume - this.stereoPosLSquare2; + this.stereoPosRTriangle = this.masterVolume - this.stereoPosLTriangle; + this.stereoPosRNoise = this.masterVolume - this.stereoPosLNoise; + this.stereoPosRDMC = this.masterVolume - this.stereoPosLDMC; + }, + + initLengthLookup: function () { + // prettier-ignore + this.lengthLookup = [ + 0x0A, 0xFE, + 0x14, 0x02, + 0x28, 0x04, + 0x50, 0x06, + 0xA0, 0x08, + 0x3C, 0x0A, + 0x0E, 0x0C, + 0x1A, 0x0E, + 0x0C, 0x10, + 0x18, 0x12, + 0x30, 0x14, + 0x60, 0x16, + 0xC0, 0x18, + 0x48, 0x1A, + 0x10, 0x1C, + 0x20, 0x1E + ]; + }, + + initDmcFrequencyLookup: function () { + this.dmcFreqLookup = new Array(16); + + this.dmcFreqLookup[0x0] = 0xd60; + this.dmcFreqLookup[0x1] = 0xbe0; + this.dmcFreqLookup[0x2] = 0xaa0; + this.dmcFreqLookup[0x3] = 0xa00; + this.dmcFreqLookup[0x4] = 0x8f0; + this.dmcFreqLookup[0x5] = 0x7f0; + this.dmcFreqLookup[0x6] = 0x710; + this.dmcFreqLookup[0x7] = 0x6b0; + this.dmcFreqLookup[0x8] = 0x5f0; + this.dmcFreqLookup[0x9] = 0x500; + this.dmcFreqLookup[0xa] = 0x470; + this.dmcFreqLookup[0xb] = 0x400; + this.dmcFreqLookup[0xc] = 0x350; + this.dmcFreqLookup[0xd] = 0x2a0; + this.dmcFreqLookup[0xe] = 0x240; + this.dmcFreqLookup[0xf] = 0x1b0; + //for(int i=0;i<16;i++)dmcFreqLookup[i]/=8; + }, + + initNoiseWavelengthLookup: function () { + this.noiseWavelengthLookup = new Array(16); + + this.noiseWavelengthLookup[0x0] = 0x004; + this.noiseWavelengthLookup[0x1] = 0x008; + this.noiseWavelengthLookup[0x2] = 0x010; + this.noiseWavelengthLookup[0x3] = 0x020; + this.noiseWavelengthLookup[0x4] = 0x040; + this.noiseWavelengthLookup[0x5] = 0x060; + this.noiseWavelengthLookup[0x6] = 0x080; + this.noiseWavelengthLookup[0x7] = 0x0a0; + this.noiseWavelengthLookup[0x8] = 0x0ca; + this.noiseWavelengthLookup[0x9] = 0x0fe; + this.noiseWavelengthLookup[0xa] = 0x17c; + this.noiseWavelengthLookup[0xb] = 0x1fc; + this.noiseWavelengthLookup[0xc] = 0x2fa; + this.noiseWavelengthLookup[0xd] = 0x3f8; + this.noiseWavelengthLookup[0xe] = 0x7f2; + this.noiseWavelengthLookup[0xf] = 0xfe4; + }, + + initDACtables: function () { + var value, ival, i; + var max_sqr = 0; + var max_tnd = 0; + + this.square_table = new Array(32 * 16); + this.tnd_table = new Array(204 * 16); + + for (i = 0; i < 32 * 16; i++) { + value = 95.52 / (8128.0 / (i / 16.0) + 100.0); + value *= 0.98411; + value *= 50000.0; + ival = Math.floor(value); + + this.square_table[i] = ival; + if (ival > max_sqr) { + max_sqr = ival; + } + } + + for (i = 0; i < 204 * 16; i++) { + value = 163.67 / (24329.0 / (i / 16.0) + 100.0); + value *= 0.98411; + value *= 50000.0; + ival = Math.floor(value); + + this.tnd_table[i] = ival; + if (ival > max_tnd) { + max_tnd = ival; + } + } + + this.dacRange = max_sqr + max_tnd; + this.dcValue = this.dacRange / 2; + }, + + JSON_PROPERTIES: [ + "frameIrqCounter", + "frameIrqCounterMax", + "initCounter", + "channelEnableValue", + "sampleRate", + "frameIrqEnabled", + "frameIrqActive", + "frameClockNow", + "startedPlaying", + "recordOutput", + "initingHardware", + "masterFrameCounter", + "derivedFrameCounter", + "countSequence", + "sampleTimer", + "frameTime", + "sampleTimerMax", + "sampleCount", + "triValue", + "smpSquare1", + "smpSquare2", + "smpTriangle", + "smpDmc", + "accCount", + "prevSampleL", + "prevSampleR", + "smpAccumL", + "smpAccumR", + "masterVolume", + "stereoPosLSquare1", + "stereoPosLSquare2", + "stereoPosLTriangle", + "stereoPosLNoise", + "stereoPosLDMC", + "stereoPosRSquare1", + "stereoPosRSquare2", + "stereoPosRTriangle", + "stereoPosRNoise", + "stereoPosRDMC", + "extraCycles", + "maxSample", + "minSample", + "panning", + ], + + toJSON: function () { + let obj = utils.toJSON(this); + obj.dmc = this.dmc.toJSON(); + obj.noise = this.noise.toJSON(); + obj.square1 = this.square1.toJSON(); + obj.square2 = this.square2.toJSON(); + obj.triangle = this.triangle.toJSON(); + return obj; + }, + + fromJSON: function (s) { + utils.fromJSON(this, s); + this.dmc.fromJSON(s.dmc); + this.noise.fromJSON(s.noise); + this.square1.fromJSON(s.square1); + this.square2.fromJSON(s.square2); + this.triangle.fromJSON(s.triangle); + }, +}; + +var ChannelDM = function (papu) { + this.papu = papu; + + this.MODE_NORMAL = 0; + this.MODE_LOOP = 1; + this.MODE_IRQ = 2; + + this.isEnabled = null; + this.hasSample = null; + this.irqGenerated = false; + + this.playMode = null; + this.dmaFrequency = null; + this.dmaCounter = null; + this.deltaCounter = null; + this.playStartAddress = null; + this.playAddress = null; + this.playLength = null; + this.playLengthCounter = null; + this.shiftCounter = null; + this.reg4012 = null; + this.reg4013 = null; + this.sample = null; + this.dacLsb = null; + this.data = null; + + this.reset(); +}; + +ChannelDM.prototype = { + clockDmc: function () { + // Only alter DAC value if the sample buffer has data: + if (this.hasSample) { + if ((this.data & 1) === 0) { + // Decrement delta: + if (this.deltaCounter > 0) { + this.deltaCounter--; + } + } else { + // Increment delta: + if (this.deltaCounter < 63) { + this.deltaCounter++; + } + } + + // Update sample value: + this.sample = this.isEnabled ? (this.deltaCounter << 1) + this.dacLsb : 0; + + // Update shift register: + this.data >>= 1; + } + + this.dmaCounter--; + if (this.dmaCounter <= 0) { + // No more sample bits. + this.hasSample = false; + this.endOfSample(); + this.dmaCounter = 8; + } + + if (this.irqGenerated) { + this.papu.nes.cpu.requestIrq(this.papu.nes.cpu.IRQ_NORMAL); + } + }, + + endOfSample: function () { + if (this.playLengthCounter === 0 && this.playMode === this.MODE_LOOP) { + // Start from beginning of sample: + this.playAddress = this.playStartAddress; + this.playLengthCounter = this.playLength; + } + + if (this.playLengthCounter > 0) { + // Fetch next sample: + this.nextSample(); + + if (this.playLengthCounter === 0) { + // Last byte of sample fetched, generate IRQ: + if (this.playMode === this.MODE_IRQ) { + // Generate IRQ: + this.irqGenerated = true; + } + } + } + }, + + nextSample: function () { + // Fetch byte: + this.data = this.papu.nes.mmap.load(this.playAddress); + this.papu.nes.cpu.haltCycles(4); + + this.playLengthCounter--; + this.playAddress++; + if (this.playAddress > 0xffff) { + this.playAddress = 0x8000; + } + + this.hasSample = true; + }, + + writeReg: function (address, value) { + if (address === 0x4010) { + // Play mode, DMA Frequency + if (value >> 6 === 0) { + this.playMode = this.MODE_NORMAL; + } else if (((value >> 6) & 1) === 1) { + this.playMode = this.MODE_LOOP; + } else if (value >> 6 === 2) { + this.playMode = this.MODE_IRQ; + } + + if ((value & 0x80) === 0) { + this.irqGenerated = false; + } + + this.dmaFrequency = this.papu.getDmcFrequency(value & 0xf); + } else if (address === 0x4011) { + // Delta counter load register: + this.deltaCounter = (value >> 1) & 63; + this.dacLsb = value & 1; + this.sample = (this.deltaCounter << 1) + this.dacLsb; // update sample value + } else if (address === 0x4012) { + // DMA address load register + this.playStartAddress = (value << 6) | 0x0c000; + this.playAddress = this.playStartAddress; + this.reg4012 = value; + } else if (address === 0x4013) { + // Length of play code + this.playLength = (value << 4) + 1; + this.playLengthCounter = this.playLength; + this.reg4013 = value; + } else if (address === 0x4015) { + // DMC/IRQ Status + if (((value >> 4) & 1) === 0) { + // Disable: + this.playLengthCounter = 0; + } else { + // Restart: + this.playAddress = this.playStartAddress; + this.playLengthCounter = this.playLength; + } + this.irqGenerated = false; + } + }, + + setEnabled: function (value) { + if (!this.isEnabled && value) { + this.playLengthCounter = this.playLength; + } + this.isEnabled = value; + }, + + getLengthStatus: function () { + return this.playLengthCounter === 0 || !this.isEnabled ? 0 : 1; + }, + + getIrqStatus: function () { + return this.irqGenerated ? 1 : 0; + }, + + reset: function () { + this.isEnabled = false; + this.irqGenerated = false; + this.playMode = this.MODE_NORMAL; + this.dmaFrequency = 0; + this.dmaCounter = 0; + this.deltaCounter = 0; + this.playStartAddress = 0; + this.playAddress = 0; + this.playLength = 0; + this.playLengthCounter = 0; + this.sample = 0; + this.dacLsb = 0; + this.shiftCounter = 0; + this.reg4012 = 0; + this.reg4013 = 0; + this.data = 0; + }, + + JSON_PROPERTIES: [ + "MODE_NORMAL", + "MODE_LOOP", + "MODE_IRQ", + "isEnabled", + "hasSample", + "irqGenerated", + "playMode", + "dmaFrequency", + "dmaCounter", + "deltaCounter", + "playStartAddress", + "playAddress", + "playLength", + "playLengthCounter", + "shiftCounter", + "reg4012", + "reg4013", + "sample", + "dacLsb", + "data", + ], + + toJSON: function () { + return utils.toJSON(this); + }, + + fromJSON: function (s) { + utils.fromJSON(this, s); + }, +}; + +var ChannelNoise = function (papu) { + this.papu = papu; + + this.isEnabled = null; + this.envDecayDisable = null; + this.envDecayLoopEnable = null; + this.lengthCounterEnable = null; + this.envReset = null; + this.shiftNow = null; + + this.lengthCounter = null; + this.progTimerCount = null; + this.progTimerMax = null; + this.envDecayRate = null; + this.envDecayCounter = null; + this.envVolume = null; + this.masterVolume = null; + this.shiftReg = 1 << 14; + this.randomBit = null; + this.randomMode = null; + this.sampleValue = null; + this.accValue = 0; + this.accCount = 1; + this.tmp = null; + + this.reset(); +}; + +ChannelNoise.prototype = { + reset: function () { + this.progTimerCount = 0; + this.progTimerMax = 0; + this.isEnabled = false; + this.lengthCounter = 0; + this.lengthCounterEnable = false; + this.envDecayDisable = false; + this.envDecayLoopEnable = false; + this.shiftNow = false; + this.envDecayRate = 0; + this.envDecayCounter = 0; + this.envVolume = 0; + this.masterVolume = 0; + this.shiftReg = 1; + this.randomBit = 0; + this.randomMode = 0; + this.sampleValue = 0; + this.tmp = 0; + }, + + clockLengthCounter: function () { + if (this.lengthCounterEnable && this.lengthCounter > 0) { + this.lengthCounter--; + if (this.lengthCounter === 0) { + this.updateSampleValue(); + } + } + }, + + clockEnvDecay: function () { + if (this.envReset) { + // Reset envelope: + this.envReset = false; + this.envDecayCounter = this.envDecayRate + 1; + this.envVolume = 0xf; + } else if (--this.envDecayCounter <= 0) { + // Normal handling: + this.envDecayCounter = this.envDecayRate + 1; + if (this.envVolume > 0) { + this.envVolume--; + } else { + this.envVolume = this.envDecayLoopEnable ? 0xf : 0; + } + } + if (this.envDecayDisable) { + this.masterVolume = this.envDecayRate; + } else { + this.masterVolume = this.envVolume; + } + this.updateSampleValue(); + }, + + updateSampleValue: function () { + if (this.isEnabled && this.lengthCounter > 0) { + this.sampleValue = this.randomBit * this.masterVolume; + } + }, + + writeReg: function (address, value) { + if (address === 0x400c) { + // Volume/Envelope decay: + this.envDecayDisable = (value & 0x10) !== 0; + this.envDecayRate = value & 0xf; + this.envDecayLoopEnable = (value & 0x20) !== 0; + this.lengthCounterEnable = (value & 0x20) === 0; + if (this.envDecayDisable) { + this.masterVolume = this.envDecayRate; + } else { + this.masterVolume = this.envVolume; + } + } else if (address === 0x400e) { + // Programmable timer: + this.progTimerMax = this.papu.getNoiseWaveLength(value & 0xf); + this.randomMode = value >> 7; + } else if (address === 0x400f) { + // Length counter + this.lengthCounter = this.papu.getLengthMax(value & 248); + this.envReset = true; + } + // Update: + //updateSampleValue(); + }, + + setEnabled: function (value) { + this.isEnabled = value; + if (!value) { + this.lengthCounter = 0; + } + this.updateSampleValue(); + }, + + getLengthStatus: function () { + return this.lengthCounter === 0 || !this.isEnabled ? 0 : 1; + }, + + JSON_PROPERTIES: [ + "isEnabled", + "envDecayDisable", + "envDecayLoopEnable", + "lengthCounterEnable", + "envReset", + "shiftNow", + "lengthCounter", + "progTimerCount", + "progTimerMax", + "envDecayRate", + "envDecayCounter", + "envVolume", + "masterVolume", + "shiftReg", + "randomBit", + "randomMode", + "sampleValue", + "accValue", + "accCount", + "tmp", + ], + + toJSON: function () { + return utils.toJSON(this); + }, + + fromJSON: function (s) { + utils.fromJSON(this, s); + }, +}; + +var ChannelSquare = function (papu, square1) { + this.papu = papu; + + // prettier-ignore + this.dutyLookup = [ + 0, 1, 0, 0, 0, 0, 0, 0, + 0, 1, 1, 0, 0, 0, 0, 0, + 0, 1, 1, 1, 1, 0, 0, 0, + 1, 0, 0, 1, 1, 1, 1, 1 + ]; + // prettier-ignore + this.impLookup = [ + 1,-1, 0, 0, 0, 0, 0, 0, + 1, 0,-1, 0, 0, 0, 0, 0, + 1, 0, 0, 0,-1, 0, 0, 0, + -1, 0, 1, 0, 0, 0, 0, 0 + ]; + + this.sqr1 = square1; + this.isEnabled = null; + this.lengthCounterEnable = null; + this.sweepActive = null; + this.envDecayDisable = null; + this.envDecayLoopEnable = null; + this.envReset = null; + this.sweepCarry = null; + this.updateSweepPeriod = null; + + this.progTimerCount = null; + this.progTimerMax = null; + this.lengthCounter = null; + this.squareCounter = null; + this.sweepCounter = null; + this.sweepCounterMax = null; + this.sweepMode = null; + this.sweepShiftAmount = null; + this.envDecayRate = null; + this.envDecayCounter = null; + this.envVolume = null; + this.masterVolume = null; + this.dutyMode = null; + this.sweepResult = null; + this.sampleValue = null; + this.vol = null; + + this.reset(); +}; + +ChannelSquare.prototype = { + reset: function () { + this.progTimerCount = 0; + this.progTimerMax = 0; + this.lengthCounter = 0; + this.squareCounter = 0; + this.sweepCounter = 0; + this.sweepCounterMax = 0; + this.sweepMode = 0; + this.sweepShiftAmount = 0; + this.envDecayRate = 0; + this.envDecayCounter = 0; + this.envVolume = 0; + this.masterVolume = 0; + this.dutyMode = 0; + this.vol = 0; + + this.isEnabled = false; + this.lengthCounterEnable = false; + this.sweepActive = false; + this.sweepCarry = false; + this.envDecayDisable = false; + this.envDecayLoopEnable = false; + }, + + clockLengthCounter: function () { + if (this.lengthCounterEnable && this.lengthCounter > 0) { + this.lengthCounter--; + if (this.lengthCounter === 0) { + this.updateSampleValue(); + } + } + }, + + clockEnvDecay: function () { + if (this.envReset) { + // Reset envelope: + this.envReset = false; + this.envDecayCounter = this.envDecayRate + 1; + this.envVolume = 0xf; + } else if (--this.envDecayCounter <= 0) { + // Normal handling: + this.envDecayCounter = this.envDecayRate + 1; + if (this.envVolume > 0) { + this.envVolume--; + } else { + this.envVolume = this.envDecayLoopEnable ? 0xf : 0; + } + } + + if (this.envDecayDisable) { + this.masterVolume = this.envDecayRate; + } else { + this.masterVolume = this.envVolume; + } + this.updateSampleValue(); + }, + + clockSweep: function () { + if (--this.sweepCounter <= 0) { + this.sweepCounter = this.sweepCounterMax + 1; + if ( + this.sweepActive && + this.sweepShiftAmount > 0 && + this.progTimerMax > 7 + ) { + // Calculate result from shifter: + this.sweepCarry = false; + if (this.sweepMode === 0) { + this.progTimerMax += this.progTimerMax >> this.sweepShiftAmount; + if (this.progTimerMax > 4095) { + this.progTimerMax = 4095; + this.sweepCarry = true; + } + } else { + this.progTimerMax = + this.progTimerMax - + ((this.progTimerMax >> this.sweepShiftAmount) - + (this.sqr1 ? 1 : 0)); + } + } + } + + if (this.updateSweepPeriod) { + this.updateSweepPeriod = false; + this.sweepCounter = this.sweepCounterMax + 1; + } + }, + + updateSampleValue: function () { + if (this.isEnabled && this.lengthCounter > 0 && this.progTimerMax > 7) { + if ( + this.sweepMode === 0 && + this.progTimerMax + (this.progTimerMax >> this.sweepShiftAmount) > 4095 + ) { + //if (this.sweepCarry) { + this.sampleValue = 0; + } else { + this.sampleValue = + this.masterVolume * + this.dutyLookup[(this.dutyMode << 3) + this.squareCounter]; + } + } else { + this.sampleValue = 0; + } + }, + + writeReg: function (address, value) { + var addrAdd = this.sqr1 ? 0 : 4; + if (address === 0x4000 + addrAdd) { + // Volume/Envelope decay: + this.envDecayDisable = (value & 0x10) !== 0; + this.envDecayRate = value & 0xf; + this.envDecayLoopEnable = (value & 0x20) !== 0; + this.dutyMode = (value >> 6) & 0x3; + this.lengthCounterEnable = (value & 0x20) === 0; + if (this.envDecayDisable) { + this.masterVolume = this.envDecayRate; + } else { + this.masterVolume = this.envVolume; + } + this.updateSampleValue(); + } else if (address === 0x4001 + addrAdd) { + // Sweep: + this.sweepActive = (value & 0x80) !== 0; + this.sweepCounterMax = (value >> 4) & 7; + this.sweepMode = (value >> 3) & 1; + this.sweepShiftAmount = value & 7; + this.updateSweepPeriod = true; + } else if (address === 0x4002 + addrAdd) { + // Programmable timer: + this.progTimerMax &= 0x700; + this.progTimerMax |= value; + } else if (address === 0x4003 + addrAdd) { + // Programmable timer, length counter + this.progTimerMax &= 0xff; + this.progTimerMax |= (value & 0x7) << 8; + + if (this.isEnabled) { + this.lengthCounter = this.papu.getLengthMax(value & 0xf8); + } + + this.envReset = true; + } + }, + + setEnabled: function (value) { + this.isEnabled = value; + if (!value) { + this.lengthCounter = 0; + } + this.updateSampleValue(); + }, + + getLengthStatus: function () { + return this.lengthCounter === 0 || !this.isEnabled ? 0 : 1; + }, + + JSON_PROPERTIES: [ + "isEnabled", + "lengthCounterEnable", + "sweepActive", + "envDecayDisable", + "envDecayLoopEnable", + "envReset", + "sweepCarry", + "updateSweepPeriod", + "progTimerCount", + "progTimerMax", + "lengthCounter", + "squareCounter", + "sweepCounter", + "sweepCounterMax", + "sweepMode", + "sweepShiftAmount", + "envDecayRate", + "envDecayCounter", + "envVolume", + "masterVolume", + "dutyMode", + "sweepResult", + "sampleValue", + "vol", + ], + + toJSON: function () { + return utils.toJSON(this); + }, + + fromJSON: function (s) { + utils.fromJSON(this, s); + }, +}; + +var ChannelTriangle = function (papu) { + this.papu = papu; + + this.isEnabled = null; + this.sampleCondition = null; + this.lengthCounterEnable = null; + this.lcHalt = null; + this.lcControl = null; + + this.progTimerCount = null; + this.progTimerMax = null; + this.triangleCounter = null; + this.lengthCounter = null; + this.linearCounter = null; + this.lcLoadValue = null; + this.sampleValue = null; + this.tmp = null; + + this.reset(); +}; + +ChannelTriangle.prototype = { + reset: function () { + this.progTimerCount = 0; + this.progTimerMax = 0; + this.triangleCounter = 0; + this.isEnabled = false; + this.sampleCondition = false; + this.lengthCounter = 0; + this.lengthCounterEnable = false; + this.linearCounter = 0; + this.lcLoadValue = 0; + this.lcHalt = true; + this.lcControl = false; + this.tmp = 0; + this.sampleValue = 0xf; + }, + + clockLengthCounter: function () { + if (this.lengthCounterEnable && this.lengthCounter > 0) { + this.lengthCounter--; + if (this.lengthCounter === 0) { + this.updateSampleCondition(); + } + } + }, + + clockLinearCounter: function () { + if (this.lcHalt) { + // Load: + this.linearCounter = this.lcLoadValue; + this.updateSampleCondition(); + } else if (this.linearCounter > 0) { + // Decrement: + this.linearCounter--; + this.updateSampleCondition(); + } + if (!this.lcControl) { + // Clear halt flag: + this.lcHalt = false; + } + }, + + getLengthStatus: function () { + return this.lengthCounter === 0 || !this.isEnabled ? 0 : 1; + }, + + // eslint-disable-next-line no-unused-vars + readReg: function (address) { + return 0; + }, + + writeReg: function (address, value) { + if (address === 0x4008) { + // New values for linear counter: + this.lcControl = (value & 0x80) !== 0; + this.lcLoadValue = value & 0x7f; + + // Length counter enable: + this.lengthCounterEnable = !this.lcControl; + } else if (address === 0x400a) { + // Programmable timer: + this.progTimerMax &= 0x700; + this.progTimerMax |= value; + } else if (address === 0x400b) { + // Programmable timer, length counter + this.progTimerMax &= 0xff; + this.progTimerMax |= (value & 0x07) << 8; + this.lengthCounter = this.papu.getLengthMax(value & 0xf8); + this.lcHalt = true; + } + + this.updateSampleCondition(); + }, + + clockProgrammableTimer: function (nCycles) { + if (this.progTimerMax > 0) { + this.progTimerCount += nCycles; + while ( + this.progTimerMax > 0 && + this.progTimerCount >= this.progTimerMax + ) { + this.progTimerCount -= this.progTimerMax; + if ( + this.isEnabled && + this.lengthCounter > 0 && + this.linearCounter > 0 + ) { + this.clockTriangleGenerator(); + } + } + } + }, + + clockTriangleGenerator: function () { + this.triangleCounter++; + this.triangleCounter &= 0x1f; + }, + + setEnabled: function (value) { + this.isEnabled = value; + if (!value) { + this.lengthCounter = 0; + } + this.updateSampleCondition(); + }, + + updateSampleCondition: function () { + this.sampleCondition = + this.isEnabled && + this.progTimerMax > 7 && + this.linearCounter > 0 && + this.lengthCounter > 0; + }, + + JSON_PROPERTIES: [ + "isEnabled", + "sampleCondition", + "lengthCounterEnable", + "lcHalt", + "lcControl", + "progTimerCount", + "progTimerMax", + "triangleCounter", + "lengthCounter", + "linearCounter", + "lcLoadValue", + "sampleValue", + "tmp", + ], + + toJSON: function () { + return utils.toJSON(this); + }, + + fromJSON: function (s) { + utils.fromJSON(this, s); + }, +}; + +module.exports = PAPU; diff --git a/jsnes/src/ppu.js b/jsnes/src/ppu.js new file mode 100644 index 0000000..7045cae --- /dev/null +++ b/jsnes/src/ppu.js @@ -0,0 +1,1753 @@ +var Tile = require("./tile"); +var utils = require("./utils"); + +var PPU = function (nes) { + this.nes = nes; + + // Keep Chrome happy + this.vramMem = null; + this.spriteMem = null; + this.vramAddress = null; + this.vramTmpAddress = null; + this.vramBufferedReadValue = null; + this.firstWrite = null; + this.sramAddress = null; + this.currentMirroring = null; + this.requestEndFrame = null; + this.nmiOk = null; + this.dummyCycleToggle = null; + this.validTileData = null; + this.nmiCounter = null; + this.scanlineAlreadyRendered = null; + this.f_nmiOnVblank = null; + this.f_spriteSize = null; + this.f_bgPatternTable = null; + this.f_spPatternTable = null; + this.f_addrInc = null; + this.f_nTblAddress = null; + this.f_color = null; + this.f_spVisibility = null; + this.f_bgVisibility = null; + this.f_spClipping = null; + this.f_bgClipping = null; + this.f_dispType = null; + this.cntFV = null; + this.cntV = null; + this.cntH = null; + this.cntVT = null; + this.cntHT = null; + this.regFV = null; + this.regV = null; + this.regH = null; + this.regVT = null; + this.regHT = null; + this.regFH = null; + this.regS = null; + this.curNt = null; + this.attrib = null; + this.buffer = null; + this.bgbuffer = null; + this.pixrendered = null; + + this.validTileData = null; + this.scantile = null; + this.scanline = null; + this.lastRenderedScanline = null; + this.curX = null; + this.sprX = null; + this.sprY = null; + this.sprTile = null; + this.sprCol = null; + this.vertFlip = null; + this.horiFlip = null; + this.bgPriority = null; + this.spr0HitX = null; + this.spr0HitY = null; + this.hitSpr0 = null; + this.sprPalette = null; + this.imgPalette = null; + this.ptTile = null; + this.ntable1 = null; + this.currentMirroring = null; + this.nameTable = null; + this.vramMirrorTable = null; + this.palTable = null; + + // Rendering Options: + this.showSpr0Hit = false; + this.clipToTvSize = true; + + this.reset(); +}; + +PPU.prototype = { + // Status flags: + STATUS_VRAMWRITE: 4, + STATUS_SLSPRITECOUNT: 5, + STATUS_SPRITE0HIT: 6, + STATUS_VBLANK: 7, + + reset: function () { + var i; + + // Memory + this.vramMem = new Array(0x8000); + this.spriteMem = new Array(0x100); + for (i = 0; i < this.vramMem.length; i++) { + this.vramMem[i] = 0; + } + for (i = 0; i < this.spriteMem.length; i++) { + this.spriteMem[i] = 0; + } + + // VRAM I/O: + this.vramAddress = null; + this.vramTmpAddress = null; + this.vramBufferedReadValue = 0; + this.firstWrite = true; // VRAM/Scroll Hi/Lo latch + + // SPR-RAM I/O: + this.sramAddress = 0; // 8-bit only. + + this.currentMirroring = -1; + this.requestEndFrame = false; + this.nmiOk = false; + this.dummyCycleToggle = false; + this.validTileData = false; + this.nmiCounter = 0; + this.scanlineAlreadyRendered = null; + + // Control Flags Register 1: + this.f_nmiOnVblank = 0; // NMI on VBlank. 0=disable, 1=enable + this.f_spriteSize = 0; // Sprite size. 0=8x8, 1=8x16 + this.f_bgPatternTable = 0; // Background Pattern Table address. 0=0x0000,1=0x1000 + this.f_spPatternTable = 0; // Sprite Pattern Table address. 0=0x0000,1=0x1000 + this.f_addrInc = 0; // PPU Address Increment. 0=1,1=32 + this.f_nTblAddress = 0; // Name Table Address. 0=0x2000,1=0x2400,2=0x2800,3=0x2C00 + + // Control Flags Register 2: + this.f_color = 0; // Background color. 0=black, 1=blue, 2=green, 4=red + this.f_spVisibility = 0; // Sprite visibility. 0=not displayed,1=displayed + this.f_bgVisibility = 0; // Background visibility. 0=Not Displayed,1=displayed + this.f_spClipping = 0; // Sprite clipping. 0=Sprites invisible in left 8-pixel column,1=No clipping + this.f_bgClipping = 0; // Background clipping. 0=BG invisible in left 8-pixel column, 1=No clipping + this.f_dispType = 0; // Display type. 0=color, 1=monochrome + + // Counters: + this.cntFV = 0; + this.cntV = 0; + this.cntH = 0; + this.cntVT = 0; + this.cntHT = 0; + + // Registers: + this.regFV = 0; + this.regV = 0; + this.regH = 0; + this.regVT = 0; + this.regHT = 0; + this.regFH = 0; + this.regS = 0; + + // These are temporary variables used in rendering and sound procedures. + // Their states outside of those procedures can be ignored. + // TODO: the use of this is a bit weird, investigate + this.curNt = null; + + // Variables used when rendering: + this.attrib = new Array(32); + this.buffer = new Array(256 * 240); + this.bgbuffer = new Array(256 * 240); + this.pixrendered = new Array(256 * 240); + + this.validTileData = null; + + this.scantile = new Array(32); + + // Initialize misc vars: + this.scanline = 0; + this.lastRenderedScanline = -1; + this.curX = 0; + + // Sprite data: + this.sprX = new Array(64); // X coordinate + this.sprY = new Array(64); // Y coordinate + this.sprTile = new Array(64); // Tile Index (into pattern table) + this.sprCol = new Array(64); // Upper two bits of color + this.vertFlip = new Array(64); // Vertical Flip + this.horiFlip = new Array(64); // Horizontal Flip + this.bgPriority = new Array(64); // Background priority + this.spr0HitX = 0; // Sprite #0 hit X coordinate + this.spr0HitY = 0; // Sprite #0 hit Y coordinate + this.hitSpr0 = false; + + // Palette data: + this.sprPalette = new Array(16); + this.imgPalette = new Array(16); + + // Create pattern table tile buffers: + this.ptTile = new Array(512); + for (i = 0; i < 512; i++) { + this.ptTile[i] = new Tile(); + } + + // Create nametable buffers: + // Name table data: + this.ntable1 = new Array(4); + this.currentMirroring = -1; + this.nameTable = new Array(4); + for (i = 0; i < 4; i++) { + this.nameTable[i] = new NameTable(32, 32, "Nt" + i); + } + + // Initialize mirroring lookup table: + this.vramMirrorTable = new Array(0x8000); + for (i = 0; i < 0x8000; i++) { + this.vramMirrorTable[i] = i; + } + + this.palTable = new PaletteTable(); + this.palTable.loadNTSCPalette(); + //this.palTable.loadDefaultPalette(); + + this.updateControlReg1(0); + this.updateControlReg2(0); + }, + + // Sets Nametable mirroring. + setMirroring: function (mirroring) { + if (mirroring === this.currentMirroring) { + return; + } + + this.currentMirroring = mirroring; + this.triggerRendering(); + + // Remove mirroring: + if (this.vramMirrorTable === null) { + this.vramMirrorTable = new Array(0x8000); + } + for (var i = 0; i < 0x8000; i++) { + this.vramMirrorTable[i] = i; + } + + // Palette mirroring: + this.defineMirrorRegion(0x3f20, 0x3f00, 0x20); + this.defineMirrorRegion(0x3f40, 0x3f00, 0x20); + this.defineMirrorRegion(0x3f80, 0x3f00, 0x20); + this.defineMirrorRegion(0x3fc0, 0x3f00, 0x20); + + // Additional mirroring: + this.defineMirrorRegion(0x3000, 0x2000, 0xf00); + this.defineMirrorRegion(0x4000, 0x0000, 0x4000); + + if (mirroring === this.nes.rom.HORIZONTAL_MIRRORING) { + // Horizontal mirroring. + + this.ntable1[0] = 0; + this.ntable1[1] = 0; + this.ntable1[2] = 1; + this.ntable1[3] = 1; + + this.defineMirrorRegion(0x2400, 0x2000, 0x400); + this.defineMirrorRegion(0x2c00, 0x2800, 0x400); + } else if (mirroring === this.nes.rom.VERTICAL_MIRRORING) { + // Vertical mirroring. + + this.ntable1[0] = 0; + this.ntable1[1] = 1; + this.ntable1[2] = 0; + this.ntable1[3] = 1; + + this.defineMirrorRegion(0x2800, 0x2000, 0x400); + this.defineMirrorRegion(0x2c00, 0x2400, 0x400); + } else if (mirroring === this.nes.rom.SINGLESCREEN_MIRRORING) { + // Single Screen mirroring + + this.ntable1[0] = 0; + this.ntable1[1] = 0; + this.ntable1[2] = 0; + this.ntable1[3] = 0; + + this.defineMirrorRegion(0x2400, 0x2000, 0x400); + this.defineMirrorRegion(0x2800, 0x2000, 0x400); + this.defineMirrorRegion(0x2c00, 0x2000, 0x400); + } else if (mirroring === this.nes.rom.SINGLESCREEN_MIRRORING2) { + this.ntable1[0] = 1; + this.ntable1[1] = 1; + this.ntable1[2] = 1; + this.ntable1[3] = 1; + + this.defineMirrorRegion(0x2400, 0x2400, 0x400); + this.defineMirrorRegion(0x2800, 0x2400, 0x400); + this.defineMirrorRegion(0x2c00, 0x2400, 0x400); + } else { + // Assume Four-screen mirroring. + + this.ntable1[0] = 0; + this.ntable1[1] = 1; + this.ntable1[2] = 2; + this.ntable1[3] = 3; + } + }, + + // Define a mirrored area in the address lookup table. + // Assumes the regions don't overlap. + // The 'to' region is the region that is physically in memory. + defineMirrorRegion: function (fromStart, toStart, size) { + for (var i = 0; i < size; i++) { + this.vramMirrorTable[fromStart + i] = toStart + i; + } + }, + + startVBlank: function () { + // Do NMI: + this.nes.cpu.requestIrq(this.nes.cpu.IRQ_NMI); + + // Make sure everything is rendered: + if (this.lastRenderedScanline < 239) { + this.renderFramePartially( + this.lastRenderedScanline + 1, + 240 - this.lastRenderedScanline + ); + } + + // End frame: + this.endFrame(); + + // Reset scanline counter: + this.lastRenderedScanline = -1; + }, + + endScanline: function () { + switch (this.scanline) { + case 19: + // Dummy scanline. + // May be variable length: + if (this.dummyCycleToggle) { + // Remove dead cycle at end of scanline, + // for next scanline: + this.curX = 1; + this.dummyCycleToggle = !this.dummyCycleToggle; + } + break; + + case 20: + // Clear VBlank flag: + this.setStatusFlag(this.STATUS_VBLANK, false); + + // Clear Sprite #0 hit flag: + this.setStatusFlag(this.STATUS_SPRITE0HIT, false); + this.hitSpr0 = false; + this.spr0HitX = -1; + this.spr0HitY = -1; + + if (this.f_bgVisibility === 1 || this.f_spVisibility === 1) { + // Update counters: + this.cntFV = this.regFV; + this.cntV = this.regV; + this.cntH = this.regH; + this.cntVT = this.regVT; + this.cntHT = this.regHT; + + if (this.f_bgVisibility === 1) { + // Render dummy scanline: + this.renderBgScanline(false, 0); + } + } + + if (this.f_bgVisibility === 1 && this.f_spVisibility === 1) { + // Check sprite 0 hit for first scanline: + this.checkSprite0(0); + } + + if (this.f_bgVisibility === 1 || this.f_spVisibility === 1) { + // Clock mapper IRQ Counter: + this.nes.mmap.clockIrqCounter(); + } + break; + + case 261: + // Dead scanline, no rendering. + // Set VINT: + this.setStatusFlag(this.STATUS_VBLANK, true); + this.requestEndFrame = true; + this.nmiCounter = 9; + + // Wrap around: + this.scanline = -1; // will be incremented to 0 + + break; + + default: + if (this.scanline >= 21 && this.scanline <= 260) { + // Render normally: + if (this.f_bgVisibility === 1) { + if (!this.scanlineAlreadyRendered) { + // update scroll: + this.cntHT = this.regHT; + this.cntH = this.regH; + this.renderBgScanline(true, this.scanline + 1 - 21); + } + this.scanlineAlreadyRendered = false; + + // Check for sprite 0 (next scanline): + if (!this.hitSpr0 && this.f_spVisibility === 1) { + if ( + this.sprX[0] >= -7 && + this.sprX[0] < 256 && + this.sprY[0] + 1 <= this.scanline - 20 && + this.sprY[0] + 1 + (this.f_spriteSize === 0 ? 8 : 16) >= + this.scanline - 20 + ) { + if (this.checkSprite0(this.scanline - 20)) { + this.hitSpr0 = true; + } + } + } + } + + if (this.f_bgVisibility === 1 || this.f_spVisibility === 1) { + // Clock mapper IRQ Counter: + this.nes.mmap.clockIrqCounter(); + } + } + } + + this.scanline++; + this.regsToAddress(); + this.cntsToAddress(); + }, + + startFrame: function () { + // Set background color: + var bgColor = 0; + + if (this.f_dispType === 0) { + // Color display. + // f_color determines color emphasis. + // Use first entry of image palette as BG color. + bgColor = this.imgPalette[0]; + } else { + // Monochrome display. + // f_color determines the bg color. + switch (this.f_color) { + case 0: + // Black + bgColor = 0x00000; + break; + case 1: + // Green + bgColor = 0x00ff00; + break; + case 2: + // Blue + bgColor = 0xff0000; + break; + case 3: + // Invalid. Use black. + bgColor = 0x000000; + break; + case 4: + // Red + bgColor = 0x0000ff; + break; + default: + // Invalid. Use black. + bgColor = 0x0; + } + } + + var buffer = this.buffer; + var i; + for (i = 0; i < 256 * 240; i++) { + buffer[i] = bgColor; + } + var pixrendered = this.pixrendered; + for (i = 0; i < pixrendered.length; i++) { + pixrendered[i] = 65; + } + }, + + endFrame: function () { + var i, x, y; + var buffer = this.buffer; + + // Draw spr#0 hit coordinates: + if (this.showSpr0Hit) { + // Spr 0 position: + if ( + this.sprX[0] >= 0 && + this.sprX[0] < 256 && + this.sprY[0] >= 0 && + this.sprY[0] < 240 + ) { + for (i = 0; i < 256; i++) { + buffer[(this.sprY[0] << 8) + i] = 0xff5555; + } + for (i = 0; i < 240; i++) { + buffer[(i << 8) + this.sprX[0]] = 0xff5555; + } + } + // Hit position: + if ( + this.spr0HitX >= 0 && + this.spr0HitX < 256 && + this.spr0HitY >= 0 && + this.spr0HitY < 240 + ) { + for (i = 0; i < 256; i++) { + buffer[(this.spr0HitY << 8) + i] = 0x55ff55; + } + for (i = 0; i < 240; i++) { + buffer[(i << 8) + this.spr0HitX] = 0x55ff55; + } + } + } + + // This is a bit lazy.. + // if either the sprites or the background should be clipped, + // both are clipped after rendering is finished. + if ( + this.clipToTvSize || + this.f_bgClipping === 0 || + this.f_spClipping === 0 + ) { + // Clip left 8-pixels column: + for (y = 0; y < 240; y++) { + for (x = 0; x < 8; x++) { + buffer[(y << 8) + x] = 0; + } + } + } + + if (this.clipToTvSize) { + // Clip right 8-pixels column too: + for (y = 0; y < 240; y++) { + for (x = 0; x < 8; x++) { + buffer[(y << 8) + 255 - x] = 0; + } + } + } + + // Clip top and bottom 8 pixels: + if (this.clipToTvSize) { + for (y = 0; y < 8; y++) { + for (x = 0; x < 256; x++) { + buffer[(y << 8) + x] = 0; + buffer[((239 - y) << 8) + x] = 0; + } + } + } + + this.nes.ui.writeFrame(buffer); + }, + + updateControlReg1: function (value) { + this.triggerRendering(); + + this.f_nmiOnVblank = (value >> 7) & 1; + this.f_spriteSize = (value >> 5) & 1; + this.f_bgPatternTable = (value >> 4) & 1; + this.f_spPatternTable = (value >> 3) & 1; + this.f_addrInc = (value >> 2) & 1; + this.f_nTblAddress = value & 3; + + this.regV = (value >> 1) & 1; + this.regH = value & 1; + this.regS = (value >> 4) & 1; + }, + + updateControlReg2: function (value) { + this.triggerRendering(); + + this.f_color = (value >> 5) & 7; + this.f_spVisibility = (value >> 4) & 1; + this.f_bgVisibility = (value >> 3) & 1; + this.f_spClipping = (value >> 2) & 1; + this.f_bgClipping = (value >> 1) & 1; + this.f_dispType = value & 1; + + if (this.f_dispType === 0) { + this.palTable.setEmphasis(this.f_color); + } + this.updatePalettes(); + }, + + setStatusFlag: function (flag, value) { + var n = 1 << flag; + this.nes.cpu.mem[0x2002] = + (this.nes.cpu.mem[0x2002] & (255 - n)) | (value ? n : 0); + }, + + // CPU Register $2002: + // Read the Status Register. + readStatusRegister: function () { + var tmp = this.nes.cpu.mem[0x2002]; + + // Reset scroll & VRAM Address toggle: + this.firstWrite = true; + + // Clear VBlank flag: + this.setStatusFlag(this.STATUS_VBLANK, false); + + // Fetch status data: + return tmp; + }, + + // CPU Register $2003: + // Write the SPR-RAM address that is used for sramWrite (Register 0x2004 in CPU memory map) + writeSRAMAddress: function (address) { + this.sramAddress = address; + }, + + // CPU Register $2004 (R): + // Read from SPR-RAM (Sprite RAM). + // The address should be set first. + sramLoad: function () { + /*short tmp = sprMem.load(sramAddress); + sramAddress++; // Increment address + sramAddress%=0x100; + return tmp;*/ + return this.spriteMem[this.sramAddress]; + }, + + // CPU Register $2004 (W): + // Write to SPR-RAM (Sprite RAM). + // The address should be set first. + sramWrite: function (value) { + this.spriteMem[this.sramAddress] = value; + this.spriteRamWriteUpdate(this.sramAddress, value); + this.sramAddress++; // Increment address + this.sramAddress %= 0x100; + }, + + // CPU Register $2005: + // Write to scroll registers. + // The first write is the vertical offset, the second is the + // horizontal offset: + scrollWrite: function (value) { + this.triggerRendering(); + + if (this.firstWrite) { + // First write, horizontal scroll: + this.regHT = (value >> 3) & 31; + this.regFH = value & 7; + } else { + // Second write, vertical scroll: + this.regFV = value & 7; + this.regVT = (value >> 3) & 31; + } + this.firstWrite = !this.firstWrite; + }, + + // CPU Register $2006: + // Sets the adress used when reading/writing from/to VRAM. + // The first write sets the high byte, the second the low byte. + writeVRAMAddress: function (address) { + if (this.firstWrite) { + this.regFV = (address >> 4) & 3; + this.regV = (address >> 3) & 1; + this.regH = (address >> 2) & 1; + this.regVT = (this.regVT & 7) | ((address & 3) << 3); + } else { + this.triggerRendering(); + + this.regVT = (this.regVT & 24) | ((address >> 5) & 7); + this.regHT = address & 31; + + this.cntFV = this.regFV; + this.cntV = this.regV; + this.cntH = this.regH; + this.cntVT = this.regVT; + this.cntHT = this.regHT; + + this.checkSprite0(this.scanline - 20); + } + + this.firstWrite = !this.firstWrite; + + // Invoke mapper latch: + this.cntsToAddress(); + if (this.vramAddress < 0x2000) { + this.nes.mmap.latchAccess(this.vramAddress); + } + }, + + // CPU Register $2007(R): + // Read from PPU memory. The address should be set first. + vramLoad: function () { + var tmp; + + this.cntsToAddress(); + this.regsToAddress(); + + // If address is in range 0x0000-0x3EFF, return buffered values: + if (this.vramAddress <= 0x3eff) { + tmp = this.vramBufferedReadValue; + + // Update buffered value: + if (this.vramAddress < 0x2000) { + this.vramBufferedReadValue = this.vramMem[this.vramAddress]; + } else { + this.vramBufferedReadValue = this.mirroredLoad(this.vramAddress); + } + + // Mapper latch access: + if (this.vramAddress < 0x2000) { + this.nes.mmap.latchAccess(this.vramAddress); + } + + // Increment by either 1 or 32, depending on d2 of Control Register 1: + this.vramAddress += this.f_addrInc === 1 ? 32 : 1; + + this.cntsFromAddress(); + this.regsFromAddress(); + + return tmp; // Return the previous buffered value. + } + + // No buffering in this mem range. Read normally. + tmp = this.mirroredLoad(this.vramAddress); + + // Increment by either 1 or 32, depending on d2 of Control Register 1: + this.vramAddress += this.f_addrInc === 1 ? 32 : 1; + + this.cntsFromAddress(); + this.regsFromAddress(); + + return tmp; + }, + + // CPU Register $2007(W): + // Write to PPU memory. The address should be set first. + vramWrite: function (value) { + this.triggerRendering(); + this.cntsToAddress(); + this.regsToAddress(); + + if (this.vramAddress >= 0x2000) { + // Mirroring is used. + this.mirroredWrite(this.vramAddress, value); + } else { + // Write normally. + this.writeMem(this.vramAddress, value); + + // Invoke mapper latch: + this.nes.mmap.latchAccess(this.vramAddress); + } + + // Increment by either 1 or 32, depending on d2 of Control Register 1: + this.vramAddress += this.f_addrInc === 1 ? 32 : 1; + this.regsFromAddress(); + this.cntsFromAddress(); + }, + + // CPU Register $4014: + // Write 256 bytes of main memory + // into Sprite RAM. + sramDMA: function (value) { + var baseAddress = value * 0x100; + var data; + for (var i = this.sramAddress; i < 256; i++) { + data = this.nes.cpu.mem[baseAddress + i]; + this.spriteMem[i] = data; + this.spriteRamWriteUpdate(i, data); + } + + this.nes.cpu.haltCycles(513); + }, + + // Updates the scroll registers from a new VRAM address. + regsFromAddress: function () { + var address = (this.vramTmpAddress >> 8) & 0xff; + this.regFV = (address >> 4) & 7; + this.regV = (address >> 3) & 1; + this.regH = (address >> 2) & 1; + this.regVT = (this.regVT & 7) | ((address & 3) << 3); + + address = this.vramTmpAddress & 0xff; + this.regVT = (this.regVT & 24) | ((address >> 5) & 7); + this.regHT = address & 31; + }, + + // Updates the scroll registers from a new VRAM address. + cntsFromAddress: function () { + var address = (this.vramAddress >> 8) & 0xff; + this.cntFV = (address >> 4) & 3; + this.cntV = (address >> 3) & 1; + this.cntH = (address >> 2) & 1; + this.cntVT = (this.cntVT & 7) | ((address & 3) << 3); + + address = this.vramAddress & 0xff; + this.cntVT = (this.cntVT & 24) | ((address >> 5) & 7); + this.cntHT = address & 31; + }, + + regsToAddress: function () { + var b1 = (this.regFV & 7) << 4; + b1 |= (this.regV & 1) << 3; + b1 |= (this.regH & 1) << 2; + b1 |= (this.regVT >> 3) & 3; + + var b2 = (this.regVT & 7) << 5; + b2 |= this.regHT & 31; + + this.vramTmpAddress = ((b1 << 8) | b2) & 0x7fff; + }, + + cntsToAddress: function () { + var b1 = (this.cntFV & 7) << 4; + b1 |= (this.cntV & 1) << 3; + b1 |= (this.cntH & 1) << 2; + b1 |= (this.cntVT >> 3) & 3; + + var b2 = (this.cntVT & 7) << 5; + b2 |= this.cntHT & 31; + + this.vramAddress = ((b1 << 8) | b2) & 0x7fff; + }, + + incTileCounter: function (count) { + for (var i = count; i !== 0; i--) { + this.cntHT++; + if (this.cntHT === 32) { + this.cntHT = 0; + this.cntVT++; + if (this.cntVT >= 30) { + this.cntH++; + if (this.cntH === 2) { + this.cntH = 0; + this.cntV++; + if (this.cntV === 2) { + this.cntV = 0; + this.cntFV++; + this.cntFV &= 0x7; + } + } + } + } + } + }, + + // Reads from memory, taking into account + // mirroring/mapping of address ranges. + mirroredLoad: function (address) { + return this.vramMem[this.vramMirrorTable[address]]; + }, + + // Writes to memory, taking into account + // mirroring/mapping of address ranges. + mirroredWrite: function (address, value) { + if (address >= 0x3f00 && address < 0x3f20) { + // Palette write mirroring. + if (address === 0x3f00 || address === 0x3f10) { + this.writeMem(0x3f00, value); + this.writeMem(0x3f10, value); + } else if (address === 0x3f04 || address === 0x3f14) { + this.writeMem(0x3f04, value); + this.writeMem(0x3f14, value); + } else if (address === 0x3f08 || address === 0x3f18) { + this.writeMem(0x3f08, value); + this.writeMem(0x3f18, value); + } else if (address === 0x3f0c || address === 0x3f1c) { + this.writeMem(0x3f0c, value); + this.writeMem(0x3f1c, value); + } else { + this.writeMem(address, value); + } + } else { + // Use lookup table for mirrored address: + if (address < this.vramMirrorTable.length) { + this.writeMem(this.vramMirrorTable[address], value); + } else { + throw new Error("Invalid VRAM address: " + address.toString(16)); + } + } + }, + + triggerRendering: function () { + if (this.scanline >= 21 && this.scanline <= 260) { + // Render sprites, and combine: + this.renderFramePartially( + this.lastRenderedScanline + 1, + this.scanline - 21 - this.lastRenderedScanline + ); + + // Set last rendered scanline: + this.lastRenderedScanline = this.scanline - 21; + } + }, + + renderFramePartially: function (startScan, scanCount) { + if (this.f_spVisibility === 1) { + this.renderSpritesPartially(startScan, scanCount, true); + } + + if (this.f_bgVisibility === 1) { + var si = startScan << 8; + var ei = (startScan + scanCount) << 8; + if (ei > 0xf000) { + ei = 0xf000; + } + var buffer = this.buffer; + var bgbuffer = this.bgbuffer; + var pixrendered = this.pixrendered; + for (var destIndex = si; destIndex < ei; destIndex++) { + if (pixrendered[destIndex] > 0xff) { + buffer[destIndex] = bgbuffer[destIndex]; + } + } + } + + if (this.f_spVisibility === 1) { + this.renderSpritesPartially(startScan, scanCount, false); + } + + this.validTileData = false; + }, + + renderBgScanline: function (bgbuffer, scan) { + var baseTile = this.regS === 0 ? 0 : 256; + var destIndex = (scan << 8) - this.regFH; + + this.curNt = this.ntable1[this.cntV + this.cntV + this.cntH]; + + this.cntHT = this.regHT; + this.cntH = this.regH; + this.curNt = this.ntable1[this.cntV + this.cntV + this.cntH]; + + if (scan < 240 && scan - this.cntFV >= 0) { + var tscanoffset = this.cntFV << 3; + var scantile = this.scantile; + var attrib = this.attrib; + var ptTile = this.ptTile; + var nameTable = this.nameTable; + var imgPalette = this.imgPalette; + var pixrendered = this.pixrendered; + var targetBuffer = bgbuffer ? this.bgbuffer : this.buffer; + + var t, tpix, att, col; + + for (var tile = 0; tile < 32; tile++) { + if (scan >= 0) { + // Fetch tile & attrib data: + if (this.validTileData) { + // Get data from array: + t = scantile[tile]; + if (typeof t === "undefined") { + continue; + } + tpix = t.pix; + att = attrib[tile]; + } else { + // Fetch data: + t = + ptTile[ + baseTile + + nameTable[this.curNt].getTileIndex(this.cntHT, this.cntVT) + ]; + if (typeof t === "undefined") { + continue; + } + tpix = t.pix; + att = nameTable[this.curNt].getAttrib(this.cntHT, this.cntVT); + scantile[tile] = t; + attrib[tile] = att; + } + + // Render tile scanline: + var sx = 0; + var x = (tile << 3) - this.regFH; + + if (x > -8) { + if (x < 0) { + destIndex -= x; + sx = -x; + } + if (t.opaque[this.cntFV]) { + for (; sx < 8; sx++) { + targetBuffer[destIndex] = + imgPalette[tpix[tscanoffset + sx] + att]; + pixrendered[destIndex] |= 256; + destIndex++; + } + } else { + for (; sx < 8; sx++) { + col = tpix[tscanoffset + sx]; + if (col !== 0) { + targetBuffer[destIndex] = imgPalette[col + att]; + pixrendered[destIndex] |= 256; + } + destIndex++; + } + } + } + } + + // Increase Horizontal Tile Counter: + if (++this.cntHT === 32) { + this.cntHT = 0; + this.cntH++; + this.cntH %= 2; + this.curNt = this.ntable1[(this.cntV << 1) + this.cntH]; + } + } + + // Tile data for one row should now have been fetched, + // so the data in the array is valid. + this.validTileData = true; + } + + // update vertical scroll: + this.cntFV++; + if (this.cntFV === 8) { + this.cntFV = 0; + this.cntVT++; + if (this.cntVT === 30) { + this.cntVT = 0; + this.cntV++; + this.cntV %= 2; + this.curNt = this.ntable1[(this.cntV << 1) + this.cntH]; + } else if (this.cntVT === 32) { + this.cntVT = 0; + } + + // Invalidate fetched data: + this.validTileData = false; + } + }, + + renderSpritesPartially: function (startscan, scancount, bgPri) { + if (this.f_spVisibility === 1) { + for (var i = 0; i < 64; i++) { + if ( + this.bgPriority[i] === bgPri && + this.sprX[i] >= 0 && + this.sprX[i] < 256 && + this.sprY[i] + 8 >= startscan && + this.sprY[i] < startscan + scancount + ) { + // Show sprite. + if (this.f_spriteSize === 0) { + // 8x8 sprites + + this.srcy1 = 0; + this.srcy2 = 8; + + if (this.sprY[i] < startscan) { + this.srcy1 = startscan - this.sprY[i] - 1; + } + + if (this.sprY[i] + 8 > startscan + scancount) { + this.srcy2 = startscan + scancount - this.sprY[i] + 1; + } + + if (this.f_spPatternTable === 0) { + this.ptTile[this.sprTile[i]].render( + this.buffer, + 0, + this.srcy1, + 8, + this.srcy2, + this.sprX[i], + this.sprY[i] + 1, + this.sprCol[i], + this.sprPalette, + this.horiFlip[i], + this.vertFlip[i], + i, + this.pixrendered + ); + } else { + this.ptTile[this.sprTile[i] + 256].render( + this.buffer, + 0, + this.srcy1, + 8, + this.srcy2, + this.sprX[i], + this.sprY[i] + 1, + this.sprCol[i], + this.sprPalette, + this.horiFlip[i], + this.vertFlip[i], + i, + this.pixrendered + ); + } + } else { + // 8x16 sprites + var top = this.sprTile[i]; + if ((top & 1) !== 0) { + top = this.sprTile[i] - 1 + 256; + } + + var srcy1 = 0; + var srcy2 = 8; + + if (this.sprY[i] < startscan) { + srcy1 = startscan - this.sprY[i] - 1; + } + + if (this.sprY[i] + 8 > startscan + scancount) { + srcy2 = startscan + scancount - this.sprY[i]; + } + + this.ptTile[top + (this.vertFlip[i] ? 1 : 0)].render( + this.buffer, + 0, + srcy1, + 8, + srcy2, + this.sprX[i], + this.sprY[i] + 1, + this.sprCol[i], + this.sprPalette, + this.horiFlip[i], + this.vertFlip[i], + i, + this.pixrendered + ); + + srcy1 = 0; + srcy2 = 8; + + if (this.sprY[i] + 8 < startscan) { + srcy1 = startscan - (this.sprY[i] + 8 + 1); + } + + if (this.sprY[i] + 16 > startscan + scancount) { + srcy2 = startscan + scancount - (this.sprY[i] + 8); + } + + this.ptTile[top + (this.vertFlip[i] ? 0 : 1)].render( + this.buffer, + 0, + srcy1, + 8, + srcy2, + this.sprX[i], + this.sprY[i] + 1 + 8, + this.sprCol[i], + this.sprPalette, + this.horiFlip[i], + this.vertFlip[i], + i, + this.pixrendered + ); + } + } + } + } + }, + + checkSprite0: function (scan) { + this.spr0HitX = -1; + this.spr0HitY = -1; + + var toffset; + var tIndexAdd = this.f_spPatternTable === 0 ? 0 : 256; + var x, y, t, i; + var bufferIndex; + + x = this.sprX[0]; + y = this.sprY[0] + 1; + + if (this.f_spriteSize === 0) { + // 8x8 sprites. + + // Check range: + if (y <= scan && y + 8 > scan && x >= -7 && x < 256) { + // Sprite is in range. + // Draw scanline: + t = this.ptTile[this.sprTile[0] + tIndexAdd]; + + if (this.vertFlip[0]) { + toffset = 7 - (scan - y); + } else { + toffset = scan - y; + } + toffset *= 8; + + bufferIndex = scan * 256 + x; + if (this.horiFlip[0]) { + for (i = 7; i >= 0; i--) { + if (x >= 0 && x < 256) { + if ( + bufferIndex >= 0 && + bufferIndex < 61440 && + this.pixrendered[bufferIndex] !== 0 + ) { + if (t.pix[toffset + i] !== 0) { + this.spr0HitX = bufferIndex % 256; + this.spr0HitY = scan; + return true; + } + } + } + x++; + bufferIndex++; + } + } else { + for (i = 0; i < 8; i++) { + if (x >= 0 && x < 256) { + if ( + bufferIndex >= 0 && + bufferIndex < 61440 && + this.pixrendered[bufferIndex] !== 0 + ) { + if (t.pix[toffset + i] !== 0) { + this.spr0HitX = bufferIndex % 256; + this.spr0HitY = scan; + return true; + } + } + } + x++; + bufferIndex++; + } + } + } + } else { + // 8x16 sprites: + + // Check range: + if (y <= scan && y + 16 > scan && x >= -7 && x < 256) { + // Sprite is in range. + // Draw scanline: + + if (this.vertFlip[0]) { + toffset = 15 - (scan - y); + } else { + toffset = scan - y; + } + + if (toffset < 8) { + // first half of sprite. + t = this.ptTile[ + this.sprTile[0] + + (this.vertFlip[0] ? 1 : 0) + + ((this.sprTile[0] & 1) !== 0 ? 255 : 0) + ]; + } else { + // second half of sprite. + t = this.ptTile[ + this.sprTile[0] + + (this.vertFlip[0] ? 0 : 1) + + ((this.sprTile[0] & 1) !== 0 ? 255 : 0) + ]; + if (this.vertFlip[0]) { + toffset = 15 - toffset; + } else { + toffset -= 8; + } + } + toffset *= 8; + + bufferIndex = scan * 256 + x; + if (this.horiFlip[0]) { + for (i = 7; i >= 0; i--) { + if (x >= 0 && x < 256) { + if ( + bufferIndex >= 0 && + bufferIndex < 61440 && + this.pixrendered[bufferIndex] !== 0 + ) { + if (t.pix[toffset + i] !== 0) { + this.spr0HitX = bufferIndex % 256; + this.spr0HitY = scan; + return true; + } + } + } + x++; + bufferIndex++; + } + } else { + for (i = 0; i < 8; i++) { + if (x >= 0 && x < 256) { + if ( + bufferIndex >= 0 && + bufferIndex < 61440 && + this.pixrendered[bufferIndex] !== 0 + ) { + if (t.pix[toffset + i] !== 0) { + this.spr0HitX = bufferIndex % 256; + this.spr0HitY = scan; + return true; + } + } + } + x++; + bufferIndex++; + } + } + } + } + + return false; + }, + + // This will write to PPU memory, and + // update internally buffered data + // appropriately. + writeMem: function (address, value) { + this.vramMem[address] = value; + + // Update internally buffered data: + if (address < 0x2000) { + this.vramMem[address] = value; + this.patternWrite(address, value); + } else if (address >= 0x2000 && address < 0x23c0) { + this.nameTableWrite(this.ntable1[0], address - 0x2000, value); + } else if (address >= 0x23c0 && address < 0x2400) { + this.attribTableWrite(this.ntable1[0], address - 0x23c0, value); + } else if (address >= 0x2400 && address < 0x27c0) { + this.nameTableWrite(this.ntable1[1], address - 0x2400, value); + } else if (address >= 0x27c0 && address < 0x2800) { + this.attribTableWrite(this.ntable1[1], address - 0x27c0, value); + } else if (address >= 0x2800 && address < 0x2bc0) { + this.nameTableWrite(this.ntable1[2], address - 0x2800, value); + } else if (address >= 0x2bc0 && address < 0x2c00) { + this.attribTableWrite(this.ntable1[2], address - 0x2bc0, value); + } else if (address >= 0x2c00 && address < 0x2fc0) { + this.nameTableWrite(this.ntable1[3], address - 0x2c00, value); + } else if (address >= 0x2fc0 && address < 0x3000) { + this.attribTableWrite(this.ntable1[3], address - 0x2fc0, value); + } else if (address >= 0x3f00 && address < 0x3f20) { + this.updatePalettes(); + } + }, + + // Reads data from $3f00 to $f20 + // into the two buffered palettes. + updatePalettes: function () { + var i; + + for (i = 0; i < 16; i++) { + if (this.f_dispType === 0) { + this.imgPalette[i] = this.palTable.getEntry( + this.vramMem[0x3f00 + i] & 63 + ); + } else { + this.imgPalette[i] = this.palTable.getEntry( + this.vramMem[0x3f00 + i] & 32 + ); + } + } + for (i = 0; i < 16; i++) { + if (this.f_dispType === 0) { + this.sprPalette[i] = this.palTable.getEntry( + this.vramMem[0x3f10 + i] & 63 + ); + } else { + this.sprPalette[i] = this.palTable.getEntry( + this.vramMem[0x3f10 + i] & 32 + ); + } + } + }, + + // Updates the internal pattern + // table buffers with this new byte. + // In vNES, there is a version of this with 4 arguments which isn't used. + patternWrite: function (address, value) { + var tileIndex = Math.floor(address / 16); + var leftOver = address % 16; + if (leftOver < 8) { + this.ptTile[tileIndex].setScanline( + leftOver, + value, + this.vramMem[address + 8] + ); + } else { + this.ptTile[tileIndex].setScanline( + leftOver - 8, + this.vramMem[address - 8], + value + ); + } + }, + + // Updates the internal name table buffers + // with this new byte. + nameTableWrite: function (index, address, value) { + this.nameTable[index].tile[address] = value; + + // Update Sprite #0 hit: + //updateSpr0Hit(); + this.checkSprite0(this.scanline - 20); + }, + + // Updates the internal pattern + // table buffers with this new attribute + // table byte. + attribTableWrite: function (index, address, value) { + this.nameTable[index].writeAttrib(address, value); + }, + + // Updates the internally buffered sprite + // data with this new byte of info. + spriteRamWriteUpdate: function (address, value) { + var tIndex = Math.floor(address / 4); + + if (tIndex === 0) { + //updateSpr0Hit(); + this.checkSprite0(this.scanline - 20); + } + + if (address % 4 === 0) { + // Y coordinate + this.sprY[tIndex] = value; + } else if (address % 4 === 1) { + // Tile index + this.sprTile[tIndex] = value; + } else if (address % 4 === 2) { + // Attributes + this.vertFlip[tIndex] = (value & 0x80) !== 0; + this.horiFlip[tIndex] = (value & 0x40) !== 0; + this.bgPriority[tIndex] = (value & 0x20) !== 0; + this.sprCol[tIndex] = (value & 3) << 2; + } else if (address % 4 === 3) { + // X coordinate + this.sprX[tIndex] = value; + } + }, + + doNMI: function () { + // Set VBlank flag: + this.setStatusFlag(this.STATUS_VBLANK, true); + //nes.getCpu().doNonMaskableInterrupt(); + this.nes.cpu.requestIrq(this.nes.cpu.IRQ_NMI); + }, + + isPixelWhite: function (x, y) { + this.triggerRendering(); + return this.nes.ppu.buffer[(y << 8) + x] === 0xffffff; + }, + + JSON_PROPERTIES: [ + // Memory + "vramMem", + "spriteMem", + // Counters + "cntFV", + "cntV", + "cntH", + "cntVT", + "cntHT", + // Registers + "regFV", + "regV", + "regH", + "regVT", + "regHT", + "regFH", + "regS", + // VRAM addr + "vramAddress", + "vramTmpAddress", + // Control/Status registers + "f_nmiOnVblank", + "f_spriteSize", + "f_bgPatternTable", + "f_spPatternTable", + "f_addrInc", + "f_nTblAddress", + "f_color", + "f_spVisibility", + "f_bgVisibility", + "f_spClipping", + "f_bgClipping", + "f_dispType", + // VRAM I/O + "vramBufferedReadValue", + "firstWrite", + // Mirroring + "currentMirroring", + "vramMirrorTable", + "ntable1", + // SPR-RAM I/O + "sramAddress", + // Sprites. Most sprite data is rebuilt from spriteMem + "hitSpr0", + // Palettes + "sprPalette", + "imgPalette", + // Rendering progression + "curX", + "scanline", + "lastRenderedScanline", + "curNt", + "scantile", + // Used during rendering + "attrib", + "buffer", + "bgbuffer", + "pixrendered", + // Misc + "requestEndFrame", + "nmiOk", + "dummyCycleToggle", + "nmiCounter", + "validTileData", + "scanlineAlreadyRendered", + ], + + toJSON: function () { + var i; + var state = utils.toJSON(this); + + state.nameTable = []; + for (i = 0; i < this.nameTable.length; i++) { + state.nameTable[i] = this.nameTable[i].toJSON(); + } + + state.ptTile = []; + for (i = 0; i < this.ptTile.length; i++) { + state.ptTile[i] = this.ptTile[i].toJSON(); + } + + return state; + }, + + fromJSON: function (state) { + var i; + + utils.fromJSON(this, state); + + for (i = 0; i < this.nameTable.length; i++) { + this.nameTable[i].fromJSON(state.nameTable[i]); + } + + for (i = 0; i < this.ptTile.length; i++) { + this.ptTile[i].fromJSON(state.ptTile[i]); + } + + // Sprite data: + for (i = 0; i < this.spriteMem.length; i++) { + this.spriteRamWriteUpdate(i, this.spriteMem[i]); + } + }, +}; + +var NameTable = function (width, height, name) { + this.width = width; + this.height = height; + this.name = name; + + this.tile = new Array(width * height); + this.attrib = new Array(width * height); + for (var i = 0; i < width * height; i++) { + this.tile[i] = 0; + this.attrib[i] = 0; + } +}; + +NameTable.prototype = { + getTileIndex: function (x, y) { + return this.tile[y * this.width + x]; + }, + + getAttrib: function (x, y) { + return this.attrib[y * this.width + x]; + }, + + writeAttrib: function (index, value) { + var basex = (index % 8) * 4; + var basey = Math.floor(index / 8) * 4; + var add; + var tx, ty; + var attindex; + + for (var sqy = 0; sqy < 2; sqy++) { + for (var sqx = 0; sqx < 2; sqx++) { + add = (value >> (2 * (sqy * 2 + sqx))) & 3; + for (var y = 0; y < 2; y++) { + for (var x = 0; x < 2; x++) { + tx = basex + sqx * 2 + x; + ty = basey + sqy * 2 + y; + attindex = ty * this.width + tx; + this.attrib[attindex] = (add << 2) & 12; + } + } + } + } + }, + + toJSON: function () { + return { + tile: this.tile, + attrib: this.attrib, + }; + }, + + fromJSON: function (s) { + this.tile = s.tile; + this.attrib = s.attrib; + }, +}; + +var PaletteTable = function () { + this.curTable = new Array(64); + this.emphTable = new Array(8); + this.currentEmph = -1; +}; + +PaletteTable.prototype = { + reset: function () { + this.setEmphasis(0); + }, + + loadNTSCPalette: function () { + // prettier-ignore + this.curTable = [0x525252, 0xB40000, 0xA00000, 0xB1003D, 0x740069, 0x00005B, 0x00005F, 0x001840, 0x002F10, 0x084A08, 0x006700, 0x124200, 0x6D2800, 0x000000, 0x000000, 0x000000, 0xC4D5E7, 0xFF4000, 0xDC0E22, 0xFF476B, 0xD7009F, 0x680AD7, 0x0019BC, 0x0054B1, 0x006A5B, 0x008C03, 0x00AB00, 0x2C8800, 0xA47200, 0x000000, 0x000000, 0x000000, 0xF8F8F8, 0xFFAB3C, 0xFF7981, 0xFF5BC5, 0xFF48F2, 0xDF49FF, 0x476DFF, 0x00B4F7, 0x00E0FF, 0x00E375, 0x03F42B, 0x78B82E, 0xE5E218, 0x787878, 0x000000, 0x000000, 0xFFFFFF, 0xFFF2BE, 0xF8B8B8, 0xF8B8D8, 0xFFB6FF, 0xFFC3FF, 0xC7D1FF, 0x9ADAFF, 0x88EDF8, 0x83FFDD, 0xB8F8B8, 0xF5F8AC, 0xFFFFB0, 0xF8D8F8, 0x000000, 0x000000]; + this.makeTables(); + this.setEmphasis(0); + }, + + loadPALPalette: function () { + // prettier-ignore + this.curTable = [0x525252, 0xB40000, 0xA00000, 0xB1003D, 0x740069, 0x00005B, 0x00005F, 0x001840, 0x002F10, 0x084A08, 0x006700, 0x124200, 0x6D2800, 0x000000, 0x000000, 0x000000, 0xC4D5E7, 0xFF4000, 0xDC0E22, 0xFF476B, 0xD7009F, 0x680AD7, 0x0019BC, 0x0054B1, 0x006A5B, 0x008C03, 0x00AB00, 0x2C8800, 0xA47200, 0x000000, 0x000000, 0x000000, 0xF8F8F8, 0xFFAB3C, 0xFF7981, 0xFF5BC5, 0xFF48F2, 0xDF49FF, 0x476DFF, 0x00B4F7, 0x00E0FF, 0x00E375, 0x03F42B, 0x78B82E, 0xE5E218, 0x787878, 0x000000, 0x000000, 0xFFFFFF, 0xFFF2BE, 0xF8B8B8, 0xF8B8D8, 0xFFB6FF, 0xFFC3FF, 0xC7D1FF, 0x9ADAFF, 0x88EDF8, 0x83FFDD, 0xB8F8B8, 0xF5F8AC, 0xFFFFB0, 0xF8D8F8, 0x000000, 0x000000]; + this.makeTables(); + this.setEmphasis(0); + }, + + makeTables: function () { + var r, g, b, col, i, rFactor, gFactor, bFactor; + + // Calculate a table for each possible emphasis setting: + for (var emph = 0; emph < 8; emph++) { + // Determine color component factors: + rFactor = 1.0; + gFactor = 1.0; + bFactor = 1.0; + + if ((emph & 1) !== 0) { + rFactor = 0.75; + bFactor = 0.75; + } + if ((emph & 2) !== 0) { + rFactor = 0.75; + gFactor = 0.75; + } + if ((emph & 4) !== 0) { + gFactor = 0.75; + bFactor = 0.75; + } + + this.emphTable[emph] = new Array(64); + + // Calculate table: + for (i = 0; i < 64; i++) { + col = this.curTable[i]; + r = Math.floor(this.getRed(col) * rFactor); + g = Math.floor(this.getGreen(col) * gFactor); + b = Math.floor(this.getBlue(col) * bFactor); + this.emphTable[emph][i] = this.getRgb(r, g, b); + } + } + }, + + setEmphasis: function (emph) { + if (emph !== this.currentEmph) { + this.currentEmph = emph; + for (var i = 0; i < 64; i++) { + this.curTable[i] = this.emphTable[emph][i]; + } + } + }, + + getEntry: function (yiq) { + return this.curTable[yiq]; + }, + + getRed: function (rgb) { + return (rgb >> 16) & 0xff; + }, + + getGreen: function (rgb) { + return (rgb >> 8) & 0xff; + }, + + getBlue: function (rgb) { + return rgb & 0xff; + }, + + getRgb: function (r, g, b) { + return (r << 16) | (g << 8) | b; + }, + + loadDefaultPalette: function () { + this.curTable[0] = this.getRgb(117, 117, 117); + this.curTable[1] = this.getRgb(39, 27, 143); + this.curTable[2] = this.getRgb(0, 0, 171); + this.curTable[3] = this.getRgb(71, 0, 159); + this.curTable[4] = this.getRgb(143, 0, 119); + this.curTable[5] = this.getRgb(171, 0, 19); + this.curTable[6] = this.getRgb(167, 0, 0); + this.curTable[7] = this.getRgb(127, 11, 0); + this.curTable[8] = this.getRgb(67, 47, 0); + this.curTable[9] = this.getRgb(0, 71, 0); + this.curTable[10] = this.getRgb(0, 81, 0); + this.curTable[11] = this.getRgb(0, 63, 23); + this.curTable[12] = this.getRgb(27, 63, 95); + this.curTable[13] = this.getRgb(0, 0, 0); + this.curTable[14] = this.getRgb(0, 0, 0); + this.curTable[15] = this.getRgb(0, 0, 0); + this.curTable[16] = this.getRgb(188, 188, 188); + this.curTable[17] = this.getRgb(0, 115, 239); + this.curTable[18] = this.getRgb(35, 59, 239); + this.curTable[19] = this.getRgb(131, 0, 243); + this.curTable[20] = this.getRgb(191, 0, 191); + this.curTable[21] = this.getRgb(231, 0, 91); + this.curTable[22] = this.getRgb(219, 43, 0); + this.curTable[23] = this.getRgb(203, 79, 15); + this.curTable[24] = this.getRgb(139, 115, 0); + this.curTable[25] = this.getRgb(0, 151, 0); + this.curTable[26] = this.getRgb(0, 171, 0); + this.curTable[27] = this.getRgb(0, 147, 59); + this.curTable[28] = this.getRgb(0, 131, 139); + this.curTable[29] = this.getRgb(0, 0, 0); + this.curTable[30] = this.getRgb(0, 0, 0); + this.curTable[31] = this.getRgb(0, 0, 0); + this.curTable[32] = this.getRgb(255, 255, 255); + this.curTable[33] = this.getRgb(63, 191, 255); + this.curTable[34] = this.getRgb(95, 151, 255); + this.curTable[35] = this.getRgb(167, 139, 253); + this.curTable[36] = this.getRgb(247, 123, 255); + this.curTable[37] = this.getRgb(255, 119, 183); + this.curTable[38] = this.getRgb(255, 119, 99); + this.curTable[39] = this.getRgb(255, 155, 59); + this.curTable[40] = this.getRgb(243, 191, 63); + this.curTable[41] = this.getRgb(131, 211, 19); + this.curTable[42] = this.getRgb(79, 223, 75); + this.curTable[43] = this.getRgb(88, 248, 152); + this.curTable[44] = this.getRgb(0, 235, 219); + this.curTable[45] = this.getRgb(0, 0, 0); + this.curTable[46] = this.getRgb(0, 0, 0); + this.curTable[47] = this.getRgb(0, 0, 0); + this.curTable[48] = this.getRgb(255, 255, 255); + this.curTable[49] = this.getRgb(171, 231, 255); + this.curTable[50] = this.getRgb(199, 215, 255); + this.curTable[51] = this.getRgb(215, 203, 255); + this.curTable[52] = this.getRgb(255, 199, 255); + this.curTable[53] = this.getRgb(255, 199, 219); + this.curTable[54] = this.getRgb(255, 191, 179); + this.curTable[55] = this.getRgb(255, 219, 171); + this.curTable[56] = this.getRgb(255, 231, 163); + this.curTable[57] = this.getRgb(227, 255, 163); + this.curTable[58] = this.getRgb(171, 243, 191); + this.curTable[59] = this.getRgb(179, 255, 207); + this.curTable[60] = this.getRgb(159, 255, 243); + this.curTable[61] = this.getRgb(0, 0, 0); + this.curTable[62] = this.getRgb(0, 0, 0); + this.curTable[63] = this.getRgb(0, 0, 0); + + this.makeTables(); + this.setEmphasis(0); + }, +}; + +module.exports = PPU; diff --git a/jsnes/src/rom.js b/jsnes/src/rom.js new file mode 100644 index 0000000..cfa26c8 --- /dev/null +++ b/jsnes/src/rom.js @@ -0,0 +1,204 @@ +var Mappers = require("./mappers"); +var Tile = require("./tile"); + +var ROM = function (nes) { + this.nes = nes; + + this.mapperName = new Array(92); + + for (var i = 0; i < 92; i++) { + this.mapperName[i] = "Unknown Mapper"; + } + this.mapperName[0] = "Direct Access"; + this.mapperName[1] = "Nintendo MMC1"; + this.mapperName[2] = "UNROM"; + this.mapperName[3] = "CNROM"; + this.mapperName[4] = "Nintendo MMC3"; + this.mapperName[5] = "Nintendo MMC5"; + this.mapperName[6] = "FFE F4xxx"; + this.mapperName[7] = "AOROM"; + this.mapperName[8] = "FFE F3xxx"; + this.mapperName[9] = "Nintendo MMC2"; + this.mapperName[10] = "Nintendo MMC4"; + this.mapperName[11] = "Color Dreams Chip"; + this.mapperName[12] = "FFE F6xxx"; + this.mapperName[15] = "100-in-1 switch"; + this.mapperName[16] = "Bandai chip"; + this.mapperName[17] = "FFE F8xxx"; + this.mapperName[18] = "Jaleco SS8806 chip"; + this.mapperName[19] = "Namcot 106 chip"; + this.mapperName[20] = "Famicom Disk System"; + this.mapperName[21] = "Konami VRC4a"; + this.mapperName[22] = "Konami VRC2a"; + this.mapperName[23] = "Konami VRC2a"; + this.mapperName[24] = "Konami VRC6"; + this.mapperName[25] = "Konami VRC4b"; + this.mapperName[32] = "Irem G-101 chip"; + this.mapperName[33] = "Taito TC0190/TC0350"; + this.mapperName[34] = "32kB ROM switch"; + + this.mapperName[64] = "Tengen RAMBO-1 chip"; + this.mapperName[65] = "Irem H-3001 chip"; + this.mapperName[66] = "GNROM switch"; + this.mapperName[67] = "SunSoft3 chip"; + this.mapperName[68] = "SunSoft4 chip"; + this.mapperName[69] = "SunSoft5 FME-7 chip"; + this.mapperName[71] = "Camerica chip"; + this.mapperName[78] = "Irem 74HC161/32-based"; + this.mapperName[91] = "Pirate HK-SF3 chip"; +}; + +ROM.prototype = { + // Mirroring types: + VERTICAL_MIRRORING: 0, + HORIZONTAL_MIRRORING: 1, + FOURSCREEN_MIRRORING: 2, + SINGLESCREEN_MIRRORING: 3, + SINGLESCREEN_MIRRORING2: 4, + SINGLESCREEN_MIRRORING3: 5, + SINGLESCREEN_MIRRORING4: 6, + CHRROM_MIRRORING: 7, + + header: null, + rom: null, + vrom: null, + vromTile: null, + + romCount: null, + vromCount: null, + mirroring: null, + batteryRam: null, + trainer: null, + fourScreen: null, + mapperType: null, + valid: false, + + load: function (data) { + var i, j, v; + + if (data.indexOf("NES\x1a") === -1) { + throw new Error("Not a valid NES ROM."); + } + this.header = new Array(16); + for (i = 0; i < 16; i++) { + this.header[i] = data.charCodeAt(i) & 0xff; + } + this.romCount = this.header[4]; + this.vromCount = this.header[5] * 2; // Get the number of 4kB banks, not 8kB + this.mirroring = (this.header[6] & 1) !== 0 ? 1 : 0; + this.batteryRam = (this.header[6] & 2) !== 0; + this.trainer = (this.header[6] & 4) !== 0; + this.fourScreen = (this.header[6] & 8) !== 0; + this.mapperType = (this.header[6] >> 4) | (this.header[7] & 0xf0); + /* TODO + if (this.batteryRam) + this.loadBatteryRam();*/ + // Check whether byte 8-15 are zero's: + var foundError = false; + for (i = 8; i < 16; i++) { + if (this.header[i] !== 0) { + foundError = true; + break; + } + } + if (foundError) { + this.mapperType &= 0xf; // Ignore byte 7 + } + // Load PRG-ROM banks: + this.rom = new Array(this.romCount); + var offset = 16; + for (i = 0; i < this.romCount; i++) { + this.rom[i] = new Array(16384); + for (j = 0; j < 16384; j++) { + if (offset + j >= data.length) { + break; + } + this.rom[i][j] = data.charCodeAt(offset + j) & 0xff; + } + offset += 16384; + } + // Load CHR-ROM banks: + this.vrom = new Array(this.vromCount); + for (i = 0; i < this.vromCount; i++) { + this.vrom[i] = new Array(4096); + for (j = 0; j < 4096; j++) { + if (offset + j >= data.length) { + break; + } + this.vrom[i][j] = data.charCodeAt(offset + j) & 0xff; + } + offset += 4096; + } + + // Create VROM tiles: + this.vromTile = new Array(this.vromCount); + for (i = 0; i < this.vromCount; i++) { + this.vromTile[i] = new Array(256); + for (j = 0; j < 256; j++) { + this.vromTile[i][j] = new Tile(); + } + } + + // Convert CHR-ROM banks to tiles: + var tileIndex; + var leftOver; + for (v = 0; v < this.vromCount; v++) { + for (i = 0; i < 4096; i++) { + tileIndex = i >> 4; + leftOver = i % 16; + if (leftOver < 8) { + this.vromTile[v][tileIndex].setScanline( + leftOver, + this.vrom[v][i], + this.vrom[v][i + 8] + ); + } else { + this.vromTile[v][tileIndex].setScanline( + leftOver - 8, + this.vrom[v][i - 8], + this.vrom[v][i] + ); + } + } + } + + this.valid = true; + }, + + getMirroringType: function () { + if (this.fourScreen) { + return this.FOURSCREEN_MIRRORING; + } + if (this.mirroring === 0) { + return this.HORIZONTAL_MIRRORING; + } + return this.VERTICAL_MIRRORING; + }, + + getMapperName: function () { + if (this.mapperType >= 0 && this.mapperType < this.mapperName.length) { + return this.mapperName[this.mapperType]; + } + return "Unknown Mapper, " + this.mapperType; + }, + + mapperSupported: function () { + return typeof Mappers[this.mapperType] !== "undefined"; + }, + + createMapper: function () { + if (this.mapperSupported()) { + return new Mappers[this.mapperType](this.nes); + } else { + throw new Error( + "This ROM uses a mapper not supported by JSNES: " + + this.getMapperName() + + "(" + + this.mapperType + + ")" + ); + } + }, +}; + +module.exports = ROM; diff --git a/jsnes/src/tile.js b/jsnes/src/tile.js new file mode 100644 index 0000000..ae1c872 --- /dev/null +++ b/jsnes/src/tile.js @@ -0,0 +1,198 @@ +var Tile = function () { + // Tile data: + this.pix = new Array(64); + + this.fbIndex = null; + this.tIndex = null; + this.x = null; + this.y = null; + this.w = null; + this.h = null; + this.incX = null; + this.incY = null; + this.palIndex = null; + this.tpri = null; + this.c = null; + this.initialized = false; + this.opaque = new Array(8); +}; + +Tile.prototype = { + setBuffer: function (scanline) { + for (this.y = 0; this.y < 8; this.y++) { + this.setScanline(this.y, scanline[this.y], scanline[this.y + 8]); + } + }, + + setScanline: function (sline, b1, b2) { + this.initialized = true; + this.tIndex = sline << 3; + for (this.x = 0; this.x < 8; this.x++) { + this.pix[this.tIndex + this.x] = + ((b1 >> (7 - this.x)) & 1) + (((b2 >> (7 - this.x)) & 1) << 1); + if (this.pix[this.tIndex + this.x] === 0) { + this.opaque[sline] = false; + } + } + }, + + render: function ( + buffer, + srcx1, + srcy1, + srcx2, + srcy2, + dx, + dy, + palAdd, + palette, + flipHorizontal, + flipVertical, + pri, + priTable + ) { + if (dx < -7 || dx >= 256 || dy < -7 || dy >= 240) { + return; + } + + this.w = srcx2 - srcx1; + this.h = srcy2 - srcy1; + + if (dx < 0) { + srcx1 -= dx; + } + if (dx + srcx2 >= 256) { + srcx2 = 256 - dx; + } + + if (dy < 0) { + srcy1 -= dy; + } + if (dy + srcy2 >= 240) { + srcy2 = 240 - dy; + } + + if (!flipHorizontal && !flipVertical) { + this.fbIndex = (dy << 8) + dx; + this.tIndex = 0; + for (this.y = 0; this.y < 8; this.y++) { + for (this.x = 0; this.x < 8; this.x++) { + if ( + this.x >= srcx1 && + this.x < srcx2 && + this.y >= srcy1 && + this.y < srcy2 + ) { + this.palIndex = this.pix[this.tIndex]; + this.tpri = priTable[this.fbIndex]; + if (this.palIndex !== 0 && pri <= (this.tpri & 0xff)) { + //console.log("Rendering upright tile to buffer"); + buffer[this.fbIndex] = palette[this.palIndex + palAdd]; + this.tpri = (this.tpri & 0xf00) | pri; + priTable[this.fbIndex] = this.tpri; + } + } + this.fbIndex++; + this.tIndex++; + } + this.fbIndex -= 8; + this.fbIndex += 256; + } + } else if (flipHorizontal && !flipVertical) { + this.fbIndex = (dy << 8) + dx; + this.tIndex = 7; + for (this.y = 0; this.y < 8; this.y++) { + for (this.x = 0; this.x < 8; this.x++) { + if ( + this.x >= srcx1 && + this.x < srcx2 && + this.y >= srcy1 && + this.y < srcy2 + ) { + this.palIndex = this.pix[this.tIndex]; + this.tpri = priTable[this.fbIndex]; + if (this.palIndex !== 0 && pri <= (this.tpri & 0xff)) { + buffer[this.fbIndex] = palette[this.palIndex + palAdd]; + this.tpri = (this.tpri & 0xf00) | pri; + priTable[this.fbIndex] = this.tpri; + } + } + this.fbIndex++; + this.tIndex--; + } + this.fbIndex -= 8; + this.fbIndex += 256; + this.tIndex += 16; + } + } else if (flipVertical && !flipHorizontal) { + this.fbIndex = (dy << 8) + dx; + this.tIndex = 56; + for (this.y = 0; this.y < 8; this.y++) { + for (this.x = 0; this.x < 8; this.x++) { + if ( + this.x >= srcx1 && + this.x < srcx2 && + this.y >= srcy1 && + this.y < srcy2 + ) { + this.palIndex = this.pix[this.tIndex]; + this.tpri = priTable[this.fbIndex]; + if (this.palIndex !== 0 && pri <= (this.tpri & 0xff)) { + buffer[this.fbIndex] = palette[this.palIndex + palAdd]; + this.tpri = (this.tpri & 0xf00) | pri; + priTable[this.fbIndex] = this.tpri; + } + } + this.fbIndex++; + this.tIndex++; + } + this.fbIndex -= 8; + this.fbIndex += 256; + this.tIndex -= 16; + } + } else { + this.fbIndex = (dy << 8) + dx; + this.tIndex = 63; + for (this.y = 0; this.y < 8; this.y++) { + for (this.x = 0; this.x < 8; this.x++) { + if ( + this.x >= srcx1 && + this.x < srcx2 && + this.y >= srcy1 && + this.y < srcy2 + ) { + this.palIndex = this.pix[this.tIndex]; + this.tpri = priTable[this.fbIndex]; + if (this.palIndex !== 0 && pri <= (this.tpri & 0xff)) { + buffer[this.fbIndex] = palette[this.palIndex + palAdd]; + this.tpri = (this.tpri & 0xf00) | pri; + priTable[this.fbIndex] = this.tpri; + } + } + this.fbIndex++; + this.tIndex--; + } + this.fbIndex -= 8; + this.fbIndex += 256; + } + } + }, + + isTransparent: function (x, y) { + return this.pix[(y << 3) + x] === 0; + }, + + toJSON: function () { + return { + opaque: this.opaque, + pix: this.pix, + }; + }, + + fromJSON: function (s) { + this.opaque = s.opaque; + this.pix = s.pix; + }, +}; + +module.exports = Tile; diff --git a/jsnes/src/utils.js b/jsnes/src/utils.js new file mode 100644 index 0000000..133d47e --- /dev/null +++ b/jsnes/src/utils.js @@ -0,0 +1,25 @@ +module.exports = { + copyArrayElements: function (src, srcPos, dest, destPos, length) { + for (var i = 0; i < length; ++i) { + dest[destPos + i] = src[srcPos + i]; + } + }, + + copyArray: function (src) { + return src.slice(0); + }, + + fromJSON: function (obj, state) { + for (var i = 0; i < obj.JSON_PROPERTIES.length; i++) { + obj[obj.JSON_PROPERTIES[i]] = state[obj.JSON_PROPERTIES[i]]; + } + }, + + toJSON: function (obj) { + var state = {}; + for (var i = 0; i < obj.JSON_PROPERTIES.length; i++) { + state[obj.JSON_PROPERTIES[i]] = obj[obj.JSON_PROPERTIES[i]]; + } + return state; + }, +}; diff --git a/jsnes/test/nes.spec.js b/jsnes/test/nes.spec.js new file mode 100644 index 0000000..60a4919 --- /dev/null +++ b/jsnes/test/nes.spec.js @@ -0,0 +1,72 @@ +var assert = require("chai").assert; +var fs = require("fs"); +var NES = require("../src/nes"); +var sinon = require("sinon"); + +describe("NES", function() { + it("can be initialized", function() { + var nes = new NES(); + }); + + it("loads a ROM and runs a frame", function(done) { + var onFrame = sinon.spy(); + var nes = new NES({ onFrame: onFrame }); + fs.readFile("roms/croom/croom.nes", function(err, data) { + if (err) return done(err); + nes.loadROM(data.toString("binary")); + nes.frame(); + assert(onFrame.calledOnce); + assert.isArray(onFrame.args[0][0]); + assert.lengthOf(onFrame.args[0][0], 256 * 240); + done(); + }); + }); + + it("generates the correct frame buffer", function(done) { + var onFrame = sinon.spy(); + var nes = new NES({ onFrame: onFrame }); + fs.readFile("roms/croom/croom.nes", function(err, data) { + if (err) return done(err); + nes.loadROM(data.toString("binary")); + // Check the first index of a white pixel on the first 6 frames of + // output. Croom only uses 2 colors on the initial screen which makes + // it easy to detect. Comparing full snapshots of each frame takes too + // long. + var expectedIndexes = [-1, -1, -1, 2056, 4104, 4104]; + for (var i = 0; i < 6; i++) { + nes.frame(); + assert.equal(onFrame.lastCall.args[0].indexOf(0xFFFFFF), expectedIndexes[i]); + } + done(); + }); + }); + + describe("#loadROM()", function() { + it("throws an error given an invalid ROM", function() { + var nes = new NES(); + assert.throws(function() { + nes.loadROM("foo"); + }, "Not a valid NES ROM."); + }); + }); + + describe("#getFPS()", function() { + var nes = new NES(); + before(function(done) { + fs.readFile("roms/croom/croom.nes", function(err, data) { + if (err) return done(err); + nes.loadROM(data.toString("binary")); + done(); + }); + }); + + it("returns an FPS count when frames have been run", function() { + assert.isNull(nes.getFPS()); + nes.frame(); + nes.frame(); + var fps = nes.getFPS(); + assert.isNumber(fps); + assert.isAbove(fps, 0); + }); + }); +}); diff --git a/jsnes/webpack.config.js b/jsnes/webpack.config.js new file mode 100644 index 0000000..3561f68 --- /dev/null +++ b/jsnes/webpack.config.js @@ -0,0 +1,37 @@ +var path = require("path"); +const UglifyJsPlugin = require("uglifyjs-webpack-plugin"); + +module.exports = { + entry: { + jsnes: "./src/index.js", + "jsnes.min": "./src/index.js", + }, + devtool: "source-map", + output: { + path: path.resolve(__dirname, "dist"), + filename: "[name].js", + library: "jsnes", + libraryTarget: "umd", + umdNamedDefine: true, + }, + module: { + rules: [ + { + test: /\.js$/, + enforce: "pre", + exclude: /node_modules/, + use: [ + { + loader: "eslint-loader", + }, + ], + }, + ], + }, + plugins: [ + new UglifyJsPlugin({ + include: /\.min\.js$/, + sourceMap: true, + }), + ], +}; diff --git a/jsnes/yarn.lock b/jsnes/yarn.lock new file mode 100644 index 0000000..ce9724e --- /dev/null +++ b/jsnes/yarn.lock @@ -0,0 +1,3465 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@babel/code-frame@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.0.0.tgz#06e2ab19bdb535385559aabb5ba59729482800f8" + dependencies: + "@babel/highlight" "^7.0.0" + +"@babel/highlight@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.0.0.tgz#f710c38c8d458e6dd9a201afb637fcb781ce99e4" + dependencies: + chalk "^2.0.0" + esutils "^2.0.2" + js-tokens "^4.0.0" + +"@sinonjs/commons@^1", "@sinonjs/commons@^1.6.0", "@sinonjs/commons@^1.7.0", "@sinonjs/commons@^1.8.1": + version "1.8.1" + resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.1.tgz#e7df00f98a203324f6dc7cc606cad9d4a8ab2217" + dependencies: + type-detect "4.0.8" + +"@sinonjs/fake-timers@^6.0.0", "@sinonjs/fake-timers@^6.0.1": + version "6.0.1" + resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-6.0.1.tgz#293674fccb3262ac782c7aadfdeca86b10c75c40" + dependencies: + "@sinonjs/commons" "^1.7.0" + +"@sinonjs/formatio@^5.0.1": + version "5.0.1" + resolved "https://registry.yarnpkg.com/@sinonjs/formatio/-/formatio-5.0.1.tgz#f13e713cb3313b1ab965901b01b0828ea6b77089" + dependencies: + "@sinonjs/commons" "^1" + "@sinonjs/samsam" "^5.0.2" + +"@sinonjs/samsam@^5.0.2", "@sinonjs/samsam@^5.3.0": + version "5.3.0" + resolved "https://registry.yarnpkg.com/@sinonjs/samsam/-/samsam-5.3.0.tgz#1d2f0743dc54bf13fe9d508baefacdffa25d4329" + dependencies: + "@sinonjs/commons" "^1.6.0" + lodash.get "^4.4.2" + type-detect "^4.0.8" + +"@sinonjs/text-encoding@^0.7.1": + version "0.7.1" + resolved "https://registry.yarnpkg.com/@sinonjs/text-encoding/-/text-encoding-0.7.1.tgz#8da5c6530915653f3a1f38fd5f101d8c3f8079c5" + +"@types/color-name@^1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0" + +"@ungap/promise-all-settled@1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz#aa58042711d6e3275dd37dc597e5d31e8c290a44" + +abbrev@1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.0.tgz#d0554c2256636e2f56e7c2e5ad183f859428d81f" + +acorn-dynamic-import@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/acorn-dynamic-import/-/acorn-dynamic-import-2.0.2.tgz#c752bd210bef679501b6c6cb7fc84f8f47158cc4" + dependencies: + acorn "^4.0.3" + +acorn-jsx@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.2.0.tgz#4c66069173d6fdd68ed85239fc256226182b2ebe" + +acorn@^4.0.3: + version "4.0.11" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-4.0.11.tgz#edcda3bd937e7556410d42ed5860f67399c794c0" + +acorn@^5.0.0: + version "5.0.3" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.0.3.tgz#c460df08491463f028ccb82eab3730bf01087b3d" + +acorn@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.1.1.tgz#e35668de0b402f359de515c5482a1ab9f89a69bf" + +ajv-keywords@^3.1.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.2.0.tgz#e86b819c602cf8821ad637413698f1dec021847a" + +ajv@^4.9.1: + version "4.11.7" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-4.11.7.tgz#8655a5d86d0824985cc471a1d913fb6729a0ec48" + dependencies: + co "^4.6.0" + json-stable-stringify "^1.0.1" + +ajv@^6.1.0, ajv@^6.10.0, ajv@^6.9.1: + version "6.12.0" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.0.tgz#06d60b96d87b8454a5adaba86e7854da629db4b7" + dependencies: + fast-deep-equal "^3.1.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + +align-text@^0.1.1, align-text@^0.1.3: + version "0.1.4" + resolved "https://registry.yarnpkg.com/align-text/-/align-text-0.1.4.tgz#0cd90a561093f35d0a99256c22b7069433fad117" + dependencies: + kind-of "^3.0.2" + longest "^1.0.1" + repeat-string "^1.5.2" + +ansi-colors@4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348" + +ansi-escapes@^4.2.1: + version "4.3.1" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.1.tgz#a5c47cc43181f1f38ffd7076837700d395522a61" + dependencies: + type-fest "^0.11.0" + +ansi-regex@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" + +ansi-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" + +ansi-regex@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997" + +ansi-regex@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75" + +ansi-styles@^3.2.0, ansi-styles@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" + dependencies: + color-convert "^1.9.0" + +ansi-styles@^4.0.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + dependencies: + color-convert "^2.0.1" + +ansi-styles@^4.1.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.2.1.tgz#90ae75c424d008d2624c5bf29ead3177ebfcf359" + dependencies: + "@types/color-name" "^1.1.1" + color-convert "^2.0.1" + +anymatch@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-1.3.0.tgz#a3e52fa39168c825ff57b0248126ce5a8ff95507" + dependencies: + arrify "^1.0.0" + micromatch "^2.1.5" + +anymatch@~3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716" + dependencies: + normalize-path "^3.0.0" + picomatch "^2.0.4" + +aproba@^1.0.3: + version "1.1.1" + resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.1.1.tgz#95d3600f07710aa0e9298c726ad5ecf2eacbabab" + +aproba@^1.1.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" + integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw== + +are-we-there-yet@~1.1.2: + version "1.1.4" + resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.4.tgz#bb5dca382bb94f05e15194373d16fd3ba1ca110d" + dependencies: + delegates "^1.0.0" + readable-stream "^2.0.6" + +argparse@^1.0.7: + version "1.0.10" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" + dependencies: + sprintf-js "~1.0.2" + +argparse@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" + +arr-diff@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-2.0.0.tgz#8f3b827f955a8bd669697e4a4256ac3ceae356cf" + dependencies: + arr-flatten "^1.0.1" + +arr-flatten@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.0.3.tgz#a274ed85ac08849b6bd7847c4580745dc51adfb1" + +array-unique@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.2.1.tgz#a1d97ccafcbc2625cc70fadceb36a50c58b01a53" + +arrify@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" + +asn1.js@^4.0.0: + version "4.9.1" + resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-4.9.1.tgz#48ba240b45a9280e94748990ba597d216617fd40" + dependencies: + bn.js "^4.0.0" + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + +asn1@~0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.3.tgz#dac8787713c9966849fc8180777ebe9c1ddf3b86" + +assert-plus@1.0.0, assert-plus@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" + +assert-plus@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-0.2.0.tgz#d74e1b87e7affc0db8aadb7021f3fe48101ab234" + +assert@^1.1.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/assert/-/assert-1.4.1.tgz#99912d591836b5a6f5b345c0f07eefc08fc65d91" + dependencies: + util "0.10.3" + +assertion-error@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.1.0.tgz#e60b6b0e8f301bd97e5375215bda406c85118c0b" + +astral-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9" + +async-each@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.1.tgz#19d386a1d9edc6e7c1c85d388aedbcc56d33602d" + +async@^2.1.2: + version "2.3.0" + resolved "https://registry.yarnpkg.com/async/-/async-2.3.0.tgz#1013d1051047dd320fe24e494d5c66ecaf6147d9" + dependencies: + lodash "^4.14.0" + +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + +aws-sign2@~0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.6.0.tgz#14342dd38dbcc94d0e5b87d763cd63612c0e794f" + +aws4@^1.2.1: + version "1.6.0" + resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.6.0.tgz#83ef5ca860b2b32e4a0deedee8c771b9db57471e" + +balanced-match@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" + +base64-js@^1.0.2: + version "1.2.0" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.2.0.tgz#a39992d723584811982be5e290bb6a53d86700f1" + +bcrypt-pbkdf@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz#63bc5dcb61331b92bc05fd528953c33462a06f8d" + dependencies: + tweetnacl "^0.14.3" + +big.js@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/big.js/-/big.js-3.1.3.tgz#4cada2193652eb3ca9ec8e55c9015669c9806978" + +binary-extensions@^1.0.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.8.0.tgz#48ec8d16df4377eae5fa5884682480af4d95c774" + +binary-extensions@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.0.0.tgz#23c0df14f6a88077f5f986c0d167ec03c3d5537c" + +block-stream@*: + version "0.0.9" + resolved "https://registry.yarnpkg.com/block-stream/-/block-stream-0.0.9.tgz#13ebfe778a03205cfe03751481ebb4b3300c126a" + dependencies: + inherits "~2.0.0" + +bluebird@^3.5.1: + version "3.7.2" + resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" + integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== + +bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.4.0: + version "4.11.9" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.9.tgz#26d556829458f9d1e81fc48952493d0ba3507828" + +boom@2.x.x: + version "2.10.1" + resolved "https://registry.yarnpkg.com/boom/-/boom-2.10.1.tgz#39c8918ceff5799f83f9492a848f625add0c766f" + dependencies: + hoek "2.x.x" + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +braces@^1.8.2: + version "1.8.5" + resolved "https://registry.yarnpkg.com/braces/-/braces-1.8.5.tgz#ba77962e12dff969d6b76711e914b737857bf6a7" + dependencies: + expand-range "^1.8.1" + preserve "^0.2.0" + repeat-element "^1.1.2" + +braces@~3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" + dependencies: + fill-range "^7.0.1" + +brorand@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" + +browser-stdout@1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" + +browserify-aes@^1.0.0, browserify-aes@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.0.6.tgz#5e7725dbdef1fd5930d4ebab48567ce451c48a0a" + dependencies: + buffer-xor "^1.0.2" + cipher-base "^1.0.0" + create-hash "^1.1.0" + evp_bytestokey "^1.0.0" + inherits "^2.0.1" + +browserify-cipher@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/browserify-cipher/-/browserify-cipher-1.0.0.tgz#9988244874bf5ed4e28da95666dcd66ac8fc363a" + dependencies: + browserify-aes "^1.0.4" + browserify-des "^1.0.0" + evp_bytestokey "^1.0.0" + +browserify-des@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/browserify-des/-/browserify-des-1.0.0.tgz#daa277717470922ed2fe18594118a175439721dd" + dependencies: + cipher-base "^1.0.1" + des.js "^1.0.0" + inherits "^2.0.1" + +browserify-rsa@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/browserify-rsa/-/browserify-rsa-4.0.1.tgz#21e0abfaf6f2029cf2fafb133567a701d4135524" + dependencies: + bn.js "^4.1.0" + randombytes "^2.0.1" + +browserify-sign@^4.0.0: + version "4.0.4" + resolved "https://registry.yarnpkg.com/browserify-sign/-/browserify-sign-4.0.4.tgz#aa4eb68e5d7b658baa6bf6a57e630cbd7a93d298" + dependencies: + bn.js "^4.1.1" + browserify-rsa "^4.0.0" + create-hash "^1.1.0" + create-hmac "^1.1.2" + elliptic "^6.0.0" + inherits "^2.0.1" + parse-asn1 "^5.0.0" + +browserify-zlib@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/browserify-zlib/-/browserify-zlib-0.1.4.tgz#bb35f8a519f600e0fa6b8485241c979d0141fb2d" + dependencies: + pako "~0.2.0" + +buffer-from@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" + integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== + +buffer-shims@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/buffer-shims/-/buffer-shims-1.0.0.tgz#9978ce317388c649ad8793028c3477ef044a8b51" + +buffer-xor@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9" + +buffer@^4.3.0: + version "4.9.1" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-4.9.1.tgz#6d1bb601b07a4efced97094132093027c95bc298" + dependencies: + base64-js "^1.0.2" + ieee754 "^1.1.4" + isarray "^1.0.0" + +builtin-modules@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" + +builtin-status-codes@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8" + +cacache@^10.0.4: + version "10.0.4" + resolved "https://registry.yarnpkg.com/cacache/-/cacache-10.0.4.tgz#6452367999eff9d4188aefd9a14e9d7c6a263460" + integrity sha512-Dph0MzuH+rTQzGPNT9fAnrPmMmjKfST6trxJeK7NQuHRaVw24VzPRWTmg9MpcwOVQZO0E1FBICUlFeNaKPIfHA== + dependencies: + bluebird "^3.5.1" + chownr "^1.0.1" + glob "^7.1.2" + graceful-fs "^4.1.11" + lru-cache "^4.1.1" + mississippi "^2.0.0" + mkdirp "^0.5.1" + move-concurrently "^1.0.1" + promise-inflight "^1.0.1" + rimraf "^2.6.2" + ssri "^5.2.4" + unique-filename "^1.1.0" + y18n "^4.0.0" + +callsites@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.0.0.tgz#fb7eb569b72ad7a45812f93fd9430a3e410b3dd3" + +camelcase@^1.0.2: + version "1.2.1" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-1.2.1.tgz#9bb5304d2e0b56698b2c758b08a3eaa9daa58a39" + +camelcase@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd" + +camelcase@^6.0.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.2.0.tgz#924af881c9d525ac9d87f40d964e5cea982a1809" + +caseless@~0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" + +center-align@^0.1.1: + version "0.1.3" + resolved "https://registry.yarnpkg.com/center-align/-/center-align-0.1.3.tgz#aa0d32629b6ee972200411cbd4461c907bc2b7ad" + dependencies: + align-text "^0.1.3" + lazy-cache "^1.0.3" + +chai@^4.1.2: + version "4.3.4" + resolved "https://registry.yarnpkg.com/chai/-/chai-4.3.4.tgz#b55e655b31e1eac7099be4c08c21964fce2e6c49" + dependencies: + assertion-error "^1.1.0" + check-error "^1.0.2" + deep-eql "^3.0.1" + get-func-name "^2.0.0" + pathval "^1.1.1" + type-detect "^4.0.5" + +chalk@^2.0.0, chalk@^2.1.0: + version "2.4.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + +chalk@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-3.0.0.tgz#3f73c2bf526591f574cc492c51e2456349f844e4" + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +chalk@^4.1.0: + version "4.1.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +chardet@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" + +check-error@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.2.tgz#574d312edd88bb5dd8912e9286dd6c0aed4aac82" + +chokidar@3.5.2: + version "3.5.2" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.2.tgz#dba3976fcadb016f66fd365021d91600d01c1e75" + dependencies: + anymatch "~3.1.2" + braces "~3.0.2" + glob-parent "~5.1.2" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.6.0" + optionalDependencies: + fsevents "~2.3.2" + +chokidar@^1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-1.7.0.tgz#798e689778151c8076b4b360e5edd28cda2bb468" + dependencies: + anymatch "^1.3.0" + async-each "^1.0.0" + glob-parent "^2.0.0" + inherits "^2.0.1" + is-binary-path "^1.0.0" + is-glob "^2.0.0" + path-is-absolute "^1.0.0" + readdirp "^2.0.0" + optionalDependencies: + fsevents "^1.0.0" + +chownr@^1.0.1: + version "1.1.4" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" + integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== + +cipher-base@^1.0.0, cipher-base@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.3.tgz#eeabf194419ce900da3018c207d212f2a6df0a07" + dependencies: + inherits "^2.0.1" + +cli-cursor@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" + dependencies: + restore-cursor "^3.1.0" + +cli-width@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.0.tgz#ff19ede8a9a5e579324147b0c11f0fbcbabed639" + +cliui@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-2.1.0.tgz#4b475760ff80264c762c3a1719032e91c7fea0d1" + dependencies: + center-align "^0.1.1" + right-align "^0.1.1" + wordwrap "0.0.2" + +cliui@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-3.2.0.tgz#120601537a916d29940f934da3b48d585a39213d" + dependencies: + string-width "^1.0.1" + strip-ansi "^3.0.1" + wrap-ansi "^2.0.0" + +cliui@^7.0.2: + version "7.0.4" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.0" + wrap-ansi "^7.0.0" + +co@^4.6.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" + +code-point-at@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" + +color-convert@^1.9.0: + version "1.9.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.1.tgz#c1261107aeb2f294ebffec9ed9ecad529a6097ed" + dependencies: + color-name "^1.1.1" + +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + dependencies: + color-name "~1.1.4" + +color-name@^1.1.1: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + +combined-stream@^1.0.5, combined-stream@~1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.5.tgz#938370a57b4a51dea2c77c15d5c5fdf895164009" + dependencies: + delayed-stream "~1.0.0" + +commander@~2.13.0: + version "2.13.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.13.0.tgz#6964bca67685df7c1f1430c584f07d7597885b9c" + integrity sha512-MVuS359B+YzaWqjCL/c+22gfryv+mCBPHAv3zyVI2GN8EY6IRP8VwtasXn8jyyhvvq84R4ImN1OKRtcbIasjYA== + +commondir@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + +concat-stream@^1.5.0: + version "1.6.2" + resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" + integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw== + dependencies: + buffer-from "^1.0.0" + inherits "^2.0.3" + readable-stream "^2.2.2" + typedarray "^0.0.6" + +console-browserify@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/console-browserify/-/console-browserify-1.1.0.tgz#f0241c45730a9fc6323b206dbf38edc741d0bb10" + dependencies: + date-now "^0.1.4" + +console-control-strings@^1.0.0, console-control-strings@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" + +constants-browserify@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/constants-browserify/-/constants-browserify-1.0.0.tgz#c20b96d8c617748aaf1c16021760cd27fcb8cb75" + +copy-concurrently@^1.0.0: + version "1.0.5" + resolved "https://registry.yarnpkg.com/copy-concurrently/-/copy-concurrently-1.0.5.tgz#92297398cae34937fcafd6ec8139c18051f0b5e0" + integrity sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A== + dependencies: + aproba "^1.1.1" + fs-write-stream-atomic "^1.0.8" + iferr "^0.1.5" + mkdirp "^0.5.1" + rimraf "^2.5.4" + run-queue "^1.0.0" + +core-util-is@~1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" + +create-ecdh@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.0.tgz#888c723596cdf7612f6498233eebd7a35301737d" + dependencies: + bn.js "^4.1.0" + elliptic "^6.0.0" + +create-hash@^1.1.0, create-hash@^1.1.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.1.2.tgz#51210062d7bb7479f6c65bb41a92208b1d61abad" + dependencies: + cipher-base "^1.0.1" + inherits "^2.0.1" + ripemd160 "^1.0.0" + sha.js "^2.3.6" + +create-hmac@^1.1.0, create-hmac@^1.1.2: + version "1.1.4" + resolved "https://registry.yarnpkg.com/create-hmac/-/create-hmac-1.1.4.tgz#d3fb4ba253eb8b3f56e39ea2fbcb8af747bd3170" + dependencies: + create-hash "^1.1.0" + inherits "^2.0.1" + +cross-spawn@^5.0.1: + version "5.1.0" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449" + dependencies: + lru-cache "^4.0.1" + shebang-command "^1.2.0" + which "^1.2.9" + +cross-spawn@^6.0.5: + version "6.0.5" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" + dependencies: + nice-try "^1.0.4" + path-key "^2.0.1" + semver "^5.5.0" + shebang-command "^1.2.0" + which "^1.2.9" + +cryptiles@2.x.x: + version "2.0.5" + resolved "https://registry.yarnpkg.com/cryptiles/-/cryptiles-2.0.5.tgz#3bdfecdc608147c1c67202fa291e7dca59eaa3b8" + dependencies: + boom "2.x.x" + +crypto-browserify@^3.11.0: + version "3.11.0" + resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.11.0.tgz#3652a0906ab9b2a7e0c3ce66a408e957a2485522" + dependencies: + browserify-cipher "^1.0.0" + browserify-sign "^4.0.0" + create-ecdh "^4.0.0" + create-hash "^1.1.0" + create-hmac "^1.1.0" + diffie-hellman "^5.0.0" + inherits "^2.0.1" + pbkdf2 "^3.0.3" + public-encrypt "^4.0.0" + randombytes "^2.0.0" + +cyclist@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-1.0.1.tgz#596e9698fd0c80e12038c2b82d6eb1b35b6224d9" + integrity sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk= + +d@1: + version "1.0.0" + resolved "https://registry.yarnpkg.com/d/-/d-1.0.0.tgz#754bb5bfe55451da69a58b94d45f4c5b0462d58f" + dependencies: + es5-ext "^0.10.9" + +dashdash@^1.12.0: + version "1.14.1" + resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" + dependencies: + assert-plus "^1.0.0" + +date-now@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/date-now/-/date-now-0.1.4.tgz#eaf439fd4d4848ad74e5cc7dbef200672b9e345b" + +debug@4.3.1, debug@^4.0.1: + version "4.3.1" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee" + dependencies: + ms "2.1.2" + +debug@^2.2.0: + version "2.6.9" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + dependencies: + ms "2.0.0" + +decamelize@^1.0.0, decamelize@^1.1.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" + +decamelize@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-4.0.0.tgz#aa472d7bf660eb15f3494efd531cab7f2a709837" + +deep-eql@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-3.0.1.tgz#dfc9404400ad1c8fe023e7da1df1c147c4b444df" + dependencies: + type-detect "^4.0.0" + +deep-extend@~0.4.0: + version "0.4.1" + resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.4.1.tgz#efe4113d08085f4e6f9687759810f807469e2253" + +deep-is@~0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" + +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + +delegates@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" + +des.js@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/des.js/-/des.js-1.0.0.tgz#c074d2e2aa6a8a9a07dbd61f9a15c2cd83ec8ecc" + dependencies: + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + +diff@5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/diff/-/diff-5.0.0.tgz#7ed6ad76d859d030787ec35855f5b1daf31d852b" + +diff@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" + +diffie-hellman@^5.0.0: + version "5.0.2" + resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.2.tgz#b5835739270cfe26acf632099fded2a07f209e5e" + dependencies: + bn.js "^4.1.0" + miller-rabin "^4.0.0" + randombytes "^2.0.0" + +doctrine@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" + dependencies: + esutils "^2.0.2" + +domain-browser@^1.1.1: + version "1.1.7" + resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.1.7.tgz#867aa4b093faa05f1de08c06f4d7b21fdf8698bc" + +duplexify@^3.4.2, duplexify@^3.6.0: + version "3.7.1" + resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-3.7.1.tgz#2a4df5317f6ccfd91f86d6fd25d8d8a103b88309" + integrity sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g== + dependencies: + end-of-stream "^1.0.0" + inherits "^2.0.1" + readable-stream "^2.0.0" + stream-shift "^1.0.0" + +ecc-jsbn@~0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz#0fc73a9ed5f0d53c38193398523ef7e543777505" + dependencies: + jsbn "~0.1.0" + +elliptic@^6.0.0: + version "6.5.3" + resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.3.tgz#cb59eb2efdaf73a0bd78ccd7015a62ad6e0f93d6" + dependencies: + bn.js "^4.4.0" + brorand "^1.0.1" + hash.js "^1.0.0" + hmac-drbg "^1.0.0" + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + minimalistic-crypto-utils "^1.0.0" + +emoji-regex@^7.0.1: + version "7.0.3" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" + +emoji-regex@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + +emojis-list@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-2.1.0.tgz#4daa4d9db00f9819880c79fa457ae5b09a1fd389" + +end-of-stream@^1.0.0, end-of-stream@^1.1.0: + version "1.4.4" + resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" + integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== + dependencies: + once "^1.4.0" + +enhanced-resolve@^3.4.0: + version "3.4.1" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-3.4.1.tgz#0421e339fd71419b3da13d129b3979040230476e" + dependencies: + graceful-fs "^4.1.2" + memory-fs "^0.4.0" + object-assign "^4.0.1" + tapable "^0.2.7" + +errno@^0.1.3: + version "0.1.4" + resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.4.tgz#b896e23a9e5e8ba33871fc996abd3635fc9a1c7d" + dependencies: + prr "~0.0.0" + +errno@~0.1.7: + version "0.1.8" + resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.8.tgz#8bb3e9c7d463be4976ff888f76b4809ebc2e811f" + integrity sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A== + dependencies: + prr "~1.0.1" + +error-ex@^1.2.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.1.tgz#f855a86ce61adc4e8621c3cda21e7a7612c3a8dc" + dependencies: + is-arrayish "^0.2.1" + +es5-ext@^0.10.14, es5-ext@^0.10.9, es5-ext@~0.10.14: + version "0.10.30" + resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.30.tgz#7141a16836697dbabfaaaeee41495ce29f52c939" + dependencies: + es6-iterator "2" + es6-symbol "~3.1" + +es6-iterator@2, es6-iterator@^2.0.1, es6-iterator@~2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.1.tgz#8e319c9f0453bf575d374940a655920e59ca5512" + dependencies: + d "1" + es5-ext "^0.10.14" + es6-symbol "^3.1" + +es6-map@^0.1.3: + version "0.1.5" + resolved "https://registry.yarnpkg.com/es6-map/-/es6-map-0.1.5.tgz#9136e0503dcc06a301690f0bb14ff4e364e949f0" + dependencies: + d "1" + es5-ext "~0.10.14" + es6-iterator "~2.0.1" + es6-set "~0.1.5" + es6-symbol "~3.1.1" + event-emitter "~0.3.5" + +es6-set@~0.1.5: + version "0.1.5" + resolved "https://registry.yarnpkg.com/es6-set/-/es6-set-0.1.5.tgz#d2b3ec5d4d800ced818db538d28974db0a73ccb1" + dependencies: + d "1" + es5-ext "~0.10.14" + es6-iterator "~2.0.1" + es6-symbol "3.1.1" + event-emitter "~0.3.5" + +es6-symbol@3.1.1, es6-symbol@^3.1, es6-symbol@^3.1.1, es6-symbol@~3.1, es6-symbol@~3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.1.tgz#bf00ef4fdab6ba1b46ecb7b629b4c7ed5715cc77" + dependencies: + d "1" + es5-ext "~0.10.14" + +es6-weak-map@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/es6-weak-map/-/es6-weak-map-2.0.2.tgz#5e3ab32251ffd1538a1f8e5ffa1357772f92d96f" + dependencies: + d "1" + es5-ext "^0.10.14" + es6-iterator "^2.0.1" + es6-symbol "^3.1.1" + +escalade@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" + +escape-string-regexp@4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" + +escape-string-regexp@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + +escope@^3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/escope/-/escope-3.6.0.tgz#e01975e812781a163a6dadfdd80398dc64c889c3" + dependencies: + es6-map "^0.1.3" + es6-weak-map "^2.0.1" + esrecurse "^4.1.0" + estraverse "^4.1.1" + +eslint-config-prettier@^6.10.1: + version "6.15.0" + resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-6.15.0.tgz#7f93f6cb7d45a92f1537a70ecc06366e1ac6fed9" + dependencies: + get-stdin "^6.0.0" + +eslint-loader@^2.0.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/eslint-loader/-/eslint-loader-2.2.1.tgz#28b9c12da54057af0845e2a6112701a2f6bf8337" + dependencies: + loader-fs-cache "^1.0.0" + loader-utils "^1.0.2" + object-assign "^4.0.1" + object-hash "^1.1.4" + rimraf "^2.6.1" + +eslint-scope@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.0.0.tgz#e87c8887c73e8d1ec84f1ca591645c358bfc8fb9" + dependencies: + esrecurse "^4.1.0" + estraverse "^4.1.1" + +eslint-utils@^1.4.3: + version "1.4.3" + resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-1.4.3.tgz#74fec7c54d0776b6f67e0251040b5806564e981f" + dependencies: + eslint-visitor-keys "^1.1.0" + +eslint-visitor-keys@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz#e2a82cea84ff246ad6fb57f9bde5b46621459ec2" + +eslint@^6.8.0: + version "6.8.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-6.8.0.tgz#62262d6729739f9275723824302fb227c8c93ffb" + dependencies: + "@babel/code-frame" "^7.0.0" + ajv "^6.10.0" + chalk "^2.1.0" + cross-spawn "^6.0.5" + debug "^4.0.1" + doctrine "^3.0.0" + eslint-scope "^5.0.0" + eslint-utils "^1.4.3" + eslint-visitor-keys "^1.1.0" + espree "^6.1.2" + esquery "^1.0.1" + esutils "^2.0.2" + file-entry-cache "^5.0.1" + functional-red-black-tree "^1.0.1" + glob-parent "^5.0.0" + globals "^12.1.0" + ignore "^4.0.6" + import-fresh "^3.0.0" + imurmurhash "^0.1.4" + inquirer "^7.0.0" + is-glob "^4.0.0" + js-yaml "^3.13.1" + json-stable-stringify-without-jsonify "^1.0.1" + levn "^0.3.0" + lodash "^4.17.14" + minimatch "^3.0.4" + mkdirp "^0.5.1" + natural-compare "^1.4.0" + optionator "^0.8.3" + progress "^2.0.0" + regexpp "^2.0.1" + semver "^6.1.2" + strip-ansi "^5.2.0" + strip-json-comments "^3.0.1" + table "^5.2.3" + text-table "^0.2.0" + v8-compile-cache "^2.0.3" + +espree@^6.1.2: + version "6.2.1" + resolved "https://registry.yarnpkg.com/espree/-/espree-6.2.1.tgz#77fc72e1fd744a2052c20f38a5b575832e82734a" + dependencies: + acorn "^7.1.1" + acorn-jsx "^5.2.0" + eslint-visitor-keys "^1.1.0" + +esprima@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.0.tgz#4499eddcd1110e0b218bacf2fa7f7f59f55ca804" + +esquery@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.0.1.tgz#406c51658b1f5991a5f9b62b1dc25b00e3e5c708" + dependencies: + estraverse "^4.0.0" + +esrecurse@^4.1.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.2.0.tgz#fa9568d98d3823f9a41d91e902dcab9ea6e5b163" + dependencies: + estraverse "^4.1.0" + object-assign "^4.0.1" + +estraverse@^4.0.0, estraverse@^4.1.0, estraverse@^4.1.1: + version "4.2.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.2.0.tgz#0dee3fed31fcd469618ce7342099fc1afa0bdb13" + +esutils@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b" + +event-emitter@~0.3.5: + version "0.3.5" + resolved "https://registry.yarnpkg.com/event-emitter/-/event-emitter-0.3.5.tgz#df8c69eef1647923c7157b9ce83840610b02cc39" + dependencies: + d "1" + es5-ext "~0.10.14" + +events@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/events/-/events-1.1.1.tgz#9ebdb7635ad099c70dcc4c2a1f5004288e8bd924" + +evp_bytestokey@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/evp_bytestokey/-/evp_bytestokey-1.0.0.tgz#497b66ad9fef65cd7c08a6180824ba1476b66e53" + dependencies: + create-hash "^1.1.1" + +execa@^0.6.0: + version "0.6.3" + resolved "https://registry.yarnpkg.com/execa/-/execa-0.6.3.tgz#57b69a594f081759c69e5370f0d17b9cb11658fe" + dependencies: + cross-spawn "^5.0.1" + get-stream "^3.0.0" + is-stream "^1.1.0" + npm-run-path "^2.0.0" + p-finally "^1.0.0" + signal-exit "^3.0.0" + strip-eof "^1.0.0" + +execa@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-0.7.0.tgz#944becd34cc41ee32a63a9faf27ad5a65fc59777" + dependencies: + cross-spawn "^5.0.1" + get-stream "^3.0.0" + is-stream "^1.1.0" + npm-run-path "^2.0.0" + p-finally "^1.0.0" + signal-exit "^3.0.0" + strip-eof "^1.0.0" + +expand-brackets@^0.1.4: + version "0.1.5" + resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-0.1.5.tgz#df07284e342a807cd733ac5af72411e581d1177b" + dependencies: + is-posix-bracket "^0.1.0" + +expand-range@^1.8.1: + version "1.8.2" + resolved "https://registry.yarnpkg.com/expand-range/-/expand-range-1.8.2.tgz#a299effd335fe2721ebae8e257ec79644fc85337" + dependencies: + fill-range "^2.1.0" + +extend@~3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" + +external-editor@^3.0.3: + version "3.1.0" + resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.1.0.tgz#cb03f740befae03ea4d283caed2741a83f335495" + dependencies: + chardet "^0.7.0" + iconv-lite "^0.4.24" + tmp "^0.0.33" + +extglob@^0.3.1: + version "0.3.2" + resolved "https://registry.yarnpkg.com/extglob/-/extglob-0.3.2.tgz#2e18ff3d2f49ab2765cec9023f011daa8d8349a1" + dependencies: + is-extglob "^1.0.0" + +extsprintf@1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.0.2.tgz#e1080e0658e300b06294990cc70e1502235fd550" + +fast-deep-equal@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz#545145077c501491e33b15ec408c294376e94ae4" + +fast-json-stable-stringify@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2" + +fast-levenshtein@~2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" + +figures@^3.0.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af" + dependencies: + escape-string-regexp "^1.0.5" + +file-entry-cache@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-5.0.1.tgz#ca0f6efa6dd3d561333fb14515065c2fafdf439c" + dependencies: + flat-cache "^2.0.1" + +filename-regex@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/filename-regex/-/filename-regex-2.0.0.tgz#996e3e80479b98b9897f15a8a58b3d084e926775" + +fill-range@^2.1.0: + version "2.2.3" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-2.2.3.tgz#50b77dfd7e469bc7492470963699fe7a8485a723" + dependencies: + is-number "^2.1.0" + isobject "^2.0.0" + randomatic "^1.1.3" + repeat-element "^1.1.2" + repeat-string "^1.5.2" + +fill-range@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" + dependencies: + to-regex-range "^5.0.1" + +find-cache-dir@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-0.1.1.tgz#c8defae57c8a52a8a784f9e31c57c742e993a0b9" + dependencies: + commondir "^1.0.1" + mkdirp "^0.5.1" + pkg-dir "^1.0.0" + +find-cache-dir@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-1.0.0.tgz#9288e3e9e3cc3748717d39eade17cf71fc30ee6f" + integrity sha1-kojj6ePMN0hxfTnq3hfPcfww7m8= + dependencies: + commondir "^1.0.1" + make-dir "^1.0.0" + pkg-dir "^2.0.0" + +find-up@5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" + dependencies: + locate-path "^6.0.0" + path-exists "^4.0.0" + +find-up@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f" + dependencies: + path-exists "^2.0.0" + pinkie-promise "^2.0.0" + +find-up@^2.0.0, find-up@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7" + integrity sha1-RdG35QbHF93UgndaK3eSCjwMV6c= + dependencies: + locate-path "^2.0.0" + +flat-cache@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-2.0.1.tgz#5d296d6f04bda44a4630a301413bdbc2ec085ec0" + dependencies: + flatted "^2.0.0" + rimraf "2.6.3" + write "1.0.3" + +flat@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/flat/-/flat-5.0.2.tgz#8ca6fe332069ffa9d324c327198c598259ceb241" + +flatted@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.1.tgz#69e57caa8f0eacbc281d2e2cb458d46fdb449e08" + +flush-write-stream@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/flush-write-stream/-/flush-write-stream-1.1.1.tgz#8dd7d873a1babc207d94ead0c2e0e44276ebf2e8" + integrity sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w== + dependencies: + inherits "^2.0.3" + readable-stream "^2.3.6" + +for-in@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" + +for-own@^0.1.4: + version "0.1.5" + resolved "https://registry.yarnpkg.com/for-own/-/for-own-0.1.5.tgz#5265c681a4f294dabbf17c9509b6763aa84510ce" + dependencies: + for-in "^1.0.1" + +forever-agent@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" + +form-data@~2.1.1: + version "2.1.4" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.1.4.tgz#33c183acf193276ecaa98143a69e94bfee1750d1" + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.5" + mime-types "^2.1.12" + +from2@^2.1.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/from2/-/from2-2.3.0.tgz#8bfb5502bde4a4d36cfdeea007fcca21d7e382af" + integrity sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8= + dependencies: + inherits "^2.0.1" + readable-stream "^2.0.0" + +fs-write-stream-atomic@^1.0.8: + version "1.0.10" + resolved "https://registry.yarnpkg.com/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz#b47df53493ef911df75731e70a9ded0189db40c9" + integrity sha1-tH31NJPvkR33VzHnCp3tAYnbQMk= + dependencies: + graceful-fs "^4.1.2" + iferr "^0.1.5" + imurmurhash "^0.1.4" + readable-stream "1 || 2" + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + +fsevents@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.1.1.tgz#f19fd28f43eeaf761680e519a203c4d0b3d31aff" + dependencies: + nan "^2.3.0" + node-pre-gyp "^0.6.29" + +fsevents@~2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" + +fstream-ignore@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/fstream-ignore/-/fstream-ignore-1.0.5.tgz#9c31dae34767018fe1d249b24dada67d092da105" + dependencies: + fstream "^1.0.0" + inherits "2" + minimatch "^3.0.0" + +fstream@^1.0.0, fstream@^1.0.10, fstream@^1.0.12: + version "1.0.12" + resolved "https://registry.yarnpkg.com/fstream/-/fstream-1.0.12.tgz#4e8ba8ee2d48be4f7d0de505455548eae5932045" + dependencies: + graceful-fs "^4.1.2" + inherits "~2.0.0" + mkdirp ">=0.5 0" + rimraf "2" + +functional-red-black-tree@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" + +gauge@~2.7.1: + version "2.7.4" + resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7" + dependencies: + aproba "^1.0.3" + console-control-strings "^1.0.0" + has-unicode "^2.0.0" + object-assign "^4.1.0" + signal-exit "^3.0.0" + string-width "^1.0.1" + strip-ansi "^3.0.1" + wide-align "^1.1.0" + +get-caller-file@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.2.tgz#f702e63127e7e231c160a80c1554acb70d5047e5" + +get-caller-file@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" + +get-func-name@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.0.tgz#ead774abee72e20409433a066366023dd6887a41" + +get-stdin@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-6.0.0.tgz#9e09bf712b360ab9225e812048f71fde9c89657b" + +get-stream@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14" + +getpass@^0.1.1: + version "0.1.7" + resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" + dependencies: + assert-plus "^1.0.0" + +glob-base@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/glob-base/-/glob-base-0.3.0.tgz#dbb164f6221b1c0b1ccf82aea328b497df0ea3c4" + dependencies: + glob-parent "^2.0.0" + is-glob "^2.0.0" + +glob-parent@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-2.0.0.tgz#81383d72db054fcccf5336daa902f182f6edbb28" + dependencies: + is-glob "^2.0.0" + +glob-parent@^5.0.0: + version "5.1.1" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.1.tgz#b6c1ef417c4e5663ea498f1c45afac6916bbc229" + dependencies: + is-glob "^4.0.1" + +glob-parent@~5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" + dependencies: + is-glob "^4.0.1" + +glob@7.1.7, glob@^7.1.3: + version "7.1.7" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.7.tgz#3b193e9233f01d42d0b3f78294bbeeb418f94a90" + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +glob@^7.1.2: + version "7.2.0" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" + integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +globals@^12.1.0: + version "12.4.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-12.4.0.tgz#a18813576a41b00a24a97e7f815918c2e19925f8" + dependencies: + type-fest "^0.8.1" + +graceful-fs@^4.1.11: + version "4.2.8" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.8.tgz#e412b8d33f5e006593cbd3cee6df9f2cebbe802a" + integrity sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg== + +graceful-fs@^4.1.2: + version "4.1.15" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.15.tgz#ffb703e1066e8a0eeaa4c8b80ba9253eeefbfb00" + +growl@1.10.5: + version "1.10.5" + resolved "https://registry.yarnpkg.com/growl/-/growl-1.10.5.tgz#f2735dc2283674fa67478b10181059355c369e5e" + +har-schema@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-1.0.5.tgz#d263135f43307c02c602afc8fe95970c0151369e" + +har-validator@~4.2.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-4.2.1.tgz#33481d0f1bbff600dd203d75812a6a5fba002e2a" + dependencies: + ajv "^4.9.1" + har-schema "^1.0.5" + +has-flag@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-2.0.0.tgz#e8207af1cc7b30d446cc70b734b5e8be18f88d51" + +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + +has-unicode@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" + +hash.js@^1.0.0, hash.js@^1.0.3: + version "1.1.7" + resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42" + dependencies: + inherits "^2.0.3" + minimalistic-assert "^1.0.1" + +hawk@~3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/hawk/-/hawk-3.1.3.tgz#078444bd7c1640b0fe540d2c9b73d59678e8e1c4" + dependencies: + boom "2.x.x" + cryptiles "2.x.x" + hoek "2.x.x" + sntp "1.x.x" + +he@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" + +hmac-drbg@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" + dependencies: + hash.js "^1.0.3" + minimalistic-assert "^1.0.0" + minimalistic-crypto-utils "^1.0.1" + +hoek@2.x.x: + version "2.16.3" + resolved "https://registry.yarnpkg.com/hoek/-/hoek-2.16.3.tgz#20bb7403d3cea398e91dc4710a8ff1b8274a25ed" + +hosted-git-info@^2.1.4: + version "2.4.2" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.4.2.tgz#0076b9f46a270506ddbaaea56496897460612a67" + +http-signature@~1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.1.1.tgz#df72e267066cd0ac67fb76adf8e134a8fbcf91bf" + dependencies: + assert-plus "^0.2.0" + jsprim "^1.2.2" + sshpk "^1.7.0" + +https-browserify@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-0.0.1.tgz#3f91365cabe60b77ed0ebba24b454e3e09d95a82" + +iconv-lite@^0.4.24: + version "0.4.24" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" + dependencies: + safer-buffer ">= 2.1.2 < 3" + +ieee754@^1.1.4: + version "1.1.8" + resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.8.tgz#be33d40ac10ef1926701f6f08a2d86fbfd1ad3e4" + +iferr@^0.1.5: + version "0.1.5" + resolved "https://registry.yarnpkg.com/iferr/-/iferr-0.1.5.tgz#c60eed69e6d8fdb6b3104a1fcbca1c192dc5b501" + integrity sha1-xg7taebY/bazEEofy8ocGS3FtQE= + +ignore@^4.0.6: + version "4.0.6" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" + +import-fresh@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.0.0.tgz#a3d897f420cab0e671236897f75bc14b4885c390" + dependencies: + parent-module "^1.0.0" + resolve-from "^4.0.0" + +imurmurhash@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + +indexof@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/indexof/-/indexof-0.0.1.tgz#82dc336d232b9062179d05ab3293a66059fd435d" + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.0, inherits@~2.0.1, inherits@~2.0.3: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + +inherits@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1" + +ini@~1.3.0: + version "1.3.8" + resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" + +inquirer@^7.0.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-7.1.0.tgz#1298a01859883e17c7264b82870ae1034f92dd29" + dependencies: + ansi-escapes "^4.2.1" + chalk "^3.0.0" + cli-cursor "^3.1.0" + cli-width "^2.0.0" + external-editor "^3.0.3" + figures "^3.0.0" + lodash "^4.17.15" + mute-stream "0.0.8" + run-async "^2.4.0" + rxjs "^6.5.3" + string-width "^4.1.0" + strip-ansi "^6.0.0" + through "^2.3.6" + +interpret@^1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.0.3.tgz#cbc35c62eeee73f19ab7b10a801511401afc0f90" + +invert-kv@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6" + +is-arrayish@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" + +is-binary-path@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-1.0.1.tgz#75f16642b480f187a711c814161fd3a4a7655898" + dependencies: + binary-extensions "^1.0.0" + +is-binary-path@~2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" + dependencies: + binary-extensions "^2.0.0" + +is-buffer@^1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.5.tgz#1f3b26ef613b214b88cbca23cc6c01d87961eecc" + +is-builtin-module@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-builtin-module/-/is-builtin-module-1.0.0.tgz#540572d34f7ac3119f8f76c30cbc1b1e037affbe" + dependencies: + builtin-modules "^1.0.0" + +is-dotfile@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-dotfile/-/is-dotfile-1.0.2.tgz#2c132383f39199f8edc268ca01b9b007d205cc4d" + +is-equal-shallow@^0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz#2238098fc221de0bcfa5d9eac4c45d638aa1c534" + dependencies: + is-primitive "^2.0.0" + +is-extendable@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" + +is-extglob@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-1.0.0.tgz#ac468177c4943405a092fc8f29760c6ffc6206c0" + +is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + +is-fullwidth-code-point@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" + dependencies: + number-is-nan "^1.0.0" + +is-fullwidth-code-point@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" + +is-fullwidth-code-point@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + +is-glob@^2.0.0, is-glob@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-2.0.1.tgz#d096f926a3ded5600f3fdfd91198cb0888c2d863" + dependencies: + is-extglob "^1.0.0" + +is-glob@^4.0.0, is-glob@^4.0.1, is-glob@~4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc" + dependencies: + is-extglob "^2.1.1" + +is-number@^2.0.2, is-number@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-2.1.0.tgz#01fcbbb393463a548f2f466cce16dece49db908f" + dependencies: + kind-of "^3.0.2" + +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + +is-plain-obj@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287" + +is-posix-bracket@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz#3334dc79774368e92f016e6fbc0a88f5cd6e6bc4" + +is-primitive@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-primitive/-/is-primitive-2.0.0.tgz#207bab91638499c07b2adf240a41a87210034575" + +is-promise@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.1.0.tgz#79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa" + +is-stream@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" + +is-typedarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" + +is-unicode-supported@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" + +isarray@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" + +isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + +isobject@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" + dependencies: + isarray "1.0.0" + +isstream@~0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" + +js-tokens@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + +js-yaml@4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" + dependencies: + argparse "^2.0.1" + +js-yaml@^3.13.1: + version "3.14.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.0.tgz#a7a34170f26a21bb162424d8adacb4113a69e482" + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + +jsbn@~0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" + +json-loader@^0.5.4: + version "0.5.4" + resolved "https://registry.yarnpkg.com/json-loader/-/json-loader-0.5.4.tgz#8baa1365a632f58a3c46d20175fc6002c96e37de" + +json-schema-traverse@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + +json-schema@0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" + +json-stable-stringify-without-jsonify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" + +json-stable-stringify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz#9a759d39c5f2ff503fd5300646ed445f88c4f9af" + dependencies: + jsonify "~0.0.0" + +json-stringify-safe@~5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" + +json5@^0.5.0, json5@^0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821" + +jsonify@~0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73" + +jsprim@^1.2.2: + version "1.4.0" + resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.0.tgz#a3b87e40298d8c380552d8cc7628a0bb95a22918" + dependencies: + assert-plus "1.0.0" + extsprintf "1.0.2" + json-schema "0.2.3" + verror "1.3.6" + +just-extend@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/just-extend/-/just-extend-4.0.2.tgz#f3f47f7dfca0f989c55410a7ebc8854b07108afc" + +kind-of@^3.0.2: + version "3.2.0" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.0.tgz#b58abe4d5c044ad33726a8c1525b48cf891bff07" + dependencies: + is-buffer "^1.1.5" + +lazy-cache@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/lazy-cache/-/lazy-cache-1.0.4.tgz#a1d78fc3a50474cb80845d3b3b6e1da49a446e8e" + +lcid@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/lcid/-/lcid-1.0.0.tgz#308accafa0bc483a3867b4b6f2b9506251d1b835" + dependencies: + invert-kv "^1.0.0" + +levn@^0.3.0, levn@~0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" + dependencies: + prelude-ls "~1.1.2" + type-check "~0.3.2" + +load-json-file@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-2.0.0.tgz#7947e42149af80d696cbf797bcaabcfe1fe29ca8" + dependencies: + graceful-fs "^4.1.2" + parse-json "^2.2.0" + pify "^2.0.0" + strip-bom "^3.0.0" + +loader-fs-cache@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/loader-fs-cache/-/loader-fs-cache-1.0.1.tgz#56e0bf08bd9708b26a765b68509840c8dec9fdbc" + dependencies: + find-cache-dir "^0.1.1" + mkdirp "0.5.1" + +loader-runner@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-2.3.0.tgz#f482aea82d543e07921700d5a46ef26fdac6b8a2" + +loader-utils@^1.0.2, loader-utils@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.1.0.tgz#c98aef488bcceda2ffb5e2de646d6a754429f5cd" + dependencies: + big.js "^3.1.3" + emojis-list "^2.0.0" + json5 "^0.5.0" + +locate-path@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e" + dependencies: + p-locate "^2.0.0" + path-exists "^3.0.0" + +locate-path@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" + dependencies: + p-locate "^5.0.0" + +lodash.get@^4.4.2: + version "4.4.2" + resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99" + +lodash@^4.14.0, lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.15: + version "4.17.20" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52" + +log-symbols@4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503" + dependencies: + chalk "^4.1.0" + is-unicode-supported "^0.1.0" + +longest@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/longest/-/longest-1.0.1.tgz#30a0b2da38f73770e8294a0d22e6625ed77d0097" + +lru-cache@^4.0.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.1.tgz#622e32e82488b49279114a4f9ecf45e7cd6bba55" + dependencies: + pseudomap "^1.0.2" + yallist "^2.1.2" + +lru-cache@^4.1.1: + version "4.1.5" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd" + integrity sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g== + dependencies: + pseudomap "^1.0.2" + yallist "^2.1.2" + +make-dir@^1.0.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-1.3.0.tgz#79c1033b80515bd6d24ec9933e860ca75ee27f0c" + integrity sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ== + dependencies: + pify "^3.0.0" + +mem@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/mem/-/mem-1.1.0.tgz#5edd52b485ca1d900fe64895505399a0dfa45f76" + dependencies: + mimic-fn "^1.0.0" + +memory-fs@^0.4.0, memory-fs@~0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.4.1.tgz#3a9a20b8462523e447cfbc7e8bb80ed667bfc552" + dependencies: + errno "^0.1.3" + readable-stream "^2.0.1" + +micromatch@^2.1.5: + version "2.3.11" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-2.3.11.tgz#86677c97d1720b363431d04d0d15293bd38c1565" + dependencies: + arr-diff "^2.0.0" + array-unique "^0.2.1" + braces "^1.8.2" + expand-brackets "^0.1.4" + extglob "^0.3.1" + filename-regex "^2.0.0" + is-extglob "^1.0.0" + is-glob "^2.0.1" + kind-of "^3.0.2" + normalize-path "^2.0.1" + object.omit "^2.0.0" + parse-glob "^3.0.4" + regex-cache "^0.4.2" + +miller-rabin@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.0.tgz#4a62fb1d42933c05583982f4c716f6fb9e6c6d3d" + dependencies: + bn.js "^4.0.0" + brorand "^1.0.1" + +mime-db@~1.27.0: + version "1.27.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.27.0.tgz#820f572296bbd20ec25ed55e5b5de869e5436eb1" + +mime-types@^2.1.12, mime-types@~2.1.7: + version "2.1.15" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.15.tgz#a4ebf5064094569237b8cf70046776d09fc92aed" + dependencies: + mime-db "~1.27.0" + +mimic-fn@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.1.0.tgz#e667783d92e89dbd342818b5230b9d62a672ad18" + +mimic-fn@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" + +minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" + +minimalistic-crypto-utils@^1.0.0, minimalistic-crypto-utils@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" + +minimatch@3.0.4, minimatch@^3.0.0, minimatch@^3.0.2, minimatch@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" + dependencies: + brace-expansion "^1.1.7" + +minimist@0.0.8: + version "0.0.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" + +minimist@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" + +minimist@^1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" + +mississippi@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/mississippi/-/mississippi-2.0.0.tgz#3442a508fafc28500486feea99409676e4ee5a6f" + integrity sha512-zHo8v+otD1J10j/tC+VNoGK9keCuByhKovAvdn74dmxJl9+mWHnx6EMsDN4lgRoMI/eYo2nchAxniIbUPb5onw== + dependencies: + concat-stream "^1.5.0" + duplexify "^3.4.2" + end-of-stream "^1.1.0" + flush-write-stream "^1.0.0" + from2 "^2.1.0" + parallel-transform "^1.1.0" + pump "^2.0.1" + pumpify "^1.3.3" + stream-each "^1.1.0" + through2 "^2.0.0" + +mkdirp@0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" + dependencies: + minimist "0.0.8" + +"mkdirp@>=0.5 0", mkdirp@^0.5.1, mkdirp@~0.5.0: + version "0.5.5" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" + dependencies: + minimist "^1.2.5" + +mocha@^9.1.1: + version "9.1.1" + resolved "https://registry.yarnpkg.com/mocha/-/mocha-9.1.1.tgz#33df2eb9c6262434630510c5f4283b36efda9b61" + dependencies: + "@ungap/promise-all-settled" "1.1.2" + ansi-colors "4.1.1" + browser-stdout "1.3.1" + chokidar "3.5.2" + debug "4.3.1" + diff "5.0.0" + escape-string-regexp "4.0.0" + find-up "5.0.0" + glob "7.1.7" + growl "1.10.5" + he "1.2.0" + js-yaml "4.1.0" + log-symbols "4.1.0" + minimatch "3.0.4" + ms "2.1.3" + nanoid "3.1.23" + serialize-javascript "6.0.0" + strip-json-comments "3.1.1" + supports-color "8.1.1" + which "2.0.2" + wide-align "1.1.3" + workerpool "6.1.5" + yargs "16.2.0" + yargs-parser "20.2.4" + yargs-unparser "2.0.0" + +move-concurrently@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/move-concurrently/-/move-concurrently-1.0.1.tgz#be2c005fda32e0b29af1f05d7c4b33214c701f92" + integrity sha1-viwAX9oy4LKa8fBdfEszIUxwH5I= + dependencies: + aproba "^1.1.1" + copy-concurrently "^1.0.0" + fs-write-stream-atomic "^1.0.8" + mkdirp "^0.5.1" + rimraf "^2.5.4" + run-queue "^1.0.3" + +ms@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + +ms@2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + +ms@2.1.3: + version "2.1.3" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + +mute-stream@0.0.8: + version "0.0.8" + resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" + +nan@^2.3.0: + version "2.6.2" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.6.2.tgz#e4ff34e6c95fdfb5aecc08de6596f43605a7db45" + +nanoid@3.1.23: + version "3.1.23" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.23.tgz#f744086ce7c2bc47ee0a8472574d5c78e4183a81" + +natural-compare@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" + +nice-try@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.4.tgz#d93962f6c52f2c1558c0fbda6d512819f1efe1c4" + +nise@^4.0.4: + version "4.0.4" + resolved "https://registry.yarnpkg.com/nise/-/nise-4.0.4.tgz#d73dea3e5731e6561992b8f570be9e363c4512dd" + dependencies: + "@sinonjs/commons" "^1.7.0" + "@sinonjs/fake-timers" "^6.0.0" + "@sinonjs/text-encoding" "^0.7.1" + just-extend "^4.0.2" + path-to-regexp "^1.7.0" + +node-libs-browser@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/node-libs-browser/-/node-libs-browser-2.0.0.tgz#a3a59ec97024985b46e958379646f96c4b616646" + dependencies: + assert "^1.1.1" + browserify-zlib "^0.1.4" + buffer "^4.3.0" + console-browserify "^1.1.0" + constants-browserify "^1.0.0" + crypto-browserify "^3.11.0" + domain-browser "^1.1.1" + events "^1.0.0" + https-browserify "0.0.1" + os-browserify "^0.2.0" + path-browserify "0.0.0" + process "^0.11.0" + punycode "^1.2.4" + querystring-es3 "^0.2.0" + readable-stream "^2.0.5" + stream-browserify "^2.0.1" + stream-http "^2.3.1" + string_decoder "^0.10.25" + timers-browserify "^2.0.2" + tty-browserify "0.0.0" + url "^0.11.0" + util "^0.10.3" + vm-browserify "0.0.4" + +node-pre-gyp@^0.6.29: + version "0.6.34" + resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.6.34.tgz#94ad1c798a11d7fc67381b50d47f8cc18d9799f7" + dependencies: + mkdirp "^0.5.1" + nopt "^4.0.1" + npmlog "^4.0.2" + rc "^1.1.7" + request "^2.81.0" + rimraf "^2.6.1" + semver "^5.3.0" + tar "^2.2.1" + tar-pack "^3.4.0" + +nopt@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.1.tgz#d0d4685afd5415193c8c7505602d0d17cd64474d" + dependencies: + abbrev "1" + osenv "^0.1.4" + +normalize-package-data@^2.3.2: + version "2.3.8" + resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.3.8.tgz#d819eda2a9dedbd1ffa563ea4071d936782295bb" + dependencies: + hosted-git-info "^2.1.4" + is-builtin-module "^1.0.0" + semver "2 || 3 || 4 || 5" + validate-npm-package-license "^3.0.1" + +normalize-path@^2.0.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" + dependencies: + remove-trailing-separator "^1.0.1" + +normalize-path@^3.0.0, normalize-path@~3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" + +npm-run-path@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" + dependencies: + path-key "^2.0.0" + +npmlog@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.0.2.tgz#d03950e0e78ce1527ba26d2a7592e9348ac3e75f" + dependencies: + are-we-there-yet "~1.1.2" + console-control-strings "~1.1.0" + gauge "~2.7.1" + set-blocking "~2.0.0" + +number-is-nan@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" + +oauth-sign@~0.8.1: + version "0.8.2" + resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43" + +object-assign@^4.0.1, object-assign@^4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + +object-hash@^1.1.4: + version "1.3.0" + resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-1.3.0.tgz#76d9ba6ff113cf8efc0d996102851fe6723963e2" + +object.omit@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/object.omit/-/object.omit-2.0.1.tgz#1a9c744829f39dbb858c76ca3579ae2a54ebd1fa" + dependencies: + for-own "^0.1.4" + is-extendable "^0.1.1" + +once@^1.3.0, once@^1.3.1, once@^1.3.3, once@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + dependencies: + wrappy "1" + +onetime@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.0.tgz#fff0f3c91617fe62bb50189636e99ac8a6df7be5" + dependencies: + mimic-fn "^2.1.0" + +optionator@^0.8.3: + version "0.8.3" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" + dependencies: + deep-is "~0.1.3" + fast-levenshtein "~2.0.6" + levn "~0.3.0" + prelude-ls "~1.1.2" + type-check "~0.3.2" + word-wrap "~1.2.3" + +os-browserify@^0.2.0: + version "0.2.1" + resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.2.1.tgz#63fc4ccee5d2d7763d26bbf8601078e6c2e0044f" + +os-homedir@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" + +os-locale@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-2.1.0.tgz#42bc2900a6b5b8bd17376c8e882b65afccf24bf2" + dependencies: + execa "^0.7.0" + lcid "^1.0.0" + mem "^1.1.0" + +os-tmpdir@^1.0.0, os-tmpdir@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" + +osenv@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.4.tgz#42fe6d5953df06c8064be6f176c3d05aaaa34644" + dependencies: + os-homedir "^1.0.0" + os-tmpdir "^1.0.0" + +p-finally@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" + +p-limit@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.1.0.tgz#b07ff2d9a5d88bec806035895a2bab66a27988bc" + +p-limit@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.0.2.tgz#1664e010af3cadc681baafd3e2a437be7b0fb5fe" + dependencies: + p-try "^2.0.0" + +p-locate@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43" + dependencies: + p-limit "^1.1.0" + +p-locate@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" + dependencies: + p-limit "^3.0.2" + +p-try@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" + +pako@~0.2.0: + version "0.2.9" + resolved "https://registry.yarnpkg.com/pako/-/pako-0.2.9.tgz#f3f7522f4ef782348da8161bad9ecfd51bf83a75" + +parallel-transform@^1.1.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/parallel-transform/-/parallel-transform-1.2.0.tgz#9049ca37d6cb2182c3b1d2c720be94d14a5814fc" + integrity sha512-P2vSmIu38uIlvdcU7fDkyrxj33gTUy/ABO5ZUbGowxNCopBq/OoD42bP4UmMrJoPyk4Uqf0mu3mtWBhHCZD8yg== + dependencies: + cyclist "^1.0.1" + inherits "^2.0.3" + readable-stream "^2.1.5" + +parent-module@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.0.tgz#df250bdc5391f4a085fb589dad761f5ad6b865b5" + dependencies: + callsites "^3.0.0" + +parse-asn1@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.0.tgz#37c4f9b7ed3ab65c74817b5f2480937fbf97c712" + dependencies: + asn1.js "^4.0.0" + browserify-aes "^1.0.0" + create-hash "^1.1.0" + evp_bytestokey "^1.0.0" + pbkdf2 "^3.0.3" + +parse-glob@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/parse-glob/-/parse-glob-3.0.4.tgz#b2c376cfb11f35513badd173ef0bb6e3a388391c" + dependencies: + glob-base "^0.3.0" + is-dotfile "^1.0.0" + is-extglob "^1.0.0" + is-glob "^2.0.0" + +parse-json@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9" + dependencies: + error-ex "^1.2.0" + +path-browserify@0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-0.0.0.tgz#a0b870729aae214005b7d5032ec2cbbb0fb4451a" + +path-exists@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b" + dependencies: + pinkie-promise "^2.0.0" + +path-exists@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" + +path-exists@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + +path-key@^2.0.0, path-key@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" + +path-to-regexp@^1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-1.7.0.tgz#59fde0f435badacba103a84e9d3bc64e96b9937d" + dependencies: + isarray "0.0.1" + +path-type@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-2.0.0.tgz#f012ccb8415b7096fc2daa1054c3d72389594c73" + dependencies: + pify "^2.0.0" + +pathval@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/pathval/-/pathval-1.1.1.tgz#8534e77a77ce7ac5a2512ea21e0fdb8fcf6c3d8d" + +pbkdf2@^3.0.3: + version "3.0.9" + resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.0.9.tgz#f2c4b25a600058b3c3773c086c37dbbee1ffe693" + dependencies: + create-hmac "^1.1.2" + +performance-now@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-0.2.0.tgz#33ef30c5c77d4ea21c5a53869d91b56d8f2555e5" + +picomatch@^2.0.4, picomatch@^2.2.1: + version "2.2.2" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.2.tgz#21f333e9b6b8eaff02468f5146ea406d345f4dad" + +pify@^2.0.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" + +pify@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" + integrity sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY= + +pinkie-promise@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" + dependencies: + pinkie "^2.0.0" + +pinkie@^2.0.0: + version "2.0.4" + resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" + +pkg-dir@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-1.0.0.tgz#7a4b508a8d5bb2d629d447056ff4e9c9314cf3d4" + dependencies: + find-up "^1.0.0" + +pkg-dir@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-2.0.0.tgz#f6d5d1109e19d63edf428e0bd57e12777615334b" + integrity sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s= + dependencies: + find-up "^2.1.0" + +prelude-ls@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" + +preserve@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b" + +prettier-check@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/prettier-check/-/prettier-check-2.0.0.tgz#edd086ee12d270579233ccb136a16e6afcfba1ae" + dependencies: + execa "^0.6.0" + +prettier@^2.0.5: + version "2.2.1" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.2.1.tgz#795a1a78dd52f073da0cd42b21f9c91381923ff5" + +process-nextick-args@~1.0.6: + version "1.0.7" + resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-1.0.7.tgz#150e20b756590ad3f91093f25a4f2ad8bff30ba3" + +process-nextick-args@~2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" + integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== + +process@^0.11.0: + version "0.11.10" + resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" + +progress@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.0.tgz#8a1be366bf8fc23db2bd23f10c6fe920b4389d1f" + +promise-inflight@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3" + integrity sha1-mEcocL8igTL8vdhoEputEsPAKeM= + +prr@~0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/prr/-/prr-0.0.0.tgz#1a84b85908325501411853d0081ee3fa86e2926a" + +prr@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476" + integrity sha1-0/wRS6BplaRexok/SEzrHXj19HY= + +pseudomap@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" + +public-encrypt@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/public-encrypt/-/public-encrypt-4.0.0.tgz#39f699f3a46560dd5ebacbca693caf7c65c18cc6" + dependencies: + bn.js "^4.1.0" + browserify-rsa "^4.0.0" + create-hash "^1.1.0" + parse-asn1 "^5.0.0" + randombytes "^2.0.1" + +pump@^2.0.0, pump@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/pump/-/pump-2.0.1.tgz#12399add6e4cf7526d973cbc8b5ce2e2908b3909" + integrity sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA== + dependencies: + end-of-stream "^1.1.0" + once "^1.3.1" + +pumpify@^1.3.3: + version "1.5.1" + resolved "https://registry.yarnpkg.com/pumpify/-/pumpify-1.5.1.tgz#36513be246ab27570b1a374a5ce278bfd74370ce" + integrity sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ== + dependencies: + duplexify "^3.6.0" + inherits "^2.0.3" + pump "^2.0.0" + +punycode@1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" + +punycode@^1.2.4, punycode@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" + +punycode@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" + +qs@~6.4.0: + version "6.4.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.4.0.tgz#13e26d28ad6b0ffaa91312cd3bf708ed351e7233" + +querystring-es3@^0.2.0: + version "0.2.1" + resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73" + +querystring@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620" + +randomatic@^1.1.3: + version "1.1.6" + resolved "https://registry.yarnpkg.com/randomatic/-/randomatic-1.1.6.tgz#110dcabff397e9dcff7c0789ccc0a49adf1ec5bb" + dependencies: + is-number "^2.0.2" + kind-of "^3.0.2" + +randombytes@^2.0.0, randombytes@^2.0.1: + version "2.0.3" + resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.0.3.tgz#674c99760901c3c4112771a31e521dc349cc09ec" + +randombytes@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" + dependencies: + safe-buffer "^5.1.0" + +rc@^1.1.7: + version "1.2.1" + resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.1.tgz#2e03e8e42ee450b8cb3dce65be1bf8974e1dfd95" + dependencies: + deep-extend "~0.4.0" + ini "~1.3.0" + minimist "^1.2.0" + strip-json-comments "~2.0.1" + +read-pkg-up@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-2.0.0.tgz#6b72a8048984e0c41e79510fd5e9fa99b3b549be" + dependencies: + find-up "^2.0.0" + read-pkg "^2.0.0" + +read-pkg@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-2.0.0.tgz#8ef1c0623c6a6db0dc6713c4bfac46332b2368f8" + dependencies: + load-json-file "^2.0.0" + normalize-package-data "^2.3.2" + path-type "^2.0.0" + +"readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.6, readable-stream@~2.3.6: + version "2.3.7" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" + integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.3" + isarray "~1.0.0" + process-nextick-args "~2.0.0" + safe-buffer "~5.1.1" + string_decoder "~1.1.1" + util-deprecate "~1.0.1" + +readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.5, readable-stream@^2.0.6, readable-stream@^2.1.4, readable-stream@^2.2.6: + version "2.2.9" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.2.9.tgz#cf78ec6f4a6d1eb43d26488cac97f042e74b7fc8" + dependencies: + buffer-shims "~1.0.0" + core-util-is "~1.0.0" + inherits "~2.0.1" + isarray "~1.0.0" + process-nextick-args "~1.0.6" + string_decoder "~1.0.0" + util-deprecate "~1.0.1" + +readdirp@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.1.0.tgz#4ed0ad060df3073300c48440373f72d1cc642d78" + dependencies: + graceful-fs "^4.1.2" + minimatch "^3.0.2" + readable-stream "^2.0.2" + set-immediate-shim "^1.0.1" + +readdirp@~3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" + dependencies: + picomatch "^2.2.1" + +regex-cache@^0.4.2: + version "0.4.3" + resolved "https://registry.yarnpkg.com/regex-cache/-/regex-cache-0.4.3.tgz#9b1a6c35d4d0dfcef5711ae651e8e9d3d7114145" + dependencies: + is-equal-shallow "^0.1.3" + is-primitive "^2.0.0" + +regexpp@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-2.0.1.tgz#8d19d31cf632482b589049f8281f93dbcba4d07f" + +remove-trailing-separator@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.0.1.tgz#615ebb96af559552d4bf4057c8436d486ab63cc4" + +repeat-element@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.2.tgz#ef089a178d1483baae4d93eb98b4f9e4e11d990a" + +repeat-string@^1.5.2: + version "1.6.1" + resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" + +request@^2.81.0: + version "2.81.0" + resolved "https://registry.yarnpkg.com/request/-/request-2.81.0.tgz#c6928946a0e06c5f8d6f8a9333469ffda46298a0" + dependencies: + aws-sign2 "~0.6.0" + aws4 "^1.2.1" + caseless "~0.12.0" + combined-stream "~1.0.5" + extend "~3.0.0" + forever-agent "~0.6.1" + form-data "~2.1.1" + har-validator "~4.2.1" + hawk "~3.1.3" + http-signature "~1.1.0" + is-typedarray "~1.0.0" + isstream "~0.1.2" + json-stringify-safe "~5.0.1" + mime-types "~2.1.7" + oauth-sign "~0.8.1" + performance-now "^0.2.0" + qs "~6.4.0" + safe-buffer "^5.0.1" + stringstream "~0.0.4" + tough-cookie "~2.3.0" + tunnel-agent "^0.6.0" + uuid "^3.0.0" + +require-directory@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + +require-main-filename@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1" + +resolve-from@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" + +restore-cursor@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e" + dependencies: + onetime "^5.1.0" + signal-exit "^3.0.2" + +right-align@^0.1.1: + version "0.1.3" + resolved "https://registry.yarnpkg.com/right-align/-/right-align-0.1.3.tgz#61339b722fe6a3515689210d24e14c96148613ef" + dependencies: + align-text "^0.1.1" + +rimraf@2, rimraf@2.6.3, rimraf@^2.5.1, rimraf@^2.6.1: + version "2.6.3" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab" + dependencies: + glob "^7.1.3" + +rimraf@^2.5.4, rimraf@^2.6.2: + version "2.7.1" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" + integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== + dependencies: + glob "^7.1.3" + +ripemd160@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-1.0.1.tgz#93a4bbd4942bc574b69a8fa57c71de10ecca7d6e" + +run-async@^2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.0.tgz#e59054a5b86876cfae07f431d18cbaddc594f1e8" + dependencies: + is-promise "^2.1.0" + +run-queue@^1.0.0, run-queue@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/run-queue/-/run-queue-1.0.3.tgz#e848396f057d223f24386924618e25694161ec47" + integrity sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec= + dependencies: + aproba "^1.1.1" + +rxjs@^6.5.3: + version "6.5.5" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.5.5.tgz#c5c884e3094c8cfee31bf27eb87e54ccfc87f9ec" + dependencies: + tslib "^1.9.0" + +safe-buffer@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.0.1.tgz#d263ca54696cd8a306b5ca6551e92de57918fbe7" + +safe-buffer@^5.1.0, safe-buffer@^5.1.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + +safe-buffer@~5.1.0, safe-buffer@~5.1.1: + version "5.1.2" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== + +"safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.0.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + +schema-utils@^0.4.5: + version "0.4.7" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-0.4.7.tgz#ba74f597d2be2ea880131746ee17d0a093c68187" + integrity sha512-v/iwU6wvwGK8HbU9yi3/nhGzP0yGSuhQMzL6ySiec1FSrZZDkhm4noOSWzrNFo/jEc+SJY6jRTwuwbSXJPDUnQ== + dependencies: + ajv "^6.1.0" + ajv-keywords "^3.1.0" + +"semver@2 || 3 || 4 || 5", semver@^5.3.0, semver@^5.5.0: + version "5.7.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" + +semver@^6.1.2: + version "6.3.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" + +serialize-javascript@6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.0.tgz#efae5d88f45d7924141da8b5c3a7a7e663fefeb8" + dependencies: + randombytes "^2.1.0" + +serialize-javascript@^1.4.0: + version "1.9.1" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-1.9.1.tgz#cfc200aef77b600c47da9bb8149c943e798c2fdb" + integrity sha512-0Vb/54WJ6k5v8sSWN09S0ora+Hnr+cX40r9F170nT+mSkaxltoE/7R3OrIdBSUv1OoiobH1QoWQbCnAO+e8J1A== + +set-blocking@^2.0.0, set-blocking@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" + +set-immediate-shim@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz#4b2b1b27eb808a9f8dcc481a58e5e56f599f3f61" + +setimmediate@^1.0.4: + version "1.0.5" + resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" + +sha.js@^2.3.6: + version "2.4.8" + resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.8.tgz#37068c2c476b6baf402d14a49c67f597921f634f" + dependencies: + inherits "^2.0.1" + +shebang-command@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" + dependencies: + shebang-regex "^1.0.0" + +shebang-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" + +signal-exit@^3.0.0, signal-exit@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" + +sinon@^9.0.1: + version "9.2.2" + resolved "https://registry.yarnpkg.com/sinon/-/sinon-9.2.2.tgz#b83cf5d43838f99cfa3644453f4c7db23e7bd535" + dependencies: + "@sinonjs/commons" "^1.8.1" + "@sinonjs/fake-timers" "^6.0.1" + "@sinonjs/formatio" "^5.0.1" + "@sinonjs/samsam" "^5.3.0" + diff "^4.0.2" + nise "^4.0.4" + supports-color "^7.1.0" + +slice-ansi@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-2.1.0.tgz#cacd7693461a637a5788d92a7dd4fba068e81636" + dependencies: + ansi-styles "^3.2.0" + astral-regex "^1.0.0" + is-fullwidth-code-point "^2.0.0" + +sntp@1.x.x: + version "1.0.9" + resolved "https://registry.yarnpkg.com/sntp/-/sntp-1.0.9.tgz#6541184cc90aeea6c6e7b35e2659082443c66198" + dependencies: + hoek "2.x.x" + +source-list-map@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.0.tgz#aaa47403f7b245a92fbc97ea08f250d6087ed085" + +source-map@^0.5.3, source-map@^0.5.6, source-map@~0.5.1, source-map@~0.5.3: + version "0.5.7" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" + +source-map@^0.6.1, source-map@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + +spdx-correct@~1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-1.0.2.tgz#4b3073d933ff51f3912f03ac5519498a4150db40" + dependencies: + spdx-license-ids "^1.0.2" + +spdx-expression-parse@~1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-1.0.4.tgz#9bdf2f20e1f40ed447fbe273266191fced51626c" + +spdx-license-ids@^1.0.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-1.2.2.tgz#c9df7a3424594ade6bd11900d596696dc06bac57" + +sprintf-js@~1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + +sshpk@^1.7.0: + version "1.15.2" + resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.15.2.tgz#c946d6bd9b1a39d0e8635763f5242d6ed6dcb629" + dependencies: + asn1 "~0.2.3" + assert-plus "^1.0.0" + bcrypt-pbkdf "^1.0.0" + dashdash "^1.12.0" + ecc-jsbn "~0.1.1" + getpass "^0.1.1" + jsbn "~0.1.0" + safer-buffer "^2.0.2" + tweetnacl "~0.14.0" + +ssri@^5.2.4: + version "5.3.0" + resolved "https://registry.yarnpkg.com/ssri/-/ssri-5.3.0.tgz#ba3872c9c6d33a0704a7d71ff045e5ec48999d06" + integrity sha512-XRSIPqLij52MtgoQavH/x/dU1qVKtWUAAZeOHsR9c2Ddi4XerFy3mc1alf+dLJKl9EUIm/Ht+EowFkTUOA6GAQ== + dependencies: + safe-buffer "^5.1.1" + +stream-browserify@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-2.0.1.tgz#66266ee5f9bdb9940a4e4514cafb43bb71e5c9db" + dependencies: + inherits "~2.0.1" + readable-stream "^2.0.2" + +stream-each@^1.1.0: + version "1.2.3" + resolved "https://registry.yarnpkg.com/stream-each/-/stream-each-1.2.3.tgz#ebe27a0c389b04fbcc233642952e10731afa9bae" + integrity sha512-vlMC2f8I2u/bZGqkdfLQW/13Zihpej/7PmSiMQsbYddxuTsJp8vRe2x2FvVExZg7FaOds43ROAuFJwPR4MTZLw== + dependencies: + end-of-stream "^1.1.0" + stream-shift "^1.0.0" + +stream-http@^2.3.1: + version "2.7.0" + resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-2.7.0.tgz#cec1f4e3b494bc4a81b451808970f8b20b4ed5f6" + dependencies: + builtin-status-codes "^3.0.0" + inherits "^2.0.1" + readable-stream "^2.2.6" + to-arraybuffer "^1.0.0" + xtend "^4.0.0" + +stream-shift@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.1.tgz#d7088281559ab2778424279b0877da3c392d5a3d" + integrity sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ== + +string-width@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" + dependencies: + code-point-at "^1.0.0" + is-fullwidth-code-point "^1.0.0" + strip-ansi "^3.0.0" + +"string-width@^1.0.2 || 2", string-width@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" + dependencies: + is-fullwidth-code-point "^2.0.0" + strip-ansi "^4.0.0" + +string-width@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961" + dependencies: + emoji-regex "^7.0.1" + is-fullwidth-code-point "^2.0.0" + strip-ansi "^5.1.0" + +string-width@^4.1.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.0.tgz#952182c46cc7b2c313d1596e623992bd163b72b5" + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.0" + +string-width@^4.2.0: + version "4.2.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.2.tgz#dafd4f9559a7585cfba529c6a0a4f73488ebd4c5" + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.0" + +string_decoder@^0.10.25: + version "0.10.31" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" + +string_decoder@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.0.0.tgz#f06f41157b664d86069f84bdbdc9b0d8ab281667" + dependencies: + buffer-shims "~1.0.0" + +string_decoder@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" + integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== + dependencies: + safe-buffer "~5.1.0" + +stringstream@~0.0.4: + version "0.0.6" + resolved "https://registry.yarnpkg.com/stringstream/-/stringstream-0.0.6.tgz#7880225b0d4ad10e30927d167a1d6f2fd3b33a72" + +strip-ansi@^3.0.0, strip-ansi@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" + dependencies: + ansi-regex "^2.0.0" + +strip-ansi@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" + dependencies: + ansi-regex "^3.0.0" + +strip-ansi@^5.1.0, strip-ansi@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" + dependencies: + ansi-regex "^4.1.0" + +strip-ansi@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.0.tgz#0b1571dd7669ccd4f3e06e14ef1eed26225ae532" + dependencies: + ansi-regex "^5.0.0" + +strip-bom@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" + +strip-eof@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" + +strip-json-comments@3.1.1, strip-json-comments@^3.0.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" + +strip-json-comments@~2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" + +supports-color@8.1.1: + version "8.1.1" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" + dependencies: + has-flag "^4.0.0" + +supports-color@^4.2.1: + version "4.4.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-4.4.0.tgz#883f7ddabc165142b2a61427f3352ded195d1a3e" + dependencies: + has-flag "^2.0.0" + +supports-color@^5.3.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" + dependencies: + has-flag "^3.0.0" + +supports-color@^7.1.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + dependencies: + has-flag "^4.0.0" + +table@^5.2.3: + version "5.4.1" + resolved "https://registry.yarnpkg.com/table/-/table-5.4.1.tgz#0691ae2ebe8259858efb63e550b6d5f9300171e8" + dependencies: + ajv "^6.9.1" + lodash "^4.17.11" + slice-ansi "^2.1.0" + string-width "^3.0.0" + +tapable@^0.2.7: + version "0.2.8" + resolved "https://registry.yarnpkg.com/tapable/-/tapable-0.2.8.tgz#99372a5c999bf2df160afc0d74bed4f47948cd22" + +tar-pack@^3.4.0: + version "3.4.0" + resolved "https://registry.yarnpkg.com/tar-pack/-/tar-pack-3.4.0.tgz#23be2d7f671a8339376cbdb0b8fe3fdebf317984" + dependencies: + debug "^2.2.0" + fstream "^1.0.10" + fstream-ignore "^1.0.5" + once "^1.3.3" + readable-stream "^2.1.4" + rimraf "^2.5.1" + tar "^2.2.1" + uid-number "^0.0.6" + +tar@^2.2.1: + version "2.2.2" + resolved "https://registry.yarnpkg.com/tar/-/tar-2.2.2.tgz#0ca8848562c7299b8b446ff6a4d60cdbb23edc40" + dependencies: + block-stream "*" + fstream "^1.0.12" + inherits "2" + +text-table@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" + +through2@^2.0.0: + version "2.0.5" + resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd" + integrity sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ== + dependencies: + readable-stream "~2.3.6" + xtend "~4.0.1" + +through@^2.3.6: + version "2.3.8" + resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" + +timers-browserify@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-2.0.2.tgz#ab4883cf597dcd50af211349a00fbca56ac86b86" + dependencies: + setimmediate "^1.0.4" + +tmp@^0.0.33: + version "0.0.33" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" + dependencies: + os-tmpdir "~1.0.2" + +to-arraybuffer@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz#7d229b1fcc637e466ca081180836a7aabff83f43" + +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + dependencies: + is-number "^7.0.0" + +tough-cookie@~2.3.0: + version "2.3.4" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.3.4.tgz#ec60cee38ac675063ffc97a5c18970578ee83655" + dependencies: + punycode "^1.4.1" + +tslib@^1.9.0: + version "1.9.3" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.3.tgz#d7e4dd79245d85428c4d7e4822a79917954ca286" + +tty-browserify@0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d524eed42901a6" + +tunnel-agent@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" + dependencies: + safe-buffer "^5.0.1" + +tweetnacl@^0.14.3, tweetnacl@~0.14.0: + version "0.14.5" + resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" + +type-check@~0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" + dependencies: + prelude-ls "~1.1.2" + +type-detect@4.0.8, type-detect@^4.0.0, type-detect@^4.0.5, type-detect@^4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" + +type-fest@^0.11.0: + version "0.11.0" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.11.0.tgz#97abf0872310fed88a5c466b25681576145e33f1" + +type-fest@^0.8.1: + version "0.8.1" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" + +typedarray@^0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" + integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= + +uglify-es@^3.3.4: + version "3.3.9" + resolved "https://registry.yarnpkg.com/uglify-es/-/uglify-es-3.3.9.tgz#0c1c4f0700bed8dbc124cdb304d2592ca203e677" + integrity sha512-r+MU0rfv4L/0eeW3xZrd16t4NZfK8Ld4SWVglYBb7ez5uXFWHuVRs6xCTrf1yirs9a4j4Y27nn7SRfO6v67XsQ== + dependencies: + commander "~2.13.0" + source-map "~0.6.1" + +uglify-js@^2.8.29: + version "2.8.29" + resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-2.8.29.tgz#29c5733148057bb4e1f75df35b7a9cb72e6a59dd" + dependencies: + source-map "~0.5.1" + yargs "~3.10.0" + optionalDependencies: + uglify-to-browserify "~1.0.0" + +uglify-to-browserify@~1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz#6e0924d6bda6b5afe349e39a6d632850a0f882b7" + +uglifyjs-webpack-plugin@^0.4.6: + version "0.4.6" + resolved "https://registry.yarnpkg.com/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-0.4.6.tgz#b951f4abb6bd617e66f63eb891498e391763e309" + dependencies: + source-map "^0.5.6" + uglify-js "^2.8.29" + webpack-sources "^1.0.1" + +uglifyjs-webpack-plugin@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-1.3.0.tgz#75f548160858163a08643e086d5fefe18a5d67de" + integrity sha512-ovHIch0AMlxjD/97j9AYovZxG5wnHOPkL7T1GKochBADp/Zwc44pEWNqpKl1Loupp1WhFg7SlYmHZRUfdAacgw== + dependencies: + cacache "^10.0.4" + find-cache-dir "^1.0.0" + schema-utils "^0.4.5" + serialize-javascript "^1.4.0" + source-map "^0.6.1" + uglify-es "^3.3.4" + webpack-sources "^1.1.0" + worker-farm "^1.5.2" + +uid-number@^0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/uid-number/-/uid-number-0.0.6.tgz#0ea10e8035e8eb5b8e4449f06da1c730663baa81" + +unique-filename@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-1.1.1.tgz#1d69769369ada0583103a1e6ae87681b56573230" + integrity sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ== + dependencies: + unique-slug "^2.0.0" + +unique-slug@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/unique-slug/-/unique-slug-2.0.2.tgz#baabce91083fc64e945b0f3ad613e264f7cd4e6c" + integrity sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w== + dependencies: + imurmurhash "^0.1.4" + +uri-js@^4.2.2: + version "4.2.2" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0" + dependencies: + punycode "^2.1.0" + +url@^0.11.0: + version "0.11.0" + resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1" + dependencies: + punycode "1.3.2" + querystring "0.2.0" + +util-deprecate@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + +util@0.10.3, util@^0.10.3: + version "0.10.3" + resolved "https://registry.yarnpkg.com/util/-/util-0.10.3.tgz#7afb1afe50805246489e3db7fe0ed379336ac0f9" + dependencies: + inherits "2.0.1" + +uuid@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.0.1.tgz#6544bba2dfda8c1cf17e629a3a305e2bb1fee6c1" + +v8-compile-cache@^2.0.3: + version "2.1.0" + resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.1.0.tgz#e14de37b31a6d194f5690d67efc4e7f6fc6ab30e" + +validate-npm-package-license@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.1.tgz#2804babe712ad3379459acfbe24746ab2c303fbc" + dependencies: + spdx-correct "~1.0.0" + spdx-expression-parse "~1.0.0" + +verror@1.3.6: + version "1.3.6" + resolved "https://registry.yarnpkg.com/verror/-/verror-1.3.6.tgz#cff5df12946d297d2baaefaa2689e25be01c005c" + dependencies: + extsprintf "1.0.2" + +vm-browserify@0.0.4: + version "0.0.4" + resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-0.0.4.tgz#5d7ea45bbef9e4a6ff65f95438e0a87c357d5a73" + dependencies: + indexof "0.0.1" + +watchpack@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.4.0.tgz#4a1472bcbb952bd0a9bb4036801f954dfb39faac" + dependencies: + async "^2.1.2" + chokidar "^1.7.0" + graceful-fs "^4.1.2" + +webpack-sources@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.0.1.tgz#c7356436a4d13123be2e2426a05d1dad9cbe65cf" + dependencies: + source-list-map "^2.0.0" + source-map "~0.5.3" + +webpack-sources@^1.1.0: + version "1.4.3" + resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.4.3.tgz#eedd8ec0b928fbf1cbfe994e22d2d890f330a933" + integrity sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ== + dependencies: + source-list-map "^2.0.0" + source-map "~0.6.1" + +webpack@^3.9.1: + version "3.12.0" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-3.12.0.tgz#3f9e34360370602fcf639e97939db486f4ec0d74" + dependencies: + acorn "^5.0.0" + acorn-dynamic-import "^2.0.0" + ajv "^6.1.0" + ajv-keywords "^3.1.0" + async "^2.1.2" + enhanced-resolve "^3.4.0" + escope "^3.6.0" + interpret "^1.0.0" + json-loader "^0.5.4" + json5 "^0.5.1" + loader-runner "^2.3.0" + loader-utils "^1.1.0" + memory-fs "~0.4.1" + mkdirp "~0.5.0" + node-libs-browser "^2.0.0" + source-map "^0.5.3" + supports-color "^4.2.1" + tapable "^0.2.7" + uglifyjs-webpack-plugin "^0.4.6" + watchpack "^1.4.0" + webpack-sources "^1.0.1" + yargs "^8.0.2" + +which-module@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" + +which@2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + dependencies: + isexe "^2.0.0" + +which@^1.2.9: + version "1.3.1" + resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" + dependencies: + isexe "^2.0.0" + +wide-align@1.1.3, wide-align@^1.1.0: + version "1.1.3" + resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457" + dependencies: + string-width "^1.0.2 || 2" + +window-size@0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.1.0.tgz#5438cd2ea93b202efa3a19fe8887aee7c94f9c9d" + +word-wrap@~1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" + +wordwrap@0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.2.tgz#b79669bb42ecb409f83d583cad52ca17eaa1643f" + +worker-farm@^1.5.2: + version "1.7.0" + resolved "https://registry.yarnpkg.com/worker-farm/-/worker-farm-1.7.0.tgz#26a94c5391bbca926152002f69b84a4bf772e5a8" + integrity sha512-rvw3QTZc8lAxyVrqcSGVm5yP/IJ2UcB3U0graE3LCFoZ0Yn2x4EoVSqJKdB/T5M+FLcRPjz4TDacRf3OCfNUzw== + dependencies: + errno "~0.1.7" + +workerpool@6.1.5: + version "6.1.5" + resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.1.5.tgz#0f7cf076b6215fd7e1da903ff6f22ddd1886b581" + +wrap-ansi@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85" + dependencies: + string-width "^1.0.1" + strip-ansi "^3.0.1" + +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + +write@1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/write/-/write-1.0.3.tgz#0800e14523b923a387e415123c865616aae0f5c3" + dependencies: + mkdirp "^0.5.1" + +xtend@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af" + +xtend@~4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" + integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== + +y18n@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.1.tgz#6d15fba884c08679c0d77e88e7759e811e07fa41" + +y18n@^4.0.0: + version "4.0.3" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.3.tgz#b5f259c82cd6e336921efd7bfd8bf560de9eeedf" + integrity sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ== + +y18n@^5.0.5: + version "5.0.8" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" + +yallist@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" + +yargs-parser@20.2.4: + version "20.2.4" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.4.tgz#b42890f14566796f85ae8e3a25290d205f154a54" + +yargs-parser@^20.2.2: + version "20.2.9" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" + +yargs-parser@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-7.0.0.tgz#8d0ac42f16ea55debd332caf4c4038b3e3f5dfd9" + dependencies: + camelcase "^4.1.0" + +yargs-unparser@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/yargs-unparser/-/yargs-unparser-2.0.0.tgz#f131f9226911ae5d9ad38c432fe809366c2325eb" + dependencies: + camelcase "^6.0.0" + decamelize "^4.0.0" + flat "^5.0.2" + is-plain-obj "^2.1.0" + +yargs@16.2.0: + version "16.2.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" + dependencies: + cliui "^7.0.2" + escalade "^3.1.1" + get-caller-file "^2.0.5" + require-directory "^2.1.1" + string-width "^4.2.0" + y18n "^5.0.5" + yargs-parser "^20.2.2" + +yargs@^8.0.2: + version "8.0.2" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-8.0.2.tgz#6299a9055b1cefc969ff7e79c1d918dceb22c360" + dependencies: + camelcase "^4.1.0" + cliui "^3.2.0" + decamelize "^1.1.1" + get-caller-file "^1.0.1" + os-locale "^2.0.0" + read-pkg-up "^2.0.0" + require-directory "^2.1.1" + require-main-filename "^1.0.1" + set-blocking "^2.0.0" + string-width "^2.0.0" + which-module "^2.0.0" + y18n "^3.2.1" + yargs-parser "^7.0.0" + +yargs@~3.10.0: + version "3.10.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-3.10.0.tgz#f7ee7bd857dd7c1d2d38c0e74efbd681d1431fd1" + dependencies: + camelcase "^1.0.2" + cliui "^2.1.0" + decamelize "^1.0.0" + window-size "0.1.0"