mirror of
https://github.com/whscullin/apple2js.git
synced 2024-01-12 14:14:38 +00:00
Squash the Drive
type hierarchy
Before, the type of the drive depended on the type of the disk in the drive. Thus, `NibbleDrive` contained a `NibbleDisk` and a `WozDrive` contained a `WozDisk`. With the extraction of the disk data to a single field, this type hierarchy makes no sense. Instead, it suffices to check the type of the disk. This change removes the `NibbleDrive` and `WozDrive` types and type guards, checking the disk type where necessary. This change also introduces the `NoFloppyDisk` type to represent the lack of a disk. This allows the drive to have metadata, for one. All tests pass, everything compiles, and both WOZ and nibble disks work locally.
This commit is contained in:
parent
62b48d3893
commit
99ad84a5ec
|
@ -14,11 +14,9 @@ import {
|
||||||
DRIVE_NUMBERS,
|
DRIVE_NUMBERS,
|
||||||
DriveNumber,
|
DriveNumber,
|
||||||
JSONDisk,
|
JSONDisk,
|
||||||
ENCODING_NIBBLE,
|
|
||||||
PROCESS_BINARY,
|
PROCESS_BINARY,
|
||||||
PROCESS_JSON_DISK,
|
PROCESS_JSON_DISK,
|
||||||
PROCESS_JSON,
|
PROCESS_JSON,
|
||||||
ENCODING_BITSTREAM,
|
|
||||||
MassStorage,
|
MassStorage,
|
||||||
MassStorageData,
|
MassStorageData,
|
||||||
DiskMetadata,
|
DiskMetadata,
|
||||||
|
@ -29,6 +27,9 @@ import {
|
||||||
NibbleDisk,
|
NibbleDisk,
|
||||||
isNibbleDisk,
|
isNibbleDisk,
|
||||||
isWozDisk,
|
isWozDisk,
|
||||||
|
NoFloppyDisk,
|
||||||
|
isNoFloppyDisk,
|
||||||
|
NO_DISK,
|
||||||
} from '../formats/types';
|
} from '../formats/types';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
@ -216,7 +217,7 @@ export interface Callbacks {
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Common information for Nibble and WOZ disks. */
|
/** Common information for Nibble and WOZ disks. */
|
||||||
interface BaseDrive {
|
interface Drive {
|
||||||
/** The disk in the drive. */
|
/** The disk in the drive. */
|
||||||
disk: FloppyDisk;
|
disk: FloppyDisk;
|
||||||
/** Metadata about the disk image */
|
/** Metadata about the disk image */
|
||||||
|
@ -233,26 +234,6 @@ interface BaseDrive {
|
||||||
dirty: boolean;
|
dirty: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** WOZ format track data from https://applesaucefdc.com/woz/reference2/. */
|
|
||||||
interface WozDrive extends BaseDrive {
|
|
||||||
disk: WozDisk;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Nibble format track data. */
|
|
||||||
interface NibbleDrive extends BaseDrive {
|
|
||||||
disk: NibbleDisk;
|
|
||||||
}
|
|
||||||
|
|
||||||
type Drive = WozDrive | NibbleDrive;
|
|
||||||
|
|
||||||
function isNibbleDrive(drive: Drive): drive is NibbleDrive {
|
|
||||||
return drive.disk.encoding === ENCODING_NIBBLE;
|
|
||||||
}
|
|
||||||
|
|
||||||
function isWozDrive(drive: Drive): drive is WozDrive {
|
|
||||||
return drive.disk.encoding === ENCODING_BITSTREAM;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface DriveState {
|
interface DriveState {
|
||||||
disk: FloppyDisk;
|
disk: FloppyDisk;
|
||||||
metadata: DiskMetadata;
|
metadata: DiskMetadata;
|
||||||
|
@ -283,10 +264,18 @@ function getDriveState(drive: Drive): DriveState {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
function getDiskState(disk: FloppyDisk): FloppyDisk;
|
function getDiskState(disk: FloppyDisk): FloppyDisk;
|
||||||
function getDiskState(disk: FloppyDisk): FloppyDisk {
|
function getDiskState(disk: FloppyDisk): FloppyDisk {
|
||||||
|
if (isNoFloppyDisk(disk)) {
|
||||||
|
return {
|
||||||
|
encoding: disk.encoding,
|
||||||
|
metadata: {...disk.metadata},
|
||||||
|
readOnly: disk.readOnly,
|
||||||
|
};
|
||||||
|
}
|
||||||
if (isNibbleDisk(disk)) {
|
if (isNibbleDisk(disk)) {
|
||||||
const result: NibbleDisk = {
|
const result: NibbleDisk = {
|
||||||
format: disk.format,
|
format: disk.format,
|
||||||
|
@ -323,30 +312,16 @@ function getDiskState(disk: FloppyDisk): FloppyDisk {
|
||||||
}
|
}
|
||||||
|
|
||||||
function setDriveState(state: DriveState) {
|
function setDriveState(state: DriveState) {
|
||||||
if (isNibbleDisk(state.disk)) {
|
const result: Drive = {
|
||||||
const result: NibbleDrive = {
|
disk: getDiskState(state.disk),
|
||||||
disk: getDiskState(state.disk),
|
track: state.track,
|
||||||
track: state.track,
|
head: state.head,
|
||||||
head: state.head,
|
phase: state.phase,
|
||||||
phase: state.phase,
|
readOnly: state.readOnly,
|
||||||
readOnly: state.readOnly,
|
dirty: state.dirty,
|
||||||
dirty: state.dirty,
|
metadata: { ...state.metadata },
|
||||||
metadata: { ...state.metadata },
|
};
|
||||||
};
|
return result;
|
||||||
return result;
|
|
||||||
} else if (isWozDisk(state.disk)) {
|
|
||||||
const result: WozDrive = {
|
|
||||||
disk: getDiskState(state.disk),
|
|
||||||
track: state.track,
|
|
||||||
head: state.head,
|
|
||||||
phase: state.phase,
|
|
||||||
readOnly: state.readOnly,
|
|
||||||
dirty: state.dirty,
|
|
||||||
metadata: { ...state.metadata },
|
|
||||||
};
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
throw new Error('Unable to restore drive state');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -357,10 +332,7 @@ 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: {
|
disk: {
|
||||||
format: 'dsk',
|
encoding: NO_DISK,
|
||||||
encoding: ENCODING_NIBBLE,
|
|
||||||
volume: 254,
|
|
||||||
tracks: [],
|
|
||||||
readOnly: true,
|
readOnly: true,
|
||||||
metadata: { name: 'Disk 1' },
|
metadata: { name: 'Disk 1' },
|
||||||
},
|
},
|
||||||
|
@ -373,10 +345,7 @@ export default class DiskII implements Card<State>, MassStorage<NibbleFormat> {
|
||||||
},
|
},
|
||||||
2: { // Drive 2
|
2: { // Drive 2
|
||||||
disk: {
|
disk: {
|
||||||
format: 'dsk',
|
encoding: NO_DISK,
|
||||||
encoding: ENCODING_NIBBLE,
|
|
||||||
volume: 254,
|
|
||||||
tracks: [],
|
|
||||||
readOnly: true,
|
readOnly: true,
|
||||||
metadata: { name: 'Disk 2' },
|
metadata: { name: 'Disk 2' },
|
||||||
},
|
},
|
||||||
|
@ -493,7 +462,7 @@ 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 (!isWozDrive(this.cur)) {
|
if (!isWozDisk(this.cur.disk)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const track =
|
const track =
|
||||||
|
@ -574,7 +543,7 @@ 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 (!isNibbleDrive(this.cur)) {
|
if (!isNibbleDisk(this.cur.disk)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const state = this.state;
|
const state = this.state;
|
||||||
|
@ -633,9 +602,11 @@ export default class DiskII implements Card<State>, MassStorage<NibbleFormat> {
|
||||||
// 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 = isNibbleDrive(this.cur)
|
const maxTrack = isNibbleDisk(this.cur.disk)
|
||||||
? this.cur.disk.tracks.length * 4 - 1
|
? this.cur.disk.tracks.length * 4 - 1
|
||||||
: this.cur.disk.trackMap.length - 1;
|
: (isWozDisk(this.cur.disk)
|
||||||
|
? this.cur.disk.trackMap.length - 1
|
||||||
|
: 0);
|
||||||
if (this.cur.track > maxTrack) {
|
if (this.cur.track > maxTrack) {
|
||||||
this.cur.track = maxTrack;
|
this.cur.track = maxTrack;
|
||||||
}
|
}
|
||||||
|
@ -733,7 +704,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 (isNibbleDrive(this.cur)) {
|
if (isNibbleDisk(this.cur.disk)) {
|
||||||
this.readWriteNext();
|
this.readWriteNext();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -743,7 +714,7 @@ 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 (isNibbleDrive(this.cur)) {
|
if (isNibbleDisk(this.cur.disk)) {
|
||||||
if (readMode && !state.q7) {
|
if (readMode && !state.q7) {
|
||||||
if (this.cur.readOnly) {
|
if (this.cur.readOnly) {
|
||||||
state.latch = 0xff;
|
state.latch = 0xff;
|
||||||
|
@ -855,7 +826,6 @@ export default class DiskII implements Card<State>, MassStorage<NibbleFormat> {
|
||||||
getMetadata(driveNo: DriveNumber) {
|
getMetadata(driveNo: DriveNumber) {
|
||||||
const drive = this.drives[driveNo];
|
const drive = this.drives[driveNo];
|
||||||
return {
|
return {
|
||||||
format: drive.disk.format,
|
|
||||||
track: drive.track,
|
track: drive.track,
|
||||||
head: drive.head,
|
head: drive.head,
|
||||||
phase: drive.phase,
|
phase: drive.phase,
|
||||||
|
@ -867,7 +837,7 @@ 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(disk: DriveNumber, track: byte, sector: byte) {
|
||||||
const cur = this.drives[disk];
|
const cur = this.drives[disk];
|
||||||
if (!isNibbleDrive(cur)) {
|
if (!isNibbleDisk(cur.disk)) {
|
||||||
throw new Error('Can\'t read WOZ disks');
|
throw new Error('Can\'t read WOZ disks');
|
||||||
}
|
}
|
||||||
return readSector(cur.disk, track, sector);
|
return readSector(cur.disk, track, sector);
|
||||||
|
@ -898,7 +868,7 @@ 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 cur = this.drives[drive];
|
||||||
if (!isNibbleDrive(cur)) {
|
if (!isNibbleDisk(cur.disk)) {
|
||||||
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(cur.disk, pretty);
|
||||||
|
@ -991,7 +961,7 @@ 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
|
||||||
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 cur = this.drives[drive];
|
||||||
if (!isNibbleDrive(cur)) {
|
if (!isNibbleDisk(cur.disk)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
const { format, readOnly, tracks, volume } = cur.disk;
|
const { format, readOnly, tracks, volume } = cur.disk;
|
||||||
|
@ -1028,7 +998,7 @@ 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 cur = this.drives[drive];
|
||||||
if (!isNibbleDrive(cur)) {
|
if (!isNibbleDisk(cur.disk)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
const data: string[][] | string[] = [];
|
const data: string[][] | string[] = [];
|
||||||
|
|
|
@ -64,12 +64,17 @@ export interface Disk {
|
||||||
readOnly: boolean;
|
readOnly: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const NO_DISK = 'empty';
|
||||||
export const ENCODING_NIBBLE = 'nibble';
|
export const ENCODING_NIBBLE = 'nibble';
|
||||||
export const ENCODING_BITSTREAM = 'bitstream';
|
export const ENCODING_BITSTREAM = 'bitstream';
|
||||||
export const ENCODING_BLOCK = 'block';
|
export const ENCODING_BLOCK = 'block';
|
||||||
|
|
||||||
export interface FloppyDisk extends Disk {
|
export interface FloppyDisk extends Disk {
|
||||||
encoding: typeof ENCODING_NIBBLE | typeof ENCODING_BITSTREAM;
|
encoding: typeof ENCODING_NIBBLE | typeof ENCODING_BITSTREAM | typeof NO_DISK;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface NoFloppyDisk extends FloppyDisk {
|
||||||
|
encoding: typeof NO_DISK;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface NibbleDisk extends FloppyDisk {
|
export interface NibbleDisk extends FloppyDisk {
|
||||||
|
@ -153,6 +158,10 @@ export function isBlockDiskFormat(f: DiskFormat): f is BlockFormat {
|
||||||
return f in BLOCK_FORMATS;
|
return f in BLOCK_FORMATS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function isNoFloppyDisk(disk: Disk): disk is NoFloppyDisk {
|
||||||
|
return (disk as NoFloppyDisk)?.encoding === NO_DISK;
|
||||||
|
}
|
||||||
|
|
||||||
/** Type guard for NibbleDisks */
|
/** Type guard for NibbleDisks */
|
||||||
export function isNibbleDisk(disk: Disk): disk is NibbleDisk {
|
export function isNibbleDisk(disk: Disk): disk is NibbleDisk {
|
||||||
return (disk as NibbleDisk)?.encoding === ENCODING_NIBBLE;
|
return (disk as NibbleDisk)?.encoding === ENCODING_NIBBLE;
|
||||||
|
|
Loading…
Reference in New Issue
Block a user