439 lines
7.5 KiB
Go
439 lines
7.5 KiB
Go
// Copyright 2014 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.
|
||
|
||
package armasm
|
||
|
||
import (
|
||
"bytes"
|
||
"fmt"
|
||
)
|
||
|
||
// A Mode is an instruction execution mode.
|
||
type Mode int
|
||
|
||
const (
|
||
_ Mode = iota
|
||
ModeARM
|
||
ModeThumb
|
||
)
|
||
|
||
func (m Mode) String() string {
|
||
switch m {
|
||
case ModeARM:
|
||
return "ARM"
|
||
case ModeThumb:
|
||
return "Thumb"
|
||
}
|
||
return fmt.Sprintf("Mode(%d)", int(m))
|
||
}
|
||
|
||
// An Op is an ARM opcode.
|
||
type Op uint16
|
||
|
||
// NOTE: The actual Op values are defined in tables.go.
|
||
// They are chosen to simplify instruction decoding and
|
||
// are not a dense packing from 0 to N, although the
|
||
// density is high, probably at least 90%.
|
||
|
||
func (op Op) String() string {
|
||
if op >= Op(len(opstr)) || opstr[op] == "" {
|
||
return fmt.Sprintf("Op(%d)", int(op))
|
||
}
|
||
return opstr[op]
|
||
}
|
||
|
||
// An Inst is a single instruction.
|
||
type Inst struct {
|
||
Op Op // Opcode mnemonic
|
||
Enc uint32 // Raw encoding bits.
|
||
Len int // Length of encoding in bytes.
|
||
Args Args // Instruction arguments, in ARM manual order.
|
||
}
|
||
|
||
func (i Inst) String() string {
|
||
var buf bytes.Buffer
|
||
buf.WriteString(i.Op.String())
|
||
for j, arg := range i.Args {
|
||
if arg == nil {
|
||
break
|
||
}
|
||
if j == 0 {
|
||
buf.WriteString(" ")
|
||
} else {
|
||
buf.WriteString(", ")
|
||
}
|
||
buf.WriteString(arg.String())
|
||
}
|
||
return buf.String()
|
||
}
|
||
|
||
// An Args holds the instruction arguments.
|
||
// If an instruction has fewer than 4 arguments,
|
||
// the final elements in the array are nil.
|
||
type Args [4]Arg
|
||
|
||
// An Arg is a single instruction argument, one of these types:
|
||
// Endian, Imm, Mem, PCRel, Reg, RegList, RegShift, RegShiftReg.
|
||
type Arg interface {
|
||
IsArg()
|
||
String() string
|
||
}
|
||
|
||
type Float32Imm float32
|
||
|
||
func (Float32Imm) IsArg() {}
|
||
|
||
func (f Float32Imm) String() string {
|
||
return fmt.Sprintf("#%v", float32(f))
|
||
}
|
||
|
||
type Float64Imm float32
|
||
|
||
func (Float64Imm) IsArg() {}
|
||
|
||
func (f Float64Imm) String() string {
|
||
return fmt.Sprintf("#%v", float64(f))
|
||
}
|
||
|
||
// An Imm is an integer constant.
|
||
type Imm uint32
|
||
|
||
func (Imm) IsArg() {}
|
||
|
||
func (i Imm) String() string {
|
||
return fmt.Sprintf("#%#x", uint32(i))
|
||
}
|
||
|
||
// A ImmAlt is an alternate encoding of an integer constant.
|
||
type ImmAlt struct {
|
||
Val uint8
|
||
Rot uint8
|
||
}
|
||
|
||
func (ImmAlt) IsArg() {}
|
||
|
||
func (i ImmAlt) Imm() Imm {
|
||
v := uint32(i.Val)
|
||
r := uint(i.Rot)
|
||
return Imm(v>>r | v<<(32-r))
|
||
}
|
||
|
||
func (i ImmAlt) String() string {
|
||
return fmt.Sprintf("#%#x, %d", i.Val, i.Rot)
|
||
}
|
||
|
||
// A Label is a text (code) address.
|
||
type Label uint32
|
||
|
||
func (Label) IsArg() {}
|
||
|
||
func (i Label) String() string {
|
||
return fmt.Sprintf("%#x", uint32(i))
|
||
}
|
||
|
||
// A Reg is a single register.
|
||
// The zero value denotes R0, not the absence of a register.
|
||
type Reg uint8
|
||
|
||
const (
|
||
R0 Reg = iota
|
||
R1
|
||
R2
|
||
R3
|
||
R4
|
||
R5
|
||
R6
|
||
R7
|
||
R8
|
||
R9
|
||
R10
|
||
R11
|
||
R12
|
||
R13
|
||
R14
|
||
R15
|
||
|
||
S0
|
||
S1
|
||
S2
|
||
S3
|
||
S4
|
||
S5
|
||
S6
|
||
S7
|
||
S8
|
||
S9
|
||
S10
|
||
S11
|
||
S12
|
||
S13
|
||
S14
|
||
S15
|
||
S16
|
||
S17
|
||
S18
|
||
S19
|
||
S20
|
||
S21
|
||
S22
|
||
S23
|
||
S24
|
||
S25
|
||
S26
|
||
S27
|
||
S28
|
||
S29
|
||
S30
|
||
S31
|
||
|
||
D0
|
||
D1
|
||
D2
|
||
D3
|
||
D4
|
||
D5
|
||
D6
|
||
D7
|
||
D8
|
||
D9
|
||
D10
|
||
D11
|
||
D12
|
||
D13
|
||
D14
|
||
D15
|
||
D16
|
||
D17
|
||
D18
|
||
D19
|
||
D20
|
||
D21
|
||
D22
|
||
D23
|
||
D24
|
||
D25
|
||
D26
|
||
D27
|
||
D28
|
||
D29
|
||
D30
|
||
D31
|
||
|
||
APSR
|
||
APSR_nzcv
|
||
FPSCR
|
||
|
||
SP = R13
|
||
LR = R14
|
||
PC = R15
|
||
)
|
||
|
||
func (Reg) IsArg() {}
|
||
|
||
func (r Reg) String() string {
|
||
switch r {
|
||
case APSR:
|
||
return "APSR"
|
||
case APSR_nzcv:
|
||
return "APSR_nzcv"
|
||
case FPSCR:
|
||
return "FPSCR"
|
||
case SP:
|
||
return "SP"
|
||
case PC:
|
||
return "PC"
|
||
case LR:
|
||
return "LR"
|
||
}
|
||
if R0 <= r && r <= R15 {
|
||
return fmt.Sprintf("R%d", int(r-R0))
|
||
}
|
||
if S0 <= r && r <= S31 {
|
||
return fmt.Sprintf("S%d", int(r-S0))
|
||
}
|
||
if D0 <= r && r <= D31 {
|
||
return fmt.Sprintf("D%d", int(r-D0))
|
||
}
|
||
return fmt.Sprintf("Reg(%d)", int(r))
|
||
}
|
||
|
||
// A RegX represents a fraction of a multi-value register.
|
||
// The Index field specifies the index number,
|
||
// but the size of the fraction is not specified.
|
||
// It must be inferred from the instruction and the register type.
|
||
// For example, in a VMOV instruction, RegX{D5, 1} represents
|
||
// the top 32 bits of the 64-bit D5 register.
|
||
type RegX struct {
|
||
Reg Reg
|
||
Index int
|
||
}
|
||
|
||
func (RegX) IsArg() {}
|
||
|
||
func (r RegX) String() string {
|
||
return fmt.Sprintf("%s[%d]", r.Reg, r.Index)
|
||
}
|
||
|
||
// A RegList is a register list.
|
||
// Bits at indexes x = 0 through 15 indicate whether the corresponding Rx register is in the list.
|
||
type RegList uint16
|
||
|
||
func (RegList) IsArg() {}
|
||
|
||
func (r RegList) String() string {
|
||
var buf bytes.Buffer
|
||
fmt.Fprintf(&buf, "{")
|
||
sep := ""
|
||
for i := 0; i < 16; i++ {
|
||
if r&(1<<uint(i)) != 0 {
|
||
fmt.Fprintf(&buf, "%s%s", sep, Reg(i).String())
|
||
sep = ","
|
||
}
|
||
}
|
||
fmt.Fprintf(&buf, "}")
|
||
return buf.String()
|
||
}
|
||
|
||
// An Endian is the argument to the SETEND instruction.
|
||
type Endian uint8
|
||
|
||
const (
|
||
LittleEndian Endian = 0
|
||
BigEndian Endian = 1
|
||
)
|
||
|
||
func (Endian) IsArg() {}
|
||
|
||
func (e Endian) String() string {
|
||
if e != 0 {
|
||
return "BE"
|
||
}
|
||
return "LE"
|
||
}
|
||
|
||
// A Shift describes an ARM shift operation.
|
||
type Shift uint8
|
||
|
||
const (
|
||
ShiftLeft Shift = 0 // left shift
|
||
ShiftRight Shift = 1 // logical (unsigned) right shift
|
||
ShiftRightSigned Shift = 2 // arithmetic (signed) right shift
|
||
RotateRight Shift = 3 // right rotate
|
||
RotateRightExt Shift = 4 // right rotate through carry (Count will always be 1)
|
||
)
|
||
|
||
var shiftName = [...]string{
|
||
"LSL", "LSR", "ASR", "ROR", "RRX",
|
||
}
|
||
|
||
func (s Shift) String() string {
|
||
if s < 5 {
|
||
return shiftName[s]
|
||
}
|
||
return fmt.Sprintf("Shift(%d)", int(s))
|
||
}
|
||
|
||
// A RegShift is a register shifted by a constant.
|
||
type RegShift struct {
|
||
Reg Reg
|
||
Shift Shift
|
||
Count uint8
|
||
}
|
||
|
||
func (RegShift) IsArg() {}
|
||
|
||
func (r RegShift) String() string {
|
||
return fmt.Sprintf("%s %s #%d", r.Reg, r.Shift, r.Count)
|
||
}
|
||
|
||
// A RegShiftReg is a register shifted by a register.
|
||
type RegShiftReg struct {
|
||
Reg Reg
|
||
Shift Shift
|
||
RegCount Reg
|
||
}
|
||
|
||
func (RegShiftReg) IsArg() {}
|
||
|
||
func (r RegShiftReg) String() string {
|
||
return fmt.Sprintf("%s %s %s", r.Reg, r.Shift, r.RegCount)
|
||
}
|
||
|
||
// A PCRel describes a memory address (usually a code label)
|
||
// as a distance relative to the program counter.
|
||
// TODO(rsc): Define which program counter (PC+4? PC+8? PC?).
|
||
type PCRel int32
|
||
|
||
func (PCRel) IsArg() {}
|
||
|
||
func (r PCRel) String() string {
|
||
return fmt.Sprintf("PC%+#x", int32(r))
|
||
}
|
||
|
||
// An AddrMode is an ARM addressing mode.
|
||
type AddrMode uint8
|
||
|
||
const (
|
||
_ AddrMode = iota
|
||
AddrPostIndex // [R], X – use address R, set R = R + X
|
||
AddrPreIndex // [R, X]! – use address R + X, set R = R + X
|
||
AddrOffset // [R, X] – use address R + X
|
||
AddrLDM // R – [R] but formats as R, for LDM/STM only
|
||
AddrLDM_WB // R! - [R], X where X is instruction-specific amount, for LDM/STM only
|
||
)
|
||
|
||
// A Mem is a memory reference made up of a base R and index expression X.
|
||
// The effective memory address is R or R+X depending on AddrMode.
|
||
// The index expression is X = Sign*(Index Shift Count) + Offset,
|
||
// but in any instruction either Sign = 0 or Offset = 0.
|
||
type Mem struct {
|
||
Base Reg
|
||
Mode AddrMode
|
||
Sign int8
|
||
Index Reg
|
||
Shift Shift
|
||
Count uint8
|
||
Offset int16
|
||
}
|
||
|
||
func (Mem) IsArg() {}
|
||
|
||
func (m Mem) String() string {
|
||
R := m.Base.String()
|
||
X := ""
|
||
if m.Sign != 0 {
|
||
X = "+"
|
||
if m.Sign < 0 {
|
||
X = "-"
|
||
}
|
||
X += m.Index.String()
|
||
if m.Shift != ShiftLeft || m.Count != 0 {
|
||
X += fmt.Sprintf(", %s #%d", m.Shift, m.Count)
|
||
}
|
||
} else {
|
||
X = fmt.Sprintf("#%d", m.Offset)
|
||
}
|
||
|
||
switch m.Mode {
|
||
case AddrOffset:
|
||
if X == "#0" {
|
||
return fmt.Sprintf("[%s]", R)
|
||
}
|
||
return fmt.Sprintf("[%s, %s]", R, X)
|
||
case AddrPreIndex:
|
||
return fmt.Sprintf("[%s, %s]!", R, X)
|
||
case AddrPostIndex:
|
||
return fmt.Sprintf("[%s], %s", R, X)
|
||
case AddrLDM:
|
||
if X == "#0" {
|
||
return R
|
||
}
|
||
case AddrLDM_WB:
|
||
if X == "#0" {
|
||
return R + "!"
|
||
}
|
||
}
|
||
return fmt.Sprintf("[%s Mode(%d) %s]", R, int(m.Mode), X)
|
||
}
|