mirror of
https://github.com/dmolony/DiskBrowser.git
synced 2025-01-03 13:31:44 +00:00
method header lines
This commit is contained in:
parent
467f5db110
commit
e4ed878f69
@ -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<PascalSegment>
|
||||
{
|
||||
private final List<PascalSegment> 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 = "<NULL" + ++nonameCounter + ">";
|
||||
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<PascalSegment> 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<PascalSegment>
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
private final List<PascalSegment> 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 = "<NULL" + ++nonameCounter + ">";
|
||||
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<PascalSegment> iterator ()
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
return segments.iterator ();
|
||||
}
|
||||
}
|
@ -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<Jump> 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, <table> - 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, <block> - 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, <chars>
|
||||
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<Jump> 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, <table> - 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, <block> - 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, <chars>
|
||||
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
|
||||
*/
|
@ -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" };
|
||||
}
|
@ -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";
|
||||
}
|
||||
|
@ -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<PascalCodeStatement> 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<PascalCodeStatement> extractStrings ()
|
||||
{
|
||||
decode ();
|
||||
List<PascalCodeStatement> 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<PascalCodeStatement> 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<PascalCodeStatement> extractStrings ()
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
decode ();
|
||||
List<PascalCodeStatement> 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 ();
|
||||
}
|
||||
}
|
@ -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<PascalProcedure> procedures;
|
||||
// private List<MultiDiskAddress> 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<MultiDiskAddress> 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<PascalCodeStatement> 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<MultiDiskAddress> 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<PascalProcedure> procedures;
|
||||
// private List<MultiDiskAddress> 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<MultiDiskAddress> 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<PascalCodeStatement> 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<MultiDiskAddress> 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;
|
||||
}
|
||||
}
|
@ -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 ();
|
||||
}
|
||||
}
|
@ -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 ();
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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<Shape> 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<String> split (String line)
|
||||
{
|
||||
List<String> 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<Shape> 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<String> split (String line)
|
||||
{
|
||||
List<String> 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);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user