m68kdasm

package module
v1.0.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 28, 2026 License: MIT Imports: 6 Imported by: 0

README

m68kdasm

A Go disassembler for the Motorola 68000 CPU.

m68kdasm translates big-endian m68k machine code into readable assembly and structured decode metadata. It is intended for emulators, debuggers, trace tools, and binary-analysis workflows that need more than just formatted text.

Features

  • Fast opcode dispatch using a hierarchical jump table.
  • Broad 68000 instruction coverage including branches, arithmetic, logic, shifts, BCD, and control flow.
  • Full 68000 addressing-mode decoding, including PC-relative and immediate forms.
  • Exact decoded instruction length via Instruction.Size.
  • Decoded extension words via Instruction.ExtensionWords.
  • Structured metadata for mnemonic, operands, branch targets, immediates, and effective-address kinds.
  • Slice, io.ReaderAt, and callback-based decode entry points.
  • Precise partial-decode errors that report missing-byte counts.
  • Optional symbol formatting hooks for resolved addresses.
  • ELF helpers for disassembling 68000 ELF binaries.

Install

go get github.com/jenska/m68kdasm

API Overview

Single-instruction decode:

  • Decode(data []byte, address uint32)
  • DecodeWithOptions(data []byte, address uint32, opts DecodeOptions)
  • DecodeReaderAt(reader io.ReaderAt, address uint32)
  • DecodeReaderAtWithOptions(reader io.ReaderAt, address uint32, opts DecodeOptions)
  • DecodeFunc(read ReadFunc, address uint32)
  • DecodeFuncWithOptions(read ReadFunc, address uint32, opts DecodeOptions)

Sequential decode:

  • DisassembleRange(data []byte, startAddress uint32)
  • DisassembleRangeWithOptions(data []byte, startAddress uint32, opts DecodeOptions)

Quick Start

package main

import (
	"fmt"
	"log"

	"github.com/jenska/m68kdasm"
)

func main() {
	code := []byte{
		0x20, 0x7C, 0x00, 0x00, 0x21, 0x40, // MOVEA.L #$00002140, A0
		0x4E, 0x75, // RTS
	}

	instrs, err := m68kdasm.DisassembleRange(code, 0x1000)
	if err != nil {
		log.Fatal(err)
	}

	for _, inst := range instrs {
		fmt.Printf("%08X  %-24s size=%d ext=%v\n",
			inst.Address,
			inst.Assembly(),
			inst.Size,
			inst.ExtensionWords,
		)
	}
}

Example output:

00001000  MOVEA.L #$00002140, A0  size=6 ext=[0 8512]
00001006  RTS                      size=2 ext=[]

Structured Metadata

Each decoded instruction includes both rendered text and structured fields:

inst, err := m68kdasm.Decode([]byte{
	0x20, 0x7C, 0x00, 0x00, 0x21, 0x40, // MOVEA.L #$00002140, A0
}, 0x2000)
if err != nil {
	log.Fatal(err)
}

fmt.Println(inst.Mnemonic)              // MOVEA.L
fmt.Println(inst.Operands)              // #$00002140, A0
fmt.Println(inst.Size)                  // 6
fmt.Println(inst.ExtensionWords)        // [0 8512]
fmt.Println(inst.Metadata.MnemonicBase) // MOVEA
fmt.Println(inst.Metadata.SizeSuffix)   // L

src := inst.Metadata.Operands[0]
fmt.Println(src.Kind)                           // effective_address
fmt.Println(src.EffectiveAddress.Kind)          // immediate
fmt.Println(src.EffectiveAddress.Immediate.Value) // 8512

dst := inst.Metadata.Operands[1]
fmt.Println(dst.Kind)               // register
fmt.Println(dst.Register.Kind)      // address
fmt.Println(dst.Register.Number)    // 0

Useful metadata fields:

  • Instruction.Size: exact decoded byte length.
  • Instruction.Bytes: exact bytes consumed by the instruction.
  • Instruction.ExtensionWords: decoded words after the opcode word.
  • Instruction.Metadata.BranchTarget: resolved branch target when applicable.
  • Instruction.Metadata.ImmediateValues: immediate operands collected in structured form.
  • Instruction.Metadata.Operands: per-operand metadata, including effective-address details.

Streaming Decode

If your emulator or debugger fetches bytes from a bus instead of a prebuilt slice, you can decode directly from an io.ReaderAt or callback.

Using io.ReaderAt:

reader := bytes.NewReader([]byte{
	0x4E, 0xB9, 0x00, 0x00, 0x12, 0x34, // JSR $00001234
})

inst, err := m68kdasm.DecodeReaderAt(reader, 0x1000)
if err != nil {
	log.Fatal(err)
}

fmt.Println(inst.Assembly()) // JSR $00001234

Using a callback:

mem := []byte{0x67, 0x08} // BEQ.S $000A

inst, err := m68kdasm.DecodeFunc(func(address uint32, p []byte) (int, error) {
	if int(address) >= len(mem) {
		return 0, io.EOF
	}
	n := copy(p, mem[address:])
	if n < len(p) {
		return n, io.EOF
	}
	return n, nil
}, 0)
if err != nil {
	log.Fatal(err)
}

