method header lines

This commit is contained in:
Denis Molony 2020-02-07 21:52:46 +10:00
parent 467f5db110
commit e4ed878f69
10 changed files with 1441 additions and 1343 deletions

View File

@ -1,88 +1,100 @@
package com.bytezone.diskbrowser.applefile; package com.bytezone.diskbrowser.applefile;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import com.bytezone.diskbrowser.utilities.FileFormatException; import com.bytezone.diskbrowser.utilities.FileFormatException;
import com.bytezone.diskbrowser.utilities.HexFormatter; import com.bytezone.diskbrowser.utilities.HexFormatter;
public class PascalCode extends AbstractFile // ---------------------------------------------------------------------------------//
implements PascalConstants, Iterable<PascalSegment> public class PascalCode extends AbstractFile
{ implements PascalConstants, Iterable<PascalSegment>
private final List<PascalSegment> segments = new ArrayList<> (16); // ---------------------------------------------------------------------------------//
private final String comment; {
// private final int blockOffset; private final List<PascalSegment> segments = new ArrayList<> (16);
// private final Relocator relocator; private final String comment;
// private final int blockOffset;
public static void print () // private final Relocator relocator;
{
for (int i = 0; i < 216; i++) // ---------------------------------------------------------------------------------//
System.out.printf ("%3d %d %3s %s%n", i + 128, PascalConstants.mnemonicSize[i], public static void print ()
PascalConstants.mnemonics[i], PascalConstants.descriptions[i]); // ---------------------------------------------------------------------------------//
} {
for (int i = 0; i < 216; i++)
public PascalCode (String name, byte[] buffer, int blockOffset) System.out.printf ("%3d %d %3s %s%n", i + 128, PascalConstants.mnemonicSize[i],
{ PascalConstants.mnemonics[i], PascalConstants.descriptions[i]);
super (name, buffer); }
SegmentDictionary segmentDictionary = new SegmentDictionary (name, buffer); // ---------------------------------------------------------------------------------//
if (!segmentDictionary.isValid ()) public PascalCode (String name, byte[] buffer, int blockOffset)
throw new FileFormatException ("Error in PascalSegment"); // ---------------------------------------------------------------------------------//
// this.blockOffset = blockOffset; {
// this.relocator = relocator; super (name, buffer);
// if (relocator != null)
// relocator.getMultiDiskAddress ("SEG-DIC", blockOffset, 1); SegmentDictionary segmentDictionary = new SegmentDictionary (name, buffer);
if (!segmentDictionary.isValid ())
int nonameCounter = 0; throw new FileFormatException ("Error in PascalSegment");
// this.blockOffset = blockOffset;
// Create segment list (up to 16 segments) // this.relocator = relocator;
for (int i = 0; i < 16; i++) // if (relocator != null)
{ // relocator.getMultiDiskAddress ("SEG-DIC", blockOffset, 1);
String codeName = HexFormatter.getString (buffer, 0x40 + i * 8, 8).trim ();
int size = HexFormatter.intValue (buffer[i * 4 + 2], buffer[i * 4 + 3]); int nonameCounter = 0;
if (codeName.length () == 0 && size > 0)
codeName = "<NULL" + ++nonameCounter + ">"; // Create segment list (up to 16 segments)
if (size > 0) for (int i = 0; i < 16; i++)
{ {
// this could throw an exception String codeName = HexFormatter.getString (buffer, 0x40 + i * 8, 8).trim ();
PascalSegment pascalSegment = int size = HexFormatter.intValue (buffer[i * 4 + 2], buffer[i * 4 + 3]);
new PascalSegment (codeName, buffer, i, blockOffset); if (codeName.length () == 0 && size > 0)
segments.add (pascalSegment); codeName = "<NULL" + ++nonameCounter + ">";
} if (size > 0)
} {
// this could throw an exception
comment = HexFormatter.getPascalString (buffer, 0x1B0); PascalSegment pascalSegment =
} new PascalSegment (codeName, buffer, i, blockOffset);
segments.add (pascalSegment);
@Override }
public String getText () }
{
StringBuilder text = new StringBuilder (getHeader ()); comment = HexFormatter.getPascalString (buffer, 0x1B0);
}
text.append ("Segment Dictionary\n==================\n\n");
// ---------------------------------------------------------------------------------//
text.append ("Slot Addr Blks Byte Name Kind" @Override
+ " Txt Seg Mch Ver I/S I/S Disk:Block\n"); public String getText ()
text.append ("---- ---- ---- ---- -------- ---------------" // ---------------------------------------------------------------------------------//
+ " --- --- --- --- --- --- ---------------------\n"); {
StringBuilder text = new StringBuilder (getHeader ());
for (PascalSegment segment : segments)
text.append (segment.toText () + "\n"); text.append ("Segment Dictionary\n==================\n\n");
text.append ("\nComment : " + comment); text.append ("Slot Addr Blks Byte Name Kind"
+ " Txt Seg Mch Ver I/S I/S Disk:Block\n");
return text.toString (); text.append ("---- ---- ---- ---- -------- ---------------"
} + " --- --- --- --- --- --- ---------------------\n");
private String getHeader () for (PascalSegment segment : segments)
{ text.append (segment.toText () + "\n");
return "Name : " + name + "\n\n";
} text.append ("\nComment : " + comment);
@Override return text.toString ();
public Iterator<PascalSegment> iterator () }
{
return segments.iterator (); // ---------------------------------------------------------------------------------//
} private String getHeader ()
// ---------------------------------------------------------------------------------//
{
return "Name : " + name + "\n\n";
}
// ---------------------------------------------------------------------------------//
@Override
public Iterator<PascalSegment> iterator ()
// ---------------------------------------------------------------------------------//
{
return segments.iterator ();
}
} }

View File

