Cleanup and refactoring.

This commit is contained in:
Will Scullin 2016-11-21 21:17:34 -08:00
parent 1dfbc0b61b
commit f9f0239d86
40 changed files with 4487 additions and 3964 deletions

14
.editorconfig Normal file
View File

@ -0,0 +1,14 @@
root = true
[*]
charset = utf-8
end_of_line = lf
indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true
[*.js]
indent_size = 4
[*.md]
trim_trailing_whitespace = true

25
.eslintrc.json Normal file
View File

@ -0,0 +1,25 @@
{
"rules": {
"indent": [
2,
4
],
"quotes": [
2,
"single"
],
"linebreak-style": [
2,
"unix"
],
"semi": [
2,
"always"
]
},
"env": {
"browser": true,
"jquery": true
},
"extends": "eslint:recommended"
}

View File

@ -1,6 +1,6 @@
<!DOCTYPE html>
<!-- -*- mode: HTML; indent-tabs-mode: nil -*- -->
<!-- Copyright 2010 Will Scullin -->
<!-- Copyright 2010-2016 Will Scullin -->
<html>
<head>
<base href="/apple" />

View File

@ -1,6 +1,6 @@
<!DOCTYPE html><!-- -*- mode: HTML; indent-tabs-mode: nil -*- -->
<!--
Copyright 2010-2013 Will Scullin <scullin@scullinsteel.com>
Copyright 2010-2016 Will Scullin <scullin@scullinsteel.com>
Permission to use, copy, modify, distribute, and sell this software and its
documentation for any purpose is hereby granted without fee, provided that
@ -50,6 +50,7 @@
<script type="text/javascript" src="js/langcard.js"></script>
<script type="text/javascript" src="js/fpbasic.js"></script>
<script type="text/javascript" src="js/apple2char.js"></script>
<script type="text/javascript" src="js/applesoft/decompiler.js"></script>
<script type="text/javascript" src="js/canvas2.js"></script>
<script type="text/javascript" src="js/apple2io.js"></script>
<script type="text/javascript" src="js/parallel.js"></script>
@ -66,772 +67,8 @@
<!-- Disk Index -->
<script type="text/javascript" src="json/disks/index.js"></script>
<script type="text/javascript">
<script type="text/javascript" src="js/main2.js"></script>
var kHz = 1023;
var focused = false;
var startTime = Date.now();
var lastCycles = 0;
var frames = 0, lastFrames = 0;
var paused = false;
var sound = true;
var hashtag;
var disk_categories = {'Local Saves': []};
var disk_sets = {}
var disk_cur_name = [];
var disk_cur_cat = [];
function DriveLights()
{
return {
driveLight: function(drive, on) {
$("#disk" + drive).css("background-image",
on ? "url(css/red-on-16.png)" :
"url(css/red-off-16.png)");
},
dirty: function(drive, dirty) {
},
label: function(drive, label) {
$("#disklabel" + drive).text(label);
}
}
}
var DISK_TYPES = ['dsk','do','po','raw','nib','2mg'];
var TAPE_TYPES = ['wav','aiff','aif','mp3'];
var _saveDrive = 1;
var _loadDrive = 1;
function openLoad(drive, event)
{
_loadDrive = drive;
if (disk_cur_cat[drive]) {
$("#category_select").val(disk_cur_cat[drive]).change();
}
$("#load").dialog("open");
}
function openSave(drive, event)
{
_saveDrive = drive;
$("#save_name").val($("#disklabel" + drive).text());
$("#save").dialog("open");
}
var loading = false;
function loadAjax(url) {
loading = true;
$("#loading").dialog("open");
$.ajax({ url: url,
cache: false,
dataType: "jsonp",
jsonp: false,
global: false
});
}
function doLoad() {
var urls = $("#disk_select").val(), url;
if (urls && urls.length) {
if (typeof(urls) == "string") {
url = urls;
} else {
url = urls[0];
}
}
var files = $("#local_file").prop("files");
if (files.length == 1) {
doLoadLocal();
} else if (url) {
var filename;
$("#load").dialog("close");
if (url.substr(0,6) == "local:") {
filename = url.substr(6);
if (filename == "__manage") {
openManage();
} else {
loadLocalStorage(_loadDrive, filename);
}
} else {
var r1 = /json\/disks\/(.*).json$/.exec(url);
if (r1 && _loadDrive == 1) {
filename = r1[1];
document.location.hash = filename;
}
loadAjax(url);
}
}
}
function doSave() {
var name = $("#save_name").val();
saveLocalStorage(_saveDrive, name);
$("#save").dialog("close");
}
function doDelete(name) {
if (confirm("Delete " + name + "?")) {
deleteLocalStorage(name);
}
}
function doLoadLocal() {
var files = $("#local_file").prop("files")
if (files.length == 1) {
var file = files[0];
var parts = file.name.split(".");
var ext = parts[parts.length - 1].toLowerCase();
if ($.inArray(ext, DISK_TYPES) >= 0) {
doLoadLocalDisk(file);
} else if ($.inArray(ext, TAPE_TYPES) >= 0) {
doLoadLocalTape(file);
} else {
alert('Unknown file type: ' + ext);
$("#load").dialog("close");
}
}
}
function doLoadLocalDisk(file) {
var fileReader = new FileReader();
fileReader.onload = function(event) {
var parts = file.name.split(".");
var name = parts[0], ext = parts[parts.length - 1].toLowerCase();
if (disk2.setBinary(_saveDrive, name, ext, this.result)) {
$("#disklabel" + _saveDrive).text(name);
$("#load").dialog("close");
initGamepad();
}
}
fileReader.readAsArrayBuffer(file);
}
function doLoadLocalTape(file) {
// Audio Buffer Source
if (typeof webkitAudioContext != "undefined") {
var context = new webkitAudioContext();
} else {
alert("Not supported by your browser");
$("#load").dialog("close");
return;
}
var fileReader = new FileReader();
fileReader.onload = function(ev) {
context.decodeAudioData(ev.target.result, function(buffer) {
var buf = [];
var data = buffer.getChannelData(0), datum = data[0];
var old = (datum > 0.0), current;
var last = 0, delta, ival;
debug('Sample Count: ' + data.length);
debug('Sample rate: ' + buffer.sampleRate);
for (var idx = 1; idx < data.length; idx++) {
datum = data[idx];
if ((datum > 0.1) || (datum < -0.1)) {
current = (datum > 0.0);
if (current != old) {
delta = idx - last;
if (delta > 2000000) {
delta = 2000000;
}
ival = delta / buffer.sampleRate * 1000;
if (ival >= 0.550 && ival < 0.750) {
ival = 0.650; // Header
} else if (ival >= 0.175 && ival < 0.225) {
ival = 0.200; // sync 1
} else if (ival >= 0.225 && ival < 0.275) {
ival = 0.250; // 0 / sync 2
} else if (ival >= 0.450 && ival < 0.550) {
ival = 0.500; // 1
} else {
// debug(idx + ' ' + buf.length + ' ' + ival);
}
buf.push(parseInt(ival * kHz));
old = current;
last = idx;
}
}
}
io.setTape(buf);
$("#load").dialog("close");
});
};
fileReader.readAsArrayBuffer(file);
}
function openManage() {
$("#manage").dialog("open");
}
function loadHTTP(url) {
loading = true;
$("#loading").dialog("open");
var req = new XMLHttpRequest();
req.open("GET", url, true);
req.responseType = "arraybuffer";
req.onload = function(event) {
var parts = url.split(/[\/\.]/);
var name = decodeURIComponent(parts[parts.length - 2]);
var ext = parts[parts.length - 1].toLowerCase();
if (disk2.setBinary(_saveDrive, name, ext, req.response)) {
$("#disklabel" + _saveDrive).text(name);
$("#loading").dialog("close");
loading = false;
}
}
req.send(null);
}
var prefs = new Prefs();
var runTimer = null;
var cpu = new CPU6502();
var ram1 = new RAM(0x00, 0x03),
ram2 = new RAM(0x0C, 0x1F),
ram3 = new RAM(0x60, 0xBF);
var hgr = new HiresPage(1);
var hgr2 = new HiresPage(2);
var gr = new LoresPage(1);
var gr2 = new LoresPage(2);
var rom = new Apple2ROM();
var vm = new VideoModes(gr, hgr, gr2, hgr2);
var mmu = {
auxRom: function(slot, rom) {
cpu.addPageHandler(rom);
}
};
var drivelights = new DriveLights();
var io = new Apple2IO(cpu, vm);
var keyboard = new KeyBoard(io);
var parallel = new Parallel(io, new Printer(), 1);
var disk2 = new DiskII(io, drivelights, 6);
var slinky = new RAMFactor(mmu, io, 2, 1024 * 1024);
var clock = new Thunderclock(mmu, io, 7);
var lc = new LanguageCard(io, rom);
cpu.addPageHandler(ram1);
cpu.addPageHandler(gr);
cpu.addPageHandler(gr2);
cpu.addPageHandler(ram2);
cpu.addPageHandler(hgr);
cpu.addPageHandler(hgr2);
cpu.addPageHandler(ram3);
cpu.addPageHandler(lc);
cpu.addPageHandler(io);
cpu.addPageHandler(parallel);
cpu.addPageHandler(slinky);
cpu.addPageHandler(disk2);
cpu.addPageHandler(clock);
var showFPS = false;
function updateKHz() {
var now = Date.now();
var ms = now - startTime;
var cycles = cpu.cycles();
var delta;
if (showFPS) {
delta = frames - lastFrames;
var fps = parseInt(delta/(ms/1000), 10);
$("#khz").html( fps + "fps");
} else {
delta = cycles - lastCycles;
khz = parseInt(delta/ms);
$("#khz").html( khz + "KHz");
}
startTime = now;
lastCycles = cycles;
lastFrames = frames;
}
/* Audio Handling */
initAudio(io);
function updateSound()
{
enableSound($("#enable_sound").attr("checked"));
}
function dumpDisk(drive) {
var wind = window.open("", "_blank");
wind.document.title = $("#disklabel" + drive).text();
wind.document.write("<pre>");
wind.document.write(disk2.getJSON(drive, true));
wind.document.write("</pre>");
wind.document.close();
}
function step()
{
if (runTimer) {
clearInterval(runTimer);
}
runTimer = null;
cpu.step(function() {
debug(cpu.dumpRegisters());
debug(cpu.dumpPC());
});
}
var running = false;
var accelerated = false;
function updateSpeed()
{
accelerated = $("#accelerator_toggle").prop("checked");
kHz = accelerated ? 4092 : 1023;
io.updateHz(kHz * 1000);
if (runTimer) {
run();
}
}
var _requestAnimationFrame =
window.requestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.msRequestAnimationFrame;
function run(pc) {
if (runTimer) {
clearInterval(runTimer);
}
if (pc) {
cpu.setPC(pc);
}
var ival = 30;
var now, last = Date.now();
var runFn = function() {
now = Date.now();
frames++;
var step = (now - last) * kHz, stepMax = kHz * ival;
last = now;
if (step > stepMax) {
step = stepMax;
}
if (document.location.hash != hashtag) {
hashtag = document.location.hash;
filename = hup()
if (filename) {
if (filename.indexOf("://") > 0) {
loadHTTP(disk);
} else {
loadAjax("json/disks/" + filename + ".json");
}
}
}
if (!loading) {
running = true;
cpu.stepCycles(step);
running = false;
vm.blit();
io.sampleTick();
}
processGamepad(io);
if (!paused && _requestAnimationFrame) {
_requestAnimationFrame(runFn);
}
};
if (_requestAnimationFrame) {
_requestAnimationFrame(runFn);
} else {
runTimer = setInterval(runFn, ival);
}
}
function stop() {
if (runTimer) {
clearInterval(runTimer);
}
runTimer = null;
}
function reset()
{
cpu.reset();
}
function loadBinary(bin) {
stop();
for (var idx = 0; idx < bin.length; idx++) {
var pos = bin.start + idx;
cpu.write(pos >> 8, pos & 0xff, bin.data[idx]);
}
run(bin.start);
}
function selectCategory(event) {
$("#disk_select").empty();
var cat = disk_categories[$("#category_select").val()];
if (cat) {
for (var idx = 0; idx < cat.length; idx++) {
var file = cat[idx], name = file.name;
if (file.disk) {
name += " - " + file.disk;
}
var option = $("<option />").val(file.filename).text(name)
.appendTo("#disk_select");
if (disk_cur_name[_loadDrive] == name) {
option.attr("selected", "selected");
}
}
}
}
function selectDisk(event) {
// Maybe display some info.
}
function clickDisk(event) {
doLoad();
}
function loadDisk(data) {
var name = data.name;
var category = data.category;
if (data.disk) {
name += " - " + data.disk;
}
disk_cur_cat[_loadDrive] = category;
disk_cur_name[_loadDrive] = name;
$("#disklabel" + _loadDrive).text(name);
disk2.setDisk(_loadDrive, data);
initGamepad(data.gamepad);
}
function loadJSON(data) {
if (data.type == "binary") {
loadBinary(data);
} else if ($.inArray(data.type, DISK_TYPES) >= 0) {
loadDisk(data);
}
initGamepad(data.gamepad);
$("#loading").dialog("close");
loading = false;
}
/*
* LocalStorage Disk Storage
*/
function updateLocalStorage() {
var diskIndex = JSON.parse(window.localStorage.diskIndex || "{}");
var names = [], name, cat;
for (name in diskIndex) {
if (diskIndex.hasOwnProperty(name)) {
names.push(name);
}
}
cat = disk_categories['Local Saves'] = [];
$("#manage").empty();
names.forEach(function(name) {
cat.push({'category': 'Local Saves',
'name': name,
'filename': 'local:' + name});
$("#manage").append("<span class='local_save'>" +
name +
" <a href='#' onclick='doDelete(\"" +
name +
"\")'>Delete</a><br /></span>");
});
cat.push({'category': 'Local Saves',
'name': 'Manage Saves...',
'filename': 'local:__manage'});
}
function saveLocalStorage(drive, name) {
var diskIndex = JSON.parse(window.localStorage.diskIndex || "{}");
var json = disk2.getJSON(drive);
diskIndex[name] = json;
window.localStorage.diskIndex = JSON.stringify(diskIndex);
window.alert("Saved");
drivelights.label(drive, name);
drivelights.dirty(drive, false);
updateLocalStorage();
}
function deleteLocalStorage(name) {
var diskIndex = JSON.parse(window.localStorage.diskIndex || "{}");
if (diskIndex[name]) {
delete diskIndex[name];
window.alert("Deleted");
}
window.localStorage.diskIndex = JSON.stringify(diskIndex);
updateLocalStorage();
}
function loadLocalStorage(drive, name) {
var diskIndex = JSON.parse(window.localStorage.diskIndex || "{}");
if (diskIndex[name]) {
disk2.setJSON(drive, diskIndex[name]);
drivelights.label(drive, name);
drivelights.dirty(drive, false);
}
}
/*
* Keyboard/Gamepad routines
*/
var _key;
function _keydown(evt) {
if (evt.keyCode === 112) { // F1 - Reset
cpu.reset();
} else if (evt.keyCode === 113) { // F2 - Full Screen
var elem = document.getElementById("screen");
if (document.webkitCancelFullScreen) {
if (document.webkitIsFullScreen) {
document.webkitCancelFullScreen();
} else {
if (Element.ALLOW_KEYBOARD_INPUT) {
elem.webkitRequestFullScreen(Element.ALLOW_KEYBOARD_INPUT);
} else {
elem.webkitRequestFullScreen();
}
}
} else if (document.mozCancelFullScreen) {
if (document.mozIsFullScreen) {
document.mozCancelFullScreen();
} else {
elem.mozRequestFullScreen();
}
}
} else if (evt.keyCode == 16) { // Shift
keyboard.shiftKey(true);
io.buttonDown(2);
} else if (evt.keyCode == 17) { // Control
keyboard.controlKey(true);
} else if (!focused && (!evt.metaKey || evt.ctrlKey)) {
evt.preventDefault();
var key = keyboard.mapKeyEvent(evt);
if (key != 0xff) {
io.keyDown(key);
_key = key;
}
}
}
function _keyup(evt) {
_key = 0xff;
if (evt.keyCode == 16) { // Shift
keyboard.shiftKey(false);
io.buttonUp(2);
} else if (evt.keyCode == 17) { // Control
keyboard.controlKey(false);
} else {
if (!focused) {
io.keyUp();
}
}
}
function updateScreen() {
var green = $("#green_screen").prop("checked");
scanlines = $("#show_scanlines").prop("checked");
vm.green(green);
}
var flipX = false;
var flipY = false;
var swapXY = false;
function updateJoystick() {
flipX = $("#flip_x").prop("checked");
flipY = $("#flip_y").prop("checked");
swapXY = $("#swap_x_y").prop("checked");
}
function _mousemove(evt) {
if (gamepad) {
return;
}
var s = $("#screen");
var offset = s.offset();
var x = (evt.pageX - offset.left) / s.width(),
y = (evt.pageY - offset.top) / s.height(),
z = x;
if (swapXY) {
x = y;
y = z;
}
io.paddle(0, flipX ? 1 - x : x);
io.paddle(1, flipY ? 1 - y : y);
}
var paused = false;
function pauseRun(b) {
if (paused) {
run();
b.value = "Pause";
} else {
stop();
b.value = "Run";
}
paused = !paused;
}
$(function() {
hashtag = document.location.hash;
$("button,input[type=button],a.button").button().focus(function() {
// Crazy hack required by Chrome
var self = this;
window.setTimeout(function() {
self.blur();
}, 1)
});
var canvas = document.getElementById("screen");
var context = canvas.getContext('2d');
vm.setContext(context);
/*
* Input Handling
*/
$(window).keydown(_keydown);
$(window).keyup(_keyup);
$("canvas").mousedown(function(evt) {
if (!gamepad) {
io.buttonDown(evt.which == 1 ? 0 : 1);
}
evt.preventDefault();
})
.mouseup(function(evt) {
if (!gamepad) {
io.buttonUp(evt.which == 1 ? 0 : 1);
}
})
.bind("contextmenu", function(e) { e.preventDefault(); });
$('body').mousemove(_mousemove);
$("input,textarea").focus(function() { focused = true; });
$("input,textarea").blur(function() { focused = false; });
keyboard.create($("#keyboard"));
if (prefs.havePrefs()) {
$("input[type=checkbox]").each(function() {
var val = prefs.readPref(this.id);
if (val != null)
this.checked = JSON.parse(val);
});
$("input[type=checkbox]").change(function() {
prefs.writePref(this.id, JSON.stringify(this.checked));
});
}
reset();
run();
setInterval(updateKHz, 1000);
updateSound();
updateScreen();
updateSpeed();
var cancel = function() { $(this).dialog("close"); };
$("#loading").dialog({ autoOpen: false, modal: true });
$("#options").dialog({ autoOpen: false,
modal: true,
width: 320,
height: 400,
buttons: {"Close": cancel }});
$("#load").dialog({ autoOpen: false,
modal: true,
width: 540,
buttons: {"Cancel": cancel, "Load": doLoad }});
$("#save").dialog({ autoOpen: false,
modal: true,
width: 320,
buttons: {"Cancel": cancel, "Save": doSave }});
$("#manage").dialog({ autoOpen: false,
modal: true,
width: 320,
buttons: {"Close": cancel }});
if (window.localStorage !== undefined) {
$("button.disksave").show();
}
var oldcat = "";
for (var idx = 0; idx < disk_index.length; idx++) {
var file = disk_index[idx];
var cat = file.category;
var name = file.name, disk = file.disk;
if (cat != oldcat) {
$("<option />").val(cat).text(cat).appendTo("#category_select");
disk_categories[cat] = [];
oldcat = cat;
}
disk_categories[cat].push(file);
if (disk) {
if (!disk_sets[name]) {
disk_sets[name] = []
}
disk_sets[name].push(file);
}
}
$("<option/>").text("Local Saves").appendTo("#category_select");
updateLocalStorage();
initGamepad();
// Check for disks in hashtag
var filename = gup("disk") || hup();
if (filename) {
if (filename.indexOf("://") > 0) {
loadHTTP(filename);
} else {
loadAjax("json/disks/" + filename + ".json");
}
}
if (navigator.userAgent.match(/(iPod|iPhone|iPad)/)) {
$("select").removeAttr("multiple").css("height", "auto");
}
});
</script>
</head>
<body>
<div style="margin: auto; width: 604px">

View File

