650 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			650 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // Copyright 2014 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 x86asm implements decoding of x86 machine code.
 | |
| package x86asm
 | |
| 
 | |
| import (
 | |
| 	"bytes"
 | |
| 	"fmt"
 | |
| )
 | |
| 
 | |
| // An Inst is a single instruction.
 | |
| type Inst struct {
 | |
| 	Prefix   Prefixes // Prefixes applied to the instruction.
 | |
| 	Op       Op       // Opcode mnemonic
 | |
| 	Opcode   uint32   // Encoded opcode bits, left aligned (first byte is Opcode>>24, etc)
 | |
| 	Args     Args     // Instruction arguments, in Intel order
 | |
| 	Mode     int      // processor mode in bits: 16, 32, or 64
 | |
| 	AddrSize int      // address size in bits: 16, 32, or 64
 | |
| 	DataSize int      // operand size in bits: 16, 32, or 64
 | |
| 	MemBytes int      // size of memory argument in bytes: 1, 2, 4, 8, 16, and so on.
 | |
| 	Len      int      // length of encoded instruction in bytes
 | |
| 	PCRel    int      // length of PC-relative address in instruction encoding
 | |
| 	PCRelOff int      // index of start of PC-relative address in instruction encoding
 | |
| }
 | |
| 
 | |
| // Prefixes is an array of prefixes associated with a single instruction.
 | |
| // The prefixes are listed in the same order as found in the instruction:
 | |
| // each prefix byte corresponds to one slot in the array. The first zero
 | |
| // in the array marks the end of the prefixes.
 | |
| type Prefixes [14]Prefix
 | |
| 
 | |
| // A Prefix represents an Intel instruction prefix.
 | |
| // The low 8 bits are the actual prefix byte encoding,
 | |
| // and the top 8 bits contain distinguishing bits and metadata.
 | |
| type Prefix uint16
 | |
| 
 | |
| const (
 | |
| 	// Metadata about the role of a prefix in an instruction.
 | |
| 	PrefixImplicit Prefix = 0x8000 // prefix is implied by instruction text
 | |
| 	PrefixIgnored  Prefix = 0x4000 // prefix is ignored: either irrelevant or overridden by a later prefix
 | |
| 	PrefixInvalid  Prefix = 0x2000 // prefix makes entire instruction invalid (bad LOCK)
 | |
| 
 | |
| 	// Memory segment overrides.
 | |
| 	PrefixES Prefix = 0x26 // ES segment override
 | |
| 	PrefixCS Prefix = 0x2E // CS segment override
 | |
| 	PrefixSS Prefix = 0x36 // SS segment override
 | |
| 	PrefixDS Prefix = 0x3E // DS segment override
 | |
| 	PrefixFS Prefix = 0x64 // FS segment override
 | |
| 	PrefixGS Prefix = 0x65 // GS segment override
 | |
| 
 | |
| 	// Branch prediction.
 | |
| 	PrefixPN Prefix = 0x12E // predict not taken (conditional branch only)
 | |
| 	PrefixPT Prefix = 0x13E // predict taken (conditional branch only)
 | |
| 
 | |
| 	// Size attributes.
 | |
| 	PrefixDataSize Prefix = 0x66 // operand size override
 | |
| 	PrefixData16   Prefix = 0x166
 | |
| 	PrefixData32   Prefix = 0x266
 | |
| 	PrefixAddrSize Prefix = 0x67 // address size override
 | |
| 	PrefixAddr16   Prefix = 0x167
 | |
| 	PrefixAddr32   Prefix = 0x267
 | |
| 
 | |
| 	// One of a kind.
 | |
| 	PrefixLOCK     Prefix = 0xF0 // lock
 | |
| 	PrefixREPN     Prefix = 0xF2 // repeat not zero
 | |
| 	PrefixXACQUIRE Prefix = 0x1F2
 | |
| 	PrefixBND      Prefix = 0x2F2
 | |
| 	PrefixREP      Prefix = 0xF3 // repeat
 | |
| 	PrefixXRELEASE Prefix = 0x1F3
 | |
| 
 | |
| 	// The REX prefixes must be in the range [PrefixREX, PrefixREX+0x10).
 | |
| 	// the other bits are set or not according to the intended use.
 | |
| 	PrefixREX       Prefix = 0x40 // REX 64-bit extension prefix
 | |
| 	PrefixREXW      Prefix = 0x08 // extension bit W (64-bit instruction width)
 | |
| 	PrefixREXR      Prefix = 0x04 // extension bit R (r field in modrm)
 | |
| 	PrefixREXX      Prefix = 0x02 // extension bit X (index field in sib)
 | |
| 	PrefixREXB      Prefix = 0x01 // extension bit B (r/m field in modrm or base field in sib)
 | |
| 	PrefixVEX2Bytes Prefix = 0xC5 // Short form of vex prefix
 | |
| 	PrefixVEX3Bytes Prefix = 0xC4 // Long form of vex prefix
 | |
| )
 | |
