Harmonize drive and disk type hierarchies

Before, the `XXXDrive` and `XXXDisk` type hierarchies were similar,
but not exactly the same. For example, `encoding` and `format` were
missing on some `XXXDisk` types where they existed on the `XXXDrive`
type. This change attempts to bring the hierarchies closer together.

However, the biggest visible consequence is the introduction of the
`FLOPPY_FORMATS` array and its associated `FloppyFormat` type.  This
replaces `NIBBLE_FORMATS` in most places.  A couple of new type guards
for disk formats and disks have been added as well.

All tests pass, everything compiles with no errors, and both WOZ and
nibble format disks load in the emulator.
This commit is contained in:
Ian Flanigan 2022-09-04 10:11:13 +02:00
parent 41e0609f55
commit 3a28fcb9fb
No known key found for this signature in database
GPG Key ID: 035F657DAE4AE7EC
16 changed files with 183 additions and 89 deletions

View File

@ -389,6 +389,7 @@ export default class CFFA implements Card, MassStorage<BlockFormat>, Restorable<
(block) => new Uint8Array(block)
),
encoding: ENCODING_BLOCK,
format: disk.format,
readOnly: disk.readOnly,
metadata: { ...disk.metadata },
};
@ -472,7 +473,7 @@ export default class CFFA implements Card, MassStorage<BlockFormat>, Restorable<
volume,
readOnly
};
const disk = createBlockDisk(options);
const disk = createBlockDisk(ext, options);
return this.setBlockVolume(drive, disk);
}
@ -485,7 +486,7 @@ export default class CFFA implements Card, MassStorage<BlockFormat>, Restorable<
}
const { blocks, readOnly } = blockDisk;
const { name } = blockDisk.metadata;
let ext;
let ext: '2mg' | 'po';
let data: ArrayBuffer;
if (this._metadata[drive]) {
ext = '2mg';

View File

@ -25,6 +25,7 @@ import {
DiskMetadata,
SupportedSectors,
FloppyDisk,
FloppyFormat,
} from '../formats/types';
import {
@ -37,6 +38,7 @@ import { jsonDecode, jsonEncode, readSector } from '../formats/format_utils';
import { BOOTSTRAP_ROM_16, BOOTSTRAP_ROM_13 } from '../roms/cards/disk2';
import Apple2IO from '../apple2io';
import { InfoChunk } from 'js/formats/woz';
/** Softswitch locations */
const LOC = {
@ -214,37 +216,42 @@ export interface Callbacks {
/** Common information for Nibble and WOZ disks. */
interface BaseDrive {
/** Current disk format. */
format: NibbleFormat;
/** Current disk volume number. */
volume: byte;
format: FloppyFormat;
/** Metadata about the disk image */
metadata: DiskMetadata;
/** Whether the drive write protect is on. */
readOnly: boolean;
/** Quarter track position of read/write head. */
track: byte;
/** Position of the head on the track. */
head: byte;
/** Current active coil in the head stepper motor. */
phase: Phase;
/** Whether the drive write protect is on. */
readOnly: boolean;
/** Whether the drive has been written to since it was loaded. */
dirty: boolean;
/** Metadata about the disk image */
metadata: DiskMetadata;
}
/** WOZ format track data from https://applesaucefdc.com/woz/reference2/. */
interface WozDrive extends BaseDrive {
/** Woz encoding */
encoding: typeof ENCODING_BITSTREAM;
format: 'woz';
/** Maps quarter tracks to data in rawTracks; `0xFF` = random garbage. */
trackMap: byte[];
/** Unique track bitstreams. The index is arbitrary; it is NOT the track number. */
rawTracks: Uint8Array[];
/** Optional `INFO` chunk from WOZ image. */
info?: InfoChunk;
}
/** Nibble format track data. */
interface NibbleDrive extends BaseDrive {
/** Nibble encoding */
encoding: typeof ENCODING_NIBBLE;
/** Format */
format: Exclude<NibbleFormat, 'woz'>;
/** Current disk volume number. */
volume: byte;
/** Nibble data. The index is the track number. */
tracks: memory[];
}
@ -259,9 +266,9 @@ function isWozDrive(drive: Drive): drive is WozDrive {
return drive.encoding === ENCODING_BITSTREAM;
}
interface DriveState {
format: NibbleFormat;
encoding: typeof ENCODING_BITSTREAM | typeof ENCODING_NIBBLE;
interface NibbleDriveState {
format: Exclude<NibbleFormat, 'woz'>;
encoding: typeof ENCODING_NIBBLE;
volume: byte;
tracks: memory[];
track: byte;
@ -274,6 +281,23 @@ interface DriveState {
metadata: DiskMetadata;
}
interface WozDriveState {
format: 'woz';
encoding: typeof ENCODING_BITSTREAM;
tracks: memory[];
track: byte;
head: byte;
phase: Phase;
readOnly: boolean;
dirty: boolean;
trackMap: number[];
rawTracks: Uint8Array[];
metadata: DiskMetadata;
info?: InfoChunk;
}
type DriveState = NibbleDriveState | WozDriveState;
/** State of the controller for saving/restoring. */
// TODO(flan): It's unclear whether reusing ControllerState here is a good idea.
interface State {
@ -283,33 +307,52 @@ interface State {
}
function getDriveState(drive: Drive): DriveState {
const result: DriveState = {
format: drive.format,
encoding: drive.encoding,
volume: drive.volume,
tracks: [],
track: drive.track,
head: drive.head,
phase: drive.phase,
readOnly: drive.readOnly,
dirty: drive.dirty,
trackMap: [],
rawTracks: [],
metadata: { ...drive.metadata },
};
if (isNibbleDrive(drive)) {
const result: NibbleDriveState = {
format: drive.format,
encoding: drive.encoding,
volume: drive.volume,
tracks: [],
track: drive.track,
head: drive.head,
phase: drive.phase,
readOnly: drive.readOnly,
dirty: drive.dirty,
trackMap: [],
rawTracks: [],
metadata: { ...drive.metadata },
};
for (let idx = 0; idx < drive.tracks.length; idx++) {
result.tracks.push(new Uint8Array(drive.tracks[idx]));
}
return result;
}
if (isWozDrive(drive)) {
const result: WozDriveState = {
format: drive.format,
encoding: drive.encoding,
tracks: [],
track: drive.track,
head: drive.head,
phase: drive.phase,
readOnly: drive.readOnly,
dirty: drive.dirty,
trackMap: [],
rawTracks: [],
metadata: { ...drive.metadata },
};
result.trackMap = [...drive.trackMap];
for (let idx = 0; idx < drive.rawTracks.length; idx++) {
result.rawTracks.push(new Uint8Array(drive.rawTracks[idx]));
}
if (drive.info) {
result.info = drive.info;
}
return result;
}
return result;
throw new Error('Unknown drive state');
}
function setDriveState(state: DriveState) {
@ -318,7 +361,7 @@ function setDriveState(state: DriveState) {
result = {
format: state.format,
encoding: ENCODING_NIBBLE,
volume: state.volume,
volume: state.volume || 254,
tracks: [],
track: state.track,
head: state.head,
@ -334,7 +377,6 @@ function setDriveState(state: DriveState) {
result = {
format: state.format,
encoding: ENCODING_BITSTREAM,
volume: state.volume,
track: state.track,
head: state.head,
phase: state.phase,
@ -850,7 +892,6 @@ export default class DiskII implements Card<State>, MassStorage<NibbleFormat> {
const drive = this.drives[driveNo];
return {
format: drive.format,
volume: drive.volume,
track: drive.track,
head: drive.head,
phase: drive.phase,
@ -916,7 +957,7 @@ export default class DiskII implements Card<State>, MassStorage<NibbleFormat> {
return true;
}
setBinary(drive: DriveNumber, name: string, fmt: NibbleFormat, rawData: ArrayBuffer) {
setBinary(drive: DriveNumber, name: string, fmt: FloppyFormat, rawData: ArrayBuffer) {
const readOnly = false;
const volume = 254;
const options = {
@ -983,7 +1024,7 @@ export default class DiskII implements Card<State>, MassStorage<NibbleFormat> {
}
// TODO(flan): Does not work with WOZ or D13 disks
getBinary(drive: DriveNumber, ext?: NibbleFormat): MassStorageData | null {
getBinary(drive: DriveNumber, ext?: Exclude<NibbleFormat, 'woz' | 'd13'>): MassStorageData | null {
const cur = this.drives[drive];
if (!isNibbleDrive(cur)) {
return null;
@ -995,7 +1036,7 @@ export default class DiskII implements Card<State>, MassStorage<NibbleFormat> {
this.sectors * tracks.length * 256;
const data = new Uint8Array(len);
ext = ext ?? format;
const extension = ext ?? format;
let idx = 0;
for (let t = 0; t < tracks.length; t++) {
if (ext === 'nib') {
@ -1003,7 +1044,7 @@ export default class DiskII implements Card<State>, MassStorage<NibbleFormat> {
idx += tracks[t].length;
} else {
for (let s = 0; s < 0x10; s++) {
const sector = readSector({ ...cur, format: ext }, t, s);
const sector = readSector({ ...cur, format: extension }, t, s);
data.set(sector, idx);
idx += sector.length;
}
@ -1011,7 +1052,7 @@ export default class DiskII implements Card<State>, MassStorage<NibbleFormat> {
}
return {
ext,
ext: extension,
metadata: { name },
data: data.buffer,
readOnly,

View File

@ -1,7 +1,7 @@
import { debug, toHex } from '../util';
import { rom as smartPortRom } from '../roms/cards/smartport';
import { Card, Restorable, byte, word, rom } from '../types';
import { MassStorage, BlockDisk, ENCODING_BLOCK, BlockFormat, MassStorageData } from '../formats/types';
import { MassStorage, BlockDisk, ENCODING_BLOCK, BlockFormat, MassStorageData, DiskFormat } from '../formats/types';
import CPU6502, { CpuState, flags } from '../cpu6502';
import { create2MGFromBlockDisk, HeaderData, read2MGHeader } from '../formats/2mg';
import createBlockDisk from '../formats/block';
@ -129,7 +129,7 @@ export default class SmartPort implements Card, MassStorage<BlockFormat>, Restor
private disks: BlockDisk[] = [];
private busy: boolean[] = [];
private busyTimeout: ReturnType<typeof setTimeout>[] = [];
private ext: string[] = [];
private ext: DiskFormat[] = [];
private metadata: Array<HeaderData | null> = [];
constructor(
@ -522,6 +522,7 @@ export default class SmartPort implements Card, MassStorage<BlockFormat>, Restor
(block) => new Uint8Array(block)
),
encoding: ENCODING_BLOCK,
format: disk.format,
readOnly: disk.readOnly,
metadata: { ...disk.metadata },
};
@ -539,6 +540,7 @@ export default class SmartPort implements Card, MassStorage<BlockFormat>, Restor
(block) => new Uint8Array(block)
),
encoding: ENCODING_BLOCK,
format: disk.format,
readOnly: disk.readOnly,
metadata: { ...disk.metadata },
};
@ -547,7 +549,7 @@ export default class SmartPort implements Card, MassStorage<BlockFormat>, Restor
);
}
setBinary(drive: DriveNumber, name: string, fmt: string, rawData: ArrayBuffer) {
setBinary(drive: DriveNumber, name: string, fmt: BlockFormat, rawData: ArrayBuffer) {
let volume = 254;
let readOnly = false;
if (fmt === '2mg') {
@ -568,7 +570,7 @@ export default class SmartPort implements Card, MassStorage<BlockFormat>, Restor
};
this.ext[drive] = fmt;
this.disks[drive] = createBlockDisk(options);
this.disks[drive] = createBlockDisk(fmt, options);
this.callbacks?.label(drive, name);
return true;

View File

@ -1,4 +1,4 @@
import { BLOCK_FORMATS, DISK_FORMATS, DriveNumber, MassStorage, NIBBLE_FORMATS } from 'js/formats/types';
import { BLOCK_FORMATS, DISK_FORMATS, DriveNumber, FLOPPY_FORMATS, MassStorage } from 'js/formats/types';
import { h, JSX, RefObject } from 'preact';
import { useEffect, useRef } from 'preact/hooks';
import { loadLocalFile } from './util/files';
@ -7,7 +7,7 @@ import { spawn } from './util/promises';
export interface DiskDragTargetProps<T> extends JSX.HTMLAttributes<HTMLDivElement> {
storage: MassStorage<T> | undefined;
drive?: DriveNumber;
formats: typeof NIBBLE_FORMATS
formats: typeof FLOPPY_FORMATS
| typeof BLOCK_FORMATS
| typeof DISK_FORMATS;
dropRef?: RefObject<HTMLElement>;

View File

@ -7,7 +7,7 @@ import { FileModal } from './FileModal';
import styles from './css/DiskII.module.css';
import { DiskDragTarget } from './DiskDragTarget';
import { NIBBLE_FORMATS } from 'js/formats/types';
import { FLOPPY_FORMATS } from 'js/formats/types';
import { DownloadModal } from './DownloadModal';
/**
@ -66,7 +66,7 @@ export const DiskII = ({ disk2, number, on, name, side }: DiskIIProps) => {
className={styles.disk}
storage={disk2}
drive={number}
formats={NIBBLE_FORMATS}
formats={FLOPPY_FORMATS}
onError={setError}
>
<FileModal disk2={disk2} number={number} onClose={doClose} isOpen={modalOpen} />

View File

@ -1,6 +1,6 @@
import { h, Fragment, JSX } from 'preact';
import { useCallback, useEffect, useState } from 'preact/hooks';
import { DiskDescriptor, DriveNumber, NibbleFormat, NIBBLE_FORMATS } from '../formats/types';
import { DiskDescriptor, DriveNumber, FLOPPY_FORMATS, NibbleFormat } from '../formats/types';
import { Modal, ModalContent, ModalFooter } from './Modal';
import { loadLocalNibbleFile, loadJSON, getHashParts, setHashParts } from './util/files';
import DiskII from '../cards/disk2';
@ -15,7 +15,7 @@ import styles from './css/FileModal.module.css';
const DISK_TYPES: FilePickerAcceptType[] = [
{
description: 'Disk Images',
accept: { 'application/octet-stream': NIBBLE_FORMATS.map(x => '.' + x) },
accept: { 'application/octet-stream': FLOPPY_FORMATS.map(x => '.' + x) },
}
];

View File

@ -2,7 +2,7 @@ import { h, Fragment } from 'preact';
import { useMemo } from 'preact/hooks';
import cs from 'classnames';
import { Apple2 as Apple2Impl } from 'js/apple2';
import { BlockDisk, DiskFormat, DriveNumber, MassStorage, NibbleDisk } from 'js/formats/types';
import { BlockDisk, DiskFormat, DriveNumber, FloppyDisk, isBlockDiskFormat, isNibbleDisk, MassStorage } from 'js/formats/types';
import { slot } from 'js/apple2io';
import DiskII from 'js/cards/disk2';
import SmartPort from 'js/cards/smartport';
@ -38,7 +38,7 @@ const formatDate = (date: Date) => {
* @param disk NibbleDisk or BlockDisk
* @returns true if is BlockDisk
*/
function isBlockDisk(disk: NibbleDisk | BlockDisk): disk is BlockDisk {
function isBlockDisk(disk: FloppyDisk | BlockDisk): disk is BlockDisk {
return !!((disk as BlockDisk).blocks);
}
@ -256,7 +256,7 @@ const DiskInfo = ({ massStorage, drive, setFileData }: DiskInfoProps) => {
if (massStorageData) {
const { data, readOnly, ext } = massStorageData;
const { name } = massStorageData.metadata;
let disk: BlockDisk | NibbleDisk | null = null;
let disk: BlockDisk | FloppyDisk | null = null;
if (ext === '2mg') {
disk = createDiskFrom2MG({
name,
@ -277,8 +277,8 @@ const DiskInfo = ({ massStorage, drive, setFileData }: DiskInfoProps) => {
}
}
}
if (!disk) {
disk = createBlockDisk({
if (!disk && isBlockDiskFormat(ext)) {
disk = createBlockDisk(ext, {
name,
rawData: data,
readOnly,
@ -330,7 +330,7 @@ const DiskInfo = ({ massStorage, drive, setFileData }: DiskInfoProps) => {
</div>
);
}
} else {
} else if (isNibbleDisk(disk)) {
const dos = new DOS33(disk);
return (
<div className={styles.volume}>

View File

@ -1,18 +1,18 @@
import { includes, word } from 'js/types';
import { initGamepad } from 'js/ui/gamepad';
import Disk2 from 'js/cards/disk2';
import SmartPort from 'js/cards/smartport';
import Debugger from 'js/debugger';
import {
BlockFormat,
BLOCK_FORMATS,
DISK_FORMATS,
DriveNumber,
FloppyFormat,
FLOPPY_FORMATS,
JSONDisk,
MassStorage,
NibbleFormat,
NIBBLE_FORMATS,
} from 'js/formats/types';
import Disk2 from 'js/cards/disk2';
import SmartPort from 'js/cards/smartport';
import Debugger from 'js/debugger';
import { includes, word } from 'js/types';
import { initGamepad } from 'js/ui/gamepad';
type ProgressCallback = (current: number, total: number) => void;
@ -46,8 +46,8 @@ export const getNameAndExtension = (url: string) => {
};
export const loadLocalFile = (
storage: MassStorage<NibbleFormat|BlockFormat>,
formats: typeof NIBBLE_FORMATS | typeof BLOCK_FORMATS | typeof DISK_FORMATS,
storage: MassStorage<FloppyFormat|BlockFormat>,
formats: typeof FLOPPY_FORMATS | typeof BLOCK_FORMATS | typeof DISK_FORMATS,
number: DriveNumber,
file: File,
) => {
@ -94,7 +94,7 @@ export const loadLocalBlockFile = (smartPort: SmartPort, number: DriveNumber, fi
* @returns true if successful
*/
export const loadLocalNibbleFile = (disk2: Disk2, number: DriveNumber, file: File) => {
return loadLocalFile(disk2, NIBBLE_FORMATS, number, file);
return loadLocalFile(disk2, FLOPPY_FORMATS, number, file);
};
/**
@ -117,7 +117,7 @@ export const loadJSON = async (
throw new Error(`Error loading: ${response.statusText}`);
}
const data = await response.json() as JSONDisk;
if (!includes(NIBBLE_FORMATS, data.type)) {
if (!includes(FLOPPY_FORMATS, data.type)) {
throw new Error(`Type "${data.type}" not recognized.`);
}
disk2.setDisk(number, data);
@ -209,7 +209,7 @@ export const loadHttpNibbleFile = async (
return loadJSON(disk2, number, url);
}
const { name, ext } = getNameAndExtension(url);
if (!includes(NIBBLE_FORMATS, ext)) {
if (!includes(FLOPPY_FORMATS, ext)) {
throw new Error(`Extension "${ext}" not recognized.`);
}
const data = await loadHttpFile(url, signal, onProgress);
@ -241,7 +241,7 @@ export class SmartStorageBroker implements MassStorage<unknown> {
} else {
throw new Error(`Unable to load "${name}"`);
}
} else if (includes(NIBBLE_FORMATS, ext)) {
} else if (includes(FLOPPY_FORMATS, ext)) {
this.disk2.setBinary(drive, name, ext, data);
} else {
throw new Error(`Unable to load "${name}"`);

View File

@ -1,10 +1,10 @@
import { DiskOptions, BlockDisk, ENCODING_BLOCK } from './types';
import { DiskOptions, BlockDisk, ENCODING_BLOCK, BlockFormat } from './types';
/**
* Returns a `Disk` object for a block volume with block-ordered data.
* @param options the disk image and options
*/
export default function createBlockDisk(options: DiskOptions): BlockDisk {
export default function createBlockDisk(fmt: BlockFormat, options: DiskOptions): BlockDisk {
const { rawData, readOnly, name } = options;
if (!rawData) {
@ -20,6 +20,7 @@ export default function createBlockDisk(options: DiskOptions): BlockDisk {
const disk: BlockDisk = {
encoding: ENCODING_BLOCK,
format: fmt,
blocks,
metadata: { name },
readOnly,

View File

@ -1,6 +1,6 @@
import { includes, memory } from '../types';
import { base64_decode } from '../base64';
import { DiskOptions, FloppyDisk, JSONDisk, NibbleFormat, NIBBLE_FORMATS } from './types';
import { BitstreamFormat, DiskOptions, FloppyDisk, FloppyFormat, JSONDisk, NibbleDisk, NibbleFormat, NIBBLE_FORMATS, WozDisk } from './types';
import createDiskFrom2MG from './2mg';
import createDiskFromD13 from './d13';
import createDiskFromDOS from './do';
@ -8,13 +8,13 @@ import createDiskFromProDOS from './po';
import createDiskFromWoz from './woz';
import createDiskFromNibble from './nib';
/**
*
* @param fmt Type of
* @param options
* @returns A nibblized disk
*/
export function createDisk(fmt: NibbleFormat, options: DiskOptions): FloppyDisk | null {
/** Creates a `NibbleDisk` from the given format and options. */
export function createDisk(fmt: NibbleFormat, options: DiskOptions): NibbleDisk | null;
/** Creates a `WozDisk` from the given format and options. */
export function createDisk(fmt: BitstreamFormat, options: DiskOptions): WozDisk | null;
/** Creates a `FloppyDisk` (either a `NibbleDisk` or a `WozDisk`) from the given format and options. */
export function createDisk(fmt: FloppyFormat, options: DiskOptions): FloppyDisk | null;
export function createDisk(fmt: FloppyFormat, options: DiskOptions): FloppyDisk | null {
let disk: FloppyDisk | null = null;
switch (fmt) {
@ -42,7 +42,8 @@ export function createDisk(fmt: NibbleFormat, options: DiskOptions): FloppyDisk
return disk;
}
export function createDiskFromJsonDisk(disk: JSONDisk): FloppyDisk | null {
/** Creates a NibbleDisk from JSON */
export function createDiskFromJsonDisk(disk: JSONDisk): NibbleDisk | null {
const fmt = disk.type;
const readOnly = disk.readOnly;
const name = disk.name;

View File

@ -1,7 +1,7 @@
import { bit, byte, memory } from '../types';
import { base64_decode, base64_encode } from '../base64';
import { bytify, debug, toHex } from '../util';
import { NibbleDisk, ENCODING_NIBBLE, JSONDisk } from './types';
import { NibbleDisk, ENCODING_NIBBLE, JSONDisk, isNibbleDiskFormat } from './types';
/**
* DOS 3.3 Physical sector order (index is physical sector, value is DOS sector).
@ -550,6 +550,9 @@ export function jsonDecode(data: string): NibbleDisk {
}
tracks[t] = bytify(track);
}
if (!isNibbleDiskFormat(json.type)) {
throw new Error(`JSON disks of type ${json.type} are not supported`);
}
const disk: NibbleDisk = {
volume: v,
format: json.type,

View File

@ -69,17 +69,19 @@ export const ENCODING_BITSTREAM = 'bitstream';
export const ENCODING_BLOCK = 'block';
export interface FloppyDisk extends Disk {
encoding: typeof ENCODING_NIBBLE | typeof ENCODING_BITSTREAM;
tracks: memory[];
}
export interface NibbleDisk extends FloppyDisk {
encoding: typeof ENCODING_NIBBLE;
format: DiskFormat;
format: Exclude<NibbleFormat, 'woz'>;
volume: byte;
}
export interface WozDisk extends FloppyDisk {
encoding: typeof ENCODING_BITSTREAM;
format: 'woz';
trackMap: number[];
rawTracks: Uint8Array[];
info: InfoChunk | undefined;
@ -87,14 +89,13 @@ export interface WozDisk extends FloppyDisk {
export interface BlockDisk extends Disk {
encoding: typeof ENCODING_BLOCK;
format: BlockFormat;
blocks: Uint8Array[];
}
/**
* File types supported by the disk format processors and
* block devices.
* File types supported by floppy devices in nibble mode.
*/
export const NIBBLE_FORMATS = [
'2mg',
'd13',
@ -102,21 +103,61 @@ export const NIBBLE_FORMATS = [
'dsk',
'po',
'nib',
'woz'
] as const;
/**
* File types supported by floppy devices in bitstream mode.
*/
export const BITSTREAM_FORMATS = [
'woz',
] as const;
/**
* All file types supported by floppy devices.
*/
export const FLOPPY_FORMATS = [
...NIBBLE_FORMATS,
...BITSTREAM_FORMATS,
] as const;
/**
* File types supported by block devices.
*/
export const BLOCK_FORMATS = [
'2mg',
'hdv',
'po',
] as const;
export const DISK_FORMATS = [...NIBBLE_FORMATS, ...BLOCK_FORMATS] as const;
/**
* All supported disk formats.
*/
export const DISK_FORMATS = [
...FLOPPY_FORMATS,
...BLOCK_FORMATS,
] as const;
export type FloppyFormat = MemberOf<typeof FLOPPY_FORMATS>;
export type NibbleFormat = MemberOf<typeof NIBBLE_FORMATS>;
export type BitstreamFormat = 'woz';
export type BlockFormat = MemberOf<typeof BLOCK_FORMATS>;
export type DiskFormat = MemberOf<typeof DISK_FORMATS>;
/** Type guard for nibble disk formats. */
export function isNibbleDiskFormat(f: DiskFormat): f is NibbleFormat {
return f in NIBBLE_FORMATS;
}
/** Type guard for block disk formats. */
export function isBlockDiskFormat(f: DiskFormat): f is BlockFormat {
return f in BLOCK_FORMATS;
}
/** Type guard for NibbleDisks */
export function isNibbleDisk(disk: Disk): disk is NibbleDisk {
return (disk as NibbleDisk)?.encoding === ENCODING_NIBBLE;
}
/**
* Base format for JSON defined disks
*/
@ -180,7 +221,7 @@ export interface ProcessBinaryMessage {
type: typeof PROCESS_BINARY;
payload: {
drive: DriveNumber;
fmt: NibbleFormat;
fmt: FloppyFormat;
options: DiskOptions;
};
}
@ -227,7 +268,7 @@ export type FormatWorkerResponse =
export interface MassStorageData {
metadata: DiskMetadata;
ext: string;
ext: DiskFormat;
readOnly: boolean;
volume?: byte;
data: ArrayBuffer;

View File

@ -293,6 +293,7 @@ export default function createDiskFromWoz(options: DiskOptions): WozDisk {
const disk: WozDisk = {
encoding: ENCODING_BITSTREAM,
format: 'woz',
trackMap: tmap?.trackMap || [],
tracks: trks?.tracks || [],
rawTracks: trks?.rawTracks || [],

View File

@ -11,10 +11,10 @@ import {
DriveNumber,
DRIVE_NUMBERS,
MassStorage,
NIBBLE_FORMATS,
JSONBinaryImage,
JSONDisk,
BlockFormat
BlockFormat,
FLOPPY_FORMATS
} from '../formats/types';
import { initGamepad } from './gamepad';
import KeyBoard from './keyboard';
@ -392,7 +392,7 @@ function doLoadLocalDisk(drive: DriveNumber, file: File) {
}
} else {
if (
includes(NIBBLE_FORMATS, ext) &&
includes(FLOPPY_FORMATS, ext) &&
_disk2.setBinary(drive, name, ext, result)
) {
initGamepad();
@ -459,7 +459,7 @@ export function doLoadHTTP(drive: DriveNumber, url?: string) {
}
} else {
if (
includes(NIBBLE_FORMATS, ext) &&
includes(FLOPPY_FORMATS, ext) &&
_disk2.setBinary(drive, name, ext, data)
) {
initGamepad();

View File

@ -193,6 +193,7 @@ describe('2mg format', () => {
metadata: { name: 'Good disk' },
readOnly: false,
encoding: ENCODING_BLOCK,
format: 'hdv',
};
const image = create2MGFromBlockDisk(header, disk);
expect(VALID_PRODOS_IMAGE.buffer).toEqual(image);

View File

@ -24,6 +24,7 @@ describe('woz', () => {
metadata: { name: 'Mock Woz 1' },
readOnly: true,
encoding: ENCODING_BITSTREAM,
format: 'woz',
trackMap: mockTMAP,
rawTracks: [new Uint8Array([
1, 1, 0, 1, 0, 1, 0, 1,
@ -64,6 +65,7 @@ describe('woz', () => {
},
readOnly: true,
encoding: ENCODING_BITSTREAM,
format: 'woz',
trackMap: mockTMAP,
rawTracks: [new Uint8Array([
1, 1, 0, 1, 0, 1, 0, 1,