dmolony-DiskBrowser/src/com/bytezone/diskbrowser/infocom/Routine.java

251 lines
7.7 KiB
Java
Raw Permalink Normal View History

2015-06-01 09:35:51 +00:00
package com.bytezone.diskbrowser.infocom;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
2019-04-22 04:35:50 +00:00
import com.bytezone.diskbrowser.infocom.Instruction.Operand;
import com.bytezone.diskbrowser.infocom.Instruction.OperandType;
2020-01-07 04:23:55 +00:00
import com.bytezone.diskbrowser.utilities.HexFormatter;
2019-04-22 04:35:50 +00:00
2020-02-09 13:02:48 +00:00
// -----------------------------------------------------------------------------------//
2016-02-05 07:18:23 +00:00
class Routine extends InfocomAbstractFile
implements Iterable<Instruction>, Comparable<Routine>
2020-02-09 13:02:48 +00:00
// -----------------------------------------------------------------------------------//
2015-06-01 09:35:51 +00:00
{
2016-02-05 07:18:23 +00:00
int startPtr, length, strings, locals;
2020-02-02 10:17:49 +00:00
List<Parameter> parameters = new ArrayList<> ();
List<Instruction> instructions = new ArrayList<> ();
List<Integer> calls = new ArrayList<> ();
List<Integer> calledBy = new ArrayList<> ();
List<Integer> actions = new ArrayList<> (); // not used yet
List<Integer> targets = new ArrayList<> ();
2016-02-05 07:18:23 +00:00
2020-02-09 13:02:48 +00:00
// ---------------------------------------------------------------------------------//
Routine (int ptr, Header header, int caller)
// ---------------------------------------------------------------------------------//
2016-02-05 07:18:23 +00:00
{
super (String.format ("Routine %05X", ptr), header.buffer);
locals = buffer[ptr] & 0xFF;
if (locals > 15)
2019-04-22 04:35:50 +00:00
{
System.out.println ("Too many locals: " + locals);
2019-12-29 02:52:06 +00:00
return; // startPtr will be zero
2019-04-22 04:35:50 +00:00
}
2016-02-05 07:18:23 +00:00
2016-07-19 10:18:27 +00:00
startPtr = ptr++; // also used to flag a valid routine
2020-01-07 04:23:55 +00:00
if (!calledBy.contains (caller))
calledBy.add (caller);
2016-02-05 07:18:23 +00:00
for (int i = 1; i <= locals; i++)
{
2016-07-19 10:18:27 +00:00
parameters.add (new Parameter (i, header.getWord (ptr))); // default values
2016-02-05 07:18:23 +00:00
ptr += 2;
}
while (true)
{
if (buffer[ptr] == 0 || buffer[ptr] == 0x20 || buffer[ptr] == 0x40)
{
System.out.println ("Bad instruction found : " + ptr);
return;
}
2016-07-19 10:18:27 +00:00
2016-02-05 07:18:23 +00:00
Instruction instruction = new Instruction (buffer, ptr, header);
instructions.add (instruction);
if (instruction.isCall () && instruction.opcode.callTarget > 0) // not stack-based
calls.add (instruction.opcode.callTarget);
if (instruction.isPrint ())
strings++;
2020-01-07 04:23:55 +00:00
if (instruction.isBranch () && !targets.contains (instruction.target ()))
targets.add (instruction.target ());
if (instruction.isJump () && !targets.contains (instruction.target ()))
targets.add (instruction.target ());
2019-04-22 04:35:50 +00:00
for (Operand operand : instruction.opcode.operands)
if (operand.operandType == OperandType.VAR_GLOBAL)
header.globals.addRoutine (this, operand);
2019-12-29 02:52:06 +00:00
ptr += instruction.length (); // point to next instruction
if (isTarget (ptr))
continue;
2016-02-05 07:18:23 +00:00
// is it a backwards jump?
2019-12-29 02:52:06 +00:00
if (instruction.isJump () && instruction.target () < ptr)
2016-02-05 07:18:23 +00:00
break;
// is it an unconditional return?
2019-12-29 02:52:06 +00:00
if (instruction.isReturn ())
2016-02-05 07:18:23 +00:00
break;
}
2019-12-29 02:52:06 +00:00
2016-02-05 07:18:23 +00:00
length = ptr - startPtr;
hexBlocks.add (new HexBlock (startPtr, length, null));
// check for branches outside this routine
if (true)
{
int endPtr = startPtr + length;
2019-12-29 02:52:06 +00:00
for (Instruction instruction : instructions)
2016-02-05 07:18:23 +00:00
{
2019-12-29 02:52:06 +00:00
int target = instruction.target () > 256 ? instruction.target ()
: instruction.opcode.jumpTarget > 256 ? instruction.opcode.jumpTarget : 0;
2016-02-05 07:18:23 +00:00
if (target == 0)
continue;
2019-12-29 02:52:06 +00:00
if (instruction.isBranch () && (target > endPtr || target < startPtr))
System.out.println (instruction);
if (instruction.isJump () && (target > endPtr || target < startPtr))
System.out.println (instruction);
2016-02-05 07:18:23 +00:00
}
}
}
2020-02-09 13:02:48 +00:00
// ---------------------------------------------------------------------------------//
2020-01-07 04:23:55 +00:00
String dump ()
2020-02-09 13:02:48 +00:00
// ---------------------------------------------------------------------------------//
2020-01-07 04:23:55 +00:00
{
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 ();
}
2020-02-09 13:02:48 +00:00
// ---------------------------------------------------------------------------------//
2019-12-29 02:52:06 +00:00
boolean isValid ()
2020-02-09 13:02:48 +00:00
// ---------------------------------------------------------------------------------//
2019-12-29 02:52:06 +00:00
{
return startPtr > 0;
}
2016-02-05 07:18:23 +00:00
// test whether the routine contains any instructions pointing to this address
2020-02-09 13:02:48 +00:00
// ---------------------------------------------------------------------------------//
2019-12-29 02:52:06 +00:00
private boolean isTarget (int ptr)
2020-02-09 13:02:48 +00:00
// ---------------------------------------------------------------------------------//
2016-02-05 07:18:23 +00:00
{
for (Instruction ins : instructions)
{
if (ins.isBranch () && ins.target () == ptr)
return true;
// should this be calling ins.target () ?
if (ins.isJump () && ins.opcode.jumpTarget == ptr)
return true;
}
return false;
}
2020-02-09 13:02:48 +00:00
// ---------------------------------------------------------------------------------//
2016-02-05 07:18:23 +00:00
public void addCaller (int caller)
2020-02-09 13:02:48 +00:00
// ---------------------------------------------------------------------------------//
2016-02-05 07:18:23 +00:00
{
calledBy.add (caller);
}
2020-02-09 13:02:48 +00:00
// ---------------------------------------------------------------------------------//
2016-02-05 07:18:23 +00:00
@Override
public String getText ()
2020-02-09 13:02:48 +00:00
// ---------------------------------------------------------------------------------//
2016-02-05 07:18:23 +00:00
{
StringBuilder text = new StringBuilder ();
2019-12-29 02:52:06 +00:00
2016-02-05 07:18:23 +00:00
text.append (String.format ("Called by : %3d%n", calledBy.size ()));
2019-12-29 02:52:06 +00:00
text.append (String.format ("Calls : %3d%n", calls.size ()));
text.append (String.format ("Length : %3d%n%n", length));
text.append (String.format ("%05X : %d%n", startPtr, locals));
2016-02-05 07:18:23 +00:00
for (Parameter parameter : parameters)
2019-12-29 02:52:06 +00:00
text.append (parameter.toString () + "\n");
2016-02-05 07:18:23 +00:00
text.append ("\n");
2019-12-29 02:52:06 +00:00
2016-02-05 07:18:23 +00:00
for (Instruction instruction : instructions)
2020-01-07 04:23:55 +00:00
{
text.append (instruction.getHex ());
int offset = instruction.startPtr;
if (targets.contains (offset))
text.append (" L000 ");
else
text.append (" ");
2016-02-05 07:18:23 +00:00
text.append (instruction + "\n");
2020-01-07 04:23:55 +00:00
}
2019-12-29 02:52:06 +00:00
if (calledBy.size () > 0)
{
text.append ("\n\nCalled by\n\n");
for (int i : calledBy)
text.append (String.format ("%05X%n", i));
}
if (calls.size () > 0)
{
text.append ("\n\nCalls\n\n");
for (int i : calls)
text.append (String.format ("%05X%n", i));
}
2020-01-07 04:23:55 +00:00
if (targets.size () > 0)
{
text.append ("\n\nTargets\n\n");
for (int i : targets)
text.append (String.format ("%05X%n", i));
}
2016-02-05 07:18:23 +00:00
return text.toString ();
}
2020-02-09 13:02:48 +00:00
// ---------------------------------------------------------------------------------//
2019-12-29 02:52:06 +00:00
@Override
public String toString ()
2020-02-09 13:02:48 +00:00
// ---------------------------------------------------------------------------------//
2019-12-29 02:52:06 +00:00
{
return String.format ("[Start: %05X, Len: %4d, Strings: %2d, Locals: %2d]", startPtr,
length, strings, locals);
}
2020-02-09 13:02:48 +00:00
// ---------------------------------------------------------------------------------//
2016-02-05 07:18:23 +00:00
class Parameter
2020-02-09 13:02:48 +00:00
// ---------------------------------------------------------------------------------//
2016-02-05 07:18:23 +00:00
{
int value;
int sequence;
public Parameter (int sequence, int value)
{
this.value = value;
this.sequence = sequence;
}
@Override
public String toString ()
{
return String.format ("%05X : L%02d : %d", (startPtr + (sequence - 1) * 2 + 1),
2018-04-25 20:41:03 +00:00
sequence, value);
2016-02-05 07:18:23 +00:00
}
}
@Override
public Iterator<Instruction> iterator ()
{
return instructions.iterator ();
}
@Override
public int compareTo (Routine o)
{
return startPtr - o.startPtr;
}
2015-06-01 09:35:51 +00:00
}