| 
 | |
| // IsREX reports whether p is a REX prefix byte.
 | |
| func (p Prefix) IsREX() bool {
 | |
| 	return p&0xF0 == PrefixREX
 | |
| }
 | |
| 
 | |
| func (p Prefix) IsVEX() bool {
 | |
| 	return p&0xFF == PrefixVEX2Bytes || p&0xFF == PrefixVEX3Bytes
 | |
| }
 | |
| 
 | |
| func (p Prefix) String() string {
 | |
| 	p &^= PrefixImplicit | PrefixIgnored | PrefixInvalid
 | |
| 	if s := prefixNames[p]; s != "" {
 | |
| 		return s
 | |
| 	}
 | |
| 
 | |
| 	if p.IsREX() {
 | |
| 		s := "REX."
 | |
| 		if p&PrefixREXW != 0 {
 | |
| 			s += "W"
 | |
| 		}
 | |
| 		if p&PrefixREXR != 0 {
 | |
| 			s += "R"
 | |
| 		}
 | |
| 		if p&PrefixREXX != 0 {
 | |
| 			s += "X"
 | |
| 		}
 | |
| 		if p&PrefixREXB != 0 {
 | |
| 			s += "B"
 | |
| 		}
 | |
| 		return s
 | |
| 	}
 | |
| 
 | |
| 	return fmt.Sprintf("Prefix(%#x)", int(p))
 | |
| }
 | |
| 
 | |
| // An Op is an x86 opcode.
 | |
| type Op uint32
 | |
| 
 | |
| func (op Op) String() string {
 | |
| 	i := int(op)
 | |
| 	if i < 0 || i >= len(opNames) || opNames[i] == "" {
 | |
| 		return fmt.Sprintf("Op(%d)", i)
 | |
| 	}
 | |
| 	return opNames[i]
 | |
| }
 | |
| 
 | |
| // An Args holds the instruction arguments.
 | |
| // If an instruction has fewer than 4 arguments,
 | |
| // the final elements in the array are nil.
 | |
| type Args [4]Arg
 | |
| 
 | |
| // An Arg is a single instruction argument,
 | |
| // one of these types: Reg, Mem, Imm, Rel.
 | |
| type Arg interface {
 | |
| 	String() string
 | |
| 	isArg()
 | |
| }
 | |
| 
 | |
| // Note that the implements of Arg that follow are all sized
 | |
| // so that on a 64-bit machine the data can be inlined in
 | |
| // the interface value instead of requiring an allocation.
 | |
| 
 | |
| // A Reg is a single register.
 | |
| // The zero Reg value has no name but indicates ``no register.''
 | |
| type Reg uint8
 | |
| 
 | |
