1 //: operating directly on a register
  2 
  3 :(before "End Initialize Op Names(name)")
  4 put(name, "01", "add r32 to rm32");
  5 
  6 :(scenario add_r32_to_r32)
  7 % Reg[EAX].i = 0x10;
  8 % Reg[EBX].i = 1;
  9 == 0x1
 10 # op  ModR/M  SIB   displacement  immediate
 11   01  d8                                      # add EBX to EAX
 12 # ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX)
 13 +run: add EBX to r/m32
 14 +run: r/m32 is EAX
 15 +run: storing 0x00000011
 16 
 17 :(before "End Single-Byte Opcodes")
 18 case 0x01: {  // add r32 to r/m32
 19   uint8_t modrm = next();
 20   uint8_t arg2 = (modrm>>3)&0x7;
 21   trace(90, "run") << "add " << rname(arg2) << " to r/m32" << end();
 22   int32_t* arg1 = effective_address(modrm);
 23   BINARY_ARITHMETIC_OP(+, *arg1, Reg[arg2].i);
 24   break;
 25 }
 26 
 27 :(code)
 28 // Implement tables 2-2 and 2-3 in the Intel manual, Volume 2.
 29 // We return a pointer so that instructions can write to multiple bytes in
 30 // 'Mem' at once.
 31 int32_t* effective_address(uint8_t modrm) {
 32   uint8_t mod = (modrm>>6);
 33   // ignore middle 3 'reg opcode' bits
 34   uint8_t rm = modrm & 0x7;
 35   uint32_t addr = 0;
 36   switch (mod) {
 37   case 3:
 38     // mod 3 is just register direct addressing
 39     trace(90, "run") << "r/m32 is " << rname(rm) << end();
 40     return &Reg[rm].i;
 41   // End Mod Special-cases(addr)
 42   default:
 43     cerr << "unrecognized mod bits: " << NUM(mod) << '\n';
 44     exit(1);
 45   }
 46   //: other mods are indirect, and they'll set addr appropriately
 47   return mem_addr_i32(addr);
 48 }
 49 
 50 string rname(uint8_t r) {
 51   switch (r) {
 52   case 0: return "EAX";
 53   case 1: return "ECX";
 54   case 2: return "EDX";
 55   case 3: return "EBX";
 56   case 4: return "ESP";
 57   case 5: return "EBP";
 58   case 6: return "ESI";
 59   case 7: return "EDI";
 60   default: raise << "invalid register " << r << '\n' << end();  return "";
 61   }
 62 }
 63 
 64 //:: subtract
 65 
 66 :(before "End Initialize Op Names(name)")
 67 put(name, "29", "subtract r32 from rm32");
 68 
 69 :(scenario subtract_r32_from_r32)
 70 % Reg[EAX].i = 10;
 71 % Reg[EBX].i = 1;
 72 == 0x1
 73 # op  ModR/M  SIB   displacement  immediate
 74   29  d8                                      # subtract EBX from EAX
 75 # ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX)
 76 +run: subtract EBX from r/m32
 77 +run: r/m32 is EAX
 78 +run: storing 0x00000009
 79 
 80 :(before "End Single-Byte Opcodes")
 81 case 0x29: {  // subtract r32 from r/m32
 82   uint8_t modrm = next();
 83   uint8_t arg2 = (modrm>>3)&0x7;
 84   trace(90, "run") << "subtract " << rname(arg2) << " from r/m32" << end();
 85   int32_t* arg1 = effective_address(modrm);
 86   BINARY_ARITHMETIC_OP(-, *arg1, Reg[arg2].i);
 87   break;
 88 }
 89 
 90 //:: and
 91 
 92 :(before "End Initialize Op Names(name)")
 93 put(name, "21", "rm32 = bitwise AND of r32 with rm32");
 94 
 95 :(scenario and_r32_with_r32)
 96 % Reg[EAX].i = 0x0a0b0c0d;
 97 % Reg[EBX].i = 0x000000ff;
 98 == 0x1
 99 # op  ModR/M  SIB   displacement  immediate
