D13 support, closer to real hardward behavior.

This commit is contained in:
Will Scullin 2017-09-23 11:34:24 -07:00
parent bdb792f8fb
commit c02ea762d8

View File

@ -10,7 +10,7 @@
*/ */
/*exported DiskII */ /*exported DiskII */
/*globals bytify: false, debug: false /*globals bytify: false, debug: false, toHex: false
base64_decode: false, base64_encode: false base64_decode: false, base64_encode: false
Uint8Array: false Uint8Array: false
*/ */
@ -72,42 +72,65 @@ function DiskII(io, slot, callbacks)
// var DO = [0x0,0x7,0xE,0x6,0xD,0x5,0xC,0x4, // var DO = [0x0,0x7,0xE,0x6,0xD,0x5,0xC,0x4,
// 0xB,0x3,0xA,0x2,0x9,0x1,0x8,0xF]; // 0xB,0x3,0xA,0x2,0x9,0x1,0x8,0xF];
var _DO = [0x0,0xD,0xB,0x9,0x7,0x5,0x3,0x1, var _DO = [
0xE,0xC,0xA,0x8,0x6,0x4,0x2,0xF]; 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, // var PO = [0x0,0x8,0x1,0x9,0x2,0xa,0x3,0xb,
// 0x4,0xc,0x5,0xd,0x6,0xe,0x7,0xf]; // 0x4,0xc,0x5,0xd,0x6,0xe,0x7,0xf];
var _PO = [0x0,0x2,0x4,0x6,0x8,0xa,0xc,0xe, var _PO = [
0x1,0x3,0x5,0x7,0x9,0xb,0xd,0xf]; 0x0,0x2,0x4,0x6,0x8,0xa,0xc,0xe,
0x1,0x3,0x5,0x7,0x9,0xb,0xd,0xf
];
var _trans = [0x96, 0x97, 0x9a, 0x9b, 0x9d, 0x9e, 0x9f, 0xa6, // var D13O = [
0xa7, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb2, 0xb3, // 0x0, 0xa, 0x7, 0x4, 0x1, 0xb, 0x8, 0x5, 0x2, 0xc, 0x9, 0x6, 0x3
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];
var _detrans = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, var _D13O = [
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, ];
0x00, 0x00, 0x02, 0x03, 0x00, 0x04, 0x05, 0x06,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x08, var _trans53 = [
0x00, 0x00, 0x00, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0xab, 0xad, 0xae, 0xaf, 0xb5, 0xb6, 0xb7, 0xba,
0x00, 0x00, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0xbb, 0xbd, 0xbe, 0xbf, 0xd6, 0xd7, 0xda, 0xdb,
0x00, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0xdd, 0xde, 0xdf, 0xea, 0xeb, 0xed, 0xee, 0xef,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf5, 0xf6, 0xf7, 0xfa, 0xfb, 0xfd, 0xfe, 0xff
0x00, 0x00, 0x00, 0x1B, 0x00, 0x1C, 0x1D, 0x1E, ];
0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x20, 0x21,
0x00, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, var _trans62 = [
0x00, 0x00, 0x00, 0x00, 0x00, 0x29, 0x2A, 0x2B, 0x96, 0x97, 0x9a, 0x9b, 0x9d, 0x9e, 0x9f, 0xa6,
0x00, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32, 0xa7, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb2, 0xb3,
0x00, 0x00, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0xb4, 0xb5, 0xb6, 0xb7, 0xb9, 0xba, 0xbb, 0xbc,
0x00, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F]; 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
];
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
];
function _debug() { function _debug() {
// debug.apply(this, arguments); console.log.apply(this, arguments);
} }
function _init() { function _init() {
@ -133,7 +156,7 @@ function DiskII(io, slot, callbacks)
return ((xx << 1) | 0x01) & yy; return ((xx << 1) | 0x01) & yy;
} }
function _explodeSector(volume, track, sector, data) { function _explodeSector16(volume, track, sector, data) {
var checksum; var checksum;
var buf = [], idx; var buf = [], idx;
@ -209,10 +232,113 @@ function DiskII(io, slot, callbacks)
var last = 0; var last = 0;
for (idx = 0; idx < 0x156; idx++) { for (idx = 0; idx < 0x156; idx++) {
var val = nibbles[idx]; var val = nibbles[idx];
buf.push(_trans[last ^ val]); buf.push(_trans62[last ^ val]);
last = val; last = val;
} }
buf.push(_trans[last]); buf.push(_trans62[last]);
buf = buf.concat([0xde, 0xaa, 0xf2]); // Epilog DE AA F2
/*
* Gap 3
*/
buf.push(0xff);
return buf;
}
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 buf = buf.concat([0xde, 0xaa, 0xeb]); // Epilog DE AA EB
@ -240,12 +366,12 @@ function DiskII(io, slot, callbacks)
} }
} }
} }
return JSON.stringify({'type': format, return JSON.stringify({
'encoding': 'base64', 'type': format,
'volume': cur.volume, 'encoding': 'base64',
'data': data}, 'volume': cur.volume,
null, 'data': data
pretty ? ' ' : null); }, null, pretty ? ' ' : null);
} }
function _json_decode(drive, data) { function _json_decode(drive, data) {
@ -258,7 +384,7 @@ function DiskII(io, slot, callbacks)
for (var s = 0; s < json.data[t].length; s++) { for (var s = 0; s < json.data[t].length; s++) {
var _s = 15 - s; var _s = 15 - s;
var d = base64_decode(json.data[t][_s]); var d = base64_decode(json.data[t][_s]);
track = track.concat(_explodeSector(v, t, _DO[_s], d)); track = track.concat(_explodeSector16(v, t, _DO[_s], d));
} }
tracks[t] = bytify(track); tracks[t] = bytify(track);
} }
@ -267,33 +393,31 @@ function DiskII(io, slot, callbacks)
_cur.tracks = tracks; _cur.tracks = tracks;
} }
function _readNext() { function _readWriteNext() {
var result = 0;
if (_skip || _writeMode) { if (_skip || _writeMode) {
var t = _cur.tracks[_cur.track >> 1]; var track = _cur.tracks[_cur.track >> 1];
if (t && t.length) { if (track && track.length) {
if (_cur.head >= t.length) if (_cur.head >= track.length) {
_cur.head = 0; _cur.head = 0;
}
if (_writeMode) { if (_writeMode) {
t[_cur.head] = _latch; if (!_cur.readOnly) {
} else track[_cur.head] = _latch;
result = t[_cur.head]; if (!_cur.dirty) {
_updateDirty(_drive, true);
}
}
} else {
_latch = track[_cur.head];
}
++_cur.head; ++_cur.head;
} }
} else {
_latch = 0;
} }
_skip = (++_skip % 4); _skip = (++_skip % 2);
return result;
}
function _writeNext(val) {
if (_writeMode) {
_latch = val;
if (!_cur.dirty) {
_updateDirty(_drive, true);
}
}
} }
function _readSector(drive, track, sector) { function _readSector(drive, track, sector) {
@ -345,12 +469,12 @@ function DiskII(io, slot, callbacks)
var data2 = []; var data2 = [];
var last = 0; var last = 0;
for (jdx = 0x55; jdx >= 0; jdx--) { for (jdx = 0x55; jdx >= 0; jdx--) {
val = _detrans[_readNext() - 0x80] ^ last; val = _detrans62[_readNext() - 0x80] ^ last;
data2[jdx] = val; data2[jdx] = val;
last = val; last = val;
} }
for (jdx = 0; jdx < 0x100; jdx++) { for (jdx = 0; jdx < 0x100; jdx++) {
val = _detrans[_readNext() - 0x80] ^ last; val = _detrans62[_readNext() - 0x80] ^ last;
data[jdx] = val; data[jdx] = val;
last = val; last = val;
} }
@ -388,7 +512,7 @@ function DiskII(io, slot, callbacks)
[ 1,-2,-1, 0]]; [ 1,-2,-1, 0]];
function setPhase(phase, on) { function setPhase(phase, on) {
_debug('phase ' + phase + (on ? ' on' : ' off')); // _debug('phase ' + phase + (on ? ' on' : ' off'));
if (on) { if (on) {
_cur.track += _phase_delta[_cur.phase][phase]; _cur.track += _phase_delta[_cur.phase][phase];
_cur.phase = phase; _cur.phase = phase;
@ -407,44 +531,46 @@ function DiskII(io, slot, callbacks)
function _access(off, val) { function _access(off, val) {
var result = 0; var result = 0;
var readMode = val === undefined;
switch (off & 0x8f) { switch (off & 0x8f) {
case LOC.PHASE0OFF: case LOC.PHASE0OFF: // 0x00
setPhase(0, false); setPhase(0, false);
break; break;
case LOC.PHASE0ON: case LOC.PHASE0ON: // 0x01
setPhase(0, true); setPhase(0, true);
break; break;
case LOC.PHASE1OFF: case LOC.PHASE1OFF: // 0x02
setPhase(1, false); setPhase(1, false);
break; break;
case LOC.PHASE1ON: case LOC.PHASE1ON: // 0x03
setPhase(1, true); setPhase(1, true);
break; break;
case LOC.PHASE2OFF: case LOC.PHASE2OFF: // 0x04
setPhase(2, false); setPhase(2, false);
break; break;
case LOC.PHASE2ON: case LOC.PHASE2ON: // 0x05
setPhase(2, true); setPhase(2, true);
break; break;
case LOC.PHASE3OFF: case LOC.PHASE3OFF: // 0x06
setPhase(3, false); setPhase(3, false);
break; break;
case LOC.PHASE3ON: case LOC.PHASE3ON: // 0x07
setPhase(3, true); setPhase(3, true);
break; break;
case LOC.DRIVEOFF: case LOC.DRIVEOFF: // 0x08
_debug('Drive Off'); _debug('Drive Off');
_on = false; _on = false;
if (callbacks.driveLight) { callbacks.driveLight(_drive, false); } if (callbacks.driveLight) { callbacks.driveLight(_drive, false); }
break; break;
case LOC.DRIVEON: case LOC.DRIVEON: // 0x09
_debug('Drive On'); _debug('Drive On');
_on = true; _on = true;
if (callbacks.driveLight) { callbacks.driveLight(_drive, true); } if (callbacks.driveLight) { callbacks.driveLight(_drive, true); }
break; break;
case LOC.DRIVE1: case LOC.DRIVE1: // 0x0a
_debug('Disk 1'); _debug('Disk 1');
_drive = 1; _drive = 1;
_cur = _drives[_drive - 1]; _cur = _drives[_drive - 1];
@ -453,7 +579,7 @@ function DiskII(io, slot, callbacks)
callbacks.driveLight(1, true); callbacks.driveLight(1, true);
} }
break; break;
case LOC.DRIVE2: case LOC.DRIVE2: // 0x0b
_debug('Disk 2'); _debug('Disk 2');
_drive = 2; _drive = 2;
_cur = _drives[_drive - 1]; _cur = _drives[_drive - 1];
@ -463,29 +589,46 @@ function DiskII(io, slot, callbacks)
} }
break; break;
case LOC.DRIVEREAD: case LOC.DRIVEREAD: // 0x0c
result = _readNext(); _readWriteNext();
// _debug('read: ' + toHex(result));
break; break;
case LOC.DRIVEWRITE: case LOC.DRIVEWRITE: // 0x0d
// _debug('write: ' + toHex(val)); if (readMode && !_writeMode) {
if (val !== undefined) { if (_cur.readOnly) {
_writeNext(val); _latch = _latch | 0x80;
_debug('Setting readOnly');
} else {
_latch = _latch & 0x7f;
_debug('Clearing readOnly');
}
} }
break; break;
case LOC.DRIVEREADMODE:
case LOC.DRIVEREADMODE: // 0x0e
_debug('Read Mode'); _debug('Read Mode');
_writeMode = false; _writeMode = false;
result = (_readNext() & 0x7f) | (_cur.readOnly ? 0x80 : 0x00);
break; break;
case LOC.DRIVEWRITEMODE: case LOC.DRIVEWRITEMODE: // 0x0f
_debug('Write Mode'); _debug('Write Mode');
_writeMode = true; _writeMode = true;
break; break;
default: default:
break; break;
} }
if (readMode) {
if ((off & 0x01) === 0) {
result = _latch;
// _debug('Read', toHex(result));
} else {
result = 0;
}
} else if (_writeMode) {
_latch = val;
}
return result; return result;
} }
@ -563,7 +706,6 @@ function DiskII(io, slot, callbacks)
0x03,0x4c,0x01,0x03,0x4c,0x2d,0xff,0xff 0x03,0x4c,0x01,0x03,0x4c,0x2d,0xff,0xff
]; ];
*/ */
_init(); _init();
return { return {
@ -645,12 +787,13 @@ function DiskII(io, slot, callbacks)
_cur = _drives[_drive - 1]; _cur = _drives[_drive - 1];
}, },
rwts: function disk2_rwts(disk, track, sector) { rwts: function disk2_rwts(disk, track, sector) {
return _readSector(disk, track, sector); var s = _drives[disk - 1].fmt == 'po' ? _PO[sector] : _DO[sector];
return _readSector(disk, track, s);
}, },
setDisk: function disk2_setDisk(drive, disk) { setDisk: function disk2_setDisk(drive, disk) {
var fmt = disk.type, readOnly = disk.readOnly; var fmt = disk.type, readOnly = disk.readOnly;
var data, t, s; var data, t, s, _s;
if (disk.encoding == 'base64') { if (disk.encoding == 'base64') {
data = []; data = [];
for (t = 0; t < disk.data.length; t++) { for (t = 0; t < disk.data.length; t++) {
@ -682,20 +825,26 @@ function DiskII(io, slot, callbacks)
var track = []; var track = [];
if (fmt === 'nib') { if (fmt === 'nib') {
track = data[t]; track = data[t];
} else if (fmt === 'd13') { // DOS 3.2 Order
for (s = 0; s < data[t].length; s++) {
track = track.concat(
_explodeSector13(v, t, _D13O[s], data[t][_D13O[s]])
);
}
} else { } else {
for (s = 0; s < data[t].length; s++) { for (s = 0; s < data[t].length; s++) {
var _s = 15 - s; _s = 15 - s;
if (fmt === 'po') { // ProDOS Order if (fmt === 'po') { // ProDOS Order
track = track.concat( track = track.concat(
_explodeSector(v, t, _PO[s], data[t][s]) _explodeSector16(v, t, _PO[s], data[t][s])
); );
} else if (fmt === 'dsk') { // DOS Order } else if (fmt === 'dsk') { // DOS 3.3 Order
track = track.concat( track = track.concat(
_explodeSector(v, t, _DO[_s], data[t][_s]) _explodeSector16(v, t, _DO[_s], data[t][_s])
); );
} else { // flat } else { // flat
track = track.concat( track = track.concat(
_explodeSector(v, t, s, data[t][s]) _explodeSector16(v, t, s, data[t][s])
); );
} }
} }
@ -752,25 +901,34 @@ function DiskII(io, slot, callbacks)
} }
} }
for (var t = 0; t < 35; t++) { for (var t = 0; t < 35; t++) {
var track, off, d; var track, off, d, s, _s;
if (fmt === 'nib') { if (fmt === 'nib') {
off = t * 0x1a00; off = t * 0x1a00;
track = new Uint8Array(data.slice(off, off + 0x1a00)); track = new Uint8Array(data.slice(off, off + 0x1a00));
} else if (fmt == 'd13') { // DOS 3.2 Order
track = [];
for (s = 0; s < 13; s++) {
off = (13 * t + _D13O[s]) * 256;
d = new Uint8Array(data.slice(off, off + 256));
track = track.concat(
_explodeSector13(v, t, _D13O[s], d)
);
}
} else { } else {
track = []; track = [];
for (var s = 0; s < 16; s++) { for (s = 0; s < 16; s++) {
var _s = 15 - s; _s = 15 - s;
if (fmt == 'po') { // ProDOS Order if (fmt == 'po') { // ProDOS Order
off = (16 * t + s) * 256; off = (16 * t + s) * 256;
d = new Uint8Array(data.slice(off, off + 256)); d = new Uint8Array(data.slice(off, off + 256));
track = track.concat( track = track.concat(
_explodeSector(v, t, _PO[s], d) _explodeSector16(v, t, _PO[s], d)
); );
} else if (fmt == 'dsk') { // DOS Order } else if (fmt == 'dsk') { // DOS 3.3 Order
off = (16 * t + _s) * 256; off = (16 * t + _s) * 256;
d = new Uint8Array(data.slice(off, off + 256)); d = new Uint8Array(data.slice(off, off + 256));
track = track.concat( track = track.concat(
_explodeSector(v, t, _DO[_s], d) _explodeSector16(v, t, _DO[_s], d)
); );
} else { } else {
return false; return false;