fmt.Println(inst.Assembly()) // BEQ.S $000A

Symbolized Output

You can keep raw metadata while rendering resolved addresses as symbols:

inst, err := m68kdasm.DecodeWithOptions([]byte{
	0x4E, 0xB9, 0x00, 0x00, 0x12, 0x34, // JSR $00001234
}, 0, m68kdasm.DecodeOptions{
	Symbolizer: m68kdasm.SymbolizeFunc(func(address uint32) (string, bool) {
		if address == 0x1234 {
			return "_bios_init", true
		}
		return "", false
	}),
})
if err != nil {
	log.Fatal(err)
}

fmt.Println(inst.Assembly())                 // JSR _bios_init
fmt.Println(inst.Metadata.Operands[0].Text)  // $00001234

This is useful for:

  • symbolized trace logs
  • debugger disassembly views
  • breakpoint or stop-condition logic based on structured targets

Partial Decode Errors

Truncated fetches return *PartialDecodeError with the missing-byte count and context:

_, err := m68kdasm.Decode([]byte{0x4E, 0x72}, 0x2000) // STOP without immediate word
if err != nil {
	var partial *m68kdasm.PartialDecodeError
	if errors.As(err, &partial) {
		fmt.Println(partial.Missing) // 2
		fmt.Println(partial.Context) // STOP immediate
	}
}

This is especially handy for trace logs and emulator diagnostics where you want to distinguish a broken fetch stream from an unknown opcode.

ELF Disassembly

Disassemble sections from a Motorola 68000 ELF binary:

package main

import (
	"fmt"
	"log"

	"github.com/jenska/m68kdasm"
)

func main() {
	elf, err := m68kdasm.OpenELF("program.elf")
	if err != nil {
		log.Fatal(err)
	}
	defer elf.Close()

	instrs, err := elf.DisassembleSection(".text")
	if err != nil {
		log.Fatal(err)
	}

	for _, inst := range instrs {
		fmt.Println(inst.String())
	}
}

Building And Testing

make build
make test
make fmt

Tests include assembler round trips, decoder dispatch parity, streaming decode coverage, metadata checks, symbolized rendering, and partial-error behavior.

Architecture

The decoder uses a two-level dispatch mechanism:

  1. A top-level jump table partitions the opcode space by high nibble.
  2. Per-region pattern tables apply masks in precedence order to select the final decoder.

Opcode masks and values live in internal/decoders/types.go, which keeps the decoder table explicit and easy to extend.

License

This project is licensed under the MIT License. See LICENSE for details.

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type DecodeMetadata added in v1.0.1

type DecodeMetadata struct {
	Mnemonic        string
	MnemonicBase    string
	SizeSuffix      string
	Operands        []Operand
	BranchTarget    *uint32
	ImmediateValues []ImmediateValue
}

type DecodeOptions added in v1.0.1

type DecodeOptions struct {
	Symbolizer Symbolizer
}

type ELFDisassembler

type ELFDisassembler struct {
	// contains filtered or unexported fields
}

ELFDisassembler holds an ELF file and provides disassembly functions

func OpenELF

func OpenELF(filePath string) (*ELFDisassembler, error)

OpenELF opens an ELF file and returns an ELFDisassembler

func (*ELFDisassembler) Close

func (ed *ELFDisassembler) Close() error

Close closes the underlying ELF file

func (*ELFDisassembler) DisassembleAllExecutableSections

func (ed *ELFDisassembler) DisassembleAllExecutableSections() (map[string][]Instruction, error)

DisassembleAllExecutableSections disassembles all executable sections (SHF_EXECINSTR flag) Returns a map of section name → instructions

func (*ELFDisassembler) DisassembleSection

func (ed *ELFDisassembler) DisassembleSection(sectionName string) ([]Instruction, error)

DisassembleSection disassembles a named ELF section by name (e.g., ".text") Returns instructions with addresses from the section's VA (virtual address)

func (*ELFDisassembler) ListSections

func (ed *ELFDisassembler) ListSections() []SectionInfo

ListSections returns information about all loadable sections in the ELF file.

type EffectiveAddress added in v1.0.1

type EffectiveAddress struct {
	Kind            EffectiveAddressKind
	Mode            uint8
	Register        uint8
	Base            *Register
	Displacement    *int32
	AbsoluteAddress *uint32
	ResolvedAddress *uint32
	Immediate       *ImmediateValue
	Index           *IndexRegister
}

type EffectiveAddressKind added in v1.0.1