@ -1,6 +1,6 @@
<!DOCTYPE html><!-- -*- mode: HTML; indent-tabs-mode: nil -*- -->
<!--
Copyright 2010-2013 Will Scullin <scullin@scullinsteel.com>
Copyright 2010-2016 Will Scullin <scullin@scullinsteel.com>
Permission to use, copy, modify, distribute, and sell this software and its
documentation for any purpose is hereby granted without fee, provided that
@ -51,6 +51,7 @@
<script type="text/javascript" src="js/apple2e.js"></script>
<script type="text/javascript" src="js/apple2e-enhanced.js"></script>
<script type="text/javascript" src="js/apple2echar.js"></script>
<script type="text/javascript" src="js/applesoft/decompiler.js"></script>
<script type="text/javascript" src="js/canvas2e.js"></script>
<script type="text/javascript" src="js/slot3.js"></script>
<script type="text/javascript" src="js/apple2io.js"></script>
@ -68,774 +69,7 @@
<!-- Disk Index -->
<script type="text/javascript" src="json/disks/index.js"></script>
<script type="text/javascript">
var enhanced = true;
var focused = false;
var startTime = Date.now();
var lastCycles = 0;
var frames = 0, lastFrames = 0;
var paused = false;
var sound = true;
var hashtag;
var disk_categories = {'Local Saves': []};
var disk_sets = {}
var disk_cur_name = [];
var disk_cur_cat = [];
function DriveLights()
{
return {
driveLight: function(drive, on) {
$("#disk" + drive).css("background-image",
on ? "url(css/red-on-16.png)" :
"url(css/red-off-16.png)");
},
dirty: function(drive, dirty) {
},
label: function(drive, label) {
$("#disklabel" + drive).text(label);
}
}
}
var DISK_TYPES = ['dsk','do','po','raw','nib','2mg'];
var TAPE_TYPES = ['wav','aiff','aif','mp3'];
var _saveDrive = 1;
var _loadDrive = 1;
function openLoad(drive, event)
{
_loadDrive = drive;
if (disk_cur_cat[drive]) {
$("#category_select").val(disk_cur_cat[drive]).change();
}
$("#load").dialog("open");
}
function openSave(drive, event)
{
_saveDrive = drive;
$("#save_name").val($("#disklabel" + drive).text());
$("#save").dialog("open");
}
var loading = false;
function loadAjax(url) {
loading = true;
$("#loading").dialog("open");
$.ajax({ url: url,
cache: false,
dataType: "jsonp",
jsonp: false,
global: false
});
}
function doLoad() {
var urls = $("#disk_select").val(), url;
if (urls && urls.length) {
if (typeof(urls) == "string") {
url = urls;
} else {
url = urls[0];
}
}
var files = $("#local_file").prop("files");
if (files.length == 1) {
doLoadLocal();
} else if (url) {
var filename;
$("#load").dialog("close");
if (url.substr(0,6) == "local:") {
filename = url.substr(6);
if (filename == "__manage") {
openManage();
} else {
loadLocalStorage(_loadDrive, filename);
}
} else {
var r1 = /json\/disks\/(.*).json$/.exec(url);
if (r1 && _loadDrive == 1) {
filename = r1[1];
document.location.hash = filename;
}
loadAjax(url);
}
}
}
function doSave() {
var name = $("#save_name").val();
saveLocalStorage(_saveDrive, name);
$("#save").dialog("close");
}
function doDelete(name) {
if (confirm("Delete " + name + "?")) {
deleteLocalStorage(name);
}
}
function doLoadLocal() {
var files = $("#local_file").prop("files")
if (files.length == 1) {
var file = files[0];
var parts = file.name.split(".");
var ext = parts[parts.length - 1].toLowerCase();
if ($.inArray(ext, DISK_TYPES) >= 0) {
doLoadLocalDisk(file);
} else if ($.inArray(ext, TAPE_TYPES) >= 0) {
doLoadLocalTape(file);
} else {
alert('Unknown file type: ' + ext);
$("#load").dialog("close");
}
}
}
function doLoadLocalDisk(file) {
var fileReader = new FileReader();
fileReader.onload = function(event) {
var parts = file.name.split(".");
var name = parts[0], ext = parts[parts.length - 1].toLowerCase();
if (disk2.setBinary(_saveDrive, name, ext, this.result)) {
$("#disklabel" + _saveDrive).text(name);
$("#load").dialog("close");
initGamepad();
}
}
fileReader.readAsArrayBuffer(file);
}
function doLoadLocalTape(file) {
// Audio Buffer Source
if (typeof webkitAudioContext != "undefined") {
var context = new webkitAudioContext();
} else {
alert("Not supported by your browser");
$("#load").dialog("close");
return;
}
var fileReader = new FileReader();
fileReader.onload = function(ev) {
context.decodeAudioData(ev.target.result, function(buffer) {
var buf = [];
var data = buffer.getChannelData(0), datum = data[0];
var old = (datum > 0.0), current;
var last = 0, delta, ival;
debug('Sample Count: ' + data.length);
debug('Sample rate: ' + buffer.sampleRate);
for (var idx = 1; idx < data.length; idx++) {
datum = data[idx];
if ((datum > 0.1) || (datum < -0.1)) {
current = (datum > 0.0);
if (current != old) {
delta = idx - last;
if (delta > 2000000) {
delta = 2000000;
}
ival = delta / buffer.sampleRate * 1000;
if (ival >= 0.550 && ival < 0.750) {
ival = 0.650; // Header
} else if (ival >= 0.175 && ival < 0.225) {
ival = 0.200; // sync 1
} else if (ival >= 0.225 && ival < 0.275) {
ival = 0.250; // 0 / sync 2
} else if (ival >= 0.450 && ival < 0.550) {
ival = 0.500; // 1
} else {
// debug(idx + ' ' + buf.length + ' ' + ival);
}
buf.push(parseInt(ival * kHz));
old = current;
last = idx;
}
}
}
io.setTape(buf);
$("#load").dialog("close");
});
};
fileReader.readAsArrayBuffer(file);
}
function openManage() {
$("#manage").dialog("open");
}
function loadHTTP(url) {
loading = true;
$("#loading").dialog("open");
var req = new XMLHttpRequest();
req.open("GET", url, true);
req.responseType = "arraybuffer";
req.onload = function(event) {
var parts = url.split(/[\/\.]/);
var name = decodeURIComponent(parts[parts.length - 2]);
var ext = parts[parts.length - 1].toLowerCase();
if (disk2.setBinary(_saveDrive, name, ext, req.response)) {
$("#disklabel" + _saveDrive).text(name);
$("#loading").dialog("close");
loading = false;
}
}
req.send(null);
}
var prefs = new Prefs();
var runTimer = null;
var cpu = new CPU6502({"65C02": enhanced});
var hgr = new HiresPage(1);
var hgr2 = new HiresPage(2);
var gr = new LoresPage(1);
var gr2 = new LoresPage(2);
var rom;
if (enhanced) {
rom = new Apple2eEnhancedROM();
} else {
rom = new Apple2eROM();
}
var vm = new VideoModes(gr, hgr, gr2, hgr2);
var drivelights = new DriveLights();
var io = new Apple2IO(cpu, vm);
var keyboard = new KeyBoard(io);
var mmu = new MMU(cpu, gr, gr2, hgr, hgr2, io, rom);
var parallel = new Parallel(io, new Printer(), 1);
var disk2 = new DiskII(io, drivelights, 6);
var slot3 = new Slot3(mmu, rom);
var slinky = new RAMFactor(mmu, io, 2, 1024 * 1024);
var clock = new Thunderclock(mmu, io, 7);
mmu.addSlot(1, parallel);
mmu.addSlot(2, slinky);
mmu.addSlot(3, slot3);
mmu.addSlot(6, disk2);
mmu.addSlot(7, clock);
cpu.addPageHandler(mmu);
var showFPS = false;
function updateKHz() {
var now = Date.now();
var ms = now - startTime;
var cycles = cpu.cycles();
var delta;
if (showFPS) {
delta = frames - lastFrames;
var fps = parseInt(delta/(ms/1000), 10);
$("#khz").html( fps + "fps");
} else {
delta = cycles - lastCycles;
khz = parseInt(delta/ms);
$("#khz").html( khz + "KHz");
}
startTime = now;
lastCycles = cycles;
lastFrames = frames;
}
/* Audio Handling */
initAudio(io);
function updateSound()
{
enableSound($("#enable_sound").attr("checked"));
}
function dumpDisk(drive) {
var wind = window.open("", "_blank");
wind.document.title = $("#disklabel" + drive).text();
wind.document.write("<pre>");
wind.document.write(disk2.getJSON(drive, true));
wind.document.write("</pre>");
wind.document.close();
}
function step()
{
if (runTimer) {
clearInterval(runTimer);
}
runTimer = null;
cpu.step(function() {
debug(cpu.dumpRegisters());
debug(cpu.dumpPC());
});
}
var running = false;
var accelerated = false;
function updateSpeed()
{
accelerated = $("#accelerator_toggle").prop("checked");
kHz = accelerated ? 4092 : 1023;
io.updateHz(kHz * 1000);
if (runTimer) {
run();
}
}
var _requestAnimationFrame =
window.requestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.msRequestAnimationFrame;
function run(pc) {
if (runTimer) {
clearInterval(runTimer);
}
if (pc) {
cpu.setPC(pc);
}
var ival = 30;
var now, last = Date.now();
var runFn = function() {
now = Date.now();
frames++;
var step = (now - last) * kHz, stepMax = kHz * ival;
last = now;
if (step > stepMax) {
step = stepMax;
}
if (document.location.hash != hashtag) {
hashtag = document.location.hash;
filename = hup()
if (filename) {
if (filename.indexOf("://") > 0) {
loadHTTP(disk);
} else {
loadAjax("json/disks/" + filename + ".json");
}
}
}
if (!loading) {
mmu.resetVB();
running = true;
cpu.stepCycles(step);
running = false;
vm.blit();
io.sampleTick();
}
processGamepad(io);
if (!paused && _requestAnimationFrame) {
_requestAnimationFrame(runFn);
}
};
if (_requestAnimationFrame) {
_requestAnimationFrame(runFn);
} else {
runTimer = setInterval(runFn, ival);
}
}
function stop() {
if (runTimer) {
clearInterval(runTimer);
}
runTimer = null;
}
function reset()
{
cpu.reset();
}
function loadBinary(bin) {
stop();
for (var idx = 0; idx < bin.length; idx++) {
var pos = bin.start + idx;
cpu.write(pos >> 8, pos & 0xff, bin.data[idx]);
}
run(bin.start);
}
function selectCategory(event) {
$("#disk_select").empty();
var cat = disk_categories[$("#category_select").val()];
if (cat) {
for (var idx = 0; idx < cat.length; idx++) {
var file = cat[idx], name = file.name;
if (file.disk) {
name += " - " + file.disk;
}
var option = $("<option />").val(file.filename).text(name)
.appendTo("#disk_select");
if (disk_cur_name[_loadDrive] == name) {
option.attr("selected", "selected");
}
}
}
}
function selectDisk(event) {
// Maybe display some info.
}
function clickDisk(event) {
doLoad();
}
function loadDisk(data) {
var name = data.name;
var category = data.category;
if (data.disk) {
name += " - " + data.disk;
}
disk_cur_cat[_loadDrive] = category;
disk_cur_name[_loadDrive] = name;
$("#disklabel" + _loadDrive).text(name);
disk2.setDisk(_loadDrive, data);
initGamepad(data.gamepad);
}
function loadJSON(data) {
if (data.type == "binary") {
loadBinary(data);
} else if ($.inArray(data.type, DISK_TYPES) >= 0) {
loadDisk(data);
}
initGamepad(data.gamepad);
$("#loading").dialog("close");
loading = false;
}
/*
* LocalStorage Disk Storage
*/
function updateLocalStorage() {
var diskIndex = JSON.parse(window.localStorage.diskIndex || "{}");
var names = [], name, cat;
for (name in diskIndex) {
if (diskIndex.hasOwnProperty(name)) {
names.push(name);
}
}
cat = disk_categories['Local Saves'] = [];
$("#manage").empty();
names.forEach(function(name) {
cat.push({'category': 'Local Saves',
'name': name,
'filename': 'local:' + name});
$("#manage").append("<span class='local_save'>" +
name +
" <a href='#' onclick='doDelete(\"" +
name +
"\")'>Delete</a><br /></span>");
});
cat.push({'category': 'Local Saves',
'name': 'Manage Saves...',
'filename': 'local:__manage'});
}
function saveLocalStorage(drive, name) {
var diskIndex = JSON.parse(window.localStorage.diskIndex || "{}");
var json = disk2.getJSON(drive);
diskIndex[name] = json;
window.localStorage.diskIndex = JSON.stringify(diskIndex);
window.alert("Saved");
drivelights.label(drive, name);
drivelights.dirty(drive, false);
updateLocalStorage();
}
function deleteLocalStorage(name) {
var diskIndex = JSON.parse(window.localStorage.diskIndex || "{}");
if (diskIndex[name]) {
delete diskIndex[name];
window.alert("Deleted");
}
window.localStorage.diskIndex = JSON.stringify(diskIndex);
updateLocalStorage();
}
function loadLocalStorage(drive, name) {
var diskIndex = JSON.parse(window.localStorage.diskIndex || "{}");
if (diskIndex[name]) {
disk2.setJSON(drive, diskIndex[name]);
drivelights.label(drive, name);
drivelights.dirty(drive, false);
}
}
/*
* Keyboard/Gamepad routines
*/
var _key;
function _keydown(evt) {
if (evt.keyCode === 112) { // F1 - Reset
cpu.reset();
} else if (evt.keyCode === 113) { // F2 - Full Screen
var elem = document.getElementById("screen");
if (document.webkitCancelFullScreen) {
if (document.webkitIsFullScreen) {
document.webkitCancelFullScreen();
} else {
if (Element.ALLOW_KEYBOARD_INPUT) {
elem.webkitRequestFullScreen(Element.ALLOW_KEYBOARD_INPUT);
} else {
elem.webkitRequestFullScreen();
}
}
} else if (document.mozCancelFullScreen) {
if (document.mozIsFullScreen) {
document.mozCancelFullScreen();
} else {
elem.mozRequestFullScreen();
}
}
} else if (evt.keyCode == 16) { // Shift
keyboard.shiftKey(true);
io.buttonDown(2);
} else if (evt.keyCode == 17) { // Control
keyboard.controlKey(true);
} else if (evt.keyCode == 91 || evt.keyCode == 93) { // Command
keyboard.commandKey(true);
} else if (evt.keyCode == 18) { // Alt
keyboard.optionKey(true);
} else if (!focused) {
evt.preventDefault();
var key = keyboard.mapKeyEvent(evt);
if (key != 0xff) {
io.keyDown(key);
_key = key;
}
}
}
function _keyup(evt) {
_key = 0xff;
if (evt.keyCode == 16) { // Shift
keyboard.shiftKey(false);
io.buttonUp(2);
} else if (evt.keyCode == 17) { // Control
keyboard.controlKey(false);
} else if (evt.keyCode == 91 || evt.keyCode == 93) { // Command
keyboard.commandKey(false);
} else if (evt.keyCode == 18) { // Alt
keyboard.optionKey(false);
} else {
if (!focused) {
io.keyUp();
}
}
}
function updateScreen() {
var green = $("#green_screen").prop("checked");
scanlines = $("#show_scanlines").prop("checked");
vm.green(green);
}
var flipX = false;
var flipY = false;
var swapXY = false;
function updateJoystick() {
flipX = $("#flip_x").prop("checked");
flipY = $("#flip_y").prop("checked");
swapXY = $("#swap_x_y").prop("checked");
}
function _mousemove(evt) {
if (gamepad) {
return;
}
var s = $("#screen");
var offset = s.offset();
var x = (evt.pageX - offset.left) / s.width(),
y = (evt.pageY - offset.top) / s.height(),
z = x;
if (swapXY) {
x = y;
y = z;
}
io.paddle(0, flipX ? 1 - x : x);
io.paddle(1, flipY ? 1 - y : y);
}
var paused = false;
function pauseRun(b) {
if (paused) {
run();
b.value = "Pause";
} else {
stop();
b.value = "Run";
}
paused = !paused;
}
$(function() {
hashtag = document.location.hash;
$("button,input[type=button],a.button").button().focus(function() {
// Crazy hack required by Chrome
var self = this;
window.setTimeout(function() {
self.blur();
}, 1)
});
var canvas = document.getElementById("screen");
var context = canvas.getContext('2d');
vm.setContext(context);
/*
* Input Handling
*/
$(window).keydown(_keydown);
$(window).keyup(_keyup);
$("canvas").mousedown(function(evt) {
if (!gamepad) {
io.buttonDown(evt.which == 1 ? 0 : 1);
}
evt.preventDefault();
})
.mouseup(function(evt) {
if (!gamepad) {
io.buttonUp(evt.which == 1 ? 0 : 1);
}
})
.mousemove(_mousemove)
.bind("contextmenu", function(e) { e.preventDefault(); });
$("input,textarea").focus(function() { focused = true; })
.blur(function() { focused = false; });
$("body > div").hover(function() { focused = false; },
function() { focused = true; });
keyboard.create($("#keyboard"));
if (prefs.havePrefs()) {
$("input[type=checkbox]").each(function() {
var val = prefs.readPref(this.id);
if (val != null)
this.checked = JSON.parse(val);
});
$("input[type=checkbox]").change(function() {
prefs.writePref(this.id, JSON.stringify(this.checked));
});
}
reset();
run();
setInterval(updateKHz, 1000);
updateSound();
updateScreen();
updateSpeed();
var cancel = function() { $(this).dialog("close"); };
$("#loading").dialog({ autoOpen: false, modal: true });
$("#options").dialog({ autoOpen: false,
modal: true,
width: 320,
height: 400,
buttons: {"Close": cancel }});
$("#load").dialog({ autoOpen: false,
modal: true,
width: 540,
buttons: {"Cancel": cancel, "Load": doLoad }});
$("#save").dialog({ autoOpen: false,
modal: true,
width: 320,
buttons: {"Cancel": cancel, "Save": doSave }});
$("#manage").dialog({ autoOpen: false,
modal: true,
width: 320,
buttons: {"Close": cancel }});
if (window.localStorage !== undefined) {
$("button.disksave").show();
}
var oldcat = "";
for (var idx = 0; idx < disk_index.length; idx++) {
var file = disk_index[idx];
var cat = file.category;
var name = file.name, disk = file.disk;
if (cat != oldcat) {
$("<option />").val(cat).text(cat).appendTo("#category_select");
disk_categories[cat] = [];
oldcat = cat;
}
disk_categories[cat].push(file);
if (disk) {
if (!disk_sets[name]) {
disk_sets[name] = []
}
disk_sets[name].push(file);
}
}
$("<option/>").text("Local Saves").appendTo("#category_select");
updateLocalStorage();
// Check for disks in hashtag
var filename = gup("disk") || hup();
if (filename) {
if (filename.indexOf("://") > 0) {
loadHTTP(filename);
} else {
loadAjax("json/disks/" + filename + ".json");
}
}
if (navigator.userAgent.match(/(iPod|iPhone|iPad)/)) {
$("select").removeAttr("multiple").css("height", "auto");
}
});
</script>
<script type="text/javascript" src="js/main2e.js"></script>
</head>
<body class="apple2e">
<div style="margin: auto; width: 604px">

View File

