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 // Applesoft BASIC in Javascript
// Bell - play an audio file for the CHR$(7) "BEL" beep // Bell - play an audio file for the CHR$(7) "BEL" beep
// //
// Copyright (C) 2009-2011 Joshua Bell // Copyright (C) 2009-2011 Joshua Bell
// //
// Licensed under the Apache License, Version 2.0 (the "License"); // Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. // you may not use this file except in compliance with the License.
// You may obtain a copy of the License at // You may obtain a copy of the License at
// //
// http://www.apache.org/licenses/LICENSE-2.0 // http://www.apache.org/licenses/LICENSE-2.0
// //
// Unless required by applicable law or agreed to in writing, software // Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, // distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
var Bell; var Bell;
if (typeof Bell !== 'function') { if (typeof Bell !== 'function') {
Bell = function(base) { Bell = function(base) {
var tag; var tag;
// Prefer HTML5 audio // Prefer HTML5 audio
tag = document.createElement('audio'); tag = document.createElement('audio');
if (typeof tag.canPlayType === 'function') { if (typeof tag.canPlayType === 'function') {
tag.setAttribute('preload', 'true'); tag.setAttribute('preload', 'true');
if (tag.canPlayType('audio/mp3')) { if (tag.canPlayType('audio/mp3')) {
tag.setAttribute('src', base + 'bell.mp3'); tag.setAttribute('src', base + 'bell.mp3');
} else if (tag.canPlayType('audio/ogg')) { } else if (tag.canPlayType('audio/ogg')) {
tag.setAttribute('src', base + 'bell.ogg'); tag.setAttribute('src', base + 'bell.ogg');
} else if (tag.canPlayType('audio/wav')) { } else if (tag.canPlayType('audio/wav')) {
tag.setAttribute('src', base + 'bell.wav'); tag.setAttribute('src', base + 'bell.wav');
} }
this.play = function() { tag.play(); }; this.play = function() { tag.play(); };
this.stop = function() { tag.pause(); tag.currentTime = 0; }; this.stop = function() { tag.pause(); tag.currentTime = 0; };
return; return;
} }
// Fallback for IE<9 // Fallback for IE<9
tag = document.createElement('bgsound'); tag = document.createElement('bgsound');
if ('loop' in tag) { if ('loop' in tag) {
tag.src = base + 'bell.wav'; tag.src = base + 'bell.wav';
tag.loop = 1; tag.loop = 1;
this.play = function() { document.body.appendChild(tag); }; this.play = function() { document.body.appendChild(tag); };
this.stop = function() { document.body.removeChild(tag); }; this.stop = function() { document.body.removeChild(tag); };
return; return;
} }
this.play = function() { }; this.play = function() { };
this.stop = 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 // Atom to HTML - fetch a feed, inject it as dl/dt/dd
// //
// Copyright (C) 2009-2010 Joshua Bell // Copyright (C) 2009-2010 Joshua Bell
// //
// Licensed under the Apache License, Version 2.0 (the "License"); // Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. // you may not use this file except in compliance with the License.
// You may obtain a copy of the License at // You may obtain a copy of the License at
// //
// http://www.apache.org/licenses/LICENSE-2.0 // http://www.apache.org/licenses/LICENSE-2.0
// //
// Unless required by applicable law or agreed to in writing, software // Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, // distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
function atomToHtml(uri, element) { function atomToHtml(uri, element) {
var READYSTATE_UNINITIALIZED = 0; var READYSTATE_UNINITIALIZED = 0;
var READYSTATE_LOADING = 1; var READYSTATE_LOADING = 1;
var READYSTATE_LOADED = 2; var READYSTATE_LOADED = 2;
var READYSTATE_INTERACTIVE = 3; var READYSTATE_INTERACTIVE = 3;
var READYSTATE_COMPLETE = 4; var READYSTATE_COMPLETE = 4;
var xhr = new XMLHttpRequest(); var xhr = new XMLHttpRequest();
var async = true; var async = true;
xhr.open("GET", uri, async); xhr.open("GET", uri, async);
xhr.onreadystatechange = function() { xhr.onreadystatechange = function() {
if (xhr.readyState === READYSTATE_COMPLETE) { if (xhr.readyState === READYSTATE_COMPLETE) {
if ((xhr.status === 200 || xhr.status === 0) && xhr.responseXML) { if ((xhr.status === 200 || xhr.status === 0) && xhr.responseXML) {
var doc = xhr.responseXML; var doc = xhr.responseXML;
var entries = doc.getElementsByTagName('entry'); var entries = doc.getElementsByTagName('entry');
var html = []; var html = [];
html.push('<dl>'); html.push('<dl>');
for (var i = 0; i < entries.length; i += 1) { for (var i = 0; i < entries.length; i += 1) {
var entry = entries[i]; var entry = entries[i];
try { try {
var entryHTML = []; var entryHTML = [];
entryHTML.push('<dt>', entry.getElementsByTagName('title')[0].childNodes[0].nodeValue); entryHTML.push('<dt>', entry.getElementsByTagName('title')[0].childNodes[0].nodeValue);
entryHTML.push('<dd>', entry.getElementsByTagName('content')[0].childNodes[0].nodeValue); entryHTML.push('<dd>', entry.getElementsByTagName('content')[0].childNodes[0].nodeValue);
html.push(entryHTML.join('')); html.push(entryHTML.join(''));
} catch (e) { } catch (e) {
if (console && console.log) { console.log("Error:", e); } if (console && console.log) { console.log("Error:", e); }
} }
} }
html.push('</dl>'); html.push('</dl>');
element.innerHTML = html.join(''); element.innerHTML = html.join('');
} else { } else {
element.innerHTML = '<em>Unable to load feed</em>'; element.innerHTML = '<em>Unable to load feed</em>';
} }
} }
}; };
try { try {
xhr.send(null); xhr.send(null);
} catch (e) { } catch (e) {
element.innerHTML = '<em>Unable to load feed</em>'; element.innerHTML = '<em>Unable to load feed</em>';
} }
} }

304
hires.js
View File

@ -1,152 +1,152 @@
// //
// Applesoft BASIC in Javascript // Applesoft BASIC in Javascript
// High Resolution Graphics (HiRes) Emulation // High Resolution Graphics (HiRes) Emulation
// //
// Copyright (C) 2009-2011 Joshua Bell // Copyright (C) 2009-2011 Joshua Bell
// //
// Licensed under the Apache License, Version 2.0 (the "License"); // Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. // you may not use this file except in compliance with the License.
// You may obtain a copy of the License at // You may obtain a copy of the License at
// //
// http://www.apache.org/licenses/LICENSE-2.0 // http://www.apache.org/licenses/LICENSE-2.0
// //
// Unless required by applicable law or agreed to in writing, software // Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, // distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
// This variation does not rely on a canvas element with the same dimensions // 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. // as the pixel buffer; instead, it paints pixels on the canvas using rectangles.
// This works even if the underlying canvas implementation does not support // This works even if the underlying canvas implementation does not support
// scaling (e.g. fallbacks for IE using VML or Flash) // scaling (e.g. fallbacks for IE using VML or Flash)
// Usage: // Usage:
// //
// var hires = new LoRes( element, width, height ) // var hires = new LoRes( element, width, height )
// hires.clear() // hires.clear()
// hires.setColor( color_index ) // hires.setColor( color_index )
// hires.plot( x, y ) // hires.plot( x, y )
// hires.plot_to( x, x ) // hires.plot_to( x, x )
// hires.getPixel( x, x ) // for "HSCRN" // hires.getPixel( x, x ) // for "HSCRN"
// hires.show( bool ) // hires.show( bool )
// { width: w, height: h } = hires.getScreenSize() // { width: w, height: h } = hires.getScreenSize()
// //
// Example: // Example:
// //
// <script> // <script>
// hires = new HiRes( document.getElementById( 'hires' ), 140, 192 ); // hires = new HiRes( document.getElementById( 'hires' ), 140, 192 );
// interpreter = new BasicInterpreter( tty, lores, hires, paddle ); // interpreter = new BasicInterpreter( tty, lores, hires, paddle );
// </script> // </script>
// <canvas id="hires"></canvas> // <canvas id="hires"></canvas>
function HiRes(element, width, height) { function HiRes(element, width, height) {
var COLORS, // Apple II to HTML color table var COLORS, // Apple II to HTML color table
last_x = 0, last_x = 0,
last_y = 0, last_y = 0,
pixels = [], pixels = [],
color = 0, color = 0,
context = element.getContext("2d"); context = element.getContext("2d");
pixels.length = width * height; pixels.length = width * height;
// Colors c/o USENET: // Colors c/o USENET:
// Date: Wed, 05 Sep 2007 01:04:20 +0200 // Date: Wed, 05 Sep 2007 01:04:20 +0200
// From: Linards Ticmanis <ticmanis@gmx.de> // From: Linards Ticmanis <ticmanis@gmx.de>
// Newsgroups: comp.sys.apple2 // Newsgroups: comp.sys.apple2
// Subject: Re: Double hires mode color artifacts // Subject: Re: Double hires mode color artifacts
// Message-ID: <46dde477$0$4527$9b4e6d93@newsspool3.arcor-online.net> // Message-ID: <46dde477$0$4527$9b4e6d93@newsspool3.arcor-online.net>
COLORS = [ COLORS = [
'#000000', // Black 1 '#000000', // Black 1
'#2fbc1a', // Green '#2fbc1a', // Green
'#d043e5', // Violet '#d043e5', // Violet
'#ffffff', // White 1 '#ffffff', // White 1
'#000000', // Black 2 '#000000', // Black 2
'#d06a1a', // Orange '#d06a1a', // Orange
'#2f95e5', // Medium Blue '#2f95e5', // Medium Blue
'#ffffff' // White 2 '#ffffff' // White 2
]; ];
this.clear = function(use_color) { this.clear = function(use_color) {
var i; var i;
if (!use_color) { if (!use_color) {
context.clearRect(0, 0, element.width, element.height); context.clearRect(0, 0, element.width, element.height);
pixels = []; pixels = [];
pixels.length = width * height; pixels.length = width * height;
} else { } else {
context.fillStyle = COLORS[color]; context.fillStyle = COLORS[color];
context.fillRect(0, 0, element.width, element.height); context.fillRect(0, 0, element.width, element.height);
pixels = []; pixels = [];
pixels.length = width * height; pixels.length = width * height;
for (i = 0; i < pixels.length; i += 1) { for (i = 0; i < pixels.length; i += 1) {
pixels[i] = color; pixels[i] = color;
} }
} }
}; };
this.setColor = function(newColor) { this.setColor = function(newColor) {
color = Math.floor(newColor) % COLORS.length; color = Math.floor(newColor) % COLORS.length;
}; };
function drawPixel(x, y) { function drawPixel(x, y) {
var sx = element.width / width, var sx = element.width / width,
sy = element.height / height; sy = element.height / height;
context.fillRect(x * sx, y * sy, sx, sy); context.fillRect(x * sx, y * sy, sx, sy);
pixels[x + y * width] = color; pixels[x + y * width] = color;
} }
this.plot = function(x, y) { this.plot = function(x, y) {
context.fillStyle = COLORS[color]; context.fillStyle = COLORS[color];
drawPixel(x, y); drawPixel(x, y);
last_x = x; last_x = x;
last_y = y; last_y = y;
}; };
this.plot_to = function(x, y) { this.plot_to = function(x, y) {
var x0 = last_x, y0 = last_y, x1 = x, y1 = y, var x0 = last_x, y0 = last_y, x1 = x, y1 = y,
dx = Math.abs(x1 - x0), dx = Math.abs(x1 - x0),
dy = Math.abs(y1 - y0), dy = Math.abs(y1 - y0),
sx = (x0 < x1) ? 1 : -1, sx = (x0 < x1) ? 1 : -1,
sy = (y0 < y1) ? 1 : -1, sy = (y0 < y1) ? 1 : -1,
err = dx - dy, err = dx - dy,
e2; e2;
while (true) { while (true) {
this.plot(x0, y0); this.plot(x0, y0);
if (x0 === x1 && y0 === y1) { return; } if (x0 === x1 && y0 === y1) { return; }
e2 = 2 * err; e2 = 2 * err;
if (e2 > -dy) { if (e2 > -dy) {
err -= dy; err -= dy;
x0 += sx; x0 += sx;
} }
if (e2 < dx) { if (e2 < dx) {
err += dx; err += dx;
y0 += sy; y0 += sy;
} }
} }
last_x = x; last_x = x;
last_y = y; last_y = y;
}; };
this.getPixel = function(x, y) { this.getPixel = function(x, y) {
/*jslint bitwise:false*/ /*jslint bitwise:false*/
return pixels[y * width + x] >>> 0; return pixels[y * width + x] >>> 0;
}; };
this.getScreenSize = function() { this.getScreenSize = function() {
return { width: width, height: height }; return { width: width, height: height };
}; };
this.show = function(state) { this.show = function(state) {
element.style.visibility = state ? "visible" : "hidden"; element.style.visibility = state ? "visible" : "hidden";
}; };
} }

654
index.htm
View File

@ -1,452 +1,202 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<title>Applesoft BASIC in JavaScript</title> <title>Applesoft BASIC in JavaScript</title>
<link rel="Stylesheet" href="styles.css" type="text/css"> <link rel="Stylesheet" href="styles.css" type="text/css">
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <!-- Suppress browser compat button --> <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/polyfill.js?update=2012-02-08"></script>
<script type="text/javascript" src="../polyfill/harmony.js?update=2011-12-17"></script> <script type="text/javascript" src="../polyfill/harmony.js?update=2011-12-17"></script>
<script type="text/javascript"> <script type="text/javascript">
(function () { (function () {
function load(url) { document.write('<script type="text/javascript" src="' + url + '"></' + 'script>'); } function load(url) { document.write('<script type="text/javascript" src="' + url + '"></' + 'script>'); }
if (!window.JSON) { load('../polyfill/json2.js'); } if (!window.JSON) { load('../polyfill/json2.js'); }
if (!window.localStorage || !window.sessionStorage) { load('../polyfill/storage.js'); } if (!window.localStorage || !window.sessionStorage) { load('../polyfill/storage.js'); }
if (!('getContext' in document.createElement('canvas'))) { load('../polyfill/flashcanvas.js'); } if (!('getContext' in document.createElement('canvas'))) { load('../polyfill/flashcanvas.js'); }
}()); }());
</script> </script>
<script type="text/javascript" src="../polyfill/keyboard.js"></script> <script type="text/javascript" src="../polyfill/keyboard.js"></script>
</head> </head>
<body> <body>
<h1>Applesoft BASIC in Javascript</h1> <h1>Applesoft BASIC in Javascript</h1>
<p> <p>
By <a href="mailto:inexorabletash@hotmail.com">Joshua Bell</a> By <a href="mailto:inexorabletash@hotmail.com">Joshua Bell</a>
| <a target="_blank" href="reference.htm">Applesoft BASIC Quick Reference</a> | <a target="_blank" href="reference.htm">Applesoft BASIC Quick Reference</a>
| <a href="#notes">Notes &amp; Known Issues</a> | <a href="#notes">Notes &amp; Known Issues</a>
| <a href="#todo">To Do</a> | <a href="#todo">To Do</a>
| <a href="#links">Links</a> | <a href="#links">Links</a>
| <a href="#history">History</a> | <a href="#history">History</a>
<p>Related projects: <p>Related projects:
<a href="/Logo">Logo in Javascript</a> <a href="/Logo">Logo in Javascript</a>
| <a href="/vnIIc">Streaming video to an Apple II - vnIIc</a> | <a href="/vnIIc">Streaming video to an Apple II - vnIIc</a>
</p> </p>
<br clear=left> <br clear=left>
<!-- Screen --> <!-- Screen -->
<div id="frame" class="frame" style="float: left; margin: 10px;"> <div id="frame" class="frame" style="float: left; margin: 10px;">
<div id="screen-wrapper" class="wrapper"> <div id="screen-wrapper" class="wrapper">
<div id="lores" class="lores"></div> <div id="lores" class="lores"></div>
<canvas id="hires" width="560" height="384" class="hires"></canvas> <canvas id="hires" width="560" height="384" class="hires"></canvas>
<canvas id="hires2" width="560" height="384" class="hires"></canvas> <canvas id="hires2" width="560" height="384" class="hires"></canvas>
<div id="screen" class="tty"></div> <div id="screen" class="tty"></div>
</div> </div>
</div> </div>
<!-- Keyboard - positioned offscreen --> <!-- Keyboard - positioned offscreen -->
<input id="keyboard" class="keyboard" type="text" title="Hidden Keyboard Input Device"> <input id="keyboard" class="keyboard" type="text" title="Hidden Keyboard Input Device">
<!-- Source --> <!-- Source -->
<div style="float: left; margin: 10px;"> <div style="float: left; margin: 10px;">
Enter code below Enter code below
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<input type="button" value="Run" id="btn_run"> <input type="button" value="Run" id="btn_run">
<input type="button" value="Stop" id="btn_stop" disabled="disabled"> <input type="button" value="Stop" id="btn_stop" disabled="disabled">
<select id="lb_files"> <select id="lb_files">
<option disabled selected="selected">Select a file...</option> <option disabled selected="selected">Select a file...</option>
<option value="sample.basic">DEMOS</option> <option value="sample.basic">DEMOS</option>
<option disabled>---- Tests ----</option> <option disabled>---- Tests ----</option>
<option value="sample.unittests">Unit Tests</option> <option value="sample.unittests">Unit Tests</option>
<option value="sample.keyboard">Keyboard Test</option> <option value="sample.keyboard">Keyboard Test</option>
<option value="sample.charset">Charset Test</option> <option value="sample.charset">Charset Test</option>
<option disabled>---- User Submissions ----</option> <option disabled>---- User Submissions ----</option>
<option disabled>Games</option> <option disabled>Games</option>
<option value="simple.pong">SIMPLE.PONG</option> <option value="simple.pong">SIMPLE.PONG</option>
<option value="sample.adventure">Text Adventure (Floyd McWilliams)</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.pacman">(Not Really) ASCII Pac-Man (Michael Kemp)</option>
<option value="sample.puzzler">Puzzler (Gregg Buntin)</option> <option value="sample.puzzler">Puzzler (Gregg Buntin)</option>
<option value="sample.hangman">Hangman (Mike Gleason)</option> <option value="sample.hangman">Hangman (Mike Gleason)</option>
<option value="sample.raindrops">Catch the Raindrop (Nicholas Merchant)</option> <option value="sample.raindrops">Catch the Raindrop (Nicholas Merchant)</option>
<option value="sample.jot">JOT (Mike Gleason)</option> <option value="sample.jot">JOT (Mike Gleason)</option>
<option disabled>Graphics</option> <option disabled>Graphics</option>
<option value="sample.rodscolorpattern">Rod&apos;s Color Pattern</option> <option value="sample.rodscolorpattern">Rod&apos;s Color Pattern</option>
<option value="sample.hacker">Hacker Logo (markwstock)</option> <option value="sample.hacker">Hacker Logo (markwstock)</option>
<option value="sample.loreswalk">Random LoRes (John Melesky)</option> <option value="sample.loreswalk">Random LoRes (John Melesky)</option>
<option value="sample.hireswalk">Random HiRes (John Melesky)</option> <option value="sample.hireswalk">Random HiRes (John Melesky)</option>
<option value="sample.sierpinski">Sierpinski Triangles (Kevin Miller)</option> <option value="sample.sierpinski">Sierpinski Triangles (Kevin Miller)</option>
<option value="sample.stringart">String Art (Chris Heric)</option> <option value="sample.stringart">String Art (Chris Heric)</option>
<option value="sample.paint">Drawing Program (Brian Broker)</option> <option value="sample.paint">Drawing Program (Brian Broker)</option>
<option value="sample.scribble">Scribble (William Simms)</option> <option value="sample.scribble">Scribble (William Simms)</option>
<option value="sample.connections">Connections (Gregg Buntin)</option> <option value="sample.connections">Connections (Gregg Buntin)</option>
<option value="sample.squiggle">Squiggle (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.boys_surface">Boy&apos;s Surface (Lukas Innig)</option>
<option value="sample.gaussian">Gaussian Distribution 2D (John Russ)</option> <option value="sample.gaussian">Gaussian Distribution 2D (John Russ)</option>
<option value="sample.bitmaps">Bitmap Images (Brian Broker)</option> <option value="sample.bitmaps">Bitmap Images (Brian Broker)</option>
<option value="sample.mandelbrot">Mandelbrot Set (c/o Gregory Lewis)</option> <option value="sample.mandelbrot">Mandelbrot Set (c/o Gregory Lewis)</option>
<option value="sample.mandelbrot2">Mandelbrot Set in Color</option> <option value="sample.mandelbrot2">Mandelbrot Set in Color</option>
<option value="sample.steve">Steve (Nicola Foggi)</option> <option value="sample.steve">Steve (Nicola Foggi)</option>
<option disabled>Other</option> <option disabled>Other</option>
<option value="sample.primes">Prime Sieve (Kevin Miller)</option> <option value="sample.primes">Prime Sieve (Kevin Miller)</option>
<option value="sample.february">February Surprise (Antti Pirskanen)</option> <option value="sample.february">February Surprise (Antti Pirskanen)</option>
<option value="sample.hellosine">Hello World Sine Wave (Jamie Beu)</option> <option value="sample.hellosine">Hello World Sine Wave (Jamie Beu)</option>
<option value="sample.bodymass">Body Mass Index Calculator (Tim Dwyer)</option> <option value="sample.bodymass">Body Mass Index Calculator (Tim Dwyer)</option>
<option disabled>---- Traveller RPG Utilities ----</option> <option disabled>---- Traveller RPG Utilities ----</option>
<option value="TRADER C">TRADER</option> <option value="TRADER C">TRADER</option>
<option value="sample.sectorgen">Traveller Sector Generator</option> <option value="sample.sectorgen">Traveller Sector Generator</option>
<option value="sample.zhorelay">Zhodani Relay Station Placement</option> <option value="sample.zhorelay">Zhodani Relay Station Placement</option>
<option value="sample.readsector">Read Sector File</option> <option value="sample.readsector">Read Sector File</option>
</select> </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"> <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 --> <!-- Source code editor inserted here -->
<div id="editorframe"></div> <div id="editorframe"></div>
<form id="submission" method="post" enctype="text/plain" action="mailto:inexorabletash@hotmail.com?subject=Applesoft%20Sample%20Submission"> <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 name="source" id="source" style="display: none;">
</textarea> </textarea>
<input type="submit" id="btn_share" value="Share your sample!"> <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_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!"> <input type="button" id="btn_load" value="Load Program" title="Load a previously saved program. WARNING: The current program will be lost!">
</form> </form>
</div> </div>
<br clear=left> <br clear=left>
<h3 id="notes">Notes &amp; Known Issues</h3> <h3 id="notes">Notes &amp; Known Issues</h3>
<ul> <ul>
<li>The BASIC program is compiled to JavaScript before execution. Syntax errors are therefore detected at compile-time <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 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> 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: <li>Handling of BASIC code that does not match the canonical <code>LIST</code> output format may not behave as on an Apple:
<ul> <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>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 <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> </ul>
<li>To improve readability, lines may start with <code>:</code> and continue the previously numbered line. <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>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>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>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>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): <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> <ul>
<li>On Windows, download <a href="basic.js">basic.js</a> and run from a command prompt via: <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> <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>, <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: 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> <code>java -jar PATH_TO/js.jar basic.js your_program.txt</code>
</ul> </ul>
</ul> </ul>
<h3 id="todo">To Do</h3> <h3 id="todo">To Do</h3>
<ul> <ul>
<li>Implement DOS functionality for consoles <li>Implement DOS functionality for consoles
</ul> </ul>
<h3 id="links">Links</h3> <h3 id="links">Links</h3>
<ul> <ul>
<li><a href="http://www.6502asm.com/">6502asm.com</a> - a 6502 assembler/emulator in JavaScript <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://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://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> <li><a href="http://www.nicholson.com/rhn/basic/">BASIC Programming Resources</a>
</ul> </ul>
<h3 id="history">History</h3> <h3 id="history">History</h3>
<div id="feed"></div> <div id="feed"></div>
<script src="cm2/lib/codemirror.js"></script> <script src="cm2/lib/codemirror.js"></script>
<link rel="stylesheet" href="cm2/lib/codemirror.css"> <link rel="stylesheet" href="cm2/lib/codemirror.css">
<link rel="stylesheet" href="cm2/theme/default.css"> <link rel="stylesheet" href="cm2/theme/default.css">
<script src="cm2/mode/basic/basic.js"></script> <script src="cm2/mode/basic/basic.js"></script>
<link rel="stylesheet" href="cm2/mode/basic/basic.css"> <link rel="stylesheet" href="cm2/mode/basic/basic.css">
<style type="text/css"> <style type="text/css">
.CodeMirror { border: solid 1px black; width: 598px; height: 384px; background-color: white; } .CodeMirror { border: solid 1px black; width: 598px; height: 384px; background-color: white; }
.CodeMirror-scroll { height: 100%; } .CodeMirror-scroll { height: 100%; }
</style> </style>
<script type="text/javascript" src="basic.js?2012-02-08"></script>
<script type="text/javascript" src="basic.js?2012-02-08"></script> <script type="text/javascript" src="bell.js"></script>
<script type="text/javascript" src="bell.js"></script> <script type="text/javascript" src="tty.js"></script>
<script type="text/javascript" src="tty.js"></script> <script type="text/javascript" src="lores.js"></script>
<script type="text/javascript" src="lores.js"></script> <script type="text/javascript" src="hires.js"></script>
<script type="text/javascript" src="hires.js"></script> <script type="text/javascript" src="dos.js"></script>
<script type="text/javascript" src="dos.js"></script> <script type="text/javascript" src="printer.js"></script>
<script type="text/javascript" src="printer.js"></script> <script type="text/javascript" src="feed.js"></script>
<script type="text/javascript" src="feed.js"></script> <script type="text/javascript" src="index.js"></script>
<script type="text/javascript">
<script type="text/javascript">
// not quite ready for jQuery... var _gaq = _gaq || [];
var $ = function (id) { return document.getElementById(id); }; _gaq.push(['_setAccount', 'UA-18610679-3']);
_gaq.push(['_trackPageview']);
//
// Main (function() {
// var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
window.onload = function() { 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);
$('lb_files').selectedIndex = 0; })();
</script>
var bell = (function() {
var b = new Bell(/^.*\/|/.exec(window.location)[0]); </body>
return function() { b.play(); }; </html>
} ());
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>

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 // Applesoft BASIC in Javascript
// Low Resolution Graphics (LoRes) Emulation // Low Resolution Graphics (LoRes) Emulation
// //
// Copyright (C) 2009-2011 Joshua Bell // Copyright (C) 2009-2011 Joshua Bell
// //
// Licensed under the Apache License, Version 2.0 (the "License"); // Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. // you may not use this file except in compliance with the License.
// You may obtain a copy of the License at // You may obtain a copy of the License at
// //
// http://www.apache.org/licenses/LICENSE-2.0 // http://www.apache.org/licenses/LICENSE-2.0
// //
// Unless required by applicable law or agreed to in writing, software // Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, // distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
// Usage: // Usage:
// //
// var lores = new LoRes( element, width, height ) // var lores = new LoRes( element, width, height )
// lores.clear() // lores.clear()
// lores.setColor( color_index ) // lores.setColor( color_index )
// lores.plot( x, y ) // lores.plot( x, y )
// lores.hlin( x1, x2, y ) // lores.hlin( x1, x2, y )
// lores.vlin( x1, x2, y ) // lores.vlin( x1, x2, y )
// lores.show( bool ) // lores.show( bool )
// color_index = lores.getPixel( x, y ) // color_index = lores.getPixel( x, y )
// { width: w, height: h } = lores.getScreenSize() // { width: w, height: h } = lores.getScreenSize()
// //
// Example: // Example:
// //
// <style> // <style>
// .loresPixel { width: 14px; height: 8px; } // .loresPixel { width: 14px; height: 8px; }
// </style> // </style>
// <script> // <script>
// lores = new LoRes( document.getElementById( 'lores' ), 40, 40 ); // lores = new LoRes( document.getElementById( 'lores' ), 40, 40 );
// interpreter = new BasicInterpreter( tty, lores, paddle ); // interpreter = new BasicInterpreter( tty, lores, paddle );
// </script> // </script>
// <div id="lores"></div> // <div id="lores"></div>
function LoRes(element, width, height) { function LoRes(element, width, height) {
/*jslint browser: true*/ /*jslint browser: true*/
var COLORS, // Apple II to HTML color table var COLORS, // Apple II to HTML color table
loresPixel = [], // element references loresPixel = [], // element references
pixels = [], // color values pixels = [], // color values
color = 0; // current color color = 0; // current color
// Colors c/o USENET: // Colors c/o USENET:
// Date: Wed, 05 Sep 2007 01:04:20 +0200 // Date: Wed, 05 Sep 2007 01:04:20 +0200
// From: Linards Ticmanis <ticmanis@gmx.de> // From: Linards Ticmanis <ticmanis@gmx.de>
// Newsgroups: comp.sys.apple2 // Newsgroups: comp.sys.apple2
// Subject: Re: Double hires mode color artifacts // Subject: Re: Double hires mode color artifacts
// Message-ID: <46dde477$0$4527$9b4e6d93@newsspool3.arcor-online.net> // Message-ID: <46dde477$0$4527$9b4e6d93@newsspool3.arcor-online.net>
COLORS = [ COLORS = [
'#000000', // Black '#000000', // Black
'#901740', // Deep Red '#901740', // Deep Red
'#402ca5', // Dark Blue '#402ca5', // Dark Blue
'#d043e5', // Purple '#d043e5', // Purple
'#006940', // Dark Green '#006940', // Dark Green
'#808080', // Gray 1 '#808080', // Gray 1
'#2f95e5', // Medium Blue '#2f95e5', // Medium Blue
'#bfabff', // Light Blue '#bfabff', // Light Blue
'#402400', // Brown // WAS #405400, error pointed out by MJM '#402400', // Brown // WAS #405400, error pointed out by MJM
'#d06a1a', // Orange '#d06a1a', // Orange
'#808080', // Gray 2 '#808080', // Gray 2
'#ff96bf', // Pink '#ff96bf', // Pink
'#2fbc1a', // Light Green '#2fbc1a', // Light Green
'#bfd35a', // Yellow '#bfd35a', // Yellow
'#6fe8bf', // Aquamarine '#6fe8bf', // Aquamarine
'#ffffff' // White '#ffffff' // White
]; ];
function init() { function init() {
var x, y, table, tbody, tr, td; var x, y, table, tbody, tr, td;
pixels = []; pixels = [];
pixels.length = width * height; pixels.length = width * height;
loresPixel = []; loresPixel = [];
loresPixel.length = width * height; loresPixel.length = width * height;
table = document.createElement('table'); table = document.createElement('table');
table.className = 'loresDisplay'; table.className = 'loresDisplay';
tbody = document.createElement('tbody'); tbody = document.createElement('tbody');
for (y = 0; y < height; y += 1) { for (y = 0; y < height; y += 1) {
tr = document.createElement('tr'); tr = document.createElement('tr');
tr.className = 'loresRow'; tr.className = 'loresRow';
for (x = 0; x < width; x += 1) { for (x = 0; x < width; x += 1) {
td = document.createElement('td'); td = document.createElement('td');
td.className = 'loresPixel'; td.className = 'loresPixel';
td.style.backgroundColor = 'black'; td.style.backgroundColor = 'black';
loresPixel[y * width + x] = td; loresPixel[y * width + x] = td;
pixels[y * width + x] = 0; pixels[y * width + x] = 0;
tr.appendChild(td); tr.appendChild(td);
} }
tbody.appendChild(tr); tbody.appendChild(tr);
} }
table.appendChild(tbody); table.appendChild(tbody);
element.innerHTML = ""; element.innerHTML = "";
element.appendChild(table); element.appendChild(table);
} }
this.clear = function() { this.clear = function() {
var x, y, pixel; var x, y, pixel;
for (y = 0; y < height; y += 1) { for (y = 0; y < height; y += 1) {
for (x = 0; x < width; x += 1) { for (x = 0; x < width; x += 1) {
pixel = loresPixel[y * width + x]; pixel = loresPixel[y * width + x];
pixel.style.backgroundColor = "black"; pixel.style.backgroundColor = "black";
pixels[y * width + x] = 0; pixels[y * width + x] = 0;
} }
} }
}; };
this.setColor = function(newColor) { this.setColor = function(newColor) {
color = Math.floor(newColor) % COLORS.length; color = Math.floor(newColor) % COLORS.length;
}; };
function plot(x, y) { function plot(x, y) {
var pixel = loresPixel[y * width + x]; var pixel = loresPixel[y * width + x];
if (pixel) { if (pixel) {
pixel.style.backgroundColor = COLORS[color]; pixel.style.backgroundColor = COLORS[color];
pixels[y * width + x] = color; pixels[y * width + x] = color;
} }
} }
this.plot = function(x, y) { this.plot = function(x, y) {
plot(x, y); plot(x, y);
}; };
this.getPixel = function(x, y) { this.getPixel = function(x, y) {
if (0 <= x && x < width && if (0 <= x && x < width &&
0 <= y && y < height) { 0 <= y && y < height) {
return pixels[y * width + x]; return pixels[y * width + x];
} else { } else {
return 0; return 0;
} }
}; };
this.hlin = function(x1, x2, y) { this.hlin = function(x1, x2, y) {
var x; var x;
if (x1 > x2) { if (x1 > x2) {
x = x1; x = x1;
x1 = x2; x1 = x2;
x2 = x; x2 = x;
} }
for (x = x1; x <= x2; x += 1) { for (x = x1; x <= x2; x += 1) {
plot(x, y); plot(x, y);
} }
}; };
this.vlin = function(y1, y2, x) { this.vlin = function(y1, y2, x) {
var y; var y;
if (y1 > y2) { if (y1 > y2) {
y = y1; y = y1;
y1 = y2; y1 = y2;
y2 = y; y2 = y;
} }
for (y = y1; y <= y2; y += 1) { for (y = y1; y <= y2; y += 1) {
plot(x, y); plot(x, y);
} }
}; };
this.getScreenSize = function() { this.getScreenSize = function() {
return { width: width, height: height }; return { width: width, height: height };
}; };
this.show = function(state) { this.show = function(state) {
element.style.visibility = state ? "visible" : "hidden"; element.style.visibility = state ? "visible" : "hidden";
}; };
//---------------------------------------------------------------------- //----------------------------------------------------------------------
// Constructor Logic // Constructor Logic
//---------------------------------------------------------------------- //----------------------------------------------------------------------
init(); init();
} }

View File

@ -1,140 +1,140 @@
// //
// Applesoft BASIC in Javascript // Applesoft BASIC in Javascript
// Paddle and Joystick Emulation // Paddle and Joystick Emulation
// //
// Copyright (C) 2009-2011 Joshua Bell // Copyright (C) 2009-2011 Joshua Bell
// //
// Licensed under the Apache License, Version 2.0 (the "License"); // Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. // you may not use this file except in compliance with the License.
// You may obtain a copy of the License at // You may obtain a copy of the License at
// //
// http://www.apache.org/licenses/LICENSE-2.0 // http://www.apache.org/licenses/LICENSE-2.0
// //
// Unless required by applicable law or agreed to in writing, software // Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, // distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
// Usage: // Usage:
// //
// initPaddle( device_element, thumb_element, callback_function ); // initPaddle( device_element, thumb_element, callback_function );
// initJoystick( device_element, thumb_element, callback_function_x, callback_function_y ); // initJoystick( device_element, thumb_element, callback_function_x, callback_function_y );
// //
// Example: // Example:
// //
// <script> // <script>
// initPaddle( device.getElementById('paddle'), device.getElementById('knob'), function ( v ) { window.status = v; /* v = 0...1 */ } ); // 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 */ } ); // initJoystick( device.getElementById('joystick'), device.getElementById('stick'), function ( v ) { window.status = v; /* v = 0...1 */ }, function ( v ) { window.status = v; /* v = 0...1 */ } );
// </script> // </script>
// <style> // <style>
// #paddle { border-style: inset; border-width: 2px; border-color: gray; background-color: #c0c0c0; height: 20px; // #paddle { border-style: inset; border-width: 2px; border-color: gray; background-color: #c0c0c0; height: 20px;
// width: 300px; text-align: center; } // width: 300px; text-align: center; }
// #knob { border-style: outset; border-width: 2px; border-color: gray; background-color: #404040; height: 16px; width: 10px; } // #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; // #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; } // 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; } // #stick { border-style: outset; border-width: 2px; border-color: gray; background-color: #404040; height: 16px; width: 16px; }
// </style> // </style>
// <div id="paddle"> <div id="knob"></div> Slide the Paddle! </div> // <div id="paddle"> <div id="knob"></div> Slide the Paddle! </div>
// <div id="joystick"> <div id="stick"></div> Twiddle me!</div> // <div id="joystick"> <div id="stick"></div> Twiddle me!</div>
/*extern document,window,addEvent,removeEvent */ /*extern document,window,addEvent,removeEvent */
function initDevice(device, thumb, fn_x, fn_y) { function initDevice(device, thumb, fn_x, fn_y) {
device.style.position = "relative"; device.style.position = "relative";
device.style.overflow = "hidden"; device.style.overflow = "hidden";
thumb.style.position = "absolute"; thumb.style.position = "absolute";
thumb.style.left = 0; thumb.style.left = 0;
thumb.style.top = 0; thumb.style.top = 0;
thumb.last_x = 0; thumb.last_x = 0;
thumb.last_y = 0; thumb.last_y = 0;
var onStickDrag = function (e) { var onStickDrag = function (e) {
e.returnValue = false; e.returnValue = false;
e.cancelBubble = true; e.cancelBubble = true;
if (e.stopPropagation) { e.stopPropagation(); } if (e.stopPropagation) { e.stopPropagation(); }
if (e.preventDefault) { e.preventDefault(); } if (e.preventDefault) { e.preventDefault(); }
if (fn_x) { if (fn_x) {
var x = e.clientX - thumb.originX + thumb.posX; var x = e.clientX - thumb.originX + thumb.posX;
var min_x = 0; var min_x = 0;
var max_x = device.clientWidth - thumb.offsetWidth; var max_x = device.clientWidth - thumb.offsetWidth;
if (x < min_x) { x = min_x; } if (x < min_x) { x = min_x; }
if (x > max_x) { x = max_x; } if (x > max_x) { x = max_x; }
if (x != thumb.last_x) { if (x != thumb.last_x) {
thumb.last_x = x; thumb.last_x = x;
thumb.style.left = x + "px"; thumb.style.left = x + "px";
fn_x((x - min_x) / (max_x - min_x)); fn_x((x - min_x) / (max_x - min_x));
} }
} }
if (fn_y) { if (fn_y) {
var y = e.clientY - thumb.originY + thumb.posY; var y = e.clientY - thumb.originY + thumb.posY;
var min_y = 0; var min_y = 0;
var max_y = device.clientHeight - thumb.offsetHeight; var max_y = device.clientHeight - thumb.offsetHeight;
if (y < min_y) { y = min_y; } if (y < min_y) { y = min_y; }
if (y > max_y) { y = max_y; } if (y > max_y) { y = max_y; }
if (y != thumb.last_y) { if (y != thumb.last_y) {
thumb.last_y = y; thumb.last_y = y;
thumb.style.top = y + "px"; thumb.style.top = y + "px";
fn_y((y - min_y) / (max_y - min_y)); fn_y((y - min_y) / (max_y - min_y));
} }
} }
return false; return false;
}; };
var onStickUp = function (e) { var onStickUp = function (e) {
e.returnValue = false; e.returnValue = false;
e.cancelBubble = true; e.cancelBubble = true;
if (e.stopPropagation) { e.stopPropagation(); } if (e.stopPropagation) { e.stopPropagation(); }
if (e.preventDefault) { e.preventDefault(); } if (e.preventDefault) { e.preventDefault(); }
removeEvent(document, "mousemove", onStickDrag); removeEvent(document, "mousemove", onStickDrag);
removeEvent(document, "mouseup", onStickUp); removeEvent(document, "mouseup", onStickUp);
if (thumb.releaseCapture) { thumb.releaseCapture(true); } if (thumb.releaseCapture) { thumb.releaseCapture(true); }
return false; return false;
}; };
var onStickDown = function (e) { var onStickDown = function (e) {
e.returnValue = false; e.returnValue = false;
e.cancelBubble = true; e.cancelBubble = true;
if (e.stopPropagation) { e.stopPropagation(); } if (e.stopPropagation) { e.stopPropagation(); }
if (e.preventDefault) { e.preventDefault(); } if (e.preventDefault) { e.preventDefault(); }
addEvent(document, "mousemove", onStickDrag); addEvent(document, "mousemove", onStickDrag);
addEvent(document, "mouseup", onStickUp); addEvent(document, "mouseup", onStickUp);
if (thumb.setCapture) { thumb.setCapture(true); } if (thumb.setCapture) { thumb.setCapture(true); }
if (fn_x) { if (fn_x) {
thumb.originX = e.clientX; thumb.originX = e.clientX;
thumb.posX = parseInt(thumb.style.left, 10); thumb.posX = parseInt(thumb.style.left, 10);
} }
if (fn_y) { if (fn_y) {
thumb.originY = e.clientY; thumb.originY = e.clientY;
thumb.posY = parseInt(thumb.style.top, 10); thumb.posY = parseInt(thumb.style.top, 10);
} }
return false; return false;
}; };
device.appendChild(thumb); device.appendChild(thumb);
addEvent(thumb, "mousedown", onStickDown); addEvent(thumb, "mousedown", onStickDown);
} }
function initPaddle(device, control, fn) { function initPaddle(device, control, fn) {
initDevice(device, control, fn); initDevice(device, control, fn);
} }
function initJoystick(device, control, fn_x, fn_y) { function initJoystick(device, control, fn_x, fn_y) {
initDevice(device, control, fn_x, fn_y); initDevice(device, control, fn_x, fn_y);
} }

View File

@ -1,95 +1,95 @@
// //
// Applesoft BASIC in Javascript // Applesoft BASIC in Javascript
// Printer (for copying output) // Printer (for copying output)
// //
// Copyright (C) 2009-2011 Joshua Bell // Copyright (C) 2009-2011 Joshua Bell
// //
// Licensed under the Apache License, Version 2.0 (the "License"); // Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. // you may not use this file except in compliance with the License.
// You may obtain a copy of the License at // You may obtain a copy of the License at
// //
// http://www.apache.org/licenses/LICENSE-2.0 // http://www.apache.org/licenses/LICENSE-2.0
// //
// Unless required by applicable law or agreed to in writing, software // Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, // distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
function Printer(tty) { function Printer(tty) {
// For closures // For closures
var self = this; var self = this;
// Create new window and document // 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 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]; var body = w.document.getElementsByTagName('body')[0];
body.style.fontFamily = 'Courier, Monospace'; body.style.fontFamily = 'Courier, Monospace';
body.style.backgroundColor = '#ffffff'; body.style.backgroundColor = '#ffffff';
body.style.backgroundImage = "url('http://calormen.com/Star_Trek/ASCII/lpt.jpg')"; body.style.backgroundImage = "url('http://calormen.com/Star_Trek/ASCII/lpt.jpg')";
body.style.color = '#000000'; body.style.color = '#000000';
body.style.paddingLeft = '50px'; body.style.paddingLeft = '50px';
var paper = w.document.createElement('div'); var paper = w.document.createElement('div');
paper.style.whiteSpace = 'pre'; paper.style.whiteSpace = 'pre';
body.appendChild(paper); body.appendChild(paper);
var tty_writeChar = tty.writeChar; var tty_writeChar = tty.writeChar;
tty.writeChar = function(c) { tty.writeChar = function(c) {
tty_writeChar(c); tty_writeChar(c);
switch (c.charCodeAt(0)) { switch (c.charCodeAt(0)) {
case 0: case 0:
case 1: case 1:
case 2: case 2:
case 3: case 3:
case 4: // DOS hook case 4: // DOS hook
case 5: case 5:
case 6: case 6:
case 7: // (BEL) bell case 7: // (BEL) bell
case 8: // (BS) backspace case 8: // (BS) backspace
case 9: case 9:
case 11: // (VT) clear EOS case 11: // (VT) clear EOS
case 12: // (FF) clear case 12: // (FF) clear
case 14: // (SO) normal case 14: // (SO) normal
case 15: // (SI) inverse case 15: // (SI) inverse
case 16: case 16:
case 17: // (DC1) 40-column case 17: // (DC1) 40-column
case 18: // (DC2) 80-column case 18: // (DC2) 80-column
case 19: // (DC3) stop list case 19: // (DC3) stop list
case 20: case 20:
case 21: // (NAK) quit (disable 80-col card) case 21: // (NAK) quit (disable 80-col card)
case 22: // (SYN) scroll case 22: // (SYN) scroll
case 23: // (ETB) scroll up case 23: // (ETB) scroll up
case 24: // (CAN) disable mousetext case 24: // (CAN) disable mousetext
case 25: // (EM) home case 25: // (EM) home
case 26: // (SUB) clear line case 26: // (SUB) clear line
case 27: // (ESC) enable mousetext case 27: // (ESC) enable mousetext
case 28: // (FS) forward space case 28: // (FS) forward space
case 29: // (GS) clear EOL case 29: // (GS) clear EOL
case 30: // (RS) gotoXY case 30: // (RS) gotoXY
case 31: case 31:
break; break;
case 10: // (LF) line feed case 10: // (LF) line feed
case 13: // (CR) return case 13: // (CR) return
paper.appendChild(w.document.createTextNode('\n')); paper.appendChild(w.document.createTextNode('\n'));
break; break;
default: default:
paper.appendChild(w.document.createTextNode(c)); paper.appendChild(w.document.createTextNode(c));
break; break;
} }
paper.parentElement.scrollTop = paper.parentElement.scrollHeight; paper.parentElement.scrollTop = paper.parentElement.scrollHeight;
}; };
w.onunload = function() { w.onunload = function() {
if (self.onclose) { if (self.onclose) {
self.onclose(); self.onclose();
} }
tty.writeChar = tty_writeChar; 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