package sbf

import "fmt"

var mnemonicTable = [0x100]string{
	OpLddw:      "lddw",
	OpLdxb:      "ldxb",
	OpLdxh:      "ldxh",
	OpLdxw:      "ldxw",
	OpLdxdw:     "ldxdw",
	OpStb:       "stb",
	OpSth:       "sth",
	OpStw:       "stw",
	OpStdw:      "stdw",
	OpStxb:      "stxb",
	OpStxh:      "stxh",
	OpStxw:      "stxw",
	OpStxdw:     "stxdw",
	OpAdd32Imm:  "add32",
	OpAdd32Reg:  "add32",
	OpSub32Imm:  "sub32",
	OpSub32Reg:  "sub32",
	OpMul32Imm:  "mul32",
	OpMul32Reg:  "mul32",
	OpDiv32Imm:  "div32",
	OpDiv32Reg:  "div32",
	OpOr32Imm:   "or32",
	OpOr32Reg:   "or32",
	OpAnd32Imm:  "and32",
	OpAnd32Reg:  "and32",
	OpLsh32Imm:  "lsh32",
	OpLsh32Reg:  "lsh32",
	OpRsh32Imm:  "rsh32",
	OpRsh32Reg:  "rsh32",
	OpNeg32:     "neg32",
	OpMod32Imm:  "mod32",
	OpMod32Reg:  "mod32",
	OpXor32Imm:  "xor32",
	OpXor32Reg:  "xor32",
	OpMov32Imm:  "mov32",
	OpMov32Reg:  "mov32",
	OpArsh32Imm: "arsh32",
	OpArsh32Reg: "arsh32",
	OpSdiv32Imm: "sdiv32",
	OpSdiv32Reg: "sdiv32",
	OpLe:        "le",
	OpBe:        "be",
	OpAdd64Imm:  "add64",
	OpAdd64Reg:  "add64",
	OpSub64Imm:  "sub64",
	OpSub64Reg:  "sub64",
	OpMul64Imm:  "mul64",
	OpMul64Reg:  "mul64",
	OpDiv64Imm:  "div64",
	OpDiv64Reg:  "div64",
	OpOr64Imm:   "or64",
	OpOr64Reg:   "or64",
	OpAnd64Imm:  "and64",
	OpAnd64Reg:  "and64",
	OpLsh64Imm:  "lsh64",
	OpLsh64Reg:  "lsh64",
	OpRsh64Imm:  "rsh64",
	OpRsh64Reg:  "rsh64",
	OpNeg64:     "neg64",
	OpMod64Imm:  "mod64",
	OpMod64Reg:  "mod64",
	OpXor64Imm:  "xor64",
	OpXor64Reg:  "xor64",
	OpMov64Imm:  "mov64",
	OpMov64Reg:  "mov64",
	OpArsh64Imm: "arsh64",
	OpArsh64Reg: "arsh64",
	OpSdiv64Imm: "sdiv64",
	OpSdiv64Reg: "sdiv64",
	OpJa:        "ja",
	OpJeqImm:    "jeq",
	OpJeqReg:    "jeq",
	OpJgtImm:    "jgt",
	OpJgtReg:    "jgt",
	OpJgeImm:    "jge",
	OpJgeReg:    "jge",
	OpJltImm:    "jlt",
	OpJltReg:    "jlt",
	OpJleImm:    "jle",
	OpJleReg:    "jle",
	OpJsetImm:   "jset",
	OpJsetReg:   "jset",
	OpJneImm:    "jne",
	OpJneReg:    "jne",
	OpJsgtImm:   "jsgt",
	OpJsgtReg:   "jsgt",
	OpJsgeImm:   "jsge",
	OpJsgeReg:   "jsge",
	OpJsltImm:   "jslt",
	OpJsltReg:   "jslt",
	OpJsleImm:   "jsle",
	OpJsleReg:   "jsle",
	OpCall:      "call",
	OpCallx:     "callx",
	OpExit:      "exit",
}

func GetOpcodeName(opc uint8) string {
	return mnemonicTable[opc]
}

func disassemble(slot Slot, slot2 Slot) string {
	opc := slot.Op()
	mnemonic := GetOpcodeName(opc)
	switch opc {
	case OpLddw:
		return fmt.Sprintf("lddw r%d, %#x", slot.Dst(), uint64(slot.Uimm())|(uint64(slot2.Uimm())<<32))
	case OpLdxb, OpLdxh, OpLdxw, OpLdxdw:
		return fmt.Sprintf("%s r%d, [r%d%#+x]", mnemonic, slot.Dst(), slot.Src(), slot.Off())
	case OpStb:
		return fmt.Sprintf("stb [r%d%#+x], %#x", slot.Src(), slot.Off(), int8(slot.Imm()))
	case OpSth:
		return fmt.Sprintf("sth [r%d%#+x], %#x", slot.Src(), slot.Off(), int16(slot.Imm()))
	case OpStw:
		return fmt.Sprintf("stw [r%d%#+x], %#x", slot.Src(), slot.Off(), slot.Imm())
	case OpStdw:
		return fmt.Sprintf("stdw [r%d%#+x], %#x", slot.Src(), slot.Off(), int64(slot.Imm()))
	case OpStxb, OpStxh, OpStxw, OpStxdw:
		return fmt.Sprintf("%s [r%d%#+x], r%d", mnemonic, slot.Src(), slot.Off(), slot.Dst())
	case OpAdd32Imm, OpSub32Imm, OpAdd64Imm, OpSub64Imm:
		return fmt.Sprintf("%s r%d, %#x", mnemonic, slot.Dst(), slot.Imm())
	case OpOr32Imm, OpAnd32Imm, OpXor32Imm, OpMov32Imm:
		return fmt.Sprintf("%s r%d, %#x", mnemonic, slot.Dst(), slot.Uimm())
	case OpDiv32Imm, OpMod32Imm, OpLsh32Imm, OpRsh32Imm, OpArsh32Imm,
		OpDiv64Imm, OpMod64Imm, OpLsh64Imm, OpRsh64Imm, OpArsh64Imm:
		return fmt.Sprintf("%s r%d, %d", mnemonic, slot.Dst(), slot.Uimm())
	case OpMul32Imm, OpSdiv32Imm, OpMul64Imm, OpSdiv64Imm:
		return fmt.Sprintf("%s r%d, %d", mnemonic, slot.Dst(), slot.Imm())
	case OpOr64Imm, OpAnd64Imm, OpXor64Imm, OpMov64Imm:
		return fmt.Sprintf("%s r%d, %#x", mnemonic, slot.Dst(), uint64(slot.Imm()))
	case OpAdd32Reg, OpSub32Reg, OpMul32Reg, OpDiv32Reg, OpOr32Reg, OpAnd32Reg, OpLsh32Reg, OpRsh32Reg, OpMod32Reg, OpXor32Reg, OpMov32Reg, OpArsh32Reg, OpSdiv32Reg,
		OpAdd64Reg, OpSub64Reg, OpMul64Reg, OpDiv64Reg, OpOr64Reg, OpAnd64Reg, OpLsh64Reg, OpRsh64Reg, OpMod64Reg, OpXor64Reg, OpMov64Reg, OpArsh64Reg, OpSdiv64Reg:
		return fmt.Sprintf("%s r%d, r%d", mnemonic, slot.Dst(), slot.Src())
	case OpNeg32, OpNeg64:
		return fmt.Sprintf("%s r%d", mnemonic, slot.Dst())
	case OpLe, OpBe:
		return fmt.Sprintf("%s%d r%d", mnemonic, slot.Uimm(), slot.Dst())
	case OpJa:
		return fmt.Sprintf("ja %+d", slot.Off())
	case OpJeqImm, OpJgtImm, OpJgeImm, OpJltImm, OpJleImm, OpJsetImm, OpJneImm, OpJsgtImm, OpJsgeImm, OpJsltImm, OpJsleImm:
		return fmt.Sprintf("%s r%d, %#x, %+d", mnemonic, slot.Dst(), int64(slot.Imm()), slot.Off())
	case OpJeqReg, OpJgtReg, OpJgeReg, OpJltReg, OpJleReg, OpJsetReg, OpJneReg, OpJsgtReg, OpJsgeReg, OpJsltReg, OpJsleReg:
		return fmt.Sprintf("%s r%d, r%d, %+d", mnemonic, slot.Dst(), slot.Src(), slot.Off())
	case OpCall:
		return fmt.Sprintf("call %#x", slot.Uimm())
	case OpCallx:
		return fmt.Sprintf("call r%d", slot.Dst())
	case OpExit:
		return "exit"
	default:
		return "invalid"
	}
}