| const (
 | |
| 	_ Reg = iota
 | |
| 
 | |
| 	// 8-bit
 | |
| 	AL
 | |
| 	CL
 | |
| 	DL
 | |
| 	BL
 | |
| 	AH
 | |
| 	CH
 | |
| 	DH
 | |
| 	BH
 | |
| 	SPB
 | |
| 	BPB
 | |
| 	SIB
 | |
| 	DIB
 | |
| 	R8B
 | |
| 	R9B
 | |
| 	R10B
 | |
| 	R11B
 | |
| 	R12B
 | |
| 	R13B
 | |
| 	R14B
 | |
| 	R15B
 | |
| 
 | |
| 	// 16-bit
 | |
| 	AX
 | |
| 	CX
 | |
| 	DX
 | |
| 	BX
 | |
| 	SP
 | |
| 	BP
 | |
| 	SI
 | |
| 	DI
 | |
| 	R8W
 | |
| 	R9W
 | |
| 	R10W
 | |
| 	R11W
 | |
| 	R12W
 | |
| 	R13W
 | |
| 	R14W
 | |
| 	R15W
 | |
| 
 | |
| 	// 32-bit
 | |
| 	EAX
 | |
| 	ECX
 | |
| 	EDX
 | |
| 	EBX
 | |
| 	ESP
 | |
| 	EBP
 | |
| 	ESI
 | |
| 	EDI
 | |
| 	R8L
 | |
| 	R9L
 | |
| 	R10L
 | |
| 	R11L
 | |
| 	R12L
 | |
| 	R13L
 | |
| 	R14L
 | |
| 	R15L
 | |
| 
 | |
| 	// 64-bit
 | |
| 	RAX
 | |
| 	RCX
 | |
| 	RDX
 | |
| 	RBX
 | |
| 	RSP
 | |
| 	RBP
 | |
| 	RSI
 | |
| 	RDI
 | |
| 	R8
 | |
| 	R9
 | |
| 	R10
 | |
| 	R11
 | |
| 	R12
 | |
| 	R13
 | |
| 	R14
 | |
| 	R15
 | |
| 
 | |
| 	// Instruction pointer.
 | |
| 	IP  // 16-bit
 | |
| 	EIP // 32-bit
 | |
| 	RIP // 64-bit
 | |
| 
 | |
| 	// 387 floating point registers.
 | |
| 	F0
 | |
| 	F1
 | |
| 	F2
 | |
| 	F3
 | |
| 	F4
 | |
| 	F5
 | |
| 	F6
 | |
| 	F7
 | |
| 
 | |
| 	// MMX registers.
 | |
| 	M0
 | |
| 	M1
 | |
| 	M2
 | |
| 	M3
 | |
| 	M4
 | |
| 	M5
 | |
| 	M6
 | |
| 	M7
 | |
| 
 | |
| 	// XMM registers.
 | |
| 	X0
 | |
| 	X1
 | |
| 	X2
 | |
| 	X3
 | |
| 	X4
 | |
| 	X5
 | |
| 	X6
 | |
| 	X7
 | |
| 	X8
 | |
| 	X9
 | |
| 	X10
 | |
| 	X11
 | |
| 	X12
 | |
| 	X13
 | |
| 	X14
 | |
| 	X15
 | |
| 
 | |
| 	// Segment registers.
 | |
| 	ES
 | |
| 	CS
 | |
| 	SS
 | |
| 	DS
 | |
| 	FS
 | |
| 	GS
 | |
| 
 | |
| 	// System registers.
 | |
| 	GDTR
 | |
| 	IDTR
 | |
| 	LDTR
 | |
| 	MSW
 | |
| 	TASK
 | |
| 
 | |
| 	// Control registers.
 | |
| 	CR0
 | |
| 	CR1
 | |
| 	CR2
 | |
| 	CR3
 | |
| 	CR4
 | |
| 	CR5
 | |
| 	CR6
 | |
| 	CR7
 | |
| 	CR8
 | |
| 	CR9
 | |
| 	CR10
 | |
| 	CR11
 | |
| 	CR12
 | |
| 	CR13
 | |
| 	CR14
 | |
| 	CR15
 | |
| 
 | |
| 	// Debug registers.
 | |
| 	DR0
 | |
| 	DR1
 | |
| 	DR2
 | |
| 	DR3
 | |
| 	DR4
 | |
| 	DR5
 | |
| 	DR6
 | |
| 	DR7
 | |
| 	DR8
 | |
| 	DR9
 | |
| 	DR10
 | |
| 	DR11
 | |
| 	DR12
 | |
| 	DR13
 | |
| 	DR14
 | |
| 	DR15
 | |
| 
 | |
| 	// Task registers.
 | |
| 	TR0
 | |
| 	TR1
 | |
| 	TR2
 | |
| 	TR3
 | |
| 	TR4
 | |
| 	TR5
 | |
| 	TR6
 | |
| 	TR7
 | |
| )
 | |
| 
 | |
| const regMax = TR7
 | |
| 
 | |
| func (Reg) isArg() {}
 | |
| 
 | |
