Skip to content

Commit

Permalink
Rebuilt WasmBoy Debugger on top of PhosphorJS (#228)
Browse files Browse the repository at this point in the history
relates to #197
relates to #225
closes #168
closes #198

This rebuilds the debugger on top of [PhosphorJs](http://phosphorjs.github.io/). This adds a much more productive UI/UX on desktop, and a more mobile friendly mobile UI. Also, moved to a vanilla rollup config.

Also, this has a tons of fixes and improvements I found while migrating the debugger, and abstracts out a lot of code to be much more cleaner.

# Example

![wasmboydebugger](https://user-images.githubusercontent.com/1448289/50895725-c23b7780-13bb-11e9-96ad-e7433cbacf7d.gif)

<img width="360" alt="screen shot 2019-01-09 at 3 09 37 am" src="https://user-images.githubusercontent.com/1448289/50895812-0a5a9a00-13bc-11e9-80da-90959753db81.png">
  • Loading branch information
torch2424 authored Jan 10, 2019
1 parent 806cd82 commit ad1313d
Show file tree
Hide file tree
Showing 107 changed files with 4,784 additions and 7,014 deletions.
146 changes: 104 additions & 42 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,18 @@
![npm bundle size (minified)](https://img.shields.io/bundlephobia/min/wasmboy.svg)
![npm](https://img.shields.io/npm/dt/wasmboy.svg)
![GitHub](https://img.shields.io/github/license/torch2424/wasmboy.svg)
[![Buy Me A Coffee](https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png)](https://www.buymeacoffee.com/torch2424)

<!--- Short Description-->

🎮👾🕹️ Gameboy Emulator Library written in Web Assembly using [AssemblyScript](https://github.com/AssemblyScript/assemblyscript), Debugger/Shell in Preact 🎮👾🕹️

**Project is still < 1.0.0. Most games are playable, but the emulator is still not very accurate. Expect bugs.**

[1.0 Roadmap Tracking Issue](https://github.com/torch2424/wasmBoy/issues/197)

[Debugger / Demo with support for mobile controls](https://torch2424.github.io/wasmBoy/)
[Core/Lib Documentation](https://github.com/torch2424/wasmBoy/wiki)

[Documentation](https://github.com/torch2424/wasmBoy/wiki)
<!-- Header Images -->

<!-- Header gif -->

![Pokemon Crystal Wasmboy Demo](./docs/images/wasmBoyPokemonCrystal.gif)
![Pokemon Crystal Wasmboy Debugger Demo](./docs/images/debuggerDesktopDemo.gif)

<!-- Generated with: https://github.com/ekalinin/github-markdown-toc -->

Expand All @@ -32,16 +27,22 @@
- [Features](#features)
- [Usage](#usage)
- [Supported Platforms](#supported-platforms)
- [Example Gifs &amp; Screenshots](#example-gifs--screenshots)
- [In-Game Screenshots](#in-game-screenshots)
- [Gameboy Support](#gameboy-support)
- [Gameboy Color Support](#gameboy-color-support)
- [Demo Applications](#demo-applications)
- [Debugger](#debugger)
- [Benchmark](#benchmark)
- [Tests](#tests)
- [Blarrg](#blarrg)
- [Mooneye](#mooneye)
- [Timing](#timing)
- [Halt](#halt)
- [Contributing](#contributing)
- [Installation](#installation)
- [CLI Commands / Npm Scripts](#cli-commands--npm-scripts)
- [Notable Projects](#notable-projects)
- [Special Thanks](#special-thanks)
- [Random Tips for new Gameboy EmuDevs](#random-tips-for-new-gameboy-emudevs)
- [Resources](#resources)

# Features
Expand Down Expand Up @@ -73,23 +74,77 @@ Documentation for the project can be found on the [WasmBoy Wiki](https://github.

Try to test and aim for support on all major browsers (Chrome, Firefox, and Safari). Also, Node support works with the [`headless` option in the WasmBoy config](https://github.com/torch2424/wasmBoy/wiki/Lib-API#wasmboyoptions), and using the [Worker Threads](https://nodejs.org/api/worker_threads.html) `--experimental-worker` flag.

# Example Gifs & Screenshots
# In-Game Screenshots

**Gameboy Support**
### Gameboy Support

![Is that a demo in your pocket](./docs/images/wasmBoyIsThatADemoInYourPocket.png) ![Megaman 2](./docs/images/wasmBoyMegaman2.png) ![Pokemon Blue](./docs/images/wasmBoyPokemonBlue.png) ![tetris](./docs/images/wasmBoyTetris.png) ![tobu tobu girl](./test/performance/testroms/tobutobugirl/tobutobugirl.gb.noPerformanceOptions.png)

**Gameboy Color Support**
### Gameboy Color Support

![Links Awakening](./docs/images/wasmBoyLinksAwakening.png) ![L s d j](./docs/images/wasmBoyLsdj.png) ![Megaman extreme 2](./docs/images/wasmBoyMegamanXtreme2.png) ![Pokemon Silver](./docs/images/wasmBoyPokemonSilver.png) ![Pokemon Yellow](./docs/images/wasmBoyPokemonYellow.png) ![back to color demo](./test/performance/testroms/back-to-color/back-to-color.gbc.noPerformanceOptions.png)

**Options & Save States**
# Demo Applications

### Debugger

[Application Link](https://wasmboy.app/)

A full debugger meant for analyzing the internals of the gameboy. Great for HomeBrew Gameboy Development, or using as a reference point for building your own GameBoy emulator. **See the gif at the top of the README for an example.**

**Features**

- Support of all Gameboy Components: CPU, PPU (Graphics), APU (Audio), Memory, Interrupts, and Timers. 🎮
- Per cycle state of each Game Boy components data, internal registers, and relevant memory addresses. 🌐
- Loaded ROM Information and parsing of the [Cartridge Header](http://gbdev.gg8.se/wiki/articles/The_Cartridge_Header). 💾
- CPU Control options. Stepping per opcode, and breakpoints. 🧠
- Graphics Background Map, with border for current "camera" location with respect to scroll registers. 🖼️
- Graphics Tile Data, to display the loaded tiles currently loaded across all VRAM Banks. 🎨
- WasmBoy Control options. Play, Pause, Save State, and Load State. ⏯️ 📚
- Ability to log the entire WasmBoy Library Object and Memory to the DevTools Console. 🖥️
- Highly productive "Docker" layout, with snapping of widgets onto sections of the screen and tab support. ⚓
- Saved Layouts between sessions. 💠
- Help widget with tips on how to be effective in the debugger. 🙋

**Anaytics / Privacy**

[Analytics Wrapper Service](./demo/debugger/analytics.js)

Analytics is used on this application simply for performance monitoring, and tracking popularity of the applications. The following events are sent, with nothing more than the event name. The analytics provider used is [Google Analytics](https://analytics.google.com/analytics/web/).

- Whenever a new ROM is loaded, and played for the first time.
- Whether attempting to load a ROM was successful.
- Whenever a state is saved.
- Whenever a state is loaded.
- Whenever custom WasmBoy options are applied.
- Whenever the Google Drive option is selected.
- Whenever the mobile demo is manually reloaded.

**Mobile Demo**

For UI/UX reasons, on mobile the debugger is simply a web app for testing the lib. This is useful for testing a ROM on the go. For playing games, I would suggest [VaporBoy](https://vaporboy.net/). Below is an example of the mobile demo:

![Pokemon Crystal Wasmboy Mobile Demo](./docs/images/debuggerMobileDemo.gif)

### Benchmark

![Wasm boy options and save states gif](./docs/images/wasmBoySaveStateOptions.gif)
[Application Link](https://wasmboy.app/benchmark/)

**Debugger**
[Medium Article](https://medium.com/@torch2424/webassembly-is-fast-a-real-world-benchmark-of-webassembly-vs-es6-d85a23f8e193)

![was boy pokemon silver debugger demo](./docs/images/wasmBoyPokemonSilverDebugger.gif)
Since WasmBoy is built in AssemblyScript, it can also run it's core through the Typescript compiler if we mock out some of the WebAssembly interface. The benchmarking tool was built as a way to compare WebAssembly performance to Javascript / ES6 performance, after compiling the core to both WebAssembly and Javascript. It includes detailed stats, live running output, and multiple graphs. Also great for comparing the performance of devices that run WasmBoy.

**Anaytics / Privacy**

Analytics is used on this application simply for performance monitoring, and tracking popularity of the application. The following events are sent, with nothing more than the event name. The analytics provider used is [Google Analytics](https://analytics.google.com/analytics/web/).

- Whenever a new ROM is loaded from the particular source.
- Whenever the benchmark is ran.
- Whenever results are rendered for the benchmark.

**Example**

![WasmBoy Benchmark Runner Section on Safari](./docs/images/benchmarkSafariBackToColorRunner.png)

# Tests

Expand Down Expand Up @@ -139,7 +194,7 @@ halt_ime0_ei, halt_ime0_nointr_timing, halt_ime1_timing

# Contributing

Feel free to fork and submit PRs! Any help is much appreciated, and would be a ton of fun!
Feel free to fork and submit PRs! Opening an issue is reccomended before starting any development, as a discussion would be nice on the idea / feature before writing code. Any help is much appreciated, and would be a ton of fun!

### Installation

Expand All @@ -149,61 +204,68 @@ Just your standard node app. Install Node with [nvm](https://github.com/creation

The project contains three different elements.

- The `debugger` is the container for the wasmboy library, which is simply a [preact](https://github.com/developit/preact) application, generated with [preact-cli](https://github.com/developit/preact-cli).
- The `core` or `wasm` which is the web assembly module for wasmboy written in [AssemblyScript](https://github.com/AssemblyScript/assemblyscript).
- The `lib` which is the importable library of wasmboy that can be used in other projects, that adds a top level API to the `core`.
- The `demo`, which is a collection of different apps that are used for demoing purposes of the `lib` and `core`.

Each of these uses a different build process. The debugger uses [webpack](https://webpack.js.org/), the wasm uses the [AssemblyScript](https://github.com/AssemblyScript/assemblyscript) compiler CLI tool, and the lib uses [Rollup.js](https://rollupjs.org/guide/en).
Most of the build process in this project is done using [Rollup.js](https://rollupjs.org/guide/en). Each element / component of the project is configured in its own `rollup.*.js` file, and are then all used within the standard `rollup.config.js` file by the rollup CLI. Also, The `core` wasm uses the [AssemblyScript](https://github.com/AssemblyScript/assemblyscript) compiler CLI tool.

Commands for each part of the project will be prepended with their element name and a colon, e.g `debugger:[command here]`.

Common command parts are:

- `dev` / `watch` - How the project should be served and developed with tools like reloading.
- `build` - Make production builds of the component / element of the project.

Commands not prepended with a colon are meant for easily building on all of the different parts as a whole.

Not all commands are documented, only ones relevant to making changes to the library for contributions. `*` represents the category of commands, and is not an actual command.

```bash
# Command to serve the project, and watch the debugger, wasm, and lib for changes
# Uses concurrently: https://github.com/kimmobrunfeldt/concurrently
# Concurrently helps cleanup the output and organizes all three watchers/servers
npm start
# Concurrently helps cleanup the output and organizes watchers on commands that require concurrent tools

# Serve the general project for development (Watches the core, lib, and debugger)
npm run start

# Same as npm start
npm run dev

# Same as npm start
npm run watch

# Build the wasm module and the lib to be ready to be pushed to npm or released
# Build everything to be ready to be pushed to npm or released
npm run build

# Linting commands used during precommit an tests
npm run prettier:*

# Commands for building/serving the core, offers commands for building with the Assemblyscript Compiler (WASM) or Typescript (JS)
npm run core:*

# Commands for building/serving the JS lib
npm run lib:*

# Run tests in `test/accuracy/test.js`
npm run test

# Run tests in `test/performance/test.js`
npm run test:performance

# Watch the debugger (preact) project for changes and livereload
npm run debugger:watch
# All commands for testing, and are test related
npm run test:*

# Build the debugger (preact) project and serve it
npm run debugger:serve
# Commands for the building / serving the debugger
npm run debugger:*

# Build the debugger (preact) project
npm run debugger:build
# Commands for building / serving the benchmark tool
npm run benchmark:*

# Watch the wasm (AssemblyScript) *.ts files and build on changes
npm run core:watch

# Build the wasm (AssemblyScript) *.ts files, with the correct CLI flags
npm run core:build

# Watch the Wasmboy ES6 Module for changes, and build
npm run lib:watch

# Build the WasmBoy Es6 module
npm run lib:build
# Commands for building / serving all available apps in wasmboy
npm run demo:*
```

The debugger application/container for wasmboy utilizes the [preact-cli](https://github.com/developit/preact-cli/blob/master/README.md). Additional workflow commands and tips can be found there.

Using the [gh-pages](https://www.npmjs.com/package/gh-pages) for debugger/demo deployment onto gh-pages.

# Notable Projects
Expand Down
6 changes: 4 additions & 2 deletions core/debug/debug-graphics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export function getLY(): i32 {
return Graphics.scanlineRegister;
}

export function drawBackgroundMapToWasmMemory(showColor: i32 = 0): void {
export function drawBackgroundMapToWasmMemory(showColor: i32): void {
// http://www.codeslinger.co.uk/pages/projects/gameboy/graphics.html
// Bit 7 - LCD Display Enable (0=Off, 1=On)
// Bit 6 - Window Tile Map Display Select (0=9800-9BFF, 1=9C00-9FFF)
Expand Down Expand Up @@ -221,7 +221,9 @@ export function drawTileDataToWasmMemory(): void {
tileDataMapGridY * 8 + tileLineY,
0x1f * 8,
TILE_DATA_LOCATION,
true
true,
0,
-1
);
}
}
Expand Down
6 changes: 3 additions & 3 deletions core/graphics/tiles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,9 @@ export function drawPixelsFromLineOfTile(
outputLineY: i32,
outputWidth: i32,
wasmMemoryStart: i32,
shouldRepresentMonochromeColorByColorId: boolean = false,
paletteLocation: i32 = 0,
bgMapAttributes: i32 = -1
shouldRepresentMonochromeColorByColorId: boolean,
paletteLocation: i32,
bgMapAttributes: i32
): i32 {
// Get our number of pixels drawn
let pixelsDrawn: i32 = 0;
Expand Down
2 changes: 1 addition & 1 deletion demo/benchmark/index.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { h, render, Component } from 'preact';

import 'bulma/css/bulma.css';
import '../debugger/style.css';
import './style.css';
import './index.css';

import valoo from 'valoo';
Expand Down
7 changes: 4 additions & 3 deletions demo/benchmark/loadrom.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import { h, Component } from 'preact';
import Portal from 'preact-portal';

import '../debugger/wasmboyFilePicker/wasmboyFilePicker.css';
import './wasmboyFilePicker.css';

// Import some functions from our lib
import { sendAnalyticsEvent } from './analytics';
import { fetchROMAsByteArray } from '../../lib/wasmboy/fetchrom.js';

// Import our open source roms from the debugger
import { openSourceROMs, getOpenSourceROMElements } from '../debugger/wasmboyFilePicker/openSourceROMs';
// Import our open source roms
import { openSourceROMS } from '../openSourceROMs';
import { getOpenSourceROMElements } from '../openSourceROMsPreact';

export default class LoadROMSelector extends Component {
constructor(props) {
Expand Down
File renamed without changes.
File renamed without changes.
85 changes: 85 additions & 0 deletions demo/debugger/analytics.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import loadScript from 'load-script';

class DebuggerAnalyticsLib {
constructor() {
/*ROLLUP_REPLACE_DEBUGGER_DEV
this.readyPromise = Promise.resolve();
return;
ROLLUP_REPLACE_DEBUGGER_DEV*/

this.readyPromise = new Promise((resolve, reject) => {
if (typeof window !== 'undefined') {
loadScript('https://www.googletagmanager.com/gtag/js?id=UA-125276735-1', (err, script) => {
if (err) {
reject(err);
}

window.dataLayer = window.dataLayer || [];
function gtag() {
window.dataLayer.push(arguments);
}
gtag('js', new Date());
gtag('config', 'UA-125276735-1');

// Attach Analytics to this class
this.gtag = gtag;
resolve();
});
} else {
reject(new Error('Not in a browser Environment'));
}
});
}

_fireAnalyticsEvent(eventAction) {
const fireEventTask = async () => {
await this.readyPromise;

if (this.gtag) {
this.gtag('event', eventAction);
} else {
console.log('Analytics Event Action:', eventAction);
}
};

fireEventTask().catch(() => {
// Do Nothing
});
}

// Define our events
ROMLoadedAndStarted() {
this._fireAnalyticsEvent('rom_loaded_and_started');
}

loadROMSuccess() {
this._fireAnalyticsEvent('load_rom_success');
}

loadROMFail() {
this._fireAnalyticsEvent('load_rom_fail');
}

saveState() {
this._fireAnalyticsEvent('save_state');
}

loadState() {
this._fireAnalyticsEvent('load_state');
}

appliedOptions() {
this._fireAnalyticsEvent('applied_options');
}

googleDriveLoad() {
this._fireAnalyticsEvent('google_drive_load');
}

reload() {
this._fireAnalyticsEvent('reload');
}
}

const DebuggerAnalytics = new DebuggerAnalyticsLib();
export default DebuggerAnalytics;
Binary file added demo/debugger/assets/debuggerWalkthrough.mp4
Binary file not shown.
Binary file added demo/debugger/assets/helpOpenROM.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added demo/debugger/assets/helpOpenWidget.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit ad1313d

Please sign in to comment.