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	"strings"
11)
12
13var saveDot = strings.NewReplacer(
14	".F16", "_dot_F16",
15	".F32", "_dot_F32",
16	".F64", "_dot_F64",
17	".S32", "_dot_S32",
18	".U32", "_dot_U32",
19	".FXS", "_dot_S",
20	".FXU", "_dot_U",
21	".32", "_dot_32",
22)
23
24// GNUSyntax returns the GNU assembler syntax for the instruction, as defined by GNU binutils.
25// This form typically matches the syntax defined in the ARM Reference Manual.
26func GNUSyntax(inst Inst) string {
27	var buf bytes.Buffer
28	op := inst.Op.String()
29	op = saveDot.Replace(op)
30	op = strings.Replace(op, ".", "", -1)
31	op = strings.Replace(op, "_dot_", ".", -1)
32	op = strings.ToLower(op)
33	buf.WriteString(op)
34	sep := " "
35	for i, arg := range inst.Args {
36		if arg == nil {
37			break
38		}
39		text := gnuArg(&inst, i, arg)
40		if text == "" {
41			continue
42		}
43		buf.WriteString(sep)
44		sep = ", "
45		buf.WriteString(text)
46	}
47	return buf.String()
48}
49
50func gnuArg(inst *Inst, argIndex int, arg Arg) string {
51	switch inst.Op &^ 15 {
52	case LDRD_EQ, LDREXD_EQ, STRD_EQ:
53		if argIndex == 1 {
54			// second argument in consecutive pair not printed
55			return ""
56		}
57	case STREXD_EQ:
58		if argIndex == 2 {
59			// second argument in consecutive pair not printed
60			return ""
61		}
62	}
63
64	switch arg := arg.(type) {
65	case Imm:
66		switch inst.Op &^ 15 {
67		case BKPT_EQ:
68			return fmt.Sprintf("%#04x", uint32(arg))
69		case SVC_EQ:
70			return fmt.Sprintf("%#08x", uint32(arg))
71		}
72		return fmt.Sprintf("#%d", int32(arg))
73
74	case ImmAlt:
75		return fmt.Sprintf("#%d, %d", arg.Val, arg.Rot)
76
77	case Mem:
78		R := gnuArg(inst, -1, arg.Base)
79		X := ""
80		if arg.Sign != 0 {
81			X = ""
82			if arg.Sign < 0 {
83				X = "-"
84			}
85			X += gnuArg(inst, -1, arg.Index)
86			if arg.Shift == ShiftLeft && arg.Count == 0 {
87				// nothing
88			} else if arg.Shift == RotateRightExt {
89				X += ", rrx"
90			} else {
91				X += fmt.Sprintf(", %s #%d", strings.ToLower(arg.Shift.String()), arg.Count)
92			}
93		} else {
94			X = fmt.Sprintf("#%d", arg.Offset)
95		}
96
97		switch arg.Mode {
98		case AddrOffset:
99			if X == "#0" {
100				return fmt.Sprintf("[%s]", R)
101			}
102			return fmt.Sprintf("[%s, %s]", R, X)
103		case AddrPreIndex:
104			return fmt.Sprintf("[%s, %s]!", R, X)
105		case AddrPostIndex:
106			return fmt.Sprintf("[%s], %s", R, X)
107		case AddrLDM:
108			if X == "#0" {
109				return R
110			}
111		case AddrLDM_WB:
112			if X == "#0" {
113				return R + "!"
114			}
115		}
116		return fmt.Sprintf("[%s Mode(%d) %s]", R, int(arg.Mode), X)
117
118	case PCRel:
119		return fmt.Sprintf(".%+#x", int32(arg)+4)
120
121	case Reg:
122		switch inst.Op &^ 15 {
123		case LDREX_EQ:
124			if argIndex == 0 {
125				return fmt.Sprintf("r%d", int32(arg))
126			}
127		}
128		switch arg {
129		case R10:
130			return "sl"
131		case R11:
132			return "fp"
133		case R12:
134			return "ip"
135		}
136
137	case RegList:
138		var buf bytes.Buffer
139		fmt.Fprintf(&buf, "{")
140		sep := ""
141		for i := 0; i < 16; i++ {
142			if arg&(1<<uint(i)) != 0 {
143				fmt.Fprintf(&buf, "%s%s", sep, gnuArg(inst, -1, Reg(i)))
144				sep = ", "
145			}
146		}
147		fmt.Fprintf(&buf, "}")
148		return buf.String()
149
150	case RegShift:
151		if arg.Shift == ShiftLeft && arg.Count == 0 {
152			return gnuArg(inst, -1, arg.Reg)
153		}
154		if arg.Shift == RotateRightExt {
155			return gnuArg(inst, -1, arg.Reg) + ", rrx"
156		}
157		return fmt.Sprintf("%s, %s #%d", gnuArg(inst, -1, arg.Reg), strings.ToLower(arg.Shift.String()), arg.Count)
158
159	case RegShiftReg:
160		return fmt.Sprintf("%s, %s %s", gnuArg(inst, -1, arg.Reg), strings.ToLower(arg.Shift.String()), gnuArg(inst, -1, arg.RegCount))
161
162	}
163	return strings.ToLower(arg.String())
164}
165