| // 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" |
| "fmt" |
| "strings" |
| ) |
| |
| var saveDot = strings.NewReplacer( |
| ".F16", "_dot_F16", |
| ".F32", "_dot_F32", |
| ".F64", "_dot_F64", |
| ".S32", "_dot_S32", |
| ".U32", "_dot_U32", |
| ".FXS", "_dot_S", |
| ".FXU", "_dot_U", |
| ".32", "_dot_32", |
| ) |
| |
| // GNUSyntax returns the GNU assembler syntax for the instruction, as defined by GNU binutils. |
| // This form typically matches the syntax defined in the ARM Reference Manual. |
| func GNUSyntax(inst Inst) string { |
| var buf bytes.Buffer |
| op := inst.Op.String() |
| op = saveDot.Replace(op) |
| op = strings.Replace(op, ".", "", -1) |
| op = strings.Replace(op, "_dot_", ".", -1) |
| op = strings.ToLower(op) |
| buf.WriteString(op) |
| sep := " " |
| for i, arg := range inst.Args { |
| if arg == nil { |
| break |
| } |
| text := gnuArg(&inst, i, arg) |
| if text == "" { |
| continue |
| } |
| buf.WriteString(sep) |
| sep = ", " |
| buf.WriteString(text) |
| } |
| return buf.String() |
| } |
| |
| func gnuArg(inst *Inst, argIndex int, arg Arg) string { |
| switch inst.Op &^ 15 { |
| case LDRD_EQ, LDREXD_EQ, STRD_EQ: |
| if argIndex == 1 { |
| // second argument in consecutive pair not printed |
| return "" |
| } |
| case STREXD_EQ: |
| if argIndex == 2 { |
| // second argument in consecutive pair not printed |
| return "" |
| } |
| } |
| |
| switch arg := arg.(type) { |
| case Imm: |
| switch inst.Op &^ 15 { |
| case BKPT_EQ: |
| return fmt.Sprintf("%#04x", uint32(arg)) |
| case SVC_EQ: |
| return fmt.Sprintf("%#08x", uint32(arg)) |
| } |
| return fmt.Sprintf("#%d", int32(arg)) |
| |
| case ImmAlt: |
| return fmt.Sprintf("#%d, %d", arg.Val, arg.Rot) |
| |
| case Mem: |
| R := gnuArg(inst, -1, arg.Base) |
| X := "" |
| if arg.Sign != 0 { |
| X = "" |
| if arg.Sign < 0 { |
| X = "-" |
| } |
| X += gnuArg(inst, -1, arg.Index) |
| if arg.Shift == ShiftLeft && arg.Count == 0 { |
| // nothing |
| } else if arg.Shift == RotateRightExt { |
| X += ", rrx" |
| } else { |
| X += fmt.Sprintf(", %s #%d", strings.ToLower(arg.Shift.String()), arg.Count) |
| } |
| } else { |
| X = fmt.Sprintf("#%d", arg.Offset) |
| } |
| |
| switch arg.Mode { |
| case AddrOffset: |
| if X == "#0" { |
| return fmt.Sprintf("[%s]", R) |
| } |
| return fmt.Sprintf("[%s, %s]", R, X) |
| case AddrPreIndex: |
| return fmt.Sprintf("[%s, %s]!", R, X) |
| case AddrPostIndex: |
| return fmt.Sprintf("[%s], %s", R, X) |
| case AddrLDM: |
| if X == "#0" { |
| return R |
| } |
| case AddrLDM_WB: |
| if X == "#0" { |
| return R + "!" |
| } |
| } |
| return fmt.Sprintf("[%s Mode(%d) %s]", R, int(arg.Mode), X) |
| |
| case PCRel: |
| return fmt.Sprintf(".%+#x", int32(arg)+4) |
| |
| case Reg: |
| switch inst.Op &^ 15 { |
| case LDREX_EQ: |
| if argIndex == 0 { |
| return fmt.Sprintf("r%d", int32(arg)) |
| } |
| } |
| switch arg { |
| case R10: |
| return "sl" |
| case R11: |
| return "fp" |
| case R12: |
| return "ip" |
| } |
| |
| case RegList: |
| var buf bytes.Buffer |
| fmt.Fprintf(&buf, "{") |
| sep := "" |
| for i := 0; i < 16; i++ { |
| if arg&(1<<uint(i)) != 0 { |
| fmt.Fprintf(&buf, "%s%s", sep, gnuArg(inst, -1, Reg(i))) |
| sep = ", " |
| } |
| } |
| fmt.Fprintf(&buf, "}") |
| return buf.String() |
| |
| case RegShift: |
| if arg.Shift == ShiftLeft && arg.Count == 0 { |
| return gnuArg(inst, -1, arg.Reg) |
| } |
| if arg.Shift == RotateRightExt { |
| return gnuArg(inst, -1, arg.Reg) + ", rrx" |
| } |
| return fmt.Sprintf("%s, %s #%d", gnuArg(inst, -1, arg.Reg), strings.ToLower(arg.Shift.String()), arg.Count) |
| |
| case RegShiftReg: |
| return fmt.Sprintf("%s, %s %s", gnuArg(inst, -1, arg.Reg), strings.ToLower(arg.Shift.String()), gnuArg(inst, -1, arg.RegCount)) |
| |
| } |
| return strings.ToLower(arg.String()) |
| } |