2015-06-01 09:35:51 +00:00
|
|
|
package com.bytezone.diskbrowser.infocom;
|
|
|
|
|
2016-07-20 05:22:33 +00:00
|
|
|
import java.util.ArrayList;
|
2015-06-01 09:35:51 +00:00
|
|
|
import java.util.List;
|
|
|
|
import java.util.Map;
|
|
|
|
import java.util.TreeMap;
|
|
|
|
|
|
|
|
import javax.swing.tree.DefaultMutableTreeNode;
|
|
|
|
|
|
|
|
import com.bytezone.diskbrowser.applefile.AbstractFile;
|
|
|
|
import com.bytezone.diskbrowser.disk.DefaultAppleFileSource;
|
2016-07-20 05:22:33 +00:00
|
|
|
import com.bytezone.diskbrowser.disk.Disk;
|
|
|
|
import com.bytezone.diskbrowser.disk.DiskAddress;
|
2016-02-24 21:11:14 +00:00
|
|
|
import com.bytezone.diskbrowser.utilities.HexFormatter;
|
2015-06-01 09:35:51 +00:00
|
|
|
|
|
|
|
class CodeManager extends AbstractFile
|
|
|
|
{
|
2016-03-23 23:37:59 +00:00
|
|
|
Header header;
|
|
|
|
int codeSize;
|
|
|
|
Map<Integer, Routine> routines = new TreeMap<Integer, Routine> ();
|
|
|
|
|
|
|
|
public CodeManager (Header header)
|
|
|
|
{
|
|
|
|
super ("Code", header.buffer);
|
|
|
|
this.header = header;
|
|
|
|
}
|
|
|
|
|
2016-07-20 05:22:33 +00:00
|
|
|
void addNodes (DefaultMutableTreeNode root, InfocomDisk disk)
|
2016-03-23 23:37:59 +00:00
|
|
|
{
|
|
|
|
root.setAllowsChildren (true);
|
|
|
|
|
2016-07-20 05:22:33 +00:00
|
|
|
// should be set by now - do this better!
|
|
|
|
codeSize = header.stringPointer - header.highMemory;
|
2016-03-23 23:37:59 +00:00
|
|
|
|
|
|
|
int count = 0;
|
|
|
|
for (Routine routine : routines.values ())
|
|
|
|
{
|
2016-07-20 05:22:33 +00:00
|
|
|
String name = String.format ("%3d %s (%04X)", ++count, routine.getName (),
|
|
|
|
routine.startPtr / 2);
|
|
|
|
DefaultAppleFileSource dafs = new DefaultAppleFileSource (name, routine, disk);
|
|
|
|
dafs.setSectors (getSectors (routine, disk.getDisk ()));
|
|
|
|
|
|
|
|
DefaultMutableTreeNode node = new DefaultMutableTreeNode (dafs);
|
2016-03-23 23:37:59 +00:00
|
|
|
node.setAllowsChildren (false);
|
|
|
|
root.add (node);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-07-20 05:22:33 +00:00
|
|
|
private List<DiskAddress> getSectors (Routine routine, Disk disk)
|
|
|
|
{
|
|
|
|
int blockNo = routine.startPtr / 256 + 48;
|
|
|
|
int size = routine.length;
|
|
|
|
List<DiskAddress> blocks = new ArrayList<DiskAddress> ();
|
|
|
|
|
|
|
|
while (size > 0)
|
|
|
|
{
|
|
|
|
blocks.add (disk.getDiskAddress (blockNo++));
|
|
|
|
size -= 256;
|
|
|
|
}
|
|
|
|
return blocks;
|
|
|
|
}
|
|
|
|
|
2016-03-23 23:37:59 +00:00
|
|
|
public void addMissingRoutines ()
|
|
|
|
{
|
|
|
|
System.out.printf ("%nWalking the code block%n%n");
|
|
|
|
int total = 0;
|
|
|
|
int ptr = header.highMemory;
|
|
|
|
|
|
|
|
while (ptr < header.stringPointer)
|
|
|
|
{
|
|
|
|
if (ptr >= 0 && ptr % 2 == 1) // routine must start on a word boundary
|
|
|
|
ptr++;
|
|
|
|
|
|
|
|
if (containsRoutineAt (ptr))
|
|
|
|
{
|
|
|
|
ptr += getRoutine (ptr).length;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
Routine routine = addRoutine (ptr, 0);
|
|
|
|
if (routine == null)
|
|
|
|
{
|
|
|
|
System.out.printf ("Invalid routine found : %05X%n", ptr);
|
|
|
|
ptr = findNextRoutine (ptr + 1);
|
|
|
|
System.out.printf ("skipping to %05X%n", ptr);
|
|
|
|
if (ptr == 0)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
total++;
|
|
|
|
ptr += routine.length;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
System.out.printf ("%n%d new routines found by walking the code block%n%n", total);
|
|
|
|
}
|
|
|
|
|
|
|
|
private int findNextRoutine (int address)
|
|
|
|
{
|
|
|
|
for (Routine routine : routines.values ())
|
|
|
|
if (routine.startPtr > address)
|
|
|
|
return routine.startPtr;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public String getText ()
|
|
|
|
{
|
|
|
|
StringBuilder text = new StringBuilder ();
|
|
|
|
int count = 0;
|
|
|
|
int nextAddress = header.highMemory;
|
|
|
|
text.append (" # Address Size Lines Strings Called Calls Gap Pack\n");
|
|
|
|
text.append ("--- ------- ---- ----- ------- ------ ----- --- ----\n");
|
|
|
|
for (Routine r : routines.values ())
|
|
|
|
{
|
|
|
|
int gap = r.startPtr - nextAddress;
|
|
|
|
text.append (String
|
|
|
|
.format ("%3d %05X %5d %3d %2d %3d %3d %4d %04X%n",
|
|
|
|
++count, r.startPtr, r.length, r.instructions.size (), r.strings,
|
|
|
|
r.calledBy.size (), r.calls.size (), gap, r.startPtr / 2));
|
|
|
|
|
|
|
|
nextAddress = r.startPtr + r.length;
|
|
|
|
}
|
|
|
|
text.deleteCharAt (text.length () - 1);
|
|
|
|
return text.toString ();
|
|
|
|
}
|
|
|
|
|
|
|
|
public boolean containsRoutineAt (int address)
|
|
|
|
{
|
|
|
|
return (routines.containsKey (address));
|
|
|
|
}
|
|
|
|
|
|
|
|
public void addCodeRoutines ()
|
|
|
|
{
|
|
|
|
List<Integer> routines = header.objectManager.getCodeRoutines ();
|
|
|
|
System.out.println ("Adding " + routines.size () + " code routines");
|
|
|
|
for (Integer address : routines)
|
|
|
|
addRoutine (address, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
public void addActionRoutines ()
|
|
|
|
{
|
|
|
|
List<Integer> routines = header.grammar.getActionRoutines ();
|
|
|
|
System.out.println ("Adding " + routines.size () + " action routines");
|
|
|
|
for (Integer address : routines)
|
|
|
|
addRoutine (address, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
public Routine addRoutine (int address, int caller)
|
|
|
|
{
|
|
|
|
if (address == 0) // stack-based call
|
|
|
|
return null;
|
|
|
|
if (address > header.fileLength)
|
|
|
|
return null;
|
|
|
|
|
|
|
|
// check whether we already have this routine
|
|
|
|
if (routines.containsKey (address))
|
|
|
|
{
|
|
|
|
Routine routine = routines.get (address);
|
|
|
|
routine.addCaller (caller);
|
|
|
|
return routine;
|
|
|
|
}
|
|
|
|
|
|
|
|
// try to create a new Routine
|
|
|
|
Routine r = new Routine (address, header, caller);
|
|
|
|
if (r.length == 0) // invalid routine
|
|
|
|
return null;
|
|
|
|
|
|
|
|
// recursively add all routines called by this one
|
|
|
|
routines.put (address, r);
|
|
|
|
for (int ptr : r.calls)
|
|
|
|
addRoutine (ptr, address);
|
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
public Routine getRoutine (int address)
|
|
|
|
{
|
|
|
|
return routines.get (address);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public String getHexDump ()
|
|
|
|
{
|
|
|
|
// this depends on codeSize being set after the strings have been processed
|
|
|
|
return HexFormatter.format (buffer, header.highMemory, codeSize);
|
|
|
|
}
|
2015-06-01 09:35:51 +00:00
|
|
|
}
|