diff --git a/src/com/bytezone/diskbrowser/nib/Dumper.java b/src/com/bytezone/diskbrowser/nib/Dumper.java new file mode 100644 index 0000000..52fdb84 --- /dev/null +++ b/src/com/bytezone/diskbrowser/nib/Dumper.java @@ -0,0 +1,411 @@ +package com.bytezone.diskbrowser.nib; + +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import com.bytezone.common.Utility; +import com.bytezone.diskbrowser.utilities.HexFormatter; + +// -----------------------------------------------------------------------------------// +public class Dumper +// -----------------------------------------------------------------------------------// +{ + private static final int[] weights = { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 }; + + private static final byte[] address16prologue = + { (byte) 0xD5, (byte) 0xAA, (byte) 0x96 }; + private static final byte[] address13prologue = + { (byte) 0xD5, (byte) 0xAA, (byte) 0xB5 }; + private static final byte[] dataPrologue = { (byte) 0xD5, (byte) 0xAA, (byte) 0xAD }; + private static final byte[] epilogue = { (byte) 0xDE, (byte) 0xAA, (byte) 0xEB }; + + List tracks; + + // ---------------------------------------------------------------------------------// + public Dumper (File file) + // ---------------------------------------------------------------------------------// + { + byte[] buffer = readFile (file); + String header = new String (buffer, 0, 4); + System.out.println (header); + + int ptr = 12; + while (ptr < buffer.length) + { + String chunkId = new String (buffer, ptr, 4); + int size = Utility.getLong (buffer, ptr + 4); + System.out.printf ("%n%s %,9d%n", chunkId, size); + switch (chunkId) + { + case "INFO": + info (buffer, ptr); + break; + case "TMAP": + tmap (buffer, ptr); + break; + case "TRKS": + tracks = trks (buffer, ptr); + break; + case "META": + meta (buffer, ptr, size); + break; + case "WRIT": + break; + default: + break; + } + ptr += size + 8; + } + + Track track = tracks.get (0x22); + for (Sector sector : track.sectors) + sector.dump (); + } + + // ---------------------------------------------------------------------------------// + private void info (byte[] buffer, int ptr) + // ---------------------------------------------------------------------------------// + { + int version = val8 (buffer, ptr + 8); + int diskType = val8 (buffer, ptr + 9); + int writeProtected = val8 (buffer, ptr + 10); + int synchronised = val8 (buffer, ptr + 11); + int cleaned = val8 (buffer, ptr + 12); + String creator = new String (buffer, ptr + 13, 32); + int sides = val8 (buffer, ptr + 45); + int bootSectorFormat = val8 (buffer, ptr + 46); + int optimalBitTiming = val8 (buffer, ptr + 47); + int compatibleHardware = val16 (buffer, ptr + 48); + int requiredRam = val16 (buffer, ptr + 50); + int largestTrack = val16 (buffer, ptr + 52); + + String bootSectorFormatText = + bootSectorFormat == 0 ? "Unknown" : bootSectorFormat == 1 ? "16 sector" + : bootSectorFormat == 2 ? "13 sector" : "Hybrid"; + String diskTypeText = diskType == 1 ? "5.25" : "3.5"; + + System.out.printf ("Version ............. %d%n", version); + System.out.printf ("Disk type ........... %d (%s\")%n", diskType, diskTypeText); + System.out.printf ("Write protected ..... %d%n", writeProtected); + System.out.printf ("Synchronized ........ %d%n", synchronised); + System.out.printf ("Cleaned ............. %d%n", cleaned); + System.out.printf ("Creator ............. %s%n", creator); + System.out.printf ("Sides ............... %d%n", sides); + System.out.printf ("Boot sector format .. %d (%s)%n", bootSectorFormat, + bootSectorFormatText); + System.out.printf ("Optimal bit timing .. %d%n", optimalBitTiming); + System.out.printf ("Compatible hardware . %d%n", compatibleHardware); + System.out.printf ("Required RAM ........ %d%n", requiredRam); + System.out.printf ("Largest track ....... %d%n", largestTrack); + } + + // ---------------------------------------------------------------------------------// + private void tmap (byte[] buffer, int ptr) + // ---------------------------------------------------------------------------------// + { + ptr += 8; + } + + // ---------------------------------------------------------------------------------// + private void meta (byte[] buffer, int ptr, int length) + // ---------------------------------------------------------------------------------// + { + ptr += 8; + String metaData = new String (buffer, ptr, length); + // System.out.println (metaData); + String[] chunks = metaData.split ("\n"); + for (String chunk : chunks) + { + String[] parts = chunk.split ("\t"); + if (parts.length >= 2) + System.out.printf ("%-20s %s%n", parts[0], parts[1]); + else + System.out.printf ("%-20s%n", parts[0]); + } + } + + // ---------------------------------------------------------------------------------// + private List trks (byte[] rawBuffer, int ptr) + // ---------------------------------------------------------------------------------// + { + List tracks = new ArrayList<> (); + ptr += 8; + for (int i = 0; i < 160; i++) + { + Track trk = new Track (i, rawBuffer, ptr); + if (trk.bitCount == 0) + break; + tracks.add (trk); + ptr += 8; + System.out.printf ("%n$%02X %s%n", i, trk); + } + return tracks; + } + + // ---------------------------------------------------------------------------------// + private int val8 (byte[] buffer, int ptr) + // ---------------------------------------------------------------------------------// + { + return (buffer[ptr] & 0xFF); + } + + // ---------------------------------------------------------------------------------// + private int val16 (byte[] buffer, int ptr) + // ---------------------------------------------------------------------------------// + { + return (buffer[ptr++] & 0xFF) + ((buffer[ptr] & 0xFF) << 8); + } + + // ---------------------------------------------------------------------------------// + private int val32 (byte[] buffer, int ptr) + // ---------------------------------------------------------------------------------// + { + return (buffer[ptr++] & 0xFF) + ((buffer[ptr++] & 0xFF) << 8) + + ((buffer[ptr++] & 0xFF) << 16) + ((buffer[ptr] & 0xFF) << 24); + } + + // ---------------------------------------------------------------------------------// + // decode4and4 + // ---------------------------------------------------------------------------------// + + private int decode4and4 (byte[] buffer, int offset) + { + int odds = ((buffer[offset] & 0xFF) << 1) | 0x01; + int evens = buffer[offset + 1] & 0xFF; + return odds & evens; + } + + // ---------------------------------------------------------------------------------// + private byte[] readFile (File file) + // ---------------------------------------------------------------------------------// + { + try + { + BufferedInputStream in = new BufferedInputStream (new FileInputStream (file)); + byte[] buffer = in.readAllBytes (); + in.close (); + return buffer; + } + catch (IOException e) + { + e.printStackTrace (); + return null; + } + } + + // ---------------------------------------------------------------------------------// + public static void main (String[] args) + // ---------------------------------------------------------------------------------// + { + File file = new File ("/Users/denismolony/code/python/wozardry-2.0/bill.woz"); + try + { + Dumper dumper = new Dumper (file); + } + catch (Exception e) + { + e.printStackTrace (); + } + } + + // ---------------------------------------------------------------------------------// + class Track + // ---------------------------------------------------------------------------------// + { + int trackNo; + int startingBlock; + int blockCount; + int bitCount; + + byte[] rawBuffer; + byte[] newBuffer; + + int bitIndex; + int byteIndex; + int trackIndex; + int revolutions; + + List sectors = new ArrayList<> (); + + // ---------------------------------------------------------------------------------// + public Track (int trackNo, byte[] rawBuffer, int ptr) + // ---------------------------------------------------------------------------------// + { + this.rawBuffer = rawBuffer; + this.trackNo = trackNo; + + startingBlock = val16 (rawBuffer, ptr); + blockCount = val16 (rawBuffer, ptr + 2); + bitCount = val32 (rawBuffer, ptr + 4); + + if (bitCount == 0) + return; + + byteIndex = startingBlock * 512; + + int offset = -1; + + while (sectors.size () < 13) // hard-coded!! + { + offset = findNext (address13prologue, offset + 1); + if (offset < 0) + break; + + Sector sector = new Sector (this, offset); + if (sectors.size () > 0) + checkDuplicates (sector, sectors.get (sectors.size () - 1)); + sectors.add (sector); + } + } + + // ---------------------------------------------------------------------------------// + private void checkDuplicates (Sector sector1, Sector sector2) + // ---------------------------------------------------------------------------------// + { + if (sector1.sector == sector2.sector) + System.out.println ("\n*** duplicate ***\n"); + } + + // ---------------------------------------------------------------------------------// + boolean nextBit () + // ---------------------------------------------------------------------------------// + { + boolean bit = ((rawBuffer[byteIndex] & 0xFF) & weights[bitIndex]) != 0; + + if (++trackIndex >= bitCount) + { + ++revolutions; + trackIndex = 0; + bitIndex = 0; + byteIndex = startingBlock * 512; + } + else if (++bitIndex >= 8) + { + ++byteIndex; + bitIndex = 0; + } + + return bit; + } + + // ---------------------------------------------------------------------------------// + int nextByte () + // ---------------------------------------------------------------------------------// + { + while (!nextBit ()) + if (revolutions >= 2) + { + System.out.println ("looping"); + return 0; + } + + int b = 0x80; + for (int i = 6; i >= 0; i--) + if (nextBit ()) + b |= (1 << i); + + return b; + } + + // ---------------------------------------------------------------------------------// + byte[] readTrack () + // ---------------------------------------------------------------------------------// + { + if (newBuffer != null) + return newBuffer; + + int max = (bitCount - 1) / 8 + 1; + max += 520; + newBuffer = new byte[max]; + + for (int i = 0; i < max; i++) + newBuffer[i] = (byte) nextByte (); + + return newBuffer; + } + + // ---------------------------------------------------------------------------------// + int findNext (byte[] key, int start) + // ---------------------------------------------------------------------------------// + { + readTrack (); + + int max = newBuffer.length - key.length; + outer: for (int ptr = start; ptr < max; ptr++) + { + for (int keyPtr = 0; keyPtr < key.length; keyPtr++) + if (newBuffer[ptr + keyPtr] != key[keyPtr]) + continue outer; + return ptr; + } + + return -1; + } + + // ---------------------------------------------------------------------------------// + @Override + public String toString () + // ---------------------------------------------------------------------------------// + { + StringBuilder text = new StringBuilder (); + text.append (String.format ("Start: %4d, Blocks: %2d, Bits: %,8d%n%n", + startingBlock, blockCount, bitCount)); + int count = 0; + for (Sector sector : sectors) + text.append (String.format ("%2d %s%n", count++, sector)); + text.deleteCharAt (text.length () - 1); + return text.toString (); + } + } + + // ---------------------------------------------------------------------------------// + class Sector + // ---------------------------------------------------------------------------------// + { + Track track; + int trackNo, sector, volume, checksum; + int addressOffset, dataOffset; + + // ---------------------------------------------------------------------------------// + Sector (Track track, int addressOffset) + // ---------------------------------------------------------------------------------// + { + this.track = track; + byte[] buffer = track.newBuffer; + volume = decode4and4 (buffer, addressOffset + 3); + trackNo = decode4and4 (buffer, addressOffset + 5); + sector = decode4and4 (buffer, addressOffset + 7); + checksum = decode4and4 (buffer, addressOffset + 9); + + this.addressOffset = addressOffset; + dataOffset = track.findNext (dataPrologue, addressOffset + 11); + if (dataOffset > addressOffset + 200) + dataOffset = -1; + } + + // ---------------------------------------------------------------------------------// + void dump () + // ---------------------------------------------------------------------------------// + { + System.out.println (); + System.out.println (this); + System.out.println ( + HexFormatter.format (track.newBuffer, addressOffset, 512, addressOffset)); + } + + // ---------------------------------------------------------------------------------// + @Override + public String toString () + // ---------------------------------------------------------------------------------// + { + String dataOffsetText = dataOffset < 0 ? "" : String.format ("%04X", dataOffset); + return String.format ( + "Vol: %02X Trk: %02X Sct: %02X Chk: %02X Add: %04X Dat: %s", volume, + trackNo, sector, checksum, addressOffset, dataOffsetText); + } + } +} diff --git a/src/com/bytezone/diskbrowser/nib/MC3470.java b/src/com/bytezone/diskbrowser/nib/MC3470.java index b07d00f..8a60a0b 100644 --- a/src/com/bytezone/diskbrowser/nib/MC3470.java +++ b/src/com/bytezone/diskbrowser/nib/MC3470.java @@ -7,7 +7,7 @@ class MC3470 { private static final int MAX_DATA = 999; private final boolean debug = false; - private final boolean dump = false; + private final boolean dump = true; private List diskSectors; @@ -50,6 +50,13 @@ class MC3470 private static final byte[] dataPrologueX = { (byte) 0xD5, (byte) 0xBB, (byte) 0xCF }; private static final byte[] epilogueX = { (byte) 0xDA, (byte) 0xAA, (byte) 0xEB }; + private static final byte[][] master = + { address16prologue, address13prologue, dataPrologue, epilogue, address16prologueX, + dataPrologueX, epilogueX }; + private static final String[] masterNames = + { "Address Prologue 16", "Address Prologue 13", "Data Prologue", "Epilogue", + "Address Prologue 16 X", "Data Prologue X", "Epilogue X" }; + private enum State { ADDRESS, DATA, OTHER @@ -80,7 +87,7 @@ class MC3470 finished = false; int zeroBits = 0; - main: while (inPtr < max && !finished) + while (inPtr < max && !finished) { byte b = buffer[inPtr++]; @@ -90,7 +97,7 @@ class MC3470 for (int mask = 0x80; mask != 0; mask >>>= 1) { value <<= 1; // make space for next bit - if ((b & mask) != 0) // is next bit = 1? + if ((b & mask) != 0) // is next bit == 1? { value |= 0x01; // store 1 zeroBits = 0; // reset zero counter @@ -126,7 +133,11 @@ class MC3470 if (currentState == State.OTHER) checkState (); else if (dataPtr == expectedDataSize) // DATA or ADDRESS is now complete + { + if (debug) + System.out.printf ("%s full%n", currentState); setState (State.OTHER); + } } if (++totalBits == bitCount) // only use this many bits @@ -138,7 +149,8 @@ class MC3470 } // check for unfinished data block, we may need to restart from the beginning - if (totalBits == bitCount && currentState == State.DATA && !restarted) + // if (totalBits == bitCount && currentState == State.DATA && !restarted) + if (totalBits == bitCount && !restarted) { inPtr = offset; restarted = true; @@ -149,12 +161,12 @@ class MC3470 if (debug) { - System.out.println ("**************************************"); - System.out.printf ("* total bits : %,5d *%n", bitCount); - System.out.printf ("* bits used : %,5d *%n", totalBits); - System.out.printf ("* total bytes : %,5d *%n", bytesUsed); - System.out.printf ("* bytes used : %,5d *%n", totalBytes); - System.out.println ("**************************************"); + System.out.println ("***************************"); + System.out.printf ("* total bits : %,6d *%n", bitCount); + System.out.printf ("* bits used : %,6d *%n", totalBits); + System.out.printf ("* total bytes : %,6d *%n", bytesUsed); + System.out.printf ("* bytes used : %,6d *%n", totalBytes); + System.out.println ("***************************"); } return diskSectors; @@ -243,6 +255,27 @@ class MC3470 // ---------------------------------------------------------------------------------// private boolean match (byte[] pattern) + { + for (int i = 0, j = dataPtr - 3; i < 3; i++, j++) + if (pattern[i] != dataBuffer[j]) + return false; + + if (debug) + { + for (int i = 0; i < master.length; i++) + if (debugMatch (master[i])) + System.out.printf ("Matched: %02X %02X %02X %s%n", pattern[0], pattern[1], + pattern[2], masterNames[i]); + } + + return true; + } + + // ---------------------------------------------------------------------------------// + // debugMatch + // ---------------------------------------------------------------------------------// + + private boolean debugMatch (byte[] pattern) { for (int i = 0, j = dataPtr - 3; i < 3; i++, j++) if (pattern[i] != dataBuffer[j]) @@ -293,24 +326,30 @@ class MC3470 { case ADDRESS: if (currentDiskSector != null) - throw new DiskNibbleException ("cannot start ADDRESS: " + currentDiskSector); + { + System.out.println ("\nskipped: " + currentDiskSector); + currentDiskSector = null; + } + // throw new DiskNibbleException ( + // "cannot start ADDRESS: " + currentDiskSector + " has no data"); expectedDataSize = 8; if (dump) - System.out.print ("ADDRESS "); + System.out.print ("\nADDRESS "); break; case DATA: if (currentDiskSector == null) - throw new DiskNibbleException ("cannot start DATA without ADDRESS"); + // throw new DiskNibbleException ("cannot start DATA without ADDRESS"); + return; expectedDataSize = diskReader.expectedDataSize (); if (dump) - System.out.println ("DATA"); + System.out.println ("\nDATA"); break; case OTHER: expectedDataSize = MAX_DATA; // what is the maximum filler? if (dump) - System.out.println ("OTHER"); + System.out.println ("\nOTHER"); break; } diff --git a/src/com/bytezone/diskbrowser/nib/RawDiskSector.java b/src/com/bytezone/diskbrowser/nib/RawDiskSector.java index 70be7d3..608325e 100644 --- a/src/com/bytezone/diskbrowser/nib/RawDiskSector.java +++ b/src/com/bytezone/diskbrowser/nib/RawDiskSector.java @@ -1,5 +1,7 @@ package com.bytezone.diskbrowser.nib; +import com.bytezone.diskbrowser.utilities.HexFormatter; + public class RawDiskSector { final DiskAddressField addressField; @@ -15,6 +17,11 @@ public class RawDiskSector this.buffer = buffer; } + void dump () + { + System.out.println (HexFormatter.format (buffer)); + } + @Override public String toString () { diff --git a/src/com/bytezone/diskbrowser/nib/WozFile.java b/src/com/bytezone/diskbrowser/nib/WozFile.java index 51cdbf4..127bceb 100644 --- a/src/com/bytezone/diskbrowser/nib/WozFile.java +++ b/src/com/bytezone/diskbrowser/nib/WozFile.java @@ -22,7 +22,8 @@ public class WozFile private static final int TMAP_SIZE = 0xA0; private static final int DATA_SIZE = TRK_SIZE - 10; - private final boolean debug = false; + private final boolean debug = true; + private final boolean dump = true; private int diskType; // 5.25 or 3.5 private int wozVersion; private int bootSectorFormat; @@ -81,9 +82,12 @@ public class WozFile { if (debug) { + int diskType = buffer[ptr + 1] & 0xFF; + String diskTypeText = diskType == 1 ? "5.25" : diskType == 2 ? "3.5" : "??"; + System.out.println (); System.out.printf ("Version ........... %02X%n", buffer[ptr]); - System.out.printf ("Disk type ......... %02X%n", buffer[ptr + 1]); + System.out.printf ("Disk type ......... %02X %s%n", diskType, diskTypeText); System.out.printf ("Write protected ... %02X%n", buffer[ptr + 2]); System.out.printf ("Synchronised ...... %02X%n", buffer[ptr + 3]); System.out.printf ("Cleaned ........... %02X%n", buffer[ptr + 4]); @@ -92,8 +96,12 @@ public class WozFile if (wozVersion > 1) { + int bootFormat = buffer[ptr + 38] & 0xFF; + String bootFormatText = bootFormat == 1 ? "16 sector" + : bootFormat == 2 ? "13 sector" : bootFormat == 3 ? "Both" : "??"; System.out.printf ("Disk sides ........ %02X%n", buffer[ptr + 37]); - System.out.printf ("Boot format ....... %02X%n", buffer[ptr + 38]); + System.out.printf ("Boot format ....... %02X %s%n", bootFormat, + bootFormatText); System.out.printf ("Optimal timing .... %02X%n", buffer[ptr + 39]); System.out.printf ("Compatible flags .. %04X%n", readInt (buffer, ptr + 40, 2)); @@ -199,6 +207,7 @@ public class WozFile { System.out.println ("******************************"); System.out.printf ("* Track ......... %,6d *%n", trackNo); + System.out.printf ("* Start block ... %,6d *%n", startingBlock); System.out.printf ("* Block count ... %,6d *%n", blockCount); System.out.printf ("* Bit count .... %,6d *%n", bitCount); System.out.println ("******************************"); @@ -213,6 +222,12 @@ public class WozFile List diskSectors = mc3470.readTrack (buffer, startingBlock * 512, blockCount * 512, bitCount); + for (RawDiskSector rawDiskSector : diskSectors) + { + System.out.println (rawDiskSector); + rawDiskSector.dump (); + } + mc3470.storeSectors (diskSectors, diskBuffer); } catch (Exception e) @@ -313,6 +328,11 @@ public class WozFile } } + void dump (int trackNo) + { + + } + // ---------------------------------------------------------------------------------// // matches // ---------------------------------------------------------------------------------// @@ -324,4 +344,17 @@ public class WozFile return false; return true; } + + public static void main (String[] args) + { + File file = new File ("/Users/denismolony/code/python/wozardry-2.0/bill.woz"); + try + { + WozFile wozFile = new WozFile (file); + } + catch (DiskNibbleException e) + { + e.printStackTrace (); + } + } }