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 "encoding/binary" 10 "fmt" 11 "io" 12 "strings" 13 ) 14 15 // Plan9Syntax returns the Go assembler syntax for the instruction. 16 // The syntax was originally defined by Plan 9. 17 // The pc is the program counter of the instruction, used for expanding 18 // PC-relative addresses into absolute ones. 19 // The symname function queries the symbol table for the program 20 // being disassembled. Given a target address it returns the name and base 21 // address of the symbol containing the target, if any; otherwise it returns "", 0. 22 // The reader r should read from the text segment using text addresses 23 // as offsets; it is used to display pc-relative loads as constant loads. 24 func Plan9Syntax(inst Inst, pc uint64, symname func(uint64) (string, uint64), text io.ReaderAt) string { 25 if symname == nil { 26 symname = func(uint64) (string, uint64) { return "", 0 } 27 } 28 29 var args []string 30 for _, a := range inst.Args { 31 if a == nil { 32 break 33 } 34 args = append(args, plan9Arg(&inst, pc, symname, a)) 35 } 36 37 op := inst.Op.String() 38 39 switch inst.Op &^ 15 { 40 case LDR_EQ, LDRB_EQ, LDRH_EQ: 41 // Check for RET 42 reg, _ := inst.Args[0].(Reg) 43 mem, _ := inst.Args[1].(Mem) 44 if inst.Op&^15 == LDR_EQ && reg == R15 && mem.Base == SP && mem.Sign == 0 && mem.Mode == AddrPostIndex { 45 return fmt.Sprintf("RET%s #%d", op[3:], mem.Offset) 46 } 47 48 // Check for PC-relative load. 49 if mem.Base == PC && mem.Sign == 0 && mem.Mode == AddrOffset && text != nil { 50 addr := uint32(pc) + 8 + uint32(mem.Offset) 51 buf := make([]byte, 4) 52 switch inst.Op &^ 15 { 53 case LDRB_EQ: 54 if _, err := text.ReadAt(buf[:1], int64(addr)); err != nil { 55 break 56 } 57 args[1] = fmt.Sprintf("$%#x", buf[0]) 58 59 case LDRH_EQ: 60 if _, err := text.ReadAt(buf[:2], int64(addr)); err != nil { 61 break 62 } 63 args[1] = fmt.Sprintf("$%#x", binary.LittleEndian.Uint16(buf)) 64 65 case LDR_EQ: 66 if _, err := text.ReadAt(buf, int64(addr)); err != nil { 67 break 68 } 69 x := binary.LittleEndian.Uint32(buf) 70 if s, base := symname(uint64(x)); s != "" && uint64(x) == base { 71 args[1] = fmt.Sprintf("$%s(SB)", s) 72 } else { 73 args[1] = fmt.Sprintf("$%#x", x) 74 } 75 } 76 } 77 } 78 79 // Move addressing mode into opcode suffix. 80 suffix := "" 81 switch inst.Op &^ 15 { 82 case LDR_EQ, LDRB_EQ, LDRH_EQ, STR_EQ, STRB_EQ, STRH_EQ: 83 mem, _ := inst.Args[1].(Mem) 84 switch mem.Mode { 85 case AddrOffset, AddrLDM: 86 // no suffix 87 case AddrPreIndex, AddrLDM_WB: 88 suffix = ".W" 89 case AddrPostIndex: 90 suffix = ".P" 91 } 92 off := "" 93 if mem.Offset != 0 { 94 off = fmt.Sprintf("%#x", mem.Offset) 95 } 96 base := fmt.Sprintf("(R%d)", int(mem.Base)) 97 index := "" 98 if mem.Sign != 0 { 99 sign := "" 100 if mem.Sign < 0 { 101 sign = "" 102 } 103 shift := "" 104 if mem.Count != 0 { 105 shift = fmt.Sprintf("%s%d", plan9Shift[mem.Shift], mem.Count) 106 } 107 index = fmt.Sprintf("(%sR%d%s)", sign, int(mem.Index), shift) 108 } 109 args[1] = off + base + index 110 } 111 112 // Reverse args, placing dest last. 113 for i, j := 0, len(args)-1; i < j; i, j = i+1, j-1 { 114 args[i], args[j] = args[j], args[i] 115 } 116 117 switch inst.Op &^ 15 { 118 case MOV_EQ: 119 op = "MOVW" + op[3:] 120 121 case LDR_EQ: 122 op = "MOVW" + op[3:] + suffix 123 case LDRB_EQ: 124 op = "MOVB" + op[4:] + suffix 125 case LDRH_EQ: 126 op = "MOVH" + op[4:] + suffix 127 128 case STR_EQ: 129 op = "MOVW" + op[3:] + suffix 130 args[0], args[1] = args[1], args[0] 131 case STRB_EQ: 132 op = "MOVB" + op[4:] + suffix 133 args[0], args[1] = args[1], args[0] 134 case STRH_EQ: 135 op = "MOVH" + op[4:] + suffix 136 args[0], args[1] = args[1], args[0] 137 } 138 139 if args != nil { 140 op += " " + strings.Join(args, ", ") 141 } 142 143 return op 144 } 145 146 // assembler syntax for the various shifts. 147 // @x> is a lie; the assembler uses @> 0 148 // instead of @x> 1, but i wanted to be clear that it 149 // was a different operation (rotate right extended, not rotate right). 150 var plan9Shift = []string{"<<", ">>", "->", "@>", "@x>"} 151 152 func plan9Arg(inst *Inst, pc uint64, symname func(uint64) (string, uint64), arg Arg) string { 153 switch a := arg.(type) { 154 case Endian: 155 156 case Imm: 157 return fmt.Sprintf("$%d", int(a)) 158 159 case Mem: 160 161 case PCRel: 162 addr := uint32(pc) + 8 + uint32(a) 163 if s, base := symname(uint64(addr)); s != "" && uint64(addr) == base { 164 return fmt.Sprintf("%s(SB)", s) 165 } 166 return fmt.Sprintf("%#x", addr) 167 168 case Reg: 169 if a < 16 { 170 return fmt.Sprintf("R%d", int(a)) 171 } 172 173 case RegList: 174 var buf bytes.Buffer 175 start := -2 176 end := -2 177 fmt.Fprintf(&buf, "[") 178 flush := func() { 179 if start >= 0 { 180 if buf.Len() > 1 { 181 fmt.Fprintf(&buf, ",") 182 } 183 if start == end { 184 fmt.Fprintf(&buf, "R%d", start) 185 } else { 186 fmt.Fprintf(&buf, "R%d-R%d", start, end) 187 } 188 } 189 } 190 for i := 0; i < 16; i++ { 191 if a&(1<<uint(i)) != 0 { 192 if i == end+1 { 193 end++ 194 continue 195 } 196 start = i 197 end = i 198 } 199 } 200 flush() 201 fmt.Fprintf(&buf, "]") 202 return buf.String() 203 204 case RegShift: 205 return fmt.Sprintf("R%d%s$%d", int(a.Reg), plan9Shift[a.Shift], int(a.Count)) 206 207 case RegShiftReg: 208 return fmt.Sprintf("R%d%sR%d", int(a.Reg), plan9Shift[a.Shift], int(a.RegCount)) 209 } 210 return strings.ToUpper(arg.String()) 211 } 212