@ -1,352 +1,374 @@
package com.bytezone.diskbrowser.applefile; package com.bytezone.diskbrowser.applefile;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import com.bytezone.diskbrowser.utilities.HexFormatter; import com.bytezone.diskbrowser.utilities.HexFormatter;
public class PascalCodeStatement implements PascalConstants // -----------------------------------------------------------------------------------//
{ public class PascalCodeStatement implements PascalConstants
private static final String[] compValue = // -----------------------------------------------------------------------------------//
{ "invalid", "", "REAL", "", "STR", "", "BOOL", "", "POWR", "", "BYT", "", "WORD" }; {
private static final String[] compValue =
int length; { "invalid", "", "REAL", "", "STR", "", "BOOL", "", "POWR", "", "BYT", "", "WORD" };
int val;
int p1, p2, p3; int length;
String mnemonic; int val;
String extras = ""; int p1, p2, p3;
String description; String mnemonic;
String text; String extras = "";
int ptr; // temp String description;
byte[] buffer; String text;
boolean jumpTarget; int ptr; // temp
List<Jump> jumps = new ArrayList<> (); byte[] buffer;
boolean jumpTarget;
public PascalCodeStatement (byte[] buffer, int ptr, int procPtr) List<Jump> jumps = new ArrayList<> ();
{
this.ptr = ptr; // ---------------------------------------------------------------------------------//
this.buffer = buffer; public PascalCodeStatement (byte[] buffer, int ptr, int procPtr)
length = 1; // ---------------------------------------------------------------------------------//
val = buffer[ptr] & 0xFF; {
if (val <= 127) this.ptr = ptr;
{ this.buffer = buffer;
mnemonic = "SLDC"; length = 1;
extras = "#" + val; val = buffer[ptr] & 0xFF;
description = "Short load constant - push #" + val; if (val <= 127)
} {
else if (val >= 248) mnemonic = "SLDC";
{ extras = "#" + val;
mnemonic = "SIND"; description = "Short load constant - push #" + val;
extras = "#" + (val - 248); }
description = "Short index load - push word *ToS + #" + (val - 248); else if (val >= 248)
} {
else if (val >= 232) mnemonic = "SIND";
{ extras = "#" + (val - 248);
mnemonic = "SLDO"; description = "Short index load - push word *ToS + #" + (val - 248);
extras = "#" + (val - 231); }
description = "Short load global - push BASE + #" + (val - 231); else if (val >= 232)
} {
else if (val >= 216) mnemonic = "SLDO";
{ extras = "#" + (val - 231);
mnemonic = "SLDL"; description = "Short load global - push BASE + #" + (val - 231);
extras = "#" + (val - 215); }
description = "Short load local - push MP + #" + (val - 215); else if (val >= 216)
} {
else mnemonic = "SLDL";
{ extras = "#" + (val - 215);
mnemonic = mnemonics[val - 128]; description = "Short load local - push MP + #" + (val - 215);
description = descriptions[val - 128]; }
else
length = mnemonicSize[val - 128]; {
if (length != 1) mnemonic = mnemonics[val - 128];
{ description = descriptions[val - 128];
switch (val)
{ length = mnemonicSize[val - 128];
// W1, W2, W3, <table> - word aligned case jump if (length != 1)
case 172: //XJP {
int padding = (ptr % 2) == 0 ? 1 : 0; switch (val)
p1 = getWord (buffer, ptr + padding + 1); {
p2 = getWord (buffer, ptr + padding + 3); // W1, W2, W3, <table> - word aligned case jump
p3 = getWord (buffer, ptr + padding + 5); case 172: //XJP
length = (p2 - p1 + 1) * 2 + 7 + padding; int padding = (ptr % 2) == 0 ? 1 : 0;
setParameters (p1, p2, String.format ("%04X", p3)); p1 = getWord (buffer, ptr + padding + 1);
int v = p1; p2 = getWord (buffer, ptr + padding + 3);
int min = ptr + padding + 7; p3 = getWord (buffer, ptr + padding + 5);
int max = min + (p2 - p1) * 2; length = (p2 - p1 + 1) * 2 + 7 + padding;
for (int i = min; i <= max; i += 2) setParameters (p1, p2, String.format ("%04X", p3));
{ int v = p1;
jumps.add (new Jump (i, int min = ptr + padding + 7;
i - HexFormatter.intValue (buffer[i], buffer[i + 1]), v++)); int max = min + (p2 - p1) * 2;
} for (int i = min; i <= max; i += 2)
break; {
jumps.add (new Jump (i,
// UB, <block> - word aligned i - HexFormatter.intValue (buffer[i], buffer[i + 1]), v++));
case 179: //LDC }
p1 = buffer[ptr + 1] & 0xFF; break;
padding = ptr % 2 == 0 ? 0 : 1;
length = p1 * 2 + padding + 2; // UB, <block> - word aligned
setParameters (p1); case 179: //LDC
break; p1 = buffer[ptr + 1] & 0xFF;
padding = ptr % 2 == 0 ? 0 : 1;
// UB, <chars> length = p1 * 2 + padding + 2;
case 166: // LSA setParameters (p1);
case 208: // LPA break;
p1 = buffer[ptr + 1] & 0xFF;
length = p1 + 2; // UB, <chars>
if (val == 166) case 166: // LSA
{ case 208: // LPA
text = HexFormatter.getPascalString (buffer, ptr + 1); p1 = buffer[ptr + 1] & 0xFF;
description += ": " + text; length = p1 + 2;
} if (val == 166)
break; {
text = HexFormatter.getPascalString (buffer, ptr + 1);
// W description += ": " + text;
case 199: // LDCI }
p1 = getWord (buffer, ptr + 1); break;
setParameters (p1);
break; // W
case 199: // LDCI
// B p1 = getWord (buffer, ptr + 1);
case 162: // INC setParameters (p1);
case 163: // IND break;
case 164: // IXA
case 165: // LAO // B
case 168: // MOV case 162: // INC
case 169: // LDO case 163: // IND
case 171: // SRO case 164: // IXA
case 198: // LLA case 165: // LAO
case 202: // LDL case 168: // MOV
case 204: // STL case 169: // LDO
case 213: // BPT case 171: // SRO
length = getLengthOfB (buffer[ptr + 1]) + 1; case 198: // LLA
p1 = getValueOfB (buffer, ptr + 1, length - 1); case 202: // LDL
setParameters (p1); case 204: // STL
break; case 213: // BPT
length = getLengthOfB (buffer[ptr + 1]) + 1;
// DB, B or UB, B p1 = getValueOfB (buffer, ptr + 1, length - 1);
case 157: // LDE setParameters (p1);
case 167: // LAE break;
case 178: // LDA
case 182: // LOD // DB, B or UB, B
case 184: // STR case 157: // LDE
case 209: // STE case 167: // LAE
length = getLengthOfB (buffer[ptr + 2]) + 2; case 178: // LDA
p1 = buffer[ptr + 1] & 0xFF; case 182: // LOD
p2 = getValueOfB (buffer, ptr + 2, length - 2); case 184: // STR
setParameters (p1, p2); case 209: // STE
break; length = getLengthOfB (buffer[ptr + 2]) + 2;
p1 = buffer[ptr + 1] & 0xFF;
// UB1, UB2 p2 = getValueOfB (buffer, ptr + 2, length - 2);
case 192: // IXP setParameters (p1, p2);
case 205: // CXP break;
p1 = buffer[ptr + 1] & 0xFF;
p2 = buffer[ptr + 2] & 0xFF; // UB1, UB2
setParameters (p1, p2); case 192: // IXP
break; case 205: // CXP
p1 = buffer[ptr + 1] & 0xFF;
// SB or DB p2 = buffer[ptr + 2] & 0xFF;
case 161: // FJP setParameters (p1, p2);
case 173: // RNP break;
case 185: // UJP
case 193: // RBP // SB or DB
case 211: // EFJ case 161: // FJP
case 212: // NFJ case 173: // RNP
p1 = buffer[ptr + 1]; case 185: // UJP
if (val == 173 || val == 193) // return from procedure case 193: // RBP
setParameters (p1); case 211: // EFJ
else if (p1 < 0) case 212: // NFJ
{ p1 = buffer[ptr + 1];
// look up jump table entry if (val == 173 || val == 193) // return from procedure
int address = procPtr + p1; setParameters (p1);
int ptr2 = address else if (p1 < 0)
- ((buffer[address + 1] & 0xFF) * 256 + (buffer[address] & 0xFF)); {
extras = String.format ("$%04X", ptr2); // look up jump table entry
jumps.add (new Jump (ptr, ptr2)); int address = procPtr + p1;
} int ptr2 = address
else - ((buffer[address + 1] & 0xFF) * 256 + (buffer[address] & 0xFF));
{ extras = String.format ("$%04X", ptr2);
int address = ptr + length + p1; jumps.add (new Jump (ptr, ptr2));
extras = String.format ("$%04X", address); }
jumps.add (new Jump (ptr, address)); else
} {
break; int address = ptr + length + p1;
extras = String.format ("$%04X", address);
// UB jumps.add (new Jump (ptr, address));
case 160: // AOJ }
case 170: // SAS break;
case 174: // CIP
case 188: // LDM // UB
case 189: // STM case 160: // AOJ
case 194: // CBP case 170: // SAS
case 206: // CLP case 174: // CIP
case 207: // CGP case 188: // LDM
p1 = buffer[ptr + 1] & 0xFF; case 189: // STM
setParameters (p1); case 194: // CBP
break; case 206: // CLP
case 207: // CGP
// CSP p1 = buffer[ptr + 1] & 0xFF;
case 158: setParameters (p1);
p1 = buffer[ptr + 1] & 0xFF; break;
if (p1 < CSP.length)
description = "Call standard procedure - " + CSP[p1]; // CSP
else case 158:
description = "Call standard procedure - index out of bounds"; p1 = buffer[ptr + 1] & 0xFF;
break; if (p1 < CSP.length)
description = "Call standard procedure - " + CSP[p1];
// Non-integer comparisons else
case 175: description = "Call standard procedure - index out of bounds";
case 176: break;
case 177:
case 180: // Non-integer comparisons
case 181: case 175:
case 183: case 176:
p1 = buffer[ptr + 1] & 0xFF; // 2/4/6/8/10/12 case 177:
if (p1 < 0 || p1 >= compValue.length) case 180:
{ case 181:
System.out.printf ("%d %d %d%n", val, p1, ptr); case 183:
mnemonic += "******************************"; p1 = buffer[ptr + 1] & 0xFF; // 2/4/6/8/10/12
break; if (p1 < 0 || p1 >= compValue.length)
} {
mnemonic += compValue[p1]; System.out.printf ("%d %d %d%n", val, p1, ptr);
if (p1 == 10 || p1 == 12) mnemonic += "******************************";
{ break;
length = getLengthOfB (buffer[ptr + 2]) + 2; }
p2 = getValueOfB (buffer, ptr + 2, length - 2); mnemonic += compValue[p1];
setParameters (p2); if (p1 == 10 || p1 == 12)
} {
break; length = getLengthOfB (buffer[ptr + 2]) + 2;
p2 = getValueOfB (buffer, ptr + 2, length - 2);
default: setParameters (p2);
System.out.println ("Forgot : " + val); }
} 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) private int getWord (byte[] buffer, int ptr)
{ // ---------------------------------------------------------------------------------//
return (b & 0x80) == 0x80 ? 2 : 1; {
} return (buffer[ptr + 1] & 0xFF) * 256 + (buffer[ptr] & 0xFF);
}
private int getValueOfB (byte[] buffer, int ptr, int length)
{ // ---------------------------------------------------------------------------------//
if (length == 2) private int getLengthOfB (byte b)
return (buffer[ptr] & 0x7F) * 256 + (buffer[ptr + 1] & 0xFF); // ---------------------------------------------------------------------------------//
return buffer[ptr] & 0xFF; {
} return (b & 0x80) == 0x80 ? 2 : 1;
}
private void setParameters (int p1)
{ // ---------------------------------------------------------------------------------//
description = description.replaceFirst (":1", p1 + ""); private int getValueOfB (byte[] buffer, int ptr, int length)
extras = "#" + p1; // ---------------------------------------------------------------------------------//
} {
if (length == 2)
private void setParameters (int p1, int p2) return (buffer[ptr] & 0x7F) * 256 + (buffer[ptr + 1] & 0xFF);
{ return buffer[ptr] & 0xFF;
setParameters (p1); }
extras += ", #" + p2;
description = description.replaceFirst (":2", p2 + ""); // ---------------------------------------------------------------------------------//
} private void setParameters (int p1)
// ---------------------------------------------------------------------------------//
private void setParameters (int p1, int p2, String p3) {
{ description = description.replaceFirst (":1", p1 + "");
setParameters (p1, p2); extras = "#" + p1;
description = description.replaceFirst (":3", p3); }
}
// ---------------------------------------------------------------------------------//
@Override private void setParameters (int p1, int p2)
public String toString () // ---------------------------------------------------------------------------------//
{ {
String hex = getHex (buffer, ptr, length > 4 ? 4 : length); setParameters (p1);
StringBuilder text = new StringBuilder (); extras += ", #" + p2;
text.append (String.format ("%2s%05X: %-11s %-6s %-10s %s%n", description = description.replaceFirst (":2", p2 + "");
jumpTarget ? "->" : "", ptr, hex, mnemonic, extras, description)); }
if (length > 4)
{ // ---------------------------------------------------------------------------------//
int bytesLeft = length - 4; private void setParameters (int p1, int p2, String p3)
int jmp = 0; // ---------------------------------------------------------------------------------//
int p = ptr + 4; {
while (bytesLeft > 0) setParameters (p1, p2);
{ description = description.replaceFirst (":3", p3);
String line = getHex (buffer, p, (bytesLeft > 4) ? 4 : bytesLeft); }
text.append (" " + line);
if (jumps.size () > 0) // ---------------------------------------------------------------------------------//
{ @Override
if (jmp < jumps.size ()) public String toString ()
text.append (" " + jumps.get (jmp++)); // ---------------------------------------------------------------------------------//
if (jmp < jumps.size ()) {
text.append (" " + jumps.get (jmp++)); String hex = getHex (buffer, ptr, length > 4 ? 4 : length);
} StringBuilder text = new StringBuilder ();
text.append ("\n"); text.append (String.format ("%2s%05X: %-11s %-6s %-10s %s%n",
bytesLeft -= 4; jumpTarget ? "->" : "", ptr, hex, mnemonic, extras, description));
p += 4; if (length > 4)
} {
} int bytesLeft = length - 4;
return text.toString (); int jmp = 0;
} int p = ptr + 4;
while (bytesLeft > 0)
private String getHex (byte[] buffer, int offset, int length) {
{ String line = getHex (buffer, p, (bytesLeft > 4) ? 4 : bytesLeft);
if ((offset + length) >= buffer.length) text.append (" " + line);
{ if (jumps.size () > 0)
System.out.println ("too many"); {
return "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; if (jmp < jumps.size ())
} text.append (" " + jumps.get (jmp++));
StringBuilder text = new StringBuilder (); if (jmp < jumps.size ())
for (int i = 0; i < length; i++) text.append (" " + jumps.get (jmp++));
text.append (String.format ("%02X ", buffer[offset + i])); }
if (text.length () > 0) text.append ("\n");
text.deleteCharAt (text.length () - 1); bytesLeft -= 4;
return text.toString (); p += 4;
} }
}
class Jump return text.toString ();
{ }
int addressFrom;
int addressTo; // ---------------------------------------------------------------------------------//
boolean caseJump; private String getHex (byte[] buffer, int offset, int length)
int caseValue; // ---------------------------------------------------------------------------------//
{
public Jump (int addressFrom, int addressTo) if ((offset + length) >= buffer.length)
{ {
this.addressFrom = addressFrom; System.out.println ("too many");
this.addressTo = addressTo; return "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
} }
StringBuilder text = new StringBuilder ();
public Jump (int addressFrom, int addressTo, int value) for (int i = 0; i < length; i++)
{ text.append (String.format ("%02X ", buffer[offset + i]));
this (addressFrom, addressTo); if (text.length () > 0)
this.caseValue = value; text.deleteCharAt (text.length () - 1);
this.caseJump = true; return text.toString ();
} }
@Override // ---------------------------------------------------------------------------------//
public String toString () class Jump
{ // ---------------------------------------------------------------------------------//
if (caseJump) {
return String.format ("%3d: %04X", caseValue, addressTo); int addressFrom;
return String.format ("%04X", addressTo); int addressTo;
} boolean caseJump;
} int caseValue;
}
public Jump (int addressFrom, int addressTo)
/* from Wizardry info 1.txt {
this.addressFrom = addressFrom;
LDC instruction this.addressTo = addressTo;
--------------- }
Earlier today I noticed the LDC pcode instruction seems to display
differently at times. Then I realized that it is operating with WORD public Jump (int addressFrom, int addressTo, int value)
values and needs to have them aligned on an even BYTE boundary. For {
example: this (addressFrom, addressTo);
this.caseValue = value;
5004 B3 02 8C 3F CD CC this.caseJump = true;
LDC. PUSH 02 WORDS }
5017 B3 02 (02)8C 3F CD CC @Override
LDC. PUSH 02 WORDS 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
*/ */

View File

@ -1,92 +1,95 @@
package com.bytezone.diskbrowser.applefile; package com.bytezone.diskbrowser.applefile;
public interface PascalConstants // -----------------------------------------------------------------------------------//
{ 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", static String[] mnemonics =
"SBR", "SGS", "SQI", "SQR", "STO", "IXS", "UNI", "LDE", "CSP", "LDCN", "ADJ", { "ABI", "ABR", "ADI", "ADR", "LAND", "DIF", "DVI", "DVR", "CHK", "FLO", "FLT",
"FJP", "INC", "IND", "IXA", "LAO", "LSA", "LAE", "MOV", "LDO", "SAS", "SRO", "INN", "INT", "LOR", "MODI", "MPI", "MPR", "NGI", "NGR", "LNOT", "SRS", "SBI",
"XJP", "RNP", "CIP", "EQU", "GEQ", "GRT", "LDA", "LDC", "LEQ", "LES", "LOD", "SBR", "SGS", "SQI", "SQR", "STO", "IXS", "UNI", "LDE", "CSP", "LDCN", "ADJ",
"NEQ", "STR", "UJP", "LDP", "STP", "LDM", "STM", "LDB", "STB", "IXP", "RBP", "FJP", "INC", "IND", "IXA", "LAO", "LSA", "LAE", "MOV", "LDO", "SAS", "SRO",
"CBP", "EQUI", "GEQI", "GRTI", "LLA", "LDCI", "LEQI", "LESI", "LDL", "NEQI", "XJP", "RNP", "CIP", "EQU", "GEQ", "GRT", "LDA", "LDC", "LEQ", "LES", "LOD",
"STL", "CXP", "CLP", "CGP", "LPA", "STE", "???", "EFJ", "NFJ", "BPT", "XIT", "NEQ", "STR", "UJP", "LDP", "STP", "LDM", "STM", "LDB", "STB", "IXP", "RBP",
"NOP" }; "CBP", "EQUI", "GEQI", "GRTI", "LLA", "LDCI", "LEQI", "LESI", "LDL", "NEQI",
"STL", "CXP", "CLP", "CGP", "LPA", "STE", "???", "EFJ", "NFJ", "BPT", "XIT",
static int[] mnemonicSize = "NOP" };
//
// 128 - 155 static int[] mnemonicSize =
// 156 - 183 //
// 184 - 211 // 128 - 155
// 212 - 239 // 156 - 183
// 240 - 255 // 184 - 211
// 212 - 239
{ 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, // 240 - 255
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, { 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, 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, 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,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }; 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,
static String[] descriptions = 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 };
{ "Absolute value of integer - push ABS(ToS)",
"Absolute value of real - push abs((real)ToS)", "Add integers (tos + tos-1)", static String[] descriptions =
"Add reals - push ToS + ToS-1", "Logical AND", { "Absolute value of integer - push ABS(ToS)",
"Set difference - push difference of sets ToS-1 and ToS", "Absolute value of real - push abs((real)ToS)", "Add integers (tos + tos-1)",
"Divide integers - push ToS-1 / ToS", "Divide reals - push ToS-1 / ToS", "Add reals - push ToS + ToS-1", "Logical AND",
"Check subrange bounds - assert ToS-1 <= ToS-2 <= ToS, pop ToS, pop ToS-1", "Set difference - push difference of sets ToS-1 and ToS",
"Float next-to-ToS - push integer ToS-1 after converting to a real", "Divide integers - push ToS-1 / ToS", "Divide reals - push ToS-1 / ToS",
"Float ToS - push integer ToS after converting to a float", "Check subrange bounds - assert ToS-1 <= ToS-2 <= ToS, pop ToS, pop ToS-1",
"Set Membership - if int ToS-1 is in set ToS, push true, else push false", "Float next-to-ToS - push integer ToS-1 after converting to a real",
"Set Intersection - push TOS AND TOS-1", "Logical OR", "Float ToS - push integer ToS after converting to a float",
"Modulo integers - push ToS-1 % ToS", "Multiply TOS by TOS-1", "Set Membership - if int ToS-1 is in set ToS, push true, else push false",
"Multiply reals - push ToS-1 * ToS", "Set Intersection - push TOS AND TOS-1", "Logical OR",
"Negate Integer - push two's complement of ToS", "Modulo integers - push ToS-1 % ToS", "Multiply TOS by TOS-1",
"Negate real - push -((real)ToS)", "Logical Not - push one's complement of ToS", "Multiply reals - push ToS-1 * ToS",
"Build a subrange set", "Subtract Integers push ToS-1 - ToS", "Negate Integer - push two's complement of ToS",
"Subtract reals - push ToS-1 - ToS", "Build a singleton set", "Negate real - push -((real)ToS)", "Logical Not - push one's complement of ToS",
"Square integer - push ToS ^ 2", "Square real - push ToS ^ 2", "Build a subrange set", "Subtract Integers push ToS-1 - ToS",
"Store indirect word - store ToS into word pointed to by ToS-1", "Subtract reals - push ToS-1 - ToS", "Build a singleton set",
"Index string array - push &(*ToS-1 + ToS)", "Square integer - push ToS ^ 2", "Square real - push ToS ^ 2",
"Set union - push union of sets ToS OR ToS-1", "Store indirect word - store ToS into word pointed to by ToS-1",
"Load extended word - push word at segment :1+:2", "Index string array - push &(*ToS-1 + ToS)",
"Call Standard Procedure #:1 - ", "Load Constant NIL", "Adjust set", "Set union - push union of sets ToS OR ToS-1",
"Jump if ToS false", "Increment field ptr - push ToS+:1", "Load extended word - push word at segment :1+:2",
"Static index and load word", "Compute word pointer from ToS-1 + ToS * :1 words", "Call Standard Procedure #:1 - ", "Load Constant NIL", "Adjust set",
"Load Global - push (BASE+:1)", "Load constant string address", "Jump if ToS false", "Increment field ptr - push ToS+:1",
"Load extended address - push address of word at segment :1+:2", "Static index and load word", "Compute word pointer from ToS-1 + ToS * :1 words",
"Move words - transfer :1 words from *ToS to *ToS-1", "Load Global - push (BASE+:1)", "Load constant string address",
"Load Global Word - push BASE+:1", "String Assign", "Store TOS into BASE+:1", "Load extended address - push address of word at segment :1+:2",
"Case Jump - :1::2, Error: :3", "Return from non-base procedure (pass :1 words)", "Move words - transfer :1 words from *ToS to *ToS-1",
"Call intermediate procedure #:1", "ToS-1 == ToS", "ToS-1 >= ToS", "ToS-1 > ToS", "Load Global Word - push BASE+:1", "String Assign", "Store TOS into BASE+:1",
"Load Intermediate Address - push :1th activation record +:2 bytes", "Case Jump - :1::2, Error: :3", "Return from non-base procedure (pass :1 words)",
"Load multi-word constant - :1 words", "ToS-1 <= ToS", "ToS-1 < ToS", "Call intermediate procedure #:1", "ToS-1 == ToS", "ToS-1 >= ToS", "ToS-1 > ToS",
"Load Intermediate Word - push :1th activation record +:2 bytes", "ToS-1 <> ToS", "Load Intermediate Address - push :1th activation record +:2 bytes",
"Store intermediate word - store TOS into :2, traverse :1", "Unconditional jump", "Load multi-word constant - :1 words", "ToS-1 <= ToS", "ToS-1 < ToS",
"Load Packed Field - push *ToS", "Store into packed field", "Load Intermediate Word - push :1th activation record +:2 bytes", "ToS-1 <> ToS",
"Load multiple words - push block of unsigned bytes at *ToS", "Store intermediate word - store TOS into :2, traverse :1", "Unconditional jump",
"Store multiple words - store block of UB at ToS to *ToS-1", "Load Packed Field - push *ToS", "Store into packed field",
"Load Byte - index the byte pointer ToS-1 by integer index ToS and push that byte", "Load multiple words - push block of unsigned bytes at *ToS",
"Store Byte - index the byte pointer ToS-2 by integer index ToS-1 and move ToS to that location", "Store multiple words - store block of UB at ToS to *ToS-1",
"Index packed array - do complicated stuff with :1 and :2", "Load Byte - index the byte pointer ToS-1 by integer index ToS and push that byte",
"Return from base procedure (pass :1 words)", "Store Byte - index the byte pointer ToS-2 by integer "
"Call Base Procedure :1 at lex level -1 or 0", "Compare Integer : ToS-1 = ToS", + "index ToS-1 and move ToS to that location",
"Compare Integer : TOS-1 >= TOS", "Compare Integer : TOS-1 > ToS", "Index packed array - do complicated stuff with :1 and :2",
"Load Local Address - push MP+:1", "Load Word - push #:1", "Return from base procedure (pass :1 words)",
"Compare Integer : TOS-1 <= TOS", "Compare Integer : TOS-1 < ToS", "Call Base Procedure :1 at lex level -1 or 0", "Compare Integer : ToS-1 = ToS",
"Load Local Word - push MP+:1", "Compare Integer : TOS-1 <> TOS", "Compare Integer : TOS-1 >= TOS", "Compare Integer : TOS-1 > ToS",
"Store Local Word - store ToS into MP+:1", "Load Local Address - push MP+:1", "Load Word - push #:1",
"Call external procedure #:2 in segment #:1", "Call local procedure #:1", "Compare Integer : TOS-1 <= TOS", "Compare Integer : TOS-1 < ToS",
"Call global procedure #:1", "Load a packed array - use :1 and :2", "Load Local Word - push MP+:1", "Compare Integer : TOS-1 <> TOS",
"Store extended word - store ToS into word at segment :1+:2", "210 ", "Store Local Word - store ToS into MP+:1",
"Equal false jump - jump :1 if ToS-1 <> ToS", "Call external procedure #:2 in segment #:1", "Call local procedure #:1",
"Not equal false jump - jump :1 if ToS-1 == ToS", "Call global procedure #:1", "Load a packed array - use :1 and :2",
"Breakpoint - not used (does NOP)", "Exit OS - cold boot", "No-op" }; "Store extended word - store ToS into word at segment :1+:2", "210 ",
"Equal false jump - jump :1 if ToS-1 <> ToS",
static String[] CSP = "Not equal false jump - jump :1 if ToS-1 == ToS",
{ "000", "NEW", "MVL", "MVR", "EXIT", "", "", "IDS", "TRS", "TIM", "FLC", "SCN", "", "Breakpoint - not used (does NOP)", "Exit OS - cold boot", "No-op" };
"", "", "", "", "", "", "", "", "021", "TNC", "RND", "", "", "", "", "", "", "",
"MRK", "RLS", "33", "34", "POT", "36", "37", "38", "39", "40" }; static String[] CSP =
{ "000", "NEW", "MVL", "MVR", "EXIT", "", "", "IDS", "TRS", "TIM", "FLC", "SCN", "",
static String[] SegmentKind = { "Linked", "HostSeg", "SegProc", "UnitSeg", "SeprtSeg", "", "", "", "", "", "", "", "", "021", "TNC", "RND", "", "", "", "", "", "", "",
"UnlinkedIntrins", "LinkedIntrins", "DataSeg" }; "MRK", "RLS", "33", "34", "POT", "36", "37", "38", "39", "40" };
static String[] SegmentKind = { "Linked", "HostSeg", "SegProc", "UnitSeg", "SeprtSeg",
"UnlinkedIntrins", "LinkedIntrins", "DataSeg" };
} }

View File

@ -1,15 +1,20 @@
package com.bytezone.diskbrowser.applefile; package com.bytezone.diskbrowser.applefile;
// -----------------------------------------------------------------------------------//
public class PascalInfo extends AbstractFile public class PascalInfo extends AbstractFile
// -----------------------------------------------------------------------------------//
{ {
// ---------------------------------------------------------------------------------//
public PascalInfo (String name, byte[] buffer) public PascalInfo (String name, byte[] buffer)
// ---------------------------------------------------------------------------------//
{ {
super (name, buffer); super (name, buffer);
} }
// ---------------------------------------------------------------------------------//
@Override @Override
public String getText () public String getText ()
// ---------------------------------------------------------------------------------//
{ {
StringBuilder text = new StringBuilder (getHeader ()); StringBuilder text = new StringBuilder (getHeader ());
@ -22,7 +27,9 @@ public class PascalInfo extends AbstractFile
return text.toString (); return text.toString ();
} }
// ---------------------------------------------------------------------------------//
private String getHeader () private String getHeader ()
// ---------------------------------------------------------------------------------//
{ {
return "Name : " + name + "\n\n"; return "Name : " + name + "\n\n";
} }

View File

@ -1,189 +1,199 @@
package com.bytezone.diskbrowser.applefile; package com.bytezone.diskbrowser.applefile;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import com.bytezone.diskbrowser.applefile.PascalCodeStatement.Jump; import com.bytezone.diskbrowser.applefile.PascalCodeStatement.Jump;
import com.bytezone.diskbrowser.utilities.HexFormatter; import com.bytezone.diskbrowser.utilities.HexFormatter;
public class PascalProcedure // -----------------------------------------------------------------------------------//
{ public class PascalProcedure
// all procedures have these fields // -----------------------------------------------------------------------------------//
byte[] buffer; {
int procOffset; // all procedures have these fields
int offset; byte[] buffer;
int slot; int procOffset;
boolean valid; int offset;
int slot;
// only valid procedures have these fields boolean valid;
int procedureNo;
int procLevel; // only valid procedures have these fields
int codeStart; int procedureNo;
int codeEnd; int procLevel;
int parmSize; int codeStart;
int dataSize; int codeEnd;
List<PascalCodeStatement> statements = new ArrayList<> (); int parmSize;
AssemblerProgram assembler; int dataSize;
int jumpTable = -8; List<PascalCodeStatement> statements = new ArrayList<> ();
AssemblerProgram assembler;
public PascalProcedure (byte[] buffer, int slot) int jumpTable = -8;
{
this.buffer = buffer; // ---------------------------------------------------------------------------------//
this.slot = slot; public PascalProcedure (byte[] buffer, int slot)
int p = buffer.length - 2 - slot * 2; // ---------------------------------------------------------------------------------//
offset = HexFormatter.intValue (buffer[p], buffer[p + 1]); {
procOffset = p - offset; this.buffer = buffer;
valid = procOffset > 0; this.slot = slot;
int p = buffer.length - 2 - slot * 2;
if (valid) offset = HexFormatter.intValue (buffer[p], buffer[p + 1]);
{ procOffset = p - offset;
procedureNo = buffer[procOffset] & 0xFF; valid = procOffset > 0;
procLevel = buffer[procOffset + 1] & 0xFF;
codeStart = HexFormatter.intValue (buffer[procOffset - 2], buffer[procOffset - 1]); if (valid)
codeEnd = HexFormatter.intValue (buffer[procOffset - 4], buffer[procOffset - 3]); {
parmSize = HexFormatter.intValue (buffer[procOffset - 6], buffer[procOffset - 5]); procedureNo = buffer[procOffset] & 0xFF;
dataSize = HexFormatter.intValue (buffer[procOffset - 8], buffer[procOffset - 7]); 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]);
private void decode () dataSize = HexFormatter.intValue (buffer[procOffset - 8], buffer[procOffset - 7]);
{ }
if (statements.size () > 0 || assembler != null) }
return;
int ptr = procOffset - codeStart - 2; // ---------------------------------------------------------------------------------//
int max = procOffset + jumpTable; private void decode ()
// ---------------------------------------------------------------------------------//
if (codeEnd == 0) {
{ if (statements.size () > 0 || assembler != null)
int len = codeStart + jumpTable + 2; return;
if (len > 0) int ptr = procOffset - codeStart - 2;
{ int max = procOffset + jumpTable;
byte[] asmBuf = new byte[len];
System.arraycopy (buffer, ptr, asmBuf, 0, len); if (codeEnd == 0)
assembler = new AssemblerProgram ("Proc", asmBuf, ptr); {
} int len = codeStart + jumpTable + 2;
return; if (len > 0)
} {
byte[] asmBuf = new byte[len];
while (ptr < max) System.arraycopy (buffer, ptr, asmBuf, 0, len);
{ assembler = new AssemblerProgram ("Proc", asmBuf, ptr);
// System.out.printf ("ptr:%d, max:%d, buf:%d %n", ptr, max, buffer.length); }
if (ptr >= buffer.length || ptr < 0) return;
{ }
System.out.printf ("Ptr outside buffer: %d %d%n", ptr, buffer.length);
break; while (ptr < max)
} {
PascalCodeStatement cs = new PascalCodeStatement (buffer, ptr, procOffset); // System.out.printf ("ptr:%d, max:%d, buf:%d %n", ptr, max, buffer.length);
if (cs.length <= 0) if (ptr >= buffer.length || ptr < 0)
{ {
System.out.println ("error - length <= 0 : " + cs); System.out.printf ("Ptr outside buffer: %d %d%n", ptr, buffer.length);
break; break;
} }
statements.add (cs); PascalCodeStatement cs = new PascalCodeStatement (buffer, ptr, procOffset);
if (cs.val == 185 || cs.val == 161) if (cs.length <= 0)
if (cs.p1 < jumpTable) {
{ System.out.println ("error - length <= 0 : " + cs);
jumpTable = cs.p1; break;
max = procOffset + jumpTable; }
} statements.add (cs);
ptr += cs.length; if (cs.val == 185 || cs.val == 161)
} if (cs.p1 < jumpTable)
{
// Tidy up left-over bytes at the end jumpTable = cs.p1;
if (statements.size () > 1) max = procOffset + jumpTable;
{ }
PascalCodeStatement lastStatement = statements.get (statements.size () - 1); ptr += cs.length;
PascalCodeStatement secondLastStatement = statements.get (statements.size () - 2); }
if (lastStatement.val == 0 && (secondLastStatement.val == 0xD6
|| secondLastStatement.val == 0xC1 || secondLastStatement.val == 0xAD)) // Tidy up left-over bytes at the end
statements.remove (statements.size () - 1); if (statements.size () > 1)
} {
PascalCodeStatement lastStatement = statements.get (statements.size () - 1);
// Mark statements that are jump targets PascalCodeStatement secondLastStatement = statements.get (statements.size () - 2);
int actualEnd = procOffset - codeEnd - 4; if (lastStatement.val == 0 && (secondLastStatement.val == 0xD6
for (PascalCodeStatement cs : statements) || secondLastStatement.val == 0xC1 || secondLastStatement.val == 0xAD))
{ statements.remove (statements.size () - 1);
if (cs.ptr == actualEnd) }
{
cs.jumpTarget = true; // Mark statements that are jump targets
continue; int actualEnd = procOffset - codeEnd - 4;
} for (PascalCodeStatement cs : statements)
for (Jump cj : cs.jumps) {
for (PascalCodeStatement cs2 : statements) if (cs.ptr == actualEnd)
if (cs2.ptr == cj.addressTo) {
{ cs.jumpTarget = true;
cs2.jumpTarget = true; continue;
break; }
} for (Jump cj : cs.jumps)
} for (PascalCodeStatement cs2 : statements)
} if (cs2.ptr == cj.addressTo)
{
public List<PascalCodeStatement> extractStrings () cs2.jumpTarget = true;
{ break;
decode (); }
List<PascalCodeStatement> strings = new ArrayList<> (); }
for (PascalCodeStatement cs : statements) }
if (cs.val == 166)
strings.add (cs); // ---------------------------------------------------------------------------------//
return strings; public List<PascalCodeStatement> extractStrings ()
} // ---------------------------------------------------------------------------------//
{
@Override decode ();
public String toString () List<PascalCodeStatement> strings = new ArrayList<> ();
{ for (PascalCodeStatement cs : statements)
if (!valid) if (cs.val == 166)
return ""; strings.add (cs);
decode (); return strings;
}
StringBuilder text = new StringBuilder ("\nProcedure Header\n================\n\n");
// ---------------------------------------------------------------------------------//
if (false) @Override
text.append ( public String toString ()
HexFormatter.format (buffer, procOffset + jumpTable, 2 - jumpTable) + "\n\n"); // ---------------------------------------------------------------------------------//
{
text.append ( if (!valid)
String.format ("Level.......%5d %02X%n", procLevel, procLevel & 0xFF)); return "";
text.append (String.format ("Proc no.....%5d %02X%n", procedureNo, procedureNo)); decode ();
text.append (String.format ("Code entry..%5d %04X (%04X - %04X = %04X)%n",
codeStart, codeStart, (procOffset - 2), codeStart, (procOffset - codeStart - 2))); StringBuilder text = new StringBuilder ("\nProcedure Header\n================\n\n");
text.append (String.format ("Code exit...%5d %04X", codeEnd, codeEnd));
if (codeEnd > 0) if (false)
text.append (String.format (" (%04X - %04X = %04X)%n", (procOffset - 4), codeEnd, text.append (
(procOffset - codeEnd - 4))); HexFormatter.format (buffer, procOffset + jumpTable, 2 - jumpTable) + "\n\n");
else
text.append (String.format ("%n")); text.append (
text.append (String.format ("Parm size...%5d %04X%n", parmSize, parmSize)); String.format ("Level.......%5d %02X%n", procLevel, procLevel & 0xFF));
text.append (String.format ("Data size...%5d %04X%n%n", dataSize, dataSize)); text.append (String.format ("Proc no.....%5d %02X%n", procedureNo, procedureNo));
text.append (String.format ("Code entry..%5d %04X (%04X - %04X = %04X)%n",
text.append ("Procedure Code\n==============\n\n"); codeStart, codeStart, (procOffset - 2), codeStart, (procOffset - codeStart - 2)));
text.append (String.format ("Code exit...%5d %04X", codeEnd, codeEnd));
int ptr = procOffset - codeStart - 2; if (codeEnd > 0)
if (false) text.append (String.format (" (%04X - %04X = %04X)%n", (procOffset - 4), codeEnd,
text.append (HexFormatter.format (buffer, ptr, codeStart + jumpTable + 2) + "\n\n"); (procOffset - codeEnd - 4)));
else
if (codeEnd == 0) text.append (String.format ("%n"));
{ text.append (String.format ("Parm size...%5d %04X%n", parmSize, parmSize));
if (assembler != null) text.append (String.format ("Data size...%5d %04X%n%n", dataSize, dataSize));
text.append (assembler.getAssembler () + "\n");
else text.append ("Procedure Code\n==============\n\n");
text.append ("Null assembler in PascalProcedure");
} int ptr = procOffset - codeStart - 2;
else if (false)
{ text.append (HexFormatter.format (buffer, ptr, codeStart + jumpTable + 2) + "\n\n");
for (PascalCodeStatement cs : statements)
text.append (cs); if (codeEnd == 0)
{
if (jumpTable < -8 && false) if (assembler != null)
{ text.append (assembler.getAssembler () + "\n");
text.append ("\nJump table:\n"); else
for (int i = procOffset + jumpTable; i < procOffset - 8; i += 2) text.append ("Null assembler in PascalProcedure");
{ }
ptr = i - ((buffer[i + 1] & 0xFF) * 256 + (buffer[i] & 0xFF)); else
text.append (String.format ("%05X : %02X %02X --> %04X%n", i, buffer[i], {
buffer[i + 1], ptr)); for (PascalCodeStatement cs : statements)
} text.append (cs);
}
} if (jumpTable < -8 && false)
return text.toString (); {
} 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 ();
}
} }

View File

@ -1,229 +1,241 @@
package com.bytezone.diskbrowser.applefile; package com.bytezone.diskbrowser.applefile;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import com.bytezone.diskbrowser.utilities.FileFormatException; import com.bytezone.diskbrowser.utilities.FileFormatException;
import com.bytezone.diskbrowser.utilities.HexFormatter; import com.bytezone.diskbrowser.utilities.HexFormatter;
public class PascalSegment extends AbstractFile implements PascalConstants // -----------------------------------------------------------------------------------//
{ public class PascalSegment extends AbstractFile implements PascalConstants
private final static int BLOCK_SIZE = 512; // -----------------------------------------------------------------------------------//
final int segmentNoHeader; {
private int segmentNoBody; private final static int BLOCK_SIZE = 512;
// private final int blockOffset; final int segmentNoHeader;
// private final Relocator relocator; private int segmentNoBody;
boolean debug = false; // private final int blockOffset;
// private final Relocator relocator;
public int blockNo; boolean debug = false;
// public int newBlockNo;
public final int size; public int blockNo;
// public int newBlockNo;
private final int segKind; public final int size;
private final int textAddress;
private final int machineType; private final int segKind;
private final int version; private final int textAddress;
private final int intrinsSegs1; private final int machineType;
private final int intrinsSegs2; private final int version;
private final int slot; private final int intrinsSegs1;
private int totalProcedures; private final int intrinsSegs2;
private List<PascalProcedure> procedures; private final int slot;
// private List<MultiDiskAddress> addresses; private int totalProcedures;
private List<PascalProcedure> procedures;
public PascalSegment (String name, byte[] fullBuffer, int seq, int blockOffset) // private List<MultiDiskAddress> addresses;
{
super (name, fullBuffer); // sets this.buffer to the full buffer temporarily // ---------------------------------------------------------------------------------//
public PascalSegment (String name, byte[] fullBuffer, int seq, int blockOffset)
this.slot = seq; // ---------------------------------------------------------------------------------//
// this.blockOffset = blockOffset; {
// this.relocator = relocator; super (name, fullBuffer); // sets this.buffer to the full buffer temporarily
this.blockNo = HexFormatter.intValue (fullBuffer[seq * 4], fullBuffer[seq * 4 + 1]); this.slot = seq;
this.size = HexFormatter.intValue (fullBuffer[seq * 4 + 2], fullBuffer[seq * 4 + 3]); // this.blockOffset = blockOffset;
// this.relocator = relocator;
segKind = HexFormatter.intValue (fullBuffer[0xC0 + seq * 2],
fullBuffer[0xC0 + seq * 2 + 1]); this.blockNo = HexFormatter.intValue (fullBuffer[seq * 4], fullBuffer[seq * 4 + 1]);
this.size = HexFormatter.intValue (fullBuffer[seq * 4 + 2], fullBuffer[seq * 4 + 3]);
textAddress = HexFormatter.intValue (fullBuffer[0xE0 + seq * 2],
fullBuffer[0xE0 + seq * 2 + 1]); segKind = HexFormatter.intValue (fullBuffer[0xC0 + seq * 2],
fullBuffer[0xC0 + seq * 2 + 1]);
// segment 1 is the main segment, 2-6 are used by the system, and 7
// onwards is for the program textAddress = HexFormatter.intValue (fullBuffer[0xE0 + seq * 2],
this.segmentNoHeader = fullBuffer[0x100 + seq * 2] & 0xFF; fullBuffer[0xE0 + seq * 2 + 1]);
int flags = fullBuffer[0x101 + seq * 2] & 0xFF;
// segment 1 is the main segment, 2-6 are used by the system, and 7
// 0 unknown, // onwards is for the program
// 1 positive byte sex p-code this.segmentNoHeader = fullBuffer[0x100 + seq * 2] & 0xFF;
// 2 negative byte sex p-code (apple pascal) int flags = fullBuffer[0x101 + seq * 2] & 0xFF;
// 3-9 6502 code (7 = apple 6502)
machineType = flags & 0x0F; // 0 unknown,
// 1 positive byte sex p-code
version = (flags & 0xD0) >> 5; // 2 negative byte sex p-code (apple pascal)
// 3-9 6502 code (7 = apple 6502)
intrinsSegs1 = HexFormatter.intValue (fullBuffer[0x120 + seq * 4], machineType = flags & 0x0F;
fullBuffer[0x120 + seq * 4 + 1]);
intrinsSegs2 = HexFormatter.intValue (fullBuffer[0x120 + seq * 4 + 2], version = (flags & 0xD0) >> 5;
fullBuffer[0x120 + seq * 4 + 3]);
intrinsSegs1 = HexFormatter.intValue (fullBuffer[0x120 + seq * 4],
int offset = blockNo * 512; fullBuffer[0x120 + seq * 4 + 1]);
intrinsSegs2 = HexFormatter.intValue (fullBuffer[0x120 + seq * 4 + 2],
// if (relocator != null) fullBuffer[0x120 + seq * 4 + 3]);
// {
// // if (segmentNoHeader > 1) int offset = blockNo * 512;
// // {
// int sizeInBlocks = (size - 1) / BLOCK_SIZE + 1; // if (relocator != null)
// int targetBlock = blockNo + blockOffset; // {
// addresses = relocator.getMultiDiskAddress (name, targetBlock, sizeInBlocks); // // if (segmentNoHeader > 1)
// if (addresses.size () > 0) // // {
// { // int sizeInBlocks = (size - 1) / BLOCK_SIZE + 1;
// MultiDiskAddress multiDiskAddress = addresses.get (0); // int targetBlock = blockNo + blockOffset;
// if (multiDiskAddress.diskNumber == 1) // addresses = relocator.getMultiDiskAddress (name, targetBlock, sizeInBlocks);
// offset = (multiDiskAddress.physicalBlockNumber - blockOffset) * BLOCK_SIZE; // if (addresses.size () > 0)
// else // {
// offset = -1; // 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) if (offset < 0)
{ {
buffer = new byte[size]; // replaces this.buffer with the segment buffer only buffer = new byte[0];
System.arraycopy (fullBuffer, offset, buffer, 0, size); }
totalProcedures = buffer[size - 1] & 0xFF; else if ((offset + size) < fullBuffer.length)
segmentNoBody = buffer[size - 2] & 0xFF; {
buffer = new byte[size]; // replaces this.buffer with the segment buffer only
if (debug) System.arraycopy (fullBuffer, offset, buffer, 0, size);
if (segmentNoHeader == 0) totalProcedures = buffer[size - 1] & 0xFF;
System.out.printf ("Zero segment header in %s seq %d%n", name, seq); segmentNoBody = buffer[size - 2] & 0xFF;
else if (segmentNoBody != segmentNoHeader)
System.out.println ( if (debug)
"Segment number mismatch : " + segmentNoBody + " / " + segmentNoHeader); if (segmentNoHeader == 0)
} System.out.printf ("Zero segment header in %s seq %d%n", name, seq);
else else if (segmentNoBody != segmentNoHeader)
{ System.out.println (
throw new FileFormatException ("Error in PascalSegment"); "Segment number mismatch : " + segmentNoBody + " / " + segmentNoHeader);
} }
} else
{
// void setMultiDiskAddresses (List<MultiDiskAddress> addresses) throw new FileFormatException ("Error in PascalSegment");
// { }
// this.addresses = addresses; }
// }
// void setMultiDiskAddresses (List<MultiDiskAddress> addresses)
private void buildProcedureList () // {
{ // this.addresses = addresses;
procedures = new ArrayList<> (totalProcedures); // }
for (int i = 1; i <= totalProcedures; i++) // ---------------------------------------------------------------------------------//
procedures.add (new PascalProcedure (buffer, i)); private void buildProcedureList ()
} // ---------------------------------------------------------------------------------//
{
public String toText () procedures = new ArrayList<> (totalProcedures);
{
int sizeInBlocks = (size - 1) / BLOCK_SIZE + 1; for (int i = 1; i <= totalProcedures; i++)
procedures.add (new PascalProcedure (buffer, i));
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, public String toText ()
getMultiDiskAddresses ()); // ---------------------------------------------------------------------------------//
} {
int sizeInBlocks = (size - 1) / BLOCK_SIZE + 1;
@Override
public String getText () return String.format (
{ " %2d %02X %02X %04X %-8s %-15s%3d " + "%02X %d %d %d %d %s",
if (procedures == null) slot, blockNo, sizeInBlocks, size, name, SegmentKind[segKind], textAddress,
buildProcedureList (); segmentNoHeader, machineType, version, intrinsSegs1, intrinsSegs2,
getMultiDiskAddresses ());
StringBuilder text = new StringBuilder (); }
String title = "Segment - " + name;
text.append (title + "\n" // ---------------------------------------------------------------------------------//
+ "===============================".substring (0, title.length ()) + "\n\n"); @Override
String warning = segmentNoBody == segmentNoHeader ? "" public String getText ()
: String.format (" (%02X in routine)", segmentNoBody); // ---------------------------------------------------------------------------------//
text.append (String.format ("Address........ %02X%n", blockNo)); {
// if (addresses != null) if (procedures == null)
text.append (String.format ("Multi disk .... %s%n", getMultiDiskAddresses ())); buildProcedureList ();
text.append (String.format ("Length......... %04X%n", buffer.length));
text.append (String.format ("Machine type... %d%n", machineType)); StringBuilder text = new StringBuilder ();
text.append (String.format ("Version........ %d%n", version)); String title = "Segment - " + name;
text.append (String.format ("Segment........ %02X%s%n", segmentNoHeader, warning)); text.append (title + "\n"
text.append (String.format ("Total procs.... %d%n", procedures.size ())); + "===============================".substring (0, title.length ()) + "\n\n");
String warning = segmentNoBody == segmentNoHeader ? ""
text.append ("\nProcedure Dictionary\n====================\n\n"); : String.format (" (%02X in routine)", segmentNoBody);
text.append (String.format ("Address........ %02X%n", blockNo));
int len = procedures.size () * 2 + 2; // if (addresses != null)
if (false) text.append (String.format ("Multi disk .... %s%n", getMultiDiskAddresses ()));
text.append (HexFormatter.format (buffer, buffer.length - len, len) + "\n\n"); text.append (String.format ("Length......... %04X%n", buffer.length));
text.append (String.format ("Machine type... %d%n", machineType));
text.append ("Proc Offset Lvl Entry Exit Parm Data Proc header\n"); text.append (String.format ("Version........ %d%n", version));
text.append ( text.append (String.format ("Segment........ %02X%s%n", segmentNoHeader, warning));
"---- ------ --- ----- ---- ---- ---- --------------------\n"); text.append (String.format ("Total procs.... %d%n", procedures.size ()));
for (PascalProcedure procedure : procedures)
{ text.append ("\nProcedure Dictionary\n====================\n\n");
if (procedure.valid)
{ int len = procedures.size () * 2 + 2;
int address = size - procedure.slot * 2 - 2; if (false)
text.append (String.format ( text.append (HexFormatter.format (buffer, buffer.length - len, len) + "\n\n");
" %3d %04X %3d %04X %04X %04X %04X (%04X - %04X = %04X)%n",
procedure.procedureNo, procedure.offset, procedure.procLevel, text.append ("Proc Offset Lvl Entry Exit Parm Data Proc header\n");
procedure.codeStart, procedure.codeEnd, procedure.parmSize, text.append (
procedure.dataSize, address, procedure.offset, procedure.procOffset)); "---- ------ --- ----- ---- ---- ---- --------------------\n");
} for (PascalProcedure procedure : procedures)
else {
text.append (String.format (" %3d %04X%n", procedure.slot, procedure.offset)); if (procedure.valid)
} {
int address = size - procedure.slot * 2 - 2;
text.append ("\nStrings\n=======\n"); text.append (String.format (
for (PascalProcedure pp : procedures) " %3d %04X %3d %04X %04X %04X %04X (%04X - %04X = %04X)%n",
{ procedure.procedureNo, procedure.offset, procedure.procLevel,
List<PascalCodeStatement> strings = pp.extractStrings (); procedure.codeStart, procedure.codeEnd, procedure.parmSize,
for (PascalCodeStatement cs : strings) procedure.dataSize, address, procedure.offset, procedure.procOffset));
text.append ( }
String.format (" %2d %04X %s%n", pp.procedureNo, cs.ptr, cs.text)); else
} text.append (String.format (" %3d %04X%n", procedure.slot, procedure.offset));
}
for (PascalProcedure procedure : procedures)
if (procedure.valid) text.append ("\nStrings\n=======\n");
text.append (procedure); for (PascalProcedure pp : procedures)
{
return text.toString (); List<PascalCodeStatement> strings = pp.extractStrings ();
} for (PascalCodeStatement cs : strings)
text.append (
private String getMultiDiskAddresses () String.format (" %2d %04X %s%n", pp.procedureNo, cs.ptr, cs.text));
{ }
String multiDiskAddressText = "";
// int sizeInBlocks = (size - 1) / BLOCK_SIZE + 1; for (PascalProcedure procedure : procedures)
if (procedure.valid)
// if (segmentNoHeader == 1) // main segment text.append (procedure);
// {
// multiDiskAddressText = String.format ("1:%03X", (blockNo + blockOffset)); return text.toString ();
// } }
// else
// if (relocator != null) // ---------------------------------------------------------------------------------//
// { private String getMultiDiskAddresses ()
// int targetBlock = blockNo + blockOffset; // ---------------------------------------------------------------------------------//
// List<MultiDiskAddress> addresses = {
// relocator.getMultiDiskAddress (name, targetBlock, sizeInBlocks); String multiDiskAddressText = "";
// if (addresses.isEmpty ()) // int sizeInBlocks = (size - 1) / BLOCK_SIZE + 1;
// multiDiskAddressText = ".";
// else // if (segmentNoHeader == 1) // main segment
// { // {
// StringBuilder locations = new StringBuilder (); // multiDiskAddressText = String.format ("1:%03X", (blockNo + blockOffset));
// for (MultiDiskAddress multiDiskAddress : addresses) // }
// locations.append (multiDiskAddress.toString () + ", "); // else
// if (locations.length () > 2) // if (relocator != null)
// { // {
// locations.deleteCharAt (locations.length () - 1); // int targetBlock = blockNo + blockOffset;
// locations.deleteCharAt (locations.length () - 1); // List<MultiDiskAddress> addresses =
// } // relocator.getMultiDiskAddress (name, targetBlock, sizeInBlocks);
// multiDiskAddressText = locations.toString (); // if (addresses.isEmpty ())
// } // multiDiskAddressText = ".";
// } // else
return multiDiskAddressText; // {
} // 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;
}
} }

View File

@ -1,53 +1,63 @@
package com.bytezone.diskbrowser.applefile; package com.bytezone.diskbrowser.applefile;
public class PascalText extends AbstractFile // -----------------------------------------------------------------------------------//
{ public class PascalText extends AbstractFile
public PascalText (String name, byte[] buffer) // -----------------------------------------------------------------------------------//
{ {
super (name, buffer); // ---------------------------------------------------------------------------------//
} public PascalText (String name, byte[] buffer)
// ---------------------------------------------------------------------------------//
@Override {
public String getText () super (name, buffer);
{ }
StringBuilder text = new StringBuilder (getHeader ());
// ---------------------------------------------------------------------------------//
int ptr = 0x400; @Override
while (ptr < buffer.length) public String getText ()
{ // ---------------------------------------------------------------------------------//
if (buffer[ptr] == 0x00) {
{ StringBuilder text = new StringBuilder (getHeader ());
++ptr;
continue; int ptr = 0x400;
} while (ptr < buffer.length)
if (buffer[ptr] == 0x10) {
{ if (buffer[ptr] == 0x00)
int tab = buffer[ptr + 1] - 0x20; {
while (tab-- > 0) ++ptr;
text.append (" "); continue;
ptr += 2; }
} if (buffer[ptr] == 0x10)
String line = getLine (ptr); {
text.append (line + "\n"); int tab = buffer[ptr + 1] - 0x20;
ptr += line.length () + 1; while (tab-- > 0)
} text.append (" ");
ptr += 2;
if (text.length () > 0) }
text.deleteCharAt (text.length () - 1); String line = getLine (ptr);
text.append (line + "\n");
return text.toString (); ptr += line.length () + 1;
} }
private String getHeader () if (text.length () > 0)
{ text.deleteCharAt (text.length () - 1);
return "Name : " + name + "\n\n";
} return text.toString ();
}
private String getLine (int ptr)
{ // ---------------------------------------------------------------------------------//
StringBuilder line = new StringBuilder (); private String getHeader ()
while (buffer[ptr] != 0x0D) // ---------------------------------------------------------------------------------//
line.append ((char) buffer[ptr++]); {
return line.toString (); 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 ();
}
} }

View File

@ -5,9 +5,13 @@ import java.awt.Graphics2D;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.awt.image.DataBuffer; import java.awt.image.DataBuffer;
// -----------------------------------------------------------------------------------//
public class PrintShopGraphic extends AbstractFile public class PrintShopGraphic extends AbstractFile
// -----------------------------------------------------------------------------------//
{ {
// ---------------------------------------------------------------------------------//
public PrintShopGraphic (String name, byte[] buffer) public PrintShopGraphic (String name, byte[] buffer)
// ---------------------------------------------------------------------------------//
{ {
super (name, buffer); super (name, buffer);
@ -27,8 +31,10 @@ public class PrintShopGraphic extends AbstractFile
g2d.dispose (); g2d.dispose ();
} }
// ---------------------------------------------------------------------------------//
@Override @Override
public String getText () public String getText ()
// ---------------------------------------------------------------------------------//
{ {
StringBuilder text = new StringBuilder (); StringBuilder text = new StringBuilder ();

View File

@ -1,15 +1,21 @@
package com.bytezone.diskbrowser.applefile; package com.bytezone.diskbrowser.applefile;
// -----------------------------------------------------------------------------------//
public class SegmentDictionary public class SegmentDictionary
// -----------------------------------------------------------------------------------//
{ {
private final boolean isValid; private final boolean isValid;
// ---------------------------------------------------------------------------------//
public SegmentDictionary (String name, byte[] buffer) public SegmentDictionary (String name, byte[] buffer)
// ---------------------------------------------------------------------------------//
{ {
isValid = !name.equals ("SYSTEM.INTERP"); // temporary isValid = !name.equals ("SYSTEM.INTERP"); // temporary
} }
// ---------------------------------------------------------------------------------//
public boolean isValid () public boolean isValid ()
// ---------------------------------------------------------------------------------//
{ {
return isValid; return isValid;
} }

View File

@ -1,346 +1,356 @@
package com.bytezone.diskbrowser.applefile; package com.bytezone.diskbrowser.applefile;
import java.awt.AlphaComposite; import java.awt.AlphaComposite;
import java.awt.Graphics2D; import java.awt.Graphics2D;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.awt.image.DataBuffer; import java.awt.image.DataBuffer;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import com.bytezone.diskbrowser.utilities.HexFormatter; import com.bytezone.diskbrowser.utilities.HexFormatter;
/*- /*-
* Offset Meaning * Offset Meaning
* 0 # of shapes * 0 # of shapes
* 1 unused * 1 unused
* 2-3 offset to shape #1 (S1) * 2-3 offset to shape #1 (S1)
* 3-4 offset to shape #2 (S2) * 3-4 offset to shape #2 (S2)
* S1-S1+1 shape definition #1 * S1-S1+1 shape definition #1
* S1+n last byte = 0 * S1+n last byte = 0
* S2-S2+1 shape definition #1 * S2-S2+1 shape definition #1
* S2+n last byte = 0 * S2+n last byte = 0
*/ */
public class ShapeTable extends AbstractFile // -----------------------------------------------------------------------------------//
{ public class ShapeTable extends AbstractFile
private final List<Shape> shapes = new ArrayList<> (); // -----------------------------------------------------------------------------------//
private static final int SIZE = 400; {
int maxWidth = 0; private final List<Shape> shapes = new ArrayList<> ();
int maxHeight = 0; private static final int SIZE = 400;
int maxWidth = 0;
public ShapeTable (String name, byte[] buffer) int maxHeight = 0;
{
super (name, buffer); // ---------------------------------------------------------------------------------//
public ShapeTable (String name, byte[] buffer)
int minRow = 200; // ---------------------------------------------------------------------------------//
int minCol = 200; {
int maxRow = 200; super (name, buffer);
int maxCol = 200;
int minRow = 200;
int totalShapes = buffer[0] & 0xFF; int minCol = 200;
for (int i = 0; i < totalShapes; i++) int maxRow = 200;
{ int maxCol = 200;
Shape shape = new Shape (buffer, i);
if (!shape.valid) int totalShapes = buffer[0] & 0xFF;
continue; // shape table should be abandoned for (int i = 0; i < totalShapes; i++)
shapes.add (shape); {
Shape shape = new Shape (buffer, i);
minRow = Math.min (minRow, shape.minRow); if (!shape.valid)
minCol = Math.min (minCol, shape.minCol); continue; // shape table should be abandoned
maxRow = Math.max (maxRow, shape.maxRow); shapes.add (shape);
maxCol = Math.max (maxCol, shape.maxCol);
} minRow = Math.min (minRow, shape.minRow);
minCol = Math.min (minCol, shape.minCol);
maxHeight = maxRow - minRow + 1; maxRow = Math.max (maxRow, shape.maxRow);
maxWidth = maxCol - minCol + 1; maxCol = Math.max (maxCol, shape.maxCol);
for (Shape shape : shapes) }
shape.convertGrid (minRow, minCol, maxHeight, maxWidth);
maxHeight = maxRow - minRow + 1;
int cols = (int) Math.sqrt (shapes.size ()); maxWidth = maxCol - minCol + 1;
int rows = (shapes.size () - 1) / cols + 1; for (Shape shape : shapes)
shape.convertGrid (minRow, minCol, maxHeight, maxWidth);
image = new BufferedImage ((cols + 1) * (maxWidth + 5), (rows + 1) * (maxHeight + 5),
BufferedImage.TYPE_BYTE_GRAY); int cols = (int) Math.sqrt (shapes.size ());
int rows = (shapes.size () - 1) / cols + 1;
int x = 10;
int y = 10; image = new BufferedImage ((cols + 1) * (maxWidth + 5), (rows + 1) * (maxHeight + 5),
int count = 0; BufferedImage.TYPE_BYTE_GRAY);
Graphics2D g2d = image.createGraphics ();
g2d.setComposite (AlphaComposite.getInstance (AlphaComposite.SRC_OVER, (float) 1.0)); int x = 10;
int y = 10;
for (Shape shape : shapes) int count = 0;
{ Graphics2D g2d = image.createGraphics ();
g2d.drawImage (shape.image, x, y, null); g2d.setComposite (AlphaComposite.getInstance (AlphaComposite.SRC_OVER, (float) 1.0));
x += maxWidth + 5;
if (++count % cols == 0) for (Shape shape : shapes)
{ {
x = 10; g2d.drawImage (shape.image, x, y, null);
y += maxHeight + 5; x += maxWidth + 5;
} if (++count % cols == 0)
} {
g2d.dispose (); x = 10;
} y += maxHeight + 5;
}
@Override }
public String getText () g2d.dispose ();
{ }
StringBuilder text = new StringBuilder ();
// ---------------------------------------------------------------------------------//
text.append (String.format ("File Name : %s%n", name)); @Override
text.append (String.format ("File size : %,d%n", buffer.length)); public String getText ()
text.append (String.format ("Total shapes : %d%n", shapes.size ())); // ---------------------------------------------------------------------------------//
text.append (String.format ("Max dimensions : %d x %d%n%n", maxWidth, maxHeight)); {
StringBuilder text = new StringBuilder ();
for (Shape shape : shapes)
{ text.append (String.format ("File Name : %s%n", name));
shape.drawText (text); text.append (String.format ("File size : %,d%n", buffer.length));
text.append ("\n"); text.append (String.format ("Total shapes : %d%n", shapes.size ()));
} text.append (String.format ("Max dimensions : %d x %d%n%n", maxWidth, maxHeight));
return text.toString (); for (Shape shape : shapes)
} {
shape.drawText (text);
public static boolean isShapeTable (byte[] buffer) text.append ("\n");
{ }
if (buffer.length == 0)
return false; return text.toString ();
}
int totalShapes = buffer[0] & 0xFF;
if (totalShapes == 0) // ---------------------------------------------------------------------------------//
return false; public static boolean isShapeTable (byte[] buffer)
// ---------------------------------------------------------------------------------//
// this flags large files that start with a very small value {
// System.out.printf ("Average shape length: %d%n", buffer.length / totalShapes); if (buffer.length == 0)
if (totalShapes * 500 < buffer.length) return false;
return false;
int totalShapes = buffer[0] & 0xFF;
for (int i = 0; i < totalShapes; i++) if (totalShapes == 0)
{ return false;
// check index table entry is inside the file
int ptr = i * 2 + 2; // this flags large files that start with a very small value
if (ptr >= buffer.length - 1) // System.out.printf ("Average shape length: %d%n", buffer.length / totalShapes);
return false; if (totalShapes * 500 < buffer.length)
return false;
// check index points inside the file
int offset = HexFormatter.unsignedShort (buffer, ptr); for (int i = 0; i < totalShapes; i++)
if (offset == 0 || offset >= buffer.length) {
return false; // check index table entry is inside the file
int ptr = i * 2 + 2;
// check if previous shape ended with zero if (ptr >= buffer.length - 1)
// if (i > 0 && buffer[offset - 1] > 0) return false;
// return false;
} // check index points inside the file
int offset = HexFormatter.unsignedShort (buffer, ptr);
return true; if (offset == 0 || offset >= buffer.length)
} return false;
class Shape // check if previous shape ended with zero
{ // if (i > 0 && buffer[offset - 1] > 0)
private final byte[] buffer; // return false;
private final int index; }
int offset; return true;
int actualLength; }
int minRow, maxRow;
int minCol, maxCol; // ---------------------------------------------------------------------------------//
int startRow = SIZE / 2; class Shape
int startCol = SIZE / 2; // ---------------------------------------------------------------------------------//
int[][] grid = new int[SIZE][SIZE]; {
int[][] displayGrid; private final byte[] buffer;
boolean valid; private final int index;
private BufferedImage image; int offset;
int actualLength;
public Shape (byte[] buffer, int index) int minRow, maxRow;
{ int minCol, maxCol;
this.index = index; int startRow = SIZE / 2;
this.buffer = buffer; int startCol = SIZE / 2;
int[][] grid = new int[SIZE][SIZE];
int row = startRow; int[][] displayGrid;
int col = startCol; boolean valid;
offset = HexFormatter.unsignedShort (buffer, index * 2 + 2); private BufferedImage image;
int ptr = offset; public Shape (byte[] buffer, int index)
while (ptr < buffer.length) {
{ this.index = index;
int value = buffer[ptr++] & 0xFF; this.buffer = buffer;
if (value == 0) int row = startRow;
break; int col = startCol;
// P = plot offset = HexFormatter.unsignedShort (buffer, index * 2 + 2);
// DD = direction to move
int v1 = value >>> 6; // DD...... int ptr = offset;
int v2 = (value & 0x38) >>> 3; // ..PDD... while (ptr < buffer.length)
int v3 = value & 0x07; // .....PDD {
int value = buffer[ptr++] & 0xFF;
// rightmost 3 bits
if (v3 >= 4) if (value == 0)
if (!plot (grid, row, col)) break;
return;
// P = plot
if (v3 == 0 || v3 == 4) // DD = direction to move
row--; int v1 = value >>> 6; // DD......
else if (v3 == 1 || v3 == 5) int v2 = (value & 0x38) >>> 3; // ..PDD...
col++; int v3 = value & 0x07; // .....PDD
else if (v3 == 2 || v3 == 6)
row++; // rightmost 3 bits
else if (v3 >= 4)
col--; if (!plot (grid, row, col))
return;
// middle 3 bits
if (v2 >= 4) if (v3 == 0 || v3 == 4)
if (!plot (grid, row, col)) row--;
return; else if (v3 == 1 || v3 == 5)
col++;
// cannot move up without plotting if v1 is zero else if (v3 == 2 || v3 == 6)
if ((v2 == 0 && v1 != 0) || v2 == 4) row++;
row--; else
else if (v2 == 1 || v2 == 5) col--;
col++;
else if (v2 == 2 || v2 == 6) // middle 3 bits
row++; if (v2 >= 4)
else if (v2 == 3 || v2 == 7) if (!plot (grid, row, col))
col--; return;
// leftmost 2 bits (cannot plot or move up) // cannot move up without plotting if v1 is zero
if (v1 == 1) if ((v2 == 0 && v1 != 0) || v2 == 4)
col++; row--;
else if (v1 == 2) else if (v2 == 1 || v2 == 5)
row++; col++;
else if (v1 == 3) else if (v2 == 2 || v2 == 6)
col--; row++;
} else if (v2 == 3 || v2 == 7)
col--;
actualLength = ptr - offset;
// leftmost 2 bits (cannot plot or move up)
// endRow = row; if (v1 == 1)
// endCol = col; col++;
else if (v1 == 2)
// find min and max rows with pixels row++;
minRow = startRow; else if (v1 == 3)
maxRow = startRow; col--;
// minRow = Math.min (minRow, endRow); }
// maxRow = Math.max (maxRow, endRow);
for (row = 1; row < grid.length; row++) actualLength = ptr - offset;
{
if (grid[row][0] > 0) // endRow = row;
{ // endCol = col;
minRow = Math.min (minRow, row);
maxRow = Math.max (maxRow, row); // find min and max rows with pixels
} minRow = startRow;
} maxRow = startRow;
// minRow = Math.min (minRow, endRow);
// find min and max columns with pixels // maxRow = Math.max (maxRow, endRow);
minCol = startCol; for (row = 1; row < grid.length; row++)
maxCol = startCol; {
// minCol = Math.min (minCol, endCol); if (grid[row][0] > 0)
// maxCol = Math.max (maxCol, endCol); {
for (col = 1; col < grid[0].length; col++) minRow = Math.min (minRow, row);
{ maxRow = Math.max (maxRow, row);
if (grid[0][col] > 0) }
{ }
minCol = Math.min (minCol, col);
maxCol = Math.max (maxCol, col); // find min and max columns with pixels
} minCol = startCol;
} maxCol = startCol;
valid = true; // minCol = Math.min (minCol, endCol);
} // maxCol = Math.max (maxCol, endCol);
for (col = 1; col < grid[0].length; col++)
void convertGrid (int offsetRows, int offsetColumns, int rows, int columns) {
{ if (grid[0][col] > 0)
// System.out.printf ("Converting shape # %d%n", index); {
// System.out.printf ("offsetRows %d offsetCols %d%n", offsetRows, minCol = Math.min (minCol, col);
// offsetColumns); maxCol = Math.max (maxCol, col);
// System.out.printf ("rows %d cols %d%n", rows, columns); }
}
displayGrid = new int[rows][columns]; valid = true;
for (int row = 0; row < rows; row++) }
for (int col = 0; col < columns; col++)
displayGrid[row][col] = grid[offsetRows + row][offsetColumns + col]; void convertGrid (int offsetRows, int offsetColumns, int rows, int columns)
grid = null; {
// System.out.printf ("Converting shape # %d%n", index);
// draw the image // System.out.printf ("offsetRows %d offsetCols %d%n", offsetRows,
image = new BufferedImage (columns, rows, BufferedImage.TYPE_BYTE_GRAY); // offsetColumns);
DataBuffer dataBuffer = image.getRaster ().getDataBuffer (); // System.out.printf ("rows %d cols %d%n", rows, columns);
int element = 0;
for (int row = 0; row < rows; row++) displayGrid = new int[rows][columns];
for (int col = 0; col < columns; col++) for (int row = 0; row < rows; row++)
dataBuffer.setElem (element++, displayGrid[row][col] == 0 ? 0 : 255); for (int col = 0; col < columns; col++)
displayGrid[row][col] = grid[offsetRows + row][offsetColumns + col];
startRow -= offsetRows; grid = null;
startCol -= offsetColumns;
// endRow -= offsetRows; // draw the image
// endCol -= offsetColumns; image = new BufferedImage (columns, rows, BufferedImage.TYPE_BYTE_GRAY);
} DataBuffer dataBuffer = image.getRaster ().getDataBuffer ();
int element = 0;
private boolean plot (int[][] grid, int row, int col) for (int row = 0; row < rows; row++)
{ for (int col = 0; col < columns; col++)
if (row < 0 || row >= SIZE || col < 0 || col >= SIZE) dataBuffer.setElem (element++, displayGrid[row][col] == 0 ? 0 : 255);
{
System.out.printf ("Shape table out of range: %d, %d%n", row, col); startRow -= offsetRows;
return false; startCol -= offsetColumns;
} // endRow -= offsetRows;
grid[row][col] = 1; // plot // endCol -= offsetColumns;
grid[0][col]++; // increment total column dots }
grid[row][0]++; // increment total row dots
private boolean plot (int[][] grid, int row, int col)
return true; {
} if (row < 0 || row >= SIZE || col < 0 || col >= SIZE)
{
public void drawText (StringBuilder text) System.out.printf ("Shape table out of range: %d, %d%n", row, col);
{ return false;
text.append (String.format ("Shape : %d%n", index)); }
text.append (String.format ("Size : %d%n", actualLength)); grid[row][col] = 1; // plot
// text.append (String.format ("Width : %d%n", width)); grid[0][col]++; // increment total column dots
// text.append (String.format ("Height : %d%n", height)); grid[row][0]++; // increment total row dots
// append the shape's data return true;
String bytes = HexFormatter.getHexString (buffer, offset, actualLength); }
int ptr = offset;
for (String s : split (bytes)) public void drawText (StringBuilder text)
{ {
text.append (String.format (" %04X : %s%n", ptr, s)); text.append (String.format ("Shape : %d%n", index));
ptr += 16; text.append (String.format ("Size : %d%n", actualLength));
} // text.append (String.format ("Width : %d%n", width));
text.append ("\n"); // text.append (String.format ("Height : %d%n", height));
for (int row = 0; row < displayGrid.length; row++) // append the shape's data
{ String bytes = HexFormatter.getHexString (buffer, offset, actualLength);
for (int col = 0; col < displayGrid[0].length; col++) int ptr = offset;
if (col == startCol && row == startRow) for (String s : split (bytes))
text.append (displayGrid[row][col] > 0 ? " @" : " ."); {
// else if (col == endCol && row == endRow) text.append (String.format (" %04X : %s%n", ptr, s));
// text.append (displayGrid[row][col] > 0 ? " #" : " ."); ptr += 16;
else if (displayGrid[row][col] == 0) }
text.append (" "); text.append ("\n");
else
text.append (" X"); for (int row = 0; row < displayGrid.length; row++)
{
text.append ("\n"); for (int col = 0; col < displayGrid[0].length; col++)
} if (col == startCol && row == startRow)
text.append (displayGrid[row][col] > 0 ? " @" : " .");
text.append ("\n"); // else if (col == endCol && row == endRow)
} // text.append (displayGrid[row][col] > 0 ? " #" : " .");
else if (displayGrid[row][col] == 0)
private List<String> split (String line) text.append (" ");
{ else
List<String> list = new ArrayList<> (); text.append (" X");
while (line.length () > 48)
{ text.append ("\n");
list.add (line.substring (0, 47)); }
line = line.substring (48);
} text.append ("\n");
list.add (line); }
return list;
} private List<String> split (String line)
{
@Override List<String> list = new ArrayList<> ();
public String toString () while (line.length () > 48)
{ {
return String.format ("%3d %3d %3d %3d %3d", index, minRow, maxRow, minCol, list.add (line.substring (0, 47));
maxCol); 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);
}
}
} }