diff options
author | Kartik Agaram <vc@akkartik.com> | 2019-05-12 07:36:18 -0700 |
---|---|---|
committer | Kartik Agaram <vc@akkartik.com> | 2019-05-12 07:41:34 -0700 |
commit | 8c1a69089bcb0be8ee869a3f83c3151a7083e27c (patch) | |
tree | 319e3b04bb5d94b0306e336dff573d6685e8d683 /subx | |
parent | d5d43e044d16335a3a865a8c9f5aea5cbad73360 (diff) | |
download | mu-8c1a69089bcb0be8ee869a3f83c3151a7083e27c.tar.gz |
snapshot of carry flag implementation
Tests failing. This approach seems wrong. I'm not sure even the tests are correct. Also, some open questions: 1. Should setting the overflow flag always set the carry flag? 2. Should the carry flag only be set on add/subtract/compare, or by all arithmetic ops? 3. Had to turn off the -ftrapv flag in `build`. Is there a way to detect overflow without actually causing overflow? Once we start setting CF correctly we have to implement jump above/below instructions (8- and 32-bit displacement variants). https://github.com/akkartik/mu/issues/30
Diffstat (limited to 'subx')
-rw-r--r-- | subx/010---vm.cc | 48 | ||||
-rw-r--r-- | subx/013direct_addressing.cc | 83 | ||||
-rw-r--r-- | subx/014indirect_addressing.cc | 33 | ||||
-rw-r--r-- | subx/015immediate_addressing.cc | 43 | ||||
-rwxr-xr-x | subx/build | 2 |
5 files changed, 147 insertions, 62 deletions
diff --git a/subx/010---vm.cc b/subx/010---vm.cc index 31e5608f..00a2dee4 100644 --- a/subx/010---vm.cc +++ b/subx/010---vm.cc @@ -62,7 +62,10 @@ put_new(Help, "registers", "- the sign flag (SF): usually set if an arithmetic result is negative, or\n" " reset if not.\n" "- the zero flag (ZF): usually set if a result is zero, or reset if not.\n" - "- the overflow flag (OF): usually set if an arithmetic result overflows.\n" + "- the carry flag (CF): usually set if an arithmetic result overflows by just one bit.\n" + " Useful for operating on unsigned numbers.\n" + "- the overflow flag (OF): usually set if an arithmetic result overflows by more than one bit.\n" + " Useful for operating on signed numbers.\n" "The flag bits are read by conditional jumps.\n" "\n" "For complete details on how different instructions update the flags, consult the IA-32\n" @@ -78,9 +81,10 @@ put_new(Help, "registers", // the subset of x86 flag registers we care about bool SF = false; // sign flag bool ZF = false; // zero flag +bool CF = false; // carry flag bool OF = false; // overflow flag :(before "End Reset") -SF = ZF = OF = false; +SF = ZF = CF = OF = false; //: how the flag registers are updated after each instruction @@ -88,25 +92,35 @@ SF = ZF = OF = false; // Combine 'arg1' and 'arg2' with arithmetic operation 'op' and store the // result in 'arg1', then update flags. // beware: no side-effects in args -#define BINARY_ARITHMETIC_OP(op, arg1, arg2) { \ - /* arg1 and arg2 must be signed */ \ - int64_t tmp = arg1 op arg2; \ - arg1 = arg1 op arg2; \ - trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << arg1 << end(); \ - SF = (arg1 < 0); \ - ZF = (arg1 == 0); \ - OF = (arg1 != tmp); \ +#define BINARY_ARITHMETIC_OP(op, signed_arg1, signed_arg2) { \ + cerr << signed_arg1 << " vs " << signed_arg2 << '\n'; \ + int64_t signed_full_result = signed_arg1 op signed_arg2; \ + signed_arg1 = signed_arg1 op signed_arg2; \ + trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << signed_arg1 << end(); \ + SF = (signed_arg1 < 0); \ + ZF = (signed_arg1 == 0); \ + OF = (signed_arg1 != signed_full_result); \ + /* CF is more complex */ \ + uint32_t unsigned_arg1 = static_cast<uint32_t>(signed_arg1); \ + uint32_t unsigned_arg2 = static_cast<uint32_t>(signed_arg2); \ + cerr << unsigned_arg1 << " vs " << unsigned_arg2 << '\n'; \ + uint32_t unsigned_result = unsigned_arg1 op unsigned_arg2; \ + cerr << "result: " << unsigned_result << '\n'; \ + uint64_t unsigned_full_result = unsigned_arg1 op unsigned_arg2; \ + CF = (unsigned_result != unsigned_full_result); \ + trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); \ } // Combine 'arg1' and 'arg2' with bitwise operation 'op' and store the result // in 'arg1', then update flags. -#define BINARY_BITWISE_OP(op, arg1, arg2) { \ - /* arg1 and arg2 must be unsigned */ \ - arg1 = arg1 op arg2; \ - trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << arg1 << end(); \ - SF = (arg1 >> 31); \ - ZF = (arg1 == 0); \ +#define BINARY_BITWISE_OP(op, unsigned_arg1, unsigned_arg2) { \ + unsigned_arg1 = unsigned_arg1 op unsigned_arg2; \ + trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << unsigned_arg1 << end(); \ + SF = (unsigned_arg1 >> 31); \ + ZF = (unsigned_arg1 == 0); \ + CF = false; \ OF = false; \ + trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); \ } //:: simulated RAM @@ -374,7 +388,7 @@ void dump_registers() { if (i > 0) out << "; "; out << " " << i << ": " << std::hex << std::setw(8) << std::setfill('_') << Reg[i].u; } - out << " -- SF: " << SF << "; ZF: " << ZF << "; OF: " << OF; + out << " -- SF: " << SF << "; ZF: " << ZF << "; CF: " << CF << "; OF: " << OF; trace(Callstack_depth+1, "run") << out.str() << end(); } diff --git a/subx/013direct_addressing.cc b/subx/013direct_addressing.cc index 160ce6d6..ef5ac752 100644 --- a/subx/013direct_addressing.cc +++ b/subx/013direct_addressing.cc @@ -31,6 +31,24 @@ case 0x01: { // add r32 to r/m32 } :(code) +void test_add_r32_to_r32_unsigned() { + Reg[EAX].i = 0x7fffffff; // largest positive signed number + Reg[EBX].i = 1; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 01 d8 \n" // add EBX to EAX + // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX) + ); + CHECK_TRACE_CONTENTS( + "run: add EBX to r/m32\n" + "run: r/m32 is EAX\n" + "run: storing 0x80000000\n" + "run: SF=1; ZF=0; CF=1; OF=0\n" // carry flag set + ); +} + +:(code) // Implement tables 2-2 and 2-3 in the Intel manual, Volume 2. // We return a pointer so that instructions can write to multiple bytes in // 'Mem' at once. @@ -116,6 +134,24 @@ case 0x29: { // subtract r32 from r/m32 break; } +:(code) +void test_subtract_r32_from_r32_unsigned() { + Reg[EAX].i = 0x7ffffffd; + Reg[EBX].i = 0x7fffffff; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 29 d8 \n" // subtract EBX from EAX + // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX) + ); + CHECK_TRACE_CONTENTS( + "run: subtract EBX from r/m32\n" + "run: r/m32 is EAX\n" + "run: storing 0xfffffffe\n" + "run: SF=1; ZF=0; CF=1; OF=0\n" + ); +} + //:: multiply :(before "End Initialize Op Names") @@ -686,7 +722,7 @@ void test_compare_r32_with_r32_greater() { CHECK_TRACE_CONTENTS( "run: compare EBX with r/m32\n" "run: r/m32 is EAX\n" - "run: SF=0; ZF=0; OF=0\n" + "run: SF=0; ZF=0; CF=0; OF=0\n" ); } @@ -695,14 +731,23 @@ case 0x39: { // set SF if r/m32 < r32 const uint8_t modrm = next(); const uint8_t reg2 = (modrm>>3)&0x7; trace(Callstack_depth+1, "run") << "compare " << rname(reg2) << " with r/m32" << end(); - const int32_t* arg1 = effective_address(modrm); - const int32_t arg2 = Reg[reg2].i; - const int32_t tmp1 = *arg1 - arg2; - SF = (tmp1 < 0); - ZF = (tmp1 == 0); - const int64_t tmp2 = *arg1 - arg2; - OF = (tmp1 != tmp2); - trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; OF=" << OF << end(); + const int32_t* signed_arg1 = effective_address(modrm); + const int32_t signed_arg2 = Reg[reg2].i; + cerr << *signed_arg1 << " vs " << signed_arg2 << '\n'; + const int32_t signed_difference = *signed_arg1 - signed_arg2; + SF = (signed_difference < 0); + ZF = (signed_difference == 0); + const int64_t signed_full_difference = *signed_arg1 - signed_arg2; + OF = (signed_difference != signed_full_difference); + const uint32_t unsigned_arg1 = static_cast<uint32_t>(*signed_arg1); + const uint32_t unsigned_arg2 = static_cast<uint32_t>(signed_arg2); + cerr << unsigned_arg1 << " vs " << unsigned_arg2 << '\n'; + const uint32_t unsigned_difference = unsigned_arg1 - unsigned_arg2; + cerr << "result: " << unsigned_difference << '\n'; + const uint64_t unsigned_full_difference = unsigned_arg1 - unsigned_arg2; + cerr << "full result: " << unsigned_full_difference << '\n'; + CF = (unsigned_difference != unsigned_full_difference); + trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); break; } @@ -719,7 +764,23 @@ void test_compare_r32_with_r32_lesser() { CHECK_TRACE_CONTENTS( "run: compare EBX with r/m32\n" "run: r/m32 is EAX\n" - "run: SF=1; ZF=0; OF=0\n" + "run: SF=1; ZF=0; CF=0; OF=0\n" + ); +} + +void test_compare_r32_with_r32_lesser_unsigned() { + Reg[EAX].i = 0x7ffffffd; + Reg[EBX].i = 0x7fffffff; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 39 d8 \n" // compare EBX with EAX + // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX) + ); + CHECK_TRACE_CONTENTS( + "run: compare EBX with r/m32\n" + "run: r/m32 is EAX\n" + "run: SF=1; ZF=0; CF=1; OF=0\n" ); } @@ -735,7 +796,7 @@ void test_compare_r32_with_r32_equal() { CHECK_TRACE_CONTENTS( "run: compare EBX with r/m32\n" "run: r/m32 is EAX\n" - "run: SF=0; ZF=1; OF=0\n" + "run: SF=0; ZF=1; CF=0; OF=0\n" ); } diff --git a/subx/014indirect_addressing.cc b/subx/014indirect_addressing.cc index 8f0d3325..b8b3d6ce 100644 --- a/subx/014indirect_addressing.cc +++ b/subx/014indirect_addressing.cc @@ -320,7 +320,7 @@ void test_compare_mem_at_r32_with_r32_greater() { CHECK_TRACE_CONTENTS( "run: compare EBX with r/m32\n" "run: effective address is 0x00002000 (EAX)\n" - "run: SF=0; ZF=0; OF=0\n" + "run: SF=0; ZF=0; CF=0; OF=0\n" ); } @@ -339,7 +339,7 @@ void test_compare_mem_at_r32_with_r32_lesser() { CHECK_TRACE_CONTENTS( "run: compare EBX with r/m32\n" "run: effective address is 0x00002000 (EAX)\n" - "run: SF=1; ZF=0; OF=0\n" + "run: SF=1; ZF=0; CF=0; OF=0\n" ); } @@ -358,7 +358,7 @@ void test_compare_mem_at_r32_with_r32_equal() { CHECK_TRACE_CONTENTS( "run: compare EBX with r/m32\n" "run: effective address is 0x00002000 (EAX)\n" - "run: SF=0; ZF=1; OF=0\n" + "run: SF=0; ZF=1; CF=0; OF=0\n" ); } @@ -382,7 +382,7 @@ void test_compare_r32_with_mem_at_r32_greater() { CHECK_TRACE_CONTENTS( "run: compare r/m32 with EBX\n" "run: effective address is 0x00002000 (EAX)\n" - "run: SF=0; ZF=0; OF=0\n" + "run: SF=0; ZF=0; CF=0; OF=0\n" ); } @@ -391,14 +391,19 @@ case 0x3b: { // set SF if r32 < r/m32 const uint8_t modrm = next(); const uint8_t reg1 = (modrm>>3)&0x7; trace(Callstack_depth+1, "run") << "compare r/m32 with " << rname(reg1) << end(); - const int32_t arg1 = Reg[reg1].i; - const int32_t* arg2 = effective_address(modrm); - const int32_t tmp1 = arg1 - *arg2; - SF = (tmp1 < 0); - ZF = (tmp1 == 0); - int64_t tmp2 = arg1 - *arg2; - OF = (tmp1 != tmp2); - trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; OF=" << OF << end(); + const int32_t signed_arg1 = Reg[reg1].i; + const int32_t* signed_arg2 = effective_address(modrm); + const int32_t signed_difference = signed_arg1 - *signed_arg2; + SF = (signed_difference < 0); + ZF = (signed_difference == 0); + int64_t full_signed_difference = signed_arg1 - *signed_arg2; + OF = (signed_difference != full_signed_difference); + const uint32_t unsigned_arg1 = static_cast<uint32_t>(signed_arg1); + const uint32_t unsigned_arg2 = static_cast<uint32_t>(*signed_arg2); + const uint32_t unsigned_difference = unsigned_arg1 - unsigned_arg2; + const uint64_t full_unsigned_difference = unsigned_arg1 - unsigned_arg2; + CF = (unsigned_difference != full_unsigned_difference); + trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); break; } @@ -417,7 +422,7 @@ void test_compare_r32_with_mem_at_r32_lesser() { CHECK_TRACE_CONTENTS( "run: compare r/m32 with EBX\n" "run: effective address is 0x00002000 (EAX)\n" - "run: SF=1; ZF=0; OF=0\n" + "run: SF=1; ZF=0; CF=0; OF=0\n" ); } @@ -436,7 +441,7 @@ void test_compare_r32_with_mem_at_r32_equal() { CHECK_TRACE_CONTENTS( "run: compare r/m32 with EBX\n" "run: effective address is 0x00002000 (EAX)\n" - "run: SF=0; ZF=1; OF=0\n" + "run: SF=0; ZF=1; CF=0; OF=0\n" ); } diff --git a/subx/015immediate_addressing.cc b/subx/015immediate_addressing.cc index 16d886e8..97c868bd 100644 --- a/subx/015immediate_addressing.cc +++ b/subx/015immediate_addressing.cc @@ -571,21 +571,26 @@ void test_compare_imm32_with_eax_greater() { ); CHECK_TRACE_CONTENTS( "run: compare EAX and imm32 0x0d0c0b07\n" - "run: SF=0; ZF=0; OF=0\n" + "run: SF=0; ZF=0; CF=0; OF=0\n" ); } :(before "End Single-Byte Opcodes") case 0x3d: { // compare EAX with imm32 - const int32_t arg1 = Reg[EAX].i; - const int32_t arg2 = next32(); - trace(Callstack_depth+1, "run") << "compare EAX and imm32 0x" << HEXWORD << arg2 << end(); - const int32_t tmp1 = arg1 - arg2; - SF = (tmp1 < 0); - ZF = (tmp1 == 0); - const int64_t tmp2 = arg1 - arg2; - OF = (tmp1 != tmp2); - trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; OF=" << OF << end(); + const int32_t signed_arg1 = Reg[EAX].i; + const int32_t signed_arg2 = next32(); + trace(Callstack_depth+1, "run") << "compare EAX and imm32 0x" << HEXWORD << signed_arg2 << end(); + const int32_t signed_difference = signed_arg1 - signed_arg2; + SF = (signed_difference < 0); + ZF = (signed_difference == 0); + const int64_t full_signed_difference = signed_arg1 - signed_arg2; + OF = (signed_difference != full_signed_difference); + const uint32_t unsigned_arg1 = static_cast<uint32_t>(signed_arg1); + const uint32_t unsigned_arg2 = static_cast<uint32_t>(signed_arg2); + const uint32_t unsigned_difference = unsigned_arg1 - unsigned_arg2; + const uint64_t full_unsigned_difference = unsigned_arg1 - unsigned_arg2; + CF = (unsigned_difference != full_unsigned_difference); + trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); break; } @@ -599,7 +604,7 @@ void test_compare_imm32_with_eax_lesser() { ); CHECK_TRACE_CONTENTS( "run: compare EAX and imm32 0x0d0c0b0a\n" - "run: SF=1; ZF=0; OF=0\n" + "run: SF=1; ZF=0; CF=0; OF=0\n" ); } @@ -613,7 +618,7 @@ void test_compare_imm32_with_eax_equal() { ); CHECK_TRACE_CONTENTS( "run: compare EAX and imm32 0x0d0c0b0a\n" - "run: SF=0; ZF=1; OF=0\n" + "run: SF=0; ZF=1; CF=0; OF=0\n" ); } @@ -632,7 +637,7 @@ void test_compare_imm32_with_r32_greater() { "run: combine imm32 with r/m32\n" "run: r/m32 is EBX\n" "run: imm32 is 0x0d0c0b07\n" - "run: SF=0; ZF=0; OF=0\n" + "run: SF=0; ZF=0; CF=0; OF=0\n" ); } @@ -644,7 +649,7 @@ case 7: { ZF = (tmp1 == 0); const int64_t tmp2 = *arg1 - arg2; OF = (tmp1 != tmp2); - trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; OF=" << OF << end(); + trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); break; } @@ -661,7 +666,7 @@ void test_compare_imm32_with_r32_lesser() { "run: combine imm32 with r/m32\n" "run: r/m32 is EBX\n" "run: imm32 is 0x0d0c0b0a\n" - "run: SF=1; ZF=0; OF=0\n" + "run: SF=1; ZF=0; CF=0; OF=0\n" ); } @@ -678,7 +683,7 @@ void test_compare_imm32_with_r32_equal() { "run: combine imm32 with r/m32\n" "run: r/m32 is EBX\n" "run: imm32 is 0x0d0c0b0a\n" - "run: SF=0; ZF=1; OF=0\n" + "run: SF=0; ZF=1; CF=0; OF=0\n" ); } @@ -697,7 +702,7 @@ void test_compare_imm32_with_mem_at_r32_greater() { "run: combine imm32 with r/m32\n" "run: effective address is 0x00002000 (EBX)\n" "run: imm32 is 0x0d0c0b07\n" - "run: SF=0; ZF=0; OF=0\n" + "run: SF=0; ZF=0; CF=0; OF=0\n" ); } @@ -716,7 +721,7 @@ void test_compare_imm32_with_mem_at_r32_lesser() { "run: combine imm32 with r/m32\n" "run: effective address is 0x00002000 (EBX)\n" "run: imm32 is 0x0d0c0b0a\n" - "run: SF=1; ZF=0; OF=0\n" + "run: SF=1; ZF=0; CF=0; OF=0\n" ); } @@ -736,7 +741,7 @@ void test_compare_imm32_with_mem_at_r32_equal() { "run: combine imm32 with r/m32\n" "run: effective address is 0x00002000 (EBX)\n" "run: imm32 is 0x0d0c0b0a\n" - "run: SF=0; ZF=1; OF=0\n" + "run: SF=0; ZF=1; CF=0; OF=0\n" ); } diff --git a/subx/build b/subx/build index 614b07d8..f29518ac 100755 --- a/subx/build +++ b/subx/build @@ -22,7 +22,7 @@ UNTIL_LAYER=${2:-zzz} test "$CXX" || export CXX=c++ test "$CC" || export CC=cc test "$CFLAGS" || export CFLAGS="-g -O3" -export CFLAGS="$CFLAGS -Wall -Wextra -ftrapv -fno-strict-aliasing" +export CFLAGS="$CFLAGS -Wall -Wextra -fno-strict-aliasing" # return 1 if $1 is older than _any_ of the remaining args older_than() { |