diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6b1da2b..59d1f5c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,7 +12,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - go-version: [ '1.25.x' ] + go-version: [ '1.27.x' ] steps: - name: Checkout uses: actions/checkout@v4 diff --git a/CHANGELOG.md b/CHANGELOG.md index 65b8d06..b551ad1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,20 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [1.1.0] - 2026-09-03 + +### Changed +- **Internal simplification**: the structured decode types (`Operand`, `Register`, `EffectiveAddress`, …) are now defined once in `internal/decoders` and re-exported from the public package via type aliases, removing ~150 lines of value-copying glue. No API or behavior change. +- Consolidated duplicated instruction decoders (immediate ALU ops, address-register ALU ops, unary EA ops, predecrement/postincrement operands) behind shared helpers. Non-test code shrinks from ~2270 to ~2000 lines. +- **Minimum Go version raised to 1.27.** Adopted the `new(expr)` builtin and `range`-over-int; `DisassembleRange` now preallocates its result slice. +- Removed a stray root `go.yml` that duplicated the real CI workflow. + +## [1.0.3] - 2026-04-03 + +### Fixed +- **PC-relative symbolizer regression**: restored correct PC-relative target resolution. +- CI build fix. + ## [1.0.2] - 2026-04-03 ### Fixed diff --git a/disasm.go b/disasm.go index 94d3cee..c1f9ab1 100644 --- a/disasm.go +++ b/disasm.go @@ -5,6 +5,7 @@ import ( "errors" "fmt" "io" + "strings" "github.com/jenska/m68kdasm/internal/decoders" ) @@ -81,7 +82,9 @@ func DisassembleRange(data []byte, startAddress uint32) ([]Instruction, error) { } func DisassembleRangeWithOptions(data []byte, startAddress uint32, opts DecodeOptions) ([]Instruction, error) { - var instructions []Instruction + // Every instruction is at least one 16-bit word, so len(data)/2 is an upper + // bound on the instruction count. + instructions := make([]Instruction, 0, len(data)/2) offset := 0 for offset < len(data) { @@ -119,27 +122,7 @@ func decodeInstruction(initial []byte, address uint32, reader addressReader, opt opcode := binary.BigEndian.Uint16(data[:2]) decoder := decoders.FindDecoder(opcode) if decoder == nil { - return finalizeInstruction(&decoders.Instruction{ - Address: address, - Opcode: opcode, - Mnemonic: "DC.W", - Operands: fmt.Sprintf("$%04X", opcode), - Size: 2, - Bytes: data[:2], - Metadata: decoders.Metadata{ - Mnemonic: "DC.W", - MnemonicBase: "DC", - SizeSuffix: "W", - Operands: []decoders.Operand{ - { - Text: fmt.Sprintf("$%04X", opcode), - Kind: decoders.OperandKindImmediate, - Immediate: &decoders.ImmediateValue{Value: uint32(opcode), Signed: int32(int16(opcode)), Size: 2}, - }, - }, - ImmediateValues: []decoders.ImmediateValue{{Value: uint32(opcode), Signed: int32(int16(opcode)), Size: 2}}, - }, - }, opts), nil + return finalizeInstruction(decoders.DecodeUnknown(data, address, opcode), opts), nil } for { @@ -210,7 +193,7 @@ func finalizeInstruction(decoderInst *decoders.Instruction, opts DecodeOptions) Size: decoderInst.Size, Bytes: append([]byte(nil), decoderInst.Bytes...), ExtensionWords: append([]uint16(nil), decoderInst.ExtensionWords...), - Metadata: convertMetadata(decoderInst.Metadata), + Metadata: decoderInst.Metadata, } if opts.Symbolizer != nil && len(inst.Metadata.Operands) > 0 { @@ -220,105 +203,12 @@ func finalizeInstruction(decoderInst *decoders.Instruction, opts DecodeOptions) return inst } -func convertMetadata(meta decoders.Metadata) DecodeMetadata { - converted := DecodeMetadata{ - Mnemonic: meta.Mnemonic, - MnemonicBase: meta.MnemonicBase, - SizeSuffix: meta.SizeSuffix, - BranchTarget: cloneUint32Ptr(meta.BranchTarget), - ImmediateValues: make([]ImmediateValue, len(meta.ImmediateValues)), - Operands: make([]Operand, len(meta.Operands)), - } - for i, imm := range meta.ImmediateValues { - converted.ImmediateValues[i] = ImmediateValue{Value: imm.Value, Signed: imm.Signed, Size: imm.Size} - } - for i, operand := range meta.Operands { - converted.Operands[i] = convertOperand(operand) - } - return converted -} - -func convertOperand(operand decoders.Operand) Operand { - converted := Operand{ - Text: operand.Text, - Kind: OperandKind(operand.Kind), - RegisterList: append([]string(nil), operand.RegisterList...), - BranchTarget: cloneUint32Ptr(operand.BranchTarget), - } - if operand.Register != nil { - converted.Register = &Register{ - Kind: RegisterKind(operand.Register.Kind), - Number: operand.Register.Number, - } - } - if operand.Immediate != nil { - converted.Immediate = &ImmediateValue{ - Value: operand.Immediate.Value, - Signed: operand.Immediate.Signed, - Size: operand.Immediate.Size, - } - } - if operand.EffectiveAddress != nil { - ea := &EffectiveAddress{ - Kind: EffectiveAddressKind(operand.EffectiveAddress.Kind), - Mode: operand.EffectiveAddress.Mode, - Register: operand.EffectiveAddress.Register, - Displacement: cloneInt32Ptr(operand.EffectiveAddress.Displacement), - AbsoluteAddress: cloneUint32Ptr(operand.EffectiveAddress.AbsoluteAddress), - ResolvedAddress: cloneUint32Ptr(operand.EffectiveAddress.ResolvedAddress), - } - if operand.EffectiveAddress.Base != nil { - ea.Base = &Register{ - Kind: RegisterKind(operand.EffectiveAddress.Base.Kind), - Number: operand.EffectiveAddress.Base.Number, - } - } - if operand.EffectiveAddress.Immediate != nil { - ea.Immediate = &ImmediateValue{ - Value: operand.EffectiveAddress.Immediate.Value, - Signed: operand.EffectiveAddress.Immediate.Signed, - Size: operand.EffectiveAddress.Immediate.Size, - } - } - if operand.EffectiveAddress.Index != nil { - ea.Index = &IndexRegister{ - Register: Register{ - Kind: RegisterKind(operand.EffectiveAddress.Index.Register.Kind), - Number: operand.EffectiveAddress.Index.Register.Number, - }, - Size: operand.EffectiveAddress.Index.Size, - } - } - converted.EffectiveAddress = ea - } - return converted -} - -func cloneUint32Ptr(v *uint32) *uint32 { - if v == nil { - return nil - } - cloned := *v - return &cloned -} - -func cloneInt32Ptr(v *int32) *int32 { - if v == nil { - return nil - } - cloned := *v - return &cloned -} - func formatOperands(operands []Operand, symbolizer Symbolizer) string { - rendered := make([]string, 0, len(operands)) - for _, operand := range operands { - rendered = append(rendered, formatOperand(operand, symbolizer)) - } - if len(rendered) == 0 { - return "" + rendered := make([]string, len(operands)) + for i, operand := range operands { + rendered[i] = formatOperand(operand, symbolizer) } - return joinOperands(rendered) + return strings.Join(rendered, ", ") } func formatOperand(operand Operand, symbolizer Symbolizer) string { @@ -341,14 +231,3 @@ func formatOperand(operand Operand, symbolizer Symbolizer) string { } return operand.Text } - -func joinOperands(parts []string) string { - if len(parts) == 0 { - return "" - } - out := parts[0] - for _, part := range parts[1:] { - out += ", " + part - } - return out -} diff --git a/go.mod b/go.mod index cf60aae..4942e0f 100644 --- a/go.mod +++ b/go.mod @@ -1,5 +1,5 @@ module github.com/jenska/m68kdasm -go 1.26 +go 1.27 require github.com/jenska/m68kasm v1.3.1 diff --git a/go.yml b/go.yml deleted file mode 100644 index acc2698..0000000 --- a/go.yml +++ /dev/null @@ -1,24 +0,0 @@ -name: Go - -on: - push: - branches: [ "main" ] - pull_request: - branches: [ "main" ] - -jobs: - build: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - - name: Set up Go - uses: actions/setup-go@v4 - with: - go-version: '1.21' - - - name: Build - run: go build -v ./... - - - name: Test - run: go test -v ./... \ No newline at end of file diff --git a/internal/decoders/addressing.go b/internal/decoders/addressing.go index 65000d7..3deca8a 100644 --- a/internal/decoders/addressing.go +++ b/internal/decoders/addressing.go @@ -82,7 +82,7 @@ func decodeAddressingMode(data []byte, mode, reg uint8, operandSize int) (string Mode: mode, Register: reg, Base: &Register{Kind: RegisterKindAddress, Number: reg}, - Displacement: int32Ptr(int32(displacement)), + Displacement: new(int32(displacement)), }), nil case 6: // Address Register Indirect with Index @@ -97,7 +97,7 @@ func decodeAddressingMode(data []byte, mode, reg uint8, operandSize int) (string Mode: mode, Register: reg, Base: &Register{Kind: RegisterKindAddress, Number: reg}, - Displacement: int32Ptr(int32(displacement)), + Displacement: new(int32(displacement)), Index: &IndexRegister{ Register: Register{Kind: parseIndexRegisterKind(indexType), Number: indexReg}, Size: string(indexSize), @@ -118,8 +118,8 @@ func decodeAddressingMode(data []byte, mode, reg uint8, operandSize int) (string Kind: EAKindAbsoluteShort, Mode: mode, Register: reg, - AbsoluteAddress: uint32Ptr(absolute), - ResolvedAddress: uint32Ptr(absolute), + AbsoluteAddress: new(absolute), + ResolvedAddress: new(absolute), }), nil case 1: // Absolute Long Address @@ -132,8 +132,8 @@ func decodeAddressingMode(data []byte, mode, reg uint8, operandSize int) (string Kind: EAKindAbsoluteLong, Mode: mode, Register: reg, - AbsoluteAddress: uint32Ptr(addr), - ResolvedAddress: uint32Ptr(addr), + AbsoluteAddress: new(addr), + ResolvedAddress: new(addr), }), nil case 2: // Program Counter with Displacement @@ -150,7 +150,7 @@ func decodeAddressingMode(data []byte, mode, reg uint8, operandSize int) (string Mode: mode, Register: reg, Base: &Register{Kind: RegisterKindPC}, - Displacement: int32Ptr(int32(displacement)), + Displacement: new(int32(displacement)), }), nil case 3: // Program Counter with Index @@ -165,7 +165,7 @@ func decodeAddressingMode(data []byte, mode, reg uint8, operandSize int) (string Mode: mode, Register: reg, Base: &Register{Kind: RegisterKindPC}, - Displacement: int32Ptr(int32(displacement)), + Displacement: new(int32(displacement)), Index: &IndexRegister{ Register: Register{Kind: parseIndexRegisterKind(indexType), Number: indexReg}, Size: string(indexSize), @@ -186,9 +186,7 @@ func decodeAddressingMode(data []byte, mode, reg uint8, operandSize int) (string Register: reg, Immediate: immediatePtr(value, operandSize), }), nil - case 1: - fallthrough - case 2: + case 1, 2: if err := requireLength(data, 2, "immediate"); err != nil { return "", 0, Operand{}, err } @@ -249,14 +247,13 @@ func formatImmediateForMOVEQ(value int32) string { // decodeIndexWord extracts index register, type, size, and displacement from index word func decodeIndexWord(indexWord uint16) (indexType string, indexReg, indexSize uint8, displacement int8) { indexType = "D" - if (indexWord>>15)&0x1 == 1 { + if indexWord&0x8000 != 0 { indexType = "A" } indexReg = uint8((indexWord >> 12) & 0x7) - if (indexWord>>11)&0x1 == 1 { + indexSize = 'W' + if indexWord&0x0800 != 0 { indexSize = 'L' - } else { - indexSize = 'W' } displacement = int8(indexWord & 0xFF) return diff --git a/internal/decoders/arithmetic.go b/internal/decoders/arithmetic.go index 662897d..85a81d7 100644 --- a/internal/decoders/arithmetic.go +++ b/internal/decoders/arithmetic.go @@ -6,7 +6,7 @@ import "fmt" // ADD Format: 1101 ddd ooo sss rrr func decodeADD(data []byte, opcode uint16, inst *Instruction) error { if isAddressRegisterArithmetic(opcode) { - return decodeAddressRegisterArithmetic("ADD", data, opcode, inst) + return decodeAddressRegisterOp("ADD", data, opcode, inst) } return decodeDirectedBinaryOp("ADD", data, opcode, inst) } @@ -15,7 +15,7 @@ func decodeADD(data []byte, opcode uint16, inst *Instruction) error { // SUB Format: 1001 ddd ooo sss rrr func decodeSUB(data []byte, opcode uint16, inst *Instruction) error { if isAddressRegisterArithmetic(opcode) { - return decodeAddressRegisterArithmetic("SUB", data, opcode, inst) + return decodeAddressRegisterOp("SUB", data, opcode, inst) } return decodeDirectedBinaryOp("SUB", data, opcode, inst) } @@ -60,25 +60,3 @@ func isAddressRegisterArithmetic(opcode uint16) bool { opmode := (opcode >> 6) & 0x7 return opmode == 3 || opmode == 7 } - -func decodeAddressRegisterArithmetic(mnemonic string, data []byte, opcode uint16, inst *Instruction) error { - opmode := (opcode >> 6) & 0x7 - dstReg := uint8((opcode >> 9) & 0x7) - srcMode := uint8((opcode >> 3) & 0x7) - srcReg := uint8(opcode & 0x7) - - sizeStr := "W" - sizeBytes := 2 - if opmode == 7 { - sizeStr = "L" - sizeBytes = 4 - } - - srcOperand, offset, srcMeta, err := decodeEAWithSize(data, inst.Address, 2, srcMode, srcReg, sizeBytes) - if err != nil { - return err - } - - setInstruction(data, inst, offset, mnemonic+"A."+sizeStr, fmt.Sprintf("%s, A%d", srcOperand, dstReg), srcMeta, registerOperand(RegisterKindAddress, dstReg)) - return nil -} diff --git a/internal/decoders/bcd.go b/internal/decoders/bcd.go index 4a97d2b..c74f82d 100644 --- a/internal/decoders/bcd.go +++ b/internal/decoders/bcd.go @@ -20,14 +20,8 @@ func decodeBCD(mn string, data []byte, opcode uint16, inst *Instruction) error { } srcText := fmt.Sprintf("-(A%d)", srcReg) dstText := fmt.Sprintf("-(A%d)", dstReg) - setInstruction(data, inst, 2, mn, fmt.Sprintf("%s, %s", srcText, dstText), effectiveAddressOperand(srcText, EffectiveAddress{ - Kind: EAKindPreDecrement, - Base: &Register{Kind: RegisterKindAddress, Number: srcReg}, - Register: srcReg, - }), effectiveAddressOperand(dstText, EffectiveAddress{ - Kind: EAKindPreDecrement, - Base: &Register{Kind: RegisterKindAddress, Number: dstReg}, - Register: dstReg, - })) + setInstruction(data, inst, 2, mn, fmt.Sprintf("%s, %s", srcText, dstText), + addrIndirectOperand(EAKindPreDecrement, srcReg, srcText), + addrIndirectOperand(EAKindPreDecrement, dstReg, dstText)) return nil } diff --git a/internal/decoders/branch.go b/internal/decoders/branch.go index 8c96eba..1029fa9 100644 --- a/internal/decoders/branch.go +++ b/internal/decoders/branch.go @@ -11,37 +11,38 @@ var branchCondNames = [...]string{ } func decodeBxx(data []byte, opcode uint16, inst *Instruction) error { - offset := 2 condition := (opcode >> 8) & 0x0F mnemonic := "?" if condition < uint16(len(branchCondNames)) { mnemonic = branchCondNames[condition] } - displacement := int8(opcode & 0xFF) - switch displacement { + + offset := 2 + var suffix string + var disp int32 + switch d8 := int8(opcode & 0xFF); d8 { case 0: if err := requireLength(data, offset+2, mnemonic+".W displacement"); err != nil { return err } - displacement16 := int16(binary.BigEndian.Uint16(data[offset : offset+2])) + disp = int32(int16(binary.BigEndian.Uint16(data[offset : offset+2]))) offset += 2 - target := uint32(int32(inst.Address) + int32(offset) + int32(displacement16)) - targetText := formatBranchTarget(target) - setInstruction(data, inst, offset, mnemonic+".W", targetText, branchOperand(targetText, target)) + suffix = "W" case -1: if err := requireLength(data, offset+4, mnemonic+".L displacement"); err != nil { return err } - displacement32 := int32(binary.BigEndian.Uint32(data[offset : offset+4])) + disp = int32(binary.BigEndian.Uint32(data[offset : offset+4])) offset += 4 - target := uint32(int32(inst.Address) + int32(offset) + displacement32) - targetText := formatBranchTarget(target) - setInstruction(data, inst, offset, mnemonic+".L", targetText, branchOperand(targetText, target)) + suffix = "L" default: - target := uint32(int32(inst.Address) + int32(offset) + int32(displacement)) - targetText := formatBranchTarget(target) - setInstruction(data, inst, offset, mnemonic+".S", targetText, branchOperand(targetText, target)) + disp = int32(d8) + suffix = "S" } + + target := uint32(int32(inst.Address) + int32(offset) + disp) + targetText := formatBranchTarget(target) + setInstruction(data, inst, offset, mnemonic+"."+suffix, targetText, branchOperand(targetText, target)) return nil } @@ -53,23 +54,9 @@ func formatBranchTarget(target uint32) string { } func decodeJSR(data []byte, opcode uint16, inst *Instruction) error { - mode := uint8((opcode >> 3) & 0x7) - reg := uint8(opcode & 0x7) - operand, offset, meta, err := decodeEA(data, inst.Address, 2, mode, reg) - if err != nil { - return err - } - setInstruction(data, inst, offset, "JSR", operand, meta) - return nil + return decodeUnaryEA("JSR", data, opcode, inst) } func decodeJMP(data []byte, opcode uint16, inst *Instruction) error { - mode := uint8((opcode >> 3) & 0x7) - reg := uint8(opcode & 0x7) - operand, offset, meta, err := decodeEA(data, inst.Address, 2, mode, reg) - if err != nil { - return err - } - setInstruction(data, inst, offset, "JMP", operand, meta) - return nil + return decodeUnaryEA("JMP", data, opcode, inst) } diff --git a/internal/decoders/common.go b/internal/decoders/common.go index d12f397..df9e4ca 100644 --- a/internal/decoders/common.go +++ b/internal/decoders/common.go @@ -7,6 +7,15 @@ import ( var sizeNames = [...]string{"B", "W", "L", "?"} +// DecodeUnknown builds the "DC.W" pseudo-instruction used when no decoder +// matches the opcode. +func DecodeUnknown(data []byte, address uint32, opcode uint16) *Instruction { + text := fmt.Sprintf("$%04X", opcode) + inst := &Instruction{Address: address, Opcode: opcode} + setInstruction(data, inst, 2, "DC.W", text, immediateOperand(text, uint32(opcode), 2)) + return inst +} + // decodeNOP - No Operation (exact opcode: 0x4E71) func decodeNOP(data []byte, opcode uint16, inst *Instruction) error { setInstruction(data, inst, 2, "NOP", "") @@ -19,23 +28,10 @@ func decodeRTS(data []byte, opcode uint16, inst *Instruction) error { return nil } -// getSizeString converts 68000 size field (bits 6-7) to string +// getSizeString converts a 68000 size field (bits 6-7) to string. // Maps: 0=B (byte), 1=W (word), 2=L (long), 3=? (undefined) func getSizeString(size uint16) string { - if int(size) < len(sizeNames) { - return sizeNames[size] - } - return "?" -} - -// buildDirectedOperands creates reversed operands based on direction bit -// direction == 0: src, dstReg format -// direction != 0: dstReg, src format -func buildDirectedOperands(direction uint16, src string, dstReg uint8) string { - if direction == 0 { - return fmt.Sprintf("%s, D%d", src, dstReg) - } - return fmt.Sprintf("D%d, %s", dstReg, src) + return sizeNames[size&0x3] } // setInstructionSize sets the instruction's Size and Bytes fields @@ -54,6 +50,18 @@ func setInstruction(data []byte, inst *Instruction, size int, mnemonic, operands populateMetadata(inst, mnemonic, structuredOperands) } +// decodeUnaryEA decodes a single effective-address operand (JSR, JMP, PEA …). +func decodeUnaryEA(mnemonic string, data []byte, opcode uint16, inst *Instruction) error { + mode := uint8((opcode >> 3) & 0x7) + reg := uint8(opcode & 0x7) + operand, offset, meta, err := decodeEA(data, inst.Address, 2, mode, reg) + if err != nil { + return err + } + setInstruction(data, inst, offset, mnemonic, operand, meta) + return nil +} + func decodeEA(data []byte, address uint32, offset int, mode, reg uint8) (string, int, Operand, error) { return decodeEAWithSize(data, address, offset, mode, reg, 2) } @@ -85,11 +93,33 @@ func decodeDirectedBinaryOp(mnemonic string, data []byte, opcode uint16, inst *I } dstMeta := registerOperand(RegisterKindData, dstReg) - if direction == 0 { - setInstruction(data, inst, offset, mnemonic+"."+sizeStr, buildDirectedOperands(direction, srcOperand, dstReg), srcMeta, dstMeta) - return nil + dstText := fmt.Sprintf("D%d", dstReg) + text, first, second := srcOperand+", "+dstText, srcMeta, dstMeta + if direction != 0 { + text, first, second = dstText+", "+srcOperand, dstMeta, srcMeta } - setInstruction(data, inst, offset, mnemonic+"."+sizeStr, buildDirectedOperands(direction, srcOperand, dstReg), dstMeta, srcMeta) + setInstruction(data, inst, offset, mnemonic+"."+sizeStr, text, first, second) + return nil +} + +// decodeAddressRegisterOp decodes the "A" form where the destination +// is an address register and the size comes from the op-mode field +// (op-mode 3 = word, op-mode 7 = long). +func decodeAddressRegisterOp(mnemonic string, data []byte, opcode uint16, inst *Instruction) error { + dstReg := uint8((opcode >> 9) & 0x7) + srcMode := uint8((opcode >> 3) & 0x7) + srcReg := uint8(opcode & 0x7) + + sizeStr, sizeBytes := "W", 2 + if (opcode>>6)&0x7 == 7 { + sizeStr, sizeBytes = "L", 4 + } + + srcOperand, offset, srcMeta, err := decodeEAWithSize(data, inst.Address, 2, srcMode, srcReg, sizeBytes) + if err != nil { + return err + } + setInstruction(data, inst, offset, mnemonic+"A."+sizeStr, fmt.Sprintf("%s, A%d", srcOperand, dstReg), srcMeta, registerOperand(RegisterKindAddress, dstReg)) return nil } @@ -192,6 +222,16 @@ func immediateOperand(text string, value uint32, size int) Operand { } } +// addrIndirectOperand builds an address-register indirect operand (optionally +// with pre-decrement or post-increment) as used by ABCD/SBCD and CMPM. +func addrIndirectOperand(kind EffectiveAddressKind, reg uint8, text string) Operand { + return effectiveAddressOperand(text, EffectiveAddress{ + Kind: kind, + Base: &Register{Kind: RegisterKindAddress, Number: reg}, + Register: reg, + }) +} + func effectiveAddressOperand(text string, ea EffectiveAddress) Operand { return Operand{ Text: text, @@ -212,18 +252,10 @@ func branchOperand(text string, target uint32) Operand { return Operand{ Text: text, Kind: OperandKindBranchTarget, - BranchTarget: uint32Ptr(target), + BranchTarget: new(target), } } -func uint32Ptr(v uint32) *uint32 { - return &v -} - -func int32Ptr(v int32) *int32 { - return &v -} - func immediatePtr(value uint32, size int) *ImmediateValue { return &ImmediateValue{ Value: value, @@ -255,7 +287,7 @@ func resolveEffectiveAddress(address uint32, nextOffset int, operand Operand) Op case EAKindPCDisplacement, EAKindPCIndex: if ea.Displacement != nil { target := uint32(int32(address) + int32(nextOffset) + *ea.Displacement) - ea.ResolvedAddress = uint32Ptr(target) + ea.ResolvedAddress = new(target) } } diff --git a/internal/decoders/compare.go b/internal/decoders/compare.go index 9e5c12e..e6d7e5a 100644 --- a/internal/decoders/compare.go +++ b/internal/decoders/compare.go @@ -8,7 +8,7 @@ func decodeCMP(data []byte, opcode uint16, inst *Instruction) error { return decodeCMPM(data, opcode, inst) } if opmode == 3 || opmode == 7 { - return decodeCMPA(data, opcode, inst) + return decodeAddressRegisterOp("CMP", data, opcode, inst) } if opmode >= 4 && opmode <= 6 { return decodeEOR(data, opcode, inst) @@ -31,28 +31,6 @@ func decodeCMP(data []byte, opcode uint16, inst *Instruction) error { return nil } -func decodeCMPA(data []byte, opcode uint16, inst *Instruction) error { - opmode := (opcode >> 6) & 0x7 - dstReg := uint8((opcode >> 9) & 0x7) - srcMode := uint8((opcode >> 3) & 0x7) - srcReg := uint8(opcode & 0x7) - - sizeStr := "W" - sizeBytes := 2 - if opmode == 7 { - sizeStr = "L" - sizeBytes = 4 - } - - srcStr, offset, srcMeta, err := decodeEAWithSize(data, inst.Address, 2, srcMode, srcReg, sizeBytes) - if err != nil { - return err - } - - setInstruction(data, inst, offset, "CMPA."+sizeStr, fmt.Sprintf("%s, A%d", srcStr, dstReg), srcMeta, registerOperand(RegisterKindAddress, dstReg)) - return nil -} - func decodeCMPM(data []byte, opcode uint16, inst *Instruction) error { sizeBits := (opcode >> 6) & 0x3 sizeStr := getSizeString(sizeBits) @@ -60,34 +38,12 @@ func decodeCMPM(data []byte, opcode uint16, inst *Instruction) error { dstReg := uint8((opcode >> 9) & 0x7) srcText := fmt.Sprintf("(A%d)+", srcReg) dstText := fmt.Sprintf("(A%d)+", dstReg) - setInstruction(data, inst, 2, "CMPM."+sizeStr, fmt.Sprintf("%s, %s", srcText, dstText), effectiveAddressOperand(srcText, EffectiveAddress{ - Kind: EAKindPostIncrement, - Base: &Register{Kind: RegisterKindAddress, Number: srcReg}, - Register: srcReg, - }), effectiveAddressOperand(dstText, EffectiveAddress{ - Kind: EAKindPostIncrement, - Base: &Register{Kind: RegisterKindAddress, Number: dstReg}, - Register: dstReg, - })) + setInstruction(data, inst, 2, "CMPM."+sizeStr, fmt.Sprintf("%s, %s", srcText, dstText), + addrIndirectOperand(EAKindPostIncrement, srcReg, srcText), + addrIndirectOperand(EAKindPostIncrement, dstReg, dstText)) return nil } func decodeCMPI(data []byte, opcode uint16, inst *Instruction) error { - sizeStr, immSize, err := immediateSpec((opcode>>6)&0x3, false, "CMPI") - if err != nil { - return err - } - dstMode := uint8((opcode >> 3) & 0x7) - dstReg := uint8(opcode & 0x7) - immediate, offset, err := readImmediate(data, 2, immSize, "CMPI") - if err != nil { - return err - } - dstOperand, offset, dstMeta, err := decodeEA(data, inst.Address, offset, dstMode, dstReg) - if err != nil { - return err - } - immText := fmt.Sprintf("#%s", formatImmediate(immediate, immSize)) - setInstruction(data, inst, offset, "CMPI."+sizeStr, fmt.Sprintf("%s, %s", immText, dstOperand), immediateOperand(immText, immediate, immSize), dstMeta) - return nil + return decodeImmediateBinaryOp("CMPI", data, opcode, inst, false) } diff --git a/internal/decoders/logical.go b/internal/decoders/logical.go index 3912aa8..b3de19e 100644 --- a/internal/decoders/logical.go +++ b/internal/decoders/logical.go @@ -1,52 +1,25 @@ package decoders -import "fmt" - func decodeAND(data []byte, opcode uint16, inst *Instruction) error { - return decodeLogical("AND", data, opcode, inst) + return decodeDirectedBinaryOp("AND", data, opcode, inst) } func decodeOR(data []byte, opcode uint16, inst *Instruction) error { - return decodeLogical("OR", data, opcode, inst) + return decodeDirectedBinaryOp("OR", data, opcode, inst) } func decodeEOR(data []byte, opcode uint16, inst *Instruction) error { - return decodeLogical("EOR", data, opcode, inst) + return decodeDirectedBinaryOp("EOR", data, opcode, inst) } func decodeANDI(data []byte, opcode uint16, inst *Instruction) error { - return decodeLogicalI("ANDI", data, opcode, inst) + return decodeImmediateBinaryOp("ANDI", data, opcode, inst, false) } func decodeORI(data []byte, opcode uint16, inst *Instruction) error { - return decodeLogicalI("ORI", data, opcode, inst) + return decodeImmediateBinaryOp("ORI", data, opcode, inst, false) } func decodeEORI(data []byte, opcode uint16, inst *Instruction) error { - return decodeLogicalI("EORI", data, opcode, inst) -} - -func decodeLogicalI(mn string, data []byte, opcode uint16, inst *Instruction) error { - sizeStr, immSize, err := immediateSpec((opcode>>6)&0x3, false, mn) - if err != nil { - return err - } - dstMode := uint8((opcode >> 3) & 0x7) - dstReg := uint8(opcode & 0x7) - - immediate, offset, err := readImmediate(data, 2, immSize, mn) - if err != nil { - return err - } - dstOperand, offset, dstMeta, err := decodeEA(data, inst.Address, offset, dstMode, dstReg) - if err != nil { - return err - } - immText := fmt.Sprintf("#%s", formatImmediate(immediate, immSize)) - setInstruction(data, inst, offset, mn+"."+sizeStr, fmt.Sprintf("%s, %s", immText, dstOperand), immediateOperand(immText, immediate, immSize), dstMeta) - return nil -} - -func decodeLogical(mn string, data []byte, opcode uint16, inst *Instruction) error { - return decodeDirectedBinaryOp(mn, data, opcode, inst) + return decodeImmediateBinaryOp("EORI", data, opcode, inst, false) } diff --git a/internal/decoders/shift.go b/internal/decoders/shift.go index 91501f4..461af08 100644 --- a/internal/decoders/shift.go +++ b/internal/decoders/shift.go @@ -27,42 +27,40 @@ func getDirectionStr(direction uint16) string { } func decodeShiftRotate(data []byte, opcode uint16, inst *Instruction) error { - direction := (opcode >> 8) & 0x1 - size := (opcode >> 6) & 0x3 - reg := uint8(opcode & 0x7) - sizeStr := []string{"B", "W", "L", "?"}[size] + dirStr := getDirectionStr((opcode >> 8) & 0x1) rotType := (opcode >> 9) & 0x7 - countIsReg := (opcode >> 5) & 0x1 - dirStr := getDirectionStr(direction) if rotType <= 3 { - // Register shift: extract shift count - mnemonicBase := getMnemonicBase(uint16(rotType)) + // Register form: shift a data register by an immediate or register count. + reg := uint8(opcode & 0x7) + mnemonic := fmt.Sprintf("%s%s.%s", getMnemonicBase(rotType), dirStr, getSizeString((opcode>>6)&0x3)) + var countStr string - if countIsReg == 0 { + var countMeta Operand + if (opcode>>5)&0x1 == 0 { count := (opcode >> 9) & 0x7 if count == 0 { count = 8 } countStr = fmt.Sprintf("#%d", count) - setInstruction(data, inst, 2, fmt.Sprintf("%s%s.%s", mnemonicBase, dirStr, sizeStr), fmt.Sprintf("%s, D%d", countStr, reg), immediateOperand(countStr, uint32(count), 1), registerOperand(RegisterKindData, reg)) + countMeta = immediateOperand(countStr, uint32(count), 1) } else { - countReg := (opcode >> 9) & 0x7 + countReg := uint8((opcode >> 9) & 0x7) countStr = fmt.Sprintf("D%d", countReg) - setInstruction(data, inst, 2, fmt.Sprintf("%s%s.%s", mnemonicBase, dirStr, sizeStr), fmt.Sprintf("%s, D%d", countStr, reg), registerOperand(RegisterKindData, uint8(countReg)), registerOperand(RegisterKindData, reg)) - } - } else { - // Memory shift: extract addressing mode - memMode := uint8((opcode >> 3) & 0x7) - memReg := uint8(opcode & 0x7) - memShiftType := (opcode >> 6) & 0x3 - mnemonicBase := getMnemonicBase(memShiftType) - mnemonic := fmt.Sprintf("%s%s.W", mnemonicBase, dirStr) - operand, extraWords, meta, err := decodeAddressingMode(data[2:], memMode, memReg, 2) - if err != nil { - return err + countMeta = registerOperand(RegisterKindData, countReg) } - setInstruction(data, inst, 2+extraWords*2, mnemonic, operand, meta) + setInstruction(data, inst, 2, mnemonic, fmt.Sprintf("%s, D%d", countStr, reg), countMeta, registerOperand(RegisterKindData, reg)) + return nil + } + + // Memory form: shift a memory word by one. + memMode := uint8((opcode >> 3) & 0x7) + memReg := uint8(opcode & 0x7) + mnemonic := fmt.Sprintf("%s%s.W", getMnemonicBase((opcode>>6)&0x3), dirStr) + operand, extraWords, meta, err := decodeAddressingMode(data[2:], memMode, memReg, 2) + if err != nil { + return err } + setInstruction(data, inst, 2+extraWords*2, mnemonic, operand, meta) return nil } diff --git a/internal/decoders/special.go b/internal/decoders/special.go index b7f28a9..43120cb 100644 --- a/internal/decoders/special.go +++ b/internal/decoders/special.go @@ -18,14 +18,7 @@ func decodeLEA(data []byte, opcode uint16, inst *Instruction) error { } func decodePEA(data []byte, opcode uint16, inst *Instruction) error { - mode := uint8((opcode >> 3) & 0x7) - reg := uint8(opcode & 0x7) - operand, offset, meta, err := decodeEA(data, inst.Address, 2, mode, reg) - if err != nil { - return err - } - setInstruction(data, inst, offset, "PEA", operand, meta) - return nil + return decodeUnaryEA("PEA", data, opcode, inst) } func decodeSWAP(data []byte, opcode uint16, inst *Instruction) error { @@ -57,29 +50,24 @@ func decodeTRAPV(data []byte, opcode uint16, inst *Instruction) error { return nil } +// formatRegisterList expands a MOVEM register-list mask. When reverse is set the +// mask is read most-significant-bit first (the -(An) predecrement encoding). func formatRegisterList(regListMask uint16, reverse bool) (string, []string) { - registers := []string{} - if reverse { - for i := 0; i < 8; i++ { - if regListMask&(1<