disassembler.library

disassembler.library is a small system library from the MMULib project (Thomas Richter, THOR-Software). It turns 680x0 machine code into Motorola-syntax assembler text. The NDK 3.2 ships the library binary in MuTools/ (disassembler.library 40.9 (24.1.2021)) and its developer files in MuManual/ (autodoc, C and assembler includes, FD file and BMAP, all at include revision 40.2).

MuForce and MuGuardianAngel use it for their DISPC/DISRANGE options. The library is just as useful in your own monitors, crash reporters and debug output.

According to its autodoc, the library:

  • knows every MC68K processor from the 68000 to the 68060, including FPU and MMU instructions;
  • is MMU aware. If mmu.library is available, it will not read invalid or unavailable memory or I/O space, provided the MMU table is set up correctly. It also works without mmu.library, but then it cannot validate its accesses;
  • is interrupt and supervisor callable, provided your output procedure is too.

Library interface

Item Value
Library name "disassembler.library" (DISASSEMBLER_NAME; assembler macro DISASSEMBLERNAME)
Base variable DisassemblerBase
Base structure struct DisassemblerBase { struct Library dllib_Library; /* private */ };
Headers libraries/disassembler.h / .i, clib/disassembler_protos.h, pragmas/disassembler_pragmas.h, proto/disassembler.h, lvo/disassembler_lvo.i
FD bias 66 (the FD notes "6 slots reserved here" before the public functions)
Function LVO Registers Prototype
Disassemble −66 ($42) A0 APTR Disassemble(struct DisData *ds);
FindStartPosition −72 ($48) A0, D0, D1 APTR FindStartPosition(APTR startpc, UWORD min, UWORD max);

struct DisData

struct DisData {
    APTR   ds_From;      /* base to start disassembly from (must be even) */
    APTR   ds_UpTo;      /* where to stop it (exclusive) */
    APTR   ds_PC;        /* the disassembler prints a "*" here */
    void  *ds_PutProc;   /* output routine; void * to avoid casts */
    APTR   ds_UserData;  /* passed to the PutProc in a1 and a3 */
    APTR   ds_UserBase;  /* passed to the PutProc in a4 */
    UWORD  ds_Truncate;  /* limit width of the output, 0 = no limit */
    UWORD  ds_reserved;  /* must be zero */
};
Field Meaning
ds_From First address to disassemble. Must be even.
ds_UpTo Exclusive end of the region.
ds_PC An address to mark with * in the output, typically the program counter of a fault.
ds_PutProc Character output routine (see below).
ds_UserData Loaded into A1 and A3 for the first call of ds_PutProc.
ds_UserBase Loaded into A4 before each call. For near-data C code, set it to the linker's data base (_LinkerDB).
ds_Truncate Maximum line length; longer lines are cut. 0 disables truncation.
ds_reserved Must be 0 for future extensions.

The PutProc

ds_PutProc is called once per output character, in the style of exec RawDoFmt():

Register Contents
D0 The character. Lines end with a single LF (0x0A).
A1, A3 ds_UserData on the first call; afterwards, whatever A3 the routine returned last time.
A4 ds_UserBase.

The routine may return an updated A3, which is passed in A1 and A3 on the next call. That makes the classic "store byte and advance pointer" routine possible:

; PutProc that appends to a buffer: a3 = current write pointer
PutChar:
        move.b  d0,(a3)+
        rts

If Disassemble() is called from an interrupt or in supervisor mode, the PutProc must be safe there as well.

Disassemble()

APTR Disassemble(struct DisData *ds);   /* A0 */

Disassemble() disassembles the memory from ds_From up to ds_UpTo and sends the text through ds_PutProc, marking ds_PC with an asterisk. It returns the address where the disassembly stopped. Pass that value back as ds_From to continue at the right place.

FindStartPosition()

APTR FindStartPosition(APTR startpc, UWORD min, UWORD max);   /* A0, D0, D1 */

68k instructions have variable length, so you cannot simply start disassembling n bytes before an address. FindStartPosition() examines the code in front of startpc and returns an address between startpc - max and startpc - min (with min < max, both positive distances). Disassembly started there includes the instruction at startpc and shows as few illegal instructions as possible. If no suitable address is found, startpc itself is returned.

startpc should be an address known to hold a valid instruction. The autodoc's recipe for disassembling "around" an address:

  1. Call FindStartPosition() with that address.
  2. Disassemble from the result for about max * 2 bytes.

MuForce's DISPC output is a disassembly of this kind: DISRANGE (default 32) sets the approximate number of bytes disassembled before and after the fault.

Example: disassemble around an address

#include <exec/types.h>
#include <libraries/disassembler.h>
#include <proto/exec.h>
#include <proto/dos.h>
#include <proto/disassembler.h>

struct Library *DisassemblerBase;

/* SAS/C style register arguments; adapt for your compiler. */
static void __asm __saveds PutProc(register __d0 UBYTE c,
                                   register __a3 UBYTE *buf)
{
    /* Simplest possible sink: print each character with dos.library.
       Not interrupt-safe; use a buffer (see the asm example) if you
       need to call Disassemble() from an exception or interrupt. */
    FPutC(Output(), c);
}

void DumpCode(APTR pc)
{
    struct DisData dd;
    APTR start;

    if (DisassemblerBase = OpenLibrary(DISASSEMBLER_NAME, 40)) {
        start = FindStartPosition(pc, 16, 32);

        dd.ds_From      = start;
        dd.ds_UpTo      = (UBYTE *)pc + 32;
        dd.ds_PC        = pc;
        dd.ds_PutProc   = (void *)PutProc;
        dd.ds_UserData  = NULL;
        dd.ds_UserBase  = NULL;  /* or the near-data base pointer */
        dd.ds_Truncate  = 79;
        dd.ds_reserved  = 0;

        Disassemble(&dd);
        Flush(Output());
        CloseLibrary(DisassemblerBase);
    }
}

The output format matches the disassembly lines in MuForce reports:

01044770 :  31c0 0000                  move.w d0,$0.w
01044774 : *4eae ff7c                  jsr -$84(a6)
01044778 :  2014                       move.l (a4),d0

Fixes recorded in the MuManual ReadMe

The ReadMe history lists these fixes. Older copies of the library disassemble the affected instructions wrongly:

Fixed issue
64-bit arithmetic instructions disassembled incorrectly
lea (offset.L,pc) offset wrong by two bytes
fmovem.x <dynamiclist>,memory disassembled incorrectly
Issues fixed by Christian Sauer in 40.5; garbled output for floating-point constants

Licensing

The header carries the THOR-Software notice: no commercial use, reassembly or modification without written permission, and including the library in commercial software requires written permission and a small fee.

See Also


Sources: NDK 3.2 R4 MuManual/Autodocs/disassembler.doc, MuManual/Include/libraries/disassembler.h, MuManual/Include/libraries/disassembler.i, MuManual/Include/clib/disassembler_protos.h, MuManual/Include/pragmas/disassembler_pragmas.h, MuManual/Include/lvo/disassembler_lvo.i, MuManual/fd/disassembler_lib.fd, MuManual/BMAPs/Disassembler_lib.bmap, MuTools/disassembler.library (version string), MuTools/MuForce.guide, MuManual/ReadMe Updated: 2026-11-13