summaryrefslogtreecommitdiff
path: root/instructions.h
blob: 04f35a30e6112f3731937a1ef707a19e2b33b4a0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
#ifndef INSTRUCTIONS_H
#define INSTRUCTIONS_H

// Prefixes
// Prefix groups are mutually exclusive

// Prefix group 1
#define PREFIX_LOCK  0xf0
#define PREFIX_REPNE 0xf2
#define PREFIX_REPNZ PREFIX_REPNE
#define PREFIX_REP   0xf3
#define PREFIX_REPE  PREFIX_REP
#define PREFIX_REPZ  PREFIX_REP

// Prefix group 2
#define PREFIX_OVERRIDE_SEGMENT_CS 0x2e
#define PREFIX_OVERRIDE_SEGMENT_SS 0x36
#define PREFIX_OVERRIDE_SEGMENT_DS 0x3e
#define PREFIX_OVERRIDE_SEGMENT_ES 0x26
#define PREFIX_OVERRIDE_SEGMENT_FS 0x64
#define PREFIX_OVERRIDE_SEGMENT_GS 0x65
#define PREFIX_BRANCH_NOT_TAKEN    PREFIX_OVERRIDE_SEGMENT_CS
#define PREFIX_BRANCH_TAKEN        PREFIX_OVERRIDE_SEGMENT_DS

// Prefix group 3
// sets opsize to 16 bit,
// if combined with REX.w, set opsize to 64,
// if only REX.w, then both opsize and address is 64 bit.
#define PREFIX_OVERRIDE_OPSIZE 0x66

// Prefix group 4
// sets address size to 32 bit.
#define PREFIX_OVERRIDE_ADDRESSSIZE 0x67

// Rex prefixes are used when
// - Using 64-bit operand sizes when default is not 64 bit (eg. rax as opposed
// to eax),
// - Using extended registers (R8 to R15, XMM8 to XMM15, YMM8 to YMM15, CR8 to
// CR15 and DR8 to DR15),
// - Using one of the uniform byte registers: SPL, BPL, SIL or DIL.
// Rex prefix
// [ 0 1 0 0 | W R X B ]
// W changes operand size to 64 bits
// R Extension to MODRM.reg field
// X Extension SIB.index field, 64-bit addressing
// B Extension to MODRM.rm or SIB.base fields, 64-bit addressing
#define REX(mode) (0b01000000 | mode)
#define REX_W     0b1000
#define REX_R     0b0100
#define REX_X     0b0010
#define REX_B     0b0001

// Rex2 prefix (2 bytes
// [ 1  1  0  1  | 0  1  0  1  ]
// [ M₀ R₄ X₄ B₄ | W  R₃ X₃ B₃ ]
// W,  R₃, X₃, and B₃ are the same as in REX
// R₄, X₄, and B₄ are used to encode 32 EGPR registers.
// M0 selects between legacy map 0 (1-byte opcodes, no escape) and legacy
//    map 1 (2-byte opcodes, escape 0x0F).
//
// Opcodes that default to 64bit in long mode (and does therefore not need REX
// prefixes) are:
// CALL (near), ENTER, Jcc, JrCXZ, JMP (near), LEAVE, LGDT, LIDT, LLDT, LOOP,
// LOOPcc, LTR, MOV CR(n), MOV DR(n), POP reg/mem, POP reg, POP FS, POP GS,
// POPFQ, PUSH imm8, PUSH imm32, PUSH reg/mem, PUSH reg, PUSH FS, PUSH GS,
// PUSHFQ, and RET (near).
#define REX2(mode) REX(REX_R | REX_B), mode
#define REX2_W     REX_W
#define REX2_R     REX_R
#define REX2_R3    REX_R
#define REX2_X     REX_X
#define REX2_X3    REX_X
#define REX2_B     REX_B
#define REX2_B3    REX_B
#define REX2_M     0b10000000
#define REX2_R4    0b01000000
#define REX2_X4    0b00100000
#define REX2_B4    0b00010000

// MODR/M
//   MOD   REG     RM  (r/m = register/memory)
// [ 1 1 | 1 0 1 | 1 0 1 ]
// if mod == 0b11, then treat r/m as register,
// (examples in OP SRC DST ordering (same as the reg-rm order)
// gas/at&t ASM        => op  reg-idx reg-idx =>   MOD | REG   | RM
// --------------------+----------------------+---------------------
// ie "xor %rcx, %rcx" => XOR 1       1       => [ 1 1 | 0 0 1 | 0 0 1 ]
// ie "xor %rsi, %rbp" => XOR 6       5       => [ 1 1 | 1 0 1 | 1 1 0 ]
//
// Other combinations of MOD means RM specifies an addressing mode.
// For 64bit systems:
//
// "disp" is displacement specified in `mod` bits.
#define MODRM(mod, reg, rm) (((mod & 3) << 6) | ((reg & 7) << 3) | (rm & 7))

// SIB (Scale, Index, Base)
// [ S S | I I I | B B B ]
// S S: equals 2^SIB.index, eg. 1, 2, 4, 8
// I I I: index register to use. REX.X, VEX.~X, and XOP.~X expands this with 1
// most significant bit. B B B: base register to use. REX.B, VEX.~B, and XOP.~B
// expands this with 1 most significant bit. Same layout as MODRM, could as well
// as define it as MODRM(scale, index, base)
#define SIB(scale, index, base)                                                \
  (((scale & 3) << 6) | ((index & 7) << 3) | (base & 7))

// Opcode encoding
// <op>
// 0x0F <op>
// 0x0F 0x38 <op>
// 0x0F 0x3A <op>

// Instructions
// Move immediates
#define MOVBI(reg, imm) (0xb0 + reg), (imm & 0xff)
#define MOVWI(reg, imm)                                                        \
  PREFIX_OVERRIDE_OPSIZE, (0xb8 + reg), (imm & 0xff), ((imm >> 8) & 0xff)
#define MOVDI(reg, imm)                                                        \
  (0xb8 + reg), (imm & 0xff), ((imm >> 8) & 0xff), ((imm >> 16) & 0xff),       \
    ((imm >> 24) & 0xff)
#define MOVQI(reg, imm)                                                        \
  REX(REX_W), MOVDI(reg, imm), ((imm >> 32) & 0xff), ((imm >> 40) & 0xff),     \
    ((imm >> 48) & 0xff), ((imm >> 56) & 0xff)
#define MOVABS(reg, imm) MOVQI(reg, imm)

// Move registers
#define MOVB(src, dst) (0x88), MODRM(0b11, src, dst)
#define MOVW(src, dst) PREFIX_OVERRIDE_OPSIZE, (0x89), MODRM(0b11, src, dst)
#define MOVD(src, dst) (0x89), MODRM(0b11, src, dst)
#define MOVQ(src, dst) REX(REX_W), MOVD(src, dst)

#define ADDBIC(reg, imm) (0x10 + reg), (imm & 0xff)
#define ADDBIC(reg, imm) (0x10 + reg), (imm & 0xff)

#define ADD
#define CALL
#define CMP
#define DEC
#define DIV
#define HLT
#define IDIV
#define IMUL
#define INC
#define INT
#define INTO
#define IRET
#define JNZ
#define JMP
#define LEA
#define POP
#define PUSH
#define RET 0xc3
#define RETN
#define SUB
#define XOR /*TODO*/

#define SYSCALL 0x0f, 0x05

#endif