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,24 +72,46 @@ 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 = [
// 0x0, 0xa, 0x7, 0x4, 0x1, 0xb, 0x8, 0x5, 0x2, 0xc, 0x9, 0x6, 0x3
// ];
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, 0xa7, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb2, 0xb3,
0xb4, 0xb5, 0xb6, 0xb7, 0xb9, 0xba, 0xbb, 0xbc, 0xb4, 0xb5, 0xb6, 0xb7, 0xb9, 0xba, 0xbb, 0xbc,
0xbd, 0xbe, 0xbf, 0xcb, 0xcd, 0xce, 0xcf, 0xd3, 0xbd, 0xbe, 0xbf, 0xcb, 0xcd, 0xce, 0xcf, 0xd3,
0xd6, 0xd7, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xd6, 0xd7, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde,
0xdf, 0xe5, 0xe6, 0xe7, 0xe9, 0xea, 0xeb, 0xec, 0xdf, 0xe5, 0xe6, 0xe7, 0xe9, 0xea, 0xeb, 0xec,
0xed, 0xee, 0xef, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xed, 0xee, 0xef, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6,
0xf7, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff]; 0xf7, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
];
var _detrans = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 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, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x02, 0x03, 0x00, 0x04, 0x05, 0x06, 0x00, 0x00, 0x02, 0x03, 0x00, 0x04, 0x05, 0x06,
@ -104,10 +126,11 @@ function DiskII(io, slot, callbacks)
0x00, 0x00, 0x00, 0x00, 0x00, 0x29, 0x2A, 0x2B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x29, 0x2A, 0x2B,
0x00, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x00, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32,
0x00, 0x00, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x00, 0x00, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38,
0x00, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F]; 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({
'type': format,
'encoding': 'base64', 'encoding': 'base64',
'volume': cur.volume, 'volume': cur.volume,
'data': data}, 'data': data
null, }, null, pretty ? ' ' : null);
pretty ? ' ' : null);
} }
function _json_decode(drive, data) { function _json_decode(drive, data) {
@ -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];
++_cur.head;
}
}
_skip = (++_skip % 4);
return result;
}
function _writeNext(val) {
if (_writeMode) {
_latch = val;
if (!_cur.dirty) { if (!_cur.dirty) {
_updateDirty(_drive, true); _updateDirty(_drive, true);
} }
} }
} else {
_latch = track[_cur.head];
}
++_cur.head;
}
} else {
_latch = 0;
}
_skip = (++_skip % 2);
} }
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;