// Copyright 2024 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 s390xasm

import (
	"fmt"
	"strconv"
	"strings"
)

var vectorSize = map[int]string{0: "B", 1: "H", 2: "F", 3: "G", 4: "Q"}
var vectorCS = map[int]string{0: "BS", 1: "HS", 2: "FS", 3: "GS"}

// GoSyntax returns the Go assembler syntax for the instruction.
// The syntax was originally defined by Plan 9.
// The inst relates to single instruction.
// The pc is the program counter of the instruction, used for
// expanding PC-relative addresses into absolute ones.
// The symname function queries the symbol table for the program
// being disassembled. Given a target address it returns the name
// and base address of the symbol containing the target, if any;
// otherwise it returns "", 0.
func GoSyntax(inst Inst, pc uint64, symname func(uint64) (string, uint64)) string {
	if symname == nil {
		symname = func(uint64) (string, uint64) { return "", 0 }
	}

	var args []string
	opString := inst.Op.String()
	op := strings.ToUpper(opString)
	for i := 0; i < len(inst.Args); i++ {
		if inst.Args[i] == nil {
			break
		}
		switch inst.Args[i].(type) {
		case Disp12, Disp20:
			var temp []string
			switch inst.Args[i+1].(type) {
			case Index: // D(X,B)
				for j := 0; j < 3; j++ {
					temp = append(temp, plan9Arg(&inst, pc, symname, inst.Args[i+j]))
				}
				args = append(args, mem_operandx(temp))
				i = i + 2
			case Base: // D(B)
				for j := 0; j < 2; j++ {
					temp = append(temp, plan9Arg(&inst, pc, symname, inst.Args[i+j]))
				}
				args = append(args, mem_operand(temp))
				i = i + 1
			case VReg: // D(B)
				for j := 0; j < 3; j++ {
					temp = append(temp, plan9Arg(&inst, pc, symname, inst.Args[i+j]))
				}
				args = append(args, mem_operandv(temp))
				i = i + 2
			case Len: // D(L,B)
				for j := 0; j < 3; j++ {
					temp = append(temp, plan9Arg(&inst, pc, symname, inst.Args[i+j]))
				}
				ar1, ar2 := mem_operandl(temp)
				args = append(args, ar1, ar2)
				i = i + 2
			default: // D(R,B)
				for j := 0; j < 3; j++ {
					temp = append(temp, plan9Arg(&inst, pc, symname, inst.Args[i+j]))
				}
				args = append(args, mem_operandx(temp))
				i = i + 2
			}
		default:
			args = append(args, plan9Arg(&inst, pc, symname, inst.Args[i]))
		}
	}
	if strings.HasPrefix(op, "V") || strings.Contains(op, "WFC") || strings.Contains(op, "WFK") {
		args = args[:len(args)-1]
	}

	switch inst.Op {
	default:
		switch len(args) {
		case 0:
			return op
		case 1:
			return fmt.Sprintf("%s %s", op, args[0])
		case 2:
			if reverseOperandOrder(inst.Op) {
				args[0], args[1] = args[1], args[0]
			}
		case 3:
			if reverseOperandOrder(inst.Op) {
				args[0], args[2] = args[2], args[0]
			} else if reverseAllOperands(inst.Op) {
				args[0], args[1], args[2] = args[1], args[2], args[0]
			}
		case 4:
			if reverseOperandOrder(inst.Op) {
				args[0], args[3] = args[3], args[0]
			} else if reverseAllOperands(inst.Op) {
				args[0], args[1], args[2], args[3] = args[1], args[2], args[3], args[0]
			}
		}
	case LCGR, LCGFR:
		switch inst.Op {
		case LCGR:
			op = "NEG"
		case LCGFR:
			op = "NEGW"
		}
		if args[0] == args[1] {
			args = args[:1]
		} else {
			args[0], args[1] = args[1], args[0]
		}
	case LD, LE, LG, LGF, LLGF, LGH, LLGH, LGB, LLGC, LDY, LEY, LRVG, LRV, LRVH:
		args[0], args[1] = args[1], args[0]
		switch inst.Op {
		case LG:
			op = "MOVD"
		case LGF:
			op = "MOVW"
		case LLGF:
			op = "MOVWZ"
		case LGH:
			op = "MOVH"
		case LLGH:
			op = "MOVHZ"
		case LGB:
			op = "MOVB"
		case LLGC:
			op = "MOVBZ"
		case LDY, LD:
			op = "FMOVD"
		case LEY, LE:
			op = "FMOVS"
		case LRVG:
			op = "MOVDBR"
		case LRV:
			op = "MOVWBR"
		case LRVH:
			op = "MOVHBR"
		}
	case LA, LAY, LARL:
		args[0], args[1] = args[1], args[0]
		op = "MOVD"

	case LAA, LAAG, LAAL, LAALG, LAN, LANG, LAX, LAXG, LAO, LAOG:
		args[0], args[1] = args[1], args[0]
	case LM, LMY, LMG: // Load Multiple
		switch inst.Op {
		case LM, LMY:
			op = "LMY"
		}
		args[0], args[1], args[2] = args[2], args[0], args[1]

	case STM, STMY, STMG: // Store Multiple
		switch inst.Op {
		case STM, STMY:
			op = "STMY"
		}
	case ST, STY, STG, STHY, STCY, STRVG, STRV:
		switch inst.Op {
		case ST, STY:
			op = "MOVW"
		case STHY:
			op = "MOVH"
		case STCY:
			op = "MOVB"
		case STG:
			op = "MOVD"
		case STRVG:
			op = "MOVDBR"
		case STRV:
			op = "MOVWBR"
		}
	case LGR, LGFR, LGHR, LGBR, LLGFR, LLGHR, LLGCR, LRVGR, LRVR, LDR:
		switch inst.Op {
		case LGR:
			op = "MOVD"
		case LGFR:
			op = "MOVW"
		case LGHR:
			op = "MOVH"
		case LGBR:
			op = "MOVB"
		case LLGFR:
			op = "MOVWZ"
		case LLGHR:
			op = "MOVHZ"
		case LLGCR:
			op = "MOVBZ"
		case LRVGR:
			op = "MOVDBR"
		case LRVR:
			op = "MOVWBR"
		case LDR:
			op = "FMOVD"
		}
		args[0], args[1] = args[1], args[0]
	case LZDR:
		op = "FMOVD"
		return op + " " + "$0" + ", " + args[0]
	case LZER:
		op = "FMOVS"
		return op + " " + "$0" + ", " + args[0]
	case STD, STDY, STE, STEY:
		switch inst.Op {
		case STD, STDY:
			op = "FMOVD"
		case STE, STEY:
			op = "FMOVS"
		}

	case LGHI, LLILH, LLIHL, LLIHH, LGFI, LLILF, LLIHF:
		switch inst.Op {
		case LGFI:
			op = "MOVW"
		case LGHI:
			num, err := strconv.ParseInt(args[1][1:], 10, 16)
			if err != nil {
				return fmt.Sprintf("plan9Arg: error in converting ParseInt:%s", err)
			}
			if num == int64(int8(num)) {
				op = "MOVB"
			} else {
				op = "MOVH"
			}
		default:
			op = "MOVD"
		}
		args[0], args[1] = args[1], args[0]
	case ARK, AGRK, ALGRK:
		switch inst.Op {
		case ARK:
			op = "ADDW"
		case AGRK:
			op = "ADD"
		case ALGRK:
			op = "ADDC"
		}
		if args[0] == args[1] {
			args[0], args[1] = args[2], args[0]
			args = args[:2]
		} else {
			args[0], args[2] = args[2], args[0]
		}
	case AGHIK, AHIK, ALGHSIK:
		num, err := strconv.ParseInt(args[2][1:], 10, 32)
		if err != nil {
			return fmt.Sprintf("plan9Arg: error in converting ParseInt:%s", err)
		}
		switch inst.Op {
		case AGHIK:
			if num < 0 {
				op = "SUB"
				args[2] = args[2][:1] + args[2][2:]
			} else {
				op = "ADD"
			}
		case AHIK:
			op = "ADDW"
		case ALGHSIK:
			if num < 0 {
				op = "SUBC"
				args[2] = args[2][:1] + args[2][2:]
			} else {
				op = "ADDC"
			}
		}
		args[0], args[2] = args[2], args[0]
	case AGHI, AHI, AGFI, AFI, AR, ALCGR:
		num, err := strconv.ParseInt(args[1][1:], 10, 32)
		if err != nil {
			return fmt.Sprintf("plan9Arg: error in converting ParseInt:%s", err)
		}
		switch inst.Op {
		case AGHI, AGFI:
			if num < 0 {
				op = "SUB"
				args[1] = args[1][:1] + args[1][2:]
			} else {
				op = "ADD"
			}
		case AHI, AFI, AR:
			op = "ADDW"
		case ALCGR:
			op = "ADDE"
		}
		args[0], args[1] = args[1], args[0]
	case AEBR, ADBR, DDBR, DEBR, MDBR, MEEBR, SDBR, SEBR, LPDBR, LNDBR, LPDFR, LNDFR, LCDFR, LCEBR, LEDBR, LDEBR, SQDBR, SQEBR:
		switch inst.Op {
		case AEBR:
			op = "FADDS"
		case ADBR:
			op = "FADD"
		case DDBR:
			op = "FDIV"
		case DEBR:
			op = "FDIVS"
		case MDBR:
			op = "FMUL"
		case MEEBR:
			op = "FMULS"
		case SDBR:
			op = "FSUB"
		case SEBR:
			op = "FSUBS"
		case LPDBR:
			op = "FABS"
		case LNDBR:
			op = "FNABS"
		case LCDFR:
			op = "FNEG"
		case LCEBR:
			op = "FNEGS"
		case SQDBR:
			op = "FSQRT"
		case SQEBR:
			op = "FSQRTS"
		}
		args[0], args[1] = args[1], args[0]
	case SR, SGR, SLGR, SLFI:
		switch inst.Op {
		case SR, SLFI:
			op = "SUBW"
		case SGR:
			op = "SUB"
		case SLGR:
			op = "SUBC"
		}
		args[0], args[1] = args[1], args[0]
	case SGRK, SLGRK, SRK:
		switch inst.Op {
		case SGRK:
			op = "SUB"
		case SLGRK:
			op = "SUBC"
		case SRK:
			op = "SUBW"
		}
		if args[0] == args[1] {
			args[0], args[1] = args[2], args[0]
			args = args[:2]
		} else {
			args[0], args[2] = args[2], args[0]
		}
	case SLBGR:
		op = "SUBE"
		args[0], args[1] = args[1], args[0]
	case MADBR:
		op = "FMADD"
		args[0], args[1], args[2] = args[1], args[2], args[0]
	case VFM:
		op = "WFMDB"
		args[0], args[1], args[2] = args[1], args[2], args[0]
		args = args[0:3]
	case VFS:
		op = "WFSDB"
		args[0], args[2] = args[2], args[0]
		args = args[0:3]
	case MSGFR, MHI, MSFI, MSGFI:
		switch inst.Op {
		case MSGFR, MHI, MSFI:
			op = "MULLW"
		case MSGFI:
			op = "MULLD"
		}
		args[0], args[1] = args[1], args[0]

	case NGR, NR, NILL, NILF, NILH, OGR, OR, OILL, OILF, OILH, XGR, XR, XILF:
		op = bitwise_op(inst.Op)
		args[0], args[1] = args[1], args[0]
		switch inst.Op {
		case NILL:
			if int(inst.Args[1].(Sign16)) < 0 {
				op = "ANDW"
			}

		case NILF:
			if int(inst.Args[1].(Sign32)) < 0 {
				op = "AND"
			}
		case OILF:
			if int(inst.Args[1].(Sign32)) < 0 {
				op = "ORW"
			}
		case XILF:
			if int(inst.Args[1].(Sign32)) < 0 {
				op = "XORW"
			}
		}

	case NGRK, NRK, OGRK, ORK, XGRK, XRK: // opcode R1, R2, R3
		op = bitwise_op(inst.Op)
		args[0], args[1], args[2] = args[1], args[2], args[0]
	case SLLG, SRLG, SLLK, SRLK, RLL, RLLG, SRAK, SRAG:
		switch inst.Op {
		case SLLG:
			op = "SLD"
		case SRLG:
			op = "SRD"
		case SLLK:
			op = "SLW"
		case SRLK:
			op = "SRW"
		case SRAK:
			op = "SRAW"
		case SRAG:
			op = "SRAD"
		}
		args[0], args[2] = args[2], args[0]
	case TRAP2, SVC:
		op = "SYSALL"
	case CR, CLR, CGR, CLGR, KDBR, CDBR, CEBR, CGHI, CHI, CGFI, CLGFI, CFI, CLFI:
		switch inst.Op {
		case CGHI, CGFI, CGR:
			op = "CMP"
		case CHI, CFI, CR:
			op = "CMPW"
		case CLGFI, CLGR:
			op = "CMPU"
		case CLFI, CLR:
			op = "CMPWU"
		case CDBR:
			op = "FCMPU"
		case KDBR:
			op = "FCMPO"
		}
	case CEFBRA, CDFBRA, CEGBRA, CDGBRA, CELFBR, CDLFBR, CELGBR, CDLGBR, CFEBRA, CFDBRA, CGEBRA, CGDBRA, CLFEBR, CLFDBR, CLGEBR, CLGDBR:
		args[0], args[1] = args[2], args[0]
		args = args[:2]
	case CGRJ, CGIJ:
		mask, err := strconv.Atoi(args[2][1:])
		if err != nil {
			return fmt.Sprintf("GoSyntax: error in converting Atoi:%s", err)
		}
		var check bool
		switch mask & 0xf {
		case 2:
			op = "CMPBGT"
			check = true
		case 4:
			op = "CMPBLT"
			check = true
		case 6:
			op = "CMPBNE"
			check = true
		case 8:
			op = "CMPBEQ"
			check = true
		case 10:
			op = "CMPBGE"
			check = true
		case 12:
			op = "CMPBLE"
			check = true
		}
		if check {
			args[2] = args[3]
			args = args[:3]
		}
	case CLGRJ, CLGIJ:
		mask, err := strconv.Atoi(args[2][1:])
		if err != nil {
			return fmt.Sprintf("GoSyntax: error in converting Atoi:%s", err)
		}
		var check bool
		switch mask & 0xf {
		case 2:
			op = "CMPUBGT"
			check = true
		case 4:
			op = "CMPUBLT"
			check = true
		case 7:
			op = "CMPUBNE"
			check = true
		case 8:
			op = "CMPUBEQ"
			check = true
		case 10:
			op = "CMPUBGE"
			check = true
		case 12:
			op = "CMPUBLE"
			check = true
		}
		if check {
			args[2] = args[3]
			args = args[:3]
		}
	case CLRJ, CRJ, CIJ, CLIJ:
		args[0], args[1], args[2] = args[2], args[0], args[1]
	case BRC, BRCL:
		mask, err := strconv.Atoi(args[0][1:])
		if err != nil {
			return fmt.Sprintf("GoSyntax: error in converting Atoi:%s", err)
		}
		opStr, check := branch_relative_op(mask, inst.Op)
		if opStr != "" {
			op = opStr
		}
		if check {
			args[0] = args[1]
			args = args[:1]
		}
	case BCR:
		mask, err := strconv.Atoi(args[0][1:])
		if err != nil {
			return fmt.Sprintf("GoSyntax: error in converting Atoi:%s", err)
		}
		opStr := branchOnConditionOp(mask, inst.Op)
		if opStr != "" {
			op = opStr
		}
		if op == "SYNC" || op == "NOPH" {
			return op
		}
		if op == "RET" {
			args = args[:0]
			return op
		}
	case LOCGR:
		mask, err := strconv.Atoi(args[2][1:])
		if err != nil {
			return fmt.Sprintf("GoSyntax: error in converting Atoi:%s", err)
		}
		var check bool
		switch mask & 0xf {
		case 2: //Greaterthan (M=2)
			op = "MOVDGT"
			check = true
		case 4: //Lessthan (M=4)
			op = "MOVDLT"
			check = true
		case 7: // Not Equal (M=7)
			op = "MOVDNE"
			check = true
		case 8: // Equal (M=8)
			op = "MOVDEQ"
			check = true
		case 10: // Greaterthan or Equal (M=10)
			op = "MOVDGE"
			check = true
		case 12: // Lessthan or Equal (M=12)
			op = "MOVDLE"
			check = true
		}
		if check {
			args[0], args[1] = args[1], args[0]
			args = args[:2]
		} else {
			args[0], args[2] = args[2], args[0]
		}
	case BRASL:
		op = "CALL" // BL
		args[0] = args[1]
		args = args[:1]
	case X, XY, XG:
		switch inst.Op {
		case X, XY:
			op = "XORW"
		case XG:
			op = "XOR"
		}
	case N, NY, NG, O, OY, OG, XC, NC, OC, MVC, MVCIN, CLC:
		switch inst.Op {
		case N, NY:
			op = "ANDW"
		case NG:
			op = "AND"
		case O, OY:
			op = "ORW"
		case OG:
			op = "OR"
		}
		args[0], args[1] = args[1], args[0]
	case S, SY, SLBG, SLG, SG:
		switch inst.Op {
		case S, SY:
			op = "SUBW"
		case SLBG:
			op = "SUBE"
		case SLG:
			op = "SUBC"
		case SG:
			op = "SUB"
		}
		args[0], args[1] = args[1], args[0]
	case MSG, MSY, MS:
		switch inst.Op {
		case MSG:
			op = "MULLD"
		case MSY, MS:
			op = "MULLW"
		}
	case A, AY, ALCG, ALG, AG:
		switch inst.Op {
		case A, AY:
			op = "ADDW"
		case ALCG:
			op = "ADDE"
		case ALG:
			op = "ADDC"
		case AG:
			op = "ADD"
		}
		args[0], args[1] = args[1], args[0]
	case RISBG, RISBGN, RISBHG, RISBLG, RNSBG, RXSBG, ROSBG:
		switch inst.Op {
		case RNSBG, RXSBG, ROSBG:
			num, err := strconv.Atoi(args[2][1:])
			if err != nil {
				return fmt.Sprintf("GoSyntax: error in converting Atoi:%s", err)
			}
			if ((num >> 7) & 0x1) != 0 {
				op = op + "T"
			}
		case RISBG, RISBGN, RISBHG, RISBLG:
			num, err := strconv.Atoi(args[3][1:])
			if err != nil {
				return fmt.Sprintf("GoSyntax: error in converting Atoi:%s", err)
			}
			if ((num >> 7) & 0x1) != 0 {
				op = op + "Z"
			}
		}
		if len(args) == 5 {
			args[0], args[1], args[2], args[3], args[4] = args[2], args[3], args[4], args[1], args[0]
		} else {
			args[0], args[1], args[2], args[3] = args[2], args[3], args[1], args[0]
		}

	case VEC, VECL, VCLZ, VCTZ, VREPI, VPOPCT: //mnemonic V1, V2, M3
		mask, err := strconv.Atoi(args[2][1:])
		if err != nil {
			return fmt.Sprintf("GoSyntax: error in converting Atoi for %q:%s", op, err)
		}
		val := mask & 0x7
		if val >= 0 && val < 4 {
			op = op + vectorSize[val]
			args = args[:2]
		} else {
			return fmt.Sprintf("specification exception is recognized for %q with mask value: %v \n", op, mask)
		}
		switch inst.Op {
		case VCLZ, VCTZ, VREPI, VPOPCT:
			args[0], args[1] = args[1], args[0]
		default:
		}
		//Mnemonic V1, V2, V3, M4 or Mnemonic V1, I2, I3, M4 or Mnemonic V1, V3, I2, M4
	case VA, VS, VACC, VAVG, VAVGL, VMX, VMXL, VMN, VMNL, VGFM, VGM, VREP, VERLLV, VESLV, VSCBI, VSUM, VSUMG, VSUMQ, VMH, VMLH, VML, VME, VMLE, VMO, VMLO:
		mask, err := strconv.Atoi(args[3][1:])
		if err != nil {
			return fmt.Sprintf("GoSyntax: error in converting Atoi:%s", err)
		}
		val := mask & 0x7
		switch inst.Op {
		case VA, VS, VACC, VSCBI:
			if val >= 0 && val < 5 {
				if args[0] == args[2] {
					args[0], args[1] = args[1], args[0]
					args = args[:2]
				} else if inst.Op == VS {
					if args[0] == args[1] {
						args[0] = args[2]
						args = args[:2]
					} else {
						args[0], args[2] = args[2], args[0]
						args = args[:3]
					}
				} else {
					args[0], args[1], args[2] = args[1], args[2], args[0]
					args = args[:3]
				}
				op = op + vectorSize[val]
			} else {
				return fmt.Sprintf("specification exception is recognized for %q with mask value: %v \n", op, mask)
			}
		case VAVG, VAVGL, VMX, VMXL, VMN, VMNL, VGFM, VGM:
			if val >= 0 && val < 4 {
				op = op + vectorSize[val]
				args[0], args[1], args[2] = args[1], args[2], args[0]
				args = args[:3]
			} else {
				return fmt.Sprintf("specification exception is recognized for %q with mask value: %v \n", op, mask)
			}
		case VREP, VERLLV, VESLV:
			if val >= 0 && val < 4 {
				op = op + vectorSize[val]
				args[0], args[2] = args[2], args[0]
				args = args[:3]
			} else {
				return fmt.Sprintf("specification exception is recognized for %q with mask value: %v \n", op, mask)
			}
		case VSUM, VSUMG, VSUMQ:
			var off int
			switch inst.Op {
			case VSUM:
				off = 0
			case VSUMG:
				off = 1
			case VSUMQ:
				off = 2
			}
			if (val > (-1 + off)) && (val < (2 + off)) {
				op = op + vectorSize[val]
			} else {
				return fmt.Sprintf("specification exception is recognized for %q with mask value: %v \n", op, mask)
			}
			args = args[:3]
		case VML, VMH, VMLH, VME, VMLE, VMO, VMLO:
			if val >= 0 && val < 3 {
				op = op + vectorSize[val]
			}
			if op == "VML" && val == 2 {
				op = op + "W"
			}
			if args[0] == args[2] {
				args[0], args[1] = args[1], args[0]
				args = args[:2]
			} else {
				args[0], args[1], args[2] = args[1], args[2], args[0]
				args = args[:3]
			}
		}

	case VGFMA, VERIM, VMAH, VMALH: // Mnemonic V1, V2, V3, V4/I4, M5
		mask, err := strconv.Atoi(args[4][1:])
		if err != nil {
			return fmt.Sprintf("GoSyntax: error in converting Atoi:%s", err)
		}
		val := mask & 0x7
		args = args[:4]
		var off int
		switch inst.Op {
		case VMAH, VMALH:
			off = -1
		}

		if val >= 0 && val < (4+off) {
			op = op + vectorSize[val]
		} else {
			return fmt.Sprintf("specification exception is recognized for %q with mask value: %v \n", op, mask)
		}
		switch inst.Op {
		case VGFMA, VMAH, VMALH:
			args[0], args[1], args[2], args[3] = args[1], args[2], args[3], args[0]
		default:
			args[0], args[3] = args[3], args[0]
		}
	case VSTRC, VFAE, VFEE, VFENE:
		var off uint8
		switch inst.Op {
		case VSTRC:
			off = uint8(1)
		default:
			off = uint8(0)
		}
		m1, err := strconv.Atoi(args[3+off][1:])
		if err != nil {
			return fmt.Sprintf("GoSyntax: error in converting Atoi:%s", err)
		}
		m2, err := strconv.Atoi(args[4+off][1:])
		if err != nil {
			return fmt.Sprintf("GoSyntax: error in converting Atoi:%s", err)
		}
		index := m1 & 0x3
		if index < 0 || index > 2 {
			return fmt.Sprintf("specification exception is recognized for %q with mask values: %v, %v \n", op, m1, m2)
		}
		switch m2 {
		case 0:
			op = op + vectorSize[index]
		case 1:
			op = op + vectorCS[index]
		case 2:
			op = op + "Z" + vectorSize[index]
		case 3:
			op = op + "Z" + vectorCS[index]
		default:
			return fmt.Sprintf("specification exception is recognized for %q with mask values: %v, %v \n", op, m1, m2)
		}
		switch inst.Op {
		case VSTRC:
			args[0], args[1], args[2], args[3] = args[1], args[2], args[3], args[0]
		default:
			args[0], args[1], args[2] = args[1], args[2], args[0]
		}
		args = args[:3+off]

	case VCEQ, VCH, VCHL: // Mnemonic V1, V2, V3, M4, M5
		m4, err := strconv.Atoi(args[3][1:])
		if err != nil {
			return fmt.Sprintf("GoSyntax: %q error in converting Atoi:%s", op, err)
		}
		m5, err := strconv.Atoi(args[4][1:])
		if err != nil {
			return fmt.Sprintf("GoSyntax: %q error in converting Atoi:%s", op, err)
		}
		val := (m4 & 0x7)
		if m5 == 0 {
			if val >= 0 && val < 4 {
				op = op + vectorSize[val]
				args[0], args[1], args[2] = args[1], args[2], args[0]
				args = args[:3]
			} else {
				return fmt.Sprintf("specification exception is recognized for %q with mask(m4) value: %v \n", op, m4)
			}
		} else if m5 == 1 {
			if val >= 0 && val < 4 {
				op = op + vectorCS[val]
				args[0], args[1], args[2] = args[1], args[2], args[0]
				args = args[:3]
			} else {
				return fmt.Sprintf("specification exception is recognized for %q with mask(m4) value: %v \n", op, m4)
			}
		} else {
			return fmt.Sprintf("specification exception is recognized for %q with mask(m5) value: %v \n", op, m5)
		}
	case VFMA, VFMS, VMSL: //Mnemonic V1, V2, V3, V4, M5, M6
		m5, err := strconv.Atoi(args[4][1:])
		if err != nil {
			return fmt.Sprintf("GoSyntax: %q error in converting Atoi:%s", op, err)
		}
		m6, err := strconv.Atoi(args[5][1:])
		if err != nil {
			return fmt.Sprintf("GoSyntax: %q error in converting Atoi:%s", op, err)
		}
		switch inst.Op {
		case VMSL:
			if m5 == 3 && m6 == 8 {
				op = op + "EG"
			} else if m5 == 3 && m6 == 4 {
				op = op + "OG"
			} else if m5 == 3 && m6 == 12 {
				op = op + "EOG"
			} else if m5 == 3 {
				op = op + "G"
			}
		default:
			if m5 == 0 && m6 == 3 {
				op = op + "DB"
			} else if m5 == 8 && m6 == 3 {
				op = "W" + op[1:] + "DB"
			} else {
				return fmt.Sprintf("specification exception is recognized for %q with m5: %v m6: %v \n", op, m5, m6)
			}
		}
		args[0], args[1], args[2], args[3] = args[1], args[2], args[3], args[0]
		args = args[:4]

	case VFCE, VFCH, VFCHE: //Mnemonic V1,V2,V3,M4,M5,M6
		m4, err := strconv.Atoi(args[3][1:])
		if err != nil {
			return fmt.Sprintf("GoSyntax: %q error in converting Atoi:%s", op, err)
		}
		m5, err := strconv.Atoi(args[4][1:])
		if err != nil {
			return fmt.Sprintf("GoSyntax: %q error in converting Atoi:%s", op, err)
		}
		m6, err := strconv.Atoi(args[5][1:])
		if err != nil {
			return fmt.Sprintf("GoSyntax: %q error in converting Atoi:%s", op, err)
		}
		if m5 == 0 {
			if m4 == 3 && m6 == 0 {
				op = op + "DB"
			} else if m4 == 3 && m6 == 1 {
				op = op + "DBS"
			} else {
				return fmt.Sprintf("specification exception is recognized for %q with m4: %v, m6: %v \n", op, m4, m6)
			}

		} else if m5 == 8 {
			if m4 == 3 && m6 == 0 {
				op = "W" + op[1:] + "DB"
			} else if m4 == 3 && m6 == 1 {
				op = "W" + op[1:] + "DBS"
			} else {
				return fmt.Sprintf("specification exception is recognized for %q with m4: %v, m6: %v \n", op, m4, m6)
			}
		} else {
			return fmt.Sprintf("specification exception is recognized for %q with m5: %v \n", op, m5)
		}
		args[0], args[1], args[2] = args[1], args[2], args[0]
		args = args[:3]

	case VFTCI:
		m4, err := strconv.Atoi(args[3][1:])
		if err != nil {
			return fmt.Sprintf("GoSyntax: %q error in converting Atoi:%s", op, err)
		}
		m5, err := strconv.Atoi(args[4][1:])
		if err != nil {
			return fmt.Sprintf("GoSyntax: %q error in converting Atoi:%s", op, err)
		}
		val := (m4 & 0x7)
		if m5 == 0 {
			switch val {
			case 2:
				op = op + "SB"
			case 3:
				op = op + "DB"
			default:
				return fmt.Sprintf("specification exception is recognized for %q with mask(m4) value: %v \n", op, m4)
			}
		} else if m5 == 8 {
			switch val {
			case 2:
				op = "W" + op[1:] + "SB"
			case 3:
				op = "W" + op[1:] + "DB"
			case 4:
				op = "W" + op[1:] + "XB"
			default:
				return fmt.Sprintf("specification exception is recognized for %q with mask(m4) value: %v \n", op, m4)
			}
		} else {
			return fmt.Sprintf("specification exception is recognized for %q with mask(m5) value: %v \n", op, m5)
		}
		args[0], args[2] = args[2], args[0]
		args = args[:3]
	case VAC, VACCC:
		mask, err := strconv.Atoi(args[4][1:])
		if err != nil {
			return fmt.Sprintf("GoSyntax: error in converting Atoi:%s", err)
		}
		if mask&0x04 == 0 {
			return fmt.Sprintf("specification exception is recognized for %q with mask value: %v \n", op, mask)
		}
		op = op + "Q"
		args[0], args[1], args[2], args[3] = args[1], args[2], args[3], args[0]
		args = args[:4]
	case VL, VLREP:
		switch inst.Op {
		case VL:
			args[0], args[1] = args[1], args[0]
		case VLREP:
			args[0], args[1] = args[1], args[0]
			mask, err := strconv.Atoi(args[2][1:])
			if err != nil {
				return fmt.Sprintf("GoSyntax: error in converting Atoi:%s", err)
			}
			if mask >= 0 && mask < 4 {
				op = op + vectorSize[mask]
			}
		}
		args = args[:2]
	case VST, VSTEB, VSTEH, VSTEF, VSTEG, VLEB, VLEH, VLEF, VLEG: //Mnemonic V1, D2(X2,B2), M3
		m, err := strconv.Atoi(args[2][1:])
		if err != nil {
			return fmt.Sprintf("GoSyntax: error in converting Atoi:%s", err)
		}
		switch inst.Op {
		case VST:
			if m == 0 || (m > 2 && m < 5) {
				args = args[:2]
			} else {
				return fmt.Sprintf("specification exception is recognized for %q with mask value: %v \n", op, m)
			}
		case VLEB, VLEH, VLEF, VLEG:
			args[0], args[2] = args[2], args[0]
		default:
			args[0], args[1], args[2] = args[2], args[0], args[1]
		}
	case VSTM, VSTL, VESL, VESRA, VLM, VERLL, VLVG: //Mnemonic V1, V3, D2(B2)[,M4] or V1, R3,D2(B2)
		switch inst.Op {
		case VSTM, VLM:
			m, err := strconv.Atoi(args[3][1:])
			if err != nil {
				return fmt.Sprintf("GoSyntax: error in converting Atoi:%s", err)
			}
			if !(m == 0 || (m > 2 && m < 5)) {
				return fmt.Sprintf("specification exception is recognized for %q with mask value: %v \n", op, m)
			}
			if inst.Op == VLM {
				args[0], args[1], args[2] = args[2], args[0], args[1]
			}
			args = args[:3]
		case VESL, VESRA, VERLL, VLVG:
			m, err := strconv.Atoi(args[3][1:])
			if err != nil {
				return fmt.Sprintf("GoSyntax: error in converting Atoi:%s", err)
			}
			if m >= 0 && m < 4 {
				op = op + vectorSize[m]
			} else {
				return fmt.Sprintf("specification exception is recognized for %q with mask value: %v \n", op, m)
			}
			switch inst.Op {
			case VLVG:
				args[0], args[2] = args[2], args[0]
				args = args[:3]
			default:
				if args[0] == args[1] {
					args[0] = args[2]
					args = args[:2]
					break
				}
				args[0], args[2] = args[2], args[0]
				args = args[:3]
			}
		case VSTL:
			args[0], args[1] = args[1], args[0]
			args = args[:3]
		}
	case VGBM:
		val, err := strconv.Atoi(args[1][1:])
		if err != nil {
			return fmt.Sprintf("GoSyntax: error in converting Atoi:%s", err)
		}
		if val == 0 {
			op = "VZERO"
			args = args[:1]
		} else if val == 0xffff {
			op = "VONE"
			args = args[:1]
		} else {
			args[0], args[1] = args[1], args[0]
			args = args[:2]
		}
	case VN, VNC, VO, VX, VNO: //mnemonic V1, V2, V3
		if args[0] == args[2] {
			args = args[:2]
			args[0], args[1] = args[1], args[0]
		} else {
			args[0], args[1], args[2] = args[1], args[2], args[0]
		}
		if op == "VNO" {
			op = op + "T"
		}
	case VGEG, VGEF, VSCEG, VSCEF: //Mnemonic V1, D2(V2, B2), M3
		args[0], args[2] = args[2], args[0]

	}
	if args != nil {
		op += " " + strings.Join(args, ", ")
	}

	return op
}

