summaryrefslogtreecommitdiff
path: root/src/libs/reference_decoder/sim86_instruction_table.inl
blob: 576c0f6018e2b3d85ce9a54d1345ab1c15d1f054 (plain) (blame)
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
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
/* ========================================================================

   (C) Copyright 2023 by Molly Rocket, Inc., All Rights Reserved.
   
   This software is provided 'as-is', without any express or implied
   warranty. In no event will the authors be held liable for any damages
   arising from the use of this software.
   
   Please see https://computerenhance.com for more information
   
   ======================================================================== */

/*
   NOTE(casey): This instruction table is a direct translation of table 4-12 in the Intel 8086 manual.
   The macros are designed to allow direct transcription, without changing the order or manner
   of specification in the table in any way. Additional "implicit" versions of the macros are provided
   so that hard-coded fields can be supplied uniformly.
   
   The table is also designed to allow you to include it multiple times to "pull out" other things
   from the table, such as opcode mnemonics as strings or enums, etc.
*/

#ifndef INST
#define INST(Mnemonic, ...) {Op_##Mnemonic, __VA_ARGS__},
#endif

#ifndef INSTALT
#define INSTALT INST
#endif

#define B(Bits) {Bits_Literal, sizeof(#Bits)-1, 0, 0b##Bits}
#define D {Bits_D, 1}
#define S {Bits_S, 1}
#define W {Bits_W, 1}
#define V {Bits_V, 1}
#define Z {Bits_Z, 1}

#define XXX {Bits_Data, 3, 0}
#define YYY {Bits_Data, 3, 3}
#define RM {Bits_RM, 3}
#define MOD {Bits_MOD, 2}
#define REG {Bits_REG, 3}
#define SR {Bits_SR, 2}

#define ImpW(Value) {Bits_W, 0, 0, Value}
#define ImpREG(Value) {Bits_REG, 0, 0, Value}
#define ImpMOD(Value) {Bits_MOD, 0, 0, Value}
#define ImpRM(Value) {Bits_RM, 0, 0, Value}
#define ImpD(Value) {Bits_D, 0, 0, Value}
#define ImpS(Value) {Bits_S, 0, 0, Value}
    
#define DISP {Bits_Disp, 0, 0, 0}
#define ADDR {Bits_Disp, 0, 0, 0}, {Bits_DispAlwaysW, 0, 0, 1}
#define DATA {Bits_Data, 0, 0, 0}
#define DATA_IF_W {Bits_WMakesDataW, 0, 0, 1}
#define Flags(F) {F, 0, 0, 1}

INST(mov, {B(100010), D, W, MOD, REG, RM})
INSTALT(mov, {B(1100011), W, MOD, B(000), RM, DATA, DATA_IF_W, ImpD(0)})
INSTALT(mov, {B(1011), W, REG, DATA, DATA_IF_W, ImpD(1)})
INSTALT(mov, {B(1010000), W, ADDR, ImpREG(0), ImpMOD(0), ImpRM(0b110), ImpD(1)})
INSTALT(mov, {B(1010001), W, ADDR, ImpREG(0), ImpMOD(0), ImpRM(0b110), ImpD(0)})
INSTALT(mov, {B(100011), D, B(0), MOD, B(0), SR, RM, ImpW(1)}) // NOTE(casey): This collapses 2 entries in the 8086 table by adding an explicit D bit

INST(push, {B(11111111), MOD, B(110), RM, ImpW(1), ImpD(1)})
INSTALT(push, {B(01010), REG, ImpW(1), ImpD(1)})
INSTALT(push, {B(000), SR, B(110), ImpW(1), ImpD(1)})

INST(pop, {B(10001111), MOD, B(000), RM, ImpW(1), ImpD(1)})
INSTALT(pop, {B(01011), REG, ImpW(1), ImpD(1)})
INSTALT(pop, {B(000), SR, B(111), ImpW(1), ImpD(1)})

INST(xchg, {B(1000011), W, MOD, REG, RM, ImpD(1)})
INSTALT(xchg, {B(10010), REG, ImpMOD(0b11), ImpW(1), ImpRM(0)})

INST(in, {B(1110010), W, DATA, ImpREG(0), ImpD(1)})
INSTALT(in, {B(1110110), W, ImpREG(0), ImpD(1), ImpMOD(0b11), ImpRM(2), Flags(Bits_RMRegAlwaysW)})
INST(out, {B(1110011), W, DATA, ImpREG(0), ImpD(0)})
INSTALT(out, {B(1110111), W, ImpREG(0), ImpD(0), ImpMOD(0b11), ImpRM(2), Flags(Bits_RMRegAlwaysW)})

INST(xlat, {B(11010111)})
INST(lea, {B(10001101), MOD, REG, RM, ImpD(1), ImpW(1)})
INST(lds, {B(11000101), MOD, REG, RM, ImpD(1), ImpW(1)})
INST(les, {B(11000100), MOD, REG, RM, ImpD(1), ImpW(1)})
INST(lahf, {B(10011111)})
INST(sahf, {B(10011110)})
INST(pushf, {B(10011100)})
INST(popf, {B(10011101)})

