This commit is contained in:
Denis Molony 2020-01-07 14:23:55 +10:00
parent 01a62e5918
commit d6ea85a062
8 changed files with 258 additions and 106 deletions

View File

@ -11,6 +11,8 @@ import com.bytezone.diskbrowser.applefile.AbstractFile;
import com.bytezone.diskbrowser.disk.DefaultAppleFileSource;
import com.bytezone.diskbrowser.disk.Disk;
import com.bytezone.diskbrowser.disk.DiskAddress;
import com.bytezone.diskbrowser.infocom.Grammar.Sentence;
import com.bytezone.diskbrowser.infocom.Grammar.SentenceGroup;
import com.bytezone.diskbrowser.utilities.HexFormatter;
class CodeManager extends AbstractFile
@ -62,18 +64,43 @@ class CodeManager extends AbstractFile
void addRoutines (int programCounter)
{
addRoutine (programCounter - 1, 0); //
addRoutine (programCounter - 1, -1);
addActionRoutines (); // obtained from Grammar
addCodeRoutines (); // obtained from Object properties
addMissingRoutines (); // requires stringPtr to be set
// checkThreeByteProperties ();
if (false)
{
int routineNo = 0;
int ptr = header.highMemory;
for (Routine routine : routines.values ())
{
if (ptr < routine.startPtr)
{
int extraBytes = routine.startPtr - ptr;
if (extraBytes > 1)
System.out.println ("Orphan bytes\n------------");
if (extraBytes == 1)
System.out.println (String.format ("%05X : %s%n", ptr,
HexFormatter.getHexString (buffer, ptr, extraBytes)));
else
System.out
.println (HexFormatter.format (buffer, ptr, extraBytes, ptr) + "\n");
}
System.out.printf ("Routine #%3d%n", ++routineNo);
System.out.println ("------------");
System.out.println (routine.dump ());
ptr = routine.startPtr + routine.length;
}
}
if (false)
{
int ptr = header.highMemory;
for (int key : routines.keySet ())
{
if (ptr % 2 == 1)
++ptr;
ptr = checkAlignment (ptr);
Routine routine = routines.get (key);
if (routine.startPtr > ptr)
System.out.printf ("skipped %d bytes%n", routine.startPtr - ptr);
@ -83,6 +110,13 @@ class CodeManager extends AbstractFile
}
}
private int checkAlignment (int ptr)
{
if (ptr % 2 == 1) // routine must start on a word boundary
++ptr;
return ptr;
}
private void addMissingRoutines ()
{
System.out.printf ("%nWalking the code block%n%n");
@ -91,8 +125,7 @@ class CodeManager extends AbstractFile
while (ptr < header.stringPointer)
{
if (ptr >= 0 && ptr % 2 == 1) // routine must start on a word boundary
ptr++;
ptr = checkAlignment (ptr);
if (routines.containsKey (ptr))
{
@ -104,9 +137,7 @@ class CodeManager extends AbstractFile
if (routine == null)
{
System.out.printf ("Invalid routine found : %05X%n", ptr);
int nextRoutinePtr = findNextRoutine (ptr + 1);
// System.out.println (Utility.getHex (buffer, ptr, nextRoutinePtr - ptr));
ptr = nextRoutinePtr;
ptr = findNextRoutine (ptr + 1);
System.out.printf ("skipping to %05X%n", ptr);
if (ptr == 0)
break;
@ -114,6 +145,7 @@ class CodeManager extends AbstractFile
else
{
ptr += routine.length;
System.out.printf ("Routine found: %05X%n", routine.startPtr);
}
}
System.out.printf ("%n%d new routines found by walking the code block%n%n",
@ -160,11 +192,17 @@ class CodeManager extends AbstractFile
private void addActionRoutines ()
{
// process actionRoutines and preActionRoutines
List<Integer> routines = header.grammar.getActionRoutines ();
System.out.println ("Adding " + routines.size () + " action routines");
for (Integer address : routines)
addRoutine (address, 0);
int total = routines.size ();
for (SentenceGroup sentenceGroup : header.grammar.getSentenceGroups ())
for (Sentence sentence : sentenceGroup)
{
if (sentence.preActionRoutine > 0)
addRoutine (sentence.preActionRoutine, sentence.startPtr);
addRoutine (sentence.actionRoutine, sentence.startPtr);
}
System.out.printf ("Added %d action routines%n", routines.size () - total);
}
Routine addRoutine (int address, int caller)

View File

@ -15,6 +15,7 @@ class Dictionary extends AbstractFile
private final int totalSeparators;
private final int dictionaryPtr, dictionarySize;
private final int entryLength;
private final String separators;
Map<Integer, List<WordEntry>> synonymList = new TreeMap<> ();
@ -26,6 +27,12 @@ class Dictionary extends AbstractFile
dictionary = new TreeMap<> ();
totalSeparators = buffer[dictionaryPtr] & 0xFF;
StringBuilder sep = new StringBuilder ();
for (int i = 0; i < totalSeparators; i++)
sep.append ((char) (buffer[dictionaryPtr + i + 1] & 0xFF));
separators = sep.toString ();
int ptr = dictionaryPtr + totalSeparators + 1;
entryLength = buffer[ptr++] & 0xFF;
@ -48,13 +55,15 @@ class Dictionary extends AbstractFile
}
wordEntryList.add (wordEntry);
// check for words with the property flag
// check for words with the direction flag
if ((buffer[ptr + 4] & 0x10) != 0)
{
int b1 = buffer[ptr + 5] & 0xFF;
int property = (b1 >= 1 && b1 <= 31) ? b1 : buffer[ptr + 6] & 0xFF;
if (header.getPropertyName (property) == null
|| header.getPropertyName (property).length () > string.value.length ())
int b2 = buffer[ptr + 6] & 0xFF;
int property = b2 == 0 ? b1 : b2;
String propertyName = header.getPropertyName (property);
System.out.printf ("%02X %s%n", property, string.value);
if (propertyName == null || propertyName.length () > string.value.length ())
header.propertyNames[property] = string.value;
}
ptr += entryLength;
@ -67,7 +76,7 @@ class Dictionary extends AbstractFile
header.propertyNames[i] = i + "";
// testing (only works in Zork 1)
if (false)
if (true)
{
if (header.propertyNames[4].equals ("4"))
header.propertyNames[4] = "PSEUDO";
@ -89,24 +98,35 @@ class Dictionary extends AbstractFile
header.propertyNames[16] = "ADJ";
}
// 4 = PSEUDO (property 4)
// 5 = GLOBAL (property 5)
// 6 = VTYPE (property 6)
// 7 = STRENGTH (property 7)
// 04 = PSEUDO (property 4)
// 05 = GLOBAL (property 5)
// 06 = VTYPE (property 6)
// 07 = STRENGTH (property 7)
// STR3 = TEXT (property 8)
// CODE2 = DESCFCN (property 9)
// 10 = CAPACITY (property 10)
// 0A = CAPACITY (property 10)
// STR1 = LDESC (property 11)
// 12 = TVALUE (property 12) value in trophy case
// 13 = VALUE (property 13)
// 0C = TVALUE (property 12) value in trophy case
// 0D = VALUE (property 13)
// STR2 = FDESC (property 14)
// 15 = SIZE (property 15)
// 16 = ADJ (property 16)
// 0F = SIZE (property 15)
// 10 = ADJ (property 16)
// CODE1 = ACTION (property 17)
// 18 = DICT (property 18)
// 19 = LAND (property 19)
// 20 = OUT (property 20)
// 21 = IN (property 21)
// 12 = DICT (property 18)
// 13 land
// 14 out
// 15 in, inside, into
// 16 d, down
// 17 u, up
// 18 sw, southw
// 19 se, southe
// 1A nw, northw
// 1B ne, northe
// 1C s, south
// 1D w, west
// 1E e, east
// 1F n, north
}
public boolean containsWordAt (int address)
@ -172,17 +192,22 @@ class Dictionary extends AbstractFile
{
StringBuilder text = new StringBuilder ();
// text.append (String.format ("Total entries : %,6d%n", totalEntries));
// text.append (String.format ("Separators : %,6d%n", totalSeparators));
// text.append (String.format ("Offset : %,6d %04X%n%n", dictionaryPtr, dictionaryPtr));
text.append (String.format ("Entries : %,6d%n", totalEntries));
text.append (String.format ("Separators : %s%n", separators));
text.append (
String.format ("Offset : %,6d %04X%n%n", dictionaryPtr, dictionaryPtr));
int count = 0;
int ptr = dictionaryPtr + totalSeparators + 4;
for (ZString word : dictionary.values ())
{
text.append (String.format ("%04X %3d %-6s %s", ptr, count++, word.value,
HexFormatter.getHexString (buffer, ptr + 4, entryLength - 4)));
String bits = Integer.toBinaryString (buffer[ptr + 4] & 0xFF);
if (bits.length () < 8)
bits = "00000000".substring (bits.length ()) + bits;
text.append (String.format ("%04X %3d %-6s %s %s", ptr, count++, word.value,
bits, HexFormatter.getHexString (buffer, ptr + 4, entryLength - 4)));
int b1 = buffer[ptr + 4] & 0xFF;
int b2 = buffer[ptr + 5] & 0xFF;
int b3 = buffer[ptr + 6] & 0xFF;
@ -195,19 +220,19 @@ class Dictionary extends AbstractFile
ptr += entryLength;
}
if (true)
if (false)
{
int lastValue = 0;
for (List<WordEntry> list : synonymList.values ())
{
WordEntry wordEntry = list.get (0);
if (wordEntry.value != lastValue)
if (wordEntry.flags != lastValue)
{
lastValue = wordEntry.value;
lastValue = wordEntry.flags;
text.append ("\n");
}
if (wordEntry.value == 0x80) // nouns are all in one entry
if (wordEntry.flags == 0x80) // nouns are all in one entry
{
for (WordEntry we : list)
text.append (we + "\n");
@ -215,8 +240,42 @@ class Dictionary extends AbstractFile
}
else
text.append (wordEntry);
if ((buffer[wordEntry.word.startPtr + 4] & 0x10) != 0)
text.append (" property");
text.append (" direction");
text.append ("\n");
}
}
if (true)
{
text.append ("\n");
int lastValue = 0;
for (int bit = 1; bit < 256; bit *= 2)
{
text.append (String.format ("Bit: %d%n", bit));
for (List<WordEntry> list : synonymList.values ())
{
WordEntry wordEntry = list.get (0);
if ((wordEntry.flags & bit) != 0)
{
if (wordEntry.flags != lastValue)
{
lastValue = wordEntry.flags;
text.append ("\n");
}
if (wordEntry.flags == 0x80) // nouns are all in one entry
{
for (WordEntry we : list)
text.append (we + "\n");
// text.deleteCharAt (text.length () - 1);
}
else
text.append (wordEntry + "\n");
}
}
text.append ("\n");
}
}
@ -229,7 +288,7 @@ class Dictionary extends AbstractFile
{
ZString word;
int seq;
int value;
int flags;
int key;
String bits;
@ -244,7 +303,7 @@ class Dictionary extends AbstractFile
int b3 = buffer[word.startPtr + 6] & 0xFF;
this.key = (b1 << 16) | (b2 << 8) | b3;
this.value = b1;
this.flags = b1;
this.bits = Integer.toBinaryString (b1);
if (bits.length () < 8)
bits = "00000000".substring (bits.length ()) + bits;
@ -253,7 +312,7 @@ class Dictionary extends AbstractFile
@Override
public int compareTo (WordEntry o)
{
return this.value - o.value;
return this.flags - o.flags;
}
@Override

View File

@ -52,8 +52,9 @@ class Grammar extends InfocomAbstractFile
totalPrepositions = header.getWord (prepositionPtr);
prepositionSize = totalPrepositions * 4 + 2;
if (false)
if (true)
{
System.out.println ("\n" + name);
System.out.printf ("indexPtr %,8d %4X%n", indexPtr, indexPtr);
System.out.printf ("indexSize %,8d%n", indexSize);
System.out.printf ("indexEntries %,8d%n", indexEntries);
@ -78,19 +79,22 @@ class Grammar extends InfocomAbstractFile
// System.out.println (getHexDump ());
// create SentenceGroup and Sentence objects and action lists
int count = 255;
int id = 255;
for (int i = 0; i < indexEntries; i++)
{
int offset = header.getWord (indexPtr + i * 2);
SentenceGroup sg = new SentenceGroup (count--, offset);
sentenceGroups.add (sg);
for (Sentence sentence : sg)
SentenceGroup sentenceGroup = new SentenceGroup (id--, offset);
sentenceGroups.add (sentenceGroup);
for (Sentence sentence : sentenceGroup)
{
if (!actionList.containsKey (sentence.actionId)) // add to hashmap
// add to hashmap
if (!actionList.containsKey (sentence.actionId))
actionList.put (sentence.actionId, new ArrayList<Sentence> ());
actionList.get (sentence.actionId).add (sentence);
if (sentence.preActionRoutine > 0 // add to pre-action routine list
// add to pre-action routine list
if (sentence.preActionRoutine > 0
&& !preActionRoutines.contains (sentence.preActionRoutine))
preActionRoutines.add (sentence.preActionRoutine);
@ -138,13 +142,13 @@ class Grammar extends InfocomAbstractFile
return highest + 1; // zero-based, so increment it
}
public List<Integer> getActionRoutines ()
{
List<Integer> routines = new ArrayList<> ();
routines.addAll (actionRoutines);
routines.addAll (preActionRoutines);
return routines;
}
// List<Integer> getActionRoutines ()
// {
// List<Integer> routines = new ArrayList<> ();
// routines.addAll (actionRoutines);
// routines.addAll (preActionRoutines);
// return routines;
// }
@Override
public String getText ()
@ -205,6 +209,11 @@ class Grammar extends InfocomAbstractFile
return text.toString ();
}
List<SentenceGroup> getSentenceGroups ()
{
return sentenceGroups;
}
private List<Sentence> getSentences (int routine)
{
List<Sentence> sentences = new ArrayList<> ();
@ -233,12 +242,12 @@ class Grammar extends InfocomAbstractFile
return text.toString ();
}
private class SentenceGroup implements Iterable<Sentence>
class SentenceGroup implements Iterable<Sentence>
{
int startPtr;
int id;
List<Sentence> sentences = new ArrayList<> ();
String verbString; // list of synonyms inside []
String verbString; // list of synonyms inside []
public SentenceGroup (int id, int ptr)
{
@ -269,7 +278,7 @@ class Grammar extends InfocomAbstractFile
}
}
private class Sentence
class Sentence
{
int startPtr;
SentenceGroup parent;
@ -302,7 +311,7 @@ class Grammar extends InfocomAbstractFile
// get action pointer from byte 7
actionId = buffer[startPtr + 7] & 0xFF;
int targetOffset = actionId * 2; // index into the action and pre-action blocks
int targetOffset = actionId * 2; // index into the action and pre-action blocks
actionRoutine = header.getWord (actionPtr + targetOffset) * 2;
preActionRoutine = header.getWord (preActionPtr + targetOffset) * 2;
}
@ -324,9 +333,11 @@ class Grammar extends InfocomAbstractFile
{
StringBuilder text =
new StringBuilder (String.format ("%3d %04X ", parent.id, startPtr));
text.append (HexFormatter.getHexString (buffer, startPtr, SENTENCE_LENGTH));
String r1 = preActionRoutine == 0 ? "" : String.format ("R:%05X", preActionRoutine);
text.append (String.format (" %-7s R:%05X %s", r1, actionRoutine, getText ()));
return text.toString ();
}
}

View File

@ -8,7 +8,7 @@ import com.bytezone.diskbrowser.utilities.HexFormatter;
class Instruction
{
final Opcode opcode;
private int startPtr;
final int startPtr;
private byte[] buffer;
// List<ZString> abbreviations;
private Header header;
@ -101,11 +101,16 @@ class Instruction
int target ()
{
return isBranch () ? opcode.branch.target : 0;
return isBranch () ? opcode.branch.target : isJump () ? opcode.jumpTarget : 0;
}
@Override
public String toString ()
String dump ()
{
return String.format ("%05X : %s", startPtr,
HexFormatter.getHexString (buffer, startPtr, opcode.length ()));
}
String getHex ()
{
int max = opcode.length ();
String extra = "";
@ -116,8 +121,13 @@ class Instruction
}
String hex = HexFormatter.getHexString (buffer, startPtr, max);
return String.format ("%05X : %-26s%2s", startPtr, hex, extra);
}
return String.format ("%05X : %-26s%2s %s", startPtr, hex, extra, opcode.toString ());
@Override
public String toString ()
{
return opcode.toString ();
}
abstract class Opcode
@ -370,6 +380,7 @@ class Instruction
if (opcodeNumber == 0 || opcodeNumber == 7)
setStore (buffer);
if (opcodeNumber == 0)
{
isCall = true;
@ -403,7 +414,7 @@ class Instruction
@Override
public String toString ()
{
return String.format ("#%05d", value);
return String.format ("#%04X", value);
}
}
@ -419,7 +430,7 @@ class Instruction
@Override
public String toString ()
{
return String.format ("#%03d", value);
return String.format ("#%02X", value);
}
}
@ -430,9 +441,9 @@ class Instruction
this.value = value & 0xFF;
length = 1;
if (value == 0)
if (this.value == 0)
operandType = OperandType.VAR_SP;
else if (value <= 15)
else if (this.value <= 15)
operandType = OperandType.VAR_LOCAL;
else
operandType = OperandType.VAR_GLOBAL;
@ -442,10 +453,10 @@ class Instruction
public String toString ()
{
if (operandType == OperandType.VAR_SP)
return ("SP");
return ("(SP)");
if (operandType == OperandType.VAR_LOCAL)
return (String.format ("L%02d", value));
return String.format ("G%03d", (value - 15));
return (String.format ("L%02X", value));
return String.format ("G%02X", (value - 16));
}
}
@ -469,15 +480,7 @@ class Instruction
ArgumentBranch (int value, int offset)
{
branchOnTrue = (value & 0x8000) != 0;
int val = ((value & 0x3FFF) << 18) >> 18; // signed 14-bit number
// int val = value & 0x3FFF; // signed
// if (val >= 0x2000)
// {
// System.out.printf ("%04X -> %d%n", val, (val - 0x4000));
// val -= 0x4000;
// }
// else
// System.out.printf ("%04X%n", val);
int val = ((value & 0x3FFF) << 18) >> 18; // signed 14-bit number
target = val + offset;
length = 2;

View File

@ -28,7 +28,7 @@ class ObjectAnalyser
createPropertyLinks ();
// assumes that all properties with exactly three bytes are routine addresses
// checkThreeByteProperties ();
checkThreeByteProperties ();
}
public void setStringPointer ()
@ -85,22 +85,25 @@ class ObjectAnalyser
System.out.println ("Routines found : " + totRoutines);
}
// private void checkThreeByteProperties ()
// {
// for (ZObject object : parent.getObjects ())
// {
// for (Property property : object.properties)
// {
// if (header.getPropertyName (property.propertyNumber).charAt (0) < 'a'
// && property.length == 3)
// {
// int address = header.getWord (property.ptr + 1) * 2;
// System.out.println ("checking " + address);
// header.codeManager.addRoutine (address, 0);
// }
// }
// }
// }
private void checkThreeByteProperties ()
{
System.out.printf ("Checking %d objects%n", parent.getObjects ().size ());
for (ZObject object : parent.getObjects ())
{
for (Property property : object.properties)
{
if (property.length == 3)
{
int address = header.getWord (property.ptr + 1) * 2;
if (address > header.highMemory && address < header.stringPointer)
{
System.out.printf ("checking %05X%n", address);
header.codeManager.addRoutine (address, 0);
}
}
}
}
}
// find the property with only dictionary entries
public void setDictionary ()

View File

@ -12,7 +12,7 @@ import com.bytezone.diskbrowser.disk.FormattedDisk;
class ObjectManager extends InfocomAbstractFile implements Iterable<ZObject>
{
private final Header header;
// private final Header header;
private final List<ZObject> list;
private List<ZObject> sortedList;
private final int defaultsPtr, defaultsSize;
@ -23,10 +23,10 @@ class ObjectManager extends InfocomAbstractFile implements Iterable<ZObject>
public ObjectManager (Header header)
{
super ("Objects", header.buffer);
this.header = header;
// this.header = header;
defaultsPtr = header.objectTableOffset;
defaultsSize = 62;
defaultsSize = 62; // 31 words
tablePtr = header.objectTableOffset + 62;
propertyPtr = header.getWord (tablePtr + 7);
propertySize = header.globalsOffset - propertyPtr;

View File

@ -6,6 +6,7 @@ import java.util.List;
import com.bytezone.diskbrowser.infocom.Instruction.Operand;
import com.bytezone.diskbrowser.infocom.Instruction.OperandType;
import com.bytezone.diskbrowser.utilities.HexFormatter;
class Routine extends InfocomAbstractFile
implements Iterable<Instruction>, Comparable<Routine>
@ -17,6 +18,7 @@ class Routine extends InfocomAbstractFile
List<Integer> calls = new ArrayList<Integer> ();
List<Integer> calledBy = new ArrayList<Integer> ();
List<Integer> actions = new ArrayList<Integer> (); // not used yet
List<Integer> targets = new ArrayList<Integer> ();
public Routine (int ptr, Header header, int caller)
{
@ -30,7 +32,9 @@ class Routine extends InfocomAbstractFile
}
startPtr = ptr++; // also used to flag a valid routine
calledBy.add (caller);
if (!calledBy.contains (caller))
calledBy.add (caller);
for (int i = 1; i <= locals; i++)
{
@ -55,6 +59,12 @@ class Routine extends InfocomAbstractFile
if (instruction.isPrint ())
strings++;
if (instruction.isBranch () && !targets.contains (instruction.target ()))
targets.add (instruction.target ());
if (instruction.isJump () && !targets.contains (instruction.target ()))
targets.add (instruction.target ());
for (Operand operand : instruction.opcode.operands)
if (operand.operandType == OperandType.VAR_GLOBAL)
header.globals.addRoutine (this, operand);
@ -94,6 +104,20 @@ class Routine extends InfocomAbstractFile
}
}
String dump ()
{
StringBuilder text = new StringBuilder ();
text.append (String.format ("%05X : %s", startPtr,
HexFormatter.getHexString (buffer, startPtr, 1 + locals * 2)));
text.append ("\n");
for (Instruction instruction : instructions)
{
text.append (instruction.dump ());
text.append ("\n");
}
return text.toString ();
}
boolean isValid ()
{
return startPtr > 0;
@ -135,7 +159,15 @@ class Routine extends InfocomAbstractFile
text.append ("\n");
for (Instruction instruction : instructions)
{
text.append (instruction.getHex ());
int offset = instruction.startPtr;
if (targets.contains (offset))
text.append (" L000 ");
else
text.append (" ");
text.append (instruction + "\n");
}
if (calledBy.size () > 0)
{
@ -151,6 +183,13 @@ class Routine extends InfocomAbstractFile
text.append (String.format ("%05X%n", i));
}
if (targets.size () > 0)
{
text.append ("\n\nTargets\n\n");
for (int i : targets)
text.append (String.format ("%05X%n", i));
}
return text.toString ();
}

View File

@ -30,7 +30,7 @@ class ZObject extends AbstractFile implements Comparable<ZObject>
this.startPtr = offset;
this.id = id;
// attributes
// 32 attributes
int bitIndex = 0;
for (int i = 0; i < 4; i++)
{
@ -195,7 +195,6 @@ class ZObject extends AbstractFile implements Comparable<ZObject>
appendRoutine (text, address);
break;
case 4:
// text.append (getObject ().name + " : ");
text.append (String.format ("%s : IF G%02X ELSE ", getObject ().name,
header.getByte (ptr + 2)));
address = header.getWord (ptr + 3) * 2;