Skip to content

Commit 63ec46a

Browse files
authored
WasmerBoy (#315)
* Made a quick lil demo * it works? * It draws a lil * Fixed it haha! * Got it working with the as-lib * Got input partly working * Yay! Input is less wonky * Removed testing logs, and it works!!1 * Added better help messages * Finished up the wasmboy cli * Added a wapm * Did the new io-devices api thing * Need to pull in latest io-devices changes * Renamed to wasmerboy * Finished up wasmerboy stuff * Moved the wasmerboy output * Added username to package name * Added readme and gif
1 parent f80f1b5 commit 63ec46a

File tree

13 files changed

+835
-421
lines changed

13 files changed

+835
-421
lines changed

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
# Ignore system files
22
**/.DS_Store
33

4+
# Ignore WAPM Files
5+
**/wapm_packages
6+
47
# Ignore game files
58
games/*
69
*.gb
710
!test/**/*.gb
11+
!demo/wasmerboy/tobutobugirl/*.gb
812
*.gbc
913
!test/**/*.gbc
1014
*.sav

.travis.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,4 @@ script:
1111
- npm run demo:build:apps
1212
- npm run test:accuracy:nobuild
1313
- npm run test:integration:nobuild
14+
- npm run wasmerboy:build

demo/wasmerboy/README.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# wasmerboy
2+
3+
A port of [WasmBoy](https://github.com/torch2424/wasmboy) to [Wasmer](https://wasmer.io/), using Wasmer's experimental [I/O Devices](https://github.com/wasmerio/io-devices-lib/tree/master/assemblyscript) and [as-wasi](https://github.com/jedisct1/as-wasi). 🔌 Deployed to [WAPM](http://wapm.io/). 📦
4+
5+
![Gif of running WasmerBoy](./assets/wasmerboy.gif)
6+
7+
## Usage
8+
9+
Install WasmerBoy from WAPM:
10+
11+
`wapm install -g torch2424/wasmerboy`.
12+
13+
Run WasmerBoy by pre-opening a directory, and a rom in that directory:
14+
15+
`wasmerboy --dir=tobutobugirl tobutobugirl/tobutobugirl.gb`
16+
17+
## Contributing and License
18+
19+
This follows the same contribution and license guidlines as [WasmBoy](https://github.com/torch2424/wasmboy).
4.33 MB
Loading

demo/wasmerboy/cli/ansi.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// Ansi Sequences and convenience functions
2+
3+
import { Console } from 'as-wasi';
4+
5+
// ANSI Escape
6+
const ESC: string = '\u001b[';
7+
8+
// Ansi Color Codes
9+
export const GREEN: string = ESC + '32m';
10+
export const WHITE: string = ESC + '37m';
11+
export const RED: string = ESC + '31m';
12+
export const CYAN: string = ESC + '36m';
13+
export const RESET: string = ESC + '0m';
14+
15+
// Ansi Cursor Codes
16+
export const HIDE_CURSOR: string = ESC + '?25h';
17+
18+
export function printColor(value: string, color: string): void {
19+
Console.write(color + value + RESET, false);
20+
}
21+
22+
export function flushConsole(): void {
23+
Console.write(ESC + '2J', false);
24+
}
25+
26+
// https://github.com/nojvek/matrix-rain/blob/master/ansi.js
27+
export function moveCursorToPosition(column: i32, row: i32): void {
28+
let cursorPosition: string = ESC + row.toString() + ';' + column.toString() + 'H';
29+
Console.write(cursorPosition, false);
30+
31+
// Hide the cursor
32+
Console.write(HIDE_CURSOR, false);
33+
}

demo/wasmerboy/cli/cli.ts

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// Convinience functions for our CLI
2+
3+
import { Console } from 'as-wasi';
4+
import { GREEN, CYAN, RED, printColor } from './ansi';
5+
6+
export function showHelp(): void {
7+
Console.log('');
8+
printColor('wasm(er)boy help', GREEN);
9+
Console.log('');
10+
Console.log('');
11+
12+
printColor('USAGE:', CYAN);
13+
Console.log('');
14+
15+
Console.log('[wapm run] wasmboy [my-rom.gb]');
16+
Console.log('');
17+
18+
printColor('FLAGS:', CYAN);
19+
Console.log('');
20+
21+
Console.log('-s, --speed');
22+
Console.log('Speed in frames per second to run the emulation. Must be an integer greater than 1.');
23+
Console.log('Suggested: 60, Default: 60');
24+
Console.log('');
25+
26+
Console.log('-h, --help');
27+
Console.log('Show this help message.');
28+
Console.log('');
29+
30+
printColor('CONTROLS:', CYAN);
31+
Console.log('');
32+
33+
Console.log('Dpad:');
34+
Console.log('Up: Up Arrow, W');
35+
Console.log('Right: Right Arrow, D');
36+
Console.log('Down: Down Arrow, S');
37+
Console.log('Left: Left Arrow, A');
38+
Console.log('');
39+
40+
Console.log('Button:');
41+
Console.log('A: X, Semicolon');
42+
Console.log('B: Z, Backspace');
43+
Console.log('Select: Shift, Tab');
44+
Console.log('Start: Enter, Space');
45+
Console.log('');
46+
}

demo/wasmerboy/index.ts

Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
// Import WasmBoy
2+
import { config, executeFrame, clearAudioBuffer, setJoypadState, CARTRIDGE_ROM_LOCATION, FRAME_LOCATION } from '../../core/index';
3+
4+
// Import all of our utils and things
5+
import { showHelp } from './cli/cli';
6+
7+
import { GREEN, CYAN, RED, printColor } from './cli/ansi';
8+
9+
// Import our CommandLine for grabbing args
10+
import { CommandLine, FileSystem, Descriptor, Console, Time, Date } from 'as-wasi';
11+
12+
import {
13+
openFrameBufferWindow,
14+
drawRgbaArrayToFrameBuffer,
15+
updateInput,
16+
isKeyPressed
17+
} from '../../node_modules/@wasmer/io-devices-lib-assemblyscript/lib/lib';
18+
19+
let GAMEBOY_CAMERA_WIDTH = 160;
20+
let GAMEBOY_CAMERA_HEIGHT = 144;
21+
let FRAMES_PER_SECOND = 60;
22+
let whence: u8 = 2;
23+
24+
function update(): void {
25+
// Update the input from the io-devices
26+
updateInput(0);
27+
28+
// Get the keys we care about
29+
let dpadUp: bool = isKeyPressed('KeyUp') || isKeyPressed('KeyW');
30+
let dpadRight: bool = isKeyPressed('KeyRight') || isKeyPressed('KeyD');
31+
let dpadDown: bool = isKeyPressed('KeyDown') || isKeyPressed('KeyS');
32+
let dpadLeft: bool = isKeyPressed('KeyLeft') || isKeyPressed('KeyA');
33+
let buttonA: bool = isKeyPressed('KeyX') || isKeyPressed('KeySemicolon');
34+
let buttonB: bool = isKeyPressed('KeyZ') || isKeyPressed('KeyBackspace');
35+
let buttonSelect: bool = isKeyPressed('KeyShift') || isKeyPressed('KeyTab');
36+
let buttonStart: bool = isKeyPressed('KeySpace') || isKeyPressed('KeyEnter');
37+
38+
setJoypadState(
39+
dpadUp ? 1 : 0, // up
40+
dpadRight ? 1 : 0, // right
41+
dpadDown ? 1 : 0, // down
42+
dpadLeft ? 1 : 0, // left
43+
buttonA ? 1 : 0, // a
44+
buttonB ? 1 : 0, // b
45+
buttonSelect ? 1 : 0, // select
46+
buttonStart ? 1 : 0 // start
47+
);
48+
49+
// Run a frame a wasmboy
50+
executeFrame();
51+
clearAudioBuffer();
52+
}
53+
54+
function draw(): void {
55+
let imageDataArray = new Array<u8>(160 * 144 * 4);
56+
57+
for (let y = 0; y < GAMEBOY_CAMERA_HEIGHT; ++y) {
58+
let stride1 = y * (GAMEBOY_CAMERA_WIDTH * 3);
59+
let stride2 = y * (GAMEBOY_CAMERA_WIDTH * 4);
60+
for (let x = 0; x < GAMEBOY_CAMERA_WIDTH; ++x) {
61+
// Each color has an R G B component
62+
const pixelStart = stride1 + x * 3;
63+
64+
const imageDataIndex = stride2 + (x << 2);
65+
66+
imageDataArray[imageDataIndex + 2] = load<u8>(FRAME_LOCATION + pixelStart + 0);
67+
imageDataArray[imageDataIndex + 1] = load<u8>(FRAME_LOCATION + pixelStart + 1);
68+
imageDataArray[imageDataIndex + 0] = load<u8>(FRAME_LOCATION + pixelStart + 2);
69+
70+
// Alpha, no transparency
71+
imageDataArray[imageDataIndex + 3] = 255;
72+
}
73+
}
74+
75+
drawRgbaArrayToFrameBuffer(imageDataArray, 0);
76+
}
77+
78+
// Entry point into WASI Module
79+
export function _start(): void {
80+
// Parse command line arguments
81+
let commandLine = new CommandLine();
82+
let args: Array<string> = commandLine.all();
83+
84+
if (args.length <= 1 || args.includes('--help') || args.includes('-h')) {
85+
showHelp();
86+
return;
87+
}
88+
89+
// Check if we have a sleep flag
90+
for (let i = 1; i < args.length - 1; i++) {
91+
if (args[i] == '--speed' || args[i] == '-s') {
92+
FRAMES_PER_SECOND = I32.parseInt(args[i + 1]);
93+
if (FRAMES_PER_SECOND < 1) {
94+
FRAMES_PER_SECOND = 1;
95+
}
96+
97+
// Remove these arguments from the args
98+
args.splice(i, 2);
99+
100+
// Reset i
101+
i = 0;
102+
}
103+
}
104+
105+
// Finally, we should have parsed all flags, the last remaining arg should be the rom.
106+
let romArg: string = args[1];
107+
108+
printColor('Loading Rom: ', CYAN);
109+
Console.log(romArg);
110+
Console.log('');
111+
112+
// Check if the Rom Exists
113+
if (!FileSystem.exists(romArg)) {
114+
printColor('Rom File not found', RED);
115+
Console.log('');
116+
Console.log('');
117+
118+
return;
119+
}
120+
121+
// Read the ROM, place into memory
122+
let rom: Descriptor = FileSystem.open(romArg) as Descriptor;
123+
let romBytes = rom.readAll() as Array<u8>;
124+
for (let i: i32 = 0; i < romBytes.length; i++) {
125+
store<u8>(CARTRIDGE_ROM_LOCATION + i, romBytes[i]);
126+
}
127+
128+
printColor('Configuring WasmBoy...', CYAN);
129+
Console.log('');
130+
Console.log('');
131+
132+
// Configure the core
133+
config(
134+
0, // enableBootRom: i32,
135+
1, // useGbcWhenAvailable: i32,
136+
0, // audioBatchProcessing: i32,
137+
0, // graphicsBatchProcessing: i32,
138+
0, // timersBatchProcessing: i32,
139+
0, // graphicsDisableScanlineRendering: i32,
140+
0, // audioAccumulateSamples: i32,
141+
0, // tileRendering: i32,
142+
0, // tileCaching: i32,
143+
0 // enableAudioDebugging: i32
144+
);
145+
146+
printColor('Running WasmBoy!', GREEN);
147+
Console.log('');
148+
Console.log('');
149+
150+
// Get how much time we want to spend on each frame to get our frames per second
151+
let timePerFrame: i32 = 1000 / FRAMES_PER_SECOND;
152+
153+
// Open a framebuffer
154+
openFrameBufferWindow(GAMEBOY_CAMERA_WIDTH, GAMEBOY_CAMERA_HEIGHT, 0);
155+
156+
while (true) {
157+
let startTime = Date.now();
158+
159+
// Update Input and things
160+
update();
161+
162+
// Draw the frame from wasmboy
163+
draw();
164+
165+
let loopTime: i32 = <i32>(Date.now() - startTime) / 1000;
166+
let millisecondsToWaitForNextLoop: i32 = timePerFrame - loopTime;
167+
if (millisecondsToWaitForNextLoop > 0) {
168+
Time.sleep(millisecondsToWaitForNextLoop * Time.MILLISECOND);
169+
}
170+
}
171+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2017 Tangram Games
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.
256 KB
Binary file not shown.

demo/wasmerboy/wapm.lock

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# Lockfile v4
2+
# This file is automatically generated by Wapm.
3+
# It is not intended for manual editing. The schema of this file may change.
4+
[modules."torch2424/wasmerboy"."0.0.1".wasmerboy]
5+
name = "wasmerboy"
6+
package_version = "0.0.1"
7+
package_name = "torch2424/wasmerboy"
8+
package_path = "torch2424/wasmerboy@0.0.1"
9+
resolved = "local"
10+
resolved_source = "local"
11+
abi = "wasi"
12+
source = "dist/wasmerboy.wasm"
13+
prehashed_module_key = "668613b3f2865b26640a164153fc84e69d48b13f846dfe3b778b76243ec9089afc40080e4bd28657fd13fb0d54206609733ff7eb3542b8f544ae1447cc0672a3"
14+
[commands.wasmerboy]
15+
name = "wasmerboy"
16+
package_name = "torch2424/wasmerboy"
17+
package_version = "0.0.1"
18+
module = "wasmerboy"
19+
is_top_level_dependency = true

0 commit comments

Comments
 (0)