| func (r Reg) String() string {
 | |
| 	i := int(r)
 | |
| 	if i < 0 || i >= len(regNames) || regNames[i] == "" {
 | |
| 		return fmt.Sprintf("Reg(%d)", i)
 | |
| 	}
 | |
| 	return regNames[i]
 | |
| }
 | |
| 
 | |
| // A Mem is a memory reference.
 | |
| // The general form is Segment:[Base+Scale*Index+Disp].
 | |
| type Mem struct {
 | |
| 	Segment Reg
 | |
| 	Base    Reg
 | |
| 	Scale   uint8
 | |
| 	Index   Reg
 | |
| 	Disp    int64
 | |
| }
 | |
| 
 | |
| func (Mem) isArg() {}
 | |
| 
 | |
| func (m Mem) String() string {
 | |
| 	var base, plus, scale, index, disp string
 | |
| 
 | |
| 	if m.Base != 0 {
 | |
| 		base = m.Base.String()
 | |
| 	}
 | |
| 	if m.Scale != 0 {
 | |
| 		if m.Base != 0 {
 | |
| 			plus = "+"
 | |
| 		}
 | |
| 		if m.Scale > 1 {
 | |
| 			scale = fmt.Sprintf("%d*", m.Scale)
 | |
| 		}
 | |
| 		index = m.Index.String()
 | |
| 	}
 | |
| 	if m.Disp != 0 || m.Base == 0 && m.Scale == 0 {
 | |
| 		disp = fmt.Sprintf("%+#x", m.Disp)
 | |
| 	}
 | |
| 	return "[" + base + plus + scale + index + disp + "]"
 | |
| }
 | |
| 
 | |
| // A Rel is an offset relative to the current instruction pointer.
 | |
| type Rel int32
 | |
| 
 | |
| func (Rel) isArg() {}
 | |
| 
 | |
| func (r Rel) String() string {
 | |
| 	return fmt.Sprintf(".%+d", r)
 | |
| }
 | |
| 
 | |
| // An Imm is an integer constant.
 | |
| type Imm int64
 | |
| 
 | |
| func (Imm) isArg() {}
 | |
| 
 | |
| func (i Imm) String() string {
 | |
| 	return fmt.Sprintf("%#x", int64(i))
 | |
| }
 | |
| 
 | |
| func (i Inst) String() string {
 | |
| 	var buf bytes.Buffer
 | |
| 	for _, p := range i.Prefix {
 | |
| 		if p == 0 {
 | |
| 			break
 | |
| 		}
 | |
| 		if p&PrefixImplicit != 0 {
 | |
| 			continue
 | |
| 		}
 | |
| 		fmt.Fprintf(&buf, "%v ", p)
 | |
| 	}
 | |
| 	fmt.Fprintf(&buf, "%v", i.Op)
 | |
| 	sep := " "
 | |
| 	for _, v := range i.Args {
 | |
| 		if v == nil {
 | |
| 			break
 | |
| 		}
 | |
| 		fmt.Fprintf(&buf, "%s%v", sep, v)
 | |
| 		sep = ", "
 | |
| 	}
 | |
| 	return buf.String()
 | |
| }
 | |
| 
 | |
| func isReg(a Arg) bool {
 | |
| 	_, ok := a.(Reg)
 | |
| 	return ok
 | |
| }
 | |
| 
 | |
| func isSegReg(a Arg) bool {
 | |
| 	r, ok := a.(Reg)
 | |
| 	return ok && ES <= r && r <= GS
 | |
| }
 | |
| 
 | |
| func isMem(a Arg) bool {
 | |
| 	_, ok := a.(Mem)
 | |
| 	return ok
 | |
| }
 | |
| 
 | |
| func isImm(a Arg) bool {
 | |
| 	_, ok := a.(Imm)
 | |
| 	return ok
 | |
| }
 | |
| 
 | |
| func regBytes(a Arg) int {
 | |
| 	r, ok := a.(Reg)
 | |
| 	if !ok {
 | |
| 		return 0
 | |
| 	}
 | |
| 	if AL <= r && r <= R15B {
 | |
| 		return 1
 | |
| 	}
 | |
| 	if AX <= r && r <= R15W {
 | |
| 		return 2
 | |
| 	}
 | |
| 	if EAX <= r && r <= R15L {
 | |
| 		return 4
 | |
| 	}
 | |
| 	if RAX <= r && r <= R15 {
 | |
| 		return 8
 | |
| 	}
 | |
| 	return 0
 | |
| }
 | |
