1// Copyright 2017 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 arm64asm
6
7import (
8	"fmt"
9	"io"
10	"sort"
11	"strings"
12)
13
14// GoSyntax returns the Go assembler syntax for the instruction.
15// The syntax was originally defined by Plan 9.
16// The pc is the program counter of the instruction, used for
17// expanding PC-relative addresses into absolute ones.
18// The symname function queries the symbol table for the program
19// being disassembled. Given a target address it returns the name
20// and base address of the symbol containing the target, if any;
21// otherwise it returns "", 0.
22// The reader text should read from the text segment using text addresses
23// as offsets; it is used to display pc-relative loads as constant loads.
24func GoSyntax(inst Inst, pc uint64, symname func(uint64) (string, uint64), text io.ReaderAt) string {
25	if symname == nil {
26		symname = func(uint64) (string, uint64) { return "", 0 }
27	}
28
29	var args []string
30	for _, a := range inst.Args {
31		if a == nil {
32			break
33		}
34		args = append(args, plan9Arg(&inst, pc, symname, a))
35	}
36
37	op := inst.Op.String()
38
39	switch inst.Op {
40	case LDR, LDRB, LDRH, LDRSB, LDRSH, LDRSW:
41		// Check for PC-relative load.
42		if offset, ok := inst.Args[1].(PCRel); ok {
43			addr := pc + uint64(offset)
44			if _, ok := inst.Args[0].(Reg); !ok {
45				break
46			}
47			if s, base := symname(addr); s != "" && addr == base {
48				args[1] = fmt.Sprintf("$%s(SB)", s)
49			}
50		}
51	}
52
53	// Move addressing mode into opcode suffix.
54	suffix := ""
55	switch inst.Op {
56	case LDR, LDRB, LDRH, LDRSB, LDRSH, LDRSW, STR, STRB, STRH, STUR, STURB, STURH, LD1, ST1:
57		switch mem := inst.Args[1].(type) {
58		case MemImmediate:
59			switch mem.Mode {
60			case AddrOffset:
61				// no suffix
62			case AddrPreIndex:
63				suffix = ".W"
64			case AddrPostIndex, AddrPostReg:
65				suffix = ".P"
66			}
67		}
68
69	case STP, LDP:
70		switch mem := inst.Args[2].(type) {
71		case MemImmediate:
72			switch mem.Mode {
73			case AddrOffset:
74				// no suffix
75			case AddrPreIndex:
76				suffix = ".W"
77			case AddrPostIndex:
78				suffix = ".P"
79			}
80		}
81	}
82
83	switch inst.Op {
84	case BL:
85		return "CALL " + args[0]
86
87	case BLR:
88		r := inst.Args[0].(Reg)
89		regno := uint16(r) & 31
90		return fmt.Sprintf("CALL (R%d)", regno)
91
92	case RET:
93		if r, ok := inst.Args[0].(Reg); ok && r == X30 {
94			return "RET"
95		}
96
97	case B:
98		if cond, ok := inst.Args[0].(Cond); ok {
99			return "B" + cond.String() + " " + args[1]
100		}
101		return "JMP" + " " + args[0]
102
103	case BR:
104		r := inst.Args[0].(Reg)
105		regno := uint16(r) & 31
106		return fmt.Sprintf("JMP (R%d)", regno)
107
108	case MOV:
109		rno := -1
110		switch a := inst.Args[0].(type) {
111		case Reg:
112			rno = int(a)
113		case RegSP:
114			rno = int(a)
115		case RegisterWithArrangementAndIndex:
116			op = "VMOV"
117		case RegisterWithArrangement:
118			op = "VMOV"
119		}
120		if rno >= 0 && rno <= int(WZR) {
121			op = "MOVW"
122		} else if rno >= int(X0) && rno <= int(XZR) {
123			op = "MOVD"
124		}
125		if _, ok := inst.Args[1].(RegisterWithArrangementAndIndex); ok {
126			op = "VMOV"
127		}
128
129	case LDR, LDUR:
130		var rno uint16
131		if r, ok := inst.Args[0].(Reg); ok {
132			rno = uint16(r)
133		} else {
134			rno = uint16(inst.Args[0].(RegSP))
135		}
136		if rno <= uint16(WZR) {
137			op = "MOVWU" + suffix
138		} else if rno >= uint16(B0) && rno <= uint16(B31) {
139			op = "FMOVB" + suffix
140			args[0] = fmt.Sprintf("F%d", rno&31)
141		} else if rno >= uint16(H0) && rno <= uint16(H31) {
142			op = "FMOVH" + suffix
143			args[0] = fmt.Sprintf("F%d", rno&31)
144		} else if rno >= uint16(S0) && rno <= uint16(S31) {
145			op = "FMOVS" + suffix
146			args[0] = fmt.Sprintf("F%d", rno&31)
147		} else if rno >= uint16(D0) && rno <= uint16(D31) {
148			op = "FMOVD" + suffix
149			args[0] = fmt.Sprintf("F%d", rno&31)
150		} else if rno >= uint16(Q0) && rno <= uint16(Q31) {
151			op = "FMOVQ" + suffix
152			args[0] = fmt.Sprintf("F%d", rno&31)
153		} else {
154			op = "MOVD" + suffix
155		}
156
157	case LDRB:
158		op = "MOVBU" + suffix
159
160	case LDRH:
161		op = "MOVHU" + suffix
162
163	case LDRSW:
164		op = "MOVW" + suffix
165
166	case LDRSB:
167		if r, ok := inst.Args[0].(Reg); ok {
168			rno := uint16(r)
169			if rno <= uint16(WZR) {
170				op = "MOVBW" + suffix
171			} else {
172				op = "MOVB" + suffix
173			}
174		}
175	case LDRSH:
176		if r, ok := inst.Args[0].(Reg); ok {
177			rno := uint16(r)
178			if rno <= uint16(WZR) {
179				op = "MOVHW" + suffix
180			} else {
181				op = "MOVH" + suffix
182			}
183		}
184	case STR, STUR:
185		var rno uint16
186		if r, ok := inst.Args[0].(Reg); ok {
187			rno = uint16(r)
188		} else {
189			rno = uint16(inst.Args[0].(RegSP))
190		}
191		if rno <= uint16(WZR) {
192			op = "MOVW" + suffix
193		} else if rno >= uint16(B0) && rno <= uint16(B31) {
194			op = "FMOVB" + suffix
195			args[0] = fmt.Sprintf("F%d", rno&31)
196		} else if rno >= uint16(H0) && rno <= uint16(H31) {
197			op = "FMOVH" + suffix
198			args[0] = fmt.Sprintf("F%d", rno&31)
199		} else if rno >= uint16(S0) && rno <= uint16(S31) {
200			op = "FMOVS" + suffix
201			args[0] = fmt.Sprintf("F%d", rno&31)
202		} else if rno >= uint16(D0) && rno <= uint16(D31) {
203			op = "FMOVD" + suffix
204			args[0] = fmt.Sprintf("F%d", rno&31)
205		} else if rno >= uint16(Q0) && rno <= uint16(Q31) {
206			op = "FMOVQ" + suffix
207			args[0] = fmt.Sprintf("F%d", rno&31)
208		} else {
209			op = "MOVD" + suffix
210		}
211		args[0], args[1] = args[1], args[0]
212
213	case STRB, STURB:
214		op = "MOVB" + suffix
215		args[0], args[1] = args[1], args[0]
216
217	case STRH, STURH:
218		op = "MOVH" + suffix
219		args[0], args[1] = args[1], args[0]
220
221	case TBNZ, TBZ:
222		args[0], args[1], args[2] = args[2], args[0], args[1]
223
224	case MADD, MSUB, SMADDL, SMSUBL, UMADDL, UMSUBL:
225		if r, ok := inst.Args[0].(Reg); ok {
226			rno := uint16(r)
227			if rno <= uint16(WZR) {
228				op += "W"
229			}
230		}
231		args[2], args[3] = args[3], args[2]
232	case STLR:
233		if r, ok := inst.Args[0].(Reg); ok {
234			rno := uint16(r)
235			if rno <= uint16(WZR) {
236				op += "W"
237			}
238		}
239		args[0], args[1] = args[1], args[0]
240
241	case STLRB, STLRH:
242		args[0], args[1] = args[1], args[0]
243
244	case STLXR, STXR:
245		if r, ok := inst.Args[1].(Reg); ok {
246			rno := uint16(r)
247			if rno <= uint16(WZR) {
248				op += "W"
249			}
250		}
251		args[1], args[2] = args[2], args[1]
252
253	case STLXRB, STLXRH, STXRB, STXRH:
254		args[1], args[2] = args[2], args[1]
255
256	case BFI, BFXIL, SBFIZ, SBFX, UBFIZ, UBFX:
257		if r, ok := inst.Args[0].(Reg); ok {
258			rno := uint16(r)
259			if rno <= uint16(WZR) {
260				op += "W"
261			}
262		}
263		args[1], args[2], args[3] = args[3], args[1], args[2]
264
265	case LDAXP, LDXP:
266		if r, ok := inst.Args[0].(Reg); ok {
267			rno := uint16(r)
268			if rno <= uint16(WZR) {
269				op += "W"
270			}
271		}
272		args[0] = fmt.Sprintf("(%s, %s)", args[0], args[1])
273		args[1] = args[2]
274		return op + " " + args[1] + ", " + args[0]
275
276	case STP, LDP:
277		args[0] = fmt.Sprintf("(%s, %s)", args[0], args[1])
278		args[1] = args[2]
279
280		rno, ok := inst.Args[0].(Reg)
281		if !ok {
282			rno = Reg(inst.Args[0].(RegSP))
283		}
284		if rno <= WZR {
285			op = op + "W"
286		} else if rno >= S0 && rno <= S31 {
287			op = "F" + op + "S"
288		} else if rno >= D0 && rno <= D31 {
289			op = "F" + op + "D"
290		} else if rno >= Q0 && rno <= Q31 {
291			op = "F" + op + "Q"
292		}
293		op = op + suffix
294		if inst.Op.String() == "STP" {
295			return op + " " + args[0] + ", " + args[1]
296		} else {
297			return op + " " + args[1] + ", " + args[0]
298		}
299
300	case STLXP, STXP:
301		if r, ok := inst.Args[1].(Reg); ok {
302			rno := uint16(r)
303			if rno <= uint16(WZR) {
304				op += "W"
305			}
306		}
307		args[1] = fmt.Sprintf("(%s, %s)", args[1], args[2])
308		args[2] = args[3]
309		return op + " " + args[1] + ", " + args[2] + ", " + args[0]
310
311	case FCCMP, FCCMPE:
312		args[0], args[1] = args[1], args[0]
313		fallthrough
314
315	case FCMP, FCMPE:
316		if _, ok := inst.Args[1].(Imm); ok {
317			args[1] = "$(0.0)"
318		}
319		fallthrough
320
321	case FADD, FSUB, FMUL, FNMUL, FDIV, FMAX, FMIN, FMAXNM, FMINNM, FCSEL, FMADD, FMSUB, FNMADD, FNMSUB:
322		if strings.HasSuffix(op, "MADD") || strings.HasSuffix(op, "MSUB") {
323			args[2], args[3] = args[3], args[2]
324		}
325		if r, ok := inst.Args[0].(Reg); ok {
326			rno := uint16(r)
327			if rno >= uint16(S0) && rno <= uint16(S31) {
328				op = fmt.Sprintf("%sS", op)
329			} else if rno >= uint16(D0) && rno <= uint16(D31) {
330				op = fmt.Sprintf("%sD", op)
331			}
332		}
333
334	case FCVT:
335		for i := 1; i >= 0; i-- {
336			if r, ok := inst.Args[i].(Reg); ok {
337				rno := uint16(r)
338				if rno >= uint16(H0) && rno <= uint16(H31) {
339					op = fmt.Sprintf("%sH", op)
340				} else if rno >= uint16(S0) && rno <= uint16(S31) {
341					op = fmt.Sprintf("%sS", op)
342				} else if rno >= uint16(D0) && rno <= uint16(D31) {
343					op = fmt.Sprintf("%sD", op)
344				}
345			}
346		}
347
348	case FABS, FNEG, FSQRT, FRINTN, FRINTP, FRINTM, FRINTZ, FRINTA, FRINTX, FRINTI:
349		if r, ok := inst.Args[1].(Reg); ok {
350			rno := uint16(r)
351			if rno >= uint16(S0) && rno <= uint16(S31) {
352				op = fmt.Sprintf("%sS", op)
353			} else if rno >= uint16(D0) && rno <= uint16(D31) {
354				op = fmt.Sprintf("%sD", op)
355			}
356		}
357
358	case FCVTZS, FCVTZU, SCVTF, UCVTF:
359		if _, ok := inst.Args[2].(Imm); !ok {
360			for i := 1; i >= 0; i-- {
361				if r, ok := inst.Args[i].(Reg); ok {
362					rno := uint16(r)
363					if rno >= uint16(S0) && rno <= uint16(S31) {
364						op = fmt.Sprintf("%sS", op)
365					} else if rno >= uint16(D0) && rno <= uint16(D31) {
366						op = fmt.Sprintf("%sD", op)
367					} else if rno <= uint16(WZR) {
368						op += "W"
369					}
370				}
371			}
372		}
373
374	case FMOV:
375		for i := 0; i <= 1; i++ {
376			if r, ok := inst.Args[i].(Reg); ok {
377				rno := uint16(r)
378				if rno >= uint16(S0) && rno <= uint16(S31) {
379					op = fmt.Sprintf("%sS", op)
380					break
381				} else if rno >= uint16(D0) && rno <= uint16(D31) {
382					op = fmt.Sprintf("%sD", op)
383					break
384				}
385			}
386		}
387
388	case SYSL:
389		op1 := int(inst.Args[1].(Imm).Imm)
390		cn := int(inst.Args[2].(Imm_c))
391		cm := int(inst.Args[3].(Imm_c))
392		op2 := int(inst.Args[4].(Imm).Imm)
393		sysregno := int32(op1<<16 | cn<<12 | cm<<8 | op2<<5)
394		args[1] = fmt.Sprintf("$%d", sysregno)
395		return op + " " + args[1] + ", " + args[0]
396
397	case CBNZ, CBZ:
398		if r, ok := inst.Args[0].(Reg); ok {
399			rno := uint16(r)
400			if rno <= uint16(WZR) {
401				op += "W"
402			}
403		}
404		args[0], args[1] = args[1], args[0]
405
406	case ADR, ADRP:
407		addr := int64(inst.Args[1].(PCRel))
408		args[1] = fmt.Sprintf("%d(PC)", addr)
409
410	case MSR:
411		args[0] = inst.Args[0].String()
412
413	case ST1:
414		op = fmt.Sprintf("V%s", op) + suffix
415		args[0], args[1] = args[1], args[0]
416
417	case LD1:
418		op = fmt.Sprintf("V%s", op) + suffix
419
420	case UMOV:
421		op = "VMOV"
422	case NOP:
423		op = "NOOP"
424
425	default:
426		index := sort.SearchStrings(noSuffixOpSet, op)
427		if !(index < len(noSuffixOpSet) && noSuffixOpSet[index] == op) {
428			rno := -1
429			switch a := inst.Args[0].(type) {
430			case Reg:
431				rno = int(a)
432			case RegSP:
433				rno = int(a)
434			case RegisterWithArrangement:
435				op = fmt.Sprintf("V%s", op)
436			}
437
438			if rno >= int(B0) && rno <= int(Q31) && !strings.HasPrefix(op, "F") {
439				op = fmt.Sprintf("V%s", op)
440			}
441			if rno >= 0 && rno <= int(WZR) {
442				// Add "w" to opcode suffix.
443				op += "W"
444			}
445		}
446		op = op + suffix
447	}
448
449	// conditional instructions, replace args.
450	if _, ok := inst.Args[3].(Cond); ok {
451		if _, ok := inst.Args[2].(Reg); ok {
452			args[1], args[2] = args[2], args[1]
453		} else {
454			args[0], args[2] = args[2], args[0]
455		}
456	}
457	// Reverse args, placing dest last.
458	for i, j := 0, len(args)-1; i < j; i, j = i+1, j-1 {
459		args[i], args[j] = args[j], args[i]
460	}
461
462	if args != nil {
463		op += " " + strings.Join(args, ", ")
464	}
465
466	return op
467}
468
469// No need add "W" to opcode suffix.
470// Opcode must be inserted in ascending order.
471var noSuffixOpSet = strings.Fields(`
472AESD
473AESE
474AESIMC
475AESMC
476CRC32B
477CRC32CB
478CRC32CH
479CRC32CW
480CRC32CX
481CRC32H
482CRC32W
483CRC32X
484LDARB
485LDARH
486LDAXRB
487LDAXRH
488LDTRH
489LDXRB
490LDXRH
491SHA1C
492SHA1H
493SHA1M
494SHA1P
495SHA1SU0
496SHA1SU1
497SHA256H
498SHA256H2
499SHA256SU0
500SHA256SU1
501`)
502
503// floating point instructions without "F" prefix.
504var fOpsWithoutFPrefix = map[Op]bool{
505	LDP: true,
506	STP: true,
507}
508
509func plan9Arg(inst *Inst, pc uint64, symname func(uint64) (string, uint64), arg Arg) string {
510	switch a := arg.(type) {
511	case Imm:
512		return fmt.Sprintf("$%d", uint32(a.Imm))
513
514	case Imm64:
515		return fmt.Sprintf("$%d", int64(a.Imm))
516
517	case ImmShift:
518		if a.shift == 0 {
519			return fmt.Sprintf("$%d", a.imm)
520		}
521		return fmt.Sprintf("$(%d<<%d)", a.imm, a.shift)
522
523	case PCRel:
524		addr := int64(pc) + int64(a)
525		if s, base := symname(uint64(addr)); s != "" && uint64(addr) == base {
526			return fmt.Sprintf("%s(SB)", s)
527		}
528		return fmt.Sprintf("%d(PC)", a/4)
529
530	case Reg:
531		regenum := uint16(a)
532		regno := uint16(a) & 31
533
534		if regenum >= uint16(B0) && regenum <= uint16(Q31) {
535			if strings.HasPrefix(inst.Op.String(), "F") || strings.HasSuffix(inst.Op.String(), "CVTF") || fOpsWithoutFPrefix[inst.Op] {
536				// FP registers are the same ones as SIMD registers
537				// Print Fn for scalar variant to align with assembler (e.g., FCVT, SCVTF, UCVTF, etc.)
538				return fmt.Sprintf("F%d", regno)
539			} else {
540				// Print Vn to align with assembler (e.g., SHA256H)
541				return fmt.Sprintf("V%d", regno)
542			}
543
544		}
545		if regno == 31 {
546			return "ZR"
547		}
548		return fmt.Sprintf("R%d", regno)
549
550	case RegSP:
551		regno := uint16(a) & 31
552		if regno == 31 {
553			return "RSP"
554		}
555		return fmt.Sprintf("R%d", regno)
556
557	case RegExtshiftAmount:
558		reg := ""
559		regno := uint16(a.reg) & 31
560		if regno == 31 {
561			reg = "ZR"
562		} else {
563			reg = fmt.Sprintf("R%d", uint16(a.reg)&31)
564		}
565		extshift := ""
566		amount := ""
567		if a.extShift != ExtShift(0) {
568			switch a.extShift {
569			default:
570				extshift = "." + a.extShift.String()
571
572			case lsl:
573				extshift = "<<"
574				amount = fmt.Sprintf("%d", a.amount)
575				return reg + extshift + amount
576
577			case lsr:
578				extshift = ">>"
579				amount = fmt.Sprintf("%d", a.amount)
580				return reg + extshift + amount
581
582			case asr:
583				extshift = "->"
584				amount = fmt.Sprintf("%d", a.amount)
585				return reg + extshift + amount
586			case ror:
587				extshift = "@>"
588				amount = fmt.Sprintf("%d", a.amount)
589				return reg + extshift + amount
590			}
591			if a.amount != 0 {
592				amount = fmt.Sprintf("<<%d", a.amount)
593			}
594		}
595		return reg + extshift + amount
596
597	case MemImmediate:
598		off := ""
599		base := ""
600		regno := uint16(a.Base) & 31
601		if regno == 31 {
602			base = "(RSP)"
603		} else {
604			base = fmt.Sprintf("(R%d)", regno)
605		}
606		if a.imm != 0 && a.Mode != AddrPostReg {
607			off = fmt.Sprintf("%d", a.imm)
608		} else if a.Mode == AddrPostReg {
609			postR := fmt.Sprintf("(R%d)", a.imm)
610			return base + postR
611		}
612		return off + base
613
614	case MemExtend:
615		base := ""
616		index := ""
617		indexreg := ""
618		regno := uint16(a.Base) & 31
619		if regno == 31 {
620			base = "(RSP)"
621		} else {
622			base = fmt.Sprintf("(R%d)", regno)
623		}
624		regno = uint16(a.Index) & 31
625		if regno == 31 {
626			indexreg = "ZR"
627		} else {
628			indexreg = fmt.Sprintf("R%d", regno)
629		}
630
631		if a.Extend == lsl {
632			// Refer to ARM reference manual, for byte load/store(register), the index
633			// shift amount must be 0, encoded in "S" as 0 if omitted, or as 1 if present.
634			// a.Amount indicates the index shift amount, encoded in "S" field.
635			// a.ShiftMustBeZero is set true indicates the index shift amount must be 0.
636			// When a.ShiftMustBeZero is true, GNU syntax prints "[Xn, Xm lsl #0]" if "S"
637			// equals to 1, or prints "[Xn, Xm]" if "S" equals to 0.
638			if a.Amount != 0 && !a.ShiftMustBeZero {
639				index = fmt.Sprintf("(%s<<%d)", indexreg, a.Amount)
640			} else if a.ShiftMustBeZero && a.Amount == 1 {
641				// When a.ShiftMustBeZero is ture, Go syntax prints "(Rm<<0)" if "a.Amount"
642				// equals to 1.
643				index = fmt.Sprintf("(%s<<0)", indexreg)
644			} else {
645				index = fmt.Sprintf("(%s)", indexreg)
646			}
647		} else {
648			if a.Amount != 0 && !a.ShiftMustBeZero {
649				index = fmt.Sprintf("(%s.%s<<%d)", indexreg, a.Extend.String(), a.Amount)
650			} else {
651				index = fmt.Sprintf("(%s.%s)", indexreg, a.Extend.String())
652			}
653		}
654
655		return base + index
656
657	case Cond:
658		switch arg.String() {
659		case "CS":
660			return "HS"
661		case "CC":
662			return "LO"
663		}
664
665	case Imm_clrex:
666		return fmt.Sprintf("$%d", uint32(a))
667
668	case Imm_dcps:
669		return fmt.Sprintf("$%d", uint32(a))
670
671	case Imm_option:
672		return fmt.Sprintf("$%d", uint8(a))
673
674	case Imm_hint:
675		return fmt.Sprintf("$%d", uint8(a))
676
677	case Imm_fp:
678		var s, pre, numerator, denominator int16
679		var result float64
680		if a.s == 0 {
681			s = 1
682		} else {
683			s = -1
684		}
685		pre = s * int16(16+a.pre)
686		if a.exp > 0 {
687			numerator = (pre << uint8(a.exp))
688			denominator = 16
689		} else {
690			numerator = pre
691			denominator = (16 << uint8(-1*a.exp))
692		}
693		result = float64(numerator) / float64(denominator)
694		return strings.TrimRight(fmt.Sprintf("$%f", result), "0")
695
696	case RegisterWithArrangement:
697		result := a.r.String()
698		arrange := a.a.String()
699		c := []rune(arrange)
700		switch len(c) {
701		case 3:
702			c[1], c[2] = c[2], c[1] // .8B -> .B8
703		case 4:
704			c[1], c[2], c[3] = c[3], c[1], c[2] // 16B -> B16
705		}
706		arrange = string(c)
707		result += arrange
708		if a.cnt > 0 {
709			result = "[" + result
710			for i := 1; i < int(a.cnt); i++ {
711				cur := V0 + Reg((uint16(a.r)-uint16(V0)+uint16(i))&31)
712				result += ", " + cur.String() + arrange
713			}
714			result += "]"
715		}
716		return result
717
718	case RegisterWithArrangementAndIndex:
719		result := a.r.String()
720		arrange := a.a.String()
721		result += arrange
722		if a.cnt > 1 {
723			result = "[" + result
724			for i := 1; i < int(a.cnt); i++ {
725				cur := V0 + Reg((uint16(a.r)-uint16(V0)+uint16(i))&31)
726				result += ", " + cur.String() + arrange
727			}
728			result += "]"
729		}
730		return fmt.Sprintf("%s[%d]", result, a.index)
731
732	case Systemreg:
733		return fmt.Sprintf("$%d", uint32(a.op0&1)<<14|uint32(a.op1&7)<<11|uint32(a.cn&15)<<7|uint32(a.cm&15)<<3|uint32(a.op2)&7)
734
735	case Imm_prfop:
736		if strings.Contains(a.String(), "#") {
737			return fmt.Sprintf("$%d", a)
738		}
739	}
740
741	return strings.ToUpper(arg.String())
742}
743