258 lines
6.2 KiB
Go
258 lines
6.2 KiB
Go
|
// Copyright 2015 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.
|
||
|
|
||
|
// This file encapsulates some of the odd characteristics of the ARM
|
||
|
// instruction set, to minimize its interaction with the core of the
|
||
|
// assembler.
|
||
|
|
||
|
package arch
|
||
|
|
||
|
import (
|
||
|
"strings"
|
||
|
|
||
|
"github.com/twitchyliquid64/golang-asm/obj"
|
||
|
"github.com/twitchyliquid64/golang-asm/obj/arm"
|
||
|
)
|
||
|
|
||
|
var armLS = map[string]uint8{
|
||
|
"U": arm.C_UBIT,
|
||
|
"S": arm.C_SBIT,
|
||
|
"W": arm.C_WBIT,
|
||
|
"P": arm.C_PBIT,
|
||
|
"PW": arm.C_WBIT | arm.C_PBIT,
|
||
|
"WP": arm.C_WBIT | arm.C_PBIT,
|
||
|
}
|
||
|
|
||
|
var armSCOND = map[string]uint8{
|
||
|
"EQ": arm.C_SCOND_EQ,
|
||
|
"NE": arm.C_SCOND_NE,
|
||
|
"CS": arm.C_SCOND_HS,
|
||
|
"HS": arm.C_SCOND_HS,
|
||
|
"CC": arm.C_SCOND_LO,
|
||
|
"LO": arm.C_SCOND_LO,
|
||
|
"MI": arm.C_SCOND_MI,
|
||
|
"PL": arm.C_SCOND_PL,
|
||
|
"VS": arm.C_SCOND_VS,
|
||
|
"VC": arm.C_SCOND_VC,
|
||
|
"HI": arm.C_SCOND_HI,
|
||
|
"LS": arm.C_SCOND_LS,
|
||
|
"GE": arm.C_SCOND_GE,
|
||
|
"LT": arm.C_SCOND_LT,
|
||
|
"GT": arm.C_SCOND_GT,
|
||
|
"LE": arm.C_SCOND_LE,
|
||
|
"AL": arm.C_SCOND_NONE,
|
||
|
"U": arm.C_UBIT,
|
||
|
"S": arm.C_SBIT,
|
||
|
"W": arm.C_WBIT,
|
||
|
"P": arm.C_PBIT,
|
||
|
"PW": arm.C_WBIT | arm.C_PBIT,
|
||
|
"WP": arm.C_WBIT | arm.C_PBIT,
|
||
|
"F": arm.C_FBIT,
|
||
|
"IBW": arm.C_WBIT | arm.C_PBIT | arm.C_UBIT,
|
||
|
"IAW": arm.C_WBIT | arm.C_UBIT,
|
||
|
"DBW": arm.C_WBIT | arm.C_PBIT,
|
||
|
"DAW": arm.C_WBIT,
|
||
|
"IB": arm.C_PBIT | arm.C_UBIT,
|
||
|
"IA": arm.C_UBIT,
|
||
|
"DB": arm.C_PBIT,
|
||
|
"DA": 0,
|
||
|
}
|
||
|
|
||
|
var armJump = map[string]bool{
|
||
|
"B": true,
|
||
|
"BL": true,
|
||
|
"BX": true,
|
||
|
"BEQ": true,
|
||
|
"BNE": true,
|
||
|
"BCS": true,
|
||
|
"BHS": true,
|
||
|
"BCC": true,
|
||
|
"BLO": true,
|
||
|
"BMI": true,
|
||
|
"BPL": true,
|
||
|
"BVS": true,
|
||
|
"BVC": true,
|
||
|
"BHI": true,
|
||
|
"BLS": true,
|
||
|
"BGE": true,
|
||
|
"BLT": true,
|
||
|
"BGT": true,
|
||
|
"BLE": true,
|
||
|
"CALL": true,
|
||
|
"JMP": true,
|
||
|
}
|
||
|
|
||
|
func jumpArm(word string) bool {
|
||
|
return armJump[word]
|
||
|
}
|
||
|
|
||
|
// IsARMCMP reports whether the op (as defined by an arm.A* constant) is
|
||
|
// one of the comparison instructions that require special handling.
|
||
|
func IsARMCMP(op obj.As) bool {
|
||
|
switch op {
|
||
|
case arm.ACMN, arm.ACMP, arm.ATEQ, arm.ATST:
|
||
|
return true
|
||
|
}
|
||
|
return false
|
||
|
}
|
||
|
|
||
|
// IsARMSTREX reports whether the op (as defined by an arm.A* constant) is
|
||
|
// one of the STREX-like instructions that require special handling.
|
||
|
func IsARMSTREX(op obj.As) bool {
|
||
|
switch op {
|
||
|
case arm.ASTREX, arm.ASTREXD, arm.ASWPW, arm.ASWPBU:
|
||
|
return true
|
||
|
}
|
||
|
return false
|
||
|
}
|
||
|
|
||
|
// MCR is not defined by the obj/arm; instead we define it privately here.
|
||
|
// It is encoded as an MRC with a bit inside the instruction word,
|
||
|
// passed to arch.ARMMRCOffset.
|
||
|
const aMCR = arm.ALAST + 1
|
||
|
|
||
|
// IsARMMRC reports whether the op (as defined by an arm.A* constant) is
|
||
|
// MRC or MCR
|
||
|
func IsARMMRC(op obj.As) bool {
|
||
|
switch op {
|
||
|
case arm.AMRC, aMCR: // Note: aMCR is defined in this package.
|
||
|
return true
|
||
|
}
|
||
|
return false
|
||
|
}
|
||
|
|
||
|
// IsARMBFX reports whether the op (as defined by an arm.A* constant) is one the
|
||
|
// BFX-like instructions which are in the form of "op $width, $LSB, (Reg,) Reg".
|
||
|
func IsARMBFX(op obj.As) bool {
|
||
|
switch op {
|
||
|
case arm.ABFX, arm.ABFXU, arm.ABFC, arm.ABFI:
|
||
|
return true
|
||
|
}
|
||
|
return false
|
||
|
}
|
||
|
|
||
|
// IsARMFloatCmp reports whether the op is a floating comparison instruction.
|
||
|
func IsARMFloatCmp(op obj.As) bool {
|
||
|
switch op {
|
||
|
case arm.ACMPF, arm.ACMPD:
|
||
|
return true
|
||
|
}
|
||
|
return false
|
||
|
}
|
||
|
|
||
|
// ARMMRCOffset implements the peculiar encoding of the MRC and MCR instructions.
|
||
|
// The difference between MRC and MCR is represented by a bit high in the word, not
|
||
|
// in the usual way by the opcode itself. Asm must use AMRC for both instructions, so
|
||
|
// we return the opcode for MRC so that asm doesn't need to import obj/arm.
|
||
|
func ARMMRCOffset(op obj.As, cond string, x0, x1, x2, x3, x4, x5 int64) (offset int64, op0 obj.As, ok bool) {
|
||
|
op1 := int64(0)
|
||
|
if op == arm.AMRC {
|
||
|
op1 = 1
|
||
|
}
|
||
|
bits, ok := ParseARMCondition(cond)
|
||
|
if !ok {
|
||
|
return
|
||
|
}
|
||
|
offset = (0xe << 24) | // opcode
|
||
|
(op1 << 20) | // MCR/MRC
|
||
|
((int64(bits) ^ arm.C_SCOND_XOR) << 28) | // scond
|
||
|
((x0 & 15) << 8) | //coprocessor number
|
||
|
((x1 & 7) << 21) | // coprocessor operation
|
||
|
((x2 & 15) << 12) | // ARM register
|
||
|
((x3 & 15) << 16) | // Crn
|
||
|
((x4 & 15) << 0) | // Crm
|
||
|
((x5 & 7) << 5) | // coprocessor information
|
||
|
(1 << 4) /* must be set */
|
||
|
return offset, arm.AMRC, true
|
||
|
}
|
||
|
|
||
|
// IsARMMULA reports whether the op (as defined by an arm.A* constant) is
|
||
|
// MULA, MULS, MMULA, MMULS, MULABB, MULAWB or MULAWT, the 4-operand instructions.
|
||
|
func IsARMMULA(op obj.As) bool {
|
||
|
switch op {
|
||
|
case arm.AMULA, arm.AMULS, arm.AMMULA, arm.AMMULS, arm.AMULABB, arm.AMULAWB, arm.AMULAWT:
|
||
|
return true
|
||
|
}
|
||
|
return false
|
||
|
}
|
||
|
|
||
|
var bcode = []obj.As{
|
||
|
arm.ABEQ,
|
||
|
arm.ABNE,
|
||
|
arm.ABCS,
|
||
|
arm.ABCC,
|
||
|
arm.ABMI,
|
||
|
arm.ABPL,
|
||
|
arm.ABVS,
|
||
|
arm.ABVC,
|
||
|
arm.ABHI,
|
||
|
arm.ABLS,
|
||
|
arm.ABGE,
|
||
|
arm.ABLT,
|
||
|
arm.ABGT,
|
||
|
arm.ABLE,
|
||
|
arm.AB,
|
||
|
obj.ANOP,
|
||
|
}
|
||
|
|
||
|
// ARMConditionCodes handles the special condition code situation for the ARM.
|
||
|
// It returns a boolean to indicate success; failure means cond was unrecognized.
|
||
|
func ARMConditionCodes(prog *obj.Prog, cond string) bool {
|
||
|
if cond == "" {
|
||
|
return true
|
||
|
}
|
||
|
bits, ok := ParseARMCondition(cond)
|
||
|
if !ok {
|
||
|
return false
|
||
|
}
|
||
|
/* hack to make B.NE etc. work: turn it into the corresponding conditional */
|
||
|
if prog.As == arm.AB {
|
||
|
prog.As = bcode[(bits^arm.C_SCOND_XOR)&0xf]
|
||
|
bits = (bits &^ 0xf) | arm.C_SCOND_NONE
|
||
|
}
|
||
|
prog.Scond = bits
|
||
|
return true
|
||
|
}
|
||
|
|
||
|
// ParseARMCondition parses the conditions attached to an ARM instruction.
|
||
|
// The input is a single string consisting of period-separated condition
|
||
|
// codes, such as ".P.W". An initial period is ignored.
|
||
|
func ParseARMCondition(cond string) (uint8, bool) {
|
||
|
return parseARMCondition(cond, armLS, armSCOND)
|
||
|
}
|
||
|
|
||
|
func parseARMCondition(cond string, ls, scond map[string]uint8) (uint8, bool) {
|
||
|
cond = strings.TrimPrefix(cond, ".")
|
||
|
if cond == "" {
|
||
|
return arm.C_SCOND_NONE, true
|
||
|
}
|
||
|
names := strings.Split(cond, ".")
|
||
|
bits := uint8(0)
|
||
|
for _, name := range names {
|
||
|
if b, present := ls[name]; present {
|
||
|
bits |= b
|
||
|
continue
|
||
|
}
|
||
|
if b, present := scond[name]; present {
|
||
|
bits = (bits &^ arm.C_SCOND) | b
|
||
|
continue
|
||
|
}
|
||
|
return 0, false
|
||
|
}
|
||
|
return bits, true
|
||
|
}
|
||
|
|
||
|
func armRegisterNumber(name string, n int16) (int16, bool) {
|
||
|
if n < 0 || 15 < n {
|
||
|
return 0, false
|
||
|
}
|
||
|
switch name {
|
||
|
case "R":
|
||
|
return arm.REG_R0 + n, true
|
||
|
case "F":
|
||
|
return arm.REG_F0 + n, true
|
||
|
}
|
||
|
return 0, false
|
||
|
}
|