// This function returns corresponding extended mnemonic for the given
// branch on relative mnemonic.
func branch_relative_op(mask int, opconst Op) (op string, check bool) {
	switch mask & 0xf {
	case 1:
		op = "BVS"
		check = true
	case 2:
		op = "BGT"
		check = true
	case 4:
		op = "BLT"
		check = true
	case 5:
		op = "BLTU"
		check = true
	case 7:
		op = "BNE"
		check = true
	case 8:
		op = "BEQ"
		check = true
	case 10:
		op = "BGE"
		check = true
	case 12:
		op = "BLE"
		check = true
	case 13:
		op = "BLEU"
		check = true
	case 15:
		op = "BR"
		check = true
	}
	return op, check
}

// This function returns corresponding extended mnemonic for the given
// brach on condition mnemonic.
func branchOnConditionOp(mask int, opconst Op) (op string) {
	switch mask & 0xf {
	case 0:
		op = "NOPH"
	case 14:
		op = "SYNC"
	case 15:
		op = "RET"
	}
	return op
}

// This function returns corresponding plan9 mnemonic for the native bitwise mnemonic.
func bitwise_op(op Op) string {
	var ret string
	switch op {
	case NGR, NGRK, NILL:
		ret = "AND"
	case NR, NRK, NILH, NILF:
		ret = "ANDW"
	case OGR, OGRK, OILF:
		ret = "OR"
	case OR, ORK, OILH, OILL:
		ret = "ORW"
	case XGR, XGRK, XILF:
		ret = "XOR"
	case XR, XRK:
		ret = "XORW"
	}
	return ret
}

