// Find lambda monitoring code and pedal map
//@category Analysis

import java.util.ArrayList;
import java.util.List;
import ghidra.app.script.GhidraScript;
import ghidra.app.decompiler.DecompInterface;
import ghidra.app.decompiler.DecompileResults;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressFactory;
import ghidra.program.model.address.AddressSet;
import ghidra.program.model.listing.Function;
import ghidra.program.model.listing.FunctionManager;
import ghidra.program.model.listing.Instruction;
import ghidra.program.model.listing.InstructionIterator;
import ghidra.program.model.listing.Listing;
import ghidra.program.model.mem.Memory;
import ghidra.program.model.symbol.Reference;
import ghidra.program.model.symbol.ReferenceIterator;
import ghidra.program.model.symbol.ReferenceManager;

public class FindLambdaAndPedal extends GhidraScript {

    private List<Reference> collect(ReferenceManager refMgr, Address a) {
        List<Reference> list = new ArrayList<>();
        ReferenceIterator it = refMgr.getReferencesTo(a);
        while (it.hasNext()) list.add(it.next());
        return list;
    }

    @Override
    public void run() throws Exception {
        AddressFactory af = currentProgram.getAddressFactory();
        FunctionManager fm = currentProgram.getFunctionManager();
        Listing listing = currentProgram.getListing();
        Memory mem = currentProgram.getMemory();
        ReferenceManager refMgr = currentProgram.getReferenceManager();

        // ============================================
        // FIND FUNCTION CONTAINING P0130 DTC CODE
        // ============================================
        println("================================================================");
        println("PART 1: Find function that PROCESSES P0130");
        println("================================================================");
        // The P0130 DTC code (0x0130) appears in calibration at 0x800217D8 (in a DTC table).
        // The function that monitors lambda B1S1 must reference this table or a near table entry.
        // Search code for references to the DTC table area 0x800217D0..0x800217E0.
        Address asw_a = af.getAddress("80040000");
        Address asw_b = af.getAddress("800FFEFF");
        AddressSet aswSet = new AddressSet(asw_a, asw_b);

        // Direct xrefs to 0x800217D8 (where P0130 code lives)
        long[] dtcAddrs = {0x800217D8L, 0x800217D4L, 0x800217D6L, 0x800217DAL, 0x800217DCL,
                           0x800217DEL, 0x800217E0L, 0x800217E2L};
        for (long da : dtcAddrs) {
            Address a = af.getAddress(Long.toHexString(da));
            List<Reference> refs = collect(refMgr, a);
            if (refs.size() > 0) {
                println(String.format("\nxrefs to 0x%08X (%d):", da, refs.size()));
                for (Reference r : refs) {
                    Function func = fm.getFunctionContaining(r.getFromAddress());
                    String fname = (func != null) ? func.getName() : "<no-func>";
                    Instruction ins = listing.getInstructionAt(r.getFromAddress());
                    String insStr = (ins != null) ? ins.toString() : "?";
                    println(String.format("  from 0x%s in [%s]  insn=%s",
                            r.getFromAddress(), fname, insStr));
                }
            }
        }

        // ============================================
        // PEDAL POSITION INPUT: usually read as a fast sensor in main control loop.
        // Common Bosch RAM addresses for "pedal position": 0xD0000XXX range.
        // Search for instructions accessing typical sensor names is hard without symbols.
        // Better: find lookup tables. Pedal map is typically 16×16 or 8×16 = 128-256 bytes,
        // with rising rows AND rising columns.
        // ============================================
        println("\n================================================================");
        println("PART 2: Pedal-like 2D maps (16-wide, monotonic rows AND rising columns)");
        println("================================================================");
        // Looking for 8 rows × 16 cols of rising monotonic data
        long calS = 0x80020000L, calE = 0x80040000L;
        int found = 0;
        outer:
        for (long off = calS; off < calE - 128; off += 1) {
            byte[] block = new byte[128];
            try { mem.getBytes(af.getAddress(Long.toHexString(off)), block); }
            catch (Exception e) { continue; }
            // Check 8 rows of 16
            boolean ok = true;
            int totalRise = 0;
            for (int row = 0; row < 8; row++) {
                int first = block[row*16] & 0xFF;
                int last  = block[row*16 + 15] & 0xFF;
                if (first > 60) { ok = false; break; }
                if (last < 100) { ok = false; break; }
                int viol = 0;
                for (int i = 1; i < 16; i++) {
                    if ((block[row*16 + i] & 0xFF) < (block[row*16 + i - 1] & 0xFF)) viol++;
                }
                if (viol > 2) { ok = false; break; }
                totalRise += last - first;
            }
            if (!ok) continue;
            // Check columns also rise from row 0 to row 7
            int colRiseOk = 0;
            for (int col = 0; col < 16; col++) {
                if ((block[7*16+col] & 0xFF) >= (block[0*16+col] & 0xFF) + 30) colRiseOk++;
            }
            if (colRiseOk < 10) continue;
            // Looks plausible
            if (found < 8) {
                println(String.format("\nMap @ 0x%08X (8×16, totalRise=%d, colsRising=%d/16):",
                                       off, totalRise, colRiseOk));
                for (int row = 0; row < 8; row++) {
                    StringBuilder sb = new StringBuilder("  ");
                    for (int i = 0; i < 16; i++) sb.append(String.format("%02X ", block[row*16+i] & 0xFF));
                    println(sb.toString());
                }
            }
            found++;
            off += 127; // skip past this match
        }
        println(String.format("Total candidate 8×16 maps: %d", found));

        println("\nDONE");
    }
}
