nes.js 3.1 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  1. const SCREEN_WIDTH = 256;
  2. const SCREEN_HEIGHT = 240;
  3. const WORKING_MEMORY = 0x800;
  4. const SAMPLE_REDUCTION = 4;
  5. const jsnes = require('jsnes')
  6. const { Subject } = require('rxjs')
  7. const chunk = (a, l) => Array(Math.ceil(a.length / l)).fill().map((_, i) => (a.slice ? a.slice(i * l, i * l + l) : a.substr(i * l, i * l + 1)))
  8. const sample = (a, n) => Array(Math.floor(a.length / n)).fill().map((_, i) => a[i * n])
  9. class NES {
  10. constructor(rom) {
  11. this.nes = new jsnes.NES({
  12. onFrame: this.onFrame.bind(this)
  13. })
  14. this.nes.loadROM(rom)
  15. this.frameCount = 0
  16. this.packetBuffer = new ArrayBuffer(256 * 4 + SCREEN_WIDTH * SCREEN_HEIGHT + WORKING_MEMORY)
  17. this.paletteBuffer = new Uint32Array(this.packetBuffer)
  18. this.pixelBuffer = new Uint8ClampedArray(this.packetBuffer, 256 * 4)
  19. this.copyMemoryBuffer = new Uint8Array(this.packetBuffer, 256 * 4 + SCREEN_WIDTH * SCREEN_HEIGHT, WORKING_MEMORY)
  20. this.frameBuffer = new ArrayBuffer(SCREEN_WIDTH * SCREEN_HEIGHT * 4)
  21. this.screen32 = new Uint32Array(this.frameBuffer)
  22. this.nes.ppu.buffer = this.screen32
  23. this.frameBytes = new Uint8ClampedArray(this.frameBuffer)
  24. this.frames = new Subject()
  25. }
  26. command(data) {
  27. if (data.type === 'button') {
  28. const player = data.player || 1
  29. this.nes.controllers[player].state[data.button] = data.pressed ? 0x41 : 0x40
  30. }
  31. }
  32. onFrame(frameBuffer) {
  33. //console.log('frames.next' + Date.now())
  34. const colorMap = new Map()
  35. for (let i = 0; i < 256; i++) {
  36. this.paletteBuffer[i] = this.nes.ppu.palTable.curTable[i]
  37. colorMap.set(this.nes.ppu.palTable.curTable[i], i)
  38. }
  39. for (let i = 0; i < this.screen32.length; i++) {
  40. this.pixelBuffer[i] = colorMap.get(this.screen32[i])
  41. }
  42. for (let i = 0; i < WORKING_MEMORY; i++) {
  43. this.copyMemoryBuffer[i] = this.nes.cpu.mem[i]
  44. }
  45. this.frames.next(this.packetBuffer)
  46. }
  47. genie(code) {
  48. const map = { A: 0x0, P: 0x1, Z: 0x2, L: 0x3, G: 0x4, I: 0x5, T: 0x6, Y: 0x7, E: 0x8, O: 0x9, X: 0xA, U: 0xB, K: 0xC, S: 0xD, V: 0xE, N: 0xF }
  49. const [n0, n1, n2, n3, n4, n5, n6, n7] = code.split('').map(c => map[c])
  50. let address, data, compare
  51. address = 0x8000 +
  52. ((n3 & 7) << 12)
  53. | ((n5 & 7) << 8) | ((n4 & 8) << 8)
  54. | ((n2 & 7) << 4) | ((n1 & 8) << 4)
  55. | (n4 & 7) | (n3 & 8)
  56. if (code.length === 6) {
  57. data = ((n1 & 7) << 4) | ((n0 & 8) << 4) | (n0 & 7) | (n5 & 8)
  58. compare = null
  59. } else {
  60. data = ((n1 & 7) << 4) | ((n0 & 8) << 4) | (n0 & 7) | (n7 & 8)
  61. compare = ((n7 & 7) << 4) | ((n6 & 8) << 4) | (n6 & 7) | (n5 & 8)
  62. }
  63. this.hack(address, data, compare)
  64. }
  65. hack(address, data, compare = null) {
  66. let val = this.nes.cpu.mem[address]
  67. Object.defineProperty(this.nes.cpu.mem, address, {
  68. get: () => compare === null || val === compare
  69. ? data
  70. : val,
  71. set: v => (val = v)
  72. })
  73. }
  74. start() {
  75. if (!this.timer) this.timer = setInterval(() => this.nes.frame(), 1000 / 60)
  76. }
  77. stop() {
  78. if (this.timer) {
  79. clearInterval(this.timer)
  80. this.timer = null
  81. }
  82. }
  83. }
  84. module.exports = NES