Skip to content

Commit

Permalink
Use ProDOS volume for name, other cleanup
Browse files Browse the repository at this point in the history
  • Loading branch information
whscullin committed May 18, 2024
1 parent 419401d commit dade0d9
Show file tree
Hide file tree
Showing 7 changed files with 132 additions and 86 deletions.
22 changes: 22 additions & 0 deletions js/cards/smartport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ import {
} from '../formats/2mg';
import createBlockDisk from '../formats/block';
import { DriveNumber } from '../formats/types';
import { VDH_BLOCK, VDH_OFFSETS } from 'js/formats/prodos/vdh';
import { readFileName } from 'js/formats/prodos/utils';

const ID = 'SMARTPORT.J.S';

Expand Down Expand Up @@ -604,6 +606,7 @@ export default class SmartPort
} else {
this.metadata[driveNo] = null;
}

const options = {
rawData,
name,
Expand All @@ -613,6 +616,8 @@ export default class SmartPort

this.ext[driveNo] = fmt;
this.disks[driveNo] = createBlockDisk(fmt, options);
name = this.getVolumeName(driveNo) || name;

this.callbacks?.label(driveNo, name);

return true;
Expand Down Expand Up @@ -644,4 +649,21 @@ export default class SmartPort
readOnly,
};
}