type EffectiveAddressKind string
const (
	EAKindDataRegisterDirect    EffectiveAddressKind = "data_register_direct"
	EAKindAddressRegisterDirect EffectiveAddressKind = "address_register_direct"
	EAKindAddressIndirect       EffectiveAddressKind = "address_indirect"
	EAKindPostIncrement         EffectiveAddressKind = "post_increment"
	EAKindPreDecrement          EffectiveAddressKind = "pre_decrement"
	EAKindDisplacement          EffectiveAddressKind = "displacement"
	EAKindIndex                 EffectiveAddressKind = "index"
	EAKindAbsoluteShort         EffectiveAddressKind = "absolute_short"
	EAKindAbsoluteLong          EffectiveAddressKind = "absolute_long"
	EAKindPCDisplacement        EffectiveAddressKind = "pc_displacement"
	EAKindPCIndex               EffectiveAddressKind = "pc_index"
	EAKindImmediate             EffectiveAddressKind = "immediate"
)

type ImmediateValue added in v1.0.1

type ImmediateValue struct {
	Value  uint32
	Signed int32
	Size   uint8
}

type IndexRegister added in v1.0.1

type IndexRegister struct {
	Register Register
	Size     string
}

type Instruction

type Instruction struct {
	Address        uint32
	Opcode         uint16
	Mnemonic       string
	Operands       string
	Size           uint32 // Größe der Instruktion in Bytes (2, 4, 6, etc.)
	Bytes          []byte // Die Rohdaten der Instruktion
	ExtensionWords []uint16
	Metadata       DecodeMetadata
}

Instruction repräsentiert eine einzelne assemblierte Instruktion.

func Decode

func Decode(data []byte, address uint32) (*Instruction, error)

Decode liest eine einzelne Instruktion an der gegebenen Adresse aus dem Byte-Slice.

func DecodeFunc added in v1.0.1

func DecodeFunc(read ReadFunc, address uint32) (*Instruction, error)

func DecodeFuncWithOptions added in v1.0.1

func DecodeFuncWithOptions(read ReadFunc, address uint32, opts DecodeOptions) (*Instruction, error)

func DecodeReaderAt added in v1.0.1

func DecodeReaderAt(reader io.ReaderAt, address uint32) (*Instruction, error)

func DecodeReaderAtWithOptions added in v1.0.1

func DecodeReaderAtWithOptions(reader io.ReaderAt, address uint32, opts DecodeOptions) (*Instruction, error)

func DecodeWithOptions added in v1.0.1

func DecodeWithOptions(data []byte, address uint32, opts DecodeOptions) (*Instruction, error)

func DisassembleRange

func DisassembleRange(data []byte, startAddress uint32) ([]Instruction, error)

DisassembleRange disassembliert einen Speicherbereich sequenziell.

func DisassembleRangeWithOptions added in v1.0.1

func DisassembleRangeWithOptions(data []byte, startAddress uint32, opts DecodeOptions) ([]Instruction, error)

func (Instruction) Assembly

func (i Instruction) Assembly() string

Assembly liefert den reinen Assembler-Code (Mnemonic + Operanden).

func (Instruction) String

func (i Instruction) String() string

String liefert eine lesbare Repräsentation der Instruktion (z.B. für CLI-Output).

type Operand added in v1.0.1

type Operand struct {
	Text             string
	Kind             OperandKind
	Register         *Register
	Immediate        *ImmediateValue
	EffectiveAddress *EffectiveAddress
	RegisterList     []string
	BranchTarget     *uint32
}

type OperandKind added in v1.0.1

type OperandKind string
const (
	OperandKindRegister      OperandKind = "register"
	OperandKindImmediate     OperandKind = "immediate"
	OperandKindEffectiveAddr OperandKind = "effective_address"
	OperandKindRegisterList  OperandKind = "register_list"
	OperandKindBranchTarget  OperandKind = "branch_target"
)

type PartialDecodeError added in v1.0.1

type PartialDecodeError struct {
	Address uint32
	Have    int
	Missing int
	Context string
	Cause   error
}

func (*PartialDecodeError) Error added in v1.0.1

func (e *PartialDecodeError) Error() string

type ReadFunc added in v1.0.1

type ReadFunc func(address uint32, p []byte) (int, error)

func (ReadFunc) ReadAtAddress added in v1.0.1

func (f ReadFunc) ReadAtAddress(address uint32, p []byte) (int, error)

type Register added in v1.0.1

type Register struct {
	Kind   RegisterKind
	Number uint8
}

type RegisterKind added in v1.0.1

type RegisterKind string
const (
	RegisterKindData    RegisterKind = "data"
	RegisterKindAddress RegisterKind = "address"
	RegisterKindPC      RegisterKind = "pc"
)

type SectionInfo

type SectionInfo struct {
	Name   string // Section name (e.g., ".text")
	Addr   uint64 // Virtual address
	Offset uint64 // File offset
	Size   uint64 // Size in bytes
	Flags  uint32 // Raw section flags
	IsExec bool   // True if executable (SHF_EXECINSTR)
}

SectionInfo describes a section in an ELF file

type SymbolizeFunc added in v1.0.1

type SymbolizeFunc func(address uint32) (string, bool)

func (SymbolizeFunc) Symbolize added in v1.0.1

func (f SymbolizeFunc) Symbolize(address uint32) (string, bool)

type Symbolizer added in v1.0.1

type Symbolizer interface {
	Symbolize(address uint32) (string, bool)
}

Directories

Path Synopsis
internal

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL