From c504ca566124d1f097e7fe8a2f9f67c1c59e9ccf Mon Sep 17 00:00:00 2001 From: Kartik Agaram Date: Tue, 14 Jan 2020 01:48:06 -0800 Subject: 5893 --- html/001help.cc.html | 4 +- html/002test.cc.html | 28 +- html/003trace.cc.html | 175 ++--- html/003trace.test.cc.html | 26 +- html/010---vm.cc.html | 790 ++++++++++++----------- html/011run.cc.html | 768 +++++++++++----------- html/012elf.cc.html | 54 +- html/013direct_addressing.cc.html | 640 +++++++++--------- html/014indirect_addressing.cc.html | 522 +++++++-------- html/015immediate_addressing.cc.html | 528 +++++++-------- html/016index_addressing.cc.html | 76 +-- html/017jump_disp8.cc.html | 170 ++--- html/018jump_disp32.cc.html | 150 ++--- html/019functions.cc.html | 62 +- html/020syscalls.cc.html | 108 ++-- html/021byte_addressing.cc.html | 66 +- html/022div.cc.html | 24 +- html/030---translate.cc.html | 409 ++++++------ html/031transforms.cc.html | 80 +-- html/032---operands.cc.html | 1069 ++++++++++++++++--------------- html/033check_operands.cc.html | 300 ++++----- html/034check_operand_bounds.cc.html | 44 +- html/035compute_segment_address.cc.html | 24 +- html/036labels.cc.html | 80 +-- html/037global_variables.cc.html | 114 ++-- html/038---literal_strings.cc.html | 654 ++++++++++--------- html/039debug.cc.html | 276 ++++---- html/040---tests.cc.html | 168 +++-- 28 files changed, 3665 insertions(+), 3744 deletions(-) diff --git a/html/001help.cc.html b/html/001help.cc.html index 52cd0df3..cb115add 100644 --- a/html/001help.cc.html +++ b/html/001help.cc.html @@ -220,7 +220,7 @@ if ('onhashchange' in window) { 159 //: yadda-yadda. Instead use this macro below to perform an unsafe cast to 160 //: signed. We'll just give up immediately if a container's ever too large. 161 //: Basically, Mu is not concerned about this being a little slower than it -162 //: could be. (https://gist.github.com/rygorous/e0f055bfb74e3d5f0af20690759de5a7) +162 //: could be. (https://gist.github.com/rygorous/e0f055bfb74e3d5f0af20690759de5a7) 163 //: 164 //: Addendum to corollary: We're going to uniformly use int everywhere, to 165 //: indicate that we're oblivious to number size, and since Clang on 32-bit @@ -232,7 +232,7 @@ if ('onhashchange' in window) { 171 172 //: 5. Integer overflow is guarded against at runtime using the -ftrapv flag 173 //: to the compiler, supported by Clang (GCC version only works sometimes: -174 //: http://stackoverflow.com/questions/20851061/how-to-make-gcc-ftrapv-work). +174 //: http://stackoverflow.com/questions/20851061/how-to-make-gcc-ftrapv-work). 175 :(before "atexit(reset)") 176 initialize_signal_handlers(); // not always necessary, but doesn't hurt 177 //? cerr << INT_MAX+1 << '\n'; // test overflow diff --git a/html/002test.cc.html b/html/002test.cc.html index 18215ec6..13e7959d 100644 --- a/html/002test.cc.html +++ b/html/002test.cc.html @@ -61,7 +61,7 @@ if ('onhashchange' in window) {
   1 //: A simple test harness. To create new tests, define functions starting with
   2 //: 'test_'. To run all tests so defined, run:
-  3 //:   $ ./mu test
+  3 //:   $ ./bootstrap test
   4 //:
   5 //: Every layer should include tests, and can reach into previous layers.
   6 //: However, it seems like a good idea never to reach into tests from previous
@@ -101,7 +101,7 @@ if ('onhashchange' in window) {
  40 Passed = true;
  41 
  42 :(before "End Commandline Parsing")
- 43 if (argc > 1 && is_equal(argv[1], "test")) {
+ 43 if (argc > 1 && is_equal(argv[1], "test")) {
  44   Run_tests = true;  --argc;  ++argv;  // shift 'test' out of commandline args
  45 }
  46 
@@ -133,7 +133,7 @@ if ('onhashchange' in window) {
  72 
  73 :(after "End Main")
  74 //: Raise other unrecognized sub-commands as errors.
- 75 //: We couldn't do this until now because we want `./subx test` to always
+ 75 //: We couldn't do this until now because we want `./bootstrap test` to always
  76 //: succeed, no matter how many layers are included in the build.
  77 cerr << "nothing to do\n";
  78 return 1;
@@ -158,18 +158,16 @@ if ('onhashchange' in window) {
  97 };
  98 :(after "Test Runs")
  99 string maybe_single_test_to_run = argv[argc-1];
-100 if (!starts_with(maybe_single_test_to_run, "test_"))
-101   maybe_single_test_to_run.insert(0, "test_");
-102 for (size_t i=0;  i < sizeof(Tests)/sizeof(Tests[0]);  ++i) {
-103   if (Test_names[i] == maybe_single_test_to_run) {
-104     run_test(i);
-105     if (Passed) cerr << ".\n";
-106     return 0;
-107   }
-108 }
-109 
-110 :(before "End Includes")
-111 #include <stdlib.h>
+100 for (size_t i=0;  i < sizeof(Tests)/sizeof(Tests[0]);  ++i) {
+101   if (Test_names[i] == maybe_single_test_to_run) {
+102     run_test(i);
+103     if (Passed) cerr << ".\n";
+104     return 0;
+105   }
+106 }
+107 
+108 :(before "End Includes")
+109 #include <stdlib.h>
 
diff --git a/html/003trace.cc.html b/html/003trace.cc.html index 2b5fba00..1aa53854 100644 --- a/html/003trace.cc.html +++ b/html/003trace.cc.html @@ -192,7 +192,7 @@ if ('onhashchange' in window) { 131 132 string unescape_newline(string& s) { 133 std::stringstream ss; -134 for (int i = 0; i < SIZE(s); ++i) { +134 for (int i = 0; i < SIZE(s); ++i) { 135 if (s.at(i) == '\n') 136 ss << "\\n"; 137 else @@ -202,7 +202,7 @@ if ('onhashchange' in window) { 141 } 142 143 void dump_trace_line(ostream& s, trace_line& t) { -144 s << std::setw(4) << t.depth << ' ' << t.label << ": " << unescape_newline(t.contents) << '\n'; +144 s << std::setw(2) << t.depth << ' ' << t.label << ": " << unescape_newline(t.contents) << '\n'; 145 } 146 147 //: Starting a new trace line. @@ -245,14 +245,14 @@ if ('onhashchange' in window) { 184 if (!curr_stream) return; 185 string curr_contents = curr_stream->str(); 186 if (!curr_contents.empty()) { -187 past_lines.push_back(trace_line(curr_contents, trim(curr_label), curr_depth)); // preserve indent in contents +187 past_lines.push_back(trace_line(curr_contents, trim(curr_label), curr_depth)); // preserve indent in contents 188 // maybe incrementally dump trace 189 trace_line& t = past_lines.back(); 190 if (should_incrementally_print_trace()) { 191 dump_trace_line(cerr, t); 192 } -193 // Hack: on 'subx --trace --dump', emit only to stderr, not 'last_run'. -194 if (Dump_trace) past_lines.pop_back(); // economize on memory +193 // Hack: on 'bootstrap --trace --dump', emit only to stderr, not 'last_run'. +194 if (Dump_trace) past_lines.pop_back(); // economize on memory 195 // End trace Commit 196 } 197 @@ -381,24 +381,24 @@ if ('onhashchange' in window) { 320 if (!Trace_stream) return false; 321 vector<string> expected_lines = split(expected, "\n"); 322 int curr_expected_line = 0; -323 while (curr_expected_line < SIZE(expected_lines) && expected_lines.at(curr_expected_line).empty()) +323 while (curr_expected_line < SIZE(expected_lines) && expected_lines.at(curr_expected_line).empty()) 324 ++curr_expected_line; -325 if (curr_expected_line == SIZE(expected_lines)) return true; +325 if (curr_expected_line == SIZE(expected_lines)) return true; 326 string label, contents; 327 split_label_contents(expected_lines.at(curr_expected_line), &label, &contents); 328 for (vector<trace_line>::iterator p = Trace_stream->past_lines.begin(); p != Trace_stream->past_lines.end(); ++p) { 329 if (label != p->label) continue; -330 string t = trim(p->contents); +330 string t = trim(p->contents); 331 if (contents != unescape_newline(t)) continue; 332 ++curr_expected_line; -333 while (curr_expected_line < SIZE(expected_lines) && expected_lines.at(curr_expected_line).empty()) +333 while (curr_expected_line < SIZE(expected_lines) && expected_lines.at(curr_expected_line).empty()) 334 ++curr_expected_line; -335 if (curr_expected_line == SIZE(expected_lines)) return true; +335 if (curr_expected_line == SIZE(expected_lines)) return true; 336 split_label_contents(expected_lines.at(curr_expected_line), &label, &contents); 337 } 338 339 if (line_exists_anywhere(label, contents)) { -340 cerr << "\nF - " << FUNCTION << "(" << FILE << ":" << LINE << "): line [" << label << ": " << contents << "] out of order in trace:\n"; +340 cerr << "\nF - " << FUNCTION << "(" << FILE << ":" << LINE << "): line [" << label << ": " << contents << "] out of order in trace:\n"; 341 DUMP(""); 342 } 343 else { @@ -411,7 +411,7 @@ if ('onhashchange' in window) { 350 351 bool trace_doesnt_contain(string expected) { 352 vector<string> tmp = split_first(expected, ": "); -353 if (SIZE(tmp) == 1) { +353 if (SIZE(tmp) == 1) { 354 raise << expected << ": missing label or contents in trace line\n" << end(); 355 assert(false); 356 } @@ -422,12 +422,12 @@ if ('onhashchange' in window) { 361 return trace_count(label, ""); 362 } 363 -364 int trace_count(string label, string line) { +364 int trace_count(string label, string line) { 365 if (!Trace_stream) return 0; 366 long result = 0; 367 for (vector<trace_line>::iterator p = Trace_stream->past_lines.begin(); p != Trace_stream->past_lines.end(); ++p) { 368 if (label == p->label) { -369 if (line == "" || trim(line) == trim(p->contents)) +369 if (line == "" || trim(line) == trim(p->contents)) 370 ++result; 371 } 372 } @@ -439,7 +439,7 @@ if ('onhashchange' in window) { 378 long result = 0; 379 for (vector<trace_line>::iterator p = Trace_stream->past_lines.begin(); p != Trace_stream->past_lines.end(); ++p) { 380 if (label == p->label) { -381 if (starts_with(trim(p->contents), trim(prefix))) +381 if (starts_with(trim(p->contents), trim(prefix))) 382 ++result; 383 } 384 } @@ -451,18 +451,18 @@ if ('onhashchange' in window) { 390 size_t pos = s.find(delim); 391 if (pos == string::npos) { 392 *label = ""; -393 *contents = trim(s); +393 *contents = trim(s); 394 } 395 else { -396 *label = trim(s.substr(0, pos)); -397 *contents = trim(s.substr(pos+SIZE(delim))); +396 *label = trim(s.substr(0, pos)); +397 *contents = trim(s.substr(pos+SIZE(delim))); 398 } 399 } 400 401 bool line_exists_anywhere(const string& label, const string& contents) { 402 for (vector<trace_line>::iterator p = Trace_stream->past_lines.begin(); p != Trace_stream->past_lines.end(); ++p) { 403 if (label != p->label) continue; -404 if (contents == trim(p->contents)) return true; +404 if (contents == trim(p->contents)) return true; 405 } 406 return false; 407 } @@ -508,79 +508,80 @@ if ('onhashchange' in window) { 447 :(before "End Globals") 448 ofstream Trace_file; 449 :(before "End Commandline Options(*arg)") -450 else if (is_equal(*arg, "--trace")) { +450 else if (is_equal(*arg, "--trace")) { 451 cerr << "saving trace to 'last_run'\n"; 452 Trace_file.open("last_run"); 453 // Add a dummy line up top; otherwise the `browse_trace` tool currently has 454 // no way to expand any lines above an error. 455 Trace_file << " 0 dummy: start\n"; -456 } -457 :(before "End trace Commit") -458 if (Trace_file) { -459 dump_trace_line(Trace_file, t); -460 } -461 :(before "End One-time Setup") -462 atexit(cleanup_main); -463 :(code) -464 void cleanup_main() { -465 if (Trace_file) Trace_file.close(); -466 // End cleanup_main -467 } -468 -469 :(before "End trace_stream Methods") -470 string readable_contents(string label) { -471 string trim(const string& s); // prototype -472 ostringstream output; -473 label = trim(label); -474 for (vector<trace_line>::iterator p = past_lines.begin(); p != past_lines.end(); ++p) -475 if (label.empty() || label == p->label) -476 dump_trace_line(output, *p); -477 return output.str(); -478 } -479 -480 //: Print traces to the screen as they happen. -481 //: Particularly useful when juggling multiple trace streams, like when -482 //: debugging sandboxes. -483 :(before "End Globals") -484 bool Dump_trace = false; -485 :(before "End Commandline Options(*arg)") -486 else if (is_equal(*arg, "--dump")) { -487 Dump_trace = true; -488 } -489 :(before "End Incremental Trace Print Conditions") -490 if (Dump_trace) return true; -491 -492 //: Miscellaneous helpers. -493 -494 :(code) -495 string trim(const string& s) { -496 string::const_iterator first = s.begin(); -497 while (first != s.end() && isspace(*first)) -498 ++first; -499 if (first == s.end()) return ""; -500 -501 string::const_iterator last = --s.end(); -502 while (last != s.begin() && isspace(*last)) -503 --last; -504 ++last; -505 return string(first, last); -506 } -507 -508 :(before "End Includes") -509 #include <vector> -510 using std::vector; -511 #include <list> -512 using std::list; -513 #include <set> -514 using std::set; -515 -516 #include <sstream> -517 using std::istringstream; -518 using std::ostringstream; -519 -520 #include <fstream> -521 using std::ifstream; -522 using std::ofstream; +456 // End --trace Settings +457 } +458 :(before "End trace Commit") +459 if (Trace_file) { +460 dump_trace_line(Trace_file, t); +461 } +462 :(before "End One-time Setup") +463 atexit(cleanup_main); +464 :(code) +465 void cleanup_main() { +466 if (Trace_file) Trace_file.close(); +467 // End cleanup_main +468 } +469 +470 :(before "End trace_stream Methods") +471 string readable_contents(string label) { +472 string trim(const string& s); // prototype +473 ostringstream output; +474 label = trim(label); +475 for (vector<trace_line>::iterator p = past_lines.begin(); p != past_lines.end(); ++p) +476 if (label.empty() || label == p->label) +477 dump_trace_line(output, *p); +478 return output.str(); +479 } +480 +481 //: Print traces to the screen as they happen. +482 //: Particularly useful when juggling multiple trace streams, like when +483 //: debugging sandboxes. +484 :(before "End Globals") +485 bool Dump_trace = false; +486 :(before "End Commandline Options(*arg)") +487 else if (is_equal(*arg, "--dump")) { +488 Dump_trace = true; +489 } +490 :(before "End Incremental Trace Print Conditions") +491 if (Dump_trace) return true; +492 +493 //: Miscellaneous helpers. +494 +495 :(code) +496 string trim(const string& s) { +497 string::const_iterator first = s.begin(); +498 while (first != s.end() && isspace(*first)) +499 ++first; +500 if (first == s.end()) return ""; +501 +502 string::const_iterator last = --s.end(); +503 while (last != s.begin() && isspace(*last)) +504 --last; +505 ++last; +506 return string(first, last); +507 } +508 +509 :(before "End Includes") +510 #include <vector> +511 using std::vector; +512 #include <list> +513 using std::list; +514 #include <set> +515 using std::set; +516 +517 #include <sstream> +518 using std::istringstream; +519 using std::ostringstream; +520 +521 #include <fstream> +522 using std::ifstream; +523 using std::ofstream; diff --git a/html/003trace.test.cc.html b/html/003trace.test.cc.html index 136345cd..c885cafd 100644 --- a/html/003trace.test.cc.html +++ b/html/003trace.test.cc.html @@ -173,19 +173,19 @@ if ('onhashchange' in window) { 117 } 118 119 void test_trim() { -120 CHECK_EQ(trim(""), ""); -121 CHECK_EQ(trim(" "), ""); -122 CHECK_EQ(trim(" "), ""); -123 CHECK_EQ(trim("a"), "a"); -124 CHECK_EQ(trim(" a"), "a"); -125 CHECK_EQ(trim(" a"), "a"); -126 CHECK_EQ(trim(" ab"), "ab"); -127 CHECK_EQ(trim("a "), "a"); -128 CHECK_EQ(trim("a "), "a"); -129 CHECK_EQ(trim("ab "), "ab"); -130 CHECK_EQ(trim(" a "), "a"); -131 CHECK_EQ(trim(" a "), "a"); -132 CHECK_EQ(trim(" ab "), "ab"); +120 CHECK_EQ(trim(""), ""); +121 CHECK_EQ(trim(" "), ""); +122 CHECK_EQ(trim(" "), ""); +123 CHECK_EQ(trim("a"), "a"); +124 CHECK_EQ(trim(" a"), "a"); +125 CHECK_EQ(trim(" a"), "a"); +126 CHECK_EQ(trim(" ab"), "ab"); +127 CHECK_EQ(trim("a "), "a"); +128 CHECK_EQ(trim("a "), "a"); +129 CHECK_EQ(trim("ab "), "ab"); +130 CHECK_EQ(trim(" a "), "a"); +131 CHECK_EQ(trim(" a "), "a"); +132 CHECK_EQ(trim(" ab "), "ab"); 133 } diff --git a/html/010---vm.cc.html b/html/010---vm.cc.html index 6d55cc8a..52d5cfba 100644 --- a/html/010---vm.cc.html +++ b/html/010---vm.cc.html @@ -59,410 +59,404 @@ if ('onhashchange' in window) { https://github.com/akkartik/mu/blob/master/010---vm.cc
-  1 //: Core data structures for simulating the SubX VM (subset of an x86 processor)
-  2 //:
-  3 //: At the lowest level ("level 1") of abstraction, SubX executes x86
-  4 //: instructions provided in the form of an array of bytes, loaded into memory
-  5 //: starting at a specific address.
-  6 //:
-  7 //: SubX is fundamentally a translator. But having a VM to execute its
-  8 //: translations affords greater confidence in it.
-  9 
- 10 //:: registers
- 11 //: assume segment registers are hard-coded to 0
- 12 //: no floating-point, MMX, etc. yet
- 13 
- 14 :(before "End Types")
- 15 enum {
- 16   EAX,
- 17   ECX,
- 18   EDX,
- 19   EBX,
- 20   ESP,
- 21   EBP,
- 22   ESI,
- 23   EDI,
- 24   NUM_INT_REGISTERS,
- 25 };
- 26 union reg {
- 27   int32_t i;
- 28   uint32_t u;
- 29 };
- 30 :(before "End Globals")
- 31 reg Reg[NUM_INT_REGISTERS] = { {0} };
- 32 uint32_t EIP = 1;  // preserve null pointer
- 33 :(before "End Reset")
- 34 bzero(Reg, sizeof(Reg));
- 35 EIP = 1;  // preserve null pointer
- 36 
- 37 :(before "End Help Contents")
- 38 cerr << "  registers\n";
- 39 :(before "End Help Texts")
- 40 put_new(Help, "registers",
- 41   "SubX currently supports eight 32-bit integer registers. From 0 to 7, they are:\n"
- 42   "  EAX ECX EDX EBX ESP EBP ESI EDI\n"
- 43   "ESP contains the top of the stack.\n"
- 44   "\n"
- 45   "-- 8-bit registers\n"
- 46   "Some instructions operate on eight *overlapping* 8-bit registers.\n"
- 47   "From 0 to 7, they are:\n"
- 48   "  AL CL DL BL AH CH DH BH\n"
- 49   "The 8-bit registers overlap with the 32-bit ones. AL is the lowest signicant byte\n"
- 50   "of EAX, AH is the second lowest significant byte, and so on.\n"
- 51   "\n"
- 52   "For example, if EBX contains 0x11223344, then BL contains 0x44, and BH contains 0x33.\n"
- 53   "\n"
- 54   "There is no way to access bytes within ESP, EBP, ESI or EDI.\n"
+  1 //: Core data structures for simulating the SubX VM (subset of an x86 processor),
+  2 //: either in tests or debug aids.
+  3 
+  4 //:: registers
+  5 //: assume segment registers are hard-coded to 0
+  6 //: no floating-point, MMX, etc. yet
+  7 
+  8 :(before "End Types")
+  9 enum {
+ 10   EAX,
+ 11   ECX,
+ 12   EDX,
+ 13   EBX,
+ 14   ESP,
+ 15   EBP,
+ 16   ESI,
+ 17   EDI,
+ 18   NUM_INT_REGISTERS,
+ 19 };
+ 20 union reg {
+ 21   int32_t i;
+ 22   uint32_t u;
+ 23 };
+ 24 :(before "End Globals")
+ 25 reg Reg[NUM_INT_REGISTERS] = { {0} };
+ 26 uint32_t EIP = 1;  // preserve null pointer
+ 27 :(before "End Reset")
+ 28 bzero(Reg, sizeof(Reg));
+ 29 EIP = 1;  // preserve null pointer
+ 30 
+ 31 :(before "End Help Contents")
+ 32 cerr << "  registers\n";
+ 33 :(before "End Help Texts")
+ 34 put_new(Help, "registers",
+ 35   "SubX currently supports eight 32-bit integer registers. From 0 to 7, they are:\n"
+ 36   "  EAX ECX EDX EBX ESP EBP ESI EDI\n"
+ 37   "ESP contains the top of the stack.\n"
+ 38   "\n"
+ 39   "-- 8-bit registers\n"
+ 40   "Some instructions operate on eight *overlapping* 8-bit registers.\n"
+ 41   "From 0 to 7, they are:\n"
+ 42   "  AL CL DL BL AH CH DH BH\n"
+ 43   "The 8-bit registers overlap with the 32-bit ones. AL is the lowest signicant byte\n"
+ 44   "of EAX, AH is the second lowest significant byte, and so on.\n"
+ 45   "\n"
+ 46   "For example, if EBX contains 0x11223344, then BL contains 0x44, and BH contains 0x33.\n"
+ 47   "\n"
+ 48   "There is no way to access bytes within ESP, EBP, ESI or EDI.\n"
+ 49   "\n"
+ 50   "For complete details consult the IA-32 software developer's manual, volume 2,\n"
+ 51   "table 2-2, \"32-bit addressing forms with the ModR/M byte\".\n"
+ 52   "It is included in this repository as 'modrm.pdf'.\n"
+ 53   "The register encodings are described in the top row of the table, but you'll need\n"
+ 54   "to spend some time with it.\n"
  55   "\n"
- 56   "For complete details consult the IA-32 software developer's manual, volume 2,\n"
- 57   "table 2-2, \"32-bit addressing forms with the ModR/M byte\".\n"
- 58   "It is included in this repository as 'modrm.pdf'.\n"
- 59   "The register encodings are described in the top row of the table, but you'll need\n"
- 60   "to spend some time with it.\n"
- 61   "\n"
- 62   "-- flag registers\n"
- 63   "Various instructions (particularly 'compare') modify one or more of four 1-bit\n"
- 64   "'flag' registers, as a side-effect:\n"
- 65   "- the sign flag (SF): usually set if an arithmetic result is negative, or\n"
- 66   "  reset if not.\n"
- 67   "- the zero flag (ZF): usually set if a result is zero, or reset if not.\n"
- 68   "- the carry flag (CF): usually set if an arithmetic result overflows by just one bit.\n"
- 69   "  Useful for operating on unsigned numbers.\n"
- 70   "- the overflow flag (OF): usually set if an arithmetic result overflows by more\n"
- 71   "  than one bit. Useful for operating on signed numbers.\n"
- 72   "The flag bits are read by conditional jumps.\n"
- 73   "\n"
- 74   "For complete details on how different instructions update the flags, consult the IA-32\n"
- 75   "manual (volume 2). There's various versions of it online, such as https://c9x.me/x86,\n"
- 76   "though of course you'll need to be careful to ignore instructions and flag registers\n"
- 77   "that SubX doesn't support.\n"
- 78   "\n"
- 79   "It isn't simple, but if this is the processor you have running on your computer.\n"
- 80   "Might as well get good at it.\n"
- 81 );
- 82 
- 83 :(before "End Globals")
- 84 // the subset of x86 flag registers we care about
- 85 bool SF = false;  // sign flag
- 86 bool ZF = false;  // zero flag
- 87 bool CF = false;  // carry flag
- 88 bool OF = false;  // overflow flag
- 89 :(before "End Reset")
- 90 SF = ZF = CF = OF = false;
- 91 
- 92 //:: simulated RAM
+ 56   "-- flag registers\n"
+ 57   "Various instructions (particularly 'compare') modify one or more of four 1-bit\n"
+ 58   "'flag' registers, as a side-effect:\n"
+ 59   "- the sign flag (SF): usually set if an arithmetic result is negative, or\n"
+ 60   "  reset if not.\n"
+ 61   "- the zero flag (ZF): usually set if a result is zero, or reset if not.\n"
+ 62   "- the carry flag (CF): usually set if an arithmetic result overflows by just one bit.\n"
+ 63   "  Useful for operating on unsigned numbers.\n"
+ 64   "- the overflow flag (OF): usually set if an arithmetic result overflows by more\n"
+ 65   "  than one bit. Useful for operating on signed numbers.\n"
+ 66   "The flag bits are read by conditional jumps.\n"
+ 67   "\n"
+ 68   "For complete details on how different instructions update the flags, consult the IA-32\n"
+ 69   "manual (volume 2). There's various versions of it online, such as https://c9x.me/x86,\n"
+ 70   "though of course you'll need to be careful to ignore instructions and flag registers\n"
+ 71   "that SubX doesn't support.\n"
+ 72   "\n"
+ 73   "It isn't simple, but if this is the processor you have running on your computer.\n"
+ 74   "Might as well get good at it.\n"
+ 75 );
+ 76 
+ 77 :(before "End Globals")
+ 78 // the subset of x86 flag registers we care about
+ 79 bool SF = false;  // sign flag
+ 80 bool ZF = false;  // zero flag
+ 81 bool CF = false;  // carry flag
+ 82 bool OF = false;  // overflow flag
+ 83 :(before "End Reset")
+ 84 SF = ZF = CF = OF = false;
+ 85 
+ 86 //:: simulated RAM
+ 87 
+ 88 :(before "End Types")
+ 89 const uint32_t SEGMENT_ALIGNMENT = 0x1000000;  // 16MB
+ 90 inline uint32_t align_upwards(uint32_t x, uint32_t align) {
+ 91   return (x+align-1) & -(align);
+ 92 }
  93 
- 94 :(before "End Types")
- 95 const uint32_t SEGMENT_ALIGNMENT = 0x1000000;  // 16MB
- 96 inline uint32_t align_upwards(uint32_t x, uint32_t align) {
- 97   return (x+align-1) & -(align);
- 98 }
- 99 
-100 // Like in real-world Linux, we'll allocate RAM for our programs in disjoint
-101 // slabs called VMAs or Virtual Memory Areas.
-102 struct vma {
-103   uint32_t start;  // inclusive
-104   uint32_t end;  // exclusive
-105   vector<uint8_t> _data;
-106   vma(uint32_t s, uint32_t e) :start(s), end(e) {}
-107   vma(uint32_t s) :start(s), end(align_upwards(s+1, SEGMENT_ALIGNMENT)) {}
-108   bool match(uint32_t a) {
-109     return a >= start && a < end;
-110   }
-111   bool match32(uint32_t a) {
-112     return a >= start && a+4 <= end;
-113   }
-114   uint8_t& data(uint32_t a) {
-115     assert(match(a));
-116     uint32_t result_index = a-start;
-117     if (_data.size() <= result_index) {
-118       const int align = 0x1000;
-119       uint32_t result_size = result_index + 1;  // size needed for result_index to be valid
-120       uint32_t new_size = align_upwards(result_size, align);
-121       // grow at least 2x to maintain some amortized complexity guarantees
-122       if (new_size < _data.size() * 2)
-123         new_size = _data.size() * 2;
-124       // never grow past the stated limit
-125       if (new_size > end-start)
-126         new_size = end-start;
-127       _data.resize(new_size);
-128     }
-129     return _data.at(result_index);
-130   }
-131   void grow_until(uint32_t new_end_address) {
-132     if (new_end_address < end) return;
-133     // Ugly: vma knows about the global Memory list of vmas
-134     void sanity_check(uint32_t start, uint32_t end);
-135     sanity_check(start, new_end_address);
-136     end = new_end_address;
-137   }
-138   // End vma Methods
-139 };
-140 :(code)
-141 void sanity_check(uint32_t start, uint32_t end) {
-142   bool dup_found = false;
-143   for (int i = 0;  i < SIZE(Mem);  ++i) {
-144     const vma& curr = Mem.at(i);
-145     if (curr.start == start) {
-146       assert(!dup_found);
-147       dup_found = true;
+ 94 // Like in real-world Linux, we'll allocate RAM for our programs in disjoint
+ 95 // slabs called VMAs or Virtual Memory Areas.
+ 96 struct vma {
+ 97   uint32_t start;  // inclusive
+ 98   uint32_t end;  // exclusive
+ 99   vector<uint8_t> _data;
+100   vma(uint32_t s, uint32_t e) :start(s), end(e) {}
+101   vma(uint32_t s) :start(s), end(align_upwards(s+1, SEGMENT_ALIGNMENT)) {}
+102   bool match(uint32_t a) {
+103     return a >= start && a < end;
+104   }
+105   bool match32(uint32_t a) {
+106     return a >= start && a+4 <= end;
+107   }
+108   uint8_t& data(uint32_t a) {
+109     assert(match(a));
+110     uint32_t result_index = a-start;
+111     if (_data.size() <= result_index) {
+112       const int align = 0x1000;
+113       uint32_t result_size = result_index + 1;  // size needed for result_index to be valid
+114       uint32_t new_size = align_upwards(result_size, align);
+115       // grow at least 2x to maintain some amortized complexity guarantees
+116       if (new_size < _data.size() * 2)
+117         new_size = _data.size() * 2;
+118       // never grow past the stated limit
+119       if (new_size > end-start)
+120         new_size = end-start;
+121       _data.resize(new_size);
+122     }
+123     return _data.at(result_index);
+124   }
+125   void grow_until(uint32_t new_end_address) {
+126     if (new_end_address < end) return;
+127     // Ugly: vma knows about the global Memory list of vmas
+128     void sanity_check(uint32_t start, uint32_t end);
+129     sanity_check(start, new_end_address);
+130     end = new_end_address;
+131   }
+132   // End vma Methods
+133 };
+134 :(code)
+135 void sanity_check(uint32_t start, uint32_t end) {
+136   bool dup_found = false;
+137   for (int i = 0;  i < SIZE(Mem);  ++i) {
+138     const vma& curr = Mem.at(i);
+139     if (curr.start == start) {
+140       assert(!dup_found);
+141       dup_found = true;
+142     }
+143     else if (curr.start > start) {
+144       assert(curr.start > end);
+145     }
+146     else if (curr.start < start) {
+147       assert(curr.end < start);
 148     }
-149     else if (curr.start > start) {
-150       assert(curr.start > end);
-151     }
-152     else if (curr.start < start) {
-153       assert(curr.end < start);
-154     }
-155   }
-156 }
-157 
-158 :(before "End Globals")
-159 // RAM is made of VMAs.
-160 vector<vma> Mem;
-161 :(code)
-162 :(before "End Globals")
-163 uint32_t End_of_program = 0;  // when the program executes past this address in tests we'll stop the test
-164 // The stack grows downward. Can't increase its size for now.
-165 :(before "End Reset")
-166 Mem.clear();
-167 End_of_program = 0;
-168 :(code)
-169 // These helpers depend on Mem being laid out contiguously (so you can't use a
-170 // map, etc.) and on the host also being little-endian.
-171 inline uint8_t read_mem_u8(uint32_t addr) {
-172   uint8_t* handle = mem_addr_u8(addr);  // error messages get printed here
-173   return handle ? *handle : 0;
-174 }
-175 inline int8_t read_mem_i8(uint32_t addr) {
-176   return static_cast<int8_t>(read_mem_u8(addr));
-177 }
-178 inline uint32_t read_mem_u32(uint32_t addr) {
-179   uint32_t* handle = mem_addr_u32(addr);  // error messages get printed here
-180   return handle ? *handle : 0;
-181 }
-182 inline int32_t read_mem_i32(uint32_t addr) {
-183   return static_cast<int32_t>(read_mem_u32(addr));
-184 }
-185 
-186 inline uint8_t* mem_addr_u8(uint32_t addr) {
-187   uint8_t* result = NULL;
-188   for (int i = 0;  i < SIZE(Mem);  ++i) {
-189     if (Mem.at(i).match(addr)) {
-190       if (result)
-191         raise << "address 0x" << HEXWORD << addr << " is in two segments\n" << end();
-192       result = &Mem.at(i).data(addr);
-193     }
-194   }
-195   if (result == NULL) {
-196     if (Trace_file) Trace_file.flush();
-197     raise << "Tried to access uninitialized memory at address 0x" << HEXWORD << addr << '\n' << end();
-198     exit(1);
-199   }
-200   return result;
-201 }
-202 inline int8_t* mem_addr_i8(uint32_t addr) {
-203   return reinterpret_cast<int8_t*>(mem_addr_u8(addr));
-204 }
-205 inline uint32_t* mem_addr_u32(uint32_t addr) {
-206   uint32_t* result = NULL;
-207   for (int i = 0;  i < SIZE(Mem);  ++i) {
-208     if (Mem.at(i).match32(addr)) {
-209       if (result)
-210         raise << "address 0x" << HEXWORD << addr << " is in two segments\n" << end();
-211       result = reinterpret_cast<uint32_t*>(&Mem.at(i).data(addr));
-212     }
+149   }
+150 }
+151 
+152 :(before "End Globals")
+153 // RAM is made of VMAs.
+154 vector<vma> Mem;
+155 :(code)
+156 :(before "End Globals")
+157 uint32_t End_of_program = 0;  // when the program executes past this address in tests we'll stop the test
+158 // The stack grows downward. Can't increase its size for now.
+159 :(before "End Reset")
+160 Mem.clear();
+161 End_of_program = 0;
+162 :(code)
+163 // These helpers depend on Mem being laid out contiguously (so you can't use a
+164 // map, etc.) and on the host also being little-endian.
+165 inline uint8_t read_mem_u8(uint32_t addr) {
+166   uint8_t* handle = mem_addr_u8(addr);  // error messages get printed here
+167   return handle ? *handle : 0;
+168 }
+169 inline int8_t read_mem_i8(uint32_t addr) {
+170   return static_cast<int8_t>(read_mem_u8(addr));
+171 }
+172 inline uint32_t read_mem_u32(uint32_t addr) {
+173   uint32_t* handle = mem_addr_u32(addr);  // error messages get printed here
+174   return handle ? *handle : 0;
+175 }
+176 inline int32_t read_mem_i32(uint32_t addr) {
+177   return static_cast<int32_t>(read_mem_u32(addr));
+178 }
+179 
+180 inline uint8_t* mem_addr_u8(uint32_t addr) {
+181   uint8_t* result = NULL;
+182   for (int i = 0;  i < SIZE(Mem);  ++i) {
+183     if (Mem.at(i).match(addr)) {
+184       if (result)
+185         raise << "address 0x" << HEXWORD << addr << " is in two segments\n" << end();
+186       result = &Mem.at(i).data(addr);
+187     }
+188   }
+189   if (result == NULL) {
+190     if (Trace_file) Trace_file.flush();
+191     raise << "Tried to access uninitialized memory at address 0x" << HEXWORD << addr << '\n' << end();
+192     exit(1);
+193   }
+194   return result;
+195 }
+196 inline int8_t* mem_addr_i8(uint32_t addr) {
+197   return reinterpret_cast<int8_t*>(mem_addr_u8(addr));
+198 }
+199 inline uint32_t* mem_addr_u32(uint32_t addr) {
+200   uint32_t* result = NULL;
+201   for (int i = 0;  i < SIZE(Mem);  ++i) {
+202     if (Mem.at(i).match32(addr)) {
+203       if (result)
+204         raise << "address 0x" << HEXWORD << addr << " is in two segments\n" << end();
+205       result = reinterpret_cast<uint32_t*>(&Mem.at(i).data(addr));
+206     }
+207   }
+208   if (result == NULL) {
+209     if (Trace_file) Trace_file.flush();
+210     raise << "Tried to access uninitialized memory at address 0x" << HEXWORD << addr << '\n' << end();
+211     raise << "The entire 4-byte word should be initialized and lie in a single segment.\n" << end();
+212     exit(1);
 213   }
-214   if (result == NULL) {
-215     if (Trace_file) Trace_file.flush();
-216     raise << "Tried to access uninitialized memory at address 0x" << HEXWORD << addr << '\n' << end();
-217     raise << "The entire 4-byte word should be initialized and lie in a single segment.\n" << end();
-218     exit(1);
-219   }
-220   return result;
-221 }
-222 inline int32_t* mem_addr_i32(uint32_t addr) {
-223   return reinterpret_cast<int32_t*>(mem_addr_u32(addr));
-224 }
-225 // helper for some syscalls. But read-only.
-226 inline const char* mem_addr_kernel_string(uint32_t addr) {
-227   return reinterpret_cast<const char*>(mem_addr_u8(addr));
+214   return result;
+215 }
+216 inline int32_t* mem_addr_i32(uint32_t addr) {
+217   return reinterpret_cast<int32_t*>(mem_addr_u32(addr));
+218 }
+219 // helper for some syscalls. But read-only.
+220 inline const char* mem_addr_kernel_string(uint32_t addr) {
+221   return reinterpret_cast<const char*>(mem_addr_u8(addr));
+222 }
+223 inline string mem_addr_string(uint32_t addr, uint32_t size) {
+224   ostringstream out;
+225   for (size_t i = 0;  i < size;  ++i)
+226     out << read_mem_u8(addr+i);
+227   return out.str();
 228 }
-229 inline string mem_addr_string(uint32_t addr, uint32_t size) {
-230   ostringstream out;
-231   for (size_t i = 0;  i < size;  ++i)
-232     out << read_mem_u8(addr+i);
-233   return out.str();
+229 
+230 
+231 inline void write_mem_u8(uint32_t addr, uint8_t val) {
+232   uint8_t* handle = mem_addr_u8(addr);
+233   if (handle != NULL) *handle = val;
 234 }
-235 
-236 
-237 inline void write_mem_u8(uint32_t addr, uint8_t val) {
-238   uint8_t* handle = mem_addr_u8(addr);
-239   if (handle != NULL) *handle = val;
-240 }
-241 inline void write_mem_i8(uint32_t addr, int8_t val) {
-242   int8_t* handle = mem_addr_i8(addr);
-243   if (handle != NULL) *handle = val;
-244 }
-245 inline void write_mem_u32(uint32_t addr, uint32_t val) {
-246   uint32_t* handle = mem_addr_u32(addr);
-247   if (handle != NULL) *handle = val;
-248 }
-249 inline void write_mem_i32(uint32_t addr, int32_t val) {
-250   int32_t* handle = mem_addr_i32(addr);
-251   if (handle != NULL) *handle = val;
-252 }
-253 
-254 inline bool already_allocated(uint32_t addr) {
-255   bool result = false;
-256   for (int i = 0;  i < SIZE(Mem);  ++i) {
-257     if (Mem.at(i).match(addr)) {
-258       if (result)
-259         raise << "address 0x" << HEXWORD << addr << " is in two segments\n" << end();
-260       result = true;
-261     }
-262   }
-263   return result;
-264 }
-265 
-266 //:: core interpreter loop
-267 
-268 :(code)
-269 // skeleton of how x86 instructions are decoded
-270 void run_one_instruction() {
-271   uint8_t op=0, op2=0, op3=0;
-272   // Run One Instruction
-273   if (Trace_file) {
-274     dump_registers();
-275     // End Dump Info for Instruction
-276   }
-277   uint32_t inst_start_address = EIP;
-278   op = next();
-279   trace(Callstack_depth+1, "run") << "0x" << HEXWORD << inst_start_address << " opcode: " << HEXBYTE << NUM(op) << end();
-280   switch (op) {
-281   case 0xf4:  // hlt
-282     EIP = End_of_program;
-283     break;
-284   // End Single-Byte Opcodes
-285   case 0x0f:
-286     switch(op2 = next()) {
-287     // End Two-Byte Opcodes Starting With 0f
-288     default:
-289       cerr << "unrecognized second opcode after 0f: " << HEXBYTE << NUM(op2) << '\n';
-290       exit(1);
-291     }
-292     break;
-293   case 0xf2:
-294     switch(op2 = next()) {
-295     // End Two-Byte Opcodes Starting With f2
-296     case 0x0f:
-297       switch(op3 = next()) {
-298       // End Three-Byte Opcodes Starting With f2 0f
-299       default:
-300         cerr << "unrecognized third opcode after f2 0f: " << HEXBYTE << NUM(op3) << '\n';
-301         exit(1);
-302       }
-303       break;
-304     default:
-305       cerr << "unrecognized second opcode after f2: " << HEXBYTE << NUM(op2) << '\n';
-306       exit(1);
-307     }
-308     break;
-309   case 0xf3:
-310     switch(op2 = next()) {
-311     // End Two-Byte Opcodes Starting With f3
-312     case 0x0f:
-313       switch(op3 = next()) {
-314       // End Three-Byte Opcodes Starting With f3 0f
-315       default:
-316         cerr << "unrecognized third opcode after f3 0f: " << HEXBYTE << NUM(op3) << '\n';
-317         exit(1);
-318       }
-319       break;
-320     default:
-321       cerr << "unrecognized second opcode after f3: " << HEXBYTE << NUM(op2) << '\n';
-322       exit(1);
-323     }
-324     break;
-325   default:
-326     cerr << "unrecognized opcode: " << HEXBYTE << NUM(op) << '\n';
-327     exit(1);
-328   }
-329 }
-330 
-331 inline uint8_t next() {
-332   return read_mem_u8(EIP++);
-333 }
-334 
-335 void dump_registers() {
-336   ostringstream out;
-337   out << "registers before: ";
-338   for (int i = 0;  i < NUM_INT_REGISTERS;  ++i) {
-339     if (i > 0) out << "; ";
-340     out << "  " << i << ": " << std::hex << std::setw(8) << std::setfill('_') << Reg[i].u;
-341   }
-342   out << " -- SF: " << SF << "; ZF: " << ZF << "; CF: " << CF << "; OF: " << OF;
-343   trace(Callstack_depth+1, "run") << out.str() << end();
-344 }
-345 
-346 //: start tracking supported opcodes
-347 :(before "End Globals")
-348 map</*op*/string, string> Name;
-349 map</*op*/string, string> Name_0f;
-350 map</*op*/string, string> Name_f3;
-351 map</*op*/string, string> Name_f3_0f;
-352 :(before "End One-time Setup")
-353 init_op_names();
-354 :(code)
-355 void init_op_names() {
-356   put(Name, "f4", "halt (hlt)");
-357   // End Initialize Op Names
-358 }
-359 
-360 :(before "End Help Special-cases(key)")
-361 if (key == "opcodes") {
-362   cerr << "Opcodes currently supported by SubX:\n";
-363   for (map<string, string>::iterator p = Name.begin();  p != Name.end();  ++p)
-364     cerr << "  " << p->first << ": " << p->second << '\n';
-365   for (map<string, string>::iterator p = Name_0f.begin();  p != Name_0f.end();  ++p)
-366     cerr << "  0f " << p->first << ": " << p->second << '\n';
-367   for (map<string, string>::iterator p = Name_f3.begin();  p != Name_f3.end();  ++p)
-368     cerr << "  f3 " << p->first << ": " << p->second << '\n';
-369   for (map<string, string>::iterator p = Name_f3_0f.begin();  p != Name_f3_0f.end();  ++p)
-370     cerr << "  f3 0f " << p->first << ": " << p->second << '\n';
-371   cerr << "Run `subx help instructions` for details on words like 'r32' and 'disp8'.\n"
-372           "For complete details on these instructions, consult the IA-32 manual (volume 2).\n"
-373           "There's various versions of it online, such as https://c9x.me/x86.\n"
-374           "The mnemonics in brackets will help you locate each instruction.\n";
-375   return 0;
-376 }
-377 :(before "End Help Contents")
-378 cerr << "  opcodes\n";
-379 
-380 //: Helpers for managing trace depths
-381 //:
-382 //: We're going to use trace depths primarily to segment code running at
-383 //: different frames of the call stack. This will make it easy for the trace
-384 //: browser to collapse over entire calls.
-385 //:
-386 //: Errors will be at depth 0.
-387 //: Warnings will be at depth 1.
-388 //: SubX instructions will occupy depth 2 and up to Max_depth, organized by
-389 //: stack frames. Each instruction's internal details will be one level deeper
-390 //: than its 'main' depth. So 'call' instruction details will be at the same
-391 //: depth as the instructions of the function it calls.
-392 :(before "End Globals")
-393 extern const int Initial_callstack_depth = 2;
-394 int Callstack_depth = Initial_callstack_depth;
-395 :(before "End Reset")
-396 Callstack_depth = Initial_callstack_depth;
-397 
-398 :(before "End Includes")
-399 #include <iomanip>
-400 #define HEXBYTE  std::hex << std::setw(2) << std::setfill('0')
-401 #define HEXWORD  std::hex << std::setw(8) << std::setfill('0')
-402 // ugly that iostream doesn't print uint8_t as an integer
-403 #define NUM(X) static_cast<int>(X)
-404 #include <stdint.h>
+235 inline void write_mem_i8(uint32_t addr, int8_t val) {
+236   int8_t* handle = mem_addr_i8(addr);
+237   if (handle != NULL) *handle = val;
+238 }
+239 inline void write_mem_u32(uint32_t addr, uint32_t val) {
+240   uint32_t* handle = mem_addr_u32(addr);
+241   if (handle != NULL) *handle = val;
+242 }
+243 inline void write_mem_i32(uint32_t addr, int32_t val) {
+244   int32_t* handle = mem_addr_i32(addr);
+245   if (handle != NULL) *handle = val;
+246 }
+247 
+248 inline bool already_allocated(uint32_t addr) {
+249   bool result = false;
+250   for (int i = 0;  i < SIZE(Mem);  ++i) {
+251     if (Mem.at(i).match(addr)) {
+252       if (result)
+253         raise << "address 0x" << HEXWORD << addr << " is in two segments\n" << end();
+254       result = true;
+255     }
+256   }
+257   return result;
+258 }
+259 
+260 //:: core interpreter loop
+261 
+262 :(code)
+263 // skeleton of how x86 instructions are decoded
+264 void run_one_instruction() {
+265   uint8_t op=0, op2=0, op3=0;
+266   // Run One Instruction
+267   if (Trace_file) {
+268     dump_registers();
+269     // End Dump Info for Instruction
+270   }
+271   uint32_t inst_start_address = EIP;
+272   op = next();
+273   trace(Callstack_depth+1, "run") << "0x" << HEXWORD << inst_start_address << " opcode: " << HEXBYTE << NUM(op) << end();
+274   switch (op) {
+275   case 0xf4:  // hlt
+276     EIP = End_of_program;
+277     break;
+278   // End Single-Byte Opcodes
+279   case 0x0f:
+280     switch(op2 = next()) {
+281     // End Two-Byte Opcodes Starting With 0f
+282     default:
+283       cerr << "unrecognized second opcode after 0f: " << HEXBYTE << NUM(op2) << '\n';
+284       exit(1);
+285     }
+286     break;
+287   case 0xf2:
+288     switch(op2 = next()) {
+289     // End Two-Byte Opcodes Starting With f2
+290     case 0x0f:
+291       switch(op3 = next()) {
+292       // End Three-Byte Opcodes Starting With f2 0f
+293       default:
+294         cerr << "unrecognized third opcode after f2 0f: " << HEXBYTE << NUM(op3) << '\n';
+295         exit(1);
+296       }
+297       break;
+298     default:
+299       cerr << "unrecognized second opcode after f2: " << HEXBYTE << NUM(op2) << '\n';
+300       exit(1);
+301     }
+302     break;
+303   case 0xf3:
+304     switch(op2 = next()) {
+305     // End Two-Byte Opcodes Starting With f3
+306     case 0x0f:
+307       switch(op3 = next()) {
+308       // End Three-Byte Opcodes Starting With f3 0f
+309       default:
+310         cerr << "unrecognized third opcode after f3 0f: " << HEXBYTE << NUM(op3) << '\n';
+311         exit(1);
+312       }
+313       break;
+314     default:
+315       cerr << "unrecognized second opcode after f3: " << HEXBYTE << NUM(op2) << '\n';
+316       exit(1);
+317     }
+318     break;
+319   default:
+320     cerr << "unrecognized opcode: " << HEXBYTE << NUM(op) << '\n';
+321     exit(1);
+322   }
+323 }
+324 
+325 inline uint8_t next() {
+326   return read_mem_u8(EIP++);
+327 }
+328 
+329 void dump_registers() {
+330   ostringstream out;
+331   out << "regs: ";
+332   for (int i = 0;  i < NUM_INT_REGISTERS;  ++i) {
+333     if (i > 0) out << "  ";
+334     out << i << ": " << std::hex << std::setw(8) << std::setfill('_') << Reg[i].u;
+335   }
+336   out << " -- SF: " << SF << "; ZF: " << ZF << "; CF: " << CF << "; OF: " << OF;
+337   trace(Callstack_depth+1, "run") << out.str() << end();
+338 }
+339 
+340 //: start tracking supported opcodes
+341 :(before "End Globals")
+342 map</*op*/string, string> Name;
+343 map</*op*/string, string> Name_0f;
+344 map</*op*/string, string> Name_f3;
+345 map</*op*/string, string> Name_f3_0f;
+346 :(before "End One-time Setup")
+347 init_op_names();
+348 :(code)
+349 void init_op_names() {
+350   put(Name, "f4", "halt (hlt)");
+351   // End Initialize Op Names
+352 }
+353 
+354 :(before "End Help Special-cases(key)")
+355 if (key == "opcodes") {
+356   cerr << "Opcodes currently supported by SubX:\n";
+357   for (map<string, string>::iterator p = Name.begin();  p != Name.end();  ++p)
+358     cerr << "  " << p->first << ": " << p->second << '\n';
+359   for (map<string, string>::iterator p = Name_0f.begin();  p != Name_0f.end();  ++p)
+360     cerr << "  0f " << p->first << ": " << p->second << '\n';
+361   for (map<string, string>::iterator p = Name_f3.begin();  p != Name_f3.end();  ++p)
+362     cerr << "  f3 " << p->first << ": " << p->second << '\n';
+363   for (map<string, string>::iterator p = Name_f3_0f.begin();  p != Name_f3_0f.end();  ++p)
+364     cerr << "  f3 0f " << p->first << ": " << p->second << '\n';
+365   cerr << "Run `bootstrap help instructions` for details on words like 'r32' and 'disp8'.\n"
+366           "For complete details on these instructions, consult the IA-32 manual (volume 2).\n"
+367           "There's various versions of it online, such as https://c9x.me/x86.\n"
+368           "The mnemonics in brackets will help you locate each instruction.\n";
+369   return 0;
+370 }
+371 :(before "End Help Contents")
+372 cerr << "  opcodes\n";
+373 
+374 //: Helpers for managing trace depths
+375 //:
+376 //: We're going to use trace depths primarily to segment code running at
+377 //: different frames of the call stack. This will make it easy for the trace
+378 //: browser to collapse over entire calls.
+379 //:
+380 //: Errors will be at depth 0.
+381 //: Warnings will be at depth 1.
+382 //: SubX instructions will occupy depth 2 and up to Max_depth, organized by
+383 //: stack frames. Each instruction's internal details will be one level deeper
+384 //: than its 'main' depth. So 'call' instruction details will be at the same
+385 //: depth as the instructions of the function it calls.
+386 :(before "End Globals")
+387 extern const int Initial_callstack_depth = 2;
+388 int Callstack_depth = Initial_callstack_depth;
+389 :(before "End Reset")
+390 Callstack_depth = Initial_callstack_depth;
+391 
+392 :(before "End Includes")
+393 #include <iomanip>
+394 #define HEXBYTE  std::hex << std::setw(2) << std::setfill('0')
+395 #define HEXWORD  std::hex << std::setw(8) << std::setfill('0')
+396 // ugly that iostream doesn't print uint8_t as an integer
+397 #define NUM(X) static_cast<int>(X)
+398 #include <stdint.h>
 
diff --git a/html/011run.cc.html b/html/011run.cc.html index 26bad7d7..003e8139 100644 --- a/html/011run.cc.html +++ b/html/011run.cc.html @@ -64,22 +64,22 @@ if ('onhashchange' in window) { 4 //: the VM. That comes later.) 5 6 :(before "End Help Texts") - 7 put_new(Help, "syntax", - 8 "SubX programs consist of segments, each segment in turn consisting of lines.\n" - 9 "Line-endings are significant; each line should contain a single\n" + 7 put_new(Help, "syntax", + 8 "SubX programs consist of segments, each segment in turn consisting of lines.\n" + 9 "Line-endings are significant; each line should contain a single\n" 10 "instruction, macro or directive.\n" 11 "\n" 12 "Comments start with the '#' character. It should be at the start of a word\n" - 13 "(start of line, or following a space).\n" + 13 "(start of line, or following a space).\n" 14 "\n" - 15 "Each segment starts with a header line: a '==' delimiter followed by the name of\n" + 15 "Each segment starts with a header line: a '==' delimiter followed by the name of\n" 16 "the segment and a (sometimes approximate) starting address in memory.\n" - 17 "The name 'code' is special; instructions to execute should always go here.\n" + 17 "The name 'code' is special; instructions to execute should always go here.\n" 18 "\n" 19 "The resulting binary starts running code from a label called 'Entry'\n" 20 "in the code segment.\n" 21 "\n" - 22 "Segments with the same name get merged together. This rule helps keep functions and\n" + 22 "Segments with the same name get merged together. This rule helps keep functions and\n" 23 "their data close together in .subx files.\n" 24 "You don't have to specify the starting address after the first time.\n" 25 "\n" @@ -87,7 +87,7 @@ if ('onhashchange' in window) { 27 "after a '/', but they can never contain whitespace. Metadata has no effect\n" 28 "at runtime, but can be handy when rewriting macros.\n" 29 "\n" - 30 "Check out the examples in the examples/ directory.\n" + 30 "Check out the example programs in the apps/ directory, particularly apps/ex*.\n" 31 ); 32 :(before "End Help Contents") 33 cerr << " syntax\n"; @@ -96,7 +96,7 @@ if ('onhashchange' in window) { 36 void test_copy_imm32_to_EAX() { 37 // At the lowest level, SubX programs are a series of hex bytes, each 38 // (variable-length) instruction on one line. - 39 run( + 39 run( 40 // Comments start with '#' and are ignored. 41 "# comment\n" 42 // Segment headers start with '==', a name and a starting hex address. @@ -138,393 +138,379 @@ if ('onhashchange' in window) { 78 ); 79 } 80 - 81 // top-level helper for scenarios: parse the input, transform any macros, load - 82 // the final hex bytes into memory, run it - 83 void run(const string& text_bytes) { - 84 program p; - 85 istringstream in(text_bytes); + 81 // top-level helper for tests: parse the input, load the hex bytes into memory, run + 82 void run(const string& text_bytes) { + 83 program p; + 84 istringstream in(text_bytes); + 85 // Loading Test Program 86 parse(in, p); 87 if (trace_contains_errors()) return; // if any stage raises errors, stop immediately - 88 transform(p); - 89 if (trace_contains_errors()) return; - 90 load(p); - 91 if (trace_contains_errors()) return; - 92 // convenience to keep tests concise: 'Entry' label need not be provided - 93 // not allowed in real programs - 94 if (p.entry) - 95 EIP = p.entry; - 96 else - 97 EIP = find(p, "code")->start; - 98 while (EIP < End_of_program) - 99 run_one_instruction(); -100 } -101 -102 //:: core data structures -103 -104 :(before "End Types") -105 struct program { -106 uint32_t entry; -107 vector<segment> segments; -108 program() { entry = 0; } -109 }; -110 :(before "struct program") -111 struct segment { -112 string name; -113 uint32_t start; -114 vector<line> lines; -115 // End segment Fields -116 segment() { -117 start = 0; -118 // End segment Constructor -119 } -120 }; -121 :(before "struct segment") -122 struct line { -123 vector<word> words; -124 vector<string> metadata; -125 string original; -126 }; -127 :(before "struct line") -128 struct word { -129 string original; -130 string data; -131 vector<string> metadata; -132 }; -133 -134 //:: parse -135 -136 :(code) -137 void parse(istream& fin, program& out) { -138 segment* curr_segment = NULL; -139 vector<line> l; -140 while (has_data(fin)) { -141 string line_data; -142 line curr; -143 getline(fin, line_data); -144 curr.original = line_data; -145 trace(99, "parse") << "line: " << line_data << end(); -146 // End Line Parsing Special-cases(line_data -> l) -147 istringstream lin(line_data); -148 while (has_data(lin)) { -149 string word_data; -150 lin >> word_data; -151 if (word_data.empty()) continue; -152 if (word_data[0] == '#') break; // comment -153 if (word_data == ".") continue; // comment token -154 if (word_data == "==") { -155 flush(curr_segment, l); -156 string segment_name; -157 lin >> segment_name; -158 curr_segment = find(out, segment_name); -159 if (curr_segment != NULL) { -160 trace(3, "parse") << "appending to segment '" << segment_name << "'" << end(); -161 } -162 else { -163 trace(3, "parse") << "new segment '" << segment_name << "'" << end(); -164 uint32_t seg_start = 0; -165 lin >> std::hex >> seg_start; -166 sanity_check_program_segment(out, seg_start); -167 out.segments.push_back(segment()); -168 curr_segment = &out.segments.back(); -169 curr_segment->name = segment_name; -170 curr_segment->start = seg_start; -171 if (trace_contains_errors()) continue; -172 trace(3, "parse") << "starts at address 0x" << HEXWORD << curr_segment->start << end(); -173 } -174 break; // skip rest of line -175 } -176 if (word_data[0] == ':') { -177 // todo: line metadata -178 break; -179 } -180 curr.words.push_back(word()); -181 parse_word(word_data, curr.words.back()); -182 trace(99, "parse") << "word: " << to_string(curr.words.back()); -183 } -184 if (!curr.words.empty()) -185 l.push_back(curr); -186 } -187 flush(curr_segment, l); -188 trace(99, "parse") << "done" << end(); -189 } -190 -191 segment* find(program& p, const string& segment_name) { -192 for (int i = 0; i < SIZE(p.segments); ++i) { -193 if (p.segments.at(i).name == segment_name) -194 return &p.segments.at(i); -195 } -196 return NULL; -197 } -198 -199 void flush(segment* s, vector<line>& lines) { -200 if (lines.empty()) return; -201 if (s == NULL) { -202 raise << "input does not start with a '==' section header\n" << end(); -203 return; -204 } -205 trace(3, "parse") << "flushing segment" << end(); -206 s->lines.insert(s->lines.end(), lines.begin(), lines.end()); -207 lines.clear(); -208 } -209 -210 void parse_word(const string& data, word& out) { -211 out.original = data; -212 istringstream win(data); -213 if (getline(win, out.data, '/')) { -214 string m; -215 while (getline(win, m, '/')) -216 out.metadata.push_back(m); -217 } -218 } -219 -220 void sanity_check_program_segment(const program& p, uint32_t addr) { -221 for (int i = 0; i < SIZE(p.segments); ++i) { -222 if (p.segments.at(i).start == addr) -223 raise << "can't have multiple segments starting at address 0x" << HEXWORD << addr << '\n' << end(); -224 } -225 } -226 -227 // helper for tests -228 void parse(const string& text_bytes) { -229 program p; -230 istringstream in(text_bytes); -231 parse(in, p); -232 } -233 -234 void test_detect_duplicate_segments() { -235 Hide_errors = true; -236 parse( -237 "== segment1 0xee\n" -238 "ab\n" -239 "== segment2 0xee\n" -240 "cd\n" -241 ); -242 CHECK_TRACE_CONTENTS( -243 "error: can't have multiple segments starting at address 0x000000ee\n" -244 ); -245 } -246 -247 //:: transform -248 -249 :(before "End Types") -250 typedef void (*transform_fn)(program&); -251 :(before "End Globals") -252 vector<transform_fn> Transform; -253 -254 :(code) -255 void transform(program& p) { -256 for (int t = 0; t < SIZE(Transform); ++t) -257 (*Transform.at(t))(p); -258 } -259 -260 //:: load -261 -262 void load(const program& p) { -263 if (find(p, "code") == NULL) { -264 raise << "no code to run\n" << end(); -265 return; -266 } -267 // Ensure segments are disjoint. -268 set<uint32_t> overlap; -269 for (int i = 0; i < SIZE(p.segments); ++i) { -270 const segment& seg = p.segments.at(i); -271 uint32_t addr = seg.start; -272 if (!already_allocated(addr)) -273 Mem.push_back(vma(seg.start)); -274 trace(99, "load") << "loading segment " << i << " from " << HEXWORD << addr << end(); -275 for (int j = 0; j < SIZE(seg.lines); ++j) { -276 const line& l = seg.lines.at(j); -277 for (int k = 0; k < SIZE(l.words); ++k) { -278 const word& w = l.words.at(k); -279 uint8_t val = hex_byte(w.data); -280 if (trace_contains_errors()) return; -281 assert(overlap.find(addr) == overlap.end()); -282 write_mem_u8(addr, val); -283 overlap.insert(addr); -284 trace(99, "load") << "0x" << HEXWORD << addr << " -> " << HEXBYTE << NUM(read_mem_u8(addr)) << end(); -285 ++addr; -286 } -287 } -288 if (seg.name == "code") { -289 End_of_program = addr; -290 } -291 } -292 } -293 -294 const segment* find(const program& p, const string& segment_name) { -295 for (int i = 0; i < SIZE(p.segments); ++i) { -296 if (p.segments.at(i).name == segment_name) -297 return &p.segments.at(i); -298 } -299 return NULL; -300 } -301 -302 uint8_t hex_byte(const string& s) { -303 if (contains_uppercase(s)) { -304 raise << "uppercase hex not allowed: " << s << '\n' << end(); -305 return 0; -306 } -307 istringstream in(s); -308 int result = 0; -309 in >> std::hex >> result; -310 if (!in || !in.eof()) { -311 raise << "token '" << s << "' is not a hex byte\n" << end(); -312 return '\0'; -313 } -314 if (result > 0xff || result < -0x8f) { -315 raise << "token '" << s << "' is not a hex byte\n" << end(); -316 return '\0'; -317 } -318 return static_cast<uint8_t>(result); -319 } -320 -321 void test_number_too_large() { -322 Hide_errors = true; -323 parse_and_load( -324 "== code 0x1\n" -325 "01 cab\n" + 88 // Running Test Program + 89 load(p); + 90 if (trace_contains_errors()) return; + 91 // convenience to keep tests concise: 'Entry' label need not be provided + 92 // not allowed in real programs + 93 if (p.entry) + 94 EIP = p.entry; + 95 else + 96 EIP = find(p, "code")->start; + 97 while (EIP < End_of_program) + 98 run_one_instruction(); + 99 } +100 +101 //:: core data structures +102 +103 :(before "End Types") +104 struct program { +105 uint32_t entry; +106 vector<segment> segments; +107 program() { entry = 0; } +108 }; +109 :(before "struct program") +110 struct segment { +111 string name; +112 uint32_t start; +113 vector<line> lines; +114 // End segment Fields +115 segment() { +116 start = 0; +117 // End segment Constructor +118 } +119 }; +120 :(before "struct segment") +121 struct line { +122 vector<word> words; +123 vector<string> metadata; +124 string original; +125 }; +126 :(before "struct line") +127 struct word { +128 string original; +129 string data; +130 vector<string> metadata; +131 }; +132 +133 //:: parse +134 +135 :(code) +136 void parse(istream& fin, program& out) { +137 segment* curr_segment = NULL; +138 vector<line> l; +139 while (has_data(fin)) { +140 string line_data; +141 line curr; +142 getline(fin, line_data); +143 curr.original = line_data; +144 trace(99, "parse") << "line: " << line_data << end(); +145 // End Line Parsing Special-cases(line_data -> l) +146 istringstream lin(line_data); +147 while (has_data(lin)) { +148 string word_data; +149 lin >> word_data; +150 if (word_data.empty()) continue; +151 if (word_data[0] == '#') break; // comment +152 if (word_data == ".") continue; // comment token +153 if (word_data == "==") { +154 flush(curr_segment, l); +155 string segment_name; +156 lin >> segment_name; +157 curr_segment = find(out, segment_name); +158 if (curr_segment != NULL) { +159 trace(3, "parse") << "appending to segment '" << segment_name << "'" << end(); +160 } +161 else { +162 trace(3, "parse") << "new segment '" << segment_name << "'" << end(); +163 uint32_t seg_start = 0; +164 lin >> std::hex >> seg_start; +165 sanity_check_program_segment(out, seg_start); +166 out.segments.push_back(segment()); +167 curr_segment = &out.segments.back(); +168 curr_segment->name = segment_name; +169 curr_segment->start = seg_start; +170 if (trace_contains_errors()) continue; +171 trace(3, "parse") << "starts at address 0x" << HEXWORD << curr_segment->start << end(); +172 } +173 break; // skip rest of line +174 } +175 if (word_data[0] == ':') { +176 // todo: line metadata +177 break; +178 } +179 curr.words.push_back(word()); +180 parse_word(word_data, curr.words.back()); +181 trace(99, "parse") << "word: " << to_string(curr.words.back()); +182 } +183 if (!curr.words.empty()) +184 l.push_back(curr); +185 } +186 flush(curr_segment, l); +187 trace(99, "parse") << "done" << end(); +188 } +189 +190 segment* find(program& p, const string& segment_name) { +191 for (int i = 0; i < SIZE(p.segments); ++i) { +192 if (p.segments.at(i).name == segment_name) +193 return &p.segments.at(i); +194 } +195 return NULL; +196 } +197 +198 void flush(segment* s, vector<line>& lines) { +199 if (lines.empty()) return; +200 if (s == NULL) { +201 raise << "input does not start with a '==' section header\n" << end(); +202 return; +203 } +204 trace(3, "parse") << "flushing segment" << end(); +205 s->lines.insert(s->lines.end(), lines.begin(), lines.end()); +206 lines.clear(); +207 } +208 +209 void parse_word(const string& data, word& out) { +210 out.original = data; +211 istringstream win(data); +212 if (getline(win, out.data, '/')) { +213 string m; +214 while (getline(win, m, '/')) +215 out.metadata.push_back(m); +216 } +217 } +218 +219 void sanity_check_program_segment(const program& p, uint32_t addr) { +220 for (int i = 0; i < SIZE(p.segments); ++i) { +221 if (p.segments.at(i).start == addr) +222 raise << "can't have multiple segments starting at address 0x" << HEXWORD << addr << '\n' << end(); +223 } +224 } +225 +226 // helper for tests +227 void parse(const string& text_bytes) { +228 program p; +229 istringstream in(text_bytes); +230 parse(in, p); +231 } +232 +233 void test_detect_duplicate_segments() { +234 Hide_errors = true; +235 parse( +236 "== segment1 0xee\n" +237 "ab\n" +238 "== segment2 0xee\n" +239 "cd\n" +240 ); +241 CHECK_TRACE_CONTENTS( +242 "error: can't have multiple segments starting at address 0x000000ee\n" +243 ); +244 } +245 +246 //:: load +247 +248 void load(const program& p) { +249 if (find(p, "code") == NULL) { +250 raise << "no code to run\n" << end(); +251 return; +252 } +253 // Ensure segments are disjoint. +254 set<uint32_t> overlap; +255 for (int i = 0; i < SIZE(p.segments); ++i) { +256 const segment& seg = p.segments.at(i); +257 uint32_t addr = seg.start; +258 if (!already_allocated(addr)) +259 Mem.push_back(vma(seg.start)); +260 trace(99, "load") << "loading segment " << i << " from " << HEXWORD << addr << end(); +261 for (int j = 0; j < SIZE(seg.lines); ++j) { +262 const line& l = seg.lines.at(j); +263 for (int k = 0; k < SIZE(l.words); ++k) { +264 const word& w = l.words.at(k); +265 uint8_t val = hex_byte(w.data); +266 if (trace_contains_errors()) return; +267 assert(overlap.find(addr) == overlap.end()); +268 write_mem_u8(addr, val); +269 overlap.insert(addr); +270 trace(99, "load") << "0x" << HEXWORD << addr << " -> " << HEXBYTE << NUM(read_mem_u8(addr)) << end(); +271 ++addr; +272 } +273 } +274 if (seg.name == "code") { +275 End_of_program = addr; +276 } +277 } +278 } +279 +280 const segment* find(const program& p, const string& segment_name) { +281 for (int i = 0; i < SIZE(p.segments); ++i) { +282 if (p.segments.at(i).name == segment_name) +283 return &p.segments.at(i); +284 } +285 return NULL; +286 } +287 +288 uint8_t hex_byte(const string& s) { +289 if (contains_uppercase(s)) { +290 raise << "uppercase hex not allowed: " << s << '\n' << end(); +291 return 0; +292 } +293 istringstream in(s); +294 int result = 0; +295 in >> std::hex >> result; +296 if (!in || !in.eof()) { +297 raise << "token '" << s << "' is not a hex byte\n" << end(); +298 return '\0'; +299 } +300 if (result > 0xff || result < -0x8f) { +301 raise << "token '" << s << "' is not a hex byte\n" << end(); +302 return '\0'; +303 } +304 return static_cast<uint8_t>(result); +305 } +306 +307 void test_number_too_large() { +308 Hide_errors = true; +309 parse_and_load( +310 "== code 0x1\n" +311 "01 cab\n" +312 ); +313 CHECK_TRACE_CONTENTS( +314 "error: token 'cab' is not a hex byte\n" +315 ); +316 } +317 +318 void test_invalid_hex() { +319 Hide_errors = true; +320 parse_and_load( +321 "== code 0x1\n" +322 "01 cx\n" +323 ); +324 CHECK_TRACE_CONTENTS( +325 "error: token 'cx' is not a hex byte\n" 326 ); -327 CHECK_TRACE_CONTENTS( -328 "error: token 'cab' is not a hex byte\n" -329 ); -330 } -331 -332 void test_invalid_hex() { -333 Hide_errors = true; -334 parse_and_load( -335 "== code 0x1\n" -336 "01 cx\n" -337 ); -338 CHECK_TRACE_CONTENTS( -339 "error: token 'cx' is not a hex byte\n" -340 ); -341 } -342 -343 void test_negative_number() { -344 parse_and_load( -345 "== code 0x1\n" -346 "01 -02\n" -347 ); -348 CHECK_TRACE_COUNT("error", 0); -349 } -350 -351 void test_negative_number_too_small() { -352 Hide_errors = true; -353 parse_and_load( -354 "== code 0x1\n" -355 "01 -12345\n" -356 ); -357 CHECK_TRACE_CONTENTS( -358 "error: token '-12345' is not a hex byte\n" -359 ); -360 } -361 -362 void test_hex_prefix() { -363 parse_and_load( -364 "== code 0x1\n" -365 "0x01 -0x02\n" -366 ); -367 CHECK_TRACE_COUNT("error", 0); -368 } -369 -370 void test_repeated_segment_merges_data() { -371 parse_and_load( -372 "== code 0x1\n" -373 "11 22\n" -374 "== code\n" // again -375 "33 44\n" -376 ); -377 CHECK_TRACE_CONTENTS( -378 "parse: new segment 'code'\n" -379 "parse: appending to segment 'code'\n" -380 // first segment -381 "load: 0x00000001 -> 11\n" -382 "load: 0x00000002 -> 22\n" -383 // second segment -384 "load: 0x00000003 -> 33\n" -385 "load: 0x00000004 -> 44\n" -386 ); -387 } -388 -389 void test_error_on_missing_segment_header() { -390 Hide_errors = true; -391 parse_and_load( -392 "01 02\n" +327 } +328 +329 void test_negative_number() { +330 parse_and_load( +331 "== code 0x1\n" +332 "01 -02\n" +333 ); +334 CHECK_TRACE_COUNT("error", 0); +335 } +336 +337 void test_negative_number_too_small() { +338 Hide_errors = true; +339 parse_and_load( +340 "== code 0x1\n" +341 "01 -12345\n" +342 ); +343 CHECK_TRACE_CONTENTS( +344 "error: token '-12345' is not a hex byte\n" +345 ); +346 } +347 +348 void test_hex_prefix() { +349 parse_and_load( +350 "== code 0x1\n" +351 "0x01 -0x02\n" +352 ); +353 CHECK_TRACE_COUNT("error", 0); +354 } +355 +356 void test_repeated_segment_merges_data() { +357 parse_and_load( +358 "== code 0x1\n" +359 "11 22\n" +360 "== code\n" // again +361 "33 44\n" +362 ); +363 CHECK_TRACE_CONTENTS( +364 "parse: new segment 'code'\n" +365 "parse: appending to segment 'code'\n" +366 // first segment +367 "load: 0x00000001 -> 11\n" +368 "load: 0x00000002 -> 22\n" +369 // second segment +370 "load: 0x00000003 -> 33\n" +371 "load: 0x00000004 -> 44\n" +372 ); +373 } +374 +375 void test_error_on_missing_segment_header() { +376 Hide_errors = true; +377 parse_and_load( +378 "01 02\n" +379 ); +380 CHECK_TRACE_CONTENTS( +381 "error: input does not start with a '==' section header\n" +382 ); +383 } +384 +385 void test_error_on_uppercase_hex() { +386 Hide_errors = true; +387 parse_and_load( +388 "== code\n" +389 "01 Ab\n" +390 ); +391 CHECK_TRACE_CONTENTS( +392 "error: uppercase hex not allowed: Ab\n" 393 ); -394 CHECK_TRACE_CONTENTS( -395 "error: input does not start with a '==' section header\n" -396 ); -397 } -398 -399 void test_error_on_uppercase_hex() { -400 Hide_errors = true; -401 parse_and_load( -402 "== code\n" -403 "01 Ab\n" -404 ); -405 CHECK_TRACE_CONTENTS( -406 "error: uppercase hex not allowed: Ab\n" -407 ); -408 } +394 } +395 +396 //: helper for tests +397 void parse_and_load(const string& text_bytes) { +398 program p; +399 istringstream in(text_bytes); +400 parse(in, p); +401 if (trace_contains_errors()) return; // if any stage raises errors, stop immediately +402 load(p); +403 } +404 +405 //:: run +406 +407 :(before "End Initialize Op Names") +408 put_new(Name, "b8", "copy imm32 to EAX (mov)"); 409 -410 //: helper for tests -411 void parse_and_load(const string& text_bytes) { -412 program p; -413 istringstream in(text_bytes); -414 parse(in, p); -415 if (trace_contains_errors()) return; // if any stage raises errors, stop immediately -416 load(p); -417 } -418 -419 //:: run -420 -421 :(before "End Initialize Op Names") -422 put_new(Name, "b8", "copy imm32 to EAX (mov)"); -423 -424 //: our first opcode -425 -426 :(before "End Single-Byte Opcodes") -427 case 0xb8: { // copy imm32 to EAX -428 const int32_t src = next32(); -429 trace(Callstack_depth+1, "run") << "copy imm32 0x" << HEXWORD << src << " to EAX" << end(); -430 Reg[EAX].i = src; -431 break; -432 } -433 -434 :(code) -435 void test_copy_imm32_to_EAX_again() { -436 run( -437 "== code 0x1\n" // code segment -438 // op ModR/M SIB displacement immediate -439 " b8 0a 0b 0c 0d \n" // copy 0x0d0c0b0a to EAX -440 ); -441 CHECK_TRACE_CONTENTS( -442 "run: copy imm32 0x0d0c0b0a to EAX\n" -443 ); -444 } -445 -446 // read a 32-bit int in little-endian order from the instruction stream -447 int32_t next32() { -448 int32_t result = read_mem_i32(EIP); -449 EIP+=4; -450 return result; -451 } -452 -453 //:: helpers -454 -455 string to_string(const word& w) { -456 ostringstream out; -457 out << w.data; -458 for (int i = 0; i < SIZE(w.metadata); ++i) -459 out << " /" << w.metadata.at(i); -460 return out.str(); -461 } -462 -463 bool contains_uppercase(const string& s) { -464 for (int i = 0; i < SIZE(s); ++i) -465 if (isupper(s.at(i))) return true; -466 return false; -467 } +410 //: our first opcode +411 +412 :(before "End Single-Byte Opcodes") +413 case 0xb8: { // copy imm32 to EAX +414 const int32_t src = next32(); +415 trace(Callstack_depth+1, "run") << "copy imm32 0x" << HEXWORD << src << " to EAX" << end(); +416 Reg[EAX].i = src; +417 break; +418 } +419 +420 :(code) +421 void test_copy_imm32_to_EAX_again() { +422 run( +423 "== code 0x1\n" // code segment +424 // op ModR/M SIB displacement immediate +425 " b8 0a 0b 0c 0d \n" // copy 0x0d0c0b0a to EAX +426 ); +427 CHECK_TRACE_CONTENTS( +428 "run: copy imm32 0x0d0c0b0a to EAX\n" +429 ); +430 } +431 +432 // read a 32-bit int in little-endian order from the instruction stream +433 int32_t next32() { +434 int32_t result = read_mem_i32(EIP); +435 EIP+=4; +436 return result; +437 } +438 +439 //:: helpers +440 +441 string to_string(const word& w) { +442 ostringstream out; +443 out << w.data; +444 for (int i = 0; i < SIZE(w.metadata); ++i) +445 out << " /" << w.metadata.at(i); +446 return out.str(); +447 } +448 +449 bool contains_uppercase(const string& s) { +450 for (int i = 0; i < SIZE(s); ++i) +451 if (isupper(s.at(i))) return true; +452 return false; +453 } diff --git a/html/012elf.cc.html b/html/012elf.cc.html index 2aaa61ab..2b8d8236 100644 --- a/html/012elf.cc.html +++ b/html/012elf.cc.html @@ -65,7 +65,7 @@ if ('onhashchange' in window) { 4 5 :(before "End Main") 6 assert(argc > 1); - 7 if (is_equal(argv[1], "run")) { + 7 if (is_equal(argv[1], "run")) { 8 // Outside of tests, traces must be explicitly requested. 9 if (Trace_file.is_open()) Trace_stream = new trace_stream; 10 trace(2, "run") << "=== Starting to run" << end(); @@ -75,9 +75,9 @@ if ('onhashchange' in window) { 14 reset(); 15 cerr << std::hex; 16 load_elf(argv[2], argc, argv); - 17 while (EIP < End_of_program) // weak final-gasp termination check - 18 run_one_instruction(); - 19 raise << "executed past end of the world: " << EIP << " vs " << End_of_program << '\n' << end(); + 17 while (EIP < End_of_program) // weak final-gasp termination check + 18 run_one_instruction(); + 19 raise << "executed past end of the world: " << EIP << " vs " << End_of_program << '\n' << end(); 20 return 1; 21 } 22 @@ -107,7 +107,7 @@ if ('onhashchange' in window) { 46 // unused: remaining 10 bytes of e_ident 47 uint32_t e_machine_type = u32_in(&elf_contents[16]); 48 if (e_machine_type != 0x00030002) - 49 raise << "ELF type/machine 0x" << HEXWORD << e_machine_type << " isn't i386 executable\n" << die(); + 49 raise << "ELF type/machine 0x" << HEXWORD << e_machine_type << " isn't i386 executable\n" << die(); 50 // unused: e_version. We only support version 1, and later versions will be backwards compatible. 51 uint32_t e_entry = u32_in(&elf_contents[24]); 52 uint32_t e_phoff = u32_in(&elf_contents[28]); @@ -128,41 +128,41 @@ if ('onhashchange' in window) { 67 68 // initialize code and stack 69 assert(overlap.find(STACK_SEGMENT) == overlap.end()); - 70 Mem.push_back(vma(STACK_SEGMENT)); + 70 Mem.push_back(vma(STACK_SEGMENT)); 71 assert(overlap.find(AFTER_STACK) == overlap.end()); 72 // The stack grows downward. - 73 Reg[ESP].u = AFTER_STACK; - 74 Reg[EBP].u = 0; - 75 EIP = e_entry; + 73 Reg[ESP].u = AFTER_STACK; + 74 Reg[EBP].u = 0; + 75 EIP = e_entry; 76 77 // initialize args on stack 78 // no envp for now 79 // we wastefully use a separate page of memory for argv - 80 Mem.push_back(vma(ARGV_DATA_SEGMENT)); + 80 Mem.push_back(vma(ARGV_DATA_SEGMENT)); 81 uint32_t argv_data = ARGV_DATA_SEGMENT; 82 for (int i = argc-1; i >= /*skip 'subx_bin' and 'run'*/2; --i) { 83 push(argv_data); 84 for (size_t j = 0; j <= strlen(argv[i]); ++j) { 85 assert(overlap.find(argv_data) == overlap.end()); // don't bother comparing ARGV and STACK - 86 write_mem_u8(argv_data, argv[i][j]); + 86 write_mem_u8(argv_data, argv[i][j]); 87 argv_data += sizeof(char); - 88 assert(argv_data < ARGV_DATA_SEGMENT + SEGMENT_ALIGNMENT); + 88 assert(argv_data < ARGV_DATA_SEGMENT + SEGMENT_ALIGNMENT); 89 } 90 } 91 push(argc-/*skip 'subx_bin' and 'run'*/2); 92 } 93 94 void push(uint32_t val) { - 95 Reg[ESP].u -= 4; - 96 if (Reg[ESP].u < STACK_SEGMENT) { + 95 Reg[ESP].u -= 4; + 96 if (Reg[ESP].u < STACK_SEGMENT) { 97 raise << "The stack overflowed its segment. " 98 << "Maybe SPACE_FOR_SEGMENT should be larger? " 99 << "Or you need to carve out an exception for the stack segment " 100 << "to be larger.\n" << die(); 101 } -102 trace(Callstack_depth+1, "run") << "decrementing ESP to 0x" << HEXWORD << Reg[ESP].u << end(); -103 trace(Callstack_depth+1, "run") << "pushing value 0x" << HEXWORD << val << end(); -104 write_mem_u32(Reg[ESP].u, val); +102 trace(Callstack_depth+1, "run") << "decrementing ESP to 0x" << HEXWORD << Reg[ESP].u << end(); +103 trace(Callstack_depth+1, "run") << "pushing value 0x" << HEXWORD << val << end(); +104 write_mem_u32(Reg[ESP].u, val); 105 } 106 107 void load_segment_from_program_header(uint8_t* elf_contents, int segment_index, size_t size, uint32_t offset, uint32_t e_ehsize, set<uint32_t>& overlap) { @@ -183,20 +183,20 @@ if ('onhashchange' in window) { 122 123 if (p_offset + p_filesz > size) 124 raise << "Invalid binary; segment at offset " << offset << " is too large: wants to end at " << p_offset+p_filesz << " but the file ends at " << size << '\n' << die(); -125 if (p_memsz >= SEGMENT_ALIGNMENT) { +125 if (p_memsz >= SEGMENT_ALIGNMENT) { 126 raise << "Code segment too small for SubX; for now please manually increase SEGMENT_ALIGNMENT.\n" << end(); 127 return; 128 } 129 trace(90, "load") << "blitting file offsets (" << p_offset << ", " << (p_offset+p_filesz) << ") to addresses (" << p_vaddr << ", " << (p_vaddr+p_memsz) << ')' << end(); 130 if (size > p_memsz) size = p_memsz; -131 Mem.push_back(vma(p_vaddr)); +131 Mem.push_back(vma(p_vaddr)); 132 for (size_t i = 0; i < p_filesz; ++i) { 133 assert(overlap.find(p_vaddr+i) == overlap.end()); -134 write_mem_u8(p_vaddr+i, elf_contents[p_offset+i]); +134 write_mem_u8(p_vaddr+i, elf_contents[p_offset+i]); 135 overlap.insert(p_vaddr+i); 136 } -137 if (segment_index == 0 && End_of_program < p_vaddr+p_memsz) -138 End_of_program = p_vaddr+p_memsz; +137 if (segment_index == 0 && End_of_program < p_vaddr+p_memsz) +138 End_of_program = p_vaddr+p_memsz; 139 } 140 141 :(before "End Includes") @@ -221,11 +221,11 @@ if ('onhashchange' in window) { 160 void dump_stack() { 161 ostringstream out; 162 trace(Callstack_depth+1, "run") << "stack:" << end(); -163 for (uint32_t a = AFTER_STACK-4; a > Reg[ESP].u; a -= 4) -164 trace(Callstack_depth+2, "run") << " 0x" << HEXWORD << a << " => 0x" << HEXWORD << read_mem_u32(a) << end(); -165 trace(Callstack_depth+2, "run") << " 0x" << HEXWORD << Reg[ESP].u << " => 0x" << HEXWORD << read_mem_u32(Reg[ESP].u) << " <=== ESP" << end(); -166 for (uint32_t a = Reg[ESP].u-4; a > Reg[ESP].u-40; a -= 4) -167 trace(Callstack_depth+2, "run") << " 0x" << HEXWORD << a << " => 0x" << HEXWORD << read_mem_u32(a) << end(); +163 for (uint32_t a = AFTER_STACK-4; a > Reg[ESP].u; a -= 4) +164 trace(Callstack_depth+2, "run") << " 0x" << HEXWORD << a << " => 0x" << HEXWORD << read_mem_u32(a) << end(); +165 trace(Callstack_depth+2, "run") << " 0x" << HEXWORD << Reg[ESP].u << " => 0x" << HEXWORD << read_mem_u32(Reg[ESP].u) << " <=== ESP" << end(); +166 for (uint32_t a = Reg[ESP].u-4; a > Reg[ESP].u-40; a -= 4) +167 trace(Callstack_depth+2, "run") << " 0x" << HEXWORD << a << " => 0x" << HEXWORD << read_mem_u32(a) << end(); 168 } 169 170 inline uint32_t u32_in(uint8_t* p) { diff --git a/html/013direct_addressing.cc.html b/html/013direct_addressing.cc.html index 26ca42a5..8256bc5b 100644 --- a/html/013direct_addressing.cc.html +++ b/html/013direct_addressing.cc.html @@ -62,20 +62,20 @@ if ('onhashchange' in window) { 1 //: operating directly on a register 2 3 :(before "End Initialize Op Names") - 4 put_new(Name, "01", "add r32 to rm32 (add)"); + 4 put_new(Name, "01", "add r32 to rm32 (add)"); 5 6 :(code) 7 void test_add_r32_to_r32() { - 8 Reg[EAX].i = 0x10; - 9 Reg[EBX].i = 1; - 10 run( + 8 Reg[EAX].i = 0x10; + 9 Reg[EBX].i = 1; + 10 run( 11 "== code 0x1\n" // code segment 12 // op ModR/M SIB displacement immediate 13 " 01 d8 \n" // add EBX to EAX 14 // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX) 15 ); 16 CHECK_TRACE_CONTENTS( - 17 "run: add EBX to r/m32\n" + 17 "run: add EBX to r/m32\n" 18 "run: r/m32 is EAX\n" 19 "run: storing 0x00000011\n" 20 ); @@ -83,38 +83,38 @@ if ('onhashchange' in window) { 22 23 :(before "End Single-Byte Opcodes") 24 case 0x01: { // add r32 to r/m32 - 25 uint8_t modrm = next(); + 25 uint8_t modrm = next(); 26 uint8_t arg2 = (modrm>>3)&0x7; 27 trace(Callstack_depth+1, "run") << "add " << rname(arg2) << " to r/m32" << end(); 28 int32_t* signed_arg1 = effective_address(modrm); - 29 int32_t signed_result = *signed_arg1 + Reg[arg2].i; + 29 int32_t signed_result = *signed_arg1 + Reg[arg2].i; 30 SF = (signed_result < 0); - 31 ZF = (signed_result == 0); - 32 int64_t signed_full_result = static_cast<int64_t>(*signed_arg1) + Reg[arg2].i; - 33 OF = (signed_result != signed_full_result); + 31 ZF = (signed_result == 0); + 32 int64_t signed_full_result = static_cast<int64_t>(*signed_arg1) + Reg[arg2].i; + 33 OF = (signed_result != signed_full_result); 34 // set CF 35 uint32_t unsigned_arg1 = static_cast<uint32_t>(*signed_arg1); - 36 uint32_t unsigned_result = unsigned_arg1 + Reg[arg2].u; - 37 uint64_t unsigned_full_result = static_cast<uint64_t>(unsigned_arg1) + Reg[arg2].u; - 38 CF = (unsigned_result != unsigned_full_result); - 39 trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); + 36 uint32_t unsigned_result = unsigned_arg1 + Reg[arg2].u; + 37 uint64_t unsigned_full_result = static_cast<uint64_t>(unsigned_arg1) + Reg[arg2].u; + 38 CF = (unsigned_result != unsigned_full_result); + 39 trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); 40 *signed_arg1 = signed_result; - 41 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *signed_arg1 << end(); + 41 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *signed_arg1 << end(); 42 break; 43 } 44 45 :(code) 46 void test_add_r32_to_r32_signed_overflow() { - 47 Reg[EAX].i = 0x7fffffff; // largest positive signed integer - 48 Reg[EBX].i = 1; - 49 run( + 47 Reg[EAX].i = 0x7fffffff; // largest positive signed integer + 48 Reg[EBX].i = 1; + 49 run( 50 "== code 0x1\n" // code segment 51 // op ModR/M SIB displacement immediate 52 " 01 d8 \n" // add EBX to EAX 53 // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX) 54 ); 55 CHECK_TRACE_CONTENTS( - 56 "run: add EBX to r/m32\n" + 56 "run: add EBX to r/m32\n" 57 "run: r/m32 is EAX\n" 58 "run: SF=1; ZF=0; CF=0; OF=1\n" 59 "run: storing 0x80000000\n" @@ -122,16 +122,16 @@ if ('onhashchange' in window) { 61 } 62 63 void test_add_r32_to_r32_unsigned_overflow() { - 64 Reg[EAX].u = 0xffffffff; // largest unsigned number - 65 Reg[EBX].u = 1; - 66 run( + 64 Reg[EAX].u = 0xffffffff; // largest unsigned number + 65 Reg[EBX].u = 1; + 66 run( 67 "== code 0x1\n" // code segment 68 // op ModR/M SIB displacement immediate 69 " 01 d8 \n" // add EBX to EAX 70 // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX) 71 ); 72 CHECK_TRACE_CONTENTS( - 73 "run: add EBX to r/m32\n" + 73 "run: add EBX to r/m32\n" 74 "run: r/m32 is EAX\n" 75 "run: SF=0; ZF=1; CF=1; OF=0\n" 76 "run: storing 0x00000000\n" @@ -139,15 +139,15 @@ if ('onhashchange' in window) { 78 } 79 80 void test_add_r32_to_r32_unsigned_and_signed_overflow() { - 81 Reg[EAX].u = Reg[EBX].u = 0x80000000; // smallest negative signed integer - 82 run( + 81 Reg[EAX].u = Reg[EBX].u = 0x80000000; // smallest negative signed integer + 82 run( 83 "== code 0x1\n" // code segment 84 // op ModR/M SIB displacement immediate 85 " 01 d8 \n" // add EBX to EAX 86 // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX) 87 ); 88 CHECK_TRACE_CONTENTS( - 89 "run: add EBX to r/m32\n" + 89 "run: add EBX to r/m32\n" 90 "run: r/m32 is EAX\n" 91 "run: SF=0; ZF=1; CF=1; OF=1\n" 92 "run: storing 0x00000000\n" @@ -166,11 +166,11 @@ if ('onhashchange' in window) { 105 if (mod == 3) { 106 // mod 3 is just register direct addressing 107 trace(Callstack_depth+1, "run") << "r/m32 is " << rname(rm) << end(); - 108 return &Reg[rm].i; + 108 return &Reg[rm].i; 109 } 110 uint32_t addr = effective_address_number(modrm); - 111 trace(Callstack_depth+1, "run") << "effective address contains " << read_mem_i32(addr) << end(); - 112 return mem_addr_i32(addr); + 111 trace(Callstack_depth+1, "run") << "effective address contains 0x" << HEXWORD << read_mem_i32(addr) << end(); + 112 return mem_addr_i32(addr); 113 } 114 115 // beware: will eventually have side-effects @@ -186,7 +186,7 @@ if ('onhashchange' in window) { 125 return 0; 126 // End Mod Special-cases(addr) 127 default: - 128 cerr << "unrecognized mod bits: " << NUM(mod) << '\n'; + 128 cerr << "unrecognized mod bits: " << NUM(mod) << '\n'; 129 exit(1); 130 } 131 //: other mods are indirect, and they'll set addr appropriately @@ -211,20 +211,20 @@ if ('onhashchange' in window) { 150 //:: subtract 151 152 :(before "End Initialize Op Names") - 153 put_new(Name, "29", "subtract r32 from rm32 (sub)"); + 153 put_new(Name, "29", "subtract r32 from rm32 (sub)"); 154 155 :(code) 156 void test_subtract_r32_from_r32() { - 157 Reg[EAX].i = 10; - 158 Reg[EBX].i = 1; - 159 run( + 157 Reg[EAX].i = 10; + 158 Reg[EBX].i = 1; + 159 run( 160 "== code 0x1\n" // code segment 161 // op ModR/M SIB displacement immediate 162 " 29 d8 \n" // subtract EBX from EAX 163 // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX) 164 ); 165 CHECK_TRACE_CONTENTS( - 166 "run: subtract EBX from r/m32\n" + 166 "run: subtract EBX from r/m32\n" 167 "run: r/m32 is EAX\n" 168 "run: storing 0x00000009\n" 169 ); @@ -232,38 +232,38 @@ if ('onhashchange' in window) { 171 172 :(before "End Single-Byte Opcodes") 173 case 0x29: { // subtract r32 from r/m32 - 174 const uint8_t modrm = next(); + 174 const uint8_t modrm = next(); 175 const uint8_t arg2 = (modrm>>3)&0x7; 176 trace(Callstack_depth+1, "run") << "subtract " << rname(arg2) << " from r/m32" << end(); 177 int32_t* signed_arg1 = effective_address(modrm); - 178 int32_t signed_result = *signed_arg1 - Reg[arg2].i; + 178 int32_t signed_result = *signed_arg1 - Reg[arg2].i; 179 SF = (signed_result < 0); - 180 ZF = (signed_result == 0); - 181 int64_t signed_full_result = static_cast<int64_t>(*signed_arg1) - Reg[arg2].i; - 182 OF = (signed_result != signed_full_result); + 180 ZF = (signed_result == 0); + 181 int64_t signed_full_result = static_cast<int64_t>(*signed_arg1) - Reg[arg2].i; + 182 OF = (signed_result != signed_full_result); 183 // set CF 184 uint32_t unsigned_arg1 = static_cast<uint32_t>(*signed_arg1); - 185 uint32_t unsigned_result = unsigned_arg1 - Reg[arg2].u; - 186 uint64_t unsigned_full_result = static_cast<uint64_t>(unsigned_arg1) - Reg[arg2].u; - 187 CF = (unsigned_result != unsigned_full_result); - 188 trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); + 185 uint32_t unsigned_result = unsigned_arg1 - Reg[arg2].u; + 186 uint64_t unsigned_full_result = static_cast<uint64_t>(unsigned_arg1) - Reg[arg2].u; + 187 CF = (unsigned_result != unsigned_full_result); + 188 trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); 189 *signed_arg1 = signed_result; - 190 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *signed_arg1 << end(); + 190 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *signed_arg1 << end(); 191 break; 192 } 193 194 :(code) 195 void test_subtract_r32_from_r32_signed_overflow() { - 196 Reg[EAX].i = 0x80000000; // smallest negative signed integer - 197 Reg[EBX].i = 0x7fffffff; // largest positive signed integer - 198 run( + 196 Reg[EAX].i = 0x80000000; // smallest negative signed integer + 197 Reg[EBX].i = 0x7fffffff; // largest positive signed integer + 198 run( 199 "== code 0x1\n" // code segment 200 // op ModR/M SIB displacement immediate 201 " 29 d8 \n" // subtract EBX from EAX 202 // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX) 203 ); 204 CHECK_TRACE_CONTENTS( - 205 "run: subtract EBX from r/m32\n" + 205 "run: subtract EBX from r/m32\n" 206 "run: r/m32 is EAX\n" 207 "run: SF=0; ZF=0; CF=0; OF=1\n" 208 "run: storing 0x00000001\n" @@ -271,16 +271,16 @@ if ('onhashchange' in window) { 210 } 211 212 void test_subtract_r32_from_r32_unsigned_overflow() { - 213 Reg[EAX].i = 0; - 214 Reg[EBX].i = 1; - 215 run( + 213 Reg[EAX].i = 0; + 214 Reg[EBX].i = 1; + 215 run( 216 "== code 0x1\n" // code segment 217 // op ModR/M SIB displacement immediate 218 " 29 d8 \n" // subtract EBX from EAX 219 // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX) 220 ); 221 CHECK_TRACE_CONTENTS( - 222 "run: subtract EBX from r/m32\n" + 222 "run: subtract EBX from r/m32\n" 223 "run: r/m32 is EAX\n" 224 "run: SF=1; ZF=0; CF=1; OF=0\n" 225 "run: storing 0xffffffff\n" @@ -288,16 +288,16 @@ if ('onhashchange' in window) { 227 } 228 229 void test_subtract_r32_from_r32_signed_and_unsigned_overflow() { - 230 Reg[EAX].i = 0; - 231 Reg[EBX].i = 0x80000000; // smallest negative signed integer - 232 run( + 230 Reg[EAX].i = 0; + 231 Reg[EBX].i = 0x80000000; // smallest negative signed integer + 232 run( 233 "== code 0x1\n" // code segment 234 // op ModR/M SIB displacement immediate 235 " 29 d8 \n" // subtract EBX from EAX 236 // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX) 237 ); 238 CHECK_TRACE_CONTENTS( - 239 "run: subtract EBX from r/m32\n" + 239 "run: subtract EBX from r/m32\n" 240 "run: r/m32 is EAX\n" 241 "run: SF=1; ZF=0; CF=1; OF=1\n" 242 "run: storing 0x80000000\n" @@ -307,13 +307,13 @@ if ('onhashchange' in window) { 246 //:: multiply 247 248 :(before "End Initialize Op Names") - 249 put_new(Name, "f7", "negate/multiply/divide rm32 (with EAX and EDX if necessary) depending on subop (neg/mul/idiv)"); + 249 put_new(Name, "f7", "negate/multiply/divide rm32 (with EAX and EDX if necessary) depending on subop (neg/mul/idiv)"); 250 251 :(code) 252 void test_multiply_EAX_by_r32() { - 253 Reg[EAX].i = 4; - 254 Reg[ECX].i = 3; - 255 run( + 253 Reg[EAX].i = 4; + 254 Reg[ECX].i = 3; + 255 run( 256 "== code 0x1\n" // code segment 257 // op ModR/M SIB displacement immediate 258 " f7 e1 \n" // multiply EAX by ECX @@ -322,32 +322,32 @@ if ('onhashchange' in window) { 261 CHECK_TRACE_CONTENTS( 262 "run: operate on r/m32\n" 263 "run: r/m32 is ECX\n" - 264 "run: subop: multiply EAX by r/m32\n" + 264 "run: subop: multiply EAX by r/m32\n" 265 "run: storing 0x0000000c\n" 266 ); 267 } 268 269 :(before "End Single-Byte Opcodes") 270 case 0xf7: { - 271 const uint8_t modrm = next(); + 271 const uint8_t modrm = next(); 272 trace(Callstack_depth+1, "run") << "operate on r/m32" << end(); 273 int32_t* arg1 = effective_address(modrm); 274 const uint8_t subop = (modrm>>3)&0x7; // middle 3 'reg opcode' bits 275 switch (subop) { 276 case 4: { // mul unsigned EAX by r/m32 - 277 trace(Callstack_depth+1, "run") << "subop: multiply EAX by r/m32" << end(); - 278 const uint64_t result = static_cast<uint64_t>(Reg[EAX].u) * static_cast<uint32_t>(*arg1); - 279 Reg[EAX].u = result & 0xffffffff; - 280 Reg[EDX].u = result >> 32; - 281 OF = (Reg[EDX].u != 0); - 282 CF = OF; - 283 trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); - 284 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << Reg[EAX].u << end(); + 277 trace(Callstack_depth+1, "run") << "subop: multiply EAX by r/m32" << end(); + 278 const uint64_t result = static_cast<uint64_t>(Reg[EAX].u) * static_cast<uint32_t>(*arg1); + 279 Reg[EAX].u = result & 0xffffffff; + 280 Reg[EDX].u = result >> 32; + 281 OF = (Reg[EDX].u != 0); + 282 CF = OF; + 283 trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); + 284 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << Reg[EAX].u << end(); 285 break; 286 } 287 // End Op f7 Subops 288 default: - 289 cerr << "unrecognized subop for opcode f7: " << NUM(subop) << '\n'; + 289 cerr << "unrecognized subop for opcode f7: " << NUM(subop) << '\n'; 290 exit(1); 291 } 292 break; @@ -356,20 +356,20 @@ if ('onhashchange' in window) { 295 //: 296 297 :(before "End Initialize Op Names") - 298 put_new(Name_0f, "af", "multiply rm32 into r32 (imul)"); + 298 put_new(Name_0f, "af", "multiply rm32 into r32 (imul)"); 299 300 :(code) 301 void test_multiply_r32_into_r32() { - 302 Reg[EAX].i = 4; - 303 Reg[EBX].i = 2; - 304 run( + 302 Reg[EAX].i = 4; + 303 Reg[EBX].i = 2; + 304 run( 305 "== code 0x1\n" // code segment 306 // op ModR/M SIB displacement immediate 307 " 0f af d8 \n" // subtract EBX into EAX 308 // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX) 309 ); 310 CHECK_TRACE_CONTENTS( - 311 "run: multiply EBX by r/m32\n" + 311 "run: multiply EBX by r/m32\n" 312 "run: r/m32 is EAX\n" 313 "run: storing 0x00000008\n" 314 ); @@ -377,19 +377,19 @@ if ('onhashchange' in window) { 316 317 :(before "End Two-Byte Opcodes Starting With 0f") 318 case 0xaf: { // multiply r32 by r/m32 - 319 const uint8_t modrm = next(); + 319 const uint8_t modrm = next(); 320 const uint8_t arg1 = (modrm>>3)&0x7; 321 trace(Callstack_depth+1, "run") << "multiply " << rname(arg1) << " by r/m32" << end(); 322 const int32_t* arg2 = effective_address(modrm); - 323 int32_t result = Reg[arg1].i * (*arg2); - 324 SF = (Reg[arg1].i < 0); - 325 ZF = (Reg[arg1].i == 0); - 326 int64_t full_result = static_cast<int64_t>(Reg[arg1].i) * (*arg2); - 327 OF = (Reg[arg1].i != full_result); - 328 CF = OF; - 329 trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); - 330 Reg[arg1].i = result; - 331 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << Reg[arg1].i << end(); + 323 int32_t result = Reg[arg1].i * (*arg2); + 324 SF = (Reg[arg1].i < 0); + 325 ZF = (Reg[arg1].i == 0); + 326 int64_t full_result = static_cast<int64_t>(Reg[arg1].i) * (*arg2); + 327 OF = (Reg[arg1].i != full_result); + 328 CF = OF; + 329 trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); + 330 Reg[arg1].i = result; + 331 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << Reg[arg1].i << end(); 332 break; 333 } 334 @@ -397,8 +397,8 @@ if ('onhashchange' in window) { 336 337 :(code) 338 void test_negate_r32() { - 339 Reg[EBX].i = 1; - 340 run( + 339 Reg[EBX].i = 1; + 340 run( 341 "== code 0x1\n" // code segment 342 // op ModR/M SIB displacement immediate 343 " f7 db \n" // negate EBX @@ -419,26 +419,26 @@ if ('onhashchange' in window) { 358 if (static_cast<uint32_t>(*arg1) == 0x80000000) { 359 trace(Callstack_depth+1, "run") << "overflow" << end(); 360 SF = true; - 361 ZF = false; - 362 OF = true; + 361 ZF = false; + 362 OF = true; 363 break; 364 } 365 int32_t result = -(*arg1); 366 SF = (result >> 31); - 367 ZF = (result == 0); - 368 OF = false; - 369 CF = (*arg1 != 0); - 370 trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); + 367 ZF = (result == 0); + 368 OF = false; + 369 CF = (*arg1 != 0); + 370 trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); 371 *arg1 = result; - 372 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *arg1 << end(); + 372 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *arg1 << end(); 373 break; 374 } 375 376 :(code) 377 // negate can overflow in exactly one situation 378 void test_negate_can_overflow() { - 379 Reg[EBX].i = 0x80000000; // INT_MIN - 380 run( + 379 Reg[EBX].i = 0x80000000; // INT_MIN + 380 run( 381 "== code 0x1\n" // code segment 382 // op ModR/M SIB displacement immediate 383 " f7 db \n" // negate EBX @@ -455,10 +455,10 @@ if ('onhashchange' in window) { 394 //:: divide with remainder 395 396 void test_divide_EAX_by_rm32() { - 397 Reg[EAX].u = 7; - 398 Reg[EDX].u = 0; - 399 Reg[ECX].i = 3; - 400 run( + 397 Reg[EAX].u = 7; + 398 Reg[EDX].u = 0; + 399 Reg[ECX].i = 3; + 400 run( 401 "== code 0x1\n" // code segment 402 // op ModR/M SIB displacement immediate 403 " f7 f9 \n" // multiply EAX by ECX @@ -467,7 +467,7 @@ if ('onhashchange' in window) { 406 CHECK_TRACE_CONTENTS( 407 "run: operate on r/m32\n" 408 "run: r/m32 is ECX\n" - 409 "run: subop: divide EDX:EAX by r/m32, storing quotient in EAX and remainder in EDX\n" + 409 "run: subop: divide EDX:EAX by r/m32, storing quotient in EAX and remainder in EDX\n" 410 "run: quotient: 0x00000002\n" 411 "run: remainder: 0x00000001\n" 412 ); @@ -475,24 +475,24 @@ if ('onhashchange' in window) { 414 415 :(before "End Op f7 Subops") 416 case 7: { // divide EDX:EAX by r/m32, storing quotient in EAX and remainder in EDX - 417 trace(Callstack_depth+1, "run") << "subop: divide EDX:EAX by r/m32, storing quotient in EAX and remainder in EDX" << end(); - 418 int64_t dividend = static_cast<int64_t>((static_cast<uint64_t>(Reg[EDX].u) << 32) | Reg[EAX].u); + 417 trace(Callstack_depth+1, "run") << "subop: divide EDX:EAX by r/m32, storing quotient in EAX and remainder in EDX" << end(); + 418 int64_t dividend = static_cast<int64_t>((static_cast<uint64_t>(Reg[EDX].u) << 32) | Reg[EAX].u); 419 int32_t divisor = *arg1; 420 assert(divisor != 0); - 421 Reg[EAX].i = dividend/divisor; // quotient - 422 Reg[EDX].i = dividend%divisor; // remainder + 421 Reg[EAX].i = dividend/divisor; // quotient + 422 Reg[EDX].i = dividend%divisor; // remainder 423 // flag state undefined - 424 trace(Callstack_depth+1, "run") << "quotient: 0x" << HEXWORD << Reg[EAX].i << end(); - 425 trace(Callstack_depth+1, "run") << "remainder: 0x" << HEXWORD << Reg[EDX].i << end(); + 424 trace(Callstack_depth+1, "run") << "quotient: 0x" << HEXWORD << Reg[EAX].i << end(); + 425 trace(Callstack_depth+1, "run") << "remainder: 0x" << HEXWORD << Reg[EDX].i << end(); 426 break; 427 } 428 429 :(code) 430 void test_divide_EAX_by_negative_rm32() { - 431 Reg[EAX].u = 7; - 432 Reg[EDX].u = 0; - 433 Reg[ECX].i = -3; - 434 run( + 431 Reg[EAX].u = 7; + 432 Reg[EDX].u = 0; + 433 Reg[ECX].i = -3; + 434 run( 435 "== code 0x1\n" // code segment 436 // op ModR/M SIB displacement immediate 437 " f7 f9 \n" // multiply EAX by ECX @@ -501,17 +501,17 @@ if ('onhashchange' in window) { 440 CHECK_TRACE_CONTENTS( 441 "run: operate on r/m32\n" 442 "run: r/m32 is ECX\n" - 443 "run: subop: divide EDX:EAX by r/m32, storing quotient in EAX and remainder in EDX\n" + 443 "run: subop: divide EDX:EAX by r/m32, storing quotient in EAX and remainder in EDX\n" 444 "run: quotient: 0xfffffffe\n" // -2 445 "run: remainder: 0x00000001\n" 446 ); 447 } 448 449 void test_divide_negative_EAX_by_rm32() { - 450 Reg[EAX].i = -7; - 451 Reg[EDX].i = -1; // sign extend - 452 Reg[ECX].i = 3; - 453 run( + 450 Reg[EAX].i = -7; + 451 Reg[EDX].i = -1; // sign extend + 452 Reg[ECX].i = 3; + 453 run( 454 "== code 0x1\n" // code segment 455 // op ModR/M SIB displacement immediate 456 " f7 f9 \n" // multiply EAX by ECX @@ -520,17 +520,17 @@ if ('onhashchange' in window) { 459 CHECK_TRACE_CONTENTS( 460 "run: operate on r/m32\n" 461 "run: r/m32 is ECX\n" - 462 "run: subop: divide EDX:EAX by r/m32, storing quotient in EAX and remainder in EDX\n" + 462 "run: subop: divide EDX:EAX by r/m32, storing quotient in EAX and remainder in EDX\n" 463 "run: quotient: 0xfffffffe\n" // -2 464 "run: remainder: 0xffffffff\n" // -1, same sign as divident (EDX:EAX) 465 ); 466 } 467 468 void test_divide_negative_EDX_EAX_by_rm32() { - 469 Reg[EAX].i = 0; // lower 32 bits are clear - 470 Reg[EDX].i = -7; - 471 Reg[ECX].i = 0x40000000; // 2^30 (largest positive power of 2) - 472 run( + 469 Reg[EAX].i = 0; // lower 32 bits are clear + 470 Reg[EDX].i = -7; + 471 Reg[ECX].i = 0x40000000; // 2^30 (largest positive power of 2) + 472 run( 473 "== code 0x1\n" // code segment 474 // op ModR/M SIB displacement immediate 475 " f7 f9 \n" // multiply EAX by ECX @@ -539,7 +539,7 @@ if ('onhashchange' in window) { 478 CHECK_TRACE_CONTENTS( 479 "run: operate on r/m32\n" 480 "run: r/m32 is ECX\n" - 481 "run: subop: divide EDX:EAX by r/m32, storing quotient in EAX and remainder in EDX\n" + 481 "run: subop: divide EDX:EAX by r/m32, storing quotient in EAX and remainder in EDX\n" 482 "run: quotient: 0xffffffe4\n" // (-7 << 32) / (1 << 30) = -7 << 2 = -28 483 "run: remainder: 0x00000000\n" 484 ); @@ -548,13 +548,13 @@ if ('onhashchange' in window) { 487 //:: shift left 488 489 :(before "End Initialize Op Names") - 490 put_new(Name, "d3", "shift rm32 by CL bits depending on subop (sal/sar/shl/shr)"); + 490 put_new(Name, "d3", "shift rm32 by CL bits depending on subop (sal/sar/shl/shr)"); 491 492 :(code) 493 void test_shift_left_r32_with_cl() { - 494 Reg[EBX].i = 13; - 495 Reg[ECX].i = 1; - 496 run( + 494 Reg[EBX].i = 13; + 495 Reg[ECX].i = 1; + 496 run( 497 "== code 0x1\n" // code segment 498 // op ModR/M SIB displacement immediate 499 " d3 e3 \n" // shift EBX left by CL bits @@ -570,32 +570,32 @@ if ('onhashchange' in window) { 509 510 :(before "End Single-Byte Opcodes") 511 case 0xd3: { - 512 const uint8_t modrm = next(); + 512 const uint8_t modrm = next(); 513 trace(Callstack_depth+1, "run") << "operate on r/m32" << end(); 514 int32_t* arg1 = effective_address(modrm); 515 const uint8_t subop = (modrm>>3)&0x7; // middle 3 'reg opcode' bits 516 switch (subop) { 517 case 4: { // shift left r/m32 by CL 518 trace(Callstack_depth+1, "run") << "subop: shift left by CL bits" << end(); - 519 uint8_t count = Reg[ECX].u & 0x1f; + 519 uint8_t count = Reg[ECX].u & 0x1f; 520 // OF is only defined if count is 1 521 if (count == 1) { 522 bool msb = (*arg1 & 0x80000000) >> 1; 523 bool pnsb = (*arg1 & 0x40000000); - 524 OF = (msb != pnsb); + 524 OF = (msb != pnsb); 525 } 526 int32_t result = (*arg1 << count); - 527 ZF = (result == 0); + 527 ZF = (result == 0); 528 SF = (result < 0); - 529 CF = (*arg1 << (count-1)) & 0x80000000; - 530 trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); + 529 CF = (*arg1 << (count-1)) & 0x80000000; + 530 trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); 531 *arg1 = result; - 532 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *arg1 << end(); + 532 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *arg1 << end(); 533 break; 534 } 535 // End Op d3 Subops 536 default: - 537 cerr << "unrecognized subop for opcode d3: " << NUM(subop) << '\n'; + 537 cerr << "unrecognized subop for opcode d3: " << NUM(subop) << '\n'; 538 exit(1); 539 } 540 break; @@ -605,9 +605,9 @@ if ('onhashchange' in window) { 544 545 :(code) 546 void test_shift_right_arithmetic_r32_with_cl() { - 547 Reg[EBX].i = 26; - 548 Reg[ECX].i = 1; - 549 run( + 547 Reg[EBX].i = 26; + 548 Reg[ECX].i = 1; + 549 run( 550 "== code 0x1\n" // code segment 551 // op ModR/M SIB displacement immediate 552 " d3 fb \n" // shift EBX right by CL bits, while preserving sign @@ -624,22 +624,22 @@ if ('onhashchange' in window) { 563 :(before "End Op d3 Subops") 564 case 7: { // shift right r/m32 by CL, preserving sign 565 trace(Callstack_depth+1, "run") << "subop: shift right by CL bits, while preserving sign" << end(); - 566 uint8_t count = Reg[ECX].u & 0x1f; + 566 uint8_t count = Reg[ECX].u & 0x1f; 567 *arg1 = (*arg1 >> count); - 568 ZF = (*arg1 == 0); + 568 ZF = (*arg1 == 0); 569 SF = (*arg1 < 0); 570 // OF is only defined if count is 1 - 571 if (count == 1) OF = false; + 571 if (count == 1) OF = false; 572 // CF undefined - 573 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *arg1 << end(); + 573 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *arg1 << end(); 574 break; 575 } 576 577 :(code) 578 void test_shift_right_arithmetic_odd_r32_with_cl() { - 579 Reg[EBX].i = 27; - 580 Reg[ECX].i = 1; - 581 run( + 579 Reg[EBX].i = 27; + 580 Reg[ECX].i = 1; + 581 run( 582 "== code 0x1\n" // code segment 583 // op ModR/M SIB displacement immediate 584 " d3 fb \n" // shift EBX right by CL bits, while preserving sign @@ -655,9 +655,9 @@ if ('onhashchange' in window) { 594 } 595 596 void test_shift_right_arithmetic_negative_r32_with_cl() { - 597 Reg[EBX].i = 0xfffffffd; // -3 - 598 Reg[ECX].i = 1; - 599 run( + 597 Reg[EBX].i = 0xfffffffd; // -3 + 598 Reg[ECX].i = 1; + 599 run( 600 "== code 0x1\n" // code segment 601 // op ModR/M SIB displacement immediate 602 " d3 fb \n" // shift EBX right by CL bits, while preserving sign @@ -676,9 +676,9 @@ if ('onhashchange' in window) { 615 616 :(code) 617 void test_shift_right_logical_r32_with_cl() { - 618 Reg[EBX].i = 26; - 619 Reg[ECX].i = 1; - 620 run( + 618 Reg[EBX].i = 26; + 619 Reg[ECX].i = 1; + 620 run( 621 "== code 0x1\n" // code segment 622 // op ModR/M SIB displacement immediate 623 " d3 eb \n" // shift EBX right by CL bits, while padding zeroes @@ -696,28 +696,28 @@ if ('onhashchange' in window) { 635 :(before "End Op d3 Subops") 636 case 5: { // shift right r/m32 by CL, padding zeroes 637 trace(Callstack_depth+1, "run") << "subop: shift right by CL bits, while padding zeroes" << end(); - 638 uint8_t count = Reg[ECX].u & 0x1f; + 638 uint8_t count = Reg[ECX].u & 0x1f; 639 // OF is only defined if count is 1 640 if (count == 1) { 641 bool msb = (*arg1 & 0x80000000) >> 1; 642 bool pnsb = (*arg1 & 0x40000000); - 643 OF = (msb != pnsb); + 643 OF = (msb != pnsb); 644 } 645 uint32_t* uarg1 = reinterpret_cast<uint32_t*>(arg1); 646 *uarg1 = (*uarg1 >> count); - 647 ZF = (*uarg1 == 0); + 647 ZF = (*uarg1 == 0); 648 // result is always positive by definition 649 SF = false; 650 // CF undefined - 651 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *arg1 << end(); + 651 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *arg1 << end(); 652 break; 653 } 654 655 :(code) 656 void test_shift_right_logical_odd_r32_with_cl() { - 657 Reg[EBX].i = 27; - 658 Reg[ECX].i = 1; - 659 run( + 657 Reg[EBX].i = 27; + 658 Reg[ECX].i = 1; + 659 run( 660 "== code 0x1\n" // code segment 661 // op ModR/M SIB displacement immediate 662 " d3 eb \n" // shift EBX right by CL bits, while padding zeroes @@ -733,9 +733,9 @@ if ('onhashchange' in window) { 672 } 673 674 void test_shift_right_logical_negative_r32_with_cl() { - 675 Reg[EBX].i = 0xfffffffd; - 676 Reg[ECX].i = 1; - 677 run( + 675 Reg[EBX].i = 0xfffffffd; + 676 Reg[ECX].i = 1; + 677 run( 678 "== code 0x1\n" // code segment 679 // op ModR/M SIB displacement immediate 680 " d3 eb \n" // shift EBX right by CL bits, while padding zeroes @@ -752,20 +752,20 @@ if ('onhashchange' in window) { 691 //:: and 692 693 :(before "End Initialize Op Names") - 694 put_new(Name, "21", "rm32 = bitwise AND of r32 with rm32 (and)"); + 694 put_new(Name, "21", "rm32 = bitwise AND of r32 with rm32 (and)"); 695 696 :(code) 697 void test_and_r32_with_r32() { - 698 Reg[EAX].i = 0x0a0b0c0d; - 699 Reg[EBX].i = 0x000000ff; - 700 run( + 698 Reg[EAX].i = 0x0a0b0c0d; + 699 Reg[EBX].i = 0x000000ff; + 700 run( 701 "== code 0x1\n" // code segment 702 // op ModR/M SIB displacement immediate 703 " 21 d8 \n" // and EBX with destination EAX 704 // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX) 705 ); 706 CHECK_TRACE_CONTENTS( - 707 "run: and EBX with r/m32\n" + 707 "run: and EBX with r/m32\n" 708 "run: r/m32 is EAX\n" 709 "run: storing 0x0000000d\n" 710 ); @@ -773,39 +773,39 @@ if ('onhashchange' in window) { 712 713 :(before "End Single-Byte Opcodes") 714 case 0x21: { // and r32 with r/m32 - 715 const uint8_t modrm = next(); + 715 const uint8_t modrm = next(); 716 const uint8_t arg2 = (modrm>>3)&0x7; 717 trace(Callstack_depth+1, "run") << "and " << rname(arg2) << " with r/m32" << end(); 718 // bitwise ops technically operate on unsigned numbers, but it makes no 719 // difference 720 int32_t* signed_arg1 = effective_address(modrm); - 721 *signed_arg1 &= Reg[arg2].i; - 722 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *signed_arg1 << end(); + 721 *signed_arg1 &= Reg[arg2].i; + 722 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *signed_arg1 << end(); 723 SF = (*signed_arg1 >> 31); - 724 ZF = (*signed_arg1 == 0); - 725 CF = false; - 726 OF = false; - 727 trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); + 724 ZF = (*signed_arg1 == 0); + 725 CF = false; + 726 OF = false; + 727 trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); 728 break; 729 } 730 731 //:: or 732 733 :(before "End Initialize Op Names") - 734 put_new(Name, "09", "rm32 = bitwise OR of r32 with rm32 (or)"); + 734 put_new(Name, "09", "rm32 = bitwise OR of r32 with rm32 (or)"); 735 736 :(code) 737 void test_or_r32_with_r32() { - 738 Reg[EAX].i = 0x0a0b0c0d; - 739 Reg[EBX].i = 0xa0b0c0d0; - 740 run( + 738 Reg[EAX].i = 0x0a0b0c0d; + 739 Reg[EBX].i = 0xa0b0c0d0; + 740 run( 741 "== code 0x1\n" // code segment 742 // op ModR/M SIB displacement immediate 743 " 09 d8 \n" // or EBX with destination EAX 744 // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX) 745 ); 746 CHECK_TRACE_CONTENTS( - 747 "run: or EBX with r/m32\n" + 747 "run: or EBX with r/m32\n" 748 "run: r/m32 is EAX\n" 749 "run: storing 0xaabbccdd\n" 750 ); @@ -813,39 +813,39 @@ if ('onhashchange' in window) { 752 753 :(before "End Single-Byte Opcodes") 754 case 0x09: { // or r32 with r/m32 - 755 const uint8_t modrm = next(); + 755 const uint8_t modrm = next(); 756 const uint8_t arg2 = (modrm>>3)&0x7; 757 trace(Callstack_depth+1, "run") << "or " << rname(arg2) << " with r/m32" << end(); 758 // bitwise ops technically operate on unsigned numbers, but it makes no 759 // difference 760 int32_t* signed_arg1 = effective_address(modrm); - 761 *signed_arg1 |= Reg[arg2].i; - 762 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *signed_arg1 << end(); + 761 *signed_arg1 |= Reg[arg2].i; + 762 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *signed_arg1 << end(); 763 SF = (*signed_arg1 >> 31); - 764 ZF = (*signed_arg1 == 0); - 765 CF = false; - 766 OF = false; - 767 trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); + 764 ZF = (*signed_arg1 == 0); + 765 CF = false; + 766 OF = false; + 767 trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); 768 break; 769 } 770 771 //:: xor 772 773 :(before "End Initialize Op Names") - 774 put_new(Name, "31", "rm32 = bitwise XOR of r32 with rm32 (xor)"); + 774 put_new(Name, "31", "rm32 = bitwise XOR of r32 with rm32 (xor)"); 775 776 :(code) 777 void test_xor_r32_with_r32() { - 778 Reg[EAX].i = 0x0a0b0c0d; - 779 Reg[EBX].i = 0xaabbc0d0; - 780 run( + 778 Reg[EAX].i = 0x0a0b0c0d; + 779 Reg[EBX].i = 0xaabbc0d0; + 780 run( 781 "== code 0x1\n" // code segment 782 // op ModR/M SIB displacement immediate 783 " 31 d8 \n" // xor EBX with destination EAX 784 // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX) 785 ); 786 CHECK_TRACE_CONTENTS( - 787 "run: xor EBX with r/m32\n" + 787 "run: xor EBX with r/m32\n" 788 "run: r/m32 is EAX\n" 789 "run: storing 0xa0b0ccdd\n" 790 ); @@ -853,19 +853,19 @@ if ('onhashchange' in window) { 792 793 :(before "End Single-Byte Opcodes") 794 case 0x31: { // xor r32 with r/m32 - 795 const uint8_t modrm = next(); + 795 const uint8_t modrm = next(); 796 const uint8_t arg2 = (modrm>>3)&0x7; 797 trace(Callstack_depth+1, "run") << "xor " << rname(arg2) << " with r/m32" << end(); 798 // bitwise ops technically operate on unsigned numbers, but it makes no 799 // difference 800 int32_t* signed_arg1 = effective_address(modrm); - 801 *signed_arg1 ^= Reg[arg2].i; - 802 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *signed_arg1 << end(); + 801 *signed_arg1 ^= Reg[arg2].i; + 802 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *signed_arg1 << end(); 803 SF = (*signed_arg1 >> 31); - 804 ZF = (*signed_arg1 == 0); - 805 CF = false; - 806 OF = false; - 807 trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); + 804 ZF = (*signed_arg1 == 0); + 805 CF = false; + 806 OF = false; + 807 trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); 808 break; 809 } 810 @@ -873,8 +873,8 @@ if ('onhashchange' in window) { 812 813 :(code) 814 void test_not_r32() { - 815 Reg[EBX].i = 0x0f0f00ff; - 816 run( + 815 Reg[EBX].i = 0x0f0f00ff; + 816 run( 817 "== code 0x1\n" // code segment 818 // op ModR/M SIB displacement immediate 819 " f7 d3 \n" // not EBX @@ -892,7 +892,7 @@ if ('onhashchange' in window) { 831 case 2: { // not r/m32 832 trace(Callstack_depth+1, "run") << "subop: not" << end(); 833 *arg1 = ~(*arg1); - 834 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *arg1 << end(); + 834 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *arg1 << end(); 835 // no flags affected 836 break; 837 } @@ -900,13 +900,13 @@ if ('onhashchange' in window) { 839 //:: compare (cmp) 840 841 :(before "End Initialize Op Names") - 842 put_new(Name, "39", "compare: set SF if rm32 < r32 (cmp)"); + 842 put_new(Name, "39", "compare: set SF if rm32 < r32 (cmp)"); 843 844 :(code) 845 void test_compare_r32_with_r32_greater() { - 846 Reg[EAX].i = 0x0a0b0c0d; - 847 Reg[EBX].i = 0x0a0b0c07; - 848 run( + 846 Reg[EAX].i = 0x0a0b0c0d; + 847 Reg[EBX].i = 0x0a0b0c07; + 848 run( 849 "== code 0x1\n" // code segment 850 // op ModR/M SIB displacement immediate 851 " 39 d8 \n" // compare EAX with EBX @@ -921,29 +921,29 @@ if ('onhashchange' in window) { 860 861 :(before "End Single-Byte Opcodes") 862 case 0x39: { // set SF if r/m32 < r32 - 863 const uint8_t modrm = next(); + 863 const uint8_t modrm = next(); 864 const uint8_t reg2 = (modrm>>3)&0x7; 865 trace(Callstack_depth+1, "run") << "compare r/m32 with " << rname(reg2) << end(); 866 const int32_t* signed_arg1 = effective_address(modrm); - 867 const int32_t signed_difference = *signed_arg1 - Reg[reg2].i; + 867 const int32_t signed_difference = *signed_arg1 - Reg[reg2].i; 868 SF = (signed_difference < 0); - 869 ZF = (signed_difference == 0); - 870 const int64_t signed_full_difference = static_cast<int64_t>(*signed_arg1) - Reg[reg2].i; - 871 OF = (signed_difference != signed_full_difference); + 869 ZF = (signed_difference == 0); + 870 const int64_t signed_full_difference = static_cast<int64_t>(*signed_arg1) - Reg[reg2].i; + 871 OF = (signed_difference != signed_full_difference); 872 // set CF 873 const uint32_t unsigned_arg1 = static_cast<uint32_t>(*signed_arg1); - 874 const uint32_t unsigned_difference = unsigned_arg1 - Reg[reg2].u; - 875 const uint64_t unsigned_full_difference = static_cast<uint64_t>(unsigned_arg1) - Reg[reg2].u; - 876 CF = (unsigned_difference != unsigned_full_difference); - 877 trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); + 874 const uint32_t unsigned_difference = unsigned_arg1 - Reg[reg2].u; + 875 const uint64_t unsigned_full_difference = static_cast<uint64_t>(unsigned_arg1) - Reg[reg2].u; + 876 CF = (unsigned_difference != unsigned_full_difference); + 877 trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); 878 break; 879 } 880 881 :(code) 882 void test_compare_r32_with_r32_lesser_unsigned_and_signed() { - 883 Reg[EAX].i = 0x0a0b0c07; - 884 Reg[EBX].i = 0x0a0b0c0d; - 885 run( + 883 Reg[EAX].i = 0x0a0b0c07; + 884 Reg[EBX].i = 0x0a0b0c0d; + 885 run( 886 "== code 0x1\n" // code segment 887 // op ModR/M SIB displacement immediate 888 " 39 d8 \n" // compare EAX with EBX @@ -957,9 +957,9 @@ if ('onhashchange' in window) { 896 } 897 898 void test_compare_r32_with_r32_lesser_unsigned_and_signed_due_to_overflow() { - 899 Reg[EAX].i = 0x7fffffff; // largest positive signed integer - 900 Reg[EBX].i = 0x80000000; // smallest negative signed integer - 901 run( + 899 Reg[EAX].i = 0x7fffffff; // largest positive signed integer + 900 Reg[EBX].i = 0x80000000; // smallest negative signed integer + 901 run( 902 "== code 0x1\n" // code segment 903 // op ModR/M SIB displacement immediate 904 " 39 d8 \n" // compare EAX with EBX @@ -973,9 +973,9 @@ if ('onhashchange' in window) { 912 } 913 914 void test_compare_r32_with_r32_lesser_signed() { - 915 Reg[EAX].i = 0xffffffff; // -1 - 916 Reg[EBX].i = 0x00000001; // 1 - 917 run( + 915 Reg[EAX].i = 0xffffffff; // -1 + 916 Reg[EBX].i = 0x00000001; // 1 + 917 run( 918 "== code 0x1\n" // code segment 919 // op ModR/M SIB displacement immediate 920 " 39 d8 \n" // compare EAX with EBX @@ -989,9 +989,9 @@ if ('onhashchange' in window) { 928 } 929 930 void test_compare_r32_with_r32_lesser_unsigned() { - 931 Reg[EAX].i = 0x00000001; // 1 - 932 Reg[EBX].i = 0xffffffff; // -1 - 933 run( + 931 Reg[EAX].i = 0x00000001; // 1 + 932 Reg[EBX].i = 0xffffffff; // -1 + 933 run( 934 "== code 0x1\n" // code segment 935 // op ModR/M SIB displacement immediate 936 " 39 d8 \n" // compare EAX with EBX @@ -1005,9 +1005,9 @@ if ('onhashchange' in window) { 944 } 945 946 void test_compare_r32_with_r32_equal() { - 947 Reg[EAX].i = 0x0a0b0c0d; - 948 Reg[EBX].i = 0x0a0b0c0d; - 949 run( + 947 Reg[EAX].i = 0x0a0b0c0d; + 948 Reg[EBX].i = 0x0a0b0c0d; + 949 run( 950 "== code 0x1\n" // code segment 951 // op ModR/M SIB displacement immediate 952 " 39 d8 \n" // compare EAX and EBX @@ -1023,19 +1023,19 @@ if ('onhashchange' in window) { 962 //:: copy (mov) 963 964 :(before "End Initialize Op Names") - 965 put_new(Name, "89", "copy r32 to rm32 (mov)"); + 965 put_new(Name, "89", "copy r32 to rm32 (mov)"); 966 967 :(code) 968 void test_copy_r32_to_r32() { - 969 Reg[EBX].i = 0xaf; - 970 run( + 969 Reg[EBX].i = 0xaf; + 970 run( 971 "== code 0x1\n" // code segment 972 // op ModR/M SIB displacement immediate 973 " 89 d8 \n" // copy EBX to EAX 974 // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX) 975 ); 976 CHECK_TRACE_CONTENTS( - 977 "run: copy EBX to r/m32\n" + 977 "run: copy EBX to r/m32\n" 978 "run: r/m32 is EAX\n" 979 "run: storing 0x000000af\n" 980 ); @@ -1043,32 +1043,32 @@ if ('onhashchange' in window) { 982 983 :(before "End Single-Byte Opcodes") 984 case 0x89: { // copy r32 to r/m32 - 985 const uint8_t modrm = next(); + 985 const uint8_t modrm = next(); 986 const uint8_t rsrc = (modrm>>3)&0x7; 987 trace(Callstack_depth+1, "run") << "copy " << rname(rsrc) << " to r/m32" << end(); 988 int32_t* dest = effective_address(modrm); - 989 *dest = Reg[rsrc].i; - 990 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *dest << end(); + 989 *dest = Reg[rsrc].i; + 990 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *dest << end(); 991 break; 992 } 993 994 //:: xchg 995 996 :(before "End Initialize Op Names") - 997 put_new(Name, "87", "swap the contents of r32 and rm32 (xchg)"); + 997 put_new(Name, "87", "swap the contents of r32 and rm32 (xchg)"); 998 999 :(code) 1000 void test_xchg_r32_with_r32() { -1001 Reg[EBX].i = 0xaf; -1002 Reg[EAX].i = 0x2e; -1003 run( +1001 Reg[EBX].i = 0xaf; +1002 Reg[EAX].i = 0x2e; +1003 run( 1004 "== code 0x1\n" // code segment 1005 // op ModR/M SIB displacement immediate 1006 " 87 d8 \n" // exchange EBX with EAX 1007 // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX) 1008 ); 1009 CHECK_TRACE_CONTENTS( -1010 "run: exchange EBX with r/m32\n" +1010 "run: exchange EBX with r/m32\n" 1011 "run: r/m32 is EAX\n" 1012 "run: storing 0x000000af in r/m32\n" 1013 "run: storing 0x0000002e in EBX\n" @@ -1077,34 +1077,34 @@ if ('onhashchange' in window) { 1016 1017 :(before "End Single-Byte Opcodes") 1018 case 0x87: { // exchange r32 with r/m32 -1019 const uint8_t modrm = next(); +1019 const uint8_t modrm = next(); 1020 const uint8_t reg2 = (modrm>>3)&0x7; 1021 trace(Callstack_depth+1, "run") << "exchange " << rname(reg2) << " with r/m32" << end(); 1022 int32_t* arg1 = effective_address(modrm); 1023 const int32_t tmp = *arg1; -1024 *arg1 = Reg[reg2].i; -1025 Reg[reg2].i = tmp; -1026 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *arg1 << " in r/m32" << end(); -1027 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << Reg[reg2].i << " in " << rname(reg2) << end(); +1024 *arg1 = Reg[reg2].i; +1025 Reg[reg2].i = tmp; +1026 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *arg1 << " in r/m32" << end(); +1027 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << Reg[reg2].i << " in " << rname(reg2) << end(); 1028 break; 1029 } 1030 1031 //:: increment 1032 1033 :(before "End Initialize Op Names") -1034 put_new(Name, "40", "increment EAX (inc)"); -1035 put_new(Name, "41", "increment ECX (inc)"); -1036 put_new(Name, "42", "increment EDX (inc)"); -1037 put_new(Name, "43", "increment EBX (inc)"); -1038 put_new(Name, "44", "increment ESP (inc)"); -1039 put_new(Name, "45", "increment EBP (inc)"); -1040 put_new(Name, "46", "increment ESI (inc)"); -1041 put_new(Name, "47", "increment EDI (inc)"); +1034 put_new(Name, "40", "increment EAX (inc)"); +1035 put_new(Name, "41", "increment ECX (inc)"); +1036 put_new(Name, "42", "increment EDX (inc)"); +1037 put_new(Name, "43", "increment EBX (inc)"); +1038 put_new(Name, "44", "increment ESP (inc)"); +1039 put_new(Name, "45", "increment EBP (inc)"); +1040 put_new(Name, "46", "increment ESI (inc)"); +1041 put_new(Name, "47", "increment EDI (inc)"); 1042 1043 :(code) 1044 void test_increment_r32() { -1045 Reg[ECX].u = 0x1f; -1046 run( +1045 Reg[ECX].u = 0x1f; +1046 run( 1047 "== code 0x1\n" // code segment 1048 // op ModR/M SIB displacement immediate 1049 " 41 \n" // increment ECX @@ -1124,20 +1124,20 @@ if ('onhashchange' in window) { 1063 case 0x45: 1064 case 0x46: 1065 case 0x47: { // increment r32 -1066 const uint8_t reg = op & 0x7; -1067 trace(Callstack_depth+1, "run") << "increment " << rname(reg) << end(); -1068 ++Reg[reg].u; -1069 trace(Callstack_depth+1, "run") << "storing value 0x" << HEXWORD << Reg[reg].u << end(); +1066 const uint8_t reg = op & 0x7; +1067 trace(Callstack_depth+1, "run") << "increment " << rname(reg) << end(); +1068 ++Reg[reg].u; +1069 trace(Callstack_depth+1, "run") << "storing value 0x" << HEXWORD << Reg[reg].u << end(); 1070 break; 1071 } 1072 1073 :(before "End Initialize Op Names") -1074 put_new(Name, "ff", "increment/decrement/jump/push/call rm32 based on subop (inc/dec/jmp/push/call)"); +1074 put_new(Name, "ff", "increment/decrement/jump/push/call rm32 based on subop (inc/dec/jmp/push/call)"); 1075 1076 :(code) 1077 void test_increment_rm32() { -1078 Reg[EAX].u = 0x20; -1079 run( +1078 Reg[EAX].u = 0x20; +1079 run( 1080 "== code 0x1\n" // code segment 1081 // op ModR/M SIB displacement immediate 1082 " ff c0 \n" // increment EAX @@ -1152,18 +1152,18 @@ if ('onhashchange' in window) { 1091 1092 :(before "End Single-Byte Opcodes") 1093 case 0xff: { -1094 const uint8_t modrm = next(); +1094 const uint8_t modrm = next(); 1095 const uint8_t subop = (modrm>>3)&0x7; // middle 3 'reg opcode' bits 1096 switch (subop) { 1097 case 0: { // increment r/m32 1098 trace(Callstack_depth+1, "run") << "increment r/m32" << end(); 1099 int32_t* arg = effective_address(modrm); 1100 ++*arg; -1101 trace(Callstack_depth+1, "run") << "storing value 0x" << HEXWORD << *arg << end(); +1101 trace(Callstack_depth+1, "run") << "storing value 0x" << HEXWORD << *arg << end(); 1102 break; 1103 } 1104 default: -1105 cerr << "unrecognized subop for ff: " << HEXBYTE << NUM(subop) << '\n'; +1105 cerr << "unrecognized subop for ff: " << HEXBYTE << NUM(subop) << '\n'; 1106 exit(1); 1107 // End Op ff Subops 1108 } @@ -1173,19 +1173,19 @@ if ('onhashchange' in window) { 1112 //:: decrement 1113 1114 :(before "End Initialize Op Names") -1115 put_new(Name, "48", "decrement EAX (dec)"); -1116 put_new(Name, "49", "decrement ECX (dec)"); -1117 put_new(Name, "4a", "decrement EDX (dec)"); -1118 put_new(Name, "4b", "decrement EBX (dec)"); -1119 put_new(Name, "4c", "decrement ESP (dec)"); -1120 put_new(Name, "4d", "decrement EBP (dec)"); -1121 put_new(Name, "4e", "decrement ESI (dec)"); -1122 put_new(Name, "4f", "decrement EDI (dec)"); +1115 put_new(Name, "48", "decrement EAX (dec)"); +1116 put_new(Name, "49", "decrement ECX (dec)"); +1117 put_new(Name, "4a", "decrement EDX (dec)"); +1118 put_new(Name, "4b", "decrement EBX (dec)"); +1119 put_new(Name, "4c", "decrement ESP (dec)"); +1120 put_new(Name, "4d", "decrement EBP (dec)"); +1121 put_new(Name, "4e", "decrement ESI (dec)"); +1122 put_new(Name, "4f", "decrement EDI (dec)"); 1123 1124 :(code) 1125 void test_decrement_r32() { -1126 Reg[ECX].u = 0x1f; -1127 run( +1126 Reg[ECX].u = 0x1f; +1127 run( 1128 "== code 0x1\n" // code segment 1129 // op ModR/M SIB displacement immediate 1130 " 49 \n" // decrement ECX @@ -1205,17 +1205,17 @@ if ('onhashchange' in window) { 1144 case 0x4d: 1145 case 0x4e: 1146 case 0x4f: { // decrement r32 -1147 const uint8_t reg = op & 0x7; -1148 trace(Callstack_depth+1, "run") << "decrement " << rname(reg) << end(); -1149 --Reg[reg].u; -1150 trace(Callstack_depth+1, "run") << "storing value 0x" << HEXWORD << Reg[reg].u << end(); +1147 const uint8_t reg = op & 0x7; +1148 trace(Callstack_depth+1, "run") << "decrement " << rname(reg) << end(); +1149 --Reg[reg].u; +1150 trace(Callstack_depth+1, "run") << "storing value 0x" << HEXWORD << Reg[reg].u << end(); 1151 break; 1152 } 1153 1154 :(code) 1155 void test_decrement_rm32() { -1156 Reg[EAX].u = 0x20; -1157 run( +1156 Reg[EAX].u = 0x20; +1157 run( 1158 "== code 0x1\n" // code segment 1159 // op ModR/M SIB displacement immediate 1160 " ff c8 \n" // decrement EAX @@ -1233,35 +1233,35 @@ if ('onhashchange' in window) { 1172 trace(Callstack_depth+1, "run") << "decrement r/m32" << end(); 1173 int32_t* arg = effective_address(modrm); 1174 --*arg; -1175 trace(Callstack_depth+1, "run") << "storing value 0x" << HEXWORD << *arg << end(); +1175 trace(Callstack_depth+1, "run") << "storing value 0x" << HEXWORD << *arg << end(); 1176 break; 1177 } 1178 1179 //:: push 1180 1181 :(before "End Initialize Op Names") -1182 put_new(Name, "50", "push EAX to stack (push)"); -1183 put_new(Name, "51", "push ECX to stack (push)"); -1184 put_new(Name, "52", "push EDX to stack (push)"); -1185 put_new(Name, "53", "push EBX to stack (push)"); -1186 put_new(Name, "54", "push ESP to stack (push)"); -1187 put_new(Name, "55", "push EBP to stack (push)"); -1188 put_new(Name, "56", "push ESI to stack (push)"); -1189 put_new(Name, "57", "push EDI to stack (push)"); +1182 put_new(Name, "50", "push EAX to stack (push)"); +1183 put_new(Name, "51", "push ECX to stack (push)"); +1184 put_new(Name, "52", "push EDX to stack (push)"); +1185 put_new(Name, "53", "push EBX to stack (push)"); +1186 put_new(Name, "54", "push ESP to stack (push)"); +1187 put_new(Name, "55", "push EBP to stack (push)"); +1188 put_new(Name, "56", "push ESI to stack (push)"); +1189 put_new(Name, "57", "push EDI to stack (push)"); 1190 1191 :(code) 1192 void test_push_r32() { -1193 Mem.push_back(vma(0xbd000000)); // manually allocate memory -1194 Reg[ESP].u = 0xbd000008; -1195 Reg[EBX].i = 0x0000000a; -1196 run( +1193 Mem.push_back(vma(0xbd000000)); // manually allocate memory +1194 Reg[ESP].u = 0xbd000008; +1195 Reg[EBX].i = 0x0000000a; +1196 run( 1197 "== code 0x1\n" // code segment 1198 // op ModR/M SIB displacement immediate 1199 " 53 \n" // push EBX to stack 1200 ); 1201 CHECK_TRACE_CONTENTS( 1202 "run: push EBX\n" -1203 "run: decrementing ESP to 0xbd000004\n" +1203 "run: decrementing ESP to 0xbd000004\n" 1204 "run: pushing value 0x0000000a\n" 1205 ); 1206 } @@ -1275,31 +1275,31 @@ if ('onhashchange' in window) { 1214 case 0x55: 1215 case 0x56: 1216 case 0x57: { // push r32 to stack -1217 uint8_t reg = op & 0x7; -1218 trace(Callstack_depth+1, "run") << "push " << rname(reg) << end(); +1217 uint8_t reg = op & 0x7; +1218 trace(Callstack_depth+1, "run") << "push " << rname(reg) << end(); 1219 //? cerr << "push: " << NUM(reg) << ": " << Reg[reg].u << " => " << Reg[ESP].u << '\n'; -1220 push(Reg[reg].u); +1220 push(Reg[reg].u); 1221 break; 1222 } 1223 1224 //:: pop 1225 1226 :(before "End Initialize Op Names") -1227 put_new(Name, "58", "pop top of stack to EAX (pop)"); -1228 put_new(Name, "59", "pop top of stack to ECX (pop)"); -1229 put_new(Name, "5a", "pop top of stack to EDX (pop)"); -1230 put_new(Name, "5b", "pop top of stack to EBX (pop)"); -1231 put_new(Name, "5c", "pop top of stack to ESP (pop)"); -1232 put_new(Name, "5d", "pop top of stack to EBP (pop)"); -1233 put_new(Name, "5e", "pop top of stack to ESI (pop)"); -1234 put_new(Name, "5f", "pop top of stack to EDI (pop)"); +1227 put_new(Name, "58", "pop top of stack to EAX (pop)"); +1228 put_new(Name, "59", "pop top of stack to ECX (pop)"); +1229 put_new(Name, "5a", "pop top of stack to EDX (pop)"); +1230 put_new(Name, "5b", "pop top of stack to EBX (pop)"); +1231 put_new(Name, "5c", "pop top of stack to ESP (pop)"); +1232 put_new(Name, "5d", "pop top of stack to EBP (pop)"); +1233 put_new(Name, "5e", "pop top of stack to ESI (pop)"); +1234 put_new(Name, "5f", "pop top of stack to EDI (pop)"); 1235 1236 :(code) 1237 void test_pop_r32() { -1238 Mem.push_back(vma(0xbd000000)); // manually allocate memory -1239 Reg[ESP].u = 0xbd000008; -1240 write_mem_i32(0xbd000008, 0x0000000a); // ..before this write -1241 run( +1238 Mem.push_back(vma(0xbd000000)); // manually allocate memory +1239 Reg[ESP].u = 0xbd000008; +1240 write_mem_i32(0xbd000008, 0x0000000a); // ..before this write +1241 run( 1242 "== code 0x1\n" // code segment 1243 // op ModR/M SIB displacement immediate 1244 " 5b \n" // pop stack to EBX @@ -1309,7 +1309,7 @@ if ('onhashchange' in window) { 1248 CHECK_TRACE_CONTENTS( 1249 "run: pop into EBX\n" 1250 "run: popping value 0x0000000a\n" -1251 "run: incrementing ESP to 0xbd00000c\n" +1251 "run: incrementing ESP to 0xbd00000c\n" 1252 ); 1253 } 1254 @@ -1322,20 +1322,20 @@ if ('onhashchange' in window) { 1261 case 0x5d: 1262 case 0x5e: 1263 case 0x5f: { // pop stack into r32 -1264 const uint8_t reg = op & 0x7; -1265 trace(Callstack_depth+1, "run") << "pop into " << rname(reg) << end(); +1264 const uint8_t reg = op & 0x7; +1265 trace(Callstack_depth+1, "run") << "pop into " << rname(reg) << end(); 1266 //? cerr << "pop from " << Reg[ESP].u << '\n'; -1267 Reg[reg].u = pop(); +1267 Reg[reg].u = pop(); 1268 //? cerr << "=> " << NUM(reg) << ": " << Reg[reg].u << '\n'; 1269 break; 1270 } 1271 :(code) 1272 uint32_t pop() { -1273 const uint32_t result = read_mem_u32(Reg[ESP].u); -1274 trace(Callstack_depth+1, "run") << "popping value 0x" << HEXWORD << result << end(); -1275 Reg[ESP].u += 4; -1276 trace(Callstack_depth+1, "run") << "incrementing ESP to 0x" << HEXWORD << Reg[ESP].u << end(); -1277 assert(Reg[ESP].u < AFTER_STACK); +1273 const uint32_t result = read_mem_u32(Reg[ESP].u); +1274 trace(Callstack_depth+1, "run") << "popping value 0x" << HEXWORD << result << end(); +1275 Reg[ESP].u += 4; +1276 trace(Callstack_depth+1, "run") << "incrementing ESP to 0x" << HEXWORD << Reg[ESP].u << end(); +1277 assert(Reg[ESP].u < AFTER_STACK); 1278 return result; 1279 } diff --git a/html/014indirect_addressing.cc.html b/html/014indirect_addressing.cc.html index a25191d9..e70b8317 100644 --- a/html/014indirect_addressing.cc.html +++ b/html/014indirect_addressing.cc.html @@ -61,9 +61,9 @@ if ('onhashchange' in window) { 2 //: we'll now start providing data in a separate segment 3 4 void test_add_r32_to_mem_at_r32() { - 5 Reg[EBX].i = 0x10; - 6 Reg[EAX].i = 0x2000; - 7 run( + 5 Reg[EBX].i = 0x10; + 6 Reg[EAX].i = 0x2000; + 7 run( 8 "== code 0x1\n" 9 // op ModR/M SIB displacement immediate 10 " 01 18 \n" // add EBX to *EAX @@ -72,8 +72,8 @@ if ('onhashchange' in window) { 13 "01 00 00 00\n" // 0x00000001 14 ); 15 CHECK_TRACE_CONTENTS( - 16 "run: add EBX to r/m32\n" - 17 "run: effective address is 0x00002000 (EAX)\n" + 16 "run: add EBX to r/m32\n" + 17 "run: effective address is 0x00002000 (EAX)\n" 18 "run: storing 0x00000011\n" 19 ); 20 } @@ -82,8 +82,8 @@ if ('onhashchange' in window) { 23 case 0: // indirect addressing 24 switch (rm) { 25 default: // address in register - 26 trace(Callstack_depth+1, "run") << "effective address is 0x" << HEXWORD << Reg[rm].u << " (" << rname(rm) << ")" << end(); - 27 addr = Reg[rm].u; + 26 trace(Callstack_depth+1, "run") << "effective address is 0x" << HEXWORD << Reg[rm].u << " (" << rname(rm) << ")" << end(); + 27 addr = Reg[rm].u; 28 break; 29 // End Mod 0 Special-cases(addr) 30 } @@ -92,13 +92,13 @@ if ('onhashchange' in window) { 33 //: 34 35 :(before "End Initialize Op Names") - 36 put_new(Name, "03", "add rm32 to r32 (add)"); + 36 put_new(Name, "03", "add rm32 to r32 (add)"); 37 38 :(code) 39 void test_add_mem_at_r32_to_r32() { - 40 Reg[EAX].i = 0x2000; - 41 Reg[EBX].i = 0x10; - 42 run( + 40 Reg[EAX].i = 0x2000; + 41 Reg[EBX].i = 0x10; + 42 run( 43 "== code 0x1\n" 44 // op ModR/M SIB displacement immediate 45 " 03 18 \n" // add *EAX to EBX @@ -108,38 +108,38 @@ if ('onhashchange' in window) { 49 ); 50 CHECK_TRACE_CONTENTS( 51 "run: add r/m32 to EBX\n" - 52 "run: effective address is 0x00002000 (EAX)\n" + 52 "run: effective address is 0x00002000 (EAX)\n" 53 "run: storing 0x00000011\n" 54 ); 55 } 56 57 :(before "End Single-Byte Opcodes") 58 case 0x03: { // add r/m32 to r32 - 59 const uint8_t modrm = next(); + 59 const uint8_t modrm = next(); 60 const uint8_t arg1 = (modrm>>3)&0x7; 61 trace(Callstack_depth+1, "run") << "add r/m32 to " << rname(arg1) << end(); 62 const int32_t* signed_arg2 = effective_address(modrm); - 63 int32_t signed_result = Reg[arg1].i + *signed_arg2; + 63 int32_t signed_result = Reg[arg1].i + *signed_arg2; 64 SF = (signed_result < 0); - 65 ZF = (signed_result == 0); - 66 int64_t signed_full_result = static_cast<int64_t>(Reg[arg1].i) + *signed_arg2; - 67 OF = (signed_result != signed_full_result); + 65 ZF = (signed_result == 0); + 66 int64_t signed_full_result = static_cast<int64_t>(Reg[arg1].i) + *signed_arg2; + 67 OF = (signed_result != signed_full_result); 68 // set CF 69 uint32_t unsigned_arg2 = static_cast<uint32_t>(*signed_arg2); - 70 uint32_t unsigned_result = Reg[arg1].u + unsigned_arg2; - 71 uint64_t unsigned_full_result = static_cast<uint64_t>(Reg[arg1].u) + unsigned_arg2; - 72 CF = (unsigned_result != unsigned_full_result); - 73 trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); - 74 Reg[arg1].i = signed_result; - 75 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << Reg[arg1].i << end(); + 70 uint32_t unsigned_result = Reg[arg1].u + unsigned_arg2; + 71 uint64_t unsigned_full_result = static_cast<uint64_t>(Reg[arg1].u) + unsigned_arg2; + 72 CF = (unsigned_result != unsigned_full_result); + 73 trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); + 74 Reg[arg1].i = signed_result; + 75 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << Reg[arg1].i << end(); 76 break; 77 } 78 79 :(code) 80 void test_add_mem_at_r32_to_r32_signed_overflow() { - 81 Reg[EAX].i = 0x2000; - 82 Reg[EBX].i = 0x7fffffff; // largest positive signed integer - 83 run( + 81 Reg[EAX].i = 0x2000; + 82 Reg[EBX].i = 0x7fffffff; // largest positive signed integer + 83 run( 84 "== code 0x1\n" 85 // op ModR/M SIB displacement immediate 86 " 03 18 \n" // add *EAX to EBX @@ -149,17 +149,17 @@ if ('onhashchange' in window) { 90 ); 91 CHECK_TRACE_CONTENTS( 92 "run: add r/m32 to EBX\n" - 93 "run: effective address is 0x00002000 (EAX)\n" - 94 "run: effective address contains 1\n" + 93 "run: effective address is 0x00002000 (EAX)\n" + 94 "run: effective address contains 0x00000001\n" 95 "run: SF=1; ZF=0; CF=0; OF=1\n" 96 "run: storing 0x80000000\n" 97 ); 98 } 99 100 void test_add_mem_at_r32_to_r32_unsigned_overflow() { - 101 Reg[EAX].u = 0x2000; - 102 Reg[EBX].u = 0xffffffff; // largest unsigned number - 103 run( + 101 Reg[EAX].u = 0x2000; + 102 Reg[EBX].u = 0xffffffff; // largest unsigned number + 103 run( 104 "== code 0x1\n" 105 // op ModR/M SIB displacement immediate 106 " 03 18 \n" // add *EAX to EBX @@ -169,17 +169,17 @@ if ('onhashchange' in window) { 110 ); 111 CHECK_TRACE_CONTENTS( 112 "run: add r/m32 to EBX\n" - 113 "run: effective address is 0x00002000 (EAX)\n" - 114 "run: effective address contains 1\n" + 113 "run: effective address is 0x00002000 (EAX)\n" + 114 "run: effective address contains 0x00000001\n" 115 "run: SF=0; ZF=1; CF=1; OF=0\n" 116 "run: storing 0x00000000\n" 117 ); 118 } 119 120 void test_add_mem_at_r32_to_r32_unsigned_and_signed_overflow() { - 121 Reg[EAX].u = 0x2000; - 122 Reg[EBX].u = 0x80000000; // smallest negative signed integer - 123 run( + 121 Reg[EAX].u = 0x2000; + 122 Reg[EBX].u = 0x80000000; // smallest negative signed integer + 123 run( 124 "== code 0x1\n" 125 // op ModR/M SIB displacement immediate 126 " 03 18 \n" // add *EAX to EBX @@ -189,8 +189,8 @@ if ('onhashchange' in window) { 130 ); 131 CHECK_TRACE_CONTENTS( 132 "run: add r/m32 to EBX\n" - 133 "run: effective address is 0x00002000 (EAX)\n" - 134 "run: effective address contains 80000000\n" + 133 "run: effective address is 0x00002000 (EAX)\n" + 134 "run: effective address contains 0x80000000\n" 135 "run: SF=0; ZF=1; CF=1; OF=1\n" 136 "run: storing 0x00000000\n" 137 ); @@ -200,9 +200,9 @@ if ('onhashchange' in window) { 141 142 :(code) 143 void test_subtract_r32_from_mem_at_r32() { - 144 Reg[EAX].i = 0x2000; - 145 Reg[EBX].i = 1; - 146 run( + 144 Reg[EAX].i = 0x2000; + 145 Reg[EBX].i = 1; + 146 run( 147 "== code 0x1\n" 148 // op ModR/M SIB displacement immediate 149 " 29 18 \n" // subtract EBX from *EAX @@ -211,8 +211,8 @@ if ('onhashchange' in window) { 152 "0a 00 00 00\n" // 0x0000000a 153 ); 154 CHECK_TRACE_CONTENTS( - 155 "run: subtract EBX from r/m32\n" - 156 "run: effective address is 0x00002000 (EAX)\n" + 155 "run: subtract EBX from r/m32\n" + 156 "run: effective address is 0x00002000 (EAX)\n" 157 "run: storing 0x00000009\n" 158 ); 159 } @@ -220,13 +220,13 @@ if ('onhashchange' in window) { 161 //: 162 163 :(before "End Initialize Op Names") - 164 put_new(Name, "2b", "subtract rm32 from r32 (sub)"); + 164 put_new(Name, "2b", "subtract rm32 from r32 (sub)"); 165 166 :(code) 167 void test_subtract_mem_at_r32_from_r32() { - 168 Reg[EAX].i = 0x2000; - 169 Reg[EBX].i = 10; - 170 run( + 168 Reg[EAX].i = 0x2000; + 169 Reg[EBX].i = 10; + 170 run( 171 "== code 0x1\n" 172 // op ModR/M SIB displacement immediate 173 " 2b 18 \n" // subtract *EAX from EBX @@ -236,38 +236,38 @@ if ('onhashchange' in window) { 177 ); 178 CHECK_TRACE_CONTENTS( 179 "run: subtract r/m32 from EBX\n" - 180 "run: effective address is 0x00002000 (EAX)\n" + 180 "run: effective address is 0x00002000 (EAX)\n" 181 "run: storing 0x00000009\n" 182 ); 183 } 184 185 :(before "End Single-Byte Opcodes") 186 case 0x2b: { // subtract r/m32 from r32 - 187 const uint8_t modrm = next(); + 187 const uint8_t modrm = next(); 188 const uint8_t arg1 = (modrm>>3)&0x7; 189 trace(Callstack_depth+1, "run") << "subtract r/m32 from " << rname(arg1) << end(); 190 const int32_t* signed_arg2 = effective_address(modrm); - 191 const int32_t signed_result = Reg[arg1].i - *signed_arg2; + 191 const int32_t signed_result = Reg[arg1].i - *signed_arg2; 192 SF = (signed_result < 0); - 193 ZF = (signed_result == 0); - 194 int64_t signed_full_result = static_cast<int64_t>(Reg[arg1].i) - *signed_arg2; - 195 OF = (signed_result != signed_full_result); + 193 ZF = (signed_result == 0); + 194 int64_t signed_full_result = static_cast<int64_t>(Reg[arg1].i) - *signed_arg2; + 195 OF = (signed_result != signed_full_result); 196 // set CF 197 uint32_t unsigned_arg2 = static_cast<uint32_t>(*signed_arg2); - 198 uint32_t unsigned_result = Reg[arg1].u - unsigned_arg2; - 199 uint64_t unsigned_full_result = static_cast<uint64_t>(Reg[arg1].u) - unsigned_arg2; - 200 CF = (unsigned_result != unsigned_full_result); - 201 trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); - 202 Reg[arg1].i = signed_result; - 203 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << Reg[arg1].i << end(); + 198 uint32_t unsigned_result = Reg[arg1].u - unsigned_arg2; + 199 uint64_t unsigned_full_result = static_cast<uint64_t>(Reg[arg1].u) - unsigned_arg2; + 200 CF = (unsigned_result != unsigned_full_result); + 201 trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); + 202 Reg[arg1].i = signed_result; + 203 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << Reg[arg1].i << end(); 204 break; 205 } 206 207 :(code) 208 void test_subtract_mem_at_r32_from_r32_signed_overflow() { - 209 Reg[EAX].i = 0x2000; - 210 Reg[EBX].i = 0x80000000; // smallest negative signed integer - 211 run( + 209 Reg[EAX].i = 0x2000; + 210 Reg[EBX].i = 0x80000000; // smallest negative signed integer + 211 run( 212 "== code 0x1\n" 213 // op ModR/M SIB displacement immediate 214 " 2b 18 \n" // subtract *EAX from EBX @@ -277,17 +277,17 @@ if ('onhashchange' in window) { 218 ); 219 CHECK_TRACE_CONTENTS( 220 "run: subtract r/m32 from EBX\n" - 221 "run: effective address is 0x00002000 (EAX)\n" - 222 "run: effective address contains 7fffffff\n" + 221 "run: effective address is 0x00002000 (EAX)\n" + 222 "run: effective address contains 0x7fffffff\n" 223 "run: SF=0; ZF=0; CF=0; OF=1\n" 224 "run: storing 0x00000001\n" 225 ); 226 } 227 228 void test_subtract_mem_at_r32_from_r32_unsigned_overflow() { - 229 Reg[EAX].i = 0x2000; - 230 Reg[EBX].i = 0; - 231 run( + 229 Reg[EAX].i = 0x2000; + 230 Reg[EBX].i = 0; + 231 run( 232 "== code 0x1\n" 233 // op ModR/M SIB displacement immediate 234 " 2b 18 \n" // subtract *EAX from EBX @@ -297,17 +297,17 @@ if ('onhashchange' in window) { 238 ); 239 CHECK_TRACE_CONTENTS( 240 "run: subtract r/m32 from EBX\n" - 241 "run: effective address is 0x00002000 (EAX)\n" - 242 "run: effective address contains 1\n" + 241 "run: effective address is 0x00002000 (EAX)\n" + 242 "run: effective address contains 0x00000001\n" 243 "run: SF=1; ZF=0; CF=1; OF=0\n" 244 "run: storing 0xffffffff\n" 245 ); 246 } 247 248 void test_subtract_mem_at_r32_from_r32_signed_and_unsigned_overflow() { - 249 Reg[EAX].i = 0x2000; - 250 Reg[EBX].i = 0; - 251 run( + 249 Reg[EAX].i = 0x2000; + 250 Reg[EBX].i = 0; + 251 run( 252 "== code 0x1\n" 253 // op ModR/M SIB displacement immediate 254 " 2b 18 \n" // subtract *EAX from EBX @@ -317,8 +317,8 @@ if ('onhashchange' in window) { 258 ); 259 CHECK_TRACE_CONTENTS( 260 "run: subtract r/m32 from EBX\n" - 261 "run: effective address is 0x00002000 (EAX)\n" - 262 "run: effective address contains 80000000\n" + 261 "run: effective address is 0x00002000 (EAX)\n" + 262 "run: effective address contains 0x80000000\n" 263 "run: SF=1; ZF=0; CF=1; OF=1\n" 264 "run: storing 0x80000000\n" 265 ); @@ -327,9 +327,9 @@ if ('onhashchange' in window) { 268 //:: and 269 :(code) 270 void test_and_r32_with_mem_at_r32() { - 271 Reg[EAX].i = 0x2000; - 272 Reg[EBX].i = 0xff; - 273 run( + 271 Reg[EAX].i = 0x2000; + 272 Reg[EBX].i = 0xff; + 273 run( 274 "== code 0x1\n" 275 // op ModR/M SIB displacement immediate 276 " 21 18 \n" // and EBX with *EAX @@ -338,8 +338,8 @@ if ('onhashchange' in window) { 279 "0d 0c 0b 0a\n" // 0x0a0b0c0d 280 ); 281 CHECK_TRACE_CONTENTS( - 282 "run: and EBX with r/m32\n" - 283 "run: effective address is 0x00002000 (EAX)\n" + 282 "run: and EBX with r/m32\n" + 283 "run: effective address is 0x00002000 (EAX)\n" 284 "run: storing 0x0000000d\n" 285 ); 286 } @@ -347,13 +347,13 @@ if ('onhashchange' in window) { 288 //: 289 290 :(before "End Initialize Op Names") - 291 put_new(Name, "23", "r32 = bitwise AND of r32 with rm32 (and)"); + 291 put_new(Name, "23", "r32 = bitwise AND of r32 with rm32 (and)"); 292 293 :(code) 294 void test_and_mem_at_r32_with_r32() { - 295 Reg[EAX].i = 0x2000; - 296 Reg[EBX].i = 0x0a0b0c0d; - 297 run( + 295 Reg[EAX].i = 0x2000; + 296 Reg[EBX].i = 0x0a0b0c0d; + 297 run( 298 "== code 0x1\n" 299 // op ModR/M SIB displacement immediate 300 " 23 18 \n" // and *EAX with EBX @@ -363,26 +363,26 @@ if ('onhashchange' in window) { 304 ); 305 CHECK_TRACE_CONTENTS( 306 "run: and r/m32 with EBX\n" - 307 "run: effective address is 0x00002000 (EAX)\n" + 307 "run: effective address is 0x00002000 (EAX)\n" 308 "run: storing 0x0000000d\n" 309 ); 310 } 311 312 :(before "End Single-Byte Opcodes") 313 case 0x23: { // and r/m32 with r32 - 314 const uint8_t modrm = next(); + 314 const uint8_t modrm = next(); 315 const uint8_t arg1 = (modrm>>3)&0x7; 316 trace(Callstack_depth+1, "run") << "and r/m32 with " << rname(arg1) << end(); 317 // bitwise ops technically operate on unsigned numbers, but it makes no 318 // difference 319 const int32_t* signed_arg2 = effective_address(modrm); - 320 Reg[arg1].i &= *signed_arg2; - 321 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << Reg[arg1].i << end(); - 322 SF = (Reg[arg1].i >> 31); - 323 ZF = (Reg[arg1].i == 0); - 324 CF = false; - 325 OF = false; - 326 trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); + 320 Reg[arg1].i &= *signed_arg2; + 321 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << Reg[arg1].i << end(); + 322 SF = (Reg[arg1].i >> 31); + 323 ZF = (Reg[arg1].i == 0); + 324 CF = false; + 325 OF = false; + 326 trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); 327 break; 328 } 329 @@ -390,9 +390,9 @@ if ('onhashchange' in window) { 331 332 :(code) 333 void test_or_r32_with_mem_at_r32() { - 334 Reg[EAX].i = 0x2000; - 335 Reg[EBX].i = 0xa0b0c0d0; - 336 run( + 334 Reg[EAX].i = 0x2000; + 335 Reg[EBX].i = 0xa0b0c0d0; + 336 run( 337 "== code 0x1\n" 338 // op ModR/M SIB displacement immediate 339 " 09 18 #\n" // EBX with *EAX @@ -401,8 +401,8 @@ if ('onhashchange' in window) { 342 "0d 0c 0b 0a\n" // 0x0a0b0c0d 343 ); 344 CHECK_TRACE_CONTENTS( - 345 "run: or EBX with r/m32\n" - 346 "run: effective address is 0x00002000 (EAX)\n" + 345 "run: or EBX with r/m32\n" + 346 "run: effective address is 0x00002000 (EAX)\n" 347 "run: storing 0xaabbccdd\n" 348 ); 349 } @@ -410,13 +410,13 @@ if ('onhashchange' in window) { 351 //: 352 353 :(before "End Initialize Op Names") - 354 put_new(Name, "0b", "r32 = bitwise OR of r32 with rm32 (or)"); + 354 put_new(Name, "0b", "r32 = bitwise OR of r32 with rm32 (or)"); 355 356 :(code) 357 void test_or_mem_at_r32_with_r32() { - 358 Reg[EAX].i = 0x2000; - 359 Reg[EBX].i = 0xa0b0c0d0; - 360 run( + 358 Reg[EAX].i = 0x2000; + 359 Reg[EBX].i = 0xa0b0c0d0; + 360 run( 361 "== code 0x1\n" 362 // op ModR/M SIB displacement immediate 363 " 0b 18 \n" // or *EAX with EBX @@ -426,26 +426,26 @@ if ('onhashchange' in window) { 367 ); 368 CHECK_TRACE_CONTENTS( 369 "run: or r/m32 with EBX\n" - 370 "run: effective address is 0x00002000 (EAX)\n" + 370 "run: effective address is 0x00002000 (EAX)\n" 371 "run: storing 0xaabbccdd\n" 372 ); 373 } 374 375 :(before "End Single-Byte Opcodes") 376 case 0x0b: { // or r/m32 with r32 - 377 const uint8_t modrm = next(); + 377 const uint8_t modrm = next(); 378 const uint8_t arg1 = (modrm>>3)&0x7; 379 trace(Callstack_depth+1, "run") << "or r/m32 with " << rname(arg1) << end(); 380 // bitwise ops technically operate on unsigned numbers, but it makes no 381 // difference 382 const int32_t* signed_arg2 = effective_address(modrm); - 383 Reg[arg1].i |= *signed_arg2; - 384 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << Reg[arg1].i << end(); - 385 SF = (Reg[arg1].i >> 31); - 386 ZF = (Reg[arg1].i == 0); - 387 CF = false; - 388 OF = false; - 389 trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); + 383 Reg[arg1].i |= *signed_arg2; + 384 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << Reg[arg1].i << end(); + 385 SF = (Reg[arg1].i >> 31); + 386 ZF = (Reg[arg1].i == 0); + 387 CF = false; + 388 OF = false; + 389 trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); 390 break; 391 } 392 @@ -453,9 +453,9 @@ if ('onhashchange' in window) { 394 395 :(code) 396 void test_xor_r32_with_mem_at_r32() { - 397 Reg[EAX].i = 0x2000; - 398 Reg[EBX].i = 0xa0b0c0d0; - 399 run( + 397 Reg[EAX].i = 0x2000; + 398 Reg[EBX].i = 0xa0b0c0d0; + 399 run( 400 "== code 0x1\n" 401 // op ModR/M SIB displacement immediate 402 " 31 18 \n" // xor EBX with *EAX @@ -463,8 +463,8 @@ if ('onhashchange' in window) { 404 "0d 0c bb aa\n" // 0xaabb0c0d 405 ); 406 CHECK_TRACE_CONTENTS( - 407 "run: xor EBX with r/m32\n" - 408 "run: effective address is 0x00002000 (EAX)\n" + 407 "run: xor EBX with r/m32\n" + 408 "run: effective address is 0x00002000 (EAX)\n" 409 "run: storing 0x0a0bccdd\n" 410 ); 411 } @@ -472,13 +472,13 @@ if ('onhashchange' in window) { 413 //: 414 415 :(before "End Initialize Op Names") - 416 put_new(Name, "33", "r32 = bitwise XOR of r32 with rm32 (xor)"); + 416 put_new(Name, "33", "r32 = bitwise XOR of r32 with rm32 (xor)"); 417 418 :(code) 419 void test_xor_mem_at_r32_with_r32() { - 420 Reg[EAX].i = 0x2000; - 421 Reg[EBX].i = 0xa0b0c0d0; - 422 run( + 420 Reg[EAX].i = 0x2000; + 421 Reg[EBX].i = 0xa0b0c0d0; + 422 run( 423 "== code 0x1\n" 424 // op ModR/M SIB displacement immediate 425 " 33 18 \n" // xor *EAX with EBX @@ -488,26 +488,26 @@ if ('onhashchange' in window) { 429 ); 430 CHECK_TRACE_CONTENTS( 431 "run: xor r/m32 with EBX\n" - 432 "run: effective address is 0x00002000 (EAX)\n" + 432 "run: effective address is 0x00002000 (EAX)\n" 433 "run: storing 0xaabbccdd\n" 434 ); 435 } 436 437 :(before "End Single-Byte Opcodes") 438 case 0x33: { // xor r/m32 with r32 - 439 const uint8_t modrm = next(); + 439 const uint8_t modrm = next(); 440 const uint8_t arg1 = (modrm>>3)&0x7; 441 trace(Callstack_depth+1, "run") << "xor r/m32 with " << rname(arg1) << end(); 442 // bitwise ops technically operate on unsigned numbers, but it makes no 443 // difference 444 const int32_t* signed_arg2 = effective_address(modrm); - 445 Reg[arg1].i |= *signed_arg2; - 446 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << Reg[arg1].i << end(); - 447 SF = (Reg[arg1].i >> 31); - 448 ZF = (Reg[arg1].i == 0); - 449 CF = false; - 450 OF = false; - 451 trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); + 445 Reg[arg1].i |= *signed_arg2; + 446 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << Reg[arg1].i << end(); + 447 SF = (Reg[arg1].i >> 31); + 448 ZF = (Reg[arg1].i == 0); + 449 CF = false; + 450 OF = false; + 451 trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); 452 break; 453 } 454 @@ -515,8 +515,8 @@ if ('onhashchange' in window) { 456 457 :(code) 458 void test_not_of_mem_at_r32() { - 459 Reg[EBX].i = 0x2000; - 460 run( + 459 Reg[EBX].i = 0x2000; + 460 run( 461 "== code 0x1\n" 462 // op ModR/M SIB displacement immediate 463 " f7 13 \n" // not *EBX @@ -526,7 +526,7 @@ if ('onhashchange' in window) { 467 ); 468 CHECK_TRACE_CONTENTS( 469 "run: operate on r/m32\n" - 470 "run: effective address is 0x00002000 (EBX)\n" + 470 "run: effective address is 0x00002000 (EBX)\n" 471 "run: subop: not\n" 472 "run: storing 0xf0f0ff00\n" 473 ); @@ -536,9 +536,9 @@ if ('onhashchange' in window) { 477 478 :(code) 479 void test_compare_mem_at_r32_with_r32_greater() { - 480 Reg[EAX].i = 0x2000; - 481 Reg[EBX].i = 0x0a0b0c07; - 482 run( + 480 Reg[EAX].i = 0x2000; + 481 Reg[EBX].i = 0x0a0b0c07; + 482 run( 483 "== code 0x1\n" 484 // op ModR/M SIB displacement immediate 485 " 39 18 \n" // compare *EAX with EBX @@ -548,16 +548,16 @@ if ('onhashchange' in window) { 489 ); 490 CHECK_TRACE_CONTENTS( 491 "run: compare r/m32 with EBX\n" - 492 "run: effective address is 0x00002000 (EAX)\n" + 492 "run: effective address is 0x00002000 (EAX)\n" 493 "run: SF=0; ZF=0; CF=0; OF=0\n" 494 ); 495 } 496 497 :(code) 498 void test_compare_mem_at_r32_with_r32_lesser() { - 499 Reg[EAX].i = 0x2000; - 500 Reg[EBX].i = 0x0a0b0c0d; - 501 run( + 499 Reg[EAX].i = 0x2000; + 500 Reg[EBX].i = 0x0a0b0c0d; + 501 run( 502 "== code 0x1\n" 503 // op ModR/M SIB displacement immediate 504 " 39 18 \n" // compare *EAX with EBX @@ -567,16 +567,16 @@ if ('onhashchange' in window) { 508 ); 509 CHECK_TRACE_CONTENTS( 510 "run: compare r/m32 with EBX\n" - 511 "run: effective address is 0x00002000 (EAX)\n" + 511 "run: effective address is 0x00002000 (EAX)\n" 512 "run: SF=1; ZF=0; CF=1; OF=0\n" 513 ); 514 } 515 516 :(code) 517 void test_compare_mem_at_r32_with_r32_equal() { - 518 Reg[EAX].i = 0x2000; - 519 Reg[EBX].i = 0x0a0b0c0d; - 520 run( + 518 Reg[EAX].i = 0x2000; + 519 Reg[EBX].i = 0x0a0b0c0d; + 520 run( 521 "== code 0x1\n" 522 // op ModR/M SIB displacement immediate 523 " 39 18 \n" // compare *EAX and EBX @@ -586,7 +586,7 @@ if ('onhashchange' in window) { 527 ); 528 CHECK_TRACE_CONTENTS( 529 "run: compare r/m32 with EBX\n" - 530 "run: effective address is 0x00002000 (EAX)\n" + 530 "run: effective address is 0x00002000 (EAX)\n" 531 "run: SF=0; ZF=1; CF=0; OF=0\n" 532 ); 533 } @@ -594,13 +594,13 @@ if ('onhashchange' in window) { 535 //: 536 537 :(before "End Initialize Op Names") - 538 put_new(Name, "3b", "compare: set SF if r32 < rm32 (cmp)"); + 538 put_new(Name, "3b", "compare: set SF if r32 < rm32 (cmp)"); 539 540 :(code) 541 void test_compare_r32_with_mem_at_r32_greater() { - 542 Reg[EAX].i = 0x2000; - 543 Reg[EBX].i = 0x0a0b0c0d; - 544 run( + 542 Reg[EAX].i = 0x2000; + 543 Reg[EBX].i = 0x0a0b0c0d; + 544 run( 545 "== code 0x1\n" 546 // op ModR/M SIB displacement immediate 547 " 3b 18 \n" // compare EBX with *EAX @@ -609,36 +609,36 @@ if ('onhashchange' in window) { 550 "07 0c 0b 0a\n" // 0x0a0b0c07 551 ); 552 CHECK_TRACE_CONTENTS( - 553 "run: compare EBX with r/m32\n" - 554 "run: effective address is 0x00002000 (EAX)\n" + 553 "run: compare EBX with r/m32\n" + 554 "run: effective address is 0x00002000 (EAX)\n" 555 "run: SF=0; ZF=0; CF=0; OF=0\n" 556 ); 557 } 558 559 :(before "End Single-Byte Opcodes") 560 case 0x3b: { // set SF if r32 < r/m32 - 561 const uint8_t modrm = next(); + 561 const uint8_t modrm = next(); 562 const uint8_t reg1 = (modrm>>3)&0x7; 563 trace(Callstack_depth+1, "run") << "compare " << rname(reg1) << " with r/m32" << end(); 564 const int32_t* signed_arg2 = effective_address(modrm); - 565 const int32_t signed_difference = Reg[reg1].i - *signed_arg2; + 565 const int32_t signed_difference = Reg[reg1].i - *signed_arg2; 566 SF = (signed_difference < 0); - 567 ZF = (signed_difference == 0); - 568 int64_t full_signed_difference = static_cast<int64_t>(Reg[reg1].i) - *signed_arg2; - 569 OF = (signed_difference != full_signed_difference); + 567 ZF = (signed_difference == 0); + 568 int64_t full_signed_difference = static_cast<int64_t>(Reg[reg1].i) - *signed_arg2; + 569 OF = (signed_difference != full_signed_difference); 570 const uint32_t unsigned_arg2 = static_cast<uint32_t>(*signed_arg2); - 571 const uint32_t unsigned_difference = Reg[reg1].u - unsigned_arg2; - 572 const uint64_t full_unsigned_difference = static_cast<uint64_t>(Reg[reg1].u) - unsigned_arg2; - 573 CF = (unsigned_difference != full_unsigned_difference); - 574 trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); + 571 const uint32_t unsigned_difference = Reg[reg1].u - unsigned_arg2; + 572 const uint64_t full_unsigned_difference = static_cast<uint64_t>(Reg[reg1].u) - unsigned_arg2; + 573 CF = (unsigned_difference != full_unsigned_difference); + 574 trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); 575 break; 576 } 577 578 :(code) 579 void test_compare_r32_with_mem_at_r32_lesser_unsigned_and_signed() { - 580 Reg[EAX].i = 0x2000; - 581 Reg[EBX].i = 0x0a0b0c07; - 582 run( + 580 Reg[EAX].i = 0x2000; + 581 Reg[EBX].i = 0x0a0b0c07; + 582 run( 583 "== code 0x1\n" 584 // op ModR/M SIB displacement immediate 585 " 3b 18 \n" // compare EBX with *EAX @@ -647,17 +647,17 @@ if ('onhashchange' in window) { 588 "0d 0c 0b 0a\n" // 0x0a0b0c0d 589 ); 590 CHECK_TRACE_CONTENTS( - 591 "run: compare EBX with r/m32\n" - 592 "run: effective address is 0x00002000 (EAX)\n" - 593 "run: effective address contains a0b0c0d\n" + 591 "run: compare EBX with r/m32\n" + 592 "run: effective address is 0x00002000 (EAX)\n" + 593 "run: effective address contains 0x0a0b0c0d\n" 594 "run: SF=1; ZF=0; CF=1; OF=0\n" 595 ); 596 } 597 598 void test_compare_r32_with_mem_at_r32_lesser_unsigned_and_signed_due_to_overflow() { - 599 Reg[EAX].i = 0x2000; - 600 Reg[EBX].i = 0x7fffffff; // largest positive signed integer - 601 run( + 599 Reg[EAX].i = 0x2000; + 600 Reg[EBX].i = 0x7fffffff; // largest positive signed integer + 601 run( 602 "== code 0x1\n" 603 // op ModR/M SIB displacement immediate 604 " 3b 18 \n" // compare EBX with *EAX @@ -666,17 +666,17 @@ if ('onhashchange' in window) { 607 "00 00 00 80\n" // smallest negative signed integer 608 ); 609 CHECK_TRACE_CONTENTS( - 610 "run: compare EBX with r/m32\n" - 611 "run: effective address is 0x00002000 (EAX)\n" - 612 "run: effective address contains 80000000\n" + 610 "run: compare EBX with r/m32\n" + 611 "run: effective address is 0x00002000 (EAX)\n" + 612 "run: effective address contains 0x80000000\n" 613 "run: SF=1; ZF=0; CF=1; OF=1\n" 614 ); 615 } 616 617 void test_compare_r32_with_mem_at_r32_lesser_signed() { - 618 Reg[EAX].i = 0x2000; - 619 Reg[EBX].i = 0xffffffff; // -1 - 620 run( + 618 Reg[EAX].i = 0x2000; + 619 Reg[EBX].i = 0xffffffff; // -1 + 620 run( 621 "== code 0x1\n" 622 // op ModR/M SIB displacement immediate 623 " 3b 18 \n" // compare EBX with *EAX @@ -685,17 +685,17 @@ if ('onhashchange' in window) { 626 "01 00 00 00\n" // 1 627 ); 628 CHECK_TRACE_CONTENTS( - 629 "run: compare EBX with r/m32\n" - 630 "run: effective address is 0x00002000 (EAX)\n" - 631 "run: effective address contains 1\n" + 629 "run: compare EBX with r/m32\n" + 630 "run: effective address is 0x00002000 (EAX)\n" + 631 "run: effective address contains 0x00000001\n" 632 "run: SF=1; ZF=0; CF=0; OF=0\n" 633 ); 634 } 635 636 void test_compare_r32_with_mem_at_r32_lesser_unsigned() { - 637 Reg[EAX].i = 0x2000; - 638 Reg[EBX].i = 0x00000001; // 1 - 639 run( + 637 Reg[EAX].i = 0x2000; + 638 Reg[EBX].i = 0x00000001; // 1 + 639 run( 640 "== code 0x1\n" 641 // op ModR/M SIB displacement immediate 642 " 3b 18 \n" // compare EBX with *EAX @@ -704,17 +704,17 @@ if ('onhashchange' in window) { 645 "ff ff ff ff\n" // -1 646 ); 647 CHECK_TRACE_CONTENTS( - 648 "run: compare EBX with r/m32\n" - 649 "run: effective address is 0x00002000 (EAX)\n" - 650 "run: effective address contains ffffffff\n" + 648 "run: compare EBX with r/m32\n" + 649 "run: effective address is 0x00002000 (EAX)\n" + 650 "run: effective address contains 0xffffffff\n" 651 "run: SF=0; ZF=0; CF=1; OF=0\n" 652 ); 653 } 654 655 void test_compare_r32_with_mem_at_r32_equal() { - 656 Reg[EAX].i = 0x2000; - 657 Reg[EBX].i = 0x0a0b0c0d; - 658 run( + 656 Reg[EAX].i = 0x2000; + 657 Reg[EBX].i = 0x0a0b0c0d; + 658 run( 659 "== code 0x1\n" 660 // op ModR/M SIB displacement immediate 661 " 3b 18 \n" // compare EBX with *EAX @@ -723,8 +723,8 @@ if ('onhashchange' in window) { 664 "0d 0c 0b 0a\n" // 0x0a0b0c0d 665 ); 666 CHECK_TRACE_CONTENTS( - 667 "run: compare EBX with r/m32\n" - 668 "run: effective address is 0x00002000 (EAX)\n" + 667 "run: compare EBX with r/m32\n" + 668 "run: effective address is 0x00002000 (EAX)\n" 669 "run: SF=0; ZF=1; CF=0; OF=0\n" 670 ); 671 } @@ -732,17 +732,17 @@ if ('onhashchange' in window) { 673 //:: copy (mov) 674 675 void test_copy_r32_to_mem_at_r32() { - 676 Reg[EBX].i = 0xaf; - 677 Reg[EAX].i = 0x60; - 678 run( + 676 Reg[EBX].i = 0xaf; + 677 Reg[EAX].i = 0x60; + 678 run( 679 "== code 0x1\n" 680 // op ModR/M SIB displacement immediate 681 " 89 18 \n" // copy EBX to *EAX 682 // ModR/M in binary: 00 (indirect mode) 011 (src EAX) 000 (dest EAX) 683 ); 684 CHECK_TRACE_CONTENTS( - 685 "run: copy EBX to r/m32\n" - 686 "run: effective address is 0x00000060 (EAX)\n" + 685 "run: copy EBX to r/m32\n" + 686 "run: effective address is 0x00000060 (EAX)\n" 687 "run: storing 0x000000af\n" 688 ); 689 } @@ -750,12 +750,12 @@ if ('onhashchange' in window) { 691 //: 692 693 :(before "End Initialize Op Names") - 694 put_new(Name, "8b", "copy rm32 to r32 (mov)"); + 694 put_new(Name, "8b", "copy rm32 to r32 (mov)"); 695 696 :(code) 697 void test_copy_mem_at_r32_to_r32() { - 698 Reg[EAX].i = 0x2000; - 699 run( + 698 Reg[EAX].i = 0x2000; + 699 run( 700 "== code 0x1\n" 701 // op ModR/M SIB displacement immediate 702 " 8b 18 \n" // copy *EAX to EBX @@ -764,19 +764,19 @@ if ('onhashchange' in window) { 705 ); 706 CHECK_TRACE_CONTENTS( 707 "run: copy r/m32 to EBX\n" - 708 "run: effective address is 0x00002000 (EAX)\n" + 708 "run: effective address is 0x00002000 (EAX)\n" 709 "run: storing 0x000000af\n" 710 ); 711 } 712 713 :(before "End Single-Byte Opcodes") 714 case 0x8b: { // copy r32 to r/m32 - 715 const uint8_t modrm = next(); + 715 const uint8_t modrm = next(); 716 const uint8_t rdest = (modrm>>3)&0x7; 717 trace(Callstack_depth+1, "run") << "copy r/m32 to " << rname(rdest) << end(); 718 const int32_t* src = effective_address(modrm); - 719 Reg[rdest].i = *src; - 720 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *src << end(); + 719 Reg[rdest].i = *src; + 720 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *src << end(); 721 break; 722 } 723 @@ -784,8 +784,8 @@ if ('onhashchange' in window) { 725 726 :(code) 727 void test_jump_mem_at_r32() { - 728 Reg[EAX].i = 0x2000; - 729 run( + 728 Reg[EAX].i = 0x2000; + 729 run( 730 "== code 0x1\n" 731 // op ModR/M SIB displacement immediate 732 " ff 20 \n" // jump to *EAX @@ -798,7 +798,7 @@ if ('onhashchange' in window) { 739 CHECK_TRACE_CONTENTS( 740 "run: 0x00000001 opcode: ff\n" 741 "run: jump to r/m32\n" - 742 "run: effective address is 0x00002000 (EAX)\n" + 742 "run: effective address is 0x00002000 (EAX)\n" 743 "run: jumping to 0x00000008\n" 744 "run: 0x00000008 opcode: b8\n" 745 ); @@ -809,8 +809,8 @@ if ('onhashchange' in window) { 750 case 4: { // jump to r/m32 751 trace(Callstack_depth+1, "run") << "jump to r/m32" << end(); 752 const int32_t* arg2 = effective_address(modrm); - 753 EIP = *arg2; - 754 trace(Callstack_depth+1, "run") << "jumping to 0x" << HEXWORD << EIP << end(); + 753 EIP = *arg2; + 754 trace(Callstack_depth+1, "run") << "jumping to 0x" << HEXWORD << EIP << end(); 755 break; 756 } 757 @@ -818,10 +818,10 @@ if ('onhashchange' in window) { 759 760 :(code) 761 void test_push_mem_at_r32() { - 762 Reg[EAX].i = 0x2000; - 763 Mem.push_back(vma(0xbd000000)); // manually allocate memory - 764 Reg[ESP].u = 0xbd000014; - 765 run( + 762 Reg[EAX].i = 0x2000; + 763 Mem.push_back(vma(0xbd000000)); // manually allocate memory + 764 Reg[ESP].u = 0xbd000014; + 765 run( 766 "== code 0x1\n" 767 // op ModR/M SIB displacement immediate 768 " ff 30 \n" // push *EAX to stack @@ -830,8 +830,8 @@ if ('onhashchange' in window) { 771 ); 772 CHECK_TRACE_CONTENTS( 773 "run: push r/m32\n" - 774 "run: effective address is 0x00002000 (EAX)\n" - 775 "run: decrementing ESP to 0xbd000010\n" + 774 "run: effective address is 0x00002000 (EAX)\n" + 775 "run: decrementing ESP to 0xbd000010\n" 776 "run: pushing value 0x000000af\n" 777 ); 778 } @@ -847,15 +847,15 @@ if ('onhashchange' in window) { 788 //:: pop 789 790 :(before "End Initialize Op Names") - 791 put_new(Name, "8f", "pop top of stack to rm32 (pop)"); + 791 put_new(Name, "8f", "pop top of stack to rm32 (pop)"); 792 793 :(code) 794 void test_pop_mem_at_r32() { - 795 Reg[EAX].i = 0x60; - 796 Mem.push_back(vma(0xbd000000)); // manually allocate memory - 797 Reg[ESP].u = 0xbd000000; - 798 write_mem_i32(0xbd000000, 0x00000030); - 799 run( + 795 Reg[EAX].i = 0x60; + 796 Mem.push_back(vma(0xbd000000)); // manually allocate memory + 797 Reg[ESP].u = 0xbd000000; + 798 write_mem_i32(0xbd000000, 0x00000030); + 799 run( 800 "== code 0x1\n" 801 // op ModR/M SIB displacement immediate 802 " 8f 00 \n" // pop stack into *EAX @@ -863,15 +863,15 @@ if ('onhashchange' in window) { 804 ); 805 CHECK_TRACE_CONTENTS( 806 "run: pop into r/m32\n" - 807 "run: effective address is 0x00000060 (EAX)\n" + 807 "run: effective address is 0x00000060 (EAX)\n" 808 "run: popping value 0x00000030\n" - 809 "run: incrementing ESP to 0xbd000004\n" + 809 "run: incrementing ESP to 0xbd000004\n" 810 ); 811 } 812 813 :(before "End Single-Byte Opcodes") 814 case 0x8f: { // pop stack into r/m32 - 815 const uint8_t modrm = next(); + 815 const uint8_t modrm = next(); 816 const uint8_t subop = (modrm>>3)&0x7; 817 switch (subop) { 818 case 0: { @@ -888,8 +888,8 @@ if ('onhashchange' in window) { 829 830 :(code) 831 void test_add_r32_to_mem_at_displacement() { - 832 Reg[EBX].i = 0x10; // source - 833 run( + 832 Reg[EBX].i = 0x10; // source + 833 run( 834 "== code 0x1\n" 835 // op ModR/M SIB displacement immediate 836 " 01 1d 00 20 00 00 \n" // add EBX to *0x2000 @@ -898,25 +898,25 @@ if ('onhashchange' in window) { 839 "01 00 00 00\n" // 0x00000001 840 ); 841 CHECK_TRACE_CONTENTS( - 842 "run: add EBX to r/m32\n" - 843 "run: effective address is 0x00002000 (disp32)\n" + 842 "run: add EBX to r/m32\n" + 843 "run: effective address is 0x00002000 (disp32)\n" 844 "run: storing 0x00000011\n" 845 ); 846 } 847 848 :(before "End Mod 0 Special-cases(addr)") 849 case 5: // exception: mod 0b00 rm 0b101 => incoming disp32 - 850 addr = next32(); - 851 trace(Callstack_depth+1, "run") << "effective address is 0x" << HEXWORD << addr << " (disp32)" << end(); + 850 addr = next32(); + 851 trace(Callstack_depth+1, "run") << "effective address is 0x" << HEXWORD << addr << " (disp32)" << end(); 852 break; 853 854 //: 855 856 :(code) 857 void test_add_r32_to_mem_at_r32_plus_disp8() { - 858 Reg[EBX].i = 0x10; // source - 859 Reg[EAX].i = 0x1ffe; // dest - 860 run( + 858 Reg[EBX].i = 0x10; // source + 859 Reg[EAX].i = 0x1ffe; // dest + 860 run( 861 "== code 0x1\n" 862 // op ModR/M SIB displacement immediate 863 " 01 58 02 \n" // add EBX to *(EAX+2) @@ -925,8 +925,8 @@ if ('onhashchange' in window) { 866 "01 00 00 00\n" // 0x00000001 867 ); 868 CHECK_TRACE_CONTENTS( - 869 "run: add EBX to r/m32\n" - 870 "run: effective address is initially 0x00001ffe (EAX)\n" + 869 "run: add EBX to r/m32\n" + 870 "run: effective address is initially 0x00001ffe (EAX)\n" 871 "run: effective address is 0x00002000 (after adding disp8)\n" 872 "run: storing 0x00000011\n" 873 ); @@ -936,15 +936,15 @@ if ('onhashchange' in window) { 877 case 1: { // indirect + disp8 addressing 878 switch (rm) { 879 default: - 880 addr = Reg[rm].u; - 881 trace(Callstack_depth+1, "run") << "effective address is initially 0x" << HEXWORD << addr << " (" << rname(rm) << ")" << end(); + 880 addr = Reg[rm].u; + 881 trace(Callstack_depth+1, "run") << "effective address is initially 0x" << HEXWORD << addr << " (" << rname(rm) << ")" << end(); 882 break; 883 // End Mod 1 Special-cases(addr) 884 } - 885 int8_t displacement = static_cast<int8_t>(next()); + 885 int8_t displacement = static_cast<int8_t>(next()); 886 if (addr > 0) { 887 addr += displacement; - 888 trace(Callstack_depth+1, "run") << "effective address is 0x" << HEXWORD << addr << " (after adding disp8)" << end(); + 888 trace(Callstack_depth+1, "run") << "effective address is 0x" << HEXWORD << addr << " (after adding disp8)" << end(); 889 } 890 else { 891 trace(Callstack_depth+1, "run") << "null address; skipping displacement" << end(); @@ -954,9 +954,9 @@ if ('onhashchange' in window) { 895 896 :(code) 897 void test_add_r32_to_mem_at_r32_plus_negative_disp8() { - 898 Reg[EBX].i = 0x10; // source - 899 Reg[EAX].i = 0x2001; // dest - 900 run( + 898 Reg[EBX].i = 0x10; // source + 899 Reg[EAX].i = 0x2001; // dest + 900 run( 901 "== code 0x1\n" 902 // op ModR/M SIB displacement immediate 903 " 01 58 ff \n" // add EBX to *(EAX-1) @@ -965,8 +965,8 @@ if ('onhashchange' in window) { 906 "01 00 00 00\n" // 0x00000001 907 ); 908 CHECK_TRACE_CONTENTS( - 909 "run: add EBX to r/m32\n" - 910 "run: effective address is initially 0x00002001 (EAX)\n" + 909 "run: add EBX to r/m32\n" + 910 "run: effective address is initially 0x00002001 (EAX)\n" 911 "run: effective address is 0x00002000 (after adding disp8)\n" 912 "run: storing 0x00000011\n" 913 ); @@ -976,9 +976,9 @@ if ('onhashchange' in window) { 917 918 :(code) 919 void test_add_r32_to_mem_at_r32_plus_disp32() { - 920 Reg[EBX].i = 0x10; // source - 921 Reg[EAX].i = 0x1ffe; // dest - 922 run( + 920 Reg[EBX].i = 0x10; // source + 921 Reg[EAX].i = 0x1ffe; // dest + 922 run( 923 "== code 0x1\n" 924 // op ModR/M SIB displacement immediate 925 " 01 98 02 00 00 00 \n" // add EBX to *(EAX+2) @@ -987,9 +987,9 @@ if ('onhashchange' in window) { 928 "01 00 00 00\n" // 0x00000001 929 ); 930 CHECK_TRACE_CONTENTS( - 931 "run: add EBX to r/m32\n" - 932 "run: effective address is initially 0x00001ffe (EAX)\n" - 933 "run: effective address is 0x00002000 (after adding disp32)\n" + 931 "run: add EBX to r/m32\n" + 932 "run: effective address is initially 0x00001ffe (EAX)\n" + 933 "run: effective address is 0x00002000 (after adding disp32)\n" 934 "run: storing 0x00000011\n" 935 ); 936 } @@ -998,22 +998,22 @@ if ('onhashchange' in window) { 939 case 2: // indirect + disp32 addressing 940 switch (rm) { 941 default: - 942 addr = Reg[rm].u; - 943 trace(Callstack_depth+1, "run") << "effective address is initially 0x" << HEXWORD << addr << " (" << rname(rm) << ")" << end(); + 942 addr = Reg[rm].u; + 943 trace(Callstack_depth+1, "run") << "effective address is initially 0x" << HEXWORD << addr << " (" << rname(rm) << ")" << end(); 944 break; 945 // End Mod 2 Special-cases(addr) 946 } 947 if (addr > 0) { - 948 addr += next32(); - 949 trace(Callstack_depth+1, "run") << "effective address is 0x" << HEXWORD << addr << " (after adding disp32)" << end(); + 948 addr += next32(); + 949 trace(Callstack_depth+1, "run") << "effective address is 0x" << HEXWORD << addr << " (after adding disp32)" << end(); 950 } 951 break; 952 953 :(code) 954 void test_add_r32_to_mem_at_r32_plus_negative_disp32() { - 955 Reg[EBX].i = 0x10; // source - 956 Reg[EAX].i = 0x2001; // dest - 957 run( + 955 Reg[EBX].i = 0x10; // source + 956 Reg[EAX].i = 0x2001; // dest + 957 run( 958 "== code 0x1\n" 959 // op ModR/M SIB displacement immediate 960 " 01 98 ff ff ff ff \n" // add EBX to *(EAX-1) @@ -1022,9 +1022,9 @@ if ('onhashchange' in window) { 963 "01 00 00 00\n" // 0x00000001 964 ); 965 CHECK_TRACE_CONTENTS( - 966 "run: add EBX to r/m32\n" - 967 "run: effective address is initially 0x00002001 (EAX)\n" - 968 "run: effective address is 0x00002000 (after adding disp32)\n" + 966 "run: add EBX to r/m32\n" + 967 "run: effective address is initially 0x00002001 (EAX)\n" + 968 "run: effective address is 0x00002000 (after adding disp32)\n" 969 "run: storing 0x00000011\n" 970 ); 971 } @@ -1032,12 +1032,12 @@ if ('onhashchange' in window) { 973 //:: copy address (lea) 974 975 :(before "End Initialize Op Names") - 976 put_new(Name, "8d", "copy address in rm32 into r32 (lea)"); + 976 put_new(Name, "8d", "copy address in rm32 into r32 (lea)"); 977 978 :(code) 979 void test_copy_address() { - 980 Reg[EAX].u = 0x2000; - 981 run( + 980 Reg[EAX].u = 0x2000; + 981 run( 982 "== code 0x1\n" 983 // op ModR/M SIB displacement immediate 984 " 8d 18 \n" // copy address in EAX into EBX @@ -1045,16 +1045,16 @@ if ('onhashchange' in window) { 986 ); 987 CHECK_TRACE_CONTENTS( 988 "run: copy address into EBX\n" - 989 "run: effective address is 0x00002000 (EAX)\n" + 989 "run: effective address is 0x00002000 (EAX)\n" 990 ); 991 } 992 993 :(before "End Single-Byte Opcodes") 994 case 0x8d: { // copy address of m32 to r32 - 995 const uint8_t modrm = next(); + 995 const uint8_t modrm = next(); 996 const uint8_t arg1 = (modrm>>3)&0x7; 997 trace(Callstack_depth+1, "run") << "copy address into " << rname(arg1) << end(); - 998 Reg[arg1].u = effective_address_number(modrm); + 998 Reg[arg1].u = effective_address_number(modrm); 999 break; 1000 } diff --git a/html/015immediate_addressing.cc.html b/html/015immediate_addressing.cc.html index c5d39b5e..31f25c72 100644 --- a/html/015immediate_addressing.cc.html +++ b/html/015immediate_addressing.cc.html @@ -62,32 +62,32 @@ if ('onhashchange' in window) { 1 //: instructions that (immediately) contain an argument to act with 2 3 :(before "End Initialize Op Names") - 4 put_new(Name, "05", "add imm32 to EAX (add)"); + 4 put_new(Name, "05", "add imm32 to EAX (add)"); 5 6 :(before "End Single-Byte Opcodes") 7 case 0x05: { // add imm32 to EAX - 8 int32_t signed_arg2 = next32(); - 9 trace(Callstack_depth+1, "run") << "add imm32 0x" << HEXWORD << signed_arg2 << " to EAX" << end(); - 10 int32_t signed_result = Reg[EAX].i + signed_arg2; + 8 int32_t signed_arg2 = next32(); + 9 trace(Callstack_depth+1, "run") << "add imm32 0x" << HEXWORD << signed_arg2 << " to EAX" << end(); + 10 int32_t signed_result = Reg[EAX].i + signed_arg2; 11 SF = (signed_result < 0); - 12 ZF = (signed_result == 0); - 13 int64_t signed_full_result = static_cast<int64_t>(Reg[EAX].i) + signed_arg2; - 14 OF = (signed_result != signed_full_result); + 12 ZF = (signed_result == 0); + 13 int64_t signed_full_result = static_cast<int64_t>(Reg[EAX].i) + signed_arg2; + 14 OF = (signed_result != signed_full_result); 15 // set CF 16 uint32_t unsigned_arg2 = static_cast<uint32_t>(signed_arg2); - 17 uint32_t unsigned_result = Reg[EAX].u + unsigned_arg2; - 18 uint64_t unsigned_full_result = static_cast<uint64_t>(Reg[EAX].u) + unsigned_arg2; - 19 CF = (unsigned_result != unsigned_full_result); - 20 trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); - 21 Reg[EAX].i = signed_result; - 22 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << Reg[EAX].i << end(); + 17 uint32_t unsigned_result = Reg[EAX].u + unsigned_arg2; + 18 uint64_t unsigned_full_result = static_cast<uint64_t>(Reg[EAX].u) + unsigned_arg2; + 19 CF = (unsigned_result != unsigned_full_result); + 20 trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); + 21 Reg[EAX].i = signed_result; + 22 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << Reg[EAX].i << end(); 23 break; 24 } 25 26 :(code) 27 void test_add_imm32_to_EAX_signed_overflow() { - 28 Reg[EAX].i = 0x7fffffff; // largest positive signed integer - 29 run( + 28 Reg[EAX].i = 0x7fffffff; // largest positive signed integer + 29 run( 30 "== code 0x1\n" 31 // op ModR/M SIB displacement immediate 32 " 05 01 00 00 00 \n" // add 1 to EAX @@ -100,9 +100,9 @@ if ('onhashchange' in window) { 39 } 40 41 void test_add_imm32_to_EAX_unsigned_overflow() { - 42 Reg[EAX].u = 0xffffffff; // largest unsigned number - 43 Reg[EBX].u = 1; - 44 run( + 42 Reg[EAX].u = 0xffffffff; // largest unsigned number + 43 Reg[EBX].u = 1; + 44 run( 45 "== code 0x1\n" 46 // op ModR/M SIB displacement immediate 47 " 05 01 00 00 00 \n" // add 1 to EAX @@ -115,8 +115,8 @@ if ('onhashchange' in window) { 54 } 55 56 void test_add_imm32_to_EAX_unsigned_and_signed_overflow() { - 57 Reg[EAX].u = 0x80000000; // smallest negative signed integer - 58 run( + 57 Reg[EAX].u = 0x80000000; // smallest negative signed integer + 58 run( 59 "== code 0x1\n" 60 // op ModR/M SIB displacement immediate 61 " 05 00 00 00 80 \n" // add 0x80000000 to EAX @@ -131,12 +131,12 @@ if ('onhashchange' in window) { 70 //: 71 72 :(before "End Initialize Op Names") - 73 put_new(Name, "81", "combine rm32 with imm32 based on subop (add/sub/and/or/xor/cmp)"); + 73 put_new(Name, "81", "combine rm32 with imm32 based on subop (add/sub/and/or/xor/cmp)"); 74 75 :(code) 76 void test_add_imm32_to_r32() { - 77 Reg[EBX].i = 1; - 78 run( + 77 Reg[EBX].i = 1; + 78 run( 79 "== code 0x1\n" 80 // op ModR/M SIB displacement immediate 81 " 81 c3 0a 0b 0c 0d\n" // add 0x0d0c0b0a to EBX @@ -154,33 +154,33 @@ if ('onhashchange' in window) { 93 :(before "End Single-Byte Opcodes") 94 case 0x81: { // combine r/m32 with imm32 95 trace(Callstack_depth+1, "run") << "combine r/m32 with imm32" << end(); - 96 const uint8_t modrm = next(); + 96 const uint8_t modrm = next(); 97 int32_t* signed_arg1 = effective_address(modrm); - 98 const int32_t signed_arg2 = next32(); - 99 trace(Callstack_depth+1, "run") << "imm32 is 0x" << HEXWORD << signed_arg2 << end(); + 98 const int32_t signed_arg2 = next32(); + 99 trace(Callstack_depth+1, "run") << "imm32 is 0x" << HEXWORD << signed_arg2 << end(); 100 const uint8_t subop = (modrm>>3)&0x7; // middle 3 'reg opcode' bits 101 switch (subop) { 102 case 0: { 103 trace(Callstack_depth+1, "run") << "subop add" << end(); 104 int32_t signed_result = *signed_arg1 + signed_arg2; 105 SF = (signed_result < 0); - 106 ZF = (signed_result == 0); + 106 ZF = (signed_result == 0); 107 int64_t signed_full_result = static_cast<int64_t>(*signed_arg1) + signed_arg2; - 108 OF = (signed_result != signed_full_result); + 108 OF = (signed_result != signed_full_result); 109 // set CF 110 uint32_t unsigned_arg1 = static_cast<uint32_t>(*signed_arg1); 111 uint32_t unsigned_arg2 = static_cast<uint32_t>(signed_arg2); 112 uint32_t unsigned_result = unsigned_arg1 + unsigned_arg2; 113 uint64_t unsigned_full_result = static_cast<uint64_t>(unsigned_arg1) + unsigned_arg2; - 114 CF = (unsigned_result != unsigned_full_result); - 115 trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); + 114 CF = (unsigned_result != unsigned_full_result); + 115 trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); 116 *signed_arg1 = signed_result; - 117 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *signed_arg1 << end(); + 117 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *signed_arg1 << end(); 118 break; 119 } 120 // End Op 81 Subops 121 default: - 122 cerr << "unrecognized subop for opcode 81: " << NUM(subop) << '\n'; + 122 cerr << "unrecognized subop for opcode 81: " << NUM(subop) << '\n'; 123 exit(1); 124 } 125 break; @@ -188,8 +188,8 @@ if ('onhashchange' in window) { 127 128 :(code) 129 void test_add_imm32_to_r32_signed_overflow() { - 130 Reg[EBX].i = 0x7fffffff; // largest positive signed integer - 131 run( + 130 Reg[EBX].i = 0x7fffffff; // largest positive signed integer + 131 run( 132 "== code 0x1\n" 133 // op ModR/M SIB displacement immediate 134 " 81 c3 01 00 00 00\n" // add 1 to EBX @@ -206,8 +206,8 @@ if ('onhashchange' in window) { 145 } 146 147 void test_add_imm32_to_r32_unsigned_overflow() { - 148 Reg[EBX].u = 0xffffffff; // largest unsigned number - 149 run( + 148 Reg[EBX].u = 0xffffffff; // largest unsigned number + 149 run( 150 "== code 0x1\n" 151 // op ModR/M SIB displacement immediate 152 " 81 c3 01 00 00 00\n" // add 1 to EBX @@ -224,8 +224,8 @@ if ('onhashchange' in window) { 163 } 164 165 void test_add_imm32_to_r32_unsigned_and_signed_overflow() { - 166 Reg[EBX].u = 0x80000000; // smallest negative signed integer - 167 run( + 166 Reg[EBX].u = 0x80000000; // smallest negative signed integer + 167 run( 168 "== code 0x1\n" 169 // op ModR/M SIB displacement immediate 170 " 81 c3 00 00 00 80\n" // add 0x80000000 to EBX @@ -245,8 +245,8 @@ if ('onhashchange' in window) { 184 185 :(code) 186 void test_add_imm32_to_mem_at_r32() { - 187 Reg[EBX].i = 0x2000; - 188 run( + 187 Reg[EBX].i = 0x2000; + 188 run( 189 "== code 0x1\n" 190 // op ModR/M SIB displacement immediate 191 " 81 03 0a 0b 0c 0d \n" // add 0x0d0c0b0a to *EBX @@ -256,7 +256,7 @@ if ('onhashchange' in window) { 195 ); 196 CHECK_TRACE_CONTENTS( 197 "run: combine r/m32 with imm32\n" - 198 "run: effective address is 0x00002000 (EBX)\n" + 198 "run: effective address is 0x00002000 (EBX)\n" 199 "run: imm32 is 0x0d0c0b0a\n" 200 "run: subop add\n" 201 "run: storing 0x0d0c0b0b\n" @@ -266,12 +266,12 @@ if ('onhashchange' in window) { 205 //:: subtract 206 207 :(before "End Initialize Op Names") - 208 put_new(Name, "2d", "subtract imm32 from EAX (sub)"); + 208 put_new(Name, "2d", "subtract imm32 from EAX (sub)"); 209 210 :(code) 211 void test_subtract_imm32_from_EAX() { - 212 Reg[EAX].i = 0x0d0c0baa; - 213 run( + 212 Reg[EAX].i = 0x0d0c0baa; + 213 run( 214 "== code 0x1\n" 215 // op ModR/M SIB displacement immediate 216 " 2d 0a 0b 0c 0d \n" // subtract 0x0d0c0b0a from EAX @@ -284,28 +284,28 @@ if ('onhashchange' in window) { 223 224 :(before "End Single-Byte Opcodes") 225 case 0x2d: { // subtract imm32 from EAX - 226 const int32_t signed_arg2 = next32(); - 227 trace(Callstack_depth+1, "run") << "subtract imm32 0x" << HEXWORD << signed_arg2 << " from EAX" << end(); - 228 int32_t signed_result = Reg[EAX].i - signed_arg2; + 226 const int32_t signed_arg2 = next32(); + 227 trace(Callstack_depth+1, "run") << "subtract imm32 0x" << HEXWORD << signed_arg2 << " from EAX" << end(); + 228 int32_t signed_result = Reg[EAX].i - signed_arg2; 229 SF = (signed_result < 0); - 230 ZF = (signed_result == 0); - 231 int64_t signed_full_result = static_cast<int64_t>(Reg[EAX].i) - signed_arg2; - 232 OF = (signed_result != signed_full_result); + 230 ZF = (signed_result == 0); + 231 int64_t signed_full_result = static_cast<int64_t>(Reg[EAX].i) - signed_arg2; + 232 OF = (signed_result != signed_full_result); 233 // set CF 234 uint32_t unsigned_arg2 = static_cast<uint32_t>(signed_arg2); - 235 uint32_t unsigned_result = Reg[EAX].u - unsigned_arg2; - 236 uint64_t unsigned_full_result = static_cast<uint64_t>(Reg[EAX].u) - unsigned_arg2; - 237 CF = (unsigned_result != unsigned_full_result); - 238 trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); - 239 Reg[EAX].i = signed_result; - 240 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << Reg[EAX].i << end(); + 235 uint32_t unsigned_result = Reg[EAX].u - unsigned_arg2; + 236 uint64_t unsigned_full_result = static_cast<uint64_t>(Reg[EAX].u) - unsigned_arg2; + 237 CF = (unsigned_result != unsigned_full_result); + 238 trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); + 239 Reg[EAX].i = signed_result; + 240 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << Reg[EAX].i << end(); 241 break; 242 } 243 244 :(code) 245 void test_subtract_imm32_from_EAX_signed_overflow() { - 246 Reg[EAX].i = 0x80000000; // smallest negative signed integer - 247 run( + 246 Reg[EAX].i = 0x80000000; // smallest negative signed integer + 247 run( 248 "== code 0x1\n" 249 // op ModR/M SIB displacement immediate 250 " 2d ff ff ff 7f \n" // subtract largest positive signed integer from EAX @@ -318,8 +318,8 @@ if ('onhashchange' in window) { 257 } 258 259 void test_subtract_imm32_from_EAX_unsigned_overflow() { - 260 Reg[EAX].i = 0; - 261 run( + 260 Reg[EAX].i = 0; + 261 run( 262 "== code 0x1\n" 263 // op ModR/M SIB displacement immediate 264 " 2d 01 00 00 00 \n" // subtract 1 from EAX @@ -332,8 +332,8 @@ if ('onhashchange' in window) { 271 } 272 273 void test_subtract_imm32_from_EAX_signed_and_unsigned_overflow() { - 274 Reg[EAX].i = 0; - 275 run( + 274 Reg[EAX].i = 0; + 275 run( 276 "== code 0x1\n" 277 // op ModR/M SIB displacement immediate 278 " 2d 00 00 00 80 \n" // subtract smallest negative signed integer from EAX @@ -348,8 +348,8 @@ if ('onhashchange' in window) { 287 //: 288 289 void test_subtract_imm32_from_mem_at_r32() { - 290 Reg[EBX].i = 0x2000; - 291 run( + 290 Reg[EBX].i = 0x2000; + 291 run( 292 "== code 0x1\n" 293 // op ModR/M SIB displacement immediate 294 " 81 2b 01 00 00 00 \n" // subtract 1 from *EBX @@ -359,7 +359,7 @@ if ('onhashchange' in window) { 298 ); 299 CHECK_TRACE_CONTENTS( 300 "run: combine r/m32 with imm32\n" - 301 "run: effective address is 0x00002000 (EBX)\n" + 301 "run: effective address is 0x00002000 (EBX)\n" 302 "run: imm32 is 0x00000001\n" 303 "run: subop subtract\n" 304 "run: storing 0x00000009\n" @@ -371,25 +371,25 @@ if ('onhashchange' in window) { 310 trace(Callstack_depth+1, "run") << "subop subtract" << end(); 311 int32_t signed_result = *signed_arg1 - signed_arg2; 312 SF = (signed_result < 0); - 313 ZF = (signed_result == 0); + 313 ZF = (signed_result == 0); 314 int64_t signed_full_result = static_cast<int64_t>(*signed_arg1) - signed_arg2; - 315 OF = (signed_result != signed_full_result); + 315 OF = (signed_result != signed_full_result); 316 // set CF 317 uint32_t unsigned_arg1 = static_cast<uint32_t>(*signed_arg1); 318 uint32_t unsigned_arg2 = static_cast<uint32_t>(signed_arg2); 319 uint32_t unsigned_result = unsigned_arg1 - unsigned_arg2; 320 uint64_t unsigned_full_result = static_cast<uint64_t>(unsigned_arg1) - unsigned_arg2; - 321 CF = (unsigned_result != unsigned_full_result); - 322 trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); + 321 CF = (unsigned_result != unsigned_full_result); + 322 trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); 323 *signed_arg1 = signed_result; - 324 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *signed_arg1 << end(); + 324 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *signed_arg1 << end(); 325 break; 326 } 327 328 :(code) 329 void test_subtract_imm32_from_mem_at_r32_signed_overflow() { - 330 Reg[EBX].i = 0x2000; - 331 run( + 330 Reg[EBX].i = 0x2000; + 331 run( 332 "== code 0x1\n" 333 // op ModR/M SIB displacement immediate 334 " 81 2b ff ff ff 7f \n" // subtract largest positive signed integer from *EBX @@ -399,8 +399,8 @@ if ('onhashchange' in window) { 338 ); 339 CHECK_TRACE_CONTENTS( 340 "run: combine r/m32 with imm32\n" - 341 "run: effective address is 0x00002000 (EBX)\n" - 342 "run: effective address contains 80000000\n" + 341 "run: effective address is 0x00002000 (EBX)\n" + 342 "run: effective address contains 0x80000000\n" 343 "run: imm32 is 0x7fffffff\n" 344 "run: subop subtract\n" 345 "run: SF=0; ZF=0; CF=0; OF=1\n" @@ -409,8 +409,8 @@ if ('onhashchange' in window) { 348 } 349 350 void test_subtract_imm32_from_mem_at_r32_unsigned_overflow() { - 351 Reg[EBX].i = 0x2000; - 352 run( + 351 Reg[EBX].i = 0x2000; + 352 run( 353 "== code 0x1\n" 354 // op ModR/M SIB displacement immediate 355 " 81 2b 01 00 00 00 \n" // subtract 1 from *EBX @@ -420,8 +420,8 @@ if ('onhashchange' in window) { 359 ); 360 CHECK_TRACE_CONTENTS( 361 "run: combine r/m32 with imm32\n" - 362 "run: effective address is 0x00002000 (EBX)\n" - 363 "run: effective address contains 0\n" + 362 "run: effective address is 0x00002000 (EBX)\n" + 363 "run: effective address contains 0x00000000\n" 364 "run: imm32 is 0x00000001\n" 365 "run: subop subtract\n" 366 "run: SF=1; ZF=0; CF=1; OF=0\n" @@ -430,8 +430,8 @@ if ('onhashchange' in window) { 369 } 370 371 void test_subtract_imm32_from_mem_at_r32_signed_and_unsigned_overflow() { - 372 Reg[EBX].i = 0x2000; - 373 run( + 372 Reg[EBX].i = 0x2000; + 373 run( 374 "== code 0x1\n" 375 // op ModR/M SIB displacement immediate 376 " 81 2b 00 00 00 80 \n" // subtract smallest negative signed integer from *EBX @@ -441,8 +441,8 @@ if ('onhashchange' in window) { 380 ); 381 CHECK_TRACE_CONTENTS( 382 "run: combine r/m32 with imm32\n" - 383 "run: effective address is 0x00002000 (EBX)\n" - 384 "run: effective address contains 0\n" + 383 "run: effective address is 0x00002000 (EBX)\n" + 384 "run: effective address contains 0x00000000\n" 385 "run: imm32 is 0x80000000\n" 386 "run: subop subtract\n" 387 "run: SF=1; ZF=0; CF=1; OF=1\n" @@ -453,8 +453,8 @@ if ('onhashchange' in window) { 392 //: 393 394 void test_subtract_imm32_from_r32() { - 395 Reg[EBX].i = 10; - 396 run( + 395 Reg[EBX].i = 10; + 396 run( 397 "== code 0x1\n" 398 // op ModR/M SIB displacement immediate 399 " 81 eb 01 00 00 00 \n" // subtract 1 from EBX @@ -472,12 +472,12 @@ if ('onhashchange' in window) { 411 //:: shift left 412 413 :(before "End Initialize Op Names") - 414 put_new(Name, "c1", "shift rm32 by imm8 bits depending on subop (sal/sar/shl/shr)"); + 414 put_new(Name, "c1", "shift rm32 by imm8 bits depending on subop (sal/sar/shl/shr)"); 415 416 :(code) 417 void test_shift_left_r32_with_imm8() { - 418 Reg[EBX].i = 13; - 419 run( + 418 Reg[EBX].i = 13; + 419 run( 420 "== code 0x1\n" 421 // op ModR/M SIB displacement immediate 422 " c1 e3 01 \n" // shift EBX left by 1 bit @@ -493,31 +493,31 @@ if ('onhashchange' in window) { 432 433 :(before "End Single-Byte Opcodes") 434 case 0xc1: { - 435 const uint8_t modrm = next(); + 435 const uint8_t modrm = next(); 436 trace(Callstack_depth+1, "run") << "operate on r/m32" << end(); 437 int32_t* arg1 = effective_address(modrm); 438 const uint8_t subop = (modrm>>3)&0x7; // middle 3 'reg opcode' bits 439 switch (subop) { 440 case 4: { // shift left r/m32 by CL 441 trace(Callstack_depth+1, "run") << "subop: shift left by CL bits" << end(); - 442 uint8_t count = next() & 0x1f; + 442 uint8_t count = next() & 0x1f; 443 // OF is only defined if count is 1 444 if (count == 1) { 445 bool msb = (*arg1 & 0x80000000) >> 1; 446 bool pnsb = (*arg1 & 0x40000000); - 447 OF = (msb != pnsb); + 447 OF = (msb != pnsb); 448 } 449 *arg1 = (*arg1 << count); - 450 ZF = (*arg1 == 0); + 450 ZF = (*arg1 == 0); 451 SF = (*arg1 < 0); 452 // CF undefined - 453 trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); - 454 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *arg1 << end(); + 453 trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); + 454 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *arg1 << end(); 455 break; 456 } 457 // End Op c1 Subops 458 default: - 459 cerr << "unrecognized subop for opcode c1: " << NUM(subop) << '\n'; + 459 cerr << "unrecognized subop for opcode c1: " << NUM(subop) << '\n'; 460 exit(1); 461 } 462 break; @@ -527,8 +527,8 @@ if ('onhashchange' in window) { 466 467 :(code) 468 void test_shift_right_arithmetic_r32_with_imm8() { - 469 Reg[EBX].i = 26; - 470 run( + 469 Reg[EBX].i = 26; + 470 run( 471 "== code 0x1\n" 472 // op ModR/M SIB displacement immediate 473 " c1 fb 01 \n" // shift EBX right by 1 bit @@ -545,24 +545,24 @@ if ('onhashchange' in window) { 484 :(before "End Op c1 Subops") 485 case 7: { // shift right r/m32 by CL, preserving sign 486 trace(Callstack_depth+1, "run") << "subop: shift right by CL bits, while preserving sign" << end(); - 487 uint8_t count = next() & 0x1f; + 487 uint8_t count = next() & 0x1f; 488 int32_t result = (*arg1 >> count); - 489 ZF = (*arg1 == 0); + 489 ZF = (*arg1 == 0); 490 SF = (*arg1 < 0); 491 // OF is only defined if count is 1 - 492 if (count == 1) OF = false; + 492 if (count == 1) OF = false; 493 // CF - 494 CF = ((*arg1 >> (count-1)) & 0x1); - 495 trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); + 494 CF = ((*arg1 >> (count-1)) & 0x1); + 495 trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); 496 *arg1 = result; - 497 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *arg1 << end(); + 497 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *arg1 << end(); 498 break; 499 } 500 501 :(code) 502 void test_shift_right_arithmetic_odd_r32_with_imm8() { - 503 Reg[EBX].i = 27; - 504 run( + 503 Reg[EBX].i = 27; + 504 run( 505 "== code 0x1\n" 506 // op ModR/M SIB displacement immediate 507 " c1 fb 01 \n" // shift EBX right by 1 bit @@ -579,8 +579,8 @@ if ('onhashchange' in window) { 518 519 :(code) 520 void test_shift_right_arithmetic_negative_r32_with_imm8() { - 521 Reg[EBX].i = 0xfffffffd; // -3 - 522 run( + 521 Reg[EBX].i = 0xfffffffd; // -3 + 522 run( 523 "== code 0x1\n" 524 // op ModR/M SIB displacement immediate 525 " c1 fb 01 \n" // shift EBX right by 1 bit, while preserving sign @@ -599,8 +599,8 @@ if ('onhashchange' in window) { 538 539 :(code) 540 void test_shift_right_logical_r32_with_imm8() { - 541 Reg[EBX].i = 26; - 542 run( + 541 Reg[EBX].i = 26; + 542 run( 543 "== code 0x1\n" 544 // op ModR/M SIB displacement immediate 545 " c1 eb 01 \n" // shift EBX right by 1 bit, while padding zeroes @@ -617,28 +617,28 @@ if ('onhashchange' in window) { 556 :(before "End Op c1 Subops") 557 case 5: { // shift right r/m32 by CL, preserving sign 558 trace(Callstack_depth+1, "run") << "subop: shift right by CL bits, while padding zeroes" << end(); - 559 uint8_t count = next() & 0x1f; + 559 uint8_t count = next() & 0x1f; 560 // OF is only defined if count is 1 561 if (count == 1) { 562 bool msb = (*arg1 & 0x80000000) >> 1; 563 bool pnsb = (*arg1 & 0x40000000); - 564 OF = (msb != pnsb); + 564 OF = (msb != pnsb); 565 } 566 uint32_t* uarg1 = reinterpret_cast<uint32_t*>(arg1); 567 *uarg1 = (*uarg1 >> count); - 568 ZF = (*uarg1 == 0); + 568 ZF = (*uarg1 == 0); 569 // result is always positive by definition 570 SF = false; 571 // CF undefined - 572 trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); - 573 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *arg1 << end(); + 572 trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); + 573 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *arg1 << end(); 574 break; 575 } 576 577 :(code) 578 void test_shift_right_logical_odd_r32_with_imm8() { - 579 Reg[EBX].i = 27; - 580 run( + 579 Reg[EBX].i = 27; + 580 run( 581 "== code 0x1\n" 582 // op ModR/M SIB displacement immediate 583 " c1 eb 01 \n" // shift EBX right by 1 bit, while padding zeroes @@ -654,8 +654,8 @@ if ('onhashchange' in window) { 593 594 :(code) 595 void test_shift_right_logical_negative_r32_with_imm8() { - 596 Reg[EBX].i = 0xfffffffd; - 597 run( + 596 Reg[EBX].i = 0xfffffffd; + 597 run( 598 "== code 0x1\n" 599 // op ModR/M SIB displacement immediate 600 " c1 eb 01 \n" // shift EBX right by 1 bit, while padding zeroes @@ -672,12 +672,12 @@ if ('onhashchange' in window) { 611 //:: and 612 613 :(before "End Initialize Op Names") - 614 put_new(Name, "25", "EAX = bitwise AND of imm32 with EAX (and)"); + 614 put_new(Name, "25", "EAX = bitwise AND of imm32 with EAX (and)"); 615 616 :(code) 617 void test_and_EAX_with_imm32() { - 618 Reg[EAX].i = 0xff; - 619 run( + 618 Reg[EAX].i = 0xff; + 619 run( 620 "== code 0x1\n" 621 // op ModR/M SIB displacement immediate 622 " 25 0a 0b 0c 0d \n" // and 0x0d0c0b0a with EAX @@ -692,15 +692,15 @@ if ('onhashchange' in window) { 631 case 0x25: { // and imm32 with EAX 632 // bitwise ops technically operate on unsigned numbers, but it makes no 633 // difference - 634 const int32_t signed_arg2 = next32(); - 635 trace(Callstack_depth+1, "run") << "and imm32 0x" << HEXWORD << signed_arg2 << " with EAX" << end(); - 636 Reg[EAX].i &= signed_arg2; - 637 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << Reg[EAX].i << end(); - 638 SF = (Reg[EAX].i >> 31); - 639 ZF = (Reg[EAX].i == 0); - 640 CF = false; - 641 OF = false; - 642 trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); + 634 const int32_t signed_arg2 = next32(); + 635 trace(Callstack_depth+1, "run") << "and imm32 0x" << HEXWORD << signed_arg2 << " with EAX" << end(); + 636 Reg[EAX].i &= signed_arg2; + 637 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << Reg[EAX].i << end(); + 638 SF = (Reg[EAX].i >> 31); + 639 ZF = (Reg[EAX].i == 0); + 640 CF = false; + 641 OF = false; + 642 trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); 643 break; 644 } 645 @@ -708,8 +708,8 @@ if ('onhashchange' in window) { 647 648 :(code) 649 void test_and_imm32_with_mem_at_r32() { - 650 Reg[EBX].i = 0x2000; - 651 run( + 650 Reg[EBX].i = 0x2000; + 651 run( 652 "== code 0x1\n" 653 // op ModR/M SIB displacement immediate 654 " 81 23 0a 0b 0c 0d \n" // and 0x0d0c0b0a with *EBX @@ -719,7 +719,7 @@ if ('onhashchange' in window) { 658 ); 659 CHECK_TRACE_CONTENTS( 660 "run: combine r/m32 with imm32\n" - 661 "run: effective address is 0x00002000 (EBX)\n" + 661 "run: effective address is 0x00002000 (EBX)\n" 662 "run: imm32 is 0x0d0c0b0a\n" 663 "run: subop and\n" 664 "run: storing 0x0000000a\n" @@ -732,12 +732,12 @@ if ('onhashchange' in window) { 671 // bitwise ops technically operate on unsigned numbers, but it makes no 672 // difference 673 *signed_arg1 &= signed_arg2; - 674 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *signed_arg1 << end(); + 674 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *signed_arg1 << end(); 675 SF = (*signed_arg1 >> 31); - 676 ZF = (*signed_arg1 == 0); - 677 CF = false; - 678 OF = false; - 679 trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); + 676 ZF = (*signed_arg1 == 0); + 677 CF = false; + 678 OF = false; + 679 trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); 680 break; 681 } 682 @@ -745,8 +745,8 @@ if ('onhashchange' in window) { 684 685 :(code) 686 void test_and_imm32_with_r32() { - 687 Reg[EBX].i = 0xff; - 688 run( + 687 Reg[EBX].i = 0xff; + 688 run( 689 "== code 0x1\n" 690 // op ModR/M SIB displacement immediate 691 " 81 e3 0a 0b 0c 0d \n" // and 0x0d0c0b0a with EBX @@ -764,12 +764,12 @@ if ('onhashchange' in window) { 703 //:: or 704 705 :(before "End Initialize Op Names") - 706 put_new(Name, "0d", "EAX = bitwise OR of imm32 with EAX (or)"); + 706 put_new(Name, "0d", "EAX = bitwise OR of imm32 with EAX (or)"); 707 708 :(code) 709 void test_or_EAX_with_imm32() { - 710 Reg[EAX].i = 0xd0c0b0a0; - 711 run( + 710 Reg[EAX].i = 0xd0c0b0a0; + 711 run( 712 "== code 0x1\n" 713 // op ModR/M SIB displacement immediate 714 " 0d 0a 0b 0c 0d \n" // or 0x0d0c0b0a with EAX @@ -784,15 +784,15 @@ if ('onhashchange' in window) { 723 case 0x0d: { // or imm32 with EAX 724 // bitwise ops technically operate on unsigned numbers, but it makes no 725 // difference - 726 const int32_t signed_arg2 = next32(); - 727 trace(Callstack_depth+1, "run") << "or imm32 0x" << HEXWORD << signed_arg2 << " with EAX" << end(); - 728 Reg[EAX].i |= signed_arg2; - 729 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << Reg[EAX].i << end(); - 730 SF = (Reg[EAX].i >> 31); - 731 ZF = (Reg[EAX].i == 0); - 732 CF = false; - 733 OF = false; - 734 trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); + 726 const int32_t signed_arg2 = next32(); + 727 trace(Callstack_depth+1, "run") << "or imm32 0x" << HEXWORD << signed_arg2 << " with EAX" << end(); + 728 Reg[EAX].i |= signed_arg2; + 729 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << Reg[EAX].i << end(); + 730 SF = (Reg[EAX].i >> 31); + 731 ZF = (Reg[EAX].i == 0); + 732 CF = false; + 733 OF = false; + 734 trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); 735 break; 736 } 737 @@ -800,8 +800,8 @@ if ('onhashchange' in window) { 739 740 :(code) 741 void test_or_imm32_with_mem_at_r32() { - 742 Reg[EBX].i = 0x2000; - 743 run( + 742 Reg[EBX].i = 0x2000; + 743 run( 744 "== code 0x1\n" 745 // op ModR/M SIB displacement immediate 746 " 81 0b 0a 0b 0c 0d \n" // or 0x0d0c0b0a with *EBX @@ -811,7 +811,7 @@ if ('onhashchange' in window) { 750 ); 751 CHECK_TRACE_CONTENTS( 752 "run: combine r/m32 with imm32\n" - 753 "run: effective address is 0x00002000 (EBX)\n" + 753 "run: effective address is 0x00002000 (EBX)\n" 754 "run: imm32 is 0x0d0c0b0a\n" 755 "run: subop or\n" 756 "run: storing 0xddccbbaa\n" @@ -824,19 +824,19 @@ if ('onhashchange' in window) { 763 // bitwise ops technically operate on unsigned numbers, but it makes no 764 // difference 765 *signed_arg1 |= signed_arg2; - 766 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *signed_arg1 << end(); + 766 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *signed_arg1 << end(); 767 SF = (*signed_arg1 >> 31); - 768 ZF = (*signed_arg1 == 0); - 769 CF = false; - 770 OF = false; - 771 trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); + 768 ZF = (*signed_arg1 == 0); + 769 CF = false; + 770 OF = false; + 771 trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); 772 break; 773 } 774 775 :(code) 776 void test_or_imm32_with_r32() { - 777 Reg[EBX].i = 0xd0c0b0a0; - 778 run( + 777 Reg[EBX].i = 0xd0c0b0a0; + 778 run( 779 "== code 0x1\n" 780 // op ModR/M SIB displacement immediate 781 " 81 cb 0a 0b 0c 0d \n" // or 0x0d0c0b0a with EBX @@ -854,12 +854,12 @@ if ('onhashchange' in window) { 793 //:: xor 794 795 :(before "End Initialize Op Names") - 796 put_new(Name, "35", "EAX = bitwise XOR of imm32 with EAX (xor)"); + 796 put_new(Name, "35", "EAX = bitwise XOR of imm32 with EAX (xor)"); 797 798 :(code) 799 void test_xor_EAX_with_imm32() { - 800 Reg[EAX].i = 0xddccb0a0; - 801 run( + 800 Reg[EAX].i = 0xddccb0a0; + 801 run( 802 "== code 0x1\n" 803 // op ModR/M SIB displacement immediate 804 " 35 0a 0b 0c 0d \n" // xor 0x0d0c0b0a with EAX @@ -874,15 +874,15 @@ if ('onhashchange' in window) { 813 case 0x35: { // xor imm32 with EAX 814 // bitwise ops technically operate on unsigned numbers, but it makes no 815 // difference - 816 const int32_t signed_arg2 = next32(); - 817 trace(Callstack_depth+1, "run") << "xor imm32 0x" << HEXWORD << signed_arg2 << " with EAX" << end(); - 818 Reg[EAX].i ^= signed_arg2; - 819 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << Reg[EAX].i << end(); - 820 SF = (Reg[EAX].i >> 31); - 821 ZF = (Reg[EAX].i == 0); - 822 CF = false; - 823 OF = false; - 824 trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); + 816 const int32_t signed_arg2 = next32(); + 817 trace(Callstack_depth+1, "run") << "xor imm32 0x" << HEXWORD << signed_arg2 << " with EAX" << end(); + 818 Reg[EAX].i ^= signed_arg2; + 819 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << Reg[EAX].i << end(); + 820 SF = (Reg[EAX].i >> 31); + 821 ZF = (Reg[EAX].i == 0); + 822 CF = false; + 823 OF = false; + 824 trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); 825 break; 826 } 827 @@ -890,8 +890,8 @@ if ('onhashchange' in window) { 829 830 :(code) 831 void test_xor_imm32_with_mem_at_r32() { - 832 Reg[EBX].i = 0x2000; - 833 run( + 832 Reg[EBX].i = 0x2000; + 833 run( 834 "== code 0x1\n" 835 // op ModR/M SIB displacement immediate 836 " 81 33 0a 0b 0c 0d \n" // xor 0x0d0c0b0a with *EBX @@ -901,7 +901,7 @@ if ('onhashchange' in window) { 840 ); 841 CHECK_TRACE_CONTENTS( 842 "run: combine r/m32 with imm32\n" - 843 "run: effective address is 0x00002000 (EBX)\n" + 843 "run: effective address is 0x00002000 (EBX)\n" 844 "run: imm32 is 0x0d0c0b0a\n" 845 "run: subop xor\n" 846 "run: storing 0xddccbbaa\n" @@ -914,19 +914,19 @@ if ('onhashchange' in window) { 853 // bitwise ops technically operate on unsigned numbers, but it makes no 854 // difference 855 *signed_arg1 ^= signed_arg2; - 856 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *signed_arg1 << end(); + 856 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *signed_arg1 << end(); 857 SF = (*signed_arg1 >> 31); - 858 ZF = (*signed_arg1 == 0); - 859 CF = false; - 860 OF = false; - 861 trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); + 858 ZF = (*signed_arg1 == 0); + 859 CF = false; + 860 OF = false; + 861 trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); 862 break; 863 } 864 865 :(code) 866 void test_xor_imm32_with_r32() { - 867 Reg[EBX].i = 0xd0c0b0a0; - 868 run( + 867 Reg[EBX].i = 0xd0c0b0a0; + 868 run( 869 "== code 0x1\n" 870 // op ModR/M SIB displacement immediate 871 " 81 f3 0a 0b 0c 0d \n" // xor 0x0d0c0b0a with EBX @@ -944,103 +944,103 @@ if ('onhashchange' in window) { 883 //:: compare (cmp) 884 885 :(before "End Initialize Op Names") - 886 put_new(Name, "3d", "compare: set SF if EAX < imm32 (cmp)"); + 886 put_new(Name, "3d", "compare: set SF if EAX < imm32 (cmp)"); 887 888 :(code) 889 void test_compare_EAX_with_imm32_greater() { - 890 Reg[EAX].i = 0x0d0c0b0a; - 891 run( + 890 Reg[EAX].i = 0x0d0c0b0a; + 891 run( 892 "== code 0x1\n" 893 // op ModR/M SIB displacement immediate 894 " 3d 07 0b 0c 0d \n" // compare EAX with 0x0d0c0b07 895 ); 896 CHECK_TRACE_CONTENTS( - 897 "run: compare EAX with imm32 0x0d0c0b07\n" + 897 "run: compare EAX with imm32 0x0d0c0b07\n" 898 "run: SF=0; ZF=0; CF=0; OF=0\n" 899 ); 900 } 901 902 :(before "End Single-Byte Opcodes") 903 case 0x3d: { // compare EAX with imm32 - 904 const int32_t signed_arg1 = Reg[EAX].i; - 905 const int32_t signed_arg2 = next32(); - 906 trace(Callstack_depth+1, "run") << "compare EAX with imm32 0x" << HEXWORD << signed_arg2 << end(); + 904 const int32_t signed_arg1 = Reg[EAX].i; + 905 const int32_t signed_arg2 = next32(); + 906 trace(Callstack_depth+1, "run") << "compare EAX with imm32 0x" << HEXWORD << signed_arg2 << end(); 907 const int32_t signed_difference = signed_arg1 - signed_arg2; 908 SF = (signed_difference < 0); - 909 ZF = (signed_difference == 0); + 909 ZF = (signed_difference == 0); 910 const int64_t full_signed_difference = static_cast<int64_t>(signed_arg1) - signed_arg2; - 911 OF = (signed_difference != full_signed_difference); + 911 OF = (signed_difference != full_signed_difference); 912 const uint32_t unsigned_arg1 = static_cast<uint32_t>(signed_arg1); 913 const uint32_t unsigned_arg2 = static_cast<uint32_t>(signed_arg2); 914 const uint32_t unsigned_difference = unsigned_arg1 - unsigned_arg2; 915 const uint64_t full_unsigned_difference = static_cast<uint64_t>(unsigned_arg1) - unsigned_arg2; - 916 CF = (unsigned_difference != full_unsigned_difference); - 917 trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); + 916 CF = (unsigned_difference != full_unsigned_difference); + 917 trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); 918 break; 919 } 920 921 :(code) 922 void test_compare_EAX_with_imm32_lesser_unsigned_and_signed() { - 923 Reg[EAX].i = 0x0a0b0c07; - 924 run( + 923 Reg[EAX].i = 0x0a0b0c07; + 924 run( 925 "== code 0x1\n" 926 // op ModR/M SIB displacement immediate 927 " 3d 0d 0c 0b 0a \n" // compare EAX with imm32 928 ); 929 CHECK_TRACE_CONTENTS( - 930 "run: compare EAX with imm32 0x0a0b0c0d\n" + 930 "run: compare EAX with imm32 0x0a0b0c0d\n" 931 "run: SF=1; ZF=0; CF=1; OF=0\n" 932 ); 933 } 934 935 void test_compare_EAX_with_imm32_lesser_unsigned_and_signed_due_to_overflow() { - 936 Reg[EAX].i = 0x7fffffff; // largest positive signed integer - 937 run( + 936 Reg[EAX].i = 0x7fffffff; // largest positive signed integer + 937 run( 938 "== code 0x1\n" 939 // op ModR/M SIB displacement immediate 940 " 3d 00 00 00 80\n" // compare EAX with smallest negative signed integer 941 ); 942 CHECK_TRACE_CONTENTS( - 943 "run: compare EAX with imm32 0x80000000\n" + 943 "run: compare EAX with imm32 0x80000000\n" 944 "run: SF=1; ZF=0; CF=1; OF=1\n" 945 ); 946 } 947 948 void test_compare_EAX_with_imm32_lesser_signed() { - 949 Reg[EAX].i = 0xffffffff; // -1 - 950 run( + 949 Reg[EAX].i = 0xffffffff; // -1 + 950 run( 951 "== code 0x1\n" 952 // op ModR/M SIB displacement immediate 953 " 3d 01 00 00 00\n" // compare EAX with 1 954 ); 955 CHECK_TRACE_CONTENTS( - 956 "run: compare EAX with imm32 0x00000001\n" + 956 "run: compare EAX with imm32 0x00000001\n" 957 "run: SF=1; ZF=0; CF=0; OF=0\n" 958 ); 959 } 960 961 void test_compare_EAX_with_imm32_lesser_unsigned() { - 962 Reg[EAX].i = 0x00000001; // 1 - 963 run( + 962 Reg[EAX].i = 0x00000001; // 1 + 963 run( 964 "== code 0x1\n" 965 // op ModR/M SIB displacement immediate 966 " 3d ff ff ff ff\n" // compare EAX with -1 967 ); 968 CHECK_TRACE_CONTENTS( - 969 "run: compare EAX with imm32 0xffffffff\n" + 969 "run: compare EAX with imm32 0xffffffff\n" 970 "run: SF=0; ZF=0; CF=1; OF=0\n" 971 ); 972 } 973 974 void test_compare_EAX_with_imm32_equal() { - 975 Reg[EAX].i = 0x0d0c0b0a; - 976 run( + 975 Reg[EAX].i = 0x0d0c0b0a; + 976 run( 977 "== code 0x1\n" 978 // op ModR/M SIB displacement immediate 979 " 3d 0a 0b 0c 0d \n" // compare 0x0d0c0b0a with EAX 980 ); 981 CHECK_TRACE_CONTENTS( - 982 "run: compare EAX with imm32 0x0d0c0b0a\n" + 982 "run: compare EAX with imm32 0x0d0c0b0a\n" 983 "run: SF=0; ZF=1; CF=0; OF=0\n" 984 ); 985 } @@ -1048,8 +1048,8 @@ if ('onhashchange' in window) { 987 //: 988 989 void test_compare_imm32_with_r32_greater() { - 990 Reg[EBX].i = 0x0d0c0b0a; - 991 run( + 990 Reg[EBX].i = 0x0d0c0b0a; + 991 run( 992 "== code 0x1\n" 993 // op ModR/M SIB displacement immediate 994 " 81 fb 07 0b 0c 0d \n" // compare 0x0d0c0b07 with EBX @@ -1068,22 +1068,22 @@ if ('onhashchange' in window) { 1007 trace(Callstack_depth+1, "run") << "subop compare" << end(); 1008 const int32_t tmp1 = *signed_arg1 - signed_arg2; 1009 SF = (tmp1 < 0); -1010 ZF = (tmp1 == 0); +1010 ZF = (tmp1 == 0); 1011 const int64_t tmp2 = static_cast<int64_t>(*signed_arg1) - signed_arg2; -1012 OF = (tmp1 != tmp2); +1012 OF = (tmp1 != tmp2); 1013 const uint32_t unsigned_arg1 = static_cast<uint32_t>(*signed_arg1); 1014 const uint32_t unsigned_arg2 = static_cast<uint32_t>(signed_arg2); 1015 const uint32_t tmp3 = unsigned_arg1 - unsigned_arg2; 1016 const uint64_t tmp4 = static_cast<uint64_t>(unsigned_arg1) - unsigned_arg2; -1017 CF = (tmp3 != tmp4); -1018 trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); +1017 CF = (tmp3 != tmp4); +1018 trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); 1019 break; 1020 } 1021 1022 :(code) 1023 void test_compare_rm32_with_imm32_lesser_unsigned_and_signed() { -1024 Reg[EAX].i = 0x0a0b0c07; -1025 run( +1024 Reg[EAX].i = 0x0a0b0c07; +1025 run( 1026 "== code 0x1\n" 1027 // op ModR/M SIB displacement immediate 1028 " 81 f8 0d 0c 0b 0a \n" // compare EAX with imm32 @@ -1099,8 +1099,8 @@ if ('onhashchange' in window) { 1038 } 1039 1040 void test_compare_rm32_with_imm32_lesser_unsigned_and_signed_due_to_overflow() { -1041 Reg[EAX].i = 0x7fffffff; // largest positive signed integer -1042 run( +1041 Reg[EAX].i = 0x7fffffff; // largest positive signed integer +1042 run( 1043 "== code 0x1\n" 1044 // op ModR/M SIB displacement immediate 1045 " 81 f8 00 00 00 80\n" // compare EAX with smallest negative signed integer @@ -1116,8 +1116,8 @@ if ('onhashchange' in window) { 1055 } 1056 1057 void test_compare_rm32_with_imm32_lesser_signed() { -1058 Reg[EAX].i = 0xffffffff; // -1 -1059 run( +1058 Reg[EAX].i = 0xffffffff; // -1 +1059 run( 1060 "== code 0x1\n" 1061 // op ModR/M SIB displacement immediate 1062 " 81 f8 01 00 00 00\n" // compare EAX with 1 @@ -1133,8 +1133,8 @@ if ('onhashchange' in window) { 1072 } 1073 1074 void test_compare_rm32_with_imm32_lesser_unsigned() { -1075 Reg[EAX].i = 0x00000001; // 1 -1076 run( +1075 Reg[EAX].i = 0x00000001; // 1 +1076 run( 1077 "== code 0x1\n" 1078 // op ModR/M SIB displacement immediate 1079 " 81 f8 ff ff ff ff\n" // compare EAX with -1 @@ -1151,8 +1151,8 @@ if ('onhashchange' in window) { 1090 1091 :(code) 1092 void test_compare_imm32_with_r32_equal() { -1093 Reg[EBX].i = 0x0d0c0b0a; -1094 run( +1093 Reg[EBX].i = 0x0d0c0b0a; +1094 run( 1095 "== code 0x1\n" 1096 // op ModR/M SIB displacement immediate 1097 " 81 fb 0a 0b 0c 0d \n" // compare 0x0d0c0b0a with EBX @@ -1168,8 +1168,8 @@ if ('onhashchange' in window) { 1107 1108 :(code) 1109 void test_compare_imm32_with_mem_at_r32_greater() { -1110 Reg[EBX].i = 0x2000; -1111 run( +1110 Reg[EBX].i = 0x2000; +1111 run( 1112 "== code 0x1\n" 1113 // op ModR/M SIB displacement immediate 1114 " 81 3b 07 0b 0c 0d \n" // compare 0x0d0c0b07 with *EBX @@ -1179,7 +1179,7 @@ if ('onhashchange' in window) { 1118 ); 1119 CHECK_TRACE_CONTENTS( 1120 "run: combine r/m32 with imm32\n" -1121 "run: effective address is 0x00002000 (EBX)\n" +1121 "run: effective address is 0x00002000 (EBX)\n" 1122 "run: imm32 is 0x0d0c0b07\n" 1123 "run: SF=0; ZF=0; CF=0; OF=0\n" 1124 ); @@ -1187,8 +1187,8 @@ if ('onhashchange' in window) { 1126 1127 :(code) 1128 void test_compare_imm32_with_mem_at_r32_lesser() { -1129 Reg[EAX].i = 0x2000; -1130 run( +1129 Reg[EAX].i = 0x2000; +1130 run( 1131 "== code 0x1\n" 1132 // op ModR/M SIB displacement immediate 1133 " 81 38 0a 0b 0c 0d \n" // compare 0x0d0c0b0a with *EAX @@ -1198,7 +1198,7 @@ if ('onhashchange' in window) { 1137 ); 1138 CHECK_TRACE_CONTENTS( 1139 "run: combine r/m32 with imm32\n" -1140 "run: effective address is 0x00002000 (EAX)\n" +1140 "run: effective address is 0x00002000 (EAX)\n" 1141 "run: imm32 is 0x0d0c0b0a\n" 1142 "run: SF=1; ZF=0; CF=1; OF=0\n" 1143 ); @@ -1206,9 +1206,9 @@ if ('onhashchange' in window) { 1145 1146 :(code) 1147 void test_compare_imm32_with_mem_at_r32_equal() { -1148 Reg[EBX].i = 0x0d0c0b0a; -1149 Reg[EBX].i = 0x2000; -1150 run( +1148 Reg[EBX].i = 0x0d0c0b0a; +1149 Reg[EBX].i = 0x2000; +1150 run( 1151 "== code 0x1\n" 1152 // op ModR/M SIB displacement immediate 1153 " 81 3b 0a 0b 0c 0d \n" // compare 0x0d0c0b0a with *EBX @@ -1218,7 +1218,7 @@ if ('onhashchange' in window) { 1157 ); 1158 CHECK_TRACE_CONTENTS( 1159 "run: combine r/m32 with imm32\n" -1160 "run: effective address is 0x00002000 (EBX)\n" +1160 "run: effective address is 0x00002000 (EBX)\n" 1161 "run: imm32 is 0x0d0c0b0a\n" 1162 "run: SF=0; ZF=1; CF=0; OF=0\n" 1163 ); @@ -1228,17 +1228,17 @@ if ('onhashchange' in window) { 1167 1168 :(before "End Initialize Op Names") 1169 // b8 defined earlier to copy imm32 to EAX -1170 put_new(Name, "b9", "copy imm32 to ECX (mov)"); -1171 put_new(Name, "ba", "copy imm32 to EDX (mov)"); -1172 put_new(Name, "bb", "copy imm32 to EBX (mov)"); -1173 put_new(Name, "bc", "copy imm32 to ESP (mov)"); -1174 put_new(Name, "bd", "copy imm32 to EBP (mov)"); -1175 put_new(Name, "be", "copy imm32 to ESI (mov)"); -1176 put_new(Name, "bf", "copy imm32 to EDI (mov)"); +1170 put_new(Name, "b9", "copy imm32 to ECX (mov)"); +1171 put_new(Name, "ba", "copy imm32 to EDX (mov)"); +1172 put_new(Name, "bb", "copy imm32 to EBX (mov)"); +1173 put_new(Name, "bc", "copy imm32 to ESP (mov)"); +1174 put_new(Name, "bd", "copy imm32 to EBP (mov)"); +1175 put_new(Name, "be", "copy imm32 to ESI (mov)"); +1176 put_new(Name, "bf", "copy imm32 to EDI (mov)"); 1177 1178 :(code) 1179 void test_copy_imm32_to_r32() { -1180 run( +1180 run( 1181 "== code 0x1\n" 1182 // op ModR/M SIB displacement immediate 1183 " bb 0a 0b 0c 0d \n" // copy 0x0d0c0b0a to EBX @@ -1257,21 +1257,21 @@ if ('onhashchange' in window) { 1196 case 0xbe: 1197 case 0xbf: { // copy imm32 to r32 1198 const uint8_t rdest = op & 0x7; -1199 const int32_t src = next32(); -1200 trace(Callstack_depth+1, "run") << "copy imm32 0x" << HEXWORD << src << " to " << rname(rdest) << end(); -1201 Reg[rdest].i = src; +1199 const int32_t src = next32(); +1200 trace(Callstack_depth+1, "run") << "copy imm32 0x" << HEXWORD << src << " to " << rname(rdest) << end(); +1201 Reg[rdest].i = src; 1202 break; 1203 } 1204 1205 //: 1206 1207 :(before "End Initialize Op Names") -1208 put_new(Name, "c7", "copy imm32 to rm32 with subop 0 (mov)"); +1208 put_new(Name, "c7", "copy imm32 to rm32 with subop 0 (mov)"); 1209 1210 :(code) 1211 void test_copy_imm32_to_mem_at_r32() { -1212 Reg[EBX].i = 0x60; -1213 run( +1212 Reg[EBX].i = 0x60; +1213 run( 1214 "== code 0x1\n" 1215 // op ModR/M SIB displacement immediate 1216 " c7 03 0a 0b 0c 0d \n" // copy 0x0d0c0b0a to *EBX @@ -1279,23 +1279,23 @@ if ('onhashchange' in window) { 1218 ); 1219 CHECK_TRACE_CONTENTS( 1220 "run: copy imm32 to r/m32\n" -1221 "run: effective address is 0x00000060 (EBX)\n" +1221 "run: effective address is 0x00000060 (EBX)\n" 1222 "run: imm32 is 0x0d0c0b0a\n" 1223 ); 1224 } 1225 1226 :(before "End Single-Byte Opcodes") 1227 case 0xc7: { // copy imm32 to r32 -1228 const uint8_t modrm = next(); +1228 const uint8_t modrm = next(); 1229 trace(Callstack_depth+1, "run") << "copy imm32 to r/m32" << end(); 1230 const uint8_t subop = (modrm>>3)&0x7; // middle 3 'reg opcode' bits 1231 if (subop != 0) { -1232 cerr << "unrecognized subop for opcode c7: " << NUM(subop) << " (only 0/copy currently implemented)\n"; +1232 cerr << "unrecognized subop for opcode c7: " << NUM(subop) << " (only 0/copy currently implemented)\n"; 1233 exit(1); 1234 } 1235 int32_t* dest = effective_address(modrm); -1236 const int32_t src = next32(); -1237 trace(Callstack_depth+1, "run") << "imm32 is 0x" << HEXWORD << src << end(); +1236 const int32_t src = next32(); +1237 trace(Callstack_depth+1, "run") << "imm32 is 0x" << HEXWORD << src << end(); 1238 *dest = src; 1239 break; 1240 } @@ -1303,32 +1303,32 @@ if ('onhashchange' in window) { 1242 //:: push 1243 1244 :(before "End Initialize Op Names") -1245 put_new(Name, "68", "push imm32 to stack (push)"); +1245 put_new(Name, "68", "push imm32 to stack (push)"); 1246 1247 :(code) 1248 void test_push_imm32() { -1249 Mem.push_back(vma(0xbd000000)); // manually allocate memory -1250 Reg[ESP].u = 0xbd000014; -1251 run( +1249 Mem.push_back(vma(0xbd000000)); // manually allocate memory +1250 Reg[ESP].u = 0xbd000014; +1251 run( 1252 "== code 0x1\n" 1253 // op ModR/M SIB displacement immediate 1254 " 68 af 00 00 00 \n" // push *EAX to stack 1255 ); 1256 CHECK_TRACE_CONTENTS( 1257 "run: push imm32 0x000000af\n" -1258 "run: ESP is now 0xbd000010\n" -1259 "run: contents at ESP: 0x000000af\n" +1258 "run: ESP is now 0xbd000010\n" +1259 "run: contents at ESP: 0x000000af\n" 1260 ); 1261 } 1262 1263 :(before "End Single-Byte Opcodes") 1264 case 0x68: { -1265 const uint32_t val = static_cast<uint32_t>(next32()); -1266 trace(Callstack_depth+1, "run") << "push imm32 0x" << HEXWORD << val << end(); +1265 const uint32_t val = static_cast<uint32_t>(next32()); +1266 trace(Callstack_depth+1, "run") << "push imm32 0x" << HEXWORD << val << end(); 1267 //? cerr << "push: " << val << " => " << Reg[ESP].u << '\n'; 1268 push(val); -1269 trace(Callstack_depth+1, "run") << "ESP is now 0x" << HEXWORD << Reg[ESP].u << end(); -1270 trace(Callstack_depth+1, "run") << "contents at ESP: 0x" << HEXWORD << read_mem_u32(Reg[ESP].u) << end(); +1269 trace(Callstack_depth+1, "run") << "ESP is now 0x" << HEXWORD << Reg[ESP].u << end(); +1270 trace(Callstack_depth+1, "run") << "contents at ESP: 0x" << HEXWORD << read_mem_u32(Reg[ESP].u) << end(); 1271 break; 1272 } diff --git a/html/016index_addressing.cc.html b/html/016index_addressing.cc.html index a50dfd81..e64badfc 100644 --- a/html/016index_addressing.cc.html +++ b/html/016index_addressing.cc.html @@ -60,9 +60,9 @@ if ('onhashchange' in window) { 2 3 :(code) 4 void test_add_r32_to_mem_at_r32_with_sib() { - 5 Reg[EBX].i = 0x10; - 6 Reg[EAX].i = 0x2000; - 7 run( + 5 Reg[EBX].i = 0x10; + 6 Reg[EAX].i = 0x2000; + 7 run( 8 "== code 0x1\n" 9 // op ModR/M SIB displacement immediate 10 " 01 1c 20 \n" // add EBX to *EAX @@ -72,8 +72,8 @@ if ('onhashchange' in window) { 14 "01 00 00 00\n" // 0x00000001 15 ); 16 CHECK_TRACE_CONTENTS( - 17 "run: add EBX to r/m32\n" - 18 "run: effective address is initially 0x00002000 (EAX)\n" + 17 "run: add EBX to r/m32\n" + 18 "run: effective address is initially 0x00002000 (EAX)\n" 19 "run: effective address is 0x00002000\n" 20 "run: storing 0x00000011\n" 21 ); @@ -85,37 +85,37 @@ if ('onhashchange' in window) { 27 break; 28 :(code) 29 uint32_t effective_address_from_sib(uint8_t mod) { - 30 const uint8_t sib = next(); + 30 const uint8_t sib = next(); 31 const uint8_t base = sib&0x7; 32 uint32_t addr = 0; - 33 if (base != EBP || mod != 0) { - 34 addr = Reg[base].u; - 35 trace(Callstack_depth+1, "run") << "effective address is initially 0x" << HEXWORD << addr << " (" << rname(base) << ")" << end(); + 33 if (base != EBP || mod != 0) { + 34 addr = Reg[base].u; + 35 trace(Callstack_depth+1, "run") << "effective address is initially 0x" << HEXWORD << addr << " (" << rname(base) << ")" << end(); 36 } 37 else { 38 // base == EBP && mod == 0 - 39 addr = next32(); // ignore base - 40 trace(Callstack_depth+1, "run") << "effective address is initially 0x" << HEXWORD << addr << " (disp32)" << end(); + 39 addr = next32(); // ignore base + 40 trace(Callstack_depth+1, "run") << "effective address is initially 0x" << HEXWORD << addr << " (disp32)" << end(); 41 } 42 const uint8_t index = (sib>>3)&0x7; - 43 if (index == ESP) { + 43 if (index == ESP) { 44 // ignore index and scale - 45 trace(Callstack_depth+1, "run") << "effective address is 0x" << HEXWORD << addr << end(); + 45 trace(Callstack_depth+1, "run") << "effective address is 0x" << HEXWORD << addr << end(); 46 } 47 else { 48 const uint8_t scale = (1 << (sib>>6)); - 49 addr += Reg[index].i*scale; // treat index register as signed. Maybe base as well? But we'll always ensure it's non-negative. - 50 trace(Callstack_depth+1, "run") << "effective address is 0x" << HEXWORD << addr << " (after adding " << rname(index) << "*" << NUM(scale) << ")" << end(); + 49 addr += Reg[index].i*scale; // treat index register as signed. Maybe base as well? But we'll always ensure it's non-negative. + 50 trace(Callstack_depth+1, "run") << "effective address is 0x" << HEXWORD << addr << " (after adding " << rname(index) << "*" << NUM(scale) << ")" << end(); 51 } 52 return addr; 53 } 54 55 :(code) 56 void test_add_r32_to_mem_at_base_r32_index_r32() { - 57 Reg[EBX].i = 0x10; // source - 58 Reg[EAX].i = 0x1ffe; // dest base - 59 Reg[ECX].i = 0x2; // dest index - 60 run( + 57 Reg[EBX].i = 0x10; // source + 58 Reg[EAX].i = 0x1ffe; // dest base + 59 Reg[ECX].i = 0x2; // dest index + 60 run( 61 "== code 0x1\n" 62 // op ModR/M SIB displacement immediate 63 " 01 1c 08 \n" // add EBX to *(EAX+ECX) @@ -125,8 +125,8 @@ if ('onhashchange' in window) { 67 "01 00 00 00\n" // 0x00000001 68 ); 69 CHECK_TRACE_CONTENTS( - 70 "run: add EBX to r/m32\n" - 71 "run: effective address is initially 0x00001ffe (EAX)\n" + 70 "run: add EBX to r/m32\n" + 71 "run: effective address is initially 0x00001ffe (EAX)\n" 72 "run: effective address is 0x00002000 (after adding ECX*1)\n" 73 "run: storing 0x00000011\n" 74 ); @@ -134,8 +134,8 @@ if ('onhashchange' in window) { 76 77 :(code) 78 void test_add_r32_to_mem_at_displacement_using_sib() { - 79 Reg[EBX].i = 0x10; // source - 80 run( + 79 Reg[EBX].i = 0x10; // source + 80 run( 81 "== code 0x1\n" 82 // op ModR/M SIB displacement immediate 83 " 01 1c 25 00 20 00 00 \n" // add EBX to *0x2000 @@ -145,8 +145,8 @@ if ('onhashchange' in window) { 87 "01 00 00 00\n" // 0x00000001 88 ); 89 CHECK_TRACE_CONTENTS( - 90 "run: add EBX to r/m32\n" - 91 "run: effective address is initially 0x00002000 (disp32)\n" + 90 "run: add EBX to r/m32\n" + 91 "run: effective address is initially 0x00002000 (disp32)\n" 92 "run: effective address is 0x00002000\n" 93 "run: storing 0x00000011\n" 94 ); @@ -156,10 +156,10 @@ if ('onhashchange' in window) { 98 99 :(code) 100 void test_add_r32_to_mem_at_base_r32_index_r32_plus_disp8() { -101 Reg[EBX].i = 0x10; // source -102 Reg[EAX].i = 0x1ff9; // dest base -103 Reg[ECX].i = 0x5; // dest index -104 run( +101 Reg[EBX].i = 0x10; // source +102 Reg[EAX].i = 0x1ff9; // dest base +103 Reg[ECX].i = 0x5; // dest index +104 run( 105 "== code 0x1\n" 106 // op ModR/M SIB displacement immediate 107 " 01 5c 08 02 \n" // add EBX to *(EAX+ECX+2) @@ -169,8 +169,8 @@ if ('onhashchange' in window) { 111 "01 00 00 00\n" // 0x00000001 112 ); 113 CHECK_TRACE_CONTENTS( -114 "run: add EBX to r/m32\n" -115 "run: effective address is initially 0x00001ff9 (EAX)\n" +114 "run: add EBX to r/m32\n" +115 "run: effective address is initially 0x00001ff9 (EAX)\n" 116 "run: effective address is 0x00001ffe (after adding ECX*1)\n" 117 "run: effective address is 0x00002000 (after adding disp8)\n" 118 "run: storing 0x00000011\n" @@ -186,10 +186,10 @@ if ('onhashchange' in window) { 128 129 :(code) 130 void test_add_r32_to_mem_at_base_r32_index_r32_plus_disp32() { -131 Reg[EBX].i = 0x10; // source -132 Reg[EAX].i = 0x1ff9; // dest base -133 Reg[ECX].i = 0x5; // dest index -134 run( +131 Reg[EBX].i = 0x10; // source +132 Reg[EAX].i = 0x1ff9; // dest base +133 Reg[ECX].i = 0x5; // dest index +134 run( 135 "== code 0x1\n" 136 // op ModR/M SIB displacement immediate 137 " 01 9c 08 02 00 00 00 \n" // add EBX to *(EAX+ECX+2) @@ -199,10 +199,10 @@ if ('onhashchange' in window) { 141 "01 00 00 00\n" // 0x00000001 142 ); 143 CHECK_TRACE_CONTENTS( -144 "run: add EBX to r/m32\n" -145 "run: effective address is initially 0x00001ff9 (EAX)\n" +144 "run: add EBX to r/m32\n" +145 "run: effective address is initially 0x00001ff9 (EAX)\n" 146 "run: effective address is 0x00001ffe (after adding ECX*1)\n" -147 "run: effective address is 0x00002000 (after adding disp32)\n" +147 "run: effective address is 0x00002000 (after adding disp32)\n" 148 "run: storing 0x00000011\n" 149 ); 150 } diff --git a/html/017jump_disp8.cc.html b/html/017jump_disp8.cc.html index e1393a3c..39c3acc3 100644 --- a/html/017jump_disp8.cc.html +++ b/html/017jump_disp8.cc.html @@ -62,11 +62,11 @@ if ('onhashchange' in window) { 3 //:: jump 4 5 :(before "End Initialize Op Names") - 6 put_new(Name, "eb", "jump disp8 bytes away (jmp)"); + 6 put_new(Name, "eb", "jump disp8 bytes away (jmp)"); 7 8 :(code) 9 void test_jump_disp8() { - 10 run( + 10 run( 11 "== code 0x1\n" 12 // op ModR/M SIB displacement immediate 13 " eb 05 \n" // skip 1 instruction @@ -83,21 +83,21 @@ if ('onhashchange' in window) { 24 25 :(before "End Single-Byte Opcodes") 26 case 0xeb: { // jump disp8 - 27 int8_t offset = static_cast<int>(next()); - 28 trace(Callstack_depth+1, "run") << "jump " << NUM(offset) << end(); - 29 EIP += offset; + 27 int8_t offset = static_cast<int>(next()); + 28 trace(Callstack_depth+1, "run") << "jump " << NUM(offset) << end(); + 29 EIP += offset; 30 break; 31 } 32 33 //:: jump if equal/zero 34 35 :(before "End Initialize Op Names") - 36 put_new(Name, "74", "jump disp8 bytes away if equal, if ZF is set (jcc/jz/je)"); + 36 put_new(Name, "74", "jump disp8 bytes away if equal, if ZF is set (jcc/jz/je)"); 37 38 :(code) 39 void test_je_disp8_success() { - 40 ZF = true; - 41 run( + 40 ZF = true; + 41 run( 42 "== code 0x1\n" 43 // op ModR/M SIB displacement immediate 44 " 74 05 \n" // skip 1 instruction @@ -114,18 +114,18 @@ if ('onhashchange' in window) { 55 56 :(before "End Single-Byte Opcodes") 57 case 0x74: { // jump disp8 if ZF - 58 const int8_t offset = static_cast<int>(next()); - 59 if (ZF) { - 60 trace(Callstack_depth+1, "run") << "jump " << NUM(offset) << end(); - 61 EIP += offset; + 58 const int8_t offset = static_cast<int>(next()); + 59 if (ZF) { + 60 trace(Callstack_depth+1, "run") << "jump " << NUM(offset) << end(); + 61 EIP += offset; 62 } 63 break; 64 } 65 66 :(code) 67 void test_je_disp8_fail() { - 68 ZF = false; - 69 run( + 68 ZF = false; + 69 run( 70 "== code 0x1\n" 71 // op ModR/M SIB displacement immediate 72 " 74 05 \n" // skip 1 instruction @@ -143,12 +143,12 @@ if ('onhashchange' in window) { 84 //:: jump if not equal/not zero 85 86 :(before "End Initialize Op Names") - 87 put_new(Name, "75", "jump disp8 bytes away if not equal, if ZF is not set (jcc/jnz/jne)"); + 87 put_new(Name, "75", "jump disp8 bytes away if not equal, if ZF is not set (jcc/jnz/jne)"); 88 89 :(code) 90 void test_jne_disp8_success() { - 91 ZF = false; - 92 run( + 91 ZF = false; + 92 run( 93 "== code 0x1\n" 94 // op ModR/M SIB displacement immediate 95 " 75 05 \n" // skip 1 instruction @@ -165,18 +165,18 @@ if ('onhashchange' in window) { 106 107 :(before "End Single-Byte Opcodes") 108 case 0x75: { // jump disp8 unless ZF -109 const int8_t offset = static_cast<int>(next()); +109 const int8_t offset = static_cast<int>(next()); 110 if (!ZF) { -111 trace(Callstack_depth+1, "run") << "jump " << NUM(offset) << end(); -112 EIP += offset; +111 trace(Callstack_depth+1, "run") << "jump " << NUM(offset) << end(); +112 EIP += offset; 113 } 114 break; 115 } 116 117 :(code) 118 void test_jne_disp8_fail() { -119 ZF = true; -120 run( +119 ZF = true; +120 run( 121 "== code 0x1\n" 122 // op ModR/M SIB displacement immediate 123 " 75 05 \n" // skip 1 instruction @@ -194,15 +194,15 @@ if ('onhashchange' in window) { 135 //:: jump if greater 136 137 :(before "End Initialize Op Names") -138 put_new(Name, "7f", "jump disp8 bytes away if greater (signed), if ZF is unset and SF == OF (jcc/jg/jnle)"); -139 put_new(Name, "77", "jump disp8 bytes away if greater (unsigned), if ZF is unset and CF is unset (jcc/ja/jnbe)"); +138 put_new(Name, "7f", "jump disp8 bytes away if greater (signed), if ZF is unset and SF == OF (jcc/jg/jnle)"); +139 put_new(Name, "77", "jump disp8 bytes away if greater (unsigned), if ZF is unset and CF is unset (jcc/ja/jnbe)"); 140 141 :(code) 142 void test_jg_disp8_success() { -143 ZF = false; +143 ZF = false; 144 SF = false; -145 OF = false; -146 run( +145 OF = false; +146 run( 147 "== code 0x1\n" 148 // op ModR/M SIB displacement immediate 149 " 7f 05 \n" // skip 1 instruction @@ -219,28 +219,28 @@ if ('onhashchange' in window) { 160 161 :(before "End Single-Byte Opcodes") 162 case 0x7f: { // jump disp8 if SF == OF and !ZF -163 const int8_t offset = static_cast<int>(next()); -164 if (SF == OF && !ZF) { -165 trace(Callstack_depth+1, "run") << "jump " << NUM(offset) << end(); -166 EIP += offset; +163 const int8_t offset = static_cast<int>(next()); +164 if (SF == OF && !ZF) { +165 trace(Callstack_depth+1, "run") << "jump " << NUM(offset) << end(); +166 EIP += offset; 167 } 168 break; 169 } 170 case 0x77: { // jump disp8 if !CF and !ZF -171 const int8_t offset = static_cast<int>(next()); +171 const int8_t offset = static_cast<int>(next()); 172 if (!CF && !ZF) { -173 trace(Callstack_depth+1, "run") << "jump " << NUM(offset) << end(); -174 EIP += offset; +173 trace(Callstack_depth+1, "run") << "jump " << NUM(offset) << end(); +174 EIP += offset; 175 } 176 break; 177 } 178 179 :(code) 180 void test_jg_disp8_fail() { -181 ZF = false; +181 ZF = false; 182 SF = true; -183 OF = false; -184 run( +183 OF = false; +184 run( 185 "== code 0x1\n" 186 // op ModR/M SIB displacement immediate 187 " 7f 05 \n" // skip 1 instruction @@ -258,14 +258,14 @@ if ('onhashchange' in window) { 199 //:: jump if greater or equal 200 201 :(before "End Initialize Op Names") -202 put_new(Name, "7d", "jump disp8 bytes away if greater or equal (signed), if SF == OF (jcc/jge/jnl)"); -203 put_new(Name, "73", "jump disp8 bytes away if greater or equal (unsigned), if CF is unset (jcc/jae/jnb)"); +202 put_new(Name, "7d", "jump disp8 bytes away if greater or equal (signed), if SF == OF (jcc/jge/jnl)"); +203 put_new(Name, "73", "jump disp8 bytes away if greater or equal (unsigned), if CF is unset (jcc/jae/jnb)"); 204 205 :(code) 206 void test_jge_disp8_success() { 207 SF = false; -208 OF = false; -209 run( +208 OF = false; +209 run( 210 "== code 0x1\n" 211 // op ModR/M SIB displacement immediate 212 " 7d 05 \n" // skip 1 instruction @@ -282,18 +282,18 @@ if ('onhashchange' in window) { 223 224 :(before "End Single-Byte Opcodes") 225 case 0x7d: { // jump disp8 if SF == OF -226 const int8_t offset = static_cast<int>(next()); -227 if (SF == OF) { -228 trace(Callstack_depth+1, "run") << "jump " << NUM(offset) << end(); -229 EIP += offset; +226 const int8_t offset = static_cast<int>(next()); +227 if (SF == OF) { +228 trace(Callstack_depth+1, "run") << "jump " << NUM(offset) << end(); +229 EIP += offset; 230 } 231 break; 232 } 233 case 0x73: { // jump disp8 if !CF -234 const int8_t offset = static_cast<int>(next()); +234 const int8_t offset = static_cast<int>(next()); 235 if (!CF) { -236 trace(Callstack_depth+1, "run") << "jump " << NUM(offset) << end(); -237 EIP += offset; +236 trace(Callstack_depth+1, "run") << "jump " << NUM(offset) << end(); +237 EIP += offset; 238 } 239 break; 240 } @@ -301,8 +301,8 @@ if ('onhashchange' in window) { 242 :(code) 243 void test_jge_disp8_fail() { 244 SF = true; -245 OF = false; -246 run( +245 OF = false; +246 run( 247 "== code 0x1\n" 248 // op ModR/M SIB displacement immediate 249 " 7d 05 \n" // skip 1 instruction @@ -320,15 +320,15 @@ if ('onhashchange' in window) { 261 //:: jump if lesser 262 263 :(before "End Initialize Op Names") -264 put_new(Name, "7c", "jump disp8 bytes away if lesser (signed), if SF != OF (jcc/jl/jnge)"); -265 put_new(Name, "72", "jump disp8 bytes away if lesser (unsigned), if CF is set (jcc/jb/jnae)"); +264 put_new(Name, "7c", "jump disp8 bytes away if lesser (signed), if SF != OF (jcc/jl/jnge)"); +265 put_new(Name, "72", "jump disp8 bytes away if lesser (unsigned), if CF is set (jcc/jb/jnae)"); 266 267 :(code) 268 void test_jl_disp8_success() { -269 ZF = false; +269 ZF = false; 270 SF = true; -271 OF = false; -272 run( +271 OF = false; +272 run( 273 "== code 0x1\n" 274 // op ModR/M SIB displacement immediate 275 " 7c 05 \n" // skip 1 instruction @@ -345,28 +345,28 @@ if ('onhashchange' in window) { 286 287 :(before "End Single-Byte Opcodes") 288 case 0x7c: { // jump disp8 if SF != OF -289 const int8_t offset = static_cast<int>(next()); -290 if (SF != OF) { -291 trace(Callstack_depth+1, "run") << "jump " << NUM(offset) << end(); -292 EIP += offset; +289 const int8_t offset = static_cast<int>(next()); +290 if (SF != OF) { +291 trace(Callstack_depth+1, "run") << "jump " << NUM(offset) << end(); +292 EIP += offset; 293 } 294 break; 295 } 296 case 0x72: { // jump disp8 if CF -297 const int8_t offset = static_cast<int>(next()); -298 if (CF) { -299 trace(Callstack_depth+1, "run") << "jump " << NUM(offset) << end(); -300 EIP += offset; +297 const int8_t offset = static_cast<int>(next()); +298 if (CF) { +299 trace(Callstack_depth+1, "run") << "jump " << NUM(offset) << end(); +300 EIP += offset; 301 } 302 break; 303 } 304 305 :(code) 306 void test_jl_disp8_fail() { -307 ZF = false; +307 ZF = false; 308 SF = false; -309 OF = false; -310 run( +309 OF = false; +310 run( 311 "== code 0x1\n" 312 // op ModR/M SIB displacement immediate 313 " 7c 05 \n" // skip 1 instruction @@ -384,15 +384,15 @@ if ('onhashchange' in window) { 325 //:: jump if lesser or equal 326 327 :(before "End Initialize Op Names") -328 put_new(Name, "7e", "jump disp8 bytes away if lesser or equal (signed), if ZF is set or SF != OF (jcc/jle/jng)"); -329 put_new(Name, "76", "jump disp8 bytes away if lesser or equal (unsigned), if ZF is set or CF is set (jcc/jbe/jna)"); +328 put_new(Name, "7e", "jump disp8 bytes away if lesser or equal (signed), if ZF is set or SF != OF (jcc/jle/jng)"); +329 put_new(Name, "76", "jump disp8 bytes away if lesser or equal (unsigned), if ZF is set or CF is set (jcc/jbe/jna)"); 330 331 :(code) 332 void test_jle_disp8_equal() { -333 ZF = true; +333 ZF = true; 334 SF = false; -335 OF = false; -336 run( +335 OF = false; +336 run( 337 "== code 0x1\n" 338 // op ModR/M SIB displacement immediate 339 " 7e 05 \n" // skip 1 instruction @@ -409,10 +409,10 @@ if ('onhashchange' in window) { 350 351 :(code) 352 void test_jle_disp8_lesser() { -353 ZF = false; +353 ZF = false; 354 SF = true; -355 OF = false; -356 run( +355 OF = false; +356 run( 357 "== code 0x1\n" 358 // op ModR/M SIB displacement immediate 359 " 7e 05 \n" // skip 1 instruction @@ -429,28 +429,28 @@ if ('onhashchange' in window) { 370 371 :(before "End Single-Byte Opcodes") 372 case 0x7e: { // jump disp8 if ZF or SF != OF -373 const int8_t offset = static_cast<int>(next()); -374 if (ZF || SF != OF) { -375 trace(Callstack_depth+1, "run") << "jump " << NUM(offset) << end(); -376 EIP += offset; +373 const int8_t offset = static_cast<int>(next()); +374 if (ZF || SF != OF) { +375 trace(Callstack_depth+1, "run") << "jump " << NUM(offset) << end(); +376 EIP += offset; 377 } 378 break; 379 } 380 case 0x76: { // jump disp8 if ZF or CF -381 const int8_t offset = static_cast<int>(next()); -382 if (ZF || CF) { -383 trace(Callstack_depth+1, "run") << "jump " << NUM(offset) << end(); -384 EIP += offset; +381 const int8_t offset = static_cast<int>(next()); +382 if (ZF || CF) { +383 trace(Callstack_depth+1, "run") << "jump " << NUM(offset) << end(); +384 EIP += offset; 385 } 386 break; 387 } 388 389 :(code) 390 void test_jle_disp8_greater() { -391 ZF = false; +391 ZF = false; 392 SF = false; -393 OF = false; -394 run( +393 OF = false; +394 run( 395 "== code 0x1\n" 396 // op ModR/M SIB displacement immediate 397 " 7e 05 \n" // skip 1 instruction diff --git a/html/018jump_disp32.cc.html b/html/018jump_disp32.cc.html index abdccaa3..fe7ef115 100644 --- a/html/018jump_disp32.cc.html +++ b/html/018jump_disp32.cc.html @@ -62,11 +62,11 @@ if ('onhashchange' in window) { 3 //:: jump 4 5 :(before "End Initialize Op Names") - 6 put_new(Name, "e9", "jump disp32 bytes away (jmp)"); + 6 put_new(Name, "e9", "jump disp32 bytes away (jmp)"); 7 8 :(code) 9 void test_jump_disp32() { - 10 run( + 10 run( 11 "== code 0x1\n" 12 // op ModR/M SIB displacement immediate 13 " e9 05 00 00 00 \n" // skip 1 instruction @@ -83,21 +83,21 @@ if ('onhashchange' in window) { 24 25 :(before "End Single-Byte Opcodes") 26 case 0xe9: { // jump disp32 - 27 const int32_t offset = next32(); + 27 const int32_t offset = next32(); 28 trace(Callstack_depth+1, "run") << "jump " << offset << end(); - 29 EIP += offset; + 29 EIP += offset; 30 break; 31 } 32 33 //:: jump if equal/zero 34 35 :(before "End Initialize Op Names") - 36 put_new(Name_0f, "84", "jump disp32 bytes away if equal, if ZF is set (jcc/jz/je)"); + 36 put_new(Name_0f, "84", "jump disp32 bytes away if equal, if ZF is set (jcc/jz/je)"); 37 38 :(code) 39 void test_je_disp32_success() { - 40 ZF = true; - 41 run( + 40 ZF = true; + 41 run( 42 "== code 0x1\n" 43 // op ModR/M SIB displacement immediate 44 " 0f 84 05 00 00 00 \n" // skip 1 instruction @@ -114,18 +114,18 @@ if ('onhashchange' in window) { 55 56 :(before "End Two-Byte Opcodes Starting With 0f") 57 case 0x84: { // jump disp32 if ZF - 58 const int32_t offset = next32(); - 59 if (ZF) { + 58 const int32_t offset = next32(); + 59 if (ZF) { 60 trace(Callstack_depth+1, "run") << "jump " << offset << end(); - 61 EIP += offset; + 61 EIP += offset; 62 } 63 break; 64 } 65 66 :(code) 67 void test_je_disp32_fail() { - 68 ZF = false; - 69 run( + 68 ZF = false; + 69 run( 70 "== code 0x1\n" 71 // op ModR/M SIB displacement immediate 72 " 0f 84 05 00 00 00 \n" // skip 1 instruction @@ -143,12 +143,12 @@ if ('onhashchange' in window) { 84 //:: jump if not equal/not zero 85 86 :(before "End Initialize Op Names") - 87 put_new(Name_0f, "85", "jump disp32 bytes away if not equal, if ZF is not set (jcc/jnz/jne)"); + 87 put_new(Name_0f, "85", "jump disp32 bytes away if not equal, if ZF is not set (jcc/jnz/jne)"); 88 89 :(code) 90 void test_jne_disp32_success() { - 91 ZF = false; - 92 run( + 91 ZF = false; + 92 run( 93 "== code 0x1\n" 94 // op ModR/M SIB displacement immediate 95 " 0f 85 05 00 00 00 \n" // skip 1 instruction @@ -165,18 +165,18 @@ if ('onhashchange' in window) { 106 107 :(before "End Two-Byte Opcodes Starting With 0f") 108 case 0x85: { // jump disp32 unless ZF -109 const int32_t offset = next32(); +109 const int32_t offset = next32(); 110 if (!ZF) { 111 trace(Callstack_depth+1, "run") << "jump " << offset << end(); -112 EIP += offset; +112 EIP += offset; 113 } 114 break; 115 } 116 117 :(code) 118 void test_jne_disp32_fail() { -119 ZF = true; -120 run( +119 ZF = true; +120 run( 121 "== code 0x1\n" 122 // op ModR/M SIB displacement immediate 123 " 0f 85 05 00 00 00 \n" // skip 1 instruction @@ -194,15 +194,15 @@ if ('onhashchange' in window) { 135 //:: jump if greater 136 137 :(before "End Initialize Op Names") -138 put_new(Name_0f, "8f", "jump disp32 bytes away if greater (signed), if ZF is unset and SF == OF (jcc/jg/jnle)"); -139 put_new(Name_0f, "87", "jump disp32 bytes away if greater (unsigned), if ZF is unset and CF is unset (jcc/ja/jnbe)"); +138 put_new(Name_0f, "8f", "jump disp32 bytes away if greater (signed), if ZF is unset and SF == OF (jcc/jg/jnle)"); +139 put_new(Name_0f, "87", "jump disp32 bytes away if greater (unsigned), if ZF is unset and CF is unset (jcc/ja/jnbe)"); 140 141 :(code) 142 void test_jg_disp32_success() { -143 ZF = false; +143 ZF = false; 144 SF = false; -145 OF = false; -146 run( +145 OF = false; +146 run( 147 "== code 0x1\n" 148 // op ModR/M SIB displacement immediate 149 " 0f 8f 05 00 00 00 \n" // skip 1 instruction @@ -219,28 +219,28 @@ if ('onhashchange' in window) { 160 161 :(before "End Two-Byte Opcodes Starting With 0f") 162 case 0x8f: { // jump disp32 if !SF and !ZF -163 const int32_t offset = next32(); -164 if (!ZF && SF == OF) { +163 const int32_t offset = next32(); +164 if (!ZF && SF == OF) { 165 trace(Callstack_depth+1, "run") << "jump " << offset << end(); -166 EIP += offset; +166 EIP += offset; 167 } 168 break; 169 } 170 case 0x87: { // jump disp32 if !CF and !ZF -171 const int32_t offset = next(); +171 const int32_t offset = next(); 172 if (!CF && !ZF) { 173 trace(Callstack_depth+1, "run") << "jump " << offset << end(); -174 EIP += offset; +174 EIP += offset; 175 } 176 break; 177 } 178 179 :(code) 180 void test_jg_disp32_fail() { -181 ZF = false; +181 ZF = false; 182 SF = true; -183 OF = false; -184 run( +183 OF = false; +184 run( 185 "== code 0x1\n" 186 // op ModR/M SIB displacement immediate 187 " 0f 8f 05 00 00 00 \n" // skip 1 instruction @@ -258,14 +258,14 @@ if ('onhashchange' in window) { 199 //:: jump if greater or equal 200 201 :(before "End Initialize Op Names") -202 put_new(Name_0f, "8d", "jump disp32 bytes away if greater or equal (signed), if SF == OF (jcc/jge/jnl)"); -203 put_new(Name_0f, "83", "jump disp32 bytes away if greater or equal (unsigned), if CF is unset (jcc/jae/jnb)"); +202 put_new(Name_0f, "8d", "jump disp32 bytes away if greater or equal (signed), if SF == OF (jcc/jge/jnl)"); +203 put_new(Name_0f, "83", "jump disp32 bytes away if greater or equal (unsigned), if CF is unset (jcc/jae/jnb)"); 204 205 :(code) 206 void test_jge_disp32_success() { 207 SF = false; -208 OF = false; -209 run( +208 OF = false; +209 run( 210 "== code 0x1\n" 211 // op ModR/M SIB displacement immediate 212 " 0f 8d 05 00 00 00 \n" // skip 1 instruction @@ -282,18 +282,18 @@ if ('onhashchange' in window) { 223 224 :(before "End Two-Byte Opcodes Starting With 0f") 225 case 0x8d: { // jump disp32 if !SF -226 const int32_t offset = next32(); -227 if (SF == OF) { +226 const int32_t offset = next32(); +227 if (SF == OF) { 228 trace(Callstack_depth+1, "run") << "jump " << offset << end(); -229 EIP += offset; +229 EIP += offset; 230 } 231 break; 232 } 233 case 0x83: { // jump disp32 if !CF -234 const int32_t offset = next32(); +234 const int32_t offset = next32(); 235 if (!CF) { 236 trace(Callstack_depth+1, "run") << "jump " << offset << end(); -237 EIP += offset; +237 EIP += offset; 238 } 239 break; 240 } @@ -301,8 +301,8 @@ if ('onhashchange' in window) { 242 :(code) 243 void test_jge_disp32_fail() { 244 SF = true; -245 OF = false; -246 run( +245 OF = false; +246 run( 247 "== code 0x1\n" 248 // op ModR/M SIB displacement immediate 249 " 0f 8d 05 00 00 00 \n" // skip 1 instruction @@ -320,15 +320,15 @@ if ('onhashchange' in window) { 261 //:: jump if lesser 262 263 :(before "End Initialize Op Names") -264 put_new(Name_0f, "8c", "jump disp32 bytes away if lesser (signed), if SF != OF (jcc/jl/jnge)"); -265 put_new(Name_0f, "82", "jump disp32 bytes away if lesser (unsigned), if CF is set (jcc/jb/jnae)"); +264 put_new(Name_0f, "8c", "jump disp32 bytes away if lesser (signed), if SF != OF (jcc/jl/jnge)"); +265 put_new(Name_0f, "82", "jump disp32 bytes away if lesser (unsigned), if CF is set (jcc/jb/jnae)"); 266 267 :(code) 268 void test_jl_disp32_success() { -269 ZF = false; +269 ZF = false; 270 SF = true; -271 OF = false; -272 run( +271 OF = false; +272 run( 273 "== code 0x1\n" 274 // op ModR/M SIB displacement immediate 275 " 0f 8c 05 00 00 00 \n" // skip 1 instruction @@ -345,28 +345,28 @@ if ('onhashchange' in window) { 286 287 :(before "End Two-Byte Opcodes Starting With 0f") 288 case 0x8c: { // jump disp32 if SF and !ZF -289 const int32_t offset = next32(); -290 if (SF != OF) { +289 const int32_t offset = next32(); +290 if (SF != OF) { 291 trace(Callstack_depth+1, "run") << "jump " << offset << end(); -292 EIP += offset; +292 EIP += offset; 293 } 294 break; 295 } -296 case 0x72: { // jump disp32 if CF -297 const int32_t offset = next32(); -298 if (CF) { +296 case 0x82: { // jump disp32 if CF +297 const int32_t offset = next32(); +298 if (CF) { 299 trace(Callstack_depth+1, "run") << "jump " << offset << end(); -300 EIP += offset; +300 EIP += offset; 301 } 302 break; 303 } 304 305 :(code) 306 void test_jl_disp32_fail() { -307 ZF = false; +307 ZF = false; 308 SF = false; -309 OF = false; -310 run( +309 OF = false; +310 run( 311 "== code 0x1\n" 312 // op ModR/M SIB displacement immediate 313 " 0f 8c 05 00 00 00 \n" // skip 1 instruction @@ -384,15 +384,15 @@ if ('onhashchange' in window) { 325 //:: jump if lesser or equal 326 327 :(before "End Initialize Op Names") -328 put_new(Name_0f, "8e", "jump disp32 bytes away if lesser or equal (signed), if ZF is set or SF != OF (jcc/jle/jng)"); -329 put_new(Name_0f, "86", "jump disp32 bytes away if lesser or equal (unsigned), if ZF is set or CF is set (jcc/jbe/jna)"); +328 put_new(Name_0f, "8e", "jump disp32 bytes away if lesser or equal (signed), if ZF is set or SF != OF (jcc/jle/jng)"); +329 put_new(Name_0f, "86", "jump disp32 bytes away if lesser or equal (unsigned), if ZF is set or CF is set (jcc/jbe/jna)"); 330 331 :(code) 332 void test_jle_disp32_equal() { -333 ZF = true; +333 ZF = true; 334 SF = false; -335 OF = false; -336 run( +335 OF = false; +336 run( 337 "== code 0x1\n" 338 // op ModR/M SIB displacement immediate 339 " 0f 8e 05 00 00 00 \n" // skip 1 instruction @@ -409,10 +409,10 @@ if ('onhashchange' in window) { 350 351 :(code) 352 void test_jle_disp32_lesser() { -353 ZF = false; +353 ZF = false; 354 SF = true; -355 OF = false; -356 run( +355 OF = false; +356 run( 357 "== code 0x1\n" 358 // op ModR/M SIB displacement immediate 359 " 0f 8e 05 00 00 00 \n" // skip 1 instruction @@ -429,28 +429,28 @@ if ('onhashchange' in window) { 370 371 :(before "End Two-Byte Opcodes Starting With 0f") 372 case 0x8e: { // jump disp32 if SF or ZF -373 const int32_t offset = next32(); -374 if (ZF || SF != OF) { +373 const int32_t offset = next32(); +374 if (ZF || SF != OF) { 375 trace(Callstack_depth+1, "run") << "jump " << offset << end(); -376 EIP += offset; +376 EIP += offset; 377 } 378 break; 379 } 380 case 0x86: { // jump disp32 if ZF or CF -381 const int32_t offset = next32(); -382 if (ZF || CF) { +381 const int32_t offset = next32(); +382 if (ZF || CF) { 383 trace(Callstack_depth+1, "run") << "jump " << offset << end(); -384 EIP += offset; +384 EIP += offset; 385 } 386 break; 387 } 388 389 :(code) 390 void test_jle_disp32_greater() { -391 ZF = false; +391 ZF = false; 392 SF = false; -393 OF = false; -394 run( +393 OF = false; +394 run( 395 "== code 0x1\n" 396 // op ModR/M SIB displacement immediate 397 " 0f 8e 05 00 00 00 \n" // skip 1 instruction diff --git a/html/019functions.cc.html b/html/019functions.cc.html index f4fc7096..bcb16130 100644 --- a/html/019functions.cc.html +++ b/html/019functions.cc.html @@ -61,13 +61,13 @@ if ('onhashchange' in window) { 1 //:: call 2 3 :(before "End Initialize Op Names") - 4 put_new(Name, "e8", "call disp32 (call)"); + 4 put_new(Name, "e8", "call disp32 (call)"); 5 6 :(code) 7 void test_call_disp32() { - 8 Mem.push_back(vma(0xbd000000)); // manually allocate memory - 9 Reg[ESP].u = 0xbd000064; - 10 run( + 8 Mem.push_back(vma(0xbd000000)); // manually allocate memory + 9 Reg[ESP].u = 0xbd000064; + 10 run( 11 "== code 0x1\n" 12 // op ModR/M SIB displacement immediate 13 " e8 a0 00 00 00 \n" // call function offset at 0x000000a0 @@ -75,7 +75,7 @@ if ('onhashchange' in window) { 15 ); 16 CHECK_TRACE_CONTENTS( 17 "run: call imm32 0x000000a0\n" - 18 "run: decrementing ESP to 0xbd000060\n" + 18 "run: decrementing ESP to 0xbd000060\n" 19 "run: pushing value 0x00000006\n" 20 "run: jumping to 0x000000a6\n" 21 ); @@ -83,13 +83,13 @@ if ('onhashchange' in window) { 23 24 :(before "End Single-Byte Opcodes") 25 case 0xe8: { // call disp32 relative to next EIP - 26 const int32_t offset = next32(); + 26 const int32_t offset = next32(); 27 ++Callstack_depth; - 28 trace(Callstack_depth+1, "run") << "call imm32 0x" << HEXWORD << offset << end(); + 28 trace(Callstack_depth+1, "run") << "call imm32 0x" << HEXWORD << offset << end(); 29 //? cerr << "push: EIP: " << EIP << " => " << Reg[ESP].u << '\n'; - 30 push(EIP); - 31 EIP += offset; - 32 trace(Callstack_depth+1, "run") << "jumping to 0x" << HEXWORD << EIP << end(); + 30 push(EIP); + 31 EIP += offset; + 32 trace(Callstack_depth+1, "run") << "jumping to 0x" << HEXWORD << EIP << end(); 33 break; 34 } 35 @@ -97,10 +97,10 @@ if ('onhashchange' in window) { 37 38 :(code) 39 void test_call_r32() { - 40 Mem.push_back(vma(0xbd000000)); // manually allocate memory - 41 Reg[ESP].u = 0xbd000064; - 42 Reg[EBX].u = 0x000000a0; - 43 run( + 40 Mem.push_back(vma(0xbd000000)); // manually allocate memory + 41 Reg[ESP].u = 0xbd000064; + 42 Reg[EBX].u = 0x000000a0; + 43 run( 44 "== code 0x1\n" 45 // op ModR/M SIB displacement immediate 46 " ff d3 \n" // call function offset at EBX @@ -109,7 +109,7 @@ if ('onhashchange' in window) { 49 CHECK_TRACE_CONTENTS( 50 "run: call to r/m32\n" 51 "run: r/m32 is EBX\n" - 52 "run: decrementing ESP to 0xbd000060\n" + 52 "run: decrementing ESP to 0xbd000060\n" 53 "run: pushing value 0x00000003\n" 54 "run: jumping to 0x000000a3\n" 55 ); @@ -119,19 +119,19 @@ if ('onhashchange' in window) { 59 case 2: { // call function pointer at r/m32 60 trace(Callstack_depth+1, "run") << "call to r/m32" << end(); 61 const int32_t* offset = effective_address(modrm); - 62 push(EIP); - 63 EIP += *offset; - 64 trace(Callstack_depth+1, "run") << "jumping to 0x" << HEXWORD << EIP << end(); + 62 push(EIP); + 63 EIP += *offset; + 64 trace(Callstack_depth+1, "run") << "jumping to 0x" << HEXWORD << EIP << end(); 65 ++Callstack_depth; 66 break; 67 } 68 69 :(code) 70 void test_call_mem_at_r32() { - 71 Mem.push_back(vma(0xbd000000)); // manually allocate memory - 72 Reg[ESP].u = 0xbd000064; - 73 Reg[EBX].u = 0x2000; - 74 run( + 71 Mem.push_back(vma(0xbd000000)); // manually allocate memory + 72 Reg[ESP].u = 0xbd000064; + 73 Reg[EBX].u = 0x2000; + 74 run( 75 "== code 0x1\n" 76 // op ModR/M SIB displacement immediate 77 " ff 13 \n" // call function offset at *EBX @@ -141,8 +141,8 @@ if ('onhashchange' in window) { 81 ); 82 CHECK_TRACE_CONTENTS( 83 "run: call to r/m32\n" - 84 "run: effective address is 0x00002000 (EBX)\n" - 85 "run: decrementing ESP to 0xbd000060\n" + 84 "run: effective address is 0x00002000 (EBX)\n" + 85 "run: decrementing ESP to 0xbd000060\n" 86 "run: pushing value 0x00000003\n" 87 "run: jumping to 0x000000a3\n" 88 ); @@ -151,14 +151,14 @@ if ('onhashchange' in window) { 91 //:: ret 92 93 :(before "End Initialize Op Names") - 94 put_new(Name, "c3", "return from most recent unfinished call (ret)"); + 94 put_new(Name, "c3", "return from most recent unfinished call (ret)"); 95 96 :(code) 97 void test_ret() { - 98 Mem.push_back(vma(0xbd000000)); // manually allocate memory - 99 Reg[ESP].u = 0xbd000064; -100 write_mem_u32(Reg[ESP].u, 0x10); -101 run( + 98 Mem.push_back(vma(0xbd000000)); // manually allocate memory + 99 Reg[ESP].u = 0xbd000064; +100 write_mem_u32(Reg[ESP].u, 0x10); +101 run( 102 "== code 0x1\n" 103 // op ModR/M SIB displacement immediate 104 " c3 \n" // return @@ -176,8 +176,8 @@ if ('onhashchange' in window) { 116 case 0xc3: { // return from a call 117 trace(Callstack_depth+1, "run") << "return" << end(); 118 --Callstack_depth; -119 EIP = pop(); -120 trace(Callstack_depth+1, "run") << "jumping to 0x" << HEXWORD << EIP << end(); +119 EIP = pop(); +120 trace(Callstack_depth+1, "run") << "jumping to 0x" << HEXWORD << EIP << end(); 121 break; 122 } diff --git a/html/020syscalls.cc.html b/html/020syscalls.cc.html index b903c5b8..ae1ee687 100644 --- a/html/020syscalls.cc.html +++ b/html/020syscalls.cc.html @@ -59,14 +59,14 @@ if ('onhashchange' in window) { https://github.com/akkartik/mu/blob/master/020syscalls.cc
   1 :(before "End Initialize Op Names")
-  2 put_new(Name, "cd", "software interrupt (int)");
+  2 put_new(Name, "cd", "software interrupt (int)");
   3 
   4 :(before "End Single-Byte Opcodes")
   5 case 0xcd: {  // int imm8 (software interrupt)
   6   trace(Callstack_depth+1, "run") << "syscall" << end();
-  7   uint8_t code = next();
+  7   uint8_t code = next();
   8   if (code != 0x80) {
-  9     raise << "Unimplemented interrupt code " << HEXBYTE << code << '\n' << end();
+  9     raise << "Unimplemented interrupt code " << HEXBYTE << code << '\n' << end();
  10     raise << "  Only `int 80h` supported for now.\n" << end();
  11     break;
  12   }
@@ -76,91 +76,91 @@ if ('onhashchange' in window) {
  16 
  17 :(code)
  18 void process_int80() {
- 19   switch (Reg[EAX].u) {
+ 19   switch (Reg[EAX].u) {
  20   case 1:
- 21     exit(/*exit code*/Reg[EBX].u);
+ 21     exit(/*exit code*/Reg[EBX].u);
  22     break;
  23   case 3:
- 24     trace(Callstack_depth+1, "run") << "read: " << Reg[EBX].u << ' ' << Reg[ECX].u << ' ' << Reg[EDX].u << end();
- 25     Reg[EAX].i = read(/*file descriptor*/Reg[EBX].u, /*memory buffer*/mem_addr_u8(Reg[ECX].u), /*size*/Reg[EDX].u);
- 26     trace(Callstack_depth+1, "run") << "result: " << Reg[EAX].i << end();
- 27     if (Reg[EAX].i == -1) raise << "read: " << strerror(errno) << '\n' << end();
+ 24     trace(Callstack_depth+1, "run") << "read: " << Reg[EBX].u << ' ' << Reg[ECX].u << ' ' << Reg[EDX].u << end();
+ 25     Reg[EAX].i = read(/*file descriptor*/Reg[EBX].u, /*memory buffer*/mem_addr_u8(Reg[ECX].u), /*size*/Reg[EDX].u);
+ 26     trace(Callstack_depth+1, "run") << "result: " << Reg[EAX].i << end();
+ 27     if (Reg[EAX].i == -1) raise << "read: " << strerror(errno) << '\n' << end();
  28     break;
  29   case 4:
- 30     trace(Callstack_depth+1, "run") << "write: " << Reg[EBX].u << ' ' << Reg[ECX].u << ' ' << Reg[EDX].u << end();
- 31     trace(Callstack_depth+1, "run") << Reg[ECX].u << " => " << mem_addr_string(Reg[ECX].u, Reg[EDX].u) << end();
- 32     Reg[EAX].i = write(/*file descriptor*/Reg[EBX].u, /*memory buffer*/mem_addr_u8(Reg[ECX].u), /*size*/Reg[EDX].u);
- 33     trace(Callstack_depth+1, "run") << "result: " << Reg[EAX].i << end();
- 34     if (Reg[EAX].i == -1) raise << "write: " << strerror(errno) << '\n' << end();
+ 30     trace(Callstack_depth+1, "run") << "write: " << Reg[EBX].u << ' ' << Reg[ECX].u << ' ' << Reg[EDX].u << end();
+ 31     trace(Callstack_depth+1, "run") << Reg[ECX].u << " => " << mem_addr_string(Reg[ECX].u, Reg[EDX].u) << end();
+ 32     Reg[EAX].i = write(/*file descriptor*/Reg[EBX].u, /*memory buffer*/mem_addr_u8(Reg[ECX].u), /*size*/Reg[EDX].u);
+ 33     trace(Callstack_depth+1, "run") << "result: " << Reg[EAX].i << end();
+ 34     if (Reg[EAX].i == -1) raise << "write: " << strerror(errno) << '\n' << end();
  35     break;
  36   case 5: {
- 37     check_flags(ECX);
- 38     check_mode(EDX);
- 39     trace(Callstack_depth+1, "run") << "open: " << Reg[EBX].u << ' ' << Reg[ECX].u << end();
- 40     trace(Callstack_depth+1, "run") << Reg[EBX].u << " => " << mem_addr_kernel_string(Reg[EBX].u) << end();
- 41     Reg[EAX].i = open(/*filename*/mem_addr_kernel_string(Reg[EBX].u), /*flags*/Reg[ECX].u, /*mode*/0640);
- 42     trace(Callstack_depth+1, "run") << "result: " << Reg[EAX].i << end();
- 43     if (Reg[EAX].i == -1) raise << "open: " << strerror(errno) << '\n' << end();
+ 37     check_flags(ECX);
+ 38     check_mode(EDX);
+ 39     trace(Callstack_depth+1, "run") << "open: " << Reg[EBX].u << ' ' << Reg[ECX].u << end();
+ 40     trace(Callstack_depth+1, "run") << Reg[EBX].u << " => " << mem_addr_kernel_string(Reg[EBX].u) << end();
+ 41     Reg[EAX].i = open(/*filename*/mem_addr_kernel_string(Reg[EBX].u), /*flags*/Reg[ECX].u, /*mode*/0640);
+ 42     trace(Callstack_depth+1, "run") << "result: " << Reg[EAX].i << end();
+ 43     if (Reg[EAX].i == -1) raise << "open: " << strerror(errno) << '\n' << end();
  44     break;
  45   }
  46   case 6:
- 47     trace(Callstack_depth+1, "run") << "close: " << Reg[EBX].u << end();
- 48     Reg[EAX].i = close(/*file descriptor*/Reg[EBX].u);
- 49     trace(Callstack_depth+1, "run") << "result: " << Reg[EAX].i << end();
- 50     if (Reg[EAX].i == -1) raise << "close: " << strerror(errno) << '\n' << end();
+ 47     trace(Callstack_depth+1, "run") << "close: " << Reg[EBX].u << end();
+ 48     Reg[EAX].i = close(/*file descriptor*/Reg[EBX].u);
+ 49     trace(Callstack_depth+1, "run") << "result: " << Reg[EAX].i << end();
+ 50     if (Reg[EAX].i == -1) raise << "close: " << strerror(errno) << '\n' << end();
  51     break;
  52   case 8:
- 53     check_mode(ECX);
- 54     trace(Callstack_depth+1, "run") << "creat: " << Reg[EBX].u << end();
- 55     trace(Callstack_depth+1, "run") << Reg[EBX].u << " => " << mem_addr_kernel_string(Reg[EBX].u) << end();
- 56     Reg[EAX].i = creat(/*filename*/mem_addr_kernel_string(Reg[EBX].u), /*mode*/0640);
- 57     trace(Callstack_depth+1, "run") << "result: " << Reg[EAX].i << end();
- 58     if (Reg[EAX].i == -1) raise << "creat: " << strerror(errno) << '\n' << end();
+ 53     check_mode(ECX);
+ 54     trace(Callstack_depth+1, "run") << "creat: " << Reg[EBX].u << end();
+ 55     trace(Callstack_depth+1, "run") << Reg[EBX].u << " => " << mem_addr_kernel_string(Reg[EBX].u) << end();
+ 56     Reg[EAX].i = creat(/*filename*/mem_addr_kernel_string(Reg[EBX].u), /*mode*/0640);
+ 57     trace(Callstack_depth+1, "run") << "result: " << Reg[EAX].i << end();
+ 58     if (Reg[EAX].i == -1) raise << "creat: " << strerror(errno) << '\n' << end();
  59     break;
  60   case 10:
- 61     trace(Callstack_depth+1, "run") << "unlink: " << Reg[EBX].u << end();
- 62     trace(Callstack_depth+1, "run") << Reg[EBX].u << " => " << mem_addr_kernel_string(Reg[EBX].u) << end();
- 63     Reg[EAX].i = unlink(/*filename*/mem_addr_kernel_string(Reg[EBX].u));
- 64     trace(Callstack_depth+1, "run") << "result: " << Reg[EAX].i << end();
- 65     if (Reg[EAX].i == -1) raise << "unlink: " << strerror(errno) << '\n' << end();
+ 61     trace(Callstack_depth+1, "run") << "unlink: " << Reg[EBX].u << end();
+ 62     trace(Callstack_depth+1, "run") << Reg[EBX].u << " => " << mem_addr_kernel_string(Reg[EBX].u) << end();
+ 63     Reg[EAX].i = unlink(/*filename*/mem_addr_kernel_string(Reg[EBX].u));
+ 64     trace(Callstack_depth+1, "run") << "result: " << Reg[EAX].i << end();
+ 65     if (Reg[EAX].i == -1) raise << "unlink: " << strerror(errno) << '\n' << end();
  66     break;
  67   case 38:
- 68     trace(Callstack_depth+1, "run") << "rename: " << Reg[EBX].u << " -> " << Reg[ECX].u << end();
- 69     trace(Callstack_depth+1, "run") << Reg[EBX].u << " => " << mem_addr_kernel_string(Reg[EBX].u) << end();
- 70     trace(Callstack_depth+1, "run") << Reg[ECX].u << " => " << mem_addr_kernel_string(Reg[ECX].u) << end();
- 71     Reg[EAX].i = rename(/*old filename*/mem_addr_kernel_string(Reg[EBX].u), /*new filename*/mem_addr_kernel_string(Reg[ECX].u));
- 72     trace(Callstack_depth+1, "run") << "result: " << Reg[EAX].i << end();
- 73     if (Reg[EAX].i == -1) raise << "rename: " << strerror(errno) << '\n' << end();
+ 68     trace(Callstack_depth+1, "run") << "rename: " << Reg[EBX].u << " -> " << Reg[ECX].u << end();
+ 69     trace(Callstack_depth+1, "run") << Reg[EBX].u << " => " << mem_addr_kernel_string(Reg[EBX].u) << end();
+ 70     trace(Callstack_depth+1, "run") << Reg[ECX].u << " => " << mem_addr_kernel_string(Reg[ECX].u) << end();
+ 71     Reg[EAX].i = rename(/*old filename*/mem_addr_kernel_string(Reg[EBX].u), /*new filename*/mem_addr_kernel_string(Reg[ECX].u));
+ 72     trace(Callstack_depth+1, "run") << "result: " << Reg[EAX].i << end();
+ 73     if (Reg[EAX].i == -1) raise << "rename: " << strerror(errno) << '\n' << end();
  74     break;
  75   case 90:  // mmap: allocate memory outside existing segment allocations
  76     trace(Callstack_depth+1, "run") << "mmap: allocate new segment" << end();
  77     // Ignore most arguments for now: address hint, protection flags, sharing flags, fd, offset.
  78     // We only support anonymous maps.
- 79     Reg[EAX].u = new_segment(/*length*/read_mem_u32(Reg[EBX].u+0x4));
- 80     trace(Callstack_depth+1, "run") << "result: " << Reg[EAX].u << end();
+ 79     Reg[EAX].u = new_segment(/*length*/read_mem_u32(Reg[EBX].u+0x4));
+ 80     trace(Callstack_depth+1, "run") << "result: " << Reg[EAX].u << end();
  81     break;
  82   default:
- 83     raise << HEXWORD << EIP << ": unimplemented syscall " << Reg[EAX].u << '\n' << end();
+ 83     raise << HEXWORD << EIP << ": unimplemented syscall " << Reg[EAX].u << '\n' << end();
  84   }
  85 }
  86 
  87 // SubX is oblivious to file permissions, directories, symbolic links, terminals, and much else besides.
  88 // Also ignoring any concurrency considerations for now.
- 89 void check_flags(int reg) {
- 90   uint32_t flags = Reg[reg].u;
+ 89 void check_flags(int reg) {
+ 90   uint32_t flags = Reg[reg].u;
  91   if (flags != ((flags & O_RDONLY) | (flags & O_WRONLY))) {
- 92     cerr << HEXWORD << EIP << ": most POSIX flags to the open() syscall are not supported. Just O_RDONLY and O_WRONLY for now. Zero concurrent access support.\n";
+ 92     cerr << HEXWORD << EIP << ": most POSIX flags to the open() syscall are not supported. Just O_RDONLY and O_WRONLY for now. Zero concurrent access support.\n";
  93     exit(1);
  94   }
  95   if ((flags & O_RDONLY) && (flags & O_WRONLY)) {
- 96     cerr << HEXWORD << EIP << ": can't open a file for both reading and writing at once. See http://man7.org/linux/man-pages/man2/open.2.html.\n";
+ 96     cerr << HEXWORD << EIP << ": can't open a file for both reading and writing at once. See http://man7.org/linux/man-pages/man2/open.2.html.\n";
  97     exit(1);
  98   }
  99 }
 100 
-101 void check_mode(int reg) {
-102   if (Reg[reg].u != 0600) {
-103     cerr << HEXWORD << EIP << ": SubX is oblivious to file permissions; register " << reg << " must be 0.\n";
+101 void check_mode(int reg) {
+102   if (Reg[reg].u != 0600) {
+103     cerr << HEXWORD << EIP << ": SubX is oblivious to file permissions; register " << reg << " must be 0.\n";
 104     exit(1);
 105   }
 106 }
@@ -175,9 +175,9 @@ if ('onhashchange' in window) {
 115   uint32_t result = (Segments_allocated_above - length) & 0xff000000;  // same number of zeroes as SEGMENT_ALIGNMENT
 116   if (result <= START_HEAP) {
 117     raise << "Allocated too many segments; the VM ran out of memory. "
-118           << "Maybe SEGMENT_ALIGNMENT can be smaller?\n" << die();
+118           << "Maybe SEGMENT_ALIGNMENT can be smaller?\n" << die();
 119   }
-120   Mem.push_back(vma(result, result+length));
+120   Mem.push_back(vma(result, result+length));
 121   Segments_allocated_above = result;
 122   return result;
 123 }
diff --git a/html/021byte_addressing.cc.html b/html/021byte_addressing.cc.html
index c8e11d91..7ba3f488 100644
--- a/html/021byte_addressing.cc.html
+++ b/html/021byte_addressing.cc.html
@@ -86,24 +86,24 @@ if ('onhashchange' in window) {
  27     return reg_8bit(rm);
  28   }
  29   // the rest is as usual
- 30   return mem_addr_u8(effective_address_number(modrm));
+ 30   return mem_addr_u8(effective_address_number(modrm));
  31 }
  32 
  33 uint8_t* reg_8bit(uint8_t rm) {
- 34   uint8_t* result = reinterpret_cast<uint8_t*>(&Reg[rm & 0x3].i);  // _L register
+ 34   uint8_t* result = reinterpret_cast<uint8_t*>(&Reg[rm & 0x3].i);  // _L register
  35   if (rm & 0x4)
  36     ++result;  // _H register;  assumes host is little-endian
  37   return result;
  38 }
  39 
  40 :(before "End Initialize Op Names")
- 41 put_new(Name, "88", "copy r8 to r8/m8-at-r32");
+ 41 put_new(Name, "88", "copy r8 to r8/m8-at-r32");
  42 
  43 :(code)
  44 void test_copy_r8_to_mem_at_r32() {
- 45   Reg[EBX].i = 0x224488ab;
- 46   Reg[EAX].i = 0x2000;
- 47   run(
+ 45   Reg[EBX].i = 0x224488ab;
+ 46   Reg[EAX].i = 0x2000;
+ 47   run(
  48       "== code 0x1\n"
  49       // op     ModR/M  SIB   displacement  immediate
  50       "  88     18                                      \n"  // copy BL to the byte at *EAX
@@ -113,35 +113,35 @@ if ('onhashchange' in window) {
  54   );
  55   CHECK_TRACE_CONTENTS(
  56       "run: copy BL to r8/m8-at-r32\n"
- 57       "run: effective address is 0x00002000 (EAX)\n"
+ 57       "run: effective address is 0x00002000 (EAX)\n"
  58       "run: storing 0xab\n"
  59   );
- 60   CHECK_EQ(0xaabbccab, read_mem_u32(0x2000));
+ 60   CHECK_EQ(0xaabbccab, read_mem_u32(0x2000));
  61 }
  62 
  63 :(before "End Single-Byte Opcodes")
  64 case 0x88: {  // copy r8 to r/m8
- 65   const uint8_t modrm = next();
+ 65   const uint8_t modrm = next();
  66   const uint8_t rsrc = (modrm>>3)&0x7;
  67   trace(Callstack_depth+1, "run") << "copy " << rname_8bit(rsrc) << " to r8/m8-at-r32" << end();
  68   // use unsigned to zero-extend 8-bit value to 32 bits
  69   uint8_t* dest = reinterpret_cast<uint8_t*>(effective_byte_address(modrm));
  70   const uint8_t* src = reg_8bit(rsrc);
  71   *dest = *src;
- 72   trace(Callstack_depth+1, "run") << "storing 0x" << HEXBYTE << NUM(*dest) << end();
+ 72   trace(Callstack_depth+1, "run") << "storing 0x" << HEXBYTE << NUM(*dest) << end();
  73   break;
  74 }
  75 
  76 //:
  77 
  78 :(before "End Initialize Op Names")
- 79 put_new(Name, "8a", "copy r8/m8-at-r32 to r8");
+ 79 put_new(Name, "8a", "copy r8/m8-at-r32 to r8");
  80 
  81 :(code)
  82 void test_copy_mem_at_r32_to_r8() {
- 83   Reg[EBX].i = 0xaabbcc0f;  // one nibble each of lowest byte set to all 0s and all 1s, to maximize value of this test
- 84   Reg[EAX].i = 0x2000;
- 85   run(
+ 83   Reg[EBX].i = 0xaabbcc0f;  // one nibble each of lowest byte set to all 0s and all 1s, to maximize value of this test
+ 84   Reg[EAX].i = 0x2000;
+ 85   run(
  86       "== code 0x1\n"
  87       // op     ModR/M  SIB   displacement  immediate
  88       "  8a     18                                      \n"  // copy just the byte at *EAX to BL
@@ -151,33 +151,33 @@ if ('onhashchange' in window) {
  92   );
  93   CHECK_TRACE_CONTENTS(
  94       "run: copy r8/m8-at-r32 to BL\n"
- 95       "run: effective address is 0x00002000 (EAX)\n"
+ 95       "run: effective address is 0x00002000 (EAX)\n"
  96       "run: storing 0xab\n"
  97       // remaining bytes of EBX are *not* cleared
- 98       "run: EBX now contains 0xaabbccab\n"
+ 98       "run: EBX now contains 0xaabbccab\n"
  99   );
 100 }
 101 
 102 :(before "End Single-Byte Opcodes")
 103 case 0x8a: {  // copy r/m8 to r8
-104   const uint8_t modrm = next();
+104   const uint8_t modrm = next();
 105   const uint8_t rdest = (modrm>>3)&0x7;
 106   trace(Callstack_depth+1, "run") << "copy r8/m8-at-r32 to " << rname_8bit(rdest) << end();
 107   // use unsigned to zero-extend 8-bit value to 32 bits
 108   const uint8_t* src = reinterpret_cast<uint8_t*>(effective_byte_address(modrm));
 109   uint8_t* dest = reg_8bit(rdest);
-110   trace(Callstack_depth+1, "run") << "storing 0x" << HEXBYTE << NUM(*src) << end();
+110   trace(Callstack_depth+1, "run") << "storing 0x" << HEXBYTE << NUM(*src) << end();
 111   *dest = *src;
 112   const uint8_t rdest_32bit = rdest & 0x3;
-113   trace(Callstack_depth+1, "run") << rname(rdest_32bit) << " now contains 0x" << HEXWORD << Reg[rdest_32bit].u << end();
+113   trace(Callstack_depth+1, "run") << rname(rdest_32bit) << " now contains 0x" << HEXWORD << Reg[rdest_32bit].u << end();
 114   break;
 115 }
 116 
 117 :(code)
 118 void test_cannot_copy_byte_to_ESP_EBP_ESI_EDI() {
-119   Reg[ESI].u = 0xaabbccdd;
-120   Reg[EBX].u = 0x11223344;
-121   run(
+119   Reg[ESI].u = 0xaabbccdd;
+120   Reg[EBX].u = 0x11223344;
+121   run(
 122       "== code 0x1\n"
 123       // op     ModR/M  SIB   displacement  immediate
 124       "  8a     f3                                      \n"  // copy just the byte at *EBX to 8-bit register '6'
@@ -189,18 +189,18 @@ if ('onhashchange' in window) {
 130       "run: storing 0x44\n"
 131   );
 132   // ensure ESI is unchanged
-133   CHECK_EQ(Reg[ESI].u, 0xaabbccdd);
+133   CHECK_EQ(Reg[ESI].u, 0xaabbccdd);
 134 }
 135 
 136 //:
 137 
 138 :(before "End Initialize Op Names")
-139 put_new(Name, "c6", "copy imm8 to r8/m8-at-r32 (mov)");
+139 put_new(Name, "c6", "copy imm8 to r8/m8-at-r32 (mov)");
 140 
 141 :(code)
 142 void test_copy_imm8_to_mem_at_r32() {
-143   Reg[EAX].i = 0x2000;
-144   run(
+143   Reg[EAX].i = 0x2000;
+144   run(
 145       "== code 0x1\n"
 146       // op     ModR/M  SIB   displacement  immediate
 147       "  c6     00                          dd          \n"  // copy to the byte at *EAX
@@ -210,27 +210,27 @@ if ('onhashchange' in window) {
 151   );
 152   CHECK_TRACE_CONTENTS(
 153       "run: copy imm8 to r8/m8-at-r32\n"
-154       "run: effective address is 0x00002000 (EAX)\n"
+154       "run: effective address is 0x00002000 (EAX)\n"
 155       "run: storing 0xdd\n"
 156   );
-157   CHECK_EQ(0xaabbccdd, read_mem_u32(0x2000));
+157   CHECK_EQ(0xaabbccdd, read_mem_u32(0x2000));
 158 }
 159 
 160 :(before "End Single-Byte Opcodes")
 161 case 0xc6: {  // copy imm8 to r/m8
-162   const uint8_t modrm = next();
-163   const uint8_t src = next();
+162   const uint8_t modrm = next();
+163   const uint8_t src = next();
 164   trace(Callstack_depth+1, "run") << "copy imm8 to r8/m8-at-r32" << end();
-165   trace(Callstack_depth+1, "run") << "imm8 is 0x" << HEXWORD << NUM(src) << end();
+165   trace(Callstack_depth+1, "run") << "imm8 is 0x" << HEXWORD << NUM(src) << end();
 166   const uint8_t subop = (modrm>>3)&0x7;  // middle 3 'reg opcode' bits
 167   if (subop != 0) {
-168     cerr << "unrecognized subop for opcode c6: " << NUM(subop) << " (only 0/copy currently implemented)\n";
+168     cerr << "unrecognized subop for opcode c6: " << NUM(subop) << " (only 0/copy currently implemented)\n";
 169     exit(1);
 170   }
 171   // use unsigned to zero-extend 8-bit value to 32 bits
 172   uint8_t* dest = reinterpret_cast<uint8_t*>(effective_byte_address(modrm));
 173   *dest = src;
-174   trace(Callstack_depth+1, "run") << "storing 0x" << HEXBYTE << NUM(*dest) << end();
+174   trace(Callstack_depth+1, "run") << "storing 0x" << HEXBYTE << NUM(*dest) << end();
 175   break;
 176 }
 
diff --git a/html/022div.cc.html b/html/022div.cc.html index 5fa65b16..256cb1be 100644 --- a/html/022div.cc.html +++ b/html/022div.cc.html @@ -59,39 +59,39 @@ if ('onhashchange' in window) { 1 //: helper for division operations: sign-extend EAX into EDX 2 3 :(before "End Initialize Op Names") - 4 put_new(Name, "99", "sign-extend EAX into EDX (cdq)"); + 4 put_new(Name, "99", "sign-extend EAX into EDX (cdq)"); 5 6 :(code) 7 void test_cdq() { - 8 Reg[EAX].i = 10; - 9 run( + 8 Reg[EAX].i = 10; + 9 run( 10 "== code 0x1\n" 11 "99\n" 12 ); 13 CHECK_TRACE_CONTENTS( -14 "run: sign-extend EAX into EDX\n" -15 "run: EDX is now 0x00000000\n" +14 "run: sign-extend EAX into EDX\n" +15 "run: EDX is now 0x00000000\n" 16 ); 17 } 18 19 :(before "End Single-Byte Opcodes") 20 case 0x99: { // sign-extend EAX into EDX -21 trace(Callstack_depth+1, "run") << "sign-extend EAX into EDX" << end(); -22 Reg[EDX].i = (Reg[EAX].i < 0) ? -1 : 0; -23 trace(Callstack_depth+1, "run") << "EDX is now 0x" << HEXWORD << Reg[EDX].u << end(); +21 trace(Callstack_depth+1, "run") << "sign-extend EAX into EDX" << end(); +22 Reg[EDX].i = (Reg[EAX].i < 0) ? -1 : 0; +23 trace(Callstack_depth+1, "run") << "EDX is now 0x" << HEXWORD << Reg[EDX].u << end(); 24 break; 25 } 26 27 :(code) 28 void test_cdq_negative() { -29 Reg[EAX].i = -10; -30 run( +29 Reg[EAX].i = -10; +30 run( 31 "== code 0x1\n" 32 "99\n" 33 ); 34 CHECK_TRACE_CONTENTS( -35 "run: sign-extend EAX into EDX\n" -36 "run: EDX is now 0xffffffff\n" +35 "run: sign-extend EAX into EDX\n" +36 "run: EDX is now 0xffffffff\n" 37 ); 38 } diff --git a/html/030---translate.cc.html b/html/030---translate.cc.html index c2f9e993..0055f063 100644 --- a/html/030---translate.cc.html +++ b/html/030---translate.cc.html @@ -59,219 +59,212 @@ if ('onhashchange' in window) { https://github.com/akkartik/mu/blob/master/030---translate.cc
-  1 //: The bedrock level 1 of abstraction is now done, and we're going to start
-  2 //: building levels above it that make programming in x86 machine code a
-  3 //: little more ergonomic.
-  4 //:
-  5 //: All levels will be "pass through by default". Whatever they don't
-  6 //: understand they will silently pass through to lower levels.
-  7 //:
-  8 //: Since raw hex bytes of machine code are always possible to inject, SubX is
-  9 //: not a language, and we aren't building a compiler. This is something
- 10 //: deliberately leakier. Levels are more for improving auditing, checks and
- 11 //: error messages rather than for hiding low-level details.
- 12 
- 13 //: Translator workflow: read 'source' file. Run a series of transforms on it,
- 14 //: each passing through what it doesn't understand. The final program should
- 15 //: be just machine code, suitable to write to an ELF binary.
- 16 //:
- 17 //: Higher levels usually transform code on the basis of metadata.
- 18 
- 19 :(before "End Main")
- 20 if (is_equal(argv[1], "translate")) {
- 21   // Outside of tests, traces must be explicitly requested.
- 22   if (Trace_file.is_open()) Trace_stream = new trace_stream;
- 23   reset();
- 24   // Begin subx translate
- 25   program p;
- 26   string output_filename;
- 27   for (int i = /*skip 'subx translate'*/2;  i < argc;  ++i) {
- 28     if (is_equal(argv[i], "-o")) {
- 29       ++i;
- 30       if (i >= argc) {
- 31         print_translate_usage();
- 32         cerr << "'-o' must be followed by a filename to write results to\n";
- 33         exit(1);
- 34       }
- 35       output_filename = argv[i];
- 36     }
- 37     else {
- 38       trace(2, "parse") << argv[i] << end();
- 39       ifstream fin(argv[i]);
- 40       if (!fin) {
- 41         cerr << "could not open " << argv[i] << '\n';
- 42         return 1;
- 43       }
- 44       parse(fin, p);
- 45       if (trace_contains_errors()) return 1;
- 46     }
- 47   }
- 48   if (p.segments.empty()) {
- 49     print_translate_usage();
- 50     cerr << "nothing to do; must provide at least one file to read\n";
- 51     exit(1);
- 52   }
- 53   if (output_filename.empty()) {
- 54     print_translate_usage();
- 55     cerr << "must provide a filename to write to using '-o'\n";
- 56     exit(1);
- 57   }
- 58   trace(2, "transform") << "begin" << end();
- 59   transform(p);
- 60   if (trace_contains_errors()) return 1;
- 61   trace(2, "translate") << "begin" << end();
- 62   save_elf(p, output_filename);
- 63   if (trace_contains_errors()) {
- 64     unlink(output_filename.c_str());
- 65     return 1;
- 66   }
- 67   // End subx translate
- 68   return 0;
- 69 }
- 70 
- 71 :(code)
- 72 void print_translate_usage() {
- 73   cerr << "Usage: subx translate file1 file2 ... -o output\n";
+  1 //: After that lengthy prelude to define an x86 emulator, we are now ready to
+  2 //: start translating SubX notation.
+  3 
+  4 //: Translator workflow: read 'source' file. Run a series of transforms on it,
+  5 //: each passing through what it doesn't understand. The final program should
+  6 //: be just machine code, suitable to emulate, or to write to an ELF binary.
+  7 
+  8 :(before "End Main")
+  9 if (is_equal(argv[1], "translate")) {
+ 10   // Outside of tests, traces must be explicitly requested.
+ 11   if (Trace_file.is_open()) Trace_stream = new trace_stream;
+ 12   reset();
+ 13   // Begin bootstrap translate
+ 14   program p;
+ 15   string output_filename;
+ 16   for (int i = /*skip 'bootstrap translate'*/2;  i < argc;  ++i) {
+ 17     if (is_equal(argv[i], "-o")) {
+ 18       ++i;
+ 19       if (i >= argc) {
+ 20         print_translate_usage();
+ 21         cerr << "'-o' must be followed by a filename to write results to\n";
+ 22         exit(1);
+ 23       }
+ 24       output_filename = argv[i];
+ 25     }
+ 26     else {
+ 27       trace(2, "parse") << argv[i] << end();
+ 28       ifstream fin(argv[i]);
+ 29       if (!fin) {
+ 30         cerr << "could not open " << argv[i] << '\n';
+ 31         return 1;
+ 32       }
+ 33       parse(fin, p);
+ 34       if (trace_contains_errors()) return 1;
+ 35     }
+ 36   }
+ 37   if (p.segments.empty()) {
+ 38     print_translate_usage();
+ 39     cerr << "nothing to do; must provide at least one file to read\n";
+ 40     exit(1);
+ 41   }
+ 42   if (output_filename.empty()) {
+ 43     print_translate_usage();
+ 44     cerr << "must provide a filename to write to using '-o'\n";
+ 45     exit(1);
+ 46   }
+ 47   trace(2, "transform") << "begin" << end();
+ 48   transform(p);
+ 49   if (trace_contains_errors()) return 1;
+ 50   trace(2, "translate") << "begin" << end();
+ 51   save_elf(p, output_filename);
+ 52   if (trace_contains_errors()) {
+ 53     unlink(output_filename.c_str());
+ 54     return 1;
+ 55   }
+ 56   // End bootstrap translate
+ 57   return 0;
+ 58 }
+ 59 
+ 60 :(code)
+ 61 void transform(program& p) {
+ 62   // End transform(program& p)
+ 63 }
+ 64 
+ 65 void print_translate_usage() {
+ 66   cerr << "Usage: bootstrap translate file1 file2 ... -o output\n";
+ 67 }
+ 68 
+ 69 // write out a program to a bare-bones ELF file
+ 70 void save_elf(const program& p, const string& filename) {
+ 71   ofstream out(filename.c_str(), ios::binary);
+ 72   save_elf(p, out);
+ 73   out.close();
  74 }
  75 
- 76 // write out a program to a bare-bones ELF file
- 77 void save_elf(const program& p, const string& filename) {
- 78   ofstream out(filename.c_str(), ios::binary);
- 79   save_elf(p, out);
- 80   out.close();
- 81 }
- 82 
- 83 void save_elf(const program& p, ostream& out) {
- 84   // validation: stay consistent with the self-hosted translator
- 85   if (p.entry == 0) {
- 86     raise << "no 'Entry' label found\n" << end();
- 87     return;
- 88   }
- 89   if (find(p, "data") == NULL) {
- 90     raise << "must include a 'data' segment\n" << end();
- 91     return;
- 92   }
- 93   // processing
- 94   write_elf_header(out, p);
- 95   for (size_t i = 0;  i < p.segments.size();  ++i)
- 96     write_segment(p.segments.at(i), out);
- 97 }
- 98 
- 99 void write_elf_header(ostream& out, const program& p) {
-100   char c = '\0';
-101 #define O(X)  c = (X); out.write(&c, sizeof(c))
-102 // host is required to be little-endian
-103 #define emit(X)  out.write(reinterpret_cast<const char*>(&X), sizeof(X))
-104   //// ehdr
-105   // e_ident
-106   O(0x7f); O(/*E*/0x45); O(/*L*/0x4c); O(/*F*/0x46);
-107     O(0x1);  // 32-bit format
-108     O(0x1);  // little-endian
-109     O(0x1); O(0x0);
-110   for (size_t i = 0;  i < 8;  ++i) { O(0x0); }
-111   // e_type
-112   O(0x02); O(0x00);
-113   // e_machine
-114   O(0x03); O(0x00);
-115   // e_version
-116   O(0x01); O(0x00); O(0x00); O(0x00);
-117   // e_entry
-118   uint32_t e_entry = p.entry;
-119   // Override e_entry
-120   emit(e_entry);
-121   // e_phoff -- immediately after ELF header
-122   uint32_t e_phoff = 0x34;
-123   emit(e_phoff);
-124   // e_shoff; unused
-125   uint32_t dummy32 = 0;
-126   emit(dummy32);
-127   // e_flags; unused
-128   emit(dummy32);
-129   // e_ehsize
-130   uint16_t e_ehsize = 0x34;
-131   emit(e_ehsize);
-132   // e_phentsize
-133   uint16_t e_phentsize = 0x20;
-134   emit(e_phentsize);
-135   // e_phnum
-136   uint16_t e_phnum = SIZE(p.segments);
-137   emit(e_phnum);
-138   // e_shentsize
-139   uint16_t dummy16 = 0x0;
-140   emit(dummy16);
-141   // e_shnum
-142   emit(dummy16);
-143   // e_shstrndx
-144   emit(dummy16);
-145 
-146   uint32_t p_offset = /*size of ehdr*/0x34 + SIZE(p.segments)*0x20/*size of each phdr*/;
-147   for (int i = 0;  i < SIZE(p.segments);  ++i) {
-148     const segment& curr = p.segments.at(i);
-149     //// phdr
-150     // p_type
-151     uint32_t p_type = 0x1;
-152     emit(p_type);
-153     // p_offset
-154     emit(p_offset);
-155     // p_vaddr
-156     uint32_t p_start = curr.start;
-157     emit(p_start);
-158     // p_paddr
-159     emit(p_start);
-160     // p_filesz
-161     uint32_t size = num_words(curr);
-162     assert(p_offset + size < SEGMENT_ALIGNMENT);
-163     emit(size);
-164     // p_memsz
-165     emit(size);
-166     // p_flags
-167     uint32_t p_flags = (curr.name == "code") ? /*r-x*/0x5 : /*rw-*/0x6;
-168     emit(p_flags);
-169 
-170     // p_align
-171     // "As the system creates or augments a process image, it logically copies
-172     // a file's segment to a virtual memory segment.  When—and if— the system
-173     // physically reads the file depends on the program's execution behavior,
-174     // system load, and so on.  A process does not require a physical page
-175     // unless it references the logical page during execution, and processes
-176     // commonly leave many pages unreferenced. Therefore delaying physical
-177     // reads frequently obviates them, improving system performance. To obtain
-178     // this efficiency in practice, executable and shared object files must
-179     // have segment images whose file offsets and virtual addresses are
-180     // congruent, modulo the page size." -- http://refspecs.linuxbase.org/elf/elf.pdf (page 95)
-181     uint32_t p_align = 0x1000;  // default page size on linux
-182     emit(p_align);
-183     if (p_offset % p_align != p_start % p_align) {
-184       raise << "segment starting at 0x" << HEXWORD << p_start << " is improperly aligned; alignment for p_offset " << p_offset << " should be " << (p_offset % p_align) << " but is " << (p_start % p_align) << '\n' << end();
-185       return;
-186     }
+ 76 void save_elf(const program& p, ostream& out) {
+ 77   // validation: stay consistent with the self-hosted translator
+ 78   if (p.entry == 0) {
+ 79     raise << "no 'Entry' label found\n" << end();
+ 80     return;
+ 81   }
+ 82   if (find(p, "data") == NULL) {
+ 83     raise << "must include a 'data' segment\n" << end();
+ 84     return;
+ 85   }
+ 86   // processing
+ 87   write_elf_header(out, p);
+ 88   for (size_t i = 0;  i < p.segments.size();  ++i)
+ 89     write_segment(p.segments.at(i), out);
+ 90 }
+ 91 
+ 92 void write_elf_header(ostream& out, const program& p) {
+ 93   char c = '\0';
+ 94 #define O(X)  c = (X); out.write(&c, sizeof(c))
+ 95 // host is required to be little-endian
+ 96 #define emit(X)  out.write(reinterpret_cast<const char*>(&X), sizeof(X))
+ 97   //// ehdr
+ 98   // e_ident
+ 99   O(0x7f); O(/*E*/0x45); O(/*L*/0x4c); O(/*F*/0x46);
+100     O(0x1);  // 32-bit format
+101     O(0x1);  // little-endian
+102     O(0x1); O(0x0);
+103   for (size_t i = 0;  i < 8;  ++i) { O(0x0); }
+104   // e_type
+105   O(0x02); O(0x00);
+106   // e_machine
+107   O(0x03); O(0x00);
+108   // e_version
+109   O(0x01); O(0x00); O(0x00); O(0x00);
+110   // e_entry
+111   uint32_t e_entry = p.entry;
+112   // Override e_entry
+113   emit(e_entry);
+114   // e_phoff -- immediately after ELF header
+115   uint32_t e_phoff = 0x34;
+116   emit(e_phoff);
+117   // e_shoff; unused
+118   uint32_t dummy32 = 0;
+119   emit(dummy32);
+120   // e_flags; unused
+121   emit(dummy32);
+122   // e_ehsize
+123   uint16_t e_ehsize = 0x34;
+124   emit(e_ehsize);
+125   // e_phentsize
+126   uint16_t e_phentsize = 0x20;
+127   emit(e_phentsize);
+128   // e_phnum
+129   uint16_t e_phnum = SIZE(p.segments);
+130   emit(e_phnum);
+131   // e_shentsize
+132   uint16_t dummy16 = 0x0;
+133   emit(dummy16);
+134   // e_shnum
+135   emit(dummy16);
+136   // e_shstrndx
+137   emit(dummy16);
+138 
+139   uint32_t p_offset = /*size of ehdr*/0x34 + SIZE(p.segments)*0x20/*size of each phdr*/;
+140   for (int i = 0;  i < SIZE(p.segments);  ++i) {
+141     const segment& curr = p.segments.at(i);
+142     //// phdr
+143     // p_type
+144     uint32_t p_type = 0x1;
+145     emit(p_type);
+146     // p_offset
+147     emit(p_offset);
+148     // p_vaddr
+149     uint32_t p_start = curr.start;
+150     emit(p_start);
+151     // p_paddr
+152     emit(p_start);
+153     // p_filesz
+154     uint32_t size = num_words(curr);
+155     assert(p_offset + size < SEGMENT_ALIGNMENT);
+156     emit(size);
+157     // p_memsz
+158     emit(size);
+159     // p_flags
+160     uint32_t p_flags = (curr.name == "code") ? /*r-x*/0x5 : /*rw-*/0x6;
+161     emit(p_flags);
+162 
+163     // p_align
+164     // "As the system creates or augments a process image, it logically copies
+165     // a file's segment to a virtual memory segment.  When—and if— the system
+166     // physically reads the file depends on the program's execution behavior,
+167     // system load, and so on.  A process does not require a physical page
+168     // unless it references the logical page during execution, and processes
+169     // commonly leave many pages unreferenced. Therefore delaying physical
+170     // reads frequently obviates them, improving system performance. To obtain
+171     // this efficiency in practice, executable and shared object files must
+172     // have segment images whose file offsets and virtual addresses are
+173     // congruent, modulo the page size." -- http://refspecs.linuxbase.org/elf/elf.pdf (page 95)
+174     uint32_t p_align = 0x1000;  // default page size on linux
+175     emit(p_align);
+176     if (p_offset % p_align != p_start % p_align) {
+177       raise << "segment starting at 0x" << HEXWORD << p_start << " is improperly aligned; alignment for p_offset " << p_offset << " should be " << (p_offset % p_align) << " but is " << (p_start % p_align) << '\n' << end();
+178       return;
+179     }
+180 
+181     // prepare for next segment
+182     p_offset += size;
+183   }
+184 #undef O
+185 #undef emit
+186 }
 187 
-188     // prepare for next segment
-189     p_offset += size;
-190   }
-191 #undef O
-192 #undef emit
-193 }
-194 
-195 void write_segment(const segment& s, ostream& out) {
-196   for (int i = 0;  i < SIZE(s.lines);  ++i) {
-197     const vector<word>& w = s.lines.at(i).words;
-198     for (int j = 0;  j < SIZE(w);  ++j) {
-199       uint8_t x = hex_byte(w.at(j).data);  // we're done with metadata by this point
-200       out.write(reinterpret_cast<const char*>(&x), /*sizeof(byte)*/1);
-201     }
-202   }
+188 void write_segment(const segment& s, ostream& out) {
+189   for (int i = 0;  i < SIZE(s.lines);  ++i) {
+190     const vector<word>& w = s.lines.at(i).words;
+191     for (int j = 0;  j < SIZE(w);  ++j) {
+192       uint8_t x = hex_byte(w.at(j).data);  // we're done with metadata by this point
+193       out.write(reinterpret_cast<const char*>(&x), /*sizeof(byte)*/1);
+194     }
+195   }
+196 }
+197 
+198 uint32_t num_words(const segment& s) {
+199   uint32_t sum = 0;
+200   for (int i = 0;  i < SIZE(s.lines);  ++i)
+201     sum += SIZE(s.lines.at(i).words);
+202   return sum;
 203 }
 204 
-205 uint32_t num_words(const segment& s) {
-206   uint32_t sum = 0;
-207   for (int i = 0;  i < SIZE(s.lines);  ++i)
-208     sum += SIZE(s.lines.at(i).words);
-209   return sum;
-210 }
-211 
-212 :(before "End Includes")
-213 using std::ios;
+205 :(before "End Includes")
+206 using std::ios;
 
diff --git a/html/031transforms.cc.html b/html/031transforms.cc.html index 2589975b..73e7b823 100644 --- a/html/031transforms.cc.html +++ b/html/031transforms.cc.html @@ -15,8 +15,11 @@ body { font-size:12pt; font-family: monospace; color: #000000; background-color: a { color:inherit; } * { font-size:12pt; font-size: 1em; } .LineNr { } +.Constant { color: #008787; } .Comment { color: #005faf; } .Delimiter { color: #c000c0; } +.Special { color: #d70000; } +.Normal { color: #000000; background-color: #c6c6c6; padding-bottom: 1px; } --> @@ -52,71 +55,18 @@ if ('onhashchange' in window) { https://github.com/akkartik/mu/blob/master/031transforms.cc
- 1 //: Ordering transforms is a well-known hard problem when building compilers.
- 2 //: In our case we also have the additional notion of layers. The ordering of
- 3 //: layers can have nothing in common with the ordering of transforms when
- 4 //: SubX is tangled and run. This can be confusing for readers, particularly
- 5 //: if later layers start inserting transforms at arbitrary points between
- 6 //: transforms introduced earlier. Over time adding transforms can get harder
- 7 //: and harder, having to meet the constraints of everything that's come
- 8 //: before. It's worth thinking about organization up-front so the ordering is
- 9 //: easy to hold in our heads, and it's obvious where to add a new transform.
-10 //: Some constraints:
-11 //:
-12 //:   1. Layers force us to build SubX bottom-up; since we want to be able to
-13 //:   build and run SubX after stopping loading at any layer, the overall
-14 //:   organization has to be to introduce primitives before we start using
-15 //:   them.
-16 //:
-17 //:   2. Transforms usually need to be run top-down, converting high-level
-18 //:   representations to low-level ones so that low-level layers can be
-19 //:   oblivious to them.
-20 //:
-21 //:   3. When running we'd often like new representations to be checked before
-22 //:   they are transformed away. The whole reason for new representations is
-23 //:   often to add new kinds of automatic checking for our machine code
-24 //:   programs.
-25 //:
-26 //: Putting these constraints together, we'll use the following broad
-27 //: organization:
-28 //:
-29 //:   a) We'll divide up our transforms into "levels", each level consisting
-30 //:   of multiple transforms, and dealing in some new set of representational
-31 //:   ideas. Levels will be added in reverse order to the one their transforms
-32 //:   will be run in.
-33 //:
-34 //:     To run all transforms:
-35 //:       Load transforms for level n
-36 //:       Load transforms for level n-1
-37 //:       ...
-38 //:       Load transforms for level 2
-39 //:       Run code at level 1
-40 //:
-41 //:   b) *Within* a level we'll usually introduce transforms in the order
-42 //:   they're run in.
-43 //:
-44 //:     To run transforms for level n:
-45 //:       Perform transform of layer l
-46 //:       Perform transform of layer l+1
-47 //:       ...
-48 //:
-49 //:   c) Within a level it's often most natural to introduce a new
-50 //:   representation by showing how it's transformed to the level below. To
-51 //:   make such exceptions more obvious checks usually won't be first-class
-52 //:   transforms; instead code that keeps the program unmodified will run
-53 //:   within transforms before they mutate the program. As an example:
-54 //:
-55 //:     Layer l introduces a transform
-56 //:     Layer l+1 adds precondition checks for the transform
-57 //:
-58 //: This may all seem abstract, but will hopefully make sense over time. The
-59 //: goals are basically to always have a working program after any layer, to
-60 //: have the order of layers make narrative sense, and to order transforms
-61 //: correctly at runtime.
-62 
-63 :(before "End One-time Setup")
-64 // Begin Transforms
-65 // End Transforms
+ 1 :(before "End Types")
+ 2 typedef void (*transform_fn)(program&);
+ 3 :(before "End Globals")
+ 4 vector<transform_fn> Transform;
+ 5 
+ 6 :(before "End transform(program& p)")
+ 7 for (int t = 0;  t < SIZE(Transform);  ++t)
+ 8   (*Transform.at(t))(p);
+ 9 
+10 :(before "End One-time Setup")
+11 // Begin Transforms
+12 // End Transforms
 
diff --git a/html/032---operands.cc.html b/html/032---operands.cc.html index ac421edd..30d288ce 100644 --- a/html/032---operands.cc.html +++ b/html/032---operands.cc.html @@ -58,545 +58,546 @@ if ('onhashchange' in window) { https://github.com/akkartik/mu/blob/master/032---operands.cc
-  1 //: Beginning of "level 2": tagging bytes with metadata around what field of
-  2 //: an x86 instruction they're for.
-  3 //:
-  4 //: The x86 instruction set is variable-length, and how a byte is interpreted
-  5 //: affects later instruction boundaries. A lot of the pain in programming
-  6 //: machine code stems from computer and programmer going out of sync on what
-  7 //: a byte means. The miscommunication is usually not immediately caught, and
-  8 //: metastasizes at runtime into kilobytes of misinterpreted instructions.
-  9 //:
- 10 //: To mitigate these issues, we'll start programming in terms of logical
- 11 //: operands rather than physical bytes. Some operands are smaller than a
- 12 //: byte, and others may consist of multiple bytes. This layer will correctly
- 13 //: pack and order the bytes corresponding to the operands in an instruction.
- 14 
- 15 :(before "End Help Texts")
- 16 put_new(Help, "instructions",
- 17   "Each x86 instruction consists of an instruction or opcode and some number\n"
- 18   "of operands.\n"
- 19   "Each operand has a type. An instruction won't have more than one operand of\n"
- 20   "any type.\n"
- 21   "Each instruction has some set of allowed operand types. It'll reject others.\n"
- 22   "The complete list of operand types: mod, subop, r32 (register), rm32\n"
- 23   "(register or memory), scale, index, base, disp8, disp16, disp32, imm8,\n"
- 24   "imm32.\n"
- 25   "Each of these has its own help page. Try reading 'subx help mod' next.\n"
- 26 );
- 27 :(before "End Help Contents")
- 28 cerr << "  instructions\n";
- 29 
- 30 :(code)
- 31 void test_pack_immediate_constants() {
- 32   run(
- 33       "== code 0x1\n"
- 34       "bb  0x2a/imm32\n"
- 35   );
- 36   CHECK_TRACE_CONTENTS(
- 37       "transform: packing instruction 'bb 0x2a/imm32'\n"
- 38       "transform: instruction after packing: 'bb 2a 00 00 00'\n"
- 39       "run: copy imm32 0x0000002a to EBX\n"
- 40   );
- 41 }
- 42 
- 43 //: complete set of valid operand types
- 44 
- 45 :(before "End Globals")
- 46 set<string> Instruction_operands;
- 47 :(before "End One-time Setup")
- 48 Instruction_operands.insert("subop");
- 49 Instruction_operands.insert("mod");
- 50 Instruction_operands.insert("rm32");
- 51 Instruction_operands.insert("base");
- 52 Instruction_operands.insert("index");
- 53 Instruction_operands.insert("scale");
- 54 Instruction_operands.insert("r32");
- 55 Instruction_operands.insert("disp8");
- 56 Instruction_operands.insert("disp16");
- 57 Instruction_operands.insert("disp32");
- 58 Instruction_operands.insert("imm8");
- 59 Instruction_operands.insert("imm32");
- 60 
- 61 :(before "End Help Texts")
- 62 init_operand_type_help();
- 63 :(code)
- 64 void init_operand_type_help() {
- 65   put(Help, "mod",
- 66     "2-bit operand controlling the _addressing mode_ of many instructions,\n"
- 67     "to determine how to compute the _effective address_ to look up memory at\n"
- 68     "based on the 'rm32' operand and potentially others.\n"
- 69     "\n"
- 70     "If mod = 3, just operate on the contents of the register specified by rm32\n"
- 71     "            (direct mode).\n"
- 72     "If mod = 2, effective address is usually* rm32 + disp32\n"
- 73     "            (indirect mode with displacement).\n"
- 74     "If mod = 1, effective address is usually* rm32 + disp8\n"
- 75     "            (indirect mode with displacement).\n"
- 76     "If mod = 0, effective address is usually* rm32 (indirect mode).\n"
- 77     "(* - The exception is when rm32 is '4'. Register 4 is the stack pointer (ESP).\n"
- 78     "     Using it as an address gets more involved. For more details,\n"
- 79     "     try reading the help pages for 'base', 'index' and 'scale'.)\n"
- 80     "\n"
- 81     "For complete details, spend some time with two tables in the IA-32 software\n"
- 82     "developer's manual that are also included in this repo:\n"
- 83     "  - modrm.pdf: volume 2, table 2-2, \"32-bit addressing with the ModR/M byte.\".\n"
- 84     "  - sib.pdf: volume 2, table 2-3, \"32-bit addressing with the SIB byte.\".\n"
- 85   );
- 86   put(Help, "subop",
- 87     "Additional 3-bit operand for determining the instruction when the opcode\n"
- 88     "is 81, 8f, d3, f7 or ff.\n"
- 89     "Can't coexist with operand of type 'r32' in a single instruction, because\n"
- 90     "the two use the same bits.\n"
- 91   );
- 92   put(Help, "r32",
- 93     "3-bit operand specifying a register operand used directly, without any further addressing modes.\n"
+  1 //: Metadata for fields of an x86 instruction.
+  2 //:
+  3 //: The x86 instruction set is variable-length, and how a byte is interpreted
+  4 //: affects later instruction boundaries. A lot of the pain in programming
+  5 //: machine code stems from computer and programmer going out of sync on what
+  6 //: a byte means. The miscommunication is usually not immediately caught, and
+  7 //: metastasizes at runtime into kilobytes of misinterpreted instructions.
+  8 //:
+  9 //: To mitigate these issues, we'll start programming in terms of logical
+ 10 //: operands rather than physical bytes. Some operands are smaller than a
+ 11 //: byte, and others may consist of multiple bytes. This layer will correctly
+ 12 //: pack and order the bytes corresponding to the operands in an instruction.
+ 13 
+ 14 :(before "End Help Texts")
+ 15 put_new(Help, "instructions",
+ 16   "Each x86 instruction consists of an instruction or opcode and some number\n"
+ 17   "of operands.\n"
+ 18   "Each operand has a type. An instruction won't have more than one operand of\n"
+ 19   "any type.\n"
+ 20   "Each instruction has some set of allowed operand types. It'll reject others.\n"
+ 21   "The complete list of operand types: mod, subop, r32 (register), rm32\n"
+ 22   "(register or memory), scale, index, base, disp8, disp16, disp32, imm8,\n"
+ 23   "imm32.\n"
+ 24   "Each of these has its own help page. Try reading 'bootstrap help mod' next.\n"
+ 25 );
+ 26 :(before "End Help Contents")
+ 27 cerr << "  instructions\n";
+ 28 
+ 29 :(before "Running Test Program")
+ 30 transform(p);
+ 31 if (trace_contains_errors()) return;
+ 32 
+ 33 :(code)
+ 34 void test_pack_immediate_constants() {
+ 35   run(
+ 36       "== code 0x1\n"
+ 37       "bb  0x2a/imm32\n"
+ 38   );
+ 39   CHECK_TRACE_CONTENTS(
+ 40       "transform: packing instruction 'bb 0x2a/imm32'\n"
+ 41       "transform: instruction after packing: 'bb 2a 00 00 00'\n"
+ 42       "run: copy imm32 0x0000002a to EBX\n"
+ 43   );
+ 44 }
+ 45 
+ 46 //: complete set of valid operand types
+ 47 
+ 48 :(before "End Globals")
+ 49 set<string> Instruction_operands;
+ 50 :(before "End One-time Setup")
+ 51 Instruction_operands.insert("subop");
+ 52 Instruction_operands.insert("mod");
+ 53 Instruction_operands.insert("rm32");
+ 54 Instruction_operands.insert("base");
+ 55 Instruction_operands.insert("index");
+ 56 Instruction_operands.insert("scale");
+ 57 Instruction_operands.insert("r32");
+ 58 Instruction_operands.insert("disp8");
+ 59 Instruction_operands.insert("disp16");
+ 60 Instruction_operands.insert("disp32");
+ 61 Instruction_operands.insert("imm8");
+ 62 Instruction_operands.insert("imm32");
+ 63 
+ 64 :(before "End Help Texts")
+ 65 init_operand_type_help();
+ 66 :(code)
+ 67 void init_operand_type_help() {
+ 68   put(Help, "mod",
+ 69     "2-bit operand controlling the _addressing mode_ of many instructions,\n"
+ 70     "to determine how to compute the _effective address_ to look up memory at\n"
+ 71     "based on the 'rm32' operand and potentially others.\n"
+ 72     "\n"
+ 73     "If mod = 3, just operate on the contents of the register specified by rm32\n"
+ 74     "            (direct mode).\n"
+ 75     "If mod = 2, effective address is usually* rm32 + disp32\n"
+ 76     "            (indirect mode with displacement).\n"
+ 77     "If mod = 1, effective address is usually* rm32 + disp8\n"
+ 78     "            (indirect mode with displacement).\n"
+ 79     "If mod = 0, effective address is usually* rm32 (indirect mode).\n"
+ 80     "(* - The exception is when rm32 is '4'. Register 4 is the stack pointer (ESP).\n"
+ 81     "     Using it as an address gets more involved. For more details,\n"
+ 82     "     try reading the help pages for 'base', 'index' and 'scale'.)\n"
+ 83     "\n"
+ 84     "For complete details, spend some time with two tables in the IA-32 software\n"
+ 85     "developer's manual that are also included in this repo:\n"
+ 86     "  - modrm.pdf: volume 2, table 2-2, \"32-bit addressing with the ModR/M byte.\".\n"
+ 87     "  - sib.pdf: volume 2, table 2-3, \"32-bit addressing with the SIB byte.\".\n"
+ 88   );
+ 89   put(Help, "subop",
+ 90     "Additional 3-bit operand for determining the instruction when the opcode\n"
+ 91     "is 81, 8f, d3, f7 or ff.\n"
+ 92     "Can't coexist with operand of type 'r32' in a single instruction, because\n"
+ 93     "the two use the same bits.\n"
  94   );
- 95   put(Help, "rm32",
- 96     "32-bit value in register or memory. The precise details of its construction\n"
- 97     "depend on the eponymous 3-bit 'rm32' operand, the 'mod' operand, and also\n"
- 98     "potentially the 'SIB' operands ('scale', 'index' and 'base') and a displacement\n"
- 99     "('disp8' or 'disp32').\n"
-100     "\n"
-101     "For complete details, spend some time with two tables in the IA-32 software\n"
-102     "developer's manual that are also included in this repo:\n"
-103     "  - modrm.pdf: volume 2, table 2-2, \"32-bit addressing with the ModR/M byte.\".\n"
-104     "  - sib.pdf: volume 2, table 2-3, \"32-bit addressing with the SIB byte.\".\n"
-105   );
-106   put(Help, "base",
-107     "Additional 3-bit operand (when 'rm32' is 4, unless 'mod' is 3) specifying the\n"
-108     "register containing an address to look up.\n"
-109     "This address may be further modified by 'index' and 'scale' operands.\n"
-110     "  effective address = base + index*scale + displacement (disp8 or disp32)\n"
-111     "For complete details, spend some time with the IA-32 software developer's manual,\n"
-112     "volume 2, table 2-3, \"32-bit addressing with the SIB byte\".\n"
-113     "It is included in this repository as 'sib.pdf'.\n"
-114   );
-115   put(Help, "index",
-116     "Optional 3-bit operand (when 'rm32' is 4 unless 'mod' is 3) that can be added to\n"
-117     "the 'base' operand to compute the 'effective address' at which to look up memory.\n"
-118     "  effective address = base + index*scale + displacement (disp8 or disp32)\n"
-119     "For complete details, spend some time with the IA-32 software developer's manual,\n"
-120     "volume 2, table 2-3, \"32-bit addressing with the SIB byte\".\n"
-121     "It is included in this repository as 'sib.pdf'.\n"
-122   );
-123   put(Help, "scale",
-124     "Optional 2-bit operand (when 'rm32' is 4 unless 'mod' is 3) that encodes a\n"
-125     "power of 2 to be multiplied to the 'index' operand before adding the result to\n"
-126     "the 'base' operand to compute the _effective address_ to operate on.\n"
-127     "  effective address = base + index * scale + displacement (disp8 or disp32)\n"
-128     "\n"
-129     "When scale is 0, use index unmodified.\n"
-130     "When scale is 1, multiply index by 2.\n"
-131     "When scale is 2, multiply index by 4.\n"
-132     "When scale is 3, multiply index by 8.\n"
-133     "\n"
-134     "For complete details, spend some time with the IA-32 software developer's manual,\n"
-135     "volume 2, table 2-3, \"32-bit addressing with the SIB byte\".\n"
-136     "It is included in this repository as 'sib.pdf'.\n"
-137   );
-138   put(Help, "disp8",
-139     "8-bit value to be added in many instructions.\n"
+ 95   put(Help, "r32",
+ 96     "3-bit operand specifying a register operand used directly, without any further addressing modes.\n"
+ 97   );
+ 98   put(Help, "rm32",
+ 99     "32-bit value in register or memory. The precise details of its construction\n"
+100     "depend on the eponymous 3-bit 'rm32' operand, the 'mod' operand, and also\n"
+101     "potentially the 'SIB' operands ('scale', 'index' and 'base') and a displacement\n"
+102     "('disp8' or 'disp32').\n"
+103     "\n"
+104     "For complete details, spend some time with two tables in the IA-32 software\n"
+105     "developer's manual that are also included in this repo:\n"
+106     "  - modrm.pdf: volume 2, table 2-2, \"32-bit addressing with the ModR/M byte.\".\n"
+107     "  - sib.pdf: volume 2, table 2-3, \"32-bit addressing with the SIB byte.\".\n"
+108   );
+109   put(Help, "base",
+110     "Additional 3-bit operand (when 'rm32' is 4, unless 'mod' is 3) specifying the\n"
+111     "register containing an address to look up.\n"
+112     "This address may be further modified by 'index' and 'scale' operands.\n"
+113     "  effective address = base + index*scale + displacement (disp8 or disp32)\n"
+114     "For complete details, spend some time with the IA-32 software developer's manual,\n"
+115     "volume 2, table 2-3, \"32-bit addressing with the SIB byte\".\n"
+116     "It is included in this repository as 'sib.pdf'.\n"
+117   );
+118   put(Help, "index",
+119     "Optional 3-bit operand (when 'rm32' is 4 unless 'mod' is 3) that can be added to\n"
+120     "the 'base' operand to compute the 'effective address' at which to look up memory.\n"
+121     "  effective address = base + index*scale + displacement (disp8 or disp32)\n"
+122     "For complete details, spend some time with the IA-32 software developer's manual,\n"
+123     "volume 2, table 2-3, \"32-bit addressing with the SIB byte\".\n"
+124     "It is included in this repository as 'sib.pdf'.\n"
+125   );
+126   put(Help, "scale",
+127     "Optional 2-bit operand (when 'rm32' is 4 unless 'mod' is 3) that encodes a\n"
+128     "power of 2 to be multiplied to the 'index' operand before adding the result to\n"
+129     "the 'base' operand to compute the _effective address_ to operate on.\n"
+130     "  effective address = base + index * scale + displacement (disp8 or disp32)\n"
+131     "\n"
+132     "When scale is 0, use index unmodified.\n"
+133     "When scale is 1, multiply index by 2.\n"
+134     "When scale is 2, multiply index by 4.\n"
+135     "When scale is 3, multiply index by 8.\n"
+136     "\n"
+137     "For complete details, spend some time with the IA-32 software developer's manual,\n"
+138     "volume 2, table 2-3, \"32-bit addressing with the SIB byte\".\n"
+139     "It is included in this repository as 'sib.pdf'.\n"
 140   );
-141   put(Help, "disp16",
-142     "16-bit value to be added in many instructions.\n"
-143     "Currently not used in any SubX instructions.\n"
-144   );
-145   put(Help, "disp32",
-146     "32-bit value to be added in many instructions.\n"
+141   put(Help, "disp8",
+142     "8-bit value to be added in many instructions.\n"
+143   );
+144   put(Help, "disp16",
+145     "16-bit value to be added in many instructions.\n"
+146     "Currently not used in any SubX instructions.\n"
 147   );
-148   put(Help, "imm8",
-149     "8-bit value for many instructions.\n"
+148   put(Help, "disp32",
+149     "32-bit value to be added in many instructions.\n"
 150   );
-151   put(Help, "imm32",
-152     "32-bit value for many instructions.\n"
+151   put(Help, "imm8",
+152     "8-bit value for many instructions.\n"
 153   );
-154 }
-155 
-156 //:: transform packing operands into bytes in the right order
-157 
-158 :(after "Begin Transforms")
-159 // Begin Level-2 Transforms
-160 Transform.push_back(pack_operands);
-161 // End Level-2 Transforms
-162 
-163 :(code)
-164 void pack_operands(program& p) {
-165   if (p.segments.empty()) return;
-166   segment& code = *find(p, "code");
-167   // Pack Operands(segment code)
-168   trace(3, "transform") << "-- pack operands" << end();
-169   for (int i = 0;  i < SIZE(code.lines);  ++i) {
-170     line& inst = code.lines.at(i);
-171     if (all_hex_bytes(inst)) continue;
-172     trace(99, "transform") << "packing instruction '" << to_string(/*with metadata*/inst) << "'" << end();
-173     pack_operands(inst);
-174     trace(99, "transform") << "instruction after packing: '" << to_string(/*without metadata*/inst.words) << "'" << end();
-175   }
-176 }
-177 
-178 void pack_operands(line& inst) {
-179   line new_inst;
-180   add_opcodes(inst, new_inst);
-181   add_modrm_byte(inst, new_inst);
-182   add_sib_byte(inst, new_inst);
-183   add_disp_bytes(inst, new_inst);
-184   add_imm_bytes(inst, new_inst);
-185   inst.words.swap(new_inst.words);
-186 }
-187 
-188 void add_opcodes(const line& in, line& out) {
-189   out.words.push_back(in.words.at(0));
-190   if (in.words.at(0).data == "0f" || in.words.at(0).data == "f2" || in.words.at(0).data == "f3")
-191     out.words.push_back(in.words.at(1));
-192   if (in.words.at(0).data == "f3" && in.words.at(1).data == "0f")
-193     out.words.push_back(in.words.at(2));
-194   if (in.words.at(0).data == "f2" && in.words.at(1).data == "0f")
-195     out.words.push_back(in.words.at(2));
-196 }
-197 
-198 void add_modrm_byte(const line& in, line& out) {
-199   uint8_t mod=0, reg_subop=0, rm32=0;
-200   bool emit = false;
-201   for (int i = 0;  i < SIZE(in.words);  ++i) {
-202     const word& curr = in.words.at(i);
-203     if (has_operand_metadata(curr, "mod")) {
-204       mod = hex_byte(curr.data);
-205       emit = true;
-206     }
-207     else if (has_operand_metadata(curr, "rm32")) {
-208       rm32 = hex_byte(curr.data);
-209       emit = true;
-210     }
-211     else if (has_operand_metadata(curr, "r32")) {
-212       reg_subop = hex_byte(curr.data);
-213       emit = true;
-214     }
-215     else if (has_operand_metadata(curr, "subop")) {
-216       reg_subop = hex_byte(curr.data);
-217       emit = true;
-218     }
-219   }
-220   if (emit)
-221     out.words.push_back(hex_byte_text((mod << 6) | (reg_subop << 3) | rm32));
-222 }
-223 
-224 void add_sib_byte(const line& in, line& out) {
-225   uint8_t scale=0, index=0, base=0;
-226   bool emit = false;
-227   for (int i = 0;  i < SIZE(in.words);  ++i) {
-228     const word& curr = in.words.at(i);
-229     if (has_operand_metadata(curr, "scale")) {
-230       scale = hex_byte(curr.data);
-231       emit = true;
-232     }
-233     else if (has_operand_metadata(curr, "index")) {
-234       index = hex_byte(curr.data);
-235       emit = true;
-236     }
-237     else if (has_operand_metadata(curr, "base")) {
-238       base = hex_byte(curr.data);
-239       emit = true;
-240     }
-241   }
-242   if (emit)
-243     out.words.push_back(hex_byte_text((scale << 6) | (index << 3) | base));
-244 }
-245 
-246 void add_disp_bytes(const line& in, line& out) {
-247   for (int i = 0;  i < SIZE(in.words);  ++i) {
-248     const word& curr = in.words.at(i);
-249     if (has_operand_metadata(curr, "disp8"))
-250       emit_hex_bytes(out, curr, 1);
-251     if (has_operand_metadata(curr, "disp16"))
-252       emit_hex_bytes(out, curr, 2);
-253     else if (has_operand_metadata(curr, "disp32"))
-254       emit_hex_bytes(out, curr, 4);
-255   }
-256 }
-257 
-258 void add_imm_bytes(const line& in, line& out) {
-259   for (int i = 0;  i < SIZE(in.words);  ++i) {
-260     const word& curr = in.words.at(i);
-261     if (has_operand_metadata(curr, "imm8"))
-262       emit_hex_bytes(out, curr, 1);
-263     else if (has_operand_metadata(curr, "imm32"))
-264       emit_hex_bytes(out, curr, 4);
-265   }
-266 }
-267 
-268 void emit_hex_bytes(line& out, const word& w, int num) {
-269   assert(num <= 4);
-270   bool is_number = looks_like_hex_int(w.data);
-271   if (num == 1 || !is_number) {
-272     out.words.push_back(w);  // preserve existing metadata
-273     if (is_number)
-274       out.words.back().data = hex_byte_to_string(parse_int(w.data));
-275     return;
-276   }
-277   emit_hex_bytes(out, static_cast<uint32_t>(parse_int(w.data)), num);
-278 }
-279 
-280 void emit_hex_bytes(line& out, uint32_t val, int num) {
-281   assert(num <= 4);
-282   for (int i = 0;  i < num;  ++i) {
-283     out.words.push_back(hex_byte_text(val & 0xff));
-284     val = val >> 8;
-285   }
-286 }
-287 
-288 word hex_byte_text(uint8_t val) {
-289   word result;
-290   result.data = hex_byte_to_string(val);
-291   result.original = result.data+"/auto";
-292   return result;
-293 }
-294 
-295 string hex_byte_to_string(uint8_t val) {
-296   ostringstream out;
-297   // uint8_t prints without padding, but int8_t will expand to 32 bits again
-298   out << HEXBYTE << NUM(val);
-299   return out.str();
-300 }
-301 
-302 string to_string(const vector<word>& in) {
-303   ostringstream out;
-304   for (int i = 0;  i < SIZE(in);  ++i) {
-305     if (i > 0) out << ' ';
-306     out << in.at(i).data;
-307   }
-308   return out.str();
-309 }
-310 
-311 :(before "End Unit Tests")
-312 void test_preserve_metadata_when_emitting_single_byte() {
-313   word in;
-314   in.data = "f0";
-315   in.original = "f0/foo";
-316   line out;
-317   emit_hex_bytes(out, in, 1);
-318   CHECK_EQ(out.words.at(0).data, "f0");
-319   CHECK_EQ(out.words.at(0).original, "f0/foo");
-320 }
-321 
-322 :(code)
-323 void test_pack_disp8() {
-324   run(
-325       "== code 0x1\n"
-326       "74 2/disp8\n"  // jump 2 bytes away if ZF is set
-327   );
-328   CHECK_TRACE_CONTENTS(
-329       "transform: packing instruction '74 2/disp8'\n"
-330       "transform: instruction after packing: '74 02'\n"
-331   );
-332 }
-333 
-334 void test_pack_disp8_negative() {
-335   transform(
-336       "== code 0x1\n"
-337       // running this will cause an infinite loop
-338       "74 -1/disp8\n"  // jump 1 byte before if ZF is set
-339   );
-340   CHECK_TRACE_CONTENTS(
-341       "transform: packing instruction '74 -1/disp8'\n"
-342       "transform: instruction after packing: '74 ff'\n"
-343   );
-344 }
-345 
-346 //: helper for scenario
-347 void transform(const string& text_bytes) {
-348   program p;
-349   istringstream in(text_bytes);
-350   parse(in, p);
-351   if (trace_contains_errors()) return;
-352   transform(p);
-353 }
-354 
-355 void test_pack_modrm_imm32() {
-356   run(
-357       "== code 0x1\n"
-358       // instruction                     effective address                                                   operand     displacement    immediate\n"
-359       // op          subop               mod             rm32          base        index         scale       r32\n"
-360       // 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"
-361       "  81          0/add/subop         3/mod/direct    3/ebx/rm32                                                                      1/imm32      \n"  // add 1 to EBX
-362   );
-363   CHECK_TRACE_CONTENTS(
-364       "transform: packing instruction '81 0/add/subop 3/mod/direct 3/ebx/rm32 1/imm32'\n"
-365       "transform: instruction after packing: '81 c3 01 00 00 00'\n"
-366   );
-367 }
-368 
-369 void test_pack_imm32_large() {
-370   run(
-371       "== code 0x1\n"
-372       "b9  0x080490a7/imm32\n"
-373   );
-374   CHECK_TRACE_CONTENTS(
-375       "transform: packing instruction 'b9 0x080490a7/imm32'\n"
-376       "transform: instruction after packing: 'b9 a7 90 04 08'\n"
-377   );
-378 }
-379 
-380 void test_pack_immediate_constants_hex() {
-381   run(
-382       "== code 0x1\n"
-383       "b9  0x2a/imm32\n"
-384   );
-385   CHECK_TRACE_CONTENTS(
-386       "transform: packing instruction 'b9 0x2a/imm32'\n"
-387       "transform: instruction after packing: 'b9 2a 00 00 00'\n"
-388       "run: copy imm32 0x0000002a to ECX\n"
-389   );
-390 }
-391 
-392 void test_pack_silently_ignores_non_hex() {
-393   Hide_errors = true;
-394   transform(
-395       "== code 0x1\n"
-396       "b9  foo/imm32\n"
-397   );
-398   CHECK_TRACE_CONTENTS(
-399       "transform: packing instruction 'b9 foo/imm32'\n"
-400       // no change (we're just not printing metadata to the trace)
-401       "transform: instruction after packing: 'b9 foo'\n"
-402   );
-403 }
-404 
-405 void test_pack_flags_bad_hex() {
-406   Hide_errors = true;
-407   run(
-408       "== code 0x1\n"
-409       "b9  0xfoo/imm32\n"
-410   );
-411   CHECK_TRACE_CONTENTS(
-412       "error: not a number: 0xfoo\n"
-413   );
-414 }
-415 
-416 void test_pack_flags_uppercase_hex() {
-417   Hide_errors = true;
-418   run(
-419       "== code 0x1\n"
-420       "b9 0xAb/imm32\n"
-421   );
-422   CHECK_TRACE_CONTENTS(
-423       "error: uppercase hex not allowed: 0xAb\n"
-424   );
-425 }
-426 
-427 //:: helpers
-428 
-429 bool all_hex_bytes(const line& inst) {
-430   for (int i = 0;  i < SIZE(inst.words);  ++i)
-431     if (!is_hex_byte(inst.words.at(i)))
-432       return false;
-433   return true;
-434 }
-435 
-436 bool is_hex_byte(const word& curr) {
-437   if (contains_any_operand_metadata(curr))
-438     return false;
-439   if (SIZE(curr.data) != 2)
-440     return false;
-441   if (curr.data.find_first_not_of("0123456789abcdef") != string::npos)
-442     return false;
-443   return true;
-444 }
-445 
-446 bool contains_any_operand_metadata(const word& word) {
-447   for (int i = 0;  i < SIZE(word.metadata);  ++i)
-448     if (Instruction_operands.find(word.metadata.at(i)) != Instruction_operands.end())
-449       return true;
-450   return false;
-451 }
-452 
-453 bool has_operand_metadata(const line& inst, const string& m) {
-454   bool result = false;
-455   for (int i = 0;  i < SIZE(inst.words);  ++i) {
-456     if (!has_operand_metadata(inst.words.at(i), m)) continue;
-457     if (result) {
-458       raise << "'" << to_string(inst) << "' has conflicting " << m << " operands\n" << end();
-459       return false;
-460     }
-461     result = true;
-462   }
-463   return result;
-464 }
-465 
-466 bool has_operand_metadata(const word& w, const string& m) {
-467   bool result = false;
-468   bool metadata_found = false;
-469   for (int i = 0;  i < SIZE(w.metadata);  ++i) {
-470     const string& curr = w.metadata.at(i);
-471     if (Instruction_operands.find(curr) == Instruction_operands.end()) continue;  // ignore unrecognized metadata
-472     if (metadata_found) {
-473       raise << "'" << w.original << "' has conflicting operand types; it should have only one\n" << end();
-474       return false;
-475     }
-476     metadata_found = true;
-477     result = (curr == m);
-478   }
-479   return result;
-480 }
-481 
-482 word metadata(const line& inst, const string& m) {
-483   for (int i = 0;  i < SIZE(inst.words);  ++i)
-484     if (has_operand_metadata(inst.words.at(i), m))
-485       return inst.words.at(i);
-486   assert(false);
-487 }
-488 
-489 bool looks_like_hex_int(const string& s) {
-490   if (s.empty()) return false;
-491   if (s.at(0) == '-' || s.at(0) == '+') return true;
-492   if (isdigit(s.at(0))) return true;  // includes '0x' prefix
-493   // End looks_like_hex_int(s) Detectors
-494   return false;
-495 }
-496 
-497 string to_string(const line& inst) {
-498   ostringstream out;
-499   for (int i = 0;  i < SIZE(inst.words);  ++i) {
-500     if (i > 0) out << ' ';
-501     out << inst.words.at(i).original;
-502   }
-503   return out.str();
-504 }
-505 
-506 int32_t parse_int(const string& s) {
-507   if (s.empty()) return 0;
-508   if (contains_uppercase(s)) {
-509     raise << "uppercase hex not allowed: " << s << '\n' << end();
-510     return 0;
-511   }
-512   istringstream in(s);
-513   in >> std::hex;
-514   if (s.at(0) == '-') {
-515     int32_t result = 0;
-516     in >> result;
-517     if (!in || !in.eof()) {
-518       raise << "not a number: " << s << '\n' << end();
-519       return 0;
-520     }
-521     return result;
-522   }
-523   uint32_t uresult = 0;
-524   in >> uresult;
-525   if (!in || !in.eof()) {
-526     raise << "not a number: " << s << '\n' << end();
-527     return 0;
-528   }
-529   return static_cast<int32_t>(uresult);
-530 }
-531 :(before "End Unit Tests")
-532 void test_parse_int() {
-533   CHECK_EQ(0, parse_int("0"));
-534   CHECK_EQ(0, parse_int("0x0"));
-535   CHECK_EQ(0, parse_int("0x0"));
-536   CHECK_EQ(16, parse_int("10"));  // hex always
-537   CHECK_EQ(-1, parse_int("-1"));
-538   CHECK_EQ(-1, parse_int("0xffffffff"));
-539 }
+154   put(Help, "imm32",
+155     "32-bit value for many instructions.\n"
+156   );
+157 }
+158 
+159 //:: transform packing operands into bytes in the right order
+160 
+161 :(after "Begin Transforms")
+162 Transform.push_back(pack_operands);
+163 
+164 :(code)
+165 void pack_operands(program& p) {
+166   if (p.segments.empty()) return;
+167   segment& code = *find(p, "code");
+168   // Pack Operands(segment code)
+169   trace(3, "transform") << "-- pack operands" << end();
+170   for (int i = 0;  i < SIZE(code.lines);  ++i) {
+171     line& inst = code.lines.at(i);
+172     if (all_hex_bytes(inst)) continue;
+173     trace(99, "transform") << "packing instruction '" << to_string(/*with metadata*/inst) << "'" << end();
+174     pack_operands(inst);
+175     trace(99, "transform") << "instruction after packing: '" << to_string(/*without metadata*/inst.words) << "'" << end();
+176   }
+177 }
+178 
+179 void pack_operands(line& inst) {
+180   line new_inst;
+181   add_opcodes(inst, new_inst);
+182   add_modrm_byte(inst, new_inst);
+183   add_sib_byte(inst, new_inst);
+184   add_disp_bytes(inst, new_inst);
+185   add_imm_bytes(inst, new_inst);
+186   inst.words.swap(new_inst.words);
+187 }
+188 
+189 void add_opcodes(const line& in, line& out) {
+190   out.words.push_back(in.words.at(0));
+191   if (in.words.at(0).data == "0f" || in.words.at(0).data == "f2" || in.words.at(0).data == "f3")
+192     out.words.push_back(in.words.at(1));
+193   if (in.words.at(0).data == "f3" && in.words.at(1).data == "0f")
+194     out.words.push_back(in.words.at(2));
+195   if (in.words.at(0).data == "f2" && in.words.at(1).data == "0f")
+196     out.words.push_back(in.words.at(2));
+197 }
+198 
+199 void add_modrm_byte(const line& in, line& out) {
+200   uint8_t mod=0, reg_subop=0, rm32=0;
+201   bool emit = false;
+202   for (int i = 0;  i < SIZE(in.words);  ++i) {
+203     const word& curr = in.words.at(i);
+204     if (has_operand_metadata(curr, "mod")) {
+205       mod = hex_byte(curr.data);
+206       emit = true;
+207     }
+208     else if (has_operand_metadata(curr, "rm32")) {
+209       rm32 = hex_byte(curr.data);
+210       emit = true;
+211     }
+212     else if (has_operand_metadata(curr, "r32")) {
+213       reg_subop = hex_byte(curr.data);
+214       emit = true;
+215     }
+216     else if (has_operand_metadata(curr, "subop")) {
+217       reg_subop = hex_byte(curr.data);
+218       emit = true;
+219     }
+220   }
+221   if (emit)
+222     out.words.push_back(hex_byte_text((mod << 6) | (reg_subop << 3) | rm32));
+223 }
+224 
+225 void add_sib_byte(const line& in, line& out) {
+226   uint8_t scale=0, index=0, base=0;
+227   bool emit = false;
+228   for (int i = 0;  i < SIZE(in.words);  ++i) {
+229     const word& curr = in.words.at(i);
+230     if (has_operand_metadata(curr, "scale")) {
+231       scale = hex_byte(curr.data);
+232       emit = true;
+233     }
+234     else if (has_operand_metadata(curr, "index")) {
+235       index = hex_byte(curr.data);
+236       emit = true;
+237     }
+238     else if (has_operand_metadata(curr, "base")) {
+239       base = hex_byte(curr.data);
+240       emit = true;
+241     }
+242   }
+243   if (emit)
+244     out.words.push_back(hex_byte_text((scale << 6) | (index << 3) | base));
+245 }
+246 
+247 void add_disp_bytes(const line& in, line& out) {
+248   for (int i = 0;  i < SIZE(in.words);  ++i) {
+249     const word& curr = in.words.at(i);
+250     if (has_operand_metadata(curr, "disp8"))
+251       emit_hex_bytes(out, curr, 1);
+252     if (has_operand_metadata(curr, "disp16"))
+253       emit_hex_bytes(out, curr, 2);
+254     else if (has_operand_metadata(curr, "disp32"))
+255       emit_hex_bytes(out, curr, 4);
+256   }
+257 }
+258 
+259 void add_imm_bytes(const line& in, line& out) {
+260   for (int i = 0;  i < SIZE(in.words);  ++i) {
+261     const word& curr = in.words.at(i);
+262     if (has_operand_metadata(curr, "imm8"))
+263       emit_hex_bytes(out, curr, 1);
+264     else if (has_operand_metadata(curr, "imm32"))
+265       emit_hex_bytes(out, curr, 4);
+266   }
+267 }
+268 
+269 void emit_hex_bytes(line& out, const word& w, int num) {
+270   assert(num <= 4);
+271   bool is_number = looks_like_hex_int(w.data);
+272   if (num == 1 || !is_number) {
+273     out.words.push_back(w);  // preserve existing metadata
+274     if (is_number)
+275       out.words.back().data = hex_byte_to_string(parse_int(w.data));
+276     return;
+277   }
+278   emit_hex_bytes(out, static_cast<uint32_t>(parse_int(w.data)), num);
+279 }
+280 
+281 void emit_hex_bytes(line& out, uint32_t val, int num) {
+282   assert(num <= 4);
+283   for (int i = 0;  i < num;  ++i) {
+284     out.words.push_back(hex_byte_text(val & 0xff));
+285     val = val >> 8;
+286   }
+287 }
+288 
+289 word hex_byte_text(uint8_t val) {
+290   word result;
+291   result.data = hex_byte_to_string(val);
+292   result.original = result.data+"/auto";
+293   return result;
+294 }
+295 
+296 string hex_byte_to_string(uint8_t val) {
+297   ostringstream out;
+298   // uint8_t prints without padding, but int8_t will expand to 32 bits again
+299   out << HEXBYTE << NUM(val);
+300   return out.str();
+301 }
+302 
+303 string to_string(const vector<word>& in) {
+304   ostringstream out;
+305   for (int i = 0;  i < SIZE(in);  ++i) {
+306     if (i > 0) out << ' ';
+307     out << in.at(i).data;
+308   }
+309   return out.str();
+310 }
+311 
+312 :(before "End Unit Tests")
+313 void test_preserve_metadata_when_emitting_single_byte() {
+314   word in;
+315   in.data = "f0";
+316   in.original = "f0/foo";
+317   line out;
+318   emit_hex_bytes(out, in, 1);
+319   CHECK_EQ(out.words.at(0).data, "f0");
+320   CHECK_EQ(out.words.at(0).original, "f0/foo");
+321 }
+322 
+323 :(code)
+324 void test_pack_disp8() {
+325   run(
+326       "== code 0x1\n"
+327       "74 2/disp8\n"  // jump 2 bytes away if ZF is set
+328   );
+329   CHECK_TRACE_CONTENTS(
+330       "transform: packing instruction '74 2/disp8'\n"
+331       "transform: instruction after packing: '74 02'\n"
+332   );
+333 }
+334 
+335 void test_pack_disp8_negative() {
+336   transform(
+337       "== code 0x1\n"
+338       // running this will cause an infinite loop
+339       "74 -1/disp8\n"  // jump 1 byte before if ZF is set
+340   );
+341   CHECK_TRACE_CONTENTS(
+342       "transform: packing instruction '74 -1/disp8'\n"
+343       "transform: instruction after packing: '74 ff'\n"
+344   );
+345 }
+346 
+347 //: helper for scenario
+348 void transform(const string& text_bytes) {
+349   program p;
+350   istringstream in(text_bytes);
+351   parse(in, p);
+352   if (trace_contains_errors()) return;
+353   transform(p);
+354 }
+355 
+356 void test_pack_modrm_imm32() {
+357   run(
+358       "== code 0x1\n"
+359       // instruction                     effective address                                                   operand     displacement    immediate\n"
+360       // op          subop               mod             rm32          base        index         scale       r32\n"
+361       // 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"
+362       "  81          0/add/subop         3/mod/direct    3/ebx/rm32                                                                      1/imm32      \n"  // add 1 to EBX
+363   );
+364   CHECK_TRACE_CONTENTS(
+365       "transform: packing instruction '81 0/add/subop 3/mod/direct 3/ebx/rm32 1/imm32'\n"
+366       "transform: instruction after packing: '81 c3 01 00 00 00'\n"
+367   );
+368 }
+369 
+370 void test_pack_imm32_large() {
+371   run(
+372       "== code 0x1\n"
+373       "b9  0x080490a7/imm32\n"
+374   );
+375   CHECK_TRACE_CONTENTS(
+376       "transform: packing instruction 'b9 0x080490a7/imm32'\n"
+377       "transform: instruction after packing: 'b9 a7 90 04 08'\n"
+378   );
+379 }
+380 
+381 void test_pack_immediate_constants_hex() {
+382   run(
+383       "== code 0x1\n"
+384       "b9  0x2a/imm32\n"
+385   );
+386   CHECK_TRACE_CONTENTS(
+387       "transform: packing instruction 'b9 0x2a/imm32'\n"
+388       "transform: instruction after packing: 'b9 2a 00 00 00'\n"
+389       "run: copy imm32 0x0000002a to ECX\n"
+390   );
+391 }
+392 
+393 void test_pack_silently_ignores_non_hex() {
+394   Hide_errors = true;
+395   transform(
+396       "== code 0x1\n"
+397       "b9  foo/imm32\n"
+398   );
+399   CHECK_TRACE_CONTENTS(
+400       "transform: packing instruction 'b9 foo/imm32'\n"
+401       // no change (we're just not printing metadata to the trace)
+402       "transform: instruction after packing: 'b9 foo'\n"
+403   );
+404 }
+405 
+406 void test_pack_flags_bad_hex() {
+407   Hide_errors = true;
+408   run(
+409       "== code 0x1\n"
+410       "b9  0xfoo/imm32\n"
+411   );
+412   CHECK_TRACE_CONTENTS(
+413       "error: not a number: 0xfoo\n"
+414   );
+415 }
+416 
+417 void test_pack_flags_uppercase_hex() {
+418   Hide_errors = true;
+419   run(
+420       "== code 0x1\n"
+421       "b9 0xAb/imm32\n"
+422   );
+423   CHECK_TRACE_CONTENTS(
+424       "error: uppercase hex not allowed: 0xAb\n"
+425   );
+426 }
+427 
+428 //:: helpers
+429 
+430 bool all_hex_bytes(const line& inst) {
+431   for (int i = 0;  i < SIZE(inst.words);  ++i)
+432     if (!is_hex_byte(inst.words.at(i)))
+433       return false;
+434   return true;
+435 }
+436 
+437 bool is_hex_byte(const word& curr) {
+438   if (contains_any_operand_metadata(curr))
+439     return false;
+440   if (SIZE(curr.data) != 2)
+441     return false;
+442   if (curr.data.find_first_not_of("0123456789abcdef") != string::npos)
+443     return false;
+444   return true;
+445 }
+446 
+447 bool contains_any_operand_metadata(const word& word) {
+448   for (int i = 0;  i < SIZE(word.metadata);  ++i)
+449     if (Instruction_operands.find(word.metadata.at(i)) != Instruction_operands.end())
+450       return true;
+451   return false;
+452 }
+453 
+454 bool has_operand_metadata(const line& inst, const string& m) {
+455   bool result = false;
+456   for (int i = 0;  i < SIZE(inst.words);  ++i) {
+457     if (!has_operand_metadata(inst.words.at(i), m)) continue;
+458     if (result) {
+459       raise << "'" << to_string(inst) << "' has conflicting " << m << " operands\n" << end();
+460       return false;
+461     }
+462     result = true;
+463   }
+464   return result;
+465 }
+466 
+467 bool has_operand_metadata(const word& w, const string& m) {
+468   bool result = false;
+469   bool metadata_found = false;
+470   for (int i = 0;  i < SIZE(w.metadata);  ++i) {
+471     const string& curr = w.metadata.at(i);
+472     if (Instruction_operands.find(curr) == Instruction_operands.end()) continue;  // ignore unrecognized metadata
+473     if (metadata_found) {
+474       raise << "'" << w.original << "' has conflicting operand types; it should have only one\n" << end();
+475       return false;
+476     }
+477     metadata_found = true;
+478     result = (curr == m);
+479   }
+480   return result;
+481 }
+482 
+483 word metadata(const line& inst, const string& m) {
+484   for (int i = 0;  i < SIZE(inst.words);  ++i)
+485     if (has_operand_metadata(inst.words.at(i), m))
+486       return inst.words.at(i);
+487   assert(false);
+488 }
+489 
+490 bool looks_like_hex_int(const string& s) {
+491   if (s.empty()) return false;
+492   if (s.at(0) == '-' || s.at(0) == '+') return true;
+493   if (isdigit(s.at(0))) return true;  // includes '0x' prefix
+494   // End looks_like_hex_int(s) Detectors
+495   return false;
+496 }
+497 
+498 string to_string(const line& inst) {
+499   ostringstream out;
+500   for (int i = 0;  i < SIZE(inst.words);  ++i) {
+501     if (i > 0) out << ' ';
+502     out << inst.words.at(i).original;
+503   }
+504   return out.str();
+505 }
+506 
+507 int32_t parse_int(const string& s) {
+508   if (s.empty()) return 0;
+509   if (contains_uppercase(s)) {
+510     raise << "uppercase hex not allowed: " << s << '\n' << end();
+511     return 0;
+512   }
+513   istringstream in(s);
+514   in >> std::hex;
+515   if (s.at(0) == '-') {
+516     int32_t result = 0;
+517     in >> result;
+518     if (!in || !in.eof()) {
+519       raise << "not a number: " << s << '\n' << end();
+520       return 0;
+521     }
+522     return result;
+523   }
+524   uint32_t uresult = 0;
+525   in >> uresult;
+526   if (!in || !in.eof()) {
+527     raise << "not a number: " << s << '\n' << end();
+528     return 0;
+529   }
+530   return static_cast<int32_t>(uresult);
+531 }
+532 :(before "End Unit Tests")
+533 void test_parse_int() {
+534   CHECK_EQ(0, parse_int("0"));
+535   CHECK_EQ(0, parse_int("0x0"));
+536   CHECK_EQ(0, parse_int("0x0"));
+537   CHECK_EQ(16, parse_int("10"));  // hex always
+538   CHECK_EQ(-1, parse_int("-1"));
+539   CHECK_EQ(-1, parse_int("0xffffffff"));
+540 }
 
diff --git a/html/033check_operands.cc.html b/html/033check_operands.cc.html index ad7fc2a9..f771f27e 100644 --- a/html/033check_operands.cc.html +++ b/html/033check_operands.cc.html @@ -64,7 +64,7 @@ if ('onhashchange' in window) { 3 4 void test_check_missing_imm8_operand() { 5 Hide_errors = true; - 6 run( + 6 run( 7 "== code 0x1\n" 8 "cd\n" // interrupt ?? 9 ); @@ -80,14 +80,14 @@ if ('onhashchange' in window) { 19 :(code) 20 void check_operands(const segment& code) { 21 trace(3, "transform") << "-- check operands" << end(); - 22 for (int i = 0; i < SIZE(code.lines); ++i) { - 23 check_operands(code.lines.at(i)); + 22 for (int i = 0; i < SIZE(code.lines); ++i) { + 23 check_operands(code.lines.at(i)); 24 if (trace_contains_errors()) return; // stop at the first mal-formed instruction 25 } 26 } 27 28 void check_operands(const line& inst) { - 29 word op = preprocess_op(inst.words.at(0)); + 29 word op = preprocess_op(inst.words.at(0)); 30 if (op.data == "0f") { 31 check_operands_0f(inst); 32 return; @@ -99,19 +99,19 @@ if ('onhashchange' in window) { 38 check_operands(inst, op); 39 } 40 - 41 word preprocess_op(word/*copy*/ op) { + 41 word preprocess_op(word/*copy*/ op) { 42 op.data = tolower(op.data.c_str()); 43 // opcodes can't be negative - 44 if (starts_with(op.data, "0x")) + 44 if (starts_with(op.data, "0x")) 45 op.data = op.data.substr(2); - 46 if (SIZE(op.data) == 1) + 46 if (SIZE(op.data) == 1) 47 op.data = string("0")+op.data; 48 return op; 49 } 50 51 void test_preprocess_op() { - 52 word w1; w1.data = "0xf"; - 53 word w2; w2.data = "0f"; + 52 word w1; w1.data = "0xf"; + 53 word w2; w2.data = "0f"; 54 CHECK_EQ(preprocess_op(w1).data, preprocess_op(w2).data); 55 } 56 @@ -165,147 +165,147 @@ if ('onhashchange' in window) { 104 void init_permitted_operands() { 105 //// Class A: just op, no operands 106 // halt -107 put(Permitted_operands, "f4", 0x00); +107 put(Permitted_operands, "f4", 0x00); 108 // inc -109 put(Permitted_operands, "40", 0x00); -110 put(Permitted_operands, "41", 0x00); -111 put(Permitted_operands, "42", 0x00); -112 put(Permitted_operands, "43", 0x00); -113 put(Permitted_operands, "44", 0x00); -114 put(Permitted_operands, "45", 0x00); -115 put(Permitted_operands, "46", 0x00); -116 put(Permitted_operands, "47", 0x00); +109 put(Permitted_operands, "40", 0x00); +110 put(Permitted_operands, "41", 0x00); +111 put(Permitted_operands, "42", 0x00); +112 put(Permitted_operands, "43", 0x00); +113 put(Permitted_operands, "44", 0x00); +114 put(Permitted_operands, "45", 0x00); +115 put(Permitted_operands, "46", 0x00); +116 put(Permitted_operands, "47", 0x00); 117 // dec -118 put(Permitted_operands, "48", 0x00); -119 put(Permitted_operands, "49", 0x00); -120 put(Permitted_operands, "4a", 0x00); -121 put(Permitted_operands, "4b", 0x00); -122 put(Permitted_operands, "4c", 0x00); -123 put(Permitted_operands, "4d", 0x00); -124 put(Permitted_operands, "4e", 0x00); -125 put(Permitted_operands, "4f", 0x00); +118 put(Permitted_operands, "48", 0x00); +119 put(Permitted_operands, "49", 0x00); +120 put(Permitted_operands, "4a", 0x00); +121 put(Permitted_operands, "4b", 0x00); +122 put(Permitted_operands, "4c", 0x00); +123 put(Permitted_operands, "4d", 0x00); +124 put(Permitted_operands, "4e", 0x00); +125 put(Permitted_operands, "4f", 0x00); 126 // push -127 put(Permitted_operands, "50", 0x00); -128 put(Permitted_operands, "51", 0x00); -129 put(Permitted_operands, "52", 0x00); -130 put(Permitted_operands, "53", 0x00); -131 put(Permitted_operands, "54", 0x00); -132 put(Permitted_operands, "55", 0x00); -133 put(Permitted_operands, "56", 0x00); -134 put(Permitted_operands, "57", 0x00); +127 put(Permitted_operands, "50", 0x00); +128 put(Permitted_operands, "51", 0x00); +129 put(Permitted_operands, "52", 0x00); +130 put(Permitted_operands, "53", 0x00); +131 put(Permitted_operands, "54", 0x00); +132 put(Permitted_operands, "55", 0x00); +133 put(Permitted_operands, "56", 0x00); +134 put(Permitted_operands, "57", 0x00); 135 // pop -136 put(Permitted_operands, "58", 0x00); -137 put(Permitted_operands, "59", 0x00); -138 put(Permitted_operands, "5a", 0x00); -139 put(Permitted_operands, "5b", 0x00); -140 put(Permitted_operands, "5c", 0x00); -141 put(Permitted_operands, "5d", 0x00); -142 put(Permitted_operands, "5e", 0x00); -143 put(Permitted_operands, "5f", 0x00); +136 put(Permitted_operands, "58", 0x00); +137 put(Permitted_operands, "59", 0x00); +138 put(Permitted_operands, "5a", 0x00); +139 put(Permitted_operands, "5b", 0x00); +140 put(Permitted_operands, "5c", 0x00); +141 put(Permitted_operands, "5d", 0x00); +142 put(Permitted_operands, "5e", 0x00); +143 put(Permitted_operands, "5f", 0x00); 144 // sign-extend EAX into EDX -145 put(Permitted_operands, "99", 0x00); +145 put(Permitted_operands, "99", 0x00); 146 // return -147 put(Permitted_operands, "c3", 0x00); +147 put(Permitted_operands, "c3", 0x00); 148 149 //// Class B: just op and disp8 150 // imm32 imm8 disp32 |disp16 disp8 subop modrm 151 // 0 0 0 |0 1 0 0 152 153 // jump -154 put(Permitted_operands, "eb", 0x04); -155 put(Permitted_operands, "72", 0x04); -156 put(Permitted_operands, "73", 0x04); -157 put(Permitted_operands, "74", 0x04); -158 put(Permitted_operands, "75", 0x04); -159 put(Permitted_operands, "76", 0x04); -160 put(Permitted_operands, "77", 0x04); -161 put(Permitted_operands, "7c", 0x04); -162 put(Permitted_operands, "7d", 0x04); -163 put(Permitted_operands, "7e", 0x04); -164 put(Permitted_operands, "7f", 0x04); +154 put(Permitted_operands, "eb", 0x04); +155 put(Permitted_operands, "72", 0x04); +156 put(Permitted_operands, "73", 0x04); +157 put(Permitted_operands, "74", 0x04); +158 put(Permitted_operands, "75", 0x04); +159 put(Permitted_operands, "76", 0x04); +160 put(Permitted_operands, "77", 0x04); +161 put(Permitted_operands, "7c", 0x04); +162 put(Permitted_operands, "7d", 0x04); +163 put(Permitted_operands, "7e", 0x04); +164 put(Permitted_operands, "7f", 0x04); 165 166 //// Class D: just op and disp32 167 // imm32 imm8 disp32 |disp16 disp8 subop modrm 168 // 0 0 1 |0 0 0 0 -169 put(Permitted_operands, "e8", 0x10); // call -170 put(Permitted_operands, "e9", 0x10); // jump +169 put(Permitted_operands, "e8", 0x10); // call +170 put(Permitted_operands, "e9", 0x10); // jump 171 172 //// Class E: just op and imm8 173 // imm32 imm8 disp32 |disp16 disp8 subop modrm 174 // 0 1 0 |0 0 0 0 -175 put(Permitted_operands, "cd", 0x20); // software interrupt +175 put(Permitted_operands, "cd", 0x20); // software interrupt 176 177 //// Class F: just op and imm32 178 // imm32 imm8 disp32 |disp16 disp8 subop modrm 179 // 1 0 0 |0 0 0 0 -180 put(Permitted_operands, "05", 0x40); // add -181 put(Permitted_operands, "2d", 0x40); // subtract -182 put(Permitted_operands, "25", 0x40); // and -183 put(Permitted_operands, "0d", 0x40); // or -184 put(Permitted_operands, "35", 0x40); // xor -185 put(Permitted_operands, "3d", 0x40); // compare -186 put(Permitted_operands, "68", 0x40); // push +180 put(Permitted_operands, "05", 0x40); // add +181 put(Permitted_operands, "2d", 0x40); // subtract +182 put(Permitted_operands, "25", 0x40); // and +183 put(Permitted_operands, "0d", 0x40); // or +184 put(Permitted_operands, "35", 0x40); // xor +185 put(Permitted_operands, "3d", 0x40); // compare +186 put(Permitted_operands, "68", 0x40); // push 187 // copy -188 put(Permitted_operands, "b8", 0x40); -189 put(Permitted_operands, "b9", 0x40); -190 put(Permitted_operands, "ba", 0x40); -191 put(Permitted_operands, "bb", 0x40); -192 put(Permitted_operands, "bc", 0x40); -193 put(Permitted_operands, "bd", 0x40); -194 put(Permitted_operands, "be", 0x40); -195 put(Permitted_operands, "bf", 0x40); +188 put(Permitted_operands, "b8", 0x40); +189 put(Permitted_operands, "b9", 0x40); +190 put(Permitted_operands, "ba", 0x40); +191 put(Permitted_operands, "bb", 0x40); +192 put(Permitted_operands, "bc", 0x40); +193 put(Permitted_operands, "bd", 0x40); +194 put(Permitted_operands, "be", 0x40); +195 put(Permitted_operands, "bf", 0x40); 196 197 //// Class M: using ModR/M byte 198 // imm32 imm8 disp32 |disp16 disp8 subop modrm 199 // 0 0 0 |0 0 0 1 200 201 // add -202 put(Permitted_operands, "01", 0x01); -203 put(Permitted_operands, "03", 0x01); +202 put(Permitted_operands, "01", 0x01); +203 put(Permitted_operands, "03", 0x01); 204 // subtract -205 put(Permitted_operands, "29", 0x01); -206 put(Permitted_operands, "2b", 0x01); +205 put(Permitted_operands, "29", 0x01); +206 put(Permitted_operands, "2b", 0x01); 207 // and -208 put(Permitted_operands, "21", 0x01); -209 put(Permitted_operands, "23", 0x01); +208 put(Permitted_operands, "21", 0x01); +209 put(Permitted_operands, "23", 0x01); 210 // or -211 put(Permitted_operands, "09", 0x01); -212 put(Permitted_operands, "0b", 0x01); +211 put(Permitted_operands, "09", 0x01); +212 put(Permitted_operands, "0b", 0x01); 213 // xor -214 put(Permitted_operands, "31", 0x01); -215 put(Permitted_operands, "33", 0x01); +214 put(Permitted_operands, "31", 0x01); +215 put(Permitted_operands, "33", 0x01); 216 // compare -217 put(Permitted_operands, "39", 0x01); -218 put(Permitted_operands, "3b", 0x01); +217 put(Permitted_operands, "39", 0x01); +218 put(Permitted_operands, "3b", 0x01); 219 // copy -220 put(Permitted_operands, "88", 0x01); -221 put(Permitted_operands, "89", 0x01); -222 put(Permitted_operands, "8a", 0x01); -223 put(Permitted_operands, "8b", 0x01); +220 put(Permitted_operands, "88", 0x01); +221 put(Permitted_operands, "89", 0x01); +222 put(Permitted_operands, "8a", 0x01); +223 put(Permitted_operands, "8b", 0x01); 224 // swap -225 put(Permitted_operands, "87", 0x01); +225 put(Permitted_operands, "87", 0x01); 226 // copy address (lea) -227 put(Permitted_operands, "8d", 0x01); +227 put(Permitted_operands, "8d", 0x01); 228 229 //// Class N: op, ModR/M and subop (not r32) 230 // imm32 imm8 disp32 |disp16 disp8 subop modrm 231 // 0 0 0 |0 0 1 1 -232 put(Permitted_operands, "8f", 0x03); // pop -233 put(Permitted_operands, "d3", 0x03); // shift -234 put(Permitted_operands, "f7", 0x03); // test/not/mul/div -235 put(Permitted_operands, "ff", 0x03); // jump/push/call +232 put(Permitted_operands, "8f", 0x03); // pop +233 put(Permitted_operands, "d3", 0x03); // shift +234 put(Permitted_operands, "f7", 0x03); // test/not/mul/div +235 put(Permitted_operands, "ff", 0x03); // jump/push/call 236 237 //// Class O: op, ModR/M, subop (not r32) and imm8 238 // imm32 imm8 disp32 |disp16 disp8 subop modrm 239 // 0 1 0 |0 0 1 1 -240 put(Permitted_operands, "c1", 0x23); // combine -241 put(Permitted_operands, "c6", 0x23); // copy +240 put(Permitted_operands, "c1", 0x23); // combine +241 put(Permitted_operands, "c6", 0x23); // copy 242 243 //// Class P: op, ModR/M, subop (not r32) and imm32 244 // imm32 imm8 disp32 |disp16 disp8 subop modrm 245 // 1 0 0 |0 0 1 1 -246 put(Permitted_operands, "81", 0x43); // combine -247 put(Permitted_operands, "c7", 0x43); // copy +246 put(Permitted_operands, "81", 0x43); // combine +247 put(Permitted_operands, "c7", 0x43); // copy 248 249 // End Init Permitted Operands 250 } @@ -329,7 +329,7 @@ if ('onhashchange' in window) { 268 //: Many instructions can be checked just by comparing bitvectors. 269 270 void compare_bitvector(const line& inst, uint8_t expected, const word& op) { -271 if (all_hex_bytes(inst) && has_operands(inst)) return; // deliberately programming in raw hex; we'll raise a warning elsewhere +271 if (all_hex_bytes(inst) && has_operands(inst)) return; // deliberately programming in raw hex; we'll raise a warning elsewhere 272 uint8_t bitvector = compute_expected_operand_bitvector(inst); 273 if (trace_contains_errors()) return; // duplicate operand type 274 if (bitvector == expected) return; // all good with this instruction @@ -348,30 +348,30 @@ if ('onhashchange' in window) { 287 288 string maybe_name(const word& op) { 289 if (!is_hex_byte(op)) return ""; -290 if (!contains_key(Name, op.data)) return ""; +290 if (!contains_key(Name, op.data)) return ""; 291 // strip stuff in parens from the name -292 const string& s = get(Name, op.data); +292 const string& s = get(Name, op.data); 293 return " ("+s.substr(0, s.find(" ("))+')'; 294 } 295 296 uint32_t compute_expected_operand_bitvector(const line& inst) { 297 set<string> operands_found; 298 uint32_t bitvector = 0; -299 for (int i = /*skip op*/1; i < SIZE(inst.words); ++i) { -300 bitvector = bitvector | expected_bit_for_received_operand(inst.words.at(i), operands_found, inst); +299 for (int i = /*skip op*/1; i < SIZE(inst.words); ++i) { +300 bitvector = bitvector | expected_bit_for_received_operand(inst.words.at(i), operands_found, inst); 301 if (trace_contains_errors()) return INVALID_OPERANDS; // duplicate operand type 302 } 303 return bitvector; 304 } 305 306 bool has_operands(const line& inst) { -307 return SIZE(inst.words) > first_operand(inst); +307 return SIZE(inst.words) > first_operand(inst); 308 } 309 310 int first_operand(const line& inst) { -311 if (inst.words.at(0).data == "0f") return 2; -312 if (inst.words.at(0).data == "f2" || inst.words.at(0).data == "f3") { -313 if (inst.words.at(1).data == "0f") +311 if (inst.words.at(0).data == "0f") return 2; +312 if (inst.words.at(0).data == "f2" || inst.words.at(0).data == "f3") { +313 if (inst.words.at(1).data == "0f") 314 return 3; 315 else 316 return 2; @@ -384,7 +384,7 @@ if ('onhashchange' in window) { 323 uint32_t expected_bit_for_received_operand(const word& w, set<string>& instruction_operands, const line& inst) { 324 uint32_t bv = 0; 325 bool found = false; -326 for (int i = 0; i < SIZE(w.metadata); ++i) { +326 for (int i = 0; i < SIZE(w.metadata); ++i) { 327 string/*copy*/ curr = w.metadata.at(i); 328 string expected_metadata = curr; 329 if (curr == "mod" || curr == "rm32" || curr == "r32" || curr == "scale" || curr == "index" || curr == "base") @@ -407,7 +407,7 @@ if ('onhashchange' in window) { 346 347 void test_conflicting_operand_type() { 348 Hide_errors = true; -349 run( +349 run( 350 "== code 0x1\n" 351 "cd/software-interrupt 80/imm8/imm32\n" 352 ); @@ -421,7 +421,7 @@ if ('onhashchange' in window) { 360 361 void test_check_missing_mod_operand() { 362 Hide_errors = true; -363 run( +363 run( 364 "== code 0x1\n" 365 "81 0/add/subop 3/rm32/ebx 1/imm32\n" 366 ); @@ -431,7 +431,7 @@ if ('onhashchange' in window) { 370 } 371 372 void check_operands_modrm(const line& inst, const word& op) { -373 if (all_hex_bytes(inst)) return; // deliberately programming in raw hex; we'll raise a warning elsewhere +373 if (all_hex_bytes(inst)) return; // deliberately programming in raw hex; we'll raise a warning elsewhere 374 check_operand_metadata_present(inst, "mod", op); 375 check_operand_metadata_present(inst, "rm32", op); 376 // no check for r32; some instructions don't use it; just assume it's 0 if missing @@ -442,7 +442,7 @@ if ('onhashchange' in window) { 381 if (trace_contains_errors()) return; 382 if (metadata(inst, "rm32").data != "4") return; 383 // SIB byte checks -384 uint8_t mod = hex_byte(metadata(inst, "mod").data); +384 uint8_t mod = hex_byte(metadata(inst, "mod").data); 385 if (mod != /*direct*/3) { 386 check_operand_metadata_present(inst, "base", op); 387 check_operand_metadata_present(inst, "index", op); // otherwise why go to SIB? @@ -457,15 +457,15 @@ if ('onhashchange' in window) { 396 // same as compare_bitvector, with one additional exception for modrm-based 397 // instructions: they may use an extra displacement on occasion 398 void compare_bitvector_modrm(const line& inst, uint8_t expected, const word& op) { -399 if (all_hex_bytes(inst) && has_operands(inst)) return; // deliberately programming in raw hex; we'll raise a warning elsewhere +399 if (all_hex_bytes(inst) && has_operands(inst)) return; // deliberately programming in raw hex; we'll raise a warning elsewhere 400 uint8_t bitvector = compute_expected_operand_bitvector(inst); 401 if (trace_contains_errors()) return; // duplicate operand type 402 // update 'expected' bitvector for the additional exception 403 if (has_operand_metadata(inst, "mod")) { -404 int32_t mod = parse_int(metadata(inst, "mod").data); +404 int32_t mod = parse_int(metadata(inst, "mod").data); 405 switch (mod) { 406 case 0: -407 if (has_operand_metadata(inst, "rm32") && parse_int(metadata(inst, "rm32").data) == 5) +407 if (has_operand_metadata(inst, "rm32") && parse_int(metadata(inst, "rm32").data) == 5) 408 expected |= (1<<DISP32); 409 break; 410 case 1: @@ -501,7 +501,7 @@ if ('onhashchange' in window) { 440 } 441 442 void test_modrm_with_displacement() { -443 Reg[EAX].u = 0x1; +443 Reg[EAX].u = 0x1; 444 transform( 445 "== code 0x1\n" 446 // just avoid null pointer @@ -528,13 +528,13 @@ if ('onhashchange' in window) { 467 "8b/copy 0/mod/indirect 5/rm32/.disp32 2/r32/EDX\n" // missing disp32 468 ); 469 CHECK_TRACE_CONTENTS( -470 "error: '8b/copy 0/mod/indirect 5/rm32/.disp32 2/r32/EDX' (copy rm32 to r32): missing disp32 operand\n" +470 "error: '8b/copy 0/mod/indirect 5/rm32/.disp32 2/r32/EDX' (copy rm32 to r32): missing disp32 operand\n" 471 ); 472 } 473 474 void test_conflicting_operands_in_modrm_instruction() { 475 Hide_errors = true; -476 run( +476 run( 477 "== code 0x1\n" 478 "01/add 0/mod 3/mod\n" 479 ); @@ -545,7 +545,7 @@ if ('onhashchange' in window) { 484 485 void test_conflicting_operand_type_modrm() { 486 Hide_errors = true; -487 run( +487 run( 488 "== code 0x1\n" 489 "01/add 0/mod 3/rm32/r32\n" 490 ); @@ -556,7 +556,7 @@ if ('onhashchange' in window) { 495 496 void test_check_missing_rm32_operand() { 497 Hide_errors = true; -498 run( +498 run( 499 "== code 0x1\n" 500 "81 0/add/subop 0/mod 1/imm32\n" 501 ); @@ -567,7 +567,7 @@ if ('onhashchange' in window) { 506 507 void test_check_missing_subop_operand() { 508 Hide_errors = true; -509 run( +509 run( 510 "== code 0x1\n" 511 "81 0/mod 3/rm32/ebx 1/imm32\n" 512 ); @@ -578,7 +578,7 @@ if ('onhashchange' in window) { 517 518 void test_check_missing_base_operand() { 519 Hide_errors = true; -520 run( +520 run( 521 "== code 0x1\n" 522 "81 0/add/subop 0/mod/indirect 4/rm32/use-sib 1/imm32\n" 523 ); @@ -589,7 +589,7 @@ if ('onhashchange' in window) { 528 529 void test_check_missing_index_operand() { 530 Hide_errors = true; -531 run( +531 run( 532 "== code 0x1\n" 533 "81 0/add/subop 0/mod/indirect 4/rm32/use-sib 0/base 1/imm32\n" 534 ); @@ -600,7 +600,7 @@ if ('onhashchange' in window) { 539 540 void test_check_missing_base_operand_2() { 541 Hide_errors = true; -542 run( +542 run( 543 "== code 0x1\n" 544 "81 0/add/subop 0/mod/indirect 4/rm32/use-sib 2/index 3/scale 1/imm32\n" 545 ); @@ -611,7 +611,7 @@ if ('onhashchange' in window) { 550 551 void test_check_extra_displacement() { 552 Hide_errors = true; -553 run( +553 run( 554 "== code 0x1\n" 555 "89/copy 0/mod/indirect 0/rm32/EAX 1/r32/ECX 4/disp8\n" 556 ); @@ -622,7 +622,7 @@ if ('onhashchange' in window) { 561 562 void test_check_duplicate_operand() { 563 Hide_errors = true; -564 run( +564 run( 565 "== code 0x1\n" 566 "89/copy 0/mod/indirect 0/rm32/EAX 1/r32/ECX 1/r32\n" 567 ); @@ -632,7 +632,7 @@ if ('onhashchange' in window) { 571 } 572 573 void test_check_base_operand_not_needed_in_direct_mode() { -574 run( +574 run( 575 "== code 0x1\n" 576 "81 0/add/subop 3/mod/indirect 4/rm32/use-sib 1/imm32\n" 577 ); @@ -641,25 +641,25 @@ if ('onhashchange' in window) { 580 581 void test_extra_modrm() { 582 Hide_errors = true; -583 run( +583 run( 584 "== code 0x1\n" 585 "59/pop-to-ECX 3/mod/direct 1/rm32/ECX 4/r32/ESP\n" 586 ); 587 CHECK_TRACE_CONTENTS( -588 "error: '59/pop-to-ECX 3/mod/direct 1/rm32/ECX 4/r32/ESP' (pop top of stack to ECX): unexpected modrm operand\n" +588 "error: '59/pop-to-ECX 3/mod/direct 1/rm32/ECX 4/r32/ESP' (pop top of stack to ECX): unexpected modrm operand\n" 589 ); 590 } 591 592 //:: similarly handle multi-byte opcodes 593 594 void check_operands_0f(const line& inst) { -595 assert(inst.words.at(0).data == "0f"); -596 if (SIZE(inst.words) == 1) { +595 assert(inst.words.at(0).data == "0f"); +596 if (SIZE(inst.words) == 1) { 597 raise << "opcode '0f' requires a second opcode\n" << end(); 598 return; 599 } -600 word op = preprocess_op(inst.words.at(1)); -601 if (!contains_key(Name_0f, op.data)) { +600 word op = preprocess_op(inst.words.at(1)); +601 if (!contains_key(Name_0f, op.data)) { 602 raise << "unknown 2-byte opcode '0f " << op.data << "'\n" << end(); 603 return; 604 } @@ -672,12 +672,12 @@ if ('onhashchange' in window) { 611 612 void test_check_missing_disp32_operand() { 613 Hide_errors = true; -614 run( +614 run( 615 "== code 0x1\n" 616 " 0f 84 # jmp if ZF to ??\n" 617 ); 618 CHECK_TRACE_CONTENTS( -619 "error: '0f 84' (jump disp32 bytes away if equal, if ZF is set): missing disp32 operand\n" +619 "error: '0f 84' (jump disp32 bytes away if equal, if ZF is set): missing disp32 operand\n" 620 ); 621 } 622 @@ -687,21 +687,21 @@ if ('onhashchange' in window) { 626 //// Class D: just op and disp32 627 // imm32 imm8 disp32 |disp16 disp8 subop modrm 628 // 0 0 1 |0 0 0 0 -629 put_new(Permitted_operands_0f, "82", 0x10); -630 put_new(Permitted_operands_0f, "83", 0x10); -631 put_new(Permitted_operands_0f, "84", 0x10); -632 put_new(Permitted_operands_0f, "85", 0x10); -633 put_new(Permitted_operands_0f, "86", 0x10); -634 put_new(Permitted_operands_0f, "87", 0x10); -635 put_new(Permitted_operands_0f, "8c", 0x10); -636 put_new(Permitted_operands_0f, "8d", 0x10); -637 put_new(Permitted_operands_0f, "8e", 0x10); -638 put_new(Permitted_operands_0f, "8f", 0x10); +629 put_new(Permitted_operands_0f, "82", 0x10); +630 put_new(Permitted_operands_0f, "83", 0x10); +631 put_new(Permitted_operands_0f, "84", 0x10); +632 put_new(Permitted_operands_0f, "85", 0x10); +633 put_new(Permitted_operands_0f, "86", 0x10); +634 put_new(Permitted_operands_0f, "87", 0x10); +635 put_new(Permitted_operands_0f, "8c", 0x10); +636 put_new(Permitted_operands_0f, "8d", 0x10); +637 put_new(Permitted_operands_0f, "8e", 0x10); +638 put_new(Permitted_operands_0f, "8f", 0x10); 639 640 //// Class M: using ModR/M byte 641 // imm32 imm8 disp32 |disp16 disp8 subop modrm 642 // 0 0 0 |0 0 0 1 -643 put_new(Permitted_operands_0f, "af", 0x01); +643 put_new(Permitted_operands_0f, "af", 0x01); 644 645 :(code) 646 void check_operands_0f(const line& inst, const word& op) { @@ -712,7 +712,7 @@ if ('onhashchange' in window) { 651 } 652 653 void compare_bitvector_0f(const line& inst, uint8_t expected, const word& op) { -654 if (all_hex_bytes(inst) && has_operands(inst)) return; // deliberately programming in raw hex; we'll raise a warning elsewhere +654 if (all_hex_bytes(inst) && has_operands(inst)) return; // deliberately programming in raw hex; we'll raise a warning elsewhere 655 uint8_t bitvector = compute_expected_operand_bitvector(inst); 656 if (trace_contains_errors()) return; // duplicate operand type 657 if (bitvector == expected) return; // all good with this instruction @@ -731,9 +731,9 @@ if ('onhashchange' in window) { 670 671 string maybe_name_0f(const word& op) { 672 if (!is_hex_byte(op)) return ""; -673 if (!contains_key(Name_0f, op.data)) return ""; +673 if (!contains_key(Name_0f, op.data)) return ""; 674 // strip stuff in parens from the name -675 const string& s = get(Name_0f, op.data); +675 const string& s = get(Name_0f, op.data); 676 return " ("+s.substr(0, s.find(" ("))+')'; 677 } 678 diff --git a/html/034check_operand_bounds.cc.html b/html/034check_operand_bounds.cc.html index cc860053..4f97a676 100644 --- a/html/034check_operand_bounds.cc.html +++ b/html/034check_operand_bounds.cc.html @@ -62,7 +62,7 @@ if ('onhashchange' in window) { 2 3 void test_check_bitfield_sizes() { 4 Hide_errors = true; - 5 run( + 5 run( 6 "== code 0x1\n" 7 "01/add 4/mod 3/rm32 1/r32\n" // add ECX to EBX 8 ); @@ -74,17 +74,17 @@ if ('onhashchange' in window) { 14 :(before "End Globals") 15 map<string, uint32_t> Operand_bound; 16 :(before "End One-time Setup") - 17 put_new(Operand_bound, "subop", 1<<3); - 18 put_new(Operand_bound, "mod", 1<<2); - 19 put_new(Operand_bound, "rm32", 1<<3); - 20 put_new(Operand_bound, "base", 1<<3); - 21 put_new(Operand_bound, "index", 1<<3); - 22 put_new(Operand_bound, "scale", 1<<2); - 23 put_new(Operand_bound, "r32", 1<<3); - 24 put_new(Operand_bound, "disp8", 1<<8); - 25 put_new(Operand_bound, "disp16", 1<<16); + 17 put_new(Operand_bound, "subop", 1<<3); + 18 put_new(Operand_bound, "mod", 1<<2); + 19 put_new(Operand_bound, "rm32", 1<<3); + 20 put_new(Operand_bound, "base", 1<<3); + 21 put_new(Operand_bound, "index", 1<<3); + 22 put_new(Operand_bound, "scale", 1<<2); + 23 put_new(Operand_bound, "r32", 1<<3); + 24 put_new(Operand_bound, "disp8", 1<<8); + 25 put_new(Operand_bound, "disp16", 1<<16); 26 // no bound needed for disp32 - 27 put_new(Operand_bound, "imm8", 1<<8); + 27 put_new(Operand_bound, "imm8", 1<<8); 28 // no bound needed for imm32 29 30 :(before "Pack Operands(segment code)") @@ -93,10 +93,10 @@ if ('onhashchange' in window) { 33 :(code) 34 void check_operand_bounds(const segment& code) { 35 trace(3, "transform") << "-- check operand bounds" << end(); - 36 for (int i = 0; i < SIZE(code.lines); ++i) { - 37 const line& inst = code.lines.at(i); - 38 for (int j = first_operand(inst); j < SIZE(inst.words); ++j) - 39 check_operand_bounds(inst.words.at(j)); + 36 for (int i = 0; i < SIZE(code.lines); ++i) { + 37 const line& inst = code.lines.at(i); + 38 for (int j = first_operand(inst); j < SIZE(inst.words); ++j) + 39 check_operand_bounds(inst.words.at(j)); 40 if (trace_contains_errors()) return; // stop at the first mal-formed instruction 41 } 42 } @@ -105,7 +105,7 @@ if ('onhashchange' in window) { 45 for (map<string, uint32_t>::iterator p = Operand_bound.begin(); p != Operand_bound.end(); ++p) { 46 if (!has_operand_metadata(w, p->first)) continue; 47 if (!looks_like_hex_int(w.data)) continue; // later transforms are on their own to do their own bounds checking - 48 int32_t x = parse_int(w.data); + 48 int32_t x = parse_int(w.data); 49 if (x >= 0) { 50 if (p->first == "disp8" || p->first == "disp16") { 51 if (static_cast<uint32_t>(x) >= p->second/2) @@ -125,7 +125,7 @@ if ('onhashchange' in window) { 65 } 66 67 void test_check_bitfield_sizes_for_imm8() { - 68 run( + 68 run( 69 "== code 0x1\n" 70 "c1/shift 4/subop/left 3/mod/direct 1/rm32/ECX 0xff/imm8" // shift EBX left 71 ); @@ -134,7 +134,7 @@ if ('onhashchange' in window) { 74 75 void test_check_bitfield_sizes_for_imm8_error() { 76 Hide_errors = true; - 77 run( + 77 run( 78 "== code 0x1\n" 79 "c1/shift 4/subop/left 3/mod/direct 1/rm32/ECX 0x100/imm8" // shift EBX left 80 ); @@ -144,7 +144,7 @@ if ('onhashchange' in window) { 84 } 85 86 void test_check_bitfield_sizes_for_negative_imm8() { - 87 run( + 87 run( 88 "== code 0x1\n" 89 "c1/shift 4/subop/left 3/mod/direct 1/rm32/ECX -0x80/imm8" // shift EBX left 90 ); @@ -153,7 +153,7 @@ if ('onhashchange' in window) { 93 94 void test_check_bitfield_sizes_for_negative_imm8_error() { 95 Hide_errors = true; - 96 run( + 96 run( 97 "== code 0x1\n" 98 "c1/shift 4/subop/left 3/mod/direct 1/rm32/ECX -0x81/imm8" // shift EBX left 99 ); @@ -173,7 +173,7 @@ if ('onhashchange' in window) { 113 114 void test_check_bitfield_sizes_for_disp8_error() { 115 Hide_errors = true; -116 run( +116 run( 117 "== code 0x1\n" 118 "01/add 1/mod/*+disp8 3/rm32 1/r32 0x80/disp8\n" // add ECX to *(EBX+0x80) 119 ); @@ -193,7 +193,7 @@ if ('onhashchange' in window) { 133 134 void test_check_bitfield_sizes_for_negative_disp8_error() { 135 Hide_errors = true; -136 run( +136 run( 137 "== code 0x1\n" 138 "01/add 1/mod/*+disp8 3/rm32 1/r32 -0x81/disp8\n" // add ECX to *(EBX-0x81) 139 ); diff --git a/html/035compute_segment_address.cc.html b/html/035compute_segment_address.cc.html index 6b151fd2..9204ba49 100644 --- a/html/035compute_segment_address.cc.html +++ b/html/035compute_segment_address.cc.html @@ -65,7 +65,7 @@ if ('onhashchange' in window) { 7 //: This gives up a measure of control in placing code and data. 8 9 void test_segment_name() { -10 run( +10 run( 11 "== code 0x09000000\n" 12 "05/add-to-EAX 0x0d0c0b0a/imm32\n" 13 // code starts at 0x09000000 + p_offset, which is 0x54 for a single-segment binary @@ -83,38 +83,38 @@ if ('onhashchange' in window) { 25 26 //: compute segment address 27 -28 :(before "End Level-2 Transforms") -29 Transform.push_back(compute_segment_starts); +28 :(before "End Transforms") +29 Transform.push_back(compute_segment_starts); 30 31 :(code) 32 void compute_segment_starts(program& p) { 33 trace(3, "transform") << "-- compute segment addresses" << end(); -34 uint32_t p_offset = /*size of ehdr*/0x34 + SIZE(p.segments)*0x20/*size of each phdr*/; -35 for (size_t i = 0; i < p.segments.size(); ++i) { -36 segment& curr = p.segments.at(i); +34 uint32_t p_offset = /*size of ehdr*/0x34 + SIZE(p.segments)*0x20/*size of each phdr*/; +35 for (size_t i = 0; i < p.segments.size(); ++i) { +36 segment& curr = p.segments.at(i); 37 if (curr.start >= 0x08000000) { 38 // valid address for user space, so assume we're creating a real ELF binary, not just running a test 39 curr.start &= 0xfffff000; // same number of zeros as the p_align used when emitting the ELF binary 40 curr.start |= (p_offset & 0xfff); -41 trace(99, "transform") << "segment " << i << " begins at address 0x" << HEXWORD << curr.start << end(); +41 trace(99, "transform") << "segment " << i << " begins at address 0x" << HEXWORD << curr.start << end(); 42 } 43 p_offset += size_of(curr); -44 assert(p_offset < SEGMENT_ALIGNMENT); // for now we get less and less available space in each successive segment +44 assert(p_offset < SEGMENT_ALIGNMENT); // for now we get less and less available space in each successive segment 45 } 46 } 47 48 uint32_t size_of(const segment& s) { 49 uint32_t sum = 0; -50 for (int i = 0; i < SIZE(s.lines); ++i) -51 sum += num_bytes(s.lines.at(i)); +50 for (int i = 0; i < SIZE(s.lines); ++i) +51 sum += num_bytes(s.lines.at(i)); 52 return sum; 53 } 54 55 // Assumes all bitfields are packed. 56 uint32_t num_bytes(const line& inst) { 57 uint32_t sum = 0; -58 for (int i = 0; i < SIZE(inst.words); ++i) -59 sum += size_of(inst.words.at(i)); +58 for (int i = 0; i < SIZE(inst.words); ++i) +59 sum += size_of(inst.words.at(i)); 60 return sum; 61 } 62 diff --git a/html/036labels.cc.html b/html/036labels.cc.html index ccf0ae1a..96657216 100644 --- a/html/036labels.cc.html +++ b/html/036labels.cc.html @@ -84,7 +84,7 @@ if ('onhashchange' in window) { 25 //: programs. 26 27 void test_Entry_label() { - 28 run( + 28 run( 29 "== code 0x1\n" 30 "05 0x0d0c0b0a/imm32\n" 31 "Entry:\n" @@ -96,8 +96,8 @@ if ('onhashchange' in window) { 37 CHECK_TRACE_DOESNT_CONTAIN("run: 0x00000001 opcode: 05"); 38 } 39 - 40 :(before "End looks_like_hex_int(s) Detectors") - 41 if (SIZE(s) == 2) return true; + 40 :(before "End looks_like_hex_int(s) Detectors") + 41 if (SIZE(s) == 2) return true; 42 43 :(code) 44 void test_pack_immediate_ignores_single_byte_nondigit_operand() { @@ -153,7 +153,7 @@ if ('onhashchange' in window) { 94 } 95 if (isdigit(s.at(0))) 96 raise << "'" << s << "' starts with a digit, and so can be confused with a number; use a different name.\n" << end(); - 97 if (SIZE(s) == 2) + 97 if (SIZE(s) == 2) 98 raise << "'" << s << "' is two characters long, which can look like raw hex bytes at a glance; use a different name\n" << end(); 99 } 100 @@ -170,12 +170,12 @@ if ('onhashchange' in window) { 111 ); 112 } 113 -114 :(before "End Level-2 Transforms") -115 Transform.push_back(rewrite_labels); +114 :(before "End Transforms") +115 Transform.push_back(rewrite_labels); 116 :(code) 117 void rewrite_labels(program& p) { 118 trace(3, "transform") << "-- rewrite labels" << end(); -119 if (p.segments.empty()) return; +119 if (p.segments.empty()) return; 120 segment& code = *find(p, "code"); 121 map<string, int32_t> byte_index; // values are unsigned, but we're going to do subtractions on them so they need to fit in 31 bits 122 compute_byte_indices_for_labels(code, byte_index); @@ -183,18 +183,18 @@ if ('onhashchange' in window) { 124 drop_labels(code); 125 if (trace_contains_errors()) return; 126 replace_labels_with_displacements(code, byte_index); -127 if (contains_key(byte_index, "Entry")) -128 p.entry = code.start + get(byte_index, "Entry"); +127 if (contains_key(byte_index, "Entry")) +128 p.entry = code.start + get(byte_index, "Entry"); 129 } 130 131 void compute_byte_indices_for_labels(const segment& code, map<string, int32_t>& byte_index) { 132 int current_byte = 0; -133 for (int i = 0; i < SIZE(code.lines); ++i) { -134 const line& inst = code.lines.at(i); -135 if (Source_lines_file.is_open() && !inst.original.empty() && /*not a label*/ *inst.words.at(0).data.rbegin() != ':') -136 Source_lines_file << "0x" << HEXWORD << (code.start + current_byte) << ' ' << inst.original << '\n'; -137 for (int j = 0; j < SIZE(inst.words); ++j) { -138 const word& curr = inst.words.at(j); +133 for (int i = 0; i < SIZE(code.lines); ++i) { +134 const line& inst = code.lines.at(i); +135 if (Source_lines_file.is_open() && !inst.original.empty() && /*not a label*/ *inst.words.at(0).data.rbegin() != ':') +136 Source_lines_file << "0x" << HEXWORD << (code.start + current_byte) << ' ' << inst.original << '\n'; +137 for (int j = 0; j < SIZE(inst.words); ++j) { +138 const word& curr = inst.words.at(j); 139 // hack: if we have any operand metadata left after previous transforms, 140 // deduce its size 141 // Maybe we should just move this transform to before instruction @@ -219,17 +219,17 @@ if ('onhashchange' in window) { 160 // ensure labels look sufficiently different from raw hex 161 check_valid_name(label); 162 if (trace_contains_errors()) return; -163 if (contains_any_operand_metadata(curr)) +163 if (contains_any_operand_metadata(curr)) 164 raise << "'" << to_string(inst) << "': label definition (':') not allowed in operand\n" << end(); 165 if (j > 0) -166 raise << "'" << to_string(inst) << "': labels can only be the first word in a line.\n" << end(); +166 raise << "'" << to_string(inst) << "': labels can only be the first word in a line.\n" << end(); 167 if (Labels_file.is_open()) -168 Labels_file << "0x" << HEXWORD << (code.start + current_byte) << ' ' << label << '\n'; -169 if (contains_key(byte_index, label) && label != "Entry") { +168 Labels_file << "0x" << HEXWORD << (code.start + current_byte) << ' ' << label << '\n'; +169 if (contains_key(byte_index, label) && label != "Entry") { 170 raise << "duplicate label '" << label << "'\n" << end(); 171 return; 172 } -173 put(byte_index, label, current_byte); +173 put(byte_index, label, current_byte); 174 trace(99, "transform") << "label '" << label << "' is at address " << (current_byte+code.start) << end(); 175 // no modifying current_byte; label definitions won't be in the final binary 176 } @@ -238,23 +238,23 @@ if ('onhashchange' in window) { 179 } 180 181 :(before "End Globals") -182 bool Dump_debug_info = false; // currently used only by 'subx translate' +182 bool Dump_debug_info = false; // currently used only by 'bootstrap translate' 183 ofstream Labels_file; 184 ofstream Source_lines_file; 185 :(before "End Commandline Options") -186 else if (is_equal(*arg, "--debug")) { +186 else if (is_equal(*arg, "--debug")) { 187 Dump_debug_info = true; 188 // End --debug Settings 189 } 190 //: wait to open "labels" for writing until we're sure we aren't trying to read it -191 :(after "Begin subx translate") +191 :(after "Begin bootstrap translate") 192 if (Dump_debug_info) { 193 cerr << "saving address->label information to 'labels'\n"; 194 Labels_file.open("labels"); 195 cerr << "saving address->source information to 'source_lines'\n"; 196 Source_lines_file.open("source_lines"); 197 } -198 :(before "End subx translate") +198 :(before "End bootstrap translate") 199 if (Dump_debug_info) { 200 Labels_file.close(); 201 Source_lines_file.close(); @@ -262,10 +262,10 @@ if ('onhashchange' in window) { 203 204 :(code) 205 void drop_labels(segment& code) { -206 for (int i = 0; i < SIZE(code.lines); ++i) { -207 line& inst = code.lines.at(i); -208 vector<word>::iterator new_end = remove_if(inst.words.begin(), inst.words.end(), is_label); -209 inst.words.erase(new_end, inst.words.end()); +206 for (int i = 0; i < SIZE(code.lines); ++i) { +207 line& inst = code.lines.at(i); +208 vector<word>::iterator new_end = remove_if(inst.words.begin(), inst.words.end(), is_label); +209 inst.words.erase(new_end, inst.words.end()); 210 } 211 } 212 @@ -275,13 +275,13 @@ if ('onhashchange' in window) { 216 217 void replace_labels_with_displacements(segment& code, const map<string, int32_t>& byte_index) { 218 int32_t byte_index_next_instruction_starts_at = 0; -219 for (int i = 0; i < SIZE(code.lines); ++i) { -220 line& inst = code.lines.at(i); +219 for (int i = 0; i < SIZE(code.lines); ++i) { +220 line& inst = code.lines.at(i); 221 byte_index_next_instruction_starts_at += num_bytes(inst); -222 line new_inst; -223 for (int j = 0; j < SIZE(inst.words); ++j) { -224 const word& curr = inst.words.at(j); -225 if (contains_key(byte_index, curr.data)) { +222 line new_inst; +223 for (int j = 0; j < SIZE(inst.words); ++j) { +224 const word& curr = inst.words.at(j); +225 if (contains_key(byte_index, curr.data)) { 226 int32_t displacement = static_cast<int32_t>(get(byte_index, curr.data)) - byte_index_next_instruction_starts_at; 227 if (has_operand_metadata(curr, "disp8")) { 228 if (displacement > 0x7f || displacement < -0x7f) @@ -302,19 +302,19 @@ if ('onhashchange' in window) { 243 } 244 } 245 else { -246 new_inst.words.push_back(curr); +246 new_inst.words.push_back(curr); 247 } 248 } -249 inst.words.swap(new_inst.words); +249 inst.words.swap(new_inst.words); 250 trace(99, "transform") << "instruction after transform: '" << data_to_string(inst) << "'" << end(); 251 } 252 } 253 254 string data_to_string(const line& inst) { 255 ostringstream out; -256 for (int i = 0; i < SIZE(inst.words); ++i) { +256 for (int i = 0; i < SIZE(inst.words); ++i) { 257 if (i > 0) out << ' '; -258 out << inst.words.at(i).data; +258 out << inst.words.at(i).data; 259 } 260 return out.str(); 261 } @@ -432,7 +432,7 @@ if ('onhashchange' in window) { 373 } 374 375 // This test could do with some refactoring. -376 // We're duplicating the flow inside `subx translate`, but without +376 // We're duplicating the flow inside `bootstrap translate`, but without 377 // reading/writing files. 378 // We can't just use run(string) because most of our tests allow programs 379 // without 'Entry' labels, as a convenience. @@ -470,7 +470,7 @@ if ('onhashchange' in window) { 411 ); 412 } 413 -414 :(before "End size_of(word w) Special-cases") +414 :(before "End size_of(word w) Special-cases") 415 else if (is_label(w)) 416 return 0; diff --git a/html/037global_variables.cc.html b/html/037global_variables.cc.html index 3ac86f25..eefe9f03 100644 --- a/html/037global_variables.cc.html +++ b/html/037global_variables.cc.html @@ -69,7 +69,7 @@ if ('onhashchange' in window) { 8 9 :(code) 10 void test_global_variable() { - 11 run( + 11 run( 12 "== code 0x1\n" 13 "b9 x/imm32\n" 14 "== data 0x2000\n" @@ -81,8 +81,8 @@ if ('onhashchange' in window) { 20 ); 21 } 22 - 23 :(before "End Level-2 Transforms") - 24 Transform.push_back(rewrite_global_variables); + 23 :(before "End Transforms") + 24 Transform.push_back(rewrite_global_variables); 25 :(code) 26 void rewrite_global_variables(program& p) { 27 trace(3, "transform") << "-- rewrite global variables" << end(); @@ -95,18 +95,18 @@ if ('onhashchange' in window) { 34 } 35 36 void compute_addresses_for_global_variables(const program& p, map<string, uint32_t>& address) { - 37 for (int i = 0; i < SIZE(p.segments); ++i) { - 38 if (p.segments.at(i).name != "code") - 39 compute_addresses_for_global_variables(p.segments.at(i), address); + 37 for (int i = 0; i < SIZE(p.segments); ++i) { + 38 if (p.segments.at(i).name != "code") + 39 compute_addresses_for_global_variables(p.segments.at(i), address); 40 } 41 } 42 43 void compute_addresses_for_global_variables(const segment& s, map<string, uint32_t>& address) { 44 int current_address = s.start; - 45 for (int i = 0; i < SIZE(s.lines); ++i) { - 46 const line& inst = s.lines.at(i); - 47 for (int j = 0; j < SIZE(inst.words); ++j) { - 48 const word& curr = inst.words.at(j); + 45 for (int i = 0; i < SIZE(s.lines); ++i) { + 46 const line& inst = s.lines.at(i); + 47 for (int j = 0; j < SIZE(inst.words); ++j) { + 48 const word& curr = inst.words.at(j); 49 if (*curr.data.rbegin() != ':') { 50 current_address += size_of(curr); 51 } @@ -116,15 +116,15 @@ if ('onhashchange' in window) { 55 check_valid_name(variable); 56 if (trace_contains_errors()) return; 57 if (j > 0) - 58 raise << "'" << to_string(inst) << "': global variable names can only be the first word in a line.\n" << end(); + 58 raise << "'" << to_string(inst) << "': global variable names can only be the first word in a line.\n" << end(); 59 if (Labels_file.is_open()) - 60 Labels_file << "0x" << HEXWORD << current_address << ' ' << variable << '\n'; - 61 if (contains_key(address, variable)) { + 60 Labels_file << "0x" << HEXWORD << current_address << ' ' << variable << '\n'; + 61 if (contains_key(address, variable)) { 62 raise << "duplicate global '" << variable << "'\n" << end(); 63 return; 64 } - 65 put(address, variable, current_address); - 66 trace(99, "transform") << "global variable '" << variable << "' is at address 0x" << HEXWORD << current_address << end(); + 65 put(address, variable, current_address); + 66 trace(99, "transform") << "global variable '" << variable << "' is at address 0x" << HEXWORD << current_address << end(); 67 // no modifying current_address; global variable definitions won't be in the final binary 68 } 69 } @@ -132,17 +132,17 @@ if ('onhashchange' in window) { 71 } 72 73 void drop_global_variables(program& p) { - 74 for (int i = 0; i < SIZE(p.segments); ++i) { - 75 if (p.segments.at(i).name != "code") - 76 drop_labels(p.segments.at(i)); + 74 for (int i = 0; i < SIZE(p.segments); ++i) { + 75 if (p.segments.at(i).name != "code") + 76 drop_labels(p.segments.at(i)); 77 } 78 } 79 80 void replace_global_variables_with_addresses(program& p, const map<string, uint32_t>& address) { - 81 if (p.segments.empty()) return; - 82 for (int i = 0; i < SIZE(p.segments); ++i) { - 83 segment& curr = p.segments.at(i); - 84 if (curr.name == "code") + 81 if (p.segments.empty()) return; + 82 for (int i = 0; i < SIZE(p.segments); ++i) { + 83 segment& curr = p.segments.at(i); + 84 if (curr.name == "code") 85 replace_global_variables_in_code_segment(curr, address); 86 else 87 replace_global_variables_in_data_segment(curr, address); @@ -150,15 +150,15 @@ if ('onhashchange' in window) { 89 } 90 91 void replace_global_variables_in_code_segment(segment& code, const map<string, uint32_t>& address) { - 92 for (int i = 0; i < SIZE(code.lines); ++i) { - 93 line& inst = code.lines.at(i); - 94 line new_inst; - 95 for (int j = 0; j < SIZE(inst.words); ++j) { - 96 const word& curr = inst.words.at(j); + 92 for (int i = 0; i < SIZE(code.lines); ++i) { + 93 line& inst = code.lines.at(i); + 94 line new_inst; + 95 for (int j = 0; j < SIZE(inst.words); ++j) { + 96 const word& curr = inst.words.at(j); 97 if (!contains_key(address, curr.data)) { 98 if (!looks_like_hex_int(curr.data)) 99 raise << "missing reference to global '" << curr.data << "'\n" << end(); -100 new_inst.words.push_back(curr); +100 new_inst.words.push_back(curr); 101 continue; 102 } 103 if (!valid_use_of_global_variable(curr)) { @@ -167,19 +167,19 @@ if ('onhashchange' in window) { 106 } 107 emit_hex_bytes(new_inst, get(address, curr.data), 4); 108 } -109 inst.words.swap(new_inst.words); +109 inst.words.swap(new_inst.words); 110 trace(99, "transform") << "instruction after transform: '" << data_to_string(inst) << "'" << end(); 111 } 112 } 113 114 void replace_global_variables_in_data_segment(segment& data, const map<string, uint32_t>& address) { -115 for (int i = 0; i < SIZE(data.lines); ++i) { -116 line& l = data.lines.at(i); -117 line new_l; -118 for (int j = 0; j < SIZE(l.words); ++j) { -119 const word& curr = l.words.at(j); +115 for (int i = 0; i < SIZE(data.lines); ++i) { +116 line& l = data.lines.at(i); +117 line new_l; +118 for (int j = 0; j < SIZE(l.words); ++j) { +119 const word& curr = l.words.at(j); 120 if (!contains_key(address, curr.data)) { -121 if (looks_like_hex_int(curr.data)) { +121 if (looks_like_hex_int(curr.data)) { 122 if (has_operand_metadata(curr, "imm32")) 123 emit_hex_bytes(new_l, curr, 4); 124 else if (has_operand_metadata(curr, "imm16")) @@ -193,18 +193,18 @@ if ('onhashchange' in window) { 132 else if (has_operand_metadata(curr, "disp32")) 133 raise << "can't use /disp32 in a non-code segment\n" << end(); 134 else -135 new_l.words.push_back(curr); +135 new_l.words.push_back(curr); 136 } 137 else { 138 raise << "missing reference to global '" << curr.data << "'\n" << end(); -139 new_l.words.push_back(curr); +139 new_l.words.push_back(curr); 140 } 141 continue; 142 } -143 trace(99, "transform") << curr.data << " maps to " << HEXWORD << get(address, curr.data) << end(); +143 trace(99, "transform") << curr.data << " maps to " << HEXWORD << get(address, curr.data) << end(); 144 emit_hex_bytes(new_l, get(address, curr.data), 4); 145 } -146 l.words.swap(new_l.words); +146 l.words.swap(new_l.words); 147 trace(99, "transform") << "after transform: '" << data_to_string(l) << "'" << end(); 148 } 149 } @@ -218,16 +218,16 @@ if ('onhashchange' in window) { 157 //:: a more complex sanity check for how we use global variables 158 //: requires first saving some data early before we pack operands 159 -160 :(after "Begin Level-2 Transforms") -161 Transform.push_back(correlate_disp32_with_mod); +160 :(after "Begin Transforms") +161 Transform.push_back(correlate_disp32_with_mod); 162 :(code) 163 void correlate_disp32_with_mod(program& p) { -164 if (p.segments.empty()) return; +164 if (p.segments.empty()) return; 165 segment& code = *find(p, "code"); -166 for (int i = 0; i < SIZE(code.lines); ++i) { -167 line& inst = code.lines.at(i); -168 for (int j = 0; j < SIZE(inst.words); ++j) { -169 word& curr = inst.words.at(j); +166 for (int i = 0; i < SIZE(code.lines); ++i) { +167 line& inst = code.lines.at(i); +168 for (int j = 0; j < SIZE(inst.words); ++j) { +169 word& curr = inst.words.at(j); 170 if (has_operand_metadata(curr, "disp32") 171 && has_operand_metadata(inst, "mod")) 172 curr.metadata.push_back("has_mod"); @@ -243,14 +243,14 @@ if ('onhashchange' in window) { 182 183 :(code) 184 bool has_metadata(const word& w, const string& m) { -185 for (int i = 0; i < SIZE(w.metadata); ++i) +185 for (int i = 0; i < SIZE(w.metadata); ++i) 186 if (w.metadata.at(i) == m) return true; 187 return false; 188 } 189 190 void test_global_variable_disallowed_in_jump() { 191 Hide_errors = true; -192 run( +192 run( 193 "== code 0x1\n" 194 "eb/jump x/disp8\n" 195 "== data 0x2000\n" @@ -266,7 +266,7 @@ if ('onhashchange' in window) { 205 206 void test_global_variable_disallowed_in_call() { 207 Hide_errors = true; -208 run( +208 run( 209 "== code 0x1\n" 210 "e8/call x/disp32\n" 211 "== data 0x2000\n" @@ -281,7 +281,7 @@ if ('onhashchange' in window) { 220 } 221 222 void test_global_variable_in_data_segment() { -223 run( +223 run( 224 "== code 0x1\n" 225 "b9 x/imm32\n" 226 "== data 0x2000\n" @@ -301,7 +301,7 @@ if ('onhashchange' in window) { 240 } 241 242 void test_raw_number_with_imm32_in_data_segment() { -243 run( +243 run( 244 "== code 0x1\n" 245 "b9 x/imm32\n" 246 "== data 0x2000\n" @@ -320,7 +320,7 @@ if ('onhashchange' in window) { 259 260 void test_duplicate_global_variable() { 261 Hide_errors = true; -262 run( +262 run( 263 "== code 0x1\n" 264 "40/increment-EAX\n" 265 "== data 0x2000\n" @@ -334,7 +334,7 @@ if ('onhashchange' in window) { 273 } 274 275 void test_global_variable_disp32_with_modrm() { -276 run( +276 run( 277 "== code 0x1\n" 278 "8b/copy 0/mod/indirect 5/rm32/.disp32 2/r32/EDX x/disp32\n" 279 "== data 0x2000\n" @@ -356,11 +356,11 @@ if ('onhashchange' in window) { 295 296 string to_full_string(const line& in) { 297 ostringstream out; -298 for (int i = 0; i < SIZE(in.words); ++i) { +298 for (int i = 0; i < SIZE(in.words); ++i) { 299 if (i > 0) out << ' '; -300 out << in.words.at(i).data; -301 for (int j = 0; j < SIZE(in.words.at(i).metadata); ++j) -302 out << '/' << in.words.at(i).metadata.at(j); +300 out << in.words.at(i).data; +301 for (int j = 0; j < SIZE(in.words.at(i).metadata); ++j) +302 out << '/' << in.words.at(i).metadata.at(j); 303 } 304 return out.str(); 305 } diff --git a/html/038---literal_strings.cc.html b/html/038---literal_strings.cc.html index 67b6c458..2cf9e245 100644 --- a/html/038---literal_strings.cc.html +++ b/html/038---literal_strings.cc.html @@ -64,7 +64,7 @@ if ('onhashchange' in window) { 4 //: always be the second segment). 5 6 void test_transform_literal_string() { - 7 run( + 7 run( 8 "== code 0x1\n" 9 "b8/copy \"test\"/imm32\n" 10 "== data 0x2000\n" // need an empty segment @@ -72,7 +72,7 @@ if ('onhashchange' in window) { 12 CHECK_TRACE_CONTENTS( 13 "transform: -- move literal strings to data segment\n" 14 "transform: adding global variable '__subx_global_1' containing \"test\"\n" - 15 "transform: line after transform: 'b8 __subx_global_1'\n" + 15 "transform: line after transform: 'b8 __subx_global_1'\n" 16 ); 17 } 18 @@ -80,332 +80,330 @@ if ('onhashchange' in window) { 20 //: knows about labels and global variables and will emit them for previous 21 //: layers to transform. 22 :(after "Begin Transforms") - 23 // Begin Level-3 Transforms - 24 Transform.push_back(transform_literal_strings); - 25 // End Level-3 Transforms - 26 - 27 :(before "End Globals") - 28 int Next_auto_global = 1; - 29 :(before "End Reset") - 30 Next_auto_global = 1; - 31 :(code) - 32 void transform_literal_strings(program& p) { - 33 trace(3, "transform") << "-- move literal strings to data segment" << end(); - 34 if (p.segments.empty()) return; - 35 vector<line> new_lines; - 36 for (int s = 0; s < SIZE(p.segments); ++s) { - 37 segment& seg = p.segments.at(s); - 38 trace(99, "transform") << "segment '" << seg.name << "'" << end(); - 39 for (int i = 0; i < SIZE(seg.lines); ++i) { - 40 //? cerr << seg.name << '/' << i << '\n'; - 41 line& line = seg.lines.at(i); - 42 for (int j = 0; j < SIZE(line.words); ++j) { - 43 word& curr = line.words.at(j); - 44 if (curr.data.at(0) != '"') continue; - 45 ostringstream global_name; - 46 global_name << "__subx_global_" << Next_auto_global; - 47 ++Next_auto_global; - 48 add_global_to_data_segment(global_name.str(), curr, new_lines); - 49 curr.data = global_name.str(); - 50 } - 51 trace(99, "transform") << "line after transform: '" << data_to_string(line) << "'" << end(); - 52 } - 53 } - 54 segment* data = find(p, "data"); - 55 if (data) - 56 data->lines.insert(data->lines.end(), new_lines.begin(), new_lines.end()); - 57 } - 58 - 59 void add_global_to_data_segment(const string& name, const word& value, vector<line>& out) { - 60 trace(99, "transform") << "adding global variable '" << name << "' containing " << value.data << end(); - 61 // emit label - 62 out.push_back(label(name)); - 63 // emit size for size-prefixed array - 64 out.push_back(line()); - 65 emit_hex_bytes(out.back(), SIZE(value.data)-/*skip quotes*/2, 4/*bytes*/); - 66 // emit data byte by byte - 67 out.push_back(line()); - 68 line& curr = out.back(); - 69 for (int i = /*skip start quote*/1; i < SIZE(value.data)-/*skip end quote*/1; ++i) { - 70 char c = value.data.at(i); - 71 curr.words.push_back(word()); - 72 curr.words.back().data = hex_byte_to_string(c); - 73 curr.words.back().metadata.push_back(string(1, c)); - 74 } - 75 } - 76 - 77 //: Within strings, whitespace is significant. So we need to redo our instruction - 78 //: parsing. - 79 - 80 void test_instruction_with_string_literal() { - 81 parse_instruction_character_by_character( - 82 "a \"abc def\" z\n" // two spaces inside string - 83 ); - 84 CHECK_TRACE_CONTENTS( - 85 "parse2: word: a\n" - 86 "parse2: word: \"abc def\"\n" - 87 "parse2: word: z\n" - 88 ); - 89 // no other words - 90 CHECK_TRACE_COUNT("parse2", 3); - 91 } - 92 - 93 void test_string_literal_in_data_segment() { - 94 run( - 95 "== code 0x1\n" - 96 "b8/copy X/imm32\n" - 97 "== data 0x2000\n" - 98 "X:\n" - 99 "\"test\"/imm32\n" -100 ); -101 CHECK_TRACE_CONTENTS( -102 "transform: -- move literal strings to data segment\n" -103 "transform: adding global variable '__subx_global_1' containing \"test\"\n" -104 "transform: line after transform: '__subx_global_1'\n" -105 ); -106 } -107 -108 :(before "End Line Parsing Special-cases(line_data -> l)") -109 if (line_data.find('"') != string::npos) { // can cause false-positives, but we can handle them -110 parse_instruction_character_by_character(line_data, l); -111 continue; -112 } -113 -114 :(code) -115 void parse_instruction_character_by_character(const string& line_data, vector<line>& out) { -116 if (line_data.find('\n') != string::npos && line_data.find('\n') != line_data.size()-1) { -117 raise << "parse_instruction_character_by_character: should receive only a single line\n" << end(); -118 return; -119 } -120 // parse literals -121 istringstream in(line_data); -122 in >> std::noskipws; -123 line result; -124 result.original = line_data; -125 // add tokens (words or strings) one by one -126 while (has_data(in)) { -127 skip_whitespace(in); -128 if (!has_data(in)) break; -129 char c = in.get(); -130 if (c == '#') break; // comment; drop rest of line -131 if (c == ':') break; // line metadata; skip for now -132 if (c == '.') { -133 if (!has_data(in)) break; // comment token at end of line -134 if (isspace(in.peek())) -135 continue; // '.' followed by space is comment token; skip -136 } -137 result.words.push_back(word()); -138 if (c == '"') { -139 // string literal; slurp everything between quotes into data -140 ostringstream d; -141 d << c; -142 while (has_data(in)) { -143 in >> c; -144 if (c == '\\') { -145 in >> c; -146 if (c == 'n') d << '\n'; -147 else if (c == '"') d << '"'; -148 else if (c == '\\') d << '\\'; -149 else { -150 raise << "parse_instruction_character_by_character: unknown escape sequence '\\" << c << "'\n" << end(); -151 return; -152 } -153 continue; -154 } else { -155 d << c; -156 } -157 if (c == '"') break; -158 } -159 result.words.back().data = d.str(); -160 result.words.back().original = d.str(); -161 // slurp metadata -162 ostringstream m; -163 while (!isspace(in.peek()) && has_data(in)) { // peek can sometimes trigger eof(), so do it first -164 in >> c; -165 if (c == '/') { -166 if (!m.str().empty()) result.words.back().metadata.push_back(m.str()); -167 m.str(""); -168 } -169 else { -170 m << c; -171 } -172 } -173 if (!m.str().empty()) result.words.back().metadata.push_back(m.str()); -174 } -175 else { -176 // not a string literal; slurp all characters until whitespace -177 ostringstream w; -178 w << c; -179 while (!isspace(in.peek()) && has_data(in)) { // peek can sometimes trigger eof(), so do it first -180 in >> c; -181 w << c; -182 } -183 parse_word(w.str(), result.words.back()); -184 } -185 trace(99, "parse2") << "word: " << to_string(result.words.back()) << end(); -186 } -187 if (!result.words.empty()) -188 out.push_back(result); -189 } -190 -191 void skip_whitespace(istream& in) { -192 while (has_data(in) && isspace(in.peek())) { -193 in.get(); -194 } -195 } -196 -197 void skip_comment(istream& in) { -198 if (has_data(in) && in.peek() == '#') { -199 in.get(); -200 while (has_data(in) && in.peek() != '\n') in.get(); -201 } -202 } -203 -204 line label(string s) { -205 line result; -206 result.words.push_back(word()); -207 result.words.back().data = (s+":"); -208 return result; -209 } -210 -211 // helper for tests -212 void parse_instruction_character_by_character(const string& line_data) { -213 vector<line> out; -214 parse_instruction_character_by_character(line_data, out); -215 } -216 -217 void test_parse2_comment_token_in_middle() { -218 parse_instruction_character_by_character( -219 "a . z\n" -220 ); -221 CHECK_TRACE_CONTENTS( -222 "parse2: word: a\n" -223 "parse2: word: z\n" -224 ); -225 CHECK_TRACE_DOESNT_CONTAIN("parse2: word: ."); -226 // no other words -227 CHECK_TRACE_COUNT("parse2", 2); -228 } -229 -230 void test_parse2_word_starting_with_dot() { -231 parse_instruction_character_by_character( -232 "a .b c\n" -233 ); -234 CHECK_TRACE_CONTENTS( -235 "parse2: word: a\n" -236 "parse2: word: .b\n" -237 "parse2: word: c\n" -238 ); -239 } -240 -241 void test_parse2_comment_token_at_start() { -242 parse_instruction_character_by_character( -243 ". a b\n" -244 ); -245 CHECK_TRACE_CONTENTS( -246 "parse2: word: a\n" -247 "parse2: word: b\n" -248 ); -249 CHECK_TRACE_DOESNT_CONTAIN("parse2: word: ."); -250 } -251 -252 void test_parse2_comment_token_at_end() { -253 parse_instruction_character_by_character( -254 "a b .\n" -255 ); -256 CHECK_TRACE_CONTENTS( -257 "parse2: word: a\n" -258 "parse2: word: b\n" -259 ); -260 CHECK_TRACE_DOESNT_CONTAIN("parse2: word: ."); -261 } -262 -263 void test_parse2_word_starting_with_dot_at_start() { -264 parse_instruction_character_by_character( -265 ".a b c\n" -266 ); -267 CHECK_TRACE_CONTENTS( -268 "parse2: word: .a\n" -269 "parse2: word: b\n" -270 "parse2: word: c\n" -271 ); -272 } -273 -274 void test_parse2_metadata() { -275 parse_instruction_character_by_character( -276 ".a b/c d\n" -277 ); -278 CHECK_TRACE_CONTENTS( -279 "parse2: word: .a\n" -280 "parse2: word: b /c\n" -281 "parse2: word: d\n" -282 ); -283 } -284 -285 void test_parse2_string_with_metadata() { -286 parse_instruction_character_by_character( -287 "a \"bc def\"/disp32 g\n" -288 ); -289 CHECK_TRACE_CONTENTS( -290 "parse2: word: a\n" -291 "parse2: word: \"bc def\" /disp32\n" -292 "parse2: word: g\n" -293 ); -294 } -295 -296 void test_parse2_string_with_metadata_at_end() { -297 parse_instruction_character_by_character( -298 "a \"bc def\"/disp32\n" -299 ); -300 CHECK_TRACE_CONTENTS( -301 "parse2: word: a\n" -302 "parse2: word: \"bc def\" /disp32\n" -303 ); -304 } -305 -306 void test_parse2_string_with_metadata_at_end_of_line_without_newline() { -307 parse_instruction_character_by_character( -308 "68/push \"test\"/f" // no newline, which is how calls from parse() will look -309 ); -310 CHECK_TRACE_CONTENTS( -311 "parse2: word: 68 /push\n" -312 "parse2: word: \"test\" /f\n" -313 ); -314 } -315 -316 //: Make sure slashes inside strings don't trigger adding stuff from inside the -317 //: string to metadata. -318 -319 void test_parse2_string_containing_slashes() { -320 parse_instruction_character_by_character( -321 "a \"bc/def\"/disp32\n" -322 ); -323 CHECK_TRACE_CONTENTS( -324 "parse2: word: \"bc/def\" /disp32\n" -325 ); -326 } -327 -328 void test_instruction_with_string_literal_with_escaped_quote() { -329 parse_instruction_character_by_character( -330 "\"a\\\"b\"\n" // escaped quote inside string -331 ); -332 CHECK_TRACE_CONTENTS( -333 "parse2: word: \"a\"b\"\n" -334 ); -335 // no other words -336 CHECK_TRACE_COUNT("parse2", 1); -337 } -338 -339 void test_instruction_with_string_literal_with_escaped_backslash() { -340 parse_instruction_character_by_character( -341 "\"a\\\\b\"\n" // escaped backslash inside string -342 ); -343 CHECK_TRACE_CONTENTS( -344 "parse2: word: \"a\\b\"\n" -345 ); -346 // no other words -347 CHECK_TRACE_COUNT("parse2", 1); -348 } + 23 Transform.push_back(transform_literal_strings); + 24 + 25 :(before "End Globals") + 26 int Next_auto_global = 1; + 27 :(before "End Reset") + 28 Next_auto_global = 1; + 29 :(code) + 30 void transform_literal_strings(program& p) { + 31 trace(3, "transform") << "-- move literal strings to data segment" << end(); + 32 if (p.segments.empty()) return; + 33 vector<line> new_lines; + 34 for (int s = 0; s < SIZE(p.segments); ++s) { + 35 segment& seg = p.segments.at(s); + 36 trace(99, "transform") << "segment '" << seg.name << "'" << end(); + 37 for (int i = 0; i < SIZE(seg.lines); ++i) { + 38 //? cerr << seg.name << '/' << i << '\n'; + 39 line& line = seg.lines.at(i); + 40 for (int j = 0; j < SIZE(line.words); ++j) { + 41 word& curr = line.words.at(j); + 42 if (curr.data.at(0) != '"') continue; + 43 ostringstream global_name; + 44 global_name << "__subx_global_" << Next_auto_global; + 45 ++Next_auto_global; + 46 add_global_to_data_segment(global_name.str(), curr, new_lines); + 47 curr.data = global_name.str(); + 48 } + 49 trace(99, "transform") << "line after transform: '" << data_to_string(line) << "'" << end(); + 50 } + 51 } + 52 segment* data = find(p, "data"); + 53 if (data) + 54 data->lines.insert(data->lines.end(), new_lines.begin(), new_lines.end()); + 55 } + 56 + 57 void add_global_to_data_segment(const string& name, const word& value, vector<line>& out) { + 58 trace(99, "transform") << "adding global variable '" << name << "' containing " << value.data << end(); + 59 // emit label + 60 out.push_back(label(name)); + 61 // emit size for size-prefixed array + 62 out.push_back(line()); + 63 emit_hex_bytes(out.back(), SIZE(value.data)-/*skip quotes*/2, 4/*bytes*/); + 64 // emit data byte by byte + 65 out.push_back(line()); + 66 line& curr = out.back(); + 67 for (int i = /*skip start quote*/1; i < SIZE(value.data)-/*skip end quote*/1; ++i) { + 68 char c = value.data.at(i); + 69 curr.words.push_back(word()); + 70 curr.words.back().data = hex_byte_to_string(c); + 71 curr.words.back().metadata.push_back(string(1, c)); + 72 } + 73 } + 74 + 75 //: Within strings, whitespace is significant. So we need to redo our instruction + 76 //: parsing. + 77 + 78 void test_instruction_with_string_literal() { + 79 parse_instruction_character_by_character( + 80 "a \"abc def\" z\n" // two spaces inside string + 81 ); + 82 CHECK_TRACE_CONTENTS( + 83 "parse2: word: a\n" + 84 "parse2: word: \"abc def\"\n" + 85 "parse2: word: z\n" + 86 ); + 87 // no other words + 88 CHECK_TRACE_COUNT("parse2", 3); + 89 } + 90 + 91 void test_string_literal_in_data_segment() { + 92 run( + 93 "== code 0x1\n" + 94 "b8/copy X/imm32\n" + 95 "== data 0x2000\n" + 96 "X:\n" + 97 "\"test\"/imm32\n" + 98 ); + 99 CHECK_TRACE_CONTENTS( +100 "transform: -- move literal strings to data segment\n" +101 "transform: adding global variable '__subx_global_1' containing \"test\"\n" +102 "transform: line after transform: '__subx_global_1'\n" +103 ); +104 } +105 +106 :(before "End Line Parsing Special-cases(line_data -> l)") +107 if (line_data.find('"') != string::npos) { // can cause false-positives, but we can handle them +108 parse_instruction_character_by_character(line_data, l); +109 continue; +110 } +111 +112 :(code) +113 void parse_instruction_character_by_character(const string& line_data, vector<line>& out) { +114 if (line_data.find('\n') != string::npos && line_data.find('\n') != line_data.size()-1) { +115 raise << "parse_instruction_character_by_character: should receive only a single line\n" << end(); +116 return; +117 } +118 // parse literals +119 istringstream in(line_data); +120 in >> std::noskipws; +121 line result; +122 result.original = line_data; +123 // add tokens (words or strings) one by one +124 while (has_data(in)) { +125 skip_whitespace(in); +126 if (!has_data(in)) break; +127 char c = in.get(); +128 if (c == '#') break; // comment; drop rest of line +129 if (c == ':') break; // line metadata; skip for now +130 if (c == '.') { +131 if (!has_data(in)) break; // comment token at end of line +132 if (isspace(in.peek())) +133 continue; // '.' followed by space is comment token; skip +134 } +135 result.words.push_back(word()); +136 if (c == '"') { +137 // string literal; slurp everything between quotes into data +138 ostringstream d; +139 d << c; +140 while (has_data(in)) { +141 in >> c; +142 if (c == '\\') { +143 in >> c; +144 if (c == 'n') d << '\n'; +145 else if (c == '"') d << '"'; +146 else if (c == '\\') d << '\\'; +147 else { +148 raise << "parse_instruction_character_by_character: unknown escape sequence '\\" << c << "'\n" << end(); +149 return; +150 } +151 continue; +152 } else { +153 d << c; +154 } +155 if (c == '"') break; +156 } +157 result.words.back().data = d.str(); +158 result.words.back().original = d.str(); +159 // slurp metadata +160 ostringstream m; +161 while (!isspace(in.peek()) && has_data(in)) { // peek can sometimes trigger eof(), so do it first +162 in >> c; +163 if (c == '/') { +164 if (!m.str().empty()) result.words.back().metadata.push_back(m.str()); +165 m.str(""); +166 } +167 else { +168 m << c; +169 } +170 } +171 if (!m.str().empty()) result.words.back().metadata.push_back(m.str()); +172 } +173 else { +174 // not a string literal; slurp all characters until whitespace +175 ostringstream w; +176 w << c; +177 while (!isspace(in.peek()) && has_data(in)) { // peek can sometimes trigger eof(), so do it first +178 in >> c; +179 w << c; +180 } +181 parse_word(w.str(), result.words.back()); +182 } +183 trace(99, "parse2") << "word: " << to_string(result.words.back()) << end(); +184 } +185 if (!result.words.empty()) +186 out.push_back(result); +187 } +188 +189 void skip_whitespace(istream& in) { +190 while (has_data(in) && isspace(in.peek())) { +191 in.get(); +192 } +193 } +194 +195 void skip_comment(istream& in) { +196 if (has_data(in) && in.peek() == '#') { +197 in.get(); +198 while (has_data(in) && in.peek() != '\n') in.get(); +199 } +200 } +201 +202 line label(string s) { +203 line result; +204 result.words.push_back(word()); +205 result.words.back().data = (s+":"); +206 return result; +207 } +208 +209 // helper for tests +210 void parse_instruction_character_by_character(const string& line_data) { +211 vector<line> out; +212 parse_instruction_character_by_character(line_data, out); +213 } +214 +215 void test_parse2_comment_token_in_middle() { +216 parse_instruction_character_by_character( +217 "a . z\n" +218 ); +219 CHECK_TRACE_CONTENTS( +220 "parse2: word: a\n" +221 "parse2: word: z\n" +222 ); +223 CHECK_TRACE_DOESNT_CONTAIN("parse2: word: ."); +224 // no other words +225 CHECK_TRACE_COUNT("parse2", 2); +226 } +227 +228 void test_parse2_word_starting_with_dot() { +229 parse_instruction_character_by_character( +230 "a .b c\n" +231 ); +232 CHECK_TRACE_CONTENTS( +233 "parse2: word: a\n" +234 "parse2: word: .b\n" +235 "parse2: word: c\n" +236 ); +237 } +238 +239 void test_parse2_comment_token_at_start() { +240 parse_instruction_character_by_character( +241 ". a b\n" +242 ); +243 CHECK_TRACE_CONTENTS( +244 "parse2: word: a\n" +245 "parse2: word: b\n" +246 ); +247 CHECK_TRACE_DOESNT_CONTAIN("parse2: word: ."); +248 } +249 +250 void test_parse2_comment_token_at_end() { +251 parse_instruction_character_by_character( +252 "a b .\n" +253 ); +254 CHECK_TRACE_CONTENTS( +255 "parse2: word: a\n" +256 "parse2: word: b\n" +257 ); +258 CHECK_TRACE_DOESNT_CONTAIN("parse2: word: ."); +259 } +260 +261 void test_parse2_word_starting_with_dot_at_start() { +262 parse_instruction_character_by_character( +263 ".a b c\n" +264 ); +265 CHECK_TRACE_CONTENTS( +266 "parse2: word: .a\n" +267 "parse2: word: b\n" +268 "parse2: word: c\n" +269 ); +270 } +271 +272 void test_parse2_metadata() { +273 parse_instruction_character_by_character( +274 ".a b/c d\n" +275 ); +276 CHECK_TRACE_CONTENTS( +277 "parse2: word: .a\n" +278 "parse2: word: b /c\n" +279 "parse2: word: d\n" +280 ); +281 } +282 +283 void test_parse2_string_with_metadata() { +284 parse_instruction_character_by_character( +285 "a \"bc def\"/disp32 g\n" +286 ); +287 CHECK_TRACE_CONTENTS( +288 "parse2: word: a\n" +289 "parse2: word: \"bc def\" /disp32\n" +290 "parse2: word: g\n" +291 ); +292 } +293 +294 void test_parse2_string_with_metadata_at_end() { +295 parse_instruction_character_by_character( +296 "a \"bc def\"/disp32\n" +297 ); +298 CHECK_TRACE_CONTENTS( +299 "parse2: word: a\n" +300 "parse2: word: \"bc def\" /disp32\n" +301 ); +302 } +303 +304 void test_parse2_string_with_metadata_at_end_of_line_without_newline() { +305 parse_instruction_character_by_character( +306 "68/push \"test\"/f" // no newline, which is how calls from parse() will look +307 ); +308 CHECK_TRACE_CONTENTS( +309 "parse2: word: 68 /push\n" +310 "parse2: word: \"test\" /f\n" +311 ); +312 } +313 +314 //: Make sure slashes inside strings don't trigger adding stuff from inside the +315 //: string to metadata. +316 +317 void test_parse2_string_containing_slashes() { +318 parse_instruction_character_by_character( +319 "a \"bc/def\"/disp32\n" +320 ); +321 CHECK_TRACE_CONTENTS( +322 "parse2: word: \"bc/def\" /disp32\n" +323 ); +324 } +325 +326 void test_instruction_with_string_literal_with_escaped_quote() { +327 parse_instruction_character_by_character( +328 "\"a\\\"b\"\n" // escaped quote inside string +329 ); +330 CHECK_TRACE_CONTENTS( +331 "parse2: word: \"a\"b\"\n" +332 ); +333 // no other words +334 CHECK_TRACE_COUNT("parse2", 1); +335 } +336 +337 void test_instruction_with_string_literal_with_escaped_backslash() { +338 parse_instruction_character_by_character( +339 "\"a\\\\b\"\n" // escaped backslash inside string +340 ); +341 CHECK_TRACE_CONTENTS( +342 "parse2: word: \"a\\b\"\n" +343 ); +344 // no other words +345 CHECK_TRACE_COUNT("parse2", 1); +346 } diff --git a/html/039debug.cc.html b/html/039debug.cc.html index 34cb62e0..9552da5c 100644 --- a/html/039debug.cc.html +++ b/html/039debug.cc.html @@ -60,151 +60,153 @@ if ('onhashchange' in window) {
   1 //:: Some helpers for debugging.
   2 
-  3 //: Load the 'map' file generated during 'subx --debug translate' when running
-  4 //: 'subx --debug --trace run'.
+  3 //: Load the 'map' file generated during 'bootstrap --debug translate' when running
+  4 //: 'bootstrap --trace run'.
   5 //: (It'll only affect the trace.)
   6 
   7 :(before "End Globals")
-  8 map</*address*/uint32_t, string> Symbol_name;  // used only by 'subx run'
-  9 map</*address*/uint32_t, string> Source_line;  // used only by 'subx run'
- 10 :(before "End --debug Settings")
+  8 map</*address*/uint32_t, string> Symbol_name;  // used only by 'bootstrap run'
+  9 map</*address*/uint32_t, string> Source_line;  // used only by 'bootstrap run'
+ 10 :(before "End --trace Settings")
  11 load_labels();
- 12 load_source_lines();
+ 12 load_source_lines();
  13 :(code)
  14 void load_labels() {
  15   ifstream fin("labels");
- 16   fin >> std::hex;
- 17   while (has_data(fin)) {
- 18     uint32_t addr = 0;
- 19     fin >> addr;
- 20     string name;
- 21     fin >> name;
- 22     put(Symbol_name, addr, name);
- 23   }
- 24 }
- 25 
- 26 void load_source_lines() {
- 27   ifstream fin("source_lines");
- 28   fin >> std::hex;
- 29   while (has_data(fin)) {
- 30     uint32_t addr = 0;
- 31     fin >> addr;
- 32     string line;
- 33     getline(fin, line);
- 34     put(Source_line, addr, hacky_squeeze_out_whitespace(line));
- 35   }
- 36 }
- 37 
- 38 :(after "Run One Instruction")
- 39 if (contains_key(Symbol_name, EIP))
- 40   trace(Callstack_depth, "run") << "== label " << get(Symbol_name, EIP) << end();
- 41 if (contains_key(Source_line, EIP))
- 42   trace(Callstack_depth, "run") << "0x" << HEXWORD << EIP << ": " << get(Source_line, EIP) << end();
- 43 else
- 44   // no source line info; do what you can
- 45   trace(Callstack_depth, "run") << "0x" << HEXWORD << EIP << ": " << debug_info(EIP) << end();
- 46 
- 47 :(code)
- 48 string debug_info(uint32_t inst_address) {
- 49   uint8_t op = read_mem_u8(inst_address);
- 50   if (op != 0xe8) {
- 51     ostringstream out;
- 52     out << HEXBYTE << NUM(op);
- 53     return out.str();
- 54   }
- 55   int32_t offset = read_mem_i32(inst_address+/*skip op*/1);
- 56   uint32_t next_eip = inst_address+/*inst length*/5+offset;
- 57   if (contains_key(Symbol_name, next_eip))
- 58     return "e8/call "+get(Symbol_name, next_eip);
- 59   ostringstream out;
- 60   out << "e8/call 0x" << HEXWORD << next_eip;
- 61   return out.str();
- 62 }
- 63 
- 64 //: If a label starts with '$watch-', make a note of the effective address
- 65 //: computed by the next instruction. Start dumping out its contents to the
- 66 //: trace after every subsequent instruction.
- 67 
- 68 :(after "Run One Instruction")
- 69 dump_watch_points();
- 70 :(before "End Globals")
- 71 map<string, uint32_t> Watch_points;
- 72 :(before "End Reset")
- 73 Watch_points.clear();
- 74 :(code)
- 75 void dump_watch_points() {
- 76   if (Watch_points.empty()) return;
- 77   trace(Callstack_depth, "dbg") << "watch points:" << end();
- 78   for (map<string, uint32_t>::iterator p = Watch_points.begin();  p != Watch_points.end();  ++p)
- 79     trace(Callstack_depth, "dbg") << "  " << p->first << ": " << HEXWORD << p->second << " -> " << HEXWORD << read_mem_u32(p->second) << end();
- 80 }
- 81 
- 82 :(before "End Globals")
- 83 string Watch_this_effective_address;
- 84 :(after "Run One Instruction")
- 85 Watch_this_effective_address = "";
- 86 if (contains_key(Symbol_name, EIP) && starts_with(get(Symbol_name, EIP), "$watch-"))
- 87   Watch_this_effective_address = get(Symbol_name, EIP);
- 88 :(after "Found effective_address(addr)")
- 89 if (!Watch_this_effective_address.empty()) {
- 90   dbg << "now watching " << HEXWORD << addr << " for " << Watch_this_effective_address << end();
- 91   put(Watch_points, Watch_this_effective_address, addr);
- 92 }
- 93 
- 94 //: Special label that dumps regions of memory.
- 95 //: Not a general mechanism; by the time you get here you're willing to hack
- 96 //: on the emulator.
- 97 :(after "Run One Instruction")
- 98 if (contains_key(Symbol_name, EIP) && get(Symbol_name, EIP) == "$dump-stream-at-EAX")
- 99   dump_stream_at(Reg[EAX].u);
-100 :(code)
-101 void dump_stream_at(uint32_t stream_start) {
-102   int32_t stream_length = read_mem_i32(stream_start + 8);
-103   dbg << "stream length: " << std::dec << stream_length << end();
-104   for (int i = 0;  i < stream_length + 12;  ++i)
-105     dbg << "0x" << HEXWORD << (stream_start+i) << ": " << HEXBYTE << NUM(read_mem_u8(stream_start+i)) << end();
-106 }
-107 
-108 //: helpers
+ 16   if (fin.fail()) return;
+ 17   fin >> std::hex;
+ 18   while (has_data(fin)) {
+ 19     uint32_t addr = 0;
+ 20     fin >> addr;
+ 21     string name;
+ 22     fin >> name;
+ 23     put(Symbol_name, addr, name);
+ 24   }
+ 25 }
+ 26 
+ 27 void load_source_lines() {
+ 28   ifstream fin("source_lines");
+ 29   if (fin.fail()) return;
+ 30   fin >> std::hex;
+ 31   while (has_data(fin)) {
+ 32     uint32_t addr = 0;
+ 33     fin >> addr;
+ 34     string line;
+ 35     getline(fin, line);
+ 36     put(Source_line, addr, hacky_squeeze_out_whitespace(line));
+ 37   }
+ 38 }
+ 39 
+ 40 :(after "Run One Instruction")
+ 41 if (contains_key(Symbol_name, EIP))
+ 42   trace(Callstack_depth, "run") << "== label " << get(Symbol_name, EIP) << end();
+ 43 if (contains_key(Source_line, EIP))
+ 44   trace(Callstack_depth, "run") << "inst: " << get(Source_line, EIP) << end();
+ 45 else
+ 46   // no source line info; do what you can
+ 47   trace(Callstack_depth, "run") << "inst: " << debug_info(EIP) << end();
+ 48 
+ 49 :(code)
+ 50 string debug_info(uint32_t inst_address) {
+ 51   uint8_t op = read_mem_u8(inst_address);
+ 52   if (op != 0xe8) {
+ 53     ostringstream out;
+ 54     out << HEXBYTE << NUM(op);
+ 55     return out.str();
+ 56   }
+ 57   int32_t offset = read_mem_i32(inst_address+/*skip op*/1);
+ 58   uint32_t next_eip = inst_address+/*inst length*/5+offset;
+ 59   if (contains_key(Symbol_name, next_eip))
+ 60     return "e8/call "+get(Symbol_name, next_eip);
+ 61   ostringstream out;
+ 62   out << "e8/call 0x" << HEXWORD << next_eip;
+ 63   return out.str();
+ 64 }
+ 65 
+ 66 //: If a label starts with '$watch-', make a note of the effective address
+ 67 //: computed by the next instruction. Start dumping out its contents to the
+ 68 //: trace after every subsequent instruction.
+ 69 
+ 70 :(after "Run One Instruction")
+ 71 dump_watch_points();
+ 72 :(before "End Globals")
+ 73 map<string, uint32_t> Watch_points;
+ 74 :(before "End Reset")
+ 75 Watch_points.clear();
+ 76 :(code)
+ 77 void dump_watch_points() {
+ 78   if (Watch_points.empty()) return;
+ 79   trace(Callstack_depth, "dbg") << "watch points:" << end();
+ 80   for (map<string, uint32_t>::iterator p = Watch_points.begin();  p != Watch_points.end();  ++p)
+ 81     trace(Callstack_depth, "dbg") << "  " << p->first << ": " << HEXWORD << p->second << " -> " << HEXWORD << read_mem_u32(p->second) << end();
+ 82 }
+ 83 
+ 84 :(before "End Globals")
+ 85 string Watch_this_effective_address;
+ 86 :(after "Run One Instruction")
+ 87 Watch_this_effective_address = "";
+ 88 if (contains_key(Symbol_name, EIP) && starts_with(get(Symbol_name, EIP), "$watch-"))
+ 89   Watch_this_effective_address = get(Symbol_name, EIP);
+ 90 :(after "Found effective_address(addr)")
+ 91 if (!Watch_this_effective_address.empty()) {
+ 92   dbg << "now watching " << HEXWORD << addr << " for " << Watch_this_effective_address << end();
+ 93   put(Watch_points, Watch_this_effective_address, addr);
+ 94 }
+ 95 
+ 96 //: Special label that dumps regions of memory.
+ 97 //: Not a general mechanism; by the time you get here you're willing to hack
+ 98 //: on the emulator.
+ 99 :(after "Run One Instruction")
+100 if (contains_key(Symbol_name, EIP) && get(Symbol_name, EIP) == "$dump-stream-at-EAX")
+101   dump_stream_at(Reg[EAX].u);
+102 :(code)
+103 void dump_stream_at(uint32_t stream_start) {
+104   int32_t stream_length = read_mem_i32(stream_start + 8);
+105   dbg << "stream length: " << std::dec << stream_length << end();
+106   for (int i = 0;  i < stream_length + 12;  ++i)
+107     dbg << "0x" << HEXWORD << (stream_start+i) << ": " << HEXBYTE << NUM(read_mem_u8(stream_start+i)) << end();
+108 }
 109 
-110 :(code)
-111 string hacky_squeeze_out_whitespace(const string& s) {
-112   // strip whitespace at start
-113   string::const_iterator first = s.begin();
-114   while (first != s.end() && isspace(*first))
-115     ++first;
-116   if (first == s.end()) return "";
-117 
-118   // strip whitespace at end
-119   string::const_iterator last = --s.end();
-120   while (last != s.begin() && isspace(*last))
-121     --last;
-122   ++last;
-123 
-124   // replace runs of spaces/dots with single space until comment or string
-125   // TODO:
-126   //   leave alone dots not surrounded by whitespace
-127   //   leave alone '#' within word
-128   //   leave alone '"' within word
-129   //   squeeze spaces after end of string
-130   ostringstream out;
-131   bool previous_was_space = false;
-132   bool in_comment_or_string = false;
-133   for (string::const_iterator curr = first;  curr != last;  ++curr) {
-134     if (in_comment_or_string)
-135       out << *curr;
-136     else if (isspace(*curr) || *curr == '.')
-137       previous_was_space = true;
-138     else {
-139       if (previous_was_space)
-140         out << ' ';
-141       out << *curr;
-142       previous_was_space = false;
-143       if (*curr == '#' || *curr == '"') in_comment_or_string = true;
-144     }
-145   }
-146   return out.str();
-147 }
+110 //: helpers
+111 
+112 :(code)
+113 string hacky_squeeze_out_whitespace(const string& s) {
+114   // strip whitespace at start
+115   string::const_iterator first = s.begin();
+116   while (first != s.end() && isspace(*first))
+117     ++first;
+118   if (first == s.end()) return "";
+119 
+120   // strip whitespace at end
+121   string::const_iterator last = --s.end();
+122   while (last != s.begin() && isspace(*last))
+123     --last;
+124   ++last;
+125 
+126   // replace runs of spaces/dots with single space until comment or string
+127   // TODO:
+128   //   leave alone dots not surrounded by whitespace
+129   //   leave alone '#' within word
+130   //   leave alone '"' within word
+131   //   squeeze spaces after end of string
+132   ostringstream out;
+133   bool previous_was_space = false;
+134   bool in_comment_or_string = false;
+135   for (string::const_iterator curr = first;  curr != last;  ++curr) {
+136     if (in_comment_or_string)
+137       out << *curr;
+138     else if (isspace(*curr) || *curr == '.')
+139       previous_was_space = true;
+140     else {
+141       if (previous_was_space)
+142         out << ' ';
+143       out << *curr;
+144       previous_was_space = false;
+145       if (*curr == '#' || *curr == '"') in_comment_or_string = true;
+146     }
+147   }
+148   return out.str();
+149 }
 
diff --git a/html/040---tests.cc.html b/html/040---tests.cc.html index a0cb8a6f..e2dbd53e 100644 --- a/html/040---tests.cc.html +++ b/html/040---tests.cc.html @@ -69,91 +69,89 @@ if ('onhashchange' in window) { 10 //: We don't rely on any transforms running in previous layers, but this layer 11 //: knows about labels and will emit labels for previous layers to transform. 12 :(after "Begin Transforms") -13 // Begin Level-4 Transforms -14 Transform.push_back(create_test_function); -15 // End Level-4 Transforms -16 -17 :(code) -18 void test_run_test() { -19 Mem.push_back(vma(0xbd000000)); // manually allocate memory -20 Reg[ESP].u = 0xbd000100; -21 run( -22 "== code 0x1\n" // code segment -23 "main:\n" -24 " e8/call run-tests/disp32\n" // 5 bytes -25 " f4/halt\n" // 1 byte -26 "test-foo:\n" // offset 7 -27 " 01 d8\n" // just some unique instruction: add EBX to EAX -28 " c3/return\n" -29 ); -30 // check that code in test-foo ran (implicitly called by run-tests) -31 CHECK_TRACE_CONTENTS( -32 "run: 0x00000007 opcode: 01\n" -33 ); -34 } -35 -36 void create_test_function(program& p) { -37 if (p.segments.empty()) return; -38 segment& code = *find(p, "code"); -39 trace(3, "transform") << "-- create 'run-tests'" << end(); -40 vector<line> new_insts; -41 for (int i = 0; i < SIZE(code.lines); ++i) { -42 line& inst = code.lines.at(i); -43 for (int j = 0; j < SIZE(inst.words); ++j) { -44 const word& curr = inst.words.at(j); -45 if (*curr.data.rbegin() != ':') continue; // not a label -46 if (!starts_with(curr.data, "test-")) continue; -47 string fn = drop_last(curr.data); -48 new_insts.push_back(call(fn)); -49 } -50 } -51 if (new_insts.empty()) return; // no tests found -52 code.lines.push_back(label("run-tests")); -53 code.lines.insert(code.lines.end(), new_insts.begin(), new_insts.end()); -54 code.lines.push_back(ret()); -55 } -56 -57 string to_string(const segment& s) { -58 ostringstream out; -59 for (int i = 0; i < SIZE(s.lines); ++i) { -60 const line& l = s.lines.at(i); -61 for (int j = 0; j < SIZE(l.words); ++j) { -62 if (j > 0) out << ' '; -63 out << to_string(l.words.at(j)); -64 } -65 out << '\n'; -66 } -67 return out.str(); -68 } -69 -70 line call(string s) { -71 line result; -72 result.words.push_back(call()); -73 result.words.push_back(disp32(s)); -74 return result; -75 } -76 -77 word call() { -78 word result; -79 result.data = "e8"; -80 result.metadata.push_back("call"); -81 return result; -82 } -83 -84 word disp32(string s) { -85 word result; -86 result.data = s; -87 result.metadata.push_back("disp32"); -88 return result; -89 } -90 -91 line ret() { -92 line result; -93 result.words.push_back(word()); -94 result.words.back().data = "c3"; -95 result.words.back().metadata.push_back("return"); -96 return result; -97 } +13 Transform.push_back(create_test_function); +14 +15 :(code) +16 void test_run_test() { +17 Mem.push_back(vma(0xbd000000)); // manually allocate memory +18 Reg[ESP].u = 0xbd000100; +19 run( +20 "== code 0x1\n" // code segment +21 "main:\n" +22 " e8/call run-tests/disp32\n" // 5 bytes +23 " f4/halt\n" // 1 byte +24 "test-foo:\n" // offset 7 +25 " 01 d8\n" // just some unique instruction: add EBX to EAX +26 " c3/return\n" +27 ); +28 // check that code in test-foo ran (implicitly called by run-tests) +29 CHECK_TRACE_CONTENTS( +30 "run: 0x00000007 opcode: 01\n" +31 ); +32 } +33 +34 void create_test_function(program& p) { +35 if (p.segments.empty()) return; +36 segment& code = *find(p, "code"); +37 trace(3, "transform") << "-- create 'run-tests'" << end(); +38 vector<line> new_insts; +39 for (int i = 0; i < SIZE(code.lines); ++i) { +40 line& inst = code.lines.at(i); +41 for (int j = 0; j < SIZE(inst.words); ++j) { +42 const word& curr = inst.words.at(j); +43 if (*curr.data.rbegin() != ':') continue; // not a label +44 if (!starts_with(curr.data, "test-")) continue; +45 string fn = drop_last(curr.data); +46 new_insts.push_back(call(fn)); +47 } +48 } +49 if (new_insts.empty()) return; // no tests found +50 code.lines.push_back(label("run-tests")); +51 code.lines.insert(code.lines.end(), new_insts.begin(), new_insts.end()); +52 code.lines.push_back(ret()); +53 } +54 +55 string to_string(const segment& s) { +56 ostringstream out; +57 for (int i = 0; i < SIZE(s.lines); ++i) { +58 const line& l = s.lines.at(i); +59 for (int j = 0; j < SIZE(l.words); ++j) { +60 if (j > 0) out << ' '; +61 out << to_string(l.words.at(j)); +62 } +63 out << '\n'; +64 } +65 return out.str(); +66 } +67 +68 line call(string s) { +69 line result; +70 result.words.push_back(call()); +71 result.words.push_back(disp32(s)); +72 return result; +73 } +74 +75 word call() { +76 word result; +77 result.data = "e8"; +78 result.metadata.push_back("call"); +79 return result; +80 } +81 +82 word disp32(string s) { +83 word result; +84 result.data = s; +85 result.metadata.push_back("disp32"); +86 return result; +87 } +88 +89 line ret() { +90 line result; +91 result.words.push_back(word()); +92 result.words.back().data = "c3"; +93 result.words.back().metadata.push_back("return"); +94 return result; +95 } -- cgit 1.4.1-2-gfad0