Home | History | Annotate | Download | only in armasm
      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 // GoSyntax 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 GoSyntax(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