n64js/index.html

677 lines
No EOL
26 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="en" data-bs-theme="light">
<head>
<!-- Google tag (gtag.js) -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-VNCSM6YVD4"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag() { dataLayer.push(arguments); }
gtag('js', new Date());
gtag('config', 'G-VNCSM6YVD4');
</script>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet"
integrity="sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN" crossorigin="anonymous">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/font/bootstrap-icons.css">
<link rel="stylesheet" href="n64js.css">
<title>n64js - An N64 Emulator in JavaScript</title>
</head>
<body>
<div class="container-fluid">
<div class="row">
<div class="col col-sm-12">
<h1 id="title">n64js</a></h1>
<p>An n64 emulator. <a href="#" onclick="$('#info').toggle()">Info</a></p>
</div>
</div>
<div class="row toolbar">
<div class="col col-sm-12">
<div class="btn-toolbar">
<div class="btn-group">
<button type="button" class="btn" onclick="n64js.ui().triggerLoad()">
<i class="bi bi-file-earmark-binary"></i> Load</button>
</div>
<div class="btn-group">
<button type="button" class="btn" onclick="n64js.toggleRun()" id="runbutton">
<i class="bi bi-play"></i> Run</button>
</div>
<div class="btn-group">
<button type="button" class="btn" onclick="n64js.toggleFullscreen()">
<i class="bi bi-fullscreen"></i> Fullscreen</button>
</div>
<div class="btn-group">
<button type="button" class="btn" onclick="n64js.debugger().toggle()">
<i class="bi bi-bug"></i> Debug</button>
<button type="button" class="btn" onclick="n64js.togglePerformance()">
<i class="bi bi-graph-up"></i> Perf</button>
</div>
<div class="btn-group">
<div class="dropdown">
<button class="btn dropdown-toggle" id="bd-theme" type="button" data-bs-toggle="dropdown"
data-bs-display="static">
<span class="theme-icon-active"><i class="bi bi-brightness-high"></i></span>
<span id="bd-theme-text">Theme</span>
</button>
<ul class="dropdown-menu">
<li>
<button class="dropdown-item" type="button" data-bs-theme-value="light"><i
class="bi bi-brightness-high"></i>
Light</button>
<button class="dropdown-item" type="button" data-bs-theme-value="dark"><i
class="bi bi-moon-stars"></i>
Dark</button>
<button class="dropdown-item" type="button" data-bs-theme-value="auto"><i
class="bi bi-circle-half"></i>
Auto</button>
</li>
</ul>
</div>
</div>
<input style="visibility:hidden; display:inline-block;" id="fileInput" name="fileInput" type="file"
onchange="n64js.ui().loadFile()" />
</div>
</div>
</div>
<div class="row">
<div class="col col-sm-12" id="alerts"></div>
</div>
<div class="row">
<div style="white-space:nowrap;" class="col col-sm-8">
<canvas id="display" width="640" height="480" style="display:inline-block; background-color:#000;"></canvas>
<div id="adjacent-debug" class="col col-sm-10 debug debug-adjacent hidden"></div>
</div>
<div class="col col-sm-2" id="performance"></div>
</div>
<div class="row">
<div class="col col-sm-8" id="info" style="display:none">
<h2>Status</h2>
<p>Many games run at close to full speed on a M2 Macbook Pro but there are plenty of graphical issues.
See <a href="https://github.com/hulkholden/n64js">github</a> for the latest status.
</p>
<h2>Supported Browsers</h2>
<p>I did some quick testing on OSX 13.5:</p>
<table class="table table-condensed">
<tr>
<td>Chrome</td>
<td>116.0.5845.140 - runs well. I've been doing most of my development in Chrome so this is the preferred
option.</td>
</tr>
<tr>
<td>Firefox</td>
<td>117.0 - runs, but is slower than Chrome.</td>
</tr>
<tr>
<td>Safari</td>
<td>16.6 - runs, but is slower than Chrome.</td>
</tr>
<tr>
<td>Edge</td>
<td>Untested.</td>
</tr>
</table>
<p>WebGL is required, which places certain restrictions on which GPUs the emulator will work with. Most modern
GPUs should work though.</p>
<p>Get in touch via <a href="https://twitter.com/#!/hulkholden">twitter</a> if you have any additional info.</p>
<h2>Known Issues</h2>
<p>See <a href="https://github.com/hulkholden/n64js/issues">github</a>.</p>
<h2>Controls</h2>
<table class="table table-sm">
<thead>
<tr>
<th>N64</th>
<th>Keyboard</th>
</tr>
</thead>
<tbody class="table-group-divider">
<tr>
<td>Start</td>
<td>A</td>
</tr>
<tr>
<td>A</td>
<td>S</td>
</tr>
<tr>
<td>B</td>
<td>X</td>
</tr>
<tr>
<td>Z</td>
<td>Z</td>
</tr>
<tr>
<td>L</td>
<td>C</td>
</tr>
<tr>
<td>R</td>
<td>V</td>
</tr>
<tr>
<td>DPad Up</td>
<td>T</td>
</tr>
<tr>
<td>DPad Down</td>
<td>G</td>
</tr>
<tr>
<td>DPad Left</td>
<td>F</td>
</tr>
<tr>
<td>DPad Right</td>
<td>H</td>
</tr>
<tr>
<td>C Up</td>
<td>I</td>
</tr>
<tr>
<td>C Down</td>
<td>K</td>
</tr>
<tr>
<td>C Left</td>
<td>J</td>
</tr>
<tr>
<td>C Right</td>
<td>L</td>
</tr>
</tbody>
</table>
<h2>Origins</h2>
<p>n64js is based on Daedalus, an emulator I started writing while I was at university under a pseudonym of <a
href="http://strmnnrmn.blogspot.co.uk/">StrmnNrmn</a>.
I've worked on Daedalus intermittantly ever since, but life and work have limited the time I've been able to
spend on the project in the past few years. The project
lives on due to the excellent work of everyone at <a href="http://forums.daedalusx64.com/">DaedalusX</a>. In
particular, Kreationz, Wally, Corn, Salvy all deserve
great credit for keeping the project going. Also, hello to Schibo, who was working on porting 1964 to
JavaScript: <a href="https://github.com/schibo/1964js">1964js</a>.</p>
<h2>Thanks</h2>
<p>Jan-Christoph Borchardt - better keyboard mapping for QWERTZ keyboards.</p>
</div>
</div>
<div class="debug hidden">
<div class="row">
<div class="col col-sm-4">
<form>
<div class="input-group">
<button type="button" class="btn" onclick="n64js.step()"><i class="bi bi-skip-end"></i> Step</button>
</div>
<div class="input-group">
<label class="form-label" for="speed">Speed</label>
<input class="form-range" type="range" min="0" max="8" value="0" id="cpu-speed" />
</div>
</form>
</div>
</div>
<div class="row">
<div class="col col-sm-12">
<div class="tabbable">
<ul class="nav nav-underline">
<li class="nav-item">
<button class="nav-link active" id="output-tab" data-bs-toggle="tab" data-bs-target="#output-content"
type="button" role="tab">Output</button>
</li>
<li class="nav-item">
<button class="nav-link" id="cpu-tab" data-bs-toggle="tab" data-bs-target="#cpu-content" type="button"
role="tab">CPU</button>
</li>
<li class="nav-item">
<button class="nav-link" id="rsp-tab" data-bs-toggle="tab" data-bs-target="#rsp-content" type="button"
role="tab">RSP</button>
</li>
<li class="nav-item">
<button class="nav-link" id="memory-tab" data-bs-toggle="tab" data-bs-target="#memory-content"
type="button" role="tab">Memory</button>
</li>
<li class="nav-item">
<button class="nav-link" id="dynarec-tab" data-bs-toggle="tab" data-bs-target="#dynarec-content"
type="button" role="tab">Dynarec</button>
</li>
<li class="nav-item">
<button class="nav-link" id="dlist-tab" data-bs-toggle="tab" data-bs-target="#dlist-content"
type="button" role="tab">DisplayList</button>
</li>
<li class="nav-item">
<button class="nav-link" id="texture-tab" data-bs-toggle="tab" data-bs-target="#texture-content"
type="button" role="tab">Textures</button>
</li>
<li class="nav-item">
<button class="nav-link" id="timeline-tab" data-bs-toggle="tab" data-bs-target="#timeline-content" type="button"
role="tab">Timeline</button>
</li>
</ul>
<div class="tab-content">
<div class="tab-pane active" id="output-content"></div>
<div class="tab-pane" id="cpu-content">
<div id="cpu" class="processor-display">
<div class="processor-details">
<table class="register-table">
<tbody>
<tr>
<td>Ops</td>
<td class="fixed" id="cpu0-status-opsexecuted"></td>
</tr>
<tr>
<td>PC</td>
<td class="fixed" id="cpu0-status-pc"></td>
<td>delayPC</td>
<td class="fixed" id="cpu0-status-delaypc"></td>
</tr>
<tr>
<td>EPC</td>
<td class="fixed" id="cpu0-status-epc"></td>
</tr>
<tr>
<td>MultHi</td>
<td class="fixed" id="cpu0-status-multhi"></td>
<td>Cause</td>
<td class="fixed" id="cpu0-status-cause"></td>
</tr>
<tr>
<td>MultLo</td>
<td class="fixed" id="cpu0-status-multlo"></td>
<td>Count</td>
<td class="fixed" id="cpu0-status-count"></td>
</tr>
<tr>
<td></td>
<td class="fixed"></td>
<td>Compare</td>
<td class="fixed" id="cpu0-status-compare"></td>
</tr>
<tr>
<td>&nbsp;</td>
</tr>
<tr>
<td>SR</td>
<td class="fixed">
<span id="cpu0-status-sr"></span>&nbsp;
<span id="cpu0-status-sr-ie">IE</span>&nbsp;
<span id="cpu0-status-sr-exl">EXL</span>&nbsp;
<span id="cpu0-status-sr-erl">ERL</span>&nbsp;
</td>
</tr>
<tr>
<td>MI Intr</td>
<td class="fixed">
<span id="cpu0-status-mi-sp">SP</span>&nbsp;
<span id="cpu0-status-mi-si">SI</span>&nbsp;
<span id="cpu0-status-mi-ai">AI</span>&nbsp;
<span id="cpu0-status-mi-vi">VI</span>&nbsp;
<span id="cpu0-status-mi-pi">PI</span>&nbsp;
<span id="cpu0-status-mi-dp">DP</span>&nbsp;
</td>
</tr>
</tbody>
</table>
<table class="register-table" id="cpu0-status-events">
<tbody>
</tbody>
</table>
<div class="tabbable tabs-left">
<ul class="nav nav-underline">
<li class="nav-item">
<button class="nav-link active" id="cpu0-tab" data-bs-toggle="tab"
data-bs-target="#cpu0-content" type="button" role="tab">cpu0</button>
</li>
<li class="nav-item">
<button class="nav-link" id="cpu1-tab" data-bs-toggle="tab" data-bs-target="#cpu1-content"
type="button" role="tab">cpu1</button>
</li>
</ul>
<div class="tab-content">
<div id="cpu0-content" class="tab-pane active"></div>
<div id="cpu1-content" class="tab-pane"></div>
</div>
</div>
</div>
<div class="processor-details">
<div class="control-group">
<label class="control-label" for="address">Address</label>
<div class="controls">
<input class="input-small" id="address" type="text" placeholder="address" />
<select id="labels">
</select>
</div>
</div>
<div id="cpu-disasm" class="fixed">
<div class="dis">
<div class="dis-gutter"></div>
<div class="dis-view"></div>
</div>
<div class="dis-recent-memory"></div>
</div>
</div>
</div>
</div>
<div class="tab-pane" id="rsp-content">
<div id="rsp" class="processor-display">
<div class="processor-details">
<table class="register-table">
<tbody>
<tr>
<td>Halted</td>
<td class="fixed" id="rsp-status-halted"></td>
</tr>
<tr>
<td>PC</td>
<td class="fixed" id="rsp-status-pc"></td>
<td>delayPC</td>
<td class="fixed" id="rsp-status-delaypc"></td>
</tr>
<tr>
<td>nextPC</td>
<td class="fixed" id="rsp-status-nextpc"></td>
<td>branchTarget</td>
<td class="fixed" id="rsp-status-branchtarget"></td>
</tr>
<tr>
<td>VCO</td>
<td class="fixed" id="rsp-status-vco"></td>
</tr>
<tr>
<td>VCC</td>
<td class="fixed" id="rsp-status-vcc"></td>
</tr>
<tr>
<td>VCE</td>
<td class="fixed" id="rsp-status-vce"></td>
</tr>
</tbody>
</table>
<div class="tabbable tabs-left">
<ul class="nav nav-underline">
<li class="nav-item">
<button class="nav-link active" data-bs-toggle="tab" data-bs-target="#rsp-scalar-content" type="button" role="tab">Scalar</button>
</li>
<li class="nav-item">
<button class="nav-link" data-bs-toggle="tab" data-bs-target="#rsp-vector-content" type="button" role="tab">Vector</button>
</li>
<li class="nav-item">
<button class="nav-link" data-bs-toggle="tab" data-bs-target="#rsp-task-content" type="button" role="tab">Task</button>
</li>
</ul>
<div class="tab-content">
<div id="rsp-scalar-content" class="tab-pane active"></div>
<div id="rsp-vector-content" class="tab-pane"></div>
<div id="rsp-task-content" class="tab-pane"></div>
</div>
</div>
</div>
<div class="processor-details">
<div class="control-group">
<label class="control-label" for="rsp-address">Address</label>
<div class="controls">
<input class="input-small" id="rsp-address" type="text" placeholder="address" />
<select id="rsp-labels">
</select>
</div>
</div>
<div id="rsp-disasm" class="fixed">
<div class="dis">
<div class="dis-gutter"></div>
<div class="dis-view"></div>
</div>
<div class="dis-recent-memory"></div>
</div>
</div>
</div>
</div>
<div class="tab-pane" id="memory-content">
<input type="text" placeholder="address" />
<pre></pre>
</div>
<div class="tab-pane" id="dlist-content">
<div id="controls">
<div class="btn-toolbar">
<div class="btn-group">
<button type="button" class="btn" id="rwd">
<i class="bi bi-skip-start"></i></button>
<button type="button" class="btn" id="stop">
<i class="bi bi-pause"></i></button>
<button type="button" class="btn" id="fwd">
<i class="bi bi-skip-end"></i></button>
</div>
</div>
<div class="scrub">
<div class="scrub-text"></div>
<div><input type="range" min="0" max="0" value="0" /></div>
</div>
<br>
<div class="hle-state">
<div class="tabbable">
<ul class="nav nav-underline">
<li class="nav-item">
<button class="nav-link" data-bs-toggle="tab" data-bs-target="#dl-geometrymode-content" type="button"
role="tab">Geometry
Mode</button>
</li>
<li class="nav-item">
<button class="nav-link" data-bs-toggle="tab" data-bs-target="#dl-vertices-content" type="button"
role="tab">Vertices</button>
</li>
<li class="nav-item">
<button class="nav-link active" data-bs-toggle="tab" data-bs-target="#dl-tiles-content" type="button"
role="tab">Tiles</button>
</li>
<li class="nav-item">
<button class="nav-link" data-bs-toggle="tab" data-bs-target="#dl-combiner-content" type="button"
role="tab">Combiner</button>
</li>
<li class="nav-item">
<button class="nav-link" data-bs-toggle="tab" data-bs-target="#dl-rdp-content" type="button" role="tab">RDP</button>
</li>
</ul>
<div class="tab-content">
<div class="tab-pane" id="dl-geometrymode-content"></div>
<div class="tab-pane" id="dl-vertices-content"></div>
<div class="tab-pane active" id="dl-tiles-content"></div>
<div class="tab-pane" id="dl-rdp-content"></div>
<div class="tab-pane" id="dl-combiner-content"></div>
</div>
</div>
</div>
</div>
</div>
<div class="tab-pane" id="dynarec-content"></div>
<div class="tab-pane" id="texture-content"></div>
<div class="tab-pane" id="timeline-content">
<div class="timeline-panel"></div>
</div>
</div>
</div>
</div>
</div>
<div class="row">
&nbsp;
</div>
<div class="row">
<div class="col col-sm-12" id="output">
<button type="button" class="btn" id="clear">
<i class="bi bi-trash"></i>
Clear</button>
<div class="output fixed"></div>
</div>
</div>
</div>
<div class="row">
<div class="col col-sm-12">
<p>By <a href="https://twitter.com/#!/hulkholden">@HulkHolden</a>. <a
href="http://n64js.blogspot.co.uk/">Blog</a>. <a href="https://github.com/hulkholden/n64js">Code</a>.
<a href="http://www.youtube.com/user/n64js">Videos</a>.
</p>
</div>
</div>
</div>
<!-- Bake local version of the imports. -->
<script type="importmap">
{
"imports": {
"lil-gui": "https://cdn.jsdelivr.net/npm/lil-gui@0.18.2/+esm"
}
}
</script>
<script src="js/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"
integrity="sha384-C6RzsynM9kWDrMNeT87bh95OGNyZPhcTNXj1NW7RuBCsyN/o0jlpcV8Qyq46cDfL"
crossorigin="anonymous"></script>
<script src="js/bootstrap-color-mode-toggler.js"></script>
<script src="js/md5.js"></script>
<script src="js/stats.js"></script>
<script src="js/webgl-debug.js"></script>
<script type="module" src="build/n64.min.js"></script>
<script id="fill-shader-vs" type="x-shader/x-vertex">
attribute vec4 aVertexPosition;
void main(void) {
gl_Position = aVertexPosition;
}
</script>
<script id="fill-shader-fs" type="x-shader/x-fragment">
precision mediump float;
uniform vec4 uFillColor;
varying mediump vec2 vTextureCoord;
void main(void) {
gl_FragColor = uFillColor;
}
</script>
<script id="blit-shader-vs" type="x-shader/x-vertex">
attribute vec4 aVertexPosition;
attribute vec2 aTextureCoord;
varying mediump vec2 vTextureCoord;
void main(void) {
gl_Position = aVertexPosition;
vTextureCoord = aTextureCoord;
}
</script>
<script id="blit-shader-fs" type="x-shader/x-fragment">
precision mediump float;
varying mediump vec2 vTextureCoord;
uniform sampler2D uSampler0;
void main(void) {
gl_FragColor = texture2D(uSampler0, vTextureCoord);
}
</script>
<script id="n64-shader-vs" type="x-shader/x-vertex">
attribute vec4 aVertexPosition;
attribute vec4 aVertexColor;
attribute vec2 aTextureCoord;
varying vec4 vColor;
varying mediump vec2 vTextureCoord;
void main(void) {
gl_Position = aVertexPosition;
vColor = aVertexColor;
vTextureCoord = aTextureCoord;
}
</script>
<script id="n64-shader-fs" type="x-shader/x-fragment">
precision mediump float;
varying vec4 vColor;
varying mediump vec2 vTextureCoord;
uniform sampler2D uSampler0;
uniform sampler2D uSampler1;
uniform vec2 uTexOffset0;
uniform vec2 uTexOffset1;
uniform vec2 uTexScale0;
uniform vec2 uTexScale1;
uniform vec4 uPrimColor;
uniform vec4 uEnvColor;
uniform float uAlphaThreshold;
void main(void) {
vec2 textureCoord0 = (vTextureCoord - uTexOffset0) * uTexScale0;
vec2 textureCoord1 = (vTextureCoord - uTexOffset1) * uTexScale1;
vec4 shade = vColor;
vec4 prim = uPrimColor;
vec4 env = uEnvColor;
vec4 one = vec4(1,1,1,1);
vec4 zero = vec4(0,0,0,0);
// TODO: consider doing the shift/mask/clamp here.
vec4 tex0 = texture2D(uSampler0, textureCoord0);
vec4 tex1 = texture2D(uSampler1, textureCoord1);
vec4 col;
vec4 combined = vec4(0,0,0,1);
float lod_frac = 0.0; // FIXME
float prim_lod_frac = 0.0; // FIXME
float k5 = 0.0; // FIXME
{{body}}
gl_FragColor = col;
}
</script>
<script defer>
document.addEventListener('DOMContentLoaded', function () {
n64js.init();
});
</script>
</body>
<template id="alert">
<div class="alert">
<button class="close" data-dismiss="alert">×</button>
<strong class="alert-type"></strong> <span class="alert-message"></span>
</div>
</template>
</html>