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
127
128
129
130
//! 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](https://github.com/rustwasm/wasm-pack), gc_nes_web has full Typescript support
//!
//! ### Try it Now
//!
//! You can try out gc_nes_emulator on my website, at https://garettcooper.com/#/nes-emulator

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()
}