instructions.go raw

   1  // Copyright 2016 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  
   5  package bpf
   6  
   7  import "fmt"
   8  
   9  // An Instruction is one instruction executed by the BPF virtual
  10  // machine.
  11  type Instruction interface {
  12  	// Assemble assembles the Instruction into a RawInstruction.
  13  	Assemble() (RawInstruction, error)
  14  }
  15  
  16  // A RawInstruction is a raw BPF virtual machine instruction.
  17  type RawInstruction struct {
  18  	// Operation to execute.
  19  	Op uint16
  20  	// For conditional jump instructions, the number of instructions
  21  	// to skip if the condition is true/false.
  22  	Jt uint8
  23  	Jf uint8
  24  	// Constant parameter. The meaning depends on the Op.
  25  	K uint32
  26  }
  27  
  28  // Assemble implements the Instruction Assemble method.
  29  func (ri RawInstruction) Assemble() (RawInstruction, error) { return ri, nil }
  30  
  31  // Disassemble parses ri into an Instruction and returns it. If ri is
  32  // not recognized by this package, ri itself is returned.
  33  func (ri RawInstruction) Disassemble() Instruction {
  34  	switch ri.Op & opMaskCls {
  35  	case opClsLoadA, opClsLoadX:
  36  		reg := Register(ri.Op & opMaskLoadDest)
  37  		sz := 0
  38  		switch ri.Op & opMaskLoadWidth {
  39  		case opLoadWidth4:
  40  			sz = 4
  41  		case opLoadWidth2:
  42  			sz = 2
  43  		case opLoadWidth1:
  44  			sz = 1
  45  		default:
  46  			return ri
  47  		}
  48  		switch ri.Op & opMaskLoadMode {
  49  		case opAddrModeImmediate:
  50  			if sz != 4 {
  51  				return ri
  52  			}
  53  			return LoadConstant{Dst: reg, Val: ri.K}
  54  		case opAddrModeScratch:
  55  			if sz != 4 || ri.K > 15 {
  56  				return ri
  57  			}
  58  			return LoadScratch{Dst: reg, N: int(ri.K)}
  59  		case opAddrModeAbsolute:
  60  			if ri.K > extOffset+0xffffffff {
  61  				return LoadExtension{Num: Extension(-extOffset + ri.K)}
  62  			}
  63  			return LoadAbsolute{Size: sz, Off: ri.K}
  64  		case opAddrModeIndirect:
  65  			return LoadIndirect{Size: sz, Off: ri.K}
  66  		case opAddrModePacketLen:
  67  			if sz != 4 {
  68  				return ri
  69  			}
  70  			return LoadExtension{Num: ExtLen}
  71  		case opAddrModeMemShift:
  72  			return LoadMemShift{Off: ri.K}
  73  		default:
  74  			return ri
  75  		}
  76  
  77  	case opClsStoreA:
  78  		if ri.Op != opClsStoreA || ri.K > 15 {
  79  			return ri
  80  		}
  81  		return StoreScratch{Src: RegA, N: int(ri.K)}
  82  
  83  	case opClsStoreX:
  84  		if ri.Op != opClsStoreX || ri.K > 15 {
  85  			return ri
  86  		}
  87  		return StoreScratch{Src: RegX, N: int(ri.K)}
  88  
  89  	case opClsALU:
  90  		switch op := ALUOp(ri.Op & opMaskOperator); op {
  91  		case ALUOpAdd, ALUOpSub, ALUOpMul, ALUOpDiv, ALUOpOr, ALUOpAnd, ALUOpShiftLeft, ALUOpShiftRight, ALUOpMod, ALUOpXor:
  92  			switch operand := opOperand(ri.Op & opMaskOperand); operand {
  93  			case opOperandX:
  94  				return ALUOpX{Op: op}
  95  			case opOperandConstant:
  96  				return ALUOpConstant{Op: op, Val: ri.K}
  97  			default:
  98  				return ri
  99  			}
 100  		case aluOpNeg:
 101  			return NegateA{}
 102  		default:
 103  			return ri
 104  		}
 105  
 106  	case opClsJump:
 107  		switch op := jumpOp(ri.Op & opMaskOperator); op {
 108  		case opJumpAlways:
 109  			return Jump{Skip: ri.K}
 110  		case opJumpEqual, opJumpGT, opJumpGE, opJumpSet:
 111  			cond, skipTrue, skipFalse := jumpOpToTest(op, ri.Jt, ri.Jf)
 112  			switch operand := opOperand(ri.Op & opMaskOperand); operand {
 113  			case opOperandX:
 114  				return JumpIfX{Cond: cond, SkipTrue: skipTrue, SkipFalse: skipFalse}
 115  			case opOperandConstant:
 116  				return JumpIf{Cond: cond, Val: ri.K, SkipTrue: skipTrue, SkipFalse: skipFalse}
 117  			default:
 118  				return ri
 119  			}
 120  		default:
 121  			return ri
 122  		}
 123  
 124  	case opClsReturn:
 125  		switch ri.Op {
 126  		case opClsReturn | opRetSrcA:
 127  			return RetA{}
 128  		case opClsReturn | opRetSrcConstant:
 129  			return RetConstant{Val: ri.K}
 130  		default:
 131  			return ri
 132  		}
 133  
 134  	case opClsMisc:
 135  		switch ri.Op {
 136  		case opClsMisc | opMiscTAX:
 137  			return TAX{}
 138  		case opClsMisc | opMiscTXA:
 139  			return TXA{}
 140  		default:
 141  			return ri
 142  		}
 143  
 144  	default:
 145  		panic("unreachable") // switch is exhaustive on the bit pattern
 146  	}
 147  }
 148  
 149  func jumpOpToTest(op jumpOp, skipTrue uint8, skipFalse uint8) (JumpTest, uint8, uint8) {
 150  	var test JumpTest
 151  
 152  	// Decode "fake" jump conditions that don't appear in machine code
 153  	// Ensures the Assemble -> Disassemble stage recreates the same instructions
 154  	// See https://github.com/golang/go/issues/18470
 155  	if skipTrue == 0 {
 156  		switch op {
 157  		case opJumpEqual:
 158  			test = JumpNotEqual
 159  		case opJumpGT:
 160  			test = JumpLessOrEqual
 161  		case opJumpGE:
 162  			test = JumpLessThan
 163  		case opJumpSet:
 164  			test = JumpBitsNotSet
 165  		}
 166  
 167  		return test, skipFalse, 0
 168  	}
 169  
 170  	switch op {
 171  	case opJumpEqual:
 172  		test = JumpEqual
 173  	case opJumpGT:
 174  		test = JumpGreaterThan
 175  	case opJumpGE:
 176  		test = JumpGreaterOrEqual
 177  	case opJumpSet:
 178  		test = JumpBitsSet
 179  	}
 180  
 181  	return test, skipTrue, skipFalse
 182  }
 183  
 184  // LoadConstant loads Val into register Dst.
 185  type LoadConstant struct {
 186  	Dst Register
 187  	Val uint32
 188  }
 189  
 190  // Assemble implements the Instruction Assemble method.
 191  func (a LoadConstant) Assemble() (RawInstruction, error) {
 192  	return assembleLoad(a.Dst, 4, opAddrModeImmediate, a.Val)
 193  }
 194  
 195  // String returns the instruction in assembler notation.
 196  func (a LoadConstant) String() string {
 197  	switch a.Dst {
 198  	case RegA:
 199  		return fmt.Sprintf("ld #%d", a.Val)
 200  	case RegX:
 201  		return fmt.Sprintf("ldx #%d", a.Val)
 202  	default:
 203  		return fmt.Sprintf("unknown instruction: %#v", a)
 204  	}
 205  }
 206  
 207  // LoadScratch loads scratch[N] into register Dst.
 208  type LoadScratch struct {
 209  	Dst Register
 210  	N   int // 0-15
 211  }
 212  
 213  // Assemble implements the Instruction Assemble method.
 214  func (a LoadScratch) Assemble() (RawInstruction, error) {
 215  	if a.N < 0 || a.N > 15 {
 216  		return RawInstruction{}, fmt.Errorf("invalid scratch slot %d", a.N)
 217  	}
 218  	return assembleLoad(a.Dst, 4, opAddrModeScratch, uint32(a.N))
 219  }
 220  
 221  // String returns the instruction in assembler notation.
 222  func (a LoadScratch) String() string {
 223  	switch a.Dst {
 224  	case RegA:
 225  		return fmt.Sprintf("ld M[%d]", a.N)
 226  	case RegX:
 227  		return fmt.Sprintf("ldx M[%d]", a.N)
 228  	default:
 229  		return fmt.Sprintf("unknown instruction: %#v", a)
 230  	}
 231  }
 232  
 233  // LoadAbsolute loads packet[Off:Off+Size] as an integer value into
 234  // register A.
 235  type LoadAbsolute struct {
 236  	Off  uint32
 237  	Size int // 1, 2 or 4
 238  }
 239  
 240  // Assemble implements the Instruction Assemble method.
 241  func (a LoadAbsolute) Assemble() (RawInstruction, error) {
 242  	return assembleLoad(RegA, a.Size, opAddrModeAbsolute, a.Off)
 243  }
 244  
 245  // String returns the instruction in assembler notation.
 246  func (a LoadAbsolute) String() string {
 247  	switch a.Size {
 248  	case 1: // byte
 249  		return fmt.Sprintf("ldb [%d]", a.Off)
 250  	case 2: // half word
 251  		return fmt.Sprintf("ldh [%d]", a.Off)
 252  	case 4: // word
 253  		if a.Off > extOffset+0xffffffff {
 254  			return LoadExtension{Num: Extension(a.Off + 0x1000)}.String()
 255  		}
 256  		return fmt.Sprintf("ld [%d]", a.Off)
 257  	default:
 258  		return fmt.Sprintf("unknown instruction: %#v", a)
 259  	}
 260  }
 261  
 262  // LoadIndirect loads packet[X+Off:X+Off+Size] as an integer value
 263  // into register A.
 264  type LoadIndirect struct {
 265  	Off  uint32
 266  	Size int // 1, 2 or 4
 267  }
 268  
 269  // Assemble implements the Instruction Assemble method.
 270  func (a LoadIndirect) Assemble() (RawInstruction, error) {
 271  	return assembleLoad(RegA, a.Size, opAddrModeIndirect, a.Off)
 272  }
 273  
 274  // String returns the instruction in assembler notation.
 275  func (a LoadIndirect) String() string {
 276  	switch a.Size {
 277  	case 1: // byte
 278  		return fmt.Sprintf("ldb [x + %d]", a.Off)
 279  	case 2: // half word
 280  		return fmt.Sprintf("ldh [x + %d]", a.Off)
 281  	case 4: // word
 282  		return fmt.Sprintf("ld [x + %d]", a.Off)
 283  	default:
 284  		return fmt.Sprintf("unknown instruction: %#v", a)
 285  	}
 286  }
 287  
 288  // LoadMemShift multiplies the first 4 bits of the byte at packet[Off]
 289  // by 4 and stores the result in register X.
 290  //
 291  // This instruction is mainly useful to load into X the length of an
 292  // IPv4 packet header in a single instruction, rather than have to do
 293  // the arithmetic on the header's first byte by hand.
 294  type LoadMemShift struct {
 295  	Off uint32
 296  }
 297  
 298  // Assemble implements the Instruction Assemble method.
 299  func (a LoadMemShift) Assemble() (RawInstruction, error) {
 300  	return assembleLoad(RegX, 1, opAddrModeMemShift, a.Off)
 301  }
 302  
 303  // String returns the instruction in assembler notation.
 304  func (a LoadMemShift) String() string {
 305  	return fmt.Sprintf("ldx 4*([%d]&0xf)", a.Off)
 306  }
 307  
 308  // LoadExtension invokes a linux-specific extension and stores the
 309  // result in register A.
 310  type LoadExtension struct {
 311  	Num Extension
 312  }
 313  
 314  // Assemble implements the Instruction Assemble method.
 315  func (a LoadExtension) Assemble() (RawInstruction, error) {
 316  	if a.Num == ExtLen {
 317  		return assembleLoad(RegA, 4, opAddrModePacketLen, 0)
 318  	}
 319  	return assembleLoad(RegA, 4, opAddrModeAbsolute, uint32(extOffset+a.Num))
 320  }
 321  
 322  // String returns the instruction in assembler notation.
 323  func (a LoadExtension) String() string {
 324  	switch a.Num {
 325  	case ExtLen:
 326  		return "ld #len"
 327  	case ExtProto:
 328  		return "ld #proto"
 329  	case ExtType:
 330  		return "ld #type"
 331  	case ExtPayloadOffset:
 332  		return "ld #poff"
 333  	case ExtInterfaceIndex:
 334  		return "ld #ifidx"
 335  	case ExtNetlinkAttr:
 336  		return "ld #nla"
 337  	case ExtNetlinkAttrNested:
 338  		return "ld #nlan"
 339  	case ExtMark:
 340  		return "ld #mark"
 341  	case ExtQueue:
 342  		return "ld #queue"
 343  	case ExtLinkLayerType:
 344  		return "ld #hatype"
 345  	case ExtRXHash:
 346  		return "ld #rxhash"
 347  	case ExtCPUID:
 348  		return "ld #cpu"
 349  	case ExtVLANTag:
 350  		return "ld #vlan_tci"
 351  	case ExtVLANTagPresent:
 352  		return "ld #vlan_avail"
 353  	case ExtVLANProto:
 354  		return "ld #vlan_tpid"
 355  	case ExtRand:
 356  		return "ld #rand"
 357  	default:
 358  		return fmt.Sprintf("unknown instruction: %#v", a)
 359  	}
 360  }
 361  
 362  // StoreScratch stores register Src into scratch[N].
 363  type StoreScratch struct {
 364  	Src Register
 365  	N   int // 0-15
 366  }
 367  
 368  // Assemble implements the Instruction Assemble method.
 369  func (a StoreScratch) Assemble() (RawInstruction, error) {
 370  	if a.N < 0 || a.N > 15 {
 371  		return RawInstruction{}, fmt.Errorf("invalid scratch slot %d", a.N)
 372  	}
 373  	var op uint16
 374  	switch a.Src {
 375  	case RegA:
 376  		op = opClsStoreA
 377  	case RegX:
 378  		op = opClsStoreX
 379  	default:
 380  		return RawInstruction{}, fmt.Errorf("invalid source register %v", a.Src)
 381  	}
 382  
 383  	return RawInstruction{
 384  		Op: op,
 385  		K:  uint32(a.N),
 386  	}, nil
 387  }
 388  
 389  // String returns the instruction in assembler notation.
 390  func (a StoreScratch) String() string {
 391  	switch a.Src {
 392  	case RegA:
 393  		return fmt.Sprintf("st M[%d]", a.N)
 394  	case RegX:
 395  		return fmt.Sprintf("stx M[%d]", a.N)
 396  	default:
 397  		return fmt.Sprintf("unknown instruction: %#v", a)
 398  	}
 399  }
 400  
 401  // ALUOpConstant executes A = A <Op> Val.
 402  type ALUOpConstant struct {
 403  	Op  ALUOp
 404  	Val uint32
 405  }
 406  
 407  // Assemble implements the Instruction Assemble method.
 408  func (a ALUOpConstant) Assemble() (RawInstruction, error) {
 409  	return RawInstruction{
 410  		Op: opClsALU | uint16(opOperandConstant) | uint16(a.Op),
 411  		K:  a.Val,
 412  	}, nil
 413  }
 414  
 415  // String returns the instruction in assembler notation.
 416  func (a ALUOpConstant) String() string {
 417  	switch a.Op {
 418  	case ALUOpAdd:
 419  		return fmt.Sprintf("add #%d", a.Val)
 420  	case ALUOpSub:
 421  		return fmt.Sprintf("sub #%d", a.Val)
 422  	case ALUOpMul:
 423  		return fmt.Sprintf("mul #%d", a.Val)
 424  	case ALUOpDiv:
 425  		return fmt.Sprintf("div #%d", a.Val)
 426  	case ALUOpMod:
 427  		return fmt.Sprintf("mod #%d", a.Val)
 428  	case ALUOpAnd:
 429  		return fmt.Sprintf("and #%d", a.Val)
 430  	case ALUOpOr:
 431  		return fmt.Sprintf("or #%d", a.Val)
 432  	case ALUOpXor:
 433  		return fmt.Sprintf("xor #%d", a.Val)
 434  	case ALUOpShiftLeft:
 435  		return fmt.Sprintf("lsh #%d", a.Val)
 436  	case ALUOpShiftRight:
 437  		return fmt.Sprintf("rsh #%d", a.Val)
 438  	default:
 439  		return fmt.Sprintf("unknown instruction: %#v", a)
 440  	}
 441  }
 442  
 443  // ALUOpX executes A = A <Op> X
 444  type ALUOpX struct {
 445  	Op ALUOp
 446  }
 447  
 448  // Assemble implements the Instruction Assemble method.
 449  func (a ALUOpX) Assemble() (RawInstruction, error) {
 450  	return RawInstruction{
 451  		Op: opClsALU | uint16(opOperandX) | uint16(a.Op),
 452  	}, nil
 453  }
 454  
 455  // String returns the instruction in assembler notation.
 456  func (a ALUOpX) String() string {
 457  	switch a.Op {
 458  	case ALUOpAdd:
 459  		return "add x"
 460  	case ALUOpSub:
 461  		return "sub x"
 462  	case ALUOpMul:
 463  		return "mul x"
 464  	case ALUOpDiv:
 465  		return "div x"
 466  	case ALUOpMod:
 467  		return "mod x"
 468  	case ALUOpAnd:
 469  		return "and x"
 470  	case ALUOpOr:
 471  		return "or x"
 472  	case ALUOpXor:
 473  		return "xor x"
 474  	case ALUOpShiftLeft:
 475  		return "lsh x"
 476  	case ALUOpShiftRight:
 477  		return "rsh x"
 478  	default:
 479  		return fmt.Sprintf("unknown instruction: %#v", a)
 480  	}
 481  }
 482  
 483  // NegateA executes A = -A.
 484  type NegateA struct{}
 485  
 486  // Assemble implements the Instruction Assemble method.
 487  func (a NegateA) Assemble() (RawInstruction, error) {
 488  	return RawInstruction{
 489  		Op: opClsALU | uint16(aluOpNeg),
 490  	}, nil
 491  }
 492  
 493  // String returns the instruction in assembler notation.
 494  func (a NegateA) String() string {
 495  	return fmt.Sprintf("neg")
 496  }
 497  
 498  // Jump skips the following Skip instructions in the program.
 499  type Jump struct {
 500  	Skip uint32
 501  }
 502  
 503  // Assemble implements the Instruction Assemble method.
 504  func (a Jump) Assemble() (RawInstruction, error) {
 505  	return RawInstruction{
 506  		Op: opClsJump | uint16(opJumpAlways),
 507  		K:  a.Skip,
 508  	}, nil
 509  }
 510  
 511  // String returns the instruction in assembler notation.
 512  func (a Jump) String() string {
 513  	return fmt.Sprintf("ja %d", a.Skip)
 514  }
 515  
 516  // JumpIf skips the following Skip instructions in the program if A
 517  // <Cond> Val is true.
 518  type JumpIf struct {
 519  	Cond      JumpTest
 520  	Val       uint32
 521  	SkipTrue  uint8
 522  	SkipFalse uint8
 523  }
 524  
 525  // Assemble implements the Instruction Assemble method.
 526  func (a JumpIf) Assemble() (RawInstruction, error) {
 527  	return jumpToRaw(a.Cond, opOperandConstant, a.Val, a.SkipTrue, a.SkipFalse)
 528  }
 529  
 530  // String returns the instruction in assembler notation.
 531  func (a JumpIf) String() string {
 532  	return jumpToString(a.Cond, fmt.Sprintf("#%d", a.Val), a.SkipTrue, a.SkipFalse)
 533  }
 534  
 535  // JumpIfX skips the following Skip instructions in the program if A
 536  // <Cond> X is true.
 537  type JumpIfX struct {
 538  	Cond      JumpTest
 539  	SkipTrue  uint8
 540  	SkipFalse uint8
 541  }
 542  
 543  // Assemble implements the Instruction Assemble method.
 544  func (a JumpIfX) Assemble() (RawInstruction, error) {
 545  	return jumpToRaw(a.Cond, opOperandX, 0, a.SkipTrue, a.SkipFalse)
 546  }
 547  
 548  // String returns the instruction in assembler notation.
 549  func (a JumpIfX) String() string {
 550  	return jumpToString(a.Cond, "x", a.SkipTrue, a.SkipFalse)
 551  }
 552  
 553  // jumpToRaw assembles a jump instruction into a RawInstruction
 554  func jumpToRaw(test JumpTest, operand opOperand, k uint32, skipTrue, skipFalse uint8) (RawInstruction, error) {
 555  	var (
 556  		cond jumpOp
 557  		flip bool
 558  	)
 559  	switch test {
 560  	case JumpEqual:
 561  		cond = opJumpEqual
 562  	case JumpNotEqual:
 563  		cond, flip = opJumpEqual, true
 564  	case JumpGreaterThan:
 565  		cond = opJumpGT
 566  	case JumpLessThan:
 567  		cond, flip = opJumpGE, true
 568  	case JumpGreaterOrEqual:
 569  		cond = opJumpGE
 570  	case JumpLessOrEqual:
 571  		cond, flip = opJumpGT, true
 572  	case JumpBitsSet:
 573  		cond = opJumpSet
 574  	case JumpBitsNotSet:
 575  		cond, flip = opJumpSet, true
 576  	default:
 577  		return RawInstruction{}, fmt.Errorf("unknown JumpTest %v", test)
 578  	}
 579  	jt, jf := skipTrue, skipFalse
 580  	if flip {
 581  		jt, jf = jf, jt
 582  	}
 583  	return RawInstruction{
 584  		Op: opClsJump | uint16(cond) | uint16(operand),
 585  		Jt: jt,
 586  		Jf: jf,
 587  		K:  k,
 588  	}, nil
 589  }
 590  
 591  // jumpToString converts a jump instruction to assembler notation
 592  func jumpToString(cond JumpTest, operand string, skipTrue, skipFalse uint8) string {
 593  	switch cond {
 594  	// K == A
 595  	case JumpEqual:
 596  		return conditionalJump(operand, skipTrue, skipFalse, "jeq", "jneq")
 597  	// K != A
 598  	case JumpNotEqual:
 599  		return fmt.Sprintf("jneq %s,%d", operand, skipTrue)
 600  	// K > A
 601  	case JumpGreaterThan:
 602  		return conditionalJump(operand, skipTrue, skipFalse, "jgt", "jle")
 603  	// K < A
 604  	case JumpLessThan:
 605  		return fmt.Sprintf("jlt %s,%d", operand, skipTrue)
 606  	// K >= A
 607  	case JumpGreaterOrEqual:
 608  		return conditionalJump(operand, skipTrue, skipFalse, "jge", "jlt")
 609  	// K <= A
 610  	case JumpLessOrEqual:
 611  		return fmt.Sprintf("jle %s,%d", operand, skipTrue)
 612  	// K & A != 0
 613  	case JumpBitsSet:
 614  		if skipFalse > 0 {
 615  			return fmt.Sprintf("jset %s,%d,%d", operand, skipTrue, skipFalse)
 616  		}
 617  		return fmt.Sprintf("jset %s,%d", operand, skipTrue)
 618  	// K & A == 0, there is no assembler instruction for JumpBitNotSet, use JumpBitSet and invert skips
 619  	case JumpBitsNotSet:
 620  		return jumpToString(JumpBitsSet, operand, skipFalse, skipTrue)
 621  	default:
 622  		return fmt.Sprintf("unknown JumpTest %#v", cond)
 623  	}
 624  }
 625  
 626  func conditionalJump(operand string, skipTrue, skipFalse uint8, positiveJump, negativeJump string) string {
 627  	if skipTrue > 0 {
 628  		if skipFalse > 0 {
 629  			return fmt.Sprintf("%s %s,%d,%d", positiveJump, operand, skipTrue, skipFalse)
 630  		}
 631  		return fmt.Sprintf("%s %s,%d", positiveJump, operand, skipTrue)
 632  	}
 633  	return fmt.Sprintf("%s %s,%d", negativeJump, operand, skipFalse)
 634  }
 635  
 636  // RetA exits the BPF program, returning the value of register A.
 637  type RetA struct{}
 638  
 639  // Assemble implements the Instruction Assemble method.
 640  func (a RetA) Assemble() (RawInstruction, error) {
 641  	return RawInstruction{
 642  		Op: opClsReturn | opRetSrcA,
 643  	}, nil
 644  }
 645  
 646  // String returns the instruction in assembler notation.
 647  func (a RetA) String() string {
 648  	return fmt.Sprintf("ret a")
 649  }
 650  
 651  // RetConstant exits the BPF program, returning a constant value.
 652  type RetConstant struct {
 653  	Val uint32
 654  }
 655  
 656  // Assemble implements the Instruction Assemble method.
 657  func (a RetConstant) Assemble() (RawInstruction, error) {
 658  	return RawInstruction{
 659  		Op: opClsReturn | opRetSrcConstant,
 660  		K:  a.Val,
 661  	}, nil
 662  }
 663  
 664  // String returns the instruction in assembler notation.
 665  func (a RetConstant) String() string {
 666  	return fmt.Sprintf("ret #%d", a.Val)
 667  }
 668  
 669  // TXA copies the value of register X to register A.
 670  type TXA struct{}
 671  
 672  // Assemble implements the Instruction Assemble method.
 673  func (a TXA) Assemble() (RawInstruction, error) {
 674  	return RawInstruction{
 675  		Op: opClsMisc | opMiscTXA,
 676  	}, nil
 677  }
 678  
 679  // String returns the instruction in assembler notation.
 680  func (a TXA) String() string {
 681  	return fmt.Sprintf("txa")
 682  }
 683  
 684  // TAX copies the value of register A to register X.
 685  type TAX struct{}
 686  
 687  // Assemble implements the Instruction Assemble method.
 688  func (a TAX) Assemble() (RawInstruction, error) {
 689  	return RawInstruction{
 690  		Op: opClsMisc | opMiscTAX,
 691  	}, nil
 692  }
 693  
 694  // String returns the instruction in assembler notation.
 695  func (a TAX) String() string {
 696  	return fmt.Sprintf("tax")
 697  }
 698  
 699  func assembleLoad(dst Register, loadSize int, mode uint16, k uint32) (RawInstruction, error) {
 700  	var (
 701  		cls uint16
 702  		sz  uint16
 703  	)
 704  	switch dst {
 705  	case RegA:
 706  		cls = opClsLoadA
 707  	case RegX:
 708  		cls = opClsLoadX
 709  	default:
 710  		return RawInstruction{}, fmt.Errorf("invalid target register %v", dst)
 711  	}
 712  	switch loadSize {
 713  	case 1:
 714  		sz = opLoadWidth1
 715  	case 2:
 716  		sz = opLoadWidth2
 717  	case 4:
 718  		sz = opLoadWidth4
 719  	default:
 720  		return RawInstruction{}, fmt.Errorf("invalid load byte length %d", sz)
 721  	}
 722  	return RawInstruction{
 723  		Op: cls | sz | mode,
 724  		K:  k,
 725  	}, nil
 726  }
 727