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

456 lines
14 KiB
Java
Raw Normal View History

2018-08-17 01:20:00 +00:00
package com.bytezone.diskbrowser.nib;
2016-11-28 04:25:52 +00:00
2018-06-10 20:45:11 +00:00
class Nibblizer
2016-11-28 04:25:52 +00:00
{
2018-08-11 00:07:50 +00:00
// still used by NibDisk and V2dDisk
2018-08-07 08:40:06 +00:00
private static byte[] addressPrologue32 = { (byte) 0xD5, (byte) 0xAA, (byte) 0xB5 };
private static byte[] addressPrologue33 = { (byte) 0xD5, (byte) 0xAA, (byte) 0x96 };
2018-06-10 20:45:11 +00:00
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;
2018-08-07 08:40:06 +00:00
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;
2018-08-11 00:07:50 +00:00
private final ByteTranslator byteTranslator62 = new ByteTranslator6and2 ();
private final ByteTranslator byteTranslator53 = new ByteTranslator5and3 ();
2016-11-28 04:25:52 +00:00
2018-08-07 08:40:06 +00:00
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];
2016-12-04 02:33:21 +00:00
2018-08-07 08:40:06 +00:00
private final byte[] decodeDos32a = new byte[BUFFER_WITH_CHECKSUM_SIZE_DOS_32];
private final byte[] decodeDos32b = new byte[RAW_BUFFER_SIZE_DOS_32];
2018-08-11 00:07:50 +00:00
private int sectorsPerTrack;
2018-08-07 08:40:06 +00:00
// ---------------------------------------------------------------------------------//
// processTrack
// ---------------------------------------------------------------------------------//
2016-11-30 00:37:10 +00:00
2018-08-11 00:07:50 +00:00
boolean processTrack (int trackNo, int maxTracks, byte[] buffer, byte[] diskBuffer)
2016-12-03 08:20:01 +00:00
{
int ptr = 0;
2018-06-08 12:14:19 +00:00
int totalSectors = 0;
boolean[] sectorsFound = new boolean[16];
2018-08-11 00:07:50 +00:00
sectorsPerTrack = maxTracks;
2016-12-03 08:20:01 +00:00
2018-08-07 08:40:06 +00:00
try
2016-12-03 08:20:01 +00:00
{
2018-08-07 08:40:06 +00:00
while (ptr < buffer.length)
2018-06-08 12:14:19 +00:00
{
2018-08-11 00:07:50 +00:00
if (sectorsPerTrack == 13)
ptr = findBytes (buffer, ptr, addressPrologue32);
2018-08-07 08:40:06 +00:00
else
2018-08-11 00:07:50 +00:00
ptr = findBytes (buffer, ptr, addressPrologue33);
if (ptr < 0)
2018-08-07 08:40:06 +00:00
{
2018-08-11 00:07:50 +00:00
System.out.printf ("Track: %02X - Address prologue not found%n", trackNo);
return false;
2018-08-07 08:40:06 +00:00
}
2018-08-11 00:07:50 +00:00
AddressField addressField = new AddressField (buffer, ptr);
2018-08-07 08:40:06 +00:00
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;
}
2018-08-11 00:07:50 +00:00
DataField dataField = new DataField (buffer, ptr);
2018-08-07 08:40:06 +00:00
if (!dataField.isValid ())
{
System.out.printf ("Track: %02X - Invalid data field%n", trackNo);
return false;
}
2018-08-11 00:07:50 +00:00
int offset;
if (sectorsPerTrack == 13)
offset = addressField.track * 0x0D00 + addressField.sector * BLOCK_SIZE;
2018-08-07 08:40:06 +00:00
else
2018-08-11 00:07:50 +00:00
offset =
addressField.track * 0x1000 + interleave[addressField.sector] * BLOCK_SIZE;
2018-08-07 08:40:06 +00:00
System.arraycopy (dataField.dataBuffer, 0, diskBuffer, offset, BLOCK_SIZE);
2018-08-11 00:07:50 +00:00
if (++totalSectors == sectorsPerTrack)
2018-08-07 08:40:06 +00:00
break;
ptr += dataField.size ();
2018-06-08 12:14:19 +00:00
}
2018-08-07 08:40:06 +00:00
}
catch (Exception e)
{
e.printStackTrace ();
}
2018-06-08 12:14:19 +00:00
2018-08-11 00:07:50 +00:00
if (totalSectors != sectorsPerTrack)
2018-06-08 12:14:19 +00:00
{
System.out.printf ("Track: %02X - Sectors found: %02X%n", trackNo, totalSectors);
return false;
2016-12-03 08:20:01 +00:00
}
return true;
}
2018-08-07 08:40:06 +00:00
// ---------------------------------------------------------------------------------//
// decode4and4
// ---------------------------------------------------------------------------------//
2016-12-01 13:25:41 +00:00
private int decode4and4 (byte[] buffer, int offset)
2016-11-28 04:25:52 +00:00
{
int odds = ((buffer[offset] & 0xFF) << 1) + 1;
int evens = buffer[offset + 1] & 0xFF;
return odds & evens;
}
2018-08-07 08:40:06 +00:00
// ---------------------------------------------------------------------------------//
// decode5and3
// ---------------------------------------------------------------------------------//
private byte[] decode5and3 (byte[] buffer, int offset)
2016-11-28 04:25:52 +00:00
{
2018-08-07 08:40:06 +00:00
// rearrange 410 bytes into 256
2018-08-07 09:22:31 +00:00
byte[] decodedBuffer = new byte[BLOCK_SIZE]; // 256 bytes
2018-08-07 08:40:06 +00:00
2018-08-11 00:07:50 +00:00
try
2016-11-28 04:25:52 +00:00
{
2018-08-11 00:07:50 +00:00
// 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]);
2018-08-11 04:12:21 +00:00
decodeDos32a[i] = (byte) (byteTranslator53.decode (buffer[offset++]) << 3);
2018-08-11 00:07:50 +00:00
}
2018-08-07 08:40:06 +00:00
2018-08-11 00:07:50 +00:00
// 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]];
2018-08-07 08:40:06 +00:00
2018-08-11 00:07:50 +00:00
k[0] |= (k[5] & 0xE0) >>> 5;
k[1] |= (k[6] & 0xE0) >>> 5;
k[2] |= (k[7] & 0xE0) >>> 5;
2016-11-28 04:25:52 +00:00
2018-08-11 00:07:50 +00:00
k[3] |= (k[5] & 0x10) >>> 2;
k[3] |= (k[6] & 0x10) >>> 3;
k[3] |= (k[7] & 0x10) >>> 4;
2018-08-07 08:40:06 +00:00
2018-08-11 00:07:50 +00:00
k[4] |= (k[5] & 0x08) >>> 1;
k[4] |= (k[6] & 0x08) >>> 2;
k[4] |= (k[7] & 0x08) >>> 3;
2018-08-07 08:40:06 +00:00
2018-08-11 00:07:50 +00:00
for (int j = 0; j < 5; j++)
decodedBuffer[ptr++] = k[j];
}
2018-08-07 09:22:31 +00:00
2018-08-11 00:07:50 +00:00
// add last byte
decodedBuffer[255] = (byte) (decodeDos32b[255] | (decodeDos32b[409] >>> 3));
}
catch (Exception e)
{
2018-08-07 09:22:31 +00:00
2018-08-11 00:07:50 +00:00
}
2018-08-07 09:22:31 +00:00
2018-08-11 00:07:50 +00:00
return decodedBuffer;
2018-08-07 08:40:06 +00:00
}
// ---------------------------------------------------------------------------------//
// decode6and2
// ---------------------------------------------------------------------------------//
private byte[] decode6and2 (byte[] buffer, int offset)
{
// rearrange 342 bytes into 256
byte[] decodedBuffer = new byte[BLOCK_SIZE]; // 256 bytes
2016-11-28 04:25:52 +00:00
2018-08-11 00:07:50 +00:00
try
2016-11-28 04:25:52 +00:00
{
2018-08-11 00:07:50 +00:00
// convert legal disk values to actual 6 bit values
for (int i = 0; i < BUFFER_WITH_CHECKSUM_SIZE_DOS_33; i++) // 343 bytes
2018-08-11 04:12:21 +00:00
decodeDos33a[i] = (byte) (byteTranslator62.decode (buffer[offset++]) << 2);
2018-08-11 00:07:50 +00:00
// 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];
2016-11-28 04:25:52 +00:00
2018-08-11 00:07:50 +00:00
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)
{
2016-11-28 04:25:52 +00:00
}
return decodedBuffer;
}
2018-08-07 08:40:06 +00:00
// ---------------------------------------------------------------------------------//
// encode6and2
// ---------------------------------------------------------------------------------//
2016-12-04 02:33:21 +00:00
// convert 256 data bytes into 342 translated bytes plus a checksum
2016-12-01 13:25:41 +00:00
private byte[] encode6and2 (byte[] buffer)
2016-11-28 04:25:52 +00:00
{
2018-08-07 08:40:06 +00:00
byte[] encodedBuffer = new byte[BUFFER_WITH_CHECKSUM_SIZE_DOS_33];
2016-11-28 04:25:52 +00:00
2016-12-04 02:33:21 +00:00
// move data buffer down to make room for the 86 extra bytes
for (int i = 0; i < BLOCK_SIZE; i++)
2018-08-07 08:40:06 +00:00
encodeDos33a[i + 86] = buffer[i];
2016-11-28 04:25:52 +00:00
2016-12-04 02:33:21 +00:00
// build extra 86 bytes from the bits stripped from the data bytes
for (int i = 0; i < 86; i++)
2016-11-28 04:25:52 +00:00
{
int b1 = reverse (buffer[i] & 0x03) << 2;
int b2 = reverse (buffer[i + 86] & 0x03) << 4;
2016-12-04 02:33:21 +00:00
if (i < 84)
{
int b3 = reverse (buffer[i + 172] & 0x03) << 6;
2018-08-07 08:40:06 +00:00
encodeDos33a[i] = (byte) (b1 | b2 | b3);
2016-12-04 02:33:21 +00:00
}
else
2018-08-07 08:40:06 +00:00
encodeDos33a[i] = (byte) (b1 | b2);
2016-11-28 04:25:52 +00:00
}
2016-12-04 02:33:21 +00:00
// convert into checksum bytes
byte checksum = 0;
2018-08-07 08:40:06 +00:00
for (int i = 0; i < RAW_BUFFER_SIZE_DOS_33; i++)
2016-11-28 04:25:52 +00:00
{
2018-08-07 08:40:06 +00:00
encodeDos33b[i] = (byte) (checksum ^ encodeDos33a[i]);
checksum = encodeDos33a[i];
2016-11-28 04:25:52 +00:00
}
2018-08-07 08:40:06 +00:00
encodeDos33b[RAW_BUFFER_SIZE_DOS_33] = checksum; // add checksum to the end
2016-11-28 04:25:52 +00:00
2016-12-04 02:33:21 +00:00
// remove two bits and convert to translated bytes
2018-08-07 08:40:06 +00:00
for (int i = 0; i < BUFFER_WITH_CHECKSUM_SIZE_DOS_33; i++)
2018-08-11 00:07:50 +00:00
// encodedBuffer[i] = writeTranslateTable6and2[(encodeDos33b[i] & 0xFC) / 4];
encodedBuffer[i] = byteTranslator62.encode (encodeDos33b[i]);
2016-11-28 04:25:52 +00:00
2016-11-30 00:37:10 +00:00
return encodedBuffer;
2016-11-28 04:25:52 +00:00
}
2018-08-07 08:40:06 +00:00
// ---------------------------------------------------------------------------------//
// reverse
// ---------------------------------------------------------------------------------//
2016-11-30 00:37:10 +00:00
// reverse 2 bits - 0 <= bits <= 3
private static int reverse (int bits)
2016-11-28 04:25:52 +00:00
{
2016-11-30 00:37:10 +00:00
return bits == 1 ? 2 : bits == 2 ? 1 : bits;
2016-11-28 04:25:52 +00:00
}
2016-11-28 09:11:11 +00:00
2018-08-07 08:40:06 +00:00
// ---------------------------------------------------------------------------------//
// listBytes
// ---------------------------------------------------------------------------------//
2016-12-01 13:25:41 +00:00
private String listBytes (byte[] buffer, int offset, int length)
2016-11-28 09:11:11 +00:00
{
2016-12-01 13:25:41 +00:00
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 ();
}
2018-08-07 08:40:06 +00:00
// ---------------------------------------------------------------------------------//
// findBytes
// ---------------------------------------------------------------------------------//
2016-12-04 02:33:21 +00:00
static int findBytes (byte[] buffer, int offset, byte[] valueBuffer)
2016-12-01 13:25:41 +00:00
{
2018-08-07 08:40:06 +00:00
while (offset + valueBuffer.length <= buffer.length)
2016-11-28 09:11:11 +00:00
{
2016-12-04 02:33:21 +00:00
if (matchBytes (buffer, offset, valueBuffer))
return offset;
++offset;
2016-11-28 09:11:11 +00:00
}
2016-11-30 00:37:10 +00:00
2016-12-01 13:25:41 +00:00
return -1;
}
2018-08-07 08:40:06 +00:00
// ---------------------------------------------------------------------------------//
// matchBytes
// ---------------------------------------------------------------------------------//
2016-12-04 02:33:21 +00:00
private static boolean matchBytes (byte[] buffer, int offset, byte[] valueBuffer)
2016-12-01 13:25:41 +00:00
{
2018-08-07 08:40:06 +00:00
if ((buffer.length - offset) < valueBuffer.length)
2016-12-04 02:33:21 +00:00
return false;
int ptr = 0;
2018-08-07 08:40:06 +00:00
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;
}
2016-12-04 02:33:21 +00:00
2016-12-01 13:25:41 +00:00
return true;
2016-11-28 09:11:11 +00:00
}
2018-08-07 08:40:06 +00:00
// ---------------------------------------------------------------------------------//
// Field
// ---------------------------------------------------------------------------------//
2018-06-10 20:45:11 +00:00
private abstract class Field
2016-11-28 09:11:11 +00:00
{
2016-11-30 00:37:10 +00:00
protected boolean valid;
protected byte[] buffer;
protected int offset;
protected int length;
2016-11-28 09:11:11 +00:00
public Field (byte[] buffer, int offset)
{
this.buffer = buffer;
this.offset = offset;
}
public boolean isValid ()
{
return valid;
}
2016-11-30 00:37:10 +00:00
public int size ()
{
assert length > 0;
return length;
}
2018-06-08 12:14:19 +00:00
@Override
public String toString ()
{
return String.format ("[Offset: %04X, Length: %04X]", offset, length);
}
2016-11-28 09:11:11 +00:00
}
2018-08-07 08:40:06 +00:00
// ---------------------------------------------------------------------------------//
// AddressField
// ---------------------------------------------------------------------------------//
2018-06-10 20:45:11 +00:00
private class AddressField extends Field
2016-11-28 09:11:11 +00:00
{
int track, sector, volume, checksum;
public AddressField (byte[] buffer, int offset)
{
super (buffer, offset);
2018-08-07 08:40:06 +00:00
volume = decode4and4 (buffer, offset + 3);
track = decode4and4 (buffer, offset + 5);
sector = decode4and4 (buffer, offset + 7);
checksum = decode4and4 (buffer, offset + 9);
valid = true;
2016-11-28 09:11:11 +00:00
2016-11-30 00:37:10 +00:00
length = 14;
2016-11-28 09:11:11 +00:00
}
2018-06-08 12:14:19 +00:00
@Override
public String toString ()
{
return String.format ("[volume: %02X, track: %02X, sector: %02X, checksum: %02X]",
volume, track, sector, checksum);
}
2016-11-28 09:11:11 +00:00
}
2018-08-07 08:40:06 +00:00
// ---------------------------------------------------------------------------------//
// DataField
// ---------------------------------------------------------------------------------//
2018-06-10 20:45:11 +00:00
private class DataField extends Field
2016-11-28 09:11:11 +00:00
{
2016-12-01 13:25:41 +00:00
byte[] dataBuffer;
2016-11-28 09:11:11 +00:00
public DataField (byte[] buffer, int offset)
{
super (buffer, offset);
2016-11-29 21:27:44 +00:00
if (matchBytes (buffer, offset, dataPrologue))
2016-11-28 09:11:11 +00:00
{
valid = true;
2018-08-11 00:07:50 +00:00
if (sectorsPerTrack == 13)
2018-08-07 08:40:06 +00:00
dataBuffer = decode5and3 (buffer, offset + 3);
2018-08-11 00:07:50 +00:00
else
dataBuffer = decode6and2 (buffer, offset + 3);
2016-11-29 21:27:44 +00:00
}
else
{
System.out.print (" bad data prologue: ");
2016-12-01 13:25:41 +00:00
System.out.println (listBytes (buffer, offset, 3));
2016-11-28 09:11:11 +00:00
}
2016-11-30 00:37:10 +00:00
length = 349;
2016-11-28 09:11:11 +00:00
}
}
}