1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
//! gc_nes_web wraps the public functions exposed by my gc_nes_core crate for use
//! in the browser through Web Assembly - Javascript interop. It provides an interface
//! to load and run NES ROMs, provide input, and extract rendered image data.
//! Audio is currently unsupported.
//!
//! ### Install in an NPM Project
//! `npm install gc_nes_web`
//!
//! ### Using the NES Emulator with Javascript
//! ```javascript
//! // Import the package
//! const wasm = await import ("gc_nes_web");
//! // Create the NES object
//! let nes = this.state.wasm.nes(romArrayOfBytes);
//! // Run the emulator to the completion of the next frame and retrieve it
//! let frame = nes.frame();
//! // Or run just one cycle and get the frame separately
//! nes.cycle();
//! let frame = nes.get_screen();
//! // Drawing to a Canvas
//! let offscreenCanvas = new OffscreenCanvas(256, 240);
//! let offscreenCanvasContext = offscreenCanvas.getContext("2d");
//! let imageData = offscreenCanvasContext?.createImageData(256, 240);
//! imageData.data.set(frame);
//! offscreenCanvasContext.putImageData(imageData, 0, 0);
//! // mainCanvasContext is the 2D context for the Canvas you actually want to draw to.
//! mainCanvasContext.drawImage(offscreenCanvas, 0, 0);
//! ```
//!
//! Through wasm-pack, gc_nes_web has full Typescript support

mod utils;

use gc_nes_core::cartridge::Cartridge;
use gc_nes_core::nes::{Nes, NES_SCREEN_DIMENSIONS};
use wasm_bindgen::prelude::*;

// When the `wee_alloc` feature is enabled, use `wee_alloc` as the global
// allocator.
#[cfg(feature = "wee_alloc")]
#[global_allocator]
static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;

#[wasm_bindgen]
/// Structure used the represent the NES itself in WASM.
pub struct WebNes {
    nes: Nes,
}

#[wasm_bindgen]
impl WebNes {
    /// Creates a new NES instance with no connected controllers.
    pub fn new(cartridge: WebCartridge) -> WebNes {
        WebNes {
            nes: Nes::new(cartridge.cartridge),
        }
    }

    /// Runs a single cycle on the NES
    pub fn cycle(&mut self) {
        self.nes.cycle();
    }

    /// Runs as many cycles as necessary to complete the current frame.
    /// Returns the frame as a Vector of bytes, with each pixel of the
    /// NES screen represented by four bytes in RGBA order.
    pub fn frame(&mut self) -> Vec<u8> {
        self.nes.frame().to_vec()
    }

    /// Gets the current state of the screen from the PPU's screen buffer.
    /// Returns the frame as a Vector of bytes, with each pixel of the
    /// NES screen represented by four bytes in RGBA order.
    pub fn get_screen(&mut self) -> Vec<u8> {
        self.nes.get_screen().to_vec()
    }

    /// Updates the state of the input device connected to the first port.
    pub fn update_controller_one(&mut self, controller_state: u8) {
        self.nes.update_controller_one(Some(controller_state));
    }

    /// Updates the state of the input device connected to the first port.
    pub fn update_controller_two(&mut self, controller_state: u8) {
        self.nes.update_controller_one(Some(controller_state));
    }

    /// Resets the state of the NES.
    pub fn reset(&mut self) {
        self.nes.reset();
    }
}

#[wasm_bindgen]
/// Structure used to represent a NES Cartridge in WASM.
pub struct WebCartridge {
    cartridge: Cartridge,
}

#[wasm_bindgen]
impl WebCartridge {
    /// Loads a NES ROM from an array of bytes into a WebCartridge struct
    pub fn load(rom: &[u8]) -> WebCartridge {
        WebCartridge {
            cartridge: Cartridge::load_from_reader(rom).unwrap(),
        }
    }
}

#[wasm_bindgen]
/// Creates a new NES instance, loading the passed array of bytes as the ROM
pub fn nes(rom: &[u8]) -> WebNes {
    WebNes::new(WebCartridge::load(rom))
}

#[wasm_bindgen]
/// Gets the screen dimensions of the NES
pub fn get_screen_dimensions() -> usize {
    NES_SCREEN_DIMENSIONS
}

#[wasm_bindgen]
/// Enables the panic hook that logs panic messages to the browser console
pub fn set_panic_hook() {
    utils::set_panic_hook()
}