amiga-bootcamp/13_toolchain/fd_files.md
Ilia Sharin fca930d0db docs(amiga): make cross-references clickable markdown links
Convert bare .md path references in 29 files to proper [name](relative/path)
markdown links for GitHub navigation.
2026-04-23 12:24:21 -04:00

5.4 KiB
Raw Blame History

← Home · Toolchain

FD/SFD Files — Function Definition Format and LVO Generation

Overview

FD files (Function Definition files) are the machine-readable specification of a library's public API. They define every function's name, LVO offset, and register argument mapping. FD files are the source of truth for generating pragma files, inline headers, and IDA LVO scripts.


File Location

NDK:fd/exec_lib.fd
NDK:fd/dos_lib.fd
NDK:fd/intuition_lib.fd
NDK:fd/graphics_lib.fd
NDK:sfd/exec_lib.sfd       (SFD = extended format with C prototypes)

FD File Syntax

* "exec.library"
##base _SysBase
##bias 30
##public
Supervisor(userFunction)(A5)
ExitIntr()()
Schedule()()
Reschedule()()
Switch()()
Dispatch()()
Exception()()
InitCode(startClass,version)(D0,D1)
InitStruct(initTable,memory,size)(A1,A2,D0)
MakeLibrary(funcInit,structInit,libInit,dataSize,segList)(A0,A1,A2,D0,D1)
MakeFunctions(target,functionArray,funcDispBase)(A0,A1,A2)
FindResident(name)(A1)
InitResident(resident,segList)(A1,D1)
Alert(alertNum)(D7)
Debug(flags)(D0)
##bias 120
Forbid()()
Permit()()
##bias 132
Disable()()
Enable()()
...

Syntax Rules

Element Meaning
* "name" Comment; library identity
##base _Symbol Global base pointer symbol name
##bias N Set current LVO offset to N
##public Following functions are public
##private Following functions are private (reserved)
FuncName(args)(regs) Function: name, C parameter names, register assignments
##end End of file

LVO auto-increment: After each function, the bias increases by 6 (one JMP instruction slot = 6 bytes).


SFD File Format (Extended)

SFD adds full C prototypes:

==id $Id: exec_lib.sfd,v 1.0 2003/01/01 00:00:00 Exp $
==base _SysBase
==basetype struct ExecBase *
==libname exec.library
==bias 30
==public
APTR Supervisor(ULONG (*userFunction)()) (A5)
==end

Parsing FD Files — Python Script

#!/usr/bin/env python3
"""parse_fd.py — Parse AmigaOS FD files into LVO tables.

Usage: python3 parse_fd.py exec_lib.fd [--json] [--ida]
"""
import re, sys, json, argparse

def parse_fd(path):
    """Parse an FD file and return list of (offset, name, args, regs)."""
    funcs = []
    bias = 30
    public = True
    
    with open(path) as f:
        for line in f:
            line = line.strip()
            if not line or line.startswith('*'):
                continue
            if line.startswith('##base'):
                continue
            if line.startswith('##bias'):
                bias = int(line.split()[1])
                continue
            if line == '##public':
                public = True
                continue
            if line == '##private':
                public = False
                continue
            if line == '##end':
                break
            
            # Parse: FuncName(args)(regs)
            m = re.match(r'(\w+)\(([^)]*)\)\(([^)]*)\)', line)
            if m and public:
                name = m.group(1)
                args = [a.strip() for a in m.group(2).split(',') if a.strip()]
                regs = [r.strip() for r in m.group(3).split(',') if r.strip()]
                funcs.append({
                    'name': name,
                    'lvo': -bias,
                    'bias': bias,
                    'args': args,
                    'regs': regs
                })
            bias += 6  # always advance, even for private
    
    return funcs

def output_ida_script(funcs, base_name):
    """Generate IDA Python LVO dict."""
    print(f"# Auto-generated from FD file")
    print(f"LVO_{base_name} = {{")
    for f in funcs:
        print(f"    {f['lvo']:+5d}: \"{f['name']}\",")
    print("}")

def output_json(funcs):
    print(json.dumps(funcs, indent=2))

def output_table(funcs):
    print(f"{'LVO':>6}  {'Bias':>5}  {'Function':<30}  {'Registers'}")
    print(f"{'─'*6}  {'─'*5}  {'─'*30}  {'─'*30}")
    for f in funcs:
        regs = ', '.join(f['regs']) if f['regs'] else '(none)'
        print(f"{f['lvo']:>+6d}  {f['bias']:>5d}  {f['name']:<30}  {regs}")

if __name__ == '__main__':
    ap = argparse.ArgumentParser(description='Parse AmigaOS FD files')
    ap.add_argument('fdfile', help='Path to .fd file')
    ap.add_argument('--json', action='store_true', help='Output JSON')
    ap.add_argument('--ida', action='store_true', help='Output IDA script')
    args = ap.parse_args()
    
    funcs = parse_fd(args.fdfile)
    if args.json:
        output_json(funcs)
    elif args.ida:
        output_ida_script(funcs, args.fdfile.split('/')[-1].replace('_lib.fd',''))
    else:
        output_table(funcs)
    
    sys.stderr.write(f"Parsed {len(funcs)} public functions\n")

Where to Get NDK FD Files

NDK Source FD path
NDK 3.9 Aminet: dev/misc/NDK39.lha (free) NDK_3.9/Include/fd/
NDK 3.2 Hyperion (commercial, ~€30) NDK3.2/Include_H/fd/
NDK 3.1 Commodore (archived) NDK3.1/Include/fd/

Auto-Generating IDA LVO Labels

# Generate IDA script from all FD files:
for fd in NDK_3.9/Include/fd/*_lib.fd; do
    python3 parse_fd.py "$fd" --ida >> all_lvos.py
done

Then in IDA: File → Script command → Run all_lvos.py


References