1 // Copyright 2014 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package armasm 6 7 import ( 8 "bytes" 9 "fmt" 10 "strings" 11 ) 12 13 var saveDot = strings.NewReplacer( 14 ".F16", "_dot_F16", 15 ".F32", "_dot_F32", 16 ".F64", "_dot_F64", 17 ".S32", "_dot_S32", 18 ".U32", "_dot_U32", 19 ".FXS", "_dot_S", 20 ".FXU", "_dot_U", 21 ".32", "_dot_32", 22 ) 23 24 // GNUSyntax returns the GNU assembler syntax for the instruction, as defined by GNU binutils. 25 // This form typically matches the syntax defined in the ARM Reference Manual. 26 func GNUSyntax(inst Inst) string { 27 var buf bytes.Buffer 28 op := inst.Op.String() 29 op = saveDot.Replace(op) 30 op = strings.Replace(op, ".", "", -1) 31 op = strings.Replace(op, "_dot_", ".", -1) 32 op = strings.ToLower(op) 33 buf.WriteString(op) 34 sep := " " 35 for i, arg := range inst.Args { 36 if arg == nil { 37 break 38 } 39 text := gnuArg(&inst, i, arg) 40 if text == "" { 41 continue 42 } 43 buf.WriteString(sep) 44 sep = ", " 45 buf.WriteString(text) 46 } 47 return buf.String() 48 } 49 50 func gnuArg(inst *Inst, argIndex int, arg Arg) string { 51 switch inst.Op &^ 15 { 52 case LDRD_EQ, LDREXD_EQ, STRD_EQ: 53 if argIndex == 1 { 54 // second argument in consecutive pair not printed 55 return "" 56 } 57 case STREXD_EQ: 58 if argIndex == 2 { 59 // second argument in consecutive pair not printed 60 return "" 61 } 62 } 63 64 switch arg := arg.(type) { 65 case Imm: 66 switch inst.Op &^ 15 { 67 case BKPT_EQ: 68 return fmt.Sprintf("%#04x", uint32(arg)) 69 case SVC_EQ: 70 return fmt.Sprintf("%#08x", uint32(arg)) 71 } 72 return fmt.Sprintf("#%d", int32(arg)) 73 74 case ImmAlt: 75 return fmt.Sprintf("#%d, %d", arg.Val, arg.Rot) 76 77 case Mem: 78 R := gnuArg(inst, -1, arg.Base) 79 X := "" 80 if arg.Sign != 0 { 81 X = "" 82 if arg.Sign < 0 { 83 X = "-" 84 } 85 X += gnuArg(inst, -1, arg.Index) 86 if arg.Shift == ShiftLeft && arg.Count == 0 { 87 // nothing 88 } else if arg.Shift == RotateRightExt { 89 X += ", rrx" 90 } else { 91 X += fmt.Sprintf(", %s #%d", strings.ToLower(arg.Shift.String()), arg.Count) 92 } 93 } else { 94 X = fmt.Sprintf("#%d", arg.Offset) 95 } 96 97 switch arg.Mode { 98 case AddrOffset: 99 if X == "#0" { 100 return fmt.Sprintf("[%s]", R) 101 } 102 return fmt.Sprintf("[%s, %s]", R, X) 103 case AddrPreIndex: 104 return fmt.Sprintf("[%s, %s]!", R, X) 105 case AddrPostIndex: 106 return fmt.Sprintf("[%s], %s", R, X) 107 case AddrLDM: 108 if X == "#0" { 109 return R 110 } 111 case AddrLDM_WB: 112 if X == "#0" { 113 return R + "!" 114 } 115 } 116 return fmt.Sprintf("[%s Mode(%d) %s]", R, int(arg.Mode), X) 117 118 case PCRel: 119 return fmt.Sprintf(".%+#x", int32(arg)+4) 120 121 case Reg: 122 switch inst.Op &^ 15 { 123 case LDREX_EQ: 124 if argIndex == 0 { 125 return fmt.Sprintf("r%d", int32(arg)) 126 } 127 } 128 switch arg { 129 case R10: 130 return "sl" 131 case R11: 132 return "fp" 133 case R12: 134 return "ip" 135 } 136 137 case RegList: 138 var buf bytes.Buffer 139 fmt.Fprintf(&buf, "{") 140 sep := "" 141 for i := 0; i < 16; i++ { 142 if arg&(1<<uint(i)) != 0 { 143 fmt.Fprintf(&buf, "%s%s", sep, gnuArg(inst, -1, Reg(i))) 144 sep = ", " 145 } 146 } 147 fmt.Fprintf(&buf, "}") 148 return buf.String() 149 150 case RegShift: 151 if arg.Shift == ShiftLeft && arg.Count == 0 { 152 return gnuArg(inst, -1, arg.Reg) 153 } 154 if arg.Shift == RotateRightExt { 155 return gnuArg(inst, -1, arg.Reg) + ", rrx" 156 } 157 return fmt.Sprintf("%s, %s #%d", gnuArg(inst, -1, arg.Reg), strings.ToLower(arg.Shift.String()), arg.Count) 158 159 case RegShiftReg: 160 return fmt.Sprintf("%s, %s %s", gnuArg(inst, -1, arg.Reg), strings.ToLower(arg.Shift.String()), gnuArg(inst, -1, arg.RegCount)) 161 162 } 163 return strings.ToLower(arg.String()) 164 } 165