getVolumeName(driveNo: number): string | null {
const buffer = this.disks[driveNo]?.blocks[VDH_BLOCK]?.buffer;
if (!buffer) {
return null;
}
const block = new DataView(buffer);

const nameLength = block.getUint8(VDH_OFFSETS.NAME_LENGTH) & 0xf;
const caseBits = block.getUint8(VDH_OFFSETS.CASE_BITS);
return readFileName(
block,
VDH_OFFSETS.VOLUME_NAME,
nameLength,
caseBits
);
}
}
30 changes: 7 additions & 23 deletions js/components/BlockDisk.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { h } from 'preact';
import { useCallback, useState } from 'preact/hooks';
import { useEffect, useState } from 'preact/hooks';
import cs from 'classnames';
import { BLOCK_FORMATS } from 'js/formats/types';
import SmartPort from '../cards/smartport';
Expand Down Expand Up @@ -30,36 +30,20 @@ export interface BlockDiskProps extends BlockDiskData {
/**
* BlockDisk component
*
* Includes drive light, disk name and side, and UI for loading disks.
* Includes drive light, disk name and UI for loading disks.
*
* @param smartPort SmartPort object
* @param number Drive 1 or 2
* @param on Active state
* @param name Disk name identifier
* @param side Disk side identifier
*
* @returns BlockDisk component
*/
export const BlockDisk = ({ smartPort, number, on, name }: BlockDiskProps) => {
const [modalOpen, setModalOpen] = useState(false);
const [downloadModalOpen, setDownloadModalOpen] = useState(false);
const [error, setError] = useState<unknown>();

const doClose = useCallback(() => {
setModalOpen(false);
}, []);

const onOpenModal = useCallback(() => {
setModalOpen(true);
}, []);

const doCloseDownload = useCallback(() => {
setDownloadModalOpen(false);
}, []);

const onOpenDownloadModal = useCallback(() => {
setDownloadModalOpen(true);
}, []);

return (
<DiskDragTarget
className={styles.disk}
Expand All @@ -72,27 +56,27 @@ export const BlockDisk = ({ smartPort, number, on, name }: BlockDiskProps) => {
<BlockFileModal
smartPort={smartPort}
driveNo={number}
onClose={doClose}
onClose={() => setModalOpen(false)}
isOpen={modalOpen}
/>
<DownloadModal
driveNo={number}
massStorage={smartPort}
isOpen={downloadModalOpen}
onClose={doCloseDownload}
onClose={() => setDownloadModalOpen(false)}
/>
<div
id={`disk${number}`}
className={cs(styles.diskLight, { [styles.on]: on })}
/>
<ControlButton
title="Load Disk"
onClick={onOpenModal}
onClick={() => setModalOpen(true)}
icon="folder-open"
/>
<ControlButton
title="Save Disk"
onClick={onOpenDownloadModal}
onClick={() => setDownloadModalOpen(false)}
icon="save"
/>
<div id={`disk-label${number}`} className={styles.diskLabel}>
Expand Down
4 changes: 2 additions & 2 deletions js/formats/create_disk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export function createDisk(

switch (fmt) {
case '2mg':
disk = createDiskFrom2MG(options);
disk = createDiskFrom2MG(options) as FloppyDisk;
break;
case 'd13':
disk = createDiskFromD13(options);
Expand All @@ -54,7 +54,7 @@ export function createDisk(
disk = createDiskFromNibble(options);
break;
case 'po':
disk = createDiskFromProDOS(options);
disk = createDiskFromProDOS(options) as FloppyDisk;
break;
case 'woz':
disk = createDiskFromWoz(options);
Expand Down
91 changes: 65 additions & 26 deletions js/formats/po.ts
Original file line number Diff line number Diff line change
@@ -1,42 +1,81 @@
import { explodeSector16, PO } from './format_utils';
import { bytify } from '../util';
import type { byte } from '../types';
import { NibbleDisk, DiskOptions, ENCODING_NIBBLE } from './types';
import {
NibbleDisk,
DiskOptions,
ENCODING_NIBBLE,
BlockDisk,
FloppyDisk,
ENCODING_BLOCK,
} from './types';
import { BLOCK_SIZE } from './prodos/constants';

/**
* Returns a `Disk` object from ProDOS-ordered image data.
* @param options the disk image and options
* @returns A nibblized disk
*/
export default function createDiskFromProDOS(options: DiskOptions) {
export default function createDiskFromProDOS(
options: DiskOptions
): BlockDisk | FloppyDisk {
const { data, name, side, rawData, volume, readOnly } = options;
const disk: NibbleDisk = {
format: 'po',
encoding: ENCODING_NIBBLE,
metadata: { name, side },
volume: volume || 254,
tracks: [],
readOnly: readOnly || false,
};
let disk: BlockDisk | NibbleDisk;
if (rawData && rawData.byteLength > 140 * 1025) {
disk = {
format: 'po',
encoding: ENCODING_BLOCK,
metadata: { name, side },
readOnly: readOnly || false,
blocks: [],
} as BlockDisk;
for (
let offset = 0;
offset < rawData.byteLength;
offset += BLOCK_SIZE
) {
disk.blocks.push(
new Uint8Array(rawData.slice(offset, offset + BLOCK_SIZE))
);
}
} else {
disk = {
format: 'po',
encoding: ENCODING_NIBBLE,
metadata: { name, side },
volume: volume || 254,
tracks: [],
readOnly: readOnly || false,
} as NibbleDisk;

for (let physical_track = 0; physical_track < 35; physical_track++) {
let track: byte[] = [];
for (let physical_sector = 0; physical_sector < 16; physical_sector++) {
const prodos_sector = PO[physical_sector];
let sector;
if (rawData) {
const off = (16 * physical_track + prodos_sector) * 256;
sector = new Uint8Array(rawData.slice(off, off + 256));
} else if (data) {
sector = data[physical_track][prodos_sector];
} else {
throw new Error('Requires data or rawData');
for (let physical_track = 0; physical_track < 35; physical_track++) {
let track: byte[] = [];
for (
let physical_sector = 0;
physical_sector < 16;
physical_sector++
) {
const prodos_sector = PO[physical_sector];
let sector;
if (rawData) {
const off = (16 * physical_track + prodos_sector) * 256;
sector = new Uint8Array(rawData.slice(off, off + 256));
} else if (data) {
sector = data[physical_track][prodos_sector];
} else {
throw new Error('Requires data or rawData');
}
track = track.concat(
explodeSector16(
volume,
physical_track,
physical_sector,
sector
)
);
}
track = track.concat(
explodeSector16(volume, physical_track, physical_sector, sector)
);
disk.tracks[physical_track] = bytify(track);
}
disk.tracks[physical_track] = bytify(track);
}

return disk;
Expand Down
34 changes: 1 addition & 33 deletions js/formats/prodos/index.ts
Original file line number Diff line number Diff line change
@@ -1,33 +1 @@
import { VDH } from './vdh';
import { BitMap } from './bit_map';
import { BlockDisk } from '../types';

export class ProDOSVolume {
_vdh: VDH;
_bitMap: BitMap;

constructor(private _disk: BlockDisk) {}

disk() {
return this._disk;
}

blocks() {
return this._disk.blocks;
}

vdh() {
if (!this._vdh) {
this._vdh = new VDH(this);
this._vdh.read();
}
return this._vdh;
}

bitMap() {
if (!this._bitMap) {
this._bitMap = new BitMap(this);
}
return this._bitMap;
}
}
export * from './prodos_volume';
33 changes: 33 additions & 0 deletions js/formats/prodos/prodos_volume.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { VDH } from './vdh';
import { BitMap } from './bit_map';
import { BlockDisk } from '../types';

export class ProDOSVolume {
_vdh: VDH;
_bitMap: BitMap;

constructor(private _disk: BlockDisk) {}

disk() {
return this._disk;
}

blocks() {
return this._disk.blocks;
}

vdh() {
if (!this._vdh) {
this._vdh = new VDH(this);
this._vdh.read();
}
return this._vdh;
}

bitMap() {
if (!this._bitMap) {
this._bitMap = new BitMap(this);
}
return this._bitMap;
}
}
4 changes: 2 additions & 2 deletions js/formats/prodos/vdh.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ import { STORAGE_TYPES, ACCESS_TYPES } from './constants';
import { byte, word } from 'js/types';
import { ProDOSVolume } from '.';

const VDH_BLOCK = 2;
const VDH_OFFSETS = {
export const VDH_BLOCK = 2;
export const VDH_OFFSETS = {
PREV: 0x00,
NEXT: 0x02,
STORAGE_TYPE: 0x04,
Expand Down

0 comments on commit dade0d9

Please sign in to comment.