dmolony-DiskBrowser/src/com/bytezone/diskbrowser/nib/Nibblizer.java

456 lines
14 KiB
Java

package com.bytezone.diskbrowser.nib;
class Nibblizer
{
// still used by NibDisk and V2dDisk
private static byte[] addressPrologue32 = { (byte) 0xD5, (byte) 0xAA, (byte) 0xB5 };
private static byte[] addressPrologue33 = { (byte) 0xD5, (byte) 0xAA, (byte) 0x96 };
private static byte[] dataPrologue = { (byte) 0xD5, (byte) 0xAA, (byte) 0xAD };
private static byte[] epilogue = { (byte) 0xDE, (byte) 0xAA, (byte) 0xEB };
private static int[] interleave =
{ 0, 8, 1, 9, 2, 10, 3, 11, 4, 12, 5, 13, 6, 14, 7, 15 };
private static final int BLOCK_SIZE = 256;
private static final int RAW_BUFFER_SIZE_DOS_33 = 342;
private static final int RAW_BUFFER_SIZE_DOS_32 = 410;
private static final int BUFFER_WITH_CHECKSUM_SIZE_DOS_33 = RAW_BUFFER_SIZE_DOS_33 + 1;
private static final int BUFFER_WITH_CHECKSUM_SIZE_DOS_32 = RAW_BUFFER_SIZE_DOS_32 + 1;
private final ByteTranslator byteTranslator62 = new ByteTranslator6and2 ();
private final ByteTranslator byteTranslator53 = new ByteTranslator5and3 ();
private final byte[] decodeDos33a = new byte[BUFFER_WITH_CHECKSUM_SIZE_DOS_33];
private final byte[] decodeDos33b = new byte[RAW_BUFFER_SIZE_DOS_33];
private final byte[] encodeDos33a = new byte[RAW_BUFFER_SIZE_DOS_33];
private final byte[] encodeDos33b = new byte[BUFFER_WITH_CHECKSUM_SIZE_DOS_33];
private final byte[] decodeDos32a = new byte[BUFFER_WITH_CHECKSUM_SIZE_DOS_32];
private final byte[] decodeDos32b = new byte[RAW_BUFFER_SIZE_DOS_32];
private int sectorsPerTrack;
// ---------------------------------------------------------------------------------//
// processTrack
// ---------------------------------------------------------------------------------//
boolean processTrack (int trackNo, int maxTracks, byte[] buffer, byte[] diskBuffer)
{
int ptr = 0;
int totalSectors = 0;
boolean[] sectorsFound = new boolean[16];
sectorsPerTrack = maxTracks;
try
{
while (ptr < buffer.length)
{
if (sectorsPerTrack == 13)
ptr = findBytes (buffer, ptr, addressPrologue32);
else
ptr = findBytes (buffer, ptr, addressPrologue33);
if (ptr < 0)
{
System.out.printf ("Track: %02X - Address prologue not found%n", trackNo);
return false;
}
AddressField addressField = new AddressField (buffer, ptr);
if (!addressField.isValid ())
{
System.out.printf ("Track: %02X - Invalid address field%n", trackNo);
return false;
}
if (addressField.track != trackNo)
{
System.out.printf ("Track: %02X - Wrong track found (%02X)%n", trackNo,
addressField.track);
return false;
}
if (sectorsFound[addressField.sector])
{
System.out.printf ("Track: %02X - Sector already processes (%02X)%n", trackNo,
addressField.sector);
return false;
}
sectorsFound[addressField.sector] = true;
assert addressField.track == trackNo;
ptr += addressField.size ();
ptr = findBytes (buffer, ptr, dataPrologue);
if (ptr < 0)
{
System.out.printf ("Track: %02X - Data prologue not found%n", trackNo);
return false;
}
DataField dataField = new DataField (buffer, ptr);
if (!dataField.isValid ())
{
System.out.printf ("Track: %02X - Invalid data field%n", trackNo);
return false;
}
int offset;
if (sectorsPerTrack == 13)
offset = addressField.track * 0x0D00 + addressField.sector * BLOCK_SIZE;
else
offset =
addressField.track * 0x1000 + interleave[addressField.sector] * BLOCK_SIZE;
System.arraycopy (dataField.dataBuffer, 0, diskBuffer, offset, BLOCK_SIZE);
if (++totalSectors == sectorsPerTrack)
break;
ptr += dataField.size ();
}
}
catch (Exception e)
{
e.printStackTrace ();
}
if (totalSectors != sectorsPerTrack)
{
System.out.printf ("Track: %02X - Sectors found: %02X%n", trackNo, totalSectors);
return false;
}
return true;
}
// ---------------------------------------------------------------------------------//
// decode4and4
// ---------------------------------------------------------------------------------//
private int decode4and4 (byte[] buffer, int offset)
{
int odds = ((buffer[offset] & 0xFF) << 1) + 1;
int evens = buffer[offset + 1] & 0xFF;
return odds & evens;
}
// ---------------------------------------------------------------------------------//
// decode5and3
// ---------------------------------------------------------------------------------//
private byte[] decode5and3 (byte[] buffer, int offset)
{
// rearrange 410 bytes into 256
byte[] decodedBuffer = new byte[BLOCK_SIZE]; // 256 bytes
try
{
// convert legal disk values to actual 5 bit values
for (int i = 0; i < BUFFER_WITH_CHECKSUM_SIZE_DOS_32; i++) // 411 bytes
{
// System.out.printf ("%,5d %02X%n", i, buffer[offset]);
decodeDos32a[i] = (byte) (byteTranslator53.decode (buffer[offset++]) << 3);
}
// reconstruct 410 bytes each with 5 bits
byte chk = 0;
int ptr = 0;
for (int i = 409; i >= 256; i--) // 154 bytes
chk = decodeDos32b[i] = (byte) (decodeDos32a[ptr++] ^ chk);
for (int i = 0; i < 256; i++) // 256 bytes
chk = decodeDos32b[i] = (byte) (decodeDos32a[ptr++] ^ chk);
assert (chk ^ decodeDos32a[ptr]) == 0;
byte[] k = new byte[8];
ptr = 0;
final int[] lines = { 0, 51, 102, 153, 204, 256, 307, 358 }; // 255 is skipped
// process 8 disk bytes at a time, giving 5 valid bytes
// do this 51 times, giving 255 bytes
for (int i = 50; i >= 0; i--)
{
for (int j = 0; j < 8; j++)
k[j] = decodeDos32b[i + lines[j]];
k[0] |= (k[5] & 0xE0) >>> 5;
k[1] |= (k[6] & 0xE0) >>> 5;
k[2] |= (k[7] & 0xE0) >>> 5;
k[3] |= (k[5] & 0x10) >>> 2;
k[3] |= (k[6] & 0x10) >>> 3;
k[3] |= (k[7] & 0x10) >>> 4;
k[4] |= (k[5] & 0x08) >>> 1;
k[4] |= (k[6] & 0x08) >>> 2;
k[4] |= (k[7] & 0x08) >>> 3;
for (int j = 0; j < 5; j++)
decodedBuffer[ptr++] = k[j];
}
// add last byte
decodedBuffer[255] = (byte) (decodeDos32b[255] | (decodeDos32b[409] >>> 3));
}
catch (Exception e)
{
}
return decodedBuffer;
}
// ---------------------------------------------------------------------------------//
// decode6and2
// ---------------------------------------------------------------------------------//
private byte[] decode6and2 (byte[] buffer, int offset)
{
// rearrange 342 bytes into 256
byte[] decodedBuffer = new byte[BLOCK_SIZE]; // 256 bytes
try
{
// convert legal disk values to actual 6 bit values
for (int i = 0; i < BUFFER_WITH_CHECKSUM_SIZE_DOS_33; i++) // 343 bytes
decodeDos33a[i] = (byte) (byteTranslator62.decode (buffer[offset++]) << 2);
// reconstruct 342 bytes each with 6 bits
byte chk = 0;
for (int i = decodeDos33b.length - 1; i >= 0; i--) // 342 bytes
chk = decodeDos33b[i] = (byte) (decodeDos33a[i + 1] ^ chk);
assert (chk ^ decodeDos33a[0]) == 0;
// move 6 bits into place
for (int i = 0; i < BLOCK_SIZE; i++)
decodedBuffer[i] = decodeDos33b[i + 86];
// reattach each byte's last 2 bits
for (int i = 0, j = 86, k = 172; i < 86; i++, j++, k++)
{
byte val = decodeDos33b[i];
decodedBuffer[i] |= reverse ((val & 0x0C) >> 2);
decodedBuffer[j] |= reverse ((val & 0x30) >> 4);
if (k < BLOCK_SIZE)
decodedBuffer[k] |= reverse ((val & 0xC0) >> 6);
}
}
catch (Exception e)
{
}
return decodedBuffer;
}
// ---------------------------------------------------------------------------------//
// encode6and2
// ---------------------------------------------------------------------------------//
// convert 256 data bytes into 342 translated bytes plus a checksum
private byte[] encode6and2 (byte[] buffer)
{
byte[] encodedBuffer = new byte[BUFFER_WITH_CHECKSUM_SIZE_DOS_33];
// move data buffer down to make room for the 86 extra bytes
for (int i = 0; i < BLOCK_SIZE; i++)
encodeDos33a[i + 86] = buffer[i];
// build extra 86 bytes from the bits stripped from the data bytes
for (int i = 0; i < 86; i++)
{
int b1 = reverse (buffer[i] & 0x03) << 2;
int b2 = reverse (buffer[i + 86] & 0x03) << 4;
if (i < 84)
{
int b3 = reverse (buffer[i + 172] & 0x03) << 6;
encodeDos33a[i] = (byte) (b1 | b2 | b3);
}
else
encodeDos33a[i] = (byte) (b1 | b2);
}
// convert into checksum bytes
byte checksum = 0;
for (int i = 0; i < RAW_BUFFER_SIZE_DOS_33; i++)
{
encodeDos33b[i] = (byte) (checksum ^ encodeDos33a[i]);
checksum = encodeDos33a[i];
}
encodeDos33b[RAW_BUFFER_SIZE_DOS_33] = checksum; // add checksum to the end
// remove two bits and convert to translated bytes
for (int i = 0; i < BUFFER_WITH_CHECKSUM_SIZE_DOS_33; i++)
// encodedBuffer[i] = writeTranslateTable6and2[(encodeDos33b[i] & 0xFC) / 4];
encodedBuffer[i] = byteTranslator62.encode (encodeDos33b[i]);
return encodedBuffer;
}
// ---------------------------------------------------------------------------------//
// reverse
// ---------------------------------------------------------------------------------//
// reverse 2 bits - 0 <= bits <= 3
private static int reverse (int bits)
{
return bits == 1 ? 2 : bits == 2 ? 1 : bits;
}
// ---------------------------------------------------------------------------------//
// listBytes
// ---------------------------------------------------------------------------------//
private String listBytes (byte[] buffer, int offset, int length)
{
StringBuilder text = new StringBuilder ();
int max = Math.min (length + offset, buffer.length);
while (offset < max)
text.append (String.format ("%02X ", buffer[offset++]));
return text.toString ();
}
// ---------------------------------------------------------------------------------//
// findBytes
// ---------------------------------------------------------------------------------//
static int findBytes (byte[] buffer, int offset, byte[] valueBuffer)
{
while (offset + valueBuffer.length <= buffer.length)
{
if (matchBytes (buffer, offset, valueBuffer))
return offset;
++offset;
}
return -1;
}
// ---------------------------------------------------------------------------------//
// matchBytes
// ---------------------------------------------------------------------------------//
private static boolean matchBytes (byte[] buffer, int offset, byte[] valueBuffer)
{
if ((buffer.length - offset) < valueBuffer.length)
return false;
int ptr = 0;
try
{
while (ptr < valueBuffer.length)
if (buffer[offset++] != valueBuffer[ptr++])
return false;
}
catch (ArrayIndexOutOfBoundsException e)
{
System.out.println ("Error in matchBytes");
e.printStackTrace ();
return false;
}
return true;
}
// ---------------------------------------------------------------------------------//
// Field
// ---------------------------------------------------------------------------------//
private abstract class Field
{
protected boolean valid;
protected byte[] buffer;
protected int offset;
protected int length;
public Field (byte[] buffer, int offset)
{
this.buffer = buffer;
this.offset = offset;
}
public boolean isValid ()
{
return valid;
}
public int size ()
{
assert length > 0;
return length;
}
@Override
public String toString ()
{
return String.format ("[Offset: %04X, Length: %04X]", offset, length);
}
}
// ---------------------------------------------------------------------------------//
// AddressField
// ---------------------------------------------------------------------------------//
private class AddressField extends Field
{
int track, sector, volume, checksum;
public AddressField (byte[] buffer, int offset)
{
super (buffer, offset);
volume = decode4and4 (buffer, offset + 3);
track = decode4and4 (buffer, offset + 5);
sector = decode4and4 (buffer, offset + 7);
checksum = decode4and4 (buffer, offset + 9);
valid = true;
length = 14;
}
@Override
public String toString ()
{
return String.format ("[volume: %02X, track: %02X, sector: %02X, checksum: %02X]",
volume, track, sector, checksum);
}
}
// ---------------------------------------------------------------------------------//
// DataField
// ---------------------------------------------------------------------------------//
private class DataField extends Field
{
byte[] dataBuffer;
public DataField (byte[] buffer, int offset)
{
super (buffer, offset);
if (matchBytes (buffer, offset, dataPrologue))
{
valid = true;
if (sectorsPerTrack == 13)
dataBuffer = decode5and3 (buffer, offset + 3);
else
dataBuffer = decode6and2 (buffer, offset + 3);
}
else
{
System.out.print (" bad data prologue: ");
System.out.println (listBytes (buffer, offset, 3));
}
length = 349;
}
}
}