Merge pull request #1 from whscullin/whscullin/cleanup-refactor

Cleanup and refactoring.
This commit is contained in:
Will Scullin 2016-11-21 21:34:31 -08:00 committed by GitHub
commit 9bd56466b4
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> <!DOCTYPE html>
<!-- -*- mode: HTML; indent-tabs-mode: nil -*- --> <!-- -*- mode: HTML; indent-tabs-mode: nil -*- -->
<!-- Copyright 2010 Will Scullin --> <!-- Copyright 2010-2016 Will Scullin -->
<html> <html>
<head> <head>
<base href="/apple" /> <base href="/apple" />

View File

@ -1,6 +1,6 @@
<!DOCTYPE html><!-- -*- mode: HTML; indent-tabs-mode: nil -*- --> <!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 Permission to use, copy, modify, distribute, and sell this software and its
documentation for any purpose is hereby granted without fee, provided that 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/langcard.js"></script>
<script type="text/javascript" src="js/fpbasic.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/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/canvas2.js"></script>
<script type="text/javascript" src="js/apple2io.js"></script> <script type="text/javascript" src="js/apple2io.js"></script>
<script type="text/javascript" src="js/parallel.js"></script> <script type="text/javascript" src="js/parallel.js"></script>
@ -66,772 +67,8 @@
<!-- Disk Index --> <!-- Disk Index -->
<script type="text/javascript" src="json/disks/index.js"></script> <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> </head>
<body> <body>
<div style="margin: auto; width: 604px"> <div style="margin: auto; width: 604px">

View File

@ -1,6 +1,6 @@
<!DOCTYPE html><!-- -*- mode: HTML; indent-tabs-mode: nil -*- --> <!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 Permission to use, copy, modify, distribute, and sell this software and its
documentation for any purpose is hereby granted without fee, provided that 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.js"></script>
<script type="text/javascript" src="js/apple2e-enhanced.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/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/canvas2e.js"></script>
<script type="text/javascript" src="js/slot3.js"></script> <script type="text/javascript" src="js/slot3.js"></script>
<script type="text/javascript" src="js/apple2io.js"></script> <script type="text/javascript" src="js/apple2io.js"></script>
@ -68,774 +69,7 @@
<!-- Disk Index --> <!-- Disk Index -->
<script type="text/javascript" src="json/disks/index.js"></script> <script type="text/javascript" src="json/disks/index.js"></script>
<script type="text/javascript"> <script type="text/javascript" src="js/main2e.js"></script>
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>
</head> </head>
<body class="apple2e"> <body class="apple2e">
<div style="margin: auto; width: 604px"> <div style="margin: auto; width: 604px">

View File

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

View File

