rewrote Prodos index traversal

This commit is contained in:
Denis Molony 2017-05-08 11:46:28 +10:00
parent bdbd837376
commit c4d789c501
9 changed files with 169 additions and 145 deletions

View File

@ -246,4 +246,29 @@ public class IntegerBasicProgram extends AbstractFile
return pgm.toString ();
}
/*
* https://groups.google.com/forum/#!topic/comp.sys.apple2/Baf36jyqwAM
* To convert Integer Basic to Applesoft
INPUT comands - change comma to semi-colon
remove all DIM of a string variable (not needed)
change string variables to use MID$ - i.e. A$(1,1)(in INT) is MID$(A$,1,1)(in AS basic)
change GOTO or GOSUB with a variable to ON GOTO
change IF statements to ON GOTO where possible and convert to multiple lines.
All statements that follow an IF on the same line are executed whether the statement
is true or not.
change MOD function to X=Y-(INT(Y/Z)*Z)
change "#" to "<>"
change TAB to HTAB
change RND(X) to INT(RND(1)*X)
relocate ML programs and change CALL'S and POKE'S. Since INT programs go from
HIMEM down, binary code is usually in low memory.
These few are not necessary but make for compact code.
change CALL -384 to INVERSE
change CALL -380 to NORMAL
change CALL -936 to HOME
*/
}

View File

@ -294,8 +294,6 @@ public abstract class AbstractFormattedDisk implements FormattedDisk
{
if (block >= sectorTypes.length)
System.out.println ("Invalid block number: " + block);
else if (sectorTypes[block] == emptySector)
System.out.printf ("Tried to assign %s to empty block %d%n", type.name, block);
else
sectorTypes[block] = type;
}

View File