| 
 | |
| func isSegment(p Prefix) bool {
 | |
| 	switch p {
 | |
| 	case PrefixCS, PrefixDS, PrefixES, PrefixFS, PrefixGS, PrefixSS:
 | |
| 		return true
 | |
| 	}
 | |
| 	return false
 | |
| }
 | |
| 
 | |
| // The Op definitions and string list are in tables.go.
 | |
| 
 | |
| var prefixNames = map[Prefix]string{
 | |
| 	PrefixCS:       "CS",
 | |
| 	PrefixDS:       "DS",
 | |
| 	PrefixES:       "ES",
 | |
| 	PrefixFS:       "FS",
 | |
| 	PrefixGS:       "GS",
 | |
| 	PrefixSS:       "SS",
 | |
| 	PrefixLOCK:     "LOCK",
 | |
| 	PrefixREP:      "REP",
 | |
| 	PrefixREPN:     "REPN",
 | |
| 	PrefixAddrSize: "ADDRSIZE",
 | |
| 	PrefixDataSize: "DATASIZE",
 | |
| 	PrefixAddr16:   "ADDR16",
 | |
| 	PrefixData16:   "DATA16",
 | |
| 	PrefixAddr32:   "ADDR32",
 | |
| 	PrefixData32:   "DATA32",
 | |
| 	PrefixBND:      "BND",
 | |
| 	PrefixXACQUIRE: "XACQUIRE",
 | |
| 	PrefixXRELEASE: "XRELEASE",
 | |
| 	PrefixREX:      "REX",
 | |
| 	PrefixPT:       "PT",
 | |
| 	PrefixPN:       "PN",
 | |
| }
 | |
| 
 | |