@ -1,9 +1,6 @@
/* -*- mode: JavaScript; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /*exported apple2_charset */
/*global bytify */ var apple2_charset = [
/*exported charset */
var charset = [
0x00,0x1c,0x22,0x2a,0x2e,0x2c,0x20,0x1e, 0x00,0x1c,0x22,0x2a,0x2e,0x2c,0x20,0x1e,
0x00,0x08,0x14,0x22,0x22,0x3e,0x22,0x22, 0x00,0x08,0x14,0x22,0x22,0x3e,0x22,0x22,
0x00,0x3c,0x22,0x22,0x3c,0x22,0x22,0x3c, 0x00,0x3c,0x22,0x22,0x3c,0x22,0x22,0x3c,
@ -261,5 +258,3 @@ var charset = [
0x80,0x90,0x88,0x84,0x82,0x84,0x88,0x90, 0x80,0x90,0x88,0x84,0x82,0x84,0x88,0x90,
0x80,0x9c,0xa2,0x84,0x88,0x88,0x80,0x88 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 */ /*exported Apple2eEnhancedROM */
function Apple2eEnhancedROM() function Apple2eEnhancedROM()
@ -2056,8 +2053,6 @@ function Apple2eEnhancedROM()
0xf5,0x03,0xfb,0x03,0x62,0xfa,0xfa,0xc3 0xf5,0x03,0xfb,0x03,0x62,0xfa,0xfa,0xc3
]; ];
rom = bytify(rom);
return { return {
start: function apple2e_start() { start: function apple2e_start() {
return 0xc1; return 0xc1;

View File

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

View File

@ -1,9 +1,6 @@
/* -*- mode: JavaScript; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /*exported apple2e_charset */
/*global bytify */ var apple2e_charset = [
/*exported charset */
var charset = [
0x1c,0x22,0x2a,0x3a,0x1a,0x02,0x3c,0x00, 0x1c,0x22,0x2a,0x3a,0x1a,0x02,0x3c,0x00,
0x08,0x14,0x22,0x22,0x3e,0x22,0x22,0x00, 0x08,0x14,0x22,0x22,0x3e,0x22,0x22,0x00,
0x1e,0x22,0x22,0x1e,0x22,0x22,0x1e,0x00, 0x1e,0x22,0x22,0x1e,0x22,0x22,0x1e,0x00,
@ -517,5 +514,3 @@ var charset = [
0x81,0x81,0x11,0x44,0x81,0x81,0x00,0x00, 0x81,0x81,0x11,0x44,0x81,0x81,0x00,0x00,
0x80,0x80,0x00,0x00,0x80,0x80,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-2016 Will Scullin <scullin@scullinsteel.com>
/* Copyright 2010-2014 Will Scullin <scullin@scullinsteel.com>
* *
* Permission to use, copy, modify, distribute, and sell this software and its * Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that * documentation for any purpose is hereby granted without fee, provided that
@ -15,7 +14,7 @@
function Apple2IO(cpu, callbacks) function Apple2IO(cpu, callbacks)
{ {
"use strict"; 'use strict';
var _hz = 1023000; var _hz = 1023000;
var _rate = 44000; var _rate = 44000;
@ -116,43 +115,43 @@ function Apple2IO(cpu, callbacks)
var delta = now - _trigger; var delta = now - _trigger;
switch (off) { switch (off) {
case LOC.CLR80VID: case LOC.CLR80VID:
// _debug("80 Column Mode off"); // _debug('80 Column Mode off');
if ('_80col' in callbacks) callbacks._80col(false); if ('_80col' in callbacks) callbacks._80col(false);
break; break;
case LOC.SET80VID: case LOC.SET80VID:
// _debug("80 Column Mode on"); // _debug('80 Column Mode on');
if ('_80col' in callbacks) callbacks._80col(true); if ('_80col' in callbacks) callbacks._80col(true);
break; break;
case LOC.CLRALTCH: case LOC.CLRALTCH:
// _debug("Alt Char off"); // _debug('Alt Char off');
if ('altchar' in callbacks) callbacks.altchar(false); if ('altchar' in callbacks) callbacks.altchar(false);
break; break;
case LOC.SETALTCH: case LOC.SETALTCH:
// _debug("Alt Char on"); // _debug('Alt Char on');
if ('altchar' in callbacks) callbacks.altchar(true); if ('altchar' in callbacks) callbacks.altchar(true);
break; break;
case LOC.CLRTEXT: case LOC.CLRTEXT:
_debug("Graphics Mode"); _debug('Graphics Mode');
callbacks.text(false); callbacks.text(false);
break; break;
case LOC.SETTEXT: case LOC.SETTEXT:
_debug("Text Mode"); _debug('Text Mode');
callbacks.text(true); callbacks.text(true);
break; break;
case LOC.CLRMIXED: case LOC.CLRMIXED:
_debug("Mixed Mode off"); _debug('Mixed Mode off');
callbacks.mixed(false); callbacks.mixed(false);
break; break;
case LOC.SETMIXED: case LOC.SETMIXED:
_debug("Mixed Mode on"); _debug('Mixed Mode on');
callbacks.mixed(true); callbacks.mixed(true);
break; break;
case LOC.CLRHIRES: case LOC.CLRHIRES:
_debug("LoRes Mode"); _debug('LoRes Mode');
callbacks.hires(false); callbacks.hires(false);
break; break;
case LOC.SETHIRES: case LOC.SETHIRES:
_debug("HiRes Mode"); _debug('HiRes Mode');
callbacks.hires(true); callbacks.hires(true);
break; break;
case LOC.PAGE1: case LOC.PAGE1:
@ -262,7 +261,7 @@ function Apple2IO(cpu, callbacks)
_trigger = cpu.cycles(); _trigger = cpu.cycles();
break; break;
case LOC.TAPEIN: case LOC.TAPEIN:
var flipped = false; // var flipped = false;
if (_tapeOffset == -1) { if (_tapeOffset == -1) {
_tapeOffset = 0; _tapeOffset = 0;
_tapeNext = now; _tapeNext = now;
@ -270,17 +269,17 @@ function Apple2IO(cpu, callbacks)
if (_tapeOffset < _tape.length) { if (_tapeOffset < _tape.length) {
while (now >= _tapeNext) { while (now >= _tapeNext) {
if ((_tapeOffset % 1000) === 0) { if ((_tapeOffset % 1000) === 0) {
debug("Read " + (_tapeOffset / 1000)); debug('Read ' + (_tapeOffset / 1000));
} }
_tapeFlip = !_tapeFlip; _tapeFlip = !_tapeFlip;
flipped = true; // flipped = true;
_tapeNext += _tape[_tapeOffset++]; _tapeNext += _tape[_tapeOffset++];
} }
result = _tapeFlip ? 0x80 : 0x00; result = _tapeFlip ? 0x80 : 0x00;
} }
/* /*
if (flipped) { 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]) { if (_locs[off]) {
result = _locs[off].ioSwitch(off); result = _locs[off].ioSwitch(off);
} else { } else {
debug("I/O read: C0" + toHex(off)); debug('I/O read: C0' + toHex(off));
} }
return result; return result;
}, },
@ -327,7 +326,7 @@ function Apple2IO(cpu, callbacks)
if (_locs[off]) { if (_locs[off]) {
_locs[off].ioSwitch(off, val); _locs[off].ioSwitch(off, val);
} else { } else {
debug("I/O write: C0" + toHex(off)); debug('I/O write: C0' + toHex(off));
} }
}, },
getState: function apple2io_getState() { return {}; }, 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); // return atob(data);
//} //}
var b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; var b64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
var o1, o2, o3, h1, h2, h3, h4, bits, i = 0, ac = 0, enc="", tmp_arr = []; var o1, o2, o3, h1, h2, h3, h4, bits, i = 0, ac = 0, enc='', tmp_arr = [];
if (!data) { if (!data) {
return data; return data;
@ -80,7 +80,7 @@ function base64_decode(data) {
// return btoa(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 = []; var o1, o2, o3, h1, h2, h3, h4, bits, i = 0, ac = 0, tmp_arr = [];
if (!data) { if (!data) {

View File

@ -1,5 +1,4 @@
/* -*- mode: JavaScript; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* Copyright 2010-2016 Will Scullin <scullin@scullinsteel.com>
/* Copyright 2010-2014 Will Scullin <scullin@scullinsteel.com>
* *
* Permission to use, copy, modify, distribute, and sell this software and its * Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that * documentation for any purpose is hereby granted without fee, provided that
@ -10,16 +9,14 @@
* implied warranty. * implied warranty.
*/ */
/*jshint browser:true */
/*globals allocMemPages: false,
charset: false,
base64_encode: false, base64_decode: false */
/*exported LoresPage, HiresPage, VideoModes*/
/* /*
* Text Page 1 Drawing * Text Page 1 Drawing
*/ */
/*globals allocMemPages: false,
base64_encode: false, base64_decode: false */
/*exported LoresPage, HiresPage, VideoModes*/
var textMode = true; var textMode = true;
var mixedMode = true; var mixedMode = true;
var hiresMode = false; var hiresMode = false;
@ -34,8 +31,10 @@ var scanlines = false;
* *
***************************************************************************/ ***************************************************************************/
function LoresPage(page) function LoresPage(page, charset)
{ {
'use strict';
// $00-$3F inverse // $00-$3F inverse
// $40-$7F flashing // $40-$7F flashing
// $80-$FF normal // $80-$FF normal
@ -159,7 +158,7 @@ function LoresPage(page)
back = _greenMode ? _green : _white; back = _greenMode ? _green : _white;
} }
for (jdx = 0; jdx < 8; jdx++) { for (jdx = 0; jdx < 8; jdx++) {
b = charset[(val & 0x3f) * 8 + jdx]; b = charset[val * 8 + jdx];
b <<= 1; b <<= 1;
for (idx = 0; idx < 7; idx++) { for (idx = 0; idx < 7; idx++) {
color = (b & 0x80) ? fore : back; color = (b & 0x80) ? fore : back;
@ -177,7 +176,7 @@ function LoresPage(page)
b = (jdx < 4) ? (val & 0x0f) : (val >> 4); b = (jdx < 4) ? (val & 0x0f) : (val >> 4);
b |= (b << 4); b |= (b << 4);
b |= (b << 8); b |= (b << 8);
if (col & 0x1) { if ((col & 0x1) !== 0) {
b <<= 2; b <<= 2;
} }
for (idx = 0; idx < 14; idx++) { for (idx = 0; idx < 14; idx++) {
@ -332,7 +331,7 @@ function HiresPage(page)
b2 = col < 39 ? _buffer[base + 1] : 0; b2 = col < 39 ? _buffer[base + 1] : 0;
val |= (b2 & 0x3) << 7; val |= (b2 & 0x3) << 7;
var v0 = b0 & 0x20, v1 = b0 & 0x40, v2 = val & 0x1, var v0 = b0 & 0x20, v1 = b0 & 0x40, v2 = val & 0x1,
odd = !(col & 0x1), odd = (col & 0x1) === 0,
color, color,
oddCol = (hbs ? orangeCol : greenCol), oddCol = (hbs ? orangeCol : greenCol),
evenCol = (hbs ? blueCol : violetCol); 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-2016 Will Scullin <scullin@scullinsteel.com>
/* Copyright 2010-2013 Will Scullin <scullin@scullinsteel.com>
* *
* Permission to use, copy, modify, distribute, and sell this software and its * Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that * documentation for any purpose is hereby granted without fee, provided that
@ -10,8 +9,9 @@
* implied warranty. * implied warranty.
*/ */
/*jshint browser:true */ /*globals allocMemPages: false, debug: false,
/*globals allocMemPages: false, charset: false, base64_encode: false, base64_decode: false, enhanced: false */ base64_encode: false, base64_decode: false,
enhanced: false */
/*exported LoresPage, HiresPage, VideoModes */ /*exported LoresPage, HiresPage, VideoModes */
@ -22,6 +22,12 @@ var pageMode = 1;
var _80colMode = false; var _80colMode = false;
var altCharMode = false; var altCharMode = false;
var doubleHiresMode = false; var doubleHiresMode = false;
var monoDHRMode = false;
var colorDHRMode = false;
var mixedDHRMode = false;
var highColorHGRMode = false;
var highColorTextMode = false;
var pages = []; var pages = [];
var context = null; var context = null;
var scanlines = false; var scanlines = false;
@ -32,9 +38,9 @@ var scanlines = false;
* *
***************************************************************************/ ***************************************************************************/
function LoresPage(page) function LoresPage(page, charset)
{ {
"use strict"; 'use strict';
// $00-$3F inverse // $00-$3F inverse
// $40-$7F flashing // $40-$7F flashing
@ -176,9 +182,6 @@ function LoresPage(page)
return; return;
_buffer[bank][base] = val; _buffer[bank][base] = val;
if (bank !== 0 && !_80colMode)
return;
var col = (base % 0x80) % 0x28, var col = (base % 0x80) % 0x28,
adj = off - col; adj = off - col;
@ -202,14 +205,15 @@ function LoresPage(page)
fore = flash ? _black : (_greenMode ? _green : _white); fore = flash ? _black : (_greenMode ? _green : _white);
back = flash ? (_greenMode ? _green : _white) : _black; back = flash ? (_greenMode ? _green : _white) : _black;
if (_80colMode) {
if (!enhanced) { if (!enhanced) {
val = (val >= 0x40 && val < 0x60) ? val - 0x40 : val; val = (val >= 0x40 && val < 0x60) ? val - 0x40 : val;
} else if (!altCharMode) { } else if (!altCharMode) {
val = (val >= 0x40 && val < 0x80) ? val - 0x40 : val; val = (val >= 0x40 && val < 0x80) ? val - 0x40 : val;
} }
if (_80colMode) {
off = (col * 14 + (bank ? 0 : 1) * 7 + row * 560 * 8 * 2) * 4; off = (col * 14 + (bank ? 0 : 1) * 7 + row * 560 * 8 * 2) * 4;
for (jdx = 0; jdx < 8; jdx++) { for (jdx = 0; jdx < 8; jdx++) {
b = charset[val * 8 + jdx]; b = charset[val * 8 + jdx];
for (idx = 0; idx < 7; idx++) { for (idx = 0; idx < 7; idx++) {
@ -221,8 +225,21 @@ function LoresPage(page)
off += 553 * 4 + 560 * 4; off += 553 * 4 + 560 * 4;
} }
} else { } 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; 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++) { for (jdx = 0; jdx < 8; jdx++) {
b = charset[val * 8 + jdx]; b = charset[val * 8 + jdx];
for (idx = 0; idx < 7; idx++) { for (idx = 0; idx < 7; idx++) {
@ -305,11 +322,9 @@ function LoresPage(page)
} }
}, },
refresh: function() { refresh: function() {
var page, off, addr = 0x400 * _page; var addr = 0x400 * _page;
_refreshing = true; _refreshing = true;
for (var idx = 0; idx < 0x400; idx++, addr++) { for (var idx = 0; idx < 0x400; idx++, addr++) {
page = addr >> 8;
off = addr & 0xff;
this._write(addr >> 8, addr & 0xff, _buffer[0][idx], 0); this._write(addr >> 8, addr & 0xff, _buffer[0][idx], 0);
if (_80colMode) if (_80colMode)
this._write(addr >> 8, addr & 0xff, _buffer[1][idx], 1); this._write(addr >> 8, addr & 0xff, _buffer[1][idx], 1);
@ -362,7 +377,7 @@ function LoresPage(page)
function HiresPage(page) function HiresPage(page)
{ {
"use strict"; 'use strict';
var _page = page; var _page = page;
@ -416,8 +431,8 @@ function HiresPage(page)
var _buffer = []; var _buffer = [];
var _refreshing = false; var _refreshing = false;
var _green = [0x00, 0xff, 0x80];
var _greenMode = false; var _greenMode = false;
var _green = [0x00, 0xff, 0x80];
function _init() { function _init() {
_buffer[0] = allocMemPages(0x20); _buffer[0] = allocMemPages(0x20);
@ -430,6 +445,7 @@ function HiresPage(page)
function _drawPixel(data, off, color) { function _drawPixel(data, off, color) {
var c0 = color[0], c1 = color[1], c2 = color[2]; var c0 = color[0], c1 = color[1], c2 = color[2];
data[off + 0] = data[off + 4] = c0; data[off + 0] = data[off + 4] = c0;
data[off + 1] = data[off + 5] = c1; data[off + 1] = data[off + 5] = c1;
data[off + 2] = data[off + 6] = c2; data[off + 2] = data[off + 6] = c2;
@ -515,9 +531,6 @@ function HiresPage(page)
return; return;
_buffer[bank][base] = val; _buffer[bank][base] = val;
if (bank !== 0 && !doubleHiresMode)
return;
var hbs = val & 0x80; var hbs = val & 0x80;
val &= 0x7f; val &= 0x7f;
@ -538,7 +551,7 @@ function HiresPage(page)
return; return;
dy = rowa * 16 + rowb * 2; dy = rowa * 16 + rowb * 2;
var bz, b0, b1, b2, b3, b4, c; var bz, b0, b1, b2, b3, b4, c, hb;
if (doubleHiresMode) { if (doubleHiresMode) {
// Every 4 bytes is 7 pixels // Every 4 bytes is 7 pixels
// 2 bytes per bank // 2 bytes per bank
@ -557,26 +570,57 @@ function HiresPage(page)
((b2 & 0x3c) >> 2), // 4 ((b2 & 0x3c) >> 2), // 4
((b2 & 0x40) >> 6) | ((b3 & 0x07) << 1), // 5 ((b2 & 0x40) >> 6) | ((b3 & 0x07) << 1), // 5
((b3 & 0x78) >> 3), // 6 ((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 0]; // 7
if (col > 0) { if (col > 0) {
c[0] = (bz & 0x78) >> 3; c[0] = (bz & 0x78) >> 3;
hb[0] = bz & 0x80;
} }
if (col < 39) { if (col < 39) {
c[8] = b4 & 0x0f; c[8] = b4 & 0x0f;
hb[8] = b4 & 0x80;
} }
dx = mcol * 14; dx = mcol * 14;
off = dx * 4 + dy * 280 * 4 * 2; 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++) { for (idx = 1; idx < 8; idx++) {
hbs = hb[idx];
var dcolor = dcolors[r4[c[idx]]]; var dcolor = dcolors[r4[c[idx]]];
var bits = c[idx-1] | (c[idx] << 4) | (c[idx+1] << 8); var bits = c[idx-1] | (c[idx] << 4) | (c[idx+1] << 8);
for (jdx = 0; jdx < 4; jdx++, off += 4) { for (jdx = 0; jdx < 4; jdx++, off += 4) {
if (_greenMode) { if (monoColor) {
if (bits & 0x10) { if (bits & 0x10) {
_drawHalfPixel(data, off, _green); _drawHalfPixel(data, off, monoColor);
} else { } else {
_drawHalfPixel(data, off, blackCol); _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) || } else if (((bits & 0x3c) == 0x3c) ||
((bits & 0xf0) == 0xf0) || ((bits & 0xf0) == 0xf0) ||
((bits & 0x1e) == 0x1e) || ((bits & 0x1e) == 0x1e) ||
@ -607,9 +651,10 @@ function HiresPage(page)
_refreshing = false; _refreshing = false;
} }
} else { } else {
val = _buffer[0][base] & 0x7f;
dx = col * 14 - 2; dx = col * 14 - 2;
b0 = col > 0 ? _buffer[bank][base - 1] : 0; b0 = col > 0 ? _buffer[0][base - 1] : 0;
b2 = col < 39 ? _buffer[bank][base + 1] : 0; b2 = col < 39 ? _buffer[0][base + 1] : 0;
val |= (b2 & 0x3) << 7; val |= (b2 & 0x3) << 7;
var v0 = b0 & 0x20, v1 = b0 & 0x40, v2 = val & 0x1, var v0 = b0 & 0x20, v1 = b0 & 0x40, v2 = val & 0x1,
odd = !(col & 0x1), odd = !(col & 0x1),
@ -623,6 +668,8 @@ function HiresPage(page)
if (v1) { if (v1) {
if (_greenMode) { if (_greenMode) {
color = _green; color = _green;
} else if (highColorHGRMode) {
color = dcolors[_buffer[1][base] >> 4];
} else if (v0 || v2) { } else if (v0 || v2) {
color = whiteCol; color = whiteCol;
} else { } else {
@ -631,6 +678,8 @@ function HiresPage(page)
} else { } else {
if (_greenMode) { if (_greenMode) {
color = blackCol; color = blackCol;
} else if (highColorHGRMode) {
color = dcolors[_buffer[1][base] & 0x0f];
} else if (odd && v2 && v0) { } else if (odd && v2 && v0) {
color = v0 ? dim(evenCol) : evenCol; color = v0 ? dim(evenCol) : evenCol;
} else if (!odd && v0 && v2) { } else if (!odd && v0 && v2) {
@ -694,6 +743,7 @@ function HiresPage(page)
function VideoModes(gr,hgr,gr2,hgr2) { function VideoModes(gr,hgr,gr2,hgr2) {
var _grs = [gr, gr2]; var _grs = [gr, gr2];
var _hgrs = [hgr, hgr2]; var _hgrs = [hgr, hgr2];
var _seq = '';
function _refresh() { function _refresh() {
gr.refresh(); gr.refresh();
@ -711,15 +761,28 @@ function VideoModes(gr,hgr,gr2,hgr2) {
mixedMode = false; mixedMode = false;
hiresMode = true; hiresMode = true;
pageMode = 1; pageMode = 1;
_80colMode = false; _80colMode = false;
altCharMode = false; altCharMode = false;
doubleHiresMode = false; doubleHiresMode = false;
monoDHRMode = false;
colorDHRMode = false;
mixedDHRMode = false;
highColorHGRMode = false;
highColorTextMode = false;
_refresh(); _refresh();
}, },
text: function(on) { text: function(on) {
var old = textMode; var old = textMode;
textMode = on; textMode = on;
highColorTextMode = false;
_seq = on ? 'T+' : 'T-';
// debug('_seq=', _seq);
if (old != on) { if (old != on) {
_refresh(); _refresh();
} }
@ -727,6 +790,11 @@ function VideoModes(gr,hgr,gr2,hgr2) {
_80col: function(on) { _80col: function(on) {
var old = _80colMode; var old = _80colMode;
_80colMode = on; _80colMode = on;
_seq += on ? '8+' : '8-';
_seq = _seq.substr(0, 16);
// debug('_seq=', _seq);
if (old != on) { if (old != on) {
_refresh(); _refresh();
} }
@ -741,6 +809,11 @@ function VideoModes(gr,hgr,gr2,hgr2) {
hires: function(on) { hires: function(on) {
var old = hiresMode; var old = hiresMode;
hiresMode = on; hiresMode = on;
highColorHGRMode = false;
_seq = (on ? 'H+' : 'H-');
// debug('_seq=', _seq);
if (old != on) { if (old != on) {
_refresh(); _refresh();
} }
@ -748,6 +821,41 @@ function VideoModes(gr,hgr,gr2,hgr2) {
doublehires: function(on) { doublehires: function(on) {
var old = doubleHiresMode; var old = doubleHiresMode;
doubleHiresMode = on; 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 (old != on) {
if (on) { if (on) {
this.page(1); 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 * Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that * 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 * Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that * documentation for any purpose is hereby granted without fee, provided that
@ -16,7 +15,7 @@
function CPU6502(options) function CPU6502(options)
{ {
"use strict"; 'use strict';
options = options || {}; options = options || {};
@ -116,7 +115,7 @@ function CPU6502(options)
function testNZ(val) { function testNZ(val) {
sr = val === 0 ? (sr | flags.Z) : (sr & ~flags.Z); 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; return val;
} }
@ -133,7 +132,7 @@ function CPU6502(options)
// KEGS // KEGS
var c, v; var c, v;
if (sr & flags.D) { if ((sr & flags.D) !== 0) {
c = (a & 0x0f) + (b & 0x0f) + (sr & flags.C); c = (a & 0x0f) + (b & 0x0f) + (sr & flags.C);
if (sub) { if (sub) {
if (c < 0x10) if (c < 0x10)
@ -155,8 +154,9 @@ function CPU6502(options)
v = (c ^ a) & 0x80; v = (c ^ a) & 0x80;
} }
if ((a ^ b) & 0x80) if (((a ^ b) & 0x80) !== 0) {
v = 0; v = 0;
}
setFlag(flags.C, c > 0xff); setFlag(flags.C, c > 0xff);
setFlag(flags.V, v); setFlag(flags.V, v);
@ -223,8 +223,7 @@ function CPU6502(options)
function pullByte() { function pullByte() {
sp = (sp + 0x01) & 0xff; sp = (sp + 0x01) & 0xff;
var result = readByte(loc.STACK | sp); return readByte(loc.STACK | sp);
return result;
} }
function pullWord() { function pullWord() {
@ -592,7 +591,7 @@ function CPU6502(options)
/* Bit */ /* Bit */
function bit(readFn) { function bit(readFn) {
var val = readFn(); var val = readFn();
setFlag(flags.Z, !(val & ar)); setFlag(flags.Z, (val & ar) === 0);
setFlag(flags.N, val & 0x80); setFlag(flags.N, val & 0x80);
setFlag(flags.V, val & 0x40); setFlag(flags.V, val & 0x40);
} }
@ -600,7 +599,7 @@ function CPU6502(options)
/* Bit Immediate*/ /* Bit Immediate*/
function bitI(readFn) { function bitI(readFn) {
var val = readFn(); var val = readFn();
setFlag(flags.Z, !(val & ar)); setFlag(flags.Z, (val & ar) === 0);
} }
function compare(a, b) function compare(a, b)
@ -626,7 +625,7 @@ function CPU6502(options)
/* Branches */ /* Branches */
function brs(f) { function brs(f) {
var off = readBytePC(); // changes pc var off = readBytePC(); // changes pc
if (f & sr) { if ((f & sr) !== 0) {
var oldPC = pc; var oldPC = pc;
pc += off > 127 ? off - 256 : off; pc += off > 127 ? off - 256 : off;
cycles++; cycles++;
@ -636,7 +635,7 @@ function CPU6502(options)
function brc(f) { function brc(f) {
var off = readBytePC(); // changes pc var off = readBytePC(); // changes pc
if (!(f & sr)) { if ((f & sr) === 0) {
var oldPC = pc; var oldPC = pc;
pc += off > 127 ? off - 256 : off; pc += off > 127 ? off - 256 : off;
cycles++; cycles++;
@ -711,334 +710,336 @@ function CPU6502(options)
var ops = { var ops = {
// LDA // LDA
0xa9: ["LDA", lda, readImmediate, modes.immediate, 2], 0xa9: ['LDA', lda, readImmediate, modes.immediate, 2],
0xa5: ["LDA", lda, readZeroPage, modes.zeroPage, 3], 0xa5: ['LDA', lda, readZeroPage, modes.zeroPage, 3],
0xb5: ["LDA", lda, readZeroPageX, modes.zeroPageX, 4], 0xb5: ['LDA', lda, readZeroPageX, modes.zeroPageX, 4],
0xad: ["LDA", lda, readAbsolute, modes.absolute, 4], 0xad: ['LDA', lda, readAbsolute, modes.absolute, 4],
0xbd: ["LDA", lda, readAbsoluteX, modes.absoluteX, 4], 0xbd: ['LDA', lda, readAbsoluteX, modes.absoluteX, 4],
0xb9: ["LDA", lda, readAbsoluteY, modes.absoluteY, 4], 0xb9: ['LDA', lda, readAbsoluteY, modes.absoluteY, 4],
0xa1: ["LDA", lda, readZeroPageXIndirect, modes.zeroPageXIndirect, 6], 0xa1: ['LDA', lda, readZeroPageXIndirect, modes.zeroPageXIndirect, 6],
0xb1: ["LDA", lda, readZeroPageIndirectY, modes.zeroPageIndirectY, 5], 0xb1: ['LDA', lda, readZeroPageIndirectY, modes.zeroPageIndirectY, 5],
// LDX // LDX
0xa2: ["LDX", ldx, readImmediate, modes.immediate, 2], 0xa2: ['LDX', ldx, readImmediate, modes.immediate, 2],
0xa6: ["LDX", ldx, readZeroPage, modes.zeroPage, 3], 0xa6: ['LDX', ldx, readZeroPage, modes.zeroPage, 3],
0xb6: ["LDX", ldx, readZeroPageY, modes.zeroPageY, 4], 0xb6: ['LDX', ldx, readZeroPageY, modes.zeroPageY, 4],
0xae: ["LDX", ldx, readAbsolute, modes.absolute, 4], 0xae: ['LDX', ldx, readAbsolute, modes.absolute, 4],
0xbe: ["LDX", ldx, readAbsoluteY, modes.absoluteY, 4], 0xbe: ['LDX', ldx, readAbsoluteY, modes.absoluteY, 4],
// LDY // LDY
0xa0: ["LDY", ldy, readImmediate, modes.immediate, 2], 0xa0: ['LDY', ldy, readImmediate, modes.immediate, 2],
0xa4: ["LDY", ldy, readZeroPage, modes.zeroPage, 3], 0xa4: ['LDY', ldy, readZeroPage, modes.zeroPage, 3],
0xb4: ["LDY", ldy, readZeroPageX, modes.zeroPageX, 4], 0xb4: ['LDY', ldy, readZeroPageX, modes.zeroPageX, 4],
0xac: ["LDY", ldy, readAbsolute, modes.absolute, 4], 0xac: ['LDY', ldy, readAbsolute, modes.absolute, 4],
0xbc: ["LDY", ldy, readAbsoluteX, modes.absoluteX, 4], 0xbc: ['LDY', ldy, readAbsoluteX, modes.absoluteX, 4],
// STA // STA
0x85: ["STA", sta, writeZeroPage, modes.zeroPage, 3], 0x85: ['STA', sta, writeZeroPage, modes.zeroPage, 3],
0x95: ["STA", sta, writeZeroPageX, modes.zeroPageX, 4], 0x95: ['STA', sta, writeZeroPageX, modes.zeroPageX, 4],
0x8d: ["STA", sta, writeAbsolute, modes.absolute, 4], 0x8d: ['STA', sta, writeAbsolute, modes.absolute, 4],
0x9d: ["STA", sta, writeAbsoluteX, modes.absoluteX, 5], 0x9d: ['STA', sta, writeAbsoluteX, modes.absoluteX, 5],
0x99: ["STA", sta, writeAbsoluteY, modes.absoluteY, 5], 0x99: ['STA', sta, writeAbsoluteY, modes.absoluteY, 5],
0x81: ["STA", sta, writeZeroPageXIndirect, modes.zeroPageXIndirect, 6], 0x81: ['STA', sta, writeZeroPageXIndirect, modes.zeroPageXIndirect, 6],
0x91: ["STA", sta, writeZeroPageIndirectY, modes.zeroPageIndirectY, 6], 0x91: ['STA', sta, writeZeroPageIndirectY, modes.zeroPageIndirectY, 6],
// STX // STX
0x86: ["STX", stx, writeZeroPage, modes.zeroPage, 3], 0x86: ['STX', stx, writeZeroPage, modes.zeroPage, 3],
0x96: ["STX", stx, writeZeroPageY, modes.zeroPageY, 4], 0x96: ['STX', stx, writeZeroPageY, modes.zeroPageY, 4],
0x8e: ["STX", stx, writeAbsolute, modes.absolute, 4], 0x8e: ['STX', stx, writeAbsolute, modes.absolute, 4],
// STY // STY
0x84: ["STY", sty, writeZeroPage, modes.zeroPage, 3], 0x84: ['STY', sty, writeZeroPage, modes.zeroPage, 3],
0x94: ["STY", sty, writeZeroPageX, modes.zeroPageX, 4], 0x94: ['STY', sty, writeZeroPageX, modes.zeroPageX, 4],
0x8c: ["STY", sty, writeAbsolute, modes.absolute, 4], 0x8c: ['STY', sty, writeAbsolute, modes.absolute, 4],
// ADC // ADC
0x69: ["ADC", adc, readImmediate, modes.immediate, 2], 0x69: ['ADC', adc, readImmediate, modes.immediate, 2],
0x65: ["ADC", adc, readZeroPage, modes.zeroPage, 3], 0x65: ['ADC', adc, readZeroPage, modes.zeroPage, 3],
0x75: ["ADC", adc, readZeroPageX, modes.zeroPageX, 4], 0x75: ['ADC', adc, readZeroPageX, modes.zeroPageX, 4],
0x6D: ["ADC", adc, readAbsolute, modes.absolute, 4], 0x6D: ['ADC', adc, readAbsolute, modes.absolute, 4],
0x7D: ["ADC", adc, readAbsoluteX, modes.absoluteX, 4], 0x7D: ['ADC', adc, readAbsoluteX, modes.absoluteX, 4],
0x79: ["ADC", adc, readAbsoluteY, modes.absoluteY, 4], 0x79: ['ADC', adc, readAbsoluteY, modes.absoluteY, 4],
0x61: ["ADC", adc, readZeroPageXIndirect, modes.zeroPageXIndirect, 6], 0x61: ['ADC', adc, readZeroPageXIndirect, modes.zeroPageXIndirect, 6],
0x71: ["ADC", adc, readZeroPageIndirectY, modes.zeroPageIndirectY, 5], 0x71: ['ADC', adc, readZeroPageIndirectY, modes.zeroPageIndirectY, 5],
// SBC // SBC
0xe9: ["SBC", sbc, readImmediate, modes.immediate, 2], 0xe9: ['SBC', sbc, readImmediate, modes.immediate, 2],
0xe5: ["SBC", sbc, readZeroPage, modes.zeroPage, 3], 0xe5: ['SBC', sbc, readZeroPage, modes.zeroPage, 3],
0xf5: ["SBC", sbc, readZeroPageX, modes.zeroPageX, 4], 0xf5: ['SBC', sbc, readZeroPageX, modes.zeroPageX, 4],
0xeD: ["SBC", sbc, readAbsolute, modes.absolute, 4], 0xeD: ['SBC', sbc, readAbsolute, modes.absolute, 4],
0xfD: ["SBC", sbc, readAbsoluteX, modes.absoluteX, 4], 0xfD: ['SBC', sbc, readAbsoluteX, modes.absoluteX, 4],
0xf9: ["SBC", sbc, readAbsoluteY, modes.absoluteY, 4], 0xf9: ['SBC', sbc, readAbsoluteY, modes.absoluteY, 4],
0xe1: ["SBC", sbc, readZeroPageXIndirect, modes.zeroPageXIndirect, 6], 0xe1: ['SBC', sbc, readZeroPageXIndirect, modes.zeroPageXIndirect, 6],
0xf1: ["SBC", sbc, readZeroPageIndirectY, modes.zeroPageIndirectY, 5], 0xf1: ['SBC', sbc, readZeroPageIndirectY, modes.zeroPageIndirectY, 5],
// INC // INC
0xe6: ["INC", inc, readAddrZeroPage, modes.zeroPage, 5], 0xe6: ['INC', inc, readAddrZeroPage, modes.zeroPage, 5],
0xf6: ["INC", inc, readAddrZeroPageX, modes.zeroPageX, 6], 0xf6: ['INC', inc, readAddrZeroPageX, modes.zeroPageX, 6],
0xee: ["INC", inc, readAddrAbsolute, modes.absolute, 6], 0xee: ['INC', inc, readAddrAbsolute, modes.absolute, 6],
0xfe: ["INC", inc, readAddrAbsoluteX, modes.absoluteX, 7], 0xfe: ['INC', inc, readAddrAbsoluteX, modes.absoluteX, 7],
// INX // INX
0xe8: ["INX", inx, null, modes.implied, 2], 0xe8: ['INX', inx, null, modes.implied, 2],
// INY // INY
0xc8: ["INY", iny, null, modes.implied, 2], 0xc8: ['INY', iny, null, modes.implied, 2],
// DEC // DEC
0xc6: ["DEC", dec, readAddrZeroPage, modes.zeroPage, 5], 0xc6: ['DEC', dec, readAddrZeroPage, modes.zeroPage, 5],
0xd6: ["DEC", dec, readAddrZeroPageX, modes.zeroPageX, 6], 0xd6: ['DEC', dec, readAddrZeroPageX, modes.zeroPageX, 6],
0xce: ["DEC", dec, readAddrAbsolute, modes.absolute, 6], 0xce: ['DEC', dec, readAddrAbsolute, modes.absolute, 6],
0xde: ["DEC", dec, readAddrAbsoluteX, modes.absoluteX, 7], 0xde: ['DEC', dec, readAddrAbsoluteX, modes.absoluteX, 7],
// DEX // DEX
0xca: ["DEX", dex, null, modes.implied, 2], 0xca: ['DEX', dex, null, modes.implied, 2],
// DEY // DEY
0x88: ["DEY", dey, null, modes.implied, 2], 0x88: ['DEY', dey, null, modes.implied, 2],
// ASL // ASL
0x0A: ["ASL", aslA, null, modes.accumulator, 2], 0x0A: ['ASL', aslA, null, modes.accumulator, 2],
0x06: ["ASL", asl, readAddrZeroPage, modes.zeroPage, 5], 0x06: ['ASL', asl, readAddrZeroPage, modes.zeroPage, 5],
0x16: ["ASL", asl, readAddrZeroPageX, modes.zeroPageX, 6], 0x16: ['ASL', asl, readAddrZeroPageX, modes.zeroPageX, 6],
0x0E: ["ASL", asl, readAddrAbsolute, modes.absolute, 6], 0x0E: ['ASL', asl, readAddrAbsolute, modes.absolute, 6],
0x1E: ["ASL", asl, readAddrAbsoluteX, modes.absoluteX, 7], 0x1E: ['ASL', asl, readAddrAbsoluteX, modes.absoluteX, 7],
// LSR // LSR
0x4A: ["LSR", lsrA, null, modes.accumulator, 2], 0x4A: ['LSR', lsrA, null, modes.accumulator, 2],
0x46: ["LSR", lsr, readAddrZeroPage, modes.zeroPage, 5], 0x46: ['LSR', lsr, readAddrZeroPage, modes.zeroPage, 5],
0x56: ["LSR", lsr, readAddrZeroPageX, modes.zeroPageX, 6], 0x56: ['LSR', lsr, readAddrZeroPageX, modes.zeroPageX, 6],
0x4E: ["LSR", lsr, readAddrAbsolute, modes.absolute, 6], 0x4E: ['LSR', lsr, readAddrAbsolute, modes.absolute, 6],
0x5E: ["LSR", lsr, readAddrAbsoluteX, modes.absoluteX, 7], 0x5E: ['LSR', lsr, readAddrAbsoluteX, modes.absoluteX, 7],
// ROL // ROL
0x2A: ["ROL", rolA, null, modes.accumulator, 2], 0x2A: ['ROL', rolA, null, modes.accumulator, 2],
0x26: ["ROL", rol, readAddrZeroPage, modes.zeroPage, 5], 0x26: ['ROL', rol, readAddrZeroPage, modes.zeroPage, 5],
0x36: ["ROL", rol, readAddrZeroPageX, modes.zeroPageX, 6], 0x36: ['ROL', rol, readAddrZeroPageX, modes.zeroPageX, 6],
0x2E: ["ROL", rol, readAddrAbsolute, modes.absolute, 6], 0x2E: ['ROL', rol, readAddrAbsolute, modes.absolute, 6],
0x3E: ["ROL", rol, readAddrAbsoluteX, modes.absoluteX, 7], 0x3E: ['ROL', rol, readAddrAbsoluteX, modes.absoluteX, 7],
// ROR // ROR
0x6A: ["ROR", rorA, null, modes.accumulator, 2], 0x6A: ['ROR', rorA, null, modes.accumulator, 2],
0x66: ["ROR", ror, readAddrZeroPage, modes.zeroPage, 5], 0x66: ['ROR', ror, readAddrZeroPage, modes.zeroPage, 5],
0x76: ["ROR", ror, readAddrZeroPageX, modes.zeroPageX, 6], 0x76: ['ROR', ror, readAddrZeroPageX, modes.zeroPageX, 6],
0x6E: ["ROR", ror, readAddrAbsolute, modes.absolute, 6], 0x6E: ['ROR', ror, readAddrAbsolute, modes.absolute, 6],
0x7E: ["ROR", ror, readAddrAbsoluteX, modes.absoluteX, 7], 0x7E: ['ROR', ror, readAddrAbsoluteX, modes.absoluteX, 7],
// AND // AND
0x29: ["AND", and, readImmediate, modes.immediate, 2], 0x29: ['AND', and, readImmediate, modes.immediate, 2],
0x25: ["AND", and, readZeroPage, modes.zeroPage, 2], 0x25: ['AND', and, readZeroPage, modes.zeroPage, 2],
0x35: ["AND", and, readZeroPageX, modes.zeroPageX, 3], 0x35: ['AND', and, readZeroPageX, modes.zeroPageX, 3],
0x2D: ["AND", and, readAbsolute, modes.absolute, 4], 0x2D: ['AND', and, readAbsolute, modes.absolute, 4],
0x3D: ["AND", and, readAbsoluteX, modes.absoluteX, 4], 0x3D: ['AND', and, readAbsoluteX, modes.absoluteX, 4],
0x39: ["AND", and, readAbsoluteY, modes.absoluteY, 4], 0x39: ['AND', and, readAbsoluteY, modes.absoluteY, 4],
0x21: ["AND", and, readZeroPageXIndirect, modes.zeroPageXIndirect, 6], 0x21: ['AND', and, readZeroPageXIndirect, modes.zeroPageXIndirect, 6],
0x31: ["AND", and, readZeroPageIndirectY, modes.zeroPageIndirectY, 5], 0x31: ['AND', and, readZeroPageIndirectY, modes.zeroPageIndirectY, 5],
// ORA // ORA
0x09: ["ORA", ora, readImmediate, modes.immediate, 2], 0x09: ['ORA', ora, readImmediate, modes.immediate, 2],
0x05: ["ORA", ora, readZeroPage, modes.zeroPage, 2], 0x05: ['ORA', ora, readZeroPage, modes.zeroPage, 2],
0x15: ["ORA", ora, readZeroPageX, modes.zeroPageX, 3], 0x15: ['ORA', ora, readZeroPageX, modes.zeroPageX, 3],
0x0D: ["ORA", ora, readAbsolute, modes.absolute, 4], 0x0D: ['ORA', ora, readAbsolute, modes.absolute, 4],
0x1D: ["ORA", ora, readAbsoluteX, modes.absoluteX, 4], 0x1D: ['ORA', ora, readAbsoluteX, modes.absoluteX, 4],
0x19: ["ORA", ora, readAbsoluteY, modes.absoluteY, 4], 0x19: ['ORA', ora, readAbsoluteY, modes.absoluteY, 4],
0x01: ["ORA", ora, readZeroPageXIndirect, modes.zeroPageXIndirect, 6], 0x01: ['ORA', ora, readZeroPageXIndirect, modes.zeroPageXIndirect, 6],
0x11: ["ORA", ora, readZeroPageIndirectY, modes.zeroPageIndirectY, 5], 0x11: ['ORA', ora, readZeroPageIndirectY, modes.zeroPageIndirectY, 5],
// EOR // EOR
0x49: ["EOR", eor, readImmediate, modes.immediate, 2], 0x49: ['EOR', eor, readImmediate, modes.immediate, 2],
0x45: ["EOR", eor, readZeroPage, modes.zeroPage, 3], 0x45: ['EOR', eor, readZeroPage, modes.zeroPage, 3],
0x55: ["EOR", eor, readZeroPageX, modes.zeroPageX, 4], 0x55: ['EOR', eor, readZeroPageX, modes.zeroPageX, 4],
0x4D: ["EOR", eor, readAbsolute, modes.absolute, 4], 0x4D: ['EOR', eor, readAbsolute, modes.absolute, 4],
0x5D: ["EOR", eor, readAbsoluteX, modes.absoluteX, 4], 0x5D: ['EOR', eor, readAbsoluteX, modes.absoluteX, 4],
0x59: ["EOR", eor, readAbsoluteY, modes.absoluteY, 4], 0x59: ['EOR', eor, readAbsoluteY, modes.absoluteY, 4],
0x41: ["EOR", eor, readZeroPageXIndirect, modes.zeroPageXIndirect, 6], 0x41: ['EOR', eor, readZeroPageXIndirect, modes.zeroPageXIndirect, 6],
0x51: ["EOR", eor, readZeroPageIndirectY, modes.zeroPageIndirectY, 5], 0x51: ['EOR', eor, readZeroPageIndirectY, modes.zeroPageIndirectY, 5],
// CMP // CMP
0xc9: ["CMP", cmp, readImmediate, modes.immediate, 2], 0xc9: ['CMP', cmp, readImmediate, modes.immediate, 2],
0xc5: ["CMP", cmp, readZeroPage, modes.zeroPage, 3], 0xc5: ['CMP', cmp, readZeroPage, modes.zeroPage, 3],
0xd5: ["CMP", cmp, readZeroPageX, modes.zeroPageX, 4], 0xd5: ['CMP', cmp, readZeroPageX, modes.zeroPageX, 4],
0xcD: ["CMP", cmp, readAbsolute, modes.absolute, 4], 0xcD: ['CMP', cmp, readAbsolute, modes.absolute, 4],
0xdD: ["CMP", cmp, readAbsoluteX, modes.absoluteX, 4], 0xdD: ['CMP', cmp, readAbsoluteX, modes.absoluteX, 4],
0xd9: ["CMP", cmp, readAbsoluteY, modes.absoluteY, 4], 0xd9: ['CMP', cmp, readAbsoluteY, modes.absoluteY, 4],
0xc1: ["CMP", cmp, readZeroPageXIndirect, modes.zeroPageXIndirect, 6], 0xc1: ['CMP', cmp, readZeroPageXIndirect, modes.zeroPageXIndirect, 6],
0xd1: ["CMP", cmp, readZeroPageIndirectY, modes.zeroPageIndirectY, 5], 0xd1: ['CMP', cmp, readZeroPageIndirectY, modes.zeroPageIndirectY, 5],
// CPX // CPX
0xE0: ["CPX", cpx, readImmediate, modes.immediate, 2], 0xE0: ['CPX', cpx, readImmediate, modes.immediate, 2],
0xE4: ["CPX", cpx, readZeroPage, modes.zeroPage, 3], 0xE4: ['CPX', cpx, readZeroPage, modes.zeroPage, 3],
0xEC: ["CPX", cpx, readAbsolute, modes.absolute, 4], 0xEC: ['CPX', cpx, readAbsolute, modes.absolute, 4],
// CPY // CPY
0xC0: ["CPY", cpy, readImmediate, modes.immediate, 2], 0xC0: ['CPY', cpy, readImmediate, modes.immediate, 2],
0xC4: ["CPY", cpy, readZeroPage, modes.zeroPage, 3], 0xC4: ['CPY', cpy, readZeroPage, modes.zeroPage, 3],
0xCC: ["CPY", cpy, readAbsolute, modes.absolute, 4], 0xCC: ['CPY', cpy, readAbsolute, modes.absolute, 4],
// BIT // BIT
0x24: ["BIT", bit, readZeroPage, modes.zeroPage, 3], 0x24: ['BIT', bit, readZeroPage, modes.zeroPage, 3],
0x2C: ["BIT", bit, readAbsolute, modes.absolute, 4], 0x2C: ['BIT', bit, readAbsolute, modes.absolute, 4],
// BCC // BCC
0x90: ["BCC", brc, flags.C, modes.relative, 2], 0x90: ['BCC', brc, flags.C, modes.relative, 2],
// BCS // BCS
0xB0: ["BCS", brs, flags.C, modes.relative, 2], 0xB0: ['BCS', brs, flags.C, modes.relative, 2],
// BEQ // BEQ
0xF0: ["BEQ", brs, flags.Z, modes.relative, 2], 0xF0: ['BEQ', brs, flags.Z, modes.relative, 2],
// BMI // BMI
0x30: ["BMI", brs, flags.N, modes.relative, 2], 0x30: ['BMI', brs, flags.N, modes.relative, 2],
// BNE // BNE
0xD0: ["BNE", brc, flags.Z, modes.relative, 2], 0xD0: ['BNE', brc, flags.Z, modes.relative, 2],
// BPL // BPL
0x10: ["BPL", brc, flags.N, modes.relative, 2], 0x10: ['BPL', brc, flags.N, modes.relative, 2],
// BVC // BVC
0x50: ["BVC", brc, flags.V, modes.relative, 2], 0x50: ['BVC', brc, flags.V, modes.relative, 2],
// BVS // BVS
0x70: ["BVS", brs, flags.V, modes.relative, 2], 0x70: ['BVS', brs, flags.V, modes.relative, 2],
// TAX // TAX
0xAA: ["TAX", tax, null, modes.implied, 2], 0xAA: ['TAX', tax, null, modes.implied, 2],
// TXA // TXA
0x8A: ["TXA", txa, null, modes.implied, 2], 0x8A: ['TXA', txa, null, modes.implied, 2],
// TAY // TAY
0xA8: ["TAY", tay, null, modes.implied, 2], 0xA8: ['TAY', tay, null, modes.implied, 2],
// TYA // TYA
0x98: ["TYA", tya, null, modes.implied, 2], 0x98: ['TYA', tya, null, modes.implied, 2],
// TSX // TSX
0xBA: ["TSX", tsx, null, modes.implied, 2], 0xBA: ['TSX', tsx, null, modes.implied, 2],
// TXS // TXS
0x9A: ["TXS", txs, null, modes.implied, 2], 0x9A: ['TXS', txs, null, modes.implied, 2],
// PHA // PHA
0x48: ["PHA", pha, null, modes.implied, 3], 0x48: ['PHA', pha, null, modes.implied, 3],
// PLA // PLA
0x68: ["PLA", pla, null, modes.implied, 4], 0x68: ['PLA', pla, null, modes.implied, 4],
// PHP // PHP
0x08: ["PHP", php, null, modes.implied, 3], 0x08: ['PHP', php, null, modes.implied, 3],
// PLP // PLP
0x28: ["PLP", plp, null, modes.implied, 4], 0x28: ['PLP', plp, null, modes.implied, 4],
// JMP // JMP
0x4C: ["JMP", jmp, 0x4C: ['JMP', jmp,
readAddrAbsolute, modes.absolute, 3], readAddrAbsolute, modes.absolute, 3],
0x6C: ["JMP", jmp, 0x6C: ['JMP', jmp,
readAddrAbsoluteIndirectBug, modes.absoluteIndirect, 5], readAddrAbsoluteIndirectBug, modes.absoluteIndirect, 5],
// JSR // JSR
0x20: ["JSR", jsr, readAddrAbsolute, modes.absolute, 6], 0x20: ['JSR', jsr, readAddrAbsolute, modes.absolute, 6],
// RTS // RTS
0x60: ["RTS", rts, null, modes.implied, 6], 0x60: ['RTS', rts, null, modes.implied, 6],
// RTI // RTI
0x40: ["RTI", rti, null, modes.implied, 6], 0x40: ['RTI', rti, null, modes.implied, 6],
// SEC // SEC
0x38: ["SEC", set, flags.C, modes.implied, 2], 0x38: ['SEC', set, flags.C, modes.implied, 2],
// SED // SED
0xF8: ["SED", set, flags.D, modes.implied, 2], 0xF8: ['SED', set, flags.D, modes.implied, 2],
// SEI // SEI
0x78: ["SEI", set, flags.I, modes.implied, 2], 0x78: ['SEI', set, flags.I, modes.implied, 2],
// CLC // CLC
0x18: ["CLC", clr, flags.C, modes.implied, 2], 0x18: ['CLC', clr, flags.C, modes.implied, 2],
// CLD // CLD
0xD8: ["CLD", clr, flags.D, modes.implied, 2], 0xD8: ['CLD', clr, flags.D, modes.implied, 2],
// CLI // CLI
0x58: ["CLI", clr, flags.I, modes.implied, 2], 0x58: ['CLI', clr, flags.I, modes.implied, 2],
// CLV // CLV
0xB8: ["CLV", clr, flags.V, modes.implied, 2], 0xB8: ['CLV', clr, flags.V, modes.implied, 2],
// NOP // NOP
0xea: ["NOP", nop, null, modes.implied, 2], 0xea: ['NOP', nop, null, modes.implied, 2],
// BRK // BRK
0x00: ["BRK", brk, readImmediate, modes.immediate, 7] 0x00: ['BRK', brk, readImmediate, modes.immediate, 7]
}; };
/* 65C02 Instructions */ /* 65C02 Instructions */
var cops = { var cops = {
// INC / DEC A // INC / DEC A
0x1A: ["INC", incA, null, modes.accumulator, 2], 0x1A: ['INC', incA, null, modes.accumulator, 2],
0x3A: ["DEC", decA, null, modes.accumulator, 2], 0x3A: ['DEC', decA, null, modes.accumulator, 2],
// Indirect Zero Page for the masses // Indirect Zero Page for the masses
0x12: ["ORA", ora, readZeroPageIndirect, modes.zeroPageIndirect, 5], 0x12: ['ORA', ora, readZeroPageIndirect, modes.zeroPageIndirect, 5],
0x32: ["AND", and, readZeroPageIndirect, modes.zeroPageIndirect, 5], 0x32: ['AND', and, readZeroPageIndirect, modes.zeroPageIndirect, 5],
0x52: ["EOR", eor, readZeroPageIndirect, modes.zeroPageIndirect, 5], 0x52: ['EOR', eor, readZeroPageIndirect, modes.zeroPageIndirect, 5],
0x72: ["ADC", adc, readZeroPageIndirect, modes.zeroPageIndirect, 5], 0x72: ['ADC', adc, readZeroPageIndirect, modes.zeroPageIndirect, 5],
0x92: ["STA", sta, writeZeroPageIndirect, modes.zeroPageIndirect, 5], 0x92: ['STA', sta, writeZeroPageIndirect, modes.zeroPageIndirect, 5],
0xB2: ["LDA", lda, readZeroPageIndirect, modes.zeroPageIndirect, 5], 0xB2: ['LDA', lda, readZeroPageIndirect, modes.zeroPageIndirect, 5],
0xD2: ["CMP", cmp, readZeroPageIndirect, modes.zeroPageIndirect, 5], 0xD2: ['CMP', cmp, readZeroPageIndirect, modes.zeroPageIndirect, 5],
0xF2: ["SBC", sbc, readZeroPageIndirect, modes.zeroPageIndirect, 5], 0xF2: ['SBC', sbc, readZeroPageIndirect, modes.zeroPageIndirect, 5],
// Better BIT // Better BIT
0x34: ["BIT", bit, readZeroPageX, modes.zeroPageX, 4], 0x34: ['BIT', bit, readZeroPageX, modes.zeroPageX, 4],
0x3C: ["BIT", bit, readAbsoluteX, modes.absoluteX, 4], 0x3C: ['BIT', bit, readAbsoluteX, modes.absoluteX, 4],
0x89: ["BIT", bitI, readImmediate, modes.immediate, 2], 0x89: ['BIT', bitI, readImmediate, modes.immediate, 2],
// JMP absolute indirect indexed // JMP absolute indirect indexed
0x6C: ["JMP", jmp, readAddrAbsoluteIndirect, 0x6C: ['JMP', jmp, readAddrAbsoluteIndirect,
modes.absoluteIndirect, 6], modes.absoluteIndirect, 6],
0x7C: ["JMP", jmp, readAddrAbsoluteXIndirect, 0x7C: ['JMP', jmp, readAddrAbsoluteXIndirect,
modes.absoluteXIndirect, 6], modes.absoluteXIndirect, 6],
// BRA // BRA
0x80: ["BRA", brc, 0, modes.relative, 3], 0x80: ['BRA', brc, 0, modes.relative, 3],
// PHX // PHX
0xDA: ["PHX", phx, null, modes.implied, 3], 0xDA: ['PHX', phx, null, modes.implied, 3],
// PHY // PHY
0x5A: ["PHY", phy, null, modes.implied, 3], 0x5A: ['PHY', phy, null, modes.implied, 3],
// PLX // PLX
0xFA: ["PLX", plx, null, modes.implied, 3], 0xFA: ['PLX', plx, null, modes.implied, 3],
// PLY // PLY
0x7A: ["PLY", ply, null, modes.implied, 3], 0x7A: ['PLY', ply, null, modes.implied, 3],
// STZ // STZ
0x64: ["STZ", stz, writeZeroPage, modes.zeroPage, 3], 0x64: ['STZ', stz, writeZeroPage, modes.zeroPage, 3],
0x74: ["STZ", stz, writeZeroPageX, modes.zeroPageX, 4], 0x74: ['STZ', stz, writeZeroPageX, modes.zeroPageX, 4],
0x9C: ["STZ", stz, writeAbsolute, modes.absolute, 4], 0x9C: ['STZ', stz, writeAbsolute, modes.absolute, 4],
0x9E: ["STZ", stz, writeAbsoluteX, modes.absoluteX, 5], 0x9E: ['STZ', stz, writeAbsoluteX, modes.absoluteX, 5],
// TRB // TRB
0x14: ["TRB", trb, readAddrZeroPage, modes.zeroPage, 5], 0x14: ['TRB', trb, readAddrZeroPage, modes.zeroPage, 5],
0x1C: ["TRB", trb, readAddrAbsolute, modes.absolute, 6], 0x1C: ['TRB', trb, readAddrAbsolute, modes.absolute, 6],
// TSB // TSB
0x04: ["TSB", tsb, readAddrZeroPage, modes.zeroPage, 5], 0x04: ['TSB', tsb, readAddrZeroPage, modes.zeroPage, 5],
0x0C: ["TSB", tsb, readAddrAbsolute, modes.absolute, 6] 0x0C: ['TSB', tsb, readAddrAbsolute, modes.absolute, 6]
}; };
if (is65C02) { if (is65C02) {
for (var key in cops) { for (var key in cops) {
if (cops.hasOwnProperty(key)) {
if (key in ops) { if (key in ops) {
debug('overriding opcode ' + toHex(key)); debug('overriding opcode ' + toHex(key));
} }
}
ops[key] = cops[key]; ops[key] = cops[key];
} }
} }
@ -1047,13 +1048,17 @@ function CPU6502(options)
var unk; var unk;
if (is65C02) { if (is65C02) {
unk = ["NOP (" + toHex(b) + ")", function() { unk = ['NOP (' + toHex(b) + ')', function() {
debug("Unknown OpCode: " + toHex(b) + " at " + toHex(pc - 1, 4)); debug('Unknown OpCode: ' + toHex(b) + ' at ' + toHex(pc - 1, 4));
}, null, modes.implied, 2]; }, null, modes.implied, 2];
} else { } else {
unk = ["???", unk = ['???',
function() { /* debug("Unknown OpCode: " + toHex(b) + function() {
" at " + toHex(pc - 1, 4)); */ }, /*
debug('Unknown OpCode: ' + toHex(b) +
' at ' + toHex(pc - 1, 4));
*/
},
null, modes.implied, null, modes.implied,
1]; 1];
} }
@ -1073,21 +1078,21 @@ function CPU6502(options)
if (symbols && symbols[v]) { if (symbols && symbols[v]) {
return symbols[v]; return symbols[v];
} else { } else {
return "$" + toHex(v, n); return '$' + toHex(v, n);
} }
} }
var result = ""; var result = '';
switch (m) { switch (m) {
case modes.implied: case modes.implied:
break; break;
case modes.immediate: case modes.immediate:
result = "#" + toHexOrSymbol(readByte(addr, true)); result = '#' + toHexOrSymbol(readByte(addr, true));
break; break;
case modes.absolute: case modes.absolute:
result = "" + toHexOrSymbol(readWord(addr, true), 4); result = '' + toHexOrSymbol(readWord(addr, true), 4);
break; break;
case modes.zeroPage: case modes.zeroPage:
result = "" + toHexOrSymbol(readByte(addr, true)); result = '' + toHexOrSymbol(readByte(addr, true));
break; break;
case modes.relative: case modes.relative:
{ {
@ -1096,38 +1101,38 @@ function CPU6502(options)
off -= 256; off -= 256;
} }
addr += off + 1; addr += off + 1;
result = "" + toHexOrSymbol(addr, 4) + " (" + off + ")"; result = '' + toHexOrSymbol(addr, 4) + ' (' + off + ')';
} }
break; break;
case modes.absoluteX: case modes.absoluteX:
result = "" + toHexOrSymbol(readWord(addr, true), 4) + ",X"; result = '' + toHexOrSymbol(readWord(addr, true), 4) + ',X';
break; break;
case modes.absoluteY: case modes.absoluteY:
result = "" + toHexOrSymbol(readWord(addr, true), 4) + ",Y"; result = '' + toHexOrSymbol(readWord(addr, true), 4) + ',Y';
break; break;
case modes.zeroPageX: case modes.zeroPageX:
result = "" + toHexOrSymbol(readByte(addr, true)) + ",X"; result = '' + toHexOrSymbol(readByte(addr, true)) + ',X';
break; break;
case modes.zeroPageY: case modes.zeroPageY:
result = "" + toHexOrSymbol(readByte(addr, true)) + ",Y"; result = '' + toHexOrSymbol(readByte(addr, true)) + ',Y';
break; break;
case modes.absoluteIndirect: case modes.absoluteIndirect:
result = "(" + toHexOrSymbol(readWord(addr, true), 4) + ")"; result = '(' + toHexOrSymbol(readWord(addr, true), 4) + ')';
break; break;
case modes.zeroPageXIndirect: case modes.zeroPageXIndirect:
result = "(" + toHexOrSymbol(readByte(addr, true)) + ",X)"; result = '(' + toHexOrSymbol(readByte(addr, true)) + ',X)';
break; break;
case modes.zeroPageIndirectY: case modes.zeroPageIndirectY:
result = "(" + toHexOrSymbol(readByte(addr, true)) + "),Y"; result = '(' + toHexOrSymbol(readByte(addr, true)) + '),Y';
break; break;
case modes.accumulator: case modes.accumulator:
result = "A"; result = 'A';
break; break;
case modes.zeroPageIndirect: case modes.zeroPageIndirect:
result = "(" + toHexOrSymbol(readByte(addr, true)) + ")"; result = '(' + toHexOrSymbol(readByte(addr, true)) + ')';
break; break;
case modes.absoluteXIndirect: case modes.absoluteXIndirect:
result = "(" + toHexOrSymbol(readWord(addr, true), 4) + ",X)"; result = '(' + toHexOrSymbol(readWord(addr, true), 4) + ',X)';
break; break;
default: default:
break; break;
@ -1179,8 +1184,9 @@ function CPU6502(options)
{ {
var op, end = cycles + c; var op, end = cycles + c;
if (inCallback) if (inCallback) {
return; return;
}
while (cycles < end) { while (cycles < end) {
sync = true; sync = true;
@ -1226,7 +1232,7 @@ function CPU6502(options)
/* IRQ - Interupt Request */ /* IRQ - Interupt Request */
irq: function cpu_irq() irq: function cpu_irq()
{ {
if (!(sr & flags.I)) { if ((sr & flags.I) === 0) {
pushWord(pc); pushWord(pc);
pushByte(sr & ~flags.B); pushByte(sr & ~flags.B);
if (is65C02) { if (is65C02) {
@ -1260,35 +1266,35 @@ function CPU6502(options)
var b = readByte(_pc, true), var b = readByte(_pc, true),
op = ops[b], op = ops[b],
size = sizes[op[3]], size = sizes[op[3]],
result = toHex(_pc, 4) + "- "; result = toHex(_pc, 4) + '- ';
if (symbols) { if (symbols) {
if (symbols[_pc]) { if (symbols[_pc]) {
result += symbols[_pc] + result += symbols[_pc] +
" ".substring(symbols[_pc].length); ' '.substring(symbols[_pc].length);
} else { } else {
result += " "; result += ' ';
} }
} }
for (var idx = 0; idx < 4; idx++) { for (var idx = 0; idx < 4; idx++) {
if (idx < size) { if (idx < size) {
result += toHex(readByte(_pc + idx, true)) + " "; result += toHex(readByte(_pc + idx, true)) + ' ';
} else { } else {
result += " "; result += ' ';
} }
} }
if (op === undefined) if (op === undefined)
result += "??? (" + toHex(b) + ")"; result += '??? (' + toHex(b) + ')';
else else
result += op[0] + " " + dumpArgs(_pc + 1, op[3], symbols); result += op[0] + ' ' + dumpArgs(_pc + 1, op[3], symbols);
return result; return result;
}, },
dumpPage: function(start, end) { dumpPage: function(start, end) {
var result = ""; var result = '';
if (start === undefined) { if (start === undefined) {
start = pc >> 8; start = pc >> 8;
} }
@ -1298,21 +1304,21 @@ function CPU6502(options)
for (var page = start; page <= end; page++) { for (var page = start; page <= end; page++) {
var b, idx, jdx; var b, idx, jdx;
for (idx = 0; idx < 16; idx++) { for (idx = 0; idx < 16; idx++) {
result += toHex(page) + toHex(idx << 4) + ": "; result += toHex(page) + toHex(idx << 4) + ': ';
for (jdx = 0; jdx < 16; jdx++) { for (jdx = 0; jdx < 16; jdx++) {
b = readByte(page * 256 + idx * 16 + jdx, true); b = readByte(page * 256 + idx * 16 + jdx, true);
result += toHex(b) + " "; result += toHex(b) + ' ';
} }
result += " "; result += ' ';
for (jdx = 0; jdx < 16; jdx++) { for (jdx = 0; jdx < 16; jdx++) {
b = readByte(page * 256 + idx * 16 + jdx, true) & 0x7f; b = readByte(page * 256 + idx * 16 + jdx, true) & 0x7f;
if (b >= 0x20 && b < 0x7f) { if (b >= 0x20 && b < 0x7f) {
result += String.fromCharCode(b); result += String.fromCharCode(b);
} else { } else {
result += "."; result += '.';
} }
} }
result += "\n"; result += '\n';
} }
} }
return result; return result;
@ -1367,20 +1373,20 @@ function CPU6502(options)
dumpRegisters: function() { dumpRegisters: function() {
return toHex(pc, 4) + return toHex(pc, 4) +
"- A=" + toHex(ar) + '- A=' + toHex(ar) +
" X=" + toHex(xr) + ' X=' + toHex(xr) +
" Y=" + toHex(yr) + ' Y=' + toHex(yr) +
" P=" + toHex(sr) + ' P=' + toHex(sr) +
" S=" + toHex(sp) + ' S=' + toHex(sp) +
" " + ' ' +
(sr & flags.N ? "N" : "-") + (sr & flags.N ? 'N' : '-') +
(sr & flags.V ? "V" : "-") + (sr & flags.V ? 'V' : '-') +
"-" + '-' +
(sr & flags.B ? "B" : "-") + (sr & flags.B ? 'B' : '-') +
(sr & flags.D ? "D" : "-") + (sr & flags.D ? 'D' : '-') +
(sr & flags.I ? "I" : "-") + (sr & flags.I ? 'I' : '-') +
(sr & flags.Z ? "Z" : "-") + (sr & flags.Z ? 'Z' : '-') +
(sr & flags.C ? "C" : "-"); (sr & flags.C ? 'C' : '-');
}, },
read: function(page, off) { 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-2016 Will Scullin <scullin@scullinsteel.com>
/* Copyright 2010-2013 Will Scullin <scullin@scullinsteel.com>
* *
* Permission to use, copy, modify, distribute, and sell this software and its * Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that * documentation for any purpose is hereby granted without fee, provided that
@ -10,18 +9,20 @@
* implied warranty. * implied warranty.
*/ */
/*jshint browser: true */
/*globals extend: false, bytify: false, base64_encode: false, base64_decode: false, each: false */
/*exported DiskII */ /*exported DiskII */
/*globals bytify, each: false, extend: false
base64_encode, base64_decode
Uint8Array
*/
function DiskII(io, callbacks, slot) function DiskII(io, callbacks, slot)
{ {
"use strict"; 'use strict';
slot = slot || 6; slot = slot || 6;
var _drives = [ var _drives = [
{ // Drive 1 { // Drive 1
format: "dsk", format: 'dsk',
volume: 254, volume: 254,
tracks: [], tracks: [],
track: 0, track: 0,
@ -31,7 +32,7 @@ function DiskII(io, callbacks, slot)
dirty: false dirty: false
}, },
{ // Drive 2 { // Drive 2
format: "dsk", format: 'dsk',
volume: 254, volume: 254,
tracks: [], tracks: [],
track: 0, track: 0,
@ -229,11 +230,11 @@ function DiskII(io, callbacks, slot)
function _json_encode(drive, pretty) { function _json_encode(drive, pretty) {
var cur = _drives[drive - 1]; var cur = _drives[drive - 1];
var data = []; var data = [];
var format = "dsk"; var format = 'dsk';
for (var t = 0; t < cur.tracks.length; t++) { for (var t = 0; t < cur.tracks.length; t++) {
data[t] = []; data[t] = [];
if (cur.format === "nib") { if (cur.format === 'nib') {
format = "nib"; format = 'nib';
data[t] = base64_encode(cur.tracks[t]); data[t] = base64_encode(cur.tracks[t]);
} else { } else {
for (var s = 0; s < 0x10; s++) { for (var s = 0; s < 0x10; s++) {
@ -241,12 +242,12 @@ function DiskII(io, callbacks, slot)
} }
} }
} }
return JSON.stringify({"type": format, return JSON.stringify({'type': format,
"encoding": "base64", 'encoding': 'base64',
"volume": cur.volume, 'volume': cur.volume,
"data": data}, 'data': data},
null, null,
pretty ? " " : null); pretty ? ' ' : null);
} }
function _json_decode(drive, data) { function _json_decode(drive, data) {
@ -385,7 +386,7 @@ function DiskII(io, callbacks, slot)
[ 1,-2,-1, 0]]; [ 1,-2,-1, 0]];
function setPhase(phase, on) { function setPhase(phase, on) {
_debug("phase " + phase + (on ? " on" : " off")); _debug('phase ' + phase + (on ? ' on' : ' off'));
if (on) { if (on) {
_cur.track += _phase_delta[_cur.phase][phase]; _cur.track += _phase_delta[_cur.phase][phase];
_cur.phase = phase; _cur.phase = phase;
@ -395,10 +396,10 @@ function DiskII(io, callbacks, slot)
if (_cur.track < 0x0) if (_cur.track < 0x0)
_cur.track = 0x0; _cur.track = 0x0;
/* _debug("Drive " + _drive + /* _debug('Drive ' + _drive +
", track " + toHex(_cur.track >> 1) + ', track ' + toHex(_cur.track >> 1) +
" (" + toHex(_cur.track) + ")" + ' (' + toHex(_cur.track) + ')' +
" [" + (_cur.track % 4) + "/" + phase + "]"); */ ' [' + (_cur.track % 4) + '/' + phase + ']'); */
} }
} }
@ -431,18 +432,18 @@ function DiskII(io, callbacks, slot)
break; break;
case LOC.DRIVEOFF: case LOC.DRIVEOFF:
_debug("Drive Off"); _debug('Drive Off');
_on = false; _on = false;
if (callbacks.driveLight) { callbacks.driveLight(_drive, false); } if (callbacks.driveLight) { callbacks.driveLight(_drive, false); }
break; break;
case LOC.DRIVEON: case LOC.DRIVEON:
_debug("Drive On"); _debug('Drive On');
_on = true; _on = true;
if (callbacks.driveLight) { callbacks.driveLight(_drive, true); } if (callbacks.driveLight) { callbacks.driveLight(_drive, true); }
break; break;
case LOC.DRIVE1: case LOC.DRIVE1:
_debug("Disk 1"); _debug('Disk 1');
_drive = 1; _drive = 1;
_cur = _drives[_drive - 1]; _cur = _drives[_drive - 1];
if (_on && callbacks.driveLight) { if (_on && callbacks.driveLight) {
@ -451,7 +452,7 @@ function DiskII(io, callbacks, slot)
} }
break; break;
case LOC.DRIVE2: case LOC.DRIVE2:
_debug("Disk 2"); _debug('Disk 2');
_drive = 2; _drive = 2;
_cur = _drives[_drive - 1]; _cur = _drives[_drive - 1];
if (_on && callbacks.driveLight) { if (_on && callbacks.driveLight) {
@ -462,22 +463,22 @@ function DiskII(io, callbacks, slot)
case LOC.DRIVEREAD: case LOC.DRIVEREAD:
result = _readNext(); result = _readNext();
// _debug("read: " + toHex(result)); // _debug('read: ' + toHex(result));
break; break;
case LOC.DRIVEWRITE: case LOC.DRIVEWRITE:
// _debug("write: " + toHex(val)); // _debug('write: ' + toHex(val));
if (val !== undefined) { if (val !== undefined) {
_writeNext(val); _writeNext(val);
} }
break; break;
case LOC.DRIVEREADMODE: case LOC.DRIVEREADMODE:
_debug("Read Mode"); _debug('Read Mode');
_writeMode = false; _writeMode = false;
result = (_readNext() & 0x7f) | (_cur.readOnly ? 0x80 : 0x00); result = (_readNext() & 0x7f) | (_cur.readOnly ? 0x80 : 0x00);
break; break;
case LOC.DRIVEWRITEMODE: case LOC.DRIVEWRITEMODE:
_debug("Write Mode"); _debug('Write Mode');
_writeMode = true; _writeMode = true;
break; break;
default: default:
@ -650,10 +651,10 @@ function DiskII(io, callbacks, slot)
var fmt = disk.type, readOnly = disk.readOnly; var fmt = disk.type, readOnly = disk.readOnly;
var data, t, s; var data, t, s;
if (disk.encoding == "base64") { if (disk.encoding == 'base64') {
data = []; data = [];
for (t = 0; t < disk.data.length; t++) { for (t = 0; t < disk.data.length; t++) {
if (fmt == "nib") { if (fmt == 'nib') {
data[t] = base64_decode(disk.data[t]); data[t] = base64_decode(disk.data[t]);
} else { } else {
data[t] = []; data[t] = [];
@ -667,7 +668,7 @@ function DiskII(io, callbacks, slot)
} }
var cur = _drives[drive - 1]; 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) { // if (v == 0x00) {
var v = disk.volume || 0xfe; var v = disk.volume || 0xfe;
// } // }
@ -679,15 +680,15 @@ function DiskII(io, callbacks, slot)
for (t = 0; t < data.length; t++) { for (t = 0; t < data.length; t++) {
var track = []; var track = [];
if (fmt === "nib") { if (fmt === 'nib') {
track = data[t]; track = data[t];
} else { } else {
for (s = 0; s < data[t].length; s++) { for (s = 0; s < data[t].length; s++) {
var _s = 15 - s; var _s = 15 - s;
if (fmt === "po") { // ProDOS Order if (fmt === 'po') { // ProDOS Order
extend(track, extend(track,
_explodeSector(v, t, _PO[s], data[t][s])); _explodeSector(v, t, _PO[s], data[t][s]));
} else if (fmt === "dsk") { // DOS Order } else if (fmt === 'dsk') { // DOS Order
extend(track, extend(track,
_explodeSector(v, t, _DO[_s], data[t][_s])); _explodeSector(v, t, _DO[_s], data[t][_s]));
} else { // flat } else { // flat
@ -712,11 +713,11 @@ function DiskII(io, callbacks, slot)
var _cur = _drives[drive - 1]; var _cur = _drives[drive - 1];
var tracks = []; var tracks = [];
var v = 254; var v = 254;
if (fmt === "do") { if (fmt === 'do') {
fmt = "dsk"; fmt = 'dsk';
} }
_cur.readOnly = false; _cur.readOnly = false;
if (fmt === "2mg") { if (fmt === '2mg') {
// Standard header size is 64 bytes. Make assumptions. // Standard header size is 64 bytes. Make assumptions.
var prefix = new Uint8Array(data.slice(0, 64)); var prefix = new Uint8Array(data.slice(0, 64));
data = data.slice(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. // Sure, it's really 64 bits. But only 2 are actually used.
switch (prefix[0xc]) { switch (prefix[0xc]) {
case 0: case 0:
fmt = "dsk"; fmt = 'dsk';
break; break;
case 1: case 1:
fmt = "po"; fmt = 'po';
break; break;
case 2: case 2:
fmt = "nib"; fmt = 'nib';
break; break;
default: // Something hinky, assume "dsk" default: // Something hinky, assume 'dsk'
fmt = "dsk"; fmt = 'dsk';
break; break;
} }
var flags = var flags =
@ -749,19 +750,19 @@ function DiskII(io, callbacks, slot)
} }
for (var t = 0; t < 35; t++) { for (var t = 0; t < 35; t++) {
var track, off, d; var track, off, d;
if (fmt === "nib") { if (fmt === 'nib') {
off = t * 0x1a00; off = t * 0x1a00;
track = new Uint8Array(data.slice(off, off + 0x1a00)); track = new Uint8Array(data.slice(off, off + 0x1a00));
} else { } else {
track = []; track = [];
for (var s = 0; s < 16; s++) { for (var s = 0; s < 16; s++) {
var _s = 15 - s; var _s = 15 - s;
if (fmt == "po") { // ProDOS Order if (fmt == 'po') { // ProDOS Order
off = (16 * t + s) * 256; off = (16 * t + s) * 256;
d = new Uint8Array(data.slice(off, off + 256)); d = new Uint8Array(data.slice(off, off + 256));
extend(track, extend(track,
_explodeSector(v, t, _PO[s], d)); _explodeSector(v, t, _PO[s], d));
} else if (fmt == "dsk") { // DOS Order } else if (fmt == 'dsk') { // DOS Order
off = (16 * t + _s) * 256; off = (16 * t + _s) * 256;
d = new Uint8Array(data.slice(off, off + 256)); d = new Uint8Array(data.slice(off, off + 256));
extend(track, extend(track,
@ -788,7 +789,7 @@ function DiskII(io, callbacks, slot)
var idx = 0; var idx = 0;
for (var t = 0; t < cur.tracks.length; t++) { for (var t = 0; t < cur.tracks.length; t++) {
if (cur.format === "nib") { if (cur.format === 'nib') {
data[idx++] = cur.tracks[t]; data[idx++] = cur.tracks[t];
} else { } else {
for (var s = 0; s < 0x10; s++) { for (var s = 0; s < 0x10; s++) {
@ -808,7 +809,7 @@ function DiskII(io, callbacks, slot)
var data = []; var data = [];
for (var t = 0; t < cur.tracks.length; t++) { for (var t = 0; t < cur.tracks.length; t++) {
data[t] = []; data[t] = [];
if (cur.format === "nib") { if (cur.format === 'nib') {
data += base64_encode(cur.tracks[t]); data += base64_encode(cur.tracks[t]);
} else { } else {
for (var s = 0; s < 0x10; s++) { 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 */ /*exported Apple2ROM */
function Apple2ROM() function Apple2ROM()
{ {
@ -1542,8 +1540,6 @@ function Apple2ROM()
0xf5,0x03,0xfb,0x03,0x62,0xfa,0x59,0xff 0xf5,0x03,0xfb,0x03,0x62,0xfa,0x59,0xff
]; ];
rom = bytify(rom);
return { return {
start: function() { start: function() {
return 0xd0; return 0xd0;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,5 +1,4 @@
/* -*- mode: JavaScript; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* Copyright 2010-2016 Will Scullin <scullin@scullinsteel.com>
/* Copyright 2010-2013 Will Scullin <scullin@scullinsteel.com>
* *
* Permission to use, copy, modify, distribute, and sell this software and its * Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that * documentation for any purpose is hereby granted without fee, provided that
@ -14,6 +13,8 @@
function Slot3(mmu, rom) function Slot3(mmu, rom)
{ {
'use strict';
var auxRomFn = { var auxRomFn = {
start: function auxRom_start() { start: function auxRom_start() {
return 0xc8; 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-2016 Will Scullin <scullin@scullinsteel.com>
/* Copyright 2010-2013 Will Scullin <scullin@scullinsteel.com>
* *
* Permission to use, copy, modify, distribute, and sell this software and its * Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that * documentation for any purpose is hereby granted without fee, provided that
@ -11,10 +10,12 @@
*/ */
/*exported Thunderclock */ /*exported Thunderclock */
/*global each */ /*global each: false */
function Thunderclock(mmu, io, slot) function Thunderclock(mmu, io, slot)
{ {
'use strict';
var rom = [ var rom = [
0x08,0x78,0x28,0x2c,0x58,0xff,0x70,0x05, // 00 0x08,0x78,0x28,0x2c,0x58,0xff,0x70,0x05, // 00
0x38,0xb0,0x01,0x18,0xb8,0x08,0x78,0x48, 0x38,0xb0,0x01,0x18,0xb8,0x08,0x78,0x48,
@ -340,7 +341,7 @@ function Thunderclock(mmu, io, slot)
switch (off) { switch (off) {
case LOC.CONTROL: case LOC.CONTROL:
if (val !== undefined) { if (val !== undefined) {
if (val & FLAGS.STROBE) { if ((val & FLAGS.STROBE) !== 0) {
if ((_command & 0x78) == 0x18) { if ((_command & 0x78) == 0x18) {
_calcbits(); _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-2016 Will Scullin <scullin@scullinsteel.com>
/* Copyright 2010-2014 Will Scullin <scullin@scullinsteel.com>
* *
* Permission to use, copy, modify, distribute, and sell this software and its * Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that * documentation for any purpose is hereby granted without fee, provided that
@ -10,7 +9,6 @@
* implied warranty. * implied warranty.
*/ */
/*jshint jquery: true, browser: true */
/*globals debug: false */ /*globals debug: false */
/*exported enableSound, initAudio */ /*exported enableSound, initAudio */
@ -23,7 +21,7 @@
var audioContext; var audioContext;
var audioNode; var audioNode;
var AC = window.webkitAudioContext || window.AudioContext; var AC = window.AudioContext;
if (typeof AC !== 'undefined') { if (typeof AC !== 'undefined') {
audioContext = new AC(); audioContext = new AC();

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 * Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that * 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 * Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that * documentation for any purpose is hereby granted without fee, provided that
@ -14,7 +14,7 @@
/*globals flipX: false, flipY: false */ /*globals flipX: false, flipY: false */
/*exported processGamepad, initGamepad, gamepad */ /*exported processGamepad, initGamepad, gamepad */
var gamepadSupportAvailable = !!navigator.webkitGetGamepads; var getGamepads = navigator.getGamepads || navigator.webkitGetGamepads;
var gamepad; var gamepad;
var gamepadMap = []; var gamepadMap = [];
var gamepadState = []; var gamepadState = [];
@ -54,13 +54,13 @@ var DEFAULT_GAMEPAD = {
'START': '\033' 'START': '\033'
}; };
window.addEventListener("gamepadconnected", function(e) { window.addEventListener('gamepadconnected', function(e) {
gamepad = e.gamepad; gamepad = e.gamepad;
}); });
function processGamepad(io) { function processGamepad(io) {
if (gamepadSupportAvailable) { if (getGamepads) {
gamepad = navigator.webkitGetGamepads()[0]; gamepad = getGamepads.call(navigator)[0];
} }
if (gamepad) { if (gamepad) {
var x = (gamepad.axes[0] * 1.414 + 1) / 2.0; var x = (gamepad.axes[0] * 1.414 + 1) / 2.0;
@ -74,7 +74,7 @@ function processGamepad(io) {
var old = gamepadState[idx]; var old = gamepadState[idx];
var button = gamepad.buttons[idx]; var button = gamepad.buttons[idx];
var pressed; var pressed;
if (typeof(button) == "object") { if (typeof(button) == 'object') {
pressed = button.pressed; pressed = button.pressed;
} else { } else {
pressed = (button == 1.0); 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-2016 Will Scullin <scullin@scullinsteel.com>
/* Copyright 2010-2013 Will Scullin <scullin@scullinsteel.com>
* *
* Permission to use, copy, modify, distribute, and sell this software and its * Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that * documentation for any purpose is hereby granted without fee, provided that
@ -10,7 +9,6 @@
* implied warranty. * implied warranty.
*/ */
/*jshint jquery: true, browser: true */
/*globals debug: false, toHex: false, reset: false */ /*globals debug: false, toHex: false, reset: false */
/*exported KeyBoard */ /*exported KeyBoard */
@ -132,7 +130,7 @@ function KeyBoard(io) {
0xDB: [0x5B, 0x5B, 0x5B], // [ 0xDB: [0x5B, 0x5B, 0x5B], // [
0xDC: [0x5C, 0x5C, 0x5C], // \ 0xDC: [0x5C, 0x5C, 0x5C], // \
0xDD: [0x5D, 0x5D, 0x5D], // ] 0xDD: [0x5D, 0x5D, 0x5D], // ]
0xDE: [0x27, 0x27, 0x22], // ' - " 0xDE: [0x27, 0x27, 0x22], // ' - '
0xFF: [0xFF, 0xFF, 0xFF] // No comma line 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;'], ['CTRL','A','S','D','F','G','H','J','K','L',';','&larr;','&rarr;'],
['SHIFT','Z','X','C','V','B','N','M',',','.','/','SHIFT'], ['SHIFT','Z','X','C','V','B','N','M',',','.','/','SHIFT'],
['POWER', '&nbsp;']], ['POWER', '&nbsp;']],
[['!','"','#','$','%','&',"'",'(',')','0','*','=','RESET'], [['!','"','#','$','%','&','\'','(',')','0','*','=','RESET'],
['ESC','Q','W','E','R','T','Y','U','I','O','@','REPT','RETURN'], ['ESC','Q','W','E','R','T','Y','U','I','O','@','REPT','RETURN'],
['CTRL','A','S','D','F','BELL','H','J','K','L','+','&larr;','&rarr;'], ['CTRL','A','S','D','F','BELL','H','J','K','L','+','&larr;','&rarr;'],
['SHIFT','Z','X','C','V','B','^',']','<','>','?','SHIFT'], ['SHIFT','Z','X','C','V','B','^',']','<','>','?','SHIFT'],
@ -160,7 +158,7 @@ function KeyBoard(io) {
return keymap[code][evt.shiftKey ? 2 : (evt.ctrlKey ? 1 : 0)]; return keymap[code][evt.shiftKey ? 2 : (evt.ctrlKey ? 1 : 0)];
} }
debug("Unhandled key = " + toHex(code)); debug('Unhandled key = ' + toHex(code));
return 0xFF; return 0xFF;
}, },
@ -168,19 +166,19 @@ function KeyBoard(io) {
shifted = down; shifted = down;
if (down) { if (down) {
io.buttonDown(2); io.buttonDown(2);
$("#keyboard .key-SHIFT").addClass("active"); $('#keyboard .key-SHIFT').addClass('active');
} else { } else {
io.buttonUp(2); io.buttonUp(2);
$("#keyboard .key-SHIFT").removeClass("active"); $('#keyboard .key-SHIFT').removeClass('active');
} }
}, },
controlKey: function keyboard_controlKey(down) { controlKey: function keyboard_controlKey(down) {
controlled = down; controlled = down;
if (down) { if (down) {
$("#keyboard .key-CTRL").addClass("active"); $('#keyboard .key-CTRL').addClass('active');
} else { } else {
$("#keyboard .key-CTRL").removeClass("active"); $('#keyboard .key-CTRL').removeClass('active');
} }
}, },
@ -190,37 +188,37 @@ function KeyBoard(io) {
kb.disableSelection(); kb.disableSelection();
function buildLabel(k) { function buildLabel(k) {
var span = $("<span>" + k + "</span>"); var span = $('<span>' + k + '</span>');
if (k.length > 1 && k.substr(0,1) != '&') if (k.length > 1 && k.substr(0,1) != '&')
span.addClass("small"); span.addClass('small');
return span; return span;
} }
function _mouseup(ev) { function _mouseup(ev) {
$(ev.currentTarget).removeClass("pressed"); $(ev.currentTarget).removeClass('pressed');
} }
function _mousedown(ev) { function _mousedown(ev) {
$(ev.currentTarget).addClass("pressed"); $(ev.currentTarget).addClass('pressed');
var key = $(ev.currentTarget).data(shifted ? "key2" : "key1"); var key = $(ev.currentTarget).data(shifted ? 'key2' : 'key1');
switch (key) { switch (key) {
case "BELL": case 'BELL':
key = "G"; key = 'G';
break; break;
case "RETURN": case 'RETURN':
key = "\r"; key = '\r';
break; break;
case "&larr;": case '&larr;':
key = "\010"; key = '\010';
break; break;
case "&rarr;": case '&rarr;':
key = "\025"; key = '\025';
break; break;
case "&nbsp;": case '&nbsp;':
key = " "; key = ' ';
break; break;
case "ESC": case 'ESC':
key = "\033"; key = '\033';
break; break;
default: default:
break; break;
@ -228,26 +226,26 @@ function KeyBoard(io) {
if (key.length > 1) { if (key.length > 1) {
switch (key) { switch (key) {
case "SHIFT": case 'SHIFT':
shifted = !shifted; shifted = !shifted;
$("#keyboard .key-SHIFT").toggleClass("active"); $('#keyboard .key-SHIFT').toggleClass('active');
break; break;
case "CTRL": case 'CTRL':
controlled = !controlled; controlled = !controlled;
$("#keyboard .key-CTRL").toggleClass("active"); $('#keyboard .key-CTRL').toggleClass('active');
break; break;
case "RESET": case 'RESET':
reset(); reset();
break; break;
case "POWER": case 'POWER':
if (window.confirm("Power Cycle?")) if (window.confirm('Power Cycle?'))
window.location.reload(); window.location.reload();
break; break;
default: default:
break; break;
} }
} else { } else {
if (controlled && key >= "@" && key <= "_") { if (controlled && key >= '@' && key <= '_') {
io.keyDown(key.charCodeAt(0) - 0x40); io.keyDown(key.charCodeAt(0) - 0x40);
} else { } else {
io.keyDown(key.charCodeAt(0)); io.keyDown(key.charCodeAt(0));
@ -256,37 +254,37 @@ function KeyBoard(io) {
} }
for (y = 0; y < 5; y++) { for (y = 0; y < 5; y++) {
row = $("<div class='row row" + y + "'/>"); row = $('<div class="row row' + y + '"/>');
kb.append(row); kb.append(row);
for (x = 0; x < keys[0][y].length; x++) { for (x = 0; x < keys[0][y].length; x++) {
key1 = keys[0][y][x]; key1 = keys[0][y][x];
key2 = keys[1][y][x]; key2 = keys[1][y][x];
label = $("<div />"); label = $('<div />');
label1 = buildLabel(key1); label1 = buildLabel(key1);
label2 = buildLabel(key2); label2 = buildLabel(key2);
key = $("<div class='key'>"); key = $('<div class="key">');
key.addClass("key-" + key1.replace(/[&;]/g,"")); key.addClass('key-' + key1.replace(/[&;]/g,''));
if (key1.length > 1) if (key1.length > 1)
key.addClass("vcenter"); key.addClass('vcenter');
if (key1 != key2) { if (key1 != key2) {
key.addClass("key-" + key2.replace(/[&;]/g,"")); key.addClass('key-' + key2.replace(/[&;]/g,''));
label.append(label2); label.append(label2);
label.append("<br/>"); label.append('<br/>');
} }
label.append(label1); label.append(label1);
key.append(label); key.append(label);
key.data({"key1": key1, "key2": key2}); key.data({'key1': key1, 'key2': key2});
if (window.ontouchstart === undefined) { if (window.ontouchstart === undefined) {
key.bind("mousedown", _mousedown); key.bind('mousedown', _mousedown);
key.bind("mouseup mouseout", _mouseup); key.bind('mouseup mouseout', _mouseup);
} else { } else {
key.bind("touchstart", _mousedown); key.bind('touchstart', _mousedown);
key.bind("touchend touchleave", _mouseup); key.bind('touchend touchleave', _mouseup);
} }
row.append(key); 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 * Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that * documentation for any purpose is hereby granted without fee, provided that
@ -151,7 +151,7 @@ function KeyBoard(io) {
0xDB: [0x5B, 0x5B, 0x7B], // [ 0xDB: [0x5B, 0x5B, 0x7B], // [
0xDC: [0x5C, 0x5C, 0x7C], // \ 0xDC: [0x5C, 0x5C, 0x7C], // \
0xDD: [0x5D, 0x5D, 0x7D], // ] 0xDD: [0x5D, 0x5D, 0x7D], // ]
0xDE: [0x27, 0x22, 0x22], // ' - " 0xDE: [0x27, 0x22, 0x22], // ' - '
0xFF: [0xFF, 0xFF, 0xFF] // No comma line 0xFF: [0xFF, 0xFF, 0xFF] // No comma line
}; };
@ -159,12 +159,12 @@ function KeyBoard(io) {
var keys = var keys =
[[['ESC','1','2','3','4','5','6','7','8','9','0','-','=','DELETE'], [[['ESC','1','2','3','4','5','6','7','8','9','0','-','=','DELETE'],
['TAB','Q','W','E','R','T','Y','U','I','O','P','[',']','\\'], ['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'], ['SHIFT','Z','X','C','V','B','N','M',',','.','/','SHIFT'],
['LOCK','`','POW','OPEN_APPLE','&nbsp;','CLOSED_APPLE','&larr;','&rarr;','&darr;','&uarr;']], ['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','{','}','|'], ['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'], ['SHIFT','Z','X','C','V','B','N','M','<','>','?','SHIFT'],
['CAPS','~','POW','OPEN_APPLE','&nbsp;','CLOSED_APPLE','&larr;','&rarr;','&darr;','&uarr;']]]; ['CAPS','~','POW','OPEN_APPLE','&nbsp;','CLOSED_APPLE','&larr;','&rarr;','&darr;','&uarr;']]];
@ -183,7 +183,7 @@ function KeyBoard(io) {
if (capslocked && key >= 0x61 && key <= 0x7A) if (capslocked && key >= 0x61 && key <= 0x7A)
key -= 0x20; key -= 0x20;
} else { } else {
debug("Unhandled key = " + toHex(code)); debug('Unhandled key = ' + toHex(code));
} }
return key; return key;
@ -193,19 +193,19 @@ function KeyBoard(io) {
shifted = down; shifted = down;
if (down) { if (down) {
io.buttonDown(2); io.buttonDown(2);
$("#keyboard .key-SHIFT").addClass("active"); $('#keyboard .key-SHIFT').addClass('active');
} else { } else {
io.buttonUp(2); io.buttonUp(2);
$("#keyboard .key-SHIFT").removeClass("active"); $('#keyboard .key-SHIFT').removeClass('active');
} }
}, },
controlKey: function keyboard_controlKey(down) { controlKey: function keyboard_controlKey(down) {
controlled = down; controlled = down;
if (down) { if (down) {
$("#keyboard .key-CTRL").addClass("active"); $('#keyboard .key-CTRL').addClass('active');
} else { } else {
$("#keyboard .key-CTRL").removeClass("active"); $('#keyboard .key-CTRL').removeClass('active');
} }
}, },
@ -213,10 +213,10 @@ function KeyBoard(io) {
commanded = down; commanded = down;
if (down) { if (down) {
io.buttonDown(0); io.buttonDown(0);
$("#keyboard .key-OPEN_APPLE").addClass("active"); $('#keyboard .key-OPEN_APPLE').addClass('active');
} else { } else {
io.buttonUp(0); io.buttonUp(0);
$("#keyboard .key-OPEN_APPLE").removeClass("active"); $('#keyboard .key-OPEN_APPLE').removeClass('active');
} }
}, },
@ -224,19 +224,19 @@ function KeyBoard(io) {
optioned = down; optioned = down;
if (down) { if (down) {
io.buttonDown(1); io.buttonDown(1);
$("#keyboard .key-CLOSED_APPLE").addClass("active"); $('#keyboard .key-CLOSED_APPLE').addClass('active');
} else { } else {
io.buttonUp(1); io.buttonUp(1);
$("#keyboard .key-CLOSED_APPLE").removeClass("active"); $('#keyboard .key-CLOSED_APPLE').removeClass('active');
} }
}, },
capslockKey: function keyboard_caplockKey(down) { capslockKey: function keyboard_caplockKey(down) {
capslocked = down; capslocked = down;
if (down) { if (down) {
$("#keyboard .key-LOCK").addClass("active"); $('#keyboard .key-LOCK').addClass('active');
} else { } else {
$("#keyboard .key-LOCK").removeClass("active"); $('#keyboard .key-LOCK').removeClass('active');
} }
}, },
@ -247,49 +247,49 @@ function KeyBoard(io) {
kb.disableSelection(); kb.disableSelection();
function buildLabel(k) { function buildLabel(k) {
var span = $("<span>" + k + "</span>"); var span = $('<span>' + k + '</span>');
if (k.length > 1 && k.substr(0,1) != '&') if (k.length > 1 && k.substr(0,1) != '&')
span.addClass("small"); span.addClass('small');
return span; return span;
} }
function _mouseup(ev) { function _mouseup(ev) {
$(ev.currentTarget).removeClass("pressed"); $(ev.currentTarget).removeClass('pressed');
} }
function _mousedown(ev) { function _mousedown(ev) {
$(this).addClass("pressed"); $(this).addClass('pressed');
var key = $(ev.currentTarget).data(shifted ? "key2" : "key1"); var key = $(ev.currentTarget).data(shifted ? 'key2' : 'key1');
switch (key) { switch (key) {
case "BELL": case 'BELL':
key = "G"; key = 'G';
break; break;
case "RETURN": case 'RETURN':
key = "\r"; key = '\r';
break; break;
case "TAB": case 'TAB':
key = "\t"; key = '\t';
break; break;
case "DELETE": case 'DELETE':
key = "\0177"; key = '\0177';
break; break;
case "&larr;": case '&larr;':
key = "\010"; key = '\010';
break; break;
case "&rarr;": case '&rarr;':
key = "\025"; key = '\025';
break; break;
case "&darr;": case '&darr;':
key = "\012"; key = '\012';
break; break;
case "&uarr;": case '&uarr;':
key = "\013"; key = '\013';
break; break;
case "&nbsp;": case '&nbsp;':
key = " "; key = ' ';
break; break;
case "ESC": case 'ESC':
key = "\033"; key = '\033';
break; break;
default: default:
break; break;
@ -297,31 +297,31 @@ function KeyBoard(io) {
if (key.length > 1) { if (key.length > 1) {
switch (key) { switch (key) {
case "SHIFT": case 'SHIFT':
self.shiftKey(!shifted); self.shiftKey(!shifted);
break; break;
case "CTRL": case 'CTRL':
self.controlKey(!controlled); self.controlKey(!controlled);
break; break;
case "CAPS": case 'CAPS':
case "LOCK": case 'LOCK':
self.capslockKey(!capslocked); self.capslockKey(!capslocked);
break; break;
case "POW": case 'POW':
if (window.confirm("Power Cycle?")) if (window.confirm('Power Cycle?'))
window.location.reload(); window.location.reload();
break; break;
case "OPEN_APPLE": case 'OPEN_APPLE':
self.commandKey(!commanded); self.commandKey(!commanded);
break; break;
case "CLOSED_APPLE": case 'CLOSED_APPLE':
self.optionKey(!optioned); self.optionKey(!optioned);
break; break;
default: default:
break; break;
} }
} else { } else {
if (controlled && key >= "@" && key <= "_") { if (controlled && key >= '@' && key <= '_') {
io.keyDown(key.charCodeAt(0) - 0x40); io.keyDown(key.charCodeAt(0) - 0x40);
} else if (!shifted && !capslocked && } else if (!shifted && !capslocked &&
key >= 'A' && key <= 'Z') { key >= 'A' && key <= 'Z') {
@ -333,43 +333,43 @@ function KeyBoard(io) {
} }
for (y = 0; y < 5; y++) { for (y = 0; y < 5; y++) {
row = $("<div class='row row" + y + "e'/>"); row = $('<div class="row row' + y + 'e"/>');
kb.append(row); kb.append(row);
for (x = 0; x < keys[0][y].length; x++) { for (x = 0; x < keys[0][y].length; x++) {
key1 = keys[0][y][x]; key1 = keys[0][y][x];
key2 = keys[1][y][x]; key2 = keys[1][y][x];
label = $("<div />"); label = $('<div />');
label1 = buildLabel(key1); label1 = buildLabel(key1);
label2 = buildLabel(key2); label2 = buildLabel(key2);
key = $("<div class='key'>"); key = $('<div class="key">');
key.addClass("key-" + key1.replace(/[&#;]/g,"")); key.addClass('key-' + key1.replace(/[&#;]/g,''));
if (key1.length > 1) { if (key1.length > 1) {
if (key1 == "LOCK") if (key1 == 'LOCK')
key.addClass("vcenter2"); key.addClass('vcenter2');
else else
key.addClass("vcenter"); key.addClass('vcenter');
} }
if (key1 != key2) { if (key1 != key2) {
key.addClass("key-" + key2.replace(/[&;]/g,"")); key.addClass('key-' + key2.replace(/[&;]/g,''));
label.append(label2); label.append(label2);
label.append("<br/>"); label.append('<br/>');
} }
if (key1 == "LOCK") if (key1 == 'LOCK')
key.addClass("active"); key.addClass('active');
label.append(label1); label.append(label1);
key.append(label); key.append(label);
key.data({"key1": key1, "key2": key2}); key.data({'key1': key1, 'key2': key2});
if (window.ontouchstart === undefined) { if (window.ontouchstart === undefined) {
key.bind("mousedown", _mousedown); key.bind('mousedown', _mousedown);
key.bind("mouseup mouseout", _mouseup); key.bind('mouseup mouseout', _mouseup);
} else { } else {
key.bind("touchstart", _mousedown); key.bind('touchstart', _mousedown);
key.bind("touchend touchleave", _mouseup); key.bind('touchend touchleave', _mouseup);
} }
row.append(key); row.append(key);
@ -380,4 +380,3 @@ function KeyBoard(io) {
} }
}; };
} }

View File

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

View File

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

View File

@ -1,5 +1,5 @@
#!/usr/bin/perl -w #!/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 # Permission to use, copy, modify, distribute, and sell this software and its
# documentation for any purpose is hereby granted without fee, provided that # documentation for any purpose is hereby granted without fee, provided that

View File

@ -1,5 +1,5 @@
#!/usr/bin/env perl -w #!/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 # Permission to use, copy, modify, distribute, and sell this software and its
# documentation for any purpose is hereby granted without fee, provided that # documentation for any purpose is hereby granted without fee, provided that