dos2unix; factor out index.js; use addEvent/getClassList shims; change screen style using CSS classes

This commit is contained in:
Joshua Bell 2012-02-08 22:37:04 -05:00
parent 9aa91a4410
commit a49ed766c9
12 changed files with 4091 additions and 4103 deletions

110
bell.js
View File

@ -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() { };
};
}

1018
dos.js

File diff suppressed because it is too large Load Diff

132
feed.js
View File

@ -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
View File

@ -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
View File

@ -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 &amp; 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
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<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&apos;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&apos;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 &quot;Printer&quot;" title="Pops up a &quot;printer&quot; 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 &amp; 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&apos;t collapse into <code>FOR I = STOP</code>.
<li>The interpreter doesn&apos;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&apos;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 &amp; 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
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<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&apos;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&apos;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 &quot;Printer&quot;" title="Pops up a &quot;printer&quot; 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 &amp; 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&apos;t collapse into <code>FOR I = STOP</code>.
<li>The interpreter doesn&apos;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&apos;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
View 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
View File

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

View File

@ -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);
}

View File

@ -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;
};
}

File diff suppressed because it is too large Load Diff

1938
styles.css

File diff suppressed because it is too large Load Diff

1808
tty.js

File diff suppressed because it is too large Load Diff