From e4ed878f69ed6b062d532fce80eeb36134cc8a28 Mon Sep 17 00:00:00 2001 From: Denis Molony Date: Fri, 7 Feb 2020 21:52:46 +1000 Subject: [PATCH] method header lines --- .../diskbrowser/applefile/PascalCode.java | 186 ++--- .../applefile/PascalCodeStatement.java | 724 +++++++++--------- .../applefile/PascalConstants.java | 185 ++--- .../diskbrowser/applefile/PascalInfo.java | 9 +- .../applefile/PascalProcedure.java | 386 +++++----- .../diskbrowser/applefile/PascalSegment.java | 468 +++++------ .../diskbrowser/applefile/PascalText.java | 114 +-- .../applefile/PrintShopGraphic.java | 6 + .../applefile/SegmentDictionary.java | 6 + .../diskbrowser/applefile/ShapeTable.java | 700 ++++++++--------- 10 files changed, 1441 insertions(+), 1343 deletions(-) diff --git a/src/com/bytezone/diskbrowser/applefile/PascalCode.java b/src/com/bytezone/diskbrowser/applefile/PascalCode.java index 889deb4..93a4bbf 100755 --- a/src/com/bytezone/diskbrowser/applefile/PascalCode.java +++ b/src/com/bytezone/diskbrowser/applefile/PascalCode.java @@ -1,88 +1,100 @@ -package com.bytezone.diskbrowser.applefile; - -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; - -import com.bytezone.diskbrowser.utilities.FileFormatException; -import com.bytezone.diskbrowser.utilities.HexFormatter; - -public class PascalCode extends AbstractFile - implements PascalConstants, Iterable -{ - private final List segments = new ArrayList<> (16); - private final String comment; - // private final int blockOffset; - // private final Relocator relocator; - - public static void print () - { - for (int i = 0; i < 216; i++) - System.out.printf ("%3d %d %3s %s%n", i + 128, PascalConstants.mnemonicSize[i], - PascalConstants.mnemonics[i], PascalConstants.descriptions[i]); - } - - public PascalCode (String name, byte[] buffer, int blockOffset) - { - super (name, buffer); - - SegmentDictionary segmentDictionary = new SegmentDictionary (name, buffer); - if (!segmentDictionary.isValid ()) - throw new FileFormatException ("Error in PascalSegment"); - // this.blockOffset = blockOffset; - // this.relocator = relocator; - // if (relocator != null) - // relocator.getMultiDiskAddress ("SEG-DIC", blockOffset, 1); - - int nonameCounter = 0; - - // Create segment list (up to 16 segments) - for (int i = 0; i < 16; i++) - { - String codeName = HexFormatter.getString (buffer, 0x40 + i * 8, 8).trim (); - int size = HexFormatter.intValue (buffer[i * 4 + 2], buffer[i * 4 + 3]); - if (codeName.length () == 0 && size > 0) - codeName = ""; - if (size > 0) - { - // this could throw an exception - PascalSegment pascalSegment = - new PascalSegment (codeName, buffer, i, blockOffset); - segments.add (pascalSegment); - } - } - - comment = HexFormatter.getPascalString (buffer, 0x1B0); - } - - @Override - public String getText () - { - StringBuilder text = new StringBuilder (getHeader ()); - - text.append ("Segment Dictionary\n==================\n\n"); - - text.append ("Slot Addr Blks Byte Name Kind" - + " Txt Seg Mch Ver I/S I/S Disk:Block\n"); - text.append ("---- ---- ---- ---- -------- ---------------" - + " --- --- --- --- --- --- ---------------------\n"); - - for (PascalSegment segment : segments) - text.append (segment.toText () + "\n"); - - text.append ("\nComment : " + comment); - - return text.toString (); - } - - private String getHeader () - { - return "Name : " + name + "\n\n"; - } - - @Override - public Iterator iterator () - { - return segments.iterator (); - } +package com.bytezone.diskbrowser.applefile; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import com.bytezone.diskbrowser.utilities.FileFormatException; +import com.bytezone.diskbrowser.utilities.HexFormatter; + +// ---------------------------------------------------------------------------------// +public class PascalCode extends AbstractFile + implements PascalConstants, Iterable +// ---------------------------------------------------------------------------------// +{ + private final List segments = new ArrayList<> (16); + private final String comment; + // private final int blockOffset; + // private final Relocator relocator; + + // ---------------------------------------------------------------------------------// + public static void print () + // ---------------------------------------------------------------------------------// + { + for (int i = 0; i < 216; i++) + System.out.printf ("%3d %d %3s %s%n", i + 128, PascalConstants.mnemonicSize[i], + PascalConstants.mnemonics[i], PascalConstants.descriptions[i]); + } + + // ---------------------------------------------------------------------------------// + public PascalCode (String name, byte[] buffer, int blockOffset) + // ---------------------------------------------------------------------------------// + { + super (name, buffer); + + SegmentDictionary segmentDictionary = new SegmentDictionary (name, buffer); + if (!segmentDictionary.isValid ()) + throw new FileFormatException ("Error in PascalSegment"); + // this.blockOffset = blockOffset; + // this.relocator = relocator; + // if (relocator != null) + // relocator.getMultiDiskAddress ("SEG-DIC", blockOffset, 1); + + int nonameCounter = 0; + + // Create segment list (up to 16 segments) + for (int i = 0; i < 16; i++) + { + String codeName = HexFormatter.getString (buffer, 0x40 + i * 8, 8).trim (); + int size = HexFormatter.intValue (buffer[i * 4 + 2], buffer[i * 4 + 3]); + if (codeName.length () == 0 && size > 0) + codeName = ""; + if (size > 0) + { + // this could throw an exception + PascalSegment pascalSegment = + new PascalSegment (codeName, buffer, i, blockOffset); + segments.add (pascalSegment); + } + } + + comment = HexFormatter.getPascalString (buffer, 0x1B0); + } + + // ---------------------------------------------------------------------------------// + @Override + public String getText () + // ---------------------------------------------------------------------------------// + { + StringBuilder text = new StringBuilder (getHeader ()); + + text.append ("Segment Dictionary\n==================\n\n"); + + text.append ("Slot Addr Blks Byte Name Kind" + + " Txt Seg Mch Ver I/S I/S Disk:Block\n"); + text.append ("---- ---- ---- ---- -------- ---------------" + + " --- --- --- --- --- --- ---------------------\n"); + + for (PascalSegment segment : segments) + text.append (segment.toText () + "\n"); + + text.append ("\nComment : " + comment); + + return text.toString (); + } + + // ---------------------------------------------------------------------------------// + private String getHeader () + // ---------------------------------------------------------------------------------// + { + return "Name : " + name + "\n\n"; + } + + // ---------------------------------------------------------------------------------// + @Override + public Iterator iterator () + // ---------------------------------------------------------------------------------// + { + return segments.iterator (); + } } \ No newline at end of file diff --git a/src/com/bytezone/diskbrowser/applefile/PascalCodeStatement.java b/src/com/bytezone/diskbrowser/applefile/PascalCodeStatement.java index 0c5207e..007ccec 100755 --- a/src/com/bytezone/diskbrowser/applefile/PascalCodeStatement.java +++ b/src/com/bytezone/diskbrowser/applefile/PascalCodeStatement.java @@ -1,352 +1,374 @@ -package com.bytezone.diskbrowser.applefile; - -import java.util.ArrayList; -import java.util.List; - -import com.bytezone.diskbrowser.utilities.HexFormatter; - -public class PascalCodeStatement implements PascalConstants -{ - private static final String[] compValue = - { "invalid", "", "REAL", "", "STR", "", "BOOL", "", "POWR", "", "BYT", "", "WORD" }; - - int length; - int val; - int p1, p2, p3; - String mnemonic; - String extras = ""; - String description; - String text; - int ptr; // temp - byte[] buffer; - boolean jumpTarget; - List jumps = new ArrayList<> (); - - public PascalCodeStatement (byte[] buffer, int ptr, int procPtr) - { - this.ptr = ptr; - this.buffer = buffer; - length = 1; - val = buffer[ptr] & 0xFF; - if (val <= 127) - { - mnemonic = "SLDC"; - extras = "#" + val; - description = "Short load constant - push #" + val; - } - else if (val >= 248) - { - mnemonic = "SIND"; - extras = "#" + (val - 248); - description = "Short index load - push word *ToS + #" + (val - 248); - } - else if (val >= 232) - { - mnemonic = "SLDO"; - extras = "#" + (val - 231); - description = "Short load global - push BASE + #" + (val - 231); - } - else if (val >= 216) - { - mnemonic = "SLDL"; - extras = "#" + (val - 215); - description = "Short load local - push MP + #" + (val - 215); - } - else - { - mnemonic = mnemonics[val - 128]; - description = descriptions[val - 128]; - - length = mnemonicSize[val - 128]; - if (length != 1) - { - switch (val) - { - // W1, W2, W3, - word aligned case jump - case 172: //XJP - int padding = (ptr % 2) == 0 ? 1 : 0; - p1 = getWord (buffer, ptr + padding + 1); - p2 = getWord (buffer, ptr + padding + 3); - p3 = getWord (buffer, ptr + padding + 5); - length = (p2 - p1 + 1) * 2 + 7 + padding; - setParameters (p1, p2, String.format ("%04X", p3)); - int v = p1; - int min = ptr + padding + 7; - int max = min + (p2 - p1) * 2; - for (int i = min; i <= max; i += 2) - { - jumps.add (new Jump (i, - i - HexFormatter.intValue (buffer[i], buffer[i + 1]), v++)); - } - break; - - // UB, - word aligned - case 179: //LDC - p1 = buffer[ptr + 1] & 0xFF; - padding = ptr % 2 == 0 ? 0 : 1; - length = p1 * 2 + padding + 2; - setParameters (p1); - break; - - // UB, - case 166: // LSA - case 208: // LPA - p1 = buffer[ptr + 1] & 0xFF; - length = p1 + 2; - if (val == 166) - { - text = HexFormatter.getPascalString (buffer, ptr + 1); - description += ": " + text; - } - break; - - // W - case 199: // LDCI - p1 = getWord (buffer, ptr + 1); - setParameters (p1); - break; - - // B - case 162: // INC - case 163: // IND - case 164: // IXA - case 165: // LAO - case 168: // MOV - case 169: // LDO - case 171: // SRO - case 198: // LLA - case 202: // LDL - case 204: // STL - case 213: // BPT - length = getLengthOfB (buffer[ptr + 1]) + 1; - p1 = getValueOfB (buffer, ptr + 1, length - 1); - setParameters (p1); - break; - - // DB, B or UB, B - case 157: // LDE - case 167: // LAE - case 178: // LDA - case 182: // LOD - case 184: // STR - case 209: // STE - length = getLengthOfB (buffer[ptr + 2]) + 2; - p1 = buffer[ptr + 1] & 0xFF; - p2 = getValueOfB (buffer, ptr + 2, length - 2); - setParameters (p1, p2); - break; - - // UB1, UB2 - case 192: // IXP - case 205: // CXP - p1 = buffer[ptr + 1] & 0xFF; - p2 = buffer[ptr + 2] & 0xFF; - setParameters (p1, p2); - break; - - // SB or DB - case 161: // FJP - case 173: // RNP - case 185: // UJP - case 193: // RBP - case 211: // EFJ - case 212: // NFJ - p1 = buffer[ptr + 1]; - if (val == 173 || val == 193) // return from procedure - setParameters (p1); - else if (p1 < 0) - { - // look up jump table entry - int address = procPtr + p1; - int ptr2 = address - - ((buffer[address + 1] & 0xFF) * 256 + (buffer[address] & 0xFF)); - extras = String.format ("$%04X", ptr2); - jumps.add (new Jump (ptr, ptr2)); - } - else - { - int address = ptr + length + p1; - extras = String.format ("$%04X", address); - jumps.add (new Jump (ptr, address)); - } - break; - - // UB - case 160: // AOJ - case 170: // SAS - case 174: // CIP - case 188: // LDM - case 189: // STM - case 194: // CBP - case 206: // CLP - case 207: // CGP - p1 = buffer[ptr + 1] & 0xFF; - setParameters (p1); - break; - - // CSP - case 158: - p1 = buffer[ptr + 1] & 0xFF; - if (p1 < CSP.length) - description = "Call standard procedure - " + CSP[p1]; - else - description = "Call standard procedure - index out of bounds"; - break; - - // Non-integer comparisons - case 175: - case 176: - case 177: - case 180: - case 181: - case 183: - p1 = buffer[ptr + 1] & 0xFF; // 2/4/6/8/10/12 - if (p1 < 0 || p1 >= compValue.length) - { - System.out.printf ("%d %d %d%n", val, p1, ptr); - mnemonic += "******************************"; - break; - } - mnemonic += compValue[p1]; - if (p1 == 10 || p1 == 12) - { - length = getLengthOfB (buffer[ptr + 2]) + 2; - p2 = getValueOfB (buffer, ptr + 2, length - 2); - setParameters (p2); - } - break; - - default: - System.out.println ("Forgot : " + val); - } - } - } - } - - private int getWord (byte[] buffer, int ptr) - { - return (buffer[ptr + 1] & 0xFF) * 256 + (buffer[ptr] & 0xFF); - } - - private int getLengthOfB (byte b) - { - return (b & 0x80) == 0x80 ? 2 : 1; - } - - private int getValueOfB (byte[] buffer, int ptr, int length) - { - if (length == 2) - return (buffer[ptr] & 0x7F) * 256 + (buffer[ptr + 1] & 0xFF); - return buffer[ptr] & 0xFF; - } - - private void setParameters (int p1) - { - description = description.replaceFirst (":1", p1 + ""); - extras = "#" + p1; - } - - private void setParameters (int p1, int p2) - { - setParameters (p1); - extras += ", #" + p2; - description = description.replaceFirst (":2", p2 + ""); - } - - private void setParameters (int p1, int p2, String p3) - { - setParameters (p1, p2); - description = description.replaceFirst (":3", p3); - } - - @Override - public String toString () - { - String hex = getHex (buffer, ptr, length > 4 ? 4 : length); - StringBuilder text = new StringBuilder (); - text.append (String.format ("%2s%05X: %-11s %-6s %-10s %s%n", - jumpTarget ? "->" : "", ptr, hex, mnemonic, extras, description)); - if (length > 4) - { - int bytesLeft = length - 4; - int jmp = 0; - int p = ptr + 4; - while (bytesLeft > 0) - { - String line = getHex (buffer, p, (bytesLeft > 4) ? 4 : bytesLeft); - text.append (" " + line); - if (jumps.size () > 0) - { - if (jmp < jumps.size ()) - text.append (" " + jumps.get (jmp++)); - if (jmp < jumps.size ()) - text.append (" " + jumps.get (jmp++)); - } - text.append ("\n"); - bytesLeft -= 4; - p += 4; - } - } - return text.toString (); - } - - private String getHex (byte[] buffer, int offset, int length) - { - if ((offset + length) >= buffer.length) - { - System.out.println ("too many"); - return "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; - } - StringBuilder text = new StringBuilder (); - for (int i = 0; i < length; i++) - text.append (String.format ("%02X ", buffer[offset + i])); - if (text.length () > 0) - text.deleteCharAt (text.length () - 1); - return text.toString (); - } - - class Jump - { - int addressFrom; - int addressTo; - boolean caseJump; - int caseValue; - - public Jump (int addressFrom, int addressTo) - { - this.addressFrom = addressFrom; - this.addressTo = addressTo; - } - - public Jump (int addressFrom, int addressTo, int value) - { - this (addressFrom, addressTo); - this.caseValue = value; - this.caseJump = true; - } - - @Override - public String toString () - { - if (caseJump) - return String.format ("%3d: %04X", caseValue, addressTo); - return String.format ("%04X", addressTo); - } - } -} - -/* from Wizardry info 1.txt - -LDC instruction ---------------- -Earlier today I noticed the LDC pcode instruction seems to display -differently at times. Then I realized that it is operating with WORD -values and needs to have them aligned on an even BYTE boundary. For -example: - -5004 B3 02 8C 3F CD CC - LDC. PUSH 02 WORDS - -5017 B3 02 (02)8C 3F CD CC - LDC. PUSH 02 WORDS +package com.bytezone.diskbrowser.applefile; + +import java.util.ArrayList; +import java.util.List; + +import com.bytezone.diskbrowser.utilities.HexFormatter; + +// -----------------------------------------------------------------------------------// +public class PascalCodeStatement implements PascalConstants +// -----------------------------------------------------------------------------------// +{ + private static final String[] compValue = + { "invalid", "", "REAL", "", "STR", "", "BOOL", "", "POWR", "", "BYT", "", "WORD" }; + + int length; + int val; + int p1, p2, p3; + String mnemonic; + String extras = ""; + String description; + String text; + int ptr; // temp + byte[] buffer; + boolean jumpTarget; + List jumps = new ArrayList<> (); + + // ---------------------------------------------------------------------------------// + public PascalCodeStatement (byte[] buffer, int ptr, int procPtr) + // ---------------------------------------------------------------------------------// + { + this.ptr = ptr; + this.buffer = buffer; + length = 1; + val = buffer[ptr] & 0xFF; + if (val <= 127) + { + mnemonic = "SLDC"; + extras = "#" + val; + description = "Short load constant - push #" + val; + } + else if (val >= 248) + { + mnemonic = "SIND"; + extras = "#" + (val - 248); + description = "Short index load - push word *ToS + #" + (val - 248); + } + else if (val >= 232) + { + mnemonic = "SLDO"; + extras = "#" + (val - 231); + description = "Short load global - push BASE + #" + (val - 231); + } + else if (val >= 216) + { + mnemonic = "SLDL"; + extras = "#" + (val - 215); + description = "Short load local - push MP + #" + (val - 215); + } + else + { + mnemonic = mnemonics[val - 128]; + description = descriptions[val - 128]; + + length = mnemonicSize[val - 128]; + if (length != 1) + { + switch (val) + { + // W1, W2, W3,
- word aligned case jump + case 172: //XJP + int padding = (ptr % 2) == 0 ? 1 : 0; + p1 = getWord (buffer, ptr + padding + 1); + p2 = getWord (buffer, ptr + padding + 3); + p3 = getWord (buffer, ptr + padding + 5); + length = (p2 - p1 + 1) * 2 + 7 + padding; + setParameters (p1, p2, String.format ("%04X", p3)); + int v = p1; + int min = ptr + padding + 7; + int max = min + (p2 - p1) * 2; + for (int i = min; i <= max; i += 2) + { + jumps.add (new Jump (i, + i - HexFormatter.intValue (buffer[i], buffer[i + 1]), v++)); + } + break; + + // UB, - word aligned + case 179: //LDC + p1 = buffer[ptr + 1] & 0xFF; + padding = ptr % 2 == 0 ? 0 : 1; + length = p1 * 2 + padding + 2; + setParameters (p1); + break; + + // UB, + case 166: // LSA + case 208: // LPA + p1 = buffer[ptr + 1] & 0xFF; + length = p1 + 2; + if (val == 166) + { + text = HexFormatter.getPascalString (buffer, ptr + 1); + description += ": " + text; + } + break; + + // W + case 199: // LDCI + p1 = getWord (buffer, ptr + 1); + setParameters (p1); + break; + + // B + case 162: // INC + case 163: // IND + case 164: // IXA + case 165: // LAO + case 168: // MOV + case 169: // LDO + case 171: // SRO + case 198: // LLA + case 202: // LDL + case 204: // STL + case 213: // BPT + length = getLengthOfB (buffer[ptr + 1]) + 1; + p1 = getValueOfB (buffer, ptr + 1, length - 1); + setParameters (p1); + break; + + // DB, B or UB, B + case 157: // LDE + case 167: // LAE + case 178: // LDA + case 182: // LOD + case 184: // STR + case 209: // STE + length = getLengthOfB (buffer[ptr + 2]) + 2; + p1 = buffer[ptr + 1] & 0xFF; + p2 = getValueOfB (buffer, ptr + 2, length - 2); + setParameters (p1, p2); + break; + + // UB1, UB2 + case 192: // IXP + case 205: // CXP + p1 = buffer[ptr + 1] & 0xFF; + p2 = buffer[ptr + 2] & 0xFF; + setParameters (p1, p2); + break; + + // SB or DB + case 161: // FJP + case 173: // RNP + case 185: // UJP + case 193: // RBP + case 211: // EFJ + case 212: // NFJ + p1 = buffer[ptr + 1]; + if (val == 173 || val == 193) // return from procedure + setParameters (p1); + else if (p1 < 0) + { + // look up jump table entry + int address = procPtr + p1; + int ptr2 = address + - ((buffer[address + 1] & 0xFF) * 256 + (buffer[address] & 0xFF)); + extras = String.format ("$%04X", ptr2); + jumps.add (new Jump (ptr, ptr2)); + } + else + { + int address = ptr + length + p1; + extras = String.format ("$%04X", address); + jumps.add (new Jump (ptr, address)); + } + break; + + // UB + case 160: // AOJ + case 170: // SAS + case 174: // CIP + case 188: // LDM + case 189: // STM + case 194: // CBP + case 206: // CLP + case 207: // CGP + p1 = buffer[ptr + 1] & 0xFF; + setParameters (p1); + break; + + // CSP + case 158: + p1 = buffer[ptr + 1] & 0xFF; + if (p1 < CSP.length) + description = "Call standard procedure - " + CSP[p1]; + else + description = "Call standard procedure - index out of bounds"; + break; + + // Non-integer comparisons + case 175: + case 176: + case 177: + case 180: + case 181: + case 183: + p1 = buffer[ptr + 1] & 0xFF; // 2/4/6/8/10/12 + if (p1 < 0 || p1 >= compValue.length) + { + System.out.printf ("%d %d %d%n", val, p1, ptr); + mnemonic += "******************************"; + break; + } + mnemonic += compValue[p1]; + if (p1 == 10 || p1 == 12) + { + length = getLengthOfB (buffer[ptr + 2]) + 2; + p2 = getValueOfB (buffer, ptr + 2, length - 2); + setParameters (p2); + } + break; + + default: + System.out.println ("Forgot : " + val); + } + } + } + } + + // ---------------------------------------------------------------------------------// + private int getWord (byte[] buffer, int ptr) + // ---------------------------------------------------------------------------------// + { + return (buffer[ptr + 1] & 0xFF) * 256 + (buffer[ptr] & 0xFF); + } + + // ---------------------------------------------------------------------------------// + private int getLengthOfB (byte b) + // ---------------------------------------------------------------------------------// + { + return (b & 0x80) == 0x80 ? 2 : 1; + } + + // ---------------------------------------------------------------------------------// + private int getValueOfB (byte[] buffer, int ptr, int length) + // ---------------------------------------------------------------------------------// + { + if (length == 2) + return (buffer[ptr] & 0x7F) * 256 + (buffer[ptr + 1] & 0xFF); + return buffer[ptr] & 0xFF; + } + + // ---------------------------------------------------------------------------------// + private void setParameters (int p1) + // ---------------------------------------------------------------------------------// + { + description = description.replaceFirst (":1", p1 + ""); + extras = "#" + p1; + } + + // ---------------------------------------------------------------------------------// + private void setParameters (int p1, int p2) + // ---------------------------------------------------------------------------------// + { + setParameters (p1); + extras += ", #" + p2; + description = description.replaceFirst (":2", p2 + ""); + } + + // ---------------------------------------------------------------------------------// + private void setParameters (int p1, int p2, String p3) + // ---------------------------------------------------------------------------------// + { + setParameters (p1, p2); + description = description.replaceFirst (":3", p3); + } + + // ---------------------------------------------------------------------------------// + @Override + public String toString () + // ---------------------------------------------------------------------------------// + { + String hex = getHex (buffer, ptr, length > 4 ? 4 : length); + StringBuilder text = new StringBuilder (); + text.append (String.format ("%2s%05X: %-11s %-6s %-10s %s%n", + jumpTarget ? "->" : "", ptr, hex, mnemonic, extras, description)); + if (length > 4) + { + int bytesLeft = length - 4; + int jmp = 0; + int p = ptr + 4; + while (bytesLeft > 0) + { + String line = getHex (buffer, p, (bytesLeft > 4) ? 4 : bytesLeft); + text.append (" " + line); + if (jumps.size () > 0) + { + if (jmp < jumps.size ()) + text.append (" " + jumps.get (jmp++)); + if (jmp < jumps.size ()) + text.append (" " + jumps.get (jmp++)); + } + text.append ("\n"); + bytesLeft -= 4; + p += 4; + } + } + return text.toString (); + } + + // ---------------------------------------------------------------------------------// + private String getHex (byte[] buffer, int offset, int length) + // ---------------------------------------------------------------------------------// + { + if ((offset + length) >= buffer.length) + { + System.out.println ("too many"); + return "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; + } + StringBuilder text = new StringBuilder (); + for (int i = 0; i < length; i++) + text.append (String.format ("%02X ", buffer[offset + i])); + if (text.length () > 0) + text.deleteCharAt (text.length () - 1); + return text.toString (); + } + + // ---------------------------------------------------------------------------------// + class Jump + // ---------------------------------------------------------------------------------// + { + int addressFrom; + int addressTo; + boolean caseJump; + int caseValue; + + public Jump (int addressFrom, int addressTo) + { + this.addressFrom = addressFrom; + this.addressTo = addressTo; + } + + public Jump (int addressFrom, int addressTo, int value) + { + this (addressFrom, addressTo); + this.caseValue = value; + this.caseJump = true; + } + + @Override + public String toString () + { + if (caseJump) + return String.format ("%3d: %04X", caseValue, addressTo); + return String.format ("%04X", addressTo); + } + } +} + +/* from Wizardry info 1.txt + +LDC instruction +--------------- +Earlier today I noticed the LDC pcode instruction seems to display +differently at times. Then I realized that it is operating with WORD +values and needs to have them aligned on an even BYTE boundary. For +example: + +5004 B3 02 8C 3F CD CC + LDC. PUSH 02 WORDS + +5017 B3 02 (02)8C 3F CD CC + LDC. PUSH 02 WORDS */ \ No newline at end of file diff --git a/src/com/bytezone/diskbrowser/applefile/PascalConstants.java b/src/com/bytezone/diskbrowser/applefile/PascalConstants.java index 241bb7f..6813319 100755 --- a/src/com/bytezone/diskbrowser/applefile/PascalConstants.java +++ b/src/com/bytezone/diskbrowser/applefile/PascalConstants.java @@ -1,92 +1,95 @@ -package com.bytezone.diskbrowser.applefile; - -public interface PascalConstants -{ - static String[] mnemonics = - { "ABI", "ABR", "ADI", "ADR", "LAND", "DIF", "DVI", "DVR", "CHK", "FLO", "FLT", - "INN", "INT", "LOR", "MODI", "MPI", "MPR", "NGI", "NGR", "LNOT", "SRS", "SBI", - "SBR", "SGS", "SQI", "SQR", "STO", "IXS", "UNI", "LDE", "CSP", "LDCN", "ADJ", - "FJP", "INC", "IND", "IXA", "LAO", "LSA", "LAE", "MOV", "LDO", "SAS", "SRO", - "XJP", "RNP", "CIP", "EQU", "GEQ", "GRT", "LDA", "LDC", "LEQ", "LES", "LOD", - "NEQ", "STR", "UJP", "LDP", "STP", "LDM", "STM", "LDB", "STB", "IXP", "RBP", - "CBP", "EQUI", "GEQI", "GRTI", "LLA", "LDCI", "LEQI", "LESI", "LDL", "NEQI", - "STL", "CXP", "CLP", "CGP", "LPA", "STE", "???", "EFJ", "NFJ", "BPT", "XIT", - "NOP" }; - - static int[] mnemonicSize = - // - // 128 - 155 - // 156 - 183 - // 184 - 211 - // 212 - 239 - // 240 - 255 - - { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 3, 2, 1, 2, 2, 2, 2, 2, 2, 0, 3, 2, 2, 2, 2, 0, 2, 2, 2, 2, 2, 3, 0, 2, 2, - 3, 2, 3, 2, 1, 1, 2, 2, 1, 1, 3, 2, 2, 1, 1, 1, 2, 3, 1, 1, 2, 1, 2, 3, 2, 2, 0, - 3, 1, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }; - - static String[] descriptions = - { "Absolute value of integer - push ABS(ToS)", - "Absolute value of real - push abs((real)ToS)", "Add integers (tos + tos-1)", - "Add reals - push ToS + ToS-1", "Logical AND", - "Set difference - push difference of sets ToS-1 and ToS", - "Divide integers - push ToS-1 / ToS", "Divide reals - push ToS-1 / ToS", - "Check subrange bounds - assert ToS-1 <= ToS-2 <= ToS, pop ToS, pop ToS-1", - "Float next-to-ToS - push integer ToS-1 after converting to a real", - "Float ToS - push integer ToS after converting to a float", - "Set Membership - if int ToS-1 is in set ToS, push true, else push false", - "Set Intersection - push TOS AND TOS-1", "Logical OR", - "Modulo integers - push ToS-1 % ToS", "Multiply TOS by TOS-1", - "Multiply reals - push ToS-1 * ToS", - "Negate Integer - push two's complement of ToS", - "Negate real - push -((real)ToS)", "Logical Not - push one's complement of ToS", - "Build a subrange set", "Subtract Integers push ToS-1 - ToS", - "Subtract reals - push ToS-1 - ToS", "Build a singleton set", - "Square integer - push ToS ^ 2", "Square real - push ToS ^ 2", - "Store indirect word - store ToS into word pointed to by ToS-1", - "Index string array - push &(*ToS-1 + ToS)", - "Set union - push union of sets ToS OR ToS-1", - "Load extended word - push word at segment :1+:2", - "Call Standard Procedure #:1 - ", "Load Constant NIL", "Adjust set", - "Jump if ToS false", "Increment field ptr - push ToS+:1", - "Static index and load word", "Compute word pointer from ToS-1 + ToS * :1 words", - "Load Global - push (BASE+:1)", "Load constant string address", - "Load extended address - push address of word at segment :1+:2", - "Move words - transfer :1 words from *ToS to *ToS-1", - "Load Global Word - push BASE+:1", "String Assign", "Store TOS into BASE+:1", - "Case Jump - :1::2, Error: :3", "Return from non-base procedure (pass :1 words)", - "Call intermediate procedure #:1", "ToS-1 == ToS", "ToS-1 >= ToS", "ToS-1 > ToS", - "Load Intermediate Address - push :1th activation record +:2 bytes", - "Load multi-word constant - :1 words", "ToS-1 <= ToS", "ToS-1 < ToS", - "Load Intermediate Word - push :1th activation record +:2 bytes", "ToS-1 <> ToS", - "Store intermediate word - store TOS into :2, traverse :1", "Unconditional jump", - "Load Packed Field - push *ToS", "Store into packed field", - "Load multiple words - push block of unsigned bytes at *ToS", - "Store multiple words - store block of UB at ToS to *ToS-1", - "Load Byte - index the byte pointer ToS-1 by integer index ToS and push that byte", - "Store Byte - index the byte pointer ToS-2 by integer index ToS-1 and move ToS to that location", - "Index packed array - do complicated stuff with :1 and :2", - "Return from base procedure (pass :1 words)", - "Call Base Procedure :1 at lex level -1 or 0", "Compare Integer : ToS-1 = ToS", - "Compare Integer : TOS-1 >= TOS", "Compare Integer : TOS-1 > ToS", - "Load Local Address - push MP+:1", "Load Word - push #:1", - "Compare Integer : TOS-1 <= TOS", "Compare Integer : TOS-1 < ToS", - "Load Local Word - push MP+:1", "Compare Integer : TOS-1 <> TOS", - "Store Local Word - store ToS into MP+:1", - "Call external procedure #:2 in segment #:1", "Call local procedure #:1", - "Call global procedure #:1", "Load a packed array - use :1 and :2", - "Store extended word - store ToS into word at segment :1+:2", "210 ", - "Equal false jump - jump :1 if ToS-1 <> ToS", - "Not equal false jump - jump :1 if ToS-1 == ToS", - "Breakpoint - not used (does NOP)", "Exit OS - cold boot", "No-op" }; - - static String[] CSP = - { "000", "NEW", "MVL", "MVR", "EXIT", "", "", "IDS", "TRS", "TIM", "FLC", "SCN", "", - "", "", "", "", "", "", "", "", "021", "TNC", "RND", "", "", "", "", "", "", "", - "MRK", "RLS", "33", "34", "POT", "36", "37", "38", "39", "40" }; - - static String[] SegmentKind = { "Linked", "HostSeg", "SegProc", "UnitSeg", "SeprtSeg", - "UnlinkedIntrins", "LinkedIntrins", "DataSeg" }; +package com.bytezone.diskbrowser.applefile; + +// -----------------------------------------------------------------------------------// +public interface PascalConstants +// -----------------------------------------------------------------------------------// +{ + static String[] mnemonics = + { "ABI", "ABR", "ADI", "ADR", "LAND", "DIF", "DVI", "DVR", "CHK", "FLO", "FLT", + "INN", "INT", "LOR", "MODI", "MPI", "MPR", "NGI", "NGR", "LNOT", "SRS", "SBI", + "SBR", "SGS", "SQI", "SQR", "STO", "IXS", "UNI", "LDE", "CSP", "LDCN", "ADJ", + "FJP", "INC", "IND", "IXA", "LAO", "LSA", "LAE", "MOV", "LDO", "SAS", "SRO", + "XJP", "RNP", "CIP", "EQU", "GEQ", "GRT", "LDA", "LDC", "LEQ", "LES", "LOD", + "NEQ", "STR", "UJP", "LDP", "STP", "LDM", "STM", "LDB", "STB", "IXP", "RBP", + "CBP", "EQUI", "GEQI", "GRTI", "LLA", "LDCI", "LEQI", "LESI", "LDL", "NEQI", + "STL", "CXP", "CLP", "CGP", "LPA", "STE", "???", "EFJ", "NFJ", "BPT", "XIT", + "NOP" }; + + static int[] mnemonicSize = + // + // 128 - 155 + // 156 - 183 + // 184 - 211 + // 212 - 239 + // 240 - 255 + + { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 3, 2, 1, 2, 2, 2, 2, 2, 2, 0, 3, 2, 2, 2, 2, 0, 2, 2, 2, 2, 2, 3, 0, 2, 2, + 3, 2, 3, 2, 1, 1, 2, 2, 1, 1, 3, 2, 2, 1, 1, 1, 2, 3, 1, 1, 2, 1, 2, 3, 2, 2, 0, + 3, 1, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }; + + static String[] descriptions = + { "Absolute value of integer - push ABS(ToS)", + "Absolute value of real - push abs((real)ToS)", "Add integers (tos + tos-1)", + "Add reals - push ToS + ToS-1", "Logical AND", + "Set difference - push difference of sets ToS-1 and ToS", + "Divide integers - push ToS-1 / ToS", "Divide reals - push ToS-1 / ToS", + "Check subrange bounds - assert ToS-1 <= ToS-2 <= ToS, pop ToS, pop ToS-1", + "Float next-to-ToS - push integer ToS-1 after converting to a real", + "Float ToS - push integer ToS after converting to a float", + "Set Membership - if int ToS-1 is in set ToS, push true, else push false", + "Set Intersection - push TOS AND TOS-1", "Logical OR", + "Modulo integers - push ToS-1 % ToS", "Multiply TOS by TOS-1", + "Multiply reals - push ToS-1 * ToS", + "Negate Integer - push two's complement of ToS", + "Negate real - push -((real)ToS)", "Logical Not - push one's complement of ToS", + "Build a subrange set", "Subtract Integers push ToS-1 - ToS", + "Subtract reals - push ToS-1 - ToS", "Build a singleton set", + "Square integer - push ToS ^ 2", "Square real - push ToS ^ 2", + "Store indirect word - store ToS into word pointed to by ToS-1", + "Index string array - push &(*ToS-1 + ToS)", + "Set union - push union of sets ToS OR ToS-1", + "Load extended word - push word at segment :1+:2", + "Call Standard Procedure #:1 - ", "Load Constant NIL", "Adjust set", + "Jump if ToS false", "Increment field ptr - push ToS+:1", + "Static index and load word", "Compute word pointer from ToS-1 + ToS * :1 words", + "Load Global - push (BASE+:1)", "Load constant string address", + "Load extended address - push address of word at segment :1+:2", + "Move words - transfer :1 words from *ToS to *ToS-1", + "Load Global Word - push BASE+:1", "String Assign", "Store TOS into BASE+:1", + "Case Jump - :1::2, Error: :3", "Return from non-base procedure (pass :1 words)", + "Call intermediate procedure #:1", "ToS-1 == ToS", "ToS-1 >= ToS", "ToS-1 > ToS", + "Load Intermediate Address - push :1th activation record +:2 bytes", + "Load multi-word constant - :1 words", "ToS-1 <= ToS", "ToS-1 < ToS", + "Load Intermediate Word - push :1th activation record +:2 bytes", "ToS-1 <> ToS", + "Store intermediate word - store TOS into :2, traverse :1", "Unconditional jump", + "Load Packed Field - push *ToS", "Store into packed field", + "Load multiple words - push block of unsigned bytes at *ToS", + "Store multiple words - store block of UB at ToS to *ToS-1", + "Load Byte - index the byte pointer ToS-1 by integer index ToS and push that byte", + "Store Byte - index the byte pointer ToS-2 by integer " + + "index ToS-1 and move ToS to that location", + "Index packed array - do complicated stuff with :1 and :2", + "Return from base procedure (pass :1 words)", + "Call Base Procedure :1 at lex level -1 or 0", "Compare Integer : ToS-1 = ToS", + "Compare Integer : TOS-1 >= TOS", "Compare Integer : TOS-1 > ToS", + "Load Local Address - push MP+:1", "Load Word - push #:1", + "Compare Integer : TOS-1 <= TOS", "Compare Integer : TOS-1 < ToS", + "Load Local Word - push MP+:1", "Compare Integer : TOS-1 <> TOS", + "Store Local Word - store ToS into MP+:1", + "Call external procedure #:2 in segment #:1", "Call local procedure #:1", + "Call global procedure #:1", "Load a packed array - use :1 and :2", + "Store extended word - store ToS into word at segment :1+:2", "210 ", + "Equal false jump - jump :1 if ToS-1 <> ToS", + "Not equal false jump - jump :1 if ToS-1 == ToS", + "Breakpoint - not used (does NOP)", "Exit OS - cold boot", "No-op" }; + + static String[] CSP = + { "000", "NEW", "MVL", "MVR", "EXIT", "", "", "IDS", "TRS", "TIM", "FLC", "SCN", "", + "", "", "", "", "", "", "", "", "021", "TNC", "RND", "", "", "", "", "", "", "", + "MRK", "RLS", "33", "34", "POT", "36", "37", "38", "39", "40" }; + + static String[] SegmentKind = { "Linked", "HostSeg", "SegProc", "UnitSeg", "SeprtSeg", + "UnlinkedIntrins", "LinkedIntrins", "DataSeg" }; } \ No newline at end of file diff --git a/src/com/bytezone/diskbrowser/applefile/PascalInfo.java b/src/com/bytezone/diskbrowser/applefile/PascalInfo.java index c584801..ac855c9 100644 --- a/src/com/bytezone/diskbrowser/applefile/PascalInfo.java +++ b/src/com/bytezone/diskbrowser/applefile/PascalInfo.java @@ -1,15 +1,20 @@ package com.bytezone.diskbrowser.applefile; +// -----------------------------------------------------------------------------------// public class PascalInfo extends AbstractFile +// -----------------------------------------------------------------------------------// { - + // ---------------------------------------------------------------------------------// public PascalInfo (String name, byte[] buffer) + // ---------------------------------------------------------------------------------// { super (name, buffer); } + // ---------------------------------------------------------------------------------// @Override public String getText () + // ---------------------------------------------------------------------------------// { StringBuilder text = new StringBuilder (getHeader ()); @@ -22,7 +27,9 @@ public class PascalInfo extends AbstractFile return text.toString (); } + // ---------------------------------------------------------------------------------// private String getHeader () + // ---------------------------------------------------------------------------------// { return "Name : " + name + "\n\n"; } diff --git a/src/com/bytezone/diskbrowser/applefile/PascalProcedure.java b/src/com/bytezone/diskbrowser/applefile/PascalProcedure.java index 90d2552..60b9aa2 100755 --- a/src/com/bytezone/diskbrowser/applefile/PascalProcedure.java +++ b/src/com/bytezone/diskbrowser/applefile/PascalProcedure.java @@ -1,189 +1,199 @@ -package com.bytezone.diskbrowser.applefile; - -import java.util.ArrayList; -import java.util.List; - -import com.bytezone.diskbrowser.applefile.PascalCodeStatement.Jump; -import com.bytezone.diskbrowser.utilities.HexFormatter; - -public class PascalProcedure -{ - // all procedures have these fields - byte[] buffer; - int procOffset; - int offset; - int slot; - boolean valid; - - // only valid procedures have these fields - int procedureNo; - int procLevel; - int codeStart; - int codeEnd; - int parmSize; - int dataSize; - List statements = new ArrayList<> (); - AssemblerProgram assembler; - int jumpTable = -8; - - public PascalProcedure (byte[] buffer, int slot) - { - this.buffer = buffer; - this.slot = slot; - int p = buffer.length - 2 - slot * 2; - offset = HexFormatter.intValue (buffer[p], buffer[p + 1]); - procOffset = p - offset; - valid = procOffset > 0; - - if (valid) - { - procedureNo = buffer[procOffset] & 0xFF; - procLevel = buffer[procOffset + 1] & 0xFF; - codeStart = HexFormatter.intValue (buffer[procOffset - 2], buffer[procOffset - 1]); - codeEnd = HexFormatter.intValue (buffer[procOffset - 4], buffer[procOffset - 3]); - parmSize = HexFormatter.intValue (buffer[procOffset - 6], buffer[procOffset - 5]); - dataSize = HexFormatter.intValue (buffer[procOffset - 8], buffer[procOffset - 7]); - } - } - - private void decode () - { - if (statements.size () > 0 || assembler != null) - return; - int ptr = procOffset - codeStart - 2; - int max = procOffset + jumpTable; - - if (codeEnd == 0) - { - int len = codeStart + jumpTable + 2; - if (len > 0) - { - byte[] asmBuf = new byte[len]; - System.arraycopy (buffer, ptr, asmBuf, 0, len); - assembler = new AssemblerProgram ("Proc", asmBuf, ptr); - } - return; - } - - while (ptr < max) - { - // System.out.printf ("ptr:%d, max:%d, buf:%d %n", ptr, max, buffer.length); - if (ptr >= buffer.length || ptr < 0) - { - System.out.printf ("Ptr outside buffer: %d %d%n", ptr, buffer.length); - break; - } - PascalCodeStatement cs = new PascalCodeStatement (buffer, ptr, procOffset); - if (cs.length <= 0) - { - System.out.println ("error - length <= 0 : " + cs); - break; - } - statements.add (cs); - if (cs.val == 185 || cs.val == 161) - if (cs.p1 < jumpTable) - { - jumpTable = cs.p1; - max = procOffset + jumpTable; - } - ptr += cs.length; - } - - // Tidy up left-over bytes at the end - if (statements.size () > 1) - { - PascalCodeStatement lastStatement = statements.get (statements.size () - 1); - PascalCodeStatement secondLastStatement = statements.get (statements.size () - 2); - if (lastStatement.val == 0 && (secondLastStatement.val == 0xD6 - || secondLastStatement.val == 0xC1 || secondLastStatement.val == 0xAD)) - statements.remove (statements.size () - 1); - } - - // Mark statements that are jump targets - int actualEnd = procOffset - codeEnd - 4; - for (PascalCodeStatement cs : statements) - { - if (cs.ptr == actualEnd) - { - cs.jumpTarget = true; - continue; - } - for (Jump cj : cs.jumps) - for (PascalCodeStatement cs2 : statements) - if (cs2.ptr == cj.addressTo) - { - cs2.jumpTarget = true; - break; - } - } - } - - public List extractStrings () - { - decode (); - List strings = new ArrayList<> (); - for (PascalCodeStatement cs : statements) - if (cs.val == 166) - strings.add (cs); - return strings; - } - - @Override - public String toString () - { - if (!valid) - return ""; - decode (); - - StringBuilder text = new StringBuilder ("\nProcedure Header\n================\n\n"); - - if (false) - text.append ( - HexFormatter.format (buffer, procOffset + jumpTable, 2 - jumpTable) + "\n\n"); - - text.append ( - String.format ("Level.......%5d %02X%n", procLevel, procLevel & 0xFF)); - text.append (String.format ("Proc no.....%5d %02X%n", procedureNo, procedureNo)); - text.append (String.format ("Code entry..%5d %04X (%04X - %04X = %04X)%n", - codeStart, codeStart, (procOffset - 2), codeStart, (procOffset - codeStart - 2))); - text.append (String.format ("Code exit...%5d %04X", codeEnd, codeEnd)); - if (codeEnd > 0) - text.append (String.format (" (%04X - %04X = %04X)%n", (procOffset - 4), codeEnd, - (procOffset - codeEnd - 4))); - else - text.append (String.format ("%n")); - text.append (String.format ("Parm size...%5d %04X%n", parmSize, parmSize)); - text.append (String.format ("Data size...%5d %04X%n%n", dataSize, dataSize)); - - text.append ("Procedure Code\n==============\n\n"); - - int ptr = procOffset - codeStart - 2; - if (false) - text.append (HexFormatter.format (buffer, ptr, codeStart + jumpTable + 2) + "\n\n"); - - if (codeEnd == 0) - { - if (assembler != null) - text.append (assembler.getAssembler () + "\n"); - else - text.append ("Null assembler in PascalProcedure"); - } - else - { - for (PascalCodeStatement cs : statements) - text.append (cs); - - if (jumpTable < -8 && false) - { - text.append ("\nJump table:\n"); - for (int i = procOffset + jumpTable; i < procOffset - 8; i += 2) - { - ptr = i - ((buffer[i + 1] & 0xFF) * 256 + (buffer[i] & 0xFF)); - text.append (String.format ("%05X : %02X %02X --> %04X%n", i, buffer[i], - buffer[i + 1], ptr)); - } - } - } - return text.toString (); - } +package com.bytezone.diskbrowser.applefile; + +import java.util.ArrayList; +import java.util.List; + +import com.bytezone.diskbrowser.applefile.PascalCodeStatement.Jump; +import com.bytezone.diskbrowser.utilities.HexFormatter; + +// -----------------------------------------------------------------------------------// +public class PascalProcedure +// -----------------------------------------------------------------------------------// +{ + // all procedures have these fields + byte[] buffer; + int procOffset; + int offset; + int slot; + boolean valid; + + // only valid procedures have these fields + int procedureNo; + int procLevel; + int codeStart; + int codeEnd; + int parmSize; + int dataSize; + List statements = new ArrayList<> (); + AssemblerProgram assembler; + int jumpTable = -8; + + // ---------------------------------------------------------------------------------// + public PascalProcedure (byte[] buffer, int slot) + // ---------------------------------------------------------------------------------// + { + this.buffer = buffer; + this.slot = slot; + int p = buffer.length - 2 - slot * 2; + offset = HexFormatter.intValue (buffer[p], buffer[p + 1]); + procOffset = p - offset; + valid = procOffset > 0; + + if (valid) + { + procedureNo = buffer[procOffset] & 0xFF; + procLevel = buffer[procOffset + 1] & 0xFF; + codeStart = HexFormatter.intValue (buffer[procOffset - 2], buffer[procOffset - 1]); + codeEnd = HexFormatter.intValue (buffer[procOffset - 4], buffer[procOffset - 3]); + parmSize = HexFormatter.intValue (buffer[procOffset - 6], buffer[procOffset - 5]); + dataSize = HexFormatter.intValue (buffer[procOffset - 8], buffer[procOffset - 7]); + } + } + + // ---------------------------------------------------------------------------------// + private void decode () + // ---------------------------------------------------------------------------------// + { + if (statements.size () > 0 || assembler != null) + return; + int ptr = procOffset - codeStart - 2; + int max = procOffset + jumpTable; + + if (codeEnd == 0) + { + int len = codeStart + jumpTable + 2; + if (len > 0) + { + byte[] asmBuf = new byte[len]; + System.arraycopy (buffer, ptr, asmBuf, 0, len); + assembler = new AssemblerProgram ("Proc", asmBuf, ptr); + } + return; + } + + while (ptr < max) + { + // System.out.printf ("ptr:%d, max:%d, buf:%d %n", ptr, max, buffer.length); + if (ptr >= buffer.length || ptr < 0) + { + System.out.printf ("Ptr outside buffer: %d %d%n", ptr, buffer.length); + break; + } + PascalCodeStatement cs = new PascalCodeStatement (buffer, ptr, procOffset); + if (cs.length <= 0) + { + System.out.println ("error - length <= 0 : " + cs); + break; + } + statements.add (cs); + if (cs.val == 185 || cs.val == 161) + if (cs.p1 < jumpTable) + { + jumpTable = cs.p1; + max = procOffset + jumpTable; + } + ptr += cs.length; + } + + // Tidy up left-over bytes at the end + if (statements.size () > 1) + { + PascalCodeStatement lastStatement = statements.get (statements.size () - 1); + PascalCodeStatement secondLastStatement = statements.get (statements.size () - 2); + if (lastStatement.val == 0 && (secondLastStatement.val == 0xD6 + || secondLastStatement.val == 0xC1 || secondLastStatement.val == 0xAD)) + statements.remove (statements.size () - 1); + } + + // Mark statements that are jump targets + int actualEnd = procOffset - codeEnd - 4; + for (PascalCodeStatement cs : statements) + { + if (cs.ptr == actualEnd) + { + cs.jumpTarget = true; + continue; + } + for (Jump cj : cs.jumps) + for (PascalCodeStatement cs2 : statements) + if (cs2.ptr == cj.addressTo) + { + cs2.jumpTarget = true; + break; + } + } + } + + // ---------------------------------------------------------------------------------// + public List extractStrings () + // ---------------------------------------------------------------------------------// + { + decode (); + List strings = new ArrayList<> (); + for (PascalCodeStatement cs : statements) + if (cs.val == 166) + strings.add (cs); + return strings; + } + + // ---------------------------------------------------------------------------------// + @Override + public String toString () + // ---------------------------------------------------------------------------------// + { + if (!valid) + return ""; + decode (); + + StringBuilder text = new StringBuilder ("\nProcedure Header\n================\n\n"); + + if (false) + text.append ( + HexFormatter.format (buffer, procOffset + jumpTable, 2 - jumpTable) + "\n\n"); + + text.append ( + String.format ("Level.......%5d %02X%n", procLevel, procLevel & 0xFF)); + text.append (String.format ("Proc no.....%5d %02X%n", procedureNo, procedureNo)); + text.append (String.format ("Code entry..%5d %04X (%04X - %04X = %04X)%n", + codeStart, codeStart, (procOffset - 2), codeStart, (procOffset - codeStart - 2))); + text.append (String.format ("Code exit...%5d %04X", codeEnd, codeEnd)); + if (codeEnd > 0) + text.append (String.format (" (%04X - %04X = %04X)%n", (procOffset - 4), codeEnd, + (procOffset - codeEnd - 4))); + else + text.append (String.format ("%n")); + text.append (String.format ("Parm size...%5d %04X%n", parmSize, parmSize)); + text.append (String.format ("Data size...%5d %04X%n%n", dataSize, dataSize)); + + text.append ("Procedure Code\n==============\n\n"); + + int ptr = procOffset - codeStart - 2; + if (false) + text.append (HexFormatter.format (buffer, ptr, codeStart + jumpTable + 2) + "\n\n"); + + if (codeEnd == 0) + { + if (assembler != null) + text.append (assembler.getAssembler () + "\n"); + else + text.append ("Null assembler in PascalProcedure"); + } + else + { + for (PascalCodeStatement cs : statements) + text.append (cs); + + if (jumpTable < -8 && false) + { + text.append ("\nJump table:\n"); + for (int i = procOffset + jumpTable; i < procOffset - 8; i += 2) + { + ptr = i - ((buffer[i + 1] & 0xFF) * 256 + (buffer[i] & 0xFF)); + text.append (String.format ("%05X : %02X %02X --> %04X%n", i, buffer[i], + buffer[i + 1], ptr)); + } + } + } + return text.toString (); + } } \ No newline at end of file diff --git a/src/com/bytezone/diskbrowser/applefile/PascalSegment.java b/src/com/bytezone/diskbrowser/applefile/PascalSegment.java index 3c9654b..34ff8e2 100755 --- a/src/com/bytezone/diskbrowser/applefile/PascalSegment.java +++ b/src/com/bytezone/diskbrowser/applefile/PascalSegment.java @@ -1,229 +1,241 @@ -package com.bytezone.diskbrowser.applefile; - -import java.util.ArrayList; -import java.util.List; - -import com.bytezone.diskbrowser.utilities.FileFormatException; -import com.bytezone.diskbrowser.utilities.HexFormatter; - -public class PascalSegment extends AbstractFile implements PascalConstants -{ - private final static int BLOCK_SIZE = 512; - final int segmentNoHeader; - private int segmentNoBody; - // private final int blockOffset; - // private final Relocator relocator; - boolean debug = false; - - public int blockNo; - // public int newBlockNo; - public final int size; - - private final int segKind; - private final int textAddress; - private final int machineType; - private final int version; - private final int intrinsSegs1; - private final int intrinsSegs2; - private final int slot; - private int totalProcedures; - private List procedures; - // private List addresses; - - public PascalSegment (String name, byte[] fullBuffer, int seq, int blockOffset) - { - super (name, fullBuffer); // sets this.buffer to the full buffer temporarily - - this.slot = seq; - // this.blockOffset = blockOffset; - // this.relocator = relocator; - - this.blockNo = HexFormatter.intValue (fullBuffer[seq * 4], fullBuffer[seq * 4 + 1]); - this.size = HexFormatter.intValue (fullBuffer[seq * 4 + 2], fullBuffer[seq * 4 + 3]); - - segKind = HexFormatter.intValue (fullBuffer[0xC0 + seq * 2], - fullBuffer[0xC0 + seq * 2 + 1]); - - textAddress = HexFormatter.intValue (fullBuffer[0xE0 + seq * 2], - fullBuffer[0xE0 + seq * 2 + 1]); - - // segment 1 is the main segment, 2-6 are used by the system, and 7 - // onwards is for the program - this.segmentNoHeader = fullBuffer[0x100 + seq * 2] & 0xFF; - int flags = fullBuffer[0x101 + seq * 2] & 0xFF; - - // 0 unknown, - // 1 positive byte sex p-code - // 2 negative byte sex p-code (apple pascal) - // 3-9 6502 code (7 = apple 6502) - machineType = flags & 0x0F; - - version = (flags & 0xD0) >> 5; - - intrinsSegs1 = HexFormatter.intValue (fullBuffer[0x120 + seq * 4], - fullBuffer[0x120 + seq * 4 + 1]); - intrinsSegs2 = HexFormatter.intValue (fullBuffer[0x120 + seq * 4 + 2], - fullBuffer[0x120 + seq * 4 + 3]); - - int offset = blockNo * 512; - - // if (relocator != null) - // { - // // if (segmentNoHeader > 1) - // // { - // int sizeInBlocks = (size - 1) / BLOCK_SIZE + 1; - // int targetBlock = blockNo + blockOffset; - // addresses = relocator.getMultiDiskAddress (name, targetBlock, sizeInBlocks); - // if (addresses.size () > 0) - // { - // MultiDiskAddress multiDiskAddress = addresses.get (0); - // if (multiDiskAddress.diskNumber == 1) - // offset = (multiDiskAddress.physicalBlockNumber - blockOffset) * BLOCK_SIZE; - // else - // offset = -1; - // } - // // } - // } - - if (offset < 0) - { - buffer = new byte[0]; - } - else if ((offset + size) < fullBuffer.length) - { - buffer = new byte[size]; // replaces this.buffer with the segment buffer only - System.arraycopy (fullBuffer, offset, buffer, 0, size); - totalProcedures = buffer[size - 1] & 0xFF; - segmentNoBody = buffer[size - 2] & 0xFF; - - if (debug) - if (segmentNoHeader == 0) - System.out.printf ("Zero segment header in %s seq %d%n", name, seq); - else if (segmentNoBody != segmentNoHeader) - System.out.println ( - "Segment number mismatch : " + segmentNoBody + " / " + segmentNoHeader); - } - else - { - throw new FileFormatException ("Error in PascalSegment"); - } - } - - // void setMultiDiskAddresses (List addresses) - // { - // this.addresses = addresses; - // } - - private void buildProcedureList () - { - procedures = new ArrayList<> (totalProcedures); - - for (int i = 1; i <= totalProcedures; i++) - procedures.add (new PascalProcedure (buffer, i)); - } - - public String toText () - { - int sizeInBlocks = (size - 1) / BLOCK_SIZE + 1; - - return String.format ( - " %2d %02X %02X %04X %-8s %-15s%3d " + "%02X %d %d %d %d %s", - slot, blockNo, sizeInBlocks, size, name, SegmentKind[segKind], textAddress, - segmentNoHeader, machineType, version, intrinsSegs1, intrinsSegs2, - getMultiDiskAddresses ()); - } - - @Override - public String getText () - { - if (procedures == null) - buildProcedureList (); - - StringBuilder text = new StringBuilder (); - String title = "Segment - " + name; - text.append (title + "\n" - + "===============================".substring (0, title.length ()) + "\n\n"); - String warning = segmentNoBody == segmentNoHeader ? "" - : String.format (" (%02X in routine)", segmentNoBody); - text.append (String.format ("Address........ %02X%n", blockNo)); - // if (addresses != null) - text.append (String.format ("Multi disk .... %s%n", getMultiDiskAddresses ())); - text.append (String.format ("Length......... %04X%n", buffer.length)); - text.append (String.format ("Machine type... %d%n", machineType)); - text.append (String.format ("Version........ %d%n", version)); - text.append (String.format ("Segment........ %02X%s%n", segmentNoHeader, warning)); - text.append (String.format ("Total procs.... %d%n", procedures.size ())); - - text.append ("\nProcedure Dictionary\n====================\n\n"); - - int len = procedures.size () * 2 + 2; - if (false) - text.append (HexFormatter.format (buffer, buffer.length - len, len) + "\n\n"); - - text.append ("Proc Offset Lvl Entry Exit Parm Data Proc header\n"); - text.append ( - "---- ------ --- ----- ---- ---- ---- --------------------\n"); - for (PascalProcedure procedure : procedures) - { - if (procedure.valid) - { - int address = size - procedure.slot * 2 - 2; - text.append (String.format ( - " %3d %04X %3d %04X %04X %04X %04X (%04X - %04X = %04X)%n", - procedure.procedureNo, procedure.offset, procedure.procLevel, - procedure.codeStart, procedure.codeEnd, procedure.parmSize, - procedure.dataSize, address, procedure.offset, procedure.procOffset)); - } - else - text.append (String.format (" %3d %04X%n", procedure.slot, procedure.offset)); - } - - text.append ("\nStrings\n=======\n"); - for (PascalProcedure pp : procedures) - { - List strings = pp.extractStrings (); - for (PascalCodeStatement cs : strings) - text.append ( - String.format (" %2d %04X %s%n", pp.procedureNo, cs.ptr, cs.text)); - } - - for (PascalProcedure procedure : procedures) - if (procedure.valid) - text.append (procedure); - - return text.toString (); - } - - private String getMultiDiskAddresses () - { - String multiDiskAddressText = ""; - // int sizeInBlocks = (size - 1) / BLOCK_SIZE + 1; - - // if (segmentNoHeader == 1) // main segment - // { - // multiDiskAddressText = String.format ("1:%03X", (blockNo + blockOffset)); - // } - // else - // if (relocator != null) - // { - // int targetBlock = blockNo + blockOffset; - // List addresses = - // relocator.getMultiDiskAddress (name, targetBlock, sizeInBlocks); - // if (addresses.isEmpty ()) - // multiDiskAddressText = "."; - // else - // { - // StringBuilder locations = new StringBuilder (); - // for (MultiDiskAddress multiDiskAddress : addresses) - // locations.append (multiDiskAddress.toString () + ", "); - // if (locations.length () > 2) - // { - // locations.deleteCharAt (locations.length () - 1); - // locations.deleteCharAt (locations.length () - 1); - // } - // multiDiskAddressText = locations.toString (); - // } - // } - return multiDiskAddressText; - } +package com.bytezone.diskbrowser.applefile; + +import java.util.ArrayList; +import java.util.List; + +import com.bytezone.diskbrowser.utilities.FileFormatException; +import com.bytezone.diskbrowser.utilities.HexFormatter; + +// -----------------------------------------------------------------------------------// +public class PascalSegment extends AbstractFile implements PascalConstants +// -----------------------------------------------------------------------------------// +{ + private final static int BLOCK_SIZE = 512; + final int segmentNoHeader; + private int segmentNoBody; + // private final int blockOffset; + // private final Relocator relocator; + boolean debug = false; + + public int blockNo; + // public int newBlockNo; + public final int size; + + private final int segKind; + private final int textAddress; + private final int machineType; + private final int version; + private final int intrinsSegs1; + private final int intrinsSegs2; + private final int slot; + private int totalProcedures; + private List procedures; + // private List addresses; + + // ---------------------------------------------------------------------------------// + public PascalSegment (String name, byte[] fullBuffer, int seq, int blockOffset) + // ---------------------------------------------------------------------------------// + { + super (name, fullBuffer); // sets this.buffer to the full buffer temporarily + + this.slot = seq; + // this.blockOffset = blockOffset; + // this.relocator = relocator; + + this.blockNo = HexFormatter.intValue (fullBuffer[seq * 4], fullBuffer[seq * 4 + 1]); + this.size = HexFormatter.intValue (fullBuffer[seq * 4 + 2], fullBuffer[seq * 4 + 3]); + + segKind = HexFormatter.intValue (fullBuffer[0xC0 + seq * 2], + fullBuffer[0xC0 + seq * 2 + 1]); + + textAddress = HexFormatter.intValue (fullBuffer[0xE0 + seq * 2], + fullBuffer[0xE0 + seq * 2 + 1]); + + // segment 1 is the main segment, 2-6 are used by the system, and 7 + // onwards is for the program + this.segmentNoHeader = fullBuffer[0x100 + seq * 2] & 0xFF; + int flags = fullBuffer[0x101 + seq * 2] & 0xFF; + + // 0 unknown, + // 1 positive byte sex p-code + // 2 negative byte sex p-code (apple pascal) + // 3-9 6502 code (7 = apple 6502) + machineType = flags & 0x0F; + + version = (flags & 0xD0) >> 5; + + intrinsSegs1 = HexFormatter.intValue (fullBuffer[0x120 + seq * 4], + fullBuffer[0x120 + seq * 4 + 1]); + intrinsSegs2 = HexFormatter.intValue (fullBuffer[0x120 + seq * 4 + 2], + fullBuffer[0x120 + seq * 4 + 3]); + + int offset = blockNo * 512; + + // if (relocator != null) + // { + // // if (segmentNoHeader > 1) + // // { + // int sizeInBlocks = (size - 1) / BLOCK_SIZE + 1; + // int targetBlock = blockNo + blockOffset; + // addresses = relocator.getMultiDiskAddress (name, targetBlock, sizeInBlocks); + // if (addresses.size () > 0) + // { + // MultiDiskAddress multiDiskAddress = addresses.get (0); + // if (multiDiskAddress.diskNumber == 1) + // offset = (multiDiskAddress.physicalBlockNumber - blockOffset) * BLOCK_SIZE; + // else + // offset = -1; + // } + // // } + // } + + if (offset < 0) + { + buffer = new byte[0]; + } + else if ((offset + size) < fullBuffer.length) + { + buffer = new byte[size]; // replaces this.buffer with the segment buffer only + System.arraycopy (fullBuffer, offset, buffer, 0, size); + totalProcedures = buffer[size - 1] & 0xFF; + segmentNoBody = buffer[size - 2] & 0xFF; + + if (debug) + if (segmentNoHeader == 0) + System.out.printf ("Zero segment header in %s seq %d%n", name, seq); + else if (segmentNoBody != segmentNoHeader) + System.out.println ( + "Segment number mismatch : " + segmentNoBody + " / " + segmentNoHeader); + } + else + { + throw new FileFormatException ("Error in PascalSegment"); + } + } + + // void setMultiDiskAddresses (List addresses) + // { + // this.addresses = addresses; + // } + + // ---------------------------------------------------------------------------------// + private void buildProcedureList () + // ---------------------------------------------------------------------------------// + { + procedures = new ArrayList<> (totalProcedures); + + for (int i = 1; i <= totalProcedures; i++) + procedures.add (new PascalProcedure (buffer, i)); + } + + // ---------------------------------------------------------------------------------// + public String toText () + // ---------------------------------------------------------------------------------// + { + int sizeInBlocks = (size - 1) / BLOCK_SIZE + 1; + + return String.format ( + " %2d %02X %02X %04X %-8s %-15s%3d " + "%02X %d %d %d %d %s", + slot, blockNo, sizeInBlocks, size, name, SegmentKind[segKind], textAddress, + segmentNoHeader, machineType, version, intrinsSegs1, intrinsSegs2, + getMultiDiskAddresses ()); + } + + // ---------------------------------------------------------------------------------// + @Override + public String getText () + // ---------------------------------------------------------------------------------// + { + if (procedures == null) + buildProcedureList (); + + StringBuilder text = new StringBuilder (); + String title = "Segment - " + name; + text.append (title + "\n" + + "===============================".substring (0, title.length ()) + "\n\n"); + String warning = segmentNoBody == segmentNoHeader ? "" + : String.format (" (%02X in routine)", segmentNoBody); + text.append (String.format ("Address........ %02X%n", blockNo)); + // if (addresses != null) + text.append (String.format ("Multi disk .... %s%n", getMultiDiskAddresses ())); + text.append (String.format ("Length......... %04X%n", buffer.length)); + text.append (String.format ("Machine type... %d%n", machineType)); + text.append (String.format ("Version........ %d%n", version)); + text.append (String.format ("Segment........ %02X%s%n", segmentNoHeader, warning)); + text.append (String.format ("Total procs.... %d%n", procedures.size ())); + + text.append ("\nProcedure Dictionary\n====================\n\n"); + + int len = procedures.size () * 2 + 2; + if (false) + text.append (HexFormatter.format (buffer, buffer.length - len, len) + "\n\n"); + + text.append ("Proc Offset Lvl Entry Exit Parm Data Proc header\n"); + text.append ( + "---- ------ --- ----- ---- ---- ---- --------------------\n"); + for (PascalProcedure procedure : procedures) + { + if (procedure.valid) + { + int address = size - procedure.slot * 2 - 2; + text.append (String.format ( + " %3d %04X %3d %04X %04X %04X %04X (%04X - %04X = %04X)%n", + procedure.procedureNo, procedure.offset, procedure.procLevel, + procedure.codeStart, procedure.codeEnd, procedure.parmSize, + procedure.dataSize, address, procedure.offset, procedure.procOffset)); + } + else + text.append (String.format (" %3d %04X%n", procedure.slot, procedure.offset)); + } + + text.append ("\nStrings\n=======\n"); + for (PascalProcedure pp : procedures) + { + List strings = pp.extractStrings (); + for (PascalCodeStatement cs : strings) + text.append ( + String.format (" %2d %04X %s%n", pp.procedureNo, cs.ptr, cs.text)); + } + + for (PascalProcedure procedure : procedures) + if (procedure.valid) + text.append (procedure); + + return text.toString (); + } + + // ---------------------------------------------------------------------------------// + private String getMultiDiskAddresses () + // ---------------------------------------------------------------------------------// + { + String multiDiskAddressText = ""; + // int sizeInBlocks = (size - 1) / BLOCK_SIZE + 1; + + // if (segmentNoHeader == 1) // main segment + // { + // multiDiskAddressText = String.format ("1:%03X", (blockNo + blockOffset)); + // } + // else + // if (relocator != null) + // { + // int targetBlock = blockNo + blockOffset; + // List addresses = + // relocator.getMultiDiskAddress (name, targetBlock, sizeInBlocks); + // if (addresses.isEmpty ()) + // multiDiskAddressText = "."; + // else + // { + // StringBuilder locations = new StringBuilder (); + // for (MultiDiskAddress multiDiskAddress : addresses) + // locations.append (multiDiskAddress.toString () + ", "); + // if (locations.length () > 2) + // { + // locations.deleteCharAt (locations.length () - 1); + // locations.deleteCharAt (locations.length () - 1); + // } + // multiDiskAddressText = locations.toString (); + // } + // } + return multiDiskAddressText; + } } \ No newline at end of file diff --git a/src/com/bytezone/diskbrowser/applefile/PascalText.java b/src/com/bytezone/diskbrowser/applefile/PascalText.java index de14835..8bbac77 100755 --- a/src/com/bytezone/diskbrowser/applefile/PascalText.java +++ b/src/com/bytezone/diskbrowser/applefile/PascalText.java @@ -1,53 +1,63 @@ -package com.bytezone.diskbrowser.applefile; - -public class PascalText extends AbstractFile -{ - public PascalText (String name, byte[] buffer) - { - super (name, buffer); - } - - @Override - public String getText () - { - StringBuilder text = new StringBuilder (getHeader ()); - - int ptr = 0x400; - while (ptr < buffer.length) - { - if (buffer[ptr] == 0x00) - { - ++ptr; - continue; - } - if (buffer[ptr] == 0x10) - { - int tab = buffer[ptr + 1] - 0x20; - while (tab-- > 0) - text.append (" "); - ptr += 2; - } - String line = getLine (ptr); - text.append (line + "\n"); - ptr += line.length () + 1; - } - - if (text.length () > 0) - text.deleteCharAt (text.length () - 1); - - return text.toString (); - } - - private String getHeader () - { - return "Name : " + name + "\n\n"; - } - - private String getLine (int ptr) - { - StringBuilder line = new StringBuilder (); - while (buffer[ptr] != 0x0D) - line.append ((char) buffer[ptr++]); - return line.toString (); - } +package com.bytezone.diskbrowser.applefile; + +// -----------------------------------------------------------------------------------// +public class PascalText extends AbstractFile +// -----------------------------------------------------------------------------------// +{ + // ---------------------------------------------------------------------------------// + public PascalText (String name, byte[] buffer) + // ---------------------------------------------------------------------------------// + { + super (name, buffer); + } + + // ---------------------------------------------------------------------------------// + @Override + public String getText () + // ---------------------------------------------------------------------------------// + { + StringBuilder text = new StringBuilder (getHeader ()); + + int ptr = 0x400; + while (ptr < buffer.length) + { + if (buffer[ptr] == 0x00) + { + ++ptr; + continue; + } + if (buffer[ptr] == 0x10) + { + int tab = buffer[ptr + 1] - 0x20; + while (tab-- > 0) + text.append (" "); + ptr += 2; + } + String line = getLine (ptr); + text.append (line + "\n"); + ptr += line.length () + 1; + } + + if (text.length () > 0) + text.deleteCharAt (text.length () - 1); + + return text.toString (); + } + + // ---------------------------------------------------------------------------------// + private String getHeader () + // ---------------------------------------------------------------------------------// + { + return "Name : " + name + "\n\n"; + } + + // ---------------------------------------------------------------------------------// + private String getLine (int ptr) + // ---------------------------------------------------------------------------------// + { + StringBuilder line = new StringBuilder (); + while (buffer[ptr] != 0x0D) + line.append ((char) buffer[ptr++]); + return line.toString (); + } } \ No newline at end of file diff --git a/src/com/bytezone/diskbrowser/applefile/PrintShopGraphic.java b/src/com/bytezone/diskbrowser/applefile/PrintShopGraphic.java index afc366f..7aa0368 100644 --- a/src/com/bytezone/diskbrowser/applefile/PrintShopGraphic.java +++ b/src/com/bytezone/diskbrowser/applefile/PrintShopGraphic.java @@ -5,9 +5,13 @@ import java.awt.Graphics2D; import java.awt.image.BufferedImage; import java.awt.image.DataBuffer; +// -----------------------------------------------------------------------------------// public class PrintShopGraphic extends AbstractFile +// -----------------------------------------------------------------------------------// { + // ---------------------------------------------------------------------------------// public PrintShopGraphic (String name, byte[] buffer) + // ---------------------------------------------------------------------------------// { super (name, buffer); @@ -27,8 +31,10 @@ public class PrintShopGraphic extends AbstractFile g2d.dispose (); } + // ---------------------------------------------------------------------------------// @Override public String getText () + // ---------------------------------------------------------------------------------// { StringBuilder text = new StringBuilder (); diff --git a/src/com/bytezone/diskbrowser/applefile/SegmentDictionary.java b/src/com/bytezone/diskbrowser/applefile/SegmentDictionary.java index d33db77..e611bd8 100644 --- a/src/com/bytezone/diskbrowser/applefile/SegmentDictionary.java +++ b/src/com/bytezone/diskbrowser/applefile/SegmentDictionary.java @@ -1,15 +1,21 @@ package com.bytezone.diskbrowser.applefile; +// -----------------------------------------------------------------------------------// public class SegmentDictionary +// -----------------------------------------------------------------------------------// { private final boolean isValid; + // ---------------------------------------------------------------------------------// public SegmentDictionary (String name, byte[] buffer) + // ---------------------------------------------------------------------------------// { isValid = !name.equals ("SYSTEM.INTERP"); // temporary } + // ---------------------------------------------------------------------------------// public boolean isValid () + // ---------------------------------------------------------------------------------// { return isValid; } diff --git a/src/com/bytezone/diskbrowser/applefile/ShapeTable.java b/src/com/bytezone/diskbrowser/applefile/ShapeTable.java index f7ac7ff..5928632 100755 --- a/src/com/bytezone/diskbrowser/applefile/ShapeTable.java +++ b/src/com/bytezone/diskbrowser/applefile/ShapeTable.java @@ -1,346 +1,356 @@ -package com.bytezone.diskbrowser.applefile; - -import java.awt.AlphaComposite; -import java.awt.Graphics2D; -import java.awt.image.BufferedImage; -import java.awt.image.DataBuffer; -import java.util.ArrayList; -import java.util.List; - -import com.bytezone.diskbrowser.utilities.HexFormatter; - -/*- - * Offset Meaning - * 0 # of shapes - * 1 unused - * 2-3 offset to shape #1 (S1) - * 3-4 offset to shape #2 (S2) - * S1-S1+1 shape definition #1 - * S1+n last byte = 0 - * S2-S2+1 shape definition #1 - * S2+n last byte = 0 - */ - -public class ShapeTable extends AbstractFile -{ - private final List shapes = new ArrayList<> (); - private static final int SIZE = 400; - int maxWidth = 0; - int maxHeight = 0; - - public ShapeTable (String name, byte[] buffer) - { - super (name, buffer); - - int minRow = 200; - int minCol = 200; - int maxRow = 200; - int maxCol = 200; - - int totalShapes = buffer[0] & 0xFF; - for (int i = 0; i < totalShapes; i++) - { - Shape shape = new Shape (buffer, i); - if (!shape.valid) - continue; // shape table should be abandoned - shapes.add (shape); - - minRow = Math.min (minRow, shape.minRow); - minCol = Math.min (minCol, shape.minCol); - maxRow = Math.max (maxRow, shape.maxRow); - maxCol = Math.max (maxCol, shape.maxCol); - } - - maxHeight = maxRow - minRow + 1; - maxWidth = maxCol - minCol + 1; - for (Shape shape : shapes) - shape.convertGrid (minRow, minCol, maxHeight, maxWidth); - - int cols = (int) Math.sqrt (shapes.size ()); - int rows = (shapes.size () - 1) / cols + 1; - - image = new BufferedImage ((cols + 1) * (maxWidth + 5), (rows + 1) * (maxHeight + 5), - BufferedImage.TYPE_BYTE_GRAY); - - int x = 10; - int y = 10; - int count = 0; - Graphics2D g2d = image.createGraphics (); - g2d.setComposite (AlphaComposite.getInstance (AlphaComposite.SRC_OVER, (float) 1.0)); - - for (Shape shape : shapes) - { - g2d.drawImage (shape.image, x, y, null); - x += maxWidth + 5; - if (++count % cols == 0) - { - x = 10; - y += maxHeight + 5; - } - } - g2d.dispose (); - } - - @Override - public String getText () - { - StringBuilder text = new StringBuilder (); - - text.append (String.format ("File Name : %s%n", name)); - text.append (String.format ("File size : %,d%n", buffer.length)); - text.append (String.format ("Total shapes : %d%n", shapes.size ())); - text.append (String.format ("Max dimensions : %d x %d%n%n", maxWidth, maxHeight)); - - for (Shape shape : shapes) - { - shape.drawText (text); - text.append ("\n"); - } - - return text.toString (); - } - - public static boolean isShapeTable (byte[] buffer) - { - if (buffer.length == 0) - return false; - - int totalShapes = buffer[0] & 0xFF; - if (totalShapes == 0) - return false; - - // this flags large files that start with a very small value - // System.out.printf ("Average shape length: %d%n", buffer.length / totalShapes); - if (totalShapes * 500 < buffer.length) - return false; - - for (int i = 0; i < totalShapes; i++) - { - // check index table entry is inside the file - int ptr = i * 2 + 2; - if (ptr >= buffer.length - 1) - return false; - - // check index points inside the file - int offset = HexFormatter.unsignedShort (buffer, ptr); - if (offset == 0 || offset >= buffer.length) - return false; - - // check if previous shape ended with zero - // if (i > 0 && buffer[offset - 1] > 0) - // return false; - } - - return true; - } - - class Shape - { - private final byte[] buffer; - private final int index; - - int offset; - int actualLength; - int minRow, maxRow; - int minCol, maxCol; - int startRow = SIZE / 2; - int startCol = SIZE / 2; - int[][] grid = new int[SIZE][SIZE]; - int[][] displayGrid; - boolean valid; - - private BufferedImage image; - - public Shape (byte[] buffer, int index) - { - this.index = index; - this.buffer = buffer; - - int row = startRow; - int col = startCol; - - offset = HexFormatter.unsignedShort (buffer, index * 2 + 2); - - int ptr = offset; - while (ptr < buffer.length) - { - int value = buffer[ptr++] & 0xFF; - - if (value == 0) - break; - - // P = plot - // DD = direction to move - int v1 = value >>> 6; // DD...... - int v2 = (value & 0x38) >>> 3; // ..PDD... - int v3 = value & 0x07; // .....PDD - - // rightmost 3 bits - if (v3 >= 4) - if (!plot (grid, row, col)) - return; - - if (v3 == 0 || v3 == 4) - row--; - else if (v3 == 1 || v3 == 5) - col++; - else if (v3 == 2 || v3 == 6) - row++; - else - col--; - - // middle 3 bits - if (v2 >= 4) - if (!plot (grid, row, col)) - return; - - // cannot move up without plotting if v1 is zero - if ((v2 == 0 && v1 != 0) || v2 == 4) - row--; - else if (v2 == 1 || v2 == 5) - col++; - else if (v2 == 2 || v2 == 6) - row++; - else if (v2 == 3 || v2 == 7) - col--; - - // leftmost 2 bits (cannot plot or move up) - if (v1 == 1) - col++; - else if (v1 == 2) - row++; - else if (v1 == 3) - col--; - } - - actualLength = ptr - offset; - - // endRow = row; - // endCol = col; - - // find min and max rows with pixels - minRow = startRow; - maxRow = startRow; - // minRow = Math.min (minRow, endRow); - // maxRow = Math.max (maxRow, endRow); - for (row = 1; row < grid.length; row++) - { - if (grid[row][0] > 0) - { - minRow = Math.min (minRow, row); - maxRow = Math.max (maxRow, row); - } - } - - // find min and max columns with pixels - minCol = startCol; - maxCol = startCol; - // minCol = Math.min (minCol, endCol); - // maxCol = Math.max (maxCol, endCol); - for (col = 1; col < grid[0].length; col++) - { - if (grid[0][col] > 0) - { - minCol = Math.min (minCol, col); - maxCol = Math.max (maxCol, col); - } - } - valid = true; - } - - void convertGrid (int offsetRows, int offsetColumns, int rows, int columns) - { - // System.out.printf ("Converting shape # %d%n", index); - // System.out.printf ("offsetRows %d offsetCols %d%n", offsetRows, - // offsetColumns); - // System.out.printf ("rows %d cols %d%n", rows, columns); - - displayGrid = new int[rows][columns]; - for (int row = 0; row < rows; row++) - for (int col = 0; col < columns; col++) - displayGrid[row][col] = grid[offsetRows + row][offsetColumns + col]; - grid = null; - - // draw the image - image = new BufferedImage (columns, rows, BufferedImage.TYPE_BYTE_GRAY); - DataBuffer dataBuffer = image.getRaster ().getDataBuffer (); - int element = 0; - for (int row = 0; row < rows; row++) - for (int col = 0; col < columns; col++) - dataBuffer.setElem (element++, displayGrid[row][col] == 0 ? 0 : 255); - - startRow -= offsetRows; - startCol -= offsetColumns; - // endRow -= offsetRows; - // endCol -= offsetColumns; - } - - private boolean plot (int[][] grid, int row, int col) - { - if (row < 0 || row >= SIZE || col < 0 || col >= SIZE) - { - System.out.printf ("Shape table out of range: %d, %d%n", row, col); - return false; - } - grid[row][col] = 1; // plot - grid[0][col]++; // increment total column dots - grid[row][0]++; // increment total row dots - - return true; - } - - public void drawText (StringBuilder text) - { - text.append (String.format ("Shape : %d%n", index)); - text.append (String.format ("Size : %d%n", actualLength)); - // text.append (String.format ("Width : %d%n", width)); - // text.append (String.format ("Height : %d%n", height)); - - // append the shape's data - String bytes = HexFormatter.getHexString (buffer, offset, actualLength); - int ptr = offset; - for (String s : split (bytes)) - { - text.append (String.format (" %04X : %s%n", ptr, s)); - ptr += 16; - } - text.append ("\n"); - - for (int row = 0; row < displayGrid.length; row++) - { - for (int col = 0; col < displayGrid[0].length; col++) - if (col == startCol && row == startRow) - text.append (displayGrid[row][col] > 0 ? " @" : " ."); - // else if (col == endCol && row == endRow) - // text.append (displayGrid[row][col] > 0 ? " #" : " ."); - else if (displayGrid[row][col] == 0) - text.append (" "); - else - text.append (" X"); - - text.append ("\n"); - } - - text.append ("\n"); - } - - private List split (String line) - { - List list = new ArrayList<> (); - while (line.length () > 48) - { - list.add (line.substring (0, 47)); - line = line.substring (48); - } - list.add (line); - return list; - } - - @Override - public String toString () - { - return String.format ("%3d %3d %3d %3d %3d", index, minRow, maxRow, minCol, - maxCol); - } - } +package com.bytezone.diskbrowser.applefile; + +import java.awt.AlphaComposite; +import java.awt.Graphics2D; +import java.awt.image.BufferedImage; +import java.awt.image.DataBuffer; +import java.util.ArrayList; +import java.util.List; + +import com.bytezone.diskbrowser.utilities.HexFormatter; + +/*- + * Offset Meaning + * 0 # of shapes + * 1 unused + * 2-3 offset to shape #1 (S1) + * 3-4 offset to shape #2 (S2) + * S1-S1+1 shape definition #1 + * S1+n last byte = 0 + * S2-S2+1 shape definition #1 + * S2+n last byte = 0 + */ + +// -----------------------------------------------------------------------------------// +public class ShapeTable extends AbstractFile +// -----------------------------------------------------------------------------------// +{ + private final List shapes = new ArrayList<> (); + private static final int SIZE = 400; + int maxWidth = 0; + int maxHeight = 0; + + // ---------------------------------------------------------------------------------// + public ShapeTable (String name, byte[] buffer) + // ---------------------------------------------------------------------------------// + { + super (name, buffer); + + int minRow = 200; + int minCol = 200; + int maxRow = 200; + int maxCol = 200; + + int totalShapes = buffer[0] & 0xFF; + for (int i = 0; i < totalShapes; i++) + { + Shape shape = new Shape (buffer, i); + if (!shape.valid) + continue; // shape table should be abandoned + shapes.add (shape); + + minRow = Math.min (minRow, shape.minRow); + minCol = Math.min (minCol, shape.minCol); + maxRow = Math.max (maxRow, shape.maxRow); + maxCol = Math.max (maxCol, shape.maxCol); + } + + maxHeight = maxRow - minRow + 1; + maxWidth = maxCol - minCol + 1; + for (Shape shape : shapes) + shape.convertGrid (minRow, minCol, maxHeight, maxWidth); + + int cols = (int) Math.sqrt (shapes.size ()); + int rows = (shapes.size () - 1) / cols + 1; + + image = new BufferedImage ((cols + 1) * (maxWidth + 5), (rows + 1) * (maxHeight + 5), + BufferedImage.TYPE_BYTE_GRAY); + + int x = 10; + int y = 10; + int count = 0; + Graphics2D g2d = image.createGraphics (); + g2d.setComposite (AlphaComposite.getInstance (AlphaComposite.SRC_OVER, (float) 1.0)); + + for (Shape shape : shapes) + { + g2d.drawImage (shape.image, x, y, null); + x += maxWidth + 5; + if (++count % cols == 0) + { + x = 10; + y += maxHeight + 5; + } + } + g2d.dispose (); + } + + // ---------------------------------------------------------------------------------// + @Override + public String getText () + // ---------------------------------------------------------------------------------// + { + StringBuilder text = new StringBuilder (); + + text.append (String.format ("File Name : %s%n", name)); + text.append (String.format ("File size : %,d%n", buffer.length)); + text.append (String.format ("Total shapes : %d%n", shapes.size ())); + text.append (String.format ("Max dimensions : %d x %d%n%n", maxWidth, maxHeight)); + + for (Shape shape : shapes) + { + shape.drawText (text); + text.append ("\n"); + } + + return text.toString (); + } + + // ---------------------------------------------------------------------------------// + public static boolean isShapeTable (byte[] buffer) + // ---------------------------------------------------------------------------------// + { + if (buffer.length == 0) + return false; + + int totalShapes = buffer[0] & 0xFF; + if (totalShapes == 0) + return false; + + // this flags large files that start with a very small value + // System.out.printf ("Average shape length: %d%n", buffer.length / totalShapes); + if (totalShapes * 500 < buffer.length) + return false; + + for (int i = 0; i < totalShapes; i++) + { + // check index table entry is inside the file + int ptr = i * 2 + 2; + if (ptr >= buffer.length - 1) + return false; + + // check index points inside the file + int offset = HexFormatter.unsignedShort (buffer, ptr); + if (offset == 0 || offset >= buffer.length) + return false; + + // check if previous shape ended with zero + // if (i > 0 && buffer[offset - 1] > 0) + // return false; + } + + return true; + } + + // ---------------------------------------------------------------------------------// + class Shape + // ---------------------------------------------------------------------------------// + { + private final byte[] buffer; + private final int index; + + int offset; + int actualLength; + int minRow, maxRow; + int minCol, maxCol; + int startRow = SIZE / 2; + int startCol = SIZE / 2; + int[][] grid = new int[SIZE][SIZE]; + int[][] displayGrid; + boolean valid; + + private BufferedImage image; + + public Shape (byte[] buffer, int index) + { + this.index = index; + this.buffer = buffer; + + int row = startRow; + int col = startCol; + + offset = HexFormatter.unsignedShort (buffer, index * 2 + 2); + + int ptr = offset; + while (ptr < buffer.length) + { + int value = buffer[ptr++] & 0xFF; + + if (value == 0) + break; + + // P = plot + // DD = direction to move + int v1 = value >>> 6; // DD...... + int v2 = (value & 0x38) >>> 3; // ..PDD... + int v3 = value & 0x07; // .....PDD + + // rightmost 3 bits + if (v3 >= 4) + if (!plot (grid, row, col)) + return; + + if (v3 == 0 || v3 == 4) + row--; + else if (v3 == 1 || v3 == 5) + col++; + else if (v3 == 2 || v3 == 6) + row++; + else + col--; + + // middle 3 bits + if (v2 >= 4) + if (!plot (grid, row, col)) + return; + + // cannot move up without plotting if v1 is zero + if ((v2 == 0 && v1 != 0) || v2 == 4) + row--; + else if (v2 == 1 || v2 == 5) + col++; + else if (v2 == 2 || v2 == 6) + row++; + else if (v2 == 3 || v2 == 7) + col--; + + // leftmost 2 bits (cannot plot or move up) + if (v1 == 1) + col++; + else if (v1 == 2) + row++; + else if (v1 == 3) + col--; + } + + actualLength = ptr - offset; + + // endRow = row; + // endCol = col; + + // find min and max rows with pixels + minRow = startRow; + maxRow = startRow; + // minRow = Math.min (minRow, endRow); + // maxRow = Math.max (maxRow, endRow); + for (row = 1; row < grid.length; row++) + { + if (grid[row][0] > 0) + { + minRow = Math.min (minRow, row); + maxRow = Math.max (maxRow, row); + } + } + + // find min and max columns with pixels + minCol = startCol; + maxCol = startCol; + // minCol = Math.min (minCol, endCol); + // maxCol = Math.max (maxCol, endCol); + for (col = 1; col < grid[0].length; col++) + { + if (grid[0][col] > 0) + { + minCol = Math.min (minCol, col); + maxCol = Math.max (maxCol, col); + } + } + valid = true; + } + + void convertGrid (int offsetRows, int offsetColumns, int rows, int columns) + { + // System.out.printf ("Converting shape # %d%n", index); + // System.out.printf ("offsetRows %d offsetCols %d%n", offsetRows, + // offsetColumns); + // System.out.printf ("rows %d cols %d%n", rows, columns); + + displayGrid = new int[rows][columns]; + for (int row = 0; row < rows; row++) + for (int col = 0; col < columns; col++) + displayGrid[row][col] = grid[offsetRows + row][offsetColumns + col]; + grid = null; + + // draw the image + image = new BufferedImage (columns, rows, BufferedImage.TYPE_BYTE_GRAY); + DataBuffer dataBuffer = image.getRaster ().getDataBuffer (); + int element = 0; + for (int row = 0; row < rows; row++) + for (int col = 0; col < columns; col++) + dataBuffer.setElem (element++, displayGrid[row][col] == 0 ? 0 : 255); + + startRow -= offsetRows; + startCol -= offsetColumns; + // endRow -= offsetRows; + // endCol -= offsetColumns; + } + + private boolean plot (int[][] grid, int row, int col) + { + if (row < 0 || row >= SIZE || col < 0 || col >= SIZE) + { + System.out.printf ("Shape table out of range: %d, %d%n", row, col); + return false; + } + grid[row][col] = 1; // plot + grid[0][col]++; // increment total column dots + grid[row][0]++; // increment total row dots + + return true; + } + + public void drawText (StringBuilder text) + { + text.append (String.format ("Shape : %d%n", index)); + text.append (String.format ("Size : %d%n", actualLength)); + // text.append (String.format ("Width : %d%n", width)); + // text.append (String.format ("Height : %d%n", height)); + + // append the shape's data + String bytes = HexFormatter.getHexString (buffer, offset, actualLength); + int ptr = offset; + for (String s : split (bytes)) + { + text.append (String.format (" %04X : %s%n", ptr, s)); + ptr += 16; + } + text.append ("\n"); + + for (int row = 0; row < displayGrid.length; row++) + { + for (int col = 0; col < displayGrid[0].length; col++) + if (col == startCol && row == startRow) + text.append (displayGrid[row][col] > 0 ? " @" : " ."); + // else if (col == endCol && row == endRow) + // text.append (displayGrid[row][col] > 0 ? " #" : " ."); + else if (displayGrid[row][col] == 0) + text.append (" "); + else + text.append (" X"); + + text.append ("\n"); + } + + text.append ("\n"); + } + + private List split (String line) + { + List list = new ArrayList<> (); + while (line.length () > 48) + { + list.add (line.substring (0, 47)); + line = line.substring (48); + } + list.add (line); + return list; + } + + @Override + public String toString () + { + return String.format ("%3d %3d %3d %3d %3d", index, minRow, maxRow, minCol, + maxCol); + } + } } \ No newline at end of file