diff options
Diffstat (limited to 'subx')
-rw-r--r-- | subx/003trace.cc | 46 | ||||
-rw-r--r-- | subx/003trace.test.cc | 4 | ||||
-rw-r--r-- | subx/011run.cc | 183 | ||||
-rw-r--r-- | subx/013direct_addressing.cc | 741 | ||||
-rw-r--r-- | subx/014indirect_addressing.cc | 847 | ||||
-rw-r--r-- | subx/015immediate_addressing.cc | 871 | ||||
-rw-r--r-- | subx/016index_addressing.cc | 178 | ||||
-rw-r--r-- | subx/017jump_disp8.cc | 424 | ||||
-rw-r--r-- | subx/018jump_disp32.cc | 424 | ||||
-rw-r--r-- | subx/019functions.cc | 116 | ||||
-rw-r--r-- | subx/021byte_addressing.cc | 126 | ||||
-rw-r--r-- | subx/030---operands.cc | 155 | ||||
-rw-r--r-- | subx/031check_operands.cc | 300 | ||||
-rw-r--r-- | subx/032check_operand_bounds.cc | 15 | ||||
-rw-r--r-- | subx/034compute_segment_address.cc | 124 | ||||
-rw-r--r-- | subx/035labels.cc | 230 | ||||
-rw-r--r-- | subx/036global_variables.cc | 189 | ||||
-rw-r--r-- | subx/038---literal_strings.cc | 192 | ||||
-rw-r--r-- | subx/040---tests.cc | 31 |
19 files changed, 3144 insertions, 2052 deletions
diff --git a/subx/003trace.cc b/subx/003trace.cc index bb614c66..9717fb80 100644 --- a/subx/003trace.cc +++ b/subx/003trace.cc @@ -16,28 +16,26 @@ //: In response, this layer introduces the notion of domain-driven *white-box* //: testing. We focus on the domain of inputs the whole program needs to //: handle rather than the correctness of individual functions. All white-box -//: tests (we call them 'scenarios') invoke the program in a single way: by -//: calling run() with some input. As the program operates on the input, it -//: traces out a list of _facts_ deduced about the domain: +//: tests invoke the program in a single way: by calling run() with some +//: input. As the program operates on the input, it traces out a list of +//: _facts_ deduced about the domain: //: trace("label") << "fact 1: " << val; //: -//: Scenarios can now check these facts: -//: :(scenario foo) -//: 34 # call run() with this input -//: +label: fact 1: 34 # 'run' should have deduced this fact -//: -label: fact 1: 35 # the trace should not contain such a fact +//: Tests can now check for these facts in the trace: +//: CHECK_TRACE_CONTENTS("label", "fact 1: 34\n" +//: "fact 2: 35\n"); //: //: Since we never call anything but the run() function directly, we never have -//: to rewrite the scenarios when we reorganize the internals of the program. We +//: to rewrite the tests when we reorganize the internals of the program. We //: just have to make sure our rewrite deduces the same facts about the domain, //: and that's something we're going to have to do anyway. //: //: To avoid the combinatorial explosion of integration tests, each layer -//: mainly logs facts to the trace with a common *label*. All scenarios in a -//: layer tend to check facts with this label. Validating the facts logged -//: with a specific label is like calling functions of that layer directly. +//: mainly logs facts to the trace with a common *label*. All tests in a layer +//: tend to check facts with this label. Validating the facts logged with a +//: specific label is like calling functions of that layer directly. //: -//: To build robust scenarios, trace facts about your domain rather than details of +//: To build robust tests, trace facts about your domain rather than details of //: how you computed them. //: //: More details: http://akkartik.name/blog/tracing-tests @@ -50,10 +48,10 @@ //: we allow programmers to engage with the a) deep, b) global structure of //: the c) domain. If you can systematically track discontinuities in the //: domain, you don't care if the code used gotos as long as it passed all -//: scenarios. If scenarios become more robust to run, it becomes easier to -//: try out radically different implementations for the same program. If code -//: is super-easy to rewrite, it becomes less important what indentation style -//: it uses, or that the objects are appropriately encapsulated, or that the +//: tests. If tests become more robust to run, it becomes easier to try out +//: radically different implementations for the same program. If code is +//: super-easy to rewrite, it becomes less important what indentation style it +//: uses, or that the objects are appropriately encapsulated, or that the //: functions are referentially transparent. //: //: Instead of plumbing, programming becomes building and gradually refining a @@ -61,7 +59,7 @@ //: is 'correct' at a given point in time is a red herring; what matters is //: avoiding regression by monotonically nailing down the more 'eventful' //: parts of the terrain. It helps readers new and old, and rewards curiosity, -//: to organize large programs in self-similar hierarchies of example scenarios +//: to organize large programs in self-similar hierarchies of example tests //: colocated with the code that makes them work. //: //: "Programming properly should be regarded as an activity by which @@ -178,7 +176,7 @@ void trace_stream::newline() { curr_depth = Max_depth; } -//:: == Initializing the trace in scenarios +//:: == Initializing the trace in tests :(before "End Includes") #define START_TRACING_UNTIL_END_OF_SCOPE lease_tracer leased_tracer; @@ -214,7 +212,7 @@ int Hide_warnings = false; // if set, don't print warnings to screen :(before "End Reset") Hide_errors = false; Hide_warnings = false; -//: Never dump warnings in scenarios +//: Never dump warnings in tests :(before "End Test Setup") Hide_warnings = true; :(code) @@ -230,7 +228,7 @@ bool should_incrementally_print_trace(); :(before "End Globals") int Trace_errors = 0; // used only when Trace_stream is NULL -// Fail scenarios that displayed (unexpected) errors. +// Fail tests that displayed (unexpected) errors. // Expected errors should always be hidden and silently checked for. :(before "End Test Teardown") if (Passed && !Hide_errors && trace_contains_errors()) { @@ -287,14 +285,14 @@ bool trace_contains_errors() { return; \ } -// Allow scenarios to ignore trace lines generated during setup. +// Allow tests to ignore trace lines generated during setup. #define CLEAR_TRACE delete Trace_stream, Trace_stream = new trace_stream :(code) bool check_trace_contents(string FUNCTION, string FILE, int LINE, string expected) { if (!Passed) return false; if (!Trace_stream) return false; - vector<string> expected_lines = split(expected, ""); + vector<string> expected_lines = split(expected, "\n"); int curr_expected_line = 0; while (curr_expected_line < SIZE(expected_lines) && expected_lines.at(curr_expected_line).empty()) ++curr_expected_line; @@ -408,7 +406,7 @@ vector<string> split_first(string s, string delim) { //:: == Helpers for debugging using traces :(before "End Includes") -// To debug why a scenario is failing, dump its trace using '?'. +// To debug why a test is failing, dump its trace using '?'. #define DUMP(label) if (Trace_stream) cerr << Trace_stream->readable_contents(label); // To add temporary prints to the trace, use 'dbg'. diff --git a/subx/003trace.test.cc b/subx/003trace.test.cc index 85751a4a..addc1f44 100644 --- a/subx/003trace.test.cc +++ b/subx/003trace.test.cc @@ -42,7 +42,9 @@ void test_trace_orders_across_layers() { trace("test layer 1") << "foo" << end(); trace("test layer 2") << "bar" << end(); trace("test layer 1") << "qux" << end(); - CHECK_TRACE_CONTENTS("test layer 1: footest layer 2: bartest layer 1: qux"); + CHECK_TRACE_CONTENTS("test layer 1: foo\n" + "test layer 2: bar\n" + "test layer 1: qux\n"); } void test_trace_supports_count() { diff --git a/subx/011run.cc b/subx/011run.cc index 90af649b..da5af920 100644 --- a/subx/011run.cc +++ b/subx/011run.cc @@ -34,46 +34,53 @@ put_new(Help, "syntax", :(before "End Help Contents") cerr << " syntax\n"; -:(scenario add_imm32_to_eax) -# At the lowest level, SubX programs are a series of hex bytes, each -# (variable-length) instruction on one line. -# -# Later we'll make things nicer using macros. But you'll always be able to -# insert hex bytes out of instructions. -# -# As you can see, comments start with '#' and are ignored. - -# Segment headers start with '==', specifying the hex address where they -# begin. There's usually one code segment and one data segment. We assume the -# code segment always comes first. Later when we emit ELF binaries we'll add -# directives for the operating system to ensure that the code segment can't be -# written to, and the data segment can't be executed as code. -== 0x1 - -# We don't show it here, but all lines can have metadata after a ':'. -# All words can have metadata after a '/'. No spaces allowed in word metadata, of course. -# Metadata doesn't directly form instructions, but some macros may look at it. -# Unrecognized metadata never causes errors, so you can also use it for -# documentation. - -# Within the code segment, x86 instructions consist of the following parts (see cheatsheet.pdf): -# opcode ModR/M SIB displacement immediate -# instruction mod, reg, Reg/Mem bits scale, index, base -# 1-3 bytes 0/1 byte 0/1 byte 0/1/2/4 bytes 0/1/2/4 bytes - 05 . . . 0a 0b 0c 0d # add 0x0d0c0b0a to EAX -# (The single periods are just to help the eye track long gaps between -# columns, and are otherwise ignored.) - -# This program, when run, causes the following events in the trace: -+load: 0x00000001 -> 05 -+load: 0x00000002 -> 0a -+load: 0x00000003 -> 0b -+load: 0x00000004 -> 0c -+load: 0x00000005 -> 0d -+run: add imm32 0x0d0c0b0a to reg EAX -+run: storing 0x0d0c0b0a - :(code) +void test_add_imm32_to_eax() { + // At the lowest level, SubX programs are a series of hex bytes, each + // (variable-length) instruction on one line. + run( + // Comments start with '#' and are ignored. + "# comment\n" + // Segment headers start with '==' and a name or starting hex address. + // There's usually one code and one data segment. The code segment + // always comes first. + "== 0x1\n" // code segment + + // After the header, each segment consists of lines, and each line + // consists of words separated by whitespace. + // + // All words can have metadata after a '/'. No spaces allowed in + // metadata, of course. + // Unrecognized metadata never causes errors, so you can use it for + // documentation. + // + // Within the code segment in particular, x86 instructions consist of + // some number of the following parts and sub-parts (see the Readme and + // cheatsheet.pdf for details): + // opcodes: 1-3 bytes + // ModR/M byte + // SIB byte + // displacement: 0/1/2/4 bytes + // immediate: 0/1/2/4 bytes + // opcode ModR/M SIB displacement immediate + // instruction mod, reg, Reg/Mem bits scale, index, base + // 1-3 bytes 0/1 byte 0/1 byte 0/1/2/4 bytes 0/1/2/4 bytes + " 05 . . . 0a 0b 0c 0d\n" // add 0x0d0c0b0a to EAX + // The periods are just to help the eye track long gaps between columns, + // and are otherwise ignored. + ); + // This program, when run, causes the following events in the trace: + CHECK_TRACE_CONTENTS( + "load: 0x00000001 -> 05\n" + "load: 0x00000002 -> 0a\n" + "load: 0x00000003 -> 0b\n" + "load: 0x00000004 -> 0c\n" + "load: 0x00000005 -> 0d\n" + "run: add imm32 0x0d0c0b0a to reg EAX\n" + "run: storing 0x0d0c0b0a\n" + ); +} + // top-level helper for scenarios: parse the input, transform any macros, load // the final hex bytes into memory, run it void run(const string& text_bytes) { @@ -207,14 +214,18 @@ void parse(const string& text_bytes) { parse(in, p); } -:(scenarios parse) -:(scenario detect_duplicate_segments) -% Hide_errors = true; -== 0xee -ab -== 0xee -cd -+error: can't have multiple segments starting at address 0x000000ee +void test_detect_duplicate_segments() { + Hide_errors = true; + parse( + "== 0xee\n" + "ab\n" + "== 0xee\n" + "cd\n" + ); + CHECK_TRACE_CONTENTS( + "error: can't have multiple segments starting at address 0x000000ee\n" + ); +} //:: transform @@ -278,37 +289,56 @@ uint8_t hex_byte(const string& s) { return static_cast<uint8_t>(result); } -:(scenarios parse_and_load) -:(scenario number_too_large) -% Hide_errors = true; -== 0x1 -05 cab -+error: token 'cab' is not a hex byte - -:(scenario invalid_hex) -% Hide_errors = true; -== 0x1 -05 cx -+error: token 'cx' is not a hex byte - -:(scenario negative_number) -== 0x1 -05 -12 -$error: 0 - -:(scenario negative_number_too_small) -% Hide_errors = true; -== 0x1 -05 -12345 -+error: token '-12345' is not a hex byte - -:(scenario hex_prefix) -== 0x1 -0x05 -0x12 -$error: 0 +void test_number_too_large() { + Hide_errors = true; + parse_and_load( + "== 0x1\n" + "05 cab\n" + ); + CHECK_TRACE_CONTENTS( + "error: token 'cab' is not a hex byte\n" + ); +} + +void test_invalid_hex() { + Hide_errors = true; + parse_and_load( + "== 0x1\n" + "05 cx\n" + ); + CHECK_TRACE_CONTENTS( + "error: token 'cx' is not a hex byte\n" + ); +} + +void test_negative_number() { + parse_and_load( + "== 0x1\n" + "05 -12\n" + ); + CHECK_TRACE_COUNT("error", 0); +} + +void test_negative_number_too_small() { + Hide_errors = true; + parse_and_load( + "== 0x1\n" + "05 -12345\n" + ); + CHECK_TRACE_CONTENTS( + "error: token '-12345' is not a hex byte\n" + ); +} + +void test_hex_prefix() { + parse_and_load( + "== 0x1\n" + "0x05 -0x12\n" + ); + CHECK_TRACE_COUNT("error", 0); +} //: helper for tests -:(code) void parse_and_load(const string& text_bytes) { program p; istringstream in(text_bytes); @@ -343,7 +373,6 @@ int32_t next32() { //:: helpers -:(code) string to_string(const word& w) { ostringstream out; out << w.data; diff --git a/subx/013direct_addressing.cc b/subx/013direct_addressing.cc index 40f9f52e..8f554772 100644 --- a/subx/013direct_addressing.cc +++ b/subx/013direct_addressing.cc @@ -3,16 +3,22 @@ :(before "End Initialize Op Names") put_new(Name, "01", "add r32 to rm32 (add)"); -:(scenario add_r32_to_r32) -% Reg[EAX].i = 0x10; -% Reg[EBX].i = 1; -== 0x1 -# op ModR/M SIB displacement immediate - 01 d8 # add EBX to EAX -# ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX) -+run: add EBX to r/m32 -+run: r/m32 is EAX -+run: storing 0x00000011 +:(code) +void test_add_r32_to_r32() { + Reg[EAX].i = 0x10; + 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 0x00000011\n" + ); +} :(before "End Single-Byte Opcodes") case 0x01: { // add r32 to r/m32 @@ -79,16 +85,22 @@ string rname(uint8_t r) { :(before "End Initialize Op Names") put_new(Name, "29", "subtract r32 from rm32 (sub)"); -:(scenario subtract_r32_from_r32) -% Reg[EAX].i = 10; -% Reg[EBX].i = 1; -== 0x1 -# op ModR/M SIB displacement immediate - 29 d8 # subtract EBX from EAX -# ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX) -+run: subtract EBX from r/m32 -+run: r/m32 is EAX -+run: storing 0x00000009 +:(code) +void test_subtract_r32_from_r32() { + Reg[EAX].i = 10; + Reg[EBX].i = 1; + 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 0x00000009\n" + ); +} :(before "End Single-Byte Opcodes") case 0x29: { // subtract r32 from r/m32 @@ -105,17 +117,23 @@ case 0x29: { // subtract r32 from r/m32 :(before "End Initialize Op Names") put_new(Name, "f7", "negate/multiply rm32 (with EAX if necessary) depending on subop (neg/mul)"); -:(scenario multiply_eax_by_r32) -% Reg[EAX].i = 4; -% Reg[ECX].i = 3; -== 0x1 -# op ModR/M SIB displacement immediate - f7 e1 # multiply EAX by ECX -# ModR/M in binary: 11 (direct mode) 100 (subop mul) 001 (src ECX) -+run: operate on r/m32 -+run: r/m32 is ECX -+run: subop: multiply EAX by r/m32 -+run: storing 0x0000000c +:(code) +void test_multiply_eax_by_r32() { + Reg[EAX].i = 4; + Reg[ECX].i = 3; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " f7 e1 \n" // multiply EAX by ECX + // ModR/M in binary: 11 (direct mode) 100 (subop mul) 001 (src ECX) + ); + CHECK_TRACE_CONTENTS( + "run: operate on r/m32\n" + "run: r/m32 is ECX\n" + "run: subop: multiply EAX by r/m32\n" + "run: storing 0x0000000c\n" + ); +} :(before "End Single-Byte Opcodes") case 0xf7: { @@ -146,16 +164,22 @@ case 0xf7: { :(before "End Initialize Op Names") put_new(Name_0f, "af", "multiply rm32 into r32 (imul)"); -:(scenario multiply_r32_into_r32) -% Reg[EAX].i = 4; -% Reg[EBX].i = 2; -== 0x1 -# op ModR/M SIB displacement immediate - 0f af d8 # subtract EBX into EAX -# ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX) -+run: multiply r/m32 into EBX -+run: r/m32 is EAX -+run: storing 0x00000008 +:(code) +void test_multiply_r32_into_r32() { + Reg[EAX].i = 4; + Reg[EBX].i = 2; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 0f af d8 \n" // subtract EBX into EAX + // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX) + ); + CHECK_TRACE_CONTENTS( + "run: multiply r/m32 into EBX\n" + "run: r/m32 is EAX\n" + "run: storing 0x00000008\n" + ); +} :(before "End Two-Byte Opcodes Starting With 0f") case 0xaf: { // multiply r32 into r/m32 @@ -169,16 +193,22 @@ case 0xaf: { // multiply r32 into r/m32 //:: negate -:(scenario negate_r32) -% Reg[EBX].i = 1; -== 0x1 -# op ModR/M SIB displacement immediate - f7 db # negate EBX -# ModR/M in binary: 11 (direct mode) 011 (subop negate) 011 (dest EBX) -+run: operate on r/m32 -+run: r/m32 is EBX -+run: subop: negate -+run: storing 0xffffffff +:(code) +void test_negate_r32() { + Reg[EBX].i = 1; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " f7 db \n" // negate EBX + // ModR/M in binary: 11 (direct mode) 011 (subop negate) 011 (dest EBX) + ); + CHECK_TRACE_CONTENTS( + "run: operate on r/m32\n" + "run: r/m32 is EBX\n" + "run: subop: negate\n" + "run: storing 0xffffffff\n" + ); +} :(before "End Op f7 Subops") case 3: { // negate r/m32 @@ -199,33 +229,46 @@ case 3: { // negate r/m32 break; } -:(scenario negate_can_overflow) // in exactly one situation -% Reg[EBX].i = 0x80000000; // INT_MIN -== 0x1 -# op ModR/M SIB displacement immediate - f7 db # negate EBX -# ModR/M in binary: 11 (direct mode) 011 (subop negate) 011 (dest EBX) -+run: operate on r/m32 -+run: r/m32 is EBX -+run: subop: negate -+run: overflow +:(code) +// negate can overflow in exactly one situation +void test_negate_can_overflow() { + Reg[EBX].i = 0x80000000; // INT_MIN + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " f7 db \n" // negate EBX + // ModR/M in binary: 11 (direct mode) 011 (subop negate) 011 (dest EBX) + ); + CHECK_TRACE_CONTENTS( + "run: operate on r/m32\n" + "run: r/m32 is EBX\n" + "run: subop: negate\n" + "run: overflow\n" + ); +} //:: shift left :(before "End Initialize Op Names") put_new(Name, "d3", "shift rm32 by CL bits depending on subop (sal/sar/shl/shr)"); -:(scenario shift_left_r32_with_cl) -% Reg[EBX].i = 13; -% Reg[ECX].i = 1; -== 0x1 -# op ModR/M SIB displacement immediate - d3 e3 # negate EBX -# ModR/M in binary: 11 (direct mode) 100 (subop shift left) 011 (dest EBX) -+run: operate on r/m32 -+run: r/m32 is EBX -+run: subop: shift left by CL bits -+run: storing 0x0000001a +:(code) +void test_shift_left_r32_with_cl() { + Reg[EBX].i = 13; + Reg[ECX].i = 1; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " d3 e3 \n" // shift EBX left by CL bits + // ModR/M in binary: 11 (direct mode) 100 (subop shift left) 011 (dest EBX) + ); + CHECK_TRACE_CONTENTS( + "run: operate on r/m32\n" + "run: r/m32 is EBX\n" + "run: subop: shift left by CL bits\n" + "run: storing 0x0000001a\n" + ); +} :(before "End Single-Byte Opcodes") case 0xd3: { @@ -259,17 +302,23 @@ case 0xd3: { //:: shift right arithmetic -:(scenario shift_right_arithmetic_r32_with_cl) -% Reg[EBX].i = 26; -% Reg[ECX].i = 1; -== 0x1 -# op ModR/M SIB displacement immediate - d3 fb # negate EBX -# ModR/M in binary: 11 (direct mode) 111 (subop shift right arithmetic) 011 (dest EBX) -+run: operate on r/m32 -+run: r/m32 is EBX -+run: subop: shift right by CL bits, while preserving sign -+run: storing 0x0000000d +:(code) +void test_shift_right_arithmetic_r32_with_cl() { + Reg[EBX].i = 26; + Reg[ECX].i = 1; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " d3 fb \n" // shift EBX right by CL bits, while preserving sign + // ModR/M in binary: 11 (direct mode) 111 (subop shift right arithmetic) 011 (dest EBX) + ); + CHECK_TRACE_CONTENTS( + "run: operate on r/m32\n" + "run: r/m32 is EBX\n" + "run: subop: shift right by CL bits, while preserving sign\n" + "run: storing 0x0000000d\n" + ); +} :(before "End Op d3 Subops") case 7: { // shift right r/m32 by CL, preserving sign @@ -284,45 +333,63 @@ case 7: { // shift right r/m32 by CL, preserving sign break; } -:(scenario shift_right_arithmetic_odd_r32_with_cl) -% Reg[EBX].i = 27; -% Reg[ECX].i = 1; -== 0x1 -# op ModR/M SIB displacement immediate - d3 fb # negate EBX -# ModR/M in binary: 11 (direct mode) 111 (subop shift right arithmetic) 011 (dest EBX) -+run: operate on r/m32 -+run: r/m32 is EBX -+run: subop: shift right by CL bits, while preserving sign -# result: 13 -+run: storing 0x0000000d - -:(scenario shift_right_arithmetic_negative_r32_with_cl) -% Reg[EBX].i = 0xfffffffd; // -3 -% Reg[ECX].i = 1; -== 0x1 -# op ModR/M SIB displacement immediate - d3 fb # negate EBX -# ModR/M in binary: 11 (direct mode) 111 (subop shift right arithmetic) 011 (dest EBX) -+run: operate on r/m32 -+run: r/m32 is EBX -+run: subop: shift right by CL bits, while preserving sign -# result: -2 -+run: storing 0xfffffffe +:(code) +void test_shift_right_arithmetic_odd_r32_with_cl() { + Reg[EBX].i = 27; + Reg[ECX].i = 1; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " d3 fb \n" // shift EBX right by CL bits, while preserving sign + // ModR/M in binary: 11 (direct mode) 111 (subop shift right arithmetic) 011 (dest EBX) + ); + CHECK_TRACE_CONTENTS( + "run: operate on r/m32\n" + "run: r/m32 is EBX\n" + "run: subop: shift right by CL bits, while preserving sign\n" + // result: 13 + "run: storing 0x0000000d\n" + ); +} + +void test_shift_right_arithmetic_negative_r32_with_cl() { + Reg[EBX].i = 0xfffffffd; // -3 + Reg[ECX].i = 1; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " d3 fb \n" // shift EBX right by CL bits, while preserving sign + // ModR/M in binary: 11 (direct mode) 111 (subop shift right arithmetic) 011 (dest EBX) + ); + CHECK_TRACE_CONTENTS( + "run: operate on r/m32\n" + "run: r/m32 is EBX\n" + "run: subop: shift right by CL bits, while preserving sign\n" + // result: -2 + "run: storing 0xfffffffe\n" + ); +} //:: shift right logical -:(scenario shift_right_logical_r32_with_cl) -% Reg[EBX].i = 26; -% Reg[ECX].i = 1; -== 0x1 -# op ModR/M SIB displacement immediate - d3 eb # negate EBX -# ModR/M in binary: 11 (direct mode) 101 (subop shift right logical) 011 (dest EBX) -+run: operate on r/m32 -+run: r/m32 is EBX -+run: subop: shift right by CL bits, while padding zeroes -+run: storing 0x0000000d +:(code) +void test_shift_right_logical_r32_with_cl() { + Reg[EBX].i = 26; + Reg[ECX].i = 1; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " d3 eb \n" // shift EBX right by CL bits, while padding zeroes + // ModR/M in binary: 11 (direct mode) 101 (subop shift right logical) 011 (dest EBX) + ); + CHECK_TRACE_CONTENTS( + "run: operate on r/m32\n" + "run: r/m32 is EBX\n" + "run: subop: shift right by CL bits, while padding zeroes\n" + // result: 13 + "run: storing 0x0000000d\n" + ); +} :(before "End Op d3 Subops") case 5: { // shift right r/m32 by CL, preserving sign @@ -343,46 +410,63 @@ case 5: { // shift right r/m32 by CL, preserving sign break; } -:(scenario shift_right_logical_odd_r32_with_cl) -% Reg[EBX].i = 27; -% Reg[ECX].i = 1; -== 0x1 -# op ModR/M SIB displacement immediate - d3 eb # negate EBX -# ModR/M in binary: 11 (direct mode) 101 (subop shift right logical) 011 (dest EBX) -+run: operate on r/m32 -+run: r/m32 is EBX -+run: subop: shift right by CL bits, while padding zeroes -# result: 13 -+run: storing 0x0000000d - -:(scenario shift_right_logical_negative_r32_with_cl) -% Reg[EBX].i = 0xfffffffd; -% Reg[ECX].i = 1; -== 0x1 -# op ModR/M SIB displacement immediate - d3 eb # negate EBX -# ModR/M in binary: 11 (direct mode) 101 (subop shift right logical) 011 (dest EBX) -+run: operate on r/m32 -+run: r/m32 is EBX -+run: subop: shift right by CL bits, while padding zeroes -+run: storing 0x7ffffffe +:(code) +void test_shift_right_logical_odd_r32_with_cl() { + Reg[EBX].i = 27; + Reg[ECX].i = 1; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " d3 eb \n" // shift EBX right by CL bits, while padding zeroes + // ModR/M in binary: 11 (direct mode) 101 (subop shift right logical) 011 (dest EBX) + ); + CHECK_TRACE_CONTENTS( + "run: operate on r/m32\n" + "run: r/m32 is EBX\n" + "run: subop: shift right by CL bits, while padding zeroes\n" + // result: 13 + "run: storing 0x0000000d\n" + ); +} + +void test_shift_right_logical_negative_r32_with_cl() { + Reg[EBX].i = 0xfffffffd; + Reg[ECX].i = 1; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " d3 eb \n" // shift EBX right by CL bits, while padding zeroes + // ModR/M in binary: 11 (direct mode) 101 (subop shift right logical) 011 (dest EBX) + ); + CHECK_TRACE_CONTENTS( + "run: operate on r/m32\n" + "run: r/m32 is EBX\n" + "run: subop: shift right by CL bits, while padding zeroes\n" + "run: storing 0x7ffffffe\n" + ); +} //:: and :(before "End Initialize Op Names") put_new(Name, "21", "rm32 = bitwise AND of r32 with rm32 (and)"); -:(scenario and_r32_with_r32) -% Reg[EAX].i = 0x0a0b0c0d; -% Reg[EBX].i = 0x000000ff; -== 0x1 -# op ModR/M SIB displacement immediate - 21 d8 # and EBX with destination EAX -# ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX) -+run: and EBX with r/m32 -+run: r/m32 is EAX -+run: storing 0x0000000d +:(code) +void test_and_r32_with_r32() { + Reg[EAX].i = 0x0a0b0c0d; + Reg[EBX].i = 0x000000ff; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 21 d8 \n" // and EBX with destination EAX + // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX) + ); + CHECK_TRACE_CONTENTS( + "run: and EBX with r/m32\n" + "run: r/m32 is EAX\n" + "run: storing 0x0000000d\n" + ); +} :(before "End Single-Byte Opcodes") case 0x21: { // and r32 with r/m32 @@ -399,16 +483,22 @@ case 0x21: { // and r32 with r/m32 :(before "End Initialize Op Names") put_new(Name, "09", "rm32 = bitwise OR of r32 with rm32 (or)"); -:(scenario or_r32_with_r32) -% Reg[EAX].i = 0x0a0b0c0d; -% Reg[EBX].i = 0xa0b0c0d0; -== 0x1 -# op ModR/M SIB displacement immediate - 09 d8 # or EBX with destination EAX -# ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX) -+run: or EBX with r/m32 -+run: r/m32 is EAX -+run: storing 0xaabbccdd +:(code) +void test_or_r32_with_r32() { + Reg[EAX].i = 0x0a0b0c0d; + Reg[EBX].i = 0xa0b0c0d0; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 09 d8 \n" // or EBX with destination EAX + // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX) + ); + CHECK_TRACE_CONTENTS( + "run: or EBX with r/m32\n" + "run: r/m32 is EAX\n" + "run: storing 0xaabbccdd\n" + ); +} :(before "End Single-Byte Opcodes") case 0x09: { // or r32 with r/m32 @@ -425,16 +515,22 @@ case 0x09: { // or r32 with r/m32 :(before "End Initialize Op Names") put_new(Name, "31", "rm32 = bitwise XOR of r32 with rm32 (xor)"); -:(scenario xor_r32_with_r32) -% Reg[EAX].i = 0x0a0b0c0d; -% Reg[EBX].i = 0xaabbc0d0; -== 0x1 -# op ModR/M SIB displacement immediate - 31 d8 # xor EBX with destination EAX -# ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX) -+run: xor EBX with r/m32 -+run: r/m32 is EAX -+run: storing 0xa0b0ccdd +:(code) +void test_xor_r32_with_r32() { + Reg[EAX].i = 0x0a0b0c0d; + Reg[EBX].i = 0xaabbc0d0; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 31 d8 \n" // xor EBX with destination EAX + // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX) + ); + CHECK_TRACE_CONTENTS( + "run: xor EBX with r/m32\n" + "run: r/m32 is EAX\n" + "run: storing 0xa0b0ccdd\n" + ); +} :(before "End Single-Byte Opcodes") case 0x31: { // xor r32 with r/m32 @@ -448,16 +544,22 @@ case 0x31: { // xor r32 with r/m32 //:: not -:(scenario not_r32) -% Reg[EBX].i = 0x0f0f00ff; -== 0x1 -# op ModR/M SIB displacement immediate - f7 d3 # not EBX -# ModR/M in binary: 11 (direct mode) 010 (subop not) 011 (dest EBX) -+run: operate on r/m32 -+run: r/m32 is EBX -+run: subop: not -+run: storing 0xf0f0ff00 +:(code) +void test_not_r32() { + Reg[EBX].i = 0x0f0f00ff; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " f7 d3 \n" // not EBX + // ModR/M in binary: 11 (direct mode) 010 (subop not) 011 (dest EBX) + ); + CHECK_TRACE_CONTENTS( + "run: operate on r/m32\n" + "run: r/m32 is EBX\n" + "run: subop: not\n" + "run: storing 0xf0f0ff00\n" + ); +} :(before "End Op f7 Subops") case 2: { // not r/m32 @@ -475,16 +577,22 @@ case 2: { // not r/m32 :(before "End Initialize Op Names") put_new(Name, "39", "compare: set SF if rm32 < r32 (cmp)"); -:(scenario compare_r32_with_r32_greater) -% Reg[EAX].i = 0x0a0b0c0d; -% Reg[EBX].i = 0x0a0b0c07; -== 0x1 -# op ModR/M SIB displacement immediate - 39 d8 # compare EBX with EAX -# ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX) -+run: compare EBX with r/m32 -+run: r/m32 is EAX -+run: SF=0; ZF=0; OF=0 +:(code) +void test_compare_r32_with_r32_greater() { + Reg[EAX].i = 0x0a0b0c0d; + Reg[EBX].i = 0x0a0b0c07; + 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=0; ZF=0; OF=0\n" + ); +} :(before "End Single-Byte Opcodes") case 0x39: { // set SF if r/m32 < r32 @@ -502,42 +610,59 @@ case 0x39: { // set SF if r/m32 < r32 break; } -:(scenario compare_r32_with_r32_lesser) -% Reg[EAX].i = 0x0a0b0c07; -% Reg[EBX].i = 0x0a0b0c0d; -== 0x1 -# op ModR/M SIB displacement immediate - 39 d8 # compare EBX with EAX -# ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX) -+run: compare EBX with r/m32 -+run: r/m32 is EAX -+run: SF=1; ZF=0; OF=0 - -:(scenario compare_r32_with_r32_equal) -% Reg[EAX].i = 0x0a0b0c0d; -% Reg[EBX].i = 0x0a0b0c0d; -== 0x1 -# op ModR/M SIB displacement immediate - 39 d8 # compare EBX with EAX -# ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX) -+run: compare EBX with r/m32 -+run: r/m32 is EAX -+run: SF=0; ZF=1; OF=0 +:(code) +void test_compare_r32_with_r32_lesser() { + Reg[EAX].i = 0x0a0b0c07; + Reg[EBX].i = 0x0a0b0c0d; + 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; OF=0\n" + ); +} + +void test_compare_r32_with_r32_equal() { + Reg[EAX].i = 0x0a0b0c0d; + Reg[EBX].i = 0x0a0b0c0d; + 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=0; ZF=1; OF=0\n" + ); +} //:: copy (mov) :(before "End Initialize Op Names") put_new(Name, "89", "copy r32 to rm32 (mov)"); -:(scenario copy_r32_to_r32) -% Reg[EBX].i = 0xaf; -== 0x1 -# op ModR/M SIB displacement immediate - 89 d8 # copy EBX to EAX -# ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX) -+run: copy EBX to r/m32 -+run: r/m32 is EAX -+run: storing 0x000000af +:(code) +void test_copy_r32_to_r32() { + Reg[EBX].i = 0xaf; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 89 d8 \n" // copy EBX to EAX + // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX) + ); + CHECK_TRACE_CONTENTS( + "run: copy EBX to r/m32\n" + "run: r/m32 is EAX\n" + "run: storing 0x000000af\n" + ); +} :(before "End Single-Byte Opcodes") case 0x89: { // copy r32 to r/m32 @@ -555,17 +680,23 @@ case 0x89: { // copy r32 to r/m32 :(before "End Initialize Op Names") put_new(Name, "87", "swap the contents of r32 and rm32 (xchg)"); -:(scenario xchg_r32_with_r32) -% Reg[EBX].i = 0xaf; -% Reg[EAX].i = 0x2e; -== 0x1 -# op ModR/M SIB displacement immediate - 87 d8 # exchange EBX with EAX -# ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX) -+run: exchange EBX with r/m32 -+run: r/m32 is EAX -+run: storing 0x000000af in r/m32 -+run: storing 0x0000002e in EBX +:(code) +void test_xchg_r32_with_r32() { + Reg[EBX].i = 0xaf; + Reg[EAX].i = 0x2e; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 87 d8 \n" // exchange EBX with EAX + // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX) + ); + CHECK_TRACE_CONTENTS( + "run: exchange EBX with r/m32\n" + "run: r/m32 is EAX\n" + "run: storing 0x000000af in r/m32\n" + "run: storing 0x0000002e in EBX\n" + ); +} :(before "End Single-Byte Opcodes") case 0x87: { // exchange r32 with r/m32 @@ -593,13 +724,19 @@ put_new(Name, "45", "increment EBP (inc)"); put_new(Name, "46", "increment ESI (inc)"); put_new(Name, "47", "increment EDI (inc)"); -:(scenario increment_r32) -% Reg[ECX].u = 0x1f; -== 0x1 # code segment -# op ModR/M SIB displacement immediate - 41 # increment ECX -+run: increment ECX -+run: storing value 0x00000020 +:(code) +void test_increment_r32() { + Reg[ECX].u = 0x1f; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 41 \n" // increment ECX + ); + CHECK_TRACE_CONTENTS( + "run: increment ECX\n" + "run: storing value 0x00000020\n" + ); +} :(before "End Single-Byte Opcodes") case 0x40: @@ -620,15 +757,21 @@ case 0x47: { // increment r32 :(before "End Initialize Op Names") put_new(Name, "ff", "increment/decrement/jump/push/call rm32 based on subop (inc/dec/jmp/push/call)"); -:(scenario increment_rm32) -% Reg[EAX].u = 0x20; -== 0x1 # code segment -# op ModR/M SIB displacement immediate - ff c0 # increment EAX -# ModR/M in binary: 11 (direct mode) 000 (subop inc) 000 (EAX) -+run: increment r/m32 -+run: r/m32 is EAX -+run: storing value 0x00000021 +:(code) +void test_increment_rm32() { + Reg[EAX].u = 0x20; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " ff c0 \n" // increment EAX + // ModR/M in binary: 11 (direct mode) 000 (subop inc) 000 (EAX) + ); + CHECK_TRACE_CONTENTS( + "run: increment r/m32\n" + "run: r/m32 is EAX\n" + "run: storing value 0x00000021\n" + ); +} :(before "End Single-Byte Opcodes") case 0xff: { @@ -663,13 +806,19 @@ put_new(Name, "4d", "decrement EBP (dec)"); put_new(Name, "4e", "decrement ESI (dec)"); put_new(Name, "4f", "decrement EDI (dec)"); -:(scenario decrement_r32) -% Reg[ECX].u = 0x1f; -== 0x1 # code segment -# op ModR/M SIB displacement immediate - 49 # decrement ECX -+run: decrement ECX -+run: storing value 0x0000001e +:(code) +void test_decrement_r32() { + Reg[ECX].u = 0x1f; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 49 \n" // decrement ECX + ); + CHECK_TRACE_CONTENTS( + "run: decrement ECX\n" + "run: storing value 0x0000001e\n" + ); +} :(before "End Single-Byte Opcodes") case 0x48: @@ -687,15 +836,21 @@ case 0x4f: { // decrement r32 break; } -:(scenario decrement_rm32) -% Reg[EAX].u = 0x20; -== 0x1 # code segment -# op ModR/M SIB displacement immediate - ff c8 # decrement EAX -# ModR/M in binary: 11 (direct mode) 001 (subop inc) 000 (EAX) -+run: decrement r/m32 -+run: r/m32 is EAX -+run: storing value 0x0000001f +:(code) +void test_decrement_rm32() { + Reg[EAX].u = 0x20; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " ff c8 \n" // decrement EAX + // ModR/M in binary: 11 (direct mode) 001 (subop inc) 000 (EAX) + ); + CHECK_TRACE_CONTENTS( + "run: decrement r/m32\n" + "run: r/m32 is EAX\n" + "run: storing value 0x0000001f\n" + ); +} :(before "End Op ff Subops") case 1: { // decrement r/m32 @@ -718,15 +873,21 @@ put_new(Name, "55", "push EBP to stack (push)"); put_new(Name, "56", "push ESI to stack (push)"); put_new(Name, "57", "push EDI to stack (push)"); -:(scenario push_r32) -% Reg[ESP].u = 0x64; -% Reg[EBX].i = 0x0000000a; -== 0x1 -# op ModR/M SIB displacement immediate - 53 # push EBX to stack -+run: push EBX -+run: decrementing ESP to 0x00000060 -+run: pushing value 0x0000000a +:(code) +void test_push_r32() { + Reg[ESP].u = 0x64; + Reg[EBX].i = 0x0000000a; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 53 \n" // push EBX to stack + ); + CHECK_TRACE_CONTENTS( + "run: push EBX\n" + "run: decrementing ESP to 0x00000060\n" + "run: pushing value 0x0000000a\n" + ); +} :(before "End Single-Byte Opcodes") case 0x50: @@ -756,18 +917,24 @@ put_new(Name, "5d", "pop top of stack to EBP (pop)"); put_new(Name, "5e", "pop top of stack to ESI (pop)"); put_new(Name, "5f", "pop top of stack to EDI (pop)"); -:(scenario pop_r32) -% Reg[ESP].u = 0x02000000; -% Mem.push_back(vma(0x02000000)); // manually allocate memory -% write_mem_i32(0x02000000, 0x0000000a); // ..before this write -== 0x1 # code segment -# op ModR/M SIB displacement immediate - 5b # pop stack to EBX -== 0x2000 # data segment -0a 00 00 00 # 0x0a -+run: pop into EBX -+run: popping value 0x0000000a -+run: incrementing ESP to 0x02000004 +:(code) +void test_pop_r32() { + Reg[ESP].u = 0x02000000; + Mem.push_back(vma(0x02000000)); // manually allocate memory + write_mem_i32(0x02000000, 0x0000000a); // ..before this write + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 5b \n" // pop stack to EBX + "== 0x2000\n" // data segment + "0a 00 00 00\n" // 0x0000000a + ); + CHECK_TRACE_CONTENTS( + "run: pop into EBX\n" + "run: popping value 0x0000000a\n" + "run: incrementing ESP to 0x02000004\n" + ); +} :(before "End Single-Byte Opcodes") case 0x58: diff --git a/subx/014indirect_addressing.cc b/subx/014indirect_addressing.cc index 448f4231..f591f0cf 100644 --- a/subx/014indirect_addressing.cc +++ b/subx/014indirect_addressing.cc @@ -1,18 +1,23 @@ //: operating on memory at the address provided by some register //: we'll now start providing data in a separate segment -:(scenario add_r32_to_mem_at_r32) -% Reg[EBX].i = 0x10; -% Reg[EAX].i = 0x2000; -== 0x1 # code segment -# op ModR/M SIB displacement immediate - 01 18 # add EBX to *EAX -# ModR/M in binary: 00 (indirect mode) 011 (src EAX) 000 (dest EAX) -== 0x2000 # data segment -01 00 00 00 # 1 -+run: add EBX to r/m32 -+run: effective address is 0x00002000 (EAX) -+run: storing 0x00000011 +void test_add_r32_to_mem_at_r32() { + Reg[EBX].i = 0x10; + Reg[EAX].i = 0x2000; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 01 18 \n" // add EBX to *EAX + // ModR/M in binary: 00 (indirect mode) 011 (src EAX) 000 (dest EAX) + "== 0x2000\n" // data segment + "01 00 00 00\n" // 0x00000001 + ); + CHECK_TRACE_CONTENTS( + "run: add EBX to r/m32\n" + "run: effective address is 0x00002000 (EAX)\n" + "run: storing 0x00000011\n" + ); +} :(before "End Mod Special-cases(addr)") case 0: // indirect addressing @@ -30,18 +35,24 @@ case 0: // indirect addressing :(before "End Initialize Op Names") put_new(Name, "03", "add rm32 to r32 (add)"); -:(scenario add_mem_at_r32_to_r32) -% Reg[EAX].i = 0x2000; -% Reg[EBX].i = 0x10; -== 0x1 # code segment -# op ModR/M SIB displacement immediate - 03 18 # add *EAX to EBX -# ModR/M in binary: 00 (indirect mode) 011 (src EAX) 000 (dest EAX) -== 0x2000 # data segment -01 00 00 00 # 1 -+run: add r/m32 to EBX -+run: effective address is 0x00002000 (EAX) -+run: storing 0x00000011 +:(code) +void test_add_mem_at_r32_to_r32() { + Reg[EAX].i = 0x2000; + Reg[EBX].i = 0x10; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 03 18 \n" // add *EAX to EBX + // ModR/M in binary: 00 (indirect mode) 011 (src EAX) 000 (dest EAX) + "== 0x2000\n" // data segment + "01 00 00 00\n" // 0x00000001 + ); + CHECK_TRACE_CONTENTS( + "run: add r/m32 to EBX\n" + "run: effective address is 0x00002000 (EAX)\n" + "run: storing 0x00000011\n" + ); +} :(before "End Single-Byte Opcodes") case 0x03: { // add r/m32 to r32 @@ -55,36 +66,48 @@ case 0x03: { // add r/m32 to r32 //:: subtract -:(scenario subtract_r32_from_mem_at_r32) -% Reg[EAX].i = 0x2000; -% Reg[EBX].i = 1; -== 0x1 # code segment -# op ModR/M SIB displacement immediate - 29 18 # subtract EBX from *EAX -# ModR/M in binary: 00 (indirect mode) 011 (src EAX) 000 (dest EAX) -== 0x2000 # data segment -0a 00 00 00 # 10 -+run: subtract EBX from r/m32 -+run: effective address is 0x00002000 (EAX) -+run: storing 0x00000009 +:(code) +void test_subtract_r32_from_mem_at_r32() { + Reg[EAX].i = 0x2000; + Reg[EBX].i = 1; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 29 18 \n" // subtract EBX from *EAX + // ModR/M in binary: 00 (indirect mode) 011 (src EAX) 000 (dest EAX) + "== 0x2000\n" // data segment + "0a 00 00 00\n" // 0x0000000a + ); + CHECK_TRACE_CONTENTS( + "run: subtract EBX from r/m32\n" + "run: effective address is 0x00002000 (EAX)\n" + "run: storing 0x00000009\n" + ); +} //: :(before "End Initialize Op Names") put_new(Name, "2b", "subtract rm32 from r32 (sub)"); -:(scenario subtract_mem_at_r32_from_r32) -% Reg[EAX].i = 0x2000; -% Reg[EBX].i = 10; -== 0x1 # code segment -# op ModR/M SIB displacement immediate - 2b 18 # subtract *EAX from EBX -# ModR/M in binary: 00 (indirect mode) 011 (src EAX) 000 (dest EAX) -== 0x2000 # data segment -01 00 00 00 # 1 -+run: subtract r/m32 from EBX -+run: effective address is 0x00002000 (EAX) -+run: storing 0x00000009 +:(code) +void test_subtract_mem_at_r32_from_r32() { + Reg[EAX].i = 0x2000; + Reg[EBX].i = 10; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 2b 18 \n" // subtract *EAX from EBX + // ModR/M in binary: 00 (indirect mode) 011 (src EAX) 000 (dest EAX) + "== 0x2000\n" // data segment + "01 00 00 00\n" // 0x00000001 + ); + CHECK_TRACE_CONTENTS( + "run: subtract r/m32 from EBX\n" + "run: effective address is 0x00002000 (EAX)\n" + "run: storing 0x00000009\n" + ); +} :(before "End Single-Byte Opcodes") case 0x2b: { // subtract r/m32 from r32 @@ -97,37 +120,48 @@ case 0x2b: { // subtract r/m32 from r32 } //:: and - -:(scenario and_r32_with_mem_at_r32) -% Reg[EAX].i = 0x2000; -% Reg[EBX].i = 0xff; -== 0x1 # code segment -# op ModR/M SIB displacement immediate - 21 18 # and EBX with *EAX -# ModR/M in binary: 00 (indirect mode) 011 (src EAX) 000 (dest EAX) -== 0x2000 # data segment -0d 0c 0b 0a # 0x0a0b0c0d -+run: and EBX with r/m32 -+run: effective address is 0x00002000 (EAX) -+run: storing 0x0000000d +:(code) +void test_and_r32_with_mem_at_r32() { + Reg[EAX].i = 0x2000; + Reg[EBX].i = 0xff; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 21 18 \n" // and EBX with *EAX + // ModR/M in binary: 00 (indirect mode) 011 (src EAX) 000 (dest EAX) + "== 0x2000\n" // data segment + "0d 0c 0b 0a\n" // 0x0a0b0c0d + ); + CHECK_TRACE_CONTENTS( + "run: and EBX with r/m32\n" + "run: effective address is 0x00002000 (EAX)\n" + "run: storing 0x0000000d\n" + ); +} //: :(before "End Initialize Op Names") put_new(Name, "23", "r32 = bitwise AND of r32 with rm32 (and)"); -:(scenario and_mem_at_r32_with_r32) -% Reg[EAX].i = 0x2000; -% Reg[EBX].i = 0x0a0b0c0d; -== 0x1 # code segment -# op ModR/M SIB displacement immediate - 23 18 # and *EAX with EBX -# ModR/M in binary: 00 (indirect mode) 011 (src EAX) 000 (dest EAX) -== 0x2000 # data segment -ff 00 00 00 # 0xff -+run: and r/m32 with EBX -+run: effective address is 0x00002000 (EAX) -+run: storing 0x0000000d +:(code) +void test_and_mem_at_r32_with_r32() { + Reg[EAX].i = 0x2000; + Reg[EBX].i = 0x0a0b0c0d; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 23 18 \n" // and *EAX with EBX + // ModR/M in binary: 00 (indirect mode) 011 (src EAX) 000 (dest EAX) + "== 0x2000\n" // data segment + "ff 00 00 00\n" // 0x000000ff + ); + CHECK_TRACE_CONTENTS( + "run: and r/m32 with EBX\n" + "run: effective address is 0x00002000 (EAX)\n" + "run: storing 0x0000000d\n" + ); +} :(before "End Single-Byte Opcodes") case 0x23: { // and r/m32 with r32 @@ -141,36 +175,48 @@ case 0x23: { // and r/m32 with r32 //:: or -:(scenario or_r32_with_mem_at_r32) -% Reg[EAX].i = 0x2000; -% Reg[EBX].i = 0xa0b0c0d0; -== 0x1 # code segment -# op ModR/M SIB displacement immediate - 09 18 # or EBX with *EAX -# ModR/M in binary: 00 (indirect mode) 011 (src EAX) 000 (dest EAX) -== 0x2000 # data segment -0d 0c 0b 0a # 0x0a0b0c0d -+run: or EBX with r/m32 -+run: effective address is 0x00002000 (EAX) -+run: storing 0xaabbccdd +:(code) +void test_or_r32_with_mem_at_r32() { + Reg[EAX].i = 0x2000; + Reg[EBX].i = 0xa0b0c0d0; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 09 18 #\n" // EBX with *EAX + // ModR/M in binary: 00 (indirect mode) 011 (src EAX) 000 (dest EAX) + "== 0x2000\n" // data segment + "0d 0c 0b 0a\n" // 0x0a0b0c0d + ); + CHECK_TRACE_CONTENTS( + "run: or EBX with r/m32\n" + "run: effective address is 0x00002000 (EAX)\n" + "run: storing 0xaabbccdd\n" + ); +} //: :(before "End Initialize Op Names") put_new(Name, "0b", "r32 = bitwise OR of r32 with rm32 (or)"); -:(scenario or_mem_at_r32_with_r32) -% Reg[EAX].i = 0x2000; -% Reg[EBX].i = 0xa0b0c0d0; -== 0x1 # code segment -# op ModR/M SIB displacement immediate - 0b 18 # or *EAX with EBX -# ModR/M in binary: 00 (indirect mode) 011 (src EAX) 000 (dest EAX) -== 0x2000 # data segment -0d 0c 0b 0a # 0x0a0b0c0d -+run: or r/m32 with EBX -+run: effective address is 0x00002000 (EAX) -+run: storing 0xaabbccdd +:(code) +void test_or_mem_at_r32_with_r32() { + Reg[EAX].i = 0x2000; + Reg[EBX].i = 0xa0b0c0d0; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 0b 18 \n" // or *EAX with EBX + // ModR/M in binary: 00 (indirect mode) 011 (src EAX) 000 (dest EAX) + "== 0x2000\n" // data segment + "0d 0c 0b 0a\n" // 0x0a0b0c0d + ); + CHECK_TRACE_CONTENTS( + "run: or r/m32 with EBX\n" + "run: effective address is 0x00002000 (EAX)\n" + "run: storing 0xaabbccdd\n" + ); +} :(before "End Single-Byte Opcodes") case 0x0b: { // or r/m32 with r32 @@ -184,36 +230,47 @@ case 0x0b: { // or r/m32 with r32 //:: xor -:(scenario xor_r32_with_mem_at_r32) -% Reg[EAX].i = 0x2000; -% Reg[EBX].i = 0xa0b0c0d0; -== 0x1 # code segment -# op ModR/M SIB displacement immediate - 31 18 # xor EBX with *EAX -# ModR/M in binary: 00 (indirect mode) 011 (src EAX) 000 (dest EAX) -== 0x2000 # data segment -0d 0c bb aa # 0xaabb0c0d -+run: xor EBX with r/m32 -+run: effective address is 0x00002000 (EAX) -+run: storing 0x0a0bccdd +:(code) +void test_xor_r32_with_mem_at_r32() { + Reg[EAX].i = 0x2000; + Reg[EBX].i = 0xa0b0c0d0; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 31 18 \n" // xor EBX with *EAX + "== 0x2000\n" // data segment + "0d 0c bb aa\n" // 0xaabb0c0d + ); + CHECK_TRACE_CONTENTS( + "run: xor EBX with r/m32\n" + "run: effective address is 0x00002000 (EAX)\n" + "run: storing 0x0a0bccdd\n" + ); +} //: :(before "End Initialize Op Names") put_new(Name, "33", "r32 = bitwise XOR of r32 with rm32 (xor)"); -:(scenario xor_mem_at_r32_with_r32) -% Reg[EAX].i = 0x2000; -% Reg[EBX].i = 0xa0b0c0d0; -== 0x1 # code segment -# op ModR/M SIB displacement immediate - 33 18 # xor *EAX with EBX -# ModR/M in binary: 00 (indirect mode) 011 (src EAX) 000 (dest EAX) -== 0x2000 # data segment -0d 0c 0b 0a # 0x0a0b0c0d -+run: xor r/m32 with EBX -+run: effective address is 0x00002000 (EAX) -+run: storing 0xaabbccdd +:(code) +void test_xor_mem_at_r32_with_r32() { + Reg[EAX].i = 0x2000; + Reg[EBX].i = 0xa0b0c0d0; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 33 18 \n" // xor *EAX with EBX + // ModR/M in binary: 00 (indirect mode) 011 (src EAX) 000 (dest EAX) + "== 0x2000\n" // data segment + "0d 0c 0b 0a\n" // 0x0a0b0c0d + ); + CHECK_TRACE_CONTENTS( + "run: xor r/m32 with EBX\n" + "run: effective address is 0x00002000 (EAX)\n" + "run: storing 0xaabbccdd\n" + ); +} :(before "End Single-Byte Opcodes") case 0x33: { // xor r/m32 with r32 @@ -227,77 +284,107 @@ case 0x33: { // xor r/m32 with r32 //:: not -:(scenario not_of_mem_at_r32) -% Reg[EBX].i = 0x2000; -== 0x1 # code segment -# op ModR/M SIB displacement immediate - f7 13 # not *EBX -# ModR/M in binary: 00 (indirect mode) 010 (subop not) 011 (dest EBX) -== 0x2000 # data segment -ff 00 0f 0f # 0x0f0f00ff -+run: operate on r/m32 -+run: effective address is 0x00002000 (EBX) -+run: subop: not -+run: storing 0xf0f0ff00 +:(code) +void test_not_of_mem_at_r32() { + Reg[EBX].i = 0x2000; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " f7 13 \n" // not *EBX + // ModR/M in binary: 00 (indirect mode) 010 (subop not) 011 (dest EBX) + "== 0x2000\n" // data segment + "ff 00 0f 0f\n" // 0x0f0f00ff + ); + CHECK_TRACE_CONTENTS( + "run: operate on r/m32\n" + "run: effective address is 0x00002000 (EBX)\n" + "run: subop: not\n" + "run: storing 0xf0f0ff00\n" + ); +} //:: compare (cmp) -:(scenario compare_mem_at_r32_with_r32_greater) -% Reg[EAX].i = 0x2000; -% Reg[EBX].i = 0x0a0b0c07; -== 0x1 # code segment -# op ModR/M SIB displacement immediate - 39 18 # compare EBX with *EAX -# ModR/M in binary: 00 (indirect mode) 011 (src EAX) 000 (dest EAX) -== 0x2000 # data segment -0d 0c 0b 0a # 0x0a0b0c0d -+run: compare EBX with r/m32 -+run: effective address is 0x00002000 (EAX) -+run: SF=0; ZF=0; OF=0 - -:(scenario compare_mem_at_r32_with_r32_lesser) -% Reg[EAX].i = 0x2000; -% Reg[EBX].i = 0x0a0b0c0d; -== 0x1 # code segment -# op ModR/M SIB displacement immediate - 39 18 # compare EBX with *EAX -# ModR/M in binary: 00 (indirect mode) 011 (src EAX) 000 (dest EAX) -== 0x2000 # data segment -07 0c 0b 0a # 0x0a0b0c0d -+run: compare EBX with r/m32 -+run: effective address is 0x00002000 (EAX) -+run: SF=1; ZF=0; OF=0 - -:(scenario compare_mem_at_r32_with_r32_equal) -% Reg[EAX].i = 0x2000; -% Reg[EBX].i = 0x0a0b0c0d; -== 0x1 # code segment -# op ModR/M SIB displacement immediate - 39 18 # compare EBX with *EAX -# ModR/M in binary: 00 (indirect mode) 011 (src EAX) 000 (dest EAX) -== 0x2000 # data segment -0d 0c 0b 0a # 0x0a0b0c0d -+run: compare EBX with r/m32 -+run: effective address is 0x00002000 (EAX) -+run: SF=0; ZF=1; OF=0 +:(code) +void test_compare_mem_at_r32_with_r32_greater() { + Reg[EAX].i = 0x2000; + Reg[EBX].i = 0x0a0b0c07; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 39 18 \n" // compare EBX with *EAX + // ModR/M in binary: 00 (indirect mode) 011 (src EAX) 000 (dest EAX) + "== 0x2000\n" // data segment + "0d 0c 0b 0a\n" // 0x0a0b0c0d + ); + 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" + ); +} + +:(code) +void test_compare_mem_at_r32_with_r32_lesser() { + Reg[EAX].i = 0x2000; + Reg[EBX].i = 0x0a0b0c0d; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 39 18 \n" // compare EBX with *EAX + // ModR/M in binary: 00 (indirect mode) 011 (src EAX) 000 (dest EAX) + "== 0x2000\n" // data segment + "07 0c 0b 0a\n" // 0x0a0b0c0d + ); + 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" + ); +} + +:(code) +void test_compare_mem_at_r32_with_r32_equal() { + Reg[EAX].i = 0x2000; + Reg[EBX].i = 0x0a0b0c0d; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 39 18 \n" // compare EBX with *EAX + // ModR/M in binary: 00 (indirect mode) 011 (src EAX) 000 (dest EAX) + "== 0x2000\n" // data segment + "0d 0c 0b 0a\n" // 0x0a0b0c0d + ); + 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" + ); +} //: :(before "End Initialize Op Names") put_new(Name, "3b", "compare: set SF if r32 < rm32 (cmp)"); -:(scenario compare_r32_with_mem_at_r32_greater) -% Reg[EAX].i = 0x2000; -% Reg[EBX].i = 0x0a0b0c0d; -== 0x1 # code segment -# op ModR/M SIB displacement immediate - 3b 18 # compare *EAX with EBX -# ModR/M in binary: 00 (indirect mode) 011 (src EAX) 000 (dest EAX) -== 0x2000 # data segment -07 0c 0b 0a # 0x0a0b0c0d -+run: compare r/m32 with EBX -+run: effective address is 0x00002000 (EAX) -+run: SF=0; ZF=0; OF=0 +:(code) +void test_compare_r32_with_mem_at_r32_greater() { + Reg[EAX].i = 0x2000; + Reg[EBX].i = 0x0a0b0c0d; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 3b 18 \n" // compare *EAX with EBX + // ModR/M in binary: 00 (indirect mode) 011 (src EAX) 000 (dest EAX) + "== 0x2000\n" // data segment + "07 0c 0b 0a\n" // 0x0a0b0c0d + ); + 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" + ); +} :(before "End Single-Byte Opcodes") case 0x3b: { // set SF if r32 < r/m32 @@ -315,61 +402,84 @@ case 0x3b: { // set SF if r32 < r/m32 break; } -:(scenario compare_r32_with_mem_at_r32_lesser) -% Reg[EAX].i = 0x2000; -% Reg[EBX].i = 0x0a0b0c07; -== 0x1 # code segment -# op ModR/M SIB displacement immediate - 3b 18 # compare *EAX with EBX -# ModR/M in binary: 00 (indirect mode) 011 (src EAX) 000 (dest EAX) -== 0x2000 # data segment -0d 0c 0b 0a # 0x0a0b0c0d -+run: compare r/m32 with EBX -+run: effective address is 0x00002000 (EAX) -+run: SF=1; ZF=0; OF=0 - -:(scenario compare_r32_with_mem_at_r32_equal) -% Reg[EAX].i = 0x2000; -% Reg[EBX].i = 0x0a0b0c0d; -== 0x1 # code segment -# op ModR/M SIB displacement immediate - 3b 18 # compare *EAX with EBX -# ModR/M in binary: 00 (indirect mode) 011 (src EAX) 000 (dest EAX) -== 0x2000 # data segment -0d 0c 0b 0a # 0x0a0b0c0d -+run: compare r/m32 with EBX -+run: effective address is 0x00002000 (EAX) -+run: SF=0; ZF=1; OF=0 +:(code) +void test_compare_r32_with_mem_at_r32_lesser() { + Reg[EAX].i = 0x2000; + Reg[EBX].i = 0x0a0b0c07; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 3b 18 \n" // compare *EAX with EBX + // ModR/M in binary: 00 (indirect mode) 011 (src EAX) 000 (dest EAX) + "== 0x2000\n" // data segment + "0d 0c 0b 0a\n" // 0x0a0b0c0d + ); + 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" + ); +} + +:(code) +void test_compare_r32_with_mem_at_r32_equal() { + Reg[EAX].i = 0x2000; + Reg[EBX].i = 0x0a0b0c0d; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 3b 18 \n" // compare *EAX with EBX + // ModR/M in binary: 00 (indirect mode) 011 (src EAX) 000 (dest EAX) + "== 0x2000\n" // data segment + "0d 0c 0b 0a\n" // 0x0a0b0c0d + ); + 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" + ); +} //:: copy (mov) -:(scenario copy_r32_to_mem_at_r32) -% Reg[EBX].i = 0xaf; -% Reg[EAX].i = 0x60; -== 0x1 -# op ModR/M SIB displacement immediate - 89 18 # copy EBX to *EAX -# ModR/M in binary: 00 (indirect mode) 011 (src EAX) 000 (dest EAX) -+run: copy EBX to r/m32 -+run: effective address is 0x00000060 (EAX) -+run: storing 0x000000af +:(code) +void test_copy_r32_to_mem_at_r32() { + Reg[EBX].i = 0xaf; + Reg[EAX].i = 0x60; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 89 18 \n" // copy EBX to *EAX + // ModR/M in binary: 00 (indirect mode) 011 (src EAX) 000 (dest EAX) + ); + CHECK_TRACE_CONTENTS( + "run: copy EBX to r/m32\n" + "run: effective address is 0x00000060 (EAX)\n" + "run: storing 0x000000af\n" + ); +} //: :(before "End Initialize Op Names") put_new(Name, "8b", "copy rm32 to r32 (mov)"); -:(scenario copy_mem_at_r32_to_r32) -% Reg[EAX].i = 0x2000; -== 0x1 # code segment -# op ModR/M SIB displacement immediate - 8b 18 # copy *EAX to EBX -# ModR/M in binary: 00 (indirect mode) 011 (src EBX) 000 (dest EAX) -== 0x2000 # data segment -af 00 00 00 # 0xaf -+run: copy r/m32 to EBX -+run: effective address is 0x00002000 (EAX) -+run: storing 0x000000af +:(code) +void test_copy_mem_at_r32_to_r32() { + Reg[EAX].i = 0x2000; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 8b 18 \n" // copy *EAX to EBX + "== 0x2000\n" // data segment + "af 00 00 00\n" // 0x000000af + ); + CHECK_TRACE_CONTENTS( + "run: copy r/m32 to EBX\n" + "run: effective address is 0x00002000 (EAX)\n" + "run: storing 0x000000af\n" + ); +} :(before "End Single-Byte Opcodes") case 0x8b: { // copy r32 to r/m32 @@ -384,22 +494,28 @@ case 0x8b: { // copy r32 to r/m32 //:: jump -:(scenario jump_mem_at_r32) -% Reg[EAX].i = 0x2000; -== 0x1 # code segment -# op ModR/M SIB displacement immediate - ff 20 # jump to *EAX -# ModR/M in binary: 00 (indirect mode) 100 (jump to r/m32) 000 (src EAX) - 05 00 00 00 01 - 05 00 00 00 02 -== 0x2000 # data segment -08 00 00 00 # 8 -+run: 0x00000001 opcode: ff -+run: jump to r/m32 -+run: effective address is 0x00002000 (EAX) -+run: jumping to 0x00000008 -+run: 0x00000008 opcode: 05 --run: 0x00000003 opcode: 05 +:(code) +void test_jump_mem_at_r32() { + Reg[EAX].i = 0x2000; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " ff 20 \n" // jump to *EAX + // ModR/M in binary: 00 (indirect mode) 100 (jump to r/m32) 000 (src EAX) + " 05 00 00 00 01\n" + " 05 00 00 00 02\n" + "== 0x2000\n" // data segment + "08 00 00 00\n" // 0x00000008 + ); + CHECK_TRACE_CONTENTS( + "run: 0x00000001 opcode: ff\n" + "run: jump to r/m32\n" + "run: effective address is 0x00002000 (EAX)\n" + "run: jumping to 0x00000008\n" + "run: 0x00000008 opcode: 05\n" + ); + CHECK_TRACE_DOESNT_CONTAIN("run: 0x00000003 opcode: 05"); +} :(before "End Op ff Subops") case 4: { // jump to r/m32 @@ -412,19 +528,24 @@ case 4: { // jump to r/m32 //:: push -:(scenario push_mem_at_r32) -% Reg[EAX].i = 0x2000; -% Reg[ESP].u = 0x14; -== 0x1 # code segment -# op ModR/M SIB displacement immediate - ff 30 # push *EAX to stack -# ModR/M in binary: 00 (indirect mode) 110 (push r/m32) 000 (src EAX) -== 0x2000 # data segment -af 00 00 00 # 0xaf -+run: push r/m32 -+run: effective address is 0x00002000 (EAX) -+run: decrementing ESP to 0x00000010 -+run: pushing value 0x000000af +:(code) +void test_push_mem_at_r32() { + Reg[EAX].i = 0x2000; + Reg[ESP].u = 0x14; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " ff 30 \n" // push *EAX to stack + "== 0x2000\n" // data segment + "af 00 00 00\n" // 0x000000af + ); + CHECK_TRACE_CONTENTS( + "run: push r/m32\n" + "run: effective address is 0x00002000 (EAX)\n" + "run: decrementing ESP to 0x00000010\n" + "run: pushing value 0x000000af\n" + ); +} :(before "End Op ff Subops") case 6: { // push r/m32 to stack @@ -439,19 +560,25 @@ case 6: { // push r/m32 to stack :(before "End Initialize Op Names") put_new(Name, "8f", "pop top of stack to rm32 (pop)"); -:(scenario pop_mem_at_r32) -% Reg[EAX].i = 0x60; -% Reg[ESP].u = 0x2000; -== 0x1 # code segment -# op ModR/M SIB displacement immediate - 8f 00 # pop stack into *EAX -# ModR/M in binary: 00 (indirect mode) 000 (pop r/m32) 000 (dest EAX) -== 0x2000 # data segment -30 00 00 00 # 0x30 -+run: pop into r/m32 -+run: effective address is 0x00000060 (EAX) -+run: popping value 0x00000030 -+run: incrementing ESP to 0x00002004 +:(code) +void test_pop_mem_at_r32() { + Reg[EAX].i = 0x60; + Reg[ESP].u = 0x2000; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 8f 00 \n" // pop stack into *EAX + // ModR/M in binary: 00 (indirect mode) 000 (pop r/m32) 000 (dest EAX) + "== 0x2000\n" // data segment + "30 00 00 00\n" // 0x00000030 + ); + CHECK_TRACE_CONTENTS( + "run: pop into r/m32\n" + "run: effective address is 0x00000060 (EAX)\n" + "run: popping value 0x00000030\n" + "run: incrementing ESP to 0x00002004\n" + ); +} :(before "End Single-Byte Opcodes") case 0x8f: { // pop stack into r/m32 @@ -470,17 +597,23 @@ case 0x8f: { // pop stack into r/m32 //:: special-case for loading address from disp32 rather than register -:(scenario add_r32_to_mem_at_displacement) -% Reg[EBX].i = 0x10; // source -== 0x1 # code segment -# op ModR/M SIB displacement immediate - 01 1d 00 20 00 00 # add EBX to *0x2000 -# ModR/M in binary: 00 (indirect mode) 011 (src EBX) 101 (dest in disp32) -== 0x2000 # data segment -01 00 00 00 # 1 -+run: add EBX to r/m32 -+run: effective address is 0x00002000 (disp32) -+run: storing 0x00000011 +:(code) +void test_add_r32_to_mem_at_displacement() { + Reg[EBX].i = 0x10; // source + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 01 1d 00 20 00 00 \n" // add EBX to *0x2000 + // ModR/M in binary: 00 (indirect mode) 011 (src EBX) 101 (dest in disp32) + "== 0x2000\n" // data segment + "01 00 00 00\n" // 0x00000001 + ); + CHECK_TRACE_CONTENTS( + "run: add EBX to r/m32\n" + "run: effective address is 0x00002000 (disp32)\n" + "run: storing 0x00000011\n" + ); +} :(before "End Mod 0 Special-cases(addr)") case 5: // exception: mod 0b00 rm 0b101 => incoming disp32 @@ -490,19 +623,25 @@ case 5: // exception: mod 0b00 rm 0b101 => incoming disp32 //: -:(scenario add_r32_to_mem_at_r32_plus_disp8) -% Reg[EBX].i = 0x10; // source -% Reg[EAX].i = 0x1ffe; // dest -== 0x1 # code segment -# op ModR/M SIB displacement immediate - 01 58 02 # add EBX to *(EAX+2) -# ModR/M in binary: 01 (indirect+disp8 mode) 011 (src EBX) 000 (dest EAX) -== 0x2000 # data segment -01 00 00 00 # 1 -+run: add EBX to r/m32 -+run: effective address is initially 0x00001ffe (EAX) -+run: effective address is 0x00002000 (after adding disp8) -+run: storing 0x00000011 +:(code) +void test_add_r32_to_mem_at_r32_plus_disp8() { + Reg[EBX].i = 0x10; // source + Reg[EAX].i = 0x1ffe; // dest + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 01 58 02 \n" // add EBX to *(EAX+2) + // ModR/M in binary: 01 (indirect+disp8 mode) 011 (src EBX) 000 (dest EAX) + "== 0x2000\n" // data segment + "01 00 00 00\n" // 0x00000001 + ); + CHECK_TRACE_CONTENTS( + "run: add EBX to r/m32\n" + "run: effective address is initially 0x00001ffe (EAX)\n" + "run: effective address is 0x00002000 (after adding disp8)\n" + "run: storing 0x00000011\n" + ); +} :(before "End Mod Special-cases(addr)") case 1: // indirect + disp8 addressing @@ -519,35 +658,47 @@ case 1: // indirect + disp8 addressing } break; -:(scenario add_r32_to_mem_at_r32_plus_negative_disp8) -% Reg[EBX].i = 0x10; // source -% Reg[EAX].i = 0x2001; // dest -== 0x1 # code segment -# op ModR/M SIB displacement immediate - 01 58 ff # add EBX to *(EAX-1) -# ModR/M in binary: 01 (indirect+disp8 mode) 011 (src EBX) 000 (dest EAX) -== 0x2000 # data segment -01 00 00 00 # 1 -+run: add EBX to r/m32 -+run: effective address is initially 0x00002001 (EAX) -+run: effective address is 0x00002000 (after adding disp8) -+run: storing 0x00000011 +:(code) +void test_add_r32_to_mem_at_r32_plus_negative_disp8() { + Reg[EBX].i = 0x10; // source + Reg[EAX].i = 0x2001; // dest + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 01 58 ff \n" // add EBX to *(EAX-1) + // ModR/M in binary: 01 (indirect+disp8 mode) 011 (src EBX) 000 (dest EAX) + "== 0x2000\n" // data segment + "01 00 00 00\n" // 0x00000001 + ); + CHECK_TRACE_CONTENTS( + "run: add EBX to r/m32\n" + "run: effective address is initially 0x00002001 (EAX)\n" + "run: effective address is 0x00002000 (after adding disp8)\n" + "run: storing 0x00000011\n" + ); +} //: -:(scenario add_r32_to_mem_at_r32_plus_disp32) -% Reg[EBX].i = 0x10; // source -% Reg[EAX].i = 0x1ffe; // dest -== 0x1 # code segment -# op ModR/M SIB displacement immediate - 01 98 02 00 00 00 # add EBX to *(EAX+2) -# ModR/M in binary: 10 (indirect+disp32 mode) 011 (src EBX) 000 (dest EAX) -== 0x2000 # data segment -01 00 00 00 # 1 -+run: add EBX to r/m32 -+run: effective address is initially 0x00001ffe (EAX) -+run: effective address is 0x00002000 (after adding disp32) -+run: storing 0x00000011 +:(code) +void test_add_r32_to_mem_at_r32_plus_disp32() { + Reg[EBX].i = 0x10; // source + Reg[EAX].i = 0x1ffe; // dest + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 01 98 02 00 00 00 \n" // add EBX to *(EAX+2) + // ModR/M in binary: 10 (indirect+disp32 mode) 011 (src EBX) 000 (dest EAX) + "== 0x2000\n" // data segment + "01 00 00 00\n" // 0x00000001 + ); + CHECK_TRACE_CONTENTS( + "run: add EBX to r/m32\n" + "run: effective address is initially 0x00001ffe (EAX)\n" + "run: effective address is 0x00002000 (after adding disp32)\n" + "run: storing 0x00000011\n" + ); +} :(before "End Mod Special-cases(addr)") case 2: // indirect + disp32 addressing @@ -564,33 +715,45 @@ case 2: // indirect + disp32 addressing } break; -:(scenario add_r32_to_mem_at_r32_plus_negative_disp32) -% Reg[EBX].i = 0x10; // source -% Reg[EAX].i = 0x2001; // dest -== 0x1 # code segment -# op ModR/M SIB displacement immediate - 01 98 ff ff ff ff # add EBX to *(EAX-1) -# ModR/M in binary: 10 (indirect+disp32 mode) 011 (src EBX) 000 (dest EAX) -== 0x2000 # data segment -01 00 00 00 # 1 -+run: add EBX to r/m32 -+run: effective address is initially 0x00002001 (EAX) -+run: effective address is 0x00002000 (after adding disp32) -+run: storing 0x00000011 +:(code) +void test_add_r32_to_mem_at_r32_plus_negative_disp32() { + Reg[EBX].i = 0x10; // source + Reg[EAX].i = 0x2001; // dest + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 01 98 ff ff ff ff \n" // add EBX to *(EAX-1) + // ModR/M in binary: 10 (indirect+disp32 mode) 011 (src EBX) 000 (dest EAX) + "== 0x2000\n" // data segment + "01 00 00 00\n" // 0x00000001 + ); + CHECK_TRACE_CONTENTS( + "run: add EBX to r/m32\n" + "run: effective address is initially 0x00002001 (EAX)\n" + "run: effective address is 0x00002000 (after adding disp32)\n" + "run: storing 0x00000011\n" + ); +} //:: copy address (lea) :(before "End Initialize Op Names") put_new(Name, "8d", "copy address in rm32 into r32 (lea)"); -:(scenario copy_address) -% Reg[EAX].u = 0x2000; -== 0x1 -# op ModR/M SIB displacement immediate - 8d 18 -# ModR/M in binary: 00 (indirect mode) 011 (dest EBX) 000 (src EAX) -+run: copy address into EBX -+run: effective address is 0x00002000 (EAX) +:(code) +void test_copy_address() { + Reg[EAX].u = 0x2000; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 8d 18 \n" // copy address in EAX into EBX + // ModR/M in binary: 00 (indirect mode) 011 (dest EBX) 000 (src EAX) + ); + CHECK_TRACE_CONTENTS( + "run: copy address into EBX\n" + "run: effective address is 0x00002000 (EAX)\n" + ); +} :(before "End Single-Byte Opcodes") case 0x8d: { // copy address of m32 to r32 diff --git a/subx/015immediate_addressing.cc b/subx/015immediate_addressing.cc index 93dd699b..18cd5334 100644 --- a/subx/015immediate_addressing.cc +++ b/subx/015immediate_addressing.cc @@ -3,17 +3,23 @@ :(before "End Initialize Op Names") put_new(Name, "81", "combine rm32 with imm32 based on subop (add/sub/and/or/xor/cmp)"); -:(scenario add_imm32_to_r32) -% Reg[EBX].i = 1; -== 0x1 -# op ModR/M SIB displacement immediate - 81 c3 0a 0b 0c 0d # add 0x0d0c0b0a to EBX -# ModR/M in binary: 11 (direct mode) 000 (add imm32) 011 (dest EBX) -+run: combine imm32 with r/m32 -+run: r/m32 is EBX -+run: imm32 is 0x0d0c0b0a -+run: subop add -+run: storing 0x0d0c0b0b +:(code) +void test_add_imm32_to_r32() { + Reg[EBX].i = 1; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 81 c3 0a 0b 0c 0d\n" // add 0x0d0c0b0a to EBX + // ModR/M in binary: 11 (direct mode) 000 (add imm32) 011 (dest EBX) + ); + CHECK_TRACE_CONTENTS( + "run: combine imm32 with r/m32\n" + "run: r/m32 is EBX\n" + "run: imm32 is 0x0d0c0b0a\n" + "run: subop add\n" + "run: storing 0x0d0c0b0b\n" + ); +} :(before "End Single-Byte Opcodes") case 0x81: { // combine imm32 with r/m32 @@ -38,32 +44,44 @@ case 0x81: { // combine imm32 with r/m32 //: -:(scenario add_imm32_to_mem_at_r32) -% Reg[EBX].i = 0x2000; -== 0x01 # code segment -# op ModR/M SIB displacement immediate - 81 03 0a 0b 0c 0d # add 0x0d0c0b0a to *EBX -# ModR/M in binary: 00 (indirect mode) 000 (add imm32) 011 (dest EBX) -== 0x2000 # data segment -01 00 00 00 # 1 -+run: combine imm32 with r/m32 -+run: effective address is 0x00002000 (EBX) -+run: imm32 is 0x0d0c0b0a -+run: subop add -+run: storing 0x0d0c0b0b +:(code) +void test_add_imm32_to_mem_at_r32() { + Reg[EBX].i = 0x2000; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 81 03 0a 0b 0c 0d \n" // add 0x0d0c0b0a to *EBX + // ModR/M in binary: 00 (indirect mode) 000 (add imm32) 011 (dest EBX) + "== 0x2000\n" // data segment + "01 00 00 00\n" // 0x00000001 + ); + CHECK_TRACE_CONTENTS( + "run: combine imm32 with r/m32\n" + "run: effective address is 0x00002000 (EBX)\n" + "run: imm32 is 0x0d0c0b0a\n" + "run: subop add\n" + "run: storing 0x0d0c0b0b\n" + ); +} //:: subtract :(before "End Initialize Op Names") put_new(Name, "2d", "subtract imm32 from EAX (sub)"); -:(scenario subtract_imm32_from_eax) -% Reg[EAX].i = 0x0d0c0baa; -== 0x1 -# op ModR/M SIB displacement immediate - 2d 0a 0b 0c 0d # subtract 0x0d0c0b0a from EAX -+run: subtract imm32 0x0d0c0b0a from EAX -+run: storing 0x000000a0 +:(code) +void test_subtract_imm32_from_eax() { + Reg[EAX].i = 0x0d0c0baa; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 2d 0a 0b 0c 0d \n" // subtract 0x0d0c0b0a from EAX + ); + CHECK_TRACE_CONTENTS( + "run: subtract imm32 0x0d0c0b0a from EAX\n" + "run: storing 0x000000a0\n" + ); +} :(before "End Single-Byte Opcodes") case 0x2d: { // subtract imm32 from EAX @@ -75,19 +93,25 @@ case 0x2d: { // subtract imm32 from EAX //: -:(scenario subtract_imm32_from_mem_at_r32) -% Reg[EBX].i = 0x2000; -== 0x01 # code segment -# op ModR/M SIB displacement immediate - 81 2b 01 00 00 00 # subtract 1 from *EBX -# ModR/M in binary: 00 (indirect mode) 101 (subtract imm32) 011 (dest EBX) -== 0x2000 # data segment -0a 00 00 00 # 10 -+run: combine imm32 with r/m32 -+run: effective address is 0x00002000 (EBX) -+run: imm32 is 0x00000001 -+run: subop subtract -+run: storing 0x00000009 +:(code) +void test_subtract_imm32_from_mem_at_r32() { + Reg[EBX].i = 0x2000; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 81 2b 01 00 00 00 \n" // subtract 1 from *EBX + // ModR/M in binary: 00 (indirect mode) 101 (subtract imm32) 011 (dest EBX) + "== 0x2000\n" // data segment + "0a 00 00 00\n" // 0x0000000a + ); + CHECK_TRACE_CONTENTS( + "run: combine imm32 with r/m32\n" + "run: effective address is 0x00002000 (EBX)\n" + "run: imm32 is 0x00000001\n" + "run: subop subtract\n" + "run: storing 0x00000009\n" + ); +} :(before "End Op 81 Subops") case 5: { @@ -98,33 +122,45 @@ case 5: { //: -:(scenario subtract_imm32_from_r32) -% Reg[EBX].i = 10; -== 0x1 -# op ModR/M SIB displacement immediate - 81 eb 01 00 00 00 # subtract 1 from EBX -# ModR/M in binary: 11 (direct mode) 101 (subtract imm32) 011 (dest EBX) -+run: combine imm32 with r/m32 -+run: r/m32 is EBX -+run: imm32 is 0x00000001 -+run: subop subtract -+run: storing 0x00000009 +:(code) +void test_subtract_imm32_from_r32() { + Reg[EBX].i = 10; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 81 eb 01 00 00 00 \n" // subtract 1 from EBX + // ModR/M in binary: 11 (direct mode) 101 (subtract imm32) 011 (dest EBX) + ); + CHECK_TRACE_CONTENTS( + "run: combine imm32 with r/m32\n" + "run: r/m32 is EBX\n" + "run: imm32 is 0x00000001\n" + "run: subop subtract\n" + "run: storing 0x00000009\n" + ); +} //:: shift left :(before "End Initialize Op Names") put_new(Name, "c1", "shift rm32 by imm8 bits depending on subop (sal/sar/shl/shr)"); -:(scenario shift_left_r32_with_imm8) -% Reg[EBX].i = 13; -== 0x1 -# op ModR/M SIB displacement immediate - c1 e3 01 # negate EBX -# ModR/M in binary: 11 (direct mode) 100 (subop shift left) 011 (dest EBX) -+run: operate on r/m32 -+run: r/m32 is EBX -+run: subop: shift left by CL bits -+run: storing 0x0000001a +:(code) +void test_shift_left_r32_with_imm8() { + Reg[EBX].i = 13; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " c1 e3 01 \n" // shift EBX left by 1 bit + // ModR/M in binary: 11 (direct mode) 100 (subop shift left) 011 (dest EBX) + ); + CHECK_TRACE_CONTENTS( + "run: operate on r/m32\n" + "run: r/m32 is EBX\n" + "run: subop: shift left by CL bits\n" + "run: storing 0x0000001a\n" + ); +} :(before "End Single-Byte Opcodes") case 0xc1: { @@ -158,16 +194,22 @@ case 0xc1: { //:: shift right arithmetic -:(scenario shift_right_arithmetic_r32_with_imm8) -% Reg[EBX].i = 26; -== 0x1 -# op ModR/M SIB displacement immediate - c1 fb 01 # negate EBX -# ModR/M in binary: 11 (direct mode) 111 (subop shift right arithmetic) 011 (dest EBX) -+run: operate on r/m32 -+run: r/m32 is EBX -+run: subop: shift right by CL bits, while preserving sign -+run: storing 0x0000000d +:(code) +void test_shift_right_arithmetic_r32_with_imm8() { + Reg[EBX].i = 26; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " c1 fb 01 \n" // shift EBX right by 1 bit + // ModR/M in binary: 11 (direct mode) 111 (subop shift right arithmetic) 011 (dest EBX) + ); + CHECK_TRACE_CONTENTS( + "run: operate on r/m32\n" + "run: r/m32 is EBX\n" + "run: subop: shift right by CL bits, while preserving sign\n" + "run: storing 0x0000000d\n" + ); +} :(before "End Op c1 Subops") case 7: { // shift right r/m32 by CL, preserving sign @@ -182,42 +224,60 @@ case 7: { // shift right r/m32 by CL, preserving sign break; } -:(scenario shift_right_arithmetic_odd_r32_with_imm8) -% Reg[EBX].i = 27; -== 0x1 -# op ModR/M SIB displacement immediate - c1 fb 01 # negate EBX -# ModR/M in binary: 11 (direct mode) 111 (subop shift right arithmetic) 011 (dest EBX) -+run: operate on r/m32 -+run: r/m32 is EBX -+run: subop: shift right by CL bits, while preserving sign -# result: 13 -+run: storing 0x0000000d - -:(scenario shift_right_arithmetic_negative_r32_with_imm8) -% Reg[EBX].i = 0xfffffffd; // -3 -== 0x1 -# op ModR/M SIB displacement immediate - c1 fb 01 # negate EBX -# ModR/M in binary: 11 (direct mode) 111 (subop shift right arithmetic) 011 (dest EBX) -+run: operate on r/m32 -+run: r/m32 is EBX -+run: subop: shift right by CL bits, while preserving sign -# result: -2 -+run: storing 0xfffffffe +:(code) +void test_shift_right_arithmetic_odd_r32_with_imm8() { + Reg[EBX].i = 27; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " c1 fb 01 \n" // shift EBX right by 1 bit + // ModR/M in binary: 11 (direct mode) 111 (subop shift right arithmetic) 011 (dest EBX) + ); + CHECK_TRACE_CONTENTS( + "run: operate on r/m32\n" + "run: r/m32 is EBX\n" + "run: subop: shift right by CL bits, while preserving sign\n" + // result: 13 + "run: storing 0x0000000d\n" + ); +} + +:(code) +void test_shift_right_arithmetic_negative_r32_with_imm8() { + Reg[EBX].i = 0xfffffffd; // -3 + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " c1 fb 01 \n" // shift EBX right by 1 bit, while preserving sign + // ModR/M in binary: 11 (direct mode) 111 (subop shift right arithmetic) 011 (dest EBX) + ); + CHECK_TRACE_CONTENTS( + "run: operate on r/m32\n" + "run: r/m32 is EBX\n" + "run: subop: shift right by CL bits, while preserving sign\n" + // result: -2 + "run: storing 0xfffffffe\n" + ); +} //:: shift right logical -:(scenario shift_right_logical_r32_with_imm8) -% Reg[EBX].i = 26; -== 0x1 -# op ModR/M SIB displacement immediate - c1 eb 01 # negate EBX -# ModR/M in binary: 11 (direct mode) 101 (subop shift right logical) 011 (dest EBX) -+run: operate on r/m32 -+run: r/m32 is EBX -+run: subop: shift right by CL bits, while padding zeroes -+run: storing 0x0000000d +:(code) +void test_shift_right_logical_r32_with_imm8() { + Reg[EBX].i = 26; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " c1 eb 01 \n" // shift EBX right by 1 bit, while padding zeroes + // ModR/M in binary: 11 (direct mode) 101 (subop shift right logical) 011 (dest EBX) + ); + CHECK_TRACE_CONTENTS( + "run: operate on r/m32\n" + "run: r/m32 is EBX\n" + "run: subop: shift right by CL bits, while padding zeroes\n" + "run: storing 0x0000000d\n" + ); +} :(before "End Op c1 Subops") case 5: { // shift right r/m32 by CL, preserving sign @@ -238,41 +298,58 @@ case 5: { // shift right r/m32 by CL, preserving sign break; } -:(scenario shift_right_logical_odd_r32_with_imm8) -% Reg[EBX].i = 27; -== 0x1 -# op ModR/M SIB displacement immediate - c1 eb 01 # negate EBX -# ModR/M in binary: 11 (direct mode) 101 (subop shift right logical) 011 (dest EBX) -+run: operate on r/m32 -+run: r/m32 is EBX -+run: subop: shift right by CL bits, while padding zeroes -# result: 13 -+run: storing 0x0000000d - -:(scenario shift_right_logical_negative_r32_with_imm8) -% Reg[EBX].i = 0xfffffffd; -== 0x1 -# op ModR/M SIB displacement immediate - c1 eb 01 # negate EBX -# ModR/M in binary: 11 (direct mode) 101 (subop shift right logical) 011 (dest EBX) -+run: operate on r/m32 -+run: r/m32 is EBX -+run: subop: shift right by CL bits, while padding zeroes -+run: storing 0x7ffffffe +:(code) +void test_shift_right_logical_odd_r32_with_imm8() { + Reg[EBX].i = 27; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " c1 eb 01 \n" // shift EBX right by 1 bit, while padding zeroes + ); + CHECK_TRACE_CONTENTS( + "run: operate on r/m32\n" + "run: r/m32 is EBX\n" + "run: subop: shift right by CL bits, while padding zeroes\n" + // result: 13 + "run: storing 0x0000000d\n" + ); +} + +:(code) +void test_shift_right_logical_negative_r32_with_imm8() { + Reg[EBX].i = 0xfffffffd; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " c1 eb 01 \n" // shift EBX right by 1 bit, while padding zeroes + // ModR/M in binary: 11 (direct mode) 101 (subop shift right logical) 011 (dest EBX) + ); + CHECK_TRACE_CONTENTS( + "run: operate on r/m32\n" + "run: r/m32 is EBX\n" + "run: subop: shift right by CL bits, while padding zeroes\n" + "run: storing 0x7ffffffe\n" + ); +} //:: and :(before "End Initialize Op Names") put_new(Name, "25", "EAX = bitwise AND of imm32 with EAX (and)"); -:(scenario and_imm32_with_eax) -% Reg[EAX].i = 0xff; -== 0x1 -# op ModR/M SIB displacement immediate - 25 0a 0b 0c 0d # and 0x0d0c0b0a with EAX -+run: and imm32 0x0d0c0b0a with EAX -+run: storing 0x0000000a +:(code) +void test_and_imm32_with_eax() { + Reg[EAX].i = 0xff; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 25 0a 0b 0c 0d \n" // and 0x0d0c0b0a with EAX + ); + CHECK_TRACE_CONTENTS( + "run: and imm32 0x0d0c0b0a with EAX\n" + "run: storing 0x0000000a\n" + ); +} :(before "End Single-Byte Opcodes") case 0x25: { // and imm32 with EAX @@ -284,19 +361,25 @@ case 0x25: { // and imm32 with EAX //: -:(scenario and_imm32_with_mem_at_r32) -% Reg[EBX].i = 0x2000; -== 0x01 # code segment -# op ModR/M SIB displacement immediate - 81 23 0a 0b 0c 0d # and 0x0d0c0b0a with *EBX -# ModR/M in binary: 00 (indirect mode) 100 (and imm32) 011 (dest EBX) -== 0x2000 # data segment -ff 00 00 00 # 0xff -+run: combine imm32 with r/m32 -+run: effective address is 0x00002000 (EBX) -+run: imm32 is 0x0d0c0b0a -+run: subop and -+run: storing 0x0000000a +:(code) +void test_and_imm32_with_mem_at_r32() { + Reg[EBX].i = 0x2000; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 81 23 0a 0b 0c 0d \n" // and 0x0d0c0b0a with *EBX + // ModR/M in binary: 00 (indirect mode) 100 (and imm32) 011 (dest EBX) + "== 0x2000\n" // data segment + "ff 00 00 00\n" // 0x000000ff + ); + CHECK_TRACE_CONTENTS( + "run: combine imm32 with r/m32\n" + "run: effective address is 0x00002000 (EBX)\n" + "run: imm32 is 0x0d0c0b0a\n" + "run: subop and\n" + "run: storing 0x0000000a\n" + ); +} :(before "End Op 81 Subops") case 4: { @@ -307,30 +390,42 @@ case 4: { //: -:(scenario and_imm32_with_r32) -% Reg[EBX].i = 0xff; -== 0x1 -# op ModR/M SIB displacement immediate - 81 e3 0a 0b 0c 0d # and 0x0d0c0b0a with EBX -# ModR/M in binary: 11 (direct mode) 100 (and imm32) 011 (dest EBX) -+run: combine imm32 with r/m32 -+run: r/m32 is EBX -+run: imm32 is 0x0d0c0b0a -+run: subop and -+run: storing 0x0000000a +:(code) +void test_and_imm32_with_r32() { + Reg[EBX].i = 0xff; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 81 e3 0a 0b 0c 0d \n" // and 0x0d0c0b0a with EBX + // ModR/M in binary: 11 (direct mode) 100 (and imm32) 011 (dest EBX) + ); + CHECK_TRACE_CONTENTS( + "run: combine imm32 with r/m32\n" + "run: r/m32 is EBX\n" + "run: imm32 is 0x0d0c0b0a\n" + "run: subop and\n" + "run: storing 0x0000000a\n" + ); +} //:: or :(before "End Initialize Op Names") put_new(Name, "0d", "EAX = bitwise OR of imm32 with EAX (or)"); -:(scenario or_imm32_with_eax) -% Reg[EAX].i = 0xd0c0b0a0; -== 0x1 -# op ModR/M SIB displacement immediate - 0d 0a 0b 0c 0d # or 0x0d0c0b0a with EAX -+run: or imm32 0x0d0c0b0a with EAX -+run: storing 0xddccbbaa +:(code) +void test_or_imm32_with_eax() { + Reg[EAX].i = 0xd0c0b0a0; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 0d 0a 0b 0c 0d \n" // or 0x0d0c0b0a with EAX + ); + CHECK_TRACE_CONTENTS( + "run: or imm32 0x0d0c0b0a with EAX\n" + "run: storing 0xddccbbaa\n" + ); +} :(before "End Single-Byte Opcodes") case 0x0d: { // or imm32 with EAX @@ -342,19 +437,25 @@ case 0x0d: { // or imm32 with EAX //: -:(scenario or_imm32_with_mem_at_r32) -% Reg[EBX].i = 0x2000; -== 0x01 # code segment -# op ModR/M SIB displacement immediate - 81 0b 0a 0b 0c 0d # or 0x0d0c0b0a with *EBX -# ModR/M in binary: 00 (indirect mode) 001 (or imm32) 011 (dest EBX) -== 0x2000 # data segment -a0 b0 c0 d0 # 0xd0c0b0a0 -+run: combine imm32 with r/m32 -+run: effective address is 0x00002000 (EBX) -+run: imm32 is 0x0d0c0b0a -+run: subop or -+run: storing 0xddccbbaa +:(code) +void test_or_imm32_with_mem_at_r32() { + Reg[EBX].i = 0x2000; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 81 0b 0a 0b 0c 0d \n" // or 0x0d0c0b0a with *EBX + // ModR/M in binary: 00 (indirect mode) 001 (or imm32) 011 (dest EBX) + "== 0x2000\n" // data segment + "a0 b0 c0 d0\n" // 0xd0c0b0a0 + ); + CHECK_TRACE_CONTENTS( + "run: combine imm32 with r/m32\n" + "run: effective address is 0x00002000 (EBX)\n" + "run: imm32 is 0x0d0c0b0a\n" + "run: subop or\n" + "run: storing 0xddccbbaa\n" + ); +} :(before "End Op 81 Subops") case 1: { @@ -363,30 +464,42 @@ case 1: { break; } -:(scenario or_imm32_with_r32) -% Reg[EBX].i = 0xd0c0b0a0; -== 0x1 -# op ModR/M SIB displacement immediate - 81 cb 0a 0b 0c 0d # or 0x0d0c0b0a with EBX -# ModR/M in binary: 11 (direct mode) 001 (or imm32) 011 (dest EBX) -+run: combine imm32 with r/m32 -+run: r/m32 is EBX -+run: imm32 is 0x0d0c0b0a -+run: subop or -+run: storing 0xddccbbaa +:(code) +void test_or_imm32_with_r32() { + Reg[EBX].i = 0xd0c0b0a0; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 81 cb 0a 0b 0c 0d \n" // or 0x0d0c0b0a with EBX + // ModR/M in binary: 11 (direct mode) 001 (or imm32) 011 (dest EBX) + ); + CHECK_TRACE_CONTENTS( + "run: combine imm32 with r/m32\n" + "run: r/m32 is EBX\n" + "run: imm32 is 0x0d0c0b0a\n" + "run: subop or\n" + "run: storing 0xddccbbaa\n" + ); +} //:: xor :(before "End Initialize Op Names") put_new(Name, "35", "EAX = bitwise XOR of imm32 with EAX (xor)"); -:(scenario xor_imm32_with_eax) -% Reg[EAX].i = 0xddccb0a0; -== 0x1 -# op ModR/M SIB displacement immediate - 35 0a 0b 0c 0d # xor 0x0d0c0b0a with EAX -+run: xor imm32 0x0d0c0b0a with EAX -+run: storing 0xd0c0bbaa +:(code) +void test_xor_imm32_with_eax() { + Reg[EAX].i = 0xddccb0a0; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 35 0a 0b 0c 0d \n" // xor 0x0d0c0b0a with EAX + ); + CHECK_TRACE_CONTENTS( + "run: xor imm32 0x0d0c0b0a with EAX\n" + "run: storing 0xd0c0bbaa\n" + ); +} :(before "End Single-Byte Opcodes") case 0x35: { // xor imm32 with EAX @@ -398,19 +511,25 @@ case 0x35: { // xor imm32 with EAX //: -:(scenario xor_imm32_with_mem_at_r32) -% Reg[EBX].i = 0x2000; -== 0x01 # code segment -# op ModR/M SIB displacement immediate - 81 33 0a 0b 0c 0d # xor 0x0d0c0b0a with *EBX -# ModR/M in binary: 00 (indirect mode) 110 (xor imm32) 011 (dest EBX) -== 0x2000 # data segment -a0 b0 c0 d0 # 0xd0c0b0a0 -+run: combine imm32 with r/m32 -+run: effective address is 0x00002000 (EBX) -+run: imm32 is 0x0d0c0b0a -+run: subop xor -+run: storing 0xddccbbaa +:(code) +void test_xor_imm32_with_mem_at_r32() { + Reg[EBX].i = 0x2000; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 81 33 0a 0b 0c 0d \n" // xor 0x0d0c0b0a with *EBX + // ModR/M in binary: 00 (indirect mode) 110 (xor imm32) 011 (dest EBX) + "== 0x2000\n" // data segment + "a0 b0 c0 d0\n" // 0xd0c0b0a0 + ); + CHECK_TRACE_CONTENTS( + "run: combine imm32 with r/m32\n" + "run: effective address is 0x00002000 (EBX)\n" + "run: imm32 is 0x0d0c0b0a\n" + "run: subop xor\n" + "run: storing 0xddccbbaa\n" + ); +} :(before "End Op 81 Subops") case 6: { @@ -419,30 +538,42 @@ case 6: { break; } -:(scenario xor_imm32_with_r32) -% Reg[EBX].i = 0xd0c0b0a0; -== 0x1 -# op ModR/M SIB displacement immediate - 81 f3 0a 0b 0c 0d # xor 0x0d0c0b0a with EBX -# ModR/M in binary: 11 (direct mode) 110 (xor imm32) 011 (dest EBX) -+run: combine imm32 with r/m32 -+run: r/m32 is EBX -+run: imm32 is 0x0d0c0b0a -+run: subop xor -+run: storing 0xddccbbaa +:(code) +void test_xor_imm32_with_r32() { + Reg[EBX].i = 0xd0c0b0a0; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 81 f3 0a 0b 0c 0d \n" // xor 0x0d0c0b0a with EBX + // ModR/M in binary: 11 (direct mode) 110 (xor imm32) 011 (dest EBX) + ); + CHECK_TRACE_CONTENTS( + "run: combine imm32 with r/m32\n" + "run: r/m32 is EBX\n" + "run: imm32 is 0x0d0c0b0a\n" + "run: subop xor\n" + "run: storing 0xddccbbaa\n" + ); +} //:: compare (cmp) :(before "End Initialize Op Names") put_new(Name, "3d", "compare: set SF if EAX < imm32 (cmp)"); -:(scenario compare_imm32_with_eax_greater) -% Reg[EAX].i = 0x0d0c0b0a; -== 0x1 -# op ModR/M SIB displacement immediate - 3d 07 0b 0c 0d # compare 0x0d0c0b07 with EAX -+run: compare EAX and imm32 0x0d0c0b07 -+run: SF=0; ZF=0; OF=0 +:(code) +void test_compare_imm32_with_eax_greater() { + Reg[EAX].i = 0x0d0c0b0a; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 3d 07 0b 0c 0d \n" // compare 0x0d0c0b07 with EAX + ); + CHECK_TRACE_CONTENTS( + "run: compare EAX and imm32 0x0d0c0b07\n" + "run: SF=0; ZF=0; OF=0\n" + ); +} :(before "End Single-Byte Opcodes") case 0x3d: { // compare EAX with imm32 @@ -458,34 +589,52 @@ case 0x3d: { // compare EAX with imm32 break; } -:(scenario compare_imm32_with_eax_lesser) -% Reg[EAX].i = 0x0d0c0b07; -== 0x1 -# op ModR/M SIB displacement immediate - 3d 0a 0b 0c 0d # compare 0x0d0c0b0a with EAX -+run: compare EAX and imm32 0x0d0c0b0a -+run: SF=1; ZF=0; OF=0 +:(code) +void test_compare_imm32_with_eax_lesser() { + Reg[EAX].i = 0x0d0c0b07; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 3d 0a 0b 0c 0d \n" // compare 0x0d0c0b0a with EAX + ); + CHECK_TRACE_CONTENTS( + "run: compare EAX and imm32 0x0d0c0b0a\n" + "run: SF=1; ZF=0; OF=0\n" + ); +} -:(scenario compare_imm32_with_eax_equal) -% Reg[EAX].i = 0x0d0c0b0a; -== 0x1 -# op ModR/M SIB displacement immediate - 3d 0a 0b 0c 0d # compare 0x0d0c0b0a with EAX -+run: compare EAX and imm32 0x0d0c0b0a -+run: SF=0; ZF=1; OF=0 +:(code) +void test_compare_imm32_with_eax_equal() { + Reg[EAX].i = 0x0d0c0b0a; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 3d 0a 0b 0c 0d \n" // compare 0x0d0c0b0a with EAX + ); + CHECK_TRACE_CONTENTS( + "run: compare EAX and imm32 0x0d0c0b0a\n" + "run: SF=0; ZF=1; OF=0\n" + ); +} //: -:(scenario compare_imm32_with_r32_greater) -% Reg[EBX].i = 0x0d0c0b0a; -== 0x1 -# op ModR/M SIB displacement immediate - 81 fb 07 0b 0c 0d # compare 0x0d0c0b07 with EBX -# ModR/M in binary: 11 (direct mode) 111 (compare imm32) 011 (dest EBX) -+run: combine imm32 with r/m32 -+run: r/m32 is EBX -+run: imm32 is 0x0d0c0b07 -+run: SF=0; ZF=0; OF=0 +:(code) +void test_compare_imm32_with_r32_greater() { + Reg[EBX].i = 0x0d0c0b0a; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 81 fb 07 0b 0c 0d \n" // compare 0x0d0c0b07 with EBX + // ModR/M in binary: 11 (direct mode) 111 (compare imm32) 011 (dest EBX) + ); + CHECK_TRACE_CONTENTS( + "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" + ); +} :(before "End Op 81 Subops") case 7: { @@ -499,67 +648,97 @@ case 7: { break; } -:(scenario compare_imm32_with_r32_lesser) -% Reg[EBX].i = 0x0d0c0b07; -== 0x1 -# op ModR/M SIB displacement immediate - 81 fb 0a 0b 0c 0d # compare 0x0d0c0b0a with EBX -# ModR/M in binary: 11 (direct mode) 111 (compare imm32) 011 (dest EBX) -+run: combine imm32 with r/m32 -+run: r/m32 is EBX -+run: imm32 is 0x0d0c0b0a -+run: SF=1; ZF=0; OF=0 - -:(scenario compare_imm32_with_r32_equal) -% Reg[EBX].i = 0x0d0c0b0a; -== 0x1 -# op ModR/M SIB displacement immediate - 81 fb 0a 0b 0c 0d # compare 0x0d0c0b0a with EBX -# ModR/M in binary: 11 (direct mode) 111 (compare imm32) 011 (dest EBX) -+run: combine imm32 with r/m32 -+run: r/m32 is EBX -+run: imm32 is 0x0d0c0b0a -+run: SF=0; ZF=1; OF=0 - -:(scenario compare_imm32_with_mem_at_r32_greater) -% Reg[EBX].i = 0x2000; -== 0x01 # code segment -# op ModR/M SIB displacement immediate - 81 3b 07 0b 0c 0d # compare 0x0d0c0b07 with *EBX -# ModR/M in binary: 00 (indirect mode) 111 (compare imm32) 011 (dest EBX) -== 0x2000 # data segment -0a 0b 0c 0d # 0x0d0c0b0a -+run: combine imm32 with r/m32 -+run: effective address is 0x00002000 (EBX) -+run: imm32 is 0x0d0c0b07 -+run: SF=0; ZF=0; OF=0 - -:(scenario compare_imm32_with_mem_at_r32_lesser) -% Reg[EBX].i = 0x2000; -== 0x01 # code segment -# op ModR/M SIB displacement immediate - 81 3b 0a 0b 0c 0d # compare 0x0d0c0b0a with *EBX -# ModR/M in binary: 00 (indirect mode) 111 (compare imm32) 011 (dest EBX) -== 0x2000 # data segment -07 0b 0c 0d # 0x0d0c0b07 -+run: combine imm32 with r/m32 -+run: effective address is 0x00002000 (EBX) -+run: imm32 is 0x0d0c0b0a -+run: SF=1; ZF=0; OF=0 - -:(scenario compare_imm32_with_mem_at_r32_equal) -% Reg[EBX].i = 0x0d0c0b0a; -% Reg[EBX].i = 0x2000; -== 0x01 # code segment -# op ModR/M SIB displacement immediate - 81 3b 0a 0b 0c 0d # compare 0x0d0c0b0a with *EBX -# ModR/M in binary: 00 (indirect mode) 111 (compare imm32) 011 (dest EBX) -== 0x2000 # data segment -0a 0b 0c 0d # 0x0d0c0b0a -+run: combine imm32 with r/m32 -+run: effective address is 0x00002000 (EBX) -+run: imm32 is 0x0d0c0b0a -+run: SF=0; ZF=1; OF=0 +:(code) +void test_compare_imm32_with_r32_lesser() { + Reg[EBX].i = 0x0d0c0b07; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 81 fb 0a 0b 0c 0d \n" // compare 0x0d0c0b0a with EBX + // ModR/M in binary: 11 (direct mode) 111 (compare imm32) 011 (dest EBX) + ); + CHECK_TRACE_CONTENTS( + "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" + ); +} + +:(code) +void test_compare_imm32_with_r32_equal() { + Reg[EBX].i = 0x0d0c0b0a; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 81 fb 0a 0b 0c 0d \n" // compare 0x0d0c0b0a with EBX + // ModR/M in binary: 11 (direct mode) 111 (compare imm32) 011 (dest EBX) + ); + CHECK_TRACE_CONTENTS( + "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" + ); +} + +:(code) +void test_compare_imm32_with_mem_at_r32_greater() { + Reg[EBX].i = 0x2000; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 81 3b 07 0b 0c 0d \n" // compare 0x0d0c0b07 with *EBX + // ModR/M in binary: 00 (indirect mode) 111 (compare imm32) 011 (dest EBX) + "== 0x2000\n" // data segment + "0a 0b 0c 0d\n" // 0x0d0c0b0a + ); + CHECK_TRACE_CONTENTS( + "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" + ); +} + +:(code) +void test_compare_imm32_with_mem_at_r32_lesser() { + Reg[EBX].i = 0x2000; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 81 3b 0a 0b 0c 0d \n" // compare 0x0d0c0b0a with *EBX + // ModR/M in binary: 00 (indirect mode) 111 (compare imm32) 011 (dest EBX) + "== 0x2000\n" // data segment + "07 0b 0c 0d\n" // 0x0d0c0b07 + ); + CHECK_TRACE_CONTENTS( + "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" + ); +} + +:(code) +void test_compare_imm32_with_mem_at_r32_equal() { + Reg[EBX].i = 0x0d0c0b0a; + Reg[EBX].i = 0x2000; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 81 3b 0a 0b 0c 0d \n" // compare 0x0d0c0b0a with *EBX + // ModR/M in binary: 00 (indirect mode) 111 (compare imm32) 011 (dest EBX) + "== 0x2000\n" // data segment + "0a 0b 0c 0d\n" // 0x0d0c0b0a + ); + CHECK_TRACE_CONTENTS( + "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" + ); +} //:: copy (mov) @@ -573,11 +752,17 @@ put_new(Name, "bd", "copy imm32 to EBP (mov)"); put_new(Name, "be", "copy imm32 to ESI (mov)"); put_new(Name, "bf", "copy imm32 to EDI (mov)"); -:(scenario copy_imm32_to_r32) -== 0x1 -# op ModR/M SIB displacement immediate - bb 0a 0b 0c 0d # copy 0x0d0c0b0a to EBX -+run: copy imm32 0x0d0c0b0a to EBX +:(code) +void test_copy_imm32_to_r32() { + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " bb 0a 0b 0c 0d \n" // copy 0x0d0c0b0a to EBX + ); + CHECK_TRACE_CONTENTS( + "run: copy imm32 0x0d0c0b0a to EBX\n" + ); +} :(before "End Single-Byte Opcodes") case 0xb8: @@ -600,15 +785,21 @@ case 0xbf: { // copy imm32 to r32 :(before "End Initialize Op Names") put_new(Name, "c7", "copy imm32 to rm32 (mov)"); -:(scenario copy_imm32_to_mem_at_r32) -% Reg[EBX].i = 0x60; -== 0x1 -# op ModR/M SIB displacement immediate - c7 03 0a 0b 0c 0d # copy 0x0d0c0b0a to *EBX -# ModR/M in binary: 00 (indirect mode) 000 (unused) 011 (dest EBX) -+run: copy imm32 to r/m32 -+run: effective address is 0x00000060 (EBX) -+run: imm32 is 0x0d0c0b0a +:(code) +void test_copy_imm32_to_mem_at_r32() { + Reg[EBX].i = 0x60; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " c7 03 0a 0b 0c 0d \n" // copy 0x0d0c0b0a to *EBX + // ModR/M in binary: 00 (indirect mode) 000 (unused) 011 (dest EBX) + ); + CHECK_TRACE_CONTENTS( + "run: copy imm32 to r/m32\n" + "run: effective address is 0x00000060 (EBX)\n" + "run: imm32 is 0x0d0c0b0a\n" + ); +} :(before "End Single-Byte Opcodes") case 0xc7: { // copy imm32 to r32 @@ -631,14 +822,20 @@ case 0xc7: { // copy imm32 to r32 :(before "End Initialize Op Names") put_new(Name, "68", "push imm32 to stack (push)"); -:(scenario push_imm32) -% Reg[ESP].u = 0x14; -== 0x1 -# op ModR/M SIB displacement immediate - 68 af 00 00 00 # push *EAX to stack -+run: push imm32 0x000000af -+run: ESP is now 0x00000010 -+run: contents at ESP: 0x000000af +:(code) +void test_push_imm32() { + Reg[ESP].u = 0x14; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 68 af 00 00 00 \n" // push *EAX to stack + ); + CHECK_TRACE_CONTENTS( + "run: push imm32 0x000000af\n" + "run: ESP is now 0x00000010\n" + "run: contents at ESP: 0x000000af\n" + ); +} :(before "End Single-Byte Opcodes") case 0x68: { diff --git a/subx/016index_addressing.cc b/subx/016index_addressing.cc index 9fb3e9bb..ef72f710 100644 --- a/subx/016index_addressing.cc +++ b/subx/016index_addressing.cc @@ -1,19 +1,25 @@ //: operating on memory at the address provided by some register plus optional scale and offset -:(scenario add_r32_to_mem_at_r32_with_sib) -% Reg[EBX].i = 0x10; -% Reg[EAX].i = 0x2000; -== 0x1 # code segment -# op ModR/M SIB displacement immediate - 01 1c 20 # add EBX to *EAX -# ModR/M in binary: 00 (indirect mode) 011 (src EBX) 100 (dest in SIB) -# SIB in binary: 00 (scale 1) 100 (no index) 000 (base EAX) -== 0x2000 # data segment -01 00 00 00 # 1 -+run: add EBX to r/m32 -+run: effective address is initially 0x00002000 (EAX) -+run: effective address is 0x00002000 -+run: storing 0x00000011 +:(code) +void test_add_r32_to_mem_at_r32_with_sib() { + Reg[EBX].i = 0x10; + Reg[EAX].i = 0x2000; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 01 1c 20 \n" // add EBX to *EAX + // ModR/M in binary: 00 (indirect mode) 011 (src EBX) 100 (dest in SIB) + // SIB in binary: 00 (scale 1) 100 (no index) 000 (base EAX) + "== 0x2000\n" // data segment + "01 00 00 00\n" // 0x00000001 + ); + CHECK_TRACE_CONTENTS( + "run: add EBX to r/m32\n" + "run: effective address is initially 0x00002000 (EAX)\n" + "run: effective address is 0x00002000\n" + "run: storing 0x00000011\n" + ); +} :(before "End Mod 0 Special-cases(addr)") case 4: // exception: mod 0b00 rm 0b100 => incoming SIB (scale-index-base) byte @@ -46,54 +52,72 @@ uint32_t effective_address_from_sib(uint8_t mod) { return addr; } -:(scenario add_r32_to_mem_at_base_r32_index_r32) -% Reg[EBX].i = 0x10; // source -% Reg[EAX].i = 0x1ffe; // dest base -% Reg[ECX].i = 0x2; // dest index -== 0x1 # code segment -# op ModR/M SIB displacement immediate - 01 1c 08 # add EBX to *(EAX+ECX) -# ModR/M in binary: 00 (indirect mode) 011 (src EBX) 100 (dest in SIB) -# SIB in binary: 00 (scale 1) 001 (index ECX) 000 (base EAX) -== 0x2000 # data segment -01 00 00 00 # 1 -+run: add EBX to r/m32 -+run: effective address is initially 0x00001ffe (EAX) -+run: effective address is 0x00002000 (after adding ECX*1) -+run: storing 0x00000011 +:(code) +void test_add_r32_to_mem_at_base_r32_index_r32() { + Reg[EBX].i = 0x10; // source + Reg[EAX].i = 0x1ffe; // dest base + Reg[ECX].i = 0x2; // dest index + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 01 1c 08 \n" // add EBX to *(EAX+ECX) + // ModR/M in binary: 00 (indirect mode) 011 (src EBX) 100 (dest in SIB) + // SIB in binary: 00 (scale 1) 001 (index ECX) 000 (base EAX) + "== 0x2000\n" // data segment + "01 00 00 00\n" // 0x00000001 + ); + CHECK_TRACE_CONTENTS( + "run: add EBX to r/m32\n" + "run: effective address is initially 0x00001ffe (EAX)\n" + "run: effective address is 0x00002000 (after adding ECX*1)\n" + "run: storing 0x00000011\n" + ); +} -:(scenario add_r32_to_mem_at_displacement_using_sib) -% Reg[EBX].i = 0x10; // source -== 0x1 # code segment -# op ModR/M SIB displacement immediate - 01 1c 25 00 20 00 00 # add EBX to *0x2000 -# ModR/M in binary: 00 (indirect mode) 011 (src EBX) 100 (dest in SIB) -# SIB in binary: 00 (scale 1) 100 (no index) 101 (not EBP but disp32) -== 0x2000 # data segment -01 00 00 00 # 1 -+run: add EBX to r/m32 -+run: effective address is initially 0x00002000 (disp32) -+run: effective address is 0x00002000 -+run: storing 0x00000011 +:(code) +void test_add_r32_to_mem_at_displacement_using_sib() { + Reg[EBX].i = 0x10; // source + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 01 1c 25 00 20 00 00 \n" // add EBX to *0x2000 + // ModR/M in binary: 00 (indirect mode) 011 (src EBX) 100 (dest in SIB) + // SIB in binary: 00 (scale 1) 100 (no index) 101 (not EBP but disp32) + "== 0x2000\n" // data segment + "01 00 00 00\n" // 0x00000001 + ); + CHECK_TRACE_CONTENTS( + "run: add EBX to r/m32\n" + "run: effective address is initially 0x00002000 (disp32)\n" + "run: effective address is 0x00002000\n" + "run: storing 0x00000011\n" + ); +} //: -:(scenario add_r32_to_mem_at_base_r32_index_r32_plus_disp8) -% Reg[EBX].i = 0x10; // source -% Reg[EAX].i = 0x1ff9; // dest base -% Reg[ECX].i = 0x5; // dest index -== 0x1 # code segment -# op ModR/M SIB displacement immediate - 01 5c 08 02 # add EBX to *(EAX+ECX+2) -# ModR/M in binary: 01 (indirect+disp8 mode) 011 (src EBX) 100 (dest in SIB) -# SIB in binary: 00 (scale 1) 001 (index ECX) 000 (base EAX) -== 0x2000 # data segment -01 00 00 00 # 1 -+run: add EBX to r/m32 -+run: effective address is initially 0x00001ff9 (EAX) -+run: effective address is 0x00001ffe (after adding ECX*1) -+run: effective address is 0x00002000 (after adding disp8) -+run: storing 0x00000011 +:(code) +void test_add_r32_to_mem_at_base_r32_index_r32_plus_disp8() { + Reg[EBX].i = 0x10; // source + Reg[EAX].i = 0x1ff9; // dest base + Reg[ECX].i = 0x5; // dest index + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 01 5c 08 02 \n" // add EBX to *(EAX+ECX+2) + // ModR/M in binary: 01 (indirect+disp8 mode) 011 (src EBX) 100 (dest in SIB) + // SIB in binary: 00 (scale 1) 001 (index ECX) 000 (base EAX) + "== 0x2000\n" // data segment + "01 00 00 00\n" // 0x00000001 + ); + CHECK_TRACE_CONTENTS( + "run: add EBX to r/m32\n" + "run: effective address is initially 0x00001ff9 (EAX)\n" + "run: effective address is 0x00001ffe (after adding ECX*1)\n" + "run: effective address is 0x00002000 (after adding disp8)\n" + "run: storing 0x00000011\n" + ); +} :(before "End Mod 1 Special-cases(addr)") case 4: // exception: mod 0b01 rm 0b100 => incoming SIB (scale-index-base) byte @@ -102,22 +126,28 @@ case 4: // exception: mod 0b01 rm 0b100 => incoming SIB (scale-index-base) byte //: -:(scenario add_r32_to_mem_at_base_r32_index_r32_plus_disp32) -% Reg[EBX].i = 0x10; // source -% Reg[EAX].i = 0x1ff9; // dest base -% Reg[ECX].i = 0x5; // dest index -== 0x1 # code segment -# op ModR/M SIB displacement immediate - 01 9c 08 02 00 00 00 # add EBX to *(EAX+ECX+2) -# ModR/M in binary: 10 (indirect+disp32 mode) 011 (src EBX) 100 (dest in SIB) -# SIB in binary: 00 (scale 1) 001 (index ECX) 000 (base EAX) -== 0x2000 # data segment -01 00 00 00 # 1 -+run: add EBX to r/m32 -+run: effective address is initially 0x00001ff9 (EAX) -+run: effective address is 0x00001ffe (after adding ECX*1) -+run: effective address is 0x00002000 (after adding disp32) -+run: storing 0x00000011 +:(code) +void test_add_r32_to_mem_at_base_r32_index_r32_plus_disp32() { + Reg[EBX].i = 0x10; // source + Reg[EAX].i = 0x1ff9; // dest base + Reg[ECX].i = 0x5; // dest index + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 01 9c 08 02 00 00 00 \n" // add EBX to *(EAX+ECX+2) + // ModR/M in binary: 10 (indirect+disp32 mode) 011 (src EBX) 100 (dest in SIB) + // SIB in binary: 00 (scale 1) 001 (index ECX) 000 (base EAX) + "== 0x2000\n" // data segment + "01 00 00 00\n" // 0x00000001 + ); + CHECK_TRACE_CONTENTS( + "run: add EBX to r/m32\n" + "run: effective address is initially 0x00001ff9 (EAX)\n" + "run: effective address is 0x00001ffe (after adding ECX*1)\n" + "run: effective address is 0x00002000 (after adding disp32)\n" + "run: storing 0x00000011\n" + ); +} :(before "End Mod 2 Special-cases(addr)") case 4: // exception: mod 0b10 rm 0b100 => incoming SIB (scale-index-base) byte diff --git a/subx/017jump_disp8.cc b/subx/017jump_disp8.cc index 24467f5c..22ae6567 100644 --- a/subx/017jump_disp8.cc +++ b/subx/017jump_disp8.cc @@ -5,16 +5,22 @@ :(before "End Initialize Op Names") put_new(Name, "eb", "jump disp8 bytes away (jmp)"); -:(scenario jump_rel8) -== 0x1 -# op ModR/M SIB displacement immediate - eb 05 # skip 1 instruction - 05 00 00 00 01 - 05 00 00 00 02 -+run: 0x00000001 opcode: eb -+run: jump 5 -+run: 0x00000008 opcode: 05 --run: 0x00000003 opcode: 05 +:(code) +void test_jump_rel8() { + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " eb 05 \n" // skip 1 instruction + " 05 00 00 00 01 \n" + " 05 00 00 00 02 \n" + ); + CHECK_TRACE_CONTENTS( + "run: 0x00000001 opcode: eb\n" + "run: jump 5\n" + "run: 0x00000008 opcode: 05\n" + ); + CHECK_TRACE_DOESNT_CONTAIN("run: 0x00000003 opcode: 05"); +} :(before "End Single-Byte Opcodes") case 0xeb: { // jump rel8 @@ -29,17 +35,23 @@ case 0xeb: { // jump rel8 :(before "End Initialize Op Names") put_new(Name, "74", "jump disp8 bytes away if equal, if ZF is set (jcc/jz/je)"); -:(scenario je_rel8_success) -% ZF = true; -== 0x1 -# op ModR/M SIB displacement immediate - 74 05 # skip 1 instruction - 05 00 00 00 01 - 05 00 00 00 02 -+run: 0x00000001 opcode: 74 -+run: jump 5 -+run: 0x00000008 opcode: 05 --run: 0x00000003 opcode: 05 +:(code) +void test_je_rel8_success() { + ZF = true; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 74 05 \n" // skip 1 instruction + " 05 00 00 00 01 \n" + " 05 00 00 00 02 \n" + ); + CHECK_TRACE_CONTENTS( + "run: 0x00000001 opcode: 74\n" + "run: jump 5\n" + "run: 0x00000008 opcode: 05\n" + ); + CHECK_TRACE_DOESNT_CONTAIN("run: 0x00000003 opcode: 05"); +} :(before "End Single-Byte Opcodes") case 0x74: { // jump rel8 if ZF @@ -51,34 +63,46 @@ case 0x74: { // jump rel8 if ZF break; } -:(scenario je_rel8_fail) -% ZF = false; -== 0x1 -# op ModR/M SIB displacement immediate - 74 05 # skip 1 instruction - 05 00 00 00 01 - 05 00 00 00 02 -+run: 0x00000001 opcode: 74 -+run: 0x00000003 opcode: 05 -+run: 0x00000008 opcode: 05 --run: jump 5 +:(code) +void test_je_rel8_fail() { + ZF = false; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 74 05 \n" // skip 1 instruction + " 05 00 00 00 01 \n" + " 05 00 00 00 02 \n" + ); + CHECK_TRACE_CONTENTS( + "run: 0x00000001 opcode: 74\n" + "run: 0x00000003 opcode: 05\n" + "run: 0x00000008 opcode: 05\n" + ); + CHECK_TRACE_DOESNT_CONTAIN("run: jump 5"); +} //:: jump if not equal/not zero :(before "End Initialize Op Names") put_new(Name, "75", "jump disp8 bytes away if not equal, if ZF is not set (jcc/jnz/jne)"); -:(scenario jne_rel8_success) -% ZF = false; -== 0x1 -# op ModR/M SIB displacement immediate - 75 05 # skip 1 instruction - 05 00 00 00 01 - 05 00 00 00 02 -+run: 0x00000001 opcode: 75 -+run: jump 5 -+run: 0x00000008 opcode: 05 --run: 0x00000003 opcode: 05 +:(code) +void test_jne_rel8_success() { + ZF = false; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 75 05 \n" // skip 1 instruction + " 05 00 00 00 01 \n" + " 05 00 00 00 02 \n" + ); + CHECK_TRACE_CONTENTS( + "run: 0x00000001 opcode: 75\n" + "run: jump 5\n" + "run: 0x00000008 opcode: 05\n" + ); + CHECK_TRACE_DOESNT_CONTAIN("run: 0x00000003 opcode: 05"); +} :(before "End Single-Byte Opcodes") case 0x75: { // jump rel8 unless ZF @@ -90,36 +114,48 @@ case 0x75: { // jump rel8 unless ZF break; } -:(scenario jne_rel8_fail) -% ZF = true; -== 0x1 -# op ModR/M SIB displacement immediate - 75 05 # skip 1 instruction - 05 00 00 00 01 - 05 00 00 00 02 -+run: 0x00000001 opcode: 75 -+run: 0x00000003 opcode: 05 -+run: 0x00000008 opcode: 05 --run: jump 5 +:(code) +void test_jne_rel8_fail() { + ZF = true; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 75 05 \n" // skip 1 instruction + " 05 00 00 00 01 \n" + " 05 00 00 00 02 \n" + ); + CHECK_TRACE_CONTENTS( + "run: 0x00000001 opcode: 75\n" + "run: 0x00000003 opcode: 05\n" + "run: 0x00000008 opcode: 05\n" + ); + CHECK_TRACE_DOESNT_CONTAIN("run: jump 5"); +} //:: jump if greater :(before "End Initialize Op Names") put_new(Name, "7f", "jump disp8 bytes away if greater, if ZF is unset and SF == OF (jcc/jg/jnle)"); -:(scenario jg_rel8_success) -% ZF = false; -% SF = false; -% OF = false; -== 0x1 -# op ModR/M SIB displacement immediate - 7f 05 # skip 1 instruction - 05 00 00 00 01 - 05 00 00 00 02 -+run: 0x00000001 opcode: 7f -+run: jump 5 -+run: 0x00000008 opcode: 05 --run: 0x00000003 opcode: 05 +:(code) +void test_jg_rel8_success() { + ZF = false; + SF = false; + OF = false; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 7f 05 \n" // skip 1 instruction + " 05 00 00 00 01 \n" + " 05 00 00 00 02 \n" + ); + CHECK_TRACE_CONTENTS( + "run: 0x00000001 opcode: 7f\n" + "run: jump 5\n" + "run: 0x00000008 opcode: 05\n" + ); + CHECK_TRACE_DOESNT_CONTAIN("run: 0x00000003 opcode: 05"); +} :(before "End Single-Byte Opcodes") case 0x7f: { // jump rel8 if !SF and !ZF @@ -131,37 +167,49 @@ case 0x7f: { // jump rel8 if !SF and !ZF break; } -:(scenario jg_rel8_fail) -% ZF = false; -% SF = true; -% OF = false; -== 0x1 -# op ModR/M SIB displacement immediate - 7f 05 # skip 1 instruction - 05 00 00 00 01 - 05 00 00 00 02 -+run: 0x00000001 opcode: 7f -+run: 0x00000003 opcode: 05 -+run: 0x00000008 opcode: 05 --run: jump 5 +:(code) +void test_jg_rel8_fail() { + ZF = false; + SF = true; + OF = false; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 7f 05 \n" // skip 1 instruction + " 05 00 00 00 01 \n" + " 05 00 00 00 02 \n" + ); + CHECK_TRACE_CONTENTS( + "run: 0x00000001 opcode: 7f\n" + "run: 0x00000003 opcode: 05\n" + "run: 0x00000008 opcode: 05\n" + ); + CHECK_TRACE_DOESNT_CONTAIN("run: jump 5"); +} //:: jump if greater or equal :(before "End Initialize Op Names") put_new(Name, "7d", "jump disp8 bytes away if greater or equal, if SF == OF (jcc/jge/jnl)"); -:(scenario jge_rel8_success) -% SF = false; -% OF = false; -== 0x1 -# op ModR/M SIB displacement immediate - 7d 05 # skip 1 instruction - 05 00 00 00 01 - 05 00 00 00 02 -+run: 0x00000001 opcode: 7d -+run: jump 5 -+run: 0x00000008 opcode: 05 --run: 0x00000003 opcode: 05 +:(code) +void test_jge_rel8_success() { + SF = false; + OF = false; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 7d 05 \n" // skip 1 instruction + " 05 00 00 00 01 \n" + " 05 00 00 00 02 \n" + ); + CHECK_TRACE_CONTENTS( + "run: 0x00000001 opcode: 7d\n" + "run: jump 5\n" + "run: 0x00000008 opcode: 05\n" + ); + CHECK_TRACE_DOESNT_CONTAIN("run: 0x00000003 opcode: 05"); +} :(before "End Single-Byte Opcodes") case 0x7d: { // jump rel8 if !SF @@ -173,37 +221,49 @@ case 0x7d: { // jump rel8 if !SF break; } -:(scenario jge_rel8_fail) -% SF = true; -% OF = false; -== 0x1 -# op ModR/M SIB displacement immediate - 7d 05 # skip 1 instruction - 05 00 00 00 01 - 05 00 00 00 02 -+run: 0x00000001 opcode: 7d -+run: 0x00000003 opcode: 05 -+run: 0x00000008 opcode: 05 --run: jump 5 +:(code) +void test_jge_rel8_fail() { + SF = true; + OF = false; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 7d 05 \n" // skip 1 instruction + " 05 00 00 00 01 \n" + " 05 00 00 00 02 \n" + ); + CHECK_TRACE_CONTENTS( + "run: 0x00000001 opcode: 7d\n" + "run: 0x00000003 opcode: 05\n" + "run: 0x00000008 opcode: 05\n" + ); + CHECK_TRACE_DOESNT_CONTAIN("run: jump 5"); +} //:: jump if lesser :(before "End Initialize Op Names") put_new(Name, "7c", "jump disp8 bytes away if lesser, if SF != OF (jcc/jl/jnge)"); -:(scenario jl_rel8_success) -% ZF = false; -% SF = true; -% OF = false; -== 0x1 -# op ModR/M SIB displacement immediate - 7c 05 # skip 1 instruction - 05 00 00 00 01 - 05 00 00 00 02 -+run: 0x00000001 opcode: 7c -+run: jump 5 -+run: 0x00000008 opcode: 05 --run: 0x00000003 opcode: 05 +:(code) +void test_jl_rel8_success() { + ZF = false; + SF = true; + OF = false; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 7c 05 \n" // skip 1 instruction + " 05 00 00 00 01 \n" + " 05 00 00 00 02 \n" + ); + CHECK_TRACE_CONTENTS( + "run: 0x00000001 opcode: 7c\n" + "run: jump 5\n" + "run: 0x00000008 opcode: 05\n" + ); + CHECK_TRACE_DOESNT_CONTAIN("run: 0x00000003 opcode: 05"); +} :(before "End Single-Byte Opcodes") case 0x7c: { // jump rel8 if SF and !ZF @@ -215,52 +275,70 @@ case 0x7c: { // jump rel8 if SF and !ZF break; } -:(scenario jl_rel8_fail) -% ZF = false; -% SF = false; -% OF = false; -== 0x1 -# op ModR/M SIB displacement immediate - 7c 05 # skip 1 instruction - 05 00 00 00 01 - 05 00 00 00 02 -+run: 0x00000001 opcode: 7c -+run: 0x00000003 opcode: 05 -+run: 0x00000008 opcode: 05 --run: jump 5 +:(code) +void test_jl_rel8_fail() { + ZF = false; + SF = false; + OF = false; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 7c 05 \n" // skip 1 instruction + " 05 00 00 00 01 \n" + " 05 00 00 00 02 \n" + ); + CHECK_TRACE_CONTENTS( + "run: 0x00000001 opcode: 7c\n" + "run: 0x00000003 opcode: 05\n" + "run: 0x00000008 opcode: 05\n" + ); + CHECK_TRACE_DOESNT_CONTAIN("run: jump 5"); +} //:: jump if lesser or equal :(before "End Initialize Op Names") put_new(Name, "7e", "jump disp8 bytes away if lesser or equal, if ZF is set or SF != OF (jcc/jle/jng)"); -:(scenario jle_rel8_equal) -% ZF = true; -% SF = false; -% OF = false; -== 0x1 -# op ModR/M SIB displacement immediate - 7e 05 # skip 1 instruction - 05 00 00 00 01 - 05 00 00 00 02 -+run: 0x00000001 opcode: 7e -+run: jump 5 -+run: 0x00000008 opcode: 05 --run: 0x00000003 opcode: 05 - -:(scenario jle_rel8_lesser) -% ZF = false; -% SF = true; -% OF = false; -== 0x1 -# op ModR/M SIB displacement immediate - 7e 05 # skip 1 instruction - 05 00 00 00 01 - 05 00 00 00 02 -+run: 0x00000001 opcode: 7e -+run: jump 5 -+run: 0x00000008 opcode: 05 --run: 0x00000003 opcode: 05 +:(code) +void test_jle_rel8_equal() { + ZF = true; + SF = false; + OF = false; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 7e 05 \n" // skip 1 instruction + " 05 00 00 00 01 \n" + " 05 00 00 00 02 \n" + ); + CHECK_TRACE_CONTENTS( + "run: 0x00000001 opcode: 7e\n" + "run: jump 5\n" + "run: 0x00000008 opcode: 05\n" + ); + CHECK_TRACE_DOESNT_CONTAIN("run: 0x00000003 opcode: 05"); +} + +:(code) +void test_jle_rel8_lesser() { + ZF = false; + SF = true; + OF = false; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 7e 05 \n" // skip 1 instruction + " 05 00 00 00 01 \n" + " 05 00 00 00 02 \n" + ); + CHECK_TRACE_CONTENTS( + "run: 0x00000001 opcode: 7e\n" + "run: jump 5\n" + "run: 0x00000008 opcode: 05\n" + ); + CHECK_TRACE_DOESNT_CONTAIN("run: 0x00000003 opcode: 05"); +} :(before "End Single-Byte Opcodes") case 0x7e: { // jump rel8 if SF or ZF @@ -272,16 +350,22 @@ case 0x7e: { // jump rel8 if SF or ZF break; } -:(scenario jle_rel8_greater) -% ZF = false; -% SF = false; -% OF = false; -== 0x1 -# op ModR/M SIB displacement immediate - 7e 05 # skip 1 instruction - 05 00 00 00 01 - 05 00 00 00 02 -+run: 0x00000001 opcode: 7e -+run: 0x00000003 opcode: 05 -+run: 0x00000008 opcode: 05 --run: jump 5 +:(code) +void test_jle_rel8_greater() { + ZF = false; + SF = false; + OF = false; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 7e 05 \n" // skip 1 instruction + " 05 00 00 00 01 \n" + " 05 00 00 00 02 \n" + ); + CHECK_TRACE_CONTENTS( + "run: 0x00000001 opcode: 7e\n" + "run: 0x00000003 opcode: 05\n" + "run: 0x00000008 opcode: 05\n" + ); + CHECK_TRACE_DOESNT_CONTAIN("run: jump 5"); +} diff --git a/subx/018jump_disp32.cc b/subx/018jump_disp32.cc index c86cd2df..836146ee 100644 --- a/subx/018jump_disp32.cc +++ b/subx/018jump_disp32.cc @@ -5,16 +5,22 @@ :(before "End Initialize Op Names") put_new(Name, "e9", "jump disp32 bytes away (jmp)"); -:(scenario jump_disp32) -== 0x1 -# op ModR/M SIB displacement immediate - e9 05 00 00 00 # skip 1 instruction - 05 00 00 00 01 - 05 00 00 00 02 -+run: 0x00000001 opcode: e9 -+run: jump 5 -+run: 0x0000000b opcode: 05 --run: 0x00000006 opcode: 05 +:(code) +void test_jump_disp32() { + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " e9 05 00 00 00 \n" // skip 1 instruction + " 05 00 00 00 01 \n" + " 05 00 00 00 02 \n" + ); + CHECK_TRACE_CONTENTS( + "run: 0x00000001 opcode: e9\n" + "run: jump 5\n" + "run: 0x0000000b opcode: 05\n" + ); + CHECK_TRACE_DOESNT_CONTAIN("run: 0x00000006 opcode: 05"); +} :(before "End Single-Byte Opcodes") case 0xe9: { // jump disp32 @@ -29,17 +35,23 @@ case 0xe9: { // jump disp32 :(before "End Initialize Op Names") put_new(Name_0f, "84", "jump disp32 bytes away if equal, if ZF is set (jcc/jz/je)"); -:(scenario je_disp32_success) -% ZF = true; -== 0x1 -# op ModR/M SIB displacement immediate - 0f 84 05 00 00 00 # skip 1 instruction - 05 00 00 00 01 - 05 00 00 00 02 -+run: 0x00000001 opcode: 0f -+run: jump 5 -+run: 0x0000000c opcode: 05 --run: 0x00000007 opcode: 05 +:(code) +void test_je_disp32_success() { + ZF = true; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 0f 84 05 00 00 00 \n" // skip 1 instruction + " 05 00 00 00 01 \n" + " 05 00 00 00 02 \n" + ); + CHECK_TRACE_CONTENTS( + "run: 0x00000001 opcode: 0f\n" + "run: jump 5\n" + "run: 0x0000000c opcode: 05\n" + ); + CHECK_TRACE_DOESNT_CONTAIN("run: 0x00000007 opcode: 05"); +} :(before "End Two-Byte Opcodes Starting With 0f") case 0x84: { // jump disp32 if ZF @@ -51,34 +63,46 @@ case 0x84: { // jump disp32 if ZF break; } -:(scenario je_disp32_fail) -% ZF = false; -== 0x1 -# op ModR/M SIB displacement immediate - 0f 84 05 00 00 00 # skip 1 instruction - 05 00 00 00 01 - 05 00 00 00 02 -+run: 0x00000001 opcode: 0f -+run: 0x00000007 opcode: 05 -+run: 0x0000000c opcode: 05 --run: jump 5 +:(code) +void test_je_disp32_fail() { + ZF = false; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 0f 84 05 00 00 00 \n" // skip 1 instruction + " 05 00 00 00 01 \n" + " 05 00 00 00 02 \n" + ); + CHECK_TRACE_CONTENTS( + "run: 0x00000001 opcode: 0f\n" + "run: 0x00000007 opcode: 05\n" + "run: 0x0000000c opcode: 05\n" + ); + CHECK_TRACE_DOESNT_CONTAIN("run: jump 5"); +} //:: jump if not equal/not zero :(before "End Initialize Op Names") put_new(Name_0f, "85", "jump disp32 bytes away if not equal, if ZF is not set (jcc/jnz/jne)"); -:(scenario jne_disp32_success) -% ZF = false; -== 0x1 -# op ModR/M SIB displacement immediate - 0f 85 05 00 00 00 # skip 1 instruction - 05 00 00 00 01 - 05 00 00 00 02 -+run: 0x00000001 opcode: 0f -+run: jump 5 -+run: 0x0000000c opcode: 05 --run: 0x00000007 opcode: 05 +:(code) +void test_jne_disp32_success() { + ZF = false; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 0f 85 05 00 00 00 \n" // skip 1 instruction + " 05 00 00 00 01 \n" + " 05 00 00 00 02 \n" + ); + CHECK_TRACE_CONTENTS( + "run: 0x00000001 opcode: 0f\n" + "run: jump 5\n" + "run: 0x0000000c opcode: 05\n" + ); + CHECK_TRACE_DOESNT_CONTAIN("run: 0x00000007 opcode: 05"); +} :(before "End Two-Byte Opcodes Starting With 0f") case 0x85: { // jump disp32 unless ZF @@ -90,36 +114,48 @@ case 0x85: { // jump disp32 unless ZF break; } -:(scenario jne_disp32_fail) -% ZF = true; -== 0x1 -# op ModR/M SIB displacement immediate - 0f 85 05 00 00 00 # skip 1 instruction - 05 00 00 00 01 - 05 00 00 00 02 -+run: 0x00000001 opcode: 0f -+run: 0x00000007 opcode: 05 -+run: 0x0000000c opcode: 05 --run: jump 5 +:(code) +void test_jne_disp32_fail() { + ZF = true; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 0f 85 05 00 00 00 \n" // skip 1 instruction + " 05 00 00 00 01 \n" + " 05 00 00 00 02 \n" + ); + CHECK_TRACE_CONTENTS( + "run: 0x00000001 opcode: 0f\n" + "run: 0x00000007 opcode: 05\n" + "run: 0x0000000c opcode: 05\n" + ); + CHECK_TRACE_DOESNT_CONTAIN("run: jump 5"); +} //:: jump if greater :(before "End Initialize Op Names") put_new(Name_0f, "8f", "jump disp32 bytes away if greater, if ZF is unset and SF == OF (jcc/jg/jnle)"); -:(scenario jg_disp32_success) -% ZF = false; -% SF = false; -% OF = false; -== 0x1 -# op ModR/M SIB displacement immediate - 0f 8f 05 00 00 00 # skip 1 instruction - 05 00 00 00 01 - 05 00 00 00 02 -+run: 0x00000001 opcode: 0f -+run: jump 5 -+run: 0x0000000c opcode: 05 --run: 0x00000007 opcode: 05 +:(code) +void test_jg_disp32_success() { + ZF = false; + SF = false; + OF = false; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 0f 8f 05 00 00 00 \n" // skip 1 instruction + " 05 00 00 00 01 \n" + " 05 00 00 00 02 \n" + ); + CHECK_TRACE_CONTENTS( + "run: 0x00000001 opcode: 0f\n" + "run: jump 5\n" + "run: 0x0000000c opcode: 05\n" + ); + CHECK_TRACE_DOESNT_CONTAIN("run: 0x00000007 opcode: 05"); +} :(before "End Two-Byte Opcodes Starting With 0f") case 0x8f: { // jump disp32 if !SF and !ZF @@ -131,37 +167,49 @@ case 0x8f: { // jump disp32 if !SF and !ZF break; } -:(scenario jg_disp32_fail) -% ZF = false; -% SF = true; -% OF = false; -== 0x1 -# op ModR/M SIB displacement immediate - 0f 8f 05 00 00 00 # skip 1 instruction - 05 00 00 00 01 - 05 00 00 00 02 -+run: 0x00000001 opcode: 0f -+run: 0x00000007 opcode: 05 -+run: 0x0000000c opcode: 05 --run: jump 5 +:(code) +void test_jg_disp32_fail() { + ZF = false; + SF = true; + OF = false; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 0f 8f 05 00 00 00 \n" // skip 1 instruction + " 05 00 00 00 01 \n" + " 05 00 00 00 02 \n" + ); + CHECK_TRACE_CONTENTS( + "run: 0x00000001 opcode: 0f\n" + "run: 0x00000007 opcode: 05\n" + "run: 0x0000000c opcode: 05\n" + ); + CHECK_TRACE_DOESNT_CONTAIN("run: jump 5"); +} //:: jump if greater or equal :(before "End Initialize Op Names") put_new(Name_0f, "8d", "jump disp32 bytes away if greater or equal, if SF == OF (jcc/jge/jnl)"); -:(scenario jge_disp32_success) -% SF = false; -% OF = false; -== 0x1 -# op ModR/M SIB displacement immediate - 0f 8d 05 00 00 00 # skip 1 instruction - 05 00 00 00 01 - 05 00 00 00 02 -+run: 0x00000001 opcode: 0f -+run: jump 5 -+run: 0x0000000c opcode: 05 --run: 0x00000007 opcode: 05 +:(code) +void test_jge_disp32_success() { + SF = false; + OF = false; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 0f 8d 05 00 00 00 \n" // skip 1 instruction + " 05 00 00 00 01 \n" + " 05 00 00 00 02 \n" + ); + CHECK_TRACE_CONTENTS( + "run: 0x00000001 opcode: 0f\n" + "run: jump 5\n" + "run: 0x0000000c opcode: 05\n" + ); + CHECK_TRACE_DOESNT_CONTAIN("run: 0x00000007 opcode: 05"); +} :(before "End Two-Byte Opcodes Starting With 0f") case 0x8d: { // jump disp32 if !SF @@ -173,37 +221,49 @@ case 0x8d: { // jump disp32 if !SF break; } -:(scenario jge_disp32_fail) -% SF = true; -% OF = false; -== 0x1 -# op ModR/M SIB displacement immediate - 0f 8d 05 00 00 00 # skip 1 instruction - 05 00 00 00 01 - 05 00 00 00 02 -+run: 0x00000001 opcode: 0f -+run: 0x00000007 opcode: 05 -+run: 0x0000000c opcode: 05 --run: jump 5 +:(code) +void test_jge_disp32_fail() { + SF = true; + OF = false; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 0f 8d 05 00 00 00 \n" // skip 1 instruction + " 05 00 00 00 01 \n" + " 05 00 00 00 02 \n" + ); + CHECK_TRACE_CONTENTS( + "run: 0x00000001 opcode: 0f\n" + "run: 0x00000007 opcode: 05\n" + "run: 0x0000000c opcode: 05\n" + ); + CHECK_TRACE_DOESNT_CONTAIN("run: jump 5"); +} //:: jump if lesser :(before "End Initialize Op Names") put_new(Name_0f, "8c", "jump disp32 bytes away if lesser, if SF != OF (jcc/jl/jnge)"); -:(scenario jl_disp32_success) -% ZF = false; -% SF = true; -% OF = false; -== 0x1 -# op ModR/M SIB displacement immediate - 0f 8c 05 00 00 00 # skip 1 instruction - 05 00 00 00 01 - 05 00 00 00 02 -+run: 0x00000001 opcode: 0f -+run: jump 5 -+run: 0x0000000c opcode: 05 --run: 0x00000007 opcode: 05 +:(code) +void test_jl_disp32_success() { + ZF = false; + SF = true; + OF = false; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 0f 8c 05 00 00 00 \n" // skip 1 instruction + " 05 00 00 00 01 \n" + " 05 00 00 00 02 \n" + ); + CHECK_TRACE_CONTENTS( + "run: 0x00000001 opcode: 0f\n" + "run: jump 5\n" + "run: 0x0000000c opcode: 05\n" + ); + CHECK_TRACE_DOESNT_CONTAIN("run: 0x00000007 opcode: 05"); +} :(before "End Two-Byte Opcodes Starting With 0f") case 0x8c: { // jump disp32 if SF and !ZF @@ -215,52 +275,70 @@ case 0x8c: { // jump disp32 if SF and !ZF break; } -:(scenario jl_disp32_fail) -% ZF = false; -% SF = false; -% OF = false; -== 0x1 -# op ModR/M SIB displacement immediate - 0f 8c 05 00 00 00 # skip 1 instruction - 05 00 00 00 01 - 05 00 00 00 02 -+run: 0x00000001 opcode: 0f -+run: 0x00000007 opcode: 05 -+run: 0x0000000c opcode: 05 --run: jump 5 +:(code) +void test_jl_disp32_fail() { + ZF = false; + SF = false; + OF = false; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 0f 8c 05 00 00 00 \n" // skip 1 instruction + " 05 00 00 00 01 \n" + " 05 00 00 00 02 \n" + ); + CHECK_TRACE_CONTENTS( + "run: 0x00000001 opcode: 0f\n" + "run: 0x00000007 opcode: 05\n" + "run: 0x0000000c opcode: 05\n" + ); + CHECK_TRACE_DOESNT_CONTAIN("run: jump 5"); +} //:: jump if lesser or equal :(before "End Initialize Op Names") put_new(Name_0f, "8e", "jump disp32 bytes away if lesser or equal, if ZF is set or SF != OF (jcc/jle/jng)"); -:(scenario jle_disp32_equal) -% ZF = true; -% SF = false; -% OF = false; -== 0x1 -# op ModR/M SIB displacement immediate - 0f 8e 05 00 00 00 # skip 1 instruction - 05 00 00 00 01 - 05 00 00 00 02 -+run: 0x00000001 opcode: 0f -+run: jump 5 -+run: 0x0000000c opcode: 05 --run: 0x00000007 opcode: 05 - -:(scenario jle_disp32_lesser) -% ZF = false; -% SF = true; -% OF = false; -== 0x1 -# op ModR/M SIB displacement immediate - 0f 8e 05 00 00 00 # skip 1 instruction - 05 00 00 00 01 - 05 00 00 00 02 -+run: 0x00000001 opcode: 0f -+run: jump 5 -+run: 0x0000000c opcode: 05 --run: 0x00000007 opcode: 05 +:(code) +void test_jle_disp32_equal() { + ZF = true; + SF = false; + OF = false; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 0f 8e 05 00 00 00 \n" // skip 1 instruction + " 05 00 00 00 01 \n" + " 05 00 00 00 02 \n" + ); + CHECK_TRACE_CONTENTS( + "run: 0x00000001 opcode: 0f\n" + "run: jump 5\n" + "run: 0x0000000c opcode: 05\n" + ); + CHECK_TRACE_DOESNT_CONTAIN("run: 0x00000007 opcode: 05"); +} + +:(code) +void test_jle_disp32_lesser() { + ZF = false; + SF = true; + OF = false; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 0f 8e 05 00 00 00 \n" // skip 1 instruction + " 05 00 00 00 01 \n" + " 05 00 00 00 02 \n" + ); + CHECK_TRACE_CONTENTS( + "run: 0x00000001 opcode: 0f\n" + "run: jump 5\n" + "run: 0x0000000c opcode: 05\n" + ); + CHECK_TRACE_DOESNT_CONTAIN("run: 0x00000007 opcode: 05"); +} :(before "End Two-Byte Opcodes Starting With 0f") case 0x8e: { // jump disp32 if SF or ZF @@ -272,16 +350,22 @@ case 0x8e: { // jump disp32 if SF or ZF break; } -:(scenario jle_disp32_greater) -% ZF = false; -% SF = false; -% OF = false; -== 0x1 -# op ModR/M SIB displacement immediate - 0f 8e 05 00 00 00 # skip 1 instruction - 05 00 00 00 01 - 05 00 00 00 02 -+run: 0x00000001 opcode: 0f -+run: 0x00000007 opcode: 05 -+run: 0x0000000c opcode: 05 --run: jump 5 +:(code) +void test_jle_disp32_greater() { + ZF = false; + SF = false; + OF = false; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 0f 8e 05 00 00 00 \n" // skip 1 instruction + " 05 00 00 00 01 \n" + " 05 00 00 00 02 \n" + ); + CHECK_TRACE_CONTENTS( + "run: 0x00000001 opcode: 0f\n" + "run: 0x00000007 opcode: 05\n" + "run: 0x0000000c opcode: 05\n" + ); + CHECK_TRACE_DOESNT_CONTAIN("run: jump 5"); +} diff --git a/subx/019functions.cc b/subx/019functions.cc index 66cfe384..7f45167b 100644 --- a/subx/019functions.cc +++ b/subx/019functions.cc @@ -3,16 +3,22 @@ :(before "End Initialize Op Names") put_new(Name, "e8", "call disp32 (call)"); -:(scenario call_disp32) -% Reg[ESP].u = 0x64; -== 0x1 -# op ModR/M SIB displacement immediate - e8 a0 00 00 00 # call function offset at 0x000000a0 - # next EIP is 6 -+run: call imm32 0x000000a0 -+run: decrementing ESP to 0x00000060 -+run: pushing value 0x00000006 -+run: jumping to 0x000000a6 +:(code) +void test_call_disp32() { + Reg[ESP].u = 0x64; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " e8 a0 00 00 00 \n" // call function offset at 0x000000a0 + // next EIP is 6 + ); + CHECK_TRACE_CONTENTS( + "run: call imm32 0x000000a0\n" + "run: decrementing ESP to 0x00000060\n" + "run: pushing value 0x00000006\n" + "run: jumping to 0x000000a6\n" + ); +} :(before "End Single-Byte Opcodes") case 0xe8: { // call disp32 relative to next EIP @@ -28,18 +34,24 @@ case 0xe8: { // call disp32 relative to next EIP //: -:(scenario call_r32) -% Reg[ESP].u = 0x64; -% Reg[EBX].u = 0x000000a0; -== 0x1 -# op ModR/M SIB displacement immediate - ff d3 # call function offset at EBX - # next EIP is 3 -+run: call to r/m32 -+run: r/m32 is EBX -+run: decrementing ESP to 0x00000060 -+run: pushing value 0x00000003 -+run: jumping to 0x000000a3 +:(code) +void test_call_r32() { + Reg[ESP].u = 0x64; + Reg[EBX].u = 0x000000a0; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " ff d3 \n" // call function offset at EBX + // next EIP is 3 + ); + CHECK_TRACE_CONTENTS( + "run: call to r/m32\n" + "run: r/m32 is EBX\n" + "run: decrementing ESP to 0x00000060\n" + "run: pushing value 0x00000003\n" + "run: jumping to 0x000000a3\n" + ); +} :(before "End Op ff Subops") case 2: { // call function pointer at r/m32 @@ -52,36 +64,48 @@ case 2: { // call function pointer at r/m32 break; } -:(scenario call_mem_at_r32) -% Reg[ESP].u = 0x64; -% Reg[EBX].u = 0x2000; -== 0x1 # code segment -# op ModR/M SIB displacement immediate - ff 13 # call function offset at *EBX - # next EIP is 3 -== 0x2000 # data segment -a0 00 00 00 # 0xa0 -+run: call to r/m32 -+run: effective address is 0x00002000 (EBX) -+run: decrementing ESP to 0x00000060 -+run: pushing value 0x00000003 -+run: jumping to 0x000000a3 +:(code) +void test_call_mem_at_r32() { + Reg[ESP].u = 0x64; + Reg[EBX].u = 0x2000; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " ff 13 \n" // call function offset at *EBX + // next EIP is 3 + "== 0x2000\n" // data segment + "a0 00 00 00\n" // 0x000000a0 + ); + CHECK_TRACE_CONTENTS( + "run: call to r/m32\n" + "run: effective address is 0x00002000 (EBX)\n" + "run: decrementing ESP to 0x00000060\n" + "run: pushing value 0x00000003\n" + "run: jumping to 0x000000a3\n" + ); +} //:: ret :(before "End Initialize Op Names") put_new(Name, "c3", "return from most recent unfinished call (ret)"); -:(scenario ret) -% Reg[ESP].u = 0x2000; -== 0x1 # code segment -# op ModR/M SIB displacement immediate - c3 -== 0x2000 # data segment -10 00 00 00 # 0x10 -+run: return -+run: popping value 0x00000010 -+run: jumping to 0x00000010 +:(code) +void test_ret() { + Reg[ESP].u = 0x2000; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " c3 \n" // return + "== 0x2000\n" // data segment + "10 00 00 00\n" // 0x00000010 + ); + CHECK_TRACE_CONTENTS( + "run: return\n" + "run: popping value 0x00000010\n" + "run: jumping to 0x00000010\n" + ); +} :(before "End Single-Byte Opcodes") case 0xc3: { // return from a call diff --git a/subx/021byte_addressing.cc b/subx/021byte_addressing.cc index c96bc9c1..106cd426 100644 --- a/subx/021byte_addressing.cc +++ b/subx/021byte_addressing.cc @@ -40,19 +40,25 @@ uint8_t* reg_8bit(uint8_t rm) { :(before "End Initialize Op Names") put_new(Name, "88", "copy r8 to r8/m8-at-r32"); -:(scenario copy_r8_to_mem_at_r32) -% Reg[EBX].i = 0x224488ab; -% Reg[EAX].i = 0x2000; -== 0x1 -# op ModR/M SIB displacement immediate - 88 18 # copy BL to the byte at *EAX -# ModR/M in binary: 00 (indirect mode) 011 (src BL) 000 (dest EAX) -== 0x2000 -f0 cc bb aa -+run: copy BL to r8/m8-at-r32 -+run: effective address is 0x00002000 (EAX) -+run: storing 0xab -% CHECK_EQ(0xaabbccab, read_mem_u32(0x2000)); +:(code) +void test_copy_r8_to_mem_at_r32() { + Reg[EBX].i = 0x224488ab; + Reg[EAX].i = 0x2000; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 88 18 \n" // copy BL to the byte at *EAX + // ModR/M in binary: 00 (indirect mode) 011 (src BL) 000 (dest EAX) + "== 0x2000\n" // data segment + "f0 cc bb aa\n" + ); + CHECK_TRACE_CONTENTS( + "run: copy BL to r8/m8-at-r32\n" + "run: effective address is 0x00002000 (EAX)\n" + "run: storing 0xab\n" + ); + CHECK_EQ(0xaabbccab, read_mem_u32(0x2000)); +} :(before "End Single-Byte Opcodes") case 0x88: { // copy r8 to r/m8 @@ -72,20 +78,26 @@ case 0x88: { // copy r8 to r/m8 :(before "End Initialize Op Names") put_new(Name, "8a", "copy r8/m8-at-r32 to r8"); -:(scenario copy_mem_at_r32_to_r8) -% Reg[EBX].i = 0xaabbcc0f; // one nibble each of lowest byte set to all 0s and all 1s, to maximize value of this test -% Reg[EAX].i = 0x2000; -== 0x1 -# op ModR/M SIB displacement immediate - 8a 18 # copy just the byte at *EAX to BL -# ModR/M in binary: 00 (indirect mode) 011 (dest EBX) 000 (src EAX) -== 0x2000 # data segment -ab ff ff ff # 0xab with more data in following bytes -+run: copy r8/m8-at-r32 to BL -+run: effective address is 0x00002000 (EAX) -+run: storing 0xab -# remaining bytes of EBX are *not* cleared -+run: EBX now contains 0xaabbccab +:(code) +void test_copy_mem_at_r32_to_r8() { + Reg[EBX].i = 0xaabbcc0f; // one nibble each of lowest byte set to all 0s and all 1s, to maximize value of this test + Reg[EAX].i = 0x2000; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 8a 18 \n" // copy just the byte at *EAX to BL + // ModR/M in binary: 00 (indirect mode) 011 (dest EBX) 000 (src EAX) + "== 0x2000\n" // data segment + "ab ff ff ff\n" // 0xab with more data in following bytes + ); + CHECK_TRACE_CONTENTS( + "run: copy r8/m8-at-r32 to BL\n" + "run: effective address is 0x00002000 (EAX)\n" + "run: storing 0xab\n" + // remaining bytes of EBX are *not* cleared + "run: EBX now contains 0xaabbccab\n" + ); +} :(before "End Single-Byte Opcodes") case 0x8a: { // copy r/m8 to r8 @@ -102,36 +114,48 @@ case 0x8a: { // copy r/m8 to r8 break; } -:(scenario cannot_copy_byte_to_ESP_EBP_ESI_EDI) -% Reg[ESI].u = 0xaabbccdd; -% Reg[EBX].u = 0x11223344; -== 0x1 -# op ModR/M SIB displacement immediate - 8a f3 # copy just the byte at *EBX to 8-bit register '6' -# ModR/M in binary: 11 (direct mode) 110 (dest 8-bit 'register 6') 011 (src EBX) -# ensure 8-bit register '6' is DH, not ESI -+run: copy r8/m8-at-r32 to DH -+run: storing 0x44 -# ensure ESI is unchanged -% CHECK_EQ(Reg[ESI].u, 0xaabbccdd); +:(code) +void test_cannot_copy_byte_to_ESP_EBP_ESI_EDI() { + Reg[ESI].u = 0xaabbccdd; + Reg[EBX].u = 0x11223344; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 8a f3 \n" // copy just the byte at *EBX to 8-bit register '6' + // ModR/M in binary: 11 (direct mode) 110 (dest 8-bit 'register 6') 011 (src EBX) + ); + CHECK_TRACE_CONTENTS( + // ensure 8-bit register '6' is DH, not ESI + "run: copy r8/m8-at-r32 to DH\n" + "run: storing 0x44\n" + ); + // ensure ESI is unchanged + CHECK_EQ(Reg[ESI].u, 0xaabbccdd); +} //: :(before "End Initialize Op Names") put_new(Name, "c6", "copy imm8 to r8/m8-at-r32 (mov)"); -:(scenario copy_imm8_to_mem_at_r32) -% Reg[EAX].i = 0x2000; -== 0x1 -# op ModR/M SIB displacement immediate - c6 00 dd # copy to the byte at *EAX -# ModR/M in binary: 00 (indirect mode) 000 (unused) 000 (dest EAX) -== 0x2000 -f0 cc bb aa -+run: copy imm8 to r8/m8-at-r32 -+run: effective address is 0x00002000 (EAX) -+run: storing 0xdd -% CHECK_EQ(0xaabbccdd, read_mem_u32(0x2000)); +:(code) +void test_copy_imm8_to_mem_at_r32() { + Reg[EAX].i = 0x2000; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " c6 00 dd \n" // copy to the byte at *EAX + // ModR/M in binary: 00 (indirect mode) 000 (unused) 000 (dest EAX) + "== 0x2000\n" // data segment + "f0 cc bb aa\n" + ); + CHECK_TRACE_CONTENTS( + "run: copy imm8 to r8/m8-at-r32\n" + "run: effective address is 0x00002000 (EAX)\n" + "run: storing 0xdd\n" + ); + CHECK_EQ(0xaabbccdd, read_mem_u32(0x2000)); +} :(before "End Single-Byte Opcodes") case 0xc6: { // copy imm8 to r/m8 diff --git a/subx/030---operands.cc b/subx/030---operands.cc index 085cf1b2..c8e2942d 100644 --- a/subx/030---operands.cc +++ b/subx/030---operands.cc @@ -27,12 +27,18 @@ put_new(Help, "instructions", :(before "End Help Contents") cerr << " instructions\n"; -:(scenario pack_immediate_constants) -== 0x1 -bb 0x2a/imm32 -+transform: packing instruction 'bb 0x2a/imm32' -+transform: instruction after packing: 'bb 2a 00 00 00' -+run: copy imm32 0x0000002a to EBX +:(code) +void test_pack_immediate_constants() { + run( + "== 0x1\n" // code segment + "bb 0x2a/imm32\n" + ); + CHECK_TRACE_CONTENTS( + "transform: packing instruction 'bb 0x2a/imm32'\n" + "transform: instruction after packing: 'bb 2a 00 00 00'\n" + "run: copy imm32 0x0000002a to EBX\n" + ); +} //: complete set of valid operand types @@ -310,23 +316,31 @@ void test_preserve_metadata_when_emitting_single_byte() { CHECK_EQ(out.words.at(0).original, "f0/foo"); } -:(scenario pack_disp8) -== 0x1 -74 2/disp8 # jump 2 bytes away if ZF is set -+transform: packing instruction '74 2/disp8' -+transform: instruction after packing: '74 02' - -:(scenarios transform) -:(scenario pack_disp8_negative) -== 0x1 -# running this will cause an infinite loop -74 -1/disp8 # jump 1 byte before if ZF is set -+transform: packing instruction '74 -1/disp8' -+transform: instruction after packing: '74 ff' -:(scenarios run) +:(code) +void test_pack_disp8() { + run( + "== 0x1\n" // code segment + "74 2/disp8\n" // jump 2 bytes away if ZF is set + ); + CHECK_TRACE_CONTENTS( + "transform: packing instruction '74 2/disp8'\n" + "transform: instruction after packing: '74 02'\n" + ); +} + +void test_pack_disp8_negative() { + transform( + "== 0x1\n" // code segment + // running this will cause an infinite loop + "74 -1/disp8\n" // jump 1 byte before if ZF is set + ); + CHECK_TRACE_CONTENTS( + "transform: packing instruction '74 -1/disp8'\n" + "transform: instruction after packing: '74 ff'\n" + ); +} //: helper for scenario -:(code) void transform(const string& text_bytes) { program p; istringstream in(text_bytes); @@ -335,47 +349,69 @@ void transform(const string& text_bytes) { transform(p); } -:(scenario pack_modrm_imm32) -== 0x1 -# 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 - 81 0/add/subop 3/mod/direct 3/ebx/rm32 1/imm32 # add 1 to EBX -+transform: packing instruction '81 0/add/subop 3/mod/direct 3/ebx/rm32 1/imm32' -+transform: instruction after packing: '81 c3 01 00 00 00' - -:(scenario pack_imm32_large) -== 0x1 -b9 0x080490a7/imm32 -+transform: packing instruction 'b9 0x080490a7/imm32' -+transform: instruction after packing: 'b9 a7 90 04 08' - -:(scenario pack_immediate_constants_hex) -== 0x1 -b9 0x2a/imm32 -+transform: packing instruction 'b9 0x2a/imm32' -+transform: instruction after packing: 'b9 2a 00 00 00' -+run: copy imm32 0x0000002a to ECX - -:(scenarios transform) -:(scenario pack_silently_ignores_non_hex) -% Hide_errors = true; -== 0x1 -b9 foo/imm32 -+transform: packing instruction 'b9 foo/imm32' -# no change (we're just not printing metadata to the trace) -+transform: instruction after packing: 'b9 foo' -:(scenarios run) - -:(scenario pack_flags_bad_hex) -% Hide_errors = true; -== 0x1 -b9 0xfoo/imm32 -+error: not a number: 0xfoo +void test_pack_modrm_imm32() { + run( + "== 0x1\n" // code segment + // instruction effective address operand displacement immediate\n" + // op subop mod rm32 base index scale r32\n" + // 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\n" + " 81 0/add/subop 3/mod/direct 3/ebx/rm32 1/imm32 \n" // add 1 to EBX + ); + CHECK_TRACE_CONTENTS( + "transform: packing instruction '81 0/add/subop 3/mod/direct 3/ebx/rm32 1/imm32'\n" + "transform: instruction after packing: '81 c3 01 00 00 00'\n" + ); +} + +void test_pack_imm32_large() { + run( + "== 0x1\n" // code segment + "b9 0x080490a7/imm32\n" + ); + CHECK_TRACE_CONTENTS( + "transform: packing instruction 'b9 0x080490a7/imm32'\n" + "transform: instruction after packing: 'b9 a7 90 04 08'\n" + ); +} + +void test_pack_immediate_constants_hex() { + run( + "== 0x1\n" // code segment + "b9 0x2a/imm32\n" + ); + CHECK_TRACE_CONTENTS( + "transform: packing instruction 'b9 0x2a/imm32'\n" + "transform: instruction after packing: 'b9 2a 00 00 00'\n" + "run: copy imm32 0x0000002a to ECX\n" + ); +} + +void test_pack_silently_ignores_non_hex() { + Hide_errors = true; + transform( + "== 0x1\n" // code segment + "b9 foo/imm32\n" + ); + CHECK_TRACE_CONTENTS( + "transform: packing instruction 'b9 foo/imm32'\n" + // no change (we're just not printing metadata to the trace) + "transform: instruction after packing: 'b9 foo'\n" + ); +} + +void test_pack_flags_bad_hex() { + Hide_errors = true; + run( + "== 0x1\n" // code segment + "b9 0xfoo/imm32\n" + ); + CHECK_TRACE_CONTENTS( + "error: not a number: 0xfoo\n" + ); +} //:: helpers -:(code) bool all_hex_bytes(const line& inst) { for (int i = 0; i < SIZE(inst.words); ++i) if (!is_hex_byte(inst.words.at(i))) @@ -444,7 +480,6 @@ bool looks_like_hex_int(const string& s) { return false; } -:(code) string to_string(const line& inst) { ostringstream out; for (int i = 0; i < SIZE(inst.words); ++i) { diff --git a/subx/031check_operands.cc b/subx/031check_operands.cc index bc76fca5..475cda5c 100644 --- a/subx/031check_operands.cc +++ b/subx/031check_operands.cc @@ -1,11 +1,16 @@ //: Since we're tagging operands with their types, let's start checking these //: operand types for each instruction. -:(scenario check_missing_imm8_operand) -% Hide_errors = true; -== 0x1 -cd # int ?? -+error: 'cd' (software interrupt): missing imm8 operand +void test_check_missing_imm8_operand() { + Hide_errors = true; + run( + "== 0x1\n" // code segment + "cd\n" // interrupt ?? + ); + CHECK_TRACE_CONTENTS( + "error: 'cd' (software interrupt): missing imm8 operand\n" + ); +} :(before "Pack Operands(segment code)") check_operands(code); @@ -239,7 +244,6 @@ void init_permitted_operands() { // End Init Permitted Operands } -:(code) #define HAS(bitvector, bit) ((bitvector) & (1 << (bit))) #define SET(bitvector, bit) ((bitvector) | (1 << (bit))) #define CLEAR(bitvector, bit) ((bitvector) & (~(1 << (bit)))) @@ -335,22 +339,31 @@ uint32_t expected_bit_for_received_operand(const word& w, set<string>& instructi return bv; } -:(scenario conflicting_operand_type) -% Hide_errors = true; -== 0x1 -cd/software-interrupt 80/imm8/imm32 -+error: '80/imm8/imm32' has conflicting operand types; it should have only one +void test_conflicting_operand_type() { + Hide_errors = true; + run( + "== 0x1\n" // code segment + "cd/software-interrupt 80/imm8/imm32\n" + ); + CHECK_TRACE_CONTENTS( + "error: '80/imm8/imm32' has conflicting operand types; it should have only one\n" + ); +} //: Instructions computing effective addresses have more complex rules, so //: we'll hard-code a common set of instruction-decoding rules. -:(scenario check_missing_mod_operand) -% Hide_errors = true; -== 0x1 -81 0/add/subop 3/rm32/ebx 1/imm32 -+error: '81 0/add/subop 3/rm32/ebx 1/imm32' (combine rm32 with imm32 based on subop): missing mod operand +void test_check_missing_mod_operand() { + Hide_errors = true; + run( + "== 0x1\n" // code segment + "81 0/add/subop 3/rm32/ebx 1/imm32\n" + ); + CHECK_TRACE_CONTENTS( + "error: '81 0/add/subop 3/rm32/ebx 1/imm32' (combine rm32 with imm32 based on subop): missing mod operand\n" + ); +} -:(code) void check_operands_modrm(const line& inst, const word& op) { if (all_hex_bytes(inst)) return; // deliberately programming in raw hex; we'll raise a warning elsewhere check_operand_metadata_present(inst, "mod", op); @@ -421,95 +434,158 @@ void check_operand_metadata_absent(const line& inst, const string& type, const w raise << "'" << to_string(inst) << "'" << maybe_name(op) << ": unexpected " << type << " operand (" << msg << ")\n" << end(); } -:(scenarios transform) -:(scenario modrm_with_displacement) -% Reg[EAX].u = 0x1; -== 0x1 -# just avoid null pointer -8b/copy 1/mod/lookup+disp8 0/rm32/EAX 2/r32/EDX 4/disp8 # copy *(EAX+4) to EDX -$error: 0 - -:(scenario check_missing_disp8) -% Hide_errors = true; -== 0x1 -89/copy 1/mod/lookup+disp8 0/rm32/EAX 1/r32/ECX # missing disp8 -+error: '89/copy 1/mod/lookup+disp8 0/rm32/EAX 1/r32/ECX' (copy r32 to rm32): missing disp8 operand - -:(scenario check_missing_disp32) -% Hide_errors = true; -== 0x1 -8b/copy 0/mod/indirect 5/rm32/.disp32 2/r32/EDX # missing disp32 -+error: '8b/copy 0/mod/indirect 5/rm32/.disp32 2/r32/EDX' (copy rm32 to r32): missing disp32 operand -:(scenarios run) - -:(scenario conflicting_operands_in_modrm_instruction) -% Hide_errors = true; -== 0x1 -01/add 0/mod 3/mod -+error: '01/add 0/mod 3/mod' has conflicting mod operands - -:(scenario conflicting_operand_type_modrm) -% Hide_errors = true; -== 0x1 -01/add 0/mod 3/rm32/r32 -+error: '3/rm32/r32' has conflicting operand types; it should have only one - -:(scenario check_missing_rm32_operand) -% Hide_errors = true; -== 0x1 -81 0/add/subop 0/mod 1/imm32 -+error: '81 0/add/subop 0/mod 1/imm32' (combine rm32 with imm32 based on subop): missing rm32 operand - -:(scenario check_missing_subop_operand) -% Hide_errors = true; -== 0x1 -81 0/mod 3/rm32/ebx 1/imm32 -+error: '81 0/mod 3/rm32/ebx 1/imm32' (combine rm32 with imm32 based on subop): missing subop operand - -:(scenario check_missing_base_operand) -% Hide_errors = true; -== 0x1 -81 0/add/subop 0/mod/indirect 4/rm32/use-sib 1/imm32 -+error: '81 0/add/subop 0/mod/indirect 4/rm32/use-sib 1/imm32' (combine rm32 with imm32 based on subop): missing base operand - -:(scenario check_missing_index_operand) -% Hide_errors = true; -== 0x1 -81 0/add/subop 0/mod/indirect 4/rm32/use-sib 0/base 1/imm32 -+error: '81 0/add/subop 0/mod/indirect 4/rm32/use-sib 0/base 1/imm32' (combine rm32 with imm32 based on subop): missing index operand - -:(scenario check_missing_base_operand_2) -% Hide_errors = true; -== 0x1 -81 0/add/subop 0/mod/indirect 4/rm32/use-sib 2/index 3/scale 1/imm32 -+error: '81 0/add/subop 0/mod/indirect 4/rm32/use-sib 2/index 3/scale 1/imm32' (combine rm32 with imm32 based on subop): missing base operand - -:(scenario check_extra_displacement) -% Hide_errors = true; -== 0x1 -89/copy 0/mod/indirect 0/rm32/EAX 1/r32/ECX 4/disp8 -+error: '89/copy 0/mod/indirect 0/rm32/EAX 1/r32/ECX 4/disp8' (copy r32 to rm32): unexpected disp8 operand - -:(scenario check_duplicate_operand) -% Hide_errors = true; -== 0x1 -89/copy 0/mod/indirect 0/rm32/EAX 1/r32/ECX 1/r32 -+error: '89/copy 0/mod/indirect 0/rm32/EAX 1/r32/ECX 1/r32': duplicate r32 operand - -:(scenario check_base_operand_not_needed_in_direct_mode) -== 0x1 -81 0/add/subop 3/mod/indirect 4/rm32/use-sib 1/imm32 -$error: 0 - -:(scenario extra_modrm) -% Hide_errors = true; -== 0x1 -59/pop-to-ECX 3/mod/direct 1/rm32/ECX 4/r32/ESP -+error: '59/pop-to-ECX 3/mod/direct 1/rm32/ECX 4/r32/ESP' (pop top of stack to ECX): unexpected modrm operand +void test_modrm_with_displacement() { + Reg[EAX].u = 0x1; + transform( + "== 0x1\n" + // just avoid null pointer + "8b/copy 1/mod/lookup+disp8 0/rm32/EAX 2/r32/EDX 4/disp8\n" // copy *(EAX+4) to EDX + ); + CHECK_TRACE_COUNT("error", 0); +} + +void test_check_missing_disp8() { + Hide_errors = true; + transform( + "== 0x1\n" // code segment + "89/copy 1/mod/lookup+disp8 0/rm32/EAX 1/r32/ECX\n" // missing disp8 + ); + CHECK_TRACE_CONTENTS( + "error: '89/copy 1/mod/lookup+disp8 0/rm32/EAX 1/r32/ECX' (copy r32 to rm32): missing disp8 operand\n" + ); +} + +void test_check_missing_disp32() { + Hide_errors = true; + transform( + "== 0x1\n" // code segment + "8b/copy 0/mod/indirect 5/rm32/.disp32 2/r32/EDX\n" // missing disp32 + ); + CHECK_TRACE_CONTENTS( + "error: '8b/copy 0/mod/indirect 5/rm32/.disp32 2/r32/EDX' (copy rm32 to r32): missing disp32 operand\n" + ); +} + +void test_conflicting_operands_in_modrm_instruction() { + Hide_errors = true; + run( + "== 0x1\n" // code segment + "01/add 0/mod 3/mod\n" + ); + CHECK_TRACE_CONTENTS( + "error: '01/add 0/mod 3/mod' has conflicting mod operands\n" + ); +} + +void test_conflicting_operand_type_modrm() { + Hide_errors = true; + run( + "== 0x1\n" // code segment + "01/add 0/mod 3/rm32/r32\n" + ); + CHECK_TRACE_CONTENTS( + "error: '3/rm32/r32' has conflicting operand types; it should have only one\n" + ); +} + +void test_check_missing_rm32_operand() { + Hide_errors = true; + run( + "== 0x1\n" // code segment + "81 0/add/subop 0/mod 1/imm32\n" + ); + CHECK_TRACE_CONTENTS( + "error: '81 0/add/subop 0/mod 1/imm32' (combine rm32 with imm32 based on subop): missing rm32 operand\n" + ); +} + +void test_check_missing_subop_operand() { + Hide_errors = true; + run( + "== 0x1\n" // code segment + "81 0/mod 3/rm32/ebx 1/imm32\n" + ); + CHECK_TRACE_CONTENTS( + "error: '81 0/mod 3/rm32/ebx 1/imm32' (combine rm32 with imm32 based on subop): missing subop operand\n" + ); +} + +void test_check_missing_base_operand() { + Hide_errors = true; + run( + "== 0x1\n" // code segment + "81 0/add/subop 0/mod/indirect 4/rm32/use-sib 1/imm32\n" + ); + CHECK_TRACE_CONTENTS( + "error: '81 0/add/subop 0/mod/indirect 4/rm32/use-sib 1/imm32' (combine rm32 with imm32 based on subop): missing base operand\n" + ); +} + +void test_check_missing_index_operand() { + Hide_errors = true; + run( + "== 0x1\n" // code segment + "81 0/add/subop 0/mod/indirect 4/rm32/use-sib 0/base 1/imm32\n" + ); + CHECK_TRACE_CONTENTS( + "error: '81 0/add/subop 0/mod/indirect 4/rm32/use-sib 0/base 1/imm32' (combine rm32 with imm32 based on subop): missing index operand\n" + ); +} + +void test_check_missing_base_operand_2() { + Hide_errors = true; + run( + "== 0x1\n" // code segment + "81 0/add/subop 0/mod/indirect 4/rm32/use-sib 2/index 3/scale 1/imm32\n" + ); + CHECK_TRACE_CONTENTS( + "error: '81 0/add/subop 0/mod/indirect 4/rm32/use-sib 2/index 3/scale 1/imm32' (combine rm32 with imm32 based on subop): missing base operand\n" + ); +} + +void test_check_extra_displacement() { + Hide_errors = true; + run( + "== 0x1\n" // code segment + "89/copy 0/mod/indirect 0/rm32/EAX 1/r32/ECX 4/disp8\n" + ); + CHECK_TRACE_CONTENTS( + "error: '89/copy 0/mod/indirect 0/rm32/EAX 1/r32/ECX 4/disp8' (copy r32 to rm32): unexpected disp8 operand\n" + ); +} + +void test_check_duplicate_operand() { + Hide_errors = true; + run( + "== 0x1\n" // code segment + "89/copy 0/mod/indirect 0/rm32/EAX 1/r32/ECX 1/r32\n" + ); + CHECK_TRACE_CONTENTS( + "error: '89/copy 0/mod/indirect 0/rm32/EAX 1/r32/ECX 1/r32': duplicate r32 operand\n" + ); +} + +void test_check_base_operand_not_needed_in_direct_mode() { + run( + "== 0x1\n" // code segment + "81 0/add/subop 3/mod/indirect 4/rm32/use-sib 1/imm32\n" + ); + CHECK_TRACE_COUNT("error", 0); +} + +void test_extra_modrm() { + Hide_errors = true; + run( + "== 0x1\n" // code segment + "59/pop-to-ECX 3/mod/direct 1/rm32/ECX 4/r32/ESP\n" + ); + CHECK_TRACE_CONTENTS( + "error: '59/pop-to-ECX 3/mod/direct 1/rm32/ECX 4/r32/ESP' (pop top of stack to ECX): unexpected modrm operand\n" + ); +} //:: similarly handle multi-byte opcodes -:(code) void check_operands_0f(const line& inst) { assert(inst.words.at(0).data == "0f"); if (SIZE(inst.words) == 1) { @@ -528,14 +604,16 @@ void check_operands_f3(const line& /*unused*/) { raise << "no supported opcodes starting with f3\n" << end(); } -:(scenario check_missing_disp32_operand) -% Hide_errors = true; -== 0x1 -# 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 - 0f 84 # jmp if ZF to ?? -+error: '0f 84' (jump disp32 bytes away if equal, if ZF is set): missing disp32 operand +void test_check_missing_disp32_operand() { + Hide_errors = true; + run( + "== 0x1\n" // code segment + " 0f 84 # jmp if ZF to ??\n" + ); + CHECK_TRACE_CONTENTS( + "error: '0f 84' (jump disp32 bytes away if equal, if ZF is set): missing disp32 operand\n" + ); +} :(before "End Globals") map</*op*/string, /*bitvector*/uint8_t> Permitted_operands_0f; diff --git a/subx/032check_operand_bounds.cc b/subx/032check_operand_bounds.cc index 58b7c7b9..ca114e22 100644 --- a/subx/032check_operand_bounds.cc +++ b/subx/032check_operand_bounds.cc @@ -1,10 +1,15 @@ //:: Check that the different operands of an instruction aren't too large for their bitfields. -:(scenario check_bitfield_sizes) -% Hide_errors = true; -== 0x1 -01/add 4/mod 3/rm32 1/r32 # add ECX to EBX -+error: '4/mod' too large to fit in bitfield mod +void test_check_bitfield_sizes() { + Hide_errors = true; + run( + "== 0x1\n" // code segment + "01/add 4/mod 3/rm32 1/r32\n" // add ECX to EBX + ); + CHECK_TRACE_CONTENTS( + "error: '4/mod' too large to fit in bitfield mod\n" + ); +} :(before "End Globals") map<string, uint32_t> Operand_bound; diff --git a/subx/034compute_segment_address.cc b/subx/034compute_segment_address.cc index 79c6e45d..a1b7482d 100644 --- a/subx/034compute_segment_address.cc +++ b/subx/034compute_segment_address.cc @@ -2,17 +2,22 @@ //: segment. //: This gives up a measure of control in placing code and data. -:(scenario segment_name) -== code -05/add-to-EAX 0x0d0c0b0a/imm32 -# code starts at 0x08048000 + p_offset, which is 0x54 for a single-segment binary -+load: 0x09000054 -> 05 -+load: 0x09000055 -> 0a -+load: 0x09000056 -> 0b -+load: 0x09000057 -> 0c -+load: 0x09000058 -> 0d -+run: add imm32 0x0d0c0b0a to reg EAX -+run: storing 0x0d0c0b0a +void test_segment_name() { + run( + "== code\n" + "05/add-to-EAX 0x0d0c0b0a/imm32\n" + // code starts at 0x08048000 + p_offset, which is 0x54 for a single-segment binary + ); + CHECK_TRACE_CONTENTS( + "load: 0x09000054 -> 05\n" + "load: 0x09000055 -> 0a\n" + "load: 0x09000056 -> 0b\n" + "load: 0x09000057 -> 0c\n" + "load: 0x09000058 -> 0d\n" + "run: add imm32 0x0d0c0b0a to reg EAX\n" + "run: storing 0x0d0c0b0a\n" + ); +} //: Update the parser to handle non-numeric segment name. //: @@ -61,44 +66,65 @@ if (Currently_parsing_named_segment) { return; } -:(scenario repeated_segment_merges_data) -== code -05/add-to-EAX 0x0d0c0b0a/imm32 -== code -2d/subtract-from-EAX 0xddccbbaa/imm32 -+parse: new segment 'code' -+parse: appending to segment 'code' -# first segment -+load: 0x09000054 -> 05 -+load: 0x09000055 -> 0a -+load: 0x09000056 -> 0b -+load: 0x09000057 -> 0c -+load: 0x09000058 -> 0d -# second segment -+load: 0x09000059 -> 2d -+load: 0x0900005a -> aa -+load: 0x0900005b -> bb -+load: 0x0900005c -> cc -+load: 0x0900005d -> dd - -:(scenario error_on_missing_segment_header) -% Hide_errors = true; -05/add-to-EAX 0/imm32 -+error: input does not start with a '==' section header - -:(scenario error_on_first_segment_not_code) -% Hide_errors = true; -== data -05 00 00 00 00 -+error: first segment must be 'code' but is 'data' - -:(scenario error_on_second_segment_not_data) -% Hide_errors = true; -== code -05/add-to-EAX 0/imm32 -== bss -05 00 00 00 00 -+error: second segment must be 'data' but is 'bss' +:(code) +void test_repeated_segment_merges_data() { + run( + "== code\n" + "05/add-to-EAX 0x0d0c0b0a/imm32\n" + "== code\n" // again + "2d/subtract-from-EAX 0xddccbbaa/imm32\n" + ); + CHECK_TRACE_CONTENTS( + "parse: new segment 'code'\n" + "parse: appending to segment 'code'\n" + // first segment + "load: 0x09000054 -> 05\n" + "load: 0x09000055 -> 0a\n" + "load: 0x09000056 -> 0b\n" + "load: 0x09000057 -> 0c\n" + "load: 0x09000058 -> 0d\n" + // second segment + "load: 0x09000059 -> 2d\n" + "load: 0x0900005a -> aa\n" + "load: 0x0900005b -> bb\n" + "load: 0x0900005c -> cc\n" + "load: 0x0900005d -> dd\n" + ); +} + +void test_error_on_missing_segment_header() { + Hide_errors = true; + run( + "05/add-to-EAX 0/imm32\n" + ); + CHECK_TRACE_CONTENTS( + "error: input does not start with a '==' section header\n" + ); +} + +void test_error_on_first_segment_not_code() { + Hide_errors = true; + run( + "== data\n" + "05 00 00 00 00\n" + ); + CHECK_TRACE_CONTENTS( + "error: first segment must be 'code' but is 'data'\n" + ); +} + +void test_error_on_second_segment_not_data() { + Hide_errors = true; + run( + "== code\n" + "05/add-to-EAX 0/imm32\n" + "== bss\n" + "05 00 00 00 00\n" + ); + CHECK_TRACE_CONTENTS( + "error: second segment must be 'data' but is 'bss'\n" + ); +} //: compute segment address diff --git a/subx/035labels.cc b/subx/035labels.cc index 4c5ea641..322d1952 100644 --- a/subx/035labels.cc +++ b/subx/035labels.cc @@ -21,13 +21,18 @@ //: One special label: the address to start running the program at. -:(scenario entry_label) -== 0x1 -05 0x0d0c0b0a/imm32 -Entry: -05 0x0d0c0b0a/imm32 -+run: 0x00000006 opcode: 05 --run: 0x00000001 opcode: 05 +void test_entry_label() { + run( + "== 0x1\n" // code segment + "05 0x0d0c0b0a/imm32\n" + "Entry:\n" + "05 0x0d0c0b0a/imm32\n" + ); + CHECK_TRACE_CONTENTS( + "run: 0x00000006 opcode: 05\n" + ); + CHECK_TRACE_DOESNT_CONTAIN("run: 0x00000001 opcode: 05"); +} :(before "End Globals") uint32_t Entry_address = 0; @@ -41,33 +46,47 @@ if (Entry_address) e_entry = Entry_address; :(before "End looks_like_hex_int(s) Detectors") if (SIZE(s) == 2) return true; -:(scenarios transform) -:(scenario pack_immediate_ignores_single_byte_nondigit_operand) -% Hide_errors = true; -== 0x1 -b9/copy a/imm32 -+transform: packing instruction 'b9/copy a/imm32' -# no change (we're just not printing metadata to the trace) -+transform: instruction after packing: 'b9 a' +:(code) +void test_pack_immediate_ignores_single_byte_nondigit_operand() { + Hide_errors = true; + transform( + "== 0x1\n" // code segment + "b9/copy a/imm32\n" + ); + CHECK_TRACE_CONTENTS( + "transform: packing instruction 'b9/copy a/imm32'\n" + // no change (we're just not printing metadata to the trace) + "transform: instruction after packing: 'b9 a'\n" + ); +} -:(scenario pack_immediate_ignores_3_hex_digit_operand) -% Hide_errors = true; -== 0x1 -b9/copy aaa/imm32 -+transform: packing instruction 'b9/copy aaa/imm32' -# no change (we're just not printing metadata to the trace) -+transform: instruction after packing: 'b9 aaa' +void test_pack_immediate_ignores_3_hex_digit_operand() { + Hide_errors = true; + transform( + "== 0x1\n" // code segment + "b9/copy aaa/imm32\n" + ); + CHECK_TRACE_CONTENTS( + "transform: packing instruction 'b9/copy aaa/imm32'\n" + // no change (we're just not printing metadata to the trace) + "transform: instruction after packing: 'b9 aaa'\n" + ); +} -:(scenario pack_immediate_ignores_non_hex_operand) -% Hide_errors = true; -== 0x1 -b9/copy xxx/imm32 -+transform: packing instruction 'b9/copy xxx/imm32' -# no change (we're just not printing metadata to the trace) -+transform: instruction after packing: 'b9 xxx' +void test_pack_immediate_ignores_non_hex_operand() { + Hide_errors = true; + transform( + "== 0x1\n" // code segment + "b9/copy xxx/imm32\n" + ); + CHECK_TRACE_CONTENTS( + "transform: packing instruction 'b9/copy xxx/imm32'\n" + // no change (we're just not printing metadata to the trace) + "transform: instruction after packing: 'b9 xxx'\n" + ); +} //: a helper we'll find handy later -:(code) void check_valid_name(const string& s) { if (s.empty()) { raise << "empty name!\n" << end(); @@ -87,11 +106,16 @@ void check_valid_name(const string& s) { //: Now that that's done, let's start using names as labels. -:(scenario map_label) -== 0x1 -loop: - 05 0x0d0c0b0a/imm32 -+transform: label 'loop' is at address 1 +void test_map_label() { + transform( + "== 0x1\n" // code segment + "loop:\n" + " 05 0x0d0c0b0a/imm32\n" + ); + CHECK_TRACE_CONTENTS( + "transform: label 'loop' is at address 1\n" + ); +} :(before "End Level-2 Transforms") Transform.push_back(rewrite_labels); @@ -241,67 +265,97 @@ string drop_last(const string& s) { //: However, you can absolutely have multiple labels map to the same address, //: as long as they're on separate lines. -:(scenario multiple_labels_at) -== 0x1 -# address 1 -loop: - $loop2: -# address 1 (labels take up no space) - 05 0x0d0c0b0a/imm32 -# address 6 - eb $loop2/disp8 -# address 8 - eb $loop3/disp8 -# address 0xa - $loop3: -+transform: label 'loop' is at address 1 -+transform: label '$loop2' is at address 1 -+transform: label '$loop3' is at address a -# first jump is to -7 -+transform: instruction after transform: 'eb f9' -# second jump is to 0 (fall through) -+transform: instruction after transform: 'eb 00' +void test_multiple_labels_at() { + transform( + "== 0x1\n" // code segment + // address 1 + "loop:\n" + " $loop2:\n" + // address 1 (labels take up no space) + " 05 0x0d0c0b0a/imm32\n" + // address 6 + " eb $loop2/disp8\n" + // address 8 + " eb $loop3/disp8\n" + // address 0xa + " $loop3:\n" + ); + CHECK_TRACE_CONTENTS( + "transform: label 'loop' is at address 1\n" + "transform: label '$loop2' is at address 1\n" + "transform: label '$loop3' is at address a\n" + // first jump is to -7 + "transform: instruction after transform: 'eb f9'\n" + // second jump is to 0 (fall through) + "transform: instruction after transform: 'eb 00'\n" + ); +} -:(scenario duplicate_label) -% Hide_errors = true; -== 0x1 -loop: -loop: - 05 0x0d0c0b0a/imm32 -+error: duplicate label 'loop' +void test_duplicate_label() { + Hide_errors = true; + transform( + "== 0x1\n" + "loop:\n" + "loop:\n" + " 05 0x0d0c0b0a/imm32\n" + ); + CHECK_TRACE_CONTENTS( + "error: duplicate label 'loop'\n" + ); +} -:(scenario label_too_short) -% Hide_errors = true; -== 0x1 -xz: - 05 0x0d0c0b0a/imm32 -+error: 'xz' is two characters long which can look like raw hex bytes at a glance; use a different name +void test_label_too_short() { + Hide_errors = true; + transform( + "== 0x1\n" + "xz:\n" + " 05 0x0d0c0b0a/imm32\n" + ); + CHECK_TRACE_CONTENTS( + "error: 'xz' is two characters long which can look like raw hex bytes at a glance; use a different name\n" + ); +} -:(scenario label_hex) -% Hide_errors = true; -== 0x1 -0xab: - 05 0x0d0c0b0a/imm32 -+error: '0xab' looks like a hex number; use a different name +void test_label_hex() { + Hide_errors = true; + transform( + "== 0x1\n" + "0xab:\n" + " 05 0x0d0c0b0a/imm32\n" + ); + CHECK_TRACE_CONTENTS( + "error: '0xab' looks like a hex number; use a different name\n" + ); +} -:(scenario label_negative_hex) -% Hide_errors = true; -== 0x1 - -a: # indent to avoid looking like a trace_should_not_contain command for this scenario - 05 0x0d0c0b0a/imm32 -+error: '-a' starts with '-', which can be confused with a negative number; use a different name +void test_label_negative_hex() { + Hide_errors = true; + transform( + "== 0x1\n" + "-a:\n" + " 05 0x0d0c0b0a/imm32\n" + ); + CHECK_TRACE_CONTENTS( + "error: '-a' starts with '-', which can be confused with a negative number; use a different name\n" + ); +} //: now that we have labels, we need to adjust segment size computation to //: ignore them. -:(scenario segment_size_ignores_labels) -== code # 0x09000074 - 05/add 0x0d0c0b0a/imm32 # 5 bytes -foo: # 0 bytes -== data # 0x0a000079 -bar: - 00 -+transform: segment 1 begins at address 0x0a000079 +void test_segment_size_ignores_labels() { + transform( + "== code\n" // 0x09000074 + " 05/add 0x0d0c0b0a/imm32\n" // 5 bytes + "foo:\n" // 0 bytes + "== data\n" // 0x0a000079 + "bar:\n" + " 00\n" + ); + CHECK_TRACE_CONTENTS( + "transform: segment 1 begins at address 0x0a000079\n" + ); +} :(before "End size_of(word w) Special-cases") else if (is_label(w)) diff --git a/subx/036global_variables.cc b/subx/036global_variables.cc index 2d20ca43..846cd291 100644 --- a/subx/036global_variables.cc +++ b/subx/036global_variables.cc @@ -6,13 +6,19 @@ //: //: This layer has much the same structure as rewriting labels. -:(scenario global_variable) -== code -b9 x/imm32 -== data -x: - 00 00 00 00 -+transform: global variable 'x' is at address 0x0a000079 +:(code) +void test_global_variable() { + run( + "== code\n" + "b9 x/imm32\n" + "== data\n" + "x:\n" + " 00 00 00 00\n" + ); + CHECK_TRACE_CONTENTS( + "transform: global variable 'x' is at address 0x0a000079\n" + ); +} :(before "End Level-2 Transforms") Transform.push_back(rewrite_global_variables); @@ -173,83 +179,112 @@ bool has_metadata(const word& w, const string& m) { return false; } -:(scenario global_variable_disallowed_in_jump) -% Hide_errors = true; -== code -eb/jump x/disp8 -== data -x: - 00 00 00 00 -+error: 'eb/jump x/disp8': can't refer to global variable 'x' -# sub-optimal error message; should be -#? +error: can't jump to data (variable 'x') +void test_global_variable_disallowed_in_jump() { + Hide_errors = true; + run( + "== code\n" + "eb/jump x/disp8\n" + "== data\n" + "x:\n" + " 00 00 00 00\n" + ); + CHECK_TRACE_CONTENTS( + "error: 'eb/jump x/disp8': can't refer to global variable 'x'\n" + // sub-optimal error message; should be +//? "error: can't jump to data (variable 'x')\n" + ); +} -:(scenario global_variable_disallowed_in_call) -% Hide_errors = true; -== code -e8/call x/disp32 -== data -x: - 00 00 00 00 -+error: 'e8/call x/disp32': can't refer to global variable 'x' -# sub-optimal error message; should be -#? +error: can't call to the data segment ('x') +void test_global_variable_disallowed_in_call() { + Hide_errors = true; + run( + "== code\n" + "e8/call x/disp32\n" + "== data\n" + "x:\n" + " 00 00 00 00\n" + ); + CHECK_TRACE_CONTENTS( + "error: 'e8/call x/disp32': can't refer to global variable 'x'\n" + // sub-optimal error message; should be +//? "error: can't call to the data segment ('x')\n" + ); +} -:(scenario global_variable_in_data_segment) -== 0x1 -b9 x/imm32 -== 0x0a000000 -x: - y/imm32 -y: - 00 00 00 00 -# check that we loaded 'x' with the address of 'y' -+load: 0x0a000000 -> 04 -+load: 0x0a000001 -> 00 -+load: 0x0a000002 -> 00 -+load: 0x0a000003 -> 0a -$error: 0 +void test_global_variable_in_data_segment() { + run( + "== 0x1\n" + "b9 x/imm32\n" + "== 0x0a000000\n" + "x:\n" + " y/imm32\n" + "y:\n" + " 00 00 00 00\n" + ); + // check that we loaded 'x' with the address of 'y' + CHECK_TRACE_CONTENTS( + "load: 0x0a000000 -> 04\n" + "load: 0x0a000001 -> 00\n" + "load: 0x0a000002 -> 00\n" + "load: 0x0a000003 -> 0a\n" + ); + CHECK_TRACE_COUNT("error", 0); +} -:(scenario raw_number_with_imm32_in_data_segment) -== 0x1 -b9 x/imm32 -== 0x0a000000 -x: - 1/imm32 -# check that we loaded 'x' with the address of 1 -+load: 0x0a000000 -> 01 -+load: 0x0a000001 -> 00 -+load: 0x0a000002 -> 00 -+load: 0x0a000003 -> 00 -$error: 0 +void test_raw_number_with_imm32_in_data_segment() { + run( + "== 0x1\n" + "b9 x/imm32\n" + "== 0x0a000000\n" + "x:\n" + " 1/imm32\n" + ); + // check that we loaded 'x' with the address of 1 + CHECK_TRACE_CONTENTS( + "load: 0x0a000000 -> 01\n" + "load: 0x0a000001 -> 00\n" + "load: 0x0a000002 -> 00\n" + "load: 0x0a000003 -> 00\n" + ); + CHECK_TRACE_COUNT("error", 0); +} -:(scenario duplicate_global_variable) -% Hide_errors = true; -== 0x1 -40/increment-EAX -== 0x0a000000 -x: -x: - 00 -+error: duplicate global 'x' +void test_duplicate_global_variable() { + Hide_errors = true; + run( + "== 0x1\n" + "40/increment-EAX\n" + "== 0x0a000000\n" + "x:\n" + "x:\n" + " 00\n" + ); + CHECK_TRACE_CONTENTS( + "error: duplicate global 'x'\n" + ); +} -:(scenario global_variable_disp32_with_modrm) -== code -8b/copy 0/mod/indirect 5/rm32/.disp32 2/r32/EDX x/disp32 -== data -x: - 00 00 00 00 -$error: 0 +void test_global_variable_disp32_with_modrm() { + run( + "== code\n" + "8b/copy 0/mod/indirect 5/rm32/.disp32 2/r32/EDX x/disp32\n" + "== data\n" + "x:\n" + " 00 00 00 00\n" + ); + CHECK_TRACE_COUNT("error", 0); +} -:(scenarios transform) -:(scenario global_variable_disp32_with_call) -== code -foo: - e8/call bar/disp32 -bar: -$error: 0 +void test_global_variable_disp32_with_call() { + transform( + "== code\n" + "foo:\n" + " e8/call bar/disp32\n" + "bar:\n" + ); + CHECK_TRACE_COUNT("error", 0); +} -:(code) string to_full_string(const line& in) { ostringstream out; for (int i = 0; i < SIZE(in.words); ++i) { diff --git a/subx/038---literal_strings.cc b/subx/038---literal_strings.cc index ce46a119..b7cb0aa4 100644 --- a/subx/038---literal_strings.cc +++ b/subx/038---literal_strings.cc @@ -3,13 +3,18 @@ //: This layer will transparently move them to the global segment (assumed to //: always be the second segment). -:(scenario transform_literal_string) -== code -b8/copy "test"/imm32 -== data # need to manually create this for now -+transform: -- move literal strings to data segment -+transform: adding global variable '__subx_global_1' containing "test" -+transform: instruction after transform: 'b8 __subx_global_1' +void test_transform_literal_string() { + run( + "== code\n" + "b8/copy \"test\"/imm32\n" + "== data\n" // need to manually create the segment for now + ); + CHECK_TRACE_CONTENTS( + "transform: -- move literal strings to data segment\n" + "transform: adding global variable '__subx_global_1' containing \"test\"\n" + "transform: instruction after transform: 'b8 __subx_global_1'\n" + ); +} //: We don't rely on any transforms running in previous layers, but this layer //: knows about labels and global variables and will emit them for previous @@ -70,14 +75,18 @@ void add_global_to_data_segment(const string& name, const word& value, segment& //: Within strings, whitespace is significant. So we need to redo our instruction //: parsing. -:(scenarios parse_instruction_character_by_character) -:(scenario instruction_with_string_literal) -a "abc def" z # two spaces inside string -+parse2: word: a -+parse2: word: "abc def" -+parse2: word: z -# no other words -$parse2: 3 +void test_instruction_with_string_literal() { + parse_instruction_character_by_character( + "a \"abc def\" z\n" // two spaces inside string + ); + CHECK_TRACE_CONTENTS( + "parse2: word: a\n" + "parse2: word: \"abc def\"\n" + "parse2: word: z\n" + ); + // no other words + CHECK_TRACE_COUNT("parse2", 3); +} :(before "End Line Parsing Special-cases(line_data -> l)") if (line_data.find('"') != string::npos) { // can cause false-positives, but we can handle them @@ -168,68 +177,113 @@ void parse_instruction_character_by_character(const string& line_data) { parse_instruction_character_by_character(line_data, out); } -:(scenario parse2_comment_token_in_middle) -a . z -+parse2: word: a -+parse2: word: z --parse2: word: . -# no other words -$parse2: 2 - -:(scenario parse2_word_starting_with_dot) -a .b c -+parse2: word: a -+parse2: word: .b -+parse2: word: c - -:(scenario parse2_comment_token_at_start) -. a b -+parse2: word: a -+parse2: word: b --parse2: word: . - -:(scenario parse2_comment_token_at_end) -a b . -+parse2: word: a -+parse2: word: b --parse2: word: . - -:(scenario parse2_word_starting_with_dot_at_start) -.a b c -+parse2: word: .a -+parse2: word: b -+parse2: word: c - -:(scenario parse2_metadata) -.a b/c d -+parse2: word: .a -+parse2: word: b /c -+parse2: word: d - -:(scenario parse2_string_with_metadata) -a "bc def"/disp32 g -+parse2: word: a -+parse2: word: "bc def" /disp32 -+parse2: word: g - -:(scenario parse2_string_with_metadata_at_end) -a "bc def"/disp32 -+parse2: word: a -+parse2: word: "bc def" /disp32 +void test_parse2_comment_token_in_middle() { + parse_instruction_character_by_character( + "a . z\n" + ); + CHECK_TRACE_CONTENTS( + "parse2: word: a\n" + "parse2: word: z\n" + ); + CHECK_TRACE_DOESNT_CONTAIN("parse2: word: ."); + // no other words + CHECK_TRACE_COUNT("parse2", 2); +} + +void test_parse2_word_starting_with_dot() { + parse_instruction_character_by_character( + "a .b c\n" + ); + CHECK_TRACE_CONTENTS( + "parse2: word: a\n" + "parse2: word: .b\n" + "parse2: word: c\n" + ); +} + +void test_parse2_comment_token_at_start() { + parse_instruction_character_by_character( + ". a b\n" + ); + CHECK_TRACE_CONTENTS( + "parse2: word: a\n" + "parse2: word: b\n" + ); + CHECK_TRACE_DOESNT_CONTAIN("parse2: word: ."); +} + +void test_parse2_comment_token_at_end() { + parse_instruction_character_by_character( + "a b .\n" + ); + CHECK_TRACE_CONTENTS( + "parse2: word: a\n" + "parse2: word: b\n" + ); + CHECK_TRACE_DOESNT_CONTAIN("parse2: word: ."); +} + +void test_parse2_word_starting_with_dot_at_start() { + parse_instruction_character_by_character( + ".a b c\n" + ); + CHECK_TRACE_CONTENTS( + "parse2: word: .a\n" + "parse2: word: b\n" + "parse2: word: c\n" + ); +} + +void test_parse2_metadata() { + parse_instruction_character_by_character( + ".a b/c d\n" + ); + CHECK_TRACE_CONTENTS( + "parse2: word: .a\n" + "parse2: word: b /c\n" + "parse2: word: d\n" + ); +} + +void test_parse2_string_with_metadata() { + parse_instruction_character_by_character( + "a \"bc def\"/disp32 g\n" + ); + CHECK_TRACE_CONTENTS( + "parse2: word: a\n" + "parse2: word: \"bc def\" /disp32\n" + "parse2: word: g\n" + ); +} + +void test_parse2_string_with_metadata_at_end() { + parse_instruction_character_by_character( + "a \"bc def\"/disp32\n" + ); + CHECK_TRACE_CONTENTS( + "parse2: word: a\n" + "parse2: word: \"bc def\" /disp32\n" + ); +} -:(code) void test_parse2_string_with_metadata_at_end_of_line_without_newline() { parse_instruction_character_by_character( "68/push \"test\"/f" // no newline, which is how calls from parse() will look ); CHECK_TRACE_CONTENTS( - "parse2: word: 68 /push" - "parse2: word: \"test\" /f" + "parse2: word: 68 /push\n" + "parse2: word: \"test\" /f\n" ); } //: Make sure slashes inside strings don't trigger adding stuff from inside the //: string to metadata. -:(scenario parse2_string_containing_slashes) -a "bc/def"/disp32 -+parse2: word: "bc/def" /disp32 + +void test_parse2_string_containing_slashes() { + parse_instruction_character_by_character( + "a \"bc/def\"/disp32\n" + ); + CHECK_TRACE_CONTENTS( + "parse2: word: \"bc/def\" /disp32\n" + ); +} diff --git a/subx/040---tests.cc b/subx/040---tests.cc index e6f2f489..d35cc711 100644 --- a/subx/040---tests.cc +++ b/subx/040---tests.cc @@ -14,21 +14,24 @@ Transform.push_back(create_test_function); // End Level-4 Transforms -:(scenario run_test) -% Reg[ESP].u = 0x100; -== 0x1 -main: - e8/call run-tests/disp32 # 5 bytes - f4/halt # 1 byte - -test-foo: # offset 7 - 01 d8 # just some unique instruction: add EBX to EAX - c3/return - -# check that code in test-foo ran (implicitly called by run-tests) -+run: 0x00000007 opcode: 01 - :(code) +void test_run_test() { + Reg[ESP].u = 0x100; + run( + "== 0x1\n" // code segment + "main:\n" + " e8/call run-tests/disp32\n" // 5 bytes + " f4/halt\n" // 1 byte + "test-foo:\n" // offset 7 + " 01 d8\n" // just some unique instruction: add EBX to EAX + " c3/return\n" + ); + // check that code in test-foo ran (implicitly called by run-tests) + CHECK_TRACE_CONTENTS( + "run: 0x00000007 opcode: 01\n" + ); +} + void create_test_function(program& p) { if (p.segments.empty()) return; segment& code = p.segments.at(0); |