Merge pull request #2 from whscullin/whscullin/more-cleanup

More cleanup.
This commit is contained in:
Will Scullin 2016-12-06 09:37:48 -08:00 committed by GitHub
commit e021c692ce
20 changed files with 840 additions and 468 deletions

View File

@ -134,7 +134,17 @@
<h3>CPU</h3>
<ul>
<li>
<input type="checkbox" id="accelerator_toggle" onclick="updateSpeed()"/>
<select id="computer_type" value="apple2plus" onchange="updateCPU()">
<option value="apple2plus">Apple ][+</option>
<option value="apple2">Autostart Apple ][</option>
<option value="original">Apple ][</option>
<select>
<label for="computer_type">
Type
</label>
</li>
<li>
<input type="checkbox" id="accelerator_toggle" onclick="updateCPU()"/>
<label for="accelerator_toggle">
Accelerated CPU
</label>
@ -142,6 +152,13 @@
</ul>
<h3>Joystick</h3>
<ul>
<li>
<input type="checkbox" id="disable_mouse"
onclick="updateJoystick()" />
<label for="disable_mouse">
Disable Mouse Joystick
</label>
</li>
<li>
<input type="checkbox" id="flip_x"
onclick="updateJoystick()" />
@ -194,12 +211,25 @@
</div>
<div id="save" title="Save Disk">
<form action="#" onsubmit="return false;">
<b>Save to Browser</a>
<br /><br />
Save Name: <input type="text" name="name" id="save_name"
style="width: 200px" />
</form>
<hr />
<div>
<b>Download to Local Disk</b>
<br /><br />
<a id="local_save_link" class="button">Download</a>
</div>
</div>
<div id="manage" title="Manage Disks">
</div>
<div id="http_load" title="Load URL">
<form action="#">
<input type="text" id="http_url" style="width: 500px"/>
</form>
</div>
<div id="load" title="Load Disk">
<table>
<tr>

View File

@ -138,7 +138,16 @@
<h3>CPU</h3>
<ul>
<li>
<input type="checkbox" id="accelerator_toggle" onclick="updateSpeed()"/>
<select id="computer_type" value="apple2enh" onchange="updateCPU()">
<option value="apple2enh">Enhanced Apple //e</option>
<option value="apple2e">Apple //e</option>
<select>
<label for="computer_type">
Type
</label>
</li>
<li>
<input type="checkbox" id="accelerator_toggle" onclick="updateCPU()"/>
<label for="accelerator_toggle">
Accelerated CPU
</label>
@ -146,6 +155,13 @@
</ul>
<h3>Joystick</h3>
<ul>
<li>
<input type="checkbox" id="disable_mouse"
onclick="updateJoystick()" />
<label for="disable_mouse">
Disable Mouse Joystick
</label>
</li>
<li>
<input type="checkbox" id="flip_x"
onclick="updateJoystick()" />
@ -198,12 +214,25 @@
</div>
<div id="save" title="Save Disk">
<form action="#" onsubmit="return false;">
<b>Save to Browser</a>
<br /><br />
Save Name: <input type="text" name="name" id="save_name"
style="width: 200px" />
</form>
<hr />
<div>
<b>Download to Local Disk</b>
<br /><br />
<a id="local_save_link" class="button">Download</a>
</div>
</div>
<div id="manage" title="Manage Disks">
</div>
<div id="http_load" title="Load URL">
<form action="#">
<input type="text" id="http_url" style="width: 500px"/>
</form>
</div>
<div id="load" title="Load Disk">
<table>
<tr>

View File

@ -9,13 +9,16 @@
* implied warranty.
*/
/*globals debug: false, toHex: false, each: false */
/*globals debug: false */
/*exported Apple2IO */
function Apple2IO(cpu, callbacks)
{
'use strict';
var _slot = [];
var _auxRom = null;
var _hz = 1023000;
var _rate = 44000;
var _sample_size = 4096;
@ -85,16 +88,15 @@ function Apple2IO(cpu, callbacks)
PADDLE2: 0x66, // bit 7: status of pdl-2 timer (read)
PADDLE3: 0x67, // bit 7: status of pdl-3 timer (read)
PDLTRIG: 0x70, // trigger paddles
BANK: 0x73, // Back switched RAM card bank
SETIOUDIS:0x7E, // Enable double hires
CLRIOUDIS:0x7F // Disable double hires
};
function _debug() {
// debug.apply(arguments);
debug.apply(arguments);
}
var _locs = [];
function _tick() {
var now = cpu.cycles();
var phase = _phase > 0 ? _high : _low;
@ -297,47 +299,95 @@ function Apple2IO(cpu, callbacks)
}
return {
registerSwitches: function apple2io_registerSwitches(a, locs) {
each(locs, function(key) {
var val = locs[key];
if (_locs[val]) {
debug('duplicate switch! ' + toHex(val));
}
_locs[val] = a;
});
},
start: function apple2io_start() {
this.registerSwitches(this, LOC);
return 0xc0;
},
end: function apple2io_end() {
return 0xc0;
return 0xcf;
},
ioSwitch: function apple2io_ioSwitch(off, val) {
var result;
if (off < 0x80) {
result = _access(off, val);
} else {
var slot = (off & 0x70) >> 4;
if (_slot[slot]) {
result = _slot[slot].ioSwitch(off, val);
}
}
return result;
},
read: function apple2io_read(page, off) {
var result = 0;
if (_locs[off]) {
result = _locs[off].ioSwitch(off);
} else {
debug('I/O read: C0' + toHex(off));
var slot;
switch (page) {
case 0xc0:
result = this.ioSwitch(off);
break;
case 0xc1:
case 0xc2:
case 0xc3:
case 0xc4:
case 0xc5:
case 0xc6:
case 0xc7:
slot = page & 0x0f;
_auxRom = _slot[slot];
if (_slot[slot]) {
result = _slot[slot].read(page, off);
}
break;
default:
if (_auxRom) {
result = _auxRom.read(page, off);
}
break;
}
return result;
},
write: function apple2io_write(page, off, val) {
if (_locs[off]) {
_locs[off].ioSwitch(off, val);
} else {
debug('I/O write: C0' + toHex(off));
var slot;
switch (page) {
case 0xc0:
this.ioSwitch(off);
break;
case 0xc1:
case 0xc2:
case 0xc3:
case 0xc4:
case 0xc5:
case 0xc6:
case 0xc7:
slot = page & 0x0f;
_auxRom = _slot[slot];
if (_slot[slot]) {
_slot[slot].write(page, off, val);
}
break;
default:
if (_auxRom) {
_auxRom.write(page, off, val);
}
break;
}
},
getState: function apple2io_getState() { return {}; },
setState: function apple2io_setState() { },
ioSwitch: function apple2io_ioSwitch(off, val) {
return _access(off, val);
addSlot: function apple2io_addSlot(slot, card) {
_slot[slot] = card;
},
keyDown: function apple2io_keyDown(ascii) {
_keyDown = true;
_key = ascii | 0x80;
},
keyUp: function apple2io_keyUp() {
_keyDown = false;
_key = 0;
@ -346,9 +396,11 @@ function Apple2IO(cpu, callbacks)
buttonDown: function apple2io_buttonDown(b) {
_button[b] = true;
},
buttonUp: function apple2io_buttonUp(b) {
_button[b] = false;
},
paddle: function apple2io_paddle(p, v) {
_paddle[p] = v;
},

View File

@ -9,6 +9,7 @@
* implied warranty.
*/
/*jshint browser:true */
/*globals allocMemPages: false, debug: false,
base64_encode: false, base64_decode: false,
enhanced: false */
@ -173,6 +174,7 @@ function LoresPage(page, charset)
base = addr - 0x400 * _page;
return _buffer[bank][base];
},
_write: function(page, off, val, bank) {
var addr = (page << 8) | off,
base = addr - 0x400 * _page,
@ -321,6 +323,7 @@ function LoresPage(page, charset)
}
}
},
refresh: function() {
var addr = 0x400 * _page;
_refreshing = true;
@ -331,6 +334,7 @@ function LoresPage(page, charset)
}
_refreshing = false;
},
blink: function() {
var addr = 0x400 * _page;
_refreshing = true;
@ -516,11 +520,14 @@ function HiresPage(page)
},
_start: function() { return (0x20 * _page); },
_end: function() { return (0x020 * _page) + 0x1f; },
_read: function(page, off, bank) {
var addr = (page << 8) | off, base = addr - 0x2000 * _page;
return _buffer[bank][base];
},
_write: function(page, off, val, bank) {
function dim(c) {
return [c[0] * 0.75, c[1] * 0.75, c[2] * 0.75];
@ -665,6 +672,7 @@ function HiresPage(page)
off = dx * 4 + dy * 280 * 4 * 2;
for (idx = 0; idx < 9; idx++, off += 8) {
val >>= 1;
if (v1) {
if (_greenMode) {
color = _green;

View File

@ -10,8 +10,8 @@
* implied warranty.
*/
/*globals toHex: false, debug: false*/
/*exported CPU6502 */
/*globals debug: false, toHex: false */
function CPU6502(options)
{
@ -1039,10 +1039,10 @@ function CPU6502(options)
if (key in ops) {
debug('overriding opcode ' + toHex(key));
}
}
ops[key] = cops[key];
}
}
}
function unknown(b) {
var unk;
@ -1379,14 +1379,14 @@ function CPU6502(options)
' P=' + toHex(sr) +
' S=' + toHex(sp) +
' ' +
(sr & flags.N ? 'N' : '-') +
(sr & flags.V ? 'V' : '-') +
((sr & flags.N) ? 'N' : '-') +
((sr & flags.V) ? 'V' : '-') +
'-' +
(sr & flags.B ? 'B' : '-') +
(sr & flags.D ? 'D' : '-') +
(sr & flags.I ? 'I' : '-') +
(sr & flags.Z ? 'Z' : '-') +
(sr & flags.C ? 'C' : '-');
((sr & flags.B) ? 'B' : '-') +
((sr & flags.D) ? 'D' : '-') +
((sr & flags.I) ? 'I' : '-') +
((sr & flags.Z) ? 'Z' : '-') +
((sr & flags.C) ? 'C' : '-');
},
read: function(page, off) {

View File

@ -10,12 +10,12 @@
*/
/*exported DiskII */
/*globals bytify, each: false, extend: false
base64_encode, base64_decode
Uint8Array
/*globals bytify: false, each: false, extend: false, debug: false
base64_decode: false, base64_encode: false
Uint8Array: false
*/
function DiskII(io, callbacks, slot)
function DiskII(io, slot, callbacks)
{
'use strict';
@ -111,6 +111,7 @@ function DiskII(io, callbacks, slot)
}
function _init() {
debug('Disk ][ in slot', slot);
each(LOC, function(key) {
LOC[key] += slot * 0x10;
});
@ -358,11 +359,15 @@ function DiskII(io, callbacks, slot)
}
for (kdx = 0, jdx = 0x55; kdx < 0x100; kdx++) {
data[kdx] <<= 1;
if (data2[jdx] & 0x01) data[kdx] |= 0x01;
if ((data2[jdx] & 0x01) !== 0) {
data[kdx] |= 0x01;
}
data2[jdx] >>= 1;
data[kdx] <<= 1;
if (data2[jdx] & 0x01) data[kdx] |= 0x01;
if ((data2[jdx] & 0x01) !== 0) {
data[kdx] |= 0x01;
}
data2[jdx] >>= 1;
if (--jdx < 0) jdx = 0x55;
@ -492,7 +497,7 @@ function DiskII(io, callbacks, slot)
if (callbacks.dirty) { callbacks.dirty(_drive, dirty); }
}
var diskII_16 = [
var diskII = [
0xa2,0x20,0xa0,0x00,0xa2,0x03,0x86,0x3c,
0x8a,0x0a,0x24,0x3c,0xf0,0x10,0x05,0x3c,
0x49,0xff,0x29,0x7e,0xb0,0x08,0x4a,0xd0,
@ -561,25 +566,28 @@ function DiskII(io, callbacks, slot)
0x03,0x4c,0x01,0x03,0x4c,0x2d,0xff,0xff
];
*/
var diskII = diskII_16;
_init();
return {
start: function disk2_start() {
io.registerSwitches(this, LOC);
return 0xc0 + slot;
},
end: function disk2_end() {
return 0xc0 + slot;
},
ioSwitch: function disk2_ioSwitch(off, val) {
return _access(off, val);
},
read: function disk2_read(page, off) {
return diskII[off];
},
write: function disk2_write() {},
reset: function disk2_reset() {
if (_on) {
_writeMode = false;
@ -587,6 +595,7 @@ function DiskII(io, callbacks, slot)
callbacks.driveLight(_drive, false);
}
},
getState: function disk2_getState() {
function getDriveState(drive) {
var result = {
@ -613,7 +622,9 @@ function DiskII(io, callbacks, slot)
drive: _drive
};
_drives.forEach(function (drive, idx) {
result.drives[idx] = getDriveState(drive);
var _drive = result.drives[idx] = getDriveState(drive);
callbacks.driveLight(idx, _drive.on);
callbacks.dirty(idx, _drive.dirty);
});
return result;
},
@ -635,7 +646,7 @@ function DiskII(io, callbacks, slot)
return result;
}
state.drives.forEach(function(drive, idx) {
_drives[idx] = setDriveState(state);
_drives[idx] = setDriveState(drive);
});
_skip = state.skip;
_latch = state.latch;
@ -741,8 +752,8 @@ function DiskII(io, callbacks, slot)
var flags =
prefix[0x10] | (prefix[0x11] << 8) |
(prefix[0x12] << 16) | (prefix[0x13] << 24);
_cur.readOnly = (flags & 0x80000000) ? true : false;
if (flags & 0x10) {
_cur.readOnly = (flags & 0x80000000) !== 0;
if ((flags & 0x10) !== 0) {
_cur.volume = flags & 0xff;
} else {
_cur.volume = 254;

View File

@ -10,12 +10,12 @@
*/
/*exported LanguageCard */
/*globals RAM: false */
/*globals debug: false
RAM: false */
function LanguageCard(io, rom) {
function LanguageCard(io, slot, rom) {
'use strict';
var _io = io;
var _rom = rom;
var _bank1 = null;
var _bank2 = null;
@ -34,6 +34,8 @@ function LanguageCard(io, rom) {
var _write2 = null;
function _init() {
debug('Language card in slot', slot);
_bank1 = new RAM(0xd0, 0xdf);
_bank2 = new RAM(0xd0, 0xdf);
_ram = new RAM(0xe0, 0xff);
@ -173,9 +175,9 @@ function LanguageCard(io, rom) {
return result;
}
return {
start: function() {
_io.registerSwitches(this, LOC);
return 0xd0;
},
end: function() {

View File

@ -1,27 +1,29 @@
/* globals debug: false, gup: false, hup: false
/* globals debug: false, gup: false, hup: false, toHex: false,
CPU6502: false,
RAM: false,
Apple2ROM: false
Apple2ROM: false, IntBASIC: false, OriginalROM: false,
apple2_charset: false,
Apple2IO: false
LoresPage: false, HiresPage: false, VideoModes: false
scanlines: true,
KeyBoard: false,
Parallel: false,
DiskII: false,
RAMFactor: false,
Printer: false,
LanguageCard: false,
RAMFactor: false,
Thunderclock: false,
Prefs: false,
disk_index: false,
initAudio: false, enableSound: false,
initGamepad: false, processGamepad: false, gamepad: false,
ApplesoftDump: false
ApplesoftDump: false, SYMBOLS: false
*/
/* exported openLoad, openSave, doDelete,
selectCategory, selectDisk, clickDisk, loadJSON,
selectCategory, selectDisk, clickDisk,
updateJoystick,
pauseRun, step
pauseRun, step,
restoreState, saveState,
dumpProgram, PageDebug
*/
@ -35,6 +37,47 @@ var paused = false;
var hashtag;
var DEBUG = false;
var TRACE = false;
var MAX_TRACE = 256;
var trace = [];
/*
* Page viewer
*/
function PageDebug(page)
{
var _page = page;
function _init() {
var r, c;
var row = $('<tr />').appendTo('#page' + toHex(_page));
$('<th>\\</th>').appendTo(row);
for (c = 0; c < 16; c++) {
$('<th>' + toHex(c) + '</th>').appendTo(row);
}
for (r = 0; r < 16; r++) {
row = $('<tr />').appendTo('#page' + toHex(_page));
$('<th>' + toHex(r * 16) + '</th>').appendTo(row);
for (c = 0; c < 16; c++) {
$('<td>--</td>').appendTo(row).attr('id', 'page' + toHex(_page) + '-' + toHex(r * 16 + c));
}
}
}
_init();
return {
start: function() { return _page; },
end: function() { return _page; },
read: null,
write: function(page, off, val) {
$('#page' + toHex(page) + '-' + toHex(off)).text(toHex(val));
}
};
}
var disk_categories = {'Local Saves': []};
var disk_sets = {};
var disk_cur_name = [];
@ -48,8 +91,8 @@ function DriveLights()
on ? 'url(css/red-on-16.png)' :
'url(css/red-off-16.png)');
},
dirty: function(drive, dirty) {
$('#disksave' + drive).button('option', 'disabled', !dirty);
dirty: function() {
// $('#disksave' + drive).button('option', 'disabled', !dirty);
},
label: function(drive, label) {
if (label) {
@ -77,12 +120,11 @@ function DriveLights()
var DISK_TYPES = ['dsk','do','po','raw','nib','2mg'];
var TAPE_TYPES = ['wav','aiff','aif','mp3'];
var _saveDrive = 1;
var _loadDrive = 1;
var _currentDrive = 1;
function openLoad(drive, event)
{
_loadDrive = drive;
_currentDrive = parseInt(drive, 10);
if (event.metaKey) {
openLoadHTTP(drive);
} else {
@ -95,11 +137,18 @@ function openLoad(drive, event)
function openSave(drive, event)
{
_saveDrive = drive;
_currentDrive = parseInt(drive, 10);
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');
if (event.metaKey) {
dumpDisk(drive);
} else if (event.altKey) {
openSaveLocal(drive);
} else {
$('#save_name').val(drivelights.label(drive));
$('#save').dialog('open');
@ -108,15 +157,29 @@ function openSave(drive, event)
var loading = false;
function loadAjax(url) {
function loadAjax(drive, url) {
loading = true;
$('#loading').dialog('open');
$.ajax({ url: url,
cache: false,
dataType: 'jsonp',
jsonp: false,
global: false
$.ajax({
url: url,
dataType: 'json',
modifiedSince: true,
error: function(xhr, status, error) {
alert(error || status);
$('#loading').dialog('close');
loading = false;
},
success: function(data) {
if (data.type == 'binary') {
loadBinary(drive, data);
} else if ($.inArray(data.type, DISK_TYPES) >= 0) {
loadDisk(drive, data);
}
initGamepad(data.gamepad);
$('#loading').dialog('close');
loading = false;
}
});
}
@ -132,7 +195,7 @@ function doLoad() {
var files = $('#local_file').prop('files');
if (files.length == 1) {
doLoadLocal();
doLoadLocal(_currentDrive);
} else if (url) {
var filename;
$('#load').dialog('close');
@ -141,22 +204,26 @@ function doLoad() {
if (filename == '__manage') {
openManage();
} else {
loadLocalStorage(_loadDrive, filename);
loadLocalStorage(_currentDrive, filename);
}
} else {
var r1 = /json\/disks\/(.*).json$/.exec(url);
if (r1 && _loadDrive == 1) {
if (r1) {
filename = r1[1];
document.location.hash = filename;
} else {
filename = url;
}
loadAjax(url);
var parts = document.location.hash.split('|');
parts[_currentDrive - 1] = filename;
document.location.hash = parts.join('|');
loadAjax(_currentDrive, url);
}
}
}
function doSave() {
var name = $('#save_name').val();
saveLocalStorage(_saveDrive, name);
saveLocalStorage(_currentDrive, name);
$('#save').dialog('close');
}
@ -166,14 +233,14 @@ function doDelete(name) {
}
}
function doLoadLocal() {
function doLoadLocal(drive) {
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);
doLoadLocalDisk(drive, file);
} else if ($.inArray(ext, TAPE_TYPES) >= 0) {
doLoadLocalTape(file);
} else {
@ -183,13 +250,13 @@ function doLoadLocal() {
}
}
function doLoadLocalDisk(file) {
function doLoadLocalDisk(drive, 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);
if (disk2.setBinary(drive, name, ext, this.result)) {
drivelights.label(drive, name);
$('#load').dialog('close');
initGamepad();
}
@ -251,7 +318,7 @@ function doLoadLocalTape(file) {
fileReader.readAsArrayBuffer(file);
}
function doLoadHTTP(_url) {
function doLoadHTTP(drive, _url) {
var url = _url || $('#http_url').val();
if (url) {
var req = new XMLHttpRequest();
@ -262,8 +329,8 @@ function doLoadHTTP(_url) {
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);
if (disk2.setBinary(drive, name, ext, req.response)) {
drivelights.label(drive, name);
$('#http_load').dialog('close');
initGamepad();
}
@ -273,23 +340,10 @@ function doLoadHTTP(_url) {
}
function openLoadHTTP(drive) {
_saveDrive = drive;
_currentDrive = parseInt(drive, 10);
$('#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');
}
@ -307,23 +361,27 @@ 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 romVersion = prefs.readPref('computer_type');
var rom;
if (romVersion == 'apple2') {
rom = new IntBASIC();
} else if (romVersion == 'original') {
rom = new OriginalROM();
} else {
rom = new Apple2ROM();
}
};
var vm = new VideoModes(gr, hgr, gr2, hgr2);
var dumper = new ApplesoftDump(cpu);
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);
var lc = new LanguageCard(io, 0, rom);
var parallel = new Parallel(io, 1, new Printer());
var slinky = new RAMFactor(io, 2, 1024 * 1024);
var disk2 = new DiskII(io, 6, drivelights);
var clock = new Thunderclock(io, 7);
cpu.addPageHandler(ram1);
cpu.addPageHandler(gr);
@ -332,13 +390,14 @@ 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);
cpu.addPageHandler(lc);
io.addSlot(0, lc);
io.addSlot(1, parallel);
io.addSlot(2, slinky);
io.addSlot(6, disk2);
io.addSlot(7, clock);
var showFPS = false;
@ -403,7 +462,7 @@ function step()
var accelerated = false;
function updateSpeed()
function updateCPU()
{
accelerated = $('#accelerator_toggle').prop('checked');
kHz = accelerated ? 4092 : 1023;
@ -429,6 +488,7 @@ function run(pc) {
}
var ival = 30;
var now, last = Date.now();
var runFn = function() {
now = Date.now();
@ -447,9 +507,23 @@ function run(pc) {
processHash(hash);
}
}
if (!loading) {
if (DEBUG) {
cpu.stepCyclesDebug(TRACE ? 1 : step, function() {
var line = cpu.dumpRegisters() + ' ' +
cpu.dumpPC(undefined, SYMBOLS);
if (TRACE) {
debug(line);
} else {
trace.push(line);
if (trace.length > MAX_TRACE) {
trace.shift();
}
}
});
} else {
cpu.stepCycles(step);
}
vm.blit();
io.sampleTick();
}
@ -479,6 +553,65 @@ function reset()
cpu.reset();
}
var state = null;
function storeStateLocal() {
window.localStorage['apple2.state'] = JSON.stringify(state);
}
function restoreStateLocal() {
var data = window.localStorage['apple2.state'];
if (data) {
state = JSON.parse(data);
}
}
function saveState() {
if (state && !window.confirm('Overwrite Saved State?')) {
return;
}
state = {
cpu: cpu.getState(),
ram1: ram1.getState(),
ram2: ram2.getState(),
ram3: ram3.getState(),
io: io.getState(),
lc: lc.getState(),
vm: vm.getState(),
disk2: disk2.getState(),
drivelights: drivelights.getState()
};
if (slinky) {
state.slinky = slinky.getState();
}
if (window.localStorage) {
storeStateLocal();
}
}
function restoreState() {
if (window.localStorage) {
restoreStateLocal();
}
if (!state) {
return;
}
cpu.setState(state.cpu);
ram1.setState(state.ram1);
ram2.setState(state.ram2);
ram3.setState(state.ram3);
io.setState(state.io);
lc.setState(state.lc);
vm.setState(state.vm);
disk2.setState(state.disk2);
drivelights.setState(state.drivelights);
if (slinky && state.slinky) {
slinky.setState(state.slinky);
}
}
function loadBinary(bin) {
stop();
for (var idx = 0; idx < bin.length; idx++) {
@ -499,7 +632,7 @@ function selectCategory() {
}
var option = $('<option />').val(file.filename).text(name)
.appendTo('#disk_select');
if (disk_cur_name[_loadDrive] == name) {
if (disk_cur_name[_currentDrive] == name) {
option.attr('selected', 'selected');
}
}
@ -514,7 +647,7 @@ function clickDisk() {
doLoad();
}
function loadDisk(disk) {
function loadDisk(drive, disk) {
var name = disk.name;
var category = disk.category;
@ -522,25 +655,14 @@ function loadDisk(disk) {
name += ' - ' + disk.disk;
}
disk_cur_cat[_loadDrive] = category;
disk_cur_name[_loadDrive] = name;
disk_cur_cat[drive] = category;
disk_cur_name[drive] = name;
drivelights.label(_loadDrive, name);
disk2.setDisk(_loadDrive, disk);
drivelights.label(drive, name);
disk2.setDisk(drive, 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
*/
@ -608,16 +730,20 @@ function loadLocalStorage(drive, name) {
}
function processHash(hash) {
if (hash.indexOf('://') > 0) {
var parts = hash.split('.');
var files = hash.split('|');
for (var idx = 0; idx < files.length; idx++) {
var file = files[idx];
if (file.indexOf('://') > 0) {
var parts = file.split('.');
var ext = parts[parts.length - 1].toLowerCase();
if (ext == 'json') {
loadAjax(hash);
loadAjax(idx + 1, file);
} else {
doLoadHTTP(hash);
doLoadHTTP(idx + 1, file);
}
} else {
loadAjax('json/disks/' + hash + '.json');
loadAjax(idx + 1, 'json/disks/' + file + '.json');
}
}
}
@ -626,6 +752,14 @@ function processHash(hash) {
*/
function _keydown(evt) {
if (!focused || (!evt.metaKey || evt.ctrlKey)) {
evt.preventDefault();
var key = keyboard.mapKeyEvent(evt);
if (key != 0xff) {
io.keyDown(key, evt.shiftKey);
}
}
if (evt.keyCode === 112) { // F1 - Reset
cpu.reset();
} else if (evt.keyCode === 113) { // F2 - Full Screen
@ -647,36 +781,31 @@ function _keydown(evt) {
elem.mozRequestFullScreen();
}
}
} else if (evt.keyCode === 114) { // F3
io.keyDown(0x1b);
} 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 (!focused)
io.keyUp();
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);
}
@ -774,21 +903,30 @@ $(function() {
keyboard.create($('#keyboard'));
if (prefs.havePrefs()) {
$('input[type=checkbox]').each(function() {
$('#options 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));
});
$('#options select').each(function() {
var val = prefs.readPref(this.id);
if (val)
this.value = val;
}).change(function() {
prefs.writePref(this.id, this.value);
});
}
if (romVersion != 'original') {
reset();
}
run();
setInterval(updateKHz, 1000);
updateSound();
updateScreen();
updateSpeed();
updateCPU();
var cancel = function() { $(this).dialog('close'); };
$('#loading').dialog({ autoOpen: false, modal: true });

View File

@ -1,25 +1,26 @@
/* globals debug: false, gup: false, hup: false,
/* globals debug: false, gup: false, hup: false, toHex: false
CPU6502: false,
Apple2eROM: false, Apple2eEnhancedROM: false,
apple2e_charset: false,
Apple2IO: false
LoresPage: false, HiresPage: false, VideoModes: false
LoresPage: false, HiresPage: false, VideoModes: false,
scanlines: true,
KeyBoard: false,
Parallel: false,
DiskII: false,
RAMFactor: false,
Printer: false,
MMU: false,
Slot3: false,
RAMFactor: false,
Thunderclock: false,
Prefs: false,
disk_index: false,
initAudio: false, enableSound: false,
initGamepad: false, processGamepad: false, gamepad: false,
ApplesoftDump: false
ApplesoftDump: false, SYMBOLS: false,
*/
/* exported openLoad, openSave, doDelete,
selectCategory, selectDisk, clickDisk, loadJSON,
selectCategory, selectDisk, clickDisk,
updateJoystick,
pauseRun, step,
restoreState, saveState,
@ -36,6 +37,47 @@ var paused = false;
var hashtag;
var DEBUG = false;
var TRACE = false;
var MAX_TRACE = 256;
var trace = [];
/*
* Page viewer
*/
function PageDebug(page)
{
var _page = page;
function _init() {
var r, c;
var row = $('<tr />').appendTo('#page' + toHex(_page));
$('<th>\\</th>').appendTo(row);
for (c = 0; c < 16; c++) {
$('<th>' + toHex(c) + '</th>').appendTo(row);
}
for (r = 0; r < 16; r++) {
row = $('<tr />').appendTo('#page' + toHex(_page));
$('<th>' + toHex(r * 16) + '</th>').appendTo(row);
for (c = 0; c < 16; c++) {
$('<td>--</td>').appendTo(row).attr('id', 'page' + toHex(_page) + '-' + toHex(r * 16 + c));
}
}
}
_init();
return {
start: function() { return _page; },
end: function() { return _page; },
read: null,
write: function(page, off, val) {
$('#page' + toHex(page) + '-' + toHex(off)).text(toHex(val));
}
};
}
var disk_categories = {'Local Saves': []};
var disk_sets = {};
var disk_cur_name = [];
@ -49,8 +91,8 @@ function DriveLights()
on ? 'url(css/red-on-16.png)' :
'url(css/red-off-16.png)');
},
dirty: function(drive, dirty) {
$('#disksave' + drive).button('option', 'disabled', !dirty);
dirty: function() {
// $('#disksave' + drive).button('option', 'disabled', !dirty);
},
label: function(drive, label) {
if (label) {
@ -78,12 +120,11 @@ function DriveLights()
var DISK_TYPES = ['dsk','do','po','raw','nib','2mg'];
var TAPE_TYPES = ['wav','aiff','aif','mp3'];
var _saveDrive = 1;
var _loadDrive = 1;
var _currentDrive = 1;
function openLoad(drive, event)
{
_loadDrive = drive;
_currentDrive = parseInt(drive, 10);
if (event.metaKey) {
openLoadHTTP(drive);
} else {
@ -96,11 +137,18 @@ function openLoad(drive, event)
function openSave(drive, event)
{
_saveDrive = drive;
_currentDrive = parseInt(drive, 10);
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');
if (event.metaKey) {
dumpDisk(drive);
} else if (event.altKey) {
openSaveLocal(drive);
} else {
$('#save_name').val(drivelights.label(drive));
$('#save').dialog('open');
@ -109,15 +157,29 @@ function openSave(drive, event)
var loading = false;
function loadAjax(url) {
function loadAjax(drive, url) {
loading = true;
$('#loading').dialog('open');
$.ajax({ url: url,
cache: false,
dataType: 'jsonp',
jsonp: false,
global: false
$.ajax({
url: url,
dataType: 'json',
modifiedSince: true,
error: function(xhr, status, error) {
alert(error || status);
$('#loading').dialog('close');
loading = false;
},
success: function(data) {
if (data.type == 'binary') {
loadBinary(drive, data);
} else if ($.inArray(data.type, DISK_TYPES) >= 0) {
loadDisk(drive, data);
}
initGamepad(data.gamepad);
$('#loading').dialog('close');
loading = false;
}
});
}
@ -133,7 +195,7 @@ function doLoad() {
var files = $('#local_file').prop('files');
if (files.length == 1) {
doLoadLocal();
doLoadLocal(_currentDrive);
} else if (url) {
var filename;
$('#load').dialog('close');
@ -142,22 +204,26 @@ function doLoad() {
if (filename == '__manage') {
openManage();
} else {
loadLocalStorage(_loadDrive, filename);
loadLocalStorage(_currentDrive, filename);
}
} else {
var r1 = /json\/disks\/(.*).json$/.exec(url);
if (r1 && _loadDrive == 1) {
if (r1) {
filename = r1[1];
document.location.hash = filename;
} else {
filename = url;
}
loadAjax(url);
var parts = document.location.hash.split('|');
parts[_currentDrive - 1] = filename;
document.location.hash = parts.join('|');
loadAjax(_currentDrive, url);
}
}
}
function doSave() {
var name = $('#save_name').val();
saveLocalStorage(_saveDrive, name);
saveLocalStorage(_currentDrive, name);
$('#save').dialog('close');
}
@ -167,14 +233,14 @@ function doDelete(name) {
}
}
function doLoadLocal() {
function doLoadLocal(drive) {
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);
doLoadLocalDisk(drive, file);
} else if ($.inArray(ext, TAPE_TYPES) >= 0) {
doLoadLocalTape(file);
} else {
@ -184,13 +250,13 @@ function doLoadLocal() {
}
}
function doLoadLocalDisk(file) {
function doLoadLocalDisk(drive, 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);
if (disk2.setBinary(drive, name, ext, this.result)) {
drivelights.label(drive, name);
$('#load').dialog('close');
initGamepad();
}
@ -252,7 +318,7 @@ function doLoadLocalTape(file) {
fileReader.readAsArrayBuffer(file);
}
function doLoadHTTP(_url) {
function doLoadHTTP(drive, _url) {
var url = _url || $('#http_url').val();
if (url) {
var req = new XMLHttpRequest();
@ -263,8 +329,8 @@ function doLoadHTTP(_url) {
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);
if (disk2.setBinary(drive, name, ext, req.response)) {
drivelights.label(drive, name);
$('#http_load').dialog('close');
initGamepad();
}
@ -274,30 +340,18 @@ function doLoadHTTP(_url) {
}
function openLoadHTTP(drive) {
_saveDrive = drive;
_currentDrive = parseInt(drive, 10);
$('#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 enhanced = prefs.readPref('computer_type') != 'apple2e';
var runTimer = null;
var cpu = new CPU6502({'65C02': enhanced});
var hgr = new HiresPage(1);
@ -311,29 +365,30 @@ if (enhanced) {
} else {
rom = new Apple2eROM();
}
var vm = new VideoModes(gr, hgr, gr2, hgr2);
var dumper = new ApplesoftDump(cpu);
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 parallel = new Parallel(io, 1, new Printer());
var slinky = new RAMFactor(io, 2, 1024 * 1024);
var slot3 = new Slot3(io, 3, rom);
var disk2 = new DiskII(io, 6, drivelights);
var clock = new Thunderclock(io, 7);
io.addSlot(1, parallel);
io.addSlot(2, slinky);
io.addSlot(3, slot3);
io.addSlot(6, disk2);
io.addSlot(7, clock);
var showFPS = false;
function updateKHz() {
@ -397,7 +452,7 @@ function step()
var accelerated = false;
function updateSpeed()
function updateCPU()
{
accelerated = $('#accelerator_toggle').prop('checked');
kHz = accelerated ? 4092 : 1023;
@ -423,6 +478,7 @@ function run(pc) {
}
var ival = 30;
var now, last = Date.now();
var runFn = function() {
now = Date.now();
@ -441,10 +497,24 @@ function run(pc) {
processHash(hash);
}
}
if (!loading) {
mmu.resetVB();
if (DEBUG) {
cpu.stepCyclesDebug(TRACE ? 1 : step, function() {
var line = cpu.dumpRegisters() + ' ' +
cpu.dumpPC(undefined, SYMBOLS);
if (TRACE) {
debug(line);
} else {
trace.push(line);
if (trace.length > MAX_TRACE) {
trace.shift();
}
}
});
} else {
cpu.stepCycles(step);
}
vm.blit();
io.sampleTick();
}
@ -474,6 +544,59 @@ function reset()
cpu.reset();
}
var state = null;
function storeStateLocal() {
window.localStorage['apple2.state'] = JSON.stringify(state);
}
function restoreStateLocal() {
var data = window.localStorage['apple2.state'];
if (data) {
state = JSON.parse(data);
}
}
function saveState() {
if (state && !window.confirm('Overwrite Saved State?')) {
return;
}
state = {
cpu: cpu.getState(),
io: io.getState(),
mmu: mmu.getState(),
vm: vm.getState(),
disk2: disk2.getState(),
drivelights: drivelights.getState()
};
if (slinky) {
state.slinky = slinky.getState();
}
if (window.localStorage) {
storeStateLocal();
}
}
function restoreState() {
if (window.localStorage) {
restoreStateLocal();
}
if (!state) {
return;
}
cpu.setState(state.cpu);
io.setState(state.io);
mmu.setState(state.mmu);
vm.setState(state.vm);
disk2.setState(state.disk2);
drivelights.setState(state.drivelights);
if (slinky && state.slinky) {
slinky.setState(state.slinky);
}
}
function loadBinary(bin) {
stop();
for (var idx = 0; idx < bin.length; idx++) {
@ -494,7 +617,7 @@ function selectCategory() {
}
var option = $('<option />').val(file.filename).text(name)
.appendTo('#disk_select');
if (disk_cur_name[_loadDrive] == name) {
if (disk_cur_name[_currentDrive] == name) {
option.attr('selected', 'selected');
}
}
@ -509,7 +632,7 @@ function clickDisk() {
doLoad();
}
function loadDisk(disk) {
function loadDisk(drive, disk) {
var name = disk.name;
var category = disk.category;
@ -517,25 +640,14 @@ function loadDisk(disk) {
name += ' - ' + disk.disk;
}
disk_cur_cat[_loadDrive] = category;
disk_cur_name[_loadDrive] = name;
disk_cur_cat[drive] = category;
disk_cur_name[drive] = name;
drivelights.label(_loadDrive, name);
disk2.setDisk(_loadDrive, disk);
drivelights.label(drive, name);
disk2.setDisk(drive, 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
*/
@ -603,16 +715,20 @@ function loadLocalStorage(drive, name) {
}
function processHash(hash) {
if (hash.indexOf('://') > 0) {
var parts = hash.split('.');
var files = hash.split('|');
for (var idx = 0; idx < files.length; idx++) {
var file = files[idx];
if (file.indexOf('://') > 0) {
var parts = file.split('.');
var ext = parts[parts.length - 1].toLowerCase();
if (ext == 'json') {
loadAjax(hash);
loadAjax(idx + 1, file);
} else {
doLoadHTTP(hash);
doLoadHTTP(idx + 1, file);
}
} else {
loadAjax('json/disks/' + hash + '.json');
loadAjax(idx + 1, 'json/disks/' + file + '.json');
}
}
}
@ -620,19 +736,16 @@ function processHash(hash) {
* 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) {
if (evt.keyCode === 112) { // F1 - Reset
cpu.reset();
} else if (evt.keyCode === 113) { // F2 - Full Screen
var elem = document.getElementById('screen');
@ -653,12 +766,11 @@ function _keydown(evt) {
elem.mozRequestFullScreen();
}
}
} else if (evt.keyCode === 114) {
} else if (evt.keyCode === 114) { // F3
io.keyDown(0x1b);
_key = 0x1b;
} else if (evt.keyCode == 16) { // Shift
keyboard.shiftKey(true);
io.buttonDown(2, true);
io.buttonDown(2);
} else if (evt.keyCode == 17) { // Control
keyboard.controlKey(true);
} else if (evt.keyCode == 91 || evt.keyCode == 93) { // Command
@ -669,14 +781,12 @@ function _keydown(evt) {
}
function _keyup(evt) {
_key = 0xff;
if (!focused)
io.keyUp();
if (evt.keyCode == 16) { // Shift
keyboard.shiftKey(false);
io.buttonDown(2, false);
io.buttonUp(2);
} else if (evt.keyCode == 17) { // Control
keyboard.controlKey(false);
} else if (evt.keyCode == 91 || evt.keyCode == 93) { // Command
@ -688,6 +798,7 @@ function _keyup(evt) {
function updateScreen() {
var green = $('#green_screen').prop('checked');
scanlines = $('#show_scanlines').prop('checked');
vm.green(green);
}
@ -788,13 +899,20 @@ $(function() {
keyboard.create($('#keyboard'));
if (prefs.havePrefs()) {
$('input[type=checkbox]').each(function() {
$('#options 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));
});
$('#options select').each(function() {
var val = prefs.readPref(this.id);
if (val)
this.value = val;
}).change(function() {
prefs.writePref(this.id, this.value);
});
}
reset();
@ -802,7 +920,7 @@ $(function() {
setInterval(updateKHz, 1000);
updateSound();
updateScreen();
updateSpeed();
updateCPU();
var cancel = function() { $(this).dialog('close'); };
$('#loading').dialog({ autoOpen: false, modal: true });
@ -823,10 +941,6 @@ $(function() {
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,

223
js/mmu.js
View File

@ -21,8 +21,6 @@ function MMU(cpu, lores1, lores2, hires1, hires2, io, rom)
var idx;
var _auxRom = 0x00;
var _readPages = new Array(0x100);
var _writePages = new Array(0x100);
var _pages = new Array(0x100);
@ -47,6 +45,69 @@ function MMU(cpu, lores1, lores2, hires1, hires2, io, rom)
var _vbEnd = 0;
/*
* I/O Switch locations
*/
var LOC = {
// 80 Column
_80STOREOFF: 0x00,
_80STOREON: 0x01,
// Aux RAM
RAMRDOFF: 0x02,
RAMRDON: 0x03,
RAMWROFF: 0x04,
RAMWRON: 0x05,
// Bank switched ROM
INTCXROMOFF: 0x06,
INTCXROMON: 0x07,
ALTZPOFF: 0x08,
ALTZPON: 0x09,
SLOTC3ROMOFF: 0x0A,
SLOTC3ROMON: 0x0B,
// Status
BSRBANK2: 0x11,
BSRREADRAM: 0x12,
RAMRD: 0x13,
RAMWRT: 0x14,
INTCXROM: 0x15,
ALTZP: 0x16,
SLOTC3ROM: 0x17,
_80STORE: 0x18,
VERTBLANK: 0x19,
PAGE1: 0x54, // select text/graphics page1 main/aux
PAGE2: 0x55, // select text/graphics page2 main/aux
// Bank 2
READBSR2: 0x80,
WRITEBSR2: 0x81,
OFFBSR2: 0x82,
READWRBSR2: 0x83,
// Shadow Bank 2
_READBSR2: 0x84,
_WRITEBSR2: 0x85,
_OFFBSR2: 0x86,
_READWRBSR2: 0x87,
// Bank 1
READBSR1: 0x88,
WRITEBSR1: 0x89,
OFFBSR1: 0x8a,
READWRBSR1: 0x8b,
// Shadow Bank 1
_READBSR1: 0x8c,
_WRITEBSR1: 0x8d,
_OFFBSR1: 0x8e,
_READWRBSR1: 0x8f
};
function _initSwitches() {
_bank1 = true;
_readbsr = false;
@ -64,28 +125,69 @@ function MMU(cpu, lores1, lores2, hires1, hires2, io, rom)
}
function _debug() {
// debug.apply(arguments);
debug.apply(arguments);
}
var _last = 0x00;
function EmptySlots() {
function Switches() {
var locs = {};
for (var loc in LOC) {
if (LOC.hasOwnProperty(loc)) {
locs[LOC[loc]] = loc;
}
}
return {
start: function slot_start() {
return 0xC1;
start: function() {
return 0xC0;
},
end: function slot_end() {
return 0xCF;
end: function() {
return 0xC0;
},
read: function slot_read() {
return 0x00;
read: function(page, off) {
var result;
if (off in locs) {
result = _access(off);
} else {
result = io.ioSwitch(off);
}
return result;
},
write: function slot_write() {
write: function(page, off, val) {
if (off in locs) {
_access(off, val);
} else {
io.ioSwitch(off, val);
}
}
};
}
var emptyslots = new EmptySlots();
function AuxRom() {
return {
start: function() {
return 0xC1;
},
end: function() {
return 0xCF;
},
read: function(page, off) {
var result;
if (page == 0xc3) {
result = io.read(page, off);
} else {
result = rom.read(page, off);
}
return result;
},
write: function() {}
};
}
var switches = new Switches();
var auxRom = new AuxRom();
var mem00_01 = [new RAM(0x0, 0x1), new RAM(0x0, 0x1)];
var mem02_03 = [new RAM(0x2, 0x3), new RAM(0x2, 0x3)];
@ -95,8 +197,8 @@ function MMU(cpu, lores1, lores2, hires1, hires2, io, rom)
var mem20_3F = [hires1.bank0(), hires1.bank1()];
var mem40_5F = [hires2.bank0(), hires2.bank1()];
var mem60_BF = [new RAM(0x60,0xBF), new RAM(0x60,0xBF)];
var memC0_C0 = [io];
// var memC1_CF = [emptyslots, rom];
var memC0_C0 = [switches];
var memC1_CF = [io, auxRom];
var memD0_DF = [rom,
new RAM(0xD0,0xDF), new RAM(0xD0,0xDF),
new RAM(0xD0,0xDF), new RAM(0xD0,0xDF)];
@ -161,7 +263,7 @@ function MMU(cpu, lores1, lores2, hires1, hires2, io, rom)
_writePages[idx] = _pages[idx][0];
// Slots
for (idx = 0xc1; idx < 0xd0; idx++) {
_pages[idx] = [emptyslots, rom]; // memC1_CF;
_pages[idx] = memC1_CF;
_readPages[idx] = _pages[idx][0];
_writePages[idx] = _pages[idx][0];
}
@ -178,69 +280,6 @@ function MMU(cpu, lores1, lores2, hires1, hires2, io, rom)
_writePages[idx] = _pages[idx][0];
}
/*
* I/O Switch locations
*/
var LOC = {
// 80 Column
_80STOREOFF: 0x00,
_80STOREON: 0x01,
// Aux RAM
RAMRDOFF: 0x02,
RAMRDON: 0x03,
RAMWROFF: 0x04,
RAMWRON: 0x05,
// Bank switched ROM
INTCXROMOFF: 0x06,
INTCXROMON: 0x07,
ALTZPOFF: 0x08,
ALTZPON: 0x09,
SLOTC3ROMOFF: 0x0A,
SLOTC3ROMON: 0x0B,
// Status
BSRBANK2: 0x11,
BSRREADRAM: 0x12,
RAMRD: 0x13,
RAMWRT: 0x14,
INTCXROM: 0x15,
ALTZP: 0x16,
SLOTC3ROM: 0x17,
_80STORE: 0x18,
VERTBLANK: 0x19,
PAGE1: 0x54, // select text/graphics page1 main/aux
PAGE2: 0x55, // select text/graphics page2 main/aux
// Bank 2
READBSR2: 0x80,
WRITEBSR2: 0x81,
OFFBSR2: 0x82,
READWRBSR2: 0x83,
// Shadow Bank 2
_READBSR2: 0x84,
_WRITEBSR2: 0x85,
_OFFBSR2: 0x86,
_READWRBSR2: 0x87,
// Bank 1
READBSR1: 0x88,
WRITEBSR1: 0x89,
OFFBSR1: 0x8a,
READWRBSR1: 0x8b,
// Shadow Bank 1
_READBSR1: 0x8c,
_WRITEBSR1: 0x8d,
_OFFBSR1: 0x8e,
_READWRBSR1: 0x8f
};
function _updateBanks() {
if (_auxRamRead) {
for (idx = 0x02; idx < 0xC0; idx++) {
@ -549,7 +588,7 @@ function MMU(cpu, lores1, lores2, hires1, hires2, io, rom)
case LOC._80STORE: // 0xC018
result = _80store ? 0x80 : 0x00;
break;
case LOC.VERTBLANK: // 0xC018
case LOC.VERTBLANK: // 0xC019
// result = cpu.cycles() % 20 < 5 ? 0x80 : 0x00;
result = (cpu.cycles() < _vbEnd) ? 0x80 : 0x00;
break;
@ -577,17 +616,11 @@ function MMU(cpu, lores1, lores2, hires1, hires2, io, rom)
lores1.start();
lores2.start();
// Do us afterward because we override some of the above
io.registerSwitches(this, LOC);
return 0x00;
},
end: function mmu_end() {
return 0xff;
},
ioSwitch: function mmu_ioswitch(off, val) {
return _access(off, val);
},
reset: function() {
_initSwitches();
_updateBanks();
@ -603,24 +636,6 @@ function MMU(cpu, lores1, lores2, hires1, hires2, io, rom)
write: function mmu_write(page, off, val) {
_writePages[page].write(page, off, val);
},
addSlot: function mmu_addSlot(slot, card) {
if (slot == 3) {
_pages[0xc0 + slot][1] = card;
}
_pages[0xc0 + slot][0] = card;
card.start();
},
auxRom: function mmu_auxRom(slot, rom) {
var idx;
if (_auxRom != slot) {
_debug("Slot " + slot + " expansion rom added");
_auxRom = slot;
for (idx = 0xc8; idx < 0xd0; idx++) {
_pages[idx][0] = rom;
}
_updateBanks();
}
},
resetVB: function mmu_resetVB() {
_vbEnd = cpu.cycles() + 1000;
},
@ -631,7 +646,6 @@ function MMU(cpu, lores1, lores2, hires1, hires2, io, rom)
bank1: _bank1,
last: _last,
auxRom: _auxRom,
intcxrom: _intcxrom,
slot3rom: _slot3rom,
auxRamRead: _auxRamRead,
@ -655,7 +669,6 @@ function MMU(cpu, lores1, lores2, hires1, hires2, io, rom)
_writebsr = state.writebsr;
_bank1 = state.bank1;
_auxRom = state.auxRom;
_intcxrom = state.intcxrom;
_slot3rom = state.slot3rom;
_auxRamRead = state.auxRamRead;

View File

@ -10,18 +10,19 @@
*/
/*exported Parallel */
/*globals debug: false */
function Parallel(io, cbs, slot) {
function Parallel(io, slot, cbs) {
'use strict';
slot = slot || 1;
debug('Parallel card in slot', slot);
var LOC = {
IOREG: 0x80
};
var _cbs = cbs;
var rom = [
0x18,0xb0,0x38,0x48,0x8a,0x48,0x98,0x48,
0x08,0x78,0x20,0x58,0xff,0xba,0x68,0x68,
@ -57,18 +58,23 @@ function Parallel(io, cbs, slot) {
0xff,0xf0,0x03,0xfe,0x38,0x07,0x70,0x84
];
LOC.IOREG += 0x10 * slot;
function _access(off, val) {
if (off == LOC.IOREG && val && 'putChar' in cbs) {
cbs.putChar(val);
}
}
return {
start: function() {
LOC.IOREG += 0x10 * slot;
io.registerSwitches(this, LOC);
return 0xc0 + slot;
},
end: function() {
return 0xc0 + slot;
},
ioSwitch: function (off, val) {
if (off == LOC.IOREG && val && 'putChar' in _cbs)
_cbs.putChar(val);
return _access(off, val);
},
read: function(page, off) {
return rom[off];

View File

@ -10,10 +10,10 @@
*/
/*exported RAMFactor */
/*globals allocMem: false, bytify: false, each: false,
/*globals allocMem: false, bytify: false, debug: false, each: false,
base64_encode: false, base64_decode: false
*/
function RAMFactor(mmu, io, slot, size) {
function RAMFactor(io, slot, size) {
'use strict';
var rom = [
@ -1066,6 +1066,8 @@ function RAMFactor(mmu, io, slot, size) {
};
function _init() {
debug('RAMFactor card in slot', slot);
each(LOC, function(key) {
LOC[key] += slot * 0x10;
});
@ -1154,19 +1156,6 @@ function RAMFactor(mmu, io, slot, size) {
return result;
}
var auxRomFn = {
start: function auxRom_start() {
return 0xc8;
},
end: function auxRom_end() {
return 0xcf;
},
read: function auxRom_read(page, off) {
return rom[_firmware * 0x1000 + (page - 0xC0) * 0x100 + off];
},
write: function auxRom_write() {}
};
_init();
return {
@ -1177,17 +1166,19 @@ function RAMFactor(mmu, io, slot, size) {
end: function ramfactor_end() {
return 0xc0 + slot;
},
read: function ramfactor_read(page, off) {
mmu.auxRom(slot, auxRomFn);
return rom[slot * 0x100 + off];
},
write: function ramfactor_write() {
mmu.auxRom(slot, auxRomFn);
},
ioSwitch: function ramfactor_ioSwitch(off, val) {
ioSwitch: function (off, val) {
return _access(off, val);
},
read: function ramfactor_read(page, off) {
var result;
if (page == 0xc0 + slot) {
result = rom[slot * 0x100 + off];
} else {
result = rom[_firmware * 0x1000 + (page - 0xC0) * 0x100 + off];
}
return result;
},
write: function ramfactor_write() {},
reset: function ramfactor_reset() {
_firmware = 0;
},

View File

@ -11,23 +11,10 @@
/*exported Slot3 */
function Slot3(mmu, rom)
function Slot3(io, slot, rom)
{
'use strict';
var auxRomFn = {
start: function auxRom_start() {
return 0xc8;
},
end: function auxRom_end() {
return 0xcf;
},
read: function auxRom_read(page, off) {
return rom.read(page, off);
},
write: function auxRom_write() {}
};
return {
start: function slot3_start() {
return 0xc3;
@ -36,11 +23,9 @@ function Slot3(mmu, rom)
return 0xc3;
},
read: function slot3_read(page, off) {
mmu.auxRom(0x3, auxRomFn);
return rom.read(page, off);
},
write: function slot3_write() {
mmu.auxRom(0x3, auxRomFn);
}
};

View File

@ -10,9 +10,9 @@
*/
/*exported Thunderclock */
/*global each: false */
/*global debug: false, each: false */
function Thunderclock(mmu, io, slot)
function Thunderclock(io, slot)
{
'use strict';
@ -287,24 +287,13 @@ function Thunderclock(mmu, io, slot)
};
function _init() {
debug('Thunderclock card in slot', slot);
each(LOC, function(key) {
LOC[key] += slot * 0x10;
});
}
var auxRomFn = {
start: function auxRom_start() {
return 0xc8;
},
end: function auxRom_end() {
return 0xcf;
},
read: function auxRom_read(page, off) {
return rom[(page - 0xc8) * 256 + off];
},
write: function auxRom_write() {}
};
var _command = 0;
var _bits = [];
@ -372,19 +361,21 @@ function Thunderclock(mmu, io, slot)
return {
start: function thunderclock_start() {
io.registerSwitches(this, LOC);
return 0xc0 + slot;
},
end: function thunderclock_end() {
return 0xc0 + slot;
},
read: function thunderclock_read(page, off) {
mmu.auxRom(slot, auxRomFn);
return rom[off];
var result;
if (page < 0xc8) {
result = rom[off];
} else {
result = rom[(page - 0xc8) * 256 + off];
}
return result;
},
write: function thunderclock_write() {
mmu.auxRom(slot, auxRomFn);
},
ioSwitch: function thunderclock_ioSwitch(off, val) {
return _access(off, val);

View File

@ -21,10 +21,9 @@ var _samples = [];
var audioContext;
var audioNode;
var AC = window.AudioContext;
if (typeof AC !== 'undefined') {
audioContext = new AC();
if (window.AudioContext) {
audioContext = new window.AudioContext();
audioNode = audioContext.createScriptProcessor(4096, 1, 1);
audioNode.onaudioprocess = function(event) {

View File

@ -1,4 +1,3 @@
/* Copyright 2010-2016 Will Scullin <scullin@scullinsteel.com>
*
* Permission to use, copy, modify, distribute, and sell this software and its

View File

@ -6,11 +6,14 @@ function Printer() {
putChar: function(val) {
if (!_printer || _printer.closed) {
_printer = window.open('', '_blank','toolbar=0,location=0');
if (_printer) {
_printer.document.title = 'Printer';
_printer.document.write('<div style="font: 12px courier">');
_printer.document.write('<span>');
window.focus();
}
}
if (_printer) {
var c = String.fromCharCode(val & 0x7f);
if (c == '\r') {
_printer.document.write('<br /></span>');
@ -20,5 +23,6 @@ function Printer() {
_printer.document.write(c);
}
}
}
};
}

View File

@ -1,4 +1,4 @@
loadJSON({
{
"name": "DOS 3.3 Master",
"category": "System",
"type": "dsk",
@ -636,4 +636,4 @@ loadJSON({
"AwhOEiBECI2UE6kAja0Tuo4cEyCLCCDaCK2sE6IWICwL0AkgYgggWQ1MPggg8QkgYgggQAutqxPQBaIPIM0KIB0LTA4IqYCFdoXZhTM4rdID6QfpHjABYCBY/KIVIM0KTNMDogG9HxMKCgoKnSETyhDzINwDhACFASDjA4QChQMgLRAg6A+pAI2rE2CpAIUiIFj8ohYgzQqiFyDNCiCO/aAAoh8gzQq5rxMg7f2iICDNCpgYaSiqIM0KyMAJ0OMgjv2iISDNCiBv/a0AAqIAICwL0LiNrBOMrhNgIFj8qQ+FJK2uExhpKKogzQqpA4UiIFj8rawTogogLAvQB6kAjQ==",
"AAAAAAAAAAAAAAAAIg4iDSIMIgsiCiIJIggiByIGIgUiBCIDIgIiASIAAwIDAQMABA8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="
]
]});
]}

View File

@ -1,4 +1,4 @@
loadJSON({
{
"name": "ProDOS",
"category": "System",
"type": "dsk",
@ -635,4 +635,4 @@ loadJSON({
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="
]
]});
]}

View File

@ -102,7 +102,7 @@ if ($ext eq '2mg') {
my $sector = 0;
my $track = 0;
print "loadJSON({\n";
print "{\n";
print " \"name\": \"$name\",\n";
print " \"type\": \"$ext\",\n";
print " \"category\": \"$category\",\n";
@ -127,6 +127,6 @@ for ($track = 0; $track < 0x23; $track++) {
}
print "\n ]";
}
print "\n]});\n";
print "\n]}\n";
close(DISK);