├── _config.yml
├── 5.nes
├── 1943.nes
├── Kage.nes
├── croom.nes
├── lj65.nes
├── Jackal.nes
├── (J) Pooyan.nes
├── Golf (JU).nes
├── RockinCats.nes
├── TaoJinZhe.nes
├── (J) Mappy [!].nes
├── Contra1(U)30.nes
├── Contra1(U)30F.nes
├── Contra1(U)30L.nes
├── Contra1(U)30M.nes
├── Contra1(U)30S.nes
├── Contra1(U)F.nes
├── Contra1(U)L.nes
├── Contra1(U)M.nes
├── Contra1(U)S.nes
├── Ninja_Gaiden1.nes
├── Ninja_Gaiden2.nes
├── Ninja_Gaiden3.nes
├── Side Pocket.nes
├── dynamicaudio.swf
├── portrait_mode.png
├── (Ch) Tank 1990.nes
├── (J) Battle City.nes
├── (J) Dig Dug [!].nes
├── (J) Ice Climber.nes
├── (J) TwinBee [!].nes
├── Cross Fire (J).nes
├── Double Dragon1.nes
├── Double Dragon2.nes
├── Double Dragon3.nes
├── Double Dragon4.nes
├── Dr. Mario (JU).nes
├── Life Force [!].nes
├── Rainbow Islands.nes
├── Tennis (JU) [!].nes
├── Tetris (U) [!].nes
├── landscape_mode.png
├── (Ch) Missile Tank.nes
├── (J) Arkanoid [!].nes
├── (J) Bomberman [!].nes
├── (J) F-1 Race [!].nes
├── (J) Galaxian [!].nes
├── (J) Spartan X [!].nes
├── (J) Spelunker [!].nes
├── Bubble Bobble (U).nes
├── Mighty Final Fight.nes
├── Shufflepuck Cafe.nes
├── Tetris 2 (U) [!].nes
├── Zhong Guo Xiang Qi.nes
├── (Hacker) AV Mahjongg.nes
├── (J) Goonies, The [!].nes
├── (J) Road Fighter [!].nes
├── (JU) Excitebike [!].nes
├── (Tengen) Tetris [!].nes
├── (J) Circus Charlie [!].nes
├── (W) Wrecking Crew [!].nes
├── (J) Kage no Densetsu [!].nes
├── (W) Super Mario Bros. [!].nes
├── (J) Antarctic Adventure [!].nes
├── (JU) (PRG0) Mach Rider [!].nes
├── Championship Lode Runner (J).nes
├── (J) (V1.2) Yie Ar Kung-Fu [!].nes
├── Super Mario Bros. 3 (U) (PRG1) [!].nes
├── Zelda II - The Adventure of Link (U).nes
├── (J) Takahashi Meijin no Bouken Shima [!].nes
├── jsnes-ie-hacks.vbscript
├── README.md
├── utils.js
├── debug.js
├── keyboard.js
├── nes.js
├── rom.js
├── jsnes.css
├── index.html
├── dynamicaudio-min.js
├── ui.js
└── mappers.js
/_config.yml:
--------------------------------------------------------------------------------
1 | theme: jekyll-theme-slate
--------------------------------------------------------------------------------
/5.nes:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icai/fcgame/master/5.nes
--------------------------------------------------------------------------------
/1943.nes:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icai/fcgame/master/1943.nes
--------------------------------------------------------------------------------
/Kage.nes:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icai/fcgame/master/Kage.nes
--------------------------------------------------------------------------------
/croom.nes:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icai/fcgame/master/croom.nes
--------------------------------------------------------------------------------
/lj65.nes:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icai/fcgame/master/lj65.nes
--------------------------------------------------------------------------------
/Jackal.nes:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icai/fcgame/master/Jackal.nes
--------------------------------------------------------------------------------
/(J) Pooyan.nes:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icai/fcgame/master/(J) Pooyan.nes
--------------------------------------------------------------------------------
/Golf (JU).nes:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icai/fcgame/master/Golf (JU).nes
--------------------------------------------------------------------------------
/RockinCats.nes:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icai/fcgame/master/RockinCats.nes
--------------------------------------------------------------------------------
/TaoJinZhe.nes:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icai/fcgame/master/TaoJinZhe.nes
--------------------------------------------------------------------------------
/(J) Mappy [!].nes:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icai/fcgame/master/(J) Mappy [!].nes
--------------------------------------------------------------------------------
/Contra1(U)30.nes:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icai/fcgame/master/Contra1(U)30.nes
--------------------------------------------------------------------------------
/Contra1(U)30F.nes:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icai/fcgame/master/Contra1(U)30F.nes
--------------------------------------------------------------------------------
/Contra1(U)30L.nes:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icai/fcgame/master/Contra1(U)30L.nes
--------------------------------------------------------------------------------
/Contra1(U)30M.nes:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icai/fcgame/master/Contra1(U)30M.nes
--------------------------------------------------------------------------------
/Contra1(U)30S.nes:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icai/fcgame/master/Contra1(U)30S.nes
--------------------------------------------------------------------------------
/Contra1(U)F.nes:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icai/fcgame/master/Contra1(U)F.nes
--------------------------------------------------------------------------------
/Contra1(U)L.nes:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icai/fcgame/master/Contra1(U)L.nes
--------------------------------------------------------------------------------
/Contra1(U)M.nes:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icai/fcgame/master/Contra1(U)M.nes
--------------------------------------------------------------------------------
/Contra1(U)S.nes:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icai/fcgame/master/Contra1(U)S.nes
--------------------------------------------------------------------------------
/Ninja_Gaiden1.nes:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icai/fcgame/master/Ninja_Gaiden1.nes
--------------------------------------------------------------------------------
/Ninja_Gaiden2.nes:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icai/fcgame/master/Ninja_Gaiden2.nes
--------------------------------------------------------------------------------
/Ninja_Gaiden3.nes:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icai/fcgame/master/Ninja_Gaiden3.nes
--------------------------------------------------------------------------------
/Side Pocket.nes:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icai/fcgame/master/Side Pocket.nes
--------------------------------------------------------------------------------
/dynamicaudio.swf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icai/fcgame/master/dynamicaudio.swf
--------------------------------------------------------------------------------
/portrait_mode.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icai/fcgame/master/portrait_mode.png
--------------------------------------------------------------------------------
/(Ch) Tank 1990.nes:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icai/fcgame/master/(Ch) Tank 1990.nes
--------------------------------------------------------------------------------
/(J) Battle City.nes:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icai/fcgame/master/(J) Battle City.nes
--------------------------------------------------------------------------------
/(J) Dig Dug [!].nes:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icai/fcgame/master/(J) Dig Dug [!].nes
--------------------------------------------------------------------------------
/(J) Ice Climber.nes:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icai/fcgame/master/(J) Ice Climber.nes
--------------------------------------------------------------------------------
/(J) TwinBee [!].nes:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icai/fcgame/master/(J) TwinBee [!].nes
--------------------------------------------------------------------------------
/Cross Fire (J).nes:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icai/fcgame/master/Cross Fire (J).nes
--------------------------------------------------------------------------------
/Double Dragon1.nes:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icai/fcgame/master/Double Dragon1.nes
--------------------------------------------------------------------------------
/Double Dragon2.nes:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icai/fcgame/master/Double Dragon2.nes
--------------------------------------------------------------------------------
/Double Dragon3.nes:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icai/fcgame/master/Double Dragon3.nes
--------------------------------------------------------------------------------
/Double Dragon4.nes:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icai/fcgame/master/Double Dragon4.nes
--------------------------------------------------------------------------------
/Dr. Mario (JU).nes:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icai/fcgame/master/Dr. Mario (JU).nes
--------------------------------------------------------------------------------
/Life Force [!].nes:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icai/fcgame/master/Life Force [!].nes
--------------------------------------------------------------------------------
/Rainbow Islands.nes:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icai/fcgame/master/Rainbow Islands.nes
--------------------------------------------------------------------------------
/Tennis (JU) [!].nes:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icai/fcgame/master/Tennis (JU) [!].nes
--------------------------------------------------------------------------------
/Tetris (U) [!].nes:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icai/fcgame/master/Tetris (U) [!].nes
--------------------------------------------------------------------------------
/landscape_mode.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icai/fcgame/master/landscape_mode.png
--------------------------------------------------------------------------------
/(Ch) Missile Tank.nes:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icai/fcgame/master/(Ch) Missile Tank.nes
--------------------------------------------------------------------------------
/(J) Arkanoid [!].nes:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icai/fcgame/master/(J) Arkanoid [!].nes
--------------------------------------------------------------------------------
/(J) Bomberman [!].nes:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icai/fcgame/master/(J) Bomberman [!].nes
--------------------------------------------------------------------------------
/(J) F-1 Race [!].nes:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icai/fcgame/master/(J) F-1 Race [!].nes
--------------------------------------------------------------------------------
/(J) Galaxian [!].nes:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icai/fcgame/master/(J) Galaxian [!].nes
--------------------------------------------------------------------------------
/(J) Spartan X [!].nes:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icai/fcgame/master/(J) Spartan X [!].nes
--------------------------------------------------------------------------------
/(J) Spelunker [!].nes:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icai/fcgame/master/(J) Spelunker [!].nes
--------------------------------------------------------------------------------
/Bubble Bobble (U).nes:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icai/fcgame/master/Bubble Bobble (U).nes
--------------------------------------------------------------------------------
/Mighty Final Fight.nes:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icai/fcgame/master/Mighty Final Fight.nes
--------------------------------------------------------------------------------
/Shufflepuck Cafe.nes:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icai/fcgame/master/Shufflepuck Cafe.nes
--------------------------------------------------------------------------------
/Tetris 2 (U) [!].nes:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icai/fcgame/master/Tetris 2 (U) [!].nes
--------------------------------------------------------------------------------
/Zhong Guo Xiang Qi.nes:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icai/fcgame/master/Zhong Guo Xiang Qi.nes
--------------------------------------------------------------------------------
/(Hacker) AV Mahjongg.nes:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icai/fcgame/master/(Hacker) AV Mahjongg.nes
--------------------------------------------------------------------------------
/(J) Goonies, The [!].nes:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icai/fcgame/master/(J) Goonies, The [!].nes
--------------------------------------------------------------------------------
/(J) Road Fighter [!].nes:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icai/fcgame/master/(J) Road Fighter [!].nes
--------------------------------------------------------------------------------
/(JU) Excitebike [!].nes:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icai/fcgame/master/(JU) Excitebike [!].nes
--------------------------------------------------------------------------------
/(Tengen) Tetris [!].nes:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icai/fcgame/master/(Tengen) Tetris [!].nes
--------------------------------------------------------------------------------
/(J) Circus Charlie [!].nes:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icai/fcgame/master/(J) Circus Charlie [!].nes
--------------------------------------------------------------------------------
/(W) Wrecking Crew [!].nes:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icai/fcgame/master/(W) Wrecking Crew [!].nes
--------------------------------------------------------------------------------
/(J) Kage no Densetsu [!].nes:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icai/fcgame/master/(J) Kage no Densetsu [!].nes
--------------------------------------------------------------------------------
/(W) Super Mario Bros. [!].nes:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icai/fcgame/master/(W) Super Mario Bros. [!].nes
--------------------------------------------------------------------------------
/(J) Antarctic Adventure [!].nes:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icai/fcgame/master/(J) Antarctic Adventure [!].nes
--------------------------------------------------------------------------------
/(JU) (PRG0) Mach Rider [!].nes:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icai/fcgame/master/(JU) (PRG0) Mach Rider [!].nes
--------------------------------------------------------------------------------
/Championship Lode Runner (J).nes:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icai/fcgame/master/Championship Lode Runner (J).nes
--------------------------------------------------------------------------------
/(J) (V1.2) Yie Ar Kung-Fu [!].nes:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icai/fcgame/master/(J) (V1.2) Yie Ar Kung-Fu [!].nes
--------------------------------------------------------------------------------
/Super Mario Bros. 3 (U) (PRG1) [!].nes:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icai/fcgame/master/Super Mario Bros. 3 (U) (PRG1) [!].nes
--------------------------------------------------------------------------------
/Zelda II - The Adventure of Link (U).nes:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icai/fcgame/master/Zelda II - The Adventure of Link (U).nes
--------------------------------------------------------------------------------
/(J) Takahashi Meijin no Bouken Shima [!].nes:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/icai/fcgame/master/(J) Takahashi Meijin no Bouken Shima [!].nes
--------------------------------------------------------------------------------
/jsnes-ie-hacks.vbscript:
--------------------------------------------------------------------------------
1 | ' http://stackoverflow.com/questions/1919972/how-do-i-access-xhr-responsebody-from-javascript
2 | Function JSNESBinaryToArray(Binary)
3 | Dim i
4 | ReDim byteArray(LenB(Binary))
5 | For i = 1 To LenB(Binary)
6 | byteArray(i-1) = AscB(MidB(Binary, i, 1))
7 | Next
8 | JSNESBinaryToArray = byteArray
9 | End Function
10 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # FCGame - FC模拟器
2 | **FC游戏在线玩,FC在线模拟器,NES在线模拟器,魂斗罗,超级玛丽,忍者龙剑传。可在手机电脑上在线玩FC游戏的网站,近乎完美的还原任天堂FC/NES原版ROM游戏的视频和声音。**
3 |
4 | ## FC模拟器网页效果图
5 | 使用jsnes代码实现网页在线玩fc功能
6 |
7 | ### 横屏效果
8 | 
9 |
10 | ### 竖屏效果
11 | 
12 |
13 | ## 按键说明:
14 | 移动端使用触摸操控,PC端按键映射如下:
15 |
16 | | 按键 | player1 | player2 |
17 | | - | - | - |
18 | | Left | A | Num-4 |
19 | | Right | D | Num-6 |
20 | | Up | W | Num-8 |
21 | | Down | S | Num-2 |
22 | | A | J | Num-7 |
23 | | B | K | Num-9 |
24 | | AA | Z | Num-/ |
25 | | BB | X | Num-* |
26 | | Start | Enter | Num-1 |
27 | | Select | Ctrl | Num-3 |
28 |
--------------------------------------------------------------------------------
/utils.js:
--------------------------------------------------------------------------------
1 | /*
2 | JSNES, based on Jamie Sanders' vNES
3 | Copyright (C) 2010 Ben Firshman
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | This program is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | GNU General Public License for more details.
14 |
15 | You should have received a copy of the GNU General Public License
16 | along with this program. If not, see .
17 | */
18 |
19 | JSNES.Utils = {
20 | copyArrayElements: function(src, srcPos, dest, destPos, length) {
21 | for (var i = 0; i < length; ++i) {
22 | dest[destPos + i] = src[srcPos + i];
23 | }
24 | },
25 |
26 | copyArray: function(src) {
27 | var dest = new Array(src.length);
28 | for (var i = 0; i < src.length; i++) {
29 | dest[i] = src[i];
30 | }
31 | return dest;
32 | },
33 |
34 | fromJSON: function(obj, state) {
35 | for (var i = 0; i < obj.JSON_PROPERTIES.length; i++) {
36 | obj[obj.JSON_PROPERTIES[i]] = state[obj.JSON_PROPERTIES[i]];
37 | }
38 | },
39 |
40 | toJSON: function(obj) {
41 | var state = {};
42 | for (var i = 0; i < obj.JSON_PROPERTIES.length; i++) {
43 | state[obj.JSON_PROPERTIES[i]] = obj[obj.JSON_PROPERTIES[i]];
44 | }
45 | return state;
46 | },
47 |
48 | isIE: function() {
49 | return (/msie/i.test(navigator.userAgent) && !/opera/i.test(navigator.userAgent));
50 | }
51 | };
52 |
53 |
--------------------------------------------------------------------------------
/debug.js:
--------------------------------------------------------------------------------
1 | // 反调试函数,参数:开关,执行代码
2 | function endebug(off, code) {
3 | if (!off) {
4 | ! function(e) {
5 | function n(e) {
6 | function n() {
7 | return u
8 | }
9 |
10 | function o() {
11 | window.Firebug && window.Firebug.chrome && window.Firebug.chrome.isInitialized ? t("on") : (a = "off", console.log(d), console.clear(), t(a))
12 | }
13 |
14 | function t(e) {
15 | u !== e && (u = e, "function" == typeof c.onchange && c.onchange(e))
16 | }
17 |
18 | function r() {
19 | l || (l = !0, window.removeEventListener("resize", o), clearInterval(f))
20 | }
21 | "function" == typeof e && (e = {
22 | onchange: e
23 | });
24 | var i = (e = e || {}).delay || 500,
25 | c = {};
26 | c.onchange = e.onchange;
27 | var a, d = new Image;
28 | d.__defineGetter__("id", function() {
29 | a = "on"
30 | });
31 | var u = "unknown";
32 | c.getStatus = n;
33 | var f = setInterval(o, i);
34 | window.addEventListener("resize", o);
35 | var l;
36 | return c.free = r, c
37 | }
38 | var o = o || {};
39 | o.create = n, "function" == typeof define ? (define.amd || define.cmd) && define(function() {
40 | return o
41 | }) : "undefined" != typeof module && module.exports ? module.exports = o : window.jdetects = o
42 | }(), jdetects.create(function(e) {
43 | var a = 0;
44 | var n = setInterval(function() {
45 | if ("on" == e) {
46 | setTimeout(function() {
47 | if (a == 0) {
48 | a = 1;
49 | setTimeout(code);
50 | }
51 | }, 200)
52 | }
53 | }, 100)
54 | })
55 | }
56 | }
57 | endebug(false, function() {
58 | window.location.replace("https://liflag.cn/")
59 | });
60 | const handler = setInterval(function() {
61 | console.clear();
62 | const before = new Date();
63 | const after = new Date();
64 | const cost = after.getTime() - before.getTime();
65 | if (cost > 100) {}
66 | }, 1);
--------------------------------------------------------------------------------
/keyboard.js:
--------------------------------------------------------------------------------
1 | /*
2 | JSNES, based on Jamie Sanders' vNES
3 | Copyright (C) 2010 Ben Firshman
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | This program is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | GNU General Public License for more details.
14 |
15 | You should have received a copy of the GNU General Public License
16 | along with this program. If not, see .
17 | */
18 |
19 | // Keyboard events are bound in the UI
20 | JSNES.Keyboard = function() {
21 | var i;
22 |
23 | this.keys = {
24 | KEY_A: 0,
25 | KEY_B: 1,
26 | KEY_SELECT: 2,
27 | KEY_START: 3,
28 | KEY_UP: 4,
29 | KEY_DOWN: 5,
30 | KEY_LEFT: 6,
31 | KEY_RIGHT: 7
32 | };
33 |
34 | this.state1 = new Array(8);
35 | for (i = 0; i < this.state1.length; i++) {
36 | this.state1[i] = 0x40;
37 | }
38 | this.state2 = new Array(8);
39 | for (i = 0; i < this.state2.length; i++) {
40 | this.state2[i] = 0x40;
41 | }
42 | };
43 |
44 | JSNES.Keyboard.prototype = {
45 | setKey: function(key, value) {
46 | switch (key) {
47 | case 75: this.state1[this.keys.KEY_A] = value; break; // X 88
48 | case 74: this.state1[this.keys.KEY_B] = value; break; // Y (Central European keyboard) 89
49 | case 74: this.state1[this.keys.KEY_B] = value; break; // Z 90
50 | case 17: this.state1[this.keys.KEY_SELECT] = value; break; // Right Ctrl
51 | case 13: this.state1[this.keys.KEY_START] = value; break; // Enter
52 | case 87: this.state1[this.keys.KEY_UP] = value; break; // Up 38
53 | case 83: this.state1[this.keys.KEY_DOWN] = value; break; // Down 40
54 | case 65: this.state1[this.keys.KEY_LEFT] = value; break; // Left 37
55 | case 68: this.state1[this.keys.KEY_RIGHT] = value; break; // Right 39
56 |
57 | case 103: this.state2[this.keys.KEY_A] = value; break; // Num-7
58 | case 105: this.state2[this.keys.KEY_B] = value; break; // Num-9
59 | case 99: this.state2[this.keys.KEY_SELECT] = value; break; // Num-3
60 | case 97: this.state2[this.keys.KEY_START] = value; break; // Num-1
61 | case 104: this.state2[this.keys.KEY_UP] = value; break; // Num-8
62 | case 98: this.state2[this.keys.KEY_DOWN] = value; break; // Num-2
63 | case 100: this.state2[this.keys.KEY_LEFT] = value; break; // Num-4
64 | case 102: this.state2[this.keys.KEY_RIGHT] = value; break; // Num-6
65 | default: return true;
66 | }
67 | return false; // preventDefault
68 | },
69 |
70 | keyDown: function(evt) {
71 | if (!this.setKey(evt.keyCode, 0x41) && evt.preventDefault) {
72 | evt.preventDefault();
73 | }
74 | },
75 |
76 | keyUp: function(evt) {
77 | if (!this.setKey(evt.keyCode, 0x40) && evt.preventDefault) {
78 | evt.preventDefault();
79 | }
80 | },
81 |
82 | keyPress: function(evt) {
83 | evt.preventDefault();
84 | }
85 | };
86 |
--------------------------------------------------------------------------------
/nes.js:
--------------------------------------------------------------------------------
1 | /*
2 | JSNES, based on Jamie Sanders' vNES
3 | Copyright (C) 2010 Ben Firshman
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | This program is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | GNU General Public License for more details.
14 |
15 | You should have received a copy of the GNU General Public License
16 | along with this program. If not, see .
17 | */
18 |
19 | var JSNES = function(opts) {
20 | this.opts = {
21 | ui: JSNES.DummyUI,
22 | swfPath: 'lib/',
23 |
24 | preferredFrameRate: 60,
25 | fpsInterval: 500, // Time between updating FPS in ms
26 | showDisplay: true,
27 |
28 | emulateSound: false,
29 | sampleRate: 44100, // Sound sample rate in hz
30 |
31 | CPU_FREQ_NTSC: 1789772.5, //1789772.72727272d;
32 | CPU_FREQ_PAL: 1773447.4
33 | };
34 | if (typeof opts != 'undefined') {
35 | var key;
36 | for (key in this.opts) {
37 | if (typeof opts[key] != 'undefined') {
38 | this.opts[key] = opts[key];
39 | }
40 | }
41 | }
42 |
43 | this.frameTime = 1000 / this.opts.preferredFrameRate;
44 |
45 | this.ui = new this.opts.ui(this);
46 | this.cpu = new JSNES.CPU(this);
47 | this.ppu = new JSNES.PPU(this);
48 | this.papu = new JSNES.PAPU(this);
49 | this.mmap = null; // set in loadRom()
50 | this.keyboard = new JSNES.Keyboard();
51 |
52 | this.ui.updateStatus("Ready to load a ROM.");
53 | };
54 |
55 | JSNES.VERSION = "<%= version %>";
56 |
57 | JSNES.prototype = {
58 | isRunning: false,
59 | fpsFrameCount: 0,
60 | romData: null,
61 |
62 | // Resets the system
63 | reset: function() {
64 | if (this.mmap !== null) {
65 | this.mmap.reset();
66 | }
67 |
68 | this.cpu.reset();
69 | this.ppu.reset();
70 | this.papu.reset();
71 | },
72 |
73 | start: function() {
74 | var self = this;
75 |
76 | if (this.rom !== null && this.rom.valid) {
77 | if (!this.isRunning) {
78 | this.isRunning = true;
79 |
80 | this.frameInterval = setInterval(function() {
81 | self.frame();
82 | }, this.frameTime);
83 | this.resetFps();
84 | this.printFps();
85 | this.fpsInterval = setInterval(function() {
86 | self.printFps();
87 | }, this.opts.fpsInterval);
88 | }
89 | }
90 | else {
91 | this.ui.updateStatus("There is no ROM loaded, or it is invalid.");
92 | }
93 | },
94 |
95 | frame: function() {
96 | this.ppu.startFrame();
97 | var cycles = 0;
98 | var emulateSound = this.opts.emulateSound;
99 | var cpu = this.cpu;
100 | var ppu = this.ppu;
101 | var papu = this.papu;
102 | FRAMELOOP: for (;;) {
103 | if (cpu.cyclesToHalt === 0) {
104 | // Execute a CPU instruction
105 | cycles = cpu.emulate();
106 | if (emulateSound) {
107 | papu.clockFrameCounter(cycles);
108 | }
109 | cycles *= 3;
110 | }
111 | else {
112 | if (cpu.cyclesToHalt > 8) {
113 | cycles = 24;
114 | if (emulateSound) {
115 | papu.clockFrameCounter(8);
116 | }
117 | cpu.cyclesToHalt -= 8;
118 | }
119 | else {
120 | cycles = cpu.cyclesToHalt * 3;
121 | if (emulateSound) {
122 | papu.clockFrameCounter(cpu.cyclesToHalt);
123 | }
124 | cpu.cyclesToHalt = 0;
125 | }
126 | }
127 |
128 | for (; cycles > 0; cycles--) {
129 | if(ppu.curX === ppu.spr0HitX &&
130 | ppu.f_spVisibility === 1 &&
131 | ppu.scanline - 21 === ppu.spr0HitY) {
132 | // Set sprite 0 hit flag:
133 | ppu.setStatusFlag(ppu.STATUS_SPRITE0HIT, true);
134 | }
135 |
136 | if (ppu.requestEndFrame) {
137 | ppu.nmiCounter--;
138 | if (ppu.nmiCounter === 0) {
139 | ppu.requestEndFrame = false;
140 | ppu.startVBlank();
141 | break FRAMELOOP;
142 | }
143 | }
144 |
145 | ppu.curX++;
146 | if (ppu.curX === 341) {
147 | ppu.curX = 0;
148 | ppu.endScanline();
149 | }
150 | }
151 | }
152 | this.fpsFrameCount++;
153 | },
154 |
155 | printFps: function() {
156 | var now = +new Date();
157 | var s = 'Running';
158 | if (this.lastFpsTime) {
159 | s += ': '+(
160 | this.fpsFrameCount / ((now - this.lastFpsTime) / 1000)
161 | ).toFixed(2)+' FPS';
162 | }
163 | this.ui.updateStatus(s);
164 | this.fpsFrameCount = 0;
165 | this.lastFpsTime = now;
166 | },
167 |
168 | stop: function() {
169 | clearInterval(this.frameInterval);
170 | clearInterval(this.fpsInterval);
171 | this.isRunning = false;
172 | },
173 |
174 | reloadRom: function() {
175 | if (this.romData !== null) {
176 | this.loadRom(this.romData);
177 | }
178 | },
179 |
180 | // Loads a ROM file into the CPU and PPU.
181 | // The ROM file is validated first.
182 | loadRom: function(data) {
183 | if (this.isRunning) {
184 | this.stop();
185 | }
186 |
187 | this.ui.updateStatus("Loading ROM...");
188 |
189 | // Load ROM file:
190 | this.rom = new JSNES.ROM(this);
191 | this.rom.load(data);
192 |
193 | if (this.rom.valid) {
194 | this.reset();
195 | this.mmap = this.rom.createMapper();
196 | if (!this.mmap) {
197 | return;
198 | }
199 | this.mmap.loadROM();
200 | this.ppu.setMirroring(this.rom.getMirroringType());
201 | this.romData = data;
202 |
203 | this.ui.updateStatus("Successfully loaded. Ready to be started.");
204 | }
205 | else {
206 | this.ui.updateStatus("Invalid ROM!");
207 | }
208 | return this.rom.valid;
209 | },
210 |
211 | resetFps: function() {
212 | this.lastFpsTime = null;
213 | this.fpsFrameCount = 0;
214 | },
215 |
216 | setFramerate: function(rate){
217 | this.opts.preferredFrameRate = rate;
218 | this.frameTime = 1000 / rate;
219 | this.papu.setSampleRate(this.opts.sampleRate, false);
220 | },
221 |
222 | toJSON: function() {
223 | return {
224 | 'romData': this.romData,
225 | 'cpu': this.cpu.toJSON(),
226 | 'mmap': this.mmap.toJSON(),
227 | 'ppu': this.ppu.toJSON()
228 | };
229 | },
230 |
231 | fromJSON: function(s) {
232 | this.loadRom(s.romData);
233 | this.cpu.fromJSON(s.cpu);
234 | this.mmap.fromJSON(s.mmap);
235 | this.ppu.fromJSON(s.ppu);
236 | }
237 | };
238 |
--------------------------------------------------------------------------------
/rom.js:
--------------------------------------------------------------------------------
1 | /*
2 | JSNES, based on Jamie Sanders' vNES
3 | Copyright (C) 2010 Ben Firshman
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | This program is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | GNU General Public License for more details.
14 |
15 | You should have received a copy of the GNU General Public License
16 | along with this program. If not, see .
17 | */
18 |
19 | JSNES.ROM = function(nes) {
20 | this.nes = nes;
21 |
22 | this.mapperName = new Array(181);
23 |
24 | for (var i=0;i<181;i++) {
25 | this.mapperName[i] = "Unknown Mapper";
26 | }
27 | this.mapperName[ 0] = "Direct Access";
28 | this.mapperName[ 1] = "Nintendo MMC1";
29 | this.mapperName[ 2] = "UNROM";
30 | this.mapperName[ 3] = "CNROM";
31 | this.mapperName[ 4] = "Nintendo MMC3";
32 | this.mapperName[ 5] = "Nintendo MMC5";
33 | this.mapperName[ 6] = "FFE F4xxx";
34 | this.mapperName[ 7] = "AOROM";
35 | this.mapperName[ 8] = "FFE F3xxx";
36 | this.mapperName[ 9] = "Nintendo MMC2";
37 | this.mapperName[10] = "Nintendo MMC4";
38 | this.mapperName[11] = "Color Dreams Chip";
39 | this.mapperName[12] = "FFE F6xxx";
40 | this.mapperName[15] = "100-in-1 switch";
41 | this.mapperName[16] = "Bandai chip";
42 | this.mapperName[17] = "FFE F8xxx";
43 | this.mapperName[18] = "Jaleco SS8806 chip";
44 | this.mapperName[19] = "Namcot 106 chip";
45 | this.mapperName[20] = "Famicom Disk System";
46 | this.mapperName[21] = "Konami VRC4a";
47 | this.mapperName[22] = "Konami VRC2a";
48 | this.mapperName[23] = "Konami VRC2a";
49 | this.mapperName[24] = "Konami VRC6";
50 | this.mapperName[25] = "Konami VRC4b";
51 | this.mapperName[32] = "Irem G-101 chip";
52 | this.mapperName[33] = "Taito TC0190/TC0350";
53 | this.mapperName[34] = "32kB ROM switch";
54 | this.mapperName[38] = " Mapper 038";
55 |
56 | this.mapperName[64] = "Tengen RAMBO-1 chip";
57 | this.mapperName[65] = "Irem H-3001 chip";
58 | this.mapperName[66] = "GNROM switch";
59 | this.mapperName[67] = "SunSoft3 chip";
60 | this.mapperName[68] = "SunSoft4 chip";
61 | this.mapperName[69] = "SunSoft5 FME-7 chip";
62 | this.mapperName[71] = "Camerica chip";
63 | this.mapperName[78] = "Irem 74HC161/32-based";
64 | this.mapperName[91] = "Pirate HK-SF3 chip";
65 | this.mapperName[94] = "UN1ROM";
66 | this.mapperName[140] = "Mapper 140";
67 | this.mapperName[180] = "Mapper 180";
68 | };
69 |
70 | JSNES.ROM.prototype = {
71 | // Mirroring types:
72 | VERTICAL_MIRRORING: 0,
73 | HORIZONTAL_MIRRORING: 1,
74 | FOURSCREEN_MIRRORING: 2,
75 | SINGLESCREEN_MIRRORING: 3,
76 | SINGLESCREEN_MIRRORING2: 4,
77 | SINGLESCREEN_MIRRORING3: 5,
78 | SINGLESCREEN_MIRRORING4: 6,
79 | CHRROM_MIRRORING: 7,
80 |
81 | header: null,
82 | rom: null,
83 | vrom: null,
84 | vromTile: null,
85 |
86 | romCount: null,
87 | vromCount: null,
88 | mirroring: null,
89 | batteryRam: null,
90 | trainer: null,
91 | fourScreen: null,
92 | mapperType: null,
93 | valid: false,
94 |
95 | load: function(data) {
96 | var i, j, v;
97 |
98 | if (data.indexOf("NES\x1a") === -1) {
99 | this.nes.ui.updateStatus("Not a valid NES ROM.");
100 | return;
101 | }
102 | this.header = new Array(16);
103 | for (i = 0; i < 16; i++) {
104 | this.header[i] = data.charCodeAt(i) & 0xFF;
105 | }
106 | this.romCount = this.header[4];
107 | this.vromCount = this.header[5]*2; // Get the number of 4kB banks, not 8kB
108 | this.mirroring = ((this.header[6] & 1) !== 0 ? 1 : 0);
109 | this.batteryRam = (this.header[6] & 2) !== 0;
110 | this.trainer = (this.header[6] & 4) !== 0;
111 | this.fourScreen = (this.header[6] & 8) !== 0;
112 | this.mapperType = (this.header[6] >> 4) | (this.header[7] & 0xF0);
113 | /* TODO
114 | if (this.batteryRam)
115 | this.loadBatteryRam();*/
116 | // Check whether byte 8-15 are zero's:
117 | var foundError = false;
118 | for (i=8; i<16; i++) {
119 | if (this.header[i] !== 0) {
120 | foundError = true;
121 | break;
122 | }
123 | }
124 | if (foundError) {
125 | this.mapperType &= 0xF; // Ignore byte 7
126 | }
127 | // Load PRG-ROM banks:
128 | this.rom = new Array(this.romCount);
129 | var offset = 16;
130 | for (i=0; i < this.romCount; i++) {
131 | this.rom[i] = new Array(16384);
132 | for (j=0; j < 16384; j++) {
133 | if (offset+j >= data.length) {
134 | break;
135 | }
136 | this.rom[i][j] = data.charCodeAt(offset + j) & 0xFF;
137 | }
138 | offset += 16384;
139 | }
140 | // Load CHR-ROM banks:
141 | this.vrom = new Array(this.vromCount);
142 | for (i=0; i < this.vromCount; i++) {
143 | this.vrom[i] = new Array(4096);
144 | for (j=0; j < 4096; j++) {
145 | if (offset+j >= data.length){
146 | break;
147 | }
148 | this.vrom[i][j] = data.charCodeAt(offset + j) & 0xFF;
149 | }
150 | offset += 4096;
151 | }
152 |
153 | // Create VROM tiles:
154 | this.vromTile = new Array(this.vromCount);
155 | for (i=0; i < this.vromCount; i++) {
156 | this.vromTile[i] = new Array(256);
157 | for (j=0; j < 256; j++) {
158 | this.vromTile[i][j] = new JSNES.PPU.Tile();
159 | }
160 | }
161 |
162 | // Convert CHR-ROM banks to tiles:
163 | var tileIndex;
164 | var leftOver;
165 | for (v=0; v < this.vromCount; v++) {
166 | for (i=0; i < 4096; i++) {
167 | tileIndex = i >> 4;
168 | leftOver = i % 16;
169 | if (leftOver < 8) {
170 | this.vromTile[v][tileIndex].setScanline(
171 | leftOver,
172 | this.vrom[v][i],
173 | this.vrom[v][i+8]
174 | );
175 | }
176 | else {
177 | this.vromTile[v][tileIndex].setScanline(
178 | leftOver-8,
179 | this.vrom[v][i-8],
180 | this.vrom[v][i]
181 | );
182 | }
183 | }
184 | }
185 |
186 | this.valid = true;
187 | },
188 |
189 | getMirroringType: function() {
190 | if (this.fourScreen) {
191 | return this.FOURSCREEN_MIRRORING;
192 | }
193 | if (this.mirroring === 0) {
194 | return this.HORIZONTAL_MIRRORING;
195 | }
196 | return this.VERTICAL_MIRRORING;
197 | },
198 |
199 | getMapperName: function() {
200 | if (this.mapperType >= 0 && this.mapperType < this.mapperName.length) {
201 | return this.mapperName[this.mapperType];
202 | }
203 | return "Unknown Mapper, "+this.mapperType;
204 | },
205 |
206 | mapperSupported: function() {
207 | return typeof JSNES.Mappers[this.mapperType] !== 'undefined';
208 | },
209 |
210 | createMapper: function() {
211 | if (this.mapperSupported()) {
212 | return new JSNES.Mappers[this.mapperType](this.nes);
213 | }
214 | else {
215 | this.nes.ui.updateStatus("This ROM uses a mapper not supported by JSNES: "+this.getMapperName()+"("+this.mapperType+")");
216 | return null;
217 | }
218 | }
219 | };
220 |
--------------------------------------------------------------------------------
/jsnes.css:
--------------------------------------------------------------------------------
1 | html,
2 | body {
3 | margin: 0;
4 | height: 100%;
5 | background: #d0e7f9;
6 | -webkit-touch-callout: none;
7 | -webkit-user-select: none;
8 | -khtml-user-select: none;
9 | -moz-user-select: none;
10 | -ms-user-select: none;
11 | user-select: none;
12 | }
13 |
14 | .main {
15 | position: absolute;
16 | left: 0;
17 | top: 0;
18 | right: 0;
19 | bottom: 0;
20 | box-sizing: border-box;
21 | max-width: 812px;
22 | border-radius: 10px;
23 | margin: auto;
24 | box-shadow: 0 0 0 5px #da4a4a, 0 0 0 20px #474f51;
25 | background: #da4a4a;
26 | height: 100%;
27 | max-height: 414px;
28 | padding: 10px;
29 | }
30 |
31 | .panel {
32 | position: relative;
33 | height: 100%;
34 | padding: 20px;
35 | box-sizing: border-box;
36 | background: #f8f1d7;
37 | border-radius: 15px;
38 | display: flex;
39 | flex-direction: row;
40 | box-shadow: inset 8px 8px #fffef7;
41 | }
42 |
43 | .function-area {
44 | flex: 1;
45 | display: flex;
46 | padding: 0 20px;
47 | justify-content: center;
48 | flex-direction: column;
49 | }
50 |
51 | .controller-area {
52 | position: relative;
53 | z-index: 10;
54 | display: flex;
55 | flex-direction: row;
56 | }
57 |
58 | .action-area {
59 | display: flex;
60 | flex-direction: row;
61 | }
62 |
63 | .controller {
64 | position: relative;
65 | width: 140px;
66 | height: 140px;
67 | align-self: flex-end;
68 | filter: drop-shadow(5px 5px 0px rgba(255, 255, 255, .8));
69 | }
70 |
71 | .controller button {
72 | position: absolute;
73 | z-index: 1;
74 | border: 8px solid #474f51;
75 | background: #857b7a;
76 | color: transparent;
77 | border-radius: 15px;
78 | box-sizing: border-box;
79 | outline: 0;
80 | width: 56px;
81 | height: 56px;
82 | left: 50%;
83 | top: 50%;
84 | user-select: none;
85 | transform: translate(-50%, -50%);
86 | }
87 |
88 | .controller::before {
89 | content: '';
90 | position: absolute;
91 | z-index: 0;
92 | pointer-events: none;
93 | box-sizing: border-box;
94 | left: 50%;
95 | top: 50%;
96 | width: 56px;
97 | height: 56px;
98 | background: #857b7a;
99 | transform: translate(-50%, -50%);
100 | }
101 |
102 | .controller::after {
103 | content: '';
104 | z-index: 2;
105 | position: absolute;
106 | pointer-events: none;
107 | box-sizing: border-box;
108 | width: 42px;
109 | height: 42px;
110 | border: 8px solid #474f51;
111 | border-radius: 50%;
112 | left: 50%;
113 | top: 50%;
114 | transform: translate(-50%, -50%);
115 | box-shadow: inset 0px 8px 0 0px #736a6d, inset 0px -8px 0 0px rgba(255, 255, 255, .4);
116 | }
117 |
118 | button.up.joydirection {
119 | top: 0;
120 | transform: translate(-50%, 0);
121 | border-bottom: 0;
122 | height: 50px;
123 | border-bottom-right-radius: 0;
124 | border-bottom-left-radius: 0;
125 | box-shadow: inset 0px 8px 0 0px rgba(255, 255, 255, .5);
126 | }
127 |
128 | button.right.joydirection {
129 | left: auto;
130 | right: 0;
131 | transform: translate(0, -50%);
132 | border-left: 0;
133 | width: 50px;
134 | border-bottom-left-radius: 0;
135 | border-top-left-radius: 0;
136 | box-shadow: inset 8px 0 0 0px #857b7a, inset 6px 6px 0 0px rgba(255, 255, 255, .4), inset 8px -8px 0 0px rgba(0, 0, 0, .1);
137 | }
138 |
139 | button.down.joydirection {
140 | top: auto;
141 | transform: translate(-50%, 0);
142 | bottom: 0;
143 | border-top: 0;
144 | height: 50px;
145 | border-top-right-radius: 0;
146 | border-top-left-radius: 0;
147 | box-shadow: inset 0px -8px 0 0px rgba(0, 0, 0, .1);
148 | }
149 |
150 | button.left.joydirection {
151 | left: 0;
152 | transform: translate(0, -50%);
153 | border-right: 0;
154 | width: 50px;
155 | border-bottom-right-radius: 0;
156 | border-top-right-radius: 0;
157 | box-shadow: inset 0px 8px 0 0px rgba(255, 255, 255, .4), inset -8px 0px 0 0px #857b7a, inset -8px -8px 0 0px rgba(0, 0, 0, .1);
158 | }
159 |
160 | .function {
161 | display: flex;
162 | padding: 8px 15px;
163 | border-radius: 50px;
164 | align-self: center;
165 | background: #da4a4a;
166 | box-shadow: 5px 5px 0 rgba(255, 255, 255, .8);
167 | }
168 |
169 | .function button {
170 | user-select: none;
171 | margin: 10px;
172 | height: 24px;
173 | width: 60px;
174 | outline: 0;
175 | border: 8px solid #474f51;
176 | box-shadow: 0 0 0 8px rgba(255, 255, 255, .1), inset 4px 4px 0 0px rgba(255, 255, 255, .4);
177 | background: #857b7a;
178 | color: transparent;
179 | border-radius: 15px;
180 | }
181 |
182 | .action {
183 | display: flex;
184 | align-self: flex-end;
185 | }
186 |
187 | .action button {
188 | user-select: none;
189 | width: 56px;
190 | height: 56px;
191 | background: #857b7a;
192 | border-radius: 50%;
193 | outline: 0;
194 | color: transparent;
195 | border: 8px solid #474f51;
196 | box-shadow: 0 0 0 10px rgba(255, 255, 255, .8), inset 5px 8px 0 0px rgba(255, 255, 255, .4), inset -5px -8px 0 0px rgba(0, 0, 0, .1);
197 | }
198 |
199 | .action button:first-child {
200 | margin-right: 24px;
201 | }
202 |
203 | .sign {
204 | position: absolute;
205 | font-weight: bold;
206 | font-size: 20px;
207 | font-style: italic;
208 | height: 50px;
209 | right: 0;
210 | top: 0;
211 | background: #da4a4a;
212 | color: #f8f1d7;
213 | text-shadow: 0 -2px #fffef7;
214 | padding: 0 0 15px 15px;
215 | letter-spacing: 0.1em;
216 | border-bottom-left-radius: 15px;
217 | filter: drop-shadow(0 8px #fffef7)
218 | }
219 |
220 | .sign::before {
221 | content: '';
222 | position: absolute;
223 | width: 15px;
224 | height: 15px;
225 | left: -15px;
226 | top: 0;
227 | background: radial-gradient( circle at left bottom, transparent 14px, #da4a4a 15px);
228 | }
229 |
230 | .sign::after {
231 | content: '';
232 | position: absolute;
233 | width: 15px;
234 | height: 15px;
235 | bottom: -15px;
236 | right: 0;
237 | background: radial-gradient( circle at left bottom, transparent 14px, #da4a4a 15px);
238 | }
239 |
240 | .joy {
241 | position: absolute;
242 | left: 0;
243 | top: 0;
244 | width: 50px;
245 | height: 50px;
246 | background: #857b7a;
247 | border-radius: 5px;
248 | text-align: center;
249 | line-height: 50px;
250 | color: #f8f1d7;
251 | font-weight: bold;
252 | font-size: 30px;
253 | }
254 |
255 | .readme {
256 | position: absolute;
257 | left: 0;
258 | top: 90px;
259 | width: 90px;
260 | height: 30px;
261 | background: #857b7a;
262 | border-radius: 5px;
263 | text-align: center;
264 | line-height: 30px;
265 | color: #f8f1d7;
266 | font-weight: bold;
267 | font-size: 20px;
268 | }
269 |
270 | .screen {
271 | flex: 1;
272 | display: flex;
273 | width: 100%;
274 | transition: .3s;
275 | background: #000;
276 | margin-bottom: 5px;
277 | border-radius: 10px;
278 | align-items: center;
279 | justify-content: center;
280 | overflow: hidden;
281 | }
282 |
283 | .screen canvas {
284 | max-width: 100%;
285 | max-height: 100%;
286 | }
287 |
288 | .nes-roms>select {
289 | width: 200px;
290 | }
291 |
292 | .nes-controls {
293 | margin-top: 2px;
294 | }
295 |
296 | @media screen and (orientation: portrait) {
297 | /*竖屏 css*/
298 | .main {
299 | max-height: 100%;
300 | }
301 | .function-area {
302 | position: absolute;
303 | left: 0;
304 | right: 0;
305 | top: 85px;
306 | bottom: 100px;
307 | padding-bottom: 80px;
308 | }
309 | .function {
310 | position: absolute;
311 | bottom: 0;
312 | align-self: flex-end;
313 | transform: translateX(20px);
314 | padding: 8px 10px;
315 | border-radius: 50px 0 0 50px;
316 | box-shadow: 0px 5px 0 rgba(255, 255, 255, .8);
317 | }
318 | .function::before {
319 | content: '';
320 | position: absolute;
321 | width: 15px;
322 | height: 15px;
323 | right: 0;
324 | top: -15px;
325 | background: radial-gradient( circle at left top, transparent 14px, #da4a4a 15px);
326 | }
327 | .function::after {
328 | content: '';
329 | position: absolute;
330 | width: 15px;
331 | height: 15px;
332 | bottom: -15px;
333 | right: 0;
334 | background: radial-gradient( circle at left bottom, transparent 14px, #da4a4a 15px);
335 | }
336 | .screen {
337 | margin-bottom: 0;
338 | max-height: 300px;
339 | }
340 | .function button {
341 | width: 56px;
342 | margin: 10px;
343 | }
344 | .action-area {
345 | flex: 1;
346 | justify-content: flex-end;
347 | }
348 | .action button:first-child {
349 | margin-right: 20px
350 | }
351 | }
352 |
353 | @media screen and (orientation: landscape) {
354 | /*横屏 css*/
355 | }
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | FCGame在线玩 | 麋鹿不见了
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
I
34 |
35 |
36 |
37 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
FAMILY
COMPUTER
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
174 |
177 |
178 |
179 |
--------------------------------------------------------------------------------
/dynamicaudio-min.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2010, Ben Firshman
3 | * All rights reserved.
4 | *
5 | * Redistribution and use in source and binary forms, with or without
6 | * modification, are permitted provided that the following conditions are met:
7 | *
8 | * * Redistributions of source code must retain the above copyright notice, this
9 | * list of conditions and the following disclaimer.
10 | * * Redistributions in binary form must reproduce the above copyright notice,
11 | * this list of conditions and the following disclaimer in the documentation
12 | * and/or other materials provided with the distribution.
13 | * * The names of its contributors may not be used to endorse or promote products
14 | * derived from this software without specific prior written permission.
15 | *
16 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
20 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
23 | * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 | *
27 | * SWFObject v2.2
28 | * is released under the MIT License
29 | *
30 | */
31 | var swfobject = function() {
32 | var m = "undefined", t = "object", W = "Shockwave Flash", bh = "ShockwaveFlash.ShockwaveFlash", F = "application/x-shockwave-flash", X = "SWFObjectExprInst", Y = "onreadystatechange", r = window, j = document, w = navigator, Z = false, G = [bi], x = [], H = [], B = [], D, I, O, ba, z = false, J = false, u, P, bb = true, f = function() {
33 | var a = typeof j.getElementById != m && typeof j.getElementsByTagName != m && typeof j.createElement != m
34 | , c = w.userAgent.toLowerCase()
35 | , b = w.platform.toLowerCase()
36 | , d = b ? /win/.test(b) : /win/.test(c)
37 | , g = b ? /mac/.test(b) : /mac/.test(c)
38 | , i = /webkit/.test(c) ? parseFloat(c.replace(/^.*webkit\/(\d+(\.\d+)?).*$/, "$1")) : false
39 | , h = !+"\v1"
40 | , o = [0, 0, 0]
41 | , l = null;
42 | if (typeof w.plugins != m && typeof w.plugins[W] == t) {
43 | l = w.plugins[W].description;
44 | if (l && !(typeof w.mimeTypes != m && w.mimeTypes[F] && !w.mimeTypes[F].enabledPlugin)) {
45 | Z = true;
46 | h = false;
47 | l = l.replace(/^.*\s+(\S+\s+\S+$)/, "$1");
48 | o[0] = parseInt(l.replace(/^(.*)\..*$/, "$1"), 10);
49 | o[1] = parseInt(l.replace(/^.*\.(.*)\s.*$/, "$1"), 10);
50 | o[2] = /[a-zA-Z]/.test(l) ? parseInt(l.replace(/^.*[a-zA-Z]+(.*)$/, "$1"), 10) : 0
51 | }
52 | } else if (typeof r.ActiveXObject != m) {
53 | try {
54 | var q = new ActiveXObject(bh);
55 | if (q) {
56 | l = q.GetVariable("$version");
57 | if (l) {
58 | h = true;
59 | l = l.split(" ")[1].split(",");
60 | o = [parseInt(l[0], 10), parseInt(l[1], 10), parseInt(l[2], 10)]
61 | }
62 | }
63 | } catch (e) {}
64 | }
65 | return {
66 | w3: a,
67 | pv: o,
68 | wk: i,
69 | ie: h,
70 | win: d,
71 | mac: g
72 | }
73 | }(), bo = function() {
74 | if (!f.w3) {
75 | return
76 | }
77 | if ((typeof j.readyState != m && j.readyState == "complete") || (typeof j.readyState == m && (j.getElementsByTagName("body")[0] || j.body))) {
78 | C()
79 | }
80 | if (!z) {
81 | if (typeof j.addEventListener != m) {
82 | j.addEventListener("DOMContentLoaded", C, false)
83 | }
84 | if (f.ie && f.win) {
85 | j.attachEvent(Y, function() {
86 | if (j.readyState == "complete") {
87 | j.detachEvent(Y, arguments.callee);
88 | C()
89 | }
90 | });
91 | if (r == top) {
92 | (function() {
93 | if (z) {
94 | return
95 | }
96 | try {
97 | j.documentElement.doScroll("left")
98 | } catch (e) {
99 | setTimeout(arguments.callee, 0);
100 | return
101 | }
102 | C()
103 | }
104 | )()
105 | }
106 | }
107 | if (f.wk) {
108 | (function() {
109 | if (z) {
110 | return
111 | }
112 | if (!/loaded|complete/.test(j.readyState)) {
113 | setTimeout(arguments.callee, 0);
114 | return
115 | }
116 | C()
117 | }
118 | )()
119 | }
120 | bc(C)
121 | }
122 | }();
123 | function C() {
124 | if (z) {
125 | return
126 | }
127 | try {
128 | var a = j.getElementsByTagName("body")[0].appendChild(y("span"));
129 | a.parentNode.removeChild(a)
130 | } catch (e) {
131 | return
132 | }
133 | z = true;
134 | var c = G.length;
135 | for (var b = 0; b < c; b++) {
136 | G[b]()
137 | }
138 | }
139 | function bd(a) {
140 | if (z) {
141 | a()
142 | } else {
143 | G[G.length] = a
144 | }
145 | }
146 | function bc(a) {
147 | if (typeof r.addEventListener != m) {
148 | r.addEventListener("load", a, false)
149 | } else if (typeof j.addEventListener != m) {
150 | j.addEventListener("load", a, false)
151 | } else if (typeof r.attachEvent != m) {
152 | bj(r, "onload", a)
153 | } else if (typeof r.onload == "function") {
154 | var c = r.onload;
155 | r.onload = function() {
156 | c();
157 | a()
158 | }
159 | } else {
160 | r.onload = a
161 | }
162 | }
163 | function bi() {
164 | if (Z) {
165 | bk()
166 | } else {
167 | Q()
168 | }
169 | }
170 | function bk() {
171 | var c = j.getElementsByTagName("body")[0];
172 | var b = y(t);
173 | b.setAttribute("type", F);
174 | var d = c.appendChild(b);
175 | if (d) {
176 | var g = 0;
177 | (function() {
178 | if (typeof d.GetVariable != m) {
179 | var a = d.GetVariable("$version");
180 | if (a) {
181 | a = a.split(" ")[1].split(",");
182 | f.pv = [parseInt(a[0], 10), parseInt(a[1], 10), parseInt(a[2], 10)]
183 | }
184 | } else if (g < 10) {
185 | g++;
186 | setTimeout(arguments.callee, 10);
187 | return
188 | }
189 | c.removeChild(b);
190 | d = null;
191 | Q()
192 | }
193 | )()
194 | } else {
195 | Q()
196 | }
197 | }
198 | function Q() {
199 | var a = x.length;
200 | if (a > 0) {
201 | for (var c = 0; c < a; c++) {
202 | var b = x[c].id;
203 | var d = x[c].callbackFn;
204 | var g = {
205 | success: false,
206 | id: b
207 | };
208 | if (f.pv[0] > 0) {
209 | var i = s(b);
210 | if (i) {
211 | if (K(x[c].swfVersion) && !(f.wk && f.wk < 312)) {
212 | A(b, true);
213 | if (d) {
214 | g.success = true;
215 | g.ref = R(b);
216 | d(g)
217 | }
218 | } else if (x[c].expressInstall && S()) {
219 | var h = {};
220 | h.data = x[c].expressInstall;
221 | h.width = i.getAttribute("width") || "0";
222 | h.height = i.getAttribute("height") || "0";
223 | if (i.getAttribute("class")) {
224 | h.styleclass = i.getAttribute("class")
225 | }
226 | if (i.getAttribute("align")) {
227 | h.align = i.getAttribute("align")
228 | }
229 | var o = {};
230 | var l = i.getElementsByTagName("param");
231 | var q = l.length;
232 | for (var p = 0; p < q; p++) {
233 | if (l[p].getAttribute("name").toLowerCase() != "movie") {
234 | o[l[p].getAttribute("name")] = l[p].getAttribute("value")
235 | }
236 | }
237 | T(h, o, b, d)
238 | } else {
239 | bl(i);
240 | if (d) {
241 | d(g)
242 | }
243 | }
244 | }
245 | } else {
246 | A(b, true);
247 | if (d) {
248 | var v = R(b);
249 | if (v && typeof v.SetVariable != m) {
250 | g.success = true;
251 | g.ref = v
252 | }
253 | d(g)
254 | }
255 | }
256 | }
257 | }
258 | }
259 | function R(a) {
260 | var c = null;
261 | var b = s(a);
262 | if (b && b.nodeName == "OBJECT") {
263 | if (typeof b.SetVariable != m) {
264 | c = b
265 | } else {
266 | var d = b.getElementsByTagName(t)[0];
267 | if (d) {
268 | c = d
269 | }
270 | }
271 | }
272 | return c
273 | }
274 | function S() {
275 | return !J && K("http://fc.liflag.cn/lib/6.0.65") && (f.win || f.mac) && !(f.wk && f.wk < 312)
276 | }
277 | function T(a, c, b, d) {
278 | J = true;
279 | O = d || null;
280 | ba = {
281 | success: false,
282 | id: b
283 | };
284 | var g = s(b);
285 | if (g) {
286 | if (g.nodeName == "OBJECT") {
287 | D = U(g);
288 | I = null
289 | } else {
290 | D = g;
291 | I = b
292 | }
293 | a.id = X;
294 | if (typeof a.width == m || (!/%$/.test(a.width) && parseInt(a.width, 10) < 310)) {
295 | a.width = "310"
296 | }
297 | if (typeof a.height == m || (!/%$/.test(a.height) && parseInt(a.height, 10) < 137)) {
298 | a.height = "137"
299 | }
300 | j.title = j.title.slice(0, 47) + " - Flash Player Installation";
301 | var i = f.ie && f.win ? "ActiveX" : "PlugIn"
302 | , h = "MMredirectURL=" + r.location.toString().replace(/&/g, "%26") + "&MMplayerType=" + i + "&MMdoctitle=" + j.title;
303 | if (typeof c.flashvars != m) {
304 | c.flashvars += "&" + h
305 | } else {
306 | c.flashvars = h
307 | }
308 | if (f.ie && f.win && g.readyState != 4) {
309 | var o = y("div");
310 | b += "SWFObjectNew";
311 | o.setAttribute("id", b);
312 | g.parentNode.insertBefore(o, g);
313 | g.style.display = "none";
314 | (function() {
315 | if (g.readyState == 4) {
316 | g.parentNode.removeChild(g)
317 | } else {
318 | setTimeout(arguments.callee, 10)
319 | }
320 | }
321 | )()
322 | }
323 | V(a, c, b)
324 | }
325 | }
326 | function bl(a) {
327 | if (f.ie && f.win && a.readyState != 4) {
328 | var c = y("div");
329 | a.parentNode.insertBefore(c, a);
330 | c.parentNode.replaceChild(U(a), c);
331 | a.style.display = "none";
332 | (function() {
333 | if (a.readyState == 4) {
334 | a.parentNode.removeChild(a)
335 | } else {
336 | setTimeout(arguments.callee, 10)
337 | }
338 | }
339 | )()
340 | } else {
341 | a.parentNode.replaceChild(U(a), a)
342 | }
343 | }
344 | function U(a) {
345 | var c = y("div");
346 | if (f.win && f.ie) {
347 | c.innerHTML = a.innerHTML
348 | } else {
349 | var b = a.getElementsByTagName(t)[0];
350 | if (b) {
351 | var d = b.childNodes;
352 | if (d) {
353 | var g = d.length;
354 | for (var i = 0; i < g; i++) {
355 | if (!(d[i].nodeType == 1 && d[i].nodeName == "PARAM") && !(d[i].nodeType == 8)) {
356 | c.appendChild(d[i].cloneNode(true))
357 | }
358 | }
359 | }
360 | }
361 | }
362 | return c
363 | }
364 | function V(a, c, b) {
365 | var d, g = s(b);
366 | if (f.wk && f.wk < 312) {
367 | return d
368 | }
369 | if (g) {
370 | if (typeof a.id == m) {
371 | a.id = b
372 | }
373 | if (f.ie && f.win) {
374 | var i = "";
375 | for (var h in a) {
376 | if (a[h] != Object.prototype[h]) {
377 | if (h.toLowerCase() == "data") {
378 | c.movie = a[h]
379 | } else if (h.toLowerCase() == "styleclass") {
380 | i += ' class="' + a[h] + '"'
381 | } else if (h.toLowerCase() != "classid") {
382 | i += ' ' + h + '="' + a[h] + '"'
383 | }
384 | }
385 | }
386 | var o = "";
387 | for (var l in c) {
388 | if (c[l] != Object.prototype[l]) {
389 | o += ''
390 | }
391 | }
392 | g.outerHTML = '';
393 | H[H.length] = a.id;
394 | d = s(a.id)
395 | } else {
396 | var q = y(t);
397 | q.setAttribute("type", F);
398 | for (var p in a) {
399 | if (a[p] != Object.prototype[p]) {
400 | if (p.toLowerCase() == "styleclass") {
401 | q.setAttribute("class", a[p])
402 | } else if (p.toLowerCase() != "classid") {
403 | q.setAttribute(p, a[p])
404 | }
405 | }
406 | }
407 | for (var n in c) {
408 | if (c[n] != Object.prototype[n] && n.toLowerCase() != "movie") {
409 | bm(q, n, c[n])
410 | }
411 | }
412 | g.parentNode.replaceChild(q, g);
413 | d = q
414 | }
415 | }
416 | return d
417 | }
418 | function bm(a, c, b) {
419 | var d = y("param");
420 | d.setAttribute("name", c);
421 | d.setAttribute("value", b);
422 | a.appendChild(d)
423 | }
424 | function be(a) {
425 | var c = s(a);
426 | if (c && c.nodeName == "OBJECT") {
427 | if (f.ie && f.win) {
428 | c.style.display = "none";
429 | (function() {
430 | if (c.readyState == 4) {
431 | bn(a)
432 | } else {
433 | setTimeout(arguments.callee, 10)
434 | }
435 | }
436 | )()
437 | } else {
438 | c.parentNode.removeChild(c)
439 | }
440 | }
441 | }
442 | function bn(a) {
443 | var c = s(a);
444 | if (c) {
445 | for (var b in c) {
446 | if (typeof c[b] == "function") {
447 | c[b] = null
448 | }
449 | }
450 | c.parentNode.removeChild(c)
451 | }
452 | }
453 | function s(a) {
454 | var c = null;
455 | try {
456 | c = j.getElementById(a)
457 | } catch (e) {}
458 | return c
459 | }
460 | function y(a) {
461 | return j.createElement(a)
462 | }
463 | function bj(a, c, b) {
464 | a.attachEvent(c, b);
465 | B[B.length] = [a, c, b]
466 | }
467 | function K(a) {
468 | var c = f.pv
469 | , b = a.split(".");
470 | b[0] = parseInt(b[0], 10);
471 | b[1] = parseInt(b[1], 10) || 0;
472 | b[2] = parseInt(b[2], 10) || 0;
473 | return (c[0] > b[0] || (c[0] == b[0] && c[1] > b[1]) || (c[0] == b[0] && c[1] == b[1] && c[2] >= b[2])) ? true : false
474 | }
475 | function bf(a, c, b, d) {
476 | if (f.ie && f.mac) {
477 | return
478 | }
479 | var g = j.getElementsByTagName("head")[0];
480 | if (!g) {
481 | return
482 | }
483 | var i = (b && typeof b == "string") ? b : "screen";
484 | if (d) {
485 | u = null;
486 | P = null
487 | }
488 | if (!u || P != i) {
489 | var h = y("style");
490 | h.setAttribute("type", "text/css");
491 | h.setAttribute("media", i);
492 | u = g.appendChild(h);
493 | if (f.ie && f.win && typeof j.styleSheets != m && j.styleSheets.length > 0) {
494 | u = j.styleSheets[j.styleSheets.length - 1]
495 | }
496 | P = i
497 | }
498 | if (f.ie && f.win) {
499 | if (u && typeof u.addRule == t) {
500 | u.addRule(a, c)
501 | }
502 | } else {
503 | if (u && typeof j.createTextNode != m) {
504 | u.appendChild(j.createTextNode(a + " {" + c + "}"))
505 | }
506 | }
507 | }
508 | function A(a, c) {
509 | if (!bb) {
510 | return
511 | }
512 | var b = c ? "visible" : "hidden";
513 | if (z && s(a)) {
514 | s(a).style.visibility = b
515 | } else {
516 | bf("#" + a, "visibility:" + b)
517 | }
518 | }
519 | function bg(a) {
520 | var c = /[\\\"<>\.;]/;
521 | var b = c.exec(a) != null;
522 | return b && typeof encodeURIComponent != m ? encodeURIComponent(a) : a
523 | }
524 | var bp = function() {
525 | if (f.ie && f.win) {
526 | window.attachEvent("onunload", function() {
527 | var a = B.length;
528 | for (var c = 0; c < a; c++) {
529 | B[c][0].detachEvent(B[c][1], B[c][2])
530 | }
531 | var b = H.length;
532 | for (var d = 0; d < b; d++) {
533 | be(H[d])
534 | }
535 | for (var g in f) {
536 | f[g] = null
537 | }
538 | f = null;
539 | for (var i in swfobject) {
540 | swfobject[i] = null
541 | }
542 | swfobject = null
543 | })
544 | }
545 | }();
546 | return {
547 | registerObject: function(a, c, b, d) {
548 | if (f.w3 && a && c) {
549 | var g = {};
550 | g.id = a;
551 | g.swfVersion = c;
552 | g.expressInstall = b;
553 | g.callbackFn = d;
554 | x[x.length] = g;
555 | A(a, false)
556 | } else if (d) {
557 | d({
558 | success: false,
559 | id: a
560 | })
561 | }
562 | },
563 | getObjectById: function(a) {
564 | if (f.w3) {
565 | return R(a)
566 | }
567 | },
568 | embedSWF: function(i, h, o, l, q, p, v, L, M, E) {
569 | var N = {
570 | success: false,
571 | id: h
572 | };
573 | if (f.w3 && !(f.wk && f.wk < 312) && i && h && o && l && q) {
574 | A(h, false);
575 | bd(function() {
576 | o += "";
577 | l += "";
578 | var a = {};
579 | if (M && typeof M === t) {
580 | for (var c in M) {
581 | a[c] = M[c]
582 | }
583 | }
584 | a.data = i;
585 | a.width = o;
586 | a.height = l;
587 | var b = {};
588 | if (L && typeof L === t) {
589 | for (var d in L) {
590 | b[d] = L[d]
591 | }
592 | }
593 | if (v && typeof v === t) {
594 | for (var k in v) {
595 | if (typeof b.flashvars != m) {
596 | b.flashvars += "&" + k + "=" + v[k]
597 | } else {
598 | b.flashvars = k + "=" + v[k]
599 | }
600 | }
601 | }
602 | if (K(q)) {
603 | var g = V(a, b, h);
604 | if (a.id == h) {
605 | A(h, true)
606 | }
607 | N.success = true;
608 | N.ref = g
609 | } else if (p && S()) {
610 | a.data = p;
611 | T(a, b, h, E);
612 | return
613 | } else {
614 | A(h, true)
615 | }
616 | if (E) {
617 | E(N)
618 | }
619 | })
620 | } else if (E) {
621 | E(N)
622 | }
623 | },
624 | switchOffAutoHideShow: function() {
625 | bb = false
626 | },
627 | ua: f,
628 | getFlashPlayerVersion: function() {
629 | return {
630 | major: f.pv[0],
631 | minor: f.pv[1],
632 | release: f.pv[2]
633 | }
634 | },
635 | hasFlashPlayerVersion: K,
636 | createSWF: function(a, c, b) {
637 | if (f.w3) {
638 | return V(a, c, b)
639 | } else {
640 | return undefined
641 | }
642 | },
643 | showExpressInstall: function(a, c, b, d) {
644 | if (f.w3 && S()) {
645 | T(a, c, b, d)
646 | }
647 | },
648 | removeSWF: function(a) {
649 | if (f.w3) {
650 | be(a)
651 | }
652 | },
653 | createCSS: function(a, c, b, d) {
654 | if (f.w3) {
655 | bf(a, c, b, d)
656 | }
657 | },
658 | addDomLoadEvent: bd,
659 | addLoadEvent: bc,
660 | getQueryParamValue: function(a) {
661 | var c = j.location.search || j.location.hash;
662 | if (c) {
663 | if (/\?/.test(c)) {
664 | c = c.split("?")[1]
665 | }
666 | if (a == null) {
667 | return bg(c)
668 | }
669 | var b = c.split("&");
670 | for (var d = 0; d < b.length; d++) {
671 | if (b[d].substring(0, b[d].indexOf("=")) == a) {
672 | return bg(b[d].substring((b[d].indexOf("=") + 1)))
673 | }
674 | }
675 | }
676 | return ""
677 | },
678 | expressInstallCallback: function() {
679 | if (J) {
680 | var a = s(X);
681 | if (a && D) {
682 | a.parentNode.replaceChild(D, a);
683 | if (I) {
684 | A(I, true);
685 | if (f.ie && f.win) {
686 | D.style.display = "block"
687 | }
688 | }
689 | if (O) {
690 | O(ba)
691 | }
692 | }
693 | J = false
694 | }
695 | }
696 | }
697 | }();
698 | window.AudioContext = window.AudioContext || window.webkitAudioContext;
699 | function DynamicAudio(a) {
700 | if (this instanceof arguments.callee) {
701 | if (typeof this.init === "function") {
702 | this.init.apply(this, (a && a.callee) ? a : arguments)
703 | }
704 | } else {
705 | return new arguments.callee(arguments)
706 | }
707 | }
708 | DynamicAudio.VERSION = "0.2";
709 | DynamicAudio.nextId = 1;
710 | DynamicAudio.prototype = {
711 | nextId: null,
712 | swf: 'dynamicaudio.swf'/*tpa=http://fc.liflag.cn/lib/dynamicaudio.swf*/,
713 | audioContext: null,
714 | flashWrapper: null,
715 | flashElement: null,
716 | init: function(c) {
717 | var b = this;
718 | b.id = DynamicAudio.nextId++;
719 | if (c && typeof c['swf'] !== 'undefined') {
720 | b.swf = c['swf']
721 | }
722 | try {
723 | b.audioContext = new AudioContext()
724 | } catch (e) {
725 | console.log('Couldn\'t create AudioContext:' + e + ', falling back to flash player');
726 | b.flashWrapper = document.createElement('div');
727 | b.flashWrapper.id = 'dynamicaudio-flashwrapper-' + b.id;
728 | var d = b.flashWrapper.style;
729 | d['position'] = 'fixed';
730 | d['width'] = d['height'] = '8px';
731 | d['bottom'] = d['left'] = '0px';
732 | d['overflow'] = 'hidden';
733 | b.flashElement = document.createElement('div');
734 | b.flashElement.id = 'dynamicaudio-flashelement-' + b.id;
735 | b.flashWrapper.appendChild(b.flashElement);
736 | document.body.appendChild(b.flashWrapper);
737 | swfobject.embedSWF(b.swf, b.flashElement.id, "8", "8", "9.0.0", null, null, {
738 | 'allowScriptAccess': 'always'
739 | }, null, function(a) {
740 | b.flashElement = a.ref
741 | })
742 | }
743 | },
744 | write: function(a) {
745 | if (this.audioContext !== null) {
746 | this.webAudioWrite(a)
747 | } else if (this.flashElement !== null) {
748 | var c = new Array(a.length);
749 | for (var b = a.length - 1; b !== 0; b--) {
750 | c[b] = this.floatToIntSample(a[b])
751 | }
752 | this.flashElement.write(c.join(' '))
753 | }
754 | },
755 | writeInt: function(a) {
756 | if (this.audioContext !== null) {
757 | this.webAudioWrite(a, this.intToFloatSample)
758 | } else if (this.flashElement !== null) {
759 | this.flashElement.write(a.join(' '))
760 | }
761 | },
762 | webAudioWrite: function(a, c) {
763 | var b = this.audioContext.createBuffer(2, a.length, this.audioContext.sampleRate);
764 | var d = b.getChannelData(0);
765 | var g = b.getChannelData(1);
766 | var i = 0;
767 | if (c) {
768 | for (var h = 0; h < a.length; h += 2) {
769 | d[i] = c(a[h]);
770 | g[i] = c(a[h + 1]);
771 | i++
772 | }
773 | } else {
774 | for (var h = 0; h < a.length; h += 2) {
775 | d[i] = a[h];
776 | g[i] = a[h + 1];
777 | i++
778 | }
779 | }
780 | var o = this.audioContext.createBufferSource();
781 | o.buffer = b;
782 | o.connect(this.audioContext.destination);
783 | o.start()
784 | },
785 | intToFloatSample: function(a) {
786 | return a / 32768
787 | },
788 | floatToIntSample: function(a) {
789 | return Math.floor(a * 32768)
790 | }
791 | };
792 |
--------------------------------------------------------------------------------
/ui.js:
--------------------------------------------------------------------------------
1 | /*
2 | JSNES, based on Jamie Sanders' vNES
3 | Copyright (C) 2010 Ben Firshman
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | This program is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | GNU General Public License for more details.
14 |
15 | You should have received a copy of the GNU General Public License
16 | along with this program. If not, see .
17 | */
18 |
19 | JSNES.DummyUI = function(nes) {
20 | this.nes = nes;
21 | this.enable = function() {};
22 | this.updateStatus = function() {};
23 | this.writeAudio = function() {};
24 | this.writeFrame = function() {};
25 | };
26 |
27 | if (typeof jQuery !== 'undefined') {
28 | (function($) {
29 | $.fn.JSNESUI = function(roms) {
30 | var parent = this;
31 | var UI = function(nes) {
32 | var self = this;
33 | self.nes = nes;
34 |
35 | /*
36 | * Create UI
37 | */
38 | self.status = $('Booting up...
').appendTo(self.root);
39 | self.root = $('');
40 | self.screen = $('').appendTo(self.root);
41 |
42 | if (!self.screen[0].getContext) {
43 | parent.html("Your browser doesn't support the <canvas> tag. Try Google Chrome, Safari, Opera or Firefox!");
44 | return;
45 | }
46 | self.romContainer = $('').appendTo(self.root);
47 | self.romSelect = $('').appendTo(self.romContainer);
48 | self.controls = $('').appendTo(self.root);
49 | self.buttons = {
50 | pause: $('').appendTo(self.controls),
51 | restart: $('').appendTo(self.controls),
52 | sound: $('').appendTo(self.controls),
53 | zoom: $('').appendTo(self.controls)
54 | };
55 | self.root.appendTo(parent);
56 |
57 | /*
58 | * ROM loading
59 | */
60 | self.romSelect.change(function() {
61 | self.loadROM();
62 | });
63 |
64 | /*
65 | * Buttons
66 | */
67 | self.buttons.pause.click(function() {
68 | if (self.nes.isRunning) {
69 | self.nes.stop();
70 | self.updateStatus("Paused");
71 | self.buttons.pause.attr("value", "继续");
72 | } else {
73 | self.nes.start();
74 | self.buttons.pause.attr("value", "暂停");
75 | }
76 | });
77 |
78 | self.buttons.restart.click(function() {
79 | self.nes.reloadRom();
80 | self.nes.start();
81 | });
82 |
83 | self.buttons.sound.click(function() {
84 | if (self.nes.opts.emulateSound) {
85 | self.nes.opts.emulateSound = false;
86 | self.buttons.sound.attr("value", "打开声音");
87 | } else {
88 | self.nes.opts.emulateSound = true;
89 | self.buttons.sound.attr("value", "关闭声音");
90 |
91 | var source = self.audio.createBufferSource();
92 | source.connect(self.audio.destination); // Output to sound
93 | source.start();
94 | }
95 | });
96 |
97 | self.zoomed = false;
98 | $('.nes-screen').css({
99 | 'max-height': document.documentElement.clientHeight,
100 | })
101 | $(window).bind('resize', function() {
102 | $('.nes-screen').css({
103 | 'max-height': document.documentElement.clientHeight,
104 | })
105 | })
106 | if (/(IPHONE|IPAD|ANDROID)/i.test(navigator.userAgent)) {
107 | $('#pc-controlls').hide();
108 | } else {
109 | $('.nes-zoom').hide();
110 | $('#mobile-controlls').hide();
111 | $('.shang').removeClass('anim_m').addClass('pc');
112 | }
113 |
114 | self.buttons.zoom.click(function() {
115 | if (self.zoomed) {
116 | // self.screen.animate({
117 | // width: '512px',
118 | // height: '480px'
119 | // });
120 | $('body').addClass('放大');
121 | if (document.documentElement.clientHeight < screen.availHeight) {
122 | $('body').css({
123 | width: document.documentElement.clientHeight,
124 | })
125 | }
126 | $('.big .nes-screen').css({
127 | height: document.documentElement.clientWidth,
128 | width: 'auto'
129 | })
130 | self.buttons.zoom.attr("value", "放大");
131 | self.zoomed = true;
132 | } else {
133 | // self.screen.animate({
134 | // width: '256px',
135 | // height: '240px'
136 | // });
137 | self.buttons.zoom.attr("value", "缩小");
138 | $('body').removeClass('big');
139 | $('.nes-screen').css({
140 | height: 'auto',
141 | width: '100%',
142 | 'max-width': document.documentElement.clientWidth,
143 | 'max-height': document.documentElement.clientHeight
144 | })
145 | $('body').css({
146 | width: 'auto'
147 | })
148 | self.zoomed = false;
149 | }
150 | });
151 |
152 | /*
153 | * Lightgun experiments with mouse
154 | * (Requires jquery.dimensions.js)
155 | */
156 | if ($.offset) {
157 | self.screen.mousedown(function(e) {
158 | if (self.nes.mmap) {
159 | self.nes.mmap.mousePressed = true;
160 | // FIXME: does not take into account zoom
161 | self.nes.mmap.mouseX = e.pageX - self.screen.offset().left;
162 | self.nes.mmap.mouseY = e.pageY - self.screen.offset().top;
163 | }
164 | }).mouseup(function() {
165 | setTimeout(function() {
166 | if (self.nes.mmap) {
167 | self.nes.mmap.mousePressed = false;
168 | self.nes.mmap.mouseX = 0;
169 | self.nes.mmap.mouseY = 0;
170 | }
171 | }, 500);
172 | });
173 | }
174 |
175 | if (typeof roms != 'undefined') {
176 | self.setRoms(roms);
177 | }
178 |
179 | /*
180 | * Canvas
181 | */
182 | self.canvasContext = self.screen[0].getContext('2d');
183 |
184 | if (!self.canvasContext.getImageData) {
185 | parent.html("Your browser doesn't support writing pixels directly to the <canvas> tag. Try the latest versions of Google Chrome, Safari, Opera or Firefox!");
186 | return;
187 | }
188 |
189 | self.canvasImageData = self.canvasContext.getImageData(0, 0, 256, 240);
190 | self.resetCanvas();
191 |
192 | /*
193 | * Keyboard
194 | */
195 | $(document).
196 | bind('keydown', function(evt) {
197 | self.nes.keyboard.keyDown(evt);
198 | }).
199 | bind('keyup', function(evt) {
200 | self.nes.keyboard.keyUp(evt);
201 | }).
202 | bind('keypress', function(evt) {
203 | self.nes.keyboard.keyPress(evt);
204 | });
205 |
206 | $('#controls-direction').bind('touchstart', function(e) {
207 | handleDirection(e);
208 | e.preventDefault();
209 | })
210 | $('#controls-direction').bind('gesturestart', function(e) {
211 | handleDirection(e);
212 | e.preventDefault();
213 | })
214 | $('#controls-direction').bind('touchmove', function(e) {
215 | handleDirection(e);
216 | })
217 |
218 | function handleDirection(e) {
219 | var myLocation = e.originalEvent.changedTouches[0];
220 | var realTarget = document.elementFromPoint(myLocation.clientX, myLocation.clientY);
221 | if ($(realTarget).hasClass('leftup')) {
222 | $('#controls-direction .left').addClass('active');
223 | $('#controls-direction .up').addClass('active');
224 | $('#controls-direction .down').removeClass('active');
225 | $('#controls-direction .right').removeClass('active');
226 | self.nes.keyboard.keyDown({
227 | keyCode: 37
228 | });
229 | self.nes.keyboard.keyDown({
230 | keyCode: 38
231 | });
232 | self.nes.keyboard.keyUp({
233 | keyCode: 39
234 | });
235 | self.nes.keyboard.keyUp({
236 | keyCode: 40
237 | });
238 | } else if ($(realTarget).hasClass('up')) {
239 | $('#controls-direction .left').removeClass('active');
240 | $('#controls-direction .up').addClass('active');
241 | $('#controls-direction .down').removeClass('active');
242 | $('#controls-direction .right').removeClass('active');
243 | self.nes.keyboard.keyUp({
244 | keyCode: 37
245 | });
246 | self.nes.keyboard.keyDown({
247 | keyCode: 38
248 | });
249 | self.nes.keyboard.keyUp({
250 | keyCode: 39
251 | });
252 | self.nes.keyboard.keyUp({
253 | keyCode: 40
254 | });
255 | } else if ($(realTarget).hasClass('rightup')) {
256 | $('#controls-direction .left').removeClass('active');
257 | $('#controls-direction .up').addClass('active');
258 | $('#controls-direction .down').removeClass('active');
259 | $('#controls-direction .right').addClass('active');
260 | self.nes.keyboard.keyUp({
261 | keyCode: 37
262 | });
263 | self.nes.keyboard.keyDown({
264 | keyCode: 38
265 | });
266 | self.nes.keyboard.keyDown({
267 | keyCode: 39
268 | });
269 | self.nes.keyboard.keyUp({
270 | keyCode: 40
271 | });
272 | } else if ($(realTarget).hasClass('left')) {
273 | $('#controls-direction .left').addClass('active');
274 | $('#controls-direction .up').removeClass('active');
275 | $('#controls-direction .down').removeClass('active');
276 | $('#controls-direction .right').removeClass('active');
277 | self.nes.keyboard.keyDown({
278 | keyCode: 37
279 | });
280 | self.nes.keyboard.keyUp({
281 | keyCode: 38
282 | });
283 | self.nes.keyboard.keyUp({
284 | keyCode: 39
285 | });
286 | self.nes.keyboard.keyUp({
287 | keyCode: 40
288 | });
289 | } else if ($(realTarget).hasClass('center')) {
290 | $('#controls-direction .left').removeClass('active');
291 | $('#controls-direction .up').removeClass('active');
292 | $('#controls-direction .down').removeClass('active');
293 | $('#controls-direction .right').removeClass('active');
294 | self.nes.keyboard.keyUp({
295 | keyCode: 37
296 | });
297 | self.nes.keyboard.keyUp({
298 | keyCode: 38
299 | });
300 | self.nes.keyboard.keyUp({
301 | keyCode: 39
302 | });
303 | self.nes.keyboard.keyUp({
304 | keyCode: 40
305 | });
306 | } else if ($(realTarget).hasClass('right')) {
307 | $('#controls-direction .left').removeClass('active');
308 | $('#controls-direction .up').removeClass('active');
309 | $('#controls-direction .down').removeClass('active');
310 | $('#controls-direction .right').addClass('active');
311 | self.nes.keyboard.keyUp({
312 | keyCode: 37
313 | });
314 | self.nes.keyboard.keyUp({
315 | keyCode: 38
316 | });
317 | self.nes.keyboard.keyDown({
318 | keyCode: 39
319 | });
320 | self.nes.keyboard.keyUp({
321 | keyCode: 40
322 | });
323 | } else if ($(realTarget).hasClass('leftdown')) {
324 | $('#controls-direction .left').addClass('active');
325 | $('#controls-direction .up').removeClass('active');
326 | $('#controls-direction .down').addClass('active');
327 | $('#controls-direction .right').removeClass('active');
328 | self.nes.keyboard.keyDown({
329 | keyCode: 37
330 | });
331 | self.nes.keyboard.keyUp({
332 | keyCode: 38
333 | });
334 | self.nes.keyboard.keyUp({
335 | keyCode: 39
336 | });
337 | self.nes.keyboard.keyDown({
338 | keyCode: 40
339 | });
340 | } else if ($(realTarget).hasClass('down')) {
341 | $('#controls-direction .left').removeClass('active');
342 | $('#controls-direction .up').removeClass('active');
343 | $('#controls-direction .down').addClass('active');
344 | $('#controls-direction .right').removeClass('active');
345 | self.nes.keyboard.keyUp({
346 | keyCode: 37
347 | });
348 | self.nes.keyboard.keyUp({
349 | keyCode: 38
350 | });
351 | self.nes.keyboard.keyUp({
352 | keyCode: 39
353 | });
354 | self.nes.keyboard.keyDown({
355 | keyCode: 40
356 | });
357 | } else if ($(realTarget).hasClass('rightdown')) {
358 | $('#controls-direction .left').removeClass('active');
359 | $('#controls-direction .up').removeClass('active');
360 | $('#controls-direction .down').addClass('active');
361 | $('#controls-direction .right').addClass('active');
362 | self.nes.keyboard.keyUp({
363 | keyCode: 37
364 | });
365 | self.nes.keyboard.keyUp({
366 | keyCode: 38
367 | });
368 | self.nes.keyboard.keyDown({
369 | keyCode: 39
370 | });
371 | self.nes.keyboard.keyDown({
372 | keyCode: 40
373 | });
374 | }
375 |
376 | }
377 | $('#controls-direction').bind('touchend', function(e) {
378 | $('#controls-direction .left').removeClass('active');
379 | $('#controls-direction .up').removeClass('active');
380 | $('#controls-direction .down').removeClass('active');
381 | $('#controls-direction .right').removeClass('active');
382 | self.nes.keyboard.keyUp({
383 | keyCode: 37
384 | });
385 | self.nes.keyboard.keyUp({
386 | keyCode: 38
387 | });
388 | self.nes.keyboard.keyUp({
389 | keyCode: 39
390 | });
391 | self.nes.keyboard.keyUp({
392 | keyCode: 40
393 | });
394 | });
395 |
396 | $('#joystick_btn_up').bind('touchstart', function(e) {
397 | self.nes.keyboard.keyDown({
398 | keyCode: 87
399 | });
400 | e.preventDefault();
401 |
402 | });
403 | $('#joystick_btn_up').bind('touchend', function(e) {
404 | self.nes.keyboard.keyUp({
405 | keyCode: 87
406 | });
407 | e.preventDefault();
408 | });
409 | $('#joystick_btn_down').bind('touchstart', function(e) {
410 | self.nes.keyboard.keyDown({
411 | keyCode: 83
412 | });
413 | e.preventDefault();
414 | });
415 | $('#joystick_btn_down').bind('touchend', function(e) {
416 | self.nes.keyboard.keyUp({
417 | keyCode: 83
418 | });
419 | e.preventDefault();
420 | });
421 | $('#joystick_btn_left').bind('touchstart', function(e) {
422 | self.nes.keyboard.keyDown({
423 | keyCode: 65
424 | });
425 | e.preventDefault();
426 | });
427 | $('#joystick_btn_left').bind('touchend', function(e) {
428 | self.nes.keyboard.keyUp({
429 | keyCode: 65
430 | });
431 | e.preventDefault();
432 | });
433 | $('#joystick_btn_right').bind('touchstart', function(e) {
434 | console.log("right");
435 | self.nes.keyboard.keyDown({
436 | keyCode: 68
437 | });
438 | e.preventDefault();
439 | });
440 | $('#joystick_btn_right').bind('touchend', function(e) {
441 | self.nes.keyboard.keyUp({
442 | keyCode: 68
443 | });
444 | e.preventDefault();
445 | });
446 | $('#joystick_btn_A').bind('touchstart', function(e) {
447 | console.log("a");
448 | self.nes.keyboard.keyDown({
449 | keyCode: 74
450 | });
451 | e.preventDefault();
452 | });
453 | $('#joystick_btn_A').bind('touchend', function(e) {
454 | self.nes.keyboard.keyUp({
455 | keyCode: 74
456 | });
457 | e.preventDefault();
458 | });
459 | $('#joystick_btn_B').bind('touchstart', function(e) {
460 | console.log("b");
461 | self.nes.keyboard.keyDown({
462 | keyCode: 75
463 | });
464 | e.preventDefault();
465 | });
466 | $('#joystick_btn_B').bind('touchend', function(e) {
467 | self.nes.keyboard.keyUp({
468 | keyCode: 75
469 | });
470 | e.preventDefault();
471 | });
472 | $('#joystick_btn_select').bind('touchstart', function(e) {
473 | self.nes.keyboard.keyDown({
474 | keyCode: 17
475 | });
476 | $('#joystick_btn_select').addClass('active');
477 | e.preventDefault();
478 | });
479 | $('#joystick_btn_select').bind('touchend', function(e) {
480 | self.nes.keyboard.keyUp({
481 | keyCode: 17
482 | });
483 | $('#joystick_btn_select').removeClass('active');
484 | e.preventDefault();
485 | });
486 | $('#joystick_btn_start').bind('touchstart', function(e) {
487 | self.nes.keyboard.keyDown({
488 | keyCode: 13
489 | });
490 | $('#joystick_btn_start').addClass('active');
491 | e.preventDefault();
492 | });
493 | $('#joystick_btn_start').bind('touchend', function(e) {
494 | self.nes.keyboard.keyUp({
495 | keyCode: 13
496 | });
497 | $('#joystick_btn_start').removeClass('active');
498 | e.preventDefault();
499 | });
500 |
501 | $('#controls-fire').bind('touchstart', function(e) {
502 | handleFire(e);
503 | e.preventDefault();
504 | });
505 | $('#controls-fire').bind('gesturestart', function(e) {
506 | handleFire(e, true);
507 | e.preventDefault();
508 | });
509 | $('#controls-fire').bind('touchmove', function(e) {
510 | handleFire(e);
511 | e.preventDefault();
512 | });
513 | $('#controls-fire').bind('touchend', function(e) {
514 | clearInterval(self.interval);
515 | $('#controls-fire .a').removeClass('active');
516 | $('#controls-fire .b').removeClass('active');
517 | self.nes.keyboard.keyUp({
518 | keyCode: 88
519 | });
520 | self.nes.keyboard.keyUp({
521 | keyCode: 90
522 | });
523 | e.preventDefault();
524 | });
525 | $('#controls-turbofire').bind('touchstart', function(e) {
526 | handleFire(e, true);
527 | e.preventDefault();
528 | });
529 | $('#controls-turbofire').bind('gesturestart', function(e) {
530 | handleFire(e, true);
531 | e.preventDefault();
532 | });
533 | $('#controls-turbofire').bind('touchmove', function(e) {
534 | handleFire(e, true);
535 | e.preventDefault();
536 | });
537 | $('#controls-turbofire').bind('touchend', function(e) {
538 | clearInterval(self.interval);
539 | $('#controls-turbofire .a').removeClass('active');
540 | $('#controls-turbofire .b').removeClass('active');
541 | self.nes.keyboard.keyUp({
542 | keyCode: 88
543 | });
544 | self.nes.keyboard.keyUp({
545 | keyCode: 90
546 | });
547 | e.preventDefault();
548 | });
549 |
550 | function handleFire(e, turbo) {
551 | var parent = $('#controls-fire');
552 | if (turbo) {
553 | parent = $('#controls-turbofire');
554 | }
555 | var myLocation = e.originalEvent.changedTouches[0];
556 | var realTarget = document.elementFromPoint(myLocation.clientX, myLocation.clientY);
557 | if ($(realTarget).hasClass('a')) {
558 | $('.a', parent).addClass('active');
559 | $('.b', parent).removeClass('active');
560 | clearInterval(self.interval);
561 | if (turbo) {
562 | self.nes.keyboard.keyDown({
563 | keyCode: 'AA'
564 | });
565 | self.nes.keyboard.keyUp({
566 | keyCode: 90
567 | });
568 | self.interval = setInterval(function() {
569 | self.nes.keyboard.keyDown({
570 | keyCode: 'AA'
571 | });
572 | }, 50);
573 | } else {
574 | self.nes.keyboard.keyDown({
575 | keyCode: 88
576 | });
577 | self.nes.keyboard.keyUp({
578 | keyCode: 90
579 | });
580 | }
581 | } else if ($(realTarget).hasClass('b')) {
582 | $('.a', parent).removeClass('active');
583 | $('.b', parent).addClass('active');
584 | clearInterval(self.interval);
585 | if (turbo) {
586 | self.nes.keyboard.keyUp({
587 | keyCode: 88
588 | });
589 | self.nes.keyboard.keyDown({
590 | keyCode: 'BB'
591 | });
592 | self.interval = setInterval(function() {
593 | self.nes.keyboard.keyDown({
594 | keyCode: 'BB'
595 | });
596 | }, 50);
597 | } else {
598 | self.nes.keyboard.keyUp({
599 | keyCode: 88
600 | });
601 | self.nes.keyboard.keyDown({
602 | keyCode: 90
603 | });
604 | }
605 | } else if ($(realTarget).hasClass('c')) {
606 | $('.a', parent).addClass('active');
607 | $('.b', parent).addClass('active');
608 | clearInterval(self.interval);
609 | if (turbo) {
610 | self.nes.keyboard.keyDown({
611 | keyCode: 'AA'
612 | });
613 | self.nes.keyboard.keyDown({
614 | keyCode: 'BB'
615 | });
616 | self.interval = setInterval(function() {
617 | self.nes.keyboard.keyDown({
618 | keyCode: 'AA'
619 | });
620 | self.nes.keyboard.keyDown({
621 | keyCode: 'BB'
622 | });
623 | }, 50);
624 | } else {
625 | self.nes.keyboard.keyDown({
626 | keyCode: 88
627 | });
628 | self.nes.keyboard.keyDown({
629 | keyCode: 90
630 | });
631 | }
632 | } else {
633 | clearInterval(self.interval);
634 | $('.a', parent).removeClass('active');
635 | $('.b', parent).removeClass('active');
636 | self.nes.keyboard.keyUp({
637 | keyCode: 88
638 | });
639 | self.nes.keyboard.keyUp({
640 | keyCode: 90
641 | });
642 |
643 | }
644 | }
645 |
646 | /*
647 | * Sound
648 | */
649 | // Workaround prefixed naming used in Safary 8-9
650 | // self.dynamicaudio = new DynamicAudio({
651 | // swf: nes.opts.swfPath+'dynamicaudio.swf'
652 | // });
653 | window.AudioContext = window.webkitAudioContext || window.AudioContext;
654 | try {
655 | self.audio = new AudioContext();
656 | } catch (e) {
657 | // lets fallback to Flash (for Internet Explorer 8-11)
658 | console.error(e);
659 | self.dynamicaudio = new DynamicAudio({
660 | swf: nes.opts.swfPath + 'dynamicaudio.swf'
661 | });
662 | }
663 | };
664 |
665 | UI.prototype = {
666 | loadROM: function() {
667 | var self = this;
668 | self.updateStatus("Downloading...");
669 | $.ajax({
670 | url: escape(self.romSelect.val()),
671 | xhr: function() {
672 | var xhr = $.ajaxSettings.xhr();
673 | if (typeof xhr.overrideMimeType !== 'undefined') {
674 | // Download as binary
675 | xhr.overrideMimeType('text/plain; charset=x-user-defined');
676 | }
677 | self.xhr = xhr;
678 | return xhr;
679 | },
680 | complete: function(xhr, status) {
681 | var i, data;
682 | if (JSNES.Utils.isIE()) {
683 | var charCodes = JSNESBinaryToArray(
684 | xhr.responseBody
685 | ).toArray();
686 | data = String.fromCharCode.apply(
687 | undefined,
688 | charCodes
689 | );
690 | } else {
691 | data = xhr.responseText;
692 | }
693 | self.nes.loadRom(data);
694 | self.nes.start();
695 | self.enable();
696 | }
697 | });
698 | },
699 |
700 | resetCanvas: function() {
701 | this.canvasContext.fillStyle = 'black';
702 | // set alpha to opaque
703 | this.canvasContext.fillRect(0, 0, 256, 240);
704 |
705 | // Set alpha
706 | for (var i = 3; i < this.canvasImageData.data.length - 3; i += 4) {
707 | this.canvasImageData.data[i] = 0xFF;
708 | }
709 | },
710 |
711 | /*
712 | *
713 | * nes.ui.screenshot() --> return
element :)
714 | */
715 | screenshot: function() {
716 | var data = this.screen[0].toDataURL("image/png"),
717 | img = new Image();
718 | img.src = data;
719 | return img;
720 | },
721 |
722 | /*
723 | * Enable and reset UI elements
724 | */
725 | enable: function() {
726 | this.buttons.pause.attr("disabled", null);
727 | if (this.nes.isRunning) {
728 | this.buttons.pause.attr("value", "暂停");
729 | } else {
730 | this.buttons.pause.attr("value", "继续");
731 | }
732 | this.buttons.restart.attr("disabled", null);
733 | if (this.nes.opts.emulateSound) {
734 | this.buttons.sound.attr("value", "关闭声音");
735 | } else {
736 | this.buttons.sound.attr("value", "打开声音");
737 | }
738 | },
739 |
740 | updateStatus: function(s) {
741 | this.status.text(s);
742 | },
743 |
744 | setRoms: function(roms) {
745 | this.romSelect.children().remove();
746 | $("").appendTo(this.romSelect);
747 | for (var groupName in roms) {
748 | if (roms.hasOwnProperty(groupName)) {
749 | var optgroup = $('').
750 | attr("label", groupName);
751 | for (var i = 0; i < roms[groupName].length; i++) {
752 | $('')
753 | .attr("value", roms[groupName][i][1])
754 | .appendTo(optgroup);
755 | }
756 | this.romSelect.append(optgroup);
757 | }
758 | }
759 | },
760 |
761 | writeAudio: function(samples) {
762 | //return this.dynamicaudio.writeInt(samples);
763 | // Use fallback if available and return early
764 | if (this.dynamicaudio) {
765 | return this.dynamicaudio.writeInt(samples);
766 | }
767 | // Create output buffer (planar buffer format)
768 | var buffer = this.audio.createBuffer(2, samples.length, this.audio.sampleRate);
769 | var channelLeft = buffer.getChannelData(0);
770 | var channelRight = buffer.getChannelData(1);
771 | // Convert from interleaved buffer format to planar buffer
772 | // by writing right into appropriate channel buffers
773 | var j = 0;
774 | for (var i = 0; i < samples.length; i += 2) {
775 | channelLeft[j] = this.intToFloatSample(samples[i]);
776 | channelRight[j] = this.intToFloatSample(samples[i + 1]);
777 | j++;
778 | }
779 | // Create sound source and play it
780 | var source = this.audio.createBufferSource();
781 | source.buffer = buffer;
782 | source.connect(this.audio.destination); // Output to sound
783 | // card
784 | source.start();
785 | },
786 | // Local helper function to convert Int output to Float
787 | // TODO: remove intToFloat and revise papu.js -> sample()
788 | // to return AudioBuffer/Float32Array output used in HTML5 WebAudio API
789 | intToFloatSample: function(value) {
790 | return value / 32767; // from -32767..32768 to -1..1 range
791 | },
792 |
793 | writeFrame: function(buffer, prevBuffer) {
794 | var imageData = this.canvasImageData.data;
795 | var pixel, i, j;
796 |
797 | for (i = 0; i < 256 * 240; i++) {
798 | pixel = buffer[i];
799 |
800 | if (pixel != prevBuffer[i]) {
801 | j = i * 4;
802 | imageData[j] = pixel & 0xFF;
803 | imageData[j + 1] = (pixel >> 8) & 0xFF;
804 | imageData[j + 2] = (pixel >> 16) & 0xFF;
805 | prevBuffer[i] = pixel;
806 | }
807 | }
808 |
809 | this.canvasContext.putImageData(this.canvasImageData, 0, 0);
810 | }
811 | };
812 |
813 | return UI;
814 | };
815 | })(jQuery);
816 | }
--------------------------------------------------------------------------------
/mappers.js:
--------------------------------------------------------------------------------
1 | /*
2 | JSNES, based on Jamie Sanders' vNES
3 | Copyright (C) 2010 Ben Firshman
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | This program is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | GNU General Public License for more details.
14 |
15 | You should have received a copy of the GNU General Public License
16 | along with this program. If not, see .
17 | */
18 |
19 | JSNES.Mappers = {};
20 |
21 | JSNES.Mappers[0] = function(nes) {
22 | this.nes = nes;
23 | };
24 |
25 | JSNES.Mappers[0].prototype = {
26 | reset: function() {
27 | this.joy1StrobeState = 0;
28 | this.joy2StrobeState = 0;
29 | this.joypadLastWrite = 0;
30 |
31 | this.mousePressed = false;
32 | this.mouseX = null;
33 | this.mouseY = null;
34 | },
35 |
36 | write: function(address, value) {
37 | if (address < 0x2000) {
38 | // Mirroring of RAM:
39 | this.nes.cpu.mem[address & 0x7FF] = value;
40 |
41 | } else if (address > 0x4017) {
42 | this.nes.cpu.mem[address] = value;
43 | if (address >= 0x6000 && address < 0x8000) {
44 | // Write to SaveRAM. Store in file:
45 | // TODO: not yet
46 | //if(this.nes.rom!=null)
47 | // this.nes.rom.writeBatteryRam(address,value);
48 | }
49 | } else if (address > 0x2007 && address < 0x4000) {
50 | this.regWrite(0x2000 + (address & 0x7), value);
51 | } else {
52 | this.regWrite(address, value);
53 | }
54 | },
55 |
56 | writelow: function(address, value) {
57 | if (address < 0x2000) {
58 | // Mirroring of RAM:
59 | this.nes.cpu.mem[address & 0x7FF] = value;
60 | } else if (address > 0x4017) {
61 | this.nes.cpu.mem[address] = value;
62 | } else if (address > 0x2007 && address < 0x4000) {
63 | this.regWrite(0x2000 + (address & 0x7), value);
64 | } else {
65 | this.regWrite(address, value);
66 | }
67 | },
68 |
69 | load: function(address) {
70 | // Wrap around:
71 | address &= 0xFFFF;
72 |
73 | // Check address range:
74 | if (address > 0x4017) {
75 | // ROM:
76 | return this.nes.cpu.mem[address];
77 | } else if (address >= 0x2000) {
78 | // I/O Ports.
79 | return this.regLoad(address);
80 | } else {
81 | // RAM (mirrored)
82 | return this.nes.cpu.mem[address & 0x7FF];
83 | }
84 | },
85 |
86 | regLoad: function(address) {
87 | switch (address >> 12) { // use fourth nibble (0xF000)
88 | case 0:
89 | break;
90 |
91 | case 1:
92 | break;
93 |
94 | case 2:
95 | // Fall through to case 3
96 | case 3:
97 | // PPU Registers
98 | switch (address & 0x7) {
99 | case 0x0:
100 | // 0x2000:
101 | // PPU Control Register 1.
102 | // (the value is stored both
103 | // in main memory and in the
104 | // PPU as flags):
105 | // (not in the real NES)
106 | return this.nes.cpu.mem[0x2000];
107 |
108 | case 0x1:
109 | // 0x2001:
110 | // PPU Control Register 2.
111 | // (the value is stored both
112 | // in main memory and in the
113 | // PPU as flags):
114 | // (not in the real NES)
115 | return this.nes.cpu.mem[0x2001];
116 |
117 | case 0x2:
118 | // 0x2002:
119 | // PPU Status Register.
120 | // The value is stored in
121 | // main memory in addition
122 | // to as flags in the PPU.
123 | // (not in the real NES)
124 | return this.nes.ppu.readStatusRegister();
125 |
126 | case 0x3:
127 | return 0;
128 |
129 | case 0x4:
130 | // 0x2004:
131 | // Sprite Memory read.
132 | return this.nes.ppu.sramLoad();
133 | case 0x5:
134 | return 0;
135 |
136 | case 0x6:
137 | return 0;
138 |
139 | case 0x7:
140 | // 0x2007:
141 | // VRAM read:
142 | return this.nes.ppu.vramLoad();
143 | }
144 | break;
145 | case 4:
146 | // Sound+Joypad registers
147 | switch (address - 0x4015) {
148 | case 0:
149 | // 0x4015:
150 | // Sound channel enable, DMC Status
151 | return this.nes.papu.readReg(address);
152 |
153 | case 1:
154 | // 0x4016:
155 | // Joystick 1 + Strobe
156 | return this.joy1Read();
157 |
158 | case 2:
159 | // 0x4017:
160 | // Joystick 2 + Strobe
161 | if (this.mousePressed) {
162 |
163 | // Check for white pixel nearby:
164 | var sx = Math.max(0, this.mouseX - 4);
165 | var ex = Math.min(256, this.mouseX + 4);
166 | var sy = Math.max(0, this.mouseY - 4);
167 | var ey = Math.min(240, this.mouseY + 4);
168 | var w = 0;
169 |
170 | for (var y = sy; y < ey; y++) {
171 | for (var x = sx; x < ex; x++) {
172 |
173 | if (this.nes.ppu.buffer[(y << 8) + x] == 0xFFFFFF) {
174 | w |= 0x1 << 3;
175 | console.debug("Clicked on white!");
176 | break;
177 | }
178 | }
179 | }
180 |
181 | w |= (this.mousePressed ? (0x1 << 4) : 0);
182 | return (this.joy2Read() | w) & 0xFFFF;
183 | } else {
184 | return this.joy2Read();
185 | }
186 |
187 | }
188 | break;
189 | }
190 | return 0;
191 | },
192 |
193 | regWrite: function(address, value) {
194 | switch (address) {
195 | case 0x2000:
196 | // PPU Control register 1
197 | this.nes.cpu.mem[address] = value;
198 | this.nes.ppu.updateControlReg1(value);
199 | break;
200 |
201 | case 0x2001:
202 | // PPU Control register 2
203 | this.nes.cpu.mem[address] = value;
204 | this.nes.ppu.updateControlReg2(value);
205 | break;
206 |
207 | case 0x2003:
208 | // Set Sprite RAM address:
209 | this.nes.ppu.writeSRAMAddress(value);
210 | break;
211 |
212 | case 0x2004:
213 | // Write to Sprite RAM:
214 | this.nes.ppu.sramWrite(value);
215 | break;
216 |
217 | case 0x2005:
218 | // Screen Scroll offsets:
219 | this.nes.ppu.scrollWrite(value);
220 | break;
221 |
222 | case 0x2006:
223 | // Set VRAM address:
224 | this.nes.ppu.writeVRAMAddress(value);
225 | break;
226 |
227 | case 0x2007:
228 | // Write to VRAM:
229 | this.nes.ppu.vramWrite(value);
230 | break;
231 |
232 | case 0x4014:
233 | // Sprite Memory DMA Access
234 | this.nes.ppu.sramDMA(value);
235 | break;
236 |
237 | case 0x4015:
238 | // Sound Channel Switch, DMC Status
239 | this.nes.papu.writeReg(address, value);
240 | break;
241 |
242 | case 0x4016:
243 | // Joystick 1 + Strobe
244 | if ((value & 1) === 0 && (this.joypadLastWrite & 1) === 1) {
245 | this.joy1StrobeState = 0;
246 | this.joy2StrobeState = 0;
247 | }
248 | this.joypadLastWrite = value;
249 | break;
250 |
251 | case 0x4017:
252 | // Sound channel frame sequencer:
253 | this.nes.papu.writeReg(address, value);
254 | break;
255 |
256 | default:
257 | // Sound registers
258 | ////System.out.println("write to sound reg");
259 | if (address >= 0x4000 && address <= 0x4017) {
260 | this.nes.papu.writeReg(address, value);
261 | }
262 |
263 | }
264 | },
265 |
266 | joy1Read: function() {
267 | var ret;
268 |
269 | switch (this.joy1StrobeState) {
270 | case 0:
271 | case 1:
272 | case 2:
273 | case 3:
274 | case 4:
275 | case 5:
276 | case 6:
277 | case 7:
278 | ret = this.nes.keyboard.state1[this.joy1StrobeState];
279 | break;
280 | case 8:
281 | case 9:
282 | case 10:
283 | case 11:
284 | case 12:
285 | case 13:
286 | case 14:
287 | case 15:
288 | case 16:
289 | case 17:
290 | case 18:
291 | ret = 0;
292 | break;
293 | case 19:
294 | ret = 1;
295 | break;
296 | default:
297 | ret = 0;
298 | }
299 |
300 | this.joy1StrobeState++;
301 | if (this.joy1StrobeState == 24) {
302 | this.joy1StrobeState = 0;
303 | }
304 |
305 | return ret;
306 | },
307 |
308 | joy2Read: function() {
309 | var ret;
310 |
311 | switch (this.joy2StrobeState) {
312 | case 0:
313 | case 1:
314 | case 2:
315 | case 3:
316 | case 4:
317 | case 5:
318 | case 6:
319 | case 7:
320 | ret = this.nes.keyboard.state2[this.joy2StrobeState];
321 | break;
322 | case 8:
323 | case 9:
324 | case 10:
325 | case 11:
326 | case 12:
327 | case 13:
328 | case 14:
329 | case 15:
330 | case 16:
331 | case 17:
332 | case 18:
333 | ret = 0;
334 | break;
335 | case 19:
336 | ret = 1;
337 | break;
338 | default:
339 | ret = 0;
340 | }
341 |
342 | this.joy2StrobeState++;
343 | if (this.joy2StrobeState == 24) {
344 | this.joy2StrobeState = 0;
345 | }
346 |
347 | return ret;
348 | },
349 |
350 | loadROM: function() {
351 | if (!this.nes.rom.valid || this.nes.rom.romCount < 1) {
352 | alert("NoMapper: Invalid ROM! Unable to load.");
353 | return;
354 | }
355 |
356 | // Load ROM into memory:
357 | this.loadPRGROM();
358 |
359 | // Load CHR-ROM:
360 | this.loadCHRROM();
361 |
362 | // Load Battery RAM (if present):
363 | this.loadBatteryRam();
364 |
365 | // Reset IRQ:
366 | //nes.getCpu().doResetInterrupt();
367 | this.nes.cpu.requestIrq(this.nes.cpu.IRQ_RESET);
368 | },
369 |
370 | loadPRGROM: function() {
371 | if (this.nes.rom.romCount > 1) {
372 | // Load the two first banks into memory.
373 | this.loadRomBank(0, 0x8000);
374 | this.loadRomBank(1, 0xC000);
375 | } else {
376 | // Load the one bank into both memory locations:
377 | this.loadRomBank(0, 0x8000);
378 | this.loadRomBank(0, 0xC000);
379 | }
380 | },
381 |
382 | loadCHRROM: function() {
383 | ////System.out.println("Loading CHR ROM..");
384 | if (this.nes.rom.vromCount > 0) {
385 | if (this.nes.rom.vromCount == 1) {
386 | this.loadVromBank(0, 0x0000);
387 | this.loadVromBank(0, 0x1000);
388 | } else {
389 | this.loadVromBank(0, 0x0000);
390 | this.loadVromBank(1, 0x1000);
391 | }
392 | } else {
393 | //System.out.println("There aren't any CHR-ROM banks..");
394 | }
395 | },
396 |
397 | loadBatteryRam: function() {
398 | if (this.nes.rom.batteryRam) {
399 | var ram = this.nes.rom.batteryRam;
400 | if (ram !== null && ram.length == 0x2000) {
401 | // Load Battery RAM into memory:
402 | JSNES.Utils.copyArrayElements(ram, 0, this.nes.cpu.mem, 0x6000, 0x2000);
403 | }
404 | }
405 | },
406 |
407 | loadRomBank: function(bank, address) {
408 | // Loads a ROM bank into the specified address.
409 | bank %= this.nes.rom.romCount;
410 | //var data = this.nes.rom.rom[bank];
411 | //cpuMem.write(address,data,data.length);
412 | JSNES.Utils.copyArrayElements(this.nes.rom.rom[bank], 0, this.nes.cpu.mem, address, 16384);
413 | },
414 |
415 | loadVromBank: function(bank, address) {
416 | if (this.nes.rom.vromCount === 0) {
417 | return;
418 | }
419 | this.nes.ppu.triggerRendering();
420 |
421 | JSNES.Utils.copyArrayElements(this.nes.rom.vrom[bank % this.nes.rom.vromCount],
422 | 0, this.nes.ppu.vramMem, address, 4096);
423 |
424 | var vromTile = this.nes.rom.vromTile[bank % this.nes.rom.vromCount];
425 | JSNES.Utils.copyArrayElements(vromTile, 0, this.nes.ppu.ptTile, address >> 4, 256);
426 | },
427 |
428 | load32kRomBank: function(bank, address) {
429 | this.loadRomBank((bank * 2) % this.nes.rom.romCount, address);
430 | this.loadRomBank((bank * 2 + 1) % this.nes.rom.romCount, address + 16384);
431 | },
432 |
433 | load8kVromBank: function(bank4kStart, address) {
434 | if (this.nes.rom.vromCount === 0) {
435 | return;
436 | }
437 | this.nes.ppu.triggerRendering();
438 |
439 | this.loadVromBank((bank4kStart) % this.nes.rom.vromCount, address);
440 | this.loadVromBank((bank4kStart + 1) % this.nes.rom.vromCount,
441 | address + 4096);
442 | },
443 |
444 | load1kVromBank: function(bank1k, address) {
445 | if (this.nes.rom.vromCount === 0) {
446 | return;
447 | }
448 | this.nes.ppu.triggerRendering();
449 |
450 | var bank4k = Math.floor(bank1k / 4) % this.nes.rom.vromCount;
451 | var bankoffset = (bank1k % 4) * 1024;
452 | JSNES.Utils.copyArrayElements(this.nes.rom.vrom[bank4k], 0,
453 | this.nes.ppu.vramMem, bankoffset, 1024);
454 |
455 | // Update tiles:
456 | var vromTile = this.nes.rom.vromTile[bank4k];
457 | var baseIndex = address >> 4;
458 | for (var i = 0; i < 64; i++) {
459 | this.nes.ppu.ptTile[baseIndex + i] = vromTile[((bank1k % 4) << 6) + i];
460 | }
461 | },
462 |
463 | load2kVromBank: function(bank2k, address) {
464 | if (this.nes.rom.vromCount === 0) {
465 | return;
466 | }
467 | this.nes.ppu.triggerRendering();
468 |
469 | var bank4k = Math.floor(bank2k / 2) % this.nes.rom.vromCount;
470 | var bankoffset = (bank2k % 2) * 2048;
471 | JSNES.Utils.copyArrayElements(this.nes.rom.vrom[bank4k], bankoffset,
472 | this.nes.ppu.vramMem, address, 2048);
473 |
474 | // Update tiles:
475 | var vromTile = this.nes.rom.vromTile[bank4k];
476 | var baseIndex = address >> 4;
477 | for (var i = 0; i < 128; i++) {
478 | this.nes.ppu.ptTile[baseIndex + i] = vromTile[((bank2k % 2) << 7) + i];
479 | }
480 | },
481 |
482 | load8kRomBank: function(bank8k, address) {
483 | var bank16k = Math.floor(bank8k / 2) % this.nes.rom.romCount;
484 | var offset = (bank8k % 2) * 8192;
485 |
486 | //this.nes.cpu.mem.write(address,this.nes.rom.rom[bank16k],offset,8192);
487 | JSNES.Utils.copyArrayElements(this.nes.rom.rom[bank16k], offset,
488 | this.nes.cpu.mem, address, 8192);
489 | },
490 |
491 | clockIrqCounter: function() {
492 | // Does nothing. This is used by the MMC3 mapper.
493 | },
494 |
495 | latchAccess: function(address) {
496 | // Does nothing. This is used by MMC2.
497 | },
498 |
499 | toJSON: function() {
500 | return {
501 | 'joy1StrobeState': this.joy1StrobeState,
502 | 'joy2StrobeState': this.joy2StrobeState,
503 | 'joypadLastWrite': this.joypadLastWrite
504 | };
505 | },
506 |
507 | fromJSON: function(s) {
508 | this.joy1StrobeState = s.joy1StrobeState;
509 | this.joy2StrobeState = s.joy2StrobeState;
510 | this.joypadLastWrite = s.joypadLastWrite;
511 | }
512 | };
513 |
514 |
515 | JSNES.Mappers[1] = function(nes) {
516 | this.nes = nes;
517 | };
518 |
519 | JSNES.Mappers[1].prototype = new JSNES.Mappers[0]();
520 |
521 | JSNES.Mappers[1].prototype.reset = function() {
522 | JSNES.Mappers[0].prototype.reset.apply(this);
523 |
524 | // 5-bit buffer:
525 | this.regBuffer = 0;
526 | this.regBufferCounter = 0;
527 |
528 | // Register 0:
529 | this.mirroring = 0;
530 | this.oneScreenMirroring = 0;
531 | this.prgSwitchingArea = 1;
532 | this.prgSwitchingSize = 1;
533 | this.vromSwitchingSize = 0;
534 |
535 | // Register 1:
536 | this.romSelectionReg0 = 0;
537 |
538 | // Register 2:
539 | this.romSelectionReg1 = 0;
540 |
541 | // Register 3:
542 | this.romBankSelect = 0;
543 | };
544 |
545 | JSNES.Mappers[1].prototype.write = function(address, value) {
546 | // Writes to addresses other than MMC registers are handled by NoMapper.
547 | if (address < 0x8000) {
548 | JSNES.Mappers[0].prototype.write.apply(this, arguments);
549 | return;
550 | }
551 |
552 | // See what should be done with the written value:
553 | if ((value & 128) !== 0) {
554 |
555 | // Reset buffering:
556 | this.regBufferCounter = 0;
557 | this.regBuffer = 0;
558 |
559 | // Reset register:
560 | if (this.getRegNumber(address) === 0) {
561 |
562 | this.prgSwitchingArea = 1;
563 | this.prgSwitchingSize = 1;
564 |
565 | }
566 | } else {
567 |
568 | // Continue buffering:
569 | //regBuffer = (regBuffer & (0xFF-(1<> 2) & 1;
611 |
612 | // PRG Switching Size:
613 | this.prgSwitchingSize = (value >> 3) & 1;
614 |
615 | // VROM Switching Size:
616 | this.vromSwitchingSize = (value >> 4) & 1;
617 |
618 | break;
619 |
620 | case 1:
621 | // ROM selection:
622 | this.romSelectionReg0 = (value >> 4) & 1;
623 |
624 | // Check whether the cart has VROM:
625 | if (this.nes.rom.vromCount > 0) {
626 |
627 | // Select VROM bank at 0x0000:
628 | if (this.vromSwitchingSize === 0) {
629 |
630 | // Swap 8kB VROM:
631 | if (this.romSelectionReg0 === 0) {
632 | this.load8kVromBank((value & 0xF), 0x0000);
633 | } else {
634 | this.load8kVromBank(
635 | Math.floor(this.nes.rom.vromCount / 2) +
636 | (value & 0xF),
637 | 0x0000
638 | );
639 | }
640 |
641 | } else {
642 | // Swap 4kB VROM:
643 | if (this.romSelectionReg0 === 0) {
644 | this.loadVromBank((value & 0xF), 0x0000);
645 | } else {
646 | this.loadVromBank(
647 | Math.floor(this.nes.rom.vromCount / 2) +
648 | (value & 0xF),
649 | 0x0000
650 | );
651 | }
652 | }
653 | }
654 |
655 | break;
656 |
657 | case 2:
658 | // ROM selection:
659 | this.romSelectionReg1 = (value >> 4) & 1;
660 |
661 | // Check whether the cart has VROM:
662 | if (this.nes.rom.vromCount > 0) {
663 |
664 | // Select VROM bank at 0x1000:
665 | if (this.vromSwitchingSize === 1) {
666 | // Swap 4kB of VROM:
667 | if (this.romSelectionReg1 === 0) {
668 | this.loadVromBank((value & 0xF), 0x1000);
669 | } else {
670 | this.loadVromBank(
671 | Math.floor(this.nes.rom.vromCount / 2) +
672 | (value & 0xF),
673 | 0x1000
674 | );
675 | }
676 | }
677 | }
678 | break;
679 |
680 | default:
681 | // Select ROM bank:
682 | // -------------------------
683 | tmp = value & 0xF;
684 | var bank;
685 | var baseBank = 0;
686 |
687 | if (this.nes.rom.romCount >= 32) {
688 | // 1024 kB cart
689 | if (this.vromSwitchingSize === 0) {
690 | if (this.romSelectionReg0 === 1) {
691 | baseBank = 16;
692 | }
693 | } else {
694 | baseBank = (this.romSelectionReg0 |
695 | (this.romSelectionReg1 << 1)) << 3;
696 | }
697 | } else if (this.nes.rom.romCount >= 16) {
698 | // 512 kB cart
699 | if (this.romSelectionReg0 === 1) {
700 | baseBank = 8;
701 | }
702 | }
703 |
704 | if (this.prgSwitchingSize === 0) {
705 | // 32kB
706 | bank = baseBank + (value & 0xF);
707 | this.load32kRomBank(bank, 0x8000);
708 | } else {
709 | // 16kB
710 | bank = baseBank * 2 + (value & 0xF);
711 | if (this.prgSwitchingArea === 0) {
712 | this.loadRomBank(bank, 0xC000);
713 | } else {
714 | this.loadRomBank(bank, 0x8000);
715 | }
716 | }
717 | }
718 | };
719 |
720 | // Returns the register number from the address written to:
721 | JSNES.Mappers[1].prototype.getRegNumber = function(address) {
722 | if (address >= 0x8000 && address <= 0x9FFF) {
723 | return 0;
724 | } else if (address >= 0xA000 && address <= 0xBFFF) {
725 | return 1;
726 | } else if (address >= 0xC000 && address <= 0xDFFF) {
727 | return 2;
728 | } else {
729 | return 3;
730 | }
731 | };
732 |
733 | JSNES.Mappers[1].prototype.loadROM = function(rom) {
734 | if (!this.nes.rom.valid) {
735 | alert("MMC1: Invalid ROM! Unable to load.");
736 | return;
737 | }
738 |
739 | // Load PRG-ROM:
740 | this.loadRomBank(0, 0x8000); // First ROM bank..
741 | this.loadRomBank(this.nes.rom.romCount - 1, 0xC000); // ..and last ROM bank.
742 |
743 | // Load CHR-ROM:
744 | this.loadCHRROM();
745 |
746 | // Load Battery RAM (if present):
747 | this.loadBatteryRam();
748 |
749 | // Do Reset-Interrupt:
750 | this.nes.cpu.requestIrq(this.nes.cpu.IRQ_RESET);
751 | };
752 |
753 | JSNES.Mappers[1].prototype.switchLowHighPrgRom = function(oldSetting) {
754 | // not yet.
755 | };
756 |
757 | JSNES.Mappers[1].prototype.switch16to32 = function() {
758 | // not yet.
759 | };
760 |
761 | JSNES.Mappers[1].prototype.switch32to16 = function() {
762 | // not yet.
763 | };
764 |
765 | JSNES.Mappers[1].prototype.toJSON = function() {
766 | var s = JSNES.Mappers[0].prototype.toJSON.apply(this);
767 | s.mirroring = this.mirroring;
768 | s.oneScreenMirroring = this.oneScreenMirroring;
769 | s.prgSwitchingArea = this.prgSwitchingArea;
770 | s.prgSwitchingSize = this.prgSwitchingSize;
771 | s.vromSwitchingSize = this.vromSwitchingSize;
772 | s.romSelectionReg0 = this.romSelectionReg0;
773 | s.romSelectionReg1 = this.romSelectionReg1;
774 | s.romBankSelect = this.romBankSelect;
775 | s.regBuffer = this.regBuffer;
776 | s.regBufferCounter = this.regBufferCounter;
777 | return s;
778 | };
779 |
780 | JSNES.Mappers[1].prototype.fromJSON = function(s) {
781 | JSNES.Mappers[0].prototype.fromJSON.apply(this, s);
782 | this.mirroring = s.mirroring;
783 | this.oneScreenMirroring = s.oneScreenMirroring;
784 | this.prgSwitchingArea = s.prgSwitchingArea;
785 | this.prgSwitchingSize = s.prgSwitchingSize;
786 | this.vromSwitchingSize = s.vromSwitchingSize;
787 | this.romSelectionReg0 = s.romSelectionReg0;
788 | this.romSelectionReg1 = s.romSelectionReg1;
789 | this.romBankSelect = s.romBankSelect;
790 | this.regBuffer = s.regBuffer;
791 | this.regBufferCounter = s.regBufferCounter;
792 | };
793 |
794 | JSNES.Mappers[2] = function(nes) {
795 | this.nes = nes;
796 | };
797 |
798 | JSNES.Mappers[2].prototype = new JSNES.Mappers[0]();
799 |
800 | JSNES.Mappers[2].prototype.write = function(address, value) {
801 | // Writes to addresses other than MMC registers are handled by NoMapper.
802 | if (address < 0x8000) {
803 | JSNES.Mappers[0].prototype.write.apply(this, arguments);
804 | return;
805 | } else {
806 | // This is a ROM bank select command.
807 | // Swap in the given ROM bank at 0x8000:
808 | this.loadRomBank(value, 0x8000);
809 | }
810 | };
811 |
812 | JSNES.Mappers[2].prototype.loadROM = function(rom) {
813 | if (!this.nes.rom.valid) {
814 | alert("UNROM: Invalid ROM! Unable to load.");
815 | return;
816 | }
817 |
818 | // Load PRG-ROM:
819 | this.loadRomBank(0, 0x8000);
820 | this.loadRomBank(this.nes.rom.romCount - 1, 0xC000);
821 |
822 | // Load CHR-ROM:
823 | this.loadCHRROM();
824 |
825 | // Do Reset-Interrupt:
826 | this.nes.cpu.requestIrq(this.nes.cpu.IRQ_RESET);
827 | };
828 |
829 | /**
830 | * Mapper 003 (CNROM)
831 | *
832 | * @constructor
833 | * @example Solomon's Key, Arkanoid, Arkista's Ring, Bump 'n' Jump, Cybernoid
834 | * @description http://wiki.nesdev.com/w/index.php/INES_Mapper_003
835 | */
836 | JSNES.Mappers[3] = function(nes) {
837 | this.nes = nes;
838 | };
839 |
840 | JSNES.Mappers[3].prototype = new JSNES.Mappers[0]();
841 |
842 | JSNES.Mappers[3].prototype.write = function(address, value) {
843 | // Writes to addresses other than MMC registers are handled by NoMapper.
844 | if (address < 0x8000) {
845 | JSNES.Mappers[0].prototype.write.apply(this, arguments);
846 | return;
847 | } else {
848 | // This is a ROM bank select command.
849 | // Swap in the given ROM bank at 0x8000:
850 | // This is a VROM bank select command.
851 | // Swap in the given VROM bank at 0x0000:
852 | var bank = (value % (this.nes.rom.vromCount / 2)) * 2;
853 | this.loadVromBank(bank, 0x0000);
854 | this.loadVromBank(bank + 1, 0x1000);
855 | this.load8kVromBank(value * 2, 0x0000);
856 | }
857 | };
858 |
859 | JSNES.Mappers[4] = function(nes) {
860 | this.nes = nes;
861 |
862 | this.CMD_SEL_2_1K_VROM_0000 = 0;
863 | this.CMD_SEL_2_1K_VROM_0800 = 1;
864 | this.CMD_SEL_1K_VROM_1000 = 2;
865 | this.CMD_SEL_1K_VROM_1400 = 3;
866 | this.CMD_SEL_1K_VROM_1800 = 4;
867 | this.CMD_SEL_1K_VROM_1C00 = 5;
868 | this.CMD_SEL_ROM_PAGE1 = 6;
869 | this.CMD_SEL_ROM_PAGE2 = 7;
870 |
871 | this.command = null;
872 | this.prgAddressSelect = null;
873 | this.chrAddressSelect = null;
874 | this.pageNumber = null;
875 | this.irqCounter = null;
876 | this.irqLatchValue = null;
877 | this.irqEnable = null;
878 | this.prgAddressChanged = false;
879 | };
880 |
881 | JSNES.Mappers[4].prototype = new JSNES.Mappers[0]();
882 |
883 | JSNES.Mappers[4].prototype.write = function(address, value) {
884 | // Writes to addresses other than MMC registers are handled by NoMapper.
885 | if (address < 0x8000) {
886 | JSNES.Mappers[0].prototype.write.apply(this, arguments);
887 | return;
888 | }
889 |
890 | switch (address) {
891 | case 0x8000:
892 | // Command/Address Select register
893 | this.command = value & 7;
894 | var tmp = (value >> 6) & 1;
895 | if (tmp != this.prgAddressSelect) {
896 | this.prgAddressChanged = true;
897 | }
898 | this.prgAddressSelect = tmp;
899 | this.chrAddressSelect = (value >> 7) & 1;
900 | break;
901 |
902 | case 0x8001:
903 | // Page number for command
904 | this.executeCommand(this.command, value);
905 | break;
906 |
907 | case 0xA000:
908 | // Mirroring select
909 | if ((value & 1) !== 0) {
910 | this.nes.ppu.setMirroring(
911 | this.nes.rom.HORIZONTAL_MIRRORING
912 | );
913 | } else {
914 | this.nes.ppu.setMirroring(this.nes.rom.VERTICAL_MIRRORING);
915 | }
916 | break;
917 |
918 | case 0xA001:
919 | // SaveRAM Toggle
920 | // TODO
921 | //nes.getRom().setSaveState((value&1)!=0);
922 | break;
923 |
924 | case 0xC000:
925 | // IRQ Counter register
926 | this.irqCounter = value;
927 | //nes.ppu.mapperIrqCounter = 0;
928 | break;
929 |
930 | case 0xC001:
931 | // IRQ Latch register
932 | this.irqLatchValue = value;
933 | break;
934 |
935 | case 0xE000:
936 | // IRQ Control Reg 0 (disable)
937 | //irqCounter = irqLatchValue;
938 | this.irqEnable = 0;
939 | break;
940 |
941 | case 0xE001:
942 | // IRQ Control Reg 1 (enable)
943 | this.irqEnable = 1;
944 | break;
945 |
946 | default:
947 | // Not a MMC3 register.
948 | // The game has probably crashed,
949 | // since it tries to write to ROM..
950 | // IGNORE.
951 | }
952 | };
953 |
954 | JSNES.Mappers[4].prototype.executeCommand = function(cmd, arg) {
955 | switch (cmd) {
956 | case this.CMD_SEL_2_1K_VROM_0000:
957 | // Select 2 1KB VROM pages at 0x0000:
958 | if (this.chrAddressSelect === 0) {
959 | this.load1kVromBank(arg, 0x0000);
960 | this.load1kVromBank(arg + 1, 0x0400);
961 | } else {
962 | this.load1kVromBank(arg, 0x1000);
963 | this.load1kVromBank(arg + 1, 0x1400);
964 | }
965 | break;
966 |
967 | case this.CMD_SEL_2_1K_VROM_0800:
968 | // Select 2 1KB VROM pages at 0x0800:
969 | if (this.chrAddressSelect === 0) {
970 | this.load1kVromBank(arg, 0x0800);
971 | this.load1kVromBank(arg + 1, 0x0C00);
972 | } else {
973 | this.load1kVromBank(arg, 0x1800);
974 | this.load1kVromBank(arg + 1, 0x1C00);
975 | }
976 | break;
977 |
978 | case this.CMD_SEL_1K_VROM_1000:
979 | // Select 1K VROM Page at 0x1000:
980 | if (this.chrAddressSelect === 0) {
981 | this.load1kVromBank(arg, 0x1000);
982 | } else {
983 | this.load1kVromBank(arg, 0x0000);
984 | }
985 | break;
986 |
987 | case this.CMD_SEL_1K_VROM_1400:
988 | // Select 1K VROM Page at 0x1400:
989 | if (this.chrAddressSelect === 0) {
990 | this.load1kVromBank(arg, 0x1400);
991 | } else {
992 | this.load1kVromBank(arg, 0x0400);
993 | }
994 | break;
995 |
996 | case this.CMD_SEL_1K_VROM_1800:
997 | // Select 1K VROM Page at 0x1800:
998 | if (this.chrAddressSelect === 0) {
999 | this.load1kVromBank(arg, 0x1800);
1000 | } else {
1001 | this.load1kVromBank(arg, 0x0800);
1002 | }
1003 | break;
1004 |
1005 | case this.CMD_SEL_1K_VROM_1C00:
1006 | // Select 1K VROM Page at 0x1C00:
1007 | if (this.chrAddressSelect === 0) {
1008 | this.load1kVromBank(arg, 0x1C00);
1009 | } else {
1010 | this.load1kVromBank(arg, 0x0C00);
1011 | }
1012 | break;
1013 |
1014 | case this.CMD_SEL_ROM_PAGE1:
1015 | if (this.prgAddressChanged) {
1016 | // Load the two hardwired banks:
1017 | if (this.prgAddressSelect === 0) {
1018 | this.load8kRomBank(
1019 | ((this.nes.rom.romCount - 1) * 2),
1020 | 0xC000
1021 | );
1022 | } else {
1023 | this.load8kRomBank(
1024 | ((this.nes.rom.romCount - 1) * 2),
1025 | 0x8000
1026 | );
1027 | }
1028 | this.prgAddressChanged = false;
1029 | }
1030 |
1031 | // Select first switchable ROM page:
1032 | if (this.prgAddressSelect === 0) {
1033 | this.load8kRomBank(arg, 0x8000);
1034 | } else {
1035 | this.load8kRomBank(arg, 0xC000);
1036 | }
1037 | break;
1038 |
1039 | case this.CMD_SEL_ROM_PAGE2:
1040 | // Select second switchable ROM page:
1041 | this.load8kRomBank(arg, 0xA000);
1042 |
1043 | // hardwire appropriate bank:
1044 | if (this.prgAddressChanged) {
1045 | // Load the two hardwired banks:
1046 | if (this.prgAddressSelect === 0) {
1047 | this.load8kRomBank(
1048 | ((this.nes.rom.romCount - 1) * 2),
1049 | 0xC000
1050 | );
1051 | } else {
1052 | this.load8kRomBank(
1053 | ((this.nes.rom.romCount - 1) * 2),
1054 | 0x8000
1055 | );
1056 | }
1057 | this.prgAddressChanged = false;
1058 | }
1059 | }
1060 | };
1061 |
1062 | JSNES.Mappers[4].prototype.loadROM = function(rom) {
1063 | if (!this.nes.rom.valid) {
1064 | alert("MMC3: Invalid ROM! Unable to load.");
1065 | return;
1066 | }
1067 |
1068 | // Load hardwired PRG banks (0xC000 and 0xE000):
1069 | this.load8kRomBank(((this.nes.rom.romCount - 1) * 2), 0xC000);
1070 | this.load8kRomBank(((this.nes.rom.romCount - 1) * 2) + 1, 0xE000);
1071 |
1072 | // Load swappable PRG banks (0x8000 and 0xA000):
1073 | this.load8kRomBank(0, 0x8000);
1074 | this.load8kRomBank(1, 0xA000);
1075 |
1076 | // Load CHR-ROM:
1077 | this.loadCHRROM();
1078 |
1079 | // Load Battery RAM (if present):
1080 | this.loadBatteryRam();
1081 |
1082 | // Do Reset-Interrupt:
1083 | this.nes.cpu.requestIrq(this.nes.cpu.IRQ_RESET);
1084 | };
1085 |
1086 | JSNES.Mappers[4].prototype.clockIrqCounter = function() {
1087 | if (this.irqEnable == 1) {
1088 | this.irqCounter--;
1089 | if (this.irqCounter < 0) {
1090 | // Trigger IRQ:
1091 | //nes.getCpu().doIrq();
1092 | this.nes.cpu.requestIrq(this.nes.cpu.IRQ_NORMAL);
1093 | this.irqCounter = this.irqLatchValue;
1094 | }
1095 | }
1096 | };
1097 |
1098 | JSNES.Mappers[4].prototype.toJSON = function() {
1099 | var s = JSNES.Mappers[0].prototype.toJSON.apply(this);
1100 | s.command = this.command;
1101 | s.prgAddressSelect = this.prgAddressSelect;
1102 | s.chrAddressSelect = this.chrAddressSelect;
1103 | s.pageNumber = this.pageNumber;
1104 | s.irqCounter = this.irqCounter;
1105 | s.irqLatchValue = this.irqLatchValue;
1106 | s.irqEnable = this.irqEnable;
1107 | s.prgAddressChanged = this.prgAddressChanged;
1108 | return s;
1109 | };
1110 |
1111 | JSNES.Mappers[4].prototype.fromJSON = function(s) {
1112 | JSNES.Mappers[0].prototype.fromJSON.apply(this, s);
1113 | this.command = s.command;
1114 | this.prgAddressSelect = s.prgAddressSelect;
1115 | this.chrAddressSelect = s.chrAddressSelect;
1116 | this.pageNumber = s.pageNumber;
1117 | this.irqCounter = s.irqCounter;
1118 | this.irqLatchValue = s.irqLatchValue;
1119 | this.irqEnable = s.irqEnable;
1120 | this.prgAddressChanged = s.prgAddressChanged;
1121 | };
1122 | /**
1123 | * Mapper005 (MMC5,ExROM)
1124 | *
1125 | * @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
1126 | * @description http://wiki.nesdev.com/w/index.php/INES_Mapper_005
1127 | * @constructor
1128 | */
1129 | JSNES.Mappers[5] = function(nes) {
1130 | this.nes = nes;
1131 | };
1132 |
1133 | JSNES.Mappers[5].prototype = new JSNES.Mappers[0]();
1134 |
1135 | JSNES.Mappers[5].prototype.write = function(address, value) {
1136 | // Writes to addresses other than MMC registers are handled by NoMapper.
1137 | if (address < 0x8000) {
1138 | JSNES.Mappers[0].prototype.write.apply(this, arguments);
1139 | } else {
1140 | this.load8kVromBank(value, 0x0000);
1141 | }
1142 | };
1143 |
1144 | JSNES.Mappers[5].prototype.write = function(address, value) {
1145 | // Writes to addresses other than MMC registers are handled by NoMapper.
1146 | if (address < 0x5000) {
1147 | JSNES.Mappers[0].prototype.write.apply(this, arguments);
1148 | return;
1149 | }
1150 |
1151 | switch (address) {
1152 | case 0x5100:
1153 | this.prg_size = value & 3;
1154 | break;
1155 | case 0x5101:
1156 | this.chr_size = value & 3;
1157 | break;
1158 | case 0x5102:
1159 | this.sram_we_a = value & 3;
1160 | break;
1161 | case 0x5103:
1162 | this.sram_we_b = value & 3;
1163 | break;
1164 | case 0x5104:
1165 | this.graphic_mode = value & 3;
1166 | break;
1167 | case 0x5105:
1168 | this.nametable_mode = value;
1169 | this.nametable_type[0] = value & 3;
1170 | this.load1kVromBank(value & 3, 0x2000);
1171 | value >>= 2;
1172 | this.nametable_type[1] = value & 3;
1173 | this.load1kVromBank(value & 3, 0x2400);
1174 | value >>= 2;
1175 | this.nametable_type[2] = value & 3;
1176 | this.load1kVromBank(value & 3, 0x2800);
1177 | value >>= 2;
1178 | this.nametable_type[3] = value & 3;
1179 | this.load1kVromBank(value & 3, 0x2c00);
1180 | break;
1181 | case 0x5106:
1182 | this.fill_chr = value;
1183 | break;
1184 | case 0x5107:
1185 | this.fill_pal = value & 3;
1186 | break;
1187 | case 0x5113:
1188 | this.SetBank_SRAM(3, value & 3);
1189 | break;
1190 | case 0x5114:
1191 | case 0x5115:
1192 | case 0x5116:
1193 | case 0x5117:
1194 | this.SetBank_CPU(address, value);
1195 | break;
1196 | case 0x5120:
1197 | case 0x5121:
1198 | case 0x5122:
1199 | case 0x5123:
1200 | case 0x5124:
1201 | case 0x5125:
1202 | case 0x5126:
1203 | case 0x5127:
1204 | this.chr_mode = 0;
1205 | this.chr_page[0][address & 7] = value;
1206 | this.SetBank_PPU();
1207 | break;
1208 | case 0x5128:
1209 | case 0x5129:
1210 | case 0x512a:
1211 | case 0x512b:
1212 | this.chr_mode = 1;
1213 | this.chr_page[1][(address & 3) + 0] = value;
1214 | this.chr_page[1][(address & 3) + 4] = value;
1215 | this.SetBank_PPU();
1216 | break;
1217 | case 0x5200:
1218 | this.split_control = value;
1219 | break;
1220 | case 0x5201:
1221 | this.split_scroll = value;
1222 | break;
1223 | case 0x5202:
1224 | this.split_page = value & 0x3f;
1225 | break;
1226 | case 0x5203:
1227 | this.irq_line = value;
1228 | this.nes.cpu.ClearIRQ();
1229 | break;
1230 | case 0x5204:
1231 | this.irq_enable = value;
1232 | this.nes.cpu.ClearIRQ();
1233 | break;
1234 | case 0x5205:
1235 | this.mult_a = value;
1236 | break;
1237 | case 0x5206:
1238 | this.mult_b = value;
1239 | break;
1240 | default:
1241 | if (address >= 0x5000 && address <= 0x5015) {
1242 | this.nes.papu.exWrite(address, value);
1243 | } else if (address >= 0x5c00 && address <= 0x5fff) {
1244 | if (this.graphic_mode === 2) {
1245 | // ExRAM
1246 | // vram write
1247 | } else if (this.graphic_mode !== 3) {
1248 | // Split,ExGraphic
1249 | if (this.irq_status & 0x40) {
1250 | // vram write
1251 | } else {
1252 | // vram write
1253 | }
1254 | }
1255 | } else if (address >= 0x6000 && address <= 0x7fff) {
1256 | if (this.sram_we_a === 2 && this.sram_we_b === 1) {
1257 | // additional ram write
1258 | }
1259 | }
1260 | break;
1261 | }
1262 | };
1263 |
1264 | JSNES.Mappers[5].prototype.loadROM = function() {
1265 | if (!this.nes.rom.valid) {
1266 | throw new Error("UNROM: Invalid ROM! Unable to load.");
1267 | }
1268 |
1269 | // Load PRG-ROM:
1270 | this.load8kRomBank(this.nes.rom.romCount * 2 - 1, 0x8000);
1271 | this.load8kRomBank(this.nes.rom.romCount * 2 - 1, 0xa000);
1272 | this.load8kRomBank(this.nes.rom.romCount * 2 - 1, 0xc000);
1273 | this.load8kRomBank(this.nes.rom.romCount * 2 - 1, 0xe000);
1274 |
1275 | // Load CHR-ROM:
1276 | this.loadCHRROM();
1277 |
1278 | // Do Reset-Interrupt:
1279 | this.nes.cpu.requestIrq(this.nes.cpu.IRQ_RESET);
1280 | };
1281 |
1282 | /**
1283 | * Mapper007 (AxROM)
1284 | * @example Battletoads, Time Lord, Marble Madness
1285 | * @description http://wiki.nesdev.com/w/index.php/INES_Mapper_007
1286 | * @constructor
1287 | */
1288 | JSNES.Mappers[7] = function(nes) {
1289 | this.nes = nes;
1290 | };
1291 |
1292 | JSNES.Mappers[7].prototype = new JSNES.Mappers[0]();
1293 |
1294 | JSNES.Mappers[7].prototype.write = function(address, value) {
1295 | // Writes to addresses other than MMC registers are handled by NoMapper.
1296 | if (address < 0x8000) {
1297 | JSNES.Mappers[0].prototype.write.apply(this, arguments);
1298 | } else {
1299 | this.load32kRomBank(value & 0x7, 0x8000);
1300 | if (value & 0x10) {
1301 | this.nes.ppu.setMirroring(this.nes.rom.SINGLESCREEN_MIRRORING2);
1302 | } else {
1303 | this.nes.ppu.setMirroring(this.nes.rom.SINGLESCREEN_MIRRORING);
1304 | }
1305 | }
1306 | };
1307 |
1308 | JSNES.Mappers[7].prototype.loadROM = function() {
1309 | if (!this.nes.rom.valid) {
1310 | throw new Error("AOROM: Invalid ROM! Unable to load.");
1311 | }
1312 |
1313 | // Load PRG-ROM:
1314 | this.loadPRGROM();
1315 |
1316 | // Load CHR-ROM:
1317 | this.loadCHRROM();
1318 |
1319 | // Do Reset-Interrupt:
1320 | this.nes.cpu.requestIrq(this.nes.cpu.IRQ_RESET);
1321 | };
1322 |
1323 | /**
1324 | * Mapper 011 (Color Dreams)
1325 | *
1326 | * @description http://wiki.nesdev.com/w/index.php/Color_Dreams
1327 | * @example Crystal Mines, Metal Fighter
1328 | * @constructor
1329 | */
1330 | JSNES.Mappers[11] = function(nes) {
1331 | this.nes = nes;
1332 | };
1333 |
1334 | JSNES.Mappers[11].prototype = new JSNES.Mappers[0]();
1335 |
1336 | JSNES.Mappers[11].prototype.write = function(address, value) {
1337 | if (address < 0x8000) {
1338 | JSNES.Mappers[0].prototype.write.apply(this, arguments);
1339 | return;
1340 | } else {
1341 | // Swap in the given PRG-ROM bank:
1342 | var prgbank1 = ((value & 0xf) * 2) % this.nes.rom.romCount;
1343 | var prgbank2 = ((value & 0xf) * 2 + 1) % this.nes.rom.romCount;
1344 |
1345 | this.loadRomBank(prgbank1, 0x8000);
1346 | this.loadRomBank(prgbank2, 0xc000);
1347 |
1348 | if (this.nes.rom.vromCount > 0) {
1349 | // Swap in the given VROM bank at 0x0000:
1350 | var bank = ((value >> 4) * 2) % this.nes.rom.vromCount;
1351 | this.loadVromBank(bank, 0x0000);
1352 | this.loadVromBank(bank + 1, 0x1000);
1353 | }
1354 | }
1355 | };
1356 |
1357 | /**
1358 | * Mapper 034 (BNROM, NINA-01)
1359 | *
1360 | * @description http://wiki.nesdev.com/w/index.php/INES_Mapper_034
1361 | * @example Darkseed, Mashou, Mission Impossible 2
1362 | * @constructor
1363 | */
1364 | JSNES.Mappers[34] = function(nes) {
1365 | this.nes = nes;
1366 | };
1367 |
1368 | JSNES.Mappers[34].prototype = new JSNES.Mappers[0]();
1369 |
1370 | JSNES.Mappers[34].prototype.write = function(address, value) {
1371 | if (address < 0x8000) {
1372 | JSNES.Mappers[0].prototype.write.apply(this, arguments);
1373 | return;
1374 | } else {
1375 | this.load32kRomBank(value, 0x8000);
1376 | }
1377 | };
1378 |
1379 | /**
1380 | * Mapper 066 (GxROM)
1381 | *
1382 | * @description http://wiki.nesdev.com/w/index.php/INES_Mapper_066
1383 | * @example Doraemon, Dragon Power, Gumshoe, Thunder & Lightning,
1384 | * Super Mario Bros. + Duck Hunt
1385 | * @constructor
1386 | */
1387 | JSNES.Mappers[66] = function(nes) {
1388 | this.nes = nes;
1389 | console.log("Mapper 66");
1390 | };
1391 |
1392 | JSNES.Mappers[66].prototype = new JSNES.Mappers[0]();
1393 |
1394 | JSNES.Mappers[66].prototype.write = function(address, value) {
1395 | if (address < 0x8000) {
1396 | JSNES.Mappers[0].prototype.write.apply(this, arguments);
1397 | return;
1398 | } else {
1399 | // Swap in the given PRG-ROM bank at 0x8000:
1400 | this.load32kRomBank((value >> 4) & 3, 0x8000);
1401 |
1402 | // Swap in the given VROM bank at 0x0000:
1403 | this.load8kVromBank((value & 3) * 2, 0x0000);
1404 | }
1405 | };
--------------------------------------------------------------------------------