checkpoint: VT1200 reverse-engineering workspace before board-model work

This commit is contained in:
SDD committed 2026-10-16 01:37:50 +03:00
commit acb92d3e44
325 files changed
+951894

No files matched your search

+72
View File
@@ -0,0 +1,72 @@
// Assert that bit-address pointer constants have been converted to references.
// Serves as the failing test for FixPtrConstants.java.
//@category VT1200
import ghidra.app.script.GhidraScript;
import ghidra.program.model.address.Address;
import ghidra.program.model.mem.MemoryBlock;
import ghidra.program.model.symbol.Reference;
public class AssertPtrFix extends GhidraScript {
// Known constant: 0x3F225CD8 at ROM+0x4A5E -> 'Connection request from user %s on node %s.'
private static final long CONST_OFF = 0x4A5EL;
private static final long CONST_VAL = 0x3F225CD8L;
private static final long TARGET_OFF = 0x4B9BL;
private int failures = 0;
private void check(boolean ok, String msg) {
println((ok ? "PASS: " : "FAIL: ") + msg);
if (!ok) {
failures++;
}
}
@Override
public void run() throws Exception {
MemoryBlock rom = null;
long best = -1;
for (MemoryBlock b : currentProgram.getMemory().getBlocks()) {
if (b.isInitialized()) {
long len = b.getEnd().getOffset() - b.getStart().getOffset();
if (len > best) {
best = len;
rom = b;
}
}
}
if (rom == null) {
throw new IllegalStateException("no initialized ROM block");
}
long base = rom.getStart().getOffset();
println(String.format("ROM block base: 0x%08X", base));
Address from = currentProgram.getAddressFactory().getDefaultAddressSpace()
.getAddress(base + CONST_OFF);
Address to = currentProgram.getAddressFactory().getDefaultAddressSpace()
.getAddress(base + TARGET_OFF);
byte[] raw = new byte[4];
rom.getBytes(from, raw);
long value = (raw[0] & 0xFFL) | ((raw[1] & 0xFFL) << 8)
| ((raw[2] & 0xFFL) << 16) | ((raw[3] & 0xFFL) << 24);
check(value == CONST_VAL,
String.format("constant at ROM+0x%X is 0x%08X (got 0x%08X)",
CONST_OFF, CONST_VAL, value));
boolean found = false;
for (Reference r : currentProgram.getReferenceManager().getReferencesFrom(from)) {
if (r.getToAddress().equals(to)) {
found = true;
break;
}
}
check(found, String.format("reference ROM+0x%X -> ROM+0x%X exists", CONST_OFF, TARGET_OFF));
if (failures > 0) {
throw new AssertionError(failures + " assertion(s) failed");
}
println("ASSERT: all pointer-fix checks passed");
}
}
+92
View File
@@ -0,0 +1,92 @@
// Convert TMS34010 bit-address pointer constants into Ghidra references.
//
// The firmware stores absolute *bit* addresses: a 32-bit constant P refers to
// physical byte P >> 3. A byte-addressed Ghidra program cannot resolve those,
// so this script scans the loaded ROM for such constants and adds a reference
// from each constant to the byte it points at. Byte data is left untouched.
//
// Companion: tools/ptr_constants.py does the same off-line (shared logic).
//@category VT1200
import ghidra.app.script.GhidraScript;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSpace;
import ghidra.program.model.mem.MemoryBlock;
import ghidra.program.model.symbol.RefType;
import ghidra.program.model.symbol.Reference;
import ghidra.program.model.symbol.SourceType;
public class FixPtrConstants extends GhidraScript {
private MemoryBlock loadRom() {
MemoryBlock rom = null;
long best = -1;
for (MemoryBlock b : currentProgram.getMemory().getBlocks()) {
if (b.isInitialized()) {
long len = b.getEnd().getOffset() - b.getStart().getOffset();
if (len > best) {
best = len;
rom = b;
}
}
}
return rom;
}
@Override
public void run() throws Exception {
MemoryBlock rom = loadRom();
if (rom == null) {
throw new IllegalStateException("no initialized ROM block");
}
AddressSpace space = currentProgram.getAddressFactory().getDefaultAddressSpace();
long base = rom.getStart().getOffset();
long size = rom.getEnd().getOffset() - base + 1;
println(String.format("ROM block 0x%08X-0x%08X (%d bytes)",
base, base + size - 1, size));
int created = 0;
int already = 0;
int failed = 0;
int skipped = 0;
byte[] raw = new byte[4];
for (long off = 0; off + 4 <= size; off += 2) {
Address from = space.getAddress(base + off);
if (currentProgram.getListing().getInstructionContaining(from) != null) {
skipped++; // don't add refs from inside code
continue;
}
rom.getBytes(from, raw);
long value = (raw[0] & 0xFFL) | ((raw[1] & 0xFFL) << 8)
| ((raw[2] & 0xFFL) << 16) | ((raw[3] & 0xFFL) << 24);
long target = value >>> 3;
if (target < base || target >= base + size) {
skipped++;
continue;
}
Address to = space.getAddress(target);
boolean exists = false;
for (Reference r : currentProgram.getReferenceManager().getReferencesFrom(from)) {
if (r.getToAddress().equals(to)) {
exists = true;
break;
}
}
if (exists) {
already++;
continue;
}
try {
currentProgram.getReferenceManager().addMemoryReference(
from, to, RefType.DATA, SourceType.USER_DEFINED, 0);
created++;
} catch (Exception e) {
failed++;
}
}
println(String.format("references created=%d already=%d failed=%d skipped=%d",
created, already, failed, skipped));
}
}
+27
View File
@@ -0,0 +1,27 @@
// Report basic analysis stats for a TMS34010 program.
//@category VT1200
import ghidra.app.script.GhidraScript;
import ghidra.program.model.listing.Instruction;
import ghidra.program.model.listing.InstructionIterator;
public class StatsTMS34010 extends GhidraScript {
@Override
public void run() throws Exception {
println("=== " + currentProgram.getName()
+ " [" + currentProgram.getLanguageID() + "]");
println(" functions : " + currentProgram.getFunctionManager().getFunctionCount());
long count = 0;
InstructionIterator it = currentProgram.getListing().getInstructions(true);
while (it.hasNext()) {
Instruction ins = it.next();
count++;
if (count <= 3) {
println(" first insn : " + ins.getAddress() + ": " + ins);
}
}
println(" instructions : " + count);
}
}
+82
View File
@@ -0,0 +1,82 @@
// Verify the TMS34010 language load + disassembly and print a few regions.
//
// NOTE: the TMS34010 module ships "Midway arcade" default memory blocks
// (VRAM/RAM/GFX/ROM ...), so currentProgram.getImageBase() is NOT the address
// of our loaded ROM. We locate the loaded block by name instead.
//@category VT1200
import ghidra.app.script.GhidraScript;
import ghidra.app.cmd.disassemble.DisassembleCommand;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSet;
import ghidra.program.model.address.AddressSpace;
import ghidra.program.model.listing.Instruction;
import ghidra.program.model.mem.MemoryBlock;
public class VerifyTMS34010 extends GhidraScript {
@Override
public void run() throws Exception {
AddressSpace space = currentProgram.getAddressFactory().getDefaultAddressSpace();
println("=== program: " + currentProgram.getName());
println("=== language: " + currentProgram.getLanguageID());
println("=== image base: 0x" + Long.toHexString(currentProgram.getImageBase().getOffset()) + " (language default)");
// The loaded ROM is the only *initialized* block; the language's
// default blocks are all uninitialized. Pick the largest such block.
long base = -1;
long best = -1;
for (MemoryBlock b : currentProgram.getMemory().getBlocks()) {
if (b.isInitialized()) {
long len = b.getEnd().getOffset() - b.getStart().getOffset();
println(String.format(" loaded block %-8s %s - %s (%,d bytes)",
b.getName(), b.getStart(), b.getEnd(), len + 1));
if (len > best) {
best = len;
base = b.getStart().getOffset();
}
}
}
if (base < 0) {
base = currentProgram.getImageBase().getOffset();
}
println(String.format("=== ROM block base: 0x%08X", base));
// Disassemble by following control flow from the reset target and the
// resolvable interrupt vectors, bounded to the ROM block.
MemoryBlock rom = null;
for (MemoryBlock b : currentProgram.getMemory().getBlocks()) {
if (b.isInitialized() && b.getStart().getOffset() == base) {
rom = b;
}
}
AddressSet restrict = new AddressSet(space.getAddress(base),
space.getAddress(base + 0x1C0000 - 1));
AddressSet starts = new AddressSet();
starts.add(space.getAddress(base + 0x8)); // reset target (POST)
for (long o : new long[] { 0xF88, 0xF98, 0xFA0 }) {
starts.add(space.getAddress(base + o)); // vector 1, 22, 23
}
new DisassembleCommand(starts, restrict, true).applyTo(currentProgram, monitor);
// the module's incomplete pcode limits flow-following, so also
// disassemble the boot/exception bank (first 256 KiB) linearly.
AddressSet bank0 = new AddressSet(space.getAddress(base),
space.getAddress(base + 0x40000 - 1));
new DisassembleCommand(bank0, null, false).applyTo(currentProgram, monitor);
long[] offs = { 0x0, 0x8, 0xF88 }; // header, reset target, vector-1 target
for (long o : offs) {
println(String.format("--- ROM+0x%X ---", o));
int n = 0;
for (Instruction ins : currentProgram.getListing()
.getInstructions(space.getAddress(base + o), true)) {
println(String.format(" 0x%08X: %s", ins.getAddress().getOffset(), ins));
if (++n >= 8) {
break;
}
}
}
}
}