mirror of
https://github.com/inexorabletash/jsbasic.git
synced 2025-02-07 22:30:52 +00:00
dos2unix; factor out index.js; use addEvent/getClassList shims; change screen style using CSS classes
This commit is contained in:
parent
9aa91a4410
commit
a49ed766c9
110
bell.js
110
bell.js
@ -1,55 +1,55 @@
|
||||
//
|
||||
// Applesoft BASIC in Javascript
|
||||
// Bell - play an audio file for the CHR$(7) "BEL" beep
|
||||
//
|
||||
|
||||
// Copyright (C) 2009-2011 Joshua Bell
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
var Bell;
|
||||
if (typeof Bell !== 'function') {
|
||||
Bell = function(base) {
|
||||
|
||||
var tag;
|
||||
|
||||
// Prefer HTML5 audio
|
||||
tag = document.createElement('audio');
|
||||
if (typeof tag.canPlayType === 'function') {
|
||||
tag.setAttribute('preload', 'true');
|
||||
if (tag.canPlayType('audio/mp3')) {
|
||||
tag.setAttribute('src', base + 'bell.mp3');
|
||||
} else if (tag.canPlayType('audio/ogg')) {
|
||||
tag.setAttribute('src', base + 'bell.ogg');
|
||||
} else if (tag.canPlayType('audio/wav')) {
|
||||
tag.setAttribute('src', base + 'bell.wav');
|
||||
}
|
||||
this.play = function() { tag.play(); };
|
||||
this.stop = function() { tag.pause(); tag.currentTime = 0; };
|
||||
return;
|
||||
}
|
||||
|
||||
// Fallback for IE<9
|
||||
tag = document.createElement('bgsound');
|
||||
if ('loop' in tag) {
|
||||
tag.src = base + 'bell.wav';
|
||||
tag.loop = 1;
|
||||
this.play = function() { document.body.appendChild(tag); };
|
||||
this.stop = function() { document.body.removeChild(tag); };
|
||||
return;
|
||||
}
|
||||
|
||||
this.play = function() { };
|
||||
this.stop = function() { };
|
||||
};
|
||||
}
|
||||
//
|
||||
// Applesoft BASIC in Javascript
|
||||
// Bell - play an audio file for the CHR$(7) "BEL" beep
|
||||
//
|
||||
|
||||
// Copyright (C) 2009-2011 Joshua Bell
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
var Bell;
|
||||
if (typeof Bell !== 'function') {
|
||||
Bell = function(base) {
|
||||
|
||||
var tag;
|
||||
|
||||
// Prefer HTML5 audio
|
||||
tag = document.createElement('audio');
|
||||
if (typeof tag.canPlayType === 'function') {
|
||||
tag.setAttribute('preload', 'true');
|
||||
if (tag.canPlayType('audio/mp3')) {
|
||||
tag.setAttribute('src', base + 'bell.mp3');
|
||||
} else if (tag.canPlayType('audio/ogg')) {
|
||||
tag.setAttribute('src', base + 'bell.ogg');
|
||||
} else if (tag.canPlayType('audio/wav')) {
|
||||
tag.setAttribute('src', base + 'bell.wav');
|
||||
}
|
||||
this.play = function() { tag.play(); };
|
||||
this.stop = function() { tag.pause(); tag.currentTime = 0; };
|
||||
return;
|
||||
}
|
||||
|
||||
// Fallback for IE<9
|
||||
tag = document.createElement('bgsound');
|
||||
if ('loop' in tag) {
|
||||
tag.src = base + 'bell.wav';
|
||||
tag.loop = 1;
|
||||
this.play = function() { document.body.appendChild(tag); };
|
||||
this.stop = function() { document.body.removeChild(tag); };
|
||||
return;
|
||||
}
|
||||
|
||||
this.play = function() { };
|
||||
this.stop = function() { };
|
||||
};
|
||||
}
|
||||
|
132
feed.js
132
feed.js
@ -1,66 +1,66 @@
|
||||
//
|
||||
// Atom to HTML - fetch a feed, inject it as dl/dt/dd
|
||||
//
|
||||
|
||||
// Copyright (C) 2009-2010 Joshua Bell
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
function atomToHtml(uri, element) {
|
||||
|
||||
var READYSTATE_UNINITIALIZED = 0;
|
||||
var READYSTATE_LOADING = 1;
|
||||
var READYSTATE_LOADED = 2;
|
||||
var READYSTATE_INTERACTIVE = 3;
|
||||
var READYSTATE_COMPLETE = 4;
|
||||
|
||||
var xhr = new XMLHttpRequest();
|
||||
var async = true;
|
||||
xhr.open("GET", uri, async);
|
||||
xhr.onreadystatechange = function() {
|
||||
if (xhr.readyState === READYSTATE_COMPLETE) {
|
||||
if ((xhr.status === 200 || xhr.status === 0) && xhr.responseXML) {
|
||||
var doc = xhr.responseXML;
|
||||
var entries = doc.getElementsByTagName('entry');
|
||||
var html = [];
|
||||
|
||||
html.push('<dl>');
|
||||
|
||||
for (var i = 0; i < entries.length; i += 1) {
|
||||
var entry = entries[i];
|
||||
try {
|
||||
var entryHTML = [];
|
||||
entryHTML.push('<dt>', entry.getElementsByTagName('title')[0].childNodes[0].nodeValue);
|
||||
entryHTML.push('<dd>', entry.getElementsByTagName('content')[0].childNodes[0].nodeValue);
|
||||
html.push(entryHTML.join(''));
|
||||
} catch (e) {
|
||||
if (console && console.log) { console.log("Error:", e); }
|
||||
}
|
||||
}
|
||||
|
||||
html.push('</dl>');
|
||||
|
||||
element.innerHTML = html.join('');
|
||||
} else {
|
||||
element.innerHTML = '<em>Unable to load feed</em>';
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
try {
|
||||
xhr.send(null);
|
||||
} catch (e) {
|
||||
element.innerHTML = '<em>Unable to load feed</em>';
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Atom to HTML - fetch a feed, inject it as dl/dt/dd
|
||||
//
|
||||
|
||||
// Copyright (C) 2009-2010 Joshua Bell
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
function atomToHtml(uri, element) {
|
||||
|
||||
var READYSTATE_UNINITIALIZED = 0;
|
||||
var READYSTATE_LOADING = 1;
|
||||
var READYSTATE_LOADED = 2;
|
||||
var READYSTATE_INTERACTIVE = 3;
|
||||
var READYSTATE_COMPLETE = 4;
|
||||
|
||||
var xhr = new XMLHttpRequest();
|
||||
var async = true;
|
||||
xhr.open("GET", uri, async);
|
||||
xhr.onreadystatechange = function() {
|
||||
if (xhr.readyState === READYSTATE_COMPLETE) {
|
||||
if ((xhr.status === 200 || xhr.status === 0) && xhr.responseXML) {
|
||||
var doc = xhr.responseXML;
|
||||
var entries = doc.getElementsByTagName('entry');
|
||||
var html = [];
|
||||
|
||||
html.push('<dl>');
|
||||
|
||||
for (var i = 0; i < entries.length; i += 1) {
|
||||
var entry = entries[i];
|
||||
try {
|
||||
var entryHTML = [];
|
||||
entryHTML.push('<dt>', entry.getElementsByTagName('title')[0].childNodes[0].nodeValue);
|
||||
entryHTML.push('<dd>', entry.getElementsByTagName('content')[0].childNodes[0].nodeValue);
|
||||
html.push(entryHTML.join(''));
|
||||
} catch (e) {
|
||||
if (console && console.log) { console.log("Error:", e); }
|
||||
}
|
||||
}
|
||||
|
||||
html.push('</dl>');
|
||||
|
||||
element.innerHTML = html.join('');
|
||||
} else {
|
||||
element.innerHTML = '<em>Unable to load feed</em>';
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
try {
|
||||
xhr.send(null);
|
||||
} catch (e) {
|
||||
element.innerHTML = '<em>Unable to load feed</em>';
|
||||
}
|
||||
}
|
||||
|
||||
|
304
hires.js
304
hires.js
@ -1,152 +1,152 @@
|
||||
//
|
||||
// Applesoft BASIC in Javascript
|
||||
// High Resolution Graphics (HiRes) Emulation
|
||||
//
|
||||
|
||||
// Copyright (C) 2009-2011 Joshua Bell
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
|
||||
// This variation does not rely on a canvas element with the same dimensions
|
||||
// as the pixel buffer; instead, it paints pixels on the canvas using rectangles.
|
||||
// This works even if the underlying canvas implementation does not support
|
||||
// scaling (e.g. fallbacks for IE using VML or Flash)
|
||||
|
||||
// Usage:
|
||||
//
|
||||
// var hires = new LoRes( element, width, height )
|
||||
// hires.clear()
|
||||
// hires.setColor( color_index )
|
||||
// hires.plot( x, y )
|
||||
// hires.plot_to( x, x )
|
||||
// hires.getPixel( x, x ) // for "HSCRN"
|
||||
// hires.show( bool )
|
||||
// { width: w, height: h } = hires.getScreenSize()
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// <script>
|
||||
// hires = new HiRes( document.getElementById( 'hires' ), 140, 192 );
|
||||
// interpreter = new BasicInterpreter( tty, lores, hires, paddle );
|
||||
// </script>
|
||||
// <canvas id="hires"></canvas>
|
||||
|
||||
function HiRes(element, width, height) {
|
||||
var COLORS, // Apple II to HTML color table
|
||||
last_x = 0,
|
||||
last_y = 0,
|
||||
pixels = [],
|
||||
color = 0,
|
||||
context = element.getContext("2d");
|
||||
|
||||
pixels.length = width * height;
|
||||
|
||||
// Colors c/o USENET:
|
||||
// Date: Wed, 05 Sep 2007 01:04:20 +0200
|
||||
// From: Linards Ticmanis <ticmanis@gmx.de>
|
||||
// Newsgroups: comp.sys.apple2
|
||||
// Subject: Re: Double hires mode color artifacts
|
||||
// Message-ID: <46dde477$0$4527$9b4e6d93@newsspool3.arcor-online.net>
|
||||
|
||||
COLORS = [
|
||||
'#000000', // Black 1
|
||||
'#2fbc1a', // Green
|
||||
'#d043e5', // Violet
|
||||
'#ffffff', // White 1
|
||||
'#000000', // Black 2
|
||||
'#d06a1a', // Orange
|
||||
'#2f95e5', // Medium Blue
|
||||
'#ffffff' // White 2
|
||||
];
|
||||
|
||||
this.clear = function(use_color) {
|
||||
var i;
|
||||
if (!use_color) {
|
||||
context.clearRect(0, 0, element.width, element.height);
|
||||
pixels = [];
|
||||
pixels.length = width * height;
|
||||
} else {
|
||||
context.fillStyle = COLORS[color];
|
||||
context.fillRect(0, 0, element.width, element.height);
|
||||
pixels = [];
|
||||
pixels.length = width * height;
|
||||
for (i = 0; i < pixels.length; i += 1) {
|
||||
pixels[i] = color;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
this.setColor = function(newColor) {
|
||||
color = Math.floor(newColor) % COLORS.length;
|
||||
};
|
||||
|
||||
|
||||
function drawPixel(x, y) {
|
||||
var sx = element.width / width,
|
||||
sy = element.height / height;
|
||||
context.fillRect(x * sx, y * sy, sx, sy);
|
||||
pixels[x + y * width] = color;
|
||||
}
|
||||
|
||||
this.plot = function(x, y) {
|
||||
context.fillStyle = COLORS[color];
|
||||
drawPixel(x, y);
|
||||
|
||||
last_x = x;
|
||||
last_y = y;
|
||||
|
||||
};
|
||||
|
||||
this.plot_to = function(x, y) {
|
||||
var x0 = last_x, y0 = last_y, x1 = x, y1 = y,
|
||||
dx = Math.abs(x1 - x0),
|
||||
dy = Math.abs(y1 - y0),
|
||||
sx = (x0 < x1) ? 1 : -1,
|
||||
sy = (y0 < y1) ? 1 : -1,
|
||||
err = dx - dy,
|
||||
e2;
|
||||
|
||||
while (true) {
|
||||
this.plot(x0, y0);
|
||||
|
||||
if (x0 === x1 && y0 === y1) { return; }
|
||||
e2 = 2 * err;
|
||||
if (e2 > -dy) {
|
||||
err -= dy;
|
||||
x0 += sx;
|
||||
}
|
||||
if (e2 < dx) {
|
||||
err += dx;
|
||||
y0 += sy;
|
||||
}
|
||||
}
|
||||
last_x = x;
|
||||
last_y = y;
|
||||
|
||||
};
|
||||
|
||||
this.getPixel = function(x, y) {
|
||||
/*jslint bitwise:false*/
|
||||
return pixels[y * width + x] >>> 0;
|
||||
};
|
||||
|
||||
this.getScreenSize = function() {
|
||||
return { width: width, height: height };
|
||||
};
|
||||
|
||||
this.show = function(state) {
|
||||
element.style.visibility = state ? "visible" : "hidden";
|
||||
};
|
||||
}
|
||||
//
|
||||
// Applesoft BASIC in Javascript
|
||||
// High Resolution Graphics (HiRes) Emulation
|
||||
//
|
||||
|
||||
// Copyright (C) 2009-2011 Joshua Bell
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
|
||||
// This variation does not rely on a canvas element with the same dimensions
|
||||
// as the pixel buffer; instead, it paints pixels on the canvas using rectangles.
|
||||
// This works even if the underlying canvas implementation does not support
|
||||
// scaling (e.g. fallbacks for IE using VML or Flash)
|
||||
|
||||
// Usage:
|
||||
//
|
||||
// var hires = new LoRes( element, width, height )
|
||||
// hires.clear()
|
||||
// hires.setColor( color_index )
|
||||
// hires.plot( x, y )
|
||||
// hires.plot_to( x, x )
|
||||
// hires.getPixel( x, x ) // for "HSCRN"
|
||||
// hires.show( bool )
|
||||
// { width: w, height: h } = hires.getScreenSize()
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// <script>
|
||||
// hires = new HiRes( document.getElementById( 'hires' ), 140, 192 );
|
||||
// interpreter = new BasicInterpreter( tty, lores, hires, paddle );
|
||||
// </script>
|
||||
// <canvas id="hires"></canvas>
|
||||
|
||||
function HiRes(element, width, height) {
|
||||
var COLORS, // Apple II to HTML color table
|
||||
last_x = 0,
|
||||
last_y = 0,
|
||||
pixels = [],
|
||||
color = 0,
|
||||
context = element.getContext("2d");
|
||||
|
||||
pixels.length = width * height;
|
||||
|
||||
// Colors c/o USENET:
|
||||
// Date: Wed, 05 Sep 2007 01:04:20 +0200
|
||||
// From: Linards Ticmanis <ticmanis@gmx.de>
|
||||
// Newsgroups: comp.sys.apple2
|
||||
// Subject: Re: Double hires mode color artifacts
|
||||
// Message-ID: <46dde477$0$4527$9b4e6d93@newsspool3.arcor-online.net>
|
||||
|
||||
COLORS = [
|
||||
'#000000', // Black 1
|
||||
'#2fbc1a', // Green
|
||||
'#d043e5', // Violet
|
||||
'#ffffff', // White 1
|
||||
'#000000', // Black 2
|
||||
'#d06a1a', // Orange
|
||||
'#2f95e5', // Medium Blue
|
||||
'#ffffff' // White 2
|
||||
];
|
||||
|
||||
this.clear = function(use_color) {
|
||||
var i;
|
||||
if (!use_color) {
|
||||
context.clearRect(0, 0, element.width, element.height);
|
||||
pixels = [];
|
||||
pixels.length = width * height;
|
||||
} else {
|
||||
context.fillStyle = COLORS[color];
|
||||
context.fillRect(0, 0, element.width, element.height);
|
||||
pixels = [];
|
||||
pixels.length = width * height;
|
||||
for (i = 0; i < pixels.length; i += 1) {
|
||||
pixels[i] = color;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
this.setColor = function(newColor) {
|
||||
color = Math.floor(newColor) % COLORS.length;
|
||||
};
|
||||
|
||||
|
||||
function drawPixel(x, y) {
|
||||
var sx = element.width / width,
|
||||
sy = element.height / height;
|
||||
context.fillRect(x * sx, y * sy, sx, sy);
|
||||
pixels[x + y * width] = color;
|
||||
}
|
||||
|
||||
this.plot = function(x, y) {
|
||||
context.fillStyle = COLORS[color];
|
||||
drawPixel(x, y);
|
||||
|
||||
last_x = x;
|
||||
last_y = y;
|
||||
|
||||
};
|
||||
|
||||
this.plot_to = function(x, y) {
|
||||
var x0 = last_x, y0 = last_y, x1 = x, y1 = y,
|
||||
dx = Math.abs(x1 - x0),
|
||||
dy = Math.abs(y1 - y0),
|
||||
sx = (x0 < x1) ? 1 : -1,
|
||||
sy = (y0 < y1) ? 1 : -1,
|
||||
err = dx - dy,
|
||||
e2;
|
||||
|
||||
while (true) {
|
||||
this.plot(x0, y0);
|
||||
|
||||
if (x0 === x1 && y0 === y1) { return; }
|
||||
e2 = 2 * err;
|
||||
if (e2 > -dy) {
|
||||
err -= dy;
|
||||
x0 += sx;
|
||||
}
|
||||
if (e2 < dx) {
|
||||
err += dx;
|
||||
y0 += sy;
|
||||
}
|
||||
}
|
||||
last_x = x;
|
||||
last_y = y;
|
||||
|
||||
};
|
||||
|
||||
this.getPixel = function(x, y) {
|
||||
/*jslint bitwise:false*/
|
||||
return pixels[y * width + x] >>> 0;
|
||||
};
|
||||
|
||||
this.getScreenSize = function() {
|
||||
return { width: width, height: height };
|
||||
};
|
||||
|
||||
this.show = function(state) {
|
||||
element.style.visibility = state ? "visible" : "hidden";
|
||||
};
|
||||
}
|
||||
|
654
index.htm
654
index.htm
@ -1,452 +1,202 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>Applesoft BASIC in JavaScript</title>
|
||||
<link rel="Stylesheet" href="styles.css" type="text/css">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <!-- Suppress browser compat button -->
|
||||
<script type="text/javascript" src="../polyfill/polyfill.js?update=2011-12-17"></script>
|
||||
<script type="text/javascript" src="../polyfill/harmony.js?update=2011-12-17"></script>
|
||||
<script type="text/javascript">
|
||||
(function () {
|
||||
function load(url) { document.write('<script type="text/javascript" src="' + url + '"></' + 'script>'); }
|
||||
if (!window.JSON) { load('../polyfill/json2.js'); }
|
||||
if (!window.localStorage || !window.sessionStorage) { load('../polyfill/storage.js'); }
|
||||
if (!('getContext' in document.createElement('canvas'))) { load('../polyfill/flashcanvas.js'); }
|
||||
}());
|
||||
</script>
|
||||
<script type="text/javascript" src="../polyfill/keyboard.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1>Applesoft BASIC in Javascript</h1>
|
||||
<p>
|
||||
By <a href="mailto:inexorabletash@hotmail.com">Joshua Bell</a>
|
||||
| <a target="_blank" href="reference.htm">Applesoft BASIC Quick Reference</a>
|
||||
| <a href="#notes">Notes & Known Issues</a>
|
||||
| <a href="#todo">To Do</a>
|
||||
| <a href="#links">Links</a>
|
||||
| <a href="#history">History</a>
|
||||
|
||||
<p>Related projects:
|
||||
<a href="/Logo">Logo in Javascript</a>
|
||||
| <a href="/vnIIc">Streaming video to an Apple II - vnIIc</a>
|
||||
</p>
|
||||
|
||||
<br clear=left>
|
||||
|
||||
<!-- Screen -->
|
||||
<div id="frame" class="frame" style="float: left; margin: 10px;">
|
||||
<div id="screen-wrapper" class="wrapper">
|
||||
<div id="lores" class="lores"></div>
|
||||
<canvas id="hires" width="560" height="384" class="hires"></canvas>
|
||||
<canvas id="hires2" width="560" height="384" class="hires"></canvas>
|
||||
<div id="screen" class="tty"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Keyboard - positioned offscreen -->
|
||||
<input id="keyboard" class="keyboard" type="text" title="Hidden Keyboard Input Device">
|
||||
|
||||
<!-- Source -->
|
||||
<div style="float: left; margin: 10px;">
|
||||
Enter code below
|
||||
|
||||
<input type="button" value="Run" id="btn_run">
|
||||
<input type="button" value="Stop" id="btn_stop" disabled="disabled">
|
||||
|
||||
<select id="lb_files">
|
||||
<option disabled selected="selected">Select a file...</option>
|
||||
<option value="sample.basic">DEMOS</option>
|
||||
|
||||
<option disabled>---- Tests ----</option>
|
||||
<option value="sample.unittests">Unit Tests</option>
|
||||
<option value="sample.keyboard">Keyboard Test</option>
|
||||
<option value="sample.charset">Charset Test</option>
|
||||
|
||||
|
||||
<option disabled>---- User Submissions ----</option>
|
||||
|
||||
<option disabled>Games</option>
|
||||
<option value="simple.pong">SIMPLE.PONG</option>
|
||||
<option value="sample.adventure">Text Adventure (Floyd McWilliams)</option>
|
||||
<option value="sample.pacman">(Not Really) ASCII Pac-Man (Michael Kemp)</option>
|
||||
<option value="sample.puzzler">Puzzler (Gregg Buntin)</option>
|
||||
<option value="sample.hangman">Hangman (Mike Gleason)</option>
|
||||
<option value="sample.raindrops">Catch the Raindrop (Nicholas Merchant)</option>
|
||||
<option value="sample.jot">JOT (Mike Gleason)</option>
|
||||
|
||||
<option disabled>Graphics</option>
|
||||
<option value="sample.rodscolorpattern">Rod's Color Pattern</option>
|
||||
<option value="sample.hacker">Hacker Logo (markwstock)</option>
|
||||
<option value="sample.loreswalk">Random LoRes (John Melesky)</option>
|
||||
<option value="sample.hireswalk">Random HiRes (John Melesky)</option>
|
||||
<option value="sample.sierpinski">Sierpinski Triangles (Kevin Miller)</option>
|
||||
<option value="sample.stringart">String Art (Chris Heric)</option>
|
||||
<option value="sample.paint">Drawing Program (Brian Broker)</option>
|
||||
<option value="sample.scribble">Scribble (William Simms)</option>
|
||||
<option value="sample.connections">Connections (Gregg Buntin)</option>
|
||||
<option value="sample.squiggle">Squiggle (Gregg Buntin)</option>
|
||||
<option value="sample.boys_surface">Boy's Surface (Lukas Innig)</option>
|
||||
<option value="sample.gaussian">Gaussian Distribution 2D (John Russ)</option>
|
||||
<option value="sample.bitmaps">Bitmap Images (Brian Broker)</option>
|
||||
<option value="sample.mandelbrot">Mandelbrot Set (c/o Gregory Lewis)</option>
|
||||
<option value="sample.mandelbrot2">Mandelbrot Set in Color</option>
|
||||
<option value="sample.steve">Steve (Nicola Foggi)</option>
|
||||
|
||||
<option disabled>Other</option>
|
||||
<option value="sample.primes">Prime Sieve (Kevin Miller)</option>
|
||||
<option value="sample.february">February Surprise (Antti Pirskanen)</option>
|
||||
<option value="sample.hellosine">Hello World Sine Wave (Jamie Beu)</option>
|
||||
<option value="sample.bodymass">Body Mass Index Calculator (Tim Dwyer)</option>
|
||||
|
||||
<option disabled>---- Traveller RPG Utilities ----</option>
|
||||
<option value="TRADER C">TRADER</option>
|
||||
<option value="sample.sectorgen">Traveller Sector Generator</option>
|
||||
<option value="sample.zhorelay">Zhodani Relay Station Placement</option>
|
||||
<option value="sample.readsector">Read Sector File</option>
|
||||
</select>
|
||||
|
||||
<input type="button" id="btn_capture" value="Echo to "Printer"" title="Pops up a "printer" window and echoes all output there, so you can copy/paste">
|
||||
|
||||
<!-- Source code editor inserted here -->
|
||||
<div id="editorframe"></div>
|
||||
|
||||
<form id="submission" method="post" enctype="text/plain" action="mailto:inexorabletash@hotmail.com?subject=Applesoft%20Sample%20Submission">
|
||||
<textarea name="source" id="source" style="display: none;">
|
||||
</textarea>
|
||||
<input type="submit" id="btn_share" value="Share your sample!">
|
||||
<input type="button" id="btn_save" value="Save Program" title="Save the current program. NOTE: Can only save one program.">
|
||||
<input type="button" id="btn_load" value="Load Program" title="Load a previously saved program. WARNING: The current program will be lost!">
|
||||
</form>
|
||||
|
||||
</div>
|
||||
|
||||
<br clear=left>
|
||||
|
||||
<h3 id="notes">Notes & Known Issues</h3>
|
||||
<ul>
|
||||
<li>The BASIC program is compiled to JavaScript before execution. Syntax errors are therefore detected at compile-time
|
||||
rather than at run-time as on a traditional interpreter. For example, the following program would run without errors
|
||||
on an Apple since the erroneous second statement is never reached. <code>10 END : CHR$(PRINT)</code>
|
||||
<li>Handling of BASIC code that does not match the canonical <code>LIST</code> output format may not behave as on an Apple:
|
||||
<ul>
|
||||
<li>Keyword parsing differs from Applesoft command line. For example <code>FOR I = S TO P</code> doesn't collapse into <code>FOR I = STOP</code>.
|
||||
<li>The interpreter doesn't actually care about line numbers for statement ordering (just for <code>GOTO</code>/<code>GOSUB</code> targets and <code>IF</code> statements). So <code>20 PRINT "A"</code>, <code>10 PRINT "B"</code> will just print A, then B
|
||||
</ul>
|
||||
<li>To improve readability, lines may start with <code>:</code> and continue the previously numbered line.
|
||||
<li>Floating point overflow is only detected on variable assignment.
|
||||
<li>The DOS operating system implements only a subset of DOS 3.3 and ProDOS useful for basic file I/O.
|
||||
<li>Except for a small number of compatibility shims for common operations (e.g. keyboard strobe), commands that refer to assembly routines (<code>PEEK</code>, <code>POKE</code>, <code>CALL</code>, <code>USR</code> etc.), shape tables, or tape I/O are not implemented.
|
||||
<li>Commands that operate on the program itself (<code>LIST</code>, <code>RUN</code>, <code>DEL</code>, etc.) are not implemented.
|
||||
<li>You can run your basic programs from the command line (with only basic text input and output, and no graphics or DOS commands):
|
||||
<ul>
|
||||
<li>On Windows, download <a href="basic.js">basic.js</a> and run from a command prompt via:
|
||||
<code>cscript.exe basic.js your_basic_program.txt</code>
|
||||
<li>On Mac/Linux, install <a href="http://www.mozilla.org/rhino/">Mozilla Rhino</a>,
|
||||
download <a href="basic.js">basic.js</a> and run from the command prompt via:
|
||||
<code>java -jar PATH_TO/js.jar basic.js your_program.txt</code>
|
||||
</ul>
|
||||
</ul>
|
||||
|
||||
<h3 id="todo">To Do</h3>
|
||||
<ul>
|
||||
<li>Implement DOS functionality for consoles
|
||||
</ul>
|
||||
|
||||
<h3 id="links">Links</h3>
|
||||
<ul>
|
||||
<li><a href="http://www.6502asm.com/">6502asm.com</a> - a 6502 assembler/emulator in JavaScript
|
||||
<li><a href="http://www.quitebasic.com/">Quite BASIC</a> - a similar project aimed at teaching programming
|
||||
<li><a href="http://navahogunleg.net/blog/my-projects/ng-basic/">NG-BASIC for Javascript</a> Navaho Gunleg's interpreter
|
||||
<li><a href="http://www.nicholson.com/rhn/basic/">BASIC Programming Resources</a>
|
||||
</ul>
|
||||
|
||||
<h3 id="history">History</h3>
|
||||
<div id="feed"></div>
|
||||
|
||||
|
||||
<script src="cm2/lib/codemirror.js"></script>
|
||||
<link rel="stylesheet" href="cm2/lib/codemirror.css">
|
||||
<link rel="stylesheet" href="cm2/theme/default.css">
|
||||
<script src="cm2/mode/basic/basic.js"></script>
|
||||
<link rel="stylesheet" href="cm2/mode/basic/basic.css">
|
||||
<style type="text/css">
|
||||
.CodeMirror { border: solid 1px black; width: 598px; height: 384px; background-color: white; }
|
||||
.CodeMirror-scroll { height: 100%; }
|
||||
</style>
|
||||
|
||||
|
||||
<script type="text/javascript" src="basic.js?2012-02-08"></script>
|
||||
<script type="text/javascript" src="bell.js"></script>
|
||||
<script type="text/javascript" src="tty.js"></script>
|
||||
<script type="text/javascript" src="lores.js"></script>
|
||||
<script type="text/javascript" src="hires.js"></script>
|
||||
<script type="text/javascript" src="dos.js"></script>
|
||||
<script type="text/javascript" src="printer.js"></script>
|
||||
<script type="text/javascript" src="feed.js"></script>
|
||||
<script type="text/javascript">
|
||||
|
||||
// not quite ready for jQuery...
|
||||
var $ = function (id) { return document.getElementById(id); };
|
||||
|
||||
//
|
||||
// Main
|
||||
//
|
||||
window.onload = function() {
|
||||
|
||||
$('lb_files').selectedIndex = 0;
|
||||
|
||||
var bell = (function() {
|
||||
var b = new Bell(/^.*\/|/.exec(window.location)[0]);
|
||||
return function() { b.play(); };
|
||||
} ());
|
||||
|
||||
|
||||
var tty = new TTY($('screen'), $('keyboard'), bell);
|
||||
var dos = new DOS(tty);
|
||||
|
||||
var lores = new LoRes($('lores'), 40, 48);
|
||||
var hires = new HiRes($('hires'), 280, 192);
|
||||
var hires2 = new HiRes($('hires2'), 280, 192);
|
||||
var display = {
|
||||
state: { graphics: false, full: true, page1: true, lores: true },
|
||||
setState: function(state, value /* ... */) {
|
||||
var args = Array.prototype.slice.call(arguments);
|
||||
while (args.length) {
|
||||
state = args.shift();
|
||||
value = args.shift();
|
||||
this.state[state] = value;
|
||||
}
|
||||
|
||||
if (this.state.graphics) {
|
||||
lores.show(this.state.lores);
|
||||
hires.show(!this.state.lores && this.state.page1);
|
||||
hires2.show(!this.state.lores && !this.state.page1);
|
||||
tty.splitScreen(tty.getScreenSize().height - (this.state.full ? 0 : 4));
|
||||
}
|
||||
else {
|
||||
lores.show(false);
|
||||
hires.show(false);
|
||||
hires2.show(false);
|
||||
tty.splitScreen(0);
|
||||
}
|
||||
}
|
||||
};
|
||||
var pdl = [0, 0, 0, 0];
|
||||
|
||||
// Lexical highlighting
|
||||
var editor = new CodeMirror($('editorframe'), {
|
||||
mode: 'basic',
|
||||
tabMode: 'default',
|
||||
content: $('source').value,
|
||||
height: '100%'
|
||||
});
|
||||
|
||||
$('btn_share').onclick = function() {
|
||||
// Load up the hidden text area with the current source
|
||||
$('source').value = getSource();
|
||||
};
|
||||
|
||||
function getSource() {
|
||||
return editor.getValue();
|
||||
}
|
||||
|
||||
function setSource(source) {
|
||||
editor.setValue(source);
|
||||
}
|
||||
|
||||
// Do not let certain events take focus away from "keyboard"
|
||||
function keyboardFocus(e) {
|
||||
e = e ? e : window.event;
|
||||
|
||||
tty.focus();
|
||||
e.cancelBubble = true; // IE
|
||||
e.returnValue = false;
|
||||
if (e.stopPropagation) { e.stopPropagation(); } // W3C
|
||||
if (e.preventDefault) { e.preventDefault(); } // e.g. to block arrows from scrolling the page
|
||||
return false;
|
||||
}
|
||||
|
||||
$('lores').onclick = keyboardFocus;
|
||||
$('hires').onclick = keyboardFocus;
|
||||
$('hires2').onclick = keyboardFocus;
|
||||
$('screen').onclick = keyboardFocus;
|
||||
$('frame').onclick = keyboardFocus;
|
||||
$('frame').onblur = keyboardFocus; // Needed on IE, not sure why it tries to get focus
|
||||
|
||||
$('keyboard').onfocus = function() { $('frame').style.backgroundColor = '#204020'; };
|
||||
$('keyboard').onblur = function() { $('frame').style.backgroundColor = ''; };
|
||||
|
||||
var program;
|
||||
$('btn_run').onclick = function() {
|
||||
dos.reset();
|
||||
tty.reset();
|
||||
tty.autoScroll = true;
|
||||
|
||||
try {
|
||||
program = basic.compile(getSource());
|
||||
}
|
||||
catch (e) {
|
||||
if (e instanceof basic.ParseError) {
|
||||
editor.setCursor({ line: e.line - 1, ch: e.column - 1 });
|
||||
console.log(e.message +
|
||||
' (source line:' + e.line + ', column:' + e.column + ')');
|
||||
}
|
||||
alert(e);
|
||||
return;
|
||||
}
|
||||
|
||||
stopped = false;
|
||||
updateUI();
|
||||
|
||||
program.init({
|
||||
tty: tty,
|
||||
hires: hires,
|
||||
hires2: hires2,
|
||||
lores: lores,
|
||||
display: display,
|
||||
paddle: function(n) { return pdl[n]; }
|
||||
});
|
||||
setTimeout(driver, 0);
|
||||
};
|
||||
|
||||
$('btn_stop').onclick = function() {
|
||||
tty.reset(); // cancel any blocking input
|
||||
stopped = true;
|
||||
updateUI();
|
||||
};
|
||||
|
||||
$('lb_files').onchange = function() {
|
||||
var sel = $('lb_files');
|
||||
loadFile('samples/' + sel.value + ".txt", setSource);
|
||||
};
|
||||
|
||||
$('btn_save').onclick = function() { window.localStorage.setItem("save_program", getSource()); };
|
||||
$('btn_load').onclick = function() { setSource(window.localStorage.getItem("save_program")); };
|
||||
|
||||
// Add a "printer" on demand
|
||||
$('btn_capture').onclick = function() {
|
||||
|
||||
$('btn_capture').disabled = true;
|
||||
var printer = new Printer(tty);
|
||||
|
||||
printer.onclose = function() {
|
||||
$('btn_capture').disabled = false;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
// Mouse-as-Joystick
|
||||
var wrapper = $('screen-wrapper');
|
||||
wrapper.onmousemove = function(e) {
|
||||
e = e || window.event;
|
||||
|
||||
// compute relative coordinates
|
||||
var x = e.clientX, y = e.clientY, elem = wrapper;
|
||||
while (elem) {
|
||||
x -= elem.offsetLeft - elem.scrollLeft;
|
||||
y -= elem.offsetTop - elem.scrollTop;
|
||||
elem = elem.offsetParent;
|
||||
}
|
||||
|
||||
function clamp(n, min, max) { return Math.min(Math.max(n, min), max); }
|
||||
pdl[0] = clamp(x / (wrapper.offsetWidth - 1), 0, 1);
|
||||
pdl[1] = clamp(y / (wrapper.offsetHeight - 1), 0, 1);
|
||||
};
|
||||
|
||||
|
||||
var stopped = true;
|
||||
function updateUI() {
|
||||
$("btn_stop").disabled = stopped ? "disabled" : "";
|
||||
$("btn_run").disabled = stopped ? "" : "disabled";
|
||||
$("lb_files").disabled = stopped ? "" : "disabled";
|
||||
|
||||
if (stopped) {
|
||||
$("btn_run").focus();
|
||||
}
|
||||
else {
|
||||
tty.focus();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// TODO: Expose a RESPONSIVE |---+--------| FAST slider in the UI
|
||||
// Number of steps to execute before yielding execution
|
||||
// (Use a prime to minimize risk of phasing with loops)
|
||||
var NUM_SYNCHRONOUS_STEPS = 37;
|
||||
|
||||
function driver() {
|
||||
var state = basic.STATE_RUNNING;
|
||||
var statements = NUM_SYNCHRONOUS_STEPS;
|
||||
|
||||
while (!stopped && state === basic.STATE_RUNNING && statements > 0) {
|
||||
|
||||
try {
|
||||
state = program.step(driver);
|
||||
}
|
||||
catch (e) {
|
||||
console.log(e);
|
||||
alert(e.message ? e.message : e);
|
||||
stopped = true;
|
||||
updateUI();
|
||||
return;
|
||||
}
|
||||
|
||||
statements -= 1;
|
||||
}
|
||||
|
||||
if (state === basic.STATE_STOPPED || stopped) {
|
||||
stopped = true;
|
||||
updateUI();
|
||||
}
|
||||
else if (state === basic.STATE_BLOCKED) {
|
||||
// Fall out
|
||||
}
|
||||
else // state === basic.STATE_RUNNING
|
||||
{
|
||||
setTimeout(driver, 0); // Keep going
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
function loadFile(filename, callback) {
|
||||
var req = new XMLHttpRequest();
|
||||
var url = encodeURI(filename); // not encodeURIComponent, so paths can be specified
|
||||
var async = true;
|
||||
req.open("GET", url, async);
|
||||
req.onreadystatechange = function() {
|
||||
if (req.readyState === XMLHttpRequest.DONE) {
|
||||
if (req.status === 200 || req.status === 0) // 0 for file:// protocol
|
||||
{
|
||||
callback(req.responseText);
|
||||
}
|
||||
}
|
||||
}
|
||||
req.send(null);
|
||||
}
|
||||
|
||||
// load default
|
||||
loadFile('samples/sample.default.txt', setSource);
|
||||
|
||||
// Show change history
|
||||
atomToHtml('feed.xml?' + Math.random(), $('feed'));
|
||||
};
|
||||
|
||||
|
||||
</script>
|
||||
<script type="text/javascript">
|
||||
var _gaq = _gaq || [];
|
||||
_gaq.push(['_setAccount', 'UA-18610679-3']);
|
||||
_gaq.push(['_trackPageview']);
|
||||
|
||||
(function() {
|
||||
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
|
||||
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
|
||||
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
|
||||
})();
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>Applesoft BASIC in JavaScript</title>
|
||||
<link rel="Stylesheet" href="styles.css" type="text/css">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <!-- Suppress browser compat button -->
|
||||
<script type="text/javascript" src="../polyfill/polyfill.js?update=2012-02-08"></script>
|
||||
<script type="text/javascript" src="../polyfill/harmony.js?update=2011-12-17"></script>
|
||||
<script type="text/javascript">
|
||||
(function () {
|
||||
function load(url) { document.write('<script type="text/javascript" src="' + url + '"></' + 'script>'); }
|
||||
if (!window.JSON) { load('../polyfill/json2.js'); }
|
||||
if (!window.localStorage || !window.sessionStorage) { load('../polyfill/storage.js'); }
|
||||
if (!('getContext' in document.createElement('canvas'))) { load('../polyfill/flashcanvas.js'); }
|
||||
}());
|
||||
</script>
|
||||
<script type="text/javascript" src="../polyfill/keyboard.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1>Applesoft BASIC in Javascript</h1>
|
||||
<p>
|
||||
By <a href="mailto:inexorabletash@hotmail.com">Joshua Bell</a>
|
||||
| <a target="_blank" href="reference.htm">Applesoft BASIC Quick Reference</a>
|
||||
| <a href="#notes">Notes & Known Issues</a>
|
||||
| <a href="#todo">To Do</a>
|
||||
| <a href="#links">Links</a>
|
||||
| <a href="#history">History</a>
|
||||
|
||||
<p>Related projects:
|
||||
<a href="/Logo">Logo in Javascript</a>
|
||||
| <a href="/vnIIc">Streaming video to an Apple II - vnIIc</a>
|
||||
</p>
|
||||
|
||||
<br clear=left>
|
||||
|
||||
<!-- Screen -->
|
||||
<div id="frame" class="frame" style="float: left; margin: 10px;">
|
||||
<div id="screen-wrapper" class="wrapper">
|
||||
<div id="lores" class="lores"></div>
|
||||
<canvas id="hires" width="560" height="384" class="hires"></canvas>
|
||||
<canvas id="hires2" width="560" height="384" class="hires"></canvas>
|
||||
<div id="screen" class="tty"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Keyboard - positioned offscreen -->
|
||||
<input id="keyboard" class="keyboard" type="text" title="Hidden Keyboard Input Device">
|
||||
|
||||
<!-- Source -->
|
||||
<div style="float: left; margin: 10px;">
|
||||
Enter code below
|
||||
|
||||
<input type="button" value="Run" id="btn_run">
|
||||
<input type="button" value="Stop" id="btn_stop" disabled="disabled">
|
||||
|
||||
<select id="lb_files">
|
||||
<option disabled selected="selected">Select a file...</option>
|
||||
<option value="sample.basic">DEMOS</option>
|
||||
|
||||
<option disabled>---- Tests ----</option>
|
||||
<option value="sample.unittests">Unit Tests</option>
|
||||
<option value="sample.keyboard">Keyboard Test</option>
|
||||
<option value="sample.charset">Charset Test</option>
|
||||
|
||||
|
||||
<option disabled>---- User Submissions ----</option>
|
||||
|
||||
<option disabled>Games</option>
|
||||
<option value="simple.pong">SIMPLE.PONG</option>
|
||||
<option value="sample.adventure">Text Adventure (Floyd McWilliams)</option>
|
||||
<option value="sample.pacman">(Not Really) ASCII Pac-Man (Michael Kemp)</option>
|
||||
<option value="sample.puzzler">Puzzler (Gregg Buntin)</option>
|
||||
<option value="sample.hangman">Hangman (Mike Gleason)</option>
|
||||
<option value="sample.raindrops">Catch the Raindrop (Nicholas Merchant)</option>
|
||||
<option value="sample.jot">JOT (Mike Gleason)</option>
|
||||
|
||||
<option disabled>Graphics</option>
|
||||
<option value="sample.rodscolorpattern">Rod's Color Pattern</option>
|
||||
<option value="sample.hacker">Hacker Logo (markwstock)</option>
|
||||
<option value="sample.loreswalk">Random LoRes (John Melesky)</option>
|
||||
<option value="sample.hireswalk">Random HiRes (John Melesky)</option>
|
||||
<option value="sample.sierpinski">Sierpinski Triangles (Kevin Miller)</option>
|
||||
<option value="sample.stringart">String Art (Chris Heric)</option>
|
||||
<option value="sample.paint">Drawing Program (Brian Broker)</option>
|
||||
<option value="sample.scribble">Scribble (William Simms)</option>
|
||||
<option value="sample.connections">Connections (Gregg Buntin)</option>
|
||||
<option value="sample.squiggle">Squiggle (Gregg Buntin)</option>
|
||||
<option value="sample.boys_surface">Boy's Surface (Lukas Innig)</option>
|
||||
<option value="sample.gaussian">Gaussian Distribution 2D (John Russ)</option>
|
||||
<option value="sample.bitmaps">Bitmap Images (Brian Broker)</option>
|
||||
<option value="sample.mandelbrot">Mandelbrot Set (c/o Gregory Lewis)</option>
|
||||
<option value="sample.mandelbrot2">Mandelbrot Set in Color</option>
|
||||
<option value="sample.steve">Steve (Nicola Foggi)</option>
|
||||
|
||||
<option disabled>Other</option>
|
||||
<option value="sample.primes">Prime Sieve (Kevin Miller)</option>
|
||||
<option value="sample.february">February Surprise (Antti Pirskanen)</option>
|
||||
<option value="sample.hellosine">Hello World Sine Wave (Jamie Beu)</option>
|
||||
<option value="sample.bodymass">Body Mass Index Calculator (Tim Dwyer)</option>
|
||||
|
||||
<option disabled>---- Traveller RPG Utilities ----</option>
|
||||
<option value="TRADER C">TRADER</option>
|
||||
<option value="sample.sectorgen">Traveller Sector Generator</option>
|
||||
<option value="sample.zhorelay">Zhodani Relay Station Placement</option>
|
||||
<option value="sample.readsector">Read Sector File</option>
|
||||
</select>
|
||||
|
||||
<input type="button" id="btn_capture" value="Echo to "Printer"" title="Pops up a "printer" window and echoes all output there, so you can copy/paste">
|
||||
|
||||
<!-- Source code editor inserted here -->
|
||||
<div id="editorframe"></div>
|
||||
|
||||
<form id="submission" method="post" enctype="text/plain" action="mailto:inexorabletash@hotmail.com?subject=Applesoft%20Sample%20Submission">
|
||||
<textarea name="source" id="source" style="display: none;">
|
||||
</textarea>
|
||||
<input type="submit" id="btn_share" value="Share your sample!">
|
||||
<input type="button" id="btn_save" value="Save Program" title="Save the current program. NOTE: Can only save one program.">
|
||||
<input type="button" id="btn_load" value="Load Program" title="Load a previously saved program. WARNING: The current program will be lost!">
|
||||
</form>
|
||||
|
||||
</div>
|
||||
|
||||
<br clear=left>
|
||||
|
||||
<h3 id="notes">Notes & Known Issues</h3>
|
||||
<ul>
|
||||
<li>The BASIC program is compiled to JavaScript before execution. Syntax errors are therefore detected at compile-time
|
||||
rather than at run-time as on a traditional interpreter. For example, the following program would run without errors
|
||||
on an Apple since the erroneous second statement is never reached. <code>10 END : CHR$(PRINT)</code>
|
||||
<li>Handling of BASIC code that does not match the canonical <code>LIST</code> output format may not behave as on an Apple:
|
||||
<ul>
|
||||
<li>Keyword parsing differs from Applesoft command line. For example <code>FOR I = S TO P</code> doesn't collapse into <code>FOR I = STOP</code>.
|
||||
<li>The interpreter doesn't actually care about line numbers for statement ordering (just for <code>GOTO</code>/<code>GOSUB</code> targets and <code>IF</code> statements). So <code>20 PRINT "A"</code>, <code>10 PRINT "B"</code> will just print A, then B
|
||||
</ul>
|
||||
<li>To improve readability, lines may start with <code>:</code> and continue the previously numbered line.
|
||||
<li>Floating point overflow is only detected on variable assignment.
|
||||
<li>The DOS operating system implements only a subset of DOS 3.3 and ProDOS useful for basic file I/O.
|
||||
<li>Except for a small number of compatibility shims for common operations (e.g. keyboard strobe), commands that refer to assembly routines (<code>PEEK</code>, <code>POKE</code>, <code>CALL</code>, <code>USR</code> etc.), shape tables, or tape I/O are not implemented.
|
||||
<li>Commands that operate on the program itself (<code>LIST</code>, <code>RUN</code>, <code>DEL</code>, etc.) are not implemented.
|
||||
<li>You can run your basic programs from the command line (with only basic text input and output, and no graphics or DOS commands):
|
||||
<ul>
|
||||
<li>On Windows, download <a href="basic.js">basic.js</a> and run from a command prompt via:
|
||||
<code>cscript.exe basic.js your_basic_program.txt</code>
|
||||
<li>On Mac/Linux, install <a href="http://www.mozilla.org/rhino/">Mozilla Rhino</a>,
|
||||
download <a href="basic.js">basic.js</a> and run from the command prompt via:
|
||||
<code>java -jar PATH_TO/js.jar basic.js your_program.txt</code>
|
||||
</ul>
|
||||
</ul>
|
||||
|
||||
<h3 id="todo">To Do</h3>
|
||||
<ul>
|
||||
<li>Implement DOS functionality for consoles
|
||||
</ul>
|
||||
|
||||
<h3 id="links">Links</h3>
|
||||
<ul>
|
||||
<li><a href="http://www.6502asm.com/">6502asm.com</a> - a 6502 assembler/emulator in JavaScript
|
||||
<li><a href="http://www.quitebasic.com/">Quite BASIC</a> - a similar project aimed at teaching programming
|
||||
<li><a href="http://navahogunleg.net/blog/my-projects/ng-basic/">NG-BASIC for Javascript</a> Navaho Gunleg's interpreter
|
||||
<li><a href="http://www.nicholson.com/rhn/basic/">BASIC Programming Resources</a>
|
||||
</ul>
|
||||
|
||||
<h3 id="history">History</h3>
|
||||
<div id="feed"></div>
|
||||
|
||||
|
||||
<script src="cm2/lib/codemirror.js"></script>
|
||||
<link rel="stylesheet" href="cm2/lib/codemirror.css">
|
||||
<link rel="stylesheet" href="cm2/theme/default.css">
|
||||
<script src="cm2/mode/basic/basic.js"></script>
|
||||
<link rel="stylesheet" href="cm2/mode/basic/basic.css">
|
||||
<style type="text/css">
|
||||
.CodeMirror { border: solid 1px black; width: 598px; height: 384px; background-color: white; }
|
||||
.CodeMirror-scroll { height: 100%; }
|
||||
</style>
|
||||
|
||||
<script type="text/javascript" src="basic.js?2012-02-08"></script>
|
||||
<script type="text/javascript" src="bell.js"></script>
|
||||
<script type="text/javascript" src="tty.js"></script>
|
||||
<script type="text/javascript" src="lores.js"></script>
|
||||
<script type="text/javascript" src="hires.js"></script>
|
||||
<script type="text/javascript" src="dos.js"></script>
|
||||
<script type="text/javascript" src="printer.js"></script>
|
||||
<script type="text/javascript" src="feed.js"></script>
|
||||
<script type="text/javascript" src="index.js"></script>
|
||||
|
||||
<script type="text/javascript">
|
||||
var _gaq = _gaq || [];
|
||||
_gaq.push(['_setAccount', 'UA-18610679-3']);
|
||||
_gaq.push(['_trackPageview']);
|
||||
|
||||
(function() {
|
||||
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
|
||||
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
|
||||
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
|
||||
})();
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
236
index.js
Normal file
236
index.js
Normal file
@ -0,0 +1,236 @@
|
||||
|
||||
window.onload = function () {
|
||||
|
||||
var $ = function (s) { return document.querySelector(s); };
|
||||
|
||||
$('#lb_files').selectedIndex = 0;
|
||||
|
||||
var bell = (function () {
|
||||
var b = new Bell(/^.*\/|/.exec(window.location)[0]);
|
||||
return function () { b.play(); };
|
||||
} ());
|
||||
|
||||
|
||||
var tty = new TTY($('#screen'), $('#keyboard'), bell);
|
||||
var dos = new DOS(tty);
|
||||
|
||||
var lores = new LoRes($('#lores'), 40, 48);
|
||||
var hires = new HiRes($('#hires'), 280, 192);
|
||||
var hires2 = new HiRes($('#hires2'), 280, 192);
|
||||
var display = {
|
||||
state: { graphics: false, full: true, page1: true, lores: true },
|
||||
setState: function (state, value /* ... */) {
|
||||
var args = Array.prototype.slice.call(arguments);
|
||||
while (args.length) {
|
||||
state = args.shift();
|
||||
value = args.shift();
|
||||
this.state[state] = value;
|
||||
}
|
||||
|
||||
if (this.state.graphics) {
|
||||
lores.show(this.state.lores);
|
||||
hires.show(!this.state.lores && this.state.page1);
|
||||
hires2.show(!this.state.lores && !this.state.page1);
|
||||
tty.splitScreen(tty.getScreenSize().height - (this.state.full ? 0 : 4));
|
||||
} else {
|
||||
lores.show(false);
|
||||
hires.show(false);
|
||||
hires2.show(false);
|
||||
tty.splitScreen(0);
|
||||
}
|
||||
}
|
||||
};
|
||||
var pdl = [0, 0, 0, 0];
|
||||
|
||||
// Lexical highlighting
|
||||
var editor = new CodeMirror($('#editorframe'), {
|
||||
mode: 'basic',
|
||||
tabMode: 'default',
|
||||
content: $('#source').value,
|
||||
height: '100%'
|
||||
});
|
||||
|
||||
$('#btn_share').onclick = function () {
|
||||
// Load up the hidden text area with the current source
|
||||
$('#source').value = getSource();
|
||||
};
|
||||
|
||||
function getSource() {
|
||||
return editor.getValue();
|
||||
}
|
||||
|
||||
function setSource(source) {
|
||||
editor.setValue(source);
|
||||
}
|
||||
|
||||
// Do not let certain events take focus away from "keyboard"
|
||||
function keyboardFocus(e) {
|
||||
tty.focus();
|
||||
e.stopPropagation(); // W3C
|
||||
e.preventDefault(); // e.g. to block arrows from scrolling the page
|
||||
}
|
||||
|
||||
addEvent($('#lores'), 'click', keyboardFocus);
|
||||
addEvent($('#hires'), 'click', keyboardFocus);
|
||||
addEvent($('#hires2'), 'click', keyboardFocus);
|
||||
addEvent($('#screen'), 'click', keyboardFocus);
|
||||
addEvent($('#frame'), 'click', keyboardFocus);
|
||||
addEvent($('#frame'), 'blur', keyboardFocus); // Needed on IE, not sure why it tries to get focus
|
||||
|
||||
addEvent($('#keyboard'), 'focus', function () {
|
||||
getClassList($('#frame')).add('focused');
|
||||
});
|
||||
addEvent($('#keyboard'), 'blur', function () {
|
||||
getClassList($('#frame')).remove('focused');
|
||||
});
|
||||
|
||||
var program;
|
||||
addEvent($('#btn_run'), 'click', function () {
|
||||
dos.reset();
|
||||
tty.reset();
|
||||
tty.autoScroll = true;
|
||||
|
||||
try {
|
||||
program = basic.compile(getSource());
|
||||
} catch (e) {
|
||||
if (e instanceof basic.ParseError) {
|
||||
editor.setCursor({ line: e.line - 1, ch: e.column - 1 });
|
||||
console.log(e.message +
|
||||
' (source line:' + e.line + ', column:' + e.column + ')');
|
||||
}
|
||||
alert(e);
|
||||
return;
|
||||
}
|
||||
|
||||
stopped = false;
|
||||
updateUI();
|
||||
|
||||
program.init({
|
||||
tty: tty,
|
||||
hires: hires,
|
||||
hires2: hires2,
|
||||
lores: lores,
|
||||
display: display,
|
||||
paddle: function (n) { return pdl[n]; }
|
||||
});
|
||||
setTimeout(driver, 0);
|
||||
});
|
||||
|
||||
addEvent($('#btn_stop'), 'click', function () {
|
||||
tty.reset(); // cancel any blocking input
|
||||
stopped = true;
|
||||
updateUI();
|
||||
});
|
||||
|
||||
addEvent($('#lb_files'), 'change', function () {
|
||||
var sel = $('#lb_files');
|
||||
loadFile('samples/' + sel.value + ".txt", setSource);
|
||||
});
|
||||
|
||||
addEvent($('#btn_save'), 'click', function () {
|
||||
window.localStorage.setItem("save_program", getSource());
|
||||
});
|
||||
addEvent($('#btn_load'), 'click', function () {
|
||||
setSource(window.localStorage.getItem("save_program"));
|
||||
});
|
||||
|
||||
// Add a "printer" on demand
|
||||
addEvent($('#btn_capture'), 'click', function () {
|
||||
$('#btn_capture').disabled = true;
|
||||
var printer = new Printer(tty);
|
||||
|
||||
printer.onclose = function () {
|
||||
$('#btn_capture').disabled = false;
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
// Mouse-as-Joystick
|
||||
var wrapper = $('#screen-wrapper');
|
||||
addEvent(wrapper, 'mousemove', function (e) {
|
||||
// compute relative coordinates
|
||||
var x = e.clientX, y = e.clientY, elem = wrapper;
|
||||
while (elem) {
|
||||
x -= elem.offsetLeft - elem.scrollLeft;
|
||||
y -= elem.offsetTop - elem.scrollTop;
|
||||
elem = elem.offsetParent;
|
||||
}
|
||||
|
||||
function clamp(n, min, max) { return Math.min(Math.max(n, min), max); }
|
||||
pdl[0] = clamp(x / (wrapper.offsetWidth - 1), 0, 1);
|
||||
pdl[1] = clamp(y / (wrapper.offsetHeight - 1), 0, 1);
|
||||
});
|
||||
|
||||
|
||||
var stopped = true;
|
||||
function updateUI() {
|
||||
$("#btn_stop").disabled = stopped ? "disabled" : "";
|
||||
$("#btn_run").disabled = stopped ? "" : "disabled";
|
||||
$("#lb_files").disabled = stopped ? "" : "disabled";
|
||||
|
||||
if (stopped) {
|
||||
$("#btn_run").focus();
|
||||
} else {
|
||||
tty.focus();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// TODO: Expose a RESPONSIVE |---+--------| FAST slider in the UI
|
||||
// Number of steps to execute before yielding execution
|
||||
// (Use a prime to minimize risk of phasing with loops)
|
||||
var NUM_SYNCHRONOUS_STEPS = 37;
|
||||
|
||||
function driver() {
|
||||
var state = basic.STATE_RUNNING;
|
||||
var statements = NUM_SYNCHRONOUS_STEPS;
|
||||
|
||||
while (!stopped && state === basic.STATE_RUNNING && statements > 0) {
|
||||
|
||||
try {
|
||||
state = program.step(driver);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
alert(e.message ? e.message : e);
|
||||
stopped = true;
|
||||
updateUI();
|
||||
return;
|
||||
}
|
||||
|
||||
statements -= 1;
|
||||
}
|
||||
|
||||
if (state === basic.STATE_STOPPED || stopped) {
|
||||
stopped = true;
|
||||
updateUI();
|
||||
} else if (state === basic.STATE_BLOCKED) {
|
||||
// Fall out
|
||||
} else { // state === basic.STATE_RUNNING
|
||||
setTimeout(driver, 0); // Keep going
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function loadFile(filename, callback) {
|
||||
var req = new XMLHttpRequest();
|
||||
var url = encodeURI(filename); // not encodeURIComponent, so paths can be specified
|
||||
var async = true;
|
||||
req.open("GET", url, async);
|
||||
req.onreadystatechange = function () {
|
||||
if (req.readyState === XMLHttpRequest.DONE) {
|
||||
if (req.status === 200 || req.status === 0) {
|
||||
callback(req.responseText);
|
||||
}
|
||||
}
|
||||
};
|
||||
req.send(null);
|
||||
}
|
||||
|
||||
// load default
|
||||
loadFile('samples/sample.default.txt', setSource);
|
||||
|
||||
// Show change history
|
||||
atomToHtml('feed.xml?' + Math.random(), $('#feed'));
|
||||
};
|
||||
|
||||
|
384
lores.js
384
lores.js
@ -1,192 +1,192 @@
|
||||
//
|
||||
// Applesoft BASIC in Javascript
|
||||
// Low Resolution Graphics (LoRes) Emulation
|
||||
//
|
||||
|
||||
// Copyright (C) 2009-2011 Joshua Bell
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
|
||||
// Usage:
|
||||
//
|
||||
// var lores = new LoRes( element, width, height )
|
||||
// lores.clear()
|
||||
// lores.setColor( color_index )
|
||||
// lores.plot( x, y )
|
||||
// lores.hlin( x1, x2, y )
|
||||
// lores.vlin( x1, x2, y )
|
||||
// lores.show( bool )
|
||||
// color_index = lores.getPixel( x, y )
|
||||
// { width: w, height: h } = lores.getScreenSize()
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// <style>
|
||||
// .loresPixel { width: 14px; height: 8px; }
|
||||
// </style>
|
||||
// <script>
|
||||
// lores = new LoRes( document.getElementById( 'lores' ), 40, 40 );
|
||||
// interpreter = new BasicInterpreter( tty, lores, paddle );
|
||||
// </script>
|
||||
// <div id="lores"></div>
|
||||
|
||||
function LoRes(element, width, height) {
|
||||
|
||||
/*jslint browser: true*/
|
||||
|
||||
var COLORS, // Apple II to HTML color table
|
||||
loresPixel = [], // element references
|
||||
pixels = [], // color values
|
||||
color = 0; // current color
|
||||
|
||||
// Colors c/o USENET:
|
||||
// Date: Wed, 05 Sep 2007 01:04:20 +0200
|
||||
// From: Linards Ticmanis <ticmanis@gmx.de>
|
||||
// Newsgroups: comp.sys.apple2
|
||||
// Subject: Re: Double hires mode color artifacts
|
||||
// Message-ID: <46dde477$0$4527$9b4e6d93@newsspool3.arcor-online.net>
|
||||
|
||||
COLORS = [
|
||||
'#000000', // Black
|
||||
'#901740', // Deep Red
|
||||
'#402ca5', // Dark Blue
|
||||
'#d043e5', // Purple
|
||||
'#006940', // Dark Green
|
||||
'#808080', // Gray 1
|
||||
'#2f95e5', // Medium Blue
|
||||
'#bfabff', // Light Blue
|
||||
'#402400', // Brown // WAS #405400, error pointed out by MJM
|
||||
'#d06a1a', // Orange
|
||||
'#808080', // Gray 2
|
||||
'#ff96bf', // Pink
|
||||
'#2fbc1a', // Light Green
|
||||
'#bfd35a', // Yellow
|
||||
'#6fe8bf', // Aquamarine
|
||||
'#ffffff' // White
|
||||
];
|
||||
|
||||
function init() {
|
||||
var x, y, table, tbody, tr, td;
|
||||
|
||||
pixels = [];
|
||||
pixels.length = width * height;
|
||||
loresPixel = [];
|
||||
loresPixel.length = width * height;
|
||||
|
||||
table = document.createElement('table');
|
||||
table.className = 'loresDisplay';
|
||||
|
||||
tbody = document.createElement('tbody');
|
||||
for (y = 0; y < height; y += 1) {
|
||||
tr = document.createElement('tr');
|
||||
tr.className = 'loresRow';
|
||||
for (x = 0; x < width; x += 1) {
|
||||
td = document.createElement('td');
|
||||
td.className = 'loresPixel';
|
||||
td.style.backgroundColor = 'black';
|
||||
|
||||
loresPixel[y * width + x] = td;
|
||||
pixels[y * width + x] = 0;
|
||||
|
||||
tr.appendChild(td);
|
||||
}
|
||||
tbody.appendChild(tr);
|
||||
}
|
||||
table.appendChild(tbody);
|
||||
|
||||
element.innerHTML = "";
|
||||
element.appendChild(table);
|
||||
}
|
||||
|
||||
this.clear = function() {
|
||||
var x, y, pixel;
|
||||
for (y = 0; y < height; y += 1) {
|
||||
for (x = 0; x < width; x += 1) {
|
||||
pixel = loresPixel[y * width + x];
|
||||
pixel.style.backgroundColor = "black";
|
||||
pixels[y * width + x] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
this.setColor = function(newColor) {
|
||||
color = Math.floor(newColor) % COLORS.length;
|
||||
};
|
||||
|
||||
function plot(x, y) {
|
||||
var pixel = loresPixel[y * width + x];
|
||||
if (pixel) {
|
||||
pixel.style.backgroundColor = COLORS[color];
|
||||
pixels[y * width + x] = color;
|
||||
}
|
||||
}
|
||||
|
||||
this.plot = function(x, y) {
|
||||
plot(x, y);
|
||||
};
|
||||
|
||||
this.getPixel = function(x, y) {
|
||||
if (0 <= x && x < width &&
|
||||
0 <= y && y < height) {
|
||||
|
||||
return pixels[y * width + x];
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
this.hlin = function(x1, x2, y) {
|
||||
var x;
|
||||
if (x1 > x2) {
|
||||
x = x1;
|
||||
x1 = x2;
|
||||
x2 = x;
|
||||
}
|
||||
|
||||
for (x = x1; x <= x2; x += 1) {
|
||||
plot(x, y);
|
||||
}
|
||||
};
|
||||
|
||||
this.vlin = function(y1, y2, x) {
|
||||
var y;
|
||||
if (y1 > y2) {
|
||||
y = y1;
|
||||
y1 = y2;
|
||||
y2 = y;
|
||||
}
|
||||
|
||||
for (y = y1; y <= y2; y += 1) {
|
||||
plot(x, y);
|
||||
}
|
||||
};
|
||||
|
||||
this.getScreenSize = function() {
|
||||
return { width: width, height: height };
|
||||
};
|
||||
|
||||
this.show = function(state) {
|
||||
element.style.visibility = state ? "visible" : "hidden";
|
||||
|
||||
};
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Constructor Logic
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
init();
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Applesoft BASIC in Javascript
|
||||
// Low Resolution Graphics (LoRes) Emulation
|
||||
//
|
||||
|
||||
// Copyright (C) 2009-2011 Joshua Bell
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
|
||||
// Usage:
|
||||
//
|
||||
// var lores = new LoRes( element, width, height )
|
||||
// lores.clear()
|
||||
// lores.setColor( color_index )
|
||||
// lores.plot( x, y )
|
||||
// lores.hlin( x1, x2, y )
|
||||
// lores.vlin( x1, x2, y )
|
||||
// lores.show( bool )
|
||||
// color_index = lores.getPixel( x, y )
|
||||
// { width: w, height: h } = lores.getScreenSize()
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// <style>
|
||||
// .loresPixel { width: 14px; height: 8px; }
|
||||
// </style>
|
||||
// <script>
|
||||
// lores = new LoRes( document.getElementById( 'lores' ), 40, 40 );
|
||||
// interpreter = new BasicInterpreter( tty, lores, paddle );
|
||||
// </script>
|
||||
// <div id="lores"></div>
|
||||
|
||||
function LoRes(element, width, height) {
|
||||
|
||||
/*jslint browser: true*/
|
||||
|
||||
var COLORS, // Apple II to HTML color table
|
||||
loresPixel = [], // element references
|
||||
pixels = [], // color values
|
||||
color = 0; // current color
|
||||
|
||||
// Colors c/o USENET:
|
||||
// Date: Wed, 05 Sep 2007 01:04:20 +0200
|
||||
// From: Linards Ticmanis <ticmanis@gmx.de>
|
||||
// Newsgroups: comp.sys.apple2
|
||||
// Subject: Re: Double hires mode color artifacts
|
||||
// Message-ID: <46dde477$0$4527$9b4e6d93@newsspool3.arcor-online.net>
|
||||
|
||||
COLORS = [
|
||||
'#000000', // Black
|
||||
'#901740', // Deep Red
|
||||
'#402ca5', // Dark Blue
|
||||
'#d043e5', // Purple
|
||||
'#006940', // Dark Green
|
||||
'#808080', // Gray 1
|
||||
'#2f95e5', // Medium Blue
|
||||
'#bfabff', // Light Blue
|
||||
'#402400', // Brown // WAS #405400, error pointed out by MJM
|
||||
'#d06a1a', // Orange
|
||||
'#808080', // Gray 2
|
||||
'#ff96bf', // Pink
|
||||
'#2fbc1a', // Light Green
|
||||
'#bfd35a', // Yellow
|
||||
'#6fe8bf', // Aquamarine
|
||||
'#ffffff' // White
|
||||
];
|
||||
|
||||
function init() {
|
||||
var x, y, table, tbody, tr, td;
|
||||
|
||||
pixels = [];
|
||||
pixels.length = width * height;
|
||||
loresPixel = [];
|
||||
loresPixel.length = width * height;
|
||||
|
||||
table = document.createElement('table');
|
||||
table.className = 'loresDisplay';
|
||||
|
||||
tbody = document.createElement('tbody');
|
||||
for (y = 0; y < height; y += 1) {
|
||||
tr = document.createElement('tr');
|
||||
tr.className = 'loresRow';
|
||||
for (x = 0; x < width; x += 1) {
|
||||
td = document.createElement('td');
|
||||
td.className = 'loresPixel';
|
||||
td.style.backgroundColor = 'black';
|
||||
|
||||
loresPixel[y * width + x] = td;
|
||||
pixels[y * width + x] = 0;
|
||||
|
||||
tr.appendChild(td);
|
||||
}
|
||||
tbody.appendChild(tr);
|
||||
}
|
||||
table.appendChild(tbody);
|
||||
|
||||
element.innerHTML = "";
|
||||
element.appendChild(table);
|
||||
}
|
||||
|
||||
this.clear = function() {
|
||||
var x, y, pixel;
|
||||
for (y = 0; y < height; y += 1) {
|
||||
for (x = 0; x < width; x += 1) {
|
||||
pixel = loresPixel[y * width + x];
|
||||
pixel.style.backgroundColor = "black";
|
||||
pixels[y * width + x] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
this.setColor = function(newColor) {
|
||||
color = Math.floor(newColor) % COLORS.length;
|
||||
};
|
||||
|
||||
function plot(x, y) {
|
||||
var pixel = loresPixel[y * width + x];
|
||||
if (pixel) {
|
||||
pixel.style.backgroundColor = COLORS[color];
|
||||
pixels[y * width + x] = color;
|
||||
}
|
||||
}
|
||||
|
||||
this.plot = function(x, y) {
|
||||
plot(x, y);
|
||||
};
|
||||
|
||||
this.getPixel = function(x, y) {
|
||||
if (0 <= x && x < width &&
|
||||
0 <= y && y < height) {
|
||||
|
||||
return pixels[y * width + x];
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
this.hlin = function(x1, x2, y) {
|
||||
var x;
|
||||
if (x1 > x2) {
|
||||
x = x1;
|
||||
x1 = x2;
|
||||
x2 = x;
|
||||
}
|
||||
|
||||
for (x = x1; x <= x2; x += 1) {
|
||||
plot(x, y);
|
||||
}
|
||||
};
|
||||
|
||||
this.vlin = function(y1, y2, x) {
|
||||
var y;
|
||||
if (y1 > y2) {
|
||||
y = y1;
|
||||
y1 = y2;
|
||||
y2 = y;
|
||||
}
|
||||
|
||||
for (y = y1; y <= y2; y += 1) {
|
||||
plot(x, y);
|
||||
}
|
||||
};
|
||||
|
||||
this.getScreenSize = function() {
|
||||
return { width: width, height: height };
|
||||
};
|
||||
|
||||
this.show = function(state) {
|
||||
element.style.visibility = state ? "visible" : "hidden";
|
||||
|
||||
};
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Constructor Logic
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
init();
|
||||
}
|
||||
|
||||
|
||||
|
280
paddles.js
280
paddles.js
@ -1,140 +1,140 @@
|
||||
//
|
||||
// Applesoft BASIC in Javascript
|
||||
// Paddle and Joystick Emulation
|
||||
//
|
||||
|
||||
// Copyright (C) 2009-2011 Joshua Bell
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
|
||||
// Usage:
|
||||
//
|
||||
// initPaddle( device_element, thumb_element, callback_function );
|
||||
// initJoystick( device_element, thumb_element, callback_function_x, callback_function_y );
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// <script>
|
||||
// initPaddle( device.getElementById('paddle'), device.getElementById('knob'), function ( v ) { window.status = v; /* v = 0...1 */ } );
|
||||
// initJoystick( device.getElementById('joystick'), device.getElementById('stick'), function ( v ) { window.status = v; /* v = 0...1 */ }, function ( v ) { window.status = v; /* v = 0...1 */ } );
|
||||
// </script>
|
||||
// <style>
|
||||
// #paddle { border-style: inset; border-width: 2px; border-color: gray; background-color: #c0c0c0; height: 20px;
|
||||
// width: 300px; text-align: center; }
|
||||
// #knob { border-style: outset; border-width: 2px; border-color: gray; background-color: #404040; height: 16px; width: 10px; }
|
||||
// #joystick { border-style: inset; border-width: 2px; border-color: gray; background-color: #c0c0c0;
|
||||
// width: 100px; height: 100px; text-align: center; vertical-align: middle; line-height: 100px; }
|
||||
// #stick { border-style: outset; border-width: 2px; border-color: gray; background-color: #404040; height: 16px; width: 16px; }
|
||||
// </style>
|
||||
// <div id="paddle"> <div id="knob"></div> Slide the Paddle! </div>
|
||||
// <div id="joystick"> <div id="stick"></div> Twiddle me!</div>
|
||||
|
||||
/*extern document,window,addEvent,removeEvent */
|
||||
|
||||
function initDevice(device, thumb, fn_x, fn_y) {
|
||||
device.style.position = "relative";
|
||||
device.style.overflow = "hidden";
|
||||
|
||||
thumb.style.position = "absolute";
|
||||
thumb.style.left = 0;
|
||||
thumb.style.top = 0;
|
||||
|
||||
thumb.last_x = 0;
|
||||
thumb.last_y = 0;
|
||||
|
||||
var onStickDrag = function (e) {
|
||||
e.returnValue = false;
|
||||
e.cancelBubble = true;
|
||||
if (e.stopPropagation) { e.stopPropagation(); }
|
||||
if (e.preventDefault) { e.preventDefault(); }
|
||||
|
||||
if (fn_x) {
|
||||
var x = e.clientX - thumb.originX + thumb.posX;
|
||||
var min_x = 0;
|
||||
var max_x = device.clientWidth - thumb.offsetWidth;
|
||||
if (x < min_x) { x = min_x; }
|
||||
if (x > max_x) { x = max_x; }
|
||||
|
||||
if (x != thumb.last_x) {
|
||||
thumb.last_x = x;
|
||||
thumb.style.left = x + "px";
|
||||
fn_x((x - min_x) / (max_x - min_x));
|
||||
}
|
||||
}
|
||||
|
||||
if (fn_y) {
|
||||
var y = e.clientY - thumb.originY + thumb.posY;
|
||||
var min_y = 0;
|
||||
var max_y = device.clientHeight - thumb.offsetHeight;
|
||||
if (y < min_y) { y = min_y; }
|
||||
if (y > max_y) { y = max_y; }
|
||||
|
||||
if (y != thumb.last_y) {
|
||||
thumb.last_y = y;
|
||||
thumb.style.top = y + "px";
|
||||
fn_y((y - min_y) / (max_y - min_y));
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
var onStickUp = function (e) {
|
||||
e.returnValue = false;
|
||||
e.cancelBubble = true;
|
||||
if (e.stopPropagation) { e.stopPropagation(); }
|
||||
if (e.preventDefault) { e.preventDefault(); }
|
||||
|
||||
removeEvent(document, "mousemove", onStickDrag);
|
||||
removeEvent(document, "mouseup", onStickUp);
|
||||
if (thumb.releaseCapture) { thumb.releaseCapture(true); }
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
var onStickDown = function (e) {
|
||||
e.returnValue = false;
|
||||
e.cancelBubble = true;
|
||||
if (e.stopPropagation) { e.stopPropagation(); }
|
||||
if (e.preventDefault) { e.preventDefault(); }
|
||||
|
||||
addEvent(document, "mousemove", onStickDrag);
|
||||
addEvent(document, "mouseup", onStickUp);
|
||||
if (thumb.setCapture) { thumb.setCapture(true); }
|
||||
|
||||
if (fn_x) {
|
||||
thumb.originX = e.clientX;
|
||||
thumb.posX = parseInt(thumb.style.left, 10);
|
||||
}
|
||||
|
||||
if (fn_y) {
|
||||
thumb.originY = e.clientY;
|
||||
thumb.posY = parseInt(thumb.style.top, 10);
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
device.appendChild(thumb);
|
||||
|
||||
addEvent(thumb, "mousedown", onStickDown);
|
||||
}
|
||||
|
||||
function initPaddle(device, control, fn) {
|
||||
initDevice(device, control, fn);
|
||||
}
|
||||
|
||||
function initJoystick(device, control, fn_x, fn_y) {
|
||||
initDevice(device, control, fn_x, fn_y);
|
||||
}
|
||||
//
|
||||
// Applesoft BASIC in Javascript
|
||||
// Paddle and Joystick Emulation
|
||||
//
|
||||
|
||||
// Copyright (C) 2009-2011 Joshua Bell
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
|
||||
// Usage:
|
||||
//
|
||||
// initPaddle( device_element, thumb_element, callback_function );
|
||||
// initJoystick( device_element, thumb_element, callback_function_x, callback_function_y );
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// <script>
|
||||
// initPaddle( device.getElementById('paddle'), device.getElementById('knob'), function ( v ) { window.status = v; /* v = 0...1 */ } );
|
||||
// initJoystick( device.getElementById('joystick'), device.getElementById('stick'), function ( v ) { window.status = v; /* v = 0...1 */ }, function ( v ) { window.status = v; /* v = 0...1 */ } );
|
||||
// </script>
|
||||
// <style>
|
||||
// #paddle { border-style: inset; border-width: 2px; border-color: gray; background-color: #c0c0c0; height: 20px;
|
||||
// width: 300px; text-align: center; }
|
||||
// #knob { border-style: outset; border-width: 2px; border-color: gray; background-color: #404040; height: 16px; width: 10px; }
|
||||
// #joystick { border-style: inset; border-width: 2px; border-color: gray; background-color: #c0c0c0;
|
||||
// width: 100px; height: 100px; text-align: center; vertical-align: middle; line-height: 100px; }
|
||||
// #stick { border-style: outset; border-width: 2px; border-color: gray; background-color: #404040; height: 16px; width: 16px; }
|
||||
// </style>
|
||||
// <div id="paddle"> <div id="knob"></div> Slide the Paddle! </div>
|
||||
// <div id="joystick"> <div id="stick"></div> Twiddle me!</div>
|
||||
|
||||
/*extern document,window,addEvent,removeEvent */
|
||||
|
||||
function initDevice(device, thumb, fn_x, fn_y) {
|
||||
device.style.position = "relative";
|
||||
device.style.overflow = "hidden";
|
||||
|
||||
thumb.style.position = "absolute";
|
||||
thumb.style.left = 0;
|
||||
thumb.style.top = 0;
|
||||
|
||||
thumb.last_x = 0;
|
||||
thumb.last_y = 0;
|
||||
|
||||
var onStickDrag = function (e) {
|
||||
e.returnValue = false;
|
||||
e.cancelBubble = true;
|
||||
if (e.stopPropagation) { e.stopPropagation(); }
|
||||
if (e.preventDefault) { e.preventDefault(); }
|
||||
|
||||
if (fn_x) {
|
||||
var x = e.clientX - thumb.originX + thumb.posX;
|
||||
var min_x = 0;
|
||||
var max_x = device.clientWidth - thumb.offsetWidth;
|
||||
if (x < min_x) { x = min_x; }
|
||||
if (x > max_x) { x = max_x; }
|
||||
|
||||
if (x != thumb.last_x) {
|
||||
thumb.last_x = x;
|
||||
thumb.style.left = x + "px";
|
||||
fn_x((x - min_x) / (max_x - min_x));
|
||||
}
|
||||
}
|
||||
|
||||
if (fn_y) {
|
||||
var y = e.clientY - thumb.originY + thumb.posY;
|
||||
var min_y = 0;
|
||||
var max_y = device.clientHeight - thumb.offsetHeight;
|
||||
if (y < min_y) { y = min_y; }
|
||||
if (y > max_y) { y = max_y; }
|
||||
|
||||
if (y != thumb.last_y) {
|
||||
thumb.last_y = y;
|
||||
thumb.style.top = y + "px";
|
||||
fn_y((y - min_y) / (max_y - min_y));
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
var onStickUp = function (e) {
|
||||
e.returnValue = false;
|
||||
e.cancelBubble = true;
|
||||
if (e.stopPropagation) { e.stopPropagation(); }
|
||||
if (e.preventDefault) { e.preventDefault(); }
|
||||
|
||||
removeEvent(document, "mousemove", onStickDrag);
|
||||
removeEvent(document, "mouseup", onStickUp);
|
||||
if (thumb.releaseCapture) { thumb.releaseCapture(true); }
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
var onStickDown = function (e) {
|
||||
e.returnValue = false;
|
||||
e.cancelBubble = true;
|
||||
if (e.stopPropagation) { e.stopPropagation(); }
|
||||
if (e.preventDefault) { e.preventDefault(); }
|
||||
|
||||
addEvent(document, "mousemove", onStickDrag);
|
||||
addEvent(document, "mouseup", onStickUp);
|
||||
if (thumb.setCapture) { thumb.setCapture(true); }
|
||||
|
||||
if (fn_x) {
|
||||
thumb.originX = e.clientX;
|
||||
thumb.posX = parseInt(thumb.style.left, 10);
|
||||
}
|
||||
|
||||
if (fn_y) {
|
||||
thumb.originY = e.clientY;
|
||||
thumb.posY = parseInt(thumb.style.top, 10);
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
device.appendChild(thumb);
|
||||
|
||||
addEvent(thumb, "mousedown", onStickDown);
|
||||
}
|
||||
|
||||
function initPaddle(device, control, fn) {
|
||||
initDevice(device, control, fn);
|
||||
}
|
||||
|
||||
function initJoystick(device, control, fn_x, fn_y) {
|
||||
initDevice(device, control, fn_x, fn_y);
|
||||
}
|
||||
|
190
printer.js
190
printer.js
@ -1,95 +1,95 @@
|
||||
//
|
||||
// Applesoft BASIC in Javascript
|
||||
// Printer (for copying output)
|
||||
//
|
||||
|
||||
// Copyright (C) 2009-2011 Joshua Bell
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
function Printer(tty) {
|
||||
|
||||
// For closures
|
||||
var self = this;
|
||||
|
||||
// Create new window and document
|
||||
var w = window.open('about:blank', '_blank', 'width=500,height=400,status=0,location=0,menubar=0,toolbar=0');
|
||||
var body = w.document.getElementsByTagName('body')[0];
|
||||
body.style.fontFamily = 'Courier, Monospace';
|
||||
body.style.backgroundColor = '#ffffff';
|
||||
body.style.backgroundImage = "url('http://calormen.com/Star_Trek/ASCII/lpt.jpg')";
|
||||
body.style.color = '#000000';
|
||||
body.style.paddingLeft = '50px';
|
||||
|
||||
var paper = w.document.createElement('div');
|
||||
paper.style.whiteSpace = 'pre';
|
||||
body.appendChild(paper);
|
||||
|
||||
var tty_writeChar = tty.writeChar;
|
||||
tty.writeChar = function(c) {
|
||||
|
||||
tty_writeChar(c);
|
||||
|
||||
switch (c.charCodeAt(0)) {
|
||||
case 0:
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
case 4: // DOS hook
|
||||
case 5:
|
||||
case 6:
|
||||
case 7: // (BEL) bell
|
||||
case 8: // (BS) backspace
|
||||
case 9:
|
||||
case 11: // (VT) clear EOS
|
||||
case 12: // (FF) clear
|
||||
case 14: // (SO) normal
|
||||
case 15: // (SI) inverse
|
||||
case 16:
|
||||
case 17: // (DC1) 40-column
|
||||
case 18: // (DC2) 80-column
|
||||
case 19: // (DC3) stop list
|
||||
case 20:
|
||||
case 21: // (NAK) quit (disable 80-col card)
|
||||
case 22: // (SYN) scroll
|
||||
case 23: // (ETB) scroll up
|
||||
case 24: // (CAN) disable mousetext
|
||||
case 25: // (EM) home
|
||||
case 26: // (SUB) clear line
|
||||
case 27: // (ESC) enable mousetext
|
||||
case 28: // (FS) forward space
|
||||
case 29: // (GS) clear EOL
|
||||
case 30: // (RS) gotoXY
|
||||
case 31:
|
||||
break;
|
||||
|
||||
case 10: // (LF) line feed
|
||||
case 13: // (CR) return
|
||||
paper.appendChild(w.document.createTextNode('\n'));
|
||||
break;
|
||||
|
||||
default:
|
||||
paper.appendChild(w.document.createTextNode(c));
|
||||
break;
|
||||
}
|
||||
|
||||
paper.parentElement.scrollTop = paper.parentElement.scrollHeight;
|
||||
};
|
||||
|
||||
w.onunload = function() {
|
||||
if (self.onclose) {
|
||||
self.onclose();
|
||||
}
|
||||
tty.writeChar = tty_writeChar;
|
||||
};
|
||||
}
|
||||
//
|
||||
// Applesoft BASIC in Javascript
|
||||
// Printer (for copying output)
|
||||
//
|
||||
|
||||
// Copyright (C) 2009-2011 Joshua Bell
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
function Printer(tty) {
|
||||
|
||||
// For closures
|
||||
var self = this;
|
||||
|
||||
// Create new window and document
|
||||
var w = window.open('about:blank', '_blank', 'width=500,height=400,status=0,location=0,menubar=0,toolbar=0');
|
||||
var body = w.document.getElementsByTagName('body')[0];
|
||||
body.style.fontFamily = 'Courier, Monospace';
|
||||
body.style.backgroundColor = '#ffffff';
|
||||
body.style.backgroundImage = "url('http://calormen.com/Star_Trek/ASCII/lpt.jpg')";
|
||||
body.style.color = '#000000';
|
||||
body.style.paddingLeft = '50px';
|
||||
|
||||
var paper = w.document.createElement('div');
|
||||
paper.style.whiteSpace = 'pre';
|
||||
body.appendChild(paper);
|
||||
|
||||
var tty_writeChar = tty.writeChar;
|
||||
tty.writeChar = function(c) {
|
||||
|
||||
tty_writeChar(c);
|
||||
|
||||
switch (c.charCodeAt(0)) {
|
||||
case 0:
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
case 4: // DOS hook
|
||||
case 5:
|
||||
case 6:
|
||||
case 7: // (BEL) bell
|
||||
case 8: // (BS) backspace
|
||||
case 9:
|
||||
case 11: // (VT) clear EOS
|
||||
case 12: // (FF) clear
|
||||
case 14: // (SO) normal
|
||||
case 15: // (SI) inverse
|
||||
case 16:
|
||||
case 17: // (DC1) 40-column
|
||||
case 18: // (DC2) 80-column
|
||||
case 19: // (DC3) stop list
|
||||
case 20:
|
||||
case 21: // (NAK) quit (disable 80-col card)
|
||||
case 22: // (SYN) scroll
|
||||
case 23: // (ETB) scroll up
|
||||
case 24: // (CAN) disable mousetext
|
||||
case 25: // (EM) home
|
||||
case 26: // (SUB) clear line
|
||||
case 27: // (ESC) enable mousetext
|
||||
case 28: // (FS) forward space
|
||||
case 29: // (GS) clear EOL
|
||||
case 30: // (RS) gotoXY
|
||||
case 31:
|
||||
break;
|
||||
|
||||
case 10: // (LF) line feed
|
||||
case 13: // (CR) return
|
||||
paper.appendChild(w.document.createTextNode('\n'));
|
||||
break;
|
||||
|
||||
default:
|
||||
paper.appendChild(w.document.createTextNode(c));
|
||||
break;
|
||||
}
|
||||
|
||||
paper.parentElement.scrollTop = paper.parentElement.scrollHeight;
|
||||
};
|
||||
|
||||
w.onunload = function() {
|
||||
if (self.onclose) {
|
||||
self.onclose();
|
||||
}
|
||||
tty.writeChar = tty_writeChar;
|
||||
};
|
||||
}
|
||||
|
1140
reference.htm
1140
reference.htm
File diff suppressed because it is too large
Load Diff
1938
styles.css
1938
styles.css
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user