mirror of
https://github.com/42wim/matterbridge.git
synced 2024-11-22 02:32:02 -08:00
216 lines
5.2 KiB
Go
216 lines
5.2 KiB
Go
// Copyright 2014 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package armasm
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/binary"
|
|
"fmt"
|
|
"io"
|
|
"strings"
|
|
)
|
|
|
|
// GoSyntax returns the Go assembler syntax for the instruction.
|
|
// The syntax was originally defined by Plan 9.
|
|
// The pc is the program counter of the instruction, used for expanding
|
|
// PC-relative addresses into absolute ones.
|
|
// The symname function queries the symbol table for the program
|
|
// being disassembled. Given a target address it returns the name and base
|
|
// address of the symbol containing the target, if any; otherwise it returns "", 0.
|
|
// The reader r should read from the text segment using text addresses
|
|
// as offsets; it is used to display pc-relative loads as constant loads.
|
|
func GoSyntax(inst Inst, pc uint64, symname func(uint64) (string, uint64), text io.ReaderAt) string {
|
|
if symname == nil {
|
|
symname = func(uint64) (string, uint64) { return "", 0 }
|
|
}
|
|
|
|
var args []string
|
|
for _, a := range inst.Args {
|
|
if a == nil {
|
|
break
|
|
}
|
|
args = append(args, plan9Arg(&inst, pc, symname, a))
|
|
}
|
|
|
|
op := inst.Op.String()
|
|
|
|
switch inst.Op &^ 15 {
|
|
case LDR_EQ, LDRB_EQ, LDRH_EQ:
|
|
// Check for RET
|
|
reg, _ := inst.Args[0].(Reg)
|
|
mem, _ := inst.Args[1].(Mem)
|
|
if inst.Op&^15 == LDR_EQ && reg == R15 && mem.Base == SP && mem.Sign == 0 && mem.Mode == AddrPostIndex {
|
|
return fmt.Sprintf("RET%s #%d", op[3:], mem.Offset)
|
|
}
|
|
|
|
// Check for PC-relative load.
|
|
if mem.Base == PC && mem.Sign == 0 && mem.Mode == AddrOffset && text != nil {
|
|
addr := uint32(pc) + 8 + uint32(mem.Offset)
|
|
buf := make([]byte, 4)
|
|
switch inst.Op &^ 15 {
|
|
case LDRB_EQ:
|
|
if _, err := text.ReadAt(buf[:1], int64(addr)); err != nil {
|
|
break
|
|
}
|
|
args[1] = fmt.Sprintf("$%#x", buf[0])
|
|
|
|
case LDRH_EQ:
|
|
if _, err := text.ReadAt(buf[:2], int64(addr)); err != nil {
|
|
break
|
|
}
|
|
args[1] = fmt.Sprintf("$%#x", binary.LittleEndian.Uint16(buf))
|
|
|
|
case LDR_EQ:
|
|
if _, err := text.ReadAt(buf, int64(addr)); err != nil {
|
|
break
|
|
}
|
|
x := binary.LittleEndian.Uint32(buf)
|
|
if s, base := symname(uint64(x)); s != "" && uint64(x) == base {
|
|
args[1] = fmt.Sprintf("$%s(SB)", s)
|
|
} else {
|
|
args[1] = fmt.Sprintf("$%#x", x)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Move addressing mode into opcode suffix.
|
|
suffix := ""
|
|
switch inst.Op &^ 15 {
|
|
case LDR_EQ, LDRB_EQ, LDRH_EQ, STR_EQ, STRB_EQ, STRH_EQ:
|
|
mem, _ := inst.Args[1].(Mem)
|
|
switch mem.Mode {
|
|
case AddrOffset, AddrLDM:
|
|
// no suffix
|
|
case AddrPreIndex, AddrLDM_WB:
|
|
suffix = ".W"
|
|
case AddrPostIndex:
|
|
suffix = ".P"
|
|
}
|
|
off := ""
|
|
if mem.Offset != 0 {
|
|
off = fmt.Sprintf("%#x", mem.Offset)
|
|
}
|
|
base := fmt.Sprintf("(R%d)", int(mem.Base))
|
|
index := ""
|
|
if mem.Sign != 0 {
|
|
sign := ""
|
|
if mem.Sign < 0 {
|
|
sign = ""
|
|
}
|
|
shift := ""
|
|
if mem.Count != 0 {
|
|
shift = fmt.Sprintf("%s%d", plan9Shift[mem.Shift], mem.Count)
|
|
}
|
|
index = fmt.Sprintf("(%sR%d%s)", sign, int(mem.Index), shift)
|
|
}
|
|
args[1] = off + base + index
|
|
}
|
|
|
|
// Reverse args, placing dest last.
|
|
for i, j := 0, len(args)-1; i < j; i, j = i+1, j-1 {
|
|
args[i], args[j] = args[j], args[i]
|
|
}
|
|
|
|
switch inst.Op &^ 15 {
|
|
case MOV_EQ:
|
|
op = "MOVW" + op[3:]
|
|
|
|
case LDR_EQ:
|
|
op = "MOVW" + op[3:] + suffix
|
|
case LDRB_EQ:
|
|
op = "MOVB" + op[4:] + suffix
|
|
case LDRH_EQ:
|
|
op = "MOVH" + op[4:] + suffix
|
|
|
|
case STR_EQ:
|
|
op = "MOVW" + op[3:] + suffix
|
|
args[0], args[1] = args[1], args[0]
|
|
case STRB_EQ:
|
|
op = "MOVB" + op[4:] + suffix
|
|
args[0], args[1] = args[1], args[0]
|
|
case STRH_EQ:
|
|
op = "MOVH" + op[4:] + suffix
|
|
args[0], args[1] = args[1], args[0]
|
|
}
|
|
|
|
if args != nil {
|
|
op += " " + strings.Join(args, ", ")
|
|
}
|
|
|
|
return op
|
|
}
|
|
|
|
// assembler syntax for the various shifts.
|
|
// @x> is a lie; the assembler uses @> 0
|
|
// instead of @x> 1, but i wanted to be clear that it
|
|
// was a different operation (rotate right extended, not rotate right).
|
|
var plan9Shift = []string{"<<", ">>", "->", "@>", "@x>"}
|
|
|
|
func plan9Arg(inst *Inst, pc uint64, symname func(uint64) (string, uint64), arg Arg) string {
|
|
switch a := arg.(type) {
|
|
case Endian:
|
|
|
|
case Imm:
|
|
return fmt.Sprintf("$%d", int(a))
|
|
|
|
case Mem:
|
|
|
|
case PCRel:
|
|
addr := uint32(pc) + 8 + uint32(a)
|
|
if s, base := symname(uint64(addr)); s != "" && uint64(addr) == base {
|
|
return fmt.Sprintf("%s(SB)", s)
|
|
}
|
|
return fmt.Sprintf("%#x", addr)
|
|
|
|
case Reg:
|
|
if a < 16 {
|
|
return fmt.Sprintf("R%d", int(a))
|
|
}
|
|
|
|
case RegList:
|
|
var buf bytes.Buffer
|
|
start := -2
|
|
end := -2
|
|
fmt.Fprintf(&buf, "[")
|
|
flush := func() {
|
|
if start >= 0 {
|
|
if buf.Len() > 1 {
|
|
fmt.Fprintf(&buf, ",")
|
|
}
|
|
if start == end {
|
|
fmt.Fprintf(&buf, "R%d", start)
|
|
} else {
|
|
fmt.Fprintf(&buf, "R%d-R%d", start, end)
|
|
}
|
|
start = -2
|
|
end = -2
|
|
}
|
|
}
|
|
for i := 0; i < 16; i++ {
|
|
if a&(1<<uint(i)) != 0 {
|
|
if i == end+1 {
|
|
end++
|
|
continue
|
|
}
|
|
start = i
|
|
end = i
|
|
} else {
|
|
flush()
|
|
}
|
|
}
|
|
flush()
|
|
fmt.Fprintf(&buf, "]")
|
|
return buf.String()
|
|
|
|
case RegShift:
|
|
return fmt.Sprintf("R%d%s$%d", int(a.Reg), plan9Shift[a.Shift], int(a.Count))
|
|
|
|
case RegShiftReg:
|
|
return fmt.Sprintf("R%d%sR%d", int(a.Reg), plan9Shift[a.Shift], int(a.RegCount))
|
|
}
|
|
return strings.ToUpper(arg.String())
|
|
}
|