| var regNames = [...]string{
 | |
| 	AL:   "AL",
 | |
| 	CL:   "CL",
 | |
| 	BL:   "BL",
 | |
| 	DL:   "DL",
 | |
| 	AH:   "AH",
 | |
| 	CH:   "CH",
 | |
| 	BH:   "BH",
 | |
| 	DH:   "DH",
 | |
| 	SPB:  "SPB",
 | |
| 	BPB:  "BPB",
 | |
| 	SIB:  "SIB",
 | |
| 	DIB:  "DIB",
 | |
| 	R8B:  "R8B",
 | |
| 	R9B:  "R9B",
 | |
| 	R10B: "R10B",
 | |
| 	R11B: "R11B",
 | |
| 	R12B: "R12B",
 | |
| 	R13B: "R13B",
 | |
| 	R14B: "R14B",
 | |
| 	R15B: "R15B",
 | |
| 	AX:   "AX",
 | |
| 	CX:   "CX",
 | |
| 	BX:   "BX",
 | |
| 	DX:   "DX",
 | |
| 	SP:   "SP",
 | |
| 	BP:   "BP",
 | |
| 	SI:   "SI",
 | |
| 	DI:   "DI",
 | |
| 	R8W:  "R8W",
 | |
| 	R9W:  "R9W",
 | |
| 	R10W: "R10W",
 | |
| 	R11W: "R11W",
 | |
| 	R12W: "R12W",
 | |
| 	R13W: "R13W",
 | |
| 	R14W: "R14W",
 | |
| 	R15W: "R15W",
 | |
| 	EAX:  "EAX",
 | |
| 	ECX:  "ECX",
 | |
| 	EDX:  "EDX",
 | |
| 	EBX:  "EBX",
 | |
| 	ESP:  "ESP",
 | |
| 	EBP:  "EBP",
 | |
| 	ESI:  "ESI",
 | |
| 	EDI:  "EDI",
 | |
| 	R8L:  "R8L",
 | |
| 	R9L:  "R9L",
 | |
| 	R10L: "R10L",
 | |
| 	R11L: "R11L",
 | |
| 	R12L: "R12L",
 | |
| 	R13L: "R13L",
 | |
| 	R14L: "R14L",
 | |
| 	R15L: "R15L",
 | |
| 	RAX:  "RAX",
 | |
| 	RCX:  "RCX",
 | |
| 	RDX:  "RDX",
 | |
| 	RBX:  "RBX",
 | |
| 	RSP:  "RSP",
 | |
| 	RBP:  "RBP",
 | |
| 	RSI:  "RSI",
 | |
| 	RDI:  "RDI",
 | |
| 	R8:   "R8",
 | |
| 	R9:   "R9",
 | |
| 	R10:  "R10",
 | |
| 	R11:  "R11",
 | |
| 	R12:  "R12",
 | |
| 	R13:  "R13",
 | |
| 	R14:  "R14",
 | |
| 	R15:  "R15",
 | |
| 	IP:   "IP",
 | |
| 	EIP:  "EIP",
 | |
| 	RIP:  "RIP",
 | |
| 	F0:   "F0",
 | |
| 	F1:   "F1",
 | |
| 	F2:   "F2",
 | |
| 	F3:   "F3",
 | |
| 	F4:   "F4",
 | |
| 	F5:   "F5",
 | |
| 	F6:   "F6",
 | |
| 	F7:   "F7",
 | |
| 	M0:   "M0",
 | |
| 	M1:   "M1",
 | |
| 	M2:   "M2",
 | |
| 	M3:   "M3",
 | |
| 	M4:   "M4",
 | |
| 	M5:   "M5",
 | |
| 	M6:   "M6",
 | |
| 	M7:   "M7",
 | |
| 	X0:   "X0",
 | |
| 	X1:   "X1",
 | |
| 	X2:   "X2",
 | |
| 	X3:   "X3",
 | |
| 	X4:   "X4",
 | |
| 	X5:   "X5",
 | |
| 	X6:   "X6",
 | |
| 	X7:   "X7",
 | |
| 	X8:   "X8",
 | |
| 	X9:   "X9",
 | |
| 	X10:  "X10",
 | |
| 	X11:  "X11",
 | |
| 	X12:  "X12",
 | |
| 	X13:  "X13",
 | |
| 	X14:  "X14",
 | |
| 	X15:  "X15",
 | |
| 	CS:   "CS",
 | |
| 	SS:   "SS",
 | |
| 	DS:   "DS",
 | |
| 	ES:   "ES",
 | |
| 	FS:   "FS",
 | |
| 	GS:   "GS",
 | |
| 	GDTR: "GDTR",
 | |
| 	IDTR: "IDTR",
 | |
| 	LDTR: "LDTR",
 | |
| 	MSW:  "MSW",
 | |
| 	TASK: "TASK",
 | |
| 	CR0:  "CR0",
 | |
| 	CR1:  "CR1",
 | |
| 	CR2:  "CR2",
 | |
| 	CR3:  "CR3",
 | |
| 	CR4:  "CR4",
 | |
| 	CR5:  "CR5",
 | |
| 	CR6:  "CR6",
 | |
| 	CR7:  "CR7",
 | |
| 	CR8:  "CR8",
 | |
| 	CR9:  "CR9",
 | |
| 	CR10: "CR10",
 | |
| 	CR11: "CR11",
 | |
| 	CR12: "CR12",
 | |
| 	CR13: "CR13",
 | |
| 	CR14: "CR14",
 | |
| 	CR15: "CR15",
 | |
| 	DR0:  "DR0",
 | |
| 	DR1:  "DR1",
 | |
| 	DR2:  "DR2",
 | |
| 	DR3:  "DR3",
 | |
| 	DR4:  "DR4",
 | |
| 	DR5:  "DR5",
 | |
| 	DR6:  "DR6",
 | |
| 	DR7:  "DR7",
 | |
| 	DR8:  "DR8",
 | |
| 	DR9:  "DR9",
 | |
| 	DR10: "DR10",
 | |
| 	DR11: "DR11",
 | |
| 	DR12: "DR12",
 | |
| 	DR13: "DR13",
 | |
| 	DR14: "DR14",
 | |
| 	DR15: "DR15",
 | |
| 	TR0:  "TR0",
 | |
| 	TR1:  "TR1",
 | |
| 	TR2:  "TR2",
 | |
| 	TR3:  "TR3",
 | |
| 	TR4:  "TR4",
 | |
| 	TR5:  "TR5",
 | |
| 	TR6:  "TR6",
 | |
| 	TR7:  "TR7",
 | |
| }
 |