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; import com.bytezone.diskbrowser.utilities.Utility; // -----------------------------------------------------------------------------------// 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 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 = Utility.intValue (buffer[p], buffer[p + 1]); procOffset = p - offset; valid = procOffset > 0; if (valid) { procedureNo = buffer[procOffset] & 0xFF; procLevel = buffer[procOffset + 1] & 0xFF; codeStart = Utility.intValue (buffer[procOffset - 2], buffer[procOffset - 1]); codeEnd = Utility.intValue (buffer[procOffset - 4], buffer[procOffset - 3]); parmSize = Utility.intValue (buffer[procOffset - 6], buffer[procOffset - 5]); dataSize = Utility.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 extractStrings () // ---------------------------------------------------------------------------------// { decode (); List 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 (); } }