about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorKartik Agaram <vc@akkartik.com>2018-07-30 11:23:23 -0700
committerKartik Agaram <vc@akkartik.com>2018-07-30 11:23:23 -0700
commit62c6d1638a2682bbc291987e2d6cb87b5c5d8da1 (patch)
tree2e23d80fac375253199082854d69270e07c0d008
parentb4c78a5939443d4fe08510b75ea51fbad94c2211 (diff)
downloadmu-62c6d1638a2682bbc291987e2d6cb87b5c5d8da1.tar.gz
4456 - example with function calls: factorial
I found a bug with multiply in the process; another case (after the swap
of call/jump opcodes in 4453) where there's no point improving testing.

Unit tests can only tell us if our instructions are internally
consistent. Validating against a real machine has to be manual.

I'm definitely feeling the pain of debugging machine code now, that's
for sure. Going over an instruction trace, comparing the state of
registers line by line.
-rw-r--r--subx/010vm.cc9
-rw-r--r--subx/012direct_addressing.cc5
-rw-r--r--subx/014immediate_addressing.cc1
-rw-r--r--subx/018functions.cc1
-rw-r--r--subx/ex7bin0 -> 156 bytes
-rw-r--r--subx/ex7.subx90
6 files changed, 105 insertions, 1 deletions
diff --git a/subx/010vm.cc b/subx/010vm.cc
index e89e2566..0a549982 100644
--- a/subx/010vm.cc
+++ b/subx/010vm.cc
@@ -147,6 +147,7 @@ inline void write_mem_i32(uint32_t addr, int32_t val) {
 void run_one_instruction() {
   uint8_t op=0, op2=0, op3=0;
   trace(90, "run") << "inst: 0x" << HEXWORD << EIP << end();
+//?   dump_registers();
 //?   cerr << "inst: 0x" << EIP << '\n';
   switch (op = next()) {
   case 0xf4:  // hlt
@@ -191,6 +192,14 @@ inline uint8_t next() {
   return read_mem_u8(EIP++);
 }
 
+void dump_registers() {
+  for (int i = 0;  i < NUM_INT_REGISTERS;  ++i) {
+    if (i > 0) cerr << "; ";
+    cerr << "  " << i << ": " << HEXWORD << Reg[i].u;
+  }
+  cerr << '\n';
+}
+
 //: start tracking supported opcodes
 :(before "End Globals")
 map</*op*/string, string> name;
diff --git a/subx/012direct_addressing.cc b/subx/012direct_addressing.cc
index aefe3214..e4f7f8a9 100644
--- a/subx/012direct_addressing.cc
+++ b/subx/012direct_addressing.cc
@@ -109,7 +109,7 @@ case 0xaf: {  // multiply r32 into r/m32
   uint8_t arg2 = (modrm>>3)&0x7;
   trace(90, "run") << "multiply " << rname(arg2) << " into r/m32" << end();
   int32_t* arg1 = effective_address(modrm);
-  BINARY_ARITHMETIC_OP(*, *arg1, Reg[arg2].i);
+  BINARY_ARITHMETIC_OP(*, Reg[arg2].i, *arg1);
   break;
 }
 
@@ -363,6 +363,7 @@ case 0x56:
 case 0x57: {  // push r32 to stack
   uint8_t reg = op & 0x7;
   trace(90, "run") << "push " << rname(reg) << end();
+//?   cerr << "push: " << NUM(reg) << ": " << Reg[reg].u << " => " << Reg[ESP].u << '\n';
   push(Reg[reg].u);
   break;
 }
@@ -409,7 +410,9 @@ case 0x5e:
 case 0x5f: {  // pop stack into r32
   uint8_t reg = op & 0x7;
   trace(90, "run") << "pop into " << rname(reg) << end();
+//?   cerr << "pop from " << Reg[ESP].u << '\n';
   Reg[reg].u = pop();
+//?   cerr << "=> " << NUM(reg) << ": " << Reg[reg].u << '\n';
   break;
 }
 :(code)
diff --git a/subx/014immediate_addressing.cc b/subx/014immediate_addressing.cc
index 8ef36f95..c75c419e 100644
--- a/subx/014immediate_addressing.cc
+++ b/subx/014immediate_addressing.cc
@@ -469,6 +469,7 @@ put(name, "68", "push imm32 to stack");
 case 0x68: {
   uint32_t val = static_cast<uint32_t>(imm32());
   trace(90, "run") << "push imm32 0x" << HEXWORD << val << end();
+//?   cerr << "push: " << val << " => " << Reg[ESP].u << '\n';
   push(val);
   trace(90, "run") << "ESP is now 0x" << HEXWORD << Reg[ESP].u << end();
   trace(90, "run") << "contents at ESP: 0x" << HEXWORD << read_mem_u32(Reg[ESP].u) << end();
diff --git a/subx/018functions.cc b/subx/018functions.cc
index a557abf5..964ca977 100644
--- a/subx/018functions.cc
+++ b/subx/018functions.cc
@@ -18,6 +18,7 @@ put(name, "e8", "call disp32");
 case 0xe8: {  // call disp32 relative to next EIP
   int32_t offset = imm32();
   trace(90, "run") << "call imm32 0x" << HEXWORD << offset << end();
+//?   cerr << "push: EIP: " << EIP << " => " << Reg[ESP].u << '\n';
   push(EIP);
   EIP += offset;
   trace(90, "run") << "jumping to 0x" << HEXWORD << EIP << end();
diff --git a/subx/ex7 b/subx/ex7
new file mode 100644
index 00000000..daea2cf4
--- /dev/null
+++ b/subx/ex7
Binary files differdiff --git a/subx/ex7.subx b/subx/ex7.subx
new file mode 100644
index 00000000..c95ed361
--- /dev/null
+++ b/subx/ex7.subx
@@ -0,0 +1,90 @@
+## compute the factorial of 5, and return the result in the exit code
+#
+# To run:
+#   $ subx translate ex7.subx ex7
+#   $ subx run ex7
+# Expected result:
+#   $ echo $?
+#   120
+
+== 0x08048054  # code segment, after leaving room for ELF header
+# instruction                     effective address                                           operand     displacement    immediate
+# op          subop               mod             rm32          base      index     scale     r32
+# 1-3 bytes   3 bits              2 bits          3 bits        3 bits    3 bits    2 bits    2 bits      0/1/2/4 bytes   0/1/2/4 bytes
+
+# main:
+  # prepare to make a call
+# 54
+  55/push                                                                                                                                   # push EBP
+# 55
+  89/copy                         3/mod/direct    5/rm32/EBP                                  4/r32/ESP                                     # copy ESP to EBP
+# 57
+  # factorial(5)
+  68/push                                                                                                                 5/imm32           # push 5
+# 5c
+  e8/call                                                                                                 factorial/disp32
+# 61
+  # discard arg
+  5a/pop                                                                                                                                    # pop into EDX
+# 62
+  # clean up after call
+  89/copy                         3/mod/direct    4/rm32/ESP                                  5/r32/EBP                                     # copy EBP to ESP
+# 64
+  5d/pop                                                                                                                                    # pop to EBP
+
+  # exit(EAX)
+# 65
+  89/copy                         3/mod/direct    3/rm32/EBX                                  0/r32/EAX                                     # copy EAX to EBX
+# 67
+  b8/copy                                                                                                                 1/imm32           # copy 1 to EAX
+# 6c
+  cd/syscall                                                                                                              0x80/imm8         # int 80h
+
+# factorial(n)
+# 6e
+factorial:
+  # initialize n
+  8b/copy                         1/mod/*+disp8   4/rm32/sib    4/base/esp   4/index/none     2/r32/edx   4/disp8                           # copy *(ESP+4) to EDX
+# 72
+  # initialize EAX to 1 (base case)
+  b8/copy                                                                                                                 1/imm32           # copy 1 to EAX
+# 77
+  # if (n <= 1) jump exit
+  81          7/subop/compare     3/mod/direct    2/rm32/EDX                                                              1/imm32           # compare EDX with 1
+# 7d
+  7e/jump-if                                                                                              factorial:exit/disp8              # jump if <= to exit
+# 7f
+  # EBX: n-1
+  89/copy                         3/mod/direct    3/rm32/EBX                                  2/r32/EDX                                     # copy EDX to EBX
+# 81
+  81          5/subop/subtract    3/mod/direct    3/rm32/EBX                                                              1/imm32           # subtract 1 from EBX
+# 87
+  # prepare call
+  55/push                                                                                                                                   # push EBP
+# 88
+  89/copy                         3/mod/direct    5/rm32/EBP                                  4/r32/ESP                                     # copy ESP to EBP
+  # EAX: factorial(n-1)
+# 8a
+  53/push                                                                                                                                   # push EBX
+# 8b
+  e8/call                                                                                                 factorial/disp32
+# 90
+  # discard arg
+  5e/pop                                                                                                                                    # pop into ESI
+# 91
+  # clean up after call
+  89/copy                         3/mod/direct    4/rm32/ESP                                  5/r32/EBP                                     # copy EBP to ESP
+# 93
+  5d/pop                                                                                                                                    # pop to EBP
+# 94
+  # refresh n
+  8b/copy                         1/mod/*+disp8   4/rm32/sib    4/base/esp   4/index/none     2/r32/edx   4/disp8                           # copy *(ESP+4) to EDX
+# 98
+  # return n * factorial(n-1)
+  0f af/multiply                  3/mod/direct    2/rm32/EDX                                  0/r32/EAX                                     # multiply EDX (n) into EAX (factorial(n-1))
+  # TODO: check for overflow
+# 9b
+factorial:exit:
+  c3/return
+
+# vim:ft=subx:nowrap:so=0