// This function parses memory operand of type D(B)
func mem_operand(args []string) string {
	if args[0] != "" && args[1] != "" {
		args[0] = fmt.Sprintf("%s(%s)", args[0], args[1])
	} else if args[0] != "" {
		args[0] = fmt.Sprintf("$%s", args[0])
	} else if args[1] != "" {
		args[0] = fmt.Sprintf("(%s)", args[1])
	} else {
		args[0] = ""
	}
	return args[0]
}

// This function parses memory operand of type D(X,B)
func mem_operandx(args []string) string {
	if args[1] != "" && args[2] != "" {
		args[1] = fmt.Sprintf("(%s)(%s*1)", args[2], args[1])
	} else if args[1] != "" {
		args[1] = fmt.Sprintf("(%s)", args[1])
	} else if args[2] != "" {
		args[1] = fmt.Sprintf("(%s)", args[2])
	} else if args[0] != "" {
		args[1] = ""
	}
	if args[0] != "" && args[1] != "" {
		args[0] = fmt.Sprintf("%s%s", args[0], args[1])
	} else if args[0] != "" {
		args[0] = fmt.Sprintf("$%s", args[0])
	} else if args[1] != "" {
		args[0] = fmt.Sprintf("%s", args[1])
	} else {
		args[0] = ""
	}
	return args[0]
}

