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 */
/*globals bytify: false, debug: false
/*globals bytify: false, debug: false, toHex: false
base64_decode: false, base64_encode: false
Uint8Array: false
*/
@ -72,42 +72,65 @@ function DiskII(io, slot, callbacks)
// var DO = [0x0,0x7,0xE,0x6,0xD,0x5,0xC,0x4,
// 0xB,0x3,0xA,0x2,0x9,0x1,0x8,0xF];
var _DO = [0x0,0xD,0xB,0x9,0x7,0x5,0x3,0x1,
0xE,0xC,0xA,0x8,0x6,0x4,0x2,0xF];
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];
var _PO = [0x0,0x2,0x4,0x6,0x8,0xa,0xc,0xe,
0x1,0x3,0x5,0x7,0x9,0xb,0xd,0xf];
var _PO = [
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,
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];
// var D13O = [
// 0x0, 0xa, 0x7, 0x4, 0x1, 0xb, 0x8, 0x5, 0x2, 0xc, 0x9, 0x6, 0x3
// ];
var _detrans = [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];
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
];
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() {
// debug.apply(this, arguments);
console.log.apply(this, arguments);
}
function _init() {
@ -133,7 +156,7 @@ function DiskII(io, slot, callbacks)
return ((xx << 1) | 0x01) & yy;
}
function _explodeSector(volume, track, sector, data) {
function _explodeSector16(volume, track, sector, data) {
var checksum;
var buf = [], idx;
@ -209,10 +232,113 @@ function DiskII(io, slot, callbacks)
var last = 0;
for (idx = 0; idx < 0x156; idx++) {
var val = nibbles[idx];
buf.push(_trans[last ^ val]);
buf.push(_trans62[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
@ -240,12 +366,12 @@ function DiskII(io, slot, callbacks)
}
}
}
return JSON.stringify({'type': format,
'encoding': 'base64',
'volume': cur.volume,
'data': data},
null,
pretty ? ' ' : null);
return JSON.stringify({
'type': format,
'encoding': 'base64',
'volume': cur.volume,
'data': data
}, null, pretty ? ' ' : null);
}
function _json_decode(drive, data) {
@ -258,7 +384,7 @@ function DiskII(io, slot, callbacks)
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(_explodeSector(v, t, _DO[_s], d));
track = track.concat(_explodeSector16(v, t, _DO[_s], d));
}
tracks[t] = bytify(track);
}
@ -267,33 +393,31 @@ function DiskII(io, slot, callbacks)
_cur.tracks = tracks;
}
function _readNext() {
var result = 0;
function _readWriteNext() {
if (_skip || _writeMode) {
var t = _cur.tracks[_cur.track >> 1];
if (t && t.length) {
if (_cur.head >= t.length)
var track = _cur.tracks[_cur.track >> 1];
if (track && track.length) {
if (_cur.head >= track.length) {
_cur.head = 0;
}
if (_writeMode) {
t[_cur.head] = _latch;
} else
result = t[_cur.head];
if (!_cur.readOnly) {
track[_cur.head] = _latch;
if (!_cur.dirty) {
_updateDirty(_drive, true);
}
}
} else {
_latch = track[_cur.head];
}
++_cur.head;
}
} else {
_latch = 0;
}
_skip = (++_skip % 4);
return result;
}
function _writeNext(val) {
if (_writeMode) {
_latch = val;
if (!_cur.dirty) {
_updateDirty(_drive, true);
}
}
_skip = (++_skip % 2);
}
function _readSector(drive, track, sector) {
@ -345,12 +469,12 @@ function DiskII(io, slot, callbacks)
var data2 = [];
var last = 0;
for (jdx = 0x55; jdx >= 0; jdx--) {
val = _detrans[_readNext() - 0x80] ^ last;
val = _detrans62[_readNext() - 0x80] ^ last;
data2[jdx] = val;
last = val;
}
for (jdx = 0; jdx < 0x100; jdx++) {
val = _detrans[_readNext() - 0x80] ^ last;
val = _detrans62[_readNext() - 0x80] ^ last;
data[jdx] = val;
last = val;
}
@ -388,7 +512,7 @@ function DiskII(io, slot, callbacks)
[ 1,-2,-1, 0]];
function setPhase(phase, on) {
_debug('phase ' + phase + (on ? ' on' : ' off'));
// _debug('phase ' + phase + (on ? ' on' : ' off'));
if (on) {
_cur.track += _phase_delta[_cur.phase][phase];
_cur.phase = phase;
@ -407,44 +531,46 @@ function DiskII(io, slot, callbacks)
function _access(off, val) {
var result = 0;
var readMode = val === undefined;
switch (off & 0x8f) {
case LOC.PHASE0OFF:
case LOC.PHASE0OFF: // 0x00
setPhase(0, false);
break;
case LOC.PHASE0ON:
case LOC.PHASE0ON: // 0x01
setPhase(0, true);
break;
case LOC.PHASE1OFF:
case LOC.PHASE1OFF: // 0x02
setPhase(1, false);
break;
case LOC.PHASE1ON:
case LOC.PHASE1ON: // 0x03
setPhase(1, true);
break;
case LOC.PHASE2OFF:
case LOC.PHASE2OFF: // 0x04
setPhase(2, false);
break;
case LOC.PHASE2ON:
case LOC.PHASE2ON: // 0x05
setPhase(2, true);
break;
case LOC.PHASE3OFF:
case LOC.PHASE3OFF: // 0x06
setPhase(3, false);
break;
case LOC.PHASE3ON:
case LOC.PHASE3ON: // 0x07
setPhase(3, true);
break;
case LOC.DRIVEOFF:
case LOC.DRIVEOFF: // 0x08
_debug('Drive Off');
_on = false;
if (callbacks.driveLight) { callbacks.driveLight(_drive, false); }
break;
case LOC.DRIVEON:
case LOC.DRIVEON: // 0x09
_debug('Drive On');
_on = true;
if (callbacks.driveLight) { callbacks.driveLight(_drive, true); }
break;
case LOC.DRIVE1:
case LOC.DRIVE1: // 0x0a
_debug('Disk 1');
_drive = 1;
_cur = _drives[_drive - 1];
@ -453,7 +579,7 @@ function DiskII(io, slot, callbacks)
callbacks.driveLight(1, true);
}
break;
case LOC.DRIVE2:
case LOC.DRIVE2: // 0x0b
_debug('Disk 2');
_drive = 2;
_cur = _drives[_drive - 1];
@ -463,29 +589,46 @@ function DiskII(io, slot, callbacks)
}
break;
case LOC.DRIVEREAD:
result = _readNext();
// _debug('read: ' + toHex(result));
case LOC.DRIVEREAD: // 0x0c
_readWriteNext();
break;
case LOC.DRIVEWRITE:
// _debug('write: ' + toHex(val));
if (val !== undefined) {
_writeNext(val);
case LOC.DRIVEWRITE: // 0x0d
if (readMode && !_writeMode) {
if (_cur.readOnly) {
_latch = _latch | 0x80;
_debug('Setting readOnly');
} else {
_latch = _latch & 0x7f;
_debug('Clearing readOnly');
}
}
break;
case LOC.DRIVEREADMODE:
case LOC.DRIVEREADMODE: // 0x0e
_debug('Read Mode');
_writeMode = false;
result = (_readNext() & 0x7f) | (_cur.readOnly ? 0x80 : 0x00);
break;
case LOC.DRIVEWRITEMODE:
case LOC.DRIVEWRITEMODE: // 0x0f
_debug('Write Mode');
_writeMode = true;
break;
default:
break;
}
if (readMode) {
if ((off & 0x01) === 0) {
result = _latch;
// _debug('Read', toHex(result));
} else {
result = 0;
}
} else if (_writeMode) {
_latch = val;
}
return result;
}
@ -563,7 +706,6 @@ function DiskII(io, slot, callbacks)
0x03,0x4c,0x01,0x03,0x4c,0x2d,0xff,0xff
];
*/
_init();
return {
@ -645,12 +787,13 @@ function DiskII(io, slot, callbacks)
_cur = _drives[_drive - 1];
},
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) {
var fmt = disk.type, readOnly = disk.readOnly;
var data, t, s;
var data, t, s, _s;
if (disk.encoding == 'base64') {
data = [];
for (t = 0; t < disk.data.length; t++) {
@ -682,20 +825,26 @@ function DiskII(io, slot, callbacks)
var track = [];
if (fmt === 'nib') {
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 {
for (s = 0; s < data[t].length; s++) {
var _s = 15 - s;
_s = 15 - s;
if (fmt === 'po') { // ProDOS Order
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(
_explodeSector(v, t, _DO[_s], data[t][_s])
_explodeSector16(v, t, _DO[_s], data[t][_s])
);
} else { // flat
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++) {
var track, off, d;
var track, off, d, s, _s;
if (fmt === 'nib') {
off = t * 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 {
track = [];
for (var s = 0; s < 16; s++) {
var _s = 15 - s;
for (s = 0; s < 16; s++) {
_s = 15 - s;
if (fmt == 'po') { // ProDOS Order
off = (16 * t + s) * 256;
d = new Uint8Array(data.slice(off, off + 256));
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;
d = new Uint8Array(data.slice(off, off + 256));
track = track.concat(
_explodeSector(v, t, _DO[_s], d)
_explodeSector16(v, t, _DO[_s], d)
);
} else {
return false;