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
5package armasm
6
7import (
8	"bytes"
9	"fmt"
10)
11
12// A Mode is an instruction execution mode.
13type Mode int
14
15const (
16	_ Mode = iota
17	ModeARM
18	ModeThumb
19)
20
21func (m Mode) String() string {
22	switch m {
23	case ModeARM:
24		return "ARM"
25	case ModeThumb:
26		return "Thumb"
27	}
28	return fmt.Sprintf("Mode(%d)", int(m))
29}
30
31// An Op is an ARM opcode.
32type Op uint16
33
34// NOTE: The actual Op values are defined in tables.go.
35// They are chosen to simplify instruction decoding and
36// are not a dense packing from 0 to N, although the
37// density is high, probably at least 90%.
38
39func (op Op) String() string {
40	if op >= Op(len(opstr)) || opstr[op] == "" {
41		return fmt.Sprintf("Op(%d)", int(op))
42	}
43	return opstr[op]
44}
45
46// An Inst is a single instruction.
47type Inst struct {
48	Op   Op     // Opcode mnemonic
49	Enc  uint32 // Raw encoding bits.
50	Len  int    // Length of encoding in bytes.
51	Args Args   // Instruction arguments, in ARM manual order.
52}
53
54func (i Inst) String() string {
55	var buf bytes.Buffer
56	buf.WriteString(i.Op.String())
57	for j, arg := range i.Args {
58		if arg == nil {
59			break
60		}
61		if j == 0 {
62			buf.WriteString(" ")
63		} else {
64			buf.WriteString(", ")
65		}
66		buf.WriteString(arg.String())
67	}
68	return buf.String()
69}
70
71// An Args holds the instruction arguments.
72// If an instruction has fewer than 4 arguments,
73// the final elements in the array are nil.
74type Args [4]Arg
75
76// An Arg is a single instruction argument, one of these types:
77// Endian, Imm, Mem, PCRel, Reg, RegList, RegShift, RegShiftReg.
78type Arg interface {
79	IsArg()
80	String() string
81}
82
83type Float32Imm float32
84
85func (Float32Imm) IsArg() {}
86
87func (f Float32Imm) String() string {
88	return fmt.Sprintf("#%v", float32(f))
89}
90
91type Float64Imm float32
92
93func (Float64Imm) IsArg() {}
94
95func (f Float64Imm) String() string {
96	return fmt.Sprintf("#%v", float64(f))
97}
98
99// An Imm is an integer constant.
100type Imm uint32
101
102func (Imm) IsArg() {}
103
104func (i Imm) String() string {
105	return fmt.Sprintf("#%#x", uint32(i))
106}
107
108// An ImmAlt is an alternate encoding of an integer constant.
109type ImmAlt struct {
110	Val uint8
111	Rot uint8
112}
113
114func (ImmAlt) IsArg() {}
115
116func (i ImmAlt) Imm() Imm {
117	v := uint32(i.Val)
118	r := uint(i.Rot)
119	return Imm(v>>r | v<<(32-r))
120}
121
122func (i ImmAlt) String() string {
123	return fmt.Sprintf("#%#x, %d", i.Val, i.Rot)
124}
125
126// A Label is a text (code) address.
127type Label uint32
128
129func (Label) IsArg() {}
130
131func (i Label) String() string {
132	return fmt.Sprintf("%#x", uint32(i))
133}
134
135// A Reg is a single register.
136// The zero value denotes R0, not the absence of a register.
137type Reg uint8
138
139const (
140	R0 Reg = iota
141	R1
142	R2
143	R3
144	R4
145	R5
146	R6
147	R7
148	R8
149	R9
150	R10
151	R11
152	R12
153	R13
154	R14
155	R15
156
157	S0
158	S1
159	S2
160	S3
161	S4
162	S5
163	S6
164	S7
165	S8
166	S9
167	S10
168	S11
169	S12
170	S13
171	S14
172	S15
173	S16
174	S17
175	S18
176	S19
177	S20
178	S21
179	S22
180	S23
181	S24
182	S25
183	S26
184	S27
185	S28
186	S29
187	S30
188	S31
189
190	D0
191	D1
192	D2
193	D3
194	D4
195	D5
196	D6
197	D7
198	D8
199	D9
200	D10
201	D11
202	D12
203	D13
204	D14
205	D15
206	D16
207	D17
208	D18
209	D19
210	D20
211	D21
212	D22
213	D23
214	D24
215	D25
216	D26
217	D27
218	D28
219	D29
220	D30
221	D31
222
223	APSR
224	APSR_nzcv
225	FPSCR
226
227	SP = R13
228	LR = R14
229	PC = R15
230)
231
232func (Reg) IsArg() {}
233
234func (r Reg) String() string {
235	switch r {
236	case APSR:
237		return "APSR"
238	case APSR_nzcv:
239		return "APSR_nzcv"
240	case FPSCR:
241		return "FPSCR"
242	case SP:
243		return "SP"
244	case PC:
245		return "PC"
246	case LR:
247		return "LR"
248	}
249	if R0 <= r && r <= R15 {
250		return fmt.Sprintf("R%d", int(r-R0))
251	}
252	if S0 <= r && r <= S31 {
253		return fmt.Sprintf("S%d", int(r-S0))
254	}
255	if D0 <= r && r <= D31 {
256		return fmt.Sprintf("D%d", int(r-D0))
257	}
258	return fmt.Sprintf("Reg(%d)", int(r))
259}
260
261// A RegX represents a fraction of a multi-value register.
262// The Index field specifies the index number,
263// but the size of the fraction is not specified.
264// It must be inferred from the instruction and the register type.
265// For example, in a VMOV instruction, RegX{D5, 1} represents
266// the top 32 bits of the 64-bit D5 register.
267type RegX struct {
268	Reg   Reg
269	Index int
270}
271
272func (RegX) IsArg() {}
273
274func (r RegX) String() string {
275	return fmt.Sprintf("%s[%d]", r.Reg, r.Index)
276}
277
278// A RegList is a register list.
279// Bits at indexes x = 0 through 15 indicate whether the corresponding Rx register is in the list.
280type RegList uint16
281
282func (RegList) IsArg() {}
283
284func (r RegList) String() string {
285	var buf bytes.Buffer
286	fmt.Fprintf(&buf, "{")
287	sep := ""
288	for i := 0; i < 16; i++ {
289		if r&(1<<uint(i)) != 0 {
290			fmt.Fprintf(&buf, "%s%s", sep, Reg(i).String())
291			sep = ","
292		}
293	}
294	fmt.Fprintf(&buf, "}")
295	return buf.String()
296}
297
298// An Endian is the argument to the SETEND instruction.
299type Endian uint8
300
301const (
302	LittleEndian Endian = 0
303	BigEndian    Endian = 1
304)
305
306func (Endian) IsArg() {}
307
308func (e Endian) String() string {
309	if e != 0 {
310		return "BE"
311	}
312	return "LE"
313}
314
315// A Shift describes an ARM shift operation.
316type Shift uint8
317
318const (
319	ShiftLeft        Shift = 0 // left shift
320	ShiftRight       Shift = 1 // logical (unsigned) right shift
321	ShiftRightSigned Shift = 2 // arithmetic (signed) right shift
322	RotateRight      Shift = 3 // right rotate
323	RotateRightExt   Shift = 4 // right rotate through carry (Count will always be 1)
324)
325
326var shiftName = [...]string{
327	"LSL", "LSR", "ASR", "ROR", "RRX",
328}
329
330func (s Shift) String() string {
331	if s < 5 {
332		return shiftName[s]
333	}
334	return fmt.Sprintf("Shift(%d)", int(s))
335}
336
337// A RegShift is a register shifted by a constant.
338type RegShift struct {
339	Reg   Reg
340	Shift Shift
341	Count uint8
342}
343
344func (RegShift) IsArg() {}
345
346func (r RegShift) String() string {
347	return fmt.Sprintf("%s %s #%d", r.Reg, r.Shift, r.Count)
348}
349
350// A RegShiftReg is a register shifted by a register.
351type RegShiftReg struct {
352	Reg      Reg
353	Shift    Shift
354	RegCount Reg
355}
356
357func (RegShiftReg) IsArg() {}
358
359func (r RegShiftReg) String() string {
360	return fmt.Sprintf("%s %s %s", r.Reg, r.Shift, r.RegCount)
361}
362
363// A PCRel describes a memory address (usually a code label)
364// as a distance relative to the program counter.
365// TODO(rsc): Define which program counter (PC+4? PC+8? PC?).
366type PCRel int32
367
368func (PCRel) IsArg() {}
369
370func (r PCRel) String() string {
371	return fmt.Sprintf("PC%+#x", int32(r))
372}
373
374// An AddrMode is an ARM addressing mode.
375type AddrMode uint8
376
377const (
378	_             AddrMode = iota
379	AddrPostIndex          // [R], X – use address R, set R = R + X
380	AddrPreIndex           // [R, X]! – use address R + X, set R = R + X
381	AddrOffset             // [R, X] – use address R + X
382	AddrLDM                // R – [R] but formats as R, for LDM/STM only
383	AddrLDM_WB             // R! - [R], X where X is instruction-specific amount, for LDM/STM only
384)
385
386// A Mem is a memory reference made up of a base R and index expression X.
387// The effective memory address is R or R+X depending on AddrMode.
388// The index expression is X = Sign*(Index Shift Count) + Offset,
389// but in any instruction either Sign = 0 or Offset = 0.
390type Mem struct {
391	Base   Reg
392	Mode   AddrMode
393	Sign   int8
394	Index  Reg
395	Shift  Shift
396	Count  uint8
397	Offset int16
398}
399
400func (Mem) IsArg() {}
401
402func (m Mem) String() string {
403	R := m.Base.String()
404	X := ""
405	if m.Sign != 0 {
406		X = "+"
407		if m.Sign < 0 {
408			X = "-"
409		}
410		X += m.Index.String()
411		if m.Shift != ShiftLeft || m.Count != 0 {
412			X += fmt.Sprintf(", %s #%d", m.Shift, m.Count)
413		}
414	} else {
415		X = fmt.Sprintf("#%d", m.Offset)
416	}
417
418	switch m.Mode {
419	case AddrOffset:
420		if X == "#0" {
421			return fmt.Sprintf("[%s]", R)
422		}
423		return fmt.Sprintf("[%s, %s]", R, X)
424	case AddrPreIndex:
425		return fmt.Sprintf("[%s, %s]!", R, X)
426	case AddrPostIndex:
427		return fmt.Sprintf("[%s], %s", R, X)
428	case AddrLDM:
429		if X == "#0" {
430			return R
431		}
432	case AddrLDM_WB:
433		if X == "#0" {
434			return R + "!"
435		}
436	}
437	return fmt.Sprintf("[%s Mode(%d) %s]", R, int(m.Mode), X)
438}
439