// This function parses memory operand of type D(V,B)
func mem_operandv(args []string) string {
	if args[1] != "" && args[2] != "" {
		args[1] = fmt.Sprintf("(%s)(%s*1)", args[2], args[1])
	} else if args[1] != "" {
		args[1] = fmt.Sprintf("(%s*1)", args[1])
	} else if args[2] != "" {
		args[1] = fmt.Sprintf("(%s)", args[2])
	} else if args[0] != "" {
		args[1] = ""
	}
	if args[0] != "" && args[1] != "" {
		args[0] = fmt.Sprintf("%s%s", args[0], args[1])
	} else if args[0] != "" {
		args[0] = fmt.Sprintf("$%s", args[0])
	} else if args[1] != "" {
		args[0] = fmt.Sprintf("%s", args[1])
	} else {
		args[0] = ""
	}
	return args[0]
}

// This function parses memory operand of type D(L,B)
func mem_operandl(args []string) (string, string) {
	if args[0] != "" && args[2] != "" {
		args[0] = fmt.Sprintf("%s(%s)", args[0], args[2])
	} else if args[2] != "" {
		args[0] = fmt.Sprintf("(%s)", args[2])
	} else {
		args[0] = fmt.Sprintf("%s", args[0])
	}
	return args[0], args[1]
}

