mirror of
https://github.com/whscullin/apple2js.git
synced 2024-01-12 14:14:38 +00:00
3b95726655
Readonly for now.
758 lines
24 KiB
JavaScript
758 lines
24 KiB
JavaScript
/* 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 { debug, toHex } from '../util';
|
|
import { jsonDecode, jsonEncode, readSector } from '../formats/format_utils';
|
|
|
|
import _2MG from '../formats/2mg';
|
|
import D13 from '../formats/d13';
|
|
import DOS from '../formats/do';
|
|
import ProDOS from '../formats/po';
|
|
import Woz from '../formats/woz';
|
|
import Nibble from '../formats/nib';
|
|
|
|
export const DISK_TYPES = [
|
|
'2mg',
|
|
'd13',
|
|
'do',
|
|
'dsk',
|
|
'po',
|
|
'nib',
|
|
'woz'
|
|
];
|
|
|
|
export default function DiskII(io, slot, callbacks, sectors = 16)
|
|
{
|
|
slot = slot || 6;
|
|
var _drives = [
|
|
{ // Drive 1
|
|
format: 'dsk',
|
|
volume: 254,
|
|
tracks: [],
|
|
trackMap: null,
|
|
rawTracks: null,
|
|
track: 0,
|
|
head: 0,
|
|
phase: 0,
|
|
readOnly: false,
|
|
dirty: false
|
|
},
|
|
{ // Drive 2
|
|
format: 'dsk',
|
|
volume: 254,
|
|
tracks: [],
|
|
trackMap: null,
|
|
rawTracks: null,
|
|
track: 0,
|
|
head: 0,
|
|
phase: 0,
|
|
readOnly: false,
|
|
dirty: false
|
|
}];
|
|
|
|
var _skip = 0;
|
|
var _bus = 0;
|
|
var _latch = 0;
|
|
var _offTimeout = null;
|
|
var _q6 = 0;
|
|
var _q7 = 0;
|
|
var _writeMode = false; // q7
|
|
var _on = false;
|
|
var _drive = 1;
|
|
var _cur = _drives[_drive - 1];
|
|
|
|
var LOC = {
|
|
// Disk II Stuff
|
|
PHASE0OFF: 0x80,
|
|
PHASE0ON: 0x81,
|
|
PHASE1OFF: 0x82,
|
|
PHASE1ON: 0x83,
|
|
PHASE2OFF: 0x84,
|
|
PHASE2ON: 0x85,
|
|
PHASE3OFF: 0x86,
|
|
PHASE3ON: 0x87,
|
|
|
|
DRIVEOFF: 0x88,
|
|
DRIVEON: 0x89,
|
|
DRIVE1: 0x8A,
|
|
DRIVE2: 0x8B,
|
|
DRIVEREAD: 0x8C, // Q6L
|
|
DRIVEWRITE: 0x8D, // Q6H
|
|
DRIVEREADMODE: 0x8E, // Q7L
|
|
DRIVEWRITEMODE: 0x8F // Q7H
|
|
};
|
|
|
|
|
|
// CODE OPERATION BEFORE AFTER
|
|
// 0 CLR XXXXXXXX 00000000
|
|
// 8 NOP ABCDEFGH ABCDEFGH
|
|
// 9 SL0 ABCDEFGH BCDEFGH0
|
|
// A SR (write protected) ABCDEFGH 11111111
|
|
// (not write protected) ABCDEFGH 0ABCDEFG
|
|
// B LOAD XXXXXXXX YYYYYYYY
|
|
// D SL1 ABCDEFGH BCDEFGH1
|
|
|
|
// Q7 Read/Write
|
|
// Q6 Shift/Load
|
|
|
|
var _P6 = [
|
|
// Q7 L (Read) Q7 H (Write)
|
|
// Q6 L Q6 H Q6 L (Shift) Q6 H (Load)
|
|
// QA L QA H QA L QA H QA L QA H QA L QA H
|
|
//1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0
|
|
0x18, 0x18, 0x18, 0x18, 0x0A, 0x0A, 0x0A, 0x0A, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, // 0
|
|
0x2D, 0x2D, 0x38, 0x38, 0x0A, 0x0A, 0x0A, 0x0A, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, // 1
|
|
0xD8, 0x38, 0x08, 0x28, 0x0A, 0x0A, 0x0A, 0x0A, 0x39, 0x39, 0x39, 0x39, 0x3B, 0x3B, 0x3B, 0x3B, // 2
|
|
0xD8, 0x48, 0x48, 0x48, 0x0A, 0x0A, 0x0A, 0x0A, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, // 3
|
|
0xD8, 0x58, 0xD8, 0x58, 0x0A, 0x0A, 0x0A, 0x0A, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, // 4
|
|
0xD8, 0x68, 0xD8, 0x68, 0x0A, 0x0A, 0x0A, 0x0A, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, // 5
|
|
0xD8, 0x78, 0xD8, 0x78, 0x0A, 0x0A, 0x0A, 0x0A, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, // 6
|
|
0xD8, 0x88, 0xD8, 0x88, 0x0A, 0x0A, 0x0A, 0x0A, 0x08, 0x08, 0x88, 0x88, 0x08, 0x08, 0x88, 0x88, // 7
|
|
0xD8, 0x98, 0xD8, 0x98, 0x0A, 0x0A, 0x0A, 0x0A, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, // 8
|
|
0xD8, 0x29, 0xD8, 0xA8, 0x0A, 0x0A, 0x0A, 0x0A, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, // 9
|
|
0xCD, 0xBD, 0xD8, 0xB8, 0x0A, 0x0A, 0x0A, 0x0A, 0xB9, 0xB9, 0xB9, 0xB9, 0xBB, 0xBB, 0xBB, 0xBB, // A
|
|
0xD9, 0x59, 0xD8, 0xC8, 0x0A, 0x0A, 0x0A, 0x0A, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, // B
|
|
0xD9, 0xD9, 0xD8, 0xA0, 0x0A, 0x0A, 0x0A, 0x0A, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, // C
|
|
0xD8, 0x08, 0xE8, 0xE8, 0x0A, 0x0A, 0x0A, 0x0A, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, // D
|
|
0xFD, 0xFD, 0xF8, 0xF8, 0x0A, 0x0A, 0x0A, 0x0A, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, // E
|
|
0xDD, 0x4D, 0xE0, 0xE0, 0x0A, 0x0A, 0x0A, 0x0A, 0x88, 0x88, 0x08, 0x08, 0x88, 0x88, 0x08, 0x08 // F
|
|
];
|
|
|
|
function _debug() {
|
|
// debug.apply(this, arguments);
|
|
}
|
|
|
|
function _init() {
|
|
debug('Disk ][ in slot', slot);
|
|
}
|
|
|
|
var _clock = 0;
|
|
var _lastCycles = io.cycles();
|
|
var _state = 0;
|
|
var _zeros = 0;
|
|
|
|
function _moveHead() {
|
|
if (!_cur.rawTracks) {
|
|
return;
|
|
}
|
|
var track = _cur.rawTracks[_cur.trackMap[_cur.track]] || [0];
|
|
|
|
var cycles = io.cycles();
|
|
var workCycles = (cycles - _lastCycles) * 2;
|
|
_lastCycles = cycles;
|
|
|
|
while (workCycles-- > 0) {
|
|
var pulse = 0;
|
|
if (_clock == 4) {
|
|
pulse = track[_cur.head];
|
|
if (!pulse) {
|
|
if (++_zeros > 2) {
|
|
pulse = Math.random() > 0.5 ? 1 : 0;
|
|
}
|
|
} else {
|
|
_zeros = 0;
|
|
}
|
|
}
|
|
|
|
var idx = 0;
|
|
idx |= pulse ? 0x00 : 0x01;
|
|
idx |= _latch & 0x80 ? 0x02 : 0x00;
|
|
idx |= _q6 ? 0x04 : 0x00;
|
|
idx |= _q7 ? 0x08 : 0x00;
|
|
idx |= _state << 4;
|
|
|
|
var command = _P6[idx];
|
|
|
|
if (_on && _q7) {
|
|
debug('clock:', _clock, 'command:', toHex(command), 'q6:', _q6);
|
|
}
|
|
|
|
switch (command & 0xf) {
|
|
case 0x0: // CLR
|
|
_latch = 0;
|
|
break;
|
|
case 0x8: // NOP
|
|
break;
|
|
case 0x9: // SL0
|
|
_latch = (_latch << 1) & 0xff;
|
|
break;
|
|
case 0xA: // SR
|
|
_latch >>= 1;
|
|
if (_cur.readOnly) {
|
|
_latch |= 0x80;
|
|
}
|
|
break;
|
|
case 0xB: // LD
|
|
_latch = _bus;
|
|
debug('Loading', toHex(_latch), 'from bus');
|
|
break;
|
|
case 0xD: // SL1
|
|
_latch = ((_latch << 1) | 0x01) & 0xff;
|
|
break;
|
|
}
|
|
_state = command >> 4;
|
|
|
|
if (_clock == 4) {
|
|
if (_on) {
|
|
if (_q7) {
|
|
track[_cur.head] = _state & 0x8 ? 0x01 : 0x00;
|
|
debug('Wrote', _state & 0x8 ? 0x01 : 0x00);
|
|
}
|
|
|
|
if (++_cur.head >= track.length) {
|
|
_cur.head = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (++_clock > 7) {
|
|
_clock = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
function _readWriteNext() {
|
|
if (_skip || _writeMode) {
|
|
var track = _cur.tracks[_cur.track >> 2];
|
|
if (track && track.length) {
|
|
if (_cur.head >= track.length) {
|
|
_cur.head = 0;
|
|
}
|
|
|
|
if (_writeMode) {
|
|
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 % 2);
|
|
}
|
|
|
|
var _phase_delta = [
|
|
[ 0, 1, 2,-1],
|
|
[-1, 0, 1, 2],
|
|
[-2,-1, 0, 1],
|
|
[ 1,-2,-1, 0]
|
|
];
|
|
|
|
var _q = [false, false, false, false]; // q0-3
|
|
|
|
function setPhase(phase, on) {
|
|
_debug('phase ' + phase + (on ? ' on' : ' off'));
|
|
if (_cur.rawTracks) {
|
|
if (on) {
|
|
var delta = _phase_delta[_cur.phase][phase] * 2;
|
|
_cur.track += delta;
|
|
_cur.phase = phase;
|
|
} else {
|
|
// foo
|
|
}
|
|
} else {
|
|
if (on) {
|
|
_cur.track += _phase_delta[_cur.phase][phase] * 2;
|
|
_cur.phase = phase;
|
|
}
|
|
}
|
|
|
|
if (_cur.track > _cur.tracks.length * 4 - 1) {
|
|
_cur.track = _cur.tracks.length * 4 - 1;
|
|
}
|
|
if (_cur.track < 0x0) {
|
|
_cur.track = 0x0;
|
|
}
|
|
|
|
// debug(
|
|
// 'Drive', _drive, 'track', toHex(_cur.track >> 2) + '.' + (_cur.track & 0x3),
|
|
// '(' + toHex(_cur.track) + ')',
|
|
// '[' + phase + ':' + (on ? 'on' : 'off') + ']');
|
|
|
|
_q[phase] = on;
|
|
}
|
|
|
|
function _access(off, val) {
|
|
var result = 0;
|
|
var readMode = val === undefined;
|
|
|
|
switch (off & 0x8f) {
|
|
case LOC.PHASE0OFF: // 0x00
|
|
setPhase(0, false);
|
|
break;
|
|
case LOC.PHASE0ON: // 0x01
|
|
setPhase(0, true);
|
|
break;
|
|
case LOC.PHASE1OFF: // 0x02
|
|
setPhase(1, false);
|
|
break;
|
|
case LOC.PHASE1ON: // 0x03
|
|
setPhase(1, true);
|
|
break;
|
|
case LOC.PHASE2OFF: // 0x04
|
|
setPhase(2, false);
|
|
break;
|
|
case LOC.PHASE2ON: // 0x05
|
|
setPhase(2, true);
|
|
break;
|
|
case LOC.PHASE3OFF: // 0x06
|
|
setPhase(3, false);
|
|
break;
|
|
case LOC.PHASE3ON: // 0x07
|
|
setPhase(3, true);
|
|
break;
|
|
|
|
case LOC.DRIVEOFF: // 0x08
|
|
if (!_offTimeout) {
|
|
if (_on) {
|
|
_offTimeout = window.setTimeout(function() {
|
|
_debug('Drive Off');
|
|
_on = false;
|
|
if (callbacks.driveLight) { callbacks.driveLight(_drive, false); }
|
|
}, 1000);
|
|
}
|
|
}
|
|
break;
|
|
case LOC.DRIVEON: // 0x09
|
|
if (_offTimeout) {
|
|
window.clearTimeout(_offTimeout);
|
|
_offTimeout = null;
|
|
}
|
|
if (!_on) {
|
|
_debug('Drive On');
|
|
_on = true;
|
|
_lastCycles = io.cycles();
|
|
if (callbacks.driveLight) { callbacks.driveLight(_drive, true); }
|
|
}
|
|
break;
|
|
|
|
case LOC.DRIVE1: // 0x0a
|
|
_debug('Disk 1');
|
|
_drive = 1;
|
|
_cur = _drives[_drive - 1];
|
|
if (_on && callbacks.driveLight) {
|
|
callbacks.driveLight(2, false);
|
|
callbacks.driveLight(1, true);
|
|
}
|
|
break;
|
|
case LOC.DRIVE2: // 0x0b
|
|
_debug('Disk 2');
|
|
_drive = 2;
|
|
_cur = _drives[_drive - 1];
|
|
if (_on && callbacks.driveLight) {
|
|
callbacks.driveLight(1, false);
|
|
callbacks.driveLight(2, true);
|
|
}
|
|
break;
|
|
|
|
case LOC.DRIVEREAD: // 0x0c (Q6L) Shift
|
|
_q6 = 0;
|
|
if (_writeMode) {
|
|
debug('clearing _q6/SHIFT');
|
|
}
|
|
if (!_cur.rawTracks) {
|
|
_readWriteNext();
|
|
}
|
|
break;
|
|
|
|
case LOC.DRIVEWRITE: // 0x0d (Q6H) LOAD
|
|
_q6 = 1;
|
|
if (_writeMode) {
|
|
debug('setting _q6/LOAD');
|
|
}
|
|
if (!_cur.rawTracks) {
|
|
if (readMode && !_writeMode) {
|
|
if (_cur.readOnly) {
|
|
_latch = 0xff;
|
|
_debug('Setting readOnly');
|
|
} else {
|
|
_latch = _latch >> 1;
|
|
_debug('Clearing readOnly');
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case LOC.DRIVEREADMODE: // 0x0e (Q7L)
|
|
_debug('Read Mode');
|
|
_q7 = 0;
|
|
_writeMode = false;
|
|
break;
|
|
case LOC.DRIVEWRITEMODE: // 0x0f (Q7H)
|
|
_debug('Write Mode');
|
|
_q7 = 1;
|
|
_writeMode = true;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
_moveHead();
|
|
|
|
if (readMode) {
|
|
if ((off & 0x01) === 0) {
|
|
result = _latch;
|
|
} else {
|
|
result = 0;
|
|
}
|
|
} else {
|
|
_bus = val;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
function _updateDirty(drive, dirty) {
|
|
_drives[drive - 1].dirty = dirty;
|
|
if (callbacks.dirty) { callbacks.dirty(_drive, dirty); }
|
|
}
|
|
|
|
var _P5_16 = [
|
|
0xa2,0x20,0xa0,0x00,0xa2,0x03,0x86,0x3c,
|
|
0x8a,0x0a,0x24,0x3c,0xf0,0x10,0x05,0x3c,
|
|
0x49,0xff,0x29,0x7e,0xb0,0x08,0x4a,0xd0,
|
|
0xfb,0x98,0x9d,0x56,0x03,0xc8,0xe8,0x10,
|
|
0xe5,0x20,0x58,0xff,0xba,0xbd,0x00,0x01,
|
|
0x0a,0x0a,0x0a,0x0a,0x85,0x2b,0xaa,0xbd,
|
|
0x8e,0xc0,0xbd,0x8c,0xc0,0xbd,0x8a,0xc0,
|
|
0xbd,0x89,0xc0,0xa0,0x50,0xbd,0x80,0xc0,
|
|
0x98,0x29,0x03,0x0a,0x05,0x2b,0xaa,0xbd,
|
|
0x81,0xc0,0xa9,0x56,0x20,0xa8,0xfc,0x88,
|
|
0x10,0xeb,0x85,0x26,0x85,0x3d,0x85,0x41,
|
|
0xa9,0x08,0x85,0x27,0x18,0x08,0xbd,0x8c,
|
|
0xc0,0x10,0xfb,0x49,0xd5,0xd0,0xf7,0xbd,
|
|
0x8c,0xc0,0x10,0xfb,0xc9,0xaa,0xd0,0xf3,
|
|
0xea,0xbd,0x8c,0xc0,0x10,0xfb,0xc9,0x96,
|
|
0xf0,0x09,0x28,0x90,0xdf,0x49,0xad,0xf0,
|
|
0x25,0xd0,0xd9,0xa0,0x03,0x85,0x40,0xbd,
|
|
0x8c,0xc0,0x10,0xfb,0x2a,0x85,0x3c,0xbd,
|
|
0x8c,0xc0,0x10,0xfb,0x25,0x3c,0x88,0xd0,
|
|
0xec,0x28,0xc5,0x3d,0xd0,0xbe,0xa5,0x40,
|
|
0xc5,0x41,0xd0,0xb8,0xb0,0xb7,0xa0,0x56,
|
|
0x84,0x3c,0xbc,0x8c,0xc0,0x10,0xfb,0x59,
|
|
0xd6,0x02,0xa4,0x3c,0x88,0x99,0x00,0x03,
|
|
0xd0,0xee,0x84,0x3c,0xbc,0x8c,0xc0,0x10,
|
|
0xfb,0x59,0xd6,0x02,0xa4,0x3c,0x91,0x26,
|
|
0xc8,0xd0,0xef,0xbc,0x8c,0xc0,0x10,0xfb,
|
|
0x59,0xd6,0x02,0xd0,0x87,0xa0,0x00,0xa2,
|
|
0x56,0xca,0x30,0xfb,0xb1,0x26,0x5e,0x00,
|
|
0x03,0x2a,0x5e,0x00,0x03,0x2a,0x91,0x26,
|
|
0xc8,0xd0,0xee,0xe6,0x27,0xe6,0x3d,0xa5,
|
|
0x3d,0xcd,0x00,0x08,0xa6,0x2b,0x90,0xdb,
|
|
0x4c,0x01,0x08,0x00,0x00,0x00,0x00,0x00 ];
|
|
|
|
var _P5_13 = [
|
|
0xa2,0x20,0xa0,0x00,0xa9,0x03,0x85,0x3c,
|
|
0x18,0x88,0x98,0x24,0x3c,0xf0,0xf5,0x26,
|
|
0x3c,0x90,0xf8,0xc0,0xd5,0xf0,0xed,0xca,
|
|
0x8a,0x99,0x00,0x08,0xd0,0xe6,0x20,0x58,
|
|
0xff,0xba,0xbd,0x00,0x01,0x48,0x0a,0x0a,
|
|
0x0a,0x0a,0x85,0x2b,0xaa,0xa9,0xd0,0x48,
|
|
0xbd,0x8e,0xc0,0xbd,0x8c,0xc0,0xbd,0x8a,
|
|
0xc0,0xbd,0x89,0xc0,0xa0,0x50,0xbd,0x80,
|
|
0xc0,0x98,0x29,0x03,0x0a,0x05,0x2b,0xaa,
|
|
0xbd,0x81,0xc0,0xa9,0x56,0x20,0xa8,0xfc,
|
|
0x88,0x10,0xeb,0xa9,0x03,0x85,0x27,0xa9,
|
|
0x00,0x85,0x26,0x85,0x3d,0x18,0x08,0xbd,
|
|
0x8c,0xc0,0x10,0xfb,0x49,0xd5,0xd0,0xf7,
|
|
0xbd,0x8c,0xc0,0x10,0xfb,0xc9,0xaa,0xd0,
|
|
0xf3,0xea,0xbd,0x8c,0xc0,0x10,0xfb,0xc9,
|
|
0xb5,0xf0,0x09,0x28,0x90,0xdf,0x49,0xad,
|
|
0xf0,0x1f,0xd0,0xd9,0xa0,0x03,0x84,0x2a,
|
|
0xbd,0x8c,0xc0,0x10,0xfb,0x2a,0x85,0x3c,
|
|
0xbd,0x8c,0xc0,0x10,0xfb,0x25,0x3c,0x88,
|
|
0xd0,0xee,0x28,0xc5,0x3d,0xd0,0xbe,0xb0,
|
|
0xbd,0xa0,0x9a,0x84,0x3c,0xbc,0x8c,0xc0,
|
|
0x10,0xfb,0x59,0x00,0x08,0xa4,0x3c,0x88,
|
|
0x99,0x00,0x08,0xd0,0xee,0x84,0x3c,0xbc,
|
|
0x8c,0xc0,0x10,0xfb,0x59,0x00,0x08,0xa4,
|
|
0x3c,0x91,0x26,0xc8,0xd0,0xef,0xbc,0x8c,
|
|
0xc0,0x10,0xfb,0x59,0x00,0x08,0xd0,0x8d,
|
|
0x60,0xa8,0xa2,0x00,0xb9,0x00,0x08,0x4a,
|
|
0x3e,0xcc,0x03,0x4a,0x3e,0x99,0x03,0x85,
|
|
0x3c,0xb1,0x26,0x0a,0x0a,0x0a,0x05,0x3c,
|
|
0x91,0x26,0xc8,0xe8,0xe0,0x33,0xd0,0xe4,
|
|
0xc6,0x2a,0xd0,0xde,0xcc,0x00,0x03,0xd0,
|
|
0x03,0x4c,0x01,0x03,0x4c,0x2d,0xff,0xff
|
|
];
|
|
|
|
var _P5 = sectors == 16 ? _P5_16 : _P5_13;
|
|
|
|
_init();
|
|
|
|
return {
|
|
ioSwitch: function disk2_ioSwitch(off, val) {
|
|
return _access(off, val);
|
|
},
|
|
|
|
read: function disk2_read(page, off) {
|
|
return _P5[off];
|
|
},
|
|
|
|
write: function disk2_write() {},
|
|
|
|
reset: function disk2_reset() {
|
|
if (_on) {
|
|
callbacks.driveLight(_drive, false);
|
|
_writeMode = false;
|
|
_on = false;
|
|
_drive = 1;
|
|
_cur = _drives[_drive - 1];
|
|
}
|
|
for (var idx = 0; idx < 4; idx++) {
|
|
_q[idx] = false;
|
|
}
|
|
},
|
|
|
|
tick: function disk2_tick() {
|
|
_moveHead();
|
|
},
|
|
|
|
getState: function disk2_getState() {
|
|
function getDriveState(drive) {
|
|
var result = {
|
|
format: drive.format,
|
|
volume: drive.volume,
|
|
tracks: [],
|
|
track: drive.track,
|
|
head: drive.head,
|
|
phase: drive.phase,
|
|
readOnly: drive.readOnly,
|
|
dirty: drive.dirty
|
|
};
|
|
for (var idx = 0; idx < drive.tracks.length; idx++) {
|
|
result.tracks.push(base64_encode(drive.tracks[idx]));
|
|
}
|
|
return result;
|
|
}
|
|
var result = {
|
|
drives: [],
|
|
skip: _skip,
|
|
latch: _latch,
|
|
writeMode: _writeMode,
|
|
on: _on,
|
|
drive: _drive
|
|
};
|
|
_drives.forEach(function(drive, idx) {
|
|
result.drives[idx] = getDriveState(drive);
|
|
});
|
|
|
|
return result;
|
|
},
|
|
|
|
setState: function disk2_setState(state) {
|
|
function setDriveState(state) {
|
|
var result = {
|
|
format: state.format,
|
|
volume: state.volume,
|
|
tracks: [],
|
|
track: state.track,
|
|
head: state.head,
|
|
phase: state.phase,
|
|
readOnly: state.readOnly,
|
|
dirty: state.dirty
|
|
};
|
|
for (var idx = 0; idx < state.tracks.length; idx++) {
|
|
result.tracks.push(base64_decode(state.tracks[idx]));
|
|
}
|
|
return result;
|
|
}
|
|
state.drives.forEach(function(drive, idx) {
|
|
_drives[idx] = setDriveState(drive);
|
|
callbacks.driveLight(idx, _drive.on);
|
|
callbacks.dirty(idx, _drive.dirty);
|
|
});
|
|
_skip = state.skip;
|
|
_latch = state.latch;
|
|
_writeMode = state.writeMode;
|
|
_on = state.on;
|
|
_drive = state.drive;
|
|
_cur = _drives[_drive - 1];
|
|
},
|
|
|
|
getMetadata: function disk_getMetadata(driveNo) {
|
|
var drive = _drives[driveNo - 1];
|
|
if (drive.tracks.length) {
|
|
return {
|
|
format: drive.format,
|
|
volume: drive.volume,
|
|
track: drive.track,
|
|
head: drive.head,
|
|
phase: drive.phase,
|
|
readOnly: drive.readOnly,
|
|
dirty: drive.dirty
|
|
};
|
|
} else {
|
|
return null;
|
|
}
|
|
},
|
|
|
|
rwts: function disk2_rwts(disk, track, sector) {
|
|
var cur = _drives[disk - 1];
|
|
return readSector(cur, track, sector);
|
|
},
|
|
|
|
setDisk: function disk2_setDisk(drive, disk) {
|
|
var fmt = disk.type, readOnly = disk.readOnly;
|
|
|
|
var data, t, s;
|
|
if (disk.encoding == 'base64') {
|
|
data = [];
|
|
for (t = 0; t < disk.data.length; t++) {
|
|
if (fmt == 'nib') {
|
|
data[t] = base64_decode(disk.data[t]);
|
|
} else {
|
|
data[t] = [];
|
|
for (s = 0; s < disk.data[t].length; s++) {
|
|
data[t][s] = base64_decode(disk.data[t][s]);
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
data = disk.data;
|
|
}
|
|
var cur = _drives[drive - 1];
|
|
|
|
// var v = (fmt === 'dsk' ? data[0x11][0x00][0x06] : 0xfe);
|
|
// if (v == 0x00) {
|
|
var volume = disk.volume || 0xfe;
|
|
// }
|
|
|
|
var options = {
|
|
volume,
|
|
readOnly,
|
|
name,
|
|
data
|
|
};
|
|
|
|
switch (fmt) {
|
|
case 'd13':
|
|
disk = new D13(options);
|
|
break;
|
|
case 'do':
|
|
case 'dsk':
|
|
disk = new DOS(options);
|
|
break;
|
|
case 'nib':
|
|
disk = new Nibble(options);
|
|
break;
|
|
case 'po':
|
|
disk = new ProDOS(options);
|
|
break;
|
|
default:
|
|
return false;
|
|
}
|
|
|
|
Object.assign(cur, disk);
|
|
_updateDirty(_drive, false);
|
|
},
|
|
|
|
getJSON: function disk2_getJSON(drive, pretty) {
|
|
var cur = _drives[drive - 1];
|
|
return jsonEncode(cur, pretty);
|
|
},
|
|
|
|
setJSON: function disk2_setJSON(drive, data) {
|
|
var cur = _drives[drive - 1];
|
|
Object.assign(cur, jsonDecode(data));
|
|
return true;
|
|
},
|
|
|
|
setBinary: function disk2_setBinary(drive, name, fmt, rawData) {
|
|
var disk;
|
|
var cur = _drives[drive - 1];
|
|
var readOnly = false;
|
|
var volume = 254;
|
|
var options = {
|
|
name,
|
|
rawData,
|
|
readOnly,
|
|
volume
|
|
};
|
|
|
|
switch (fmt) {
|
|
case '2mg':
|
|
disk = new _2MG(options);
|
|
break;
|
|
case 'd13':
|
|
disk = new D13(options);
|
|
break;
|
|
case 'do':
|
|
case 'dsk':
|
|
disk = new DOS(options);
|
|
break;
|
|
case 'nib':
|
|
disk = new Nibble(options);
|
|
break;
|
|
case 'po':
|
|
disk = new ProDOS(options);
|
|
break;
|
|
case 'woz':
|
|
disk = new Woz(options);
|
|
break;
|
|
default:
|
|
return false;
|
|
}
|
|
|
|
Object.assign(cur, disk);
|
|
_updateDirty(drive, true);
|
|
return true;
|
|
},
|
|
|
|
getBinary: function disk2_getBinary(drive) {
|
|
var cur = _drives[drive - 1];
|
|
var len = (16 * cur.tracks.length * 256);
|
|
var data = new Uint8Array(len);
|
|
var idx = 0;
|
|
|
|
for (var t = 0; t < cur.tracks.length; t++) {
|
|
if (cur.format === 'nib') {
|
|
data[idx++] = cur.tracks[t];
|
|
} else {
|
|
for (var s = 0; s < 0x10; s++) {
|
|
var sector = readSector(cur, t);
|
|
for (var b = 0; b < 256; b++) {
|
|
data[idx++] = sector[b];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return data;
|
|
},
|
|
|
|
getBase64: function disk2_getBase64(drive) {
|
|
var cur = _drives[drive - 1];
|
|
var data = [];
|
|
for (var t = 0; t < cur.tracks.length; t++) {
|
|
data[t] = [];
|
|
if (cur.format === 'nib') {
|
|
data += base64_encode(cur.tracks[t]);
|
|
} else {
|
|
for (var s = 0; s < 0x10; s++) {
|
|
data += base64_encode(readSector(cur, t));
|
|
}
|
|
}
|
|
}
|
|
return data;
|
|
}
|
|
};
|
|
}
|