diff options
author | Kartik Agaram <vc@akkartik.com> | 2018-07-30 11:23:23 -0700 |
---|---|---|
committer | Kartik Agaram <vc@akkartik.com> | 2018-07-30 11:23:23 -0700 |
commit | 62c6d1638a2682bbc291987e2d6cb87b5c5d8da1 (patch) | |
tree | 2e23d80fac375253199082854d69270e07c0d008 /subx | |
parent | b4c78a5939443d4fe08510b75ea51fbad94c2211 (diff) | |
download | mu-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.
Diffstat (limited to 'subx')
-rw-r--r-- | subx/010vm.cc | 9 | ||||
-rw-r--r-- | subx/012direct_addressing.cc | 5 | ||||
-rw-r--r-- | subx/014immediate_addressing.cc | 1 | ||||
-rw-r--r-- | subx/018functions.cc | 1 | ||||
-rw-r--r-- | subx/ex7 | bin | 0 -> 156 bytes | |||
-rw-r--r-- | subx/ex7.subx | 90 |
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 |