@ -34,11 +34,13 @@ abstract class AbstractCatalogEntry implements AppleFileSource
{
this.dosDisk = dosDisk;
this.disk = dosDisk.getDisk ();
this.disk = dosDisk.getDisk ();
this.catalogSectorDA = catalogSector;
reportedSize = HexFormatter.intValue (entryBuffer[33], entryBuffer[34]);
// reportedSize = HexFormatter.intValue (entryBuffer[33], entryBuffer[34]);
reportedSize = HexFormatter.unsignedShort (entryBuffer, 33);
int type = entryBuffer[2] & 0xFF;
locked = (type & 0x80) > 0;
this.disk = dosDisk.getDisk ();
if ((type & 0x7F) == 0)
fileType = FileType.Text;
@ -154,14 +156,14 @@ abstract class AbstractCatalogEntry implements AppleFileSource
break;
case IntegerBasic:
reportedLength = HexFormatter.intValue (buffer[0], buffer[1]);
reportedLength = HexFormatter.unsignedShort (buffer, 0);
exactBuffer = new byte[reportedLength];
System.arraycopy (buffer, 2, exactBuffer, 0, reportedLength);
appleFile = new IntegerBasicProgram (name, exactBuffer);
break;
case ApplesoftBasic:
reportedLength = HexFormatter.intValue (buffer[0], buffer[1]);
reportedLength = HexFormatter.unsignedShort (buffer, 0);
exactBuffer = new byte[reportedLength];
if (reportedLength > buffer.length)
reportedLength = buffer.length - 2;
@ -171,12 +173,8 @@ abstract class AbstractCatalogEntry implements AppleFileSource
case Binary: // binary file
case Relocatable: // relocatable binary file
// if (buffer.length == 0)
// appleFile = new AssemblerProgram (name, buffer, 0);
// else
// {
int loadAddress = HexFormatter.intValue (buffer[0], buffer[1]);
reportedLength = HexFormatter.intValue (buffer[2], buffer[3]);
int loadAddress = HexFormatter.unsignedShort (buffer, 0);
reportedLength = HexFormatter.unsignedShort (buffer, 2);
if (reportedLength == 0)
{
System.out.println (name.trim () + " reported length : 0 - reverting to "
@ -275,8 +273,10 @@ abstract class AbstractCatalogEntry implements AppleFileSource
{
byte[] exactBuffer;
int loadAddress = HexFormatter.intValue (buffer[0], buffer[1]);
int reportedLength = HexFormatter.intValue (buffer[2], buffer[3]);
// int loadAddress = HexFormatter.intValue (buffer[0], buffer[1]);
int loadAddress = HexFormatter.unsignedShort (buffer, 0);
// int reportedLength = HexFormatter.intValue (buffer[2], buffer[3]);
int reportedLength = HexFormatter.unsignedShort (buffer, 2);
if (reportedLength == 0)
{
System.out.println (

View File

@ -6,13 +6,14 @@ import com.bytezone.diskbrowser.utilities.HexFormatter;
class CatalogEntry extends AbstractCatalogEntry
{
int textFileGaps;
int length;
int address;
private int textFileGaps;
private int length;
private int address;
public CatalogEntry (DosDisk dosDisk, DiskAddress catalogSector, byte[] entryBuffer)
{
super (dosDisk, catalogSector, entryBuffer); // build lists of ts and data sectors
if (reportedSize > 0 && disk.isValidAddress (entryBuffer[0], entryBuffer[1]))
{
// Get address of first TS-list sector

View File

@ -68,7 +68,8 @@ class DosTSListSector extends AbstractSector
if (buffer[i] == 0 && buffer[i + 1] == 0)
msg = "";
else
msg = "Track/sector of file sector " + ((i - 10) / 2 + sectorBase);
msg = String.format ("Track/sector of file sector %04X (%<,d)",
((i - 12) / 2 + sectorBase));
addText (text, buffer, i, 2, msg);
}

View File

@ -40,73 +40,53 @@ class FileEntry extends CatalogEntry implements ProdosConstants
this.parentDirectory = parent;
this.catalogBlock = this.disk.getDiskAddress (parentBlock);
fileType = entryBuffer[16] & 0xFF;
// keyPtr = HexFormatter.intValue (entryBuffer[17], entryBuffer[18]);
keyPtr = HexFormatter.unsignedShort (entryBuffer, 17);
// System.out.printf ("%5d %5d%n",
// HexFormatter.intValue (entryBuffer[17], entryBuffer[18]), keyPtr);
// blocksUsed = HexFormatter.intValue (entryBuffer[19], entryBuffer[20]);
blocksUsed = HexFormatter.unsignedShort (entryBuffer, 19);
fileType = entryBuffer[0x10] & 0xFF;
keyPtr = HexFormatter.unsignedShort (entryBuffer, 0x11);
blocksUsed = HexFormatter.unsignedShort (entryBuffer, 0x13);
endOfFile = HexFormatter.intValue (entryBuffer[21], entryBuffer[22], entryBuffer[23]);
// auxType = HexFormatter.intValue (entryBuffer[31], entryBuffer[32]);
auxType = HexFormatter.unsignedShort (entryBuffer, 31);
modified = HexFormatter.getAppleDate (entryBuffer, 33);
// headerPointer = HexFormatter.intValue (entryBuffer[37], entryBuffer[38]);
headerPointer = HexFormatter.unsignedShort (entryBuffer, 37);
auxType = HexFormatter.unsignedShort (entryBuffer, 0x1F);
modified = HexFormatter.getAppleDate (entryBuffer, 0x21);
headerPointer = HexFormatter.unsignedShort (entryBuffer, 0x25);
if (isGSOSFile ()) // I think this is wrong
System.out.printf ("************************************ %s is GS/OS%n", name);
switch (storageType)
{
case TYPE_SEEDLING:
parentDisk.setSectorType (keyPtr, fDisk.dataSector);
DiskAddress da = disk.getDiskAddress (keyPtr);
if (da != null)
dataBlocks.add (da);
else
invalid = true;
addDataBlocks (storageType, keyPtr);
break;
case TYPE_SAPLING:
if (isGEOSFile ())
if (isGSOSFile ()) // not sure why this exists
traverseGEOSIndex (keyPtr);
else
traverseIndex (keyPtr);
addDataBlocks (storageType, keyPtr);
break;
case TYPE_TREE:
parentDisk.setSectorType (keyPtr, fDisk.masterIndexSector);
masterIndexBlock = disk.getDiskAddress (keyPtr);
indexBlocks.add (masterIndexBlock);
if (isGEOSFile ())
if (isGSOSFile ()) // not sure why this exists
traverseGEOSMasterIndex (keyPtr);
else
traverseMasterIndex (keyPtr);
addDataBlocks (storageType, keyPtr);
break;
case TYPE_GSOS_EXTENDED_FILE:
parentDisk.setSectorType (keyPtr, fDisk.extendedKeySector);
parentDisk.setSectorType (keyPtr, parentDisk.extendedKeySector);
indexBlocks.add (disk.getDiskAddress (keyPtr));
byte[] buffer2 = disk.readSector (keyPtr); // data fork and resource fork
// read 2 mini entries (data fork / resource fork)
for (int i = 0; i < 512; i += 256)
{
int storageType = buffer2[i] & 0x0F;
int keyBlock = HexFormatter.intValue (buffer2[i + 1], buffer2[i + 2]);
switch (storageType)
{
case ProdosConstants.TYPE_SEEDLING:
parentDisk.setSectorType (keyBlock, fDisk.dataSector);
dataBlocks.add (disk.getDiskAddress (keyBlock));
break;
case ProdosConstants.TYPE_SAPLING:
traverseIndex (keyBlock);
break;
case ProdosConstants.TYPE_TREE:
traverseMasterIndex (keyBlock);
break;
default:
System.out.println ("fork not a tree, sapling or seedling!!!");
}
int keyBlock = HexFormatter.unsignedShort (buffer2, i + 1);
int eof =
HexFormatter.intValue (buffer2[i + 3], buffer2[i + 4], buffer2[i + 5]);
addDataBlocks (storageType, keyBlock);
}
break;
@ -129,90 +109,87 @@ class FileEntry extends CatalogEntry implements ProdosConstants
}
}
private boolean isGEOSFile ()
private void addDataBlocks (int storageType, int keyPtr)
{
DiskAddress emptyDiskAddress = disk.getDiskAddress (0);
List<Integer> blocks = new ArrayList<Integer> ();
switch (storageType)
{
case TYPE_SEEDLING:
blocks.add (keyPtr);
break;
case TYPE_SAPLING:
blocks.addAll (readIndex (keyPtr));
break;
case TYPE_TREE:
for (Integer indexBlock : readMasterIndex (keyPtr))
blocks.addAll (readIndex (indexBlock));
break;
}
// remove trailing empty blocks
while (blocks.size () > 0 && blocks.get (blocks.size () - 1) == 0)
blocks.remove (blocks.size () - 1);
for (Integer block : blocks)
{
if (block == 0)
dataBlocks.add (emptyDiskAddress);
else
{
dataBlocks.add (disk.getDiskAddress (block));
parentDisk.setSectorType (block, parentDisk.dataSector);
}
}
}
private List<Integer> readIndex (int blockPtr)
{
List<Integer> blocks = new ArrayList<Integer> (256);
if (blockPtr == 0) // master index contains a zero
for (int i = 0; i < 256; i++)
blocks.add (0);
else
{
byte[] buffer = disk.readSector (blockPtr);
for (int i = 0; i < 256; i++)
blocks.add ((buffer[i] & 0xFF) | ((buffer[i + 256] & 0xFF) << 8));
parentDisk.setSectorType (blockPtr, parentDisk.indexSector);
indexBlocks.add (disk.getDiskAddress (blockPtr));
}
return blocks;
}
private List<Integer> readMasterIndex (int blockPtr)
{
List<Integer> blocks = new ArrayList<Integer> (128);
byte[] buffer = disk.readSector (blockPtr); // master index
parentDisk.setSectorType (blockPtr, parentDisk.masterIndexSector);
indexBlocks.add (disk.getDiskAddress (blockPtr));
int max = 128;
while (max-- > 0)
if (buffer[max] != 0 || buffer[max + 256] != 0)
break;
for (int i = 0; i <= max; i++)
blocks.add ((buffer[i] & 0xFF) | ((buffer[i + 256] & 0xFF) << 8));
return blocks;
}
private boolean isGSOSFile ()
{
return ((fileType & 0xF0) == 0x80);
}
private void traverseMasterIndex (int keyPtr)
{
byte[] buffer = disk.readSector (keyPtr); // master index
// find the last used index block
// get the file size from the catalog and only check those blocks
int highestBlock = 0;
// A master index block can never be more than half full
for (int i = 127; i >= 0; i--)
{
int block = HexFormatter.intValue (buffer[i], buffer[i + 256]);
if (block > 0)
{
highestBlock = i;
break;
}
}
for (int i = 0; i <= highestBlock; i++)
{
int block = HexFormatter.intValue (buffer[i], buffer[i + 256]); // index
if (block != 0)
traverseIndex (block);
else
// add 256 empty data blocks
{
DiskAddress da = disk.getDiskAddress (0);
for (int j = 0; j < 256; j++)
dataBlocks.add (da);
}
}
removeEmptyBlocks ();
}
private void removeEmptyBlocks ()
{
while (dataBlocks.size () > 0)
{
DiskAddress da = dataBlocks.get (dataBlocks.size () - 1);
if (da == null || da.getBlock () != 0)
break;
dataBlocks.remove (dataBlocks.size () - 1);
}
}
private void traverseIndex (int keyBlock)
{
parentDisk.setSectorType (keyBlock, parentDisk.indexSector);
indexBlocks.add (disk.getDiskAddress (keyBlock));
byte[] buffer = disk.readSector (keyBlock);
int maxBlocks = (endOfFile - 1) / 512 + 1;
for (int i = 0; i < 256; i++)
{
int block = HexFormatter.intValue (buffer[i], buffer[i + 256]);
if (!disk.isValidAddress (block))
{
System.out.println ("Invalid block in " + name + " : " + block);
invalid = true;
break;
}
if (dataBlocks.size () == maxBlocks)
break;
if (block == 0)
dataBlocks.add (null); // allow for sparse image files
else
{
parentDisk.setSectorType (block, parentDisk.dataSector);
dataBlocks.add (disk.getDiskAddress (block));
}
}
}
// should be removed
private void traverseGEOSMasterIndex (int keyPtr)
{
byte[] buffer = disk.readSector (keyPtr); // master index
@ -227,6 +204,7 @@ class FileEntry extends CatalogEntry implements ProdosConstants
}
}
// should be removed
private void traverseGEOSIndex (int keyPtr)
{
parentDisk.setSectorType (keyPtr, parentDisk.indexSector);
@ -260,7 +238,7 @@ class FileEntry extends CatalogEntry implements ProdosConstants
if (fileType == FILE_TYPE_TEXT && auxType > 0) // random access file
return getRandomAccessTextFile ();
byte[] buffer = isGEOSFile () ? getGEOSBuffer () : getBuffer ();
byte[] buffer = isGSOSFile () ? getGEOSBuffer () : getBuffer ();
byte[] exactBuffer = getExactBuffer (buffer);
try
@ -379,6 +357,9 @@ class FileEntry extends CatalogEntry implements ProdosConstants
case FILE_TYPE_PASCAL_VOLUME:
file = new DefaultAppleFile (name, exactBuffer);
break;
case FILE_TYPE_FINDER:
file = new DefaultAppleFile (name, exactBuffer);
break;
default:
System.out.format ("%s - Unknown Prodos file type : %02X%n", name, fileType);
file = new DefaultAppleFile (name, exactBuffer);

View File

@ -20,6 +20,7 @@ public interface ProdosConstants
int FILE_TYPE_PNT = 0xC0;
int FILE_TYPE_PIC = 0xC1;
int FILE_TYPE_FONT = 0xC8;
int FILE_TYPE_FINDER = 0xC9;
int FILE_TYPE_ICN = 0xCA;
int FILE_TYPE_APPLETALK = 0xE2;
int FILE_TYPE_PASCAL_VOLUME = 0xEF;

View File

@ -4,6 +4,7 @@ import com.bytezone.diskbrowser.disk.AbstractSector;
import com.bytezone.diskbrowser.disk.Disk;
import com.bytezone.diskbrowser.disk.DiskAddress;
// see Prodos 8 Tech note #25
class ProdosExtendedKeySector extends AbstractSector
{
public ProdosExtendedKeySector (Disk disk, byte[] buffer, DiskAddress diskAddress)
@ -18,11 +19,24 @@ class ProdosExtendedKeySector extends AbstractSector
for (int i = 0; i < 512; i += 256)
{
addText (text, buffer, i, 1, "Storage type (" + getType (buffer[i]) + ")");
String type = i == 0 ? "Data" : "Resource";
addText (text, buffer, i, 1,
type + " fork storage type (" + getType (buffer[i]) + ")");
addTextAndDecimal (text, buffer, i + 1, 2, "Key block");
addTextAndDecimal (text, buffer, i + 3, 2, "Blocks used");
addTextAndDecimal (text, buffer, i + 5, 3, "EOF");
text.append ("\n");
// check for Finder Info records
if (i == 0 && buffer[8] != 0)
{
for (int j = 0; j <= 18; j += 18)
{
addTextAndDecimal (text, buffer, j + 8, 1, "Size");
addTextAndDecimal (text, buffer, j + 9, 1, "Type");
addTextAndDecimal (text, buffer, j + 10, 16, "Finder info");
}
}
}
return text.toString ();

View File

@ -25,9 +25,10 @@ class VolumeDirectoryHeader extends DirectoryHeader
bitMapBlock = HexFormatter.unsignedShort (entryBuffer, 35);
totalBlocks = HexFormatter.unsignedShort (entryBuffer, 37);
if (totalBlocks == 0xFFFF || totalBlocks == 0x7FFF)
totalBlocks = (int) disk.getFile ().length () / 4096 * 8; // ignore extra bytes
// totalBitMapBlocks = (totalBlocks * 8 - 1) / 4096 + 1;
// if (totalBlocks == 0xFFFF || totalBlocks == 0x7FFF)
// totalBlocks = (int) disk.getFile ().length () / 4096 * 8;// ignore extra bytes
totalBitMapBlocks = (totalBlocks - 1) / 512 + 1;
int block = 2;
@ -39,7 +40,7 @@ class VolumeDirectoryHeader extends DirectoryHeader
} while (block > 0);
// convert the Free Sector Table
int bitMapBytes = totalBlocks / 8; // one bit per block
int bitMapBytes = totalBlocks / 8; // one bit per block
byte[] buffer = new byte[bitMapBytes];
int bitMapBlocks = (bitMapBytes - 1) / disk.getSectorsPerTrack () + 1;
int lastBitMapBlock = bitMapBlock + bitMapBlocks - 1;
@ -63,6 +64,8 @@ class VolumeDirectoryHeader extends DirectoryHeader
// int max2 = (disk.getTotalBlocks () - 1) / 8 + 1; // bytes required for sector map
int max = (Math.min (totalBlocks, disk.getTotalBlocks ()) - 1) / 8 + 1;
// System.out.printf ("total blocks %,d%n", totalBlocks);
// System.out.printf ("disk blocks %,d%n", disk.getTotalBlocks ());
for (int i = 0; i < max; i++)
{