Crude native woz support. (#13)

Readonly for now.
This commit is contained in:
Will Scullin 2019-10-01 19:56:10 -07:00 committed by GitHub
parent 7d9090133b
commit 3b95726655
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 1692 additions and 585 deletions

View File

@ -1,6 +1,6 @@
The MIT License (MIT) The MIT License (MIT)
Copyright (c) 2013 whscullin Copyright (c) 2013-2019 Will Scullin
Permission is hereby granted, free of charge, to any person obtaining a copy of Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in this software and associated documentation files (the "Software"), to deal in

File diff suppressed because it is too large Load Diff

425
js/cards/smartport.js Normal file
View File

@ -0,0 +1,425 @@
/* Copyright 2010-2019 Will Scullin <scullin@scullinsteel.com>
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
* the above copyright notice appear in all copies and that both that
* copyright notice and this permission notice appear in supporting
* documentation. No representations are made about the suitability of this
* software for any purpose. It is provided "as is" without express or
* implied warranty.
*/
import { base64_decode } from '../base64';
import { debug, toHex } from '../util';
export default function SmartPort(io, slot, cpu) {
/*
$Cn01=$20
$Cn03=$00
$Cn05=$03
$Cn07=$00
*/
var ROM = [
0xA2, 0x20, 0xA0, 0x00, 0xA2, 0x03, 0xA0, 0x3C, 0x20, 0x58, 0xFF, 0xBA, 0xBD, 0x00, 0x01, 0x0A,
0x0A, 0x0A, 0x0A, 0xAA, 0x4C, 0x01, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xDF, 0x20
];
var disks = [];
function decodeDisk(unit, disk) {
disks[unit] = [];
for (var idx = 0; idx < disk.blocks.length; idx++) {
disks[unit][idx] = base64_decode(disk.blocks[idx]);
}
}
function Address() {
var lo;
var hi;
if (arguments.length == 1) {
lo = arguments[0] & 0xff;
hi = arguments[0] >> 8;
} else if (arguments.length == 2) {
lo = arguments[0];
hi = arguments[1];
}
return {
loByte: function() {
return lo;
},
hiByte: function() {
return hi;
},
inc: function(val) {
return new Address(((hi << 8 | lo) + val) & 0xffff);
},
readByte: function() {
return cpu.read(hi, lo);
},
readWord: function() {
var readLo = this.readByte();
var readHi = this.inc(1).readByte();
return readHi << 8 | readLo;
},
readAddress: function() {
var readLo = this.readByte();
var readHi = this.inc(1).readByte();
return new Address(readLo, readHi);
},
writeByte: function(val) {
cpu.write(hi, lo, val);
},
writeWord: function(val) {
this.writeByte(val & 0xff);
this.inc(1).writeByte(val >> 8);
},
writeAddress: function(val) {
this.writeByte(val.loByte());
this.inc(1).writeByte(val.hiByte());
},
toString: function() {
return '$' + toHex(hi) + toHex(lo);
}
};
}
/*
* dumpBlock
*/
/*
function dumpBlock(drive, block) {
var result = '';
var b;
var jdx;
for (var idx = 0; idx < 32; idx++) {
result += toHex(idx << 4, 4) + ': ';
for (jdx = 0; jdx < 16; jdx++) {
b = disks[drive][block][idx * 16 + jdx];
if (jdx == 8) {
result += ' ';
}
result += toHex(b) + ' ';
}
result += ' ';
for (jdx = 0; jdx < 16; jdx++) {
b = disks[drive][block][idx * 16 + jdx] & 0x7f;
if (jdx == 8) {
result += ' ';
}
if (b >= 0x20 && b < 0x7f) {
result += String.fromCharCode(b);
} else {
result += '.';
}
}
result += '\n';
}
return result;
}
*/
/*
* getDeviceInfo
*/
function getDeviceInfo(state, drive) {
if (disks[drive]) {
var blocks = disks[drive].length;
state.x = blocks & 0xff;
state.y = blocks >> 8;
state.a = 0;
state.s &= 0xfe;
} else {
state.a = 0x28;
state.s |= 0x01;
}
}
/*
* readBlock
*/
function readBlock(state, drive, block, buffer) {
debug('read drive=' + drive);
debug('read buffer=' + buffer);
debug('read block=$' + toHex(block));
if (!disks[drive] || !disks[drive].length) {
debug('Drive', drive, 'is empty');
return;
}
// debug('read', '\n' + dumpBlock(drive, block));
for (var idx = 0; idx < 512; idx++) {
buffer.writeByte(disks[drive][block][idx]);
buffer = buffer.inc(1);
}
state.a = 0;
state.s &= 0xfe;
}
/*
* writeBlock
*/
function writeBlock(state, drive, block, buffer) {
debug('write drive=' + drive);
debug('write buffer=' + buffer);
debug('write block=$' + toHex(block));
if (!disks[drive] || !disks[drive].length) {
debug('Drive', drive, 'is empty');
return;
}
// debug('write', '\n' + dumpBlock(drive, block));
for (var idx = 0; idx < 512; idx++) {
disks[drive][block][idx] = buffer.readByte();
buffer = buffer.inc(1);
}
state.a = 0;
state.s &= 0xfe;
}
/*
* formatDevice
*/
function formatDevice(state, drive) {
for (var idx = 0; idx < disks[drive].length; idx++) {
disks[drive][idx] = [];
for (var jdx = 0; jdx < 512; jdx++) {
disks[drive][idx][jdx] = 0;
}
}
state.a = 0;
state.s &= 0xfe;
}
/*
* Interface
*/
return {
read: function(page, off, debugFlag) {
var state = cpu.getState();
var cmd;
var unit;
var buffer;
var block;
if (!debugFlag) {
debug('read $' + toHex(page) + toHex(off) + '=$' + toHex(ROM[off]), cpu.sync());
}
if (off == 0x00 && cpu.sync()) {
readBlock(state, 1, 0, new Address(0x0800));
} else if (off == 0x20 && cpu.sync()) { // Regular block device entry POINT
debug('block device entry');
cmd = cpu.read(0x00, 0x42);
unit = cpu.read(0x00, 0x43);
var bufferAddr;
var blockAddr;
var drive = (unit & 0x80) ? 2 : 1;
var driveSlot = (unit & 0x70) >> 4;
debug('cmd=' + cmd);
debug('unit=$' + toHex(unit));
debug('slot=' + driveSlot + ' drive=' + drive);
switch (cmd) {
case 0: // INFO
getDeviceInfo(state, drive);
break;
case 1: // READ
bufferAddr = new Address(0x44);
buffer = bufferAddr.readAddress();
blockAddr = new Address(0x46);
block = blockAddr.readWord();
readBlock(state, drive, block, buffer);
break;
case 2: // WRITE
bufferAddr = new Address(0x44);
buffer = bufferAddr.readAddress();
blockAddr = new Address(0x46);
block = blockAddr.readWord();
writeBlock(state, drive, block, buffer);
break;
case 3: // FORMAT
formatDevice(state, unit);
break;
}
} else if (off == 0x23 && cpu.sync()) {
debug('smartport entry');
var retVal = {};
var stackAddr = new Address(state.sp + 1, 0x01);
retVal = stackAddr.readAddress();
debug('return=' + retVal);
var cmdBlockAddr = retVal.inc(1);
cmd = cmdBlockAddr.readByte();
var cmdListAddr = cmdBlockAddr.inc(1).readAddress();
debug('cmd=' + cmd);
debug('cmdListAddr=' + cmdListAddr);
stackAddr.writeAddress(retVal.inc(3));
var parameterCount = cmdListAddr.readByte();
unit = cmdListAddr.inc(1).readByte();
buffer = cmdListAddr.inc(2).readAddress();
var status;
debug('parameterCount=' + parameterCount);
switch (cmd) {
case 0x00: // INFO
status = cmdListAddr.inc(4).readByte();
debug('info unit=' + unit);
debug('info buffer=' + buffer);
debug('info status=' + status);
switch (unit) {
case 0:
switch (status) {
case 0:
buffer.writeByte(1); // one device
buffer.inc(1).writeByte(1 << 6); // no interrupts
buffer.inc(2).writeByte(0); // reserved
buffer.inc(3).writeByte(0); // reserved
buffer.inc(4).writeByte(0); // reserved
buffer.inc(5).writeByte(0); // reserved
buffer.inc(6).writeByte(0); // reserved
buffer.inc(7).writeByte(0); // reserved
state.x = 8;
state.y = 0;
state.a = 0;
state.s &= 0xfe;
break;
}
break;
default: // Unit 1
switch (status) {
case 0:
var blocks = disks[unit].length;
buffer.writeByte(0xf0); // W/R Block device in drive
buffer.inc(1).writeByte(blocks & 0xff); // 1600 blocks
buffer.inc(2).writeByte((blocks & 0xff00) >> 8);
buffer.inc(3).writeByte((blocks & 0xff0000) >> 16);
state.x = 4;
state.y = 0;
state.a = 0;
state.s &= 0xfe;
break;
}
break;
}
state.a = 0;
state.s &= 0xfe;
break;
case 0x01: // READ BLOCK
block = cmdListAddr.inc(4).readWord();
readBlock(state, unit, block, buffer);
break;
case 0x02: // WRITE BLOCK
block = cmdListAddr.inc(4).readWord();
writeBlock(state, unit, block, buffer);
break;
case 0x03: // FORMAT
formatDevice(state, unit);
break;
case 0x04: // CONTROL
break;
case 0x05: // INIT
break;
case 0x06: // OPEN
break;
case 0x07: // CLOSE
break;
case 0x08: // READ
break;
case 0x09: // WRITE
break;
}
}
cpu.setState(state);
return ROM[off];
},
write: function() {
},
getState: function() {
},
setState: function() {
},
setBinary: function (drive, name, fmt, data) {
disks[drive] = [];
if (fmt == '2mg') {
data = data.slice(64);
}
for (var idx = 0; idx < data.byteLength; idx += 512) {
disks[drive].push(new Uint8Array(data.slice(idx, idx + 512)));
}
},
setDisk: function(drive, json) {
decodeDisk(drive, json);
}
};
}

53
js/formats/2mg.js Normal file
View File

@ -0,0 +1,53 @@
/* Copyright 2010-2019 Will Scullin <scullin@scullinsteel.com>
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
* the above copyright notice appear in all copies and that both that
* copyright notice and this permission notice appear in supporting
* documentation. No representations are made about the suitability of this
* software for any purpose. It is provided "as is" without express or
* implied warranty.
*/
import DOS from './do';
import Nibble from './nib';
import ProDOS from './po';
export default function _2MG(options) {
var { rawData } = options;
var disk;
var volume = 254;
// Standard header size is 64 bytes. Make assumptions.
var prefix = new Uint8Array(rawData.slice(0, 64));
rawData = rawData.slice(64);
var flags =
prefix[0x10] |
(prefix[0x11] << 8) |
(prefix[0x12] << 16) |
(prefix[0x13] << 24);
var readOnly = (flags & 0x80000000) !== 0;
if ((flags & 0x10) !== 0) {
volume = flags & 0xff;
}
options = { rawData, readOnly, volume };
// Check image format.
// Sure, it's really 64 bits. But only 2 are actually used.
switch (prefix[0xc]) {
case 1: // PO
disk = new ProDOS(options);
break;
case 2: // NIB
disk = new Nibble(options);
break;
case 0: // dsk
default: // Something hinky, assume 'dsk'
disk = new DOS(options);
break;
}
return disk;
}

44
js/formats/d13.js Normal file
View File

@ -0,0 +1,44 @@
/* Copyright 2010-2019 Will Scullin <scullin@scullinsteel.com>
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
* the above copyright notice appear in all copies and that both that
* copyright notice and this permission notice appear in supporting
* documentation. No representations are made about the suitability of this
* software for any purpose. It is provided "as is" without express or
* implied warranty.
*/
import { explodeSector13, _D13O } from './format_utils';
export default function Nibble(options) {
var { data, name, rawData, volume, readOnly } = options;
var disk = {
format: 'd13',
name,
volume,
readOnly,
tracks: [],
trackMap: null,
rawTracks: null
};
for (var t = 0; t < 35; t++) {
var track = [];
for (var s = 0; s < 13; s++) {
var sector;
if (rawData) {
var off = (13 * t + _D13O[s]) * 256;
sector = new Uint8Array(rawData.slice(off, off + 256));
} else {
sector = data[t][_D13O[s]];
}
track = track.concat(
explodeSector13(volume, t, _D13O[s], sector)
);
}
disk.tracks.push(track);
}
return disk;
}

46
js/formats/do.js Normal file
View File

@ -0,0 +1,46 @@
/* Copyright 2010-2019 Will Scullin <scullin@scullinsteel.com>
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
* the above copyright notice appear in all copies and that both that
* copyright notice and this permission notice appear in supporting
* documentation. No representations are made about the suitability of this
* software for any purpose. It is provided "as is" without express or
* implied warranty.
*/
import { explodeSector16, _DO } from './format_utils';
import { bytify } from '../util';
export default function DOS(options) {
var { data, name, rawData, volume, readOnly } = options;
var disk = {
format: 'dsk',
name,
volume,
readOnly,
tracks: [],
trackMap: null,
rawTracks: null
};
for (var t = 0; t < 35; t++) {
var track = [];
for (var s = 0; s < 16; s++) {
var _s = 15 - s;
var sector;
if (rawData) {
var off = (16 * t + _s) * 256;
sector = new Uint8Array(rawData.slice(off, off + 256));
} else {
sector = data[t][_s];
}
track = track.concat(
explodeSector16(volume, t, _DO[_s], sector)
);
}
disk.tracks[t] = bytify(track);
}
return disk;
}

425
js/formats/format_utils.js Normal file
View File

@ -0,0 +1,425 @@
/* Copyright 2010-2019 Will Scullin <scullin@scullinsteel.com>
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
* the above copyright notice appear in all copies and that both that
* copyright notice and this permission notice appear in supporting
* documentation. No representations are made about the suitability of this
* software for any purpose. It is provided "as is" without express or
* implied warranty.
*/
import { base64_decode, base64_encode } from '../base64';
import { bytify, debug, toHex } from '../util';
// var DO = [0x0,0x7,0xE,0x6,0xD,0x5,0xC,0x4,
// 0xB,0x3,0xA,0x2,0x9,0x1,0x8,0xF];
export var _DO = [
0x0,0xD,0xB,0x9,0x7,0x5,0x3,0x1,
0xE,0xC,0xA,0x8,0x6,0x4,0x2,0xF
];
// var PO = [0x0,0x8,0x1,0x9,0x2,0xa,0x3,0xb,
// 0x4,0xc,0x5,0xd,0x6,0xe,0x7,0xf];
export var _PO = [
0x0,0x2,0x4,0x6,0x8,0xa,0xc,0xe,
0x1,0x3,0x5,0x7,0x9,0xb,0xd,0xf
];
// var D13O = [
// 0x0, 0xa, 0x7, 0x4, 0x1, 0xb, 0x8, 0x5, 0x2, 0xc, 0x9, 0x6, 0x3
// ];
export var _D13O = [
0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc
];
var _trans53 = [
0xab, 0xad, 0xae, 0xaf, 0xb5, 0xb6, 0xb7, 0xba,
0xbb, 0xbd, 0xbe, 0xbf, 0xd6, 0xd7, 0xda, 0xdb,
0xdd, 0xde, 0xdf, 0xea, 0xeb, 0xed, 0xee, 0xef,
0xf5, 0xf6, 0xf7, 0xfa, 0xfb, 0xfd, 0xfe, 0xff
];
var _trans62 = [
0x96, 0x97, 0x9a, 0x9b, 0x9d, 0x9e, 0x9f, 0xa6,
0xa7, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb2, 0xb3,
0xb4, 0xb5, 0xb6, 0xb7, 0xb9, 0xba, 0xbb, 0xbc,
0xbd, 0xbe, 0xbf, 0xcb, 0xcd, 0xce, 0xcf, 0xd3,
0xd6, 0xd7, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde,
0xdf, 0xe5, 0xe6, 0xe7, 0xe9, 0xea, 0xeb, 0xec,
0xed, 0xee, 0xef, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6,
0xf7, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
];
export var detrans62 = [
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x02, 0x03, 0x00, 0x04, 0x05, 0x06,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x08,
0x00, 0x00, 0x00, 0x09, 0x0A, 0x0B, 0x0C, 0x0D,
0x00, 0x00, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13,
0x00, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x1B, 0x00, 0x1C, 0x1D, 0x1E,
0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x20, 0x21,
0x00, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
0x00, 0x00, 0x00, 0x00, 0x00, 0x29, 0x2A, 0x2B,
0x00, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32,
0x00, 0x00, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38,
0x00, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F
];
/**
* From Beneath Apple DOS
*/
export function fourXfour(val) {
var xx = val & 0xaa;
var yy = val & 0x55;
xx >>= 1;
xx |= 0xaa;
yy |= 0xaa;
return [xx, yy];
}
export function defourXfour(xx, yy) {
return ((xx << 1) | 0x01) & yy;
}
export function explodeSector16(volume, track, sector, data) {
var checksum;
var buf = [], idx;
var gap;
/*
* Gap 1/3 (40/0x28 bytes)
*/
if (sector === 0) // Gap 1
gap = 0x80;
else { // Gap 3
gap = track === 0 ? 0x28 : 0x26;
}
for (idx = 0; idx < gap; idx++) {
buf.push(0xff);
}
/*
* Address Field
*/
checksum = volume ^ track ^ sector;
buf = buf.concat([0xd5, 0xaa, 0x96]); // Address Prolog D5 AA 96
buf = buf.concat(fourXfour(volume));
buf = buf.concat(fourXfour(track));
buf = buf.concat(fourXfour(sector));
buf = buf.concat(fourXfour(checksum));
buf = buf.concat([0xde, 0xaa, 0xeb]); // Epilog DE AA EB
/*
* Gap 2 (5 bytes)
*/
for (idx = 0; idx < 0x05; idx++) {
buf.push(0xff);
}
/*
* Data Field
*/
buf = buf.concat([0xd5, 0xaa, 0xad]); // Data Prolog D5 AA AD
var nibbles = [];
var ptr2 = 0;
var ptr6 = 0x56;
var idx2, idx6;
for (idx = 0; idx < 0x156; idx++) {
nibbles[idx] = 0;
}
idx2 = 0x55;
for (idx6 = 0x101; idx6 >= 0; idx6--) {
var val6 = data[idx6 % 0x100];
var val2 = nibbles[ptr2 + idx2];
val2 = (val2 << 1) | (val6 & 1);
val6 >>= 1;
val2 = (val2 << 1) | (val6 & 1);
val6 >>= 1;
nibbles[ptr6 + idx6] = val6;
nibbles[ptr2 + idx2] = val2;
if (--idx2 < 0)
idx2 = 0x55;
}
var last = 0;
for (idx = 0; idx < 0x156; idx++) {
var val = nibbles[idx];
buf.push(_trans62[last ^ val]);
last = val;
}
buf.push(_trans62[last]);
buf = buf.concat([0xde, 0xaa, 0xf2]); // Epilog DE AA F2
/*
* Gap 3
*/
buf.push(0xff);
return buf;
}
export function explodeSector13(volume, track, sector, data) {
var checksum;
var buf = [], idx;
var gap;
/*
* Gap 1/3 (40/0x28 bytes)
*/
if (sector === 0) // Gap 1
gap = 0x80;
else { // Gap 3
gap = track === 0 ? 0x28 : 0x26;
}
for (idx = 0; idx < gap; idx++) {
buf.push(0xff);
}
/*
* Address Field
*/
checksum = volume ^ track ^ sector;
buf = buf.concat([0xd5, 0xaa, 0xb5]); // Address Prolog D5 AA B5
buf = buf.concat(fourXfour(volume));
buf = buf.concat(fourXfour(track));
buf = buf.concat(fourXfour(sector));
buf = buf.concat(fourXfour(checksum));
buf = buf.concat([0xde, 0xaa, 0xeb]); // Epilog DE AA EB
/*
* Gap 2 (5 bytes)
*/
for (idx = 0; idx < 0x05; idx++) {
buf.push(0xff);
}
/*
* Data Field
*/
buf = buf.concat([0xd5, 0xaa, 0xad]); // Data Prolog D5 AA AD
var nibbles = [];
var jdx = 0;
for (idx = 0x32; idx >= 0; idx--) {
var a5 = data[jdx] >> 3;
var a3 = data[jdx] & 0x07;
jdx++;
var b5 = data[jdx] >> 3;
var b3 = data[jdx] & 0x07;
jdx++;
var c5 = data[jdx] >> 3;
var c3 = data[jdx] & 0x07;
jdx++;
var d5 = data[jdx] >> 3;
var d3 = data[jdx] & 0x07;
jdx++;
var e5 = data[jdx] >> 3;
var e3 = data[jdx] & 0x07;
jdx++;
nibbles[idx + 0x00] = a5;
nibbles[idx + 0x33] = b5;
nibbles[idx + 0x66] = c5;
nibbles[idx + 0x99] = d5;
nibbles[idx + 0xcc] = e5;
nibbles[idx + 0x100] = a3 << 2 | (d3 & 0x4) >> 1 | (e3 & 0x4) >> 2;
nibbles[idx + 0x133] = b3 << 2 | (d3 & 0x2) | (e3 & 0x2) >> 1;
nibbles[idx + 0x166] = c3 << 2 | (d3 & 0x1) << 1 | (e3 & 0x1);
}
nibbles[0xff] = data[jdx] >> 3;
nibbles[0x199] = data[jdx] & 0x07;
var val;
var last = 0;
for (idx = 0x199; idx >= 0x100; idx--) {
val = nibbles[idx];
buf.push(_trans53[last ^ val]);
last = val;
}
for (idx = 0x0; idx < 0x100; idx++) {
val = nibbles[idx];
buf.push(_trans53[last ^ val]);
last = val;
}
buf.push(_trans53[last]);
buf = buf.concat([0xde, 0xaa, 0xeb]); // Epilog DE AA EB
/*
* Gap 3
*/
buf.push(0xff);
return buf;
}
export function readSector(drive, track, sector) {
var _sector = cur.fmt == 'po' ? _PO[sector] : _DO[sector];
var val, state = 0;
var idx = 0;
var retry = 0;
var cur = drive.tracks[track];
function _readNext() {
var result = cur[idx++];
if (idx >= cur.length) {
idx = 0;
retry++;
}
return result;
}
function _skipBytes(count) {
idx += count;
if (idx >= cur.length) {
idx %= cur.length;
retry++;
}
}
var t = 0, s = 0, v = 0, jdx, kdx, checkSum;
var data = [];
while (retry < 4) {
switch (state) {
case 0:
val = _readNext();
state = (val === 0xd5) ? 1 : 0;
break;
case 1:
val = _readNext();
state = (val === 0xaa) ? 2 : 0;
break;
case 2:
val = _readNext();
state = (val === 0x96) ? 3 : (val === 0xad ? 4 : 0);
break;
case 3: // Address
v = defourXfour(_readNext(), _readNext()); // Volume
t = defourXfour(_readNext(), _readNext());
s = defourXfour(_readNext(), _readNext());
checkSum = defourXfour(_readNext(), _readNext());
if (checkSum != (v ^ t ^ s)) {
debug('Invalid header checksum:', toHex(v), toHex(t), toHex(s), toHex(checkSum));
}
_skipBytes(3); // Skip footer
state = 0;
break;
case 4: // Data
if (s === _sector && t === track) {
var data2 = [];
var last = 0;
for (jdx = 0x55; jdx >= 0; jdx--) {
val = detrans62[_readNext() - 0x80] ^ last;
data2[jdx] = val;
last = val;
}
for (jdx = 0; jdx < 0x100; jdx++) {
val = detrans62[_readNext() - 0x80] ^ last;
data[jdx] = val;
last = val;
}
checkSum = detrans62[_readNext() - 0x80] ^ last;
if (checkSum) {
debug('Invalid data checksum:', toHex(v), toHex(t), toHex(s), toHex(checkSum));
}
for (kdx = 0, jdx = 0x55; kdx < 0x100; kdx++) {
data[kdx] <<= 1;
if ((data2[jdx] & 0x01) !== 0) {
data[kdx] |= 0x01;
}
data2[jdx] >>= 1;
data[kdx] <<= 1;
if ((data2[jdx] & 0x01) !== 0) {
data[kdx] |= 0x01;
}
data2[jdx] >>= 1;
if (--jdx < 0) jdx = 0x55;
}
return data;
}
else
_skipBytes(0x159); // Skip data, checksum and footer
state = 0;
break;
default:
break;
}
}
return [];
}
export function jsonEncode(cur, pretty) {
var data = [];
var format = 'dsk';
for (var t = 0; t < cur.tracks.length; t++) {
data[t] = [];
if (cur.format === 'nib') {
format = 'nib';
data[t] = base64_encode(cur.tracks[t]);
} else {
for (var s = 0; s < 0x10; s++) {
data[t][s] = base64_encode(readSector(cur, t));
}
}
}
return JSON.stringify({
'type': format,
'encoding': 'base64',
'volume': cur.volume,
'data': data
}, null, pretty ? ' ' : null);
}
export function jsonDecode(data) {
var cur = {};
var tracks = [];
var json = JSON.parse(data);
var v = json.volume;
for (var t = 0; t < json.data.length; t++) {
var track = [];
for (var s = 0; s < json.data[t].length; s++) {
var _s = 15 - s;
var d = base64_decode(json.data[t][_s]);
track = track.concat(explodeSector16(v, t, s, d));
}
tracks[t] = bytify(track);
}
cur.volume = v;
cur.format = json.type;
cur.tracks = tracks;
cur.trackMap = null;
return cur;
}

36
js/formats/nib.js Normal file
View File

@ -0,0 +1,36 @@
/* Copyright 2010-2019 Will Scullin <scullin@scullinsteel.com>
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
* the above copyright notice appear in all copies and that both that
* copyright notice and this permission notice appear in supporting
* documentation. No representations are made about the suitability of this
* software for any purpose. It is provided "as is" without express or
* implied warranty.
*/
export default function Nibble(options) {
var { data, name, rawData, volume, readOnly } = options;
var disk = {
format: 'nib',
name,
volume: volume || 254,
readOnly: readOnly || false,
tracks: [],
trackMap: null,
rawTracks: null
};
for (var t = 0; t < 35; t++) {
var track;
if (rawData) {
var off = t * 0x1a00;
track = new Uint8Array(data.slice(off, off + 0x1a00));
} else {
track = data[t];
}
disk.tracks[t] = track;
}
return disk;
}

45
js/formats/po.js Normal file
View File

@ -0,0 +1,45 @@
/* Copyright 2010-2019 Will Scullin <scullin@scullinsteel.com>
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
* the above copyright notice appear in all copies and that both that
* copyright notice and this permission notice appear in supporting
* documentation. No representations are made about the suitability of this
* software for any purpose. It is provided "as is" without express or
* implied warranty.
*/
import { explodeSector16, _PO } from './format_utils';
import { bytify } from '../util';
export default function ProDOS(options) {
var { data, name, rawData, volume, readOnly } = options;
var disk = {
format: 'nib',
name,
volume: volume || 254,
tracks: [],
readOnly: readOnly || false,
trackMap: null,
rawTracks: null
};
for (var t = 0; t < 35; t++) {
var track = [];
for (var s = 0; s < 16; s++) {
var sector;
if (rawData) {
var off = (16 * t + s) * 256;
sector = new Uint8Array(rawData.slice(off, off + 256));
} else {
sector = data[t][s];
}
track = track.concat(
explodeSector16(volume, t, _PO[s], sector)
);
}
disk.tracks[t] = bytify(track);
}
return disk;
}

276
js/formats/woz.js Normal file
View File

@ -0,0 +1,276 @@
/* Copyright 2010-2019 Will Scullin <scullin@scullinsteel.com>
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
* the above copyright notice appear in all copies and that both that
* copyright notice and this permission notice appear in supporting
* documentation. No representations are made about the suitability of this
* software for any purpose. It is provided "as is" without express or
* implied warranty.
*/
import { debug, toHex } from '../util';
var WOZ_HEADER_START = 0;
var WOZ_HEADER_SIZE = 12;
var WOZ1_SIGNATURE = 0x315A4F57;
var WOZ2_SIGNATURE = 0x325A4F57;
var WOZ_INTEGRITY_CHECK = 0x0a0d0aff;
function stringFromBytes(data, start, end) {
return String.fromCharCode.apply(
null,
new Uint8Array(data.buffer.slice(data.byteOffset + start, data.byteOffset + end))
);
}
function grabNibble(bits, offset) {
var nibble = 0;
var waitForOne = true;
while (offset < bits.length) {
var bit = bits[offset];
if (bit) {
nibble = (nibble << 1) | 0x01;
waitForOne = false;
} else {
if (!waitForOne) {
nibble = nibble << 1;
}
}
if (nibble & 0x80) {
// nibble complete return it
break;
}
offset += 1;
}
return {
nibble: nibble,
offset: offset
};
}
function InfoChunk(data) {
Object.assign(this, {
version: data.getUint8(0),
diskType: data.getUint8(1),
writeProtected: data.getUint8(2),
synchronized: data.getUint8(3),
cleaned: data.getUint8(4),
creator: stringFromBytes(data, 5, 37)
});
if (this.version === 2) {
Object.assign(this, {
sides: data.getUint8(37),
bootSector: data.getUint8(38),
bitTiming: data.getUint8(39),
compatibleHardware: data.getUint16(40, true),
requiredRAM: data.getUint16(42, true),
largestTrack: data.getUint16(44, true)
});
}
return this;
}
function TMapChunk(data) {
this.trackMap = [];
for (var idx = 0; idx < 160; idx++) {
this.trackMap.push(data.getUint8(idx));
}
return this;
}
function TrksChunk(data) {
var WOZ_TRACK_SIZE = 6656;
var WOZ_TRACK_INFO_BITS = 6648;
this.rawTracks = [];
this.tracks = [];
for (var trackNo = 0, idx = 0; idx < data.byteLength; idx += WOZ_TRACK_SIZE, trackNo++) {
var jdx;
var track = [];
var rawTrack = [];
var slice = data.buffer.slice(data.byteOffset + idx, data.byteOffset + idx + WOZ_TRACK_SIZE);
var trackData = new Uint8Array(slice);
var trackInfo = new DataView(slice);
var trackBitCount = trackInfo.getUint16(WOZ_TRACK_INFO_BITS, true);
for (jdx = 0; jdx < trackBitCount; jdx++) {
var byteIndex = jdx >> 3;
var bitIndex = 7 - (jdx & 0x07);
rawTrack[jdx] = (trackData[byteIndex] >> bitIndex) & 0x1;
}
track = [];
var offset = 0;
while (offset < rawTrack.length) {
var result = grabNibble(rawTrack, offset);
if (!result.nibble) { break; }
track.push(result.nibble);
offset = result.offset + 1;
}
this.tracks[trackNo] = track;
this.rawTracks[trackNo] = rawTrack;
}
return this;
}
function TrksChunk2(data) {
var trackNo;
this.trks = [];
for (trackNo = 0; trackNo < 160; trackNo++) {
var startBlock = data.getUint16(trackNo * 8, true);
var blockCount = data.getUint16(trackNo * 8 + 2, true);
var bitCount = data.getUint32(trackNo * 8 + 4, true);
if (bitCount === 0) { break; }
this.trks.push({
startBlock: startBlock,
blockCount: blockCount,
bitCount: bitCount
});
}
this.tracks = [];
this.rawTracks = [];
var bits = data.buffer;
for (trackNo = 0; trackNo < this.trks.length; trackNo++) {
var trk = this.trks[trackNo];
var track = [];
var rawTrack = [];
var start = trk.startBlock * 512;
var end = start + trk.blockCount * 512;
var slice = bits.slice(start, end);
var trackData = new Uint8Array(slice);
for (var jdx = 0; jdx < trk.bitCount; jdx++) {
var byteIndex = jdx >> 3;
var bitIndex = 7 - (jdx & 0x07);
rawTrack[jdx] = (trackData[byteIndex] >> bitIndex) & 0x1;
}
track = [];
var offset = 0;
while (offset < rawTrack.length) {
var result = grabNibble(rawTrack, offset);
if (!result.nibble) { break; }
track.push(result.nibble);
offset = result.offset + 1;
}
this.tracks[trackNo] = track;
this.rawTracks[trackNo] = rawTrack;
}
return this;
}
function MetaChunk(data) {
var infoStr = stringFromBytes(data, 0, data.byteLength);
var parts = infoStr.split('\n');
var info = parts.reduce(function(acc, part) {
var subParts = part.split('\t');
acc[subParts[0]] = subParts[1];
return acc;
}, {});
Object.assign(this, info);
return this;
}
export default function Woz(options) {
var { rawData } = options;
var dv = new DataView(rawData, 0);
var dvOffset = 0;
var disk = {
format: 'woz'
};
var wozVersion;
var chunks = {};
function readHeader() {
var wozSignature = dv.getUint32(WOZ_HEADER_START + 0, true);
switch (wozSignature) {
case WOZ1_SIGNATURE:
wozVersion = 1;
break;
case WOZ2_SIGNATURE:
wozVersion = 2;
break;
default:
return false;
}
if (dv.getUint32(WOZ_HEADER_START + 4, true) !== WOZ_INTEGRITY_CHECK) {
return false;
}
return true;
}
function readChunk() {
if (dvOffset >= dv.byteLength) {
return null;
}
var type = dv.getUint32(dvOffset, true);
var size = dv.getUint32(dvOffset + 4, true);
var data = new DataView(dv.buffer, dvOffset + 8, size);
dvOffset += size + 8;
return {
type: type,
size: size,
data: data
};
}
if (readHeader()) {
dvOffset = WOZ_HEADER_SIZE;
var chunk = readChunk();
while (chunk) {
switch (chunk.type) {
case 0x4F464E49: // INFO
chunks.info = new InfoChunk(chunk.data);
break;
case 0x50414D54: // TMAP
chunks.tmap = new TMapChunk(chunk.data);
break;
case 0x534B5254: // TRKS
if (wozVersion === 1) {
chunks.trks = new TrksChunk(chunk.data);
} else {
chunks.trks = new TrksChunk2(chunk.data);
}
break;
case 0x4154454D: // META
chunks.meta = new MetaChunk(chunk.data);
break;
case 0x54495257: // WRIT
// Ignore
break;
default:
debug('Unsupported chunk', toHex(chunk.type, 8));
}
chunk = readChunk();
}
}
debug(chunks);
disk.trackMap = chunks.tmap.trackMap;
disk.tracks = chunks.trks.tracks;
disk.rawTracks = chunks.trks.rawTracks;
disk.readOnly = true; //chunks.info.writeProtected === 1;
disk.name = chunks.info.title;
return disk;
}

View File

@ -80,12 +80,12 @@ export function openSave(drive, event)
var blob = new Blob([data], { 'type': mimetype }); var blob = new Blob([data], { 'type': mimetype });
a.href = window.URL.createObjectURL(blob); a.href = window.URL.createObjectURL(blob);
a.download = drivelights.label(drive) + '.dsk'; a.download = driveLights.label(drive) + '.dsk';
if (event.metaKey) { if (event.metaKey) {
dumpDisk(drive); dumpDisk(drive);
} else { } else {
document.querySelector('#save_name').value = drivelights.label(drive); document.querySelector('#save_name').value = driveLights.label(drive);
MicroModal.show('save-modal'); MicroModal.show('save-modal');
} }
} }
@ -230,7 +230,7 @@ function doLoadLocalDisk(drive, file) {
var ext = parts.pop().toLowerCase(); var ext = parts.pop().toLowerCase();
var name = parts.join('.'); var name = parts.join('.');
if (disk2.setBinary(drive, name, ext, this.result)) { if (disk2.setBinary(drive, name, ext, this.result)) {
drivelights.label(drive, name); driveLights.label(drive, name);
initGamepad(); initGamepad();
} }
}; };
@ -251,7 +251,7 @@ function doLoadHTTP(drive, _url) {
var ext = fileParts.pop().toLowerCase(); var ext = fileParts.pop().toLowerCase();
var name = decodeURIComponent(fileParts.join('.')); var name = decodeURIComponent(fileParts.join('.'));
if (disk2.setBinary(drive, name, ext, req.response)) { if (disk2.setBinary(drive, name, ext, req.response)) {
drivelights.label(drive, name); driveLights.label(drive, name);
MicroModal.close('http-modal'); MicroModal.close('http-modal');
initGamepad(); initGamepad();
} }
@ -338,7 +338,7 @@ var vm = new VideoModes(gr, hgr, gr2, hgr2, false);
vm.multiScreen(multiScreen); vm.multiScreen(multiScreen);
var dumper = new ApplesoftDump(cpu); var dumper = new ApplesoftDump(cpu);
var drivelights = new DriveLights(); var driveLights = new DriveLights();
var io = new Apple2IO(cpu, vm); var io = new Apple2IO(cpu, vm);
var keyboard = new KeyBoard(cpu, io); var keyboard = new KeyBoard(cpu, io);
var audio = new Audio(io); var audio = new Audio(io);
@ -349,7 +349,7 @@ var lc = new LanguageCard(io, 0, rom);
var parallel = new Parallel(io, 1, printer); var parallel = new Parallel(io, 1, printer);
var slinky = new RAMFactor(io, 2, 1024 * 1024); var slinky = new RAMFactor(io, 2, 1024 * 1024);
var videoterm = new Videoterm(io, 3, context1); var videoterm = new Videoterm(io, 3, context1);
var disk2 = new DiskII(io, 6, drivelights); var disk2 = new DiskII(io, 6, driveLights);
var clock = new Thunderclock(io, 7); var clock = new Thunderclock(io, 7);
cpu.addPageHandler(ram1); cpu.addPageHandler(ram1);
@ -411,7 +411,7 @@ export function updateSound() {
function dumpDisk(drive) { function dumpDisk(drive) {
var wind = window.open('', '_blank'); var wind = window.open('', '_blank');
wind.document.title = drivelights.label(drive); wind.document.title = driveLights.label(drive);
wind.document.write('<pre>'); wind.document.write('<pre>');
wind.document.write(disk2.getJSON(drive, true)); wind.document.write(disk2.getJSON(drive, true));
wind.document.write('</pre>'); wind.document.write('</pre>');
@ -565,7 +565,7 @@ function saveState() {
lc: lc.getState(), lc: lc.getState(),
vm: vm.getState(), vm: vm.getState(),
disk2: disk2.getState(), disk2: disk2.getState(),
drivelights: drivelights.getState() driveLights: driveLights.getState()
}; };
if (slinky) { if (slinky) {
state.slinky = slinky.getState(); state.slinky = slinky.getState();
@ -591,7 +591,7 @@ function restoreState() {
lc.setState(state.lc); lc.setState(state.lc);
vm.setState(state.vm); vm.setState(state.vm);
disk2.setState(state.disk2); disk2.setState(state.disk2);
drivelights.setState(state.drivelights); driveLights.setState(state.driveLights);
if (slinky && state.slinky) { if (slinky && state.slinky) {
slinky.setState(state.slinky); slinky.setState(state.slinky);
} }
@ -645,7 +645,7 @@ function loadDisk(drive, disk) {
disk_cur_cat[drive] = category; disk_cur_cat[drive] = category;
disk_cur_name[drive] = name; disk_cur_name[drive] = name;
drivelights.label(drive, name); driveLights.label(drive, name);
disk2.setDisk(drive, disk); disk2.setDisk(drive, disk);
initGamepad(disk.gamepad); initGamepad(disk.gamepad);
} }
@ -697,8 +697,8 @@ function saveLocalStorage(drive, name) {
window.alert('Saved'); window.alert('Saved');
drivelights.label(drive, name); driveLights.label(drive, name);
drivelights.dirty(drive, false); driveLights.dirty(drive, false);
updateLocalStorage(); updateLocalStorage();
} }
@ -716,8 +716,8 @@ function loadLocalStorage(drive, name) {
var diskIndex = JSON.parse(window.localStorage.diskIndex || '{}'); var diskIndex = JSON.parse(window.localStorage.diskIndex || '{}');
if (diskIndex[name]) { if (diskIndex[name]) {
disk2.setJSON(drive, diskIndex[name]); disk2.setJSON(drive, diskIndex[name]);
drivelights.label(drive, name); driveLights.label(drive, name);
drivelights.dirty(drive, false); driveLights.dirty(drive, false);
} }
} }

View File

@ -75,12 +75,12 @@ export function openSave(drive, event)
var blob = new Blob([data], { 'type': mimetype }); var blob = new Blob([data], { 'type': mimetype });
a.href = window.URL.createObjectURL(blob); a.href = window.URL.createObjectURL(blob);
a.download = drivelights.label(drive) + '.dsk'; a.download = driveLights.label(drive) + '.dsk';
if (event.metaKey) { if (event.metaKey) {
dumpDisk(drive); dumpDisk(drive);
} else { } else {
document.querySelector('#save_name').value = drivelights.label(drive); document.querySelector('#save_name').value = driveLights.label(drive);
MicroModal.show('save-modal'); MicroModal.show('save-modal');
} }
} }
@ -226,7 +226,7 @@ function doLoadLocalDisk(drive, file) {
var ext = parts.pop().toLowerCase(); var ext = parts.pop().toLowerCase();
var name = parts.join('.'); var name = parts.join('.');
if (disk2.setBinary(drive, name, ext, this.result)) { if (disk2.setBinary(drive, name, ext, this.result)) {
drivelights.label(drive, name); driveLights.label(drive, name);
initGamepad(); initGamepad();
} }
}; };
@ -247,7 +247,7 @@ function doLoadHTTP(drive, _url) {
var ext = fileParts.pop().toLowerCase(); var ext = fileParts.pop().toLowerCase();
var name = decodeURIComponent(fileParts.join('.')); var name = decodeURIComponent(fileParts.join('.'));
if (disk2.setBinary(drive, name, ext, req.response)) { if (disk2.setBinary(drive, name, ext, req.response)) {
drivelights.label(drive, name); driveLights.label(drive, name);
MicroModal.close('http-modal'); MicroModal.close('http-modal');
initGamepad(); initGamepad();
} }
@ -323,7 +323,7 @@ vm.enhanced(enhanced);
vm.multiScreen(multiScreen); vm.multiScreen(multiScreen);
var dumper = new ApplesoftDump(cpu); var dumper = new ApplesoftDump(cpu);
var drivelights = new DriveLights(); var driveLights = new DriveLights();
var io = new Apple2IO(cpu, vm); var io = new Apple2IO(cpu, vm);
var keyboard = new KeyBoard(cpu, io, true); var keyboard = new KeyBoard(cpu, io, true);
var audio = new Audio(io); var audio = new Audio(io);
@ -336,7 +336,7 @@ cpu.addPageHandler(mmu);
var parallel = new Parallel(io, 1, printer); var parallel = new Parallel(io, 1, printer);
var slinky = new RAMFactor(io, 2, 1024 * 1024); var slinky = new RAMFactor(io, 2, 1024 * 1024);
var disk2 = new DiskII(io, 6, drivelights); var disk2 = new DiskII(io, 6, driveLights);
var clock = new Thunderclock(io, 7); var clock = new Thunderclock(io, 7);
io.setSlot(1, parallel); io.setSlot(1, parallel);
@ -386,7 +386,7 @@ export function updateSound() {
function dumpDisk(drive) { function dumpDisk(drive) {
var wind = window.open('', '_blank'); var wind = window.open('', '_blank');
wind.document.title = drivelights.label(drive); wind.document.title = driveLights.label(drive);
wind.document.write('<pre>'); wind.document.write('<pre>');
wind.document.write(disk2.getJSON(drive, true)); wind.document.write(disk2.getJSON(drive, true));
wind.document.write('</pre>'); wind.document.write('</pre>');
@ -534,7 +534,7 @@ function saveState() {
mmu: mmu.getState(), mmu: mmu.getState(),
vm: vm.getState(), vm: vm.getState(),
disk2: disk2.getState(), disk2: disk2.getState(),
drivelights: drivelights.getState() driveLights: driveLights.getState()
}; };
if (slinky) { if (slinky) {
state.slinky = slinky.getState(); state.slinky = slinky.getState();
@ -557,7 +557,7 @@ function restoreState() {
mmu.setState(state.mmu); mmu.setState(state.mmu);
vm.setState(state.vm); vm.setState(state.vm);
disk2.setState(state.disk2); disk2.setState(state.disk2);
drivelights.setState(state.drivelights); driveLights.setState(state.driveLights);
if (slinky && state.slinky) { if (slinky && state.slinky) {
slinky.setState(state.slinky); slinky.setState(state.slinky);
} }
@ -611,7 +611,7 @@ function loadDisk(drive, disk) {
disk_cur_cat[drive] = category; disk_cur_cat[drive] = category;
disk_cur_name[drive] = name; disk_cur_name[drive] = name;
drivelights.label(drive, name); driveLights.label(drive, name);
disk2.setDisk(drive, disk); disk2.setDisk(drive, disk);
initGamepad(disk.gamepad); initGamepad(disk.gamepad);
} }
@ -663,8 +663,8 @@ function saveLocalStorage(drive, name) {
window.alert('Saved'); window.alert('Saved');
drivelights.label(drive, name); driveLights.label(drive, name);
drivelights.dirty(drive, false); driveLights.dirty(drive, false);
updateLocalStorage(); updateLocalStorage();
} }
@ -682,8 +682,8 @@ function loadLocalStorage(drive, name) {
var diskIndex = JSON.parse(window.localStorage.diskIndex || '{}'); var diskIndex = JSON.parse(window.localStorage.diskIndex || '{}');
if (diskIndex[name]) { if (diskIndex[name]) {
disk2.setJSON(drive, diskIndex[name]); disk2.setJSON(drive, diskIndex[name]);
drivelights.label(drive, name); driveLights.label(drive, name);
drivelights.dirty(drive, false); driveLights.dirty(drive, false);
} }
} }