// plan9Arg formats arg (which is the argIndex's arg in inst) according to Plan 9 rules.
// NOTE: because Plan9Syntax is the only caller of this func, and it receives a copy
// of inst, it's ok to modify inst.Args here.
func plan9Arg(inst *Inst, pc uint64, symname func(uint64) (string, uint64), arg Arg) string {
	switch arg.(type) {
	case Reg:
		if arg == R13 {
			return "g"
		}
		return strings.ToUpper(arg.String(pc)[1:])
	case Base:
		if arg == R13 {
			return "g"
		}
		s := arg.String(pc)
		if s != "" {
			return strings.ToUpper(s[1 : len(s)-1])
		}
		return "R0"
	case Index:
		if arg == R13 {
			return "g"
		}
		s := arg.String(pc)
		if s != "" {
			return strings.ToUpper(s[1:])
		}
		return ""
	case VReg:
		return strings.ToUpper(arg.String(pc)[1:])
	case Disp20, Disp12:
		numstr := arg.String(pc)
		num, err := strconv.Atoi(numstr[:len(numstr)])
		if err != nil {
			return fmt.Sprintf("plan9Arg: error in converting Atoi:%s", err)
		}
		if num == 0 {
			return ""
		} else {
			return strconv.Itoa(num)
		}
	case RegIm12, RegIm16, RegIm24, RegIm32:
		addr, err := strconv.ParseUint(arg.String(pc)[2:], 16, 64)
		if err != nil {
			return fmt.Sprintf("plan9Arg: error in converting ParseUint:%s", err)
		}
		off := int(addr - pc)
		s, base := symname(addr)
		if s != "" && addr == base {
			return fmt.Sprintf("%s(SB)", s)
		}
		off = off / inst.Len
		return fmt.Sprintf("%v(PC)", off)
	case Imm, Sign8, Sign16, Sign32:
		numImm := arg.String(pc)
		switch arg.(type) {
		case Sign32, Sign16, Imm:
			num, err := strconv.ParseInt(numImm, 10, 64)
			if err != nil {
				return fmt.Sprintf("plan9Arg: error in converting ParseInt:%s", err)
			}
			switch inst.Op {
			case LLIHF:
				num = num << 32
			case LLILH:
				num = num << 16
			case NILH:
				num = (num << 16) | int64(0xFFFF)
			case OILH:
				num = num << 16
			}
			numImm = fmt.Sprintf("%d", num)
		}
		return fmt.Sprintf("$%s", numImm)
	case Mask, Len:
		num := arg.String(pc)
		return fmt.Sprintf("$%s", num)
	}
	return fmt.Sprintf("???(%v)", arg)
}

// It checks any 2 args of given instructions to swap or not
func reverseOperandOrder(op Op) bool {
	switch op {
	case LOCR, MLGR:
		return true
	case LTEBR, LTDBR, LCDBR, LGDR:
		return true
	case VLEIB, VLEIH, VLEIF, VLEIG, VPDI:
		return true
	case VSLDB:
		return true
	}
	return false
}

// It checks whether to reverse all the args of given mnemonic or not
func reverseAllOperands(op Op) bool {
	switch op {
	case VLVGP: //3-operand list
		return true
	case VSEL, VPERM: //4-Operand list
		return true
	}
	return false
}