INST(add, {B(000000), D, W, MOD, REG, RM})
INSTALT(add, {B(100000), S, W, MOD, B(000), RM, DATA, DATA_IF_W})
INSTALT(add, {B(0000010), W, DATA, DATA_IF_W, ImpREG(0), ImpD(1)})

INST(adc, {B(000100), D, W, MOD, REG, RM})
INSTALT(adc, {B(100000), S, W, MOD, B(010), RM, DATA, DATA_IF_W})
INSTALT(adc, {B(0001010), W, DATA, DATA_IF_W, ImpREG(0), ImpD(1)})

INST(inc, {B(1111111), W, MOD, B(000), RM, ImpD(1)})
INSTALT(inc, {B(01000), REG, ImpW(1), ImpD(1)})

INST(aaa, {B(00110111)})
INST(daa, {B(00100111)})

INST(sub, {B(001010), D, W, MOD, REG, RM})
INSTALT(sub, {B(100000), S, W, MOD, B(101), RM, DATA, DATA_IF_W})
INSTALT(sub, {B(0010110), W, DATA, DATA_IF_W, ImpREG(0), ImpD(1)})

INST(sbb, {B(000110), D, W, MOD, REG, RM})
INSTALT(sbb, {B(100000), S, W, MOD, B(011), RM, DATA, DATA_IF_W})
INSTALT(sbb, {B(0001110), W, DATA, DATA_IF_W, ImpREG(0), ImpD(1)})

INST(dec, {B(1111111), W, MOD, B(001), RM, ImpD(1)})
INSTALT(dec, {B(01001), REG, ImpW(1), ImpD(1)})

INST(neg, {B(1111011), W, MOD, B(011), RM})

INST(cmp, {B(001110), D, W, MOD, REG, RM})
INSTALT(cmp, {B(100000), S, W, MOD, B(111), RM, DATA, DATA_IF_W})
INSTALT(cmp, {B(0011110), W, DATA, DATA_IF_W, ImpREG(0), ImpD(1)}) // NOTE(casey): The manual table suggests this data is only 8-bit, but wouldn't it be 16 as well?

INST(aas, {B(00111111)})
INST(das, {B(00101111)})
INST(mul, {B(1111011), W, MOD, B(100), RM, ImpS(0)})
INST(imul, {B(1111011), W, MOD, B(101), RM, ImpS(1)})
INST(aam, {B(11010100), B(00001010)}) // NOTE(casey): The manual says this has a DISP... but how could it? What for??
INST(div, {B(1111011), W, MOD, B(110), RM, ImpS(0)})
INST(idiv, {B(1111011), W, MOD, B(111), RM, ImpS(1)})
INST(aad, {B(11010101), B(00001010)})
INST(cbw, {B(10011000)})
INST(cwd, {B(10011001)})

INST(not, {B(1111011), W, MOD, B(010), RM})
INST(shl, {B(110100), V, W, MOD, B(100), RM})
INST(shr, {B(110100), V, W, MOD, B(101), RM})
INST(sar, {B(110100), V, W, MOD, B(111), RM})
INST(rol, {B(110100), V, W, MOD, B(000), RM})
INST(ror, {B(110100), V, W, MOD, B(001), RM})
INST(rcl, {B(110100), V, W, MOD, B(010), RM})
INST(rcr, {B(110100), V, W, MOD, B(011), RM})

INST(and, {B(001000), D, W, MOD, REG, RM})
INSTALT(and, {B(1000000), W, MOD, B(100), RM, DATA, DATA_IF_W})
INSTALT(and, {B(0010010), W, DATA, DATA_IF_W, ImpREG(0), ImpD(1)})

INST(test, {B(1000010), W, MOD, REG, RM}) // NOTE(casey): The manual suggests there is a D flag here, but it doesn't appear to be true (it would conflict with xchg if it did)
INSTALT(test, {B(1111011), W, MOD, B(000), RM, DATA, DATA_IF_W})
INSTALT(test, {B(1010100), W, DATA, DATA_IF_W, ImpREG(0), ImpD(1)}) // NOTE(casey): The manual table suggests this data is only 8-bit, but it seems like it could be 16 too?

INST(or, {B(000010), D, W, MOD, REG, RM})
INSTALT(or, {B(1000000), W, MOD, B(001), RM, DATA, DATA_IF_W})
INSTALT(or, {B(0000110), W, DATA, DATA_IF_W, ImpREG(0), ImpD(1)})

INST(xor, {B(001100), D, W, MOD, REG, RM})
INSTALT(xor, {B(1000000), W, MOD, B(110), RM, DATA, DATA_IF_W}) // NOTE(casey): The manual has conflicting information about this encoding, but I believe this is the correct binary pattern.
INSTALT(xor, {B(0011010), W, DATA, DATA_IF_W, ImpREG(0), ImpD(1)})

