├── _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 | ![横屏预览](./landscape_mode.png) 9 | 10 | ### 竖屏效果 11 | ![竖屏预览](./portrait_mode.png) 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 |
38 |
39 |
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 = '' + o + ''; 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 | }; --------------------------------------------------------------------------------