dmolony-DiskBrowser/src/com/bytezone/diskbrowser/applefile/AssemblerProgram.java

327 lines
9.6 KiB
Java
Raw Normal View History

2015-06-01 09:35:51 +00:00
package com.bytezone.diskbrowser.applefile;
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
2019-08-09 23:45:49 +00:00
import java.util.TreeMap;
2015-06-01 09:35:51 +00:00
2019-08-15 07:02:40 +00:00
import com.bytezone.common.Utility;
import com.bytezone.diskbrowser.gui.AssemblerPreferences;
2015-06-01 09:35:51 +00:00
import com.bytezone.diskbrowser.gui.DiskBrowser;
2016-02-24 21:11:14 +00:00
import com.bytezone.diskbrowser.utilities.HexFormatter;
2015-06-01 09:35:51 +00:00
public class AssemblerProgram extends AbstractFile
{
2016-02-05 00:23:53 +00:00
private static Map<Integer, String> equates;
2015-06-01 09:35:51 +00:00
private final int loadAddress;
private int executeOffset;
2016-02-05 00:23:53 +00:00
2016-02-05 01:36:16 +00:00
private byte[] extraBuffer = new byte[0];
2015-06-01 09:35:51 +00:00
2019-08-15 07:02:40 +00:00
static AssemblerPreferences assemblerPreferences;
2015-06-01 09:35:51 +00:00
public AssemblerProgram (String name, byte[] buffer, int address)
{
super (name, buffer);
this.loadAddress = address;
if (equates == null)
getEquates ();
}
public AssemblerProgram (String name, byte[] buffer, int address, int executeOffset)
{
this (name, buffer, address);
this.executeOffset = executeOffset;
}
2019-08-15 07:02:40 +00:00
public static void setAssemblerPreferences (AssemblerPreferences assemblerPreferences)
{
AssemblerProgram.assemblerPreferences = assemblerPreferences;
}
2016-02-05 00:23:53 +00:00
public void setExtraBuffer (byte[] fullBuffer, int offset, int length)
{
2016-02-20 09:48:47 +00:00
if (length >= 0)
{
this.extraBuffer = new byte[length];
System.arraycopy (fullBuffer, offset, extraBuffer, 0, length);
}
else
System.out.println ("Invalid length in setExtraBuffer() : " + length);
2016-02-05 00:23:53 +00:00
}
@Override
public String getHexDump ()
{
2016-02-18 07:44:22 +00:00
// It might be useful to add opt-O to change the offset. Sometimes it's useful
// to see the hex dump offset from zero, other times it's better to use the
// load address.
2016-02-05 07:18:23 +00:00
String text = HexFormatter.format (buffer, 0, buffer.length, loadAddress);
2016-02-05 00:23:53 +00:00
2016-02-05 01:36:16 +00:00
if (extraBuffer.length == 0)
2016-02-05 00:23:53 +00:00
return text;
2017-04-17 03:29:29 +00:00
return text + "\n\nData outside actual buffer:\n\n" + HexFormatter
.format (extraBuffer, 0, extraBuffer.length, loadAddress + buffer.length);
2016-02-05 00:23:53 +00:00
}
@Override
public String getAssembler ()
{
2016-02-05 07:18:23 +00:00
if (buffer == null)
return "No buffer";
if (assembler == null)
this.assembler = new AssemblerProgram (name, buffer, loadAddress);
2016-02-05 00:23:53 +00:00
2016-02-05 01:36:16 +00:00
if (extraBuffer.length == 0)
2016-02-05 07:18:23 +00:00
return assembler.getText ();
2016-02-05 00:23:53 +00:00
String extraName = String.format ("%s (extra)", name);
AssemblerProgram assemblerProgram =
2016-02-05 07:18:23 +00:00
new AssemblerProgram (extraName, extraBuffer, loadAddress + buffer.length);
2016-02-05 00:23:53 +00:00
2016-02-05 07:18:23 +00:00
return assembler.getText () + "\n\n" + assemblerProgram.getText ();
2016-02-05 00:23:53 +00:00
}
2015-06-01 09:35:51 +00:00
@Override
public String getText ()
{
StringBuilder pgm = new StringBuilder ();
pgm.append (String.format ("Name : %s%n", name));
pgm.append (String.format ("Length : $%04X (%,d)%n", buffer.length, buffer.length));
pgm.append (String.format ("Load at : $%04X (%,d)%n", loadAddress, loadAddress));
if (executeOffset > 0)
pgm.append (String.format ("Entry : $%04X%n", (loadAddress + executeOffset)));
2019-08-15 07:02:40 +00:00
pgm.append ("\n");
pgm.append (getListing ());
if (assemblerPreferences.showStrings)
pgm.append (getStringsText ());
2019-08-09 23:45:49 +00:00
2019-08-15 07:02:40 +00:00
return pgm.toString ();
2015-06-01 09:35:51 +00:00
}
2019-08-15 07:02:40 +00:00
private String getListing ()
2015-06-01 09:35:51 +00:00
{
StringBuilder pgm = new StringBuilder ();
List<AssemblerStatement> lines = getLines ();
2018-12-19 20:05:42 +00:00
// if the assembly doesn't start at the beginning, just dump the bytes that
2016-02-05 00:23:53 +00:00
// are skipped
2015-06-01 09:35:51 +00:00
for (int i = 0; i < executeOffset; i++)
pgm.append (String.format (" %04X: %02X%n", (loadAddress + i), buffer[i]));
for (AssemblerStatement cmd : lines)
{
StringBuilder line = new StringBuilder ();
2019-08-15 07:02:40 +00:00
String arrowText = assemblerPreferences.showTargets ? getArrow (cmd) : "";
2016-11-29 21:27:44 +00:00
line.append (
2019-08-15 07:02:40 +00:00
String.format ("%3.3s %04X: %02X ", arrowText, cmd.address, cmd.value));
2015-06-01 09:35:51 +00:00
if (cmd.size > 1)
line.append (String.format ("%02X ", cmd.operand1));
if (cmd.size > 2)
line.append (String.format ("%02X ", cmd.operand2));
while (line.length () < 23)
line.append (" ");
line.append (cmd.mnemonic + " " + cmd.operand);
if (cmd.offset != 0)
{
int branch = cmd.address + cmd.offset + 2;
line.append (String.format ("$%04X", branch < 0 ? branch += 0xFFFF : branch));
}
2016-12-17 08:34:47 +00:00
else if (cmd.target > 0
2016-02-05 00:23:53 +00:00
&& (cmd.target < loadAddress - 1 || cmd.target > (loadAddress + buffer.length)))
2015-06-01 09:35:51 +00:00
{
while (line.length () < 40)
line.append (" ");
String text = equates.get (cmd.target);
if (text != null)
line.append ("; " + text);
else
for (int i = 0, max = ApplesoftConstants.tokenAddresses.length; i < max; i++)
if (cmd.target == ApplesoftConstants.tokenAddresses[i])
{
line.append ("; Applesoft - " + ApplesoftConstants.tokens[i]);
break;
}
}
pgm.append (line.toString () + "\n");
}
if (pgm.length () > 0)
pgm.deleteCharAt (pgm.length () - 1);
2019-08-15 07:02:40 +00:00
return pgm.toString ();
2015-06-01 09:35:51 +00:00
}
private List<AssemblerStatement> getLines ()
{
List<AssemblerStatement> lines = new ArrayList<AssemblerStatement> ();
2016-02-05 00:23:53 +00:00
Map<Integer, AssemblerStatement> linesMap =
new HashMap<Integer, AssemblerStatement> ();
2015-06-01 09:35:51 +00:00
List<Integer> targets = new ArrayList<Integer> ();
int ptr = executeOffset;
int address = loadAddress + executeOffset;
while (ptr < buffer.length)
{
AssemblerStatement cmd = new AssemblerStatement (buffer[ptr]);
lines.add (cmd);
linesMap.put (address, cmd);
cmd.address = address;
if (cmd.size == 2 && ptr < buffer.length - 1)
cmd.addData (buffer[ptr + 1]);
else if (cmd.size == 3 && ptr < buffer.length - 2)
cmd.addData (buffer[ptr + 1], buffer[ptr + 2]);
else
cmd.size = 1;
if (cmd.target >= loadAddress && cmd.target < (loadAddress + buffer.length)
2016-02-05 00:23:53 +00:00
&& (cmd.value == 0x4C || cmd.value == 0x6C || cmd.value == 0x20))
2015-06-01 09:35:51 +00:00
targets.add (cmd.target);
if (cmd.offset != 0)
targets.add (cmd.address + cmd.offset + 2);
address += cmd.size;
ptr += cmd.size;
}
for (Integer target : targets)
{
AssemblerStatement cmd = linesMap.get (target);
if (cmd != null)
cmd.isTarget = true;
}
return lines;
}
2019-08-15 07:02:40 +00:00
private String getStringsText ()
{
Map<Integer, String> strings = getStrings ();
if (strings.size () == 0)
return "";
List<Integer> entryPoints = getEntryPoints ();
StringBuilder text = new StringBuilder ("\n\nPossible strings:\n\n");
for (Integer key : strings.keySet ())
{
String s = strings.get (key);
int start = key + loadAddress;
text.append (String.format ("%s %04X - %04X %s %n",
entryPoints.contains (start) ? "*" : " ", start, start + s.length (), s));
}
if (text.length () > 0)
text.deleteCharAt (text.length () - 1);
return text.toString ();
}
2019-08-09 23:45:49 +00:00
private Map<Integer, String> getStrings ()
{
TreeMap<Integer, String> strings = new TreeMap<> ();
int start = 0;
for (int ptr = 0; ptr < buffer.length; ptr++)
{
2019-08-15 07:02:40 +00:00
if ((buffer[ptr] & 0x80) != 0) // high bit set
2019-08-09 23:45:49 +00:00
continue;
2019-08-15 07:02:40 +00:00
if (buffer[ptr] == 0 // possible end of string
&& ptr - start > 5)
2019-08-09 23:45:49 +00:00
strings.put (start, HexFormatter.getString (buffer, start, ptr - start));
start = ptr + 1;
}
return strings;
}
2019-08-15 07:02:40 +00:00
private List<Integer> getEntryPoints ()
2019-08-09 23:45:49 +00:00
{
2019-08-15 07:02:40 +00:00
List<Integer> entryPoints = new ArrayList<> ();
2019-08-09 23:45:49 +00:00
2019-08-15 07:02:40 +00:00
for (int ptr = 0; ptr < buffer.length; ptr++)
if ((buffer[ptr] == (byte) 0xBD || buffer[ptr] == (byte) 0xB9)
&& (ptr + 2 < buffer.length))
{
int address = Utility.getWord (buffer, ptr + 1);
if (address > loadAddress && address < loadAddress + buffer.length)
entryPoints.add (address);
}
2019-08-09 23:45:49 +00:00
2019-08-15 07:02:40 +00:00
return entryPoints;
2019-08-09 23:45:49 +00:00
}
2015-06-01 09:35:51 +00:00
private String getArrow (AssemblerStatement cmd)
{
String arrow = "";
2019-08-15 07:02:40 +00:00
2015-06-01 09:35:51 +00:00
if (cmd.value == 0x4C || cmd.value == 0x6C || cmd.value == 0x60 || cmd.offset != 0)
arrow = "<--";
2019-08-15 07:02:40 +00:00
2016-02-05 01:36:16 +00:00
if (cmd.value == 0x20 && isLocal (cmd.target)) // JSR
2015-06-01 09:35:51 +00:00
arrow = "<--";
2019-08-15 07:02:40 +00:00
2015-06-01 09:35:51 +00:00
if (cmd.isTarget)
if (arrow.isEmpty ())
arrow = "-->";
else
arrow = "<->";
2019-08-15 07:02:40 +00:00
2015-06-01 09:35:51 +00:00
return arrow;
}
2016-02-05 01:36:16 +00:00
private boolean isLocal (int target)
{
return target >= loadAddress
&& target < loadAddress + buffer.length + extraBuffer.length;
}
2015-06-01 09:35:51 +00:00
private void getEquates ()
{
equates = new HashMap<Integer, String> ();
DataInputStream inputEquates =
2016-02-05 00:23:53 +00:00
new DataInputStream (DiskBrowser.class.getClassLoader ()
.getResourceAsStream ("com/bytezone/diskbrowser/applefile/equates.txt"));
2015-06-01 09:35:51 +00:00
BufferedReader in = new BufferedReader (new InputStreamReader (inputEquates));
String line;
try
{
while ((line = in.readLine ()) != null)
{
if (!line.isEmpty () && !line.startsWith ("*"))
{
int address = Integer.parseInt (line.substring (0, 4), 16);
if (equates.containsKey (address))
2016-11-29 21:27:44 +00:00
System.out.printf ("Duplicate equate entry : %04X%n" + address);
2015-06-01 09:35:51 +00:00
else
equates.put (address, line.substring (6));
}
}
in.close ();
}
catch (IOException e)
{
e.printStackTrace ();
}
}
}