INST(rep, {B(1111001), Z})
INST(movs, {B(1010010), W})
INST(cmps, {B(1010011), W})
INST(scas, {B(1010111), W})
INST(lods, {B(1010110), W})
INST(stos, {B(1010101), W})

INST(call, {B(11101000), ADDR, Flags(Bits_RelJMPDisp)})
INSTALT(call, {B(11111111), MOD, B(010), RM, ImpW(1)})
INSTALT(call, {B(10011010), ADDR, DATA, DATA_IF_W, ImpW(1), Flags(Bits_Far)})
INSTALT(call, {B(11111111), MOD, B(011), RM, ImpW(1), Flags(Bits_Far)})

INST(jmp, {B(11101001), ADDR, Flags(Bits_RelJMPDisp)})
INSTALT(jmp, {B(11101011), DISP, Flags(Bits_RelJMPDisp)})
INSTALT(jmp, {B(11111111), MOD, B(100), RM, ImpW(1)})
INSTALT(jmp, {B(11101010), ADDR, DATA, DATA_IF_W, ImpW(1), Flags(Bits_Far)})
INSTALT(jmp, {B(11111111), MOD, B(101), RM, ImpW(1), Flags(Bits_Far)})

// NOTE(casey): The actual Intel manual does not distinguish mnemonics RET and RETF,
// but NASM needs this to reassemble properly, so we do.
INST(ret, {B(11000011)})
INSTALT(ret, {B(11000010), DATA, DATA_IF_W, ImpW(1)})
INST(retf, {B(11001011), Flags(Bits_Far)})
INSTALT(retf, {B(11001010), DATA, DATA_IF_W, ImpW(1), Flags(Bits_Far)})

INST(je, {B(01110100), DISP, Flags(Bits_RelJMPDisp)})
INST(jl, {B(01111100), DISP, Flags(Bits_RelJMPDisp)})
INST(jle, {B(01111110), DISP, Flags(Bits_RelJMPDisp)})
INST(jb, {B(01110010), DISP, Flags(Bits_RelJMPDisp)})
INST(jbe, {B(01110110), DISP, Flags(Bits_RelJMPDisp)})
INST(jp, {B(01111010), DISP, Flags(Bits_RelJMPDisp)})
INST(jo, {B(01110000), DISP, Flags(Bits_RelJMPDisp)})
INST(js, {B(01111000), DISP, Flags(Bits_RelJMPDisp)})
INST(jne, {B(01110101), DISP, Flags(Bits_RelJMPDisp)})
INST(jnl, {B(01111101), DISP, Flags(Bits_RelJMPDisp)})
INST(jg, {B(01111111), DISP, Flags(Bits_RelJMPDisp)})
INST(jnb, {B(01110011), DISP, Flags(Bits_RelJMPDisp)})
INST(ja, {B(01110111), DISP, Flags(Bits_RelJMPDisp)})
INST(jnp, {B(01111011), DISP, Flags(Bits_RelJMPDisp)})
INST(jno, {B(01110001), DISP, Flags(Bits_RelJMPDisp)})
INST(jns, {B(01111001), DISP, Flags(Bits_RelJMPDisp)})
INST(loop, {B(11100010), DISP, Flags(Bits_RelJMPDisp)})
INST(loopz, {B(11100001), DISP, Flags(Bits_RelJMPDisp)})
INST(loopnz, {B(11100000), DISP, Flags(Bits_RelJMPDisp)})
INST(jcxz, {B(11100011), DISP, Flags(Bits_RelJMPDisp)})

INST(int, {B(11001101), DATA})
INST(int3, {B(11001100)}) // TODO(casey): The manual does not suggest that this intrinsic has an "int3" mnemonic, but NASM thinks so

INST(into, {B(11001110)})
INST(iret, {B(11001111)})

INST(clc, {B(11111000)})
INST(cmc, {B(11110101)})
INST(stc, {B(11111001)})
INST(cld, {B(11111100)})
INST(std, {B(11111101)})
INST(cli, {B(11111010)})
INST(sti, {B(11111011)})
INST(hlt, {B(11110100)})
INST(wait, {B(10011011)})
INST(esc, {B(11011), XXX, MOD, YYY, RM})
INST(lock, {B(11110000)})
INST(segment, {B(001), SR, B(110)})

#undef INST
#undef INSTALT

#undef B
#undef D
#undef S
#undef W
#undef V
#undef Z

#undef XXX
#undef YYY
#undef RM
#undef MOD
#undef REG
#undef SR

#undef ImpW
#undef ImpREG
#undef ImpMOD
#undef ImpRM
#undef ImpD
#undef ImpS

#undef DISP
#undef ADDR
#undef DATA
#undef DATA_IF_W
#undef Flags