mirror of
https://github.com/AppleCommander/AppleCommander.git
synced 2024-12-22 23:29:34 +00:00
These classes support the image order and image layout concept.
This commit is contained in:
parent
b8d0a62183
commit
33cd86911f
@ -0,0 +1,115 @@
|
||||
/*
|
||||
* AppleCommander - An Apple ][ image utility.
|
||||
* Copyright (C) 2003 by Robert Greene
|
||||
* robgreene at users.sourceforge.net
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
package com.webcodepro.applecommander.storage.physical;
|
||||
|
||||
/**
|
||||
* Manages the layout of the physical disk. This hides implementation details,
|
||||
* such as if the disk is in 2IMG order.
|
||||
* <p>
|
||||
* @author Rob Greene (RobGreene@users.sourceforge.net)
|
||||
*/
|
||||
public class ByteArrayImageLayout {
|
||||
/**
|
||||
* This is the physical copy of the disk image which a particular
|
||||
* implementation of ImageOrder will interpret.
|
||||
*/
|
||||
private byte[] diskImage;
|
||||
/**
|
||||
* Indicates if the disk image has changed.
|
||||
*/
|
||||
private boolean changed;
|
||||
|
||||
/**
|
||||
* Construct a ByteArrayImageLayout.
|
||||
*/
|
||||
public ByteArrayImageLayout(byte[] diskImage) {
|
||||
setDiskImage(diskImage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a ByteArrayImageLayout.
|
||||
*/
|
||||
public ByteArrayImageLayout(byte[] diskImage, boolean changed) {
|
||||
setDiskImage(diskImage);
|
||||
this.changed = changed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a ByteArrayImageLayout.
|
||||
*/
|
||||
public ByteArrayImageLayout(int size) {
|
||||
diskImage = new byte[size];
|
||||
changed = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the physical disk image.
|
||||
*/
|
||||
public byte[] getDiskImage() {
|
||||
return diskImage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the physical disk image.
|
||||
*/
|
||||
public void setDiskImage(byte[] diskImage) {
|
||||
this.diskImage = diskImage;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Answer with the physical size of this disk volume.
|
||||
*/
|
||||
public int getPhysicalSize() {
|
||||
return (diskImage != null) ? diskImage.length : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract a portion of the disk image.
|
||||
*/
|
||||
public byte[] readBytes(int start, int length) {
|
||||
byte[] buffer = new byte[length];
|
||||
System.arraycopy(diskImage, start, buffer, 0, length);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write data to the disk image.
|
||||
*/
|
||||
public void writeBytes(int start, byte[] bytes) {
|
||||
changed = true;
|
||||
System.arraycopy(bytes, 0, diskImage, start, bytes.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if the disk has changed. Triggered when data is
|
||||
* written and cleared when data is saved.
|
||||
*/
|
||||
public boolean hasChanged() {
|
||||
return changed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the changed indicator.
|
||||
*/
|
||||
public void setChanged(boolean changed) {
|
||||
this.changed = changed;
|
||||
}
|
||||
}
|
122
src/com/webcodepro/applecommander/storage/physical/DosOrder.java
Normal file
122
src/com/webcodepro/applecommander/storage/physical/DosOrder.java
Normal file
@ -0,0 +1,122 @@
|
||||
/*
|
||||
* AppleCommander - An Apple ][ image utility.
|
||||
* Copyright (C) 2003 by Robert Greene
|
||||
* robgreene at users.sourceforge.net
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
package com.webcodepro.applecommander.storage.physical;
|
||||
|
||||
import com.webcodepro.applecommander.storage.Disk;
|
||||
|
||||
/**
|
||||
* Supports disk images stored in DOS physical order.
|
||||
* <p>
|
||||
* @author Rob Greene (RobGreene@users.sourceforge.net)
|
||||
*/
|
||||
public class DosOrder extends ImageOrder {
|
||||
/**
|
||||
* Construct a DosOrder.
|
||||
*/
|
||||
public DosOrder(ByteArrayImageLayout diskImageManager) {
|
||||
super(diskImageManager);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates that this device is block ordered.
|
||||
*/
|
||||
public boolean isBlockDevice() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates that this device is track and sector ordered.
|
||||
*/
|
||||
public boolean isTrackAndSectorDevice() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the specified sector.
|
||||
*/
|
||||
public byte[] readSector(int track, int sector) throws IllegalArgumentException {
|
||||
return readBytes(getOffset(track, sector), Disk.SECTOR_SIZE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the specified sector.
|
||||
*/
|
||||
public void writeSector(int track, int sector, byte[] bytes)
|
||||
throws IllegalArgumentException {
|
||||
writeBytes(getOffset(track, sector), bytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the track and sector offset into the disk image.
|
||||
* This takes into account what type of format is being dealt
|
||||
* with.
|
||||
*/
|
||||
protected int getOffset(int track, int sector) throws IllegalArgumentException {
|
||||
if (!isSizeApprox(Disk.APPLE_140KB_DISK)
|
||||
&& !isSizeApprox(Disk.APPLE_800KB_DISK)
|
||||
&& !isSizeApprox(Disk.APPLE_800KB_2IMG_DISK)
|
||||
&& track != 0 && sector != 0) { // HACK: Allows boot sector writing
|
||||
throw new IllegalArgumentException("Unrecognized DOS format!");
|
||||
}
|
||||
int offset = (track * getSectorsPerTrack() + sector) * Disk.SECTOR_SIZE;
|
||||
if (offset > getPhysicalSize()) {
|
||||
throw new IllegalArgumentException(
|
||||
"The track (" + track + ") and sector (" + sector
|
||||
+ ") do not match the disk image size.");
|
||||
} else {
|
||||
return offset;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the block from the disk image.
|
||||
* Note: Defined in terms of reading sectors.
|
||||
*/
|
||||
public byte[] readBlock(int block) {
|
||||
int track = block / 8;
|
||||
int sectorIndex = block % 8;
|
||||
int[] sectorMapping1 = { 0, 13, 11, 9, 7, 5, 3, 1 };
|
||||
int[] sectorMapping2 = { 14, 12, 10, 8, 6, 4, 2, 15 };
|
||||
int sector1 = sectorMapping1[sectorIndex];
|
||||
int sector2 = sectorMapping2[sectorIndex];
|
||||
byte[] blockData = new byte[Disk.BLOCK_SIZE];
|
||||
System.arraycopy(readSector(track, sector1), 0, blockData, 0, Disk.SECTOR_SIZE);
|
||||
System.arraycopy(readSector(track, sector2), 0, blockData, Disk.SECTOR_SIZE, Disk.SECTOR_SIZE);
|
||||
return blockData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the block to the disk image.
|
||||
* Note: Defined in terms of reading sectors.
|
||||
*/
|
||||
public void writeBlock(int block, byte[] data) {
|
||||
int track = block / 8;
|
||||
int sectorIndex = block % 8;
|
||||
int[] sectorMapping1 = { 0, 13, 11, 9, 7, 5, 3, 1 };
|
||||
int[] sectorMapping2 = { 14, 12, 10, 8, 6, 4, 2, 15 };
|
||||
int sector1 = sectorMapping1[sectorIndex];
|
||||
int sector2 = sectorMapping2[sectorIndex];
|
||||
byte[] sectorData = new byte[Disk.SECTOR_SIZE];
|
||||
System.arraycopy(data, 0, sectorData, 0, Disk.SECTOR_SIZE);
|
||||
writeSector(track, sector1, sectorData);
|
||||
System.arraycopy(data, Disk.SECTOR_SIZE, sectorData, 0, Disk.SECTOR_SIZE);
|
||||
writeSector(track, sector2, sectorData);
|
||||
}
|
||||
}
|
@ -0,0 +1,179 @@
|
||||
/*
|
||||
* AppleCommander - An Apple ][ image utility.
|
||||
* Copyright (C) 2003 by Robert Greene
|
||||
* robgreene at users.sourceforge.net
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
package com.webcodepro.applecommander.storage.physical;
|
||||
|
||||
import com.webcodepro.applecommander.storage.Disk;
|
||||
|
||||
/**
|
||||
* Manages the interface between the physical disk image order and the
|
||||
* logical operating system specific order. These management objects
|
||||
* are intended to be hidden by Disk itself, although the ImageOrder
|
||||
* may be changed (overridden).
|
||||
* <p>
|
||||
* To implement this class, over-ride the block-oriented methods
|
||||
* (readBlock, writeBlock) or the track and sector-oriented
|
||||
* methods (readSector, writeSector).
|
||||
* Ensure that isBlockDevice or isTrackAndSectorDevice is set
|
||||
* appropriately.
|
||||
* <p>
|
||||
* Note that a block is generally assumed to be an Apple ProDOS
|
||||
* (or Apple Pascal) formatted volume where a block is 512 bytes.
|
||||
* The track and sector device is generally a 140K 5.25" disk,
|
||||
* although it may be an 800K 3.5" disk with two 400K DOS volumes.
|
||||
* In either case, the sector size will be 256 bytes.
|
||||
* <p>
|
||||
* At this time, the RDOS block of 256 bytes is managed by the
|
||||
* RdosFormatDisk, and the 1024 byte CP/M block is managed by the
|
||||
* CpmFormatDisk (the CP/M sector of 128 bytes is largely ignored).
|
||||
* <p>
|
||||
* Design note: The physical order could alternatively be implemented
|
||||
* with a BlockPhysicalOrder structure which includes ProdosOrder
|
||||
* and a Track and Sector adapter, as well as a TrackAndSectorPhysicalOrder
|
||||
* which includes a NibblePhysicalOrder and DosOrder as well as
|
||||
* a Block adapter class. This way, Disk contains two separate classes
|
||||
* (block as well as a track/sector) to manage the disk.
|
||||
* <p>
|
||||
* @author Rob Greene (RobGreene@users.sourceforge.net)
|
||||
*/
|
||||
public abstract class ImageOrder {
|
||||
/**
|
||||
* This is the physical copy of the disk image which a particular
|
||||
* implementation of ImageOrder will interpret.
|
||||
*/
|
||||
private ByteArrayImageLayout diskImageManager;
|
||||
|
||||
/**
|
||||
* Construct a ImageOrder.
|
||||
*/
|
||||
public ImageOrder(ByteArrayImageLayout diskImageManager) {
|
||||
setDiskImageManager(diskImageManager);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the physical disk image.
|
||||
*/
|
||||
public ByteArrayImageLayout getDiskImageManager() {
|
||||
return diskImageManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Answer with the physical size of this disk volume.
|
||||
*/
|
||||
public int getPhysicalSize() {
|
||||
return diskImageManager.getPhysicalSize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the physical disk image.
|
||||
*/
|
||||
public void setDiskImageManager(ByteArrayImageLayout diskImageManager) {
|
||||
this.diskImageManager = diskImageManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract a portion of the disk image.
|
||||
*/
|
||||
public byte[] readBytes(int start, int length) {
|
||||
return diskImageManager.readBytes(start, length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write data to the disk image.
|
||||
*/
|
||||
public void writeBytes(int start, byte[] bytes) {
|
||||
diskImageManager.writeBytes(start, bytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Answer with the number of blocks on this device.
|
||||
*/
|
||||
public int getBlocksOnDevice() {
|
||||
return getPhysicalSize() / Disk.BLOCK_SIZE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the block from the disk image.
|
||||
*/
|
||||
public abstract byte[] readBlock(int block);
|
||||
|
||||
/**
|
||||
* Write the block to the disk image.
|
||||
*/
|
||||
public abstract void writeBlock(int block, byte[] data);
|
||||
|
||||
/**
|
||||
* Indicates that this device is block ordered.
|
||||
*/
|
||||
public abstract boolean isBlockDevice();
|
||||
|
||||
/**
|
||||
* Indicates that this device is track and sector ordered.
|
||||
*/
|
||||
public abstract boolean isTrackAndSectorDevice();
|
||||
|
||||
/**
|
||||
* Answer with the number of tracks on this device.
|
||||
*/
|
||||
public int getTracksPerDisk() {
|
||||
return getPhysicalSize() / (getSectorsPerTrack() * Disk.SECTOR_SIZE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Answer with the number of sectors per track on this device.
|
||||
*/
|
||||
public int getSectorsPerTrack() {
|
||||
if (isSizeApprox(Disk.APPLE_800KB_DISK)
|
||||
|| isSizeApprox(Disk.APPLE_800KB_2IMG_DISK)) {
|
||||
return 32;
|
||||
}
|
||||
return 16;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the specified sector.
|
||||
*/
|
||||
public abstract byte[] readSector(int track, int sector) throws IllegalArgumentException;
|
||||
|
||||
/**
|
||||
* Write the specified sector.
|
||||
*/
|
||||
public abstract void writeSector(int track, int sector, byte[] bytes) throws IllegalArgumentException;
|
||||
|
||||
/**
|
||||
* Indicates if the physical disk is approximately this size.
|
||||
* Currently hardcoded to allow up to 10 extra bytes at the end of a
|
||||
* disk image. Must be at least the requested size!
|
||||
*/
|
||||
public boolean isSizeApprox(int value) {
|
||||
return getPhysicalSize() >= value && getPhysicalSize() <= value + 10;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format the media. Formatting at the ImageOrder level deals with
|
||||
* low-level issues. A typical ordering just needs to have the image
|
||||
* "wiped," and that is the assumed implementation. However, specialized
|
||||
* orders - such as a nibbilized disk - need to lay down track and
|
||||
* sector markers.
|
||||
*/
|
||||
public void format() {
|
||||
int size = diskImageManager.getPhysicalSize();
|
||||
diskImageManager.setDiskImage(new byte[size]);
|
||||
}
|
||||
}
|
@ -0,0 +1,361 @@
|
||||
/*
|
||||
* AppleCommander - An Apple ][ image utility.
|
||||
* Copyright (C) 2003 by Robert Greene
|
||||
* robgreene at users.sourceforge.net
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
package com.webcodepro.applecommander.storage.physical;
|
||||
|
||||
import com.webcodepro.applecommander.util.AppleUtil;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Supports disk images stored in nibbilized DOS physical order.
|
||||
* <p>
|
||||
* @author Rob Greene (RobGreene@users.sourceforge.net)
|
||||
*/
|
||||
public class NibbleOrder extends DosOrder {
|
||||
/**
|
||||
* This is the 6 and 2 write translate table, as given in Beneath
|
||||
* Apple DOS, pg 3-21.
|
||||
*/
|
||||
private static int[] writeTranslateTable = {
|
||||
//$0 $1 $2 $3 $4 $5 $6 $7
|
||||
0x96, 0x97, 0x9a, 0x9b, 0x9d, 0x9e, 0x9f, 0xa6, // +$00
|
||||
0xa7, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb2, 0xb3, // +$08
|
||||
0xb4, 0xb5, 0xb6, 0xb7, 0xb9, 0xba, 0xbb, 0xbc, // +$10
|
||||
0xbd, 0xbe, 0xbf, 0xcb, 0xcd, 0xce, 0xcf, 0xd3, // +$18
|
||||
0xd6, 0xd7, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, // +$20
|
||||
0xdf, 0xe5, 0xe6, 0xe7, 0xe9, 0xea, 0xeb, 0xec, // +$28
|
||||
0xed, 0xee, 0xef, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, // +$30
|
||||
0xf7, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff // +$38
|
||||
};
|
||||
/**
|
||||
* This maps a DOS 3.3 sector to a physical sector.
|
||||
* (readSector and writeSector work off of the DOS 3.3
|
||||
* sector numbering.)
|
||||
*/
|
||||
private static int[] sectorInterleave = {
|
||||
0x0, 0xd, 0xb, 0x9, 0x7, 0x5, 0x3, 0x1,
|
||||
0xe, 0xc, 0xa, 0x8, 0x6, 0x4, 0x2, 0xf
|
||||
};
|
||||
|
||||
/**
|
||||
* The read translation table. Constructed from the write
|
||||
* translate table. Used to decode a disk byte into a value
|
||||
* from 0x00 to 0x3f which is further decoded...
|
||||
*/
|
||||
public int[] readTranslateTable;
|
||||
|
||||
/**
|
||||
* Construct a NibbleOrder.
|
||||
*/
|
||||
public NibbleOrder(ByteArrayImageLayout diskImageManager) {
|
||||
super(diskImageManager);
|
||||
// Construct the read translation table:
|
||||
readTranslateTable = new int[256];
|
||||
for (int i=0; i<writeTranslateTable.length; i++) {
|
||||
readTranslateTable[writeTranslateTable[i]] = i;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read nibbilized track data.
|
||||
*/
|
||||
protected byte[] readTrackData(int track) {
|
||||
int trackSize = getPhysicalSize() / getTracksPerDisk();
|
||||
return readBytes(track * trackSize, trackSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write nibbilized track data.
|
||||
*/
|
||||
protected void writeTrackData(int track, byte[] trackData) {
|
||||
int trackSize = getPhysicalSize() / getTracksPerDisk();
|
||||
writeBytes(track * trackSize, trackData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the specified sector. The primary source of information
|
||||
* for this process is directly from Beneath Apple DOS, chapter 3.
|
||||
*/
|
||||
public byte[] readSector(int track, int dosSector) throws IllegalArgumentException {
|
||||
int sector = sectorInterleave[dosSector];
|
||||
// 1. read track
|
||||
byte[] trackData = readTrackData(track);
|
||||
// 2. locate address field for this track and sector
|
||||
int offset = 0;
|
||||
byte[] addressField = new byte[14];
|
||||
boolean found = false;
|
||||
while (!found && offset < trackData.length) {
|
||||
int nextOffset = locateField(0xd5, 0xaa, 0x96, trackData, addressField, offset);
|
||||
if (nextOffset < offset) { // we wrapped!
|
||||
throw new IllegalArgumentException("Unable to locate physical sector "
|
||||
+ sector + " on track " + track);
|
||||
}
|
||||
offset = nextOffset;
|
||||
int t = decodeOddEven(addressField, 5);
|
||||
int s = decodeOddEven(addressField, 7);
|
||||
found = (t == track && s == sector);
|
||||
}
|
||||
if (!found) {
|
||||
throw new IllegalArgumentException("Unable to locate physical sector "
|
||||
+ sector + " on track " + track + "(#2)");
|
||||
}
|
||||
// 3. read data field that immediately follows the address field
|
||||
byte[] dataField = new byte[349];
|
||||
locateField(0xd5, 0xaa, 0xad, trackData, dataField, offset);
|
||||
// 4. translate data field
|
||||
byte[] buffer = new byte[342];
|
||||
int checksum = 0;
|
||||
for (int i=0; i<buffer.length; i++) {
|
||||
int b = AppleUtil.getUnsignedByte(dataField[i+3]);
|
||||
checksum ^= readTranslateTable[b]; // XOR
|
||||
if (i < 86) {
|
||||
buffer[buffer.length - i - 1] = (byte) checksum;
|
||||
} else {
|
||||
buffer[i - 86] = (byte) checksum;
|
||||
}
|
||||
}
|
||||
checksum ^= readTranslateTable[AppleUtil.getUnsignedByte(dataField[345])];
|
||||
if (checksum != 0) return null; // BAD DATA
|
||||
// 5. decode data field
|
||||
byte[] sectorData = new byte[256];
|
||||
for (int i=0; i<sectorData.length; i++) {
|
||||
int b1 = AppleUtil.getUnsignedByte(buffer[i]);
|
||||
int lowerBits = buffer.length - (i % 86) - 1;
|
||||
int b2 = AppleUtil.getUnsignedByte(buffer[lowerBits]);
|
||||
int shiftPairs = (i / 86) * 2;
|
||||
// shift b1 up by 2 bytes (contains bits 7-2)
|
||||
// align 2 bits in b2 appropriately, mask off anything but
|
||||
// bits 0 and 1 and then REVERSE THEM...
|
||||
int[] reverseValues = { 0x0, 0x2, 0x1, 0x3 };
|
||||
int b = (b1 << 2) | reverseValues[(b2 >> shiftPairs) & 0x03];
|
||||
sectorData[i] = (byte) b;
|
||||
}
|
||||
return sectorData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Locate a field on the track. These are identified by a 3 byte unique
|
||||
* signature. Because of the way in which disk bytes are captured, we need
|
||||
* to wrap around the track to ensure all sequences of bytes are accounted for.
|
||||
* <p>
|
||||
* This methid fills fieldData as well as returning the last position referenced
|
||||
* in the track buffer.
|
||||
*/
|
||||
protected int locateField(int byte1, int byte2, int byte3, byte[] trackData, byte[] fieldData, int startingOffset) {
|
||||
int i = startingOffset; // logical position in track buffer (can wrap)
|
||||
int position = 0; // physical position in field buffer
|
||||
while (i < trackData.length + fieldData.length) {
|
||||
int offset = i % trackData.length; // physical posistion in track buffer
|
||||
int b = AppleUtil.getUnsignedByte(trackData[offset]);
|
||||
if (position == 0 && b == byte1) {
|
||||
fieldData[position++] = (byte) b;
|
||||
} else if (position == 1 && b == byte2) {
|
||||
fieldData[position++] = (byte) b;
|
||||
} else if (position == 2 && b == byte3) {
|
||||
fieldData[position++] = (byte) b;
|
||||
} else if (position >= 3 && position <= fieldData.length) {
|
||||
if (position < fieldData.length) fieldData[position++] = (byte) b;
|
||||
if (position == fieldData.length) break; // done!
|
||||
} else {
|
||||
position = 0;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
return i % trackData.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode odd-even bytes as stored on disk. The format will be
|
||||
* in two bytes. They are stored as such:<pre>
|
||||
* XX = 1d1d1d1d (odd data bits)
|
||||
* YY = 1d1d1d1d (even data bits)
|
||||
* </pre>
|
||||
* XX is then shifted by a bit and ANDed with YY to get the databyte.
|
||||
* See page 3-12 in Beneath Apple DOS for more information.
|
||||
*/
|
||||
protected int decodeOddEven(byte[] buffer, int offset) {
|
||||
int b1 = AppleUtil.getUnsignedByte(buffer[offset]);
|
||||
int b2 = AppleUtil.getUnsignedByte(buffer[offset+1]);
|
||||
return (b1 << 1 | 0x01) & b2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode odd-even bytes to be stored on disk. See decodeOddEven
|
||||
* for the format.
|
||||
* @see #decodeOddEven
|
||||
*/
|
||||
protected void encodeOddEven(byte[] buffer, int offset, int value) {
|
||||
buffer[offset] = (byte) ((value >> 1) | 0xaa);
|
||||
buffer[offset+1] = (byte) (value | 0xaa);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the specified sector.
|
||||
*/
|
||||
public void writeSector(int track, int dosSector, byte[] sectorData) throws IllegalArgumentException {
|
||||
int sector = sectorInterleave[dosSector];
|
||||
// 1. read track
|
||||
byte[] trackData = readTrackData(track);
|
||||
// 2. locate address field for this track and sector
|
||||
int offset = 0;
|
||||
byte[] addressField = new byte[14];
|
||||
boolean found = false;
|
||||
while (!found && offset < trackData.length) {
|
||||
int nextOffset = locateField(0xd5, 0xaa, 0x96, trackData, addressField, offset);
|
||||
if (nextOffset < offset) { // we wrapped!
|
||||
throw new IllegalArgumentException("Unable to locate physical sector "
|
||||
+ sector + " on track " + track);
|
||||
}
|
||||
offset = nextOffset;
|
||||
int t = decodeOddEven(addressField, 5);
|
||||
int s = decodeOddEven(addressField, 7);
|
||||
found = (t == track && s == sector);
|
||||
}
|
||||
if (!found) {
|
||||
throw new IllegalArgumentException("Unable to locate physical sector "
|
||||
+ sector + " on track " + track + "(#2)");
|
||||
}
|
||||
|
||||
// 3. PRENIBBLE: This is Java translated from assembly @ $B800
|
||||
// The Java routine was not working... :o(
|
||||
int[] bb00 = new int[0x100];
|
||||
int[] bc00 = new int[0x56];
|
||||
int x = 0;
|
||||
int y = 2;
|
||||
while (true) {
|
||||
y--;
|
||||
if (y < 0) {
|
||||
y+= 256;
|
||||
}
|
||||
int a = AppleUtil.getUnsignedByte(sectorData[y]);
|
||||
bc00[x]<<= 1;
|
||||
bc00[x]|= a & 1;
|
||||
a>>= 1;
|
||||
bc00[x]<<= 1;
|
||||
bc00[x]|= a & 1;
|
||||
a>>= 1;
|
||||
bb00[y] = a;
|
||||
x++;
|
||||
if (x >= 0x56) {
|
||||
x = 0;
|
||||
if (y == 0) break; // done
|
||||
}
|
||||
}
|
||||
for (x=0; x<0x56; x++) {
|
||||
bc00[x]&= 0x3f;
|
||||
}
|
||||
|
||||
// 4. Translated from portions of WRITE at $B82A:
|
||||
byte[] diskData = new byte[343];
|
||||
int pos = 0;
|
||||
for (y=0x56; y>0; y--) {
|
||||
if (y == 0x56) {
|
||||
diskData[pos++] = (byte) writeTranslateTable[bc00[y-1]];
|
||||
} else {
|
||||
diskData[pos++] = (byte) writeTranslateTable[bc00[y] ^ bc00[y-1]];
|
||||
}
|
||||
}
|
||||
diskData[pos++] = (byte) writeTranslateTable[bc00[0] ^ bb00[y]];
|
||||
for (y=1; y<256; y++) {
|
||||
diskData[pos++] = (byte) writeTranslateTable[bb00[y] ^ bb00[y-1]];
|
||||
}
|
||||
diskData[pos++] = (byte) writeTranslateTable[bb00[255]];
|
||||
|
||||
// 5. write to disk (data may wrap - hence the manual copy)
|
||||
byte[] dataFieldPrologue = new byte[3];
|
||||
offset= locateField(0xd5, 0xaa, 0xad, trackData, dataFieldPrologue, offset);
|
||||
for (int i=0; i<diskData.length; i++) {
|
||||
pos = (offset + i) % trackData.length;
|
||||
trackData[pos] = diskData[i];
|
||||
}
|
||||
writeTrackData(track, trackData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Answer with the number of tracks on this device.
|
||||
*/
|
||||
public int getTracksPerDisk() {
|
||||
return 35;
|
||||
}
|
||||
|
||||
/**
|
||||
* Answer with the number of sectors per track on this device.
|
||||
*/
|
||||
public int getSectorsPerTrack() {
|
||||
return 16;
|
||||
}
|
||||
|
||||
/**
|
||||
* Answer with the number of blocks on this device.
|
||||
* This cannot be computed since the physical size relates to disk
|
||||
* bytes (6+2 encoded) instead of a full 8-bit byte.
|
||||
*/
|
||||
public int getBlocksOnDevice() {
|
||||
return 280;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format the media. Formatting at the ImageOrder level deals with
|
||||
* low-level issues. A typical ordering just needs to have the image
|
||||
* "wiped," and that is the assumed implementation. However, specialized
|
||||
* orders - such as a nibbilized disk - need to lay down track and
|
||||
* sector markers.
|
||||
*/
|
||||
public void format() {
|
||||
// pre-fill entire disk with 0xff
|
||||
byte[] diskImage = new byte[232960]; // 6656 bytes per track
|
||||
Arrays.fill(diskImage, (byte)0xff);
|
||||
getDiskImageManager().setDiskImage(diskImage);
|
||||
// create initial address and data fields
|
||||
byte[] addressField = new byte[14];
|
||||
byte[] dataField = new byte[349];
|
||||
Arrays.fill(dataField, (byte)0x96); // decodes to zeros
|
||||
byte[] addressPrologue = new byte[] { (byte)0xd5, (byte)0xaa, (byte)0x96 };
|
||||
byte[] dataPrologue = new byte[] { (byte)0xd5, (byte)0xaa, (byte)0xad };
|
||||
byte[] epilogue = new byte[] { (byte)0xde, (byte)0xaa, (byte)0xeb };
|
||||
System.arraycopy(addressPrologue, 0, addressField, 0, 3);
|
||||
System.arraycopy(epilogue, 0, addressField, 11, 3);
|
||||
System.arraycopy(dataPrologue, 0, dataField, 0, 3);
|
||||
System.arraycopy(epilogue, 0, dataField, 346, 3);
|
||||
// lay out track with address and data fields
|
||||
int addressSync = 43; // number of sync bytes before address field
|
||||
int dataSync = 10; // number of sync bytes before data field
|
||||
int volume = 254; // disk volume# is always 254
|
||||
for (int track=0; track < getTracksPerDisk(); track++) {
|
||||
byte[] trackData = readTrackData(track);
|
||||
int offset = 0;
|
||||
for (int sector=0; sector < getSectorsPerTrack(); sector++) {
|
||||
// fill in address field:
|
||||
encodeOddEven(addressField, 3, volume);
|
||||
encodeOddEven(addressField, 5, track);
|
||||
encodeOddEven(addressField, 7, sector);
|
||||
encodeOddEven(addressField, 9, volume ^ track ^ sector);
|
||||
// write out sector data:
|
||||
offset+= addressSync;
|
||||
System.arraycopy(addressField, 0, trackData, offset, addressField.length);
|
||||
offset+= addressField.length;
|
||||
offset+= dataSync;
|
||||
System.arraycopy(dataField, 0, trackData, offset, dataField.length);
|
||||
offset+= dataField.length;
|
||||
}
|
||||
writeTrackData(track, trackData);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,104 @@
|
||||
/*
|
||||
* AppleCommander - An Apple ][ image utility.
|
||||
* Copyright (C) 2003 by Robert Greene
|
||||
* robgreene at users.sourceforge.net
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
package com.webcodepro.applecommander.storage.physical;
|
||||
|
||||
import com.webcodepro.applecommander.storage.Disk;
|
||||
|
||||
/**
|
||||
* Supports disk images stored in ProDOS physical order.
|
||||
* <p>
|
||||
* @author Rob Greene (RobGreene@users.sourceforge.net)
|
||||
*/
|
||||
public class ProdosOrder extends ImageOrder {
|
||||
/**
|
||||
* This table contains the block offset for a particular DOS sector.
|
||||
*/
|
||||
private static final int[] blockInterleave = {
|
||||
0, 7, 6, 6, 5, 5, 4, 4, 3, 3, 2, 2, 1, 1, 0, 7
|
||||
};
|
||||
/**
|
||||
* Defines the location within a block in which the DOS sector resides.
|
||||
* (0 = 0-255 and 1 = 256-511.)
|
||||
*/
|
||||
private static final int[] blockOffsets = {
|
||||
0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1
|
||||
};
|
||||
|
||||
/**
|
||||
* Construct a ProdosOrder.
|
||||
*/
|
||||
public ProdosOrder(ByteArrayImageLayout diskImageManager) {
|
||||
super(diskImageManager);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates that this device is block ordered.
|
||||
*/
|
||||
public boolean isBlockDevice() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates that this device is track and sector ordered.
|
||||
*/
|
||||
public boolean isTrackAndSectorDevice() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the block from the disk image.
|
||||
* Note: Defined in terms of reading sectors.
|
||||
*/
|
||||
public byte[] readBlock(int block) {
|
||||
return readBytes(block * Disk.BLOCK_SIZE, Disk.BLOCK_SIZE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the block to the disk image.
|
||||
* Note: Defined in terms of reading sectors.
|
||||
*/
|
||||
public void writeBlock(int block, byte[] data) {
|
||||
writeBytes(block * Disk.BLOCK_SIZE, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the specified sector.
|
||||
*/
|
||||
public byte[] readSector(int track, int sector) throws IllegalArgumentException {
|
||||
int block = track * 8 + blockInterleave[sector];
|
||||
byte[] blockData = readBlock(block);
|
||||
int offset = blockOffsets[sector];
|
||||
byte[] sectorData = new byte[Disk.SECTOR_SIZE];
|
||||
System.arraycopy(blockData, offset * Disk.SECTOR_SIZE,
|
||||
sectorData, 0, Disk.SECTOR_SIZE);
|
||||
return sectorData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the specified sector.
|
||||
*/
|
||||
public void writeSector(int track, int sector, byte[] bytes) throws IllegalArgumentException {
|
||||
int block = track * 8 + blockInterleave[sector];
|
||||
byte[] blockData = readBlock(block);
|
||||
int offset = blockOffsets[sector];
|
||||
System.arraycopy(bytes, 0, blockData, offset * Disk.SECTOR_SIZE, bytes.length);
|
||||
writeBlock(block, blockData);
|
||||
}
|
||||
}
|
@ -0,0 +1,67 @@
|
||||
/*
|
||||
* AppleCommander - An Apple ][ image utility.
|
||||
* Copyright (C) 2003 by Robert Greene
|
||||
* robgreene at users.sourceforge.net
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
package com.webcodepro.applecommander.storage.physical;
|
||||
|
||||
/**
|
||||
* Manages the physical 2IMG disk.
|
||||
* @author Rob Greene (RobGreene@users.sourceforge.net)
|
||||
*/
|
||||
public class UniversalDiskImageLayout extends ByteArrayImageLayout {
|
||||
/**
|
||||
* This is the 2IMG offset.
|
||||
*/
|
||||
public static final int OFFSET = 0x40;
|
||||
|
||||
/**
|
||||
* Construct a UniversalDiskImageLayout.
|
||||
*/
|
||||
public UniversalDiskImageLayout(byte[] diskImage) {
|
||||
super(diskImage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a UniversalDiskImageLayout.
|
||||
*/
|
||||
public UniversalDiskImageLayout(byte[] diskImage, boolean changed) {
|
||||
super(diskImage, changed);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a UniversalDiskImageLayout.
|
||||
*/
|
||||
public UniversalDiskImageLayout(int size) {
|
||||
super(size + 0x40);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract a portion of the disk image.
|
||||
*/
|
||||
public byte[] readBytes(int start, int length) {
|
||||
return super.readBytes(start + 0x40, length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write data to the disk image.
|
||||
*/
|
||||
public void writeBytes(int start, byte[] bytes) {
|
||||
super.writeBytes(start + 0x40, bytes);
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user