mirror of
https://github.com/whscullin/apple2js.git
synced 2024-01-12 14:14:38 +00:00
Merge pull request #1 from whscullin/whscullin/cleanup-refactor
Cleanup and refactoring.
This commit is contained in:
commit
9bd56466b4
14
.editorconfig
Normal file
14
.editorconfig
Normal 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
25
.eslintrc.json
Normal 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"
|
||||||
|
}
|
@ -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" />
|
||||||
|
769
apple2js.html
769
apple2js.html
@ -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">
|
||||||
|
772
apple2jse.html
772
apple2jse.html
@ -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">
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/* Copyright 2010-2013 Will Scullin */
|
/* Copyright 2010-2016 Will Scullin */
|
||||||
|
|
||||||
.disklabel {
|
.disklabel {
|
||||||
color: #000;
|
color: #000;
|
||||||
|
@ -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);
|
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
|
@ -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);
|
|
||||||
|
@ -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
173
js/applesoft/decompiler.js
Normal 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;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
@ -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) {
|
||||||
|
@ -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) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
156
js/canvas2e.js
156
js/canvas2e.js
@ -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) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
489
js/cpu6502.js
489
js/cpu6502.js
@ -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)
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
95
js/disk2.js
95
js/disk2.js
@ -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++) {
|
||||||
|
@ -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;
|
||||||
|
@ -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() {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
876
js/main2.js
Normal 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
877
js/main2e.js
Normal 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');
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
11
js/mmu.js
11
js/mmu.js
@ -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;
|
||||||
|
@ -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() {}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
@ -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;
|
||||||
|
@ -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);
|
||||||
|
@ -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;
|
||||||
|
1474
js/symbols.js
1474
js/symbols.js
File diff suppressed because it is too large
Load Diff
@ -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)
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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();
|
||||||
|
@ -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
|
||||||
|
@ -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) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -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',';','←','→'],
|
['CTRL','A','S','D','F','G','H','J','K','L',';','←','→'],
|
||||||
['SHIFT','Z','X','C','V','B','N','M',',','.','/','SHIFT'],
|
['SHIFT','Z','X','C','V','B','N','M',',','.','/','SHIFT'],
|
||||||
['POWER', ' ']],
|
['POWER', ' ']],
|
||||||
[['!','"','#','$','%','&',"'",'(',')','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','+','←','→'],
|
['CTRL','A','S','D','F','BELL','H','J','K','L','+','←','→'],
|
||||||
['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 "←":
|
case '←':
|
||||||
key = "\010";
|
key = '\010';
|
||||||
break;
|
break;
|
||||||
case "→":
|
case '→':
|
||||||
key = "\025";
|
key = '\025';
|
||||||
break;
|
break;
|
||||||
case " ":
|
case ' ':
|
||||||
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) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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',' ','CLOSED_APPLE','←','→','↓','↑']],
|
['LOCK','`','POW','OPEN_APPLE',' ','CLOSED_APPLE','←','→','↓','↑']],
|
||||||
[['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',' ','CLOSED_APPLE','←','→','↓','↑']]];
|
['CAPS','~','POW','OPEN_APPLE',' ','CLOSED_APPLE','←','→','↓','↑']]];
|
||||||
|
|
||||||
@ -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 "←":
|
case '←':
|
||||||
key = "\010";
|
key = '\010';
|
||||||
break;
|
break;
|
||||||
case "→":
|
case '→':
|
||||||
key = "\025";
|
key = '\025';
|
||||||
break;
|
break;
|
||||||
case "↓":
|
case '↓':
|
||||||
key = "\012";
|
key = '\012';
|
||||||
break;
|
break;
|
||||||
case "↑":
|
case '↑':
|
||||||
key = "\013";
|
key = '\013';
|
||||||
break;
|
break;
|
||||||
case " ":
|
case ' ':
|
||||||
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) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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(" ");
|
_printer.document.write(' ');
|
||||||
} else {
|
} else {
|
||||||
_printer.document.write(c);
|
_printer.document.write(c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
35
js/util.js
35
js/util.js
@ -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];
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user