A pixel art painter geared specifically at NES pixel art. Includes export for .chr binary file as well as palette and namespace data.
您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

711 行
20KB

  1. import Utils from "/app/js/common/Utils.js";
  2. import ISurface from "/app/js/ifaces/ISurface.js";
  3. import NESTile from "/app/js/models/NESTile.js";
  4. import NESPalette from "/app/js/models/NESPalette.js";
  5. function CnvIdx(x, y, am, off){
  6. var res = {
  7. side: 0,
  8. tileidx: 0,
  9. x: 0,
  10. y: 0
  11. }
  12. switch(am){
  13. case NESBank.ACCESSMODE_8K:
  14. res.side = (x >= 128) ? 1 : 0;
  15. x -= (res.side === 1) ? 128 : 0;
  16. res.tileidx = (Math.floor(y/8) * 16) + Math.floor(x / 8);
  17. break;
  18. case NESBank.ACCESSMODE_4K:
  19. res.side = off;
  20. res.tileidx = (Math.floor(y/8) * 16) + Math.floor(x / 8);
  21. break;
  22. case NESBank.ACCESSMODE_2K:
  23. res.side = Math.floor(off * 0.5);
  24. off -= (off > 1) ? 2 : 0;
  25. res.tileidx = (off*128) + ((Math.floor(y/8) * 16) + Math.floor(x / 8));
  26. break;
  27. case NESBank.ACCESSMODE_1K:
  28. res.side = Math.floor(off * 0.25);
  29. off -= (off > 3) ? 4 : 0;
  30. res.tileidx = (off * 64) + ((Math.floor(y/8) * 16) + Math.floor(x / 8));
  31. break;
  32. case NESBank.ACCESSMODE_1T:
  33. res.side = (off >= 256) ? 1 : 0;
  34. res.tileidx = (off >= 256) ? off - 256 : off;
  35. break;
  36. }
  37. res.x = x%8;
  38. res.y = y%8;
  39. return res;
  40. }
  41. function AdjOffsetToNewMode(nmode, omode, ooff){
  42. // NOTE: 8K never shows up because it will ALWAYS return an offset of 0, so it's easier to just let it all fall through
  43. // to the default return value.
  44. switch(nmode){
  45. case NESBank.ACCESSMODE_4K:
  46. if (ooff > 1){
  47. switch(omode){
  48. case NESBank.ACCESSMODE_2K:
  49. return Math.floor(ooff * 0.5);
  50. case NESBank.ACCESSMODE_1K:
  51. return Math.floor(ooff * 0.25);
  52. case NESBank.ACCESSMODE_1T:
  53. return Math.floor(ooff / 256);
  54. }
  55. }
  56. return ooff;
  57. case NESBank.ACCESSMODE_2K:
  58. switch(omode){
  59. case NESBank.ACCESSMODE_4K:
  60. return ooff * 2;
  61. case NESBank.ACCESSMODE_1K:
  62. return Math.floor(ooff * 0.5);
  63. case NESBank.ACCESSMODE_1T:
  64. return Math.floor(ooff * 0.0078125); // divide by 128
  65. }
  66. break;
  67. case NESBank.ACCESSMODE_1K:
  68. switch(omode){
  69. case NESBank.ACCESSMODE_4K:
  70. return ooff * 4;
  71. case NESBank.ACCESSMODE_2K:
  72. return ooff * 2;
  73. case NESBank.ACCESSMODE_1T:
  74. return Math.floor(ooff * 0.015625); // divide by 64
  75. }
  76. break;
  77. case NESBank.ACCESSMODE_1T:
  78. switch(omode){
  79. case NESBank.ACCESSMODE_4K:
  80. return ooff * 256;
  81. case NESBank.ACCESSMODE_2K:
  82. return ooff * 128;
  83. case NESBank.ACCESSMODE_1K:
  84. return ooff * 64;
  85. }
  86. break;
  87. }
  88. return 0;
  89. }
  90. export default class NESBank extends ISurface{
  91. constructor(){
  92. super();
  93. this.__LP = []; // Left Patterns (Sprites)
  94. this.__RP = []; // Right Patterns (Backgrounds)
  95. this.__View = [];
  96. this.__AccessMode = NESBank.ACCESSMODE_8K;
  97. this.__AccessOffset = 0;
  98. this.__undos = []; // Holds Base64 snapshots of the bank.
  99. this.__redos = []; // Holds Base64 snapshots of work undone.
  100. this.__emitsEnabled = true;
  101. var handle_datachanged = Utils.debounce((function(side, idx){
  102. var sendEmit = false;
  103. switch(this.__AccessMode){
  104. case NESBank.ACCESSMODE_1T:
  105. if (side === Math.floor(this.__AccessOffset / 256)){
  106. if (idx === this.__AccessOffset)
  107. sendEmit = true;
  108. }
  109. case NESBank.ACCESSMODE_1K:
  110. if (side === Math.floor(this.__AccessOffset / 4)){
  111. if (Math.floor(idx / 64) === Math.floor(this.__AccessOffset/4))
  112. sendEmit = true;
  113. }
  114. break;
  115. case NESBank.ACCESSMODE_2K:
  116. if (side === Math.floor(this.__AccessOffset / 2)){
  117. if (Math.floor(idx / 128) === Math.floor(this.__AccessOffset/2))
  118. sendEmit = true;
  119. }
  120. break;
  121. case NESBank.ACCESSMODE_4K:
  122. if (side === this.__AccessOffset)
  123. sendEmit = true;
  124. break;
  125. case NESBank.ACCESSMODE_8K:
  126. sendEmit = true;
  127. }
  128. if (sendEmit && this.__emitsEnabled){
  129. this.emit("data_changed");
  130. }
  131. }).bind(this), 250);
  132. for (var i=0; i < 256; i++){
  133. this.__LP.push(new NESTile());
  134. this.__LP[i].listen("data_changed", handle_datachanged.bind(this, 0, i));
  135. this.__RP.push(new NESTile());
  136. this.__RP[i].listen("data_changed", handle_datachanged.bind(this, 1, i));
  137. }
  138. this.__palette = null;
  139. }
  140. get access_mode(){return this.__AccessMode;}
  141. set access_mode(m){
  142. if (!Utils.isInt(m))
  143. throw new TypeError("Access mode expected to be integer.");
  144. var oam = this.__AccessMode;
  145. switch(m){
  146. case NESBank.ACCESSMODE_8K:
  147. this.__AccessMode = NESBank.ACCESSMODE_8K;
  148. break;
  149. case NESBank.ACCESSMODE_4K:
  150. this.__AccessMode = NESBank.ACCESSMODE_4K
  151. break;
  152. case NESBank.ACCESSMODE_2K:
  153. this.__AccessMode = NESBank.ACCESSMODE_2K;
  154. break;
  155. case NESBank.ACCESSMODE_1K:
  156. this.__AccessMode = NESBank.ACCESSMODE_1K;
  157. break;
  158. case NESBank.ACCESSMODE_1T:
  159. this.__AccessMode = NESBank.ACCESSMODE_1T;
  160. break;
  161. default:
  162. throw new ValueError("Unknown Access Mode.");
  163. }
  164. this.__AccessOffset = AdjOffsetToNewMode(m, oam, this.__AccessOffset);
  165. if (this.__emitsEnabled)
  166. this.emit("data_changed");
  167. }
  168. get access_offset(){return this.__AccessOffset;}
  169. set access_offset(o){
  170. if (!Utils.isInt(o))
  171. throw new TypeError("Access offset expected to be integer.");
  172. switch (this.__AccessMode){
  173. case NESBank.ACCESSMODE_8K:
  174. if (o !== 0)
  175. throw new RangeError("Access Offset is out of bounds based on current Access Mode.");
  176. break;
  177. case NESBank.ACCESSMODE_4K:
  178. if (o !== 0 && o !== 1)
  179. throw new RangeError("Access Offset is out of bounds based on current Access Mode.");
  180. break;
  181. case NESBank.ACCESSMODE_2K:
  182. if (o < 0 || o >= 4)
  183. throw new RangeError("Access Offset is out of bounds based on current Access Mode.");
  184. break;
  185. case NESBank.ACCESSMODE_1K:
  186. if (o < 0 || o >= 8)
  187. throw new RangeError("Access Offset is out of bounds based on current Access Mode.");
  188. break;
  189. case NESBank.ACCESSMODE_1T:
  190. if (o < 0 || o >= 512)
  191. throw new RangeError("Access Offset is out of bounds based on current Access Mode.");
  192. }
  193. this.__AccessOffset = o;
  194. if (this.__emitsEnabled)
  195. this.emit("data_changed");
  196. }
  197. get access_offset_length(){
  198. switch(this.__AccessMode){
  199. case NESBank.ACCESSMODE_4K:
  200. return 2;
  201. case NESBank.ACCESSMODE_2K:
  202. return 4;
  203. case NESBank.ACCESSMODE_1K:
  204. return 8;
  205. case NESBank.ACCESSMODE_1T:
  206. return 512;
  207. }
  208. return 0;
  209. }
  210. get json(){
  211. JSON.stringify({
  212. LP: this.__LP.map(x=>x.base64),
  213. RP: this.__RP.map(x=>x.base64)
  214. });
  215. }
  216. get chr(){
  217. var buff = null;
  218. var offset = 0;
  219. switch (this.__AccessMode){
  220. case NESBank.ACCESSMODE_8K:
  221. buff = new Uint8Array(8192);
  222. this.__LP.forEach((i) => {
  223. buff.set(i.chr, offset);
  224. offset += 16;
  225. });
  226. this.__RP.forEach((i) => {
  227. buff.set(i.chr, offset);
  228. offset += 16;
  229. });
  230. break;
  231. case NESBank.ACCESSMODE_4K:
  232. buff = new Uint8Array(4096);
  233. var list = (this.__AccessOffset === 0) ? this.__LP : this.__RP;
  234. list.forEach((i) => {
  235. buff.set(i.chr, offset);
  236. offset += 16;
  237. });
  238. break;
  239. case NESBank.ACCESSMODE_2K:
  240. buff = new Uint8Array(2048);
  241. var list = (this.__AccessOffset < 2) ? this.__LP : this.__RP;
  242. var s = Math.floor(this.__AccessOffset * 0.5) * 128;
  243. var e = s + 128;
  244. for (let i=s; i < e; i++){
  245. buff.set(list[i].chr, offset);
  246. offset += 16;
  247. }
  248. break;
  249. case NESBank.ACCESSMODE_1K:
  250. buff = new Uint8Array(1024);
  251. var list = (this.__AccessOffset < 4) ? this.__LP : this.__RP;
  252. var s = Math.floor(this.__AccessOffset * 0.25) * 64;
  253. var e = s + 64;
  254. for (let i=s; i < e; i++){
  255. buff.set(list[i].chr, offset);
  256. offset += 16;
  257. }
  258. break;
  259. case NESBank.ACCESSMODE_1T:
  260. var list = (this.__AccessOffset < 256) ? this.__LP : this.__RP;
  261. var idx = this.__AccessOffset % 256;
  262. buff = list[idx].chr;
  263. }
  264. return buff;
  265. }
  266. set chr(buff){
  267. if (!(buff instanceof Uint8Array))
  268. throw new TypeError("Expected Uint8Array buffer.");
  269. this.setCHR(buff);
  270. }
  271. get base64(){
  272. var b = "";
  273. var data = this.chr;
  274. for (var i = 0; i < data.length; i++) {
  275. b += String.fromCharCode(data[i]);
  276. }
  277. return window.btoa(b);
  278. }
  279. set base64(s){
  280. var b = window.atob(s);
  281. var len = b.length;
  282. if (b.length !== 8192){
  283. throw new Error("Base64 string contains invalid byte count.");
  284. }
  285. b = new Uint8Array(b.split("").map(function(c){
  286. return c.charCodeAt(0);
  287. }));
  288. this.chr = b;
  289. }
  290. get palette(){return this.__palette;}
  291. set palette(p){
  292. if (p !== null && !(p instanceof NESPalette))
  293. throw new TypeError("Expected null or NESPalette object.");
  294. if (p !== this.__palette){
  295. this.__palette = p;
  296. }
  297. }
  298. get width(){
  299. if (this.__AccessMode == NESBank.ACCESSMODE_8K)
  300. return 256;
  301. if (this.__AccessMode == NESBank.ACCESSMODE_1T)
  302. return 8;
  303. return 128;
  304. }
  305. get height(){
  306. switch(this.__AccessMode){
  307. case NESBank.ACCESSMODE_2K:
  308. return 64;
  309. case NESBank.ACCESSMODE_1K:
  310. return 32;
  311. case NESBank.ACCESSMODE_1T:
  312. return 8;
  313. }
  314. return 128;
  315. }
  316. get length(){return this.width * this.height;}
  317. get undos(){return this.__undos.length;}
  318. get redos(){return this.__redos.length;}
  319. get coloridx(){
  320. return new Proxy(this, {
  321. get:function(obj, prop){
  322. var len = obj.length * 8;
  323. if (prop === "length")
  324. return len;
  325. if (!Utils.isInt(prop))
  326. throw new TypeError("Expected integer index.");
  327. prop = parseInt(prop);
  328. if (prop < 0 || prop >= len)
  329. return NESPalette.Default[4];
  330. var x = Math.floor(prop % this.width);
  331. var y = Math.floor(prop / this.width);
  332. var res = CnvIdx(x, y, this.__AccessMode, this.__AccessOffset);
  333. var list = (res.side === 0) ? obj.__LP : obj.__RP;
  334. return list[res.tileidx].getPixelIndex(res.x, res.y);
  335. },
  336. set:function(obj, prop, value){
  337. if (!Utils.isInt(prop))
  338. throw new TypeError("Expected integer index.");
  339. if (!Utils.isInt(value))
  340. throw new TypeError("Color expected to be integer.");
  341. prop = parseInt(prop);
  342. value = parseInt(value);
  343. if (prop < 0 || prop >= len)
  344. throw new RangeError("Index out of bounds.");
  345. if (value < 0 || value >= 4)
  346. throw new RangeError("Color index out of bounds.");
  347. var x = Math.floor(prop % this.width);
  348. var y = Math.floor(prop / this.width);
  349. var res = CnvIdx(x, y, this.__AccessMode, this.__AccessOffset);
  350. var list = (res.side === 0) ? obj.__LP : obj.__RP;
  351. list[res.tileidx].setPixelIndex(res.x, res.y, value);
  352. return true;
  353. }
  354. });
  355. }
  356. get lp(){
  357. return new Proxy(this, {
  358. get: function(obj, prop){
  359. if (prop === "length")
  360. return obj.__LP.length;
  361. if (!Utils.isInt(prop))
  362. throw new TypeError("Expected integer index.");
  363. prop = parseInt(prop);
  364. if (prop < 0 || prop >= 256)
  365. throw new RangeError("Index out of bounds.");
  366. return obj.__LP[prop];
  367. },
  368. set: function(obj, prop, value){
  369. if (!Utils.isInt(prop))
  370. throw new TypeError("Expected integer index.");
  371. if (!(value instanceof NESTile))
  372. throw new TypeError("Can only assign NESTile objects.");
  373. prop = parseInt(prop);
  374. if (prop < 0 || prop >= 256)
  375. throw new RangeError("Index out of bounds.");
  376. obj.__LP[prop].copy(value);
  377. return true;
  378. }
  379. });
  380. }
  381. get rp(){
  382. return new Proxy(this, {
  383. get: function(obj, prop){
  384. if (prop === "length")
  385. return obj.__RP.length;
  386. if (!Utils.isInt(prop))
  387. throw new TypeError("Expected integer index.");
  388. prop = parseInt(prop);
  389. if (prop < 0 || prop >= 256)
  390. throw new RangeError("Index out of bounds.");
  391. return obj.__RP[prop];
  392. },
  393. set: function(obj, prop, value){
  394. if (!Utils.isInt(prop))
  395. throw new TypeError("Expected integer index.");
  396. if (!(value instanceof NESTile))
  397. throw new TypeError("Can only assign NESTile objects.");
  398. prop = parseInt(prop);
  399. if (prop < 0 || prop >= 256)
  400. throw new RangeError("Index out of bounds.");
  401. obj.__RP[prop].copy(value);
  402. return true;
  403. }
  404. });
  405. }
  406. copy(b){
  407. if (!(b instanceof NESBank))
  408. throw new TypeError("Expected NESBank object.");
  409. for (var i=0; i < 256; i++){
  410. this.lp[i] = b.lp[i];
  411. this.rp[i] = b.rp[i];
  412. }
  413. return this;
  414. }
  415. clone(){
  416. return (new NESBank()).copy(this);
  417. }
  418. getCHR(mode, offset){
  419. this.__emitsEnabled = false;
  420. var oam = this.access_mode;
  421. var oao = this.access_offset;
  422. try{
  423. this.access_mode = mode;
  424. this.access_offset = offset;
  425. } catch (e){
  426. this.access_mode = oam;
  427. this.access_offset = oao;
  428. this.__emitsEnabled = true;
  429. throw e;
  430. }
  431. var chr = this.chr;
  432. this.access_mode = oam;
  433. this.access_offset = oao;
  434. this.__emitsEnabled = true;
  435. return chr;
  436. }
  437. setCHR(buff, offset){
  438. if (!Utils.isInt(offset))
  439. offset = -1;
  440. var idx = 0;
  441. switch(buff.length){
  442. case 8192:
  443. if (offset < 0)
  444. offset = AdjOffsetToNewMode(NESBank.ACCESSMODE_8K, this.__AccessMode, this.__AccessOffset);
  445. this.__LP.forEach((i) => {
  446. i.chr = buff.slice(idx, idx+16);
  447. idx += 16;
  448. });
  449. this.__RP.forEach((i) => {
  450. i.chr = buff.slice(idx, idx+16);
  451. idx += 16;
  452. });
  453. break;
  454. case 4096:
  455. if (offset < 0)
  456. offset = AdjOffsetToNewMode(NESBank.ACCESSMODE_4K, this.__AccessMode, this.__AccessOffset);
  457. if (offset >= 2)
  458. throw new RangeError("Offset mismatch based on Buffer length.");
  459. var list = (offset === 0) ? this.__LP : this.__RP;
  460. list.forEach((i) => {
  461. i.chr = buff.slice(idx, idx+16);
  462. idx += 16;
  463. });
  464. break;
  465. case 2048:
  466. if (offset < 0)
  467. offset = AdjOffsetToNewMode(NESBank.ACCESSMODE_2K, this.__AccessMode, this.__AccessOffset);
  468. if (offset >= 4)
  469. throw new RangeError("Offset mismatch based on Buffer length.");
  470. var list = (offset < 2) ? this.__LP : this.__RP;
  471. var s = Math.floor(offset * 0.5) * 128;
  472. var e = s + 128;
  473. for (let i=s; i < e; i++){
  474. list[i].chr = buff.slice(idx, idx+16);
  475. idx += 16;
  476. }
  477. break;
  478. case 1024:
  479. if (offset < 0)
  480. offset = AdjOffsetToNewMode(NESBank.ACCESSMODE_1K, this.__AccessMode, this.__AccessOffset);
  481. if (offset >= 8)
  482. throw new RangeError("Offset mismatch based on Buffer length.");
  483. var list = (offset < 4) ? this.__LP : this.__RP;
  484. var s = Math.floor(this.__AccessOffset * 0.25) * 64;
  485. var e = s + 64;
  486. for (let i=s; i < e; i++){
  487. list[i].chr = buff.slice(idx, idx+16);
  488. idx += 16;
  489. }
  490. break;
  491. case 16:
  492. if (offset < 0)
  493. offset = AdjOffsetToNewMode(NESBank.ACCESSMODE_1T, this.__AccessMode, this.__AccessOffset);
  494. if (offset >= 512)
  495. throw new RangeError("Offset mismatch based on Buffer length.");
  496. var list = (offset < 256) ? this.__LP : this.__RP;
  497. var idx = offset % 256;
  498. list[idx].chr = buff;
  499. break;
  500. default:
  501. throw new RangeError("Buffer length does not match any of the supported bank sizes.");
  502. }
  503. return this;
  504. }
  505. getColor(x,y){
  506. if (x < 0 || x >= this.width || y < 0 || y >= this.height)
  507. return this.__default_pi[4];
  508. var res = CnvIdx(x, y, this.__AccessMode, this.__AccessOffset);
  509. var list = (res.side === 0) ? this.__LP : this.__RP;
  510. var pi = list[res.tileidx].paletteIndex + ((res.side === 0) ? 4 : 0);
  511. var ci = list[res.tileidx].getPixelIndex(res.x, res.y);
  512. if (this.__palette !== null){
  513. return this.__palette.get_palette_color(pi, ci);
  514. }
  515. return NESPalette.Default[ci];
  516. }
  517. getColorIndex(x, y){
  518. if (x < 0 || x >= this.width || y < 0 || y >= this.height)
  519. return {pi: -1, ci:-1};
  520. var res = CnvIdx(x, y, this.__AccessMode, this.__AccessOffset);
  521. var list = (res.side === 0) ? this.__LP : this.__RP;
  522. return {
  523. pi: list[res.tileidx].paletteIndex,
  524. ci: list[res.tileidx].getPixelIndex(res.x, res.y)
  525. };
  526. }
  527. getRegion(x, y, w, h){
  528. if (w <= 0 || h <= 0)
  529. throw new RangeError("Width and/or Height must be greater than zero.");
  530. var region = [];
  531. for (let j=y; j<y+h; j++){
  532. if (j === this.height){
  533. h = j - y;
  534. break;
  535. }
  536. for (let i=x; i<x+w; i++){
  537. if (i === this.width){
  538. w = i - x;
  539. break;
  540. }
  541. region.push(this.getColorIndex(i, j));
  542. }
  543. }
  544. return {w:w, h:h, r:region};
  545. }
  546. setColorIndex(x, y, ci, pi){
  547. if (x < 0 || x >= this.width || y < 0 || y > this.height)
  548. throw new RangeError("Coordinates out of bounds.");
  549. if (!Utils.isInt(pi))
  550. pi = -1;
  551. if (!Utils.isInt(ci))
  552. ci = 0;
  553. if (pi < 0){
  554. this.coloridx[(y*this.width)+x] = ci;
  555. } else {
  556. var res = CnvIdx(x, y, this.__AccessMode, this.__AccessOffset);
  557. var list = (res.side === 0) ? this.__LP : this.__RP;
  558. list[res.tileidx].paletteIndex = pi;
  559. list[res.tileidx].setPixelIndex(res.x, res.y, ci);
  560. }
  561. return this;
  562. }
  563. setRegion(x, y, w, h, r){
  564. if (w <= 0 || h <= 0)
  565. throw new RangeError("Width and/or Height must be greater than zero.");
  566. if (!(r instanceof Array)){
  567. throw new TypeError("Region expected to be an array.");
  568. }
  569. if (r.length !== w*h)
  570. throw new RangeError("Region length does not match given width/height values.");
  571. for (let j=0; j < h; j++){
  572. for (let i=0; i < w; i++){
  573. var index = (j*w) + i;
  574. if ("pi" in r[index] && "ci" in r[index] && r[index].ci >= 0){
  575. var _x = i+x;
  576. var _y = j+y;
  577. if (_x >= 0 && _x < this.width && _y >= 0 && _y < this.height)
  578. this.setColorIndex(_x, _y, r[index].ci, r[index].pi);
  579. }
  580. }
  581. }
  582. return this;
  583. }
  584. snapshot(){
  585. if (this.__redos.length > 0) // Remove the redo history. We're adding a new snapshot.
  586. this.__redos = [];
  587. var snap = this.base64;
  588. if (this.__undos.length === this.__historyLength){
  589. this.__undos.pop();
  590. }
  591. this.__undos.splice(0,0,snap);
  592. return this;
  593. }
  594. undo(){
  595. if (this.__undos.length > 0){
  596. var usnap = this.__undos.splice(0, 1)[0];
  597. var rsnap = this.base64;
  598. this.base64 = usnap;
  599. if (this.__redos.length === this.__historyLength){
  600. this.__redos.pop();
  601. }
  602. this.__redos.splice(0,0,rsnap);
  603. }
  604. return this;
  605. }
  606. redo(){
  607. if (this.__redos.length > 0){
  608. var rsnap = this.__redos.splice(0,1)[0];
  609. var usnap = this.base64;
  610. this.base64 = rsnap;
  611. if (this.__undos.length === this.__historyLength){
  612. this.__undos.pop();
  613. }
  614. this.__undos.splice(0,0,usnap);
  615. }
  616. return this;
  617. }
  618. clearUndos(){
  619. this.__undos = [];
  620. return this;
  621. }
  622. clearRedos(){
  623. this.__redos = [];
  624. return this;
  625. }
  626. }
  627. NESBank.ACCESSMODE_8K = 0;
  628. NESBank.ACCESSMODE_1K = 1;
  629. NESBank.ACCESSMODE_2K = 2;
  630. NESBank.ACCESSMODE_4K = 3;
  631. NESBank.ACCESSMODE_1T = 4;