mirror of
https://github.com/whscullin/apple2js.git
synced 2024-01-12 14:14:38 +00:00
Remove the Disk
object from the Drive
object
This change splits out the disk objects into a record parallel to the drive objects. The idea is that the `Drive` structure becomes a representation of the state of the drive that is separate from the disk image actually in the drive. This helps in an upcoming refactoring. This also changes the default empty disks to be writable. While odd, the write protect switch should be in the "off" position since there is no disk pressing on it. Finally, `insertDisk` now resets the head position to 0 since there is no way of preserving the head position across disks. (Even in the real world, the motor-off delay plus spindle spin-down would make it impossible to know the disk head position with any accuracy.)
This commit is contained in:
parent
f98aa93f24
commit
2c01448f07
|
@ -19,7 +19,6 @@ import {
|
||||||
PROCESS_JSON,
|
PROCESS_JSON,
|
||||||
MassStorage,
|
MassStorage,
|
||||||
MassStorageData,
|
MassStorageData,
|
||||||
DiskMetadata,
|
|
||||||
SupportedSectors,
|
SupportedSectors,
|
||||||
FloppyDisk,
|
FloppyDisk,
|
||||||
FloppyFormat,
|
FloppyFormat,
|
||||||
|
@ -218,10 +217,6 @@ export interface Callbacks {
|
||||||
|
|
||||||
/** Common information for Nibble and WOZ disks. */
|
/** Common information for Nibble and WOZ disks. */
|
||||||
interface Drive {
|
interface Drive {
|
||||||
/** The disk in the drive. */
|
|
||||||
disk: FloppyDisk;
|
|
||||||
/** Metadata about the disk image */
|
|
||||||
metadata: DiskMetadata;
|
|
||||||
/** Whether the drive write protect is on. */
|
/** Whether the drive write protect is on. */
|
||||||
readOnly: boolean;
|
readOnly: boolean;
|
||||||
/** Quarter track position of read/write head. */
|
/** Quarter track position of read/write head. */
|
||||||
|
@ -236,7 +231,6 @@ interface Drive {
|
||||||
|
|
||||||
interface DriveState {
|
interface DriveState {
|
||||||
disk: FloppyDisk;
|
disk: FloppyDisk;
|
||||||
metadata: DiskMetadata;
|
|
||||||
readOnly: boolean;
|
readOnly: boolean;
|
||||||
track: byte;
|
track: byte;
|
||||||
head: byte;
|
head: byte;
|
||||||
|
@ -252,19 +246,6 @@ interface State {
|
||||||
controllerState: ControllerState;
|
controllerState: ControllerState;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getDriveState(drive: Drive): DriveState {
|
|
||||||
const { metadata, readOnly, track, head, phase, dirty } = drive;
|
|
||||||
return {
|
|
||||||
disk: getDiskState(drive.disk),
|
|
||||||
metadata: {...metadata},
|
|
||||||
readOnly,
|
|
||||||
track,
|
|
||||||
head,
|
|
||||||
phase,
|
|
||||||
dirty,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function getDiskState(disk: NoFloppyDisk): NoFloppyDisk;
|
function getDiskState(disk: NoFloppyDisk): NoFloppyDisk;
|
||||||
function getDiskState(disk: NibbleDisk): NibbleDisk;
|
function getDiskState(disk: NibbleDisk): NibbleDisk;
|
||||||
function getDiskState(disk: WozDisk): WozDisk;
|
function getDiskState(disk: WozDisk): WozDisk;
|
||||||
|
@ -315,20 +296,6 @@ function getDiskState(disk: FloppyDisk): FloppyDisk {
|
||||||
throw new Error('Unknown drive state');
|
throw new Error('Unknown drive state');
|
||||||
}
|
}
|
||||||
|
|
||||||
function setDriveState(state: DriveState) {
|
|
||||||
const { track, head, phase, readOnly, dirty, metadata } = state;
|
|
||||||
const result: Drive = {
|
|
||||||
disk: getDiskState(state.disk),
|
|
||||||
track,
|
|
||||||
head,
|
|
||||||
phase,
|
|
||||||
readOnly,
|
|
||||||
dirty,
|
|
||||||
metadata: { ...metadata },
|
|
||||||
};
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Emulates the 16-sector and 13-sector versions of the Disk ][ drive and controller.
|
* Emulates the 16-sector and 13-sector versions of the Disk ][ drive and controller.
|
||||||
*/
|
*/
|
||||||
|
@ -336,29 +303,30 @@ export default class DiskII implements Card<State>, MassStorage<NibbleFormat> {
|
||||||
|
|
||||||
private drives: Record<DriveNumber, Drive> = {
|
private drives: Record<DriveNumber, Drive> = {
|
||||||
1: { // Drive 1
|
1: { // Drive 1
|
||||||
disk: {
|
|
||||||
encoding: NO_DISK,
|
|
||||||
readOnly: true,
|
|
||||||
metadata: { name: 'Disk 1' },
|
|
||||||
},
|
|
||||||
track: 0,
|
track: 0,
|
||||||
head: 0,
|
head: 0,
|
||||||
phase: 0,
|
phase: 0,
|
||||||
readOnly: false,
|
readOnly: false,
|
||||||
dirty: false,
|
dirty: false,
|
||||||
metadata: { name: 'Disk 1' },
|
|
||||||
},
|
},
|
||||||
2: { // Drive 2
|
2: { // Drive 2
|
||||||
disk: {
|
|
||||||
encoding: NO_DISK,
|
|
||||||
readOnly: true,
|
|
||||||
metadata: { name: 'Disk 2' },
|
|
||||||
},
|
|
||||||
track: 0,
|
track: 0,
|
||||||
head: 0,
|
head: 0,
|
||||||
phase: 0,
|
phase: 0,
|
||||||
readOnly: false,
|
readOnly: false,
|
||||||
dirty: false,
|
dirty: false,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private disks: Record<DriveNumber, FloppyDisk> = {
|
||||||
|
1: {
|
||||||
|
encoding: NO_DISK,
|
||||||
|
readOnly: false,
|
||||||
|
metadata: { name: 'Disk 1' },
|
||||||
|
},
|
||||||
|
2: {
|
||||||
|
encoding: NO_DISK,
|
||||||
|
readOnly: false,
|
||||||
metadata: { name: 'Disk 2' },
|
metadata: { name: 'Disk 2' },
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -373,8 +341,10 @@ export default class DiskII implements Card<State>, MassStorage<NibbleFormat> {
|
||||||
private skip = 0;
|
private skip = 0;
|
||||||
/** Drive off timeout id or null. */
|
/** Drive off timeout id or null. */
|
||||||
private offTimeout: number | null = null;
|
private offTimeout: number | null = null;
|
||||||
/** Current drive object. */
|
/** Current drive object. Must only be set by `updateActiveDrive()`. */
|
||||||
private cur: Drive;
|
private curDrive: Drive;
|
||||||
|
/** Current disk object. Must only be set by `updateActiveDrive()`. */
|
||||||
|
private curDisk: FloppyDisk;
|
||||||
|
|
||||||
/** Nibbles read this on cycle */
|
/** Nibbles read this on cycle */
|
||||||
private nibbleCount = 0;
|
private nibbleCount = 0;
|
||||||
|
@ -412,17 +382,23 @@ export default class DiskII implements Card<State>, MassStorage<NibbleFormat> {
|
||||||
state: 2,
|
state: 2,
|
||||||
};
|
};
|
||||||
|
|
||||||
this.cur = this.drives[this.state.drive];
|
this.updateActiveDrive();
|
||||||
|
|
||||||
this.initWorker();
|
this.initWorker();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Updates the active drive based on the controller state. */
|
||||||
|
private updateActiveDrive() {
|
||||||
|
this.curDrive = this.drives[this.state.drive];
|
||||||
|
this.curDisk = this.disks[this.state.drive];
|
||||||
|
}
|
||||||
|
|
||||||
private debug(..._args: unknown[]) {
|
private debug(..._args: unknown[]) {
|
||||||
// debug(..._args);
|
// debug(..._args);
|
||||||
}
|
}
|
||||||
|
|
||||||
public head(): number {
|
public head(): number {
|
||||||
return this.cur.head;
|
return this.curDrive.head;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -467,18 +443,18 @@ export default class DiskII implements Card<State>, MassStorage<NibbleFormat> {
|
||||||
let workCycles = (cycles - this.lastCycles) * 2;
|
let workCycles = (cycles - this.lastCycles) * 2;
|
||||||
this.lastCycles = cycles;
|
this.lastCycles = cycles;
|
||||||
|
|
||||||
if (!isWozDisk(this.cur.disk)) {
|
if (!isWozDisk(this.curDisk)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const track =
|
const track =
|
||||||
this.cur.disk.rawTracks[this.cur.disk.trackMap[this.cur.track]] || [0];
|
this.curDisk.rawTracks[this.curDisk.trackMap[this.curDrive.track]] || [0];
|
||||||
|
|
||||||
const state = this.state;
|
const state = this.state;
|
||||||
|
|
||||||
while (workCycles-- > 0) {
|
while (workCycles-- > 0) {
|
||||||
let pulse: number = 0;
|
let pulse: number = 0;
|
||||||
if (state.clock === 4) {
|
if (state.clock === 4) {
|
||||||
pulse = track[this.cur.head];
|
pulse = track[this.curDrive.head];
|
||||||
if (!pulse) {
|
if (!pulse) {
|
||||||
// More than 2 zeros can not be read reliably.
|
// More than 2 zeros can not be read reliably.
|
||||||
if (++this.zeros > 2) {
|
if (++this.zeros > 2) {
|
||||||
|
@ -511,7 +487,7 @@ export default class DiskII implements Card<State>, MassStorage<NibbleFormat> {
|
||||||
break;
|
break;
|
||||||
case 0xA: // SR
|
case 0xA: // SR
|
||||||
state.latch >>= 1;
|
state.latch >>= 1;
|
||||||
if (this.cur.readOnly) {
|
if (this.curDrive.readOnly) {
|
||||||
state.latch |= 0x80;
|
state.latch |= 0x80;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -530,12 +506,12 @@ export default class DiskII implements Card<State>, MassStorage<NibbleFormat> {
|
||||||
if (state.clock === 4) {
|
if (state.clock === 4) {
|
||||||
if (state.on) {
|
if (state.on) {
|
||||||
if (state.q7) {
|
if (state.q7) {
|
||||||
track[this.cur.head] = state.state & 0x8 ? 0x01 : 0x00;
|
track[this.curDrive.head] = state.state & 0x8 ? 0x01 : 0x00;
|
||||||
this.debug('Wrote', state.state & 0x8 ? 0x01 : 0x00);
|
this.debug('Wrote', state.state & 0x8 ? 0x01 : 0x00);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (++this.cur.head >= track.length) {
|
if (++this.curDrive.head >= track.length) {
|
||||||
this.cur.head = 0;
|
this.curDrive.head = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -548,29 +524,29 @@ export default class DiskII implements Card<State>, MassStorage<NibbleFormat> {
|
||||||
|
|
||||||
// Only called for non-WOZ disks
|
// Only called for non-WOZ disks
|
||||||
private readWriteNext() {
|
private readWriteNext() {
|
||||||
if (!isNibbleDisk(this.cur.disk)) {
|
if (!isNibbleDisk(this.curDisk)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const state = this.state;
|
const state = this.state;
|
||||||
if (state.on && (this.skip || state.q7)) {
|
if (state.on && (this.skip || state.q7)) {
|
||||||
const track = this.cur.disk.tracks[this.cur.track >> 2];
|
const track = this.curDisk.tracks[this.curDrive.track >> 2];
|
||||||
if (track && track.length) {
|
if (track && track.length) {
|
||||||
if (this.cur.head >= track.length) {
|
if (this.curDrive.head >= track.length) {
|
||||||
this.cur.head = 0;
|
this.curDrive.head = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state.q7) {
|
if (state.q7) {
|
||||||
if (!this.cur.readOnly) {
|
if (!this.curDrive.readOnly) {
|
||||||
track[this.cur.head] = state.bus;
|
track[this.curDrive.head] = state.bus;
|
||||||
if (!this.cur.dirty) {
|
if (!this.curDrive.dirty) {
|
||||||
this.updateDirty(state.drive, true);
|
this.updateDirty(state.drive, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
state.latch = track[this.cur.head];
|
state.latch = track[this.curDrive.head];
|
||||||
}
|
}
|
||||||
|
|
||||||
++this.cur.head;
|
++this.curDrive.head;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
state.latch = 0;
|
state.latch = 0;
|
||||||
|
@ -599,24 +575,24 @@ export default class DiskII implements Card<State>, MassStorage<NibbleFormat> {
|
||||||
|
|
||||||
this.debug(`phase ${phase}${on ? ' on' : ' off'}`);
|
this.debug(`phase ${phase}${on ? ' on' : ' off'}`);
|
||||||
if (on) {
|
if (on) {
|
||||||
this.cur.track += PHASE_DELTA[this.cur.phase][phase] * 2;
|
this.curDrive.track += PHASE_DELTA[this.curDrive.phase][phase] * 2;
|
||||||
this.cur.phase = phase;
|
this.curDrive.phase = phase;
|
||||||
}
|
}
|
||||||
|
|
||||||
// The emulator clamps the track to the valid track range available
|
// The emulator clamps the track to the valid track range available
|
||||||
// in the image, but real Disk II drives can seek past track 34 by
|
// in the image, but real Disk II drives can seek past track 34 by
|
||||||
// at least a half track, usually a full track. Some 3rd party
|
// at least a half track, usually a full track. Some 3rd party
|
||||||
// drives can seek to track 39.
|
// drives can seek to track 39.
|
||||||
const maxTrack = isNibbleDisk(this.cur.disk)
|
const maxTrack = isNibbleDisk(this.curDisk)
|
||||||
? this.cur.disk.tracks.length * 4 - 1
|
? this.curDisk.tracks.length * 4 - 1
|
||||||
: (isWozDisk(this.cur.disk)
|
: (isWozDisk(this.curDisk)
|
||||||
? this.cur.disk.trackMap.length - 1
|
? this.curDisk.trackMap.length - 1
|
||||||
: 0);
|
: 0);
|
||||||
if (this.cur.track > maxTrack) {
|
if (this.curDrive.track > maxTrack) {
|
||||||
this.cur.track = maxTrack;
|
this.curDrive.track = maxTrack;
|
||||||
}
|
}
|
||||||
if (this.cur.track < 0x0) {
|
if (this.curDrive.track < 0x0) {
|
||||||
this.cur.track = 0x0;
|
this.curDrive.track = 0x0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// debug(
|
// debug(
|
||||||
|
@ -688,7 +664,7 @@ export default class DiskII implements Card<State>, MassStorage<NibbleFormat> {
|
||||||
case LOC.DRIVE1: // 0x0a
|
case LOC.DRIVE1: // 0x0a
|
||||||
this.debug('Disk 1');
|
this.debug('Disk 1');
|
||||||
state.drive = 1;
|
state.drive = 1;
|
||||||
this.cur = this.drives[state.drive];
|
this.updateActiveDrive();
|
||||||
if (state.on) {
|
if (state.on) {
|
||||||
this.callbacks.driveLight(2, false);
|
this.callbacks.driveLight(2, false);
|
||||||
this.callbacks.driveLight(1, true);
|
this.callbacks.driveLight(1, true);
|
||||||
|
@ -697,7 +673,7 @@ export default class DiskII implements Card<State>, MassStorage<NibbleFormat> {
|
||||||
case LOC.DRIVE2: // 0x0b
|
case LOC.DRIVE2: // 0x0b
|
||||||
this.debug('Disk 2');
|
this.debug('Disk 2');
|
||||||
state.drive = 2;
|
state.drive = 2;
|
||||||
this.cur = this.drives[state.drive];
|
this.updateActiveDrive();
|
||||||
if (state.on) {
|
if (state.on) {
|
||||||
this.callbacks.driveLight(1, false);
|
this.callbacks.driveLight(1, false);
|
||||||
this.callbacks.driveLight(2, true);
|
this.callbacks.driveLight(2, true);
|
||||||
|
@ -709,7 +685,7 @@ export default class DiskII implements Card<State>, MassStorage<NibbleFormat> {
|
||||||
if (state.q7) {
|
if (state.q7) {
|
||||||
this.debug('clearing _q6/SHIFT');
|
this.debug('clearing _q6/SHIFT');
|
||||||
}
|
}
|
||||||
if (isNibbleDisk(this.cur.disk)) {
|
if (isNibbleDisk(this.curDisk)) {
|
||||||
this.readWriteNext();
|
this.readWriteNext();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -719,9 +695,9 @@ export default class DiskII implements Card<State>, MassStorage<NibbleFormat> {
|
||||||
if (state.q7) {
|
if (state.q7) {
|
||||||
this.debug('setting _q6/LOAD');
|
this.debug('setting _q6/LOAD');
|
||||||
}
|
}
|
||||||
if (isNibbleDisk(this.cur.disk)) {
|
if (isNibbleDisk(this.curDisk)) {
|
||||||
if (readMode && !state.q7) {
|
if (readMode && !state.q7) {
|
||||||
if (this.cur.readOnly) {
|
if (this.curDrive.readOnly) {
|
||||||
state.latch = 0xff;
|
state.latch = 0xff;
|
||||||
this.debug('Setting readOnly');
|
this.debug('Setting readOnly');
|
||||||
} else {
|
} else {
|
||||||
|
@ -794,38 +770,65 @@ export default class DiskII implements Card<State>, MassStorage<NibbleFormat> {
|
||||||
state.q7 = false;
|
state.q7 = false;
|
||||||
state.on = false;
|
state.on = false;
|
||||||
state.drive = 1;
|
state.drive = 1;
|
||||||
this.cur = this.drives[state.drive];
|
|
||||||
}
|
}
|
||||||
|
this.updateActiveDrive();
|
||||||
}
|
}
|
||||||
|
|
||||||
tick() {
|
tick() {
|
||||||
this.moveHead();
|
this.moveHead();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private getDriveState(drive: DriveNumber): DriveState {
|
||||||
|
const curDrive = this.drives[drive];
|
||||||
|
const curDisk = this.disks[drive];
|
||||||
|
const { readOnly, track, head, phase, dirty } = curDrive;
|
||||||
|
return {
|
||||||
|
disk: getDiskState(curDisk),
|
||||||
|
readOnly,
|
||||||
|
track,
|
||||||
|
head,
|
||||||
|
phase,
|
||||||
|
dirty,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
getState(): State {
|
getState(): State {
|
||||||
const result = {
|
const result = {
|
||||||
drives: [] as DriveState[],
|
drives: [] as DriveState[],
|
||||||
skip: this.skip,
|
skip: this.skip,
|
||||||
controllerState: { ...this.state },
|
controllerState: { ...this.state },
|
||||||
};
|
};
|
||||||
result.drives[1] = getDriveState(this.drives[1]);
|
result.drives[1] = this.getDriveState(1);
|
||||||
result.drives[2] = getDriveState(this.drives[2]);
|
result.drives[2] = this.getDriveState(2);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private setDriveState(drive: DriveNumber, state: DriveState) {
|
||||||
|
const { track, head, phase, readOnly, dirty } = state;
|
||||||
|
this.drives[drive] = {
|
||||||
|
track,
|
||||||
|
head,
|
||||||
|
phase,
|
||||||
|
readOnly,
|
||||||
|
dirty,
|
||||||
|
};
|
||||||
|
this.disks[drive] = getDiskState(state.disk);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
setState(state: State) {
|
setState(state: State) {
|
||||||
this.skip = state.skip;
|
this.skip = state.skip;
|
||||||
this.state = { ...state.controllerState };
|
this.state = { ...state.controllerState };
|
||||||
for (const d of DRIVE_NUMBERS) {
|
for (const d of DRIVE_NUMBERS) {
|
||||||
this.drives[d] = setDriveState(state.drives[d]);
|
this.setDriveState(d, state.drives[d]);
|
||||||
const { name, side } = state.drives[d].metadata;
|
const { name, side } = state.drives[d].disk.metadata;
|
||||||
const { dirty } = state.drives[d];
|
const { dirty } = state.drives[d];
|
||||||
this.callbacks.label(d, name, side);
|
this.callbacks.label(d, name, side);
|
||||||
this.callbacks.driveLight(d, this.state.on);
|
this.callbacks.driveLight(d, this.state.on);
|
||||||
this.callbacks.dirty(d, dirty);
|
this.callbacks.dirty(d, dirty);
|
||||||
}
|
}
|
||||||
this.cur = this.drives[this.state.drive];
|
this.updateActiveDrive();
|
||||||
}
|
}
|
||||||
|
|
||||||
getMetadata(driveNo: DriveNumber) {
|
getMetadata(driveNo: DriveNumber) {
|
||||||
|
@ -840,12 +843,12 @@ export default class DiskII implements Card<State>, MassStorage<NibbleFormat> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(flan): Does not work on WOZ disks
|
// TODO(flan): Does not work on WOZ disks
|
||||||
rwts(disk: DriveNumber, track: byte, sector: byte) {
|
rwts(drive: DriveNumber, track: byte, sector: byte) {
|
||||||
const cur = this.drives[disk];
|
const curDisk = this.disks[drive];
|
||||||
if (!isNibbleDisk(cur.disk)) {
|
if (!isNibbleDisk(curDisk)) {
|
||||||
throw new Error('Can\'t read WOZ disks');
|
throw new Error('Can\'t read WOZ disks');
|
||||||
}
|
}
|
||||||
return readSector(cur.disk, track, sector);
|
return readSector(curDisk, track, sector);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Sets the data for `drive` from `disk`, which is expected to be JSON. */
|
/** Sets the data for `drive` from `disk`, which is expected to be JSON. */
|
||||||
|
@ -872,11 +875,11 @@ export default class DiskII implements Card<State>, MassStorage<NibbleFormat> {
|
||||||
}
|
}
|
||||||
|
|
||||||
getJSON(drive: DriveNumber, pretty: boolean = false) {
|
getJSON(drive: DriveNumber, pretty: boolean = false) {
|
||||||
const cur = this.drives[drive];
|
const curDisk = this.disks[drive];
|
||||||
if (!isNibbleDisk(cur.disk)) {
|
if (!isNibbleDisk(curDisk)) {
|
||||||
throw new Error('Can\'t save WOZ disks to JSON');
|
throw new Error('Can\'t save WOZ disks to JSON');
|
||||||
}
|
}
|
||||||
return jsonEncode(cur.disk, pretty);
|
return jsonEncode(curDisk, pretty);
|
||||||
}
|
}
|
||||||
|
|
||||||
setJSON(drive: DriveNumber, json: string) {
|
setJSON(drive: DriveNumber, json: string) {
|
||||||
|
@ -955,22 +958,22 @@ export default class DiskII implements Card<State>, MassStorage<NibbleFormat> {
|
||||||
}
|
}
|
||||||
|
|
||||||
private insertDisk(drive: DriveNumber, disk: FloppyDisk) {
|
private insertDisk(drive: DriveNumber, disk: FloppyDisk) {
|
||||||
const cur = this.drives[drive];
|
this.disks[drive] = disk;
|
||||||
cur.disk = disk as WozDisk | NibbleDisk;
|
this.drives[drive].head = 0;
|
||||||
cur.metadata = disk.metadata;
|
this.updateActiveDrive();
|
||||||
const { name, side } = cur.metadata;
|
const { name, side } = disk.metadata;
|
||||||
this.updateDirty(drive, true);
|
this.updateDirty(drive, true);
|
||||||
this.callbacks.label(drive, name, side);
|
this.callbacks.label(drive, name, side);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(flan): Does not work with WOZ or D13 disks
|
// TODO(flan): Does not work with WOZ or D13 disks
|
||||||
getBinary(drive: DriveNumber, ext?: Exclude<NibbleFormat, 'woz' | 'd13'>): MassStorageData | null {
|
getBinary(drive: DriveNumber, ext?: Exclude<NibbleFormat, 'woz' | 'd13'>): MassStorageData | null {
|
||||||
const cur = this.drives[drive];
|
const curDisk = this.disks[drive];
|
||||||
if (!isNibbleDisk(cur.disk)) {
|
if (!isNibbleDisk(curDisk)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
const { format, readOnly, tracks, volume } = cur.disk;
|
const { format, readOnly, tracks, volume } = curDisk;
|
||||||
const { name } = cur.metadata;
|
const { name } = curDisk.metadata;
|
||||||
const len = format === 'nib' ?
|
const len = format === 'nib' ?
|
||||||
tracks.reduce((acc, track) => acc + track.length, 0) :
|
tracks.reduce((acc, track) => acc + track.length, 0) :
|
||||||
this.sectors * tracks.length * 256;
|
this.sectors * tracks.length * 256;
|
||||||
|
@ -984,7 +987,7 @@ export default class DiskII implements Card<State>, MassStorage<NibbleFormat> {
|
||||||
idx += tracks[t].length;
|
idx += tracks[t].length;
|
||||||
} else {
|
} else {
|
||||||
for (let s = 0; s < 0x10; s++) {
|
for (let s = 0; s < 0x10; s++) {
|
||||||
const sector = readSector({ ...cur.disk, format: extension }, t, s);
|
const sector = readSector({ ...curDisk, format: extension }, t, s);
|
||||||
data.set(sector, idx);
|
data.set(sector, idx);
|
||||||
idx += sector.length;
|
idx += sector.length;
|
||||||
}
|
}
|
||||||
|
@ -1002,18 +1005,18 @@ export default class DiskII implements Card<State>, MassStorage<NibbleFormat> {
|
||||||
|
|
||||||
// TODO(flan): Does not work with WOZ or D13 disks
|
// TODO(flan): Does not work with WOZ or D13 disks
|
||||||
getBase64(drive: DriveNumber) {
|
getBase64(drive: DriveNumber) {
|
||||||
const cur = this.drives[drive];
|
const curDisk = this.disks[drive];
|
||||||
if (!isNibbleDisk(cur.disk)) {
|
if (!isNibbleDisk(curDisk)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
const data: string[][] | string[] = [];
|
const data: string[][] | string[] = [];
|
||||||
for (let t = 0; t < cur.disk.tracks.length; t++) {
|
for (let t = 0; t < curDisk.tracks.length; t++) {
|
||||||
if (isNibbleDisk(cur.disk)) {
|
if (isNibbleDisk(curDisk)) {
|
||||||
data[t] = base64_encode(cur.disk.tracks[t]);
|
data[t] = base64_encode(curDisk.tracks[t]);
|
||||||
} else {
|
} else {
|
||||||
const track: string[] = [];
|
const track: string[] = [];
|
||||||
for (let s = 0; s < 0x10; s++) {
|
for (let s = 0; s < 0x10; s++) {
|
||||||
track[s] = base64_encode(readSector(cur.disk, t, s));
|
track[s] = base64_encode(readSector(curDisk, t, s));
|
||||||
}
|
}
|
||||||
data[t] = track;
|
data[t] = track;
|
||||||
}
|
}
|
||||||
|
|
|
@ -824,7 +824,7 @@ class TestDiskReader {
|
||||||
|
|
||||||
rawTracks() {
|
rawTracks() {
|
||||||
// NOTE(flan): Hack to access private properties.
|
// NOTE(flan): Hack to access private properties.
|
||||||
const disk = (this.diskII as unknown as { cur: { disk: WozDisk } }).cur.disk;
|
const disk = (this.diskII as unknown as { curDisk: WozDisk }).curDisk;
|
||||||
const result: Uint8Array[] = [];
|
const result: Uint8Array[] = [];
|
||||||
for (let i = 0; i < disk.rawTracks.length; i++) {
|
for (let i = 0; i < disk.rawTracks.length; i++) {
|
||||||
result[i] = disk.rawTracks[i].slice(0);
|
result[i] = disk.rawTracks[i].slice(0);
|
||||||
|
|
Loading…
Reference in New Issue
Block a user