arm.go raw

   1  // Copyright 2015 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  // This file encapsulates some of the odd characteristics of the ARM
   6  // instruction set, to minimize its interaction with the core of the
   7  // assembler.
   8  
   9  package arch
  10  
  11  import (
  12  	"strings"
  13  
  14  	"github.com/twitchyliquid64/golang-asm/obj"
  15  	"github.com/twitchyliquid64/golang-asm/obj/arm"
  16  )
  17  
  18  var armLS = map[string]uint8{
  19  	"U":  arm.C_UBIT,
  20  	"S":  arm.C_SBIT,
  21  	"W":  arm.C_WBIT,
  22  	"P":  arm.C_PBIT,
  23  	"PW": arm.C_WBIT | arm.C_PBIT,
  24  	"WP": arm.C_WBIT | arm.C_PBIT,
  25  }
  26  
  27  var armSCOND = map[string]uint8{
  28  	"EQ":  arm.C_SCOND_EQ,
  29  	"NE":  arm.C_SCOND_NE,
  30  	"CS":  arm.C_SCOND_HS,
  31  	"HS":  arm.C_SCOND_HS,
  32  	"CC":  arm.C_SCOND_LO,
  33  	"LO":  arm.C_SCOND_LO,
  34  	"MI":  arm.C_SCOND_MI,
  35  	"PL":  arm.C_SCOND_PL,
  36  	"VS":  arm.C_SCOND_VS,
  37  	"VC":  arm.C_SCOND_VC,
  38  	"HI":  arm.C_SCOND_HI,
  39  	"LS":  arm.C_SCOND_LS,
  40  	"GE":  arm.C_SCOND_GE,
  41  	"LT":  arm.C_SCOND_LT,
  42  	"GT":  arm.C_SCOND_GT,
  43  	"LE":  arm.C_SCOND_LE,
  44  	"AL":  arm.C_SCOND_NONE,
  45  	"U":   arm.C_UBIT,
  46  	"S":   arm.C_SBIT,
  47  	"W":   arm.C_WBIT,
  48  	"P":   arm.C_PBIT,
  49  	"PW":  arm.C_WBIT | arm.C_PBIT,
  50  	"WP":  arm.C_WBIT | arm.C_PBIT,
  51  	"F":   arm.C_FBIT,
  52  	"IBW": arm.C_WBIT | arm.C_PBIT | arm.C_UBIT,
  53  	"IAW": arm.C_WBIT | arm.C_UBIT,
  54  	"DBW": arm.C_WBIT | arm.C_PBIT,
  55  	"DAW": arm.C_WBIT,
  56  	"IB":  arm.C_PBIT | arm.C_UBIT,
  57  	"IA":  arm.C_UBIT,
  58  	"DB":  arm.C_PBIT,
  59  	"DA":  0,
  60  }
  61  
  62  var armJump = map[string]bool{
  63  	"B":    true,
  64  	"BL":   true,
  65  	"BX":   true,
  66  	"BEQ":  true,
  67  	"BNE":  true,
  68  	"BCS":  true,
  69  	"BHS":  true,
  70  	"BCC":  true,
  71  	"BLO":  true,
  72  	"BMI":  true,
  73  	"BPL":  true,
  74  	"BVS":  true,
  75  	"BVC":  true,
  76  	"BHI":  true,
  77  	"BLS":  true,
  78  	"BGE":  true,
  79  	"BLT":  true,
  80  	"BGT":  true,
  81  	"BLE":  true,
  82  	"CALL": true,
  83  	"JMP":  true,
  84  }
  85  
  86  func jumpArm(word string) bool {
  87  	return armJump[word]
  88  }
  89  
  90  // IsARMCMP reports whether the op (as defined by an arm.A* constant) is
  91  // one of the comparison instructions that require special handling.
  92  func IsARMCMP(op obj.As) bool {
  93  	switch op {
  94  	case arm.ACMN, arm.ACMP, arm.ATEQ, arm.ATST:
  95  		return true
  96  	}
  97  	return false
  98  }
  99  
 100  // IsARMSTREX reports whether the op (as defined by an arm.A* constant) is
 101  // one of the STREX-like instructions that require special handling.
 102  func IsARMSTREX(op obj.As) bool {
 103  	switch op {
 104  	case arm.ASTREX, arm.ASTREXD, arm.ASWPW, arm.ASWPBU:
 105  		return true
 106  	}
 107  	return false
 108  }
 109  
 110  // MCR is not defined by the obj/arm; instead we define it privately here.
 111  // It is encoded as an MRC with a bit inside the instruction word,
 112  // passed to arch.ARMMRCOffset.
 113  const aMCR = arm.ALAST + 1
 114  
 115  // IsARMMRC reports whether the op (as defined by an arm.A* constant) is
 116  // MRC or MCR
 117  func IsARMMRC(op obj.As) bool {
 118  	switch op {
 119  	case arm.AMRC, aMCR: // Note: aMCR is defined in this package.
 120  		return true
 121  	}
 122  	return false
 123  }
 124  
 125  // IsARMBFX reports whether the op (as defined by an arm.A* constant) is one the
 126  // BFX-like instructions which are in the form of "op $width, $LSB, (Reg,) Reg".
 127  func IsARMBFX(op obj.As) bool {
 128  	switch op {
 129  	case arm.ABFX, arm.ABFXU, arm.ABFC, arm.ABFI:
 130  		return true
 131  	}
 132  	return false
 133  }
 134  
 135  // IsARMFloatCmp reports whether the op is a floating comparison instruction.
 136  func IsARMFloatCmp(op obj.As) bool {
 137  	switch op {
 138  	case arm.ACMPF, arm.ACMPD:
 139  		return true
 140  	}
 141  	return false
 142  }
 143  
 144  // ARMMRCOffset implements the peculiar encoding of the MRC and MCR instructions.
 145  // The difference between MRC and MCR is represented by a bit high in the word, not
 146  // in the usual way by the opcode itself. Asm must use AMRC for both instructions, so
 147  // we return the opcode for MRC so that asm doesn't need to import obj/arm.
 148  func ARMMRCOffset(op obj.As, cond string, x0, x1, x2, x3, x4, x5 int64) (offset int64, op0 obj.As, ok bool) {
 149  	op1 := int64(0)
 150  	if op == arm.AMRC {
 151  		op1 = 1
 152  	}
 153  	bits, ok := ParseARMCondition(cond)
 154  	if !ok {
 155  		return
 156  	}
 157  	offset = (0xe << 24) | // opcode
 158  		(op1 << 20) | // MCR/MRC
 159  		((int64(bits) ^ arm.C_SCOND_XOR) << 28) | // scond
 160  		((x0 & 15) << 8) | //coprocessor number
 161  		((x1 & 7) << 21) | // coprocessor operation
 162  		((x2 & 15) << 12) | // ARM register
 163  		((x3 & 15) << 16) | // Crn
 164  		((x4 & 15) << 0) | // Crm
 165  		((x5 & 7) << 5) | // coprocessor information
 166  		(1 << 4) /* must be set */
 167  	return offset, arm.AMRC, true
 168  }
 169  
 170  // IsARMMULA reports whether the op (as defined by an arm.A* constant) is
 171  // MULA, MULS, MMULA, MMULS, MULABB, MULAWB or MULAWT, the 4-operand instructions.
 172  func IsARMMULA(op obj.As) bool {
 173  	switch op {
 174  	case arm.AMULA, arm.AMULS, arm.AMMULA, arm.AMMULS, arm.AMULABB, arm.AMULAWB, arm.AMULAWT:
 175  		return true
 176  	}
 177  	return false
 178  }
 179  
 180  var bcode = []obj.As{
 181  	arm.ABEQ,
 182  	arm.ABNE,
 183  	arm.ABCS,
 184  	arm.ABCC,
 185  	arm.ABMI,
 186  	arm.ABPL,
 187  	arm.ABVS,
 188  	arm.ABVC,
 189  	arm.ABHI,
 190  	arm.ABLS,
 191  	arm.ABGE,
 192  	arm.ABLT,
 193  	arm.ABGT,
 194  	arm.ABLE,
 195  	arm.AB,
 196  	obj.ANOP,
 197  }
 198  
 199  // ARMConditionCodes handles the special condition code situation for the ARM.
 200  // It returns a boolean to indicate success; failure means cond was unrecognized.
 201  func ARMConditionCodes(prog *obj.Prog, cond string) bool {
 202  	if cond == "" {
 203  		return true
 204  	}
 205  	bits, ok := ParseARMCondition(cond)
 206  	if !ok {
 207  		return false
 208  	}
 209  	/* hack to make B.NE etc. work: turn it into the corresponding conditional */
 210  	if prog.As == arm.AB {
 211  		prog.As = bcode[(bits^arm.C_SCOND_XOR)&0xf]
 212  		bits = (bits &^ 0xf) | arm.C_SCOND_NONE
 213  	}
 214  	prog.Scond = bits
 215  	return true
 216  }
 217  
 218  // ParseARMCondition parses the conditions attached to an ARM instruction.
 219  // The input is a single string consisting of period-separated condition
 220  // codes, such as ".P.W". An initial period is ignored.
 221  func ParseARMCondition(cond string) (uint8, bool) {
 222  	return parseARMCondition(cond, armLS, armSCOND)
 223  }
 224  
 225  func parseARMCondition(cond string, ls, scond map[string]uint8) (uint8, bool) {
 226  	cond = strings.TrimPrefix(cond, ".")
 227  	if cond == "" {
 228  		return arm.C_SCOND_NONE, true
 229  	}
 230  	names := strings.Split(cond, ".")
 231  	bits := uint8(0)
 232  	for _, name := range names {
 233  		if b, present := ls[name]; present {
 234  			bits |= b
 235  			continue
 236  		}
 237  		if b, present := scond[name]; present {
 238  			bits = (bits &^ arm.C_SCOND) | b
 239  			continue
 240  		}
 241  		return 0, false
 242  	}
 243  	return bits, true
 244  }
 245  
 246  func armRegisterNumber(name string, n int16) (int16, bool) {
 247  	if n < 0 || 15 < n {
 248  		return 0, false
 249  	}
 250  	switch name {
 251  	case "R":
 252  		return arm.REG_R0 + n, true
 253  	case "F":
 254  		return arm.REG_F0 + n, true
 255  	}
 256  	return 0, false
 257  }
 258