100   21  d8                                      # and EBX with destination EAX
101 # ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX)
102 +run: and EBX with r/m32
103 +run: r/m32 is EAX
104 +run: storing 0x0000000d
105 
106 :(before "End Single-Byte Opcodes")
107 case 0x21: {  // and r32 with r/m32
108   uint8_t modrm = next();
109   uint8_t arg2 = (modrm>>3)&0x7;
110   trace(90, "run") << "and " << rname(arg2) << " with r/m32" << end();
111   int32_t* arg1 = effective_address(modrm);
112   BINARY_BITWISE_OP(&, *arg1, Reg[arg2].u);
113   break;
114 }
115 
116 //:: or
117 
118 :(before "End Initialize Op Names(name)")
119 put(name, "09", "rm32 = bitwise OR of r32 with rm32");
120 
121 :(scenario or_r32_with_r32)
122 % Reg[EAX].i = 0x0a0b0c0d;
123 % Reg[EBX].i = 0xa0b0c0d0;
124 == 0x1
125 # op  ModR/M  SIB   displacement  immediate
126   09  d8                                      # or EBX with destination EAX
127 # ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX)
128 +run: or EBX with r/m32
129 +run: r/m32 is EAX
130 +run: storing 0xaabbccdd
131 
132 :(before "End Single-Byte Opcodes")
133 case 0x09: {  // or r32 with r/m32
134   uint8_t modrm = next();
135   uint8_t arg2 = (modrm>>3)&0x7;
136   trace(90, "run") << "or " << rname(arg2) << " with r/m32" << end();
137   int32_t* arg1 = effective_address(modrm);
138   BINARY_BITWISE_OP(|, *arg1, Reg[arg2].u);
139   break;
140 }
141 
142 //:: xor
143 
144 :(before "End Initialize Op Names(name)")
145 put(name, "31", "rm32 = bitwise XOR of r32 with rm32");
146 
147 :(scenario xor_r32_with_r32)
148 % Reg[EAX].i = 0x0a0b0c0d;
149 % Reg[EBX].i = 0xaabbc0d0;
150 == 0x1
151 # op  ModR/M  SIB   displacement  immediate
152   31  d8                                      # xor EBX with destination EAX
153 # ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX)
154 +run: xor EBX with r/m32
155 +run: r/m32 is EAX
156 +run: storing 0xa0b0ccdd
157 
158 :(before "End Single-Byte Opcodes")
159 case 0x31: {  // xor r32 with r/m32
160   uint8_t modrm = next();
161   uint8_t arg2 = (modrm>>3)&0x7;
162   trace(90, "run") << "xor " << rname(arg2) << " with r/m32" << end();
163   int32_t* arg1 = effective_address(modrm);
164   BINARY_BITWISE_OP(^, *arg1, Reg[arg2].u);
165   break;
166 }
167 
168 //:: not
169 
170 :(before "End Initialize Op Names(name)")
171 put(name, "f7", "bitwise complement of rm32");
172 
173 :(scenario not_r32)
174 % Reg[EBX].i = 0x0f0f00ff;
175 == 0x1
176 # op  ModR/M  SIB   displacement  immediate
177   f7  c3                                      # not EBX
178 # ModR/M in binary: 11 (direct mode) 000 (unused) 011 (dest EBX)
179 +run: 'not' of r/m32
180 +run: r/m32 is EBX
181 +run: storing 0xf0f0ff00
182 
183 :(before "End Single-Byte Opcodes")
184 case 0xf7: {  // xor r32 with r/m32
185   uint8_t modrm = next();
186   trace(90, "run") << "'not' of r/m32" << end();
187   int32_t* arg1 = effective_address(modrm);
188   *arg1 = ~(*arg1);
189   trace(90, "run") << "storing 0x" << HEXWORD << *arg1 << end();
190   SF = (*arg1 >> 31);
191   ZF = (*arg1 == 0);
192   OF = false;
193   break;
194 }
195 
196 //:: compare (cmp)
197 
198 :(before "End Initialize Op Names(name)")
199 put(name, "39", "set SF if rm32 < r32");
200 
201 :(scenario compare_r32_with_r32_greater)
202 % Reg[EAX].i = 0x0a0b0c0d;
203 % Reg[EBX].i = 0x0a0b0c07;
204 == 0x1
205 # op  ModR/M  SIB   displacement  immediate
206   39  d8                                      # compare EBX with EAX
207 # ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX)
208 +run: compare EBX with r/m32
209 +run: r/m32 is EAX
210 +run: SF=0; ZF=0; OF=0
211 
212 :(before "End Single-Byte Opcodes")
213 case 0x39: {  // set SF if r/m32 < r32
214   uint8_t modrm = next();
215   uint8_t reg2 = (modrm>>3)&0x7;
216   trace(90, "run") << "compare " << rname(reg2) << " with r/m32" << end();
217   int32_t* arg1 = effective_address(modrm);
218   int32_t arg2 = Reg[reg2].i;
219   int32_t tmp1 = *arg1 - arg2;
220   SF = (tmp1 < 0);
221   ZF = (tmp1 == 0);
222   int64_t tmp2 = *arg1 - arg2;
223   OF = (tmp1 != tmp2);
224   trace(90, "run") << "SF=" << SF << "; ZF=" << ZF << "; OF=" << OF << end();
225   break;
226 }
227 
228 :(scenario compare_r32_with_r32_lesser)
229 % Reg[EAX].i = 0x0a0b0c07;
230 % Reg[EBX].i = 0x0a0b0c0d;
231 == 0x1
232 # op  ModR/M  SIB   displacement  immediate
233   39  d8                                      # compare EBX with EAX
234 # ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX)
235 +run: compare EBX with r/m32
236 +run: r/m32 is EAX
237 +run: SF=1; ZF=0; OF=0
238 
239 :(scenario compare_r32_with_r32_equal)
240 % Reg[EAX].i = 0x0a0b0c0d;
241 % Reg[EBX].i = 0x0a0b0c0d;
242 == 0x1
243 # op  ModR/M  SIB   displacement  immediate
244   39  d8                                      # compare EBX with EAX
245 # ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX)
246 +run: compare EBX with r/m32
247 +run: r/m32 is EAX
248 +run: SF=0; ZF=1; OF=0
249 
250 //:: copy (mov)
251 
252 :(before "End Initialize Op Names(name)")
253 put(name, "89", "copy r32 to rm32");
254 
255 :(scenario copy_r32_to_r32)
256 % Reg[EBX].i = 0xaf;
257 == 0x1
258 # op  ModR/M  SIB   displacement  immediate
259   89  d8                                      # copy EBX to EAX
260 # ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX)
261 +run: copy EBX to r/m32
262 +run: r/m32 is EAX
263 +run: storing 0x000000af
264 
265 :(before "End Single-Byte Opcodes")
266 case 0x89: {  // copy r32 to r/m32
267   uint8_t modrm = next();
268   uint8_t reg2 = (modrm>>3)&0x7;
269   trace(90, "run") << "copy " << rname(reg2) << " to r/m32" << end();
270   int32_t* arg1 = effective_address(modrm);
271   *arg1 = Reg[reg2].i;
272   trace(90, "run") << "storing 0x" << HEXWORD << *arg1 << end();
273   break;
274 }
275 
276 //:: xchg
277 
278 :(before "End Initialize Op Names(name)")
279 put(name, "87", "swap the contents of r32 and rm32");
280 
281 :(scenario xchg_r32_with_r32)
282 % Reg[EBX].i = 0xaf;
283 % Reg[EAX].i = 0x2e;
284 == 0x1
285 # op  ModR/M  SIB   displacement  immediate
286   87  d8                                      # exchange EBX with EAX
287 # ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX)
288 +run: exchange EBX with r/m32
289 +run: r/m32 is EAX
290 +run: storing 0x000000af in r/m32
291 +run: storing 0x0000002e in EBX
292 
293 :(before "End Single-Byte Opcodes")
294 case 0x87: {  // exchange r32 with r/m32
295   uint8_t modrm = next();
296   uint8_t reg2 = (modrm>>3)&0x7;
297   trace(90, "run") << "exchange " << rname(reg2) << " with r/m32" << end();
298   int32_t* arg1 = effective_address(modrm);
299   int32_t tmp = *arg1;
300   *arg1 = Reg[reg2].i;
301   Reg[reg2].i = tmp;
302   trace(90, "run") << "storing 0x" << HEXWORD << *arg1 << " in r/m32" << end();
303   trace(90, "run") << "storing 0x" << HEXWORD << Reg[reg2].i << " in " << rname(reg2) << end();
304   break;
305 }
306 
307 //:: push
308 
309 :(before "End Initialize Op Names(name)")
310 put(name, "50", "push R0 (EAX) to stack");
311 put(name, "51", "push R1 (ECX) to stack");
312 put(name, "52", "push R2 (EDX) to stack");
313 put(name, "53", "push R3 (EBX) to stack");
314 put(name, "54", "push R4 (ESP) to stack");
315 put(name, "55", "push R5 (EBP) to stack");
316 put(name, "56", "push R6 (ESI) to stack");
317 put(name, "57", "push R7 (EDI) to stack");
318 
319 :(scenario push_r32)
320 % Reg[ESP].u = 0x64;
321 % Reg[EBX].i = 0x0000000a;
322 == 0x1
323 # op  ModR/M  SIB   displacement  immediate
324   53                                          # push EBX to stack
325 +run: push EBX
326 +run: decrementing ESP to 0x00000060
327 +run: pushing value 0x0000000a
328 
329 :(before "End Single-Byte Opcodes")
330 case 0x50:
331 case 0x51:
332 case 0x52:
333 case 0x53:
334 case 0x54:
335 case 0x55:
336 case 0x56:
337 case 0x57: {  // push r32 to stack
338   uint8_t reg = op & 0x7;
339   trace(90, "run") << "push " << rname(reg) << end();
340   push(Reg[reg].u);
341   break;
342 }
343 :(code)
344 void push(uint32_t val) {
345   Reg[ESP].u -= 4;
346   trace(90, "run") << "decrementing ESP to 0x" << HEXWORD << Reg[ESP].u << end();
347   trace(90, "run") << "pushing value 0x" << HEXWORD << val << end();
348   write_mem_u32(Reg[ESP].u, val);
349 }
350 
351 //:: pop
352 
353 :(before "End Initialize Op Names(name)")
354 put(name, "58", "pop top of stack to R0 (EAX)");
355 put(name, "59", "pop top of stack to R1 (ECX)");
356 put(name, "5a", "pop top of stack to R2 (EDX)");
357 put(name, "5b", "pop top of stack to R3 (EBX)");
358 put(name, "5c", "pop top of stack to R4 (ESP)");
359 put(name, "5d", "pop top of stack to R5 (EBP)");
360 put(name, "5e", "pop top of stack to R6 (ESI)");
361 put(name, "5f", "pop top of stack to R7 (EDI)");
362 
363 :(scenario pop_r32)
364 % Reg[ESP].u = 0x60;
365 % write_mem_i32(0x60, 0x0000000a);
366 == 0x1  # code segment
367 # op  ModR/M  SIB   displacement  immediate
368   5b                                          # pop stack to EBX
369 == 0x60  # data segment
370 0a 00 00 00  # 0x0a
371 +run: pop into EBX
372 +run: popping value 0x0000000a
373 +run: incrementing ESP to 0x00000064
374 
375 :(before "End Single-Byte Opcodes")
376 case 0x58:
377 case 0x59:
378 case 0x5a:
379 case 0x5b:
380 case 0x5c:
381 case 0x5d:
382 case 0x5e:
383 case 0x5f: {  // pop stack into r32
384   uint8_t reg = op & 0x7;
385   trace(90, "run") << "pop into " << rname(reg) << end();
386   Reg[reg].u = pop();
387   break;
388 }
389 :(code)
390 uint32_t pop() {
391   uint32_t result = read_mem_u32(Reg[ESP].u);
392   trace(90, "run") << "popping value 0x" << HEXWORD << result << end();
393   Reg[ESP].u += 4;
394   trace(90, "run") << "incrementing ESP to 0x" << HEXWORD << Reg[ESP].u << end();
395   return result;
396 }