about summary refs log tree commit diff stats
path: root/subx
diff options
context:
space:
mode:
authorKartik Agaram <vc@akkartik.com>2019-04-21 23:35:23 -0700
committerKartik Agaram <vc@akkartik.com>2019-04-21 23:35:23 -0700
commita78deb23b503783e2384ddc4e73c72578cf15894 (patch)
tree50d13135398ecff82417d6f21bc7d6b5b6ca923a /subx
parent0824f5df851772b4d2804b89a837dd2ebf1c1f0e (diff)
downloadmu-a78deb23b503783e2384ddc4e73c72578cf15894.tar.gz
5113 - x86's integer division (idiv) instruction
Diffstat (limited to 'subx')
-rw-r--r--subx/013direct_addressing.cc94
-rw-r--r--subx/opcodes2
2 files changed, 94 insertions, 2 deletions
diff --git a/subx/013direct_addressing.cc b/subx/013direct_addressing.cc
index 2a984dfe..779c7e0a 100644
--- a/subx/013direct_addressing.cc
+++ b/subx/013direct_addressing.cc
@@ -119,7 +119,7 @@ case 0x29: {  // subtract r32 from r/m32
 //:: multiply
 
 :(before "End Initialize Op Names")
-put_new(Name, "f7", "negate/multiply rm32 (with EAX if necessary) depending on subop (neg/mul)");
+put_new(Name, "f7", "negate/multiply/divide rm32 (with EAX and EDX if necessary) depending on subop (neg/mul/idiv)");
 
 :(code)
 void test_multiply_eax_by_r32() {
@@ -251,6 +251,98 @@ void test_negate_can_overflow() {
   );
 }
 
+//:: divide with remainder
+
+void test_divide_eax_by_rm32() {
+  Reg[EAX].u = 7;
+  Reg[EDX].u = 0;
+  Reg[ECX].i = 3;
+  run(
+      "== 0x1\n"  // code segment
+      // op     ModR/M  SIB   displacement  immediate
+      "  f7     f9                                    \n"  // multiply EAX by ECX
+      // ModR/M in binary: 11 (direct mode) 111 (subop idiv) 001 (divisor ECX)
+  );
+  CHECK_TRACE_CONTENTS(
+      "run: operate on r/m32\n"
+      "run: r/m32 is ECX\n"
+      "run: subop: divide EDX:EAX by r/m32, storing quotient in EAX and remainder in EDX\n"
+      "run: quotient: 0x00000002\n"
+      "run: remainder: 0x00000001\n"
+  );
+}
+
+:(before "End Op f7 Subops")
+case 7: {  // divide EDX:EAX by r/m32, storing quotient in EAX and remainder in EDX
+  trace(Callstack_depth+1, "run") << "subop: divide EDX:EAX by r/m32, storing quotient in EAX and remainder in EDX" << end();
+  int64_t dividend = static_cast<int64_t>((static_cast<uint64_t>(Reg[EDX].u) << 32) | Reg[EAX].u);
+  int32_t divisor = *arg1;
+  assert(divisor != 0);
+  Reg[EAX].i = dividend/divisor;  // quotient
+  Reg[EDX].i = dividend%divisor;  // remainder
+  trace(Callstack_depth+1, "run") << "quotient: 0x" << HEXWORD << Reg[EAX].i << end();
+  trace(Callstack_depth+1, "run") << "remainder: 0x" << HEXWORD << Reg[EDX].i << end();
+  break;
+}
+
+:(code)
+void test_divide_eax_by_negative_rm32() {
+  Reg[EAX].u = 7;
+  Reg[EDX].u = 0;
+  Reg[ECX].i = -3;
+  run(
+      "== 0x1\n"  // code segment
+      // op     ModR/M  SIB   displacement  immediate
+      "  f7     f9                                    \n"  // multiply EAX by ECX
+      // ModR/M in binary: 11 (direct mode) 111 (subop idiv) 001 (divisor ECX)
+  );
+  CHECK_TRACE_CONTENTS(
+      "run: operate on r/m32\n"
+      "run: r/m32 is ECX\n"
+      "run: subop: divide EDX:EAX by r/m32, storing quotient in EAX and remainder in EDX\n"
+      "run: quotient: 0xfffffffe\n"  // -2
+      "run: remainder: 0x00000001\n"
+  );
+}
+
+void test_divide_negative_eax_by_rm32() {
+  Reg[EAX].i = -7;
+  Reg[EDX].i = -1;  // sign extend
+  Reg[ECX].i = 3;
+  run(
+      "== 0x1\n"  // code segment
+      // op     ModR/M  SIB   displacement  immediate
+      "  f7     f9                                    \n"  // multiply EAX by ECX
+      // ModR/M in binary: 11 (direct mode) 111 (subop idiv) 001 (divisor ECX)
+  );
+  CHECK_TRACE_CONTENTS(
+      "run: operate on r/m32\n"
+      "run: r/m32 is ECX\n"
+      "run: subop: divide EDX:EAX by r/m32, storing quotient in EAX and remainder in EDX\n"
+      "run: quotient: 0xfffffffe\n"  // -2
+      "run: remainder: 0xffffffff\n"  // -1, same sign as divident (EDX:EAX)
+  );
+}
+
+void test_divide_negative_edx_eax_by_rm32() {
+  Reg[EAX].i = 0;  // lower 32 bits are clear
+  Reg[EDX].i = -7;
+  Reg[ECX].i = 0x40000000;  // 2^30 (largest positive power of 2)
+  run(
+      "== 0x1\n"  // code segment
+      // op     ModR/M  SIB   displacement  immediate
+      "  f7     f9                                    \n"  // multiply EAX by ECX
+      // ModR/M in binary: 11 (direct mode) 111 (subop idiv) 001 (divisor ECX)
+  );
+  CHECK_TRACE_CONTENTS(
+      "run: operate on r/m32\n"
+      "run: r/m32 is ECX\n"
+      "run: subop: divide EDX:EAX by r/m32, storing quotient in EAX and remainder in EDX\n"
+      "run: quotient: 0xffffffe4\n"  // (-7 << 32) / (1 << 30) = -7 << 2 = -28
+      "run: remainder: 0x00000000\n"
+  );
+}
+
 //:: shift left
 
 :(before "End Initialize Op Names")
diff --git a/subx/opcodes b/subx/opcodes
index 0eb0792b..2a11df2b 100644
--- a/subx/opcodes
+++ b/subx/opcodes
@@ -82,7 +82,7 @@ Opcodes currently supported by SubX:
   e9: jump disp32 bytes away (jmp)
   eb: jump disp8 bytes away (jmp)
   f4: halt (hlt)
-  f7: negate/multiply rm32 (with EAX if necessary) depending on subop (neg/mul)
+  f7: negate/multiply/divide rm32 (with EAX and EDX if necessary) depending on subop (neg/mul/idiv)
   ff: increment/decrement/jump/push/call rm32 based on subop (inc/dec/jmp/push/call)
   0f 84: jump disp32 bytes away if equal, if ZF is set (jcc/jz/je)
   0f 85: jump disp32 bytes away if not equal, if ZF is not set (jcc/jnz/jne)
94'>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
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322