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 //:: multiply
 91 
 92 :(before "End Initialize Op Names(name)")
 93 put(name, "f7", "test/negate/mul/div rm32 (with EAX if necessary) depending on subop");
 94 
 95 :(scenario multiply_eax_by_r32)
 96 % Reg[EAX].i = 4;
 97 % Reg[ECX].i = 3;
 98 == 0x1
 99 # op      ModR/M  SIB   displacement  immediate
100   f7      e1                                      # multiply EAX by ECX
101 # ModR/M in binary: 11 (direct mode) 100 (subop mul) 001 (src ECX)
102 +run: operate on r/m32
103 +run: r/m32 is ECX
104 +run: subop: multiply EAX by r/m32
105 +run: storing 0x0000000c
106 
107 :(before "End Single-Byte Opcodes")
108 case 0xf7: {  // xor r32 with r/m32
109   uint8_t modrm = next();
110   trace(90, "run") << "operate on r/m32" << end();
111   int32_t* arg1 = effective_address(modrm);
112   uint8_t subop = (modrm>>3)&0x7;  // middle 3 'reg opcode' bits
113   switch (subop) {
114   case 4: {  // mul unsigned EAX by r/m32
115     trace(90, "run") << "subop: multiply EAX by r/m32" << end();
116     uint64_t result = Reg[EAX].u * static_cast<uint32_t>(*arg1);
117     Reg[EAX].u = result & 0xffffffff;
118     Reg[EDX].u = result >> 32;
119     OF = (Reg[EDX].u != 0);
120     trace(90, "run") << "storing 0x" << HEXWORD << Reg[EAX].u << end();
121     break;
122   }
123   // End Op f7 Subops
124   default:
125     cerr << "unrecognized sub-opcode after f7: " << NUM(subop) << '\n';
126     exit(1);
127   }
128   break;
129 }
130 
131 //:
132 
133 :(before "End Initialize Op Names(name)")
134 put(name_0f, "af", "multiply rm32 into r32");
135 
136 :(scenario multiply_r32_into_r32)
137 % Reg[EAX].i = 4;
138 % Reg[EBX].i = 2;
139 == 0x1
140 # op      ModR/M  SIB   displacement  immediate
141   0f af   d8                                      # subtract EBX into EAX
142 # ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX)
143 +run: multiply r/m32 into EBX
144 +run: r/m32 is EAX
145 +run: storing 0x00000008
146 
147 :(before "End Two-Byte Opcodes Starting With 0f")
148 case 0xaf: {  // multiply r32 into r/m32
149   uint8_t modrm = next();
150   uint8_t arg2 = (modrm>>3)&0x7;
151   trace(90, "run") << "multiply r/m32 into " << rname(arg2) << end();
152   int32_t* arg1 = effective_address(modrm);
153   BINARY_ARITHMETIC_OP(*, Reg[arg2].i, *arg1);
154   break;
155 }
156 
157 //:: and
158 
159 :(before "End Initialize Op Names(name)")
160 put(name, "21", "rm32 = bitwise AND of r32 with rm32");
161 
162 :(scenario and_r32_with_r32)
163 % Reg[EAX].i = 0x0a0b0c0d;
164 % Reg[EBX].i = 0x000000ff;
165 == 0x1
166 # op  ModR/M  SIB   displacement  immediate
167   21  d8                                      # and EBX with destination EAX
168 # ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX)
169 +run: and EBX with r/m32
170 +run: r/m32 is EAX
171 +run: storing 0x0000000d
172 
173 :(before "End Single-Byte Opcodes")
174 case 0x21: {  // and r32 with r/m32
175   uint8_t modrm = next();
176   uint8_t arg2 = (modrm>>3)&0x7;
177   trace(90, "run") << "and " << rname(arg2) << " with r/m32" << end();
178   int32_t* arg1 = effective_address(modrm);
179   BINARY_BITWISE_OP(&, *arg1, Reg[arg2].u);
180   break;
181 }
182 
183 //:: or
184 
185 :(before "End Initialize Op Names(name)")
186 put(name, "09", "rm32 = bitwise OR of r32 with rm32");
187 
188 :(scenario or_r32_with_r32)
189 % Reg[EAX].i = 0x0a0b0c0d;
190 % Reg[EBX].i = 0xa0b0c0d0;
191 == 0x1
192 # op  ModR/M  SIB   displacement  immediate
193   09  d8                                      # or EBX with destination EAX
194 # ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX)
195 +run: or EBX with r/m32
196 +run: r/m32 is EAX
197 +run: storing 0xaabbccdd
198 
199 :(before "End Single-Byte Opcodes")
200 case 0x09: {  // or r32 with r/m32
201   uint8_t modrm = next();
202   uint8_t arg2 = (modrm>>3)&0x7;
203   trace(90, "run") << "or " << rname(arg2) << " with r/m32" << end();
204   int32_t* arg1 = effective_address(modrm);
205   BINARY_BITWISE_OP(|, *arg1, Reg[arg2].u);
206   break;
207 }
208 
209 //:: xor
210 
211 :(before "End Initialize Op Names(name)")
212 put(name, "31", "rm32 = bitwise XOR of r32 with rm32");
213 
214 :(scenario xor_r32_with_r32)
215 % Reg[EAX].i = 0x0a0b0c0d;
216 % Reg[EBX].i = 0xaabbc0d0;
217 == 0x1
218 # op  ModR/M  SIB   displacement  immediate
219   31  d8                                      # xor EBX with destination EAX
220 # ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX)
221 +run: xor EBX with r/m32
222 +run: r/m32 is EAX
223 +run: storing 0xa0b0ccdd
224 
225 :(before "End Single-Byte Opcodes")
226 case 0x31: {  // xor r32 with r/m32
227   uint8_t modrm = next();
228   uint8_t arg2 = (modrm>>3)&0x7;
229   trace(90, "run") << "xor " << rname(arg2) << " with r/m32" << end();
230   int32_t* arg1 = effective_address(modrm);
231   BINARY_BITWISE_OP(^, *arg1, Reg[arg2].u);
232   break;
233 }
234 
235 //:: not
236 
237 :(before "End Initialize Op Names(name)")
238 put(name, "f7", "bitwise complement of rm32");
239 
240 :(scenario not_r32)
241 % Reg[EBX].i = 0x0f0f00ff;
242 == 0x1
243 # op  ModR/M  SIB   displacement  immediate
244   f7  d3                                      # not EBX
245 # ModR/M in binary: 11 (direct mode) 010 (subop not) 011 (dest EBX)
246 +run: operate on r/m32
247 +run: r/m32 is EBX
248 +run: subop: not
249 +run: storing 0xf0f0ff00
250 
251 :(before "End Op f7 Subops")
252 case 2: {  // not r/m32
253   trace(90, "run") << "subop: not" << end();
254   *arg1 = ~(*arg1);
255   trace(90, "run") << "storing 0x" << HEXWORD << *arg1 << end();
256   SF = (*arg1 >> 31);
257   ZF = (*arg1 == 0);
258   OF = false;
259   break;
260 }
261 
262 //:: compare (cmp)
263 
264 :(before "End Initialize Op Names(name)")
265 put(name, "39", "set SF if rm32 < r32");
266 
267 :(scenario compare_r32_with_r32_greater)
268 % Reg[EAX].i = 0x0a0b0c0d;
269 % Reg[EBX].i = 0x0a0b0c07;
270 == 0x1
271 # op  ModR/M  SIB   displacement  immediate
272   39  d8                                      # compare EBX with EAX
273 # ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX)
274 +run: compare EBX with r/m32
275 +run: r/m32 is EAX
276 +run: SF=0; ZF=0; OF=0
277 
278 :(before "End Single-Byte Opcodes")
279 case 0x39: {  // set SF if r/m32 < r32
280   uint8_t modrm = next();
281   uint8_t reg2 = (modrm>>3)&0x7;
282   trace(90, "run") << "compare " << rname(reg2) << " with r/m32" << end();
283   int32_t* arg1 = effective_address(modrm);
284   int32_t arg2 = Reg[reg2].i;
285   int32_t tmp1 = *arg1 - arg2;
286   SF = (tmp1 < 0);
287   ZF = (tmp1 == 0);
288   int64_t tmp2 = *arg1 - arg2;
289   OF = (tmp1 != tmp2);
290   trace(90, "run") << "SF=" << SF << "; ZF=" << ZF << "; OF=" << OF << end();
291   break;
292 }
293 
294 :(scenario compare_r32_with_r32_lesser)
295 % Reg[EAX].i = 0x0a0b0c07;
296 % Reg[EBX].i = 0x0a0b0c0d;
297 == 0x1
298 # op  ModR/M  SIB   displacement  immediate
299   39  d8                                      # compare EBX with EAX
300 # ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX)
301 +run: compare EBX with r/m32
302 +run: r/m32 is EAX
303 +run: SF=1; ZF=0; OF=0
304 
305 :(scenario compare_r32_with_r32_equal)
306 % Reg[EAX].i = 0x0a0b0c0d;
307 % Reg[EBX].i = 0x0a0b0c0d;
308 == 0x1
309 # op  ModR/M  SIB   displacement  immediate
310   39  d8                                      # compare EBX with EAX
311 # ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX)
312 +run: compare EBX with r/m32
313 +run: r/m32 is EAX
314 +run: SF=0; ZF=1; OF=0
315 
316 //:: copy (mov)
317 
318 :(before "End Initialize Op Names(name)")
319 put(name, "89", "copy r32 to rm32");
320 
321 :(scenario copy_r32_to_r32)
322 % Reg[EBX].i = 0xaf;
323 == 0x1
324 # op  ModR/M  SIB   displacement  immediate
325   89  d8                                      # copy EBX to EAX
326 # ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX)
327 +run: copy EBX to r/m32
328 +run: r/m32 is EAX
329 +run: storing 0x000000af
330 
331 :(before "End Single-Byte Opcodes")
332 case 0x89: {  // copy r32 to r/m32
333   uint8_t modrm = next();
334   uint8_t reg2 = (modrm>>3)&0x7;
335   trace(90, "run") << "copy " << rname(reg2) << " to r/m32" << end();
336   int32_t* arg1 = effective_address(modrm);
337   *arg1 = Reg[reg2].i;
338   trace(90, "run") << "storing 0x" << HEXWORD << *arg1 << end();
339   break;
340 }
341 
342 //:: xchg
343 
344 :(before "End Initialize Op Names(name)")
345 put(name, "87", "swap the contents of r32 and rm32");
346 
347 :(scenario xchg_r32_with_r32)
348 % Reg[EBX].i = 0xaf;
349 % Reg[EAX].i = 0x2e;
350 == 0x1
351 # op  ModR/M  SIB   displacement  immediate
352   87  d8                                      # exchange EBX with EAX
353 # ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX)
354 +run: exchange EBX with r/m32
355 +run: r/m32 is EAX
356 +run: storing 0x000000af in r/m32
357 +run: storing 0x0000002e in EBX
358 
359 :(before "End Single-Byte Opcodes")
360 case 0x87: {  // exchange r32 with r/m32
361   uint8_t modrm = next();
362   uint8_t reg2 = (modrm>>3)&0x7;
363   trace(90, "run") << "exchange " << rname(reg2) << " with r/m32" << end();
364   int32_t* arg1 = effective_address(modrm);
365   int32_t tmp = *arg1;
366   *arg1 = Reg[reg2].i;
367   Reg[reg2].i = tmp;
368   trace(90, "run") << "storing 0x" << HEXWORD << *arg1 << " in r/m32" << end();
369   trace(90, "run") << "storing 0x" << HEXWORD << Reg[reg2].i << " in " << rname(reg2) << end();
370   break;
371 }
372 
373 //:: push
374 
375 :(before "End Initialize Op Names(name)")
376 put(name, "50", "push R0 (EAX) to stack");
377 put(name, "51", "push R1 (ECX) to stack");
378 put(name, "52", "push R2 (EDX) to stack");
379 put(name, "53", "push R3 (EBX) to stack");
380 put(name, "54", "push R4 (ESP) to stack");
381 put(name, "55", "push R5 (EBP) to stack");
382 put(name, "56", "push R6 (ESI) to stack");
383 put(name, "57", "push R7 (EDI) to stack");
384 
385 :(scenario push_r32)
386 % Reg[ESP].u = 0x64;
387 % Reg[EBX].i = 0x0000000a;
388 == 0x1
389 # op  ModR/M  SIB   displacement  immediate
390   53                                          # push EBX to stack
391 +run: push EBX
392 +run: decrementing ESP to 0x00000060
393 +run: pushing value 0x0000000a
394 
395 :(before "End Single-Byte Opcodes")
396 case 0x50:
397 case 0x51:
398 case 0x52:
399 case 0x53:
400 case 0x54:
401 case 0x55:
402 case 0x56:
403 case 0x57: {  // push r32 to stack
404   uint8_t reg = op & 0x7;
405   trace(90, "run") << "push " << rname(reg) << end();
406 //?   cerr << "push: " << NUM(reg) << ": " << Reg[reg].u << " => " << Reg[ESP].u << '\n';
407   push(Reg[reg].u);
408   break;
409 }
410 
411 //:: pop
412 
413 :(before "End Initialize Op Names(name)")
414 put(name, "58", "pop top of stack to R0 (EAX)");
415 put(name, "59", "pop top of stack to R1 (ECX)");
416 put(name, "5a", "pop top of stack to R2 (EDX)");
417 put(name, "5b", "pop top of stack to R3 (EBX)");
418 put(name, "5c", "pop top of stack to R4 (ESP)");
419 put(name, "5d", "pop top of stack to R5 (EBP)");
420 put(name, "5e", "pop top of stack to R6 (ESI)");
421 put(name, "5f", "pop top of stack to R7 (EDI)");
422 
423 :(scenario pop_r32)
424 % Reg[ESP].u = 0x60;
425 % write_mem_i32(0x60, 0x0000000a);
426 == 0x1  # code segment
427 # op  ModR/M  SIB   displacement  immediate
428   5b                                          # pop stack to EBX
429 == 0x60  # data segment
430 0a 00 00 00  # 0x0a
431 +run: pop into EBX
432 +run: popping value 0x0000000a
433 +run: incrementing ESP to 0x00000064
434 
435 :(before "End Single-Byte Opcodes")
436 case 0x58:
437 case 0x59:
438 case 0x5a:
439 case 0x5b:
440 case 0x5c:
441 case 0x5d:
442 case 0x5e:
443 case 0x5f: {  // pop stack into r32
444   uint8_t reg = op & 0x7;
445   trace(90, "run") << "pop into " << rname(reg) << end();
446 //?   cerr << "pop from " << Reg[ESP].u << '\n';
447   Reg[reg].u = pop();
448 //?   cerr << "=> " << NUM(reg) << ": " << Reg[reg].u << '\n';
449   break;
450 }
451 :(code)
452 uint32_t pop() {
453   uint32_t result = read_mem_u32(Reg[ESP].u);
454   trace(90, "run") << "popping value 0x" << HEXWORD << result << end();
455   Reg[ESP].u += 4;
456   trace(90, "run") << "incrementing ESP to 0x" << HEXWORD << Reg[ESP].u << end();
457   return result;
458 }