# tdinfo

> **Tool `tools/dos/tdinfo.py`** (program, 16-bit DOS). Read Borland Turbo Debugger symbols (TDINFO, signature FB52) from a DOS or NE executable.

Run it with `./gameframe run tdinfo ARGS` or `python3 tools/dos/tdinfo.py`. [Source on GitHub](https://github.com/tanepiper/game-frame/blob/main/tools/dos/tdinfo.py).

## Its own help

This is the help text in the tool itself, as `./gameframe tool tdinfo` prints it.

```text
Read Borland Turbo Debugger symbols (TDINFO, signature FB52) from a DOS or NE executable.

Usage: tdinfo.py BINARY COMMAND [ARGS]

Commands:
  summary            where the symbols are, their version and counts, and the modules (units)
  labels             TSV for labels/names.tsv: every procedure and global as seg:off, name, evidence
  procs [TEXT]       procedures: address, size, module, parameters and locals with their types
  globals [TEXT]     global variables and typed constants: address, size, type
  types [TEXT]       named types; records show each field with its offset (summed from sizes)
  lines [MODULE]     line numbers: address -> source file and line
  json OUT           everything above as one JSON file
  rust TYPE ...      Rust structs and readers for record types (and arrays of records), with a
                     comment on every field giving its Pascal type and offset
  rust-globals DIR   a module directory: one file per Pascal unit with its variables at their
                     data-segment offsets, types/ (one file per record type), and mod.rs's Globals with load/store to
                     and from a 64 KB image of the segment (checks ported code against the
                     original's memory after an emulated run)
  rust-locals PROC   a struct of a procedure's locals at their BP offsets, with load/store on an
                     image of the stack segment (for code nested in a big procedure)
  rust-reader        the small PascalReader / PascalWriter module the generated code uses

Borland Pascal and Borland C++ write these symbols when a program is linked with debug
information (TPC /V, BPC /V, TLINK /v), either appended to the program or in a .TDS file. Many
shipped games kept them. They give the original names of procedures, globals, parameters,
locals and types, and a map from code addresses to source lines: run this before tracing
anything by hand, and load the labels into Ghidra (scripts/ghidra-run.sh BINARY labels ...).

Addresses use Ghidra's numbering: NE segment N is selector 0x1000 + 8 * (N - 1); in an MZ
program the symbol's segment is relative to the load image, which Ghidra loads at 0x1000.

Format notes (from reading real files; Borland's own description is not public in full):
  - The block starts with the 16-bit magic 0x52FB and a version word (0x0300 = 3.0). A CodeView
    style "NBxx" trailer at the end of the file may point to it.
  - Tables follow the header and its extension, in this order: symbols (9 bytes), modules (16),
    source files (6), line numbers (4), scopes (12), segments (16), correlations (8), types (8),
    members (5, one more than the header's count), then the name pool (zero-terminated names,
    numbered from 1).
  - A type is one 8-byte slot: id, name, size, then 3 bytes whose meaning depends on the id.
    Subranges and arrays take a second slot (bounds, index type); type numbers are slot numbers.
  - Record fields (members) store a name and a type but no offset: field offsets here are the
    running sum of field sizes (Pascal records are not padded). Treat them as *inferred* and
    check one against the code before relying on it. Variant records are not decoded.
It does not read Turbo Debugger 4+ (32-bit) symbols, CodeView or Microsoft/Watcom formats.
```

## Named by

Skills: [re-start](/skills/re-start), [re-inventory](/skills/re-inventory), [re-ghidra](/skills/re-ghidra), [re-trace](/skills/re-trace), [re-format](/skills/re-format), [re-port](/skills/re-port).