@ -1,4 +1,4 @@
/* Copyright 2010-2013 Will Scullin */
/* Copyright 2010-2016 Will Scullin */
.disklabel {
color: #000;

View File

@ -1,9 +1,6 @@
/* -*- mode: JavaScript; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*exported apple2_charset */
/*global bytify */
/*exported charset */
var charset = [
var apple2_charset = [
0x00,0x1c,0x22,0x2a,0x2e,0x2c,0x20,0x1e,
0x00,0x08,0x14,0x22,0x22,0x3e,0x22,0x22,
0x00,0x3c,0x22,0x22,0x3c,0x22,0x22,0x3c,
@ -260,6 +257,4 @@ var charset = [
0x80,0x80,0x80,0xbe,0x80,0xbe,0x80,0x80,
0x80,0x90,0x88,0x84,0x82,0x84,0x88,0x90,
0x80,0x9c,0xa2,0x84,0x88,0x88,0x80,0x88
];
charset = bytify(charset);
];

View File

@ -1,6 +1,3 @@
/* -*- mode: JavaScript; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*global bytify:false */
/*exported Apple2eEnhancedROM */
function Apple2eEnhancedROM()
@ -2056,8 +2053,6 @@ function Apple2eEnhancedROM()
0xf5,0x03,0xfb,0x03,0x62,0xfa,0xfa,0xc3
];
rom = bytify(rom);
return {
start: function apple2e_start() {
return 0xc1;

View File

@ -1,5 +1,3 @@
/* -*- mode: JavaScript; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*global bytify:false */
/*exported Apple2eROM*/
function Apple2eROM()
@ -2057,8 +2055,6 @@ function Apple2eROM()
0xf5,0x03,0xfb,0x03,0x62,0xfa,0x40,0xfa
];
rom = bytify(rom);
return {
start: function apple2e_start() {
return 0xc1;

View File

@ -1,9 +1,6 @@
/* -*- mode: JavaScript; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*exported apple2e_charset */
/*global bytify */
/*exported charset */
var charset = [
var apple2e_charset = [
0x1c,0x22,0x2a,0x3a,0x1a,0x02,0x3c,0x00,
0x08,0x14,0x22,0x22,0x3e,0x22,0x22,0x00,
0x1e,0x22,0x22,0x1e,0x22,0x22,0x1e,0x00,
@ -516,6 +513,4 @@ var charset = [
0x82,0x82,0x22,0x88,0x82,0x82,0x00,0x00,
0x81,0x81,0x11,0x44,0x81,0x81,0x00,0x00,
0x80,0x80,0x00,0x00,0x80,0x80,0x00,0x00
];
charset = bytify(charset);
];

View File

@ -1,5 +1,4 @@
/* -*- mode: JavaScript; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* Copyright 2010-2014 Will Scullin <scullin@scullinsteel.com>
/* Copyright 2010-2016 Will Scullin <scullin@scullinsteel.com>
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
@ -15,7 +14,7 @@
function Apple2IO(cpu, callbacks)
{
"use strict";
'use strict';
var _hz = 1023000;
var _rate = 44000;
@ -116,43 +115,43 @@ function Apple2IO(cpu, callbacks)
var delta = now - _trigger;
switch (off) {
case LOC.CLR80VID:
// _debug("80 Column Mode off");
// _debug('80 Column Mode off');
if ('_80col' in callbacks) callbacks._80col(false);
break;
case LOC.SET80VID:
// _debug("80 Column Mode on");
// _debug('80 Column Mode on');
if ('_80col' in callbacks) callbacks._80col(true);
break;
case LOC.CLRALTCH:
// _debug("Alt Char off");
// _debug('Alt Char off');
if ('altchar' in callbacks) callbacks.altchar(false);
break;
case LOC.SETALTCH:
// _debug("Alt Char on");
// _debug('Alt Char on');
if ('altchar' in callbacks) callbacks.altchar(true);
break;
case LOC.CLRTEXT:
_debug("Graphics Mode");
_debug('Graphics Mode');
callbacks.text(false);
break;
case LOC.SETTEXT:
_debug("Text Mode");
_debug('Text Mode');
callbacks.text(true);
break;
case LOC.CLRMIXED:
_debug("Mixed Mode off");
_debug('Mixed Mode off');
callbacks.mixed(false);
break;
case LOC.SETMIXED:
_debug("Mixed Mode on");
_debug('Mixed Mode on');
callbacks.mixed(true);
break;
case LOC.CLRHIRES:
_debug("LoRes Mode");
_debug('LoRes Mode');
callbacks.hires(false);
break;
case LOC.SETHIRES:
_debug("HiRes Mode");
_debug('HiRes Mode');
callbacks.hires(true);
break;
case LOC.PAGE1:
@ -262,7 +261,7 @@ function Apple2IO(cpu, callbacks)
_trigger = cpu.cycles();
break;
case LOC.TAPEIN:
var flipped = false;
// var flipped = false;
if (_tapeOffset == -1) {
_tapeOffset = 0;
_tapeNext = now;
@ -270,17 +269,17 @@ function Apple2IO(cpu, callbacks)
if (_tapeOffset < _tape.length) {
while (now >= _tapeNext) {
if ((_tapeOffset % 1000) === 0) {
debug("Read " + (_tapeOffset / 1000));
debug('Read ' + (_tapeOffset / 1000));
}
_tapeFlip = !_tapeFlip;
flipped = true;
// flipped = true;
_tapeNext += _tape[_tapeOffset++];
}
result = _tapeFlip ? 0x80 : 0x00;
}
/*
if (flipped) {
debug("now=" + now + " next=" + _tapeNext + " (" + (_tapeNext - now) + ")");
debug('now=' + now + ' next=' + _tapeNext + ' (' + (_tapeNext - now) + ')');
}
*/
@ -319,7 +318,7 @@ function Apple2IO(cpu, callbacks)
if (_locs[off]) {
result = _locs[off].ioSwitch(off);
} else {
debug("I/O read: C0" + toHex(off));
debug('I/O read: C0' + toHex(off));
}
return result;
},
@ -327,7 +326,7 @@ function Apple2IO(cpu, callbacks)
if (_locs[off]) {
_locs[off].ioSwitch(off, val);
} else {
debug("I/O write: C0" + toHex(off));
debug('I/O write: C0' + toHex(off));
}
},
getState: function apple2io_getState() { return {}; },

173
js/applesoft/decompiler.js Normal file
View File

@ -0,0 +1,173 @@
function ApplesoftDump(mem)
{
var _mem = mem;
var LETTERS =
" " +
" !\"#$%&'()*+,-./0123456789:;<=>?" +
"@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_" +
"`abcdefghijklmnopqrstuvwxyz{|}~ ";
var TOKENS = {
0x80: "END",
0x81: "FOR",
0x82: "NEXT",
0x83: "DATA",
0x84: "INPUT",
0x85: "DEL",
0x86: "DIM",
0x87: "READ",
0x88: "GR",
0x89: "TEXT",
0x8a: "PR#",
0x8b: "IN#",
0x8c: "CALL",
0x8d: "PLOT",
0x8e: "HLIN",
0x8f: "VLIN",
0x90: "HGR2",
0x91: "HGR",
0x92: "HCOLOR=",
0x93: "HPLOT",
0x94: "DRAW",
0x95: "XDRAW",
0x96: "HTAB",
0x97: "HOME",
0x98: "ROT=",
0x99: "SCALE=",
0x9a: "SHLOAD",
0x9b: "TRACE",
0x9c: "NOTRACE",
0x9d: "NORMAL",
0x9e: "INVERSE",
0x9f: "FLASH",
0xa0: "COLOR=",
0xa1: "POP=",
0xa2: "VTAB",
0xa3: "HIMEM:",
0xa4: "LOMEM:",
0xa5: "ONERR",
0xa6: "RESUME",
0xa7: "RECALL",
0xa8: "STORE",
0xa9: "SPEED=",
0xaa: "LET",
0xab: "GOTO",
0xac: "RUN",
0xad: "IF",
0xae: "RESTORE",
0xaf: "&",
0xb0: "GOSUB",
0xb1: "RETURN",
0xb2: "REM",
0xb3: "STOP",
0xb4: "ON",
0xb5: "WAIT",
0xb6: "LOAD",
0xb7: "SAVE",
0xb8: "DEF",
0xb9: "POKE",
0xba: "PRINT",
0xbb: "CONT",
0xbc: "LIST",
0xbd: "CLEAR",
0xbe: "GET",
0xbf: "NEW",
0xc0: "TAB(",
0xc1: "TO",
0xc2: "FN",
0xc3: "SPC(",
0xc4: "THEN",
0xc5: "AT",
0xc6: "NOT",
0xc7: "STEP",
0xc8: "+",
0xc9: "-",
0xca: "*",
0xcb: "/",
0xcc: "^",
0xcd: "AND",
0xce: "OR",
0xcf: ">",
0xd0: "=",
0xd1: "<",
0xd2: "SGN",
0xd3: "INT",
0xd4: "ABS",
0xd5: "USR",
0xd6: "FRE",
0xd7: "SCRN(",
0xd8: "PDL",
0xd9: "POS",
0xda: "SQR",
0xdb: "RND",
0xdc: "LOG",
0xdd: "EXP",
0xde: "COS",
0xdf: "SIN",
0xe0: "TAN",
0xe1: "ATN",
0xe2: "PEEK",
0xe3: "LEN",
0xe4: "STR$",
0xe5: "VAL",
0xe6: "ASC",
0xe7: "CHR$",
0xe8: "LEFT$",
0xe9: "RIGHT$",
0xea: "MID$"
};
function readByte(addr) {
var page = addr >> 8,
off = addr & 0xff;
return _mem.read(page, off);
}
function readWord(addr) {
var lsb, msb;
lsb = readByte(addr, debug);
msb = readByte(addr + 1, debug);
return (msb << 8) | lsb;
}
return {
toString: function () {
var str = "";
var start = readWord(0x67); // Start
var end = readWord(0xaf); // End of program
var addr = start;
do {
var line = "";
var next = readWord(addr);
addr += 2;
var lineno = readWord(addr);
addr += 2;
line += lineno;
line += " ";
var val = false;
do {
if (addr < start || addr > end)
return str;
var val = readByte(addr++);
if (val > 0x80) {
line += " ";
line += TOKENS[val];
line += " ";
}
else
line += LETTERS[val];
} while (val);
line += "\n";
str += line;
addr = next;
} while (addr && addr >= start && addr < end);
return str;
}
};
}

View File

@ -20,8 +20,8 @@ function base64_encode (data) {
// return atob(data);
//}
var b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
var o1, o2, o3, h1, h2, h3, h4, bits, i = 0, ac = 0, enc="", tmp_arr = [];
var b64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
var o1, o2, o3, h1, h2, h3, h4, bits, i = 0, ac = 0, enc='', tmp_arr = [];
if (!data) {
return data;
@ -80,7 +80,7 @@ function base64_decode(data) {
// return btoa(data);
//}
var b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
var b64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
var o1, o2, o3, h1, h2, h3, h4, bits, i = 0, ac = 0, tmp_arr = [];
if (!data) {

View File

@ -1,5 +1,4 @@
/* -*- mode: JavaScript; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* Copyright 2010-2014 Will Scullin <scullin@scullinsteel.com>
/* Copyright 2010-2016 Will Scullin <scullin@scullinsteel.com>
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
@ -10,16 +9,14 @@
* implied warranty.
*/
/*jshint browser:true */
/*globals allocMemPages: false,
charset: false,
base64_encode: false, base64_decode: false */
/*exported LoresPage, HiresPage, VideoModes*/
/*
* Text Page 1 Drawing
*/
/*globals allocMemPages: false,
base64_encode: false, base64_decode: false */
/*exported LoresPage, HiresPage, VideoModes*/
var textMode = true;
var mixedMode = true;
var hiresMode = false;
@ -34,8 +31,10 @@ var scanlines = false;
*
***************************************************************************/
function LoresPage(page)
function LoresPage(page, charset)
{
'use strict';
// $00-$3F inverse
// $40-$7F flashing
// $80-$FF normal
@ -159,7 +158,7 @@ function LoresPage(page)
back = _greenMode ? _green : _white;
}
for (jdx = 0; jdx < 8; jdx++) {
b = charset[(val & 0x3f) * 8 + jdx];
b = charset[val * 8 + jdx];
b <<= 1;
for (idx = 0; idx < 7; idx++) {
color = (b & 0x80) ? fore : back;
@ -177,7 +176,7 @@ function LoresPage(page)
b = (jdx < 4) ? (val & 0x0f) : (val >> 4);
b |= (b << 4);
b |= (b << 8);
if (col & 0x1) {
if ((col & 0x1) !== 0) {
b <<= 2;
}
for (idx = 0; idx < 14; idx++) {
@ -332,7 +331,7 @@ function HiresPage(page)
b2 = col < 39 ? _buffer[base + 1] : 0;
val |= (b2 & 0x3) << 7;
var v0 = b0 & 0x20, v1 = b0 & 0x40, v2 = val & 0x1,
odd = !(col & 0x1),
odd = (col & 0x1) === 0,
color,
oddCol = (hbs ? orangeCol : greenCol),
evenCol = (hbs ? blueCol : violetCol);
@ -490,4 +489,3 @@ function VideoModes(gr,hgr,gr2,hgr2) {
}
};
}

View File

@ -1,5 +1,4 @@
/* -*- mode: JavaScript; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* Copyright 2010-2013 Will Scullin <scullin@scullinsteel.com>
/* Copyright 2010-2016 Will Scullin <scullin@scullinsteel.com>
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
@ -10,8 +9,9 @@
* implied warranty.
*/
/*jshint browser:true */
/*globals allocMemPages: false, charset: false, base64_encode: false, base64_decode: false, enhanced: false */
/*globals allocMemPages: false, debug: false,
base64_encode: false, base64_decode: false,
enhanced: false */
/*exported LoresPage, HiresPage, VideoModes */
@ -22,6 +22,12 @@ var pageMode = 1;
var _80colMode = false;
var altCharMode = false;
var doubleHiresMode = false;
var monoDHRMode = false;
var colorDHRMode = false;
var mixedDHRMode = false;
var highColorHGRMode = false;
var highColorTextMode = false;
var pages = [];
var context = null;
var scanlines = false;
@ -32,9 +38,9 @@ var scanlines = false;
*
***************************************************************************/
function LoresPage(page)
function LoresPage(page, charset)
{
"use strict";
'use strict';
// $00-$3F inverse
// $40-$7F flashing
@ -176,9 +182,6 @@ function LoresPage(page)
return;
_buffer[bank][base] = val;
if (bank !== 0 && !_80colMode)
return;
var col = (base % 0x80) % 0x28,
adj = off - col;
@ -202,14 +205,15 @@ function LoresPage(page)
fore = flash ? _black : (_greenMode ? _green : _white);
back = flash ? (_greenMode ? _green : _white) : _black;
if (_80colMode) {
if (!enhanced) {
val = (val >= 0x40 && val < 0x60) ? val - 0x40 : val;
} else if (!altCharMode) {
val = (val >= 0x40 && val < 0x80) ? val - 0x40 : val;
}
if (_80colMode) {
off = (col * 14 + (bank ? 0 : 1) * 7 + row * 560 * 8 * 2) * 4;
for (jdx = 0; jdx < 8; jdx++) {
b = charset[val * 8 + jdx];
for (idx = 0; idx < 7; idx++) {
@ -221,8 +225,21 @@ function LoresPage(page)
off += 553 * 4 + 560 * 4;
}
} else {
val = _buffer[0][base];
if (!enhanced) {
val = (val >= 0x40 && val < 0x60) ? val - 0x40 : val;
} else if (!altCharMode) {
val = (val >= 0x40 && val < 0x80) ? val - 0x40 : val;
}
off = (col * 14 + row * 560 * 8 * 2) * 4;
if (highColorTextMode) {
fore = _colors[_buffer[0][base] >> 4];
back = _colors[_buffer[0][base] & 0x0f];
}
for (jdx = 0; jdx < 8; jdx++) {
b = charset[val * 8 + jdx];
for (idx = 0; idx < 7; idx++) {
@ -305,11 +322,9 @@ function LoresPage(page)
}
},
refresh: function() {
var page, off, addr = 0x400 * _page;
var addr = 0x400 * _page;
_refreshing = true;
for (var idx = 0; idx < 0x400; idx++, addr++) {
page = addr >> 8;
off = addr & 0xff;
this._write(addr >> 8, addr & 0xff, _buffer[0][idx], 0);
if (_80colMode)
this._write(addr >> 8, addr & 0xff, _buffer[1][idx], 1);
@ -362,7 +377,7 @@ function LoresPage(page)
function HiresPage(page)
{
"use strict";
'use strict';
var _page = page;
@ -416,8 +431,8 @@ function HiresPage(page)
var _buffer = [];
var _refreshing = false;
var _green = [0x00, 0xff, 0x80];
var _greenMode = false;
var _green = [0x00, 0xff, 0x80];
function _init() {
_buffer[0] = allocMemPages(0x20);
@ -430,6 +445,7 @@ function HiresPage(page)
function _drawPixel(data, off, color) {
var c0 = color[0], c1 = color[1], c2 = color[2];
data[off + 0] = data[off + 4] = c0;
data[off + 1] = data[off + 5] = c1;
data[off + 2] = data[off + 6] = c2;
@ -515,9 +531,6 @@ function HiresPage(page)
return;
_buffer[bank][base] = val;
if (bank !== 0 && !doubleHiresMode)
return;
var hbs = val & 0x80;
val &= 0x7f;
@ -538,7 +551,7 @@ function HiresPage(page)
return;
dy = rowa * 16 + rowb * 2;
var bz, b0, b1, b2, b3, b4, c;
var bz, b0, b1, b2, b3, b4, c, hb;
if (doubleHiresMode) {
// Every 4 bytes is 7 pixels
// 2 bytes per bank
@ -557,26 +570,57 @@ function HiresPage(page)
((b2 & 0x3c) >> 2), // 4
((b2 & 0x40) >> 6) | ((b3 & 0x07) << 1), // 5
((b3 & 0x78) >> 3), // 6
0], // 7
hb = [0,
b0 & 0x80, // 0
b0 & 0x80, // 1
b1 & 0x80, // 2
b2 & 0x80, // 3
b2 & 0x80, // 4
b3 & 0x80, // 5
b3 & 0x80, // 6
0]; // 7
if (col > 0) {
c[0] = (bz & 0x78) >> 3;
hb[0] = bz & 0x80;
}
if (col < 39) {
c[8] = b4 & 0x0f;
hb[8] = b4 & 0x80;
}
dx = mcol * 14;
off = dx * 4 + dy * 280 * 4 * 2;
var monoColor = null;
if (_greenMode) {
monoColor = _green;
} else if (monoDHRMode) {
monoColor = whiteCol;
}
for (idx = 1; idx < 8; idx++) {
hbs = hb[idx];
var dcolor = dcolors[r4[c[idx]]];
var bits = c[idx-1] | (c[idx] << 4) | (c[idx+1] << 8);
for (jdx = 0; jdx < 4; jdx++, off += 4) {
if (_greenMode) {
if (monoColor) {
if (bits & 0x10) {
_drawHalfPixel(data, off, _green);
_drawHalfPixel(data, off, monoColor);
} else {
_drawHalfPixel(data, off, blackCol);
}
} else if (mixedDHRMode) {
if (hbs) {
_drawHalfPixel(data, off, dcolor);
} else {
if (bits & 0x10) {
_drawHalfPixel(data, off, whiteCol);
} else {
_drawHalfPixel(data, off, blackCol);
}
}
} else if (colorDHRMode) {
_drawHalfPixel(data, off, dcolor);
} else if (((bits & 0x3c) == 0x3c) ||
((bits & 0xf0) == 0xf0) ||
((bits & 0x1e) == 0x1e) ||
@ -607,9 +651,10 @@ function HiresPage(page)
_refreshing = false;
}
} else {
val = _buffer[0][base] & 0x7f;
dx = col * 14 - 2;
b0 = col > 0 ? _buffer[bank][base - 1] : 0;
b2 = col < 39 ? _buffer[bank][base + 1] : 0;
b0 = col > 0 ? _buffer[0][base - 1] : 0;
b2 = col < 39 ? _buffer[0][base + 1] : 0;
val |= (b2 & 0x3) << 7;
var v0 = b0 & 0x20, v1 = b0 & 0x40, v2 = val & 0x1,
odd = !(col & 0x1),
@ -623,6 +668,8 @@ function HiresPage(page)
if (v1) {
if (_greenMode) {
color = _green;
} else if (highColorHGRMode) {
color = dcolors[_buffer[1][base] >> 4];
} else if (v0 || v2) {
color = whiteCol;
} else {
@ -631,6 +678,8 @@ function HiresPage(page)
} else {
if (_greenMode) {
color = blackCol;
} else if (highColorHGRMode) {
color = dcolors[_buffer[1][base] & 0x0f];
} else if (odd && v2 && v0) {
color = v0 ? dim(evenCol) : evenCol;
} else if (!odd && v0 && v2) {
@ -694,6 +743,7 @@ function HiresPage(page)
function VideoModes(gr,hgr,gr2,hgr2) {
var _grs = [gr, gr2];
var _hgrs = [hgr, hgr2];
var _seq = '';
function _refresh() {
gr.refresh();
@ -711,15 +761,28 @@ function VideoModes(gr,hgr,gr2,hgr2) {
mixedMode = false;
hiresMode = true;
pageMode = 1;
_80colMode = false;
altCharMode = false;
doubleHiresMode = false;
monoDHRMode = false;
colorDHRMode = false;
mixedDHRMode = false;
highColorHGRMode = false;
highColorTextMode = false;
_refresh();
},
text: function(on) {
var old = textMode;
textMode = on;
highColorTextMode = false;
_seq = on ? 'T+' : 'T-';
// debug('_seq=', _seq);
if (old != on) {
_refresh();
}
@ -727,6 +790,11 @@ function VideoModes(gr,hgr,gr2,hgr2) {
_80col: function(on) {
var old = _80colMode;
_80colMode = on;
_seq += on ? '8+' : '8-';
_seq = _seq.substr(0, 16);
// debug('_seq=', _seq);
if (old != on) {
_refresh();
}
@ -741,6 +809,11 @@ function VideoModes(gr,hgr,gr2,hgr2) {
hires: function(on) {
var old = hiresMode;
hiresMode = on;
highColorHGRMode = false;
_seq = (on ? 'H+' : 'H-');
// debug('_seq=', _seq);
if (old != on) {
_refresh();
}
@ -748,6 +821,41 @@ function VideoModes(gr,hgr,gr2,hgr2) {
doublehires: function(on) {
var old = doubleHiresMode;
doubleHiresMode = on;
_seq += on ? 'D+' : 'D-';
_seq = _seq.substr(0, 16);
// debug('_seq=', _seq);
if (on) {
if (_seq == 'T+D+') {
debug('High color text mode');
highColorTextMode = true;
doubleHiresMode = false;
} else if (_seq == 'H+8-D+') {
debug('High color hgr');
highColorHGRMode = true;
doubleHiresMode = false;
} else if (_seq == 'H+8+D+D-D+D-D+') {
debug('DoubleHires color');
colorDHRMode = true;
monoDHRMode = false;
mixedDHRMode = false;
_seq = '';
} else if (_seq == 'H+8-D+D-D+D-8+D+') {
debug('DoubleHires mono');
colorDHRMode = false;
monoDHRMode = true;
mixedDHRMode = false;
_seq = '';
} else if (_seq == 'H+8-D+D-8+D+D-D+') {
debug('DoubleHires mixed');
colorDHRMode = false;
monoDHRMode = false;
mixedDHRMode = true;
_seq = '';
}
}
if (old != on) {
if (on) {
this.page(1);
@ -835,5 +943,3 @@ function VideoModes(gr,hgr,gr2,hgr2) {
}
};
}

View File

@ -1,5 +1,5 @@
/*!
* Copyright 2010-2013 Will Scullin <scullin@scullinsteel.com>
* Copyright 2010-2016 Will Scullin <scullin@scullinsteel.com>
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that

View File

@ -1,6 +1,5 @@
/* -*- mode: Javascript; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* Copyright 2010-2013 Will Scullin <scullin@scullinsteel.com>
* Copyright 2010-2016 Will Scullin <scullin@scullinsteel.com>
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
@ -16,7 +15,7 @@
function CPU6502(options)
{
"use strict";
'use strict';
options = options || {};
@ -116,7 +115,7 @@ function CPU6502(options)
function testNZ(val) {
sr = val === 0 ? (sr | flags.Z) : (sr & ~flags.Z);
sr = val & 0x80 ? (sr | flags.N) : (sr & ~flags.N);
sr = (val & 0x80) ? (sr | flags.N) : (sr & ~flags.N);
return val;
}
@ -133,7 +132,7 @@ function CPU6502(options)
// KEGS
var c, v;
if (sr & flags.D) {
if ((sr & flags.D) !== 0) {
c = (a & 0x0f) + (b & 0x0f) + (sr & flags.C);
if (sub) {
if (c < 0x10)
@ -155,8 +154,9 @@ function CPU6502(options)
v = (c ^ a) & 0x80;
}
if ((a ^ b) & 0x80)
if (((a ^ b) & 0x80) !== 0) {
v = 0;
}
setFlag(flags.C, c > 0xff);
setFlag(flags.V, v);
@ -223,8 +223,7 @@ function CPU6502(options)
function pullByte() {
sp = (sp + 0x01) & 0xff;
var result = readByte(loc.STACK | sp);
return result;
return readByte(loc.STACK | sp);
}
function pullWord() {
@ -592,7 +591,7 @@ function CPU6502(options)
/* Bit */
function bit(readFn) {
var val = readFn();
setFlag(flags.Z, !(val & ar));
setFlag(flags.Z, (val & ar) === 0);
setFlag(flags.N, val & 0x80);
setFlag(flags.V, val & 0x40);
}
@ -600,7 +599,7 @@ function CPU6502(options)
/* Bit Immediate*/
function bitI(readFn) {
var val = readFn();
setFlag(flags.Z, !(val & ar));
setFlag(flags.Z, (val & ar) === 0);
}
function compare(a, b)
@ -626,7 +625,7 @@ function CPU6502(options)
/* Branches */
function brs(f) {
var off = readBytePC(); // changes pc
if (f & sr) {
if ((f & sr) !== 0) {
var oldPC = pc;
pc += off > 127 ? off - 256 : off;
cycles++;
@ -636,7 +635,7 @@ function CPU6502(options)
function brc(f) {
var off = readBytePC(); // changes pc
if (!(f & sr)) {
if ((f & sr) === 0) {
var oldPC = pc;
pc += off > 127 ? off - 256 : off;
cycles++;
@ -711,334 +710,336 @@ function CPU6502(options)
var ops = {
// LDA
0xa9: ["LDA", lda, readImmediate, modes.immediate, 2],
0xa5: ["LDA", lda, readZeroPage, modes.zeroPage, 3],
0xb5: ["LDA", lda, readZeroPageX, modes.zeroPageX, 4],
0xad: ["LDA", lda, readAbsolute, modes.absolute, 4],
0xbd: ["LDA", lda, readAbsoluteX, modes.absoluteX, 4],
0xb9: ["LDA", lda, readAbsoluteY, modes.absoluteY, 4],
0xa1: ["LDA", lda, readZeroPageXIndirect, modes.zeroPageXIndirect, 6],
0xb1: ["LDA", lda, readZeroPageIndirectY, modes.zeroPageIndirectY, 5],
0xa9: ['LDA', lda, readImmediate, modes.immediate, 2],
0xa5: ['LDA', lda, readZeroPage, modes.zeroPage, 3],
0xb5: ['LDA', lda, readZeroPageX, modes.zeroPageX, 4],
0xad: ['LDA', lda, readAbsolute, modes.absolute, 4],
0xbd: ['LDA', lda, readAbsoluteX, modes.absoluteX, 4],
0xb9: ['LDA', lda, readAbsoluteY, modes.absoluteY, 4],
0xa1: ['LDA', lda, readZeroPageXIndirect, modes.zeroPageXIndirect, 6],
0xb1: ['LDA', lda, readZeroPageIndirectY, modes.zeroPageIndirectY, 5],
// LDX
0xa2: ["LDX", ldx, readImmediate, modes.immediate, 2],
0xa6: ["LDX", ldx, readZeroPage, modes.zeroPage, 3],
0xb6: ["LDX", ldx, readZeroPageY, modes.zeroPageY, 4],
0xae: ["LDX", ldx, readAbsolute, modes.absolute, 4],
0xbe: ["LDX", ldx, readAbsoluteY, modes.absoluteY, 4],
0xa2: ['LDX', ldx, readImmediate, modes.immediate, 2],
0xa6: ['LDX', ldx, readZeroPage, modes.zeroPage, 3],
0xb6: ['LDX', ldx, readZeroPageY, modes.zeroPageY, 4],
0xae: ['LDX', ldx, readAbsolute, modes.absolute, 4],
0xbe: ['LDX', ldx, readAbsoluteY, modes.absoluteY, 4],
// LDY
0xa0: ["LDY", ldy, readImmediate, modes.immediate, 2],
0xa4: ["LDY", ldy, readZeroPage, modes.zeroPage, 3],
0xb4: ["LDY", ldy, readZeroPageX, modes.zeroPageX, 4],
0xac: ["LDY", ldy, readAbsolute, modes.absolute, 4],
0xbc: ["LDY", ldy, readAbsoluteX, modes.absoluteX, 4],
0xa0: ['LDY', ldy, readImmediate, modes.immediate, 2],
0xa4: ['LDY', ldy, readZeroPage, modes.zeroPage, 3],
0xb4: ['LDY', ldy, readZeroPageX, modes.zeroPageX, 4],
0xac: ['LDY', ldy, readAbsolute, modes.absolute, 4],
0xbc: ['LDY', ldy, readAbsoluteX, modes.absoluteX, 4],
// STA
0x85: ["STA", sta, writeZeroPage, modes.zeroPage, 3],
0x95: ["STA", sta, writeZeroPageX, modes.zeroPageX, 4],
0x8d: ["STA", sta, writeAbsolute, modes.absolute, 4],
0x9d: ["STA", sta, writeAbsoluteX, modes.absoluteX, 5],
0x99: ["STA", sta, writeAbsoluteY, modes.absoluteY, 5],
0x81: ["STA", sta, writeZeroPageXIndirect, modes.zeroPageXIndirect, 6],
0x91: ["STA", sta, writeZeroPageIndirectY, modes.zeroPageIndirectY, 6],
0x85: ['STA', sta, writeZeroPage, modes.zeroPage, 3],
0x95: ['STA', sta, writeZeroPageX, modes.zeroPageX, 4],
0x8d: ['STA', sta, writeAbsolute, modes.absolute, 4],
0x9d: ['STA', sta, writeAbsoluteX, modes.absoluteX, 5],
0x99: ['STA', sta, writeAbsoluteY, modes.absoluteY, 5],
0x81: ['STA', sta, writeZeroPageXIndirect, modes.zeroPageXIndirect, 6],
0x91: ['STA', sta, writeZeroPageIndirectY, modes.zeroPageIndirectY, 6],
// STX
0x86: ["STX", stx, writeZeroPage, modes.zeroPage, 3],
0x96: ["STX", stx, writeZeroPageY, modes.zeroPageY, 4],
0x8e: ["STX", stx, writeAbsolute, modes.absolute, 4],
0x86: ['STX', stx, writeZeroPage, modes.zeroPage, 3],
0x96: ['STX', stx, writeZeroPageY, modes.zeroPageY, 4],
0x8e: ['STX', stx, writeAbsolute, modes.absolute, 4],
// STY
0x84: ["STY", sty, writeZeroPage, modes.zeroPage, 3],
0x94: ["STY", sty, writeZeroPageX, modes.zeroPageX, 4],
0x8c: ["STY", sty, writeAbsolute, modes.absolute, 4],
0x84: ['STY', sty, writeZeroPage, modes.zeroPage, 3],
0x94: ['STY', sty, writeZeroPageX, modes.zeroPageX, 4],
0x8c: ['STY', sty, writeAbsolute, modes.absolute, 4],
// ADC
0x69: ["ADC", adc, readImmediate, modes.immediate, 2],
0x65: ["ADC", adc, readZeroPage, modes.zeroPage, 3],
0x75: ["ADC", adc, readZeroPageX, modes.zeroPageX, 4],
0x6D: ["ADC", adc, readAbsolute, modes.absolute, 4],
0x7D: ["ADC", adc, readAbsoluteX, modes.absoluteX, 4],
0x79: ["ADC", adc, readAbsoluteY, modes.absoluteY, 4],
0x61: ["ADC", adc, readZeroPageXIndirect, modes.zeroPageXIndirect, 6],
0x71: ["ADC", adc, readZeroPageIndirectY, modes.zeroPageIndirectY, 5],
0x69: ['ADC', adc, readImmediate, modes.immediate, 2],
0x65: ['ADC', adc, readZeroPage, modes.zeroPage, 3],
0x75: ['ADC', adc, readZeroPageX, modes.zeroPageX, 4],
0x6D: ['ADC', adc, readAbsolute, modes.absolute, 4],
0x7D: ['ADC', adc, readAbsoluteX, modes.absoluteX, 4],
0x79: ['ADC', adc, readAbsoluteY, modes.absoluteY, 4],
0x61: ['ADC', adc, readZeroPageXIndirect, modes.zeroPageXIndirect, 6],
0x71: ['ADC', adc, readZeroPageIndirectY, modes.zeroPageIndirectY, 5],
// SBC
0xe9: ["SBC", sbc, readImmediate, modes.immediate, 2],
0xe5: ["SBC", sbc, readZeroPage, modes.zeroPage, 3],
0xf5: ["SBC", sbc, readZeroPageX, modes.zeroPageX, 4],
0xeD: ["SBC", sbc, readAbsolute, modes.absolute, 4],
0xfD: ["SBC", sbc, readAbsoluteX, modes.absoluteX, 4],
0xf9: ["SBC", sbc, readAbsoluteY, modes.absoluteY, 4],
0xe1: ["SBC", sbc, readZeroPageXIndirect, modes.zeroPageXIndirect, 6],
0xf1: ["SBC", sbc, readZeroPageIndirectY, modes.zeroPageIndirectY, 5],
0xe9: ['SBC', sbc, readImmediate, modes.immediate, 2],
0xe5: ['SBC', sbc, readZeroPage, modes.zeroPage, 3],
0xf5: ['SBC', sbc, readZeroPageX, modes.zeroPageX, 4],
0xeD: ['SBC', sbc, readAbsolute, modes.absolute, 4],
0xfD: ['SBC', sbc, readAbsoluteX, modes.absoluteX, 4],
0xf9: ['SBC', sbc, readAbsoluteY, modes.absoluteY, 4],
0xe1: ['SBC', sbc, readZeroPageXIndirect, modes.zeroPageXIndirect, 6],
0xf1: ['SBC', sbc, readZeroPageIndirectY, modes.zeroPageIndirectY, 5],
// INC
0xe6: ["INC", inc, readAddrZeroPage, modes.zeroPage, 5],
0xf6: ["INC", inc, readAddrZeroPageX, modes.zeroPageX, 6],
0xee: ["INC", inc, readAddrAbsolute, modes.absolute, 6],
0xfe: ["INC", inc, readAddrAbsoluteX, modes.absoluteX, 7],
0xe6: ['INC', inc, readAddrZeroPage, modes.zeroPage, 5],
0xf6: ['INC', inc, readAddrZeroPageX, modes.zeroPageX, 6],
0xee: ['INC', inc, readAddrAbsolute, modes.absolute, 6],
0xfe: ['INC', inc, readAddrAbsoluteX, modes.absoluteX, 7],
// INX
0xe8: ["INX", inx, null, modes.implied, 2],
0xe8: ['INX', inx, null, modes.implied, 2],
// INY
0xc8: ["INY", iny, null, modes.implied, 2],
0xc8: ['INY', iny, null, modes.implied, 2],
// DEC
0xc6: ["DEC", dec, readAddrZeroPage, modes.zeroPage, 5],
0xd6: ["DEC", dec, readAddrZeroPageX, modes.zeroPageX, 6],
0xce: ["DEC", dec, readAddrAbsolute, modes.absolute, 6],
0xde: ["DEC", dec, readAddrAbsoluteX, modes.absoluteX, 7],
0xc6: ['DEC', dec, readAddrZeroPage, modes.zeroPage, 5],
0xd6: ['DEC', dec, readAddrZeroPageX, modes.zeroPageX, 6],
0xce: ['DEC', dec, readAddrAbsolute, modes.absolute, 6],
0xde: ['DEC', dec, readAddrAbsoluteX, modes.absoluteX, 7],
// DEX
0xca: ["DEX", dex, null, modes.implied, 2],
0xca: ['DEX', dex, null, modes.implied, 2],
// DEY
0x88: ["DEY", dey, null, modes.implied, 2],
0x88: ['DEY', dey, null, modes.implied, 2],
// ASL
0x0A: ["ASL", aslA, null, modes.accumulator, 2],
0x06: ["ASL", asl, readAddrZeroPage, modes.zeroPage, 5],
0x16: ["ASL", asl, readAddrZeroPageX, modes.zeroPageX, 6],
0x0E: ["ASL", asl, readAddrAbsolute, modes.absolute, 6],
0x1E: ["ASL", asl, readAddrAbsoluteX, modes.absoluteX, 7],
0x0A: ['ASL', aslA, null, modes.accumulator, 2],
0x06: ['ASL', asl, readAddrZeroPage, modes.zeroPage, 5],
0x16: ['ASL', asl, readAddrZeroPageX, modes.zeroPageX, 6],
0x0E: ['ASL', asl, readAddrAbsolute, modes.absolute, 6],
0x1E: ['ASL', asl, readAddrAbsoluteX, modes.absoluteX, 7],
// LSR
0x4A: ["LSR", lsrA, null, modes.accumulator, 2],
0x46: ["LSR", lsr, readAddrZeroPage, modes.zeroPage, 5],
0x56: ["LSR", lsr, readAddrZeroPageX, modes.zeroPageX, 6],
0x4E: ["LSR", lsr, readAddrAbsolute, modes.absolute, 6],
0x5E: ["LSR", lsr, readAddrAbsoluteX, modes.absoluteX, 7],
0x4A: ['LSR', lsrA, null, modes.accumulator, 2],
0x46: ['LSR', lsr, readAddrZeroPage, modes.zeroPage, 5],
0x56: ['LSR', lsr, readAddrZeroPageX, modes.zeroPageX, 6],
0x4E: ['LSR', lsr, readAddrAbsolute, modes.absolute, 6],
0x5E: ['LSR', lsr, readAddrAbsoluteX, modes.absoluteX, 7],
// ROL
0x2A: ["ROL", rolA, null, modes.accumulator, 2],
0x26: ["ROL", rol, readAddrZeroPage, modes.zeroPage, 5],
0x36: ["ROL", rol, readAddrZeroPageX, modes.zeroPageX, 6],
0x2E: ["ROL", rol, readAddrAbsolute, modes.absolute, 6],
0x3E: ["ROL", rol, readAddrAbsoluteX, modes.absoluteX, 7],
0x2A: ['ROL', rolA, null, modes.accumulator, 2],
0x26: ['ROL', rol, readAddrZeroPage, modes.zeroPage, 5],
0x36: ['ROL', rol, readAddrZeroPageX, modes.zeroPageX, 6],
0x2E: ['ROL', rol, readAddrAbsolute, modes.absolute, 6],
0x3E: ['ROL', rol, readAddrAbsoluteX, modes.absoluteX, 7],
// ROR
0x6A: ["ROR", rorA, null, modes.accumulator, 2],
0x66: ["ROR", ror, readAddrZeroPage, modes.zeroPage, 5],
0x76: ["ROR", ror, readAddrZeroPageX, modes.zeroPageX, 6],
0x6E: ["ROR", ror, readAddrAbsolute, modes.absolute, 6],
0x7E: ["ROR", ror, readAddrAbsoluteX, modes.absoluteX, 7],
0x6A: ['ROR', rorA, null, modes.accumulator, 2],
0x66: ['ROR', ror, readAddrZeroPage, modes.zeroPage, 5],
0x76: ['ROR', ror, readAddrZeroPageX, modes.zeroPageX, 6],
0x6E: ['ROR', ror, readAddrAbsolute, modes.absolute, 6],
0x7E: ['ROR', ror, readAddrAbsoluteX, modes.absoluteX, 7],
// AND
0x29: ["AND", and, readImmediate, modes.immediate, 2],
0x25: ["AND", and, readZeroPage, modes.zeroPage, 2],
0x35: ["AND", and, readZeroPageX, modes.zeroPageX, 3],
0x2D: ["AND", and, readAbsolute, modes.absolute, 4],
0x3D: ["AND", and, readAbsoluteX, modes.absoluteX, 4],
0x39: ["AND", and, readAbsoluteY, modes.absoluteY, 4],
0x21: ["AND", and, readZeroPageXIndirect, modes.zeroPageXIndirect, 6],
0x31: ["AND", and, readZeroPageIndirectY, modes.zeroPageIndirectY, 5],
0x29: ['AND', and, readImmediate, modes.immediate, 2],
0x25: ['AND', and, readZeroPage, modes.zeroPage, 2],
0x35: ['AND', and, readZeroPageX, modes.zeroPageX, 3],
0x2D: ['AND', and, readAbsolute, modes.absolute, 4],
0x3D: ['AND', and, readAbsoluteX, modes.absoluteX, 4],
0x39: ['AND', and, readAbsoluteY, modes.absoluteY, 4],
0x21: ['AND', and, readZeroPageXIndirect, modes.zeroPageXIndirect, 6],
0x31: ['AND', and, readZeroPageIndirectY, modes.zeroPageIndirectY, 5],
// ORA
0x09: ["ORA", ora, readImmediate, modes.immediate, 2],
0x05: ["ORA", ora, readZeroPage, modes.zeroPage, 2],
0x15: ["ORA", ora, readZeroPageX, modes.zeroPageX, 3],
0x0D: ["ORA", ora, readAbsolute, modes.absolute, 4],
0x1D: ["ORA", ora, readAbsoluteX, modes.absoluteX, 4],
0x19: ["ORA", ora, readAbsoluteY, modes.absoluteY, 4],
0x01: ["ORA", ora, readZeroPageXIndirect, modes.zeroPageXIndirect, 6],
0x11: ["ORA", ora, readZeroPageIndirectY, modes.zeroPageIndirectY, 5],
0x09: ['ORA', ora, readImmediate, modes.immediate, 2],
0x05: ['ORA', ora, readZeroPage, modes.zeroPage, 2],
0x15: ['ORA', ora, readZeroPageX, modes.zeroPageX, 3],
0x0D: ['ORA', ora, readAbsolute, modes.absolute, 4],
0x1D: ['ORA', ora, readAbsoluteX, modes.absoluteX, 4],
0x19: ['ORA', ora, readAbsoluteY, modes.absoluteY, 4],
0x01: ['ORA', ora, readZeroPageXIndirect, modes.zeroPageXIndirect, 6],
0x11: ['ORA', ora, readZeroPageIndirectY, modes.zeroPageIndirectY, 5],
// EOR
0x49: ["EOR", eor, readImmediate, modes.immediate, 2],
0x45: ["EOR", eor, readZeroPage, modes.zeroPage, 3],
0x55: ["EOR", eor, readZeroPageX, modes.zeroPageX, 4],
0x4D: ["EOR", eor, readAbsolute, modes.absolute, 4],
0x5D: ["EOR", eor, readAbsoluteX, modes.absoluteX, 4],
0x59: ["EOR", eor, readAbsoluteY, modes.absoluteY, 4],
0x41: ["EOR", eor, readZeroPageXIndirect, modes.zeroPageXIndirect, 6],
0x51: ["EOR", eor, readZeroPageIndirectY, modes.zeroPageIndirectY, 5],
0x49: ['EOR', eor, readImmediate, modes.immediate, 2],
0x45: ['EOR', eor, readZeroPage, modes.zeroPage, 3],
0x55: ['EOR', eor, readZeroPageX, modes.zeroPageX, 4],
0x4D: ['EOR', eor, readAbsolute, modes.absolute, 4],
0x5D: ['EOR', eor, readAbsoluteX, modes.absoluteX, 4],
0x59: ['EOR', eor, readAbsoluteY, modes.absoluteY, 4],
0x41: ['EOR', eor, readZeroPageXIndirect, modes.zeroPageXIndirect, 6],
0x51: ['EOR', eor, readZeroPageIndirectY, modes.zeroPageIndirectY, 5],
// CMP
0xc9: ["CMP", cmp, readImmediate, modes.immediate, 2],
0xc5: ["CMP", cmp, readZeroPage, modes.zeroPage, 3],
0xd5: ["CMP", cmp, readZeroPageX, modes.zeroPageX, 4],
0xcD: ["CMP", cmp, readAbsolute, modes.absolute, 4],
0xdD: ["CMP", cmp, readAbsoluteX, modes.absoluteX, 4],
0xd9: ["CMP", cmp, readAbsoluteY, modes.absoluteY, 4],
0xc1: ["CMP", cmp, readZeroPageXIndirect, modes.zeroPageXIndirect, 6],
0xd1: ["CMP", cmp, readZeroPageIndirectY, modes.zeroPageIndirectY, 5],
0xc9: ['CMP', cmp, readImmediate, modes.immediate, 2],
0xc5: ['CMP', cmp, readZeroPage, modes.zeroPage, 3],
0xd5: ['CMP', cmp, readZeroPageX, modes.zeroPageX, 4],
0xcD: ['CMP', cmp, readAbsolute, modes.absolute, 4],
0xdD: ['CMP', cmp, readAbsoluteX, modes.absoluteX, 4],
0xd9: ['CMP', cmp, readAbsoluteY, modes.absoluteY, 4],
0xc1: ['CMP', cmp, readZeroPageXIndirect, modes.zeroPageXIndirect, 6],
0xd1: ['CMP', cmp, readZeroPageIndirectY, modes.zeroPageIndirectY, 5],
// CPX
0xE0: ["CPX", cpx, readImmediate, modes.immediate, 2],
0xE4: ["CPX", cpx, readZeroPage, modes.zeroPage, 3],
0xEC: ["CPX", cpx, readAbsolute, modes.absolute, 4],
0xE0: ['CPX', cpx, readImmediate, modes.immediate, 2],
0xE4: ['CPX', cpx, readZeroPage, modes.zeroPage, 3],
0xEC: ['CPX', cpx, readAbsolute, modes.absolute, 4],
// CPY
0xC0: ["CPY", cpy, readImmediate, modes.immediate, 2],
0xC4: ["CPY", cpy, readZeroPage, modes.zeroPage, 3],
0xCC: ["CPY", cpy, readAbsolute, modes.absolute, 4],
0xC0: ['CPY', cpy, readImmediate, modes.immediate, 2],
0xC4: ['CPY', cpy, readZeroPage, modes.zeroPage, 3],
0xCC: ['CPY', cpy, readAbsolute, modes.absolute, 4],
// BIT
0x24: ["BIT", bit, readZeroPage, modes.zeroPage, 3],
0x2C: ["BIT", bit, readAbsolute, modes.absolute, 4],
0x24: ['BIT', bit, readZeroPage, modes.zeroPage, 3],
0x2C: ['BIT', bit, readAbsolute, modes.absolute, 4],
// BCC
0x90: ["BCC", brc, flags.C, modes.relative, 2],
0x90: ['BCC', brc, flags.C, modes.relative, 2],
// BCS
0xB0: ["BCS", brs, flags.C, modes.relative, 2],
0xB0: ['BCS', brs, flags.C, modes.relative, 2],
// BEQ
0xF0: ["BEQ", brs, flags.Z, modes.relative, 2],
0xF0: ['BEQ', brs, flags.Z, modes.relative, 2],
// BMI
0x30: ["BMI", brs, flags.N, modes.relative, 2],
0x30: ['BMI', brs, flags.N, modes.relative, 2],
// BNE
0xD0: ["BNE", brc, flags.Z, modes.relative, 2],
0xD0: ['BNE', brc, flags.Z, modes.relative, 2],
// BPL
0x10: ["BPL", brc, flags.N, modes.relative, 2],
0x10: ['BPL', brc, flags.N, modes.relative, 2],
// BVC
0x50: ["BVC", brc, flags.V, modes.relative, 2],
0x50: ['BVC', brc, flags.V, modes.relative, 2],
// BVS
0x70: ["BVS", brs, flags.V, modes.relative, 2],
0x70: ['BVS', brs, flags.V, modes.relative, 2],
// TAX
0xAA: ["TAX", tax, null, modes.implied, 2],
0xAA: ['TAX', tax, null, modes.implied, 2],
// TXA
0x8A: ["TXA", txa, null, modes.implied, 2],
0x8A: ['TXA', txa, null, modes.implied, 2],
// TAY
0xA8: ["TAY", tay, null, modes.implied, 2],
0xA8: ['TAY', tay, null, modes.implied, 2],
// TYA
0x98: ["TYA", tya, null, modes.implied, 2],
0x98: ['TYA', tya, null, modes.implied, 2],
// TSX
0xBA: ["TSX", tsx, null, modes.implied, 2],
0xBA: ['TSX', tsx, null, modes.implied, 2],
// TXS
0x9A: ["TXS", txs, null, modes.implied, 2],
0x9A: ['TXS', txs, null, modes.implied, 2],
// PHA
0x48: ["PHA", pha, null, modes.implied, 3],
0x48: ['PHA', pha, null, modes.implied, 3],
// PLA
0x68: ["PLA", pla, null, modes.implied, 4],
0x68: ['PLA', pla, null, modes.implied, 4],
// PHP
0x08: ["PHP", php, null, modes.implied, 3],
0x08: ['PHP', php, null, modes.implied, 3],
// PLP
0x28: ["PLP", plp, null, modes.implied, 4],
0x28: ['PLP', plp, null, modes.implied, 4],
// JMP
0x4C: ["JMP", jmp,
0x4C: ['JMP', jmp,
readAddrAbsolute, modes.absolute, 3],
0x6C: ["JMP", jmp,
0x6C: ['JMP', jmp,
readAddrAbsoluteIndirectBug, modes.absoluteIndirect, 5],
// JSR
0x20: ["JSR", jsr, readAddrAbsolute, modes.absolute, 6],
0x20: ['JSR', jsr, readAddrAbsolute, modes.absolute, 6],
// RTS
0x60: ["RTS", rts, null, modes.implied, 6],
0x60: ['RTS', rts, null, modes.implied, 6],
// RTI
0x40: ["RTI", rti, null, modes.implied, 6],
0x40: ['RTI', rti, null, modes.implied, 6],
// SEC
0x38: ["SEC", set, flags.C, modes.implied, 2],
0x38: ['SEC', set, flags.C, modes.implied, 2],
// SED
0xF8: ["SED", set, flags.D, modes.implied, 2],
0xF8: ['SED', set, flags.D, modes.implied, 2],
// SEI
0x78: ["SEI", set, flags.I, modes.implied, 2],
0x78: ['SEI', set, flags.I, modes.implied, 2],
// CLC
0x18: ["CLC", clr, flags.C, modes.implied, 2],
0x18: ['CLC', clr, flags.C, modes.implied, 2],
// CLD
0xD8: ["CLD", clr, flags.D, modes.implied, 2],
0xD8: ['CLD', clr, flags.D, modes.implied, 2],
// CLI
0x58: ["CLI", clr, flags.I, modes.implied, 2],
0x58: ['CLI', clr, flags.I, modes.implied, 2],
// CLV
0xB8: ["CLV", clr, flags.V, modes.implied, 2],
0xB8: ['CLV', clr, flags.V, modes.implied, 2],
// NOP
0xea: ["NOP", nop, null, modes.implied, 2],
0xea: ['NOP', nop, null, modes.implied, 2],
// BRK
0x00: ["BRK", brk, readImmediate, modes.immediate, 7]
0x00: ['BRK', brk, readImmediate, modes.immediate, 7]
};
/* 65C02 Instructions */
var cops = {
// INC / DEC A
0x1A: ["INC", incA, null, modes.accumulator, 2],
0x3A: ["DEC", decA, null, modes.accumulator, 2],
0x1A: ['INC', incA, null, modes.accumulator, 2],
0x3A: ['DEC', decA, null, modes.accumulator, 2],
// Indirect Zero Page for the masses
0x12: ["ORA", ora, readZeroPageIndirect, modes.zeroPageIndirect, 5],
0x32: ["AND", and, readZeroPageIndirect, modes.zeroPageIndirect, 5],
0x52: ["EOR", eor, readZeroPageIndirect, modes.zeroPageIndirect, 5],
0x72: ["ADC", adc, readZeroPageIndirect, modes.zeroPageIndirect, 5],
0x92: ["STA", sta, writeZeroPageIndirect, modes.zeroPageIndirect, 5],
0xB2: ["LDA", lda, readZeroPageIndirect, modes.zeroPageIndirect, 5],
0xD2: ["CMP", cmp, readZeroPageIndirect, modes.zeroPageIndirect, 5],
0xF2: ["SBC", sbc, readZeroPageIndirect, modes.zeroPageIndirect, 5],
0x12: ['ORA', ora, readZeroPageIndirect, modes.zeroPageIndirect, 5],
0x32: ['AND', and, readZeroPageIndirect, modes.zeroPageIndirect, 5],
0x52: ['EOR', eor, readZeroPageIndirect, modes.zeroPageIndirect, 5],
0x72: ['ADC', adc, readZeroPageIndirect, modes.zeroPageIndirect, 5],
0x92: ['STA', sta, writeZeroPageIndirect, modes.zeroPageIndirect, 5],
0xB2: ['LDA', lda, readZeroPageIndirect, modes.zeroPageIndirect, 5],
0xD2: ['CMP', cmp, readZeroPageIndirect, modes.zeroPageIndirect, 5],
0xF2: ['SBC', sbc, readZeroPageIndirect, modes.zeroPageIndirect, 5],
// Better BIT
0x34: ["BIT", bit, readZeroPageX, modes.zeroPageX, 4],
0x3C: ["BIT", bit, readAbsoluteX, modes.absoluteX, 4],
0x89: ["BIT", bitI, readImmediate, modes.immediate, 2],
0x34: ['BIT', bit, readZeroPageX, modes.zeroPageX, 4],
0x3C: ['BIT', bit, readAbsoluteX, modes.absoluteX, 4],
0x89: ['BIT', bitI, readImmediate, modes.immediate, 2],
// JMP absolute indirect indexed
0x6C: ["JMP", jmp, readAddrAbsoluteIndirect,
0x6C: ['JMP', jmp, readAddrAbsoluteIndirect,
modes.absoluteIndirect, 6],
0x7C: ["JMP", jmp, readAddrAbsoluteXIndirect,
0x7C: ['JMP', jmp, readAddrAbsoluteXIndirect,
modes.absoluteXIndirect, 6],
// BRA
0x80: ["BRA", brc, 0, modes.relative, 3],
0x80: ['BRA', brc, 0, modes.relative, 3],
// PHX
0xDA: ["PHX", phx, null, modes.implied, 3],
0xDA: ['PHX', phx, null, modes.implied, 3],
// PHY
0x5A: ["PHY", phy, null, modes.implied, 3],
0x5A: ['PHY', phy, null, modes.implied, 3],
// PLX
0xFA: ["PLX", plx, null, modes.implied, 3],
0xFA: ['PLX', plx, null, modes.implied, 3],
// PLY
0x7A: ["PLY", ply, null, modes.implied, 3],
0x7A: ['PLY', ply, null, modes.implied, 3],
// STZ
0x64: ["STZ", stz, writeZeroPage, modes.zeroPage, 3],
0x74: ["STZ", stz, writeZeroPageX, modes.zeroPageX, 4],
0x9C: ["STZ", stz, writeAbsolute, modes.absolute, 4],
0x9E: ["STZ", stz, writeAbsoluteX, modes.absoluteX, 5],
0x64: ['STZ', stz, writeZeroPage, modes.zeroPage, 3],
0x74: ['STZ', stz, writeZeroPageX, modes.zeroPageX, 4],
0x9C: ['STZ', stz, writeAbsolute, modes.absolute, 4],
0x9E: ['STZ', stz, writeAbsoluteX, modes.absoluteX, 5],
// TRB
0x14: ["TRB", trb, readAddrZeroPage, modes.zeroPage, 5],
0x1C: ["TRB", trb, readAddrAbsolute, modes.absolute, 6],
0x14: ['TRB', trb, readAddrZeroPage, modes.zeroPage, 5],
0x1C: ['TRB', trb, readAddrAbsolute, modes.absolute, 6],
// TSB
0x04: ["TSB", tsb, readAddrZeroPage, modes.zeroPage, 5],
0x0C: ["TSB", tsb, readAddrAbsolute, modes.absolute, 6]
0x04: ['TSB', tsb, readAddrZeroPage, modes.zeroPage, 5],
0x0C: ['TSB', tsb, readAddrAbsolute, modes.absolute, 6]
};
if (is65C02) {
for (var key in cops) {
if (cops.hasOwnProperty(key)) {
if (key in ops) {
debug('overriding opcode ' + toHex(key));
}
}
ops[key] = cops[key];
}
}
@ -1047,13 +1048,17 @@ function CPU6502(options)
var unk;
if (is65C02) {
unk = ["NOP (" + toHex(b) + ")", function() {
debug("Unknown OpCode: " + toHex(b) + " at " + toHex(pc - 1, 4));
unk = ['NOP (' + toHex(b) + ')', function() {
debug('Unknown OpCode: ' + toHex(b) + ' at ' + toHex(pc - 1, 4));
}, null, modes.implied, 2];
} else {
unk = ["???",
function() { /* debug("Unknown OpCode: " + toHex(b) +
" at " + toHex(pc - 1, 4)); */ },
unk = ['???',
function() {
/*
debug('Unknown OpCode: ' + toHex(b) +
' at ' + toHex(pc - 1, 4));
*/
},
null, modes.implied,
1];
}
@ -1073,21 +1078,21 @@ function CPU6502(options)
if (symbols && symbols[v]) {
return symbols[v];
} else {
return "$" + toHex(v, n);
return '$' + toHex(v, n);
}
}
var result = "";
var result = '';
switch (m) {
case modes.implied:
break;
case modes.immediate:
result = "#" + toHexOrSymbol(readByte(addr, true));
result = '#' + toHexOrSymbol(readByte(addr, true));
break;
case modes.absolute:
result = "" + toHexOrSymbol(readWord(addr, true), 4);
result = '' + toHexOrSymbol(readWord(addr, true), 4);
break;
case modes.zeroPage:
result = "" + toHexOrSymbol(readByte(addr, true));
result = '' + toHexOrSymbol(readByte(addr, true));
break;
case modes.relative:
{
@ -1096,38 +1101,38 @@ function CPU6502(options)
off -= 256;
}
addr += off + 1;
result = "" + toHexOrSymbol(addr, 4) + " (" + off + ")";
result = '' + toHexOrSymbol(addr, 4) + ' (' + off + ')';
}
break;
case modes.absoluteX:
result = "" + toHexOrSymbol(readWord(addr, true), 4) + ",X";
result = '' + toHexOrSymbol(readWord(addr, true), 4) + ',X';
break;
case modes.absoluteY:
result = "" + toHexOrSymbol(readWord(addr, true), 4) + ",Y";
result = '' + toHexOrSymbol(readWord(addr, true), 4) + ',Y';
break;
case modes.zeroPageX:
result = "" + toHexOrSymbol(readByte(addr, true)) + ",X";
result = '' + toHexOrSymbol(readByte(addr, true)) + ',X';
break;
case modes.zeroPageY:
result = "" + toHexOrSymbol(readByte(addr, true)) + ",Y";
result = '' + toHexOrSymbol(readByte(addr, true)) + ',Y';
break;
case modes.absoluteIndirect:
result = "(" + toHexOrSymbol(readWord(addr, true), 4) + ")";
result = '(' + toHexOrSymbol(readWord(addr, true), 4) + ')';
break;
case modes.zeroPageXIndirect:
result = "(" + toHexOrSymbol(readByte(addr, true)) + ",X)";
result = '(' + toHexOrSymbol(readByte(addr, true)) + ',X)';
break;
case modes.zeroPageIndirectY:
result = "(" + toHexOrSymbol(readByte(addr, true)) + "),Y";
result = '(' + toHexOrSymbol(readByte(addr, true)) + '),Y';
break;
case modes.accumulator:
result = "A";
result = 'A';
break;
case modes.zeroPageIndirect:
result = "(" + toHexOrSymbol(readByte(addr, true)) + ")";
result = '(' + toHexOrSymbol(readByte(addr, true)) + ')';
break;
case modes.absoluteXIndirect:
result = "(" + toHexOrSymbol(readWord(addr, true), 4) + ",X)";
result = '(' + toHexOrSymbol(readWord(addr, true), 4) + ',X)';
break;
default:
break;
@ -1179,8 +1184,9 @@ function CPU6502(options)
{
var op, end = cycles + c;
if (inCallback)
if (inCallback) {
return;
}
while (cycles < end) {
sync = true;
@ -1226,7 +1232,7 @@ function CPU6502(options)
/* IRQ - Interupt Request */
irq: function cpu_irq()
{
if (!(sr & flags.I)) {
if ((sr & flags.I) === 0) {
pushWord(pc);
pushByte(sr & ~flags.B);
if (is65C02) {
@ -1260,35 +1266,35 @@ function CPU6502(options)
var b = readByte(_pc, true),
op = ops[b],
size = sizes[op[3]],
result = toHex(_pc, 4) + "- ";
result = toHex(_pc, 4) + '- ';
if (symbols) {
if (symbols[_pc]) {
result += symbols[_pc] +
" ".substring(symbols[_pc].length);
' '.substring(symbols[_pc].length);
} else {
result += " ";
result += ' ';
}
}
for (var idx = 0; idx < 4; idx++) {
if (idx < size) {
result += toHex(readByte(_pc + idx, true)) + " ";
result += toHex(readByte(_pc + idx, true)) + ' ';
} else {
result += " ";
result += ' ';
}
}
if (op === undefined)
result += "??? (" + toHex(b) + ")";
result += '??? (' + toHex(b) + ')';
else
result += op[0] + " " + dumpArgs(_pc + 1, op[3], symbols);
result += op[0] + ' ' + dumpArgs(_pc + 1, op[3], symbols);
return result;
},
dumpPage: function(start, end) {
var result = "";
var result = '';
if (start === undefined) {
start = pc >> 8;
}
@ -1298,21 +1304,21 @@ function CPU6502(options)
for (var page = start; page <= end; page++) {
var b, idx, jdx;
for (idx = 0; idx < 16; idx++) {
result += toHex(page) + toHex(idx << 4) + ": ";
result += toHex(page) + toHex(idx << 4) + ': ';
for (jdx = 0; jdx < 16; jdx++) {
b = readByte(page * 256 + idx * 16 + jdx, true);
result += toHex(b) + " ";
result += toHex(b) + ' ';
}
result += " ";
result += ' ';
for (jdx = 0; jdx < 16; jdx++) {
b = readByte(page * 256 + idx * 16 + jdx, true) & 0x7f;
if (b >= 0x20 && b < 0x7f) {
result += String.fromCharCode(b);
} else {
result += ".";
result += '.';
}
}
result += "\n";
result += '\n';
}
}
return result;
@ -1367,20 +1373,20 @@ function CPU6502(options)
dumpRegisters: function() {
return toHex(pc, 4) +
"- A=" + toHex(ar) +
" X=" + toHex(xr) +
" Y=" + toHex(yr) +
" P=" + toHex(sr) +
" S=" + toHex(sp) +
" " +
(sr & flags.N ? "N" : "-") +
(sr & flags.V ? "V" : "-") +
"-" +
(sr & flags.B ? "B" : "-") +
(sr & flags.D ? "D" : "-") +
(sr & flags.I ? "I" : "-") +
(sr & flags.Z ? "Z" : "-") +
(sr & flags.C ? "C" : "-");
'- A=' + toHex(ar) +
' X=' + toHex(xr) +
' Y=' + toHex(yr) +
' P=' + toHex(sr) +
' S=' + toHex(sp) +
' ' +
(sr & flags.N ? 'N' : '-') +
(sr & flags.V ? 'V' : '-') +
'-' +
(sr & flags.B ? 'B' : '-') +
(sr & flags.D ? 'D' : '-') +
(sr & flags.I ? 'I' : '-') +
(sr & flags.Z ? 'Z' : '-') +
(sr & flags.C ? 'C' : '-');
},
read: function(page, off) {
@ -1392,4 +1398,3 @@ function CPU6502(options)
}
};
}

View File

@ -1,5 +1,4 @@
/* -*- mode: JavaScript; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* Copyright 2010-2013 Will Scullin <scullin@scullinsteel.com>
/* Copyright 2010-2016 Will Scullin <scullin@scullinsteel.com>
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
@ -10,18 +9,20 @@
* implied warranty.
*/
/*jshint browser: true */
/*globals extend: false, bytify: false, base64_encode: false, base64_decode: false, each: false */
/*exported DiskII */
/*globals bytify, each: false, extend: false
base64_encode, base64_decode
Uint8Array
*/
function DiskII(io, callbacks, slot)
{
"use strict";
'use strict';
slot = slot || 6;
var _drives = [
{ // Drive 1
format: "dsk",
format: 'dsk',
volume: 254,
tracks: [],
track: 0,
@ -31,7 +32,7 @@ function DiskII(io, callbacks, slot)
dirty: false
},
{ // Drive 2
format: "dsk",
format: 'dsk',
volume: 254,
tracks: [],
track: 0,
@ -229,11 +230,11 @@ function DiskII(io, callbacks, slot)
function _json_encode(drive, pretty) {
var cur = _drives[drive - 1];
var data = [];
var format = "dsk";
var format = 'dsk';
for (var t = 0; t < cur.tracks.length; t++) {
data[t] = [];
if (cur.format === "nib") {
format = "nib";
if (cur.format === 'nib') {
format = 'nib';
data[t] = base64_encode(cur.tracks[t]);
} else {
for (var s = 0; s < 0x10; s++) {
@ -241,12 +242,12 @@ function DiskII(io, callbacks, slot)
}
}
}
return JSON.stringify({"type": format,
"encoding": "base64",
"volume": cur.volume,
"data": data},
return JSON.stringify({'type': format,
'encoding': 'base64',
'volume': cur.volume,
'data': data},
null,
pretty ? " " : null);
pretty ? ' ' : null);
}
function _json_decode(drive, data) {
@ -385,7 +386,7 @@ function DiskII(io, callbacks, slot)
[ 1,-2,-1, 0]];
function setPhase(phase, on) {
_debug("phase " + phase + (on ? " on" : " off"));
_debug('phase ' + phase + (on ? ' on' : ' off'));
if (on) {
_cur.track += _phase_delta[_cur.phase][phase];
_cur.phase = phase;
@ -395,10 +396,10 @@ function DiskII(io, callbacks, slot)
if (_cur.track < 0x0)
_cur.track = 0x0;
/* _debug("Drive " + _drive +
", track " + toHex(_cur.track >> 1) +
" (" + toHex(_cur.track) + ")" +
" [" + (_cur.track % 4) + "/" + phase + "]"); */
/* _debug('Drive ' + _drive +
', track ' + toHex(_cur.track >> 1) +
' (' + toHex(_cur.track) + ')' +
' [' + (_cur.track % 4) + '/' + phase + ']'); */
}
}
@ -431,18 +432,18 @@ function DiskII(io, callbacks, slot)
break;
case LOC.DRIVEOFF:
_debug("Drive Off");
_debug('Drive Off');
_on = false;
if (callbacks.driveLight) { callbacks.driveLight(_drive, false); }
break;
case LOC.DRIVEON:
_debug("Drive On");
_debug('Drive On');
_on = true;
if (callbacks.driveLight) { callbacks.driveLight(_drive, true); }
break;
case LOC.DRIVE1:
_debug("Disk 1");
_debug('Disk 1');
_drive = 1;
_cur = _drives[_drive - 1];
if (_on && callbacks.driveLight) {
@ -451,7 +452,7 @@ function DiskII(io, callbacks, slot)
}
break;
case LOC.DRIVE2:
_debug("Disk 2");
_debug('Disk 2');
_drive = 2;
_cur = _drives[_drive - 1];
if (_on && callbacks.driveLight) {
@ -462,22 +463,22 @@ function DiskII(io, callbacks, slot)
case LOC.DRIVEREAD:
result = _readNext();
// _debug("read: " + toHex(result));
// _debug('read: ' + toHex(result));
break;
case LOC.DRIVEWRITE:
// _debug("write: " + toHex(val));
// _debug('write: ' + toHex(val));
if (val !== undefined) {
_writeNext(val);
}
break;
case LOC.DRIVEREADMODE:
_debug("Read Mode");
_debug('Read Mode');
_writeMode = false;
result = (_readNext() & 0x7f) | (_cur.readOnly ? 0x80 : 0x00);
break;
case LOC.DRIVEWRITEMODE:
_debug("Write Mode");
_debug('Write Mode');
_writeMode = true;
break;
default:
@ -650,10 +651,10 @@ function DiskII(io, callbacks, slot)
var fmt = disk.type, readOnly = disk.readOnly;
var data, t, s;
if (disk.encoding == "base64") {
if (disk.encoding == 'base64') {
data = [];
for (t = 0; t < disk.data.length; t++) {
if (fmt == "nib") {
if (fmt == 'nib') {
data[t] = base64_decode(disk.data[t]);
} else {
data[t] = [];
@ -667,7 +668,7 @@ function DiskII(io, callbacks, slot)
}
var cur = _drives[drive - 1];
// var v = (fmt === "dsk" ? data[0x11][0x00][0x06] : 0xfe);
// var v = (fmt === 'dsk' ? data[0x11][0x00][0x06] : 0xfe);
// if (v == 0x00) {
var v = disk.volume || 0xfe;
// }
@ -679,15 +680,15 @@ function DiskII(io, callbacks, slot)
for (t = 0; t < data.length; t++) {
var track = [];
if (fmt === "nib") {
if (fmt === 'nib') {
track = data[t];
} else {
for (s = 0; s < data[t].length; s++) {
var _s = 15 - s;
if (fmt === "po") { // ProDOS Order
if (fmt === 'po') { // ProDOS Order
extend(track,
_explodeSector(v, t, _PO[s], data[t][s]));
} else if (fmt === "dsk") { // DOS Order
} else if (fmt === 'dsk') { // DOS Order
extend(track,
_explodeSector(v, t, _DO[_s], data[t][_s]));
} else { // flat
@ -712,11 +713,11 @@ function DiskII(io, callbacks, slot)
var _cur = _drives[drive - 1];
var tracks = [];
var v = 254;
if (fmt === "do") {
fmt = "dsk";
if (fmt === 'do') {
fmt = 'dsk';
}
_cur.readOnly = false;
if (fmt === "2mg") {
if (fmt === '2mg') {
// Standard header size is 64 bytes. Make assumptions.
var prefix = new Uint8Array(data.slice(0, 64));
data = data.slice(64);
@ -725,16 +726,16 @@ function DiskII(io, callbacks, slot)
// Sure, it's really 64 bits. But only 2 are actually used.
switch (prefix[0xc]) {
case 0:
fmt = "dsk";
fmt = 'dsk';
break;
case 1:
fmt = "po";
fmt = 'po';
break;
case 2:
fmt = "nib";
fmt = 'nib';
break;
default: // Something hinky, assume "dsk"
fmt = "dsk";
default: // Something hinky, assume 'dsk'
fmt = 'dsk';
break;
}
var flags =
@ -749,19 +750,19 @@ function DiskII(io, callbacks, slot)
}
for (var t = 0; t < 35; t++) {
var track, off, d;
if (fmt === "nib") {
if (fmt === 'nib') {
off = t * 0x1a00;
track = new Uint8Array(data.slice(off, off + 0x1a00));
} else {
track = [];
for (var s = 0; s < 16; s++) {
var _s = 15 - s;
if (fmt == "po") { // ProDOS Order
if (fmt == 'po') { // ProDOS Order
off = (16 * t + s) * 256;
d = new Uint8Array(data.slice(off, off + 256));
extend(track,
_explodeSector(v, t, _PO[s], d));
} else if (fmt == "dsk") { // DOS Order
} else if (fmt == 'dsk') { // DOS Order
off = (16 * t + _s) * 256;
d = new Uint8Array(data.slice(off, off + 256));
extend(track,
@ -788,7 +789,7 @@ function DiskII(io, callbacks, slot)
var idx = 0;
for (var t = 0; t < cur.tracks.length; t++) {
if (cur.format === "nib") {
if (cur.format === 'nib') {
data[idx++] = cur.tracks[t];
} else {
for (var s = 0; s < 0x10; s++) {
@ -808,7 +809,7 @@ function DiskII(io, callbacks, slot)
var data = [];
for (var t = 0; t < cur.tracks.length; t++) {
data[t] = [];
if (cur.format === "nib") {
if (cur.format === 'nib') {
data += base64_encode(cur.tracks[t]);
} else {
for (var s = 0; s < 0x10; s++) {

View File

@ -1,5 +1,3 @@
/* -*- mode: JavaScript; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*global bytify:false */
/*exported Apple2ROM */
function Apple2ROM()
{
@ -1542,8 +1540,6 @@ function Apple2ROM()
0xf5,0x03,0xfb,0x03,0x62,0xfa,0x59,0xff
];
rom = bytify(rom);
return {
start: function() {
return 0xd0;

View File

@ -1,4 +1,4 @@
/* -*- mode: JavaScript; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*exported IntBASIC */
function IntBASIC()
{
@ -1551,8 +1551,7 @@ function IntBASIC()
read: function(page, off) {
return rom[((page - 0xD0) << 8) + off];
},
write: function(page, off, val) {
write: function() {
}
};
}

View File

@ -1,5 +1,4 @@
/* -*- mode: JavaScript; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* Copyright 2010-2013 Will Scullin <scullin@scullinsteel.com>
/* Copyright 2010-2016 Will Scullin <scullin@scullinsteel.com>
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
@ -10,10 +9,12 @@
* implied warranty.
*/
/*globals RAM */
/*exported LanguageCard */
/*globals RAM: false */
function LanguageCard(io, rom) {
'use strict';
var _io = io;
var _rom = rom;
var _bank1 = null;
@ -88,28 +89,28 @@ function LanguageCard(io, rom) {
_readbsr = true;
_writebsr = false;
_bsr2 = true;
_debug("Bank 2 Read");
_debug('Bank 2 Read');
break;
case LOC.WRITEBSR2:
case LOC._WRITEBSR2:
_readbsr = false;
_writebsr = ((_last & 0xF3) == (off & 0xF3));
_bsr2 = true;
_debug("Bank 2 Write");
_debug('Bank 2 Write');
break;
case LOC.OFFBSR2:
case LOC._OFFBSR2:
_readbsr = false;
_writebsr = false;
_bsr2 = true;
_debug("Bank 2 Off");
_debug('Bank 2 Off');
break;
case LOC.READWRBSR2:
case LOC._READWRBSR2:
_readbsr = true;
_writebsr = ((_last & 0xF3) == (off & 0xF3));
_bsr2 = true;
_debug("Bank 2 Read/Write");
_debug('Bank 2 Read/Write');
break;
case LOC.READBSR1:
@ -117,37 +118,37 @@ function LanguageCard(io, rom) {
_readbsr = true;
_writebsr = false;
_bsr2 = false;
_debug("Bank 1 Read");
_debug('Bank 1 Read');
break;
case LOC.WRITEBSR1:
case LOC._WRITEBSR1:
_readbsr = false;
_writebsr = ((_last & 0xF3) == (off & 0xF3));
_bsr2 = false;
_debug("Bank 1 Write");
_debug('Bank 1 Write');
break;
case LOC.OFFBSR1:
case LOC._OFFBSR1:
_readbsr = false;
_writebsr = false;
_bsr2 = false;
_debug("Bank 1 Off");
_debug('Bank 1 Off');
break;
case LOC.READWRBSR1:
case LOC._READWRBSR1:
_readbsr = true;
_writebsr = ((_last & 0xF3) == (off & 0xF3));
_bsr2 = false;
_debug("Bank 1 Read/Write");
_debug('Bank 1 Read/Write');
break;
case LOC.BSRBANK2:
result = _bsr2 ? 0x80 : 0x00;
_debug("Bank 2 Read " + _bsr2);
_debug('Bank 2 Read ' + _bsr2);
break;
case LOC.BSRREADRAM:
result = _readbsr ? 0x80 : 0x00;
_debug("Bank SW RAM Read " + _readbsr);
_debug('Bank SW RAM Read ' + _readbsr);
break;
default:
break;

876
js/main2.js Normal file
View File

@ -0,0 +1,876 @@
/* globals debug: false, gup: false, hup: false
CPU6502: false,
RAM: false,
Apple2ROM: false
apple2_charset: false,
Apple2IO: false
LoresPage: false, HiresPage: false, VideoModes: false
KeyBoard: false,
Parallel: false,
DiskII: false,
RAMFactor: false,
Printer: false,
LanguageCard: false,
Thunderclock: false,
Prefs: false,
disk_index: false,
initAudio: false, enableSound: false,
initGamepad: false, processGamepad: false, gamepad: false,
ApplesoftDump: false
*/
/* exported openLoad, openSave, doDelete,
selectCategory, selectDisk, clickDisk, loadJSON,
updateJoystick,
pauseRun, step
dumpProgram, PageDebug
*/
var kHz = 1023;
var focused = false;
var startTime = Date.now();
var lastCycles = 0;
var renderedFrames = 0, lastFrames = 0;
var paused = false;
var hashtag;
var disk_categories = {'Local Saves': []};
var disk_sets = {};
var disk_cur_name = [];
var disk_cur_cat = [];
function DriveLights()
{
return {
driveLight: function(drive, on) {
$('#disk' + drive).css('background-image',
on ? 'url(css/red-on-16.png)' :
'url(css/red-off-16.png)');
},
dirty: function(drive, dirty) {
$('#disksave' + drive).button('option', 'disabled', !dirty);
},
label: function(drive, label) {
if (label) {
$('#disklabel' + drive).text(label);
}
return $('#disklabel' + drive).text();
},
getState: function() {
return {
disks: [
this.label(1),
this.label(2)
]
};
},
setState: function(state) {
if (state && state.disks) {
this.label(1, state.disks[0].label);
this.label(2, state.disks[1].label);
}
}
};
}
var DISK_TYPES = ['dsk','do','po','raw','nib','2mg'];
var TAPE_TYPES = ['wav','aiff','aif','mp3'];
var _saveDrive = 1;
var _loadDrive = 1;
function openLoad(drive, event)
{
_loadDrive = drive;
if (event.metaKey) {
openLoadHTTP(drive);
} else {
if (disk_cur_cat[drive]) {
$('#category_select').val(disk_cur_cat[drive]).change();
}
$('#load').dialog('open');
}
}
function openSave(drive, event)
{
_saveDrive = drive;
if (event.metaKey) {
dumpDisk(drive);
} else if (event.altKey) {
openSaveLocal(drive);
} else {
$('#save_name').val(drivelights.label(drive));
$('#save').dialog('open');
}
}
var loading = false;
function loadAjax(url) {
loading = true;
$('#loading').dialog('open');
$.ajax({ url: url,
cache: false,
dataType: 'jsonp',
jsonp: false,
global: false
});
}
function doLoad() {
var urls = $('#disk_select').val(), url;
if (urls && urls.length) {
if (typeof(urls) == 'string') {
url = urls;
} else {
url = urls[0];
}
}
var files = $('#local_file').prop('files');
if (files.length == 1) {
doLoadLocal();
} else if (url) {
var filename;
$('#load').dialog('close');
if (url.substr(0,6) == 'local:') {
filename = url.substr(6);
if (filename == '__manage') {
openManage();
} else {
loadLocalStorage(_loadDrive, filename);
}
} else {
var r1 = /json\/disks\/(.*).json$/.exec(url);
if (r1 && _loadDrive == 1) {
filename = r1[1];
document.location.hash = filename;
}
loadAjax(url);
}
}
}
function doSave() {
var name = $('#save_name').val();
saveLocalStorage(_saveDrive, name);
$('#save').dialog('close');
}
function doDelete(name) {
if (window.confirm('Delete ' + name + '?')) {
deleteLocalStorage(name);
}
}
function doLoadLocal() {
var files = $('#local_file').prop('files');
if (files.length == 1) {
var file = files[0];
var parts = file.name.split('.');
var ext = parts[parts.length - 1].toLowerCase();
if ($.inArray(ext, DISK_TYPES) >= 0) {
doLoadLocalDisk(file);
} else if ($.inArray(ext, TAPE_TYPES) >= 0) {
doLoadLocalTape(file);
} else {
window.alert('Unknown file type: ' + ext);
$('#load').dialog('close');
}
}
}
function doLoadLocalDisk(file) {
var fileReader = new FileReader();
fileReader.onload = function() {
var parts = file.name.split('.');
var name = parts[0], ext = parts[parts.length - 1].toLowerCase();
if (disk2.setBinary(_saveDrive, name, ext, this.result)) {
drivelights.label(_saveDrive, name);
$('#load').dialog('close');
initGamepad();
}
};
fileReader.readAsArrayBuffer(file);
}
function doLoadLocalTape(file) {
// Audio Buffer Source
var context;
if (typeof window.AudioContext != 'undefined') {
context = new window.AudioContext();
} else {
window.alert('Not supported by your browser');
$('#load').dialog('close');
return;
}
var fileReader = new FileReader();
fileReader.onload = function(ev) {
context.decodeAudioData(ev.target.result, function(buffer) {
var buf = [];
var data = buffer.getChannelData(0), datum = data[0];
var old = (datum > 0.0), current;
var last = 0, delta, ival;
debug('Sample Count: ' + data.length);
debug('Sample rate: ' + buffer.sampleRate);
for (var idx = 1; idx < data.length; idx++) {
datum = data[idx];
if ((datum > 0.1) || (datum < -0.1)) {
current = (datum > 0.0);
if (current != old) {
delta = idx - last;
if (delta > 2000000) {
delta = 2000000;
}
ival = delta / buffer.sampleRate * 1000;
if (ival >= 0.550 && ival < 0.750) {
ival = 0.650; // Header
} else if (ival >= 0.175 && ival < 0.225) {
ival = 0.200; // sync 1
} else if (ival >= 0.225 && ival < 0.275) {
ival = 0.250; // 0 / sync 2
} else if (ival >= 0.450 && ival < 0.550) {
ival = 0.500; // 1
} else {
// debug(idx + ' ' + buf.length + ' ' + ival);
}
buf.push(parseInt(ival * kHz));
old = current;
last = idx;
}
}
}
io.setTape(buf);
$('#load').dialog('close');
});
};
fileReader.readAsArrayBuffer(file);
}
function doLoadHTTP(_url) {
var url = _url || $('#http_url').val();
if (url) {
var req = new XMLHttpRequest();
req.open('GET', url, true);
req.responseType = 'arraybuffer';
req.onload = function() {
var parts = url.split(/[\/\.]/);
var name = decodeURIComponent(parts[parts.length - 2]);
var ext = parts[parts.length - 1].toLowerCase();
if (disk2.setBinary(_saveDrive, name, ext, req.response)) {
drivelights.label(_saveDrive, name);
$('#http_load').dialog('close');
initGamepad();
}
};
req.send(null);
}
}
function openLoadHTTP(drive) {
_saveDrive = drive;
$('#http_load').dialog('open');
}
function openSaveLocal(drive) {
_saveDrive = drive;
var mimetype = 'application/octet-stream';
var data = disk2.getBinary(drive);
var a = $('#local_save_link');
var blob = new Blob([data], { 'type': mimetype });
a.attr('href', window.URL.createObjectURL(blob));
a.attr('download', drivelights.label(drive) + '.dsk');
$('#local_save').dialog('open');
}
function openManage() {
$('#manage').dialog('open');
}
var prefs = new Prefs();
var runTimer = null;
var cpu = new CPU6502();
var ram1 = new RAM(0x00, 0x03),
ram2 = new RAM(0x0C, 0x1F),
ram3 = new RAM(0x60, 0xBF);
var hgr = new HiresPage(1);
var hgr2 = new HiresPage(2);
var gr = new LoresPage(1, apple2_charset);
var gr2 = new LoresPage(2, apple2_charset);
var rom = new Apple2ROM();
var dumper = new ApplesoftDump(ram2);
var vm = new VideoModes(gr, hgr, gr2, hgr2);
var mmu = {
auxRom: function(slot, rom) {
cpu.addPageHandler(rom);
}
};
var drivelights = new DriveLights();
var io = new Apple2IO(cpu, vm);
var keyboard = new KeyBoard(io);
var parallel = new Parallel(io, new Printer(), 1);
var disk2 = new DiskII(io, drivelights, 6);
var slinky = new RAMFactor(mmu, io, 2, 1024 * 1024);
var clock = new Thunderclock(mmu, io, 7);
var lc = new LanguageCard(io, rom);
cpu.addPageHandler(ram1);
cpu.addPageHandler(gr);
cpu.addPageHandler(gr2);
cpu.addPageHandler(ram2);
cpu.addPageHandler(hgr);
cpu.addPageHandler(hgr2);
cpu.addPageHandler(ram3);
cpu.addPageHandler(lc);
cpu.addPageHandler(io);
cpu.addPageHandler(parallel);
cpu.addPageHandler(slinky);
cpu.addPageHandler(disk2);
cpu.addPageHandler(clock);
var showFPS = false;
function updateKHz() {
var now = Date.now();
var ms = now - startTime;
var cycles = cpu.cycles();
var delta;
if (showFPS) {
delta = renderedFrames - lastFrames;
var fps = parseInt(delta/(ms/1000), 10);
$('#khz').text( fps + 'fps');
} else {
delta = cycles - lastCycles;
var khz = parseInt(delta/ms);
$('#khz').text( khz + 'KHz');
}
startTime = now;
lastCycles = cycles;
lastFrames = renderedFrames;
}
/* Audio Handling */
initAudio(io);
function updateSound() {
enableSound($('#enable_sound').attr('checked'));
}
function dumpDisk(drive) {
var wind = window.open('', '_blank');
wind.document.title = drivelights.label(drive);
wind.document.write('<pre>');
wind.document.write(disk2.getJSON(drive, true));
wind.document.write('</pre>');
wind.document.close();
}
function dumpProgram() {
var wind = window.open('', '_blank');
wind.document.title = 'Program Listing';
wind.document.write('<pre>');
wind.document.write(dumper.toString());
wind.document.write('</pre>');
wind.document.close();
}
function step()
{
if (runTimer) {
clearInterval(runTimer);
}
runTimer = null;
cpu.step(function() {
debug(cpu.dumpRegisters());
debug(cpu.dumpPC());
});
}
var accelerated = false;
function updateSpeed()
{
accelerated = $('#accelerator_toggle').prop('checked');
kHz = accelerated ? 4092 : 1023;
io.updateHz(kHz * 1000);
if (runTimer) {
run();
}
}
var _requestAnimationFrame =
window.requestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.msRequestAnimationFrame;
function run(pc) {
if (runTimer) {
clearInterval(runTimer);
}
if (pc) {
cpu.setPC(pc);
}
var ival = 30;
var now, last = Date.now();
var runFn = function() {
now = Date.now();
renderedFrames++;
var step = (now - last) * kHz, stepMax = kHz * ival;
last = now;
if (step > stepMax) {
step = stepMax;
}
if (document.location.hash != hashtag) {
hashtag = document.location.hash;
var hash = hup();
if (hash) {
processHash(hash);
}
}
if (!loading) {
cpu.stepCycles(step);
vm.blit();
io.sampleTick();
}
processGamepad(io);
if (!paused && _requestAnimationFrame) {
_requestAnimationFrame(runFn);
}
};
if (_requestAnimationFrame) {
_requestAnimationFrame(runFn);
} else {
runTimer = setInterval(runFn, ival);
}
}
function stop() {
if (runTimer) {
clearInterval(runTimer);
}
runTimer = null;
}
function reset()
{
cpu.reset();
}
function loadBinary(bin) {
stop();
for (var idx = 0; idx < bin.length; idx++) {
var pos = bin.start + idx;
cpu.write(pos >> 8, pos & 0xff, bin.data[idx]);
}
run(bin.start);
}
function selectCategory() {
$('#disk_select').empty();
var cat = disk_categories[$('#category_select').val()];
if (cat) {
for (var idx = 0; idx < cat.length; idx++) {
var file = cat[idx], name = file.name;
if (file.disk) {
name += ' - ' + file.disk;
}
var option = $('<option />').val(file.filename).text(name)
.appendTo('#disk_select');
if (disk_cur_name[_loadDrive] == name) {
option.attr('selected', 'selected');
}
}
}
}
function selectDisk() {
$('#local_file').val('');
}
function clickDisk() {
doLoad();
}
function loadDisk(disk) {
var name = disk.name;
var category = disk.category;
if (disk.disk) {
name += ' - ' + disk.disk;
}
disk_cur_cat[_loadDrive] = category;
disk_cur_name[_loadDrive] = name;
drivelights.label(_loadDrive, name);
disk2.setDisk(_loadDrive, disk);
initGamepad(disk.gamepad);
}
function loadJSON(data) {
if (data.type == 'binary') {
loadBinary(data);
} else if ($.inArray(data.type, DISK_TYPES) >= 0) {
loadDisk(data);
}
initGamepad(data.gamepad);
$('#loading').dialog('close');
loading = false;
}
/*
* LocalStorage Disk Storage
*/
function updateLocalStorage() {
var diskIndex = JSON.parse(window.localStorage.diskIndex || '{}');
var names = [], name, cat;
for (name in diskIndex) {
if (diskIndex.hasOwnProperty(name)) {
names.push(name);
}
}
cat = disk_categories['Local Saves'] = [];
$('#manage').empty();
names.forEach(function(name) {
cat.push({'category': 'Local Saves',
'name': name,
'filename': 'local:' + name});
$('#manage').append('<span class="local_save">' +
name +
' <a href="#" onclick="doDelete(\'' +
name +
'\')">Delete</a><br /></span>');
});
cat.push({'category': 'Local Saves',
'name': 'Manage Saves...',
'filename': 'local:__manage'});
}
function saveLocalStorage(drive, name) {
var diskIndex = JSON.parse(window.localStorage.diskIndex || '{}');
var json = disk2.getJSON(drive);
diskIndex[name] = json;
window.localStorage.diskIndex = JSON.stringify(diskIndex);
window.alert('Saved');
drivelights.label(drive, name);
drivelights.dirty(drive, false);
updateLocalStorage();
}
function deleteLocalStorage(name) {
var diskIndex = JSON.parse(window.localStorage.diskIndex || '{}');
if (diskIndex[name]) {
delete diskIndex[name];
window.alert('Deleted');
}
window.localStorage.diskIndex = JSON.stringify(diskIndex);
updateLocalStorage();
}
function loadLocalStorage(drive, name) {
var diskIndex = JSON.parse(window.localStorage.diskIndex || '{}');
if (diskIndex[name]) {
disk2.setJSON(drive, diskIndex[name]);
drivelights.label(drive, name);
drivelights.dirty(drive, false);
}
}
function processHash(hash) {
if (hash.indexOf('://') > 0) {
var parts = hash.split('.');
var ext = parts[parts.length - 1].toLowerCase();
if (ext == 'json') {
loadAjax(hash);
} else {
doLoadHTTP(hash);
}
} else {
loadAjax('json/disks/' + hash + '.json');
}
}
/*
* Keyboard/Gamepad routines
*/
function _keydown(evt) {
if (evt.keyCode === 112) { // F1 - Reset
cpu.reset();
} else if (evt.keyCode === 113) { // F2 - Full Screen
var elem = document.getElementById('screen');
if (document.webkitCancelFullScreen) {
if (document.webkitIsFullScreen) {
document.webkitCancelFullScreen();
} else {
if (Element.ALLOW_KEYBOARD_INPUT) {
elem.webkitRequestFullScreen(Element.ALLOW_KEYBOARD_INPUT);
} else {
elem.webkitRequestFullScreen();
}
}
} else if (document.mozCancelFullScreen) {
if (document.mozIsFullScreen) {
document.mozCancelFullScreen();
} else {
elem.mozRequestFullScreen();
}
}
} else if (evt.keyCode == 16) { // Shift
keyboard.shiftKey(true);
io.buttonDown(2);
} else if (evt.keyCode == 17) { // Control
keyboard.controlKey(true);
} else if (!focused && (!evt.metaKey || evt.ctrlKey)) {
evt.preventDefault();
var key = keyboard.mapKeyEvent(evt);
if (key != 0xff) {
io.keyDown(key);
}
}
}
function _keyup(evt) {
if (evt.keyCode == 16) { // Shift
keyboard.shiftKey(false);
io.buttonUp(2);
} else if (evt.keyCode == 17) { // Control
keyboard.controlKey(false);
} else {
if (!focused) {
io.keyUp();
}
}
}
function updateScreen() {
var green = $('#green_screen').prop('checked');
vm.green(green);
}
var disableMouseJoystick = false;
var flipX = false;
var flipY = false;
var swapXY = false;
function updateJoystick() {
disableMouseJoystick = $('#disable_mouse').prop('checked');
flipX = $('#flip_x').prop('checked');
flipY = $('#flip_y').prop('checked');
swapXY = $('#swap_x_y').prop('checked');
if (disableMouseJoystick) {
io.paddle(0, 0.5);
io.paddle(1, 0.5);
return;
}
}
function _mousemove(evt) {
if (gamepad || disableMouseJoystick) {
return;
}
var s = $('#screen');
var offset = s.offset();
var x = (evt.pageX - offset.left) / s.width(),
y = (evt.pageY - offset.top) / s.height(),
z = x;
if (swapXY) {
x = y;
y = z;
}
io.paddle(0, flipX ? 1 - x : x);
io.paddle(1, flipY ? 1 - y : y);
}
function pauseRun(b) {
if (paused) {
run();
b.value = 'Pause';
} else {
stop();
b.value = 'Run';
}
paused = !paused;
}
$(function() {
hashtag = document.location.hash;
$('button,input[type=button],a.button').button().focus(function() {
// Crazy hack required by Chrome
var self = this;
window.setTimeout(function() {
self.blur();
}, 1);
});
var canvas = document.getElementById('screen');
var context = canvas.getContext('2d');
vm.setContext(context);
/*
* Input Handling
*/
$(window).keydown(_keydown);
$(window).keyup(_keyup);
$('canvas').mousedown(function(evt) {
if (!gamepad) {
io.buttonDown(evt.which == 1 ? 0 : 1);
}
evt.preventDefault();
})
.mouseup(function(evt) {
if (!gamepad) {
io.buttonUp(evt.which == 1 ? 0 : 1);
}
})
.bind('contextmenu', function(evt) { evt.preventDefault(); });
$('body').mousemove(_mousemove);
$('input,textarea').focus(function() { focused = true; })
.blur(function() { focused = false; });
keyboard.create($('#keyboard'));
if (prefs.havePrefs()) {
$('input[type=checkbox]').each(function() {
var val = prefs.readPref(this.id);
if (val)
this.checked = JSON.parse(val);
}).change(function() {
prefs.writePref(this.id, JSON.stringify(this.checked));
});
}
reset();
run();
setInterval(updateKHz, 1000);
updateSound();
updateScreen();
updateSpeed();
var cancel = function() { $(this).dialog('close'); };
$('#loading').dialog({ autoOpen: false, modal: true });
$('#options').dialog({ autoOpen: false,
modal: true,
width: 320,
height: 400,
buttons: {'Close': cancel }});
$('#load').dialog({ autoOpen: false,
modal: true,
width: 540,
buttons: {'Cancel': cancel, 'Load': doLoad }});
$('#save').dialog({ autoOpen: false,
modal: true,
width: 320,
buttons: {'Cancel': cancel, 'Save': doSave }});
$('#manage').dialog({ autoOpen: false,
modal: true,
width: 320,
buttons: {'Close': cancel }});
$('#local_save').dialog({ autoOpen: false,
modal: true,
width: 530,
buttons: {'OK': cancel }});
$('#http_load').dialog({ autoOpen: false,
modal: true,
width: 530,
buttons: {'Cancel': cancel, 'OK': doLoadHTTP }});
if (window.localStorage !== undefined) {
$('.disksave').show();
}
var oldcat = '';
for (var idx = 0; idx < disk_index.length; idx++) {
var file = disk_index[idx];
var cat = file.category;
var name = file.name, disk = file.disk;
if (file.e) {
continue;
}
if (cat != oldcat) {
$('<option />').val(cat).text(cat).appendTo('#category_select');
disk_categories[cat] = [];
oldcat = cat;
}
disk_categories[cat].push(file);
if (disk) {
if (!disk_sets[name]) {
disk_sets[name] = [];
}
disk_sets[name].push(file);
}
}
$('<option/>').text('Local Saves').appendTo('#category_select');
updateLocalStorage();
initGamepad();
// Check for disks in hashtag
var hash = gup('disk') || hup();
if (hash) {
processHash(hash);
}
if (navigator.userAgent.match(/(iPod|iPhone|iPad)/)) {
$('select').removeAttr('multiple').css('height', 'auto');
}
if (navigator.standalone) {
$('body').addClass('standalone');
}
$('.key-REPT').click(function() {
$('#keyboard').hide();
$('#textarea').show();
$('#text_input').focus();
});
$('#text_input').keydown(function() {
focused = $('#buffering').prop('checked');
}).keyup(function() {
focused = $('#buffering').prop('checked');
});
});

877
js/main2e.js Normal file
View File

@ -0,0 +1,877 @@
/* globals debug: false, gup: false, hup: false,
CPU6502: false,
Apple2eROM: false, Apple2eEnhancedROM: false,
apple2e_charset: false,
Apple2IO: false
LoresPage: false, HiresPage: false, VideoModes: false
KeyBoard: false,
Parallel: false,
DiskII: false,
RAMFactor: false,
Printer: false,
MMU: false,
Slot3: false,
Thunderclock: false,
Prefs: false,
disk_index: false,
initAudio: false, enableSound: false,
initGamepad: false, processGamepad: false, gamepad: false,
ApplesoftDump: false
*/
/* exported openLoad, openSave, doDelete,
selectCategory, selectDisk, clickDisk, loadJSON,
updateJoystick,
pauseRun, step,
restoreState, saveState,
dumpProgram, PageDebug
*/
var kHz = 1023;
var focused = false;
var startTime = Date.now();
var lastCycles = 0;
var renderedFrames = 0, lastFrames = 0;
var paused = false;
var hashtag;
var disk_categories = {'Local Saves': []};
var disk_sets = {};
var disk_cur_name = [];
var disk_cur_cat = [];
function DriveLights()
{
return {
driveLight: function(drive, on) {
$('#disk' + drive).css('background-image',
on ? 'url(css/red-on-16.png)' :
'url(css/red-off-16.png)');
},
dirty: function(drive, dirty) {
$('#disksave' + drive).button('option', 'disabled', !dirty);
},
label: function(drive, label) {
if (label) {
$('#disklabel' + drive).text(label);
}
return $('#disklabel' + drive).text();
},
getState: function() {
return {
disks: [
this.label(1),
this.label(2)
]
};
},
setState: function(state) {
if (state && state.disks) {
this.label(1, state.disks[0].label);
this.label(2, state.disks[1].label);
}
}
};
}
var DISK_TYPES = ['dsk','do','po','raw','nib','2mg'];
var TAPE_TYPES = ['wav','aiff','aif','mp3'];
var _saveDrive = 1;
var _loadDrive = 1;
function openLoad(drive, event)
{
_loadDrive = drive;
if (event.metaKey) {
openLoadHTTP(drive);
} else {
if (disk_cur_cat[drive]) {
$('#category_select').val(disk_cur_cat[drive]).change();
}
$('#load').dialog('open');
}
}
function openSave(drive, event)
{
_saveDrive = drive;
if (event.metaKey) {
dumpDisk(drive);
} else if (event.altKey) {
openSaveLocal(drive);
} else {
$('#save_name').val(drivelights.label(drive));
$('#save').dialog('open');
}
}
var loading = false;
function loadAjax(url) {
loading = true;
$('#loading').dialog('open');
$.ajax({ url: url,
cache: false,
dataType: 'jsonp',
jsonp: false,
global: false
});
}
function doLoad() {
var urls = $('#disk_select').val(), url;
if (urls && urls.length) {
if (typeof(urls) == 'string') {
url = urls;
} else {
url = urls[0];
}
}
var files = $('#local_file').prop('files');
if (files.length == 1) {
doLoadLocal();
} else if (url) {
var filename;
$('#load').dialog('close');
if (url.substr(0,6) == 'local:') {
filename = url.substr(6);
if (filename == '__manage') {
openManage();
} else {
loadLocalStorage(_loadDrive, filename);
}
} else {
var r1 = /json\/disks\/(.*).json$/.exec(url);
if (r1 && _loadDrive == 1) {
filename = r1[1];
document.location.hash = filename;
}
loadAjax(url);
}
}
}
function doSave() {
var name = $('#save_name').val();
saveLocalStorage(_saveDrive, name);
$('#save').dialog('close');
}
function doDelete(name) {
if (window.confirm('Delete ' + name + '?')) {
deleteLocalStorage(name);
}
}
function doLoadLocal() {
var files = $('#local_file').prop('files');
if (files.length == 1) {
var file = files[0];
var parts = file.name.split('.');
var ext = parts[parts.length - 1].toLowerCase();
if ($.inArray(ext, DISK_TYPES) >= 0) {
doLoadLocalDisk(file);
} else if ($.inArray(ext, TAPE_TYPES) >= 0) {
doLoadLocalTape(file);
} else {
window.alert('Unknown file type: ' + ext);
$('#load').dialog('close');
}
}
}
function doLoadLocalDisk(file) {
var fileReader = new FileReader();
fileReader.onload = function() {
var parts = file.name.split('.');
var name = parts[0], ext = parts[parts.length - 1].toLowerCase();
if (disk2.setBinary(_saveDrive, name, ext, this.result)) {
drivelights.label(_saveDrive, name);
$('#load').dialog('close');
initGamepad();
}
};
fileReader.readAsArrayBuffer(file);
}
function doLoadLocalTape(file) {
// Audio Buffer Source
var context;
if (typeof window.AudioContext != 'undefined') {
context = new window.AudioContext();
} else {
window.alert('Not supported by your browser');
$('#load').dialog('close');
return;
}
var fileReader = new FileReader();
fileReader.onload = function(ev) {
context.decodeAudioData(ev.target.result, function(buffer) {
var buf = [];
var data = buffer.getChannelData(0), datum = data[0];
var old = (datum > 0.0), current;
var last = 0, delta, ival;
debug('Sample Count: ' + data.length);
debug('Sample rate: ' + buffer.sampleRate);
for (var idx = 1; idx < data.length; idx++) {
datum = data[idx];
if ((datum > 0.1) || (datum < -0.1)) {
current = (datum > 0.0);
if (current != old) {
delta = idx - last;
if (delta > 2000000) {
delta = 2000000;
}
ival = delta / buffer.sampleRate * 1000;
if (ival >= 0.550 && ival < 0.750) {
ival = 0.650; // Header
} else if (ival >= 0.175 && ival < 0.225) {
ival = 0.200; // sync 1
} else if (ival >= 0.225 && ival < 0.275) {
ival = 0.250; // 0 / sync 2
} else if (ival >= 0.450 && ival < 0.550) {
ival = 0.500; // 1
} else {
// debug(idx + ' ' + buf.length + ' ' + ival);
}
buf.push(parseInt(ival * kHz));
old = current;
last = idx;
}
}
}
io.setTape(buf);
$('#load').dialog('close');
});
};
fileReader.readAsArrayBuffer(file);
}
function doLoadHTTP(_url) {
var url = _url || $('#http_url').val();
if (url) {
var req = new XMLHttpRequest();
req.open('GET', url, true);
req.responseType = 'arraybuffer';
req.onload = function() {
var parts = url.split(/[\/\.]/);
var name = decodeURIComponent(parts[parts.length - 2]);
var ext = parts[parts.length - 1].toLowerCase();
if (disk2.setBinary(_saveDrive, name, ext, req.response)) {
drivelights.label(_saveDrive, name);
$('#http_load').dialog('close');
initGamepad();
}
};
req.send(null);
}
}
function openLoadHTTP(drive) {
_saveDrive = drive;
$('#http_load').dialog('open');
}
function openSaveLocal(drive) {
_saveDrive = drive;
var mimetype = 'application/octet-stream';
var data = disk2.getBinary(drive);
var a = $('#local_save_link');
var blob = new Blob([data], { 'type': mimetype });
a.attr('href', window.URL.createObjectURL(blob));
a.attr('download', drivelights.label(drive) + '.dsk');
$('#local_save').dialog('open');
}
function openManage() {
$('#manage').dialog('open');
}
var prefs = new Prefs();
var enhanced = true;
var runTimer = null;
var cpu = new CPU6502({'65C02': enhanced});
var hgr = new HiresPage(1);
var hgr2 = new HiresPage(2);
var gr = new LoresPage(1, apple2e_charset);
var gr2 = new LoresPage(2, apple2e_charset);
var rom;
if (enhanced) {
rom = new Apple2eEnhancedROM();
} else {
rom = new Apple2eROM();
}
var vm = new VideoModes(gr, hgr, gr2, hgr2);
var drivelights = new DriveLights();
var io = new Apple2IO(cpu, vm);
var keyboard = new KeyBoard(io);
var mmu = new MMU(cpu, gr, gr2, hgr, hgr2, io, rom);
var dumper = new ApplesoftDump(mmu);
var parallel = new Parallel(io, new Printer(), 1);
var disk2 = new DiskII(io, drivelights, 6);
var slot3 = new Slot3(mmu, rom);
var slinky = new RAMFactor(mmu, io, 2, 1024 * 1024);
var clock = new Thunderclock(mmu, io, 7);
mmu.addSlot(1, parallel);
mmu.addSlot(2, slinky);
mmu.addSlot(3, slot3);
mmu.addSlot(6, disk2);
mmu.addSlot(7, clock);
cpu.addPageHandler(mmu);
var showFPS = false;
function updateKHz() {
var now = Date.now();
var ms = now - startTime;
var cycles = cpu.cycles();
var delta;
if (showFPS) {
delta = renderedFrames - lastFrames;
var fps = parseInt(delta/(ms/1000), 10);
$('#khz').text( fps + 'fps');
} else {
delta = cycles - lastCycles;
var khz = parseInt(delta/ms);
$('#khz').text( khz + 'KHz');
}
startTime = now;
lastCycles = cycles;
lastFrames = renderedFrames;
}
/* Audio Handling */
initAudio(io);
function updateSound() {
enableSound($('#enable_sound').attr('checked'));
}
function dumpDisk(drive) {
var wind = window.open('', '_blank');
wind.document.title = drivelights.label(drive);
wind.document.write('<pre>');
wind.document.write(disk2.getJSON(drive, true));
wind.document.write('</pre>');
wind.document.close();
}
function dumpProgram() {
var wind = window.open('', '_blank');
wind.document.title = 'Program Listing';
wind.document.write('<pre>');
wind.document.write(dumper.toString());
wind.document.write('</pre>');
wind.document.close();
}
function step()
{
if (runTimer) {
clearInterval(runTimer);
}
runTimer = null;
cpu.step(function() {
debug(cpu.dumpRegisters());
debug(cpu.dumpPC());
});
}
var accelerated = false;
function updateSpeed()
{
accelerated = $('#accelerator_toggle').prop('checked');
kHz = accelerated ? 4092 : 1023;
io.updateHz(kHz * 1000);
if (runTimer) {
run();
}
}
var _requestAnimationFrame =
window.requestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.msRequestAnimationFrame;
function run(pc) {
if (runTimer) {
clearInterval(runTimer);
}
if (pc) {
cpu.setPC(pc);
}
var ival = 30;
var now, last = Date.now();
var runFn = function() {
now = Date.now();
renderedFrames++;
var step = (now - last) * kHz, stepMax = kHz * ival;
last = now;
if (step > stepMax) {
step = stepMax;
}
if (document.location.hash != hashtag) {
hashtag = document.location.hash;
var hash = hup();
if (hash) {
processHash(hash);
}
}
if (!loading) {
mmu.resetVB();
cpu.stepCycles(step);
vm.blit();
io.sampleTick();
}
processGamepad(io);
if (!paused && _requestAnimationFrame) {
_requestAnimationFrame(runFn);
}
};
if (_requestAnimationFrame) {
_requestAnimationFrame(runFn);
} else {
runTimer = setInterval(runFn, ival);
}
}
function stop() {
if (runTimer) {
clearInterval(runTimer);
}
runTimer = null;
}
function reset()
{
cpu.reset();
}
function loadBinary(bin) {
stop();
for (var idx = 0; idx < bin.length; idx++) {
var pos = bin.start + idx;
cpu.write(pos >> 8, pos & 0xff, bin.data[idx]);
}
run(bin.start);
}
function selectCategory() {
$('#disk_select').empty();
var cat = disk_categories[$('#category_select').val()];
if (cat) {
for (var idx = 0; idx < cat.length; idx++) {
var file = cat[idx], name = file.name;
if (file.disk) {
name += ' - ' + file.disk;
}
var option = $('<option />').val(file.filename).text(name)
.appendTo('#disk_select');
if (disk_cur_name[_loadDrive] == name) {
option.attr('selected', 'selected');
}
}
}
}
function selectDisk() {
$('#local_file').val('');
}
function clickDisk() {
doLoad();
}
function loadDisk(disk) {
var name = disk.name;
var category = disk.category;
if (disk.disk) {
name += ' - ' + disk.disk;
}
disk_cur_cat[_loadDrive] = category;
disk_cur_name[_loadDrive] = name;
drivelights.label(_loadDrive, name);
disk2.setDisk(_loadDrive, disk);
initGamepad(disk.gamepad);
}
function loadJSON(data) {
if (data.type == 'binary') {
loadBinary(data);
} else if ($.inArray(data.type, DISK_TYPES) >= 0) {
loadDisk(data);
}
initGamepad(data.gamepad);
$('#loading').dialog('close');
loading = false;
}
/*
* LocalStorage Disk Storage
*/
function updateLocalStorage() {
var diskIndex = JSON.parse(window.localStorage.diskIndex || '{}');
var names = [], name, cat;
for (name in diskIndex) {
if (diskIndex.hasOwnProperty(name)) {
names.push(name);
}
}
cat = disk_categories['Local Saves'] = [];
$('#manage').empty();
names.forEach(function(name) {
cat.push({'category': 'Local Saves',
'name': name,
'filename': 'local:' + name});
$('#manage').append('<span class="local_save">' +
name +
' <a href="#" onclick="doDelete(\'' +
name +
'\')">Delete</a><br /></span>');
});
cat.push({'category': 'Local Saves',
'name': 'Manage Saves...',
'filename': 'local:__manage'});
}
function saveLocalStorage(drive, name) {
var diskIndex = JSON.parse(window.localStorage.diskIndex || '{}');
var json = disk2.getJSON(drive);
diskIndex[name] = json;
window.localStorage.diskIndex = JSON.stringify(diskIndex);
window.alert('Saved');
drivelights.label(drive, name);
drivelights.dirty(drive, false);
updateLocalStorage();
}
function deleteLocalStorage(name) {
var diskIndex = JSON.parse(window.localStorage.diskIndex || '{}');
if (diskIndex[name]) {
delete diskIndex[name];
window.alert('Deleted');
}
window.localStorage.diskIndex = JSON.stringify(diskIndex);
updateLocalStorage();
}
function loadLocalStorage(drive, name) {
var diskIndex = JSON.parse(window.localStorage.diskIndex || '{}');
if (diskIndex[name]) {
disk2.setJSON(drive, diskIndex[name]);
drivelights.label(drive, name);
drivelights.dirty(drive, false);
}
}
function processHash(hash) {
if (hash.indexOf('://') > 0) {
var parts = hash.split('.');
var ext = parts[parts.length - 1].toLowerCase();
if (ext == 'json') {
loadAjax(hash);
} else {
doLoadHTTP(hash);
}
} else {
loadAjax('json/disks/' + hash + '.json');
}
}
/*
* Keyboard/Gamepad routines
*/
var _key;
function _keydown(evt) {
if (!focused) {
evt.preventDefault();
var key = keyboard.mapKeyEvent(evt);
if (key != 0xff) {
if (_key != 0xff) io.keyUp();
io.keyDown(key, evt.shiftKey);
_key = key;
}
}
if (evt.keyCode === 112) {
cpu.reset();
} else if (evt.keyCode === 113) { // F2 - Full Screen
var elem = document.getElementById('screen');
if (document.webkitCancelFullScreen) {
if (document.webkitIsFullScreen) {
document.webkitCancelFullScreen();
} else {
if (Element.ALLOW_KEYBOARD_INPUT) {
elem.webkitRequestFullScreen(Element.ALLOW_KEYBOARD_INPUT);
} else {
elem.webkitRequestFullScreen();
}
}
} else if (document.mozCancelFullScreen) {
if (document.mozIsFullScreen) {
document.mozCancelFullScreen();
} else {
elem.mozRequestFullScreen();
}
}
} else if (evt.keyCode === 114) {
io.keyDown(0x1b);
_key = 0x1b;
} else if (evt.keyCode == 16) { // Shift
keyboard.shiftKey(true);
io.buttonDown(2, true);
} else if (evt.keyCode == 17) { // Control
keyboard.controlKey(true);
} else if (evt.keyCode == 91 || evt.keyCode == 93) { // Command
keyboard.commandKey(true);
} else if (evt.keyCode == 18) { // Alt
keyboard.optionKey(true);
}
}
function _keyup(evt) {
_key = 0xff;
if (!focused)
io.keyUp();
if (evt.keyCode == 16) { // Shift
keyboard.shiftKey(false);
io.buttonDown(2, false);
} else if (evt.keyCode == 17) { // Control
keyboard.controlKey(false);
} else if (evt.keyCode == 91 || evt.keyCode == 93) { // Command
keyboard.commandKey(false);
} else if (evt.keyCode == 18) { // Alt
keyboard.optionKey(false);
}
}
function updateScreen() {
var green = $('#green_screen').prop('checked');
vm.green(green);
}
var disableMouseJoystick = false;
var flipX = false;
var flipY = false;
var swapXY = false;
function updateJoystick() {
disableMouseJoystick = $('#disable_mouse').prop('checked');
flipX = $('#flip_x').prop('checked');
flipY = $('#flip_y').prop('checked');
swapXY = $('#swap_x_y').prop('checked');
if (disableMouseJoystick) {
io.paddle(0, 0.5);
io.paddle(1, 0.5);
return;
}
}
function _mousemove(evt) {
if (gamepad || disableMouseJoystick) {
return;
}
var s = $('#screen');
var offset = s.offset();
var x = (evt.pageX - offset.left) / s.width(),
y = (evt.pageY - offset.top) / s.height(),
z = x;
if (swapXY) {
x = y;
y = z;
}
io.paddle(0, flipX ? 1 - x : x);
io.paddle(1, flipY ? 1 - y : y);
}
function pauseRun(b) {
if (paused) {
run();
b.value = 'Pause';
} else {
stop();
b.value = 'Run';
}
paused = !paused;
}
$(function() {
hashtag = document.location.hash;
$('button,input[type=button],a.button').button().focus(function() {
// Crazy hack required by Chrome
var self = this;
window.setTimeout(function() {
self.blur();
}, 1);
});
var canvas = document.getElementById('screen');
var context = canvas.getContext('2d');
vm.setContext(context);
/*
* Input Handling
*/
$(window).keydown(_keydown);
$(window).keyup(_keyup);
$('canvas').mousedown(function(evt) {
if (!gamepad) {
io.buttonDown(evt.which == 1 ? 0 : 1);
}
evt.preventDefault();
})
.mouseup(function(evt) {
if (!gamepad) {
io.buttonUp(evt.which == 1 ? 0 : 1);
}
})
.bind('contextmenu', function(evt) { evt.preventDefault(); });
$('body').mousemove(_mousemove);
$('body > div').hover(function() { focused = false; },
function() { focused = true; });
$('input,textarea').focus(function() { focused = true; })
.blur(function() { focused = false; });
keyboard.create($('#keyboard'));
if (prefs.havePrefs()) {
$('input[type=checkbox]').each(function() {
var val = prefs.readPref(this.id);
if (val)
this.checked = JSON.parse(val);
}).change(function() {
prefs.writePref(this.id, JSON.stringify(this.checked));
});
}
reset();
run();
setInterval(updateKHz, 1000);
updateSound();
updateScreen();
updateSpeed();
var cancel = function() { $(this).dialog('close'); };
$('#loading').dialog({ autoOpen: false, modal: true });
$('#options').dialog({ autoOpen: false,
modal: true,
width: 320,
height: 400,
buttons: {'Close': cancel }});
$('#load').dialog({ autoOpen: false,
modal: true,
width: 540,
buttons: {'Cancel': cancel, 'Load': doLoad }});
$('#save').dialog({ autoOpen: false,
modal: true,
width: 320,
buttons: {'Cancel': cancel, 'Save': doSave }});
$('#manage').dialog({ autoOpen: false,
modal: true,
width: 320,
buttons: {'Close': cancel }});
$('#local_save').dialog({ autoOpen: false,
modal: true,
width: 530,
buttons: {'OK': cancel }});
$('#http_load').dialog({ autoOpen: false,
modal: true,
width: 530,
buttons: {'Cancel': cancel, 'OK': doLoadHTTP }});
if (window.localStorage !== undefined) {
$('.disksave').show();
}
var oldcat = '';
for (var idx = 0; idx < disk_index.length; idx++) {
var file = disk_index[idx];
var cat = file.category;
var name = file.name, disk = file.disk;
if (cat != oldcat) {
$('<option />').val(cat).text(cat).appendTo('#category_select');
disk_categories[cat] = [];
oldcat = cat;
}
disk_categories[cat].push(file);
if (disk) {
if (!disk_sets[name]) {
disk_sets[name] = [];
}
disk_sets[name].push(file);
}
}
$('<option/>').text('Local Saves').appendTo('#category_select');
updateLocalStorage();
initGamepad();
// Check for disks in hashtag
var hash = gup('disk') || hup();
if (hash) {
processHash(hash);
}
if (navigator.userAgent.match(/(iPod|iPhone|iPad)/)) {
$('select').removeAttr('multiple').css('height', 'auto');
}
if (navigator.standalone) {
$('body').addClass('standalone');
}
});

View File

@ -1,5 +1,4 @@
/* -*- mode: JavaScript; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* Copyright 2010-2013 Will Scullin <scullin@scullinsteel.com>
/* Copyright 2010-2016 Will Scullin <scullin@scullinsteel.com>
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
@ -10,10 +9,16 @@
* implied warranty.
*/
/*globals RAM: false, toHex: false, hiresMode: false, debug: false */
/*exported MMU */
/*globals debug: false, toHex: false
hiresMode: false,
RAM: false
*/
function MMU(cpu, lores1, lores2, hires1, hires2, io, rom)
{
'use strict';
var idx;
var _auxRom = 0x00;

View File

@ -1,5 +1,4 @@
/* -*- mode: JavaScript; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* Copyright 2010-2013 Will Scullin <scullin@scullinsteel.com>
/* Copyright 2010-2016 Will Scullin <scullin@scullinsteel.com>
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
@ -13,6 +12,8 @@
/*exported Parallel */
function Parallel(io, cbs, slot) {
'use strict';
slot = slot || 1;
var LOC = {
@ -75,5 +76,3 @@ function Parallel(io, cbs, slot) {
write: function() {}
};
}

View File

@ -1,5 +1,4 @@
/* -*- mode: JavaScript; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* Copyright 2010-2013 Will Scullin <scullin@scullinsteel.com>
/* Copyright 2010-2016 Will Scullin <scullin@scullinsteel.com>
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
@ -10,14 +9,15 @@
* implied warranty.
*/
/*jshint browser: true */
/*exported Prefs */
function Prefs()
{
'use strict';
return {
havePrefs: function() {
return typeof(localStorage) != "undefined";
return typeof(localStorage) != 'undefined';
},
readPref: function(name) {
if (localStorage)

View File

@ -1,5 +1,4 @@
/* -*- mode: JavaScript; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* Copyright 2010-2013 Will Scullin <scullin@scullinsteel.com>
/* Copyright 2010-2016 Will Scullin <scullin@scullinsteel.com>
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
@ -10,10 +9,14 @@
* implied warranty.
*/
/*globals allocMemPages, base64_encode, base64_decode */
/*exported RAM */
/*globals allocMemPages: false
base64_encode: false, base64_decode: false
*/
function RAM(sp, ep) {
'use strict';
var mem;
var start_page = sp;
var end_page = ep;

View File

@ -1,5 +1,4 @@
/* -*- mode: JavaScript; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* Copyright 2010-2013 Will Scullin <scullin@scullinsteel.com>
/* Copyright 2010-2016 Will Scullin <scullin@scullinsteel.com>
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
@ -10,10 +9,13 @@
* implied warranty.
*/
/*globals allocMem:false, bytify, base64_encode, base64_decode, each: false */
/*exported RAMFactor*/
/*globals allocMem: false, bytify: false, each: false,
base64_encode: false, base64_decode: false
*/
function RAMFactor(mmu, io, slot, size) {
'use strict';
var rom = [
0x43,0x4f,0x50,0x59,0x52,0x49,0x47,0x48,
0x54,0x20,0x28,0x43,0x29,0x20,0x31,0x39,
@ -1079,14 +1081,14 @@ function RAMFactor(mmu, io, slot, size) {
}
function _setmid(val) {
if ((_rammid & 0x80) && !(val & 0x80)) {
if (((_rammid & 0x80) !== 0) && ((val & 0x80) === 0)) {
_sethi(_ramhi + 1);
}
_rammid = (val & 0xff);
}
function _setlo(val) {
if ((_ramlo & 0x80) && !(val & 0x80)) {
if (((_ramlo & 0x80) !== 0) && ((val & 0x80) === 0)) {
_setmid(_rammid + 1);
}
_ramlo = (val & 0xff);

View File

@ -1,5 +1,4 @@
/* -*- mode: JavaScript; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* Copyright 2010-2013 Will Scullin <scullin@scullinsteel.com>
/* Copyright 2010-2016 Will Scullin <scullin@scullinsteel.com>
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
@ -14,6 +13,8 @@
function Slot3(mmu, rom)
{
'use strict';
var auxRomFn = {
start: function auxRom_start() {
return 0xc8;

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,4 @@
/* -*- mode: JavaScript; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* Copyright 2010-2013 Will Scullin <scullin@scullinsteel.com>
/* Copyright 2010-2016 Will Scullin <scullin@scullinsteel.com>
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
@ -11,10 +10,12 @@
*/
/*exported Thunderclock */
/*global each */
/*global each: false */
function Thunderclock(mmu, io, slot)
{
'use strict';
var rom = [
0x08,0x78,0x28,0x2c,0x58,0xff,0x70,0x05, // 00
0x38,0xb0,0x01,0x18,0xb8,0x08,0x78,0x48,
@ -340,7 +341,7 @@ function Thunderclock(mmu, io, slot)
switch (off) {
case LOC.CONTROL:
if (val !== undefined) {
if (val & FLAGS.STROBE) {
if ((val & FLAGS.STROBE) !== 0) {
if ((_command & 0x78) == 0x18) {
_calcbits();
}
@ -390,4 +391,3 @@ function Thunderclock(mmu, io, slot)
}
};
}

View File

@ -1,5 +1,4 @@
/* -*- mode: JavaScript; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* Copyright 2010-2014 Will Scullin <scullin@scullinsteel.com>
/* Copyright 2010-2016 Will Scullin <scullin@scullinsteel.com>
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
@ -10,7 +9,6 @@
* implied warranty.
*/
/*jshint jquery: true, browser: true */
/*globals debug: false */
/*exported enableSound, initAudio */
@ -18,14 +16,14 @@
* Audio Handling
*/
var sound = true;
var _samples = [];
var sound = true;
var _samples = [];
var audioContext;
var audioNode;
var AC = window.webkitAudioContext || window.AudioContext;
var audioContext;
var audioNode;
var AC = window.AudioContext;
if (typeof AC !== 'undefined') {
if (typeof AC !== 'undefined') {
audioContext = new AC();
audioNode = audioContext.createScriptProcessor(4096, 1, 1);
@ -57,7 +55,7 @@
*/
audioNode.connect(audioContext.destination);
}
}
function initAudio(io) {
if (audioContext) {

View File

@ -1,5 +1,5 @@
/*!
* Copyright 2010-2013 Will Scullin <scullin@scullinsteel.com>
* Copyright 2010-2016 Will Scullin <scullin@scullinsteel.com>
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that

View File

@ -1,5 +1,5 @@
/* -*- mode: JavaScript; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* Copyright 2010-2014 Will Scullin <scullin@scullinsteel.com>
/* Copyright 2010-2016 Will Scullin <scullin@scullinsteel.com>
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
@ -14,7 +14,7 @@
/*globals flipX: false, flipY: false */
/*exported processGamepad, initGamepad, gamepad */
var gamepadSupportAvailable = !!navigator.webkitGetGamepads;
var getGamepads = navigator.getGamepads || navigator.webkitGetGamepads;
var gamepad;
var gamepadMap = [];
var gamepadState = [];
@ -54,13 +54,13 @@ var DEFAULT_GAMEPAD = {
'START': '\033'
};
window.addEventListener("gamepadconnected", function(e) {
window.addEventListener('gamepadconnected', function(e) {
gamepad = e.gamepad;
});
function processGamepad(io) {
if (gamepadSupportAvailable) {
gamepad = navigator.webkitGetGamepads()[0];
if (getGamepads) {
gamepad = getGamepads.call(navigator)[0];
}
if (gamepad) {
var x = (gamepad.axes[0] * 1.414 + 1) / 2.0;
@ -74,7 +74,7 @@ function processGamepad(io) {
var old = gamepadState[idx];
var button = gamepad.buttons[idx];
var pressed;
if (typeof(button) == "object") {
if (typeof(button) == 'object') {
pressed = button.pressed;
} else {
pressed = (button == 1.0);
@ -117,5 +117,3 @@ function initGamepad(data) {
}
});
}

View File

@ -1,5 +1,4 @@
/* -*- mode: JavaScript; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* Copyright 2010-2013 Will Scullin <scullin@scullinsteel.com>
/* Copyright 2010-2016 Will Scullin <scullin@scullinsteel.com>
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
@ -10,7 +9,6 @@
* implied warranty.
*/
/*jshint jquery: true, browser: true */
/*globals debug: false, toHex: false, reset: false */
/*exported KeyBoard */
@ -132,7 +130,7 @@ function KeyBoard(io) {
0xDB: [0x5B, 0x5B, 0x5B], // [
0xDC: [0x5C, 0x5C, 0x5C], // \
0xDD: [0x5D, 0x5D, 0x5D], // ]
0xDE: [0x27, 0x27, 0x22], // ' - "
0xDE: [0x27, 0x27, 0x22], // ' - '
0xFF: [0xFF, 0xFF, 0xFF] // No comma line
};
@ -143,7 +141,7 @@ function KeyBoard(io) {
['CTRL','A','S','D','F','G','H','J','K','L',';','&larr;','&rarr;'],
['SHIFT','Z','X','C','V','B','N','M',',','.','/','SHIFT'],
['POWER', '&nbsp;']],
[['!','"','#','$','%','&',"'",'(',')','0','*','=','RESET'],
[['!','"','#','$','%','&','\'','(',')','0','*','=','RESET'],
['ESC','Q','W','E','R','T','Y','U','I','O','@','REPT','RETURN'],
['CTRL','A','S','D','F','BELL','H','J','K','L','+','&larr;','&rarr;'],
['SHIFT','Z','X','C','V','B','^',']','<','>','?','SHIFT'],
@ -160,7 +158,7 @@ function KeyBoard(io) {
return keymap[code][evt.shiftKey ? 2 : (evt.ctrlKey ? 1 : 0)];
}
debug("Unhandled key = " + toHex(code));
debug('Unhandled key = ' + toHex(code));
return 0xFF;
},
@ -168,19 +166,19 @@ function KeyBoard(io) {
shifted = down;
if (down) {
io.buttonDown(2);
$("#keyboard .key-SHIFT").addClass("active");
$('#keyboard .key-SHIFT').addClass('active');
} else {
io.buttonUp(2);
$("#keyboard .key-SHIFT").removeClass("active");
$('#keyboard .key-SHIFT').removeClass('active');
}
},
controlKey: function keyboard_controlKey(down) {
controlled = down;
if (down) {
$("#keyboard .key-CTRL").addClass("active");
$('#keyboard .key-CTRL').addClass('active');
} else {
$("#keyboard .key-CTRL").removeClass("active");
$('#keyboard .key-CTRL').removeClass('active');
}
},
@ -190,37 +188,37 @@ function KeyBoard(io) {
kb.disableSelection();
function buildLabel(k) {
var span = $("<span>" + k + "</span>");
var span = $('<span>' + k + '</span>');
if (k.length > 1 && k.substr(0,1) != '&')
span.addClass("small");
span.addClass('small');
return span;
}
function _mouseup(ev) {
$(ev.currentTarget).removeClass("pressed");
$(ev.currentTarget).removeClass('pressed');
}
function _mousedown(ev) {
$(ev.currentTarget).addClass("pressed");
var key = $(ev.currentTarget).data(shifted ? "key2" : "key1");
$(ev.currentTarget).addClass('pressed');
var key = $(ev.currentTarget).data(shifted ? 'key2' : 'key1');
switch (key) {
case "BELL":
key = "G";
case 'BELL':
key = 'G';
break;
case "RETURN":
key = "\r";
case 'RETURN':
key = '\r';
break;
case "&larr;":
key = "\010";
case '&larr;':
key = '\010';
break;
case "&rarr;":
key = "\025";
case '&rarr;':
key = '\025';
break;
case "&nbsp;":
key = " ";
case '&nbsp;':
key = ' ';
break;
case "ESC":
key = "\033";
case 'ESC':
key = '\033';
break;
default:
break;
@ -228,26 +226,26 @@ function KeyBoard(io) {
if (key.length > 1) {
switch (key) {
case "SHIFT":
case 'SHIFT':
shifted = !shifted;
$("#keyboard .key-SHIFT").toggleClass("active");
$('#keyboard .key-SHIFT').toggleClass('active');
break;
case "CTRL":
case 'CTRL':
controlled = !controlled;
$("#keyboard .key-CTRL").toggleClass("active");
$('#keyboard .key-CTRL').toggleClass('active');
break;
case "RESET":
case 'RESET':
reset();
break;
case "POWER":
if (window.confirm("Power Cycle?"))
case 'POWER':
if (window.confirm('Power Cycle?'))
window.location.reload();
break;
default:
break;
}
} else {
if (controlled && key >= "@" && key <= "_") {
if (controlled && key >= '@' && key <= '_') {
io.keyDown(key.charCodeAt(0) - 0x40);
} else {
io.keyDown(key.charCodeAt(0));
@ -256,37 +254,37 @@ function KeyBoard(io) {
}
for (y = 0; y < 5; y++) {
row = $("<div class='row row" + y + "'/>");
row = $('<div class="row row' + y + '"/>');
kb.append(row);
for (x = 0; x < keys[0][y].length; x++) {
key1 = keys[0][y][x];
key2 = keys[1][y][x];
label = $("<div />");
label = $('<div />');
label1 = buildLabel(key1);
label2 = buildLabel(key2);
key = $("<div class='key'>");
key.addClass("key-" + key1.replace(/[&;]/g,""));
key = $('<div class="key">');
key.addClass('key-' + key1.replace(/[&;]/g,''));
if (key1.length > 1)
key.addClass("vcenter");
key.addClass('vcenter');
if (key1 != key2) {
key.addClass("key-" + key2.replace(/[&;]/g,""));
key.addClass('key-' + key2.replace(/[&;]/g,''));
label.append(label2);
label.append("<br/>");
label.append('<br/>');
}
label.append(label1);
key.append(label);
key.data({"key1": key1, "key2": key2});
key.data({'key1': key1, 'key2': key2});
if (window.ontouchstart === undefined) {
key.bind("mousedown", _mousedown);
key.bind("mouseup mouseout", _mouseup);
key.bind('mousedown', _mousedown);
key.bind('mouseup mouseout', _mouseup);
} else {
key.bind("touchstart", _mousedown);
key.bind("touchend touchleave", _mouseup);
key.bind('touchstart', _mousedown);
key.bind('touchend touchleave', _mouseup);
}
row.append(key);
}
@ -294,4 +292,3 @@ function KeyBoard(io) {
}
};
}

View File

@ -1,5 +1,5 @@
/* -*- mode: JavaScript; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* Copyright 2010-2013 Will Scullin <scullin@scullinsteel.com>
/* Copyright 2010-2016 Will Scullin <scullin@scullinsteel.com>
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
@ -151,7 +151,7 @@ function KeyBoard(io) {
0xDB: [0x5B, 0x5B, 0x7B], // [
0xDC: [0x5C, 0x5C, 0x7C], // \
0xDD: [0x5D, 0x5D, 0x7D], // ]
0xDE: [0x27, 0x22, 0x22], // ' - "
0xDE: [0x27, 0x22, 0x22], // ' - '
0xFF: [0xFF, 0xFF, 0xFF] // No comma line
};
@ -159,12 +159,12 @@ function KeyBoard(io) {
var keys =
[[['ESC','1','2','3','4','5','6','7','8','9','0','-','=','DELETE'],
['TAB','Q','W','E','R','T','Y','U','I','O','P','[',']','\\'],
['CTRL','A','S','D','F','G','H','J','K','L',';','\'','RETURN'],
['CTRL','A','S','D','F','G','H','J','K','L',';','"','RETURN'],
['SHIFT','Z','X','C','V','B','N','M',',','.','/','SHIFT'],
['LOCK','`','POW','OPEN_APPLE','&nbsp;','CLOSED_APPLE','&larr;','&rarr;','&darr;','&uarr;']],
[['ESC','!','@','#','$','%','^','&',"*",'(',')','_','+','DELETE'],
[['ESC','!','@','#','$','%','^','&','*','(',')','_','+','DELETE'],
['TAB','Q','W','E','R','T','Y','U','I','O','P','{','}','|'],
['CTRL','A','S','D','F','G','H','J','K','L',':','"','RETURN'],
['CTRL','A','S','D','F','G','H','J','K','L',':','\'','RETURN'],
['SHIFT','Z','X','C','V','B','N','M','<','>','?','SHIFT'],
['CAPS','~','POW','OPEN_APPLE','&nbsp;','CLOSED_APPLE','&larr;','&rarr;','&darr;','&uarr;']]];
@ -183,7 +183,7 @@ function KeyBoard(io) {
if (capslocked && key >= 0x61 && key <= 0x7A)
key -= 0x20;
} else {
debug("Unhandled key = " + toHex(code));
debug('Unhandled key = ' + toHex(code));
}
return key;
@ -193,19 +193,19 @@ function KeyBoard(io) {
shifted = down;
if (down) {
io.buttonDown(2);
$("#keyboard .key-SHIFT").addClass("active");
$('#keyboard .key-SHIFT').addClass('active');
} else {
io.buttonUp(2);
$("#keyboard .key-SHIFT").removeClass("active");
$('#keyboard .key-SHIFT').removeClass('active');
}
},
controlKey: function keyboard_controlKey(down) {
controlled = down;
if (down) {
$("#keyboard .key-CTRL").addClass("active");
$('#keyboard .key-CTRL').addClass('active');
} else {
$("#keyboard .key-CTRL").removeClass("active");
$('#keyboard .key-CTRL').removeClass('active');
}
},
@ -213,10 +213,10 @@ function KeyBoard(io) {
commanded = down;
if (down) {
io.buttonDown(0);
$("#keyboard .key-OPEN_APPLE").addClass("active");
$('#keyboard .key-OPEN_APPLE').addClass('active');
} else {
io.buttonUp(0);
$("#keyboard .key-OPEN_APPLE").removeClass("active");
$('#keyboard .key-OPEN_APPLE').removeClass('active');
}
},
@ -224,19 +224,19 @@ function KeyBoard(io) {
optioned = down;
if (down) {
io.buttonDown(1);
$("#keyboard .key-CLOSED_APPLE").addClass("active");
$('#keyboard .key-CLOSED_APPLE').addClass('active');
} else {
io.buttonUp(1);
$("#keyboard .key-CLOSED_APPLE").removeClass("active");
$('#keyboard .key-CLOSED_APPLE').removeClass('active');
}
},
capslockKey: function keyboard_caplockKey(down) {
capslocked = down;
if (down) {
$("#keyboard .key-LOCK").addClass("active");
$('#keyboard .key-LOCK').addClass('active');
} else {
$("#keyboard .key-LOCK").removeClass("active");
$('#keyboard .key-LOCK').removeClass('active');
}
},
@ -247,49 +247,49 @@ function KeyBoard(io) {
kb.disableSelection();
function buildLabel(k) {
var span = $("<span>" + k + "</span>");
var span = $('<span>' + k + '</span>');
if (k.length > 1 && k.substr(0,1) != '&')
span.addClass("small");
span.addClass('small');
return span;
}
function _mouseup(ev) {
$(ev.currentTarget).removeClass("pressed");
$(ev.currentTarget).removeClass('pressed');
}
function _mousedown(ev) {
$(this).addClass("pressed");
var key = $(ev.currentTarget).data(shifted ? "key2" : "key1");
$(this).addClass('pressed');
var key = $(ev.currentTarget).data(shifted ? 'key2' : 'key1');
switch (key) {
case "BELL":
key = "G";
case 'BELL':
key = 'G';
break;
case "RETURN":
key = "\r";
case 'RETURN':
key = '\r';
break;
case "TAB":
key = "\t";
case 'TAB':
key = '\t';
break;
case "DELETE":
key = "\0177";
case 'DELETE':
key = '\0177';
break;
case "&larr;":
key = "\010";
case '&larr;':
key = '\010';
break;
case "&rarr;":
key = "\025";
case '&rarr;':
key = '\025';
break;
case "&darr;":
key = "\012";
case '&darr;':
key = '\012';
break;
case "&uarr;":
key = "\013";
case '&uarr;':
key = '\013';
break;
case "&nbsp;":
key = " ";
case '&nbsp;':
key = ' ';
break;
case "ESC":
key = "\033";
case 'ESC':
key = '\033';
break;
default:
break;
@ -297,31 +297,31 @@ function KeyBoard(io) {
if (key.length > 1) {
switch (key) {
case "SHIFT":
case 'SHIFT':
self.shiftKey(!shifted);
break;
case "CTRL":
case 'CTRL':
self.controlKey(!controlled);
break;
case "CAPS":
case "LOCK":
case 'CAPS':
case 'LOCK':
self.capslockKey(!capslocked);
break;
case "POW":
if (window.confirm("Power Cycle?"))
case 'POW':
if (window.confirm('Power Cycle?'))
window.location.reload();
break;
case "OPEN_APPLE":
case 'OPEN_APPLE':
self.commandKey(!commanded);
break;
case "CLOSED_APPLE":
case 'CLOSED_APPLE':
self.optionKey(!optioned);
break;
default:
break;
}
} else {
if (controlled && key >= "@" && key <= "_") {
if (controlled && key >= '@' && key <= '_') {
io.keyDown(key.charCodeAt(0) - 0x40);
} else if (!shifted && !capslocked &&
key >= 'A' && key <= 'Z') {
@ -333,43 +333,43 @@ function KeyBoard(io) {
}
for (y = 0; y < 5; y++) {
row = $("<div class='row row" + y + "e'/>");
row = $('<div class="row row' + y + 'e"/>');
kb.append(row);
for (x = 0; x < keys[0][y].length; x++) {
key1 = keys[0][y][x];
key2 = keys[1][y][x];
label = $("<div />");
label = $('<div />');
label1 = buildLabel(key1);
label2 = buildLabel(key2);
key = $("<div class='key'>");
key.addClass("key-" + key1.replace(/[&#;]/g,""));
key = $('<div class="key">');
key.addClass('key-' + key1.replace(/[&#;]/g,''));
if (key1.length > 1) {
if (key1 == "LOCK")
key.addClass("vcenter2");
if (key1 == 'LOCK')
key.addClass('vcenter2');
else
key.addClass("vcenter");
key.addClass('vcenter');
}
if (key1 != key2) {
key.addClass("key-" + key2.replace(/[&;]/g,""));
key.addClass('key-' + key2.replace(/[&;]/g,''));
label.append(label2);
label.append("<br/>");
label.append('<br/>');
}
if (key1 == "LOCK")
key.addClass("active");
if (key1 == 'LOCK')
key.addClass('active');
label.append(label1);
key.append(label);
key.data({"key1": key1, "key2": key2});
key.data({'key1': key1, 'key2': key2});
if (window.ontouchstart === undefined) {
key.bind("mousedown", _mousedown);
key.bind("mouseup mouseout", _mouseup);
key.bind('mousedown', _mousedown);
key.bind('mouseup mouseout', _mouseup);
} else {
key.bind("touchstart", _mousedown);
key.bind("touchend touchleave", _mouseup);
key.bind('touchstart', _mousedown);
key.bind('touchend touchleave', _mouseup);
}
row.append(key);
@ -380,4 +380,3 @@ function KeyBoard(io) {
}
};
}

View File

@ -1,4 +1,3 @@
/*jshint browser:true */
/*exported Printer */
function Printer() {
@ -6,21 +5,20 @@ function Printer() {
return {
putChar: function(val) {
if (!_printer || _printer.closed) {
_printer = window.open("", "_blank","toolbar=0,location=0");
_printer.document.title = "Printer";
_printer.document.write("<div style='font: 12px courier'>");
_printer.document.write("<span>");
_printer = window.open('', '_blank','toolbar=0,location=0');
_printer.document.title = 'Printer';
_printer.document.write('<div style="font: 12px courier">');
_printer.document.write('<span>');
window.focus();
}
var c = String.fromCharCode(val & 0x7f);
if (c == '\r') {
_printer.document.write("<br /></span>");
_printer.document.write('<br /></span>');
} else if (c == ' ') {
_printer.document.write("&nbsp;");
_printer.document.write('&nbsp;');
} else {
_printer.document.write(c);
}
}
};
}

View File

@ -1,5 +1,4 @@
/* -*- mode: JavaScript; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* Copyright 2010-2013 Will Scullin <scullin@scullinsteel.com>
/* Copyright 2010-2016 Will Scullin <scullin@scullinsteel.com>
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
@ -10,8 +9,10 @@
* implied warranty.
*/
/*jshint rhino:true, browser: true, devel: true */
/*exported allocMem, allocMemPages, bytify, debug, toHex, toBinary, extend, gup, hup, each */
/*eslint no-console: 0*/
/*exported allocMemPages, bytify, debug, each extend, gup, hup, toBinary, toHex
*/
/*global Uint8Array: false */
if (!Date.now) {
Date.now = function now() {
@ -19,8 +20,8 @@ if (!Date.now) {
};
}
var hex_digits = "0123456789ABCDEF";
var bin_digits = "01";
var hex_digits = '0123456789ABCDEF';
var bin_digits = '01';
function allocMem(size) {
var result;
@ -51,11 +52,9 @@ function extend(ary1, ary2) {
return ary1;
}
function debug(msg) {
if (typeof(console) != 'undefined' && 'log' in console) {
console.log(msg);
} else if (typeof(environment) == 'object') { // rhino shell
print(msg);
function debug() {
if (typeof console != 'undefined' && 'log' in console) {
console.log.apply(console, arguments);
}
}
@ -63,7 +62,7 @@ function toHex(v, n) {
if (!n) {
n = v < 256 ? 2 : 4;
}
var result = "";
var result = '';
for (var idx = 0; idx < n; idx++) {
result = hex_digits[v & 0x0f] + result;
v >>= 4;
@ -72,7 +71,7 @@ function toHex(v, n) {
}
function toBinary(v) {
var result = "";
var result = '';
for (var idx = 0; idx < 8; idx++) {
result = bin_digits[v & 0x01] + result;
v >>= 1;
@ -83,21 +82,21 @@ function toBinary(v) {
// From http://www.netlobo.com/url_query_string_javascript.html
function gup( name )
{
name = name.replace(/[\[]/,"\\[").replace(/[\]]/,"\\]");
var regexS = "[\\?&]"+name+"=([^&#]*)";
name = name.replace(/[\[]/,'\\[').replace(/[\]]/,'\\]');
var regexS = '[\\?&]'+name+'=([^&#]*)';
var regex = new RegExp( regexS );
var results = regex.exec( window.location.href );
if( !results )
return "";
return '';
else
return results[1];
}
function hup() {
var regex = new RegExp("#(.*)");
var regex = new RegExp('#(.*)');
var results = regex.exec(window.location.hash);
if ( !results )
return "";
return '';
else
return results[1];
}

View File

@ -1,5 +1,5 @@
#!/usr/bin/perl -w
# Copyright 2010-2013 Will Scullin <scullin@scullinsteel.com>
# Copyright 2010-2016 Will Scullin <scullin@scullinsteel.com>
#
# Permission to use, copy, modify, distribute, and sell this software and its
# documentation for any purpose is hereby granted without fee, provided that

View File

@ -1,5 +1,5 @@
#!/usr/bin/env perl -w
# Copyright 2010-2013 Will Scullin <scullin@scullinsteel.com>
# Copyright 2010-2016 Will Scullin <scullin@scullinsteel.com>
#
# Permission to use, copy, modify, distribute, and sell this software and its
# documentation for any purpose is hereby granted without fee, provided that