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