From 3350c34a74844e21ea69077e01efff3bae64bdcd Mon Sep 17 00:00:00 2001 From: Kartik Agaram Date: Tue, 23 Mar 2021 17:31:08 -0700 Subject: . --- html/linux/bootstrap/000organization.cc.html | 227 ++++ html/linux/bootstrap/001help.cc.html | 335 +++++ html/linux/bootstrap/002test.cc.html | 187 +++ html/linux/bootstrap/003trace.cc.html | 586 +++++++++ html/linux/bootstrap/003trace.test.cc.html | 194 +++ html/linux/bootstrap/010vm.cc.html | 474 +++++++ html/linux/bootstrap/011run.cc.html | 515 ++++++++ html/linux/bootstrap/012elf.cc.html | 257 ++++ html/linux/bootstrap/013direct_addressing.cc.html | 1344 +++++++++++++++++++ .../linux/bootstrap/014indirect_addressing.cc.html | 1067 +++++++++++++++ .../bootstrap/015immediate_addressing.cc.html | 1374 ++++++++++++++++++++ html/linux/bootstrap/016index_addressing.cc.html | 217 ++++ html/linux/bootstrap/017jump_disp8.cc.html | 469 +++++++ html/linux/bootstrap/018jump_disp32.cc.html | 469 +++++++ html/linux/bootstrap/019functions.cc.html | 185 +++ html/linux/bootstrap/020byte_addressing.cc.html | 334 +++++ html/linux/bootstrap/021div.cc.html | 100 ++ html/linux/bootstrap/022float.cc.html | 582 +++++++++ html/linux/bootstrap/029syscalls.cc.html | 189 +++ html/linux/bootstrap/030translate.cc.html | 270 ++++ html/linux/bootstrap/031transforms.cc.html | 72 + html/linux/bootstrap/032operands.cc.html | 703 ++++++++++ html/linux/bootstrap/033check_operands.cc.html | 851 ++++++++++++ .../bootstrap/034check_operand_bounds.cc.html | 205 +++ .../bootstrap/035compute_segment_address.cc.html | 148 +++ html/linux/bootstrap/036labels.cc.html | 491 +++++++ html/linux/bootstrap/037global_variables.cc.html | 368 ++++++ html/linux/bootstrap/038literal_strings.cc.html | 425 ++++++ html/linux/bootstrap/039debug.cc.html | 237 ++++ html/linux/bootstrap/040tests.cc.html | 157 +++ 30 files changed, 13032 insertions(+) create mode 100644 html/linux/bootstrap/000organization.cc.html create mode 100644 html/linux/bootstrap/001help.cc.html create mode 100644 html/linux/bootstrap/002test.cc.html create mode 100644 html/linux/bootstrap/003trace.cc.html create mode 100644 html/linux/bootstrap/003trace.test.cc.html create mode 100644 html/linux/bootstrap/010vm.cc.html create mode 100644 html/linux/bootstrap/011run.cc.html create mode 100644 html/linux/bootstrap/012elf.cc.html create mode 100644 html/linux/bootstrap/013direct_addressing.cc.html create mode 100644 html/linux/bootstrap/014indirect_addressing.cc.html create mode 100644 html/linux/bootstrap/015immediate_addressing.cc.html create mode 100644 html/linux/bootstrap/016index_addressing.cc.html create mode 100644 html/linux/bootstrap/017jump_disp8.cc.html create mode 100644 html/linux/bootstrap/018jump_disp32.cc.html create mode 100644 html/linux/bootstrap/019functions.cc.html create mode 100644 html/linux/bootstrap/020byte_addressing.cc.html create mode 100644 html/linux/bootstrap/021div.cc.html create mode 100644 html/linux/bootstrap/022float.cc.html create mode 100644 html/linux/bootstrap/029syscalls.cc.html create mode 100644 html/linux/bootstrap/030translate.cc.html create mode 100644 html/linux/bootstrap/031transforms.cc.html create mode 100644 html/linux/bootstrap/032operands.cc.html create mode 100644 html/linux/bootstrap/033check_operands.cc.html create mode 100644 html/linux/bootstrap/034check_operand_bounds.cc.html create mode 100644 html/linux/bootstrap/035compute_segment_address.cc.html create mode 100644 html/linux/bootstrap/036labels.cc.html create mode 100644 html/linux/bootstrap/037global_variables.cc.html create mode 100644 html/linux/bootstrap/038literal_strings.cc.html create mode 100644 html/linux/bootstrap/039debug.cc.html create mode 100644 html/linux/bootstrap/040tests.cc.html (limited to 'html/linux/bootstrap') diff --git a/html/linux/bootstrap/000organization.cc.html b/html/linux/bootstrap/000organization.cc.html new file mode 100644 index 00000000..56125b37 --- /dev/null +++ b/html/linux/bootstrap/000organization.cc.html @@ -0,0 +1,227 @@ + + + + +Mu - linux/bootstrap/000organization.cc + + + + + + + + + + +https://github.com/akkartik/mu/blob/main/linux/bootstrap/000organization.cc +
+  1 //: You guessed right: the '000' prefix means you should start reading here.
+  2 //:
+  3 //: This project is set up to load all files with a numeric prefix. Just
+  4 //: create a new file and start hacking.
+  5 //:
+  6 //: The first few files (00*) are independent of what this program does, an
+  7 //: experimental skeleton that will hopefully make it both easier for others to
+  8 //: understand and more malleable, easier to rewrite and remould into radically
+  9 //: different shapes without breaking in subtle corner cases. The premise is
+ 10 //: that understandability and rewrite-friendliness are related in a virtuous
+ 11 //: cycle. Doing one well makes it easier to do the other.
+ 12 //:
+ 13 //: Lower down, this file contains a legal, bare-bones C++ program. It doesn't
+ 14 //: do anything yet; subsequent files will contain :(...) directives to insert
+ 15 //: lines into it. For example:
+ 16 //:   :(after "more events")
+ 17 //: This directive means: insert the following lines after a line in the
+ 18 //: program containing the words "more events".
+ 19 //:
+ 20 //: A simple tool is included to 'tangle' all the files together in sequence
+ 21 //: according to their directives into a single source file containing all the
+ 22 //: code for the project, and then feed the source file to the compiler.
+ 23 //: (It'll drop these comments starting with a '//:' prefix that only make
+ 24 //: sense before tangling.)
+ 25 //:
+ 26 //: Directives free up the programmer to order code for others to read rather
+ 27 //: than as forced by the computer or compiler. Each individual feature can be
+ 28 //: organized in a self-contained 'layer' that adds code to many different data
+ 29 //: structures and functions all over the program. The right decomposition into
+ 30 //: layers will let each layer make sense in isolation.
+ 31 //:
+ 32 //:   "If I look at any small part of it, I can see what is going on -- I don't
+ 33 //:   need to refer to other parts to understand what something is doing.
+ 34 //:
+ 35 //:   If I look at any large part in overview, I can see what is going on -- I
+ 36 //:   don't need to know all the details to get it.
+ 37 //:
+ 38 //:   Every level of detail is as locally coherent and as well thought-out as
+ 39 //:   any other level."
+ 40 //:
+ 41 //:       -- Richard Gabriel, "The Quality Without A Name"
+ 42 //:          (http://dreamsongs.com/Files/PatternsOfSoftware.pdf, page 42)
+ 43 //:
+ 44 //: Directives are powerful; they permit inserting or modifying any point in
+ 45 //: the program. Using them tastefully requires mapping out specific lines as
+ 46 //: waypoints for future layers to hook into. Often such waypoints will be in
+ 47 //: comments, capitalized to hint that other layers rely on their presence.
+ 48 //:
+ 49 //: A single waypoint might have many different code fragments hooking into
+ 50 //: it from all over the codebase. Use 'before' directives to insert
+ 51 //: code at a location in order, top to bottom, and 'after' directives to
+ 52 //: insert code in reverse order. By convention waypoints intended for insertion
+ 53 //: before begin with 'End'. Notice below how the layers line up above the "End
+ 54 //: Foo" waypoint.
+ 55 //:
+ 56 //:   File 001          File 002                File 003
+ 57 //:   ============      ===================     ===================
+ 58 //:   // Foo
+ 59 //:   ------------
+ 60 //:              <----  :(before "End Foo")
+ 61 //:                     ....
+ 62 //:                     ...
+ 63 //:   ------------
+ 64 //:              <----------------------------  :(before "End Foo")
+ 65 //:                                             ....
+ 66 //:                                             ...
+ 67 //:   // End Foo
+ 68 //:   ============
+ 69 //:
+ 70 //: Here's part of a layer in color: http://i.imgur.com/0eONnyX.png. Directives
+ 71 //: are shaded dark.
+ 72 //:
+ 73 //: Layers do more than just shuffle code around. In a well-organized codebase
+ 74 //: it should be possible to stop loading after any file/layer, build and run
+ 75 //: the program, and pass all tests for loaded features. (Relevant is
+ 76 //: http://youtube.com/watch?v=c8N72t7aScY, a scene from "2001: A Space
+ 77 //: Odyssey".) Get into the habit of running the included script called
+ 78 //: 'test_layers' before you commit any changes.
+ 79 //:
+ 80 //: This 'subsetting guarantee' ensures that this directory contains a
+ 81 //: cleaned-up narrative of the evolution of this codebase. Organizing
+ 82 //: autobiographically allows newcomers to rapidly orient themselves, reading
+ 83 //: the first few files to understand a simple gestalt of a program's core
+ 84 //: purpose and features, and later gradually working their way through other
+ 85 //: features as the need arises.
+ 86 //:
+ 87 //: Programmers shouldn't need to understand everything about a program to
+ 88 //: hack on it. But they shouldn't be prevented from a thorough understanding
+ 89 //: of each aspect either. The goal of layers is to reward curiosity.
+ 90 //:
+ 91 //: More information: http://akkartik.name/post/wart-layers
+ 92 
+ 93 // Includes
+ 94 // End Includes
+ 95 
+ 96 // Types
+ 97 // End Types
+ 98 
+ 99 // Function prototypes are auto-generated in the 'build' script; define your
+100 // functions in any order. Just be sure to declare each function header all on
+101 // one line, ending with the '{'. Our auto-generation scripts are too minimal
+102 // and simple-minded to handle anything else.
+103 #include "function_list"  // by convention, files ending with '_list' are auto-generated
+104 
+105 // Globals
+106 //
+107 // All statements in this section should always define a single variable on a
+108 // single line. The 'build' script will simple-mindedly auto-generate extern
+109 // declarations for them. Remember to define (not just declare) constants with
+110 // extern linkage in this section, since C++ global constants have internal
+111 // linkage by default.
+112 //
+113 // End Globals
+114 
+115 int main(int argc, char* argv[]) {
+116   atexit(reset);
+117   // we require a 32-bit little-endian system
+118   assert(sizeof(int) == 4);
+119   assert(sizeof(float) == 4);
+120   assert_little_endian();
+121 
+122   // End One-time Setup
+123 
+124   // Commandline Parsing
+125   // End Commandline Parsing
+126 
+127   // End Main
+128 
+129   return 0;
+130 }
+131 
+132 // Unit Tests
+133 // End Unit Tests
+134 
+135 //: our first directive; insert the following headers at the start of the program
+136 :(before "End Includes")
+137 #include <assert.h>
+138 #include <stdlib.h>
+139 
+140 //: Without directives or with the :(code) directive, lines get added at the
+141 //: end.
+142 //:
+143 //: Regardless of where functions are defined, we can call them anywhere we
+144 //: like as long as we format the function header in a specific way: put it
+145 //: all on a single line without indent, end the line with ') {' and no
+146 //: trailing whitespace. As long as functions uniformly start this way, our
+147 //: 'build' script contains a little command to automatically generate
+148 //: declarations for them.
+149 :(code)
+150 void reset() {
+151   // End Reset
+152 }
+153 
+154 void assert_little_endian() {
+155   const int x = 1;
+156   const char* y = reinterpret_cast<const char*>(&x);
+157   if (*y != 1) {
+158     cerr << "SubX requires a little-endian processor. Do you have Intel (or AMD or Atom) inside?\n";
+159     exit(1);
+160   }
+161 }
+162 :(before "End Includes")
+163 #include<iostream>
+164 using std::cerr;
+
+ + + diff --git a/html/linux/bootstrap/001help.cc.html b/html/linux/bootstrap/001help.cc.html new file mode 100644 index 00000000..ff76f367 --- /dev/null +++ b/html/linux/bootstrap/001help.cc.html @@ -0,0 +1,335 @@ + + + + +Mu - linux/bootstrap/001help.cc + + + + + + + + + + +https://github.com/akkartik/mu/blob/main/linux/bootstrap/001help.cc +
+  1 //: Everything this project/binary supports.
+  2 //: This should give you a sense for what to look forward to in later layers.
+  3 
+  4 :(before "End Commandline Parsing")
+  5 if (argc <= 1 || is_equal(argv[1], "--help")) {
+  6   //: this is the functionality later layers will provide
+  7   // currently no automated tests for commandline arg parsing
+  8   cerr << get(Help, "usage");
+  9   return 0;
+ 10 }
+ 11 
+ 12 //: Support for option parsing.
+ 13 //: Options always begin with '--' and are always the first arguments. An
+ 14 //: option will never follow a non-option.
+ 15 char** arg = &argv[1];
+ 16 while (argc > 1 && starts_with(*arg, "--")) {
+ 17   if (false)
+ 18     ;  // no-op branch just so any further additions can consistently always start with 'else'
+ 19   // End Commandline Options(*arg)
+ 20   else
+ 21     cerr << "skipping unknown option " << *arg << '\n';
+ 22   --argc;  ++argv;  ++arg;
+ 23 }
+ 24 
+ 25 if (is_equal(argv[1], "help")) {
+ 26   if (argc == 2) {
+ 27     cerr << "help on what?\n";
+ 28     help_contents();
+ 29     return 0;
+ 30   }
+ 31   string key(argv[2]);
+ 32   // End Help Special-cases(key)
+ 33   if (contains_key(Help, key)) {
+ 34     cerr << get(Help, key);
+ 35     return 0;
+ 36   }
+ 37   else {
+ 38     cerr << "No help found for '" << key << "'\n";
+ 39     help_contents();
+ 40     cerr << "Please check your command for typos.\n";
+ 41     return 1;
+ 42   }
+ 43 }
+ 44 
+ 45 :(code)
+ 46 void help_contents() {
+ 47   cerr << "Available top-level topics:\n";
+ 48   cerr << "  usage\n";
+ 49   // End Help Contents
+ 50 }
+ 51 
+ 52 :(before "End Globals")
+ 53 map<string, string> Help;
+ 54 :(before "End Includes")
+ 55 #include <map>
+ 56 using std::map;
+ 57 :(before "End One-time Setup")
+ 58 init_help();
+ 59 :(code)
+ 60 void init_help() {
+ 61   put(Help, "usage",
+ 62     "bootstrap: the bootstrap translator for SubX.\n"
+ 63     "This program also wraps some miscellaneous useful functionality:\n"
+ 64     "  - an x86 emulator: `bootstrap run`\n"
+ 65     "  - online help: `bootstrap help`\n"
+ 66     "\n"
+ 67     "== Ways to invoke bootstrap\n"
+ 68     "- See this message:\n"
+ 69     "    bootstrap --help\n"
+ 70     "- Convert a textual SubX program into a standard ELF binary that you can\n"
+ 71     "  run on your computer:\n"
+ 72     "    bootstrap translate input1.subx input2.subx ... -o <output ELF binary>\n"
+ 73     "- Run a SubX binary using SubX itself (for better error messages):\n"
+ 74     "    bootstrap run <ELF binary>\n"
+ 75     "- Run all bootstrap's unit tests:\n"
+ 76     "    bootstrap test\n"
+ 77     "- Run a single unit test:\n"
+ 78     "    bootstrap test <test name>\n"
+ 79     "     e.g. bootstrap test test_copy_imm32_to_EAX\n"
+ 80     "\n"
+ 81     "== Debugging aids\n"
+ 82     "- Add '--trace' to any of these commands to save a trace to disk at the end.\n"
+ 83     "  This can run out of memory for long-running commands.\n"
+ 84     "- Add '--debug' to emit additional debug information during translation.\n"
+ 85     "  'bootstrap --debug translate' will save metadata to disk that\n"
+ 86     "  'bootstrap --trace run' uses to make traces more informative.\n"
+ 87     "\n"
+ 88     "Options starting with '--' must always come before any other arguments.\n"
+ 89     "\n"
+ 90     "To start learning how to write SubX programs, see Readme.md (particularly\n"
+ 91     "the section on the x86 instruction set) and then run:\n"
+ 92     "  bootstrap help\n"
+ 93   );
+ 94   // End Help Texts
+ 95 }
+ 96 
+ 97 :(code)
+ 98 bool is_equal(const char* s, const char* lit) {
+ 99   size_t len = strlen(lit);
+100   if (strlen(s) != len) return false;
+101   return strncmp(s, lit, len) == 0;
+102 }
+103 
+104 bool starts_with(const string& s, const string& pat) {
+105   string::const_iterator a=s.begin(), b=pat.begin();
+106   for (/*nada*/;  a!=s.end() && b!=pat.end();  ++a, ++b)
+107     if (*a != *b) return false;
+108   return b == pat.end();
+109 }
+110 
+111 //: I'll throw some style conventions here for want of a better place for them.
+112 //: As a rule I hate style guides. Do what you want, that's my motto. But since
+113 //: we're dealing with C/C++, the one big thing we want to avoid is undefined
+114 //: behavior. If a compiler ever encounters undefined behavior it can make
+115 //: your program do anything it wants.
+116 //:
+117 //: For reference, my checklist of undefined behaviors to watch out for:
+118 //:   out-of-bounds access
+119 //:   uninitialized variables
+120 //:   use after free
+121 //:   dereferencing invalid pointers: null, a new of size 0, others
+122 //:
+123 //:   casting a large number to a type too small to hold it
+124 //:
+125 //:   integer overflow
+126 //:   division by zero and other undefined expressions
+127 //:   left-shift by negative count
+128 //:   shifting values by more than or equal to the number of bits they contain
+129 //:   bitwise operations on signed numbers
+130 //:
+131 //:   Converting pointers to types of different alignment requirements
+132 //:     T* -> void* -> T*: defined
+133 //:     T* -> U* -> T*: defined if non-function pointers and alignment requirements are same
+134 //:     function pointers may be cast to other function pointers
+135 //:
+136 //:       Casting a numeric value into a value that can't be represented by the target type (either directly or via static_cast)
+137 //:
+138 //: To guard against these, some conventions:
+139 //:
+140 //: 0. Initialize all primitive variables in functions and constructors.
+141 //:
+142 //: 1. Minimize use of pointers and pointer arithmetic. Avoid 'new' and
+143 //: 'delete' as far as possible. Rely on STL to perform memory management to
+144 //: avoid use-after-free issues (and memory leaks).
+145 //:
+146 //: 2. Avoid naked arrays to avoid out-of-bounds access. Never use operator[]
+147 //: except with map. Use at() with STL vectors and so on.
+148 //:
+149 //: 3. Valgrind all the things.
+150 //:
+151 //: 4. Avoid unsigned numbers. Not strictly an undefined-behavior issue, but
+152 //: the extra range doesn't matter, and it's one less confusing category of
+153 //: interaction gotchas to worry about.
+154 //:
+155 //: Corollary: don't use the size() method on containers, since it returns an
+156 //: unsigned and that'll cause warnings about mixing signed and unsigned,
+157 //: yadda-yadda. Instead use this macro below to perform an unsafe cast to
+158 //: signed. We'll just give up immediately if a container's ever too large.
+159 //: Basically, Mu is not concerned about this being a little slower than it
+160 //: could be. (https://gist.github.com/rygorous/e0f055bfb74e3d5f0af20690759de5a7)
+161 //:
+162 //: Addendum to corollary: We're going to uniformly use int everywhere, to
+163 //: indicate that we're oblivious to number size, and since Clang on 32-bit
+164 //: platforms doesn't yet support multiplication over 64-bit integers, and
+165 //: since multiplying two integers seems like a more common situation to end
+166 //: up in than integer overflow.
+167 :(before "End Includes")
+168 #define SIZE(X) (assert((X).size() < (1LL<<(sizeof(int)*8-2))), static_cast<int>((X).size()))
+169 
+170 //: 5. Integer overflow is guarded against at runtime using the -ftrapv flag
+171 //: to the compiler, supported by Clang (GCC version only works sometimes:
+172 //: http://stackoverflow.com/questions/20851061/how-to-make-gcc-ftrapv-work).
+173 :(before "atexit(reset)")
+174 initialize_signal_handlers();  // not always necessary, but doesn't hurt
+175 //? cerr << INT_MAX+1 << '\n';  // test overflow
+176 //? assert(false);  // test SIGABRT
+177 :(code)
+178 // based on https://spin.atomicobject.com/2013/01/13/exceptions-stack-traces-c
+179 void initialize_signal_handlers() {
+180   struct sigaction action;
+181   bzero(&action, sizeof(action));
+182   action.sa_sigaction = dump_and_exit;
+183   sigemptyset(&action.sa_mask);
+184   sigaction(SIGABRT, &action, NULL);  // assert() failure or integer overflow on linux (with -ftrapv)
+185   sigaction(SIGILL,  &action, NULL);  // integer overflow on OS X (with -ftrapv)
+186 }
+187 void dump_and_exit(int sig, siginfo_t* /*unused*/, void* /*unused*/) {
+188   switch (sig) {
+189     case SIGABRT:
+190       #ifndef __APPLE__
+191         cerr << "SIGABRT: might be an integer overflow if it wasn't an assert() failure\n";
+192         _Exit(1);
+193       #endif
+194       break;
+195     case SIGILL:
+196       #ifdef __APPLE__
+197         cerr << "SIGILL: most likely caused by integer overflow\n";
+198         _Exit(1);
+199       #endif
+200       break;
+201     default:
+202       break;
+203   }
+204 }
+205 :(before "End Includes")
+206 #include <signal.h>
+207 
+208 //: 6. Map's operator[] being non-const is fucking evil.
+209 :(before "Globals")  // can't generate prototypes for these
+210 // from http://stackoverflow.com/questions/152643/idiomatic-c-for-reading-from-a-const-map
+211 template<typename T> typename T::mapped_type& get(T& map, typename T::key_type const& key) {
+212   typename T::iterator iter(map.find(key));
+213   if (iter == map.end()) {
+214     cerr << "get couldn't find key '" << key << "'\n";
+215     assert(iter != map.end());
+216   }
+217   return iter->second;
+218 }
+219 template<typename T> typename T::mapped_type const& get(const T& map, typename T::key_type const& key) {
+220   typename T::const_iterator iter(map.find(key));
+221   if (iter == map.end()) {
+222     cerr << "get couldn't find key '" << key << "'\n";
+223     assert(iter != map.end());
+224   }
+225   return iter->second;
+226 }
+227 template<typename T> typename T::mapped_type const& put(T& map, typename T::key_type const& key, typename T::mapped_type const& value) {
+228   map[key] = value;
+229   return map[key];
+230 }
+231 template<typename T> bool contains_key(T& map, typename T::key_type const& key) {
+232   return map.find(key) != map.end();
+233 }
+234 template<typename T> typename T::mapped_type& get_or_insert(T& map, typename T::key_type const& key) {
+235   return map[key];
+236 }
+237 template<typename T> typename T::mapped_type const& put_new(T& map, typename T::key_type const& key, typename T::mapped_type const& value) {
+238   assert(map.find(key) == map.end());
+239   map[key] = value;
+240   return map[key];
+241 }
+242 //: The contract: any container that relies on get_or_insert should never call
+243 //: contains_key.
+244 
+245 //: 7. istreams are a royal pain in the arse. You have to be careful about
+246 //: what subclass you try to putback into. You have to watch out for the pesky
+247 //: failbit and badbit. Just avoid eof() and use this helper instead.
+248 :(code)
+249 bool has_data(istream& in) {
+250   return in && !in.eof();
+251 }
+252 
+253 :(before "End Includes")
+254 #include <assert.h>
+255 
+256 #include <iostream>
+257 using std::istream;
+258 using std::ostream;
+259 using std::iostream;
+260 using std::cin;
+261 using std::cout;
+262 using std::cerr;
+263 #include <iomanip>
+264 
+265 #include <string.h>
+266 #include <string>
+267 using std::string;
+268 
+269 #include <algorithm>
+270 using std::min;
+271 using std::max;
+
+ + + diff --git a/html/linux/bootstrap/002test.cc.html b/html/linux/bootstrap/002test.cc.html new file mode 100644 index 00000000..1c6d84f8 --- /dev/null +++ b/html/linux/bootstrap/002test.cc.html @@ -0,0 +1,187 @@ + + + + +Mu - linux/bootstrap/002test.cc + + + + + + + + + + +https://github.com/akkartik/mu/blob/main/linux/bootstrap/002test.cc +
+  1 //: A simple test harness. To create new tests, define functions starting with
+  2 //: 'test_'. To run all tests so defined, run:
+  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
+  7 //: layers. Every test should be a contract that always passes as originally
+  8 //: written, regardless of any later layers. Avoid writing 'temporary' tests
+  9 //: that are only meant to work until some layer.
+ 10 
+ 11 :(before "End Types")
+ 12 typedef void (*test_fn)(void);
+ 13 :(before "Globals")
+ 14 // move a global ahead into types that we can't generate an extern declaration for
+ 15 const test_fn Tests[] = {
+ 16   #include "test_list"  // auto-generated; see 'build*' scripts
+ 17 };
+ 18 
+ 19 :(before "End Globals")
+ 20 bool Run_tests = false;
+ 21 bool Passed = true;  // set this to false inside any test to indicate failure
+ 22 
+ 23 :(before "End Includes")
+ 24 #define CHECK(X) \
+ 25   if (Passed && !(X)) { \
+ 26     cerr << "\nF - " << __FUNCTION__ << "(" << __FILE__ << ":" << __LINE__ << "): " << #X << '\n'; \
+ 27     Passed = false; \
+ 28     return;  /* Currently we stop at the very first failure. */ \
+ 29   }
+ 30 
+ 31 #define CHECK_EQ(X, Y) \
+ 32   if (Passed && (X) != (Y)) { \
+ 33     cerr << "\nF - " << __FUNCTION__ << "(" << __FILE__ << ":" << __LINE__ << "): " << #X << " == " << #Y << '\n'; \
+ 34     cerr << "  got " << (X) << '\n';  /* BEWARE: multiple eval */ \
+ 35     Passed = false; \
+ 36     return;  /* Currently we stop at the very first failure. */ \
+ 37   }
+ 38 
+ 39 :(before "End Reset")
+ 40 Passed = true;
+ 41 
+ 42 :(before "End Commandline Parsing")
+ 43 if (argc > 1 && is_equal(argv[1], "test")) {
+ 44   Run_tests = true;  --argc;  ++argv;  // shift 'test' out of commandline args
+ 45 }
+ 46 
+ 47 :(before "End Main")
+ 48 if (Run_tests) {
+ 49   // Test Runs
+ 50   // we run some tests and then exit; assume no state need be maintained afterward
+ 51 
+ 52   long num_failures = 0;
+ 53   // End Test Run Initialization
+ 54   time_t t;  time(&t);
+ 55   cerr << "C tests: " << ctime(&t);
+ 56   for (size_t i=0;  i < sizeof(Tests)/sizeof(Tests[0]);  ++i) {
+ 57 //?     cerr << "running " << Test_names[i] << '\n';
+ 58     run_test(i);
+ 59     if (Passed) cerr << '.';
+ 60     else ++num_failures;
+ 61   }
+ 62   cerr << '\n';
+ 63   // End Tests
+ 64   if (num_failures > 0) {
+ 65     cerr << num_failures << " failure"
+ 66          << (num_failures > 1 ? "s" : "")
+ 67          << '\n';
+ 68     return 1;
+ 69   }
+ 70   return 0;
+ 71 }
+ 72 
+ 73 :(after "End Main")
+ 74 //: Raise other unrecognized sub-commands as errors.
+ 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;
+ 79 
+ 80 :(code)
+ 81 void run_test(size_t i) {
+ 82   if (i >= sizeof(Tests)/sizeof(Tests[0])) {
+ 83     cerr << "no test " << i << '\n';
+ 84     return;
+ 85   }
+ 86   reset();
+ 87   // End Test Setup
+ 88   (*Tests[i])();
+ 89   // End Test Teardown
+ 90 }
+ 91 
+ 92 //: Convenience: run a single test
+ 93 :(before "Globals")
+ 94 // Names for each element of the 'Tests' global, respectively.
+ 95 const string Test_names[] = {
+ 96   #include "test_name_list"  // auto-generated; see 'build*' scripts
+ 97 };
+ 98 :(after "Test Runs")
+ 99 string maybe_single_test_to_run = argv[argc-1];
+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 //: A pending test that also serves to put our test harness through its paces.
+109 
+110 :(code)
+111 void test_is_equal() {
+112   CHECK(is_equal("", ""));
+113   CHECK(!is_equal("", "foo"));
+114   CHECK(!is_equal("foo", ""));
+115   CHECK(!is_equal("f", "bar"));
+116   CHECK(!is_equal("bar", "f"));
+117   CHECK(!is_equal("bar", "ba"));
+118   CHECK(!is_equal("ba", "bar"));
+119   CHECK(is_equal("bar", "bar"));
+120 }
+121 
+122 :(before "End Includes")
+123 #include <stdlib.h>
+
+ + + diff --git a/html/linux/bootstrap/003trace.cc.html b/html/linux/bootstrap/003trace.cc.html new file mode 100644 index 00000000..e417260d --- /dev/null +++ b/html/linux/bootstrap/003trace.cc.html @@ -0,0 +1,586 @@ + + + + +Mu - linux/bootstrap/003trace.cc + + + + + + + + + + +https://github.com/akkartik/mu/blob/main/linux/bootstrap/003trace.cc +
+  1 //: The goal of layers is to make programs more easy to understand and more
+  2 //: malleable, easy to rewrite in radical ways without accidentally breaking
+  3 //: some corner case. Tests further both goals. They help understandability by
+  4 //: letting one make small changes and get feedback. What if I wrote this line
+  5 //: like so? What if I removed this function call, is it really necessary?
+  6 //: Just try it, see if the tests pass. Want to explore rewriting this bit in
+  7 //: this way? Tests put many refactorings on a firmer footing.
+  8 //:
+  9 //: But the usual way we write tests seems incomplete. Refactorings tend to
+ 10 //: work in the small, but don't help with changes to function boundaries. If
+ 11 //: you want to extract a new function you have to manually test-drive it to
+ 12 //: create tests for it. If you want to inline a function its tests are no
+ 13 //: longer valid. In both cases you end up having to reorganize code as well as
+ 14 //: tests, an error-prone activity.
+ 15 //:
+ 16 //: In response, this layer introduces the notion of domain-driven *white-box*
+ 17 //: testing. We focus on the domain of inputs the whole program needs to
+ 18 //: handle rather than the correctness of individual functions. All white-box
+ 19 //: tests invoke the program in a single way: by calling run() with some
+ 20 //: input. As the program operates on the input, it traces out a list of
+ 21 //: _facts_ deduced about the domain:
+ 22 //:   trace("label") << "fact 1: " << val;
+ 23 //:
+ 24 //: Tests can now check for these facts in the trace:
+ 25 //:   CHECK_TRACE_CONTENTS("label", "fact 1: 34\n"
+ 26 //:                                 "fact 2: 35\n");
+ 27 //:
+ 28 //: Since we never call anything but the run() function directly, we never have
+ 29 //: to rewrite the tests when we reorganize the internals of the program. We
+ 30 //: just have to make sure our rewrite deduces the same facts about the domain,
+ 31 //: and that's something we're going to have to do anyway.
+ 32 //:
+ 33 //: To avoid the combinatorial explosion of integration tests, each layer
+ 34 //: mainly logs facts to the trace with a common *label*. All tests in a layer
+ 35 //: tend to check facts with this label. Validating the facts logged with a
+ 36 //: specific label is like calling functions of that layer directly.
+ 37 //:
+ 38 //: To build robust tests, trace facts about your domain rather than details of
+ 39 //: how you computed them.
+ 40 //:
+ 41 //: More details: http://akkartik.name/blog/tracing-tests
+ 42 //:
+ 43 //: ---
+ 44 //:
+ 45 //: Between layers and domain-driven testing, programming starts to look like a
+ 46 //: fundamentally different activity. Instead of focusing on a) superficial,
+ 47 //: b) local rules on c) code [like say http://blog.bbv.ch/2013/06/05/clean-code-cheat-sheet],
+ 48 //: we allow programmers to engage with the a) deep, b) global structure of
+ 49 //: the c) domain. If you can systematically track discontinuities in the
+ 50 //: domain, you don't care if the code used gotos as long as it passed all
+ 51 //: tests. If tests become more robust to run, it becomes easier to try out
+ 52 //: radically different implementations for the same program. If code is
+ 53 //: super-easy to rewrite, it becomes less important what indentation style it
+ 54 //: uses, or that the objects are appropriately encapsulated, or that the
+ 55 //: functions are referentially transparent.
+ 56 //:
+ 57 //: Instead of plumbing, programming becomes building and gradually refining a
+ 58 //: map of the environment the program must operate under. Whether a program
+ 59 //: is 'correct' at a given point in time is a red herring; what matters is
+ 60 //: avoiding regression by monotonically nailing down the more 'eventful'
+ 61 //: parts of the terrain. It helps readers new and old, and rewards curiosity,
+ 62 //: to organize large programs in self-similar hierarchies of example tests
+ 63 //: colocated with the code that makes them work.
+ 64 //:
+ 65 //:   "Programming properly should be regarded as an activity by which
+ 66 //:   programmers form a mental model, rather than as production of a program."
+ 67 //:   -- Peter Naur (http://akkartik.name/naur.pdf)
+ 68 
+ 69 //:: == Core data structures
+ 70 
+ 71 :(before "End Globals")
+ 72 trace_stream* Trace_stream = NULL;
+ 73 
+ 74 :(before "End Types")
+ 75 struct trace_stream {
+ 76   vector<trace_line> past_lines;
+ 77   // End trace_stream Fields
+ 78 
+ 79   trace_stream() {
+ 80     // End trace_stream Constructor
+ 81   }
+ 82   ~trace_stream() {
+ 83     // End trace_stream Destructor
+ 84   }
+ 85   // End trace_stream Methods
+ 86 };
+ 87 
+ 88 //:: == Adding to the trace
+ 89 
+ 90 //: Top-level method is trace() which can be used like an ostream. Usage:
+ 91 //:   trace(depth, label) << ... << end();
+ 92 //: Don't forget the 'end()' to actually append to the trace.
+ 93 :(before "End Includes")
+ 94 // No brackets around the expansion so that it prints nothing if Trace_stream
+ 95 // isn't initialized.
+ 96 #define trace(...)  !Trace_stream ? cerr : Trace_stream->stream(__VA_ARGS__)
+ 97 
+ 98 :(before "End trace_stream Fields")
+ 99 // accumulator for current trace_line
+100 ostringstream* curr_stream;
+101 string curr_label;
+102 int curr_depth;
+103 // other stuff
+104 int collect_depth;  // avoid tracing lower levels for speed
+105 ofstream null_stream;  // never opened, so writes to it silently fail
+106 
+107 //: Some constants.
+108 :(before "struct trace_stream")  // include constants in all cleaved compilation units
+109 const int Max_depth = 9999;
+110 :(before "End trace_stream Constructor")
+111 curr_stream = NULL;
+112 curr_depth = Max_depth;
+113 collect_depth = Max_depth;
+114 
+115 :(before "struct trace_stream")
+116 struct trace_line {
+117   string contents;
+118   string label;
+119   int depth;  // 0 is 'sea level'; positive integers are progressively 'deeper' and lower level
+120   trace_line(string c, string l) {
+121     contents = c;
+122     label = l;
+123     depth = 0;
+124   }
+125   trace_line(string c, string l, int d) {
+126     contents = c;
+127     label = l;
+128     depth = d;
+129   }
+130 };
+131 
+132 string unescape_newline(string& s) {
+133   std::stringstream ss;
+134   for (int i = 0;  i < SIZE(s);  ++i) {
+135     if (s.at(i) == '\n')
+136       ss << "\\n";
+137     else
+138       ss << s.at(i);
+139   }
+140   return ss.str();
+141 }
+142 
+143 void dump_trace_line(ostream& s, trace_line& t) {
+144   s << std::setw(2) << t.depth << ' ' << t.label << ": " << unescape_newline(t.contents) << '\n';
+145 }
+146 
+147 //: Starting a new trace line.
+148 :(before "End trace_stream Methods")
+149 ostream& stream(string label) {
+150   return stream(Max_depth, label);
+151 }
+152 
+153 ostream& stream(int depth, string label) {
+154   if (depth > collect_depth) return null_stream;
+155   curr_stream = new ostringstream;
+156   curr_label = label;
+157   curr_depth = depth;
+158   (*curr_stream) << std::hex;  // printing addresses is the common case
+159   return *curr_stream;
+160 }
+161 
+162 //: End of a trace line; append it to the trace.
+163 :(before "End Types")
+164 struct end {};
+165 :(code)
+166 ostream& operator<<(ostream& os, end /*unused*/) {
+167   if (Trace_stream) Trace_stream->newline();
+168   return os;
+169 }
+170 
+171 //: Fatal error.
+172 :(before "End Types")
+173 struct die {};
+174 :(code)
+175 ostream& operator<<(ostream& /*unused*/, die /*unused*/) {
+176   if (Trace_stream) Trace_stream->newline();
+177   exit(1);
+178 }
+179 
+180 :(before "End trace_stream Methods")
+181 void newline();
+182 :(code)
+183 void trace_stream::newline() {
+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
+188     // maybe print this line to stderr
+189     trace_line& t = past_lines.back();
+190     if (should_incrementally_print_trace()) {
+191       dump_trace_line(cerr, t);
+192     }
+193     // End trace Commit
+194   }
+195 
+196   // clean up
+197   delete curr_stream;
+198   curr_stream = NULL;
+199   curr_label.clear();
+200   curr_depth = Max_depth;
+201 }
+202 
+203 //:: == Initializing the trace in tests
+204 
+205 :(before "End Includes")
+206 #define START_TRACING_UNTIL_END_OF_SCOPE  lease_tracer leased_tracer;
+207 :(before "End Test Setup")
+208 START_TRACING_UNTIL_END_OF_SCOPE
+209 
+210 //: Trace_stream is a resource, lease_tracer uses RAII to manage it.
+211 :(before "End Types")
+212 struct lease_tracer {
+213   lease_tracer();
+214   ~lease_tracer();
+215 };
+216 :(code)
+217 lease_tracer::lease_tracer() { Trace_stream = new trace_stream; }
+218 lease_tracer::~lease_tracer() {
+219   delete Trace_stream;
+220   Trace_stream = NULL;
+221 }
+222 
+223 //:: == Errors and warnings using traces
+224 
+225 :(before "End Includes")
+226 #define raise  (!Trace_stream ? (++Trace_errors,cerr) /*do print*/ : Trace_stream->stream(Error_depth, "error"))
+227 #define warn (!Trace_stream ? (++Trace_errors,cerr) /*do print*/ : Trace_stream->stream(Warn_depth, "warn"))
+228 
+229 //: Print errors and warnings to the screen by default.
+230 :(before "struct trace_stream")  // include constants in all cleaved compilation units
+231 const int Error_depth = 0;
+232 const int Warn_depth = 1;
+233 :(before "End Globals")
+234 int Hide_errors = false;  // if set, don't print errors or warnings to screen
+235 int Hide_warnings = false;  // if set, don't print warnings to screen
+236 :(before "End Reset")
+237 Hide_errors = false;
+238 Hide_warnings = false;
+239 //: Never dump warnings in tests
+240 :(before "End Test Setup")
+241 Hide_warnings = true;
+242 :(code)
+243 bool trace_stream::should_incrementally_print_trace() {
+244   if (!Hide_errors && curr_depth == Error_depth) return true;
+245   if (!Hide_warnings && !Hide_errors && curr_depth == Warn_depth) return true;
+246   // End Incremental Trace Print Conditions
+247   return false;
+248 }
+249 :(before "End trace_stream Methods")
+250 bool should_incrementally_print_trace();
+251 
+252 :(before "End Globals")
+253 int Trace_errors = 0;  // used only when Trace_stream is NULL
+254 
+255 // Fail tests that displayed (unexpected) errors.
+256 // Expected errors should always be hidden and silently checked for.
+257 :(before "End Test Teardown")
+258 if (Passed && !Hide_errors && trace_contains_errors()) {
+259   Passed = false;
+260 }
+261 :(code)
+262 bool trace_contains_errors() {
+263   return Trace_errors > 0 || trace_count("error") > 0;
+264 }
+265 
+266 :(before "End Includes")
+267 // If we aren't yet sure how to deal with some corner case, use assert_for_now
+268 // to indicate that it isn't an inviolable invariant.
+269 #define assert_for_now assert
+270 #define raise_for_now raise
+271 
+272 //:: == Other assertions on traces
+273 //: Primitives:
+274 //:   - CHECK_TRACE_CONTENTS(lines)
+275 //:     Assert that the trace contains the given lines (separated by newlines)
+276 //:     in order. There can be other intervening lines between them.
+277 //:   - CHECK_TRACE_DOESNT_CONTAIN(line)
+278 //:   - CHECK_TRACE_DOESNT_CONTAIN(label, contents)
+279 //:     Assert that the trace doesn't contain the given (single) line.
+280 //:   - CHECK_TRACE_COUNT(label, count)
+281 //:     Assert that the trace contains exactly 'count' lines with the given
+282 //:     'label'.
+283 //:   - CHECK_TRACE_CONTAINS_ERRORS()
+284 //:   - CHECK_TRACE_DOESNT_CONTAIN_ERRORS()
+285 //:   - trace_count_prefix(label, prefix)
+286 //:     Count the number of trace lines with the given 'label' that start with
+287 //:     the given 'prefix'.
+288 
+289 :(before "End Includes")
+290 #define CHECK_TRACE_CONTENTS(...)  check_trace_contents(__FUNCTION__, __FILE__, __LINE__, __VA_ARGS__)
+291 
+292 #define CHECK_TRACE_DOESNT_CONTAIN(...)  CHECK(trace_doesnt_contain(__VA_ARGS__))
+293 
+294 #define CHECK_TRACE_COUNT(label, count) \
+295   if (Passed && trace_count(label) != (count)) { \
+296     cerr << "\nF - " << __FUNCTION__ << "(" << __FILE__ << ":" << __LINE__ << "): trace_count of " << label << " should be " << count << '\n'; \
+297     cerr << "  got " << trace_count(label) << '\n';  /* multiple eval */ \
+298     DUMP(label); \
+299     Passed = false; \
+300     return;  /* Currently we stop at the very first failure. */ \
+301   }
+302 
+303 #define CHECK_TRACE_CONTAINS_ERRORS()  CHECK(trace_contains_errors())
+304 #define CHECK_TRACE_DOESNT_CONTAIN_ERRORS() \
+305   if (Passed && trace_contains_errors()) { \
+306     cerr << "\nF - " << __FUNCTION__ << "(" << __FILE__ << ":" << __LINE__ << "): unexpected errors\n"; \
+307     DUMP("error"); \
+308     Passed = false; \
+309     return; \
+310   }
+311 
+312 // Allow tests to ignore trace lines generated during setup.
+313 #define CLEAR_TRACE  delete Trace_stream, Trace_stream = new trace_stream
+314 
+315 :(code)
+316 bool check_trace_contents(string FUNCTION, string FILE, int LINE, string expected) {
+317   if (!Passed) return false;
+318   if (!Trace_stream) return false;
+319   vector<string> expected_lines = split(expected, "\n");
+320   int curr_expected_line = 0;
+321   while (curr_expected_line < SIZE(expected_lines) && expected_lines.at(curr_expected_line).empty())
+322     ++curr_expected_line;
+323   if (curr_expected_line == SIZE(expected_lines)) return true;
+324   string label, contents;
+325   split_label_contents(expected_lines.at(curr_expected_line), &label, &contents);
+326   for (vector<trace_line>::iterator p = Trace_stream->past_lines.begin();  p != Trace_stream->past_lines.end();  ++p) {
+327     if (label != p->label) continue;
+328     string t = trim(p->contents);
+329     if (contents != unescape_newline(t)) continue;
+330     ++curr_expected_line;
+331     while (curr_expected_line < SIZE(expected_lines) && expected_lines.at(curr_expected_line).empty())
+332       ++curr_expected_line;
+333     if (curr_expected_line == SIZE(expected_lines)) return true;
+334     split_label_contents(expected_lines.at(curr_expected_line), &label, &contents);
+335   }
+336 
+337   if (line_exists_anywhere(label, contents)) {
+338     cerr << "\nF - " << FUNCTION << "(" << FILE << ":" << LINE << "): line [" << label << ": " << contents << "] out of order in trace:\n";
+339     DUMP("");
+340   }
+341   else {
+342     cerr << "\nF - " << FUNCTION << "(" << FILE << ":" << LINE << "): missing [" << contents << "] in trace:\n";
+343     DUMP(label);
+344   }
+345   Passed = false;
+346   return false;
+347 }
+348 
+349 bool trace_doesnt_contain(string expected) {
+350   vector<string> tmp = split_first(expected, ": ");
+351   if (SIZE(tmp) == 1) {
+352     raise << expected << ": missing label or contents in trace line\n" << end();
+353     assert(false);
+354   }
+355   return trace_count(tmp.at(0), tmp.at(1)) == 0;
+356 }
+357 
+358 int trace_count(string label) {
+359   return trace_count(label, "");
+360 }
+361 
+362 int trace_count(string label, string line) {
+363   if (!Trace_stream) return 0;
+364   long result = 0;
+365   for (vector<trace_line>::iterator p = Trace_stream->past_lines.begin();  p != Trace_stream->past_lines.end();  ++p) {
+366     if (label == p->label) {
+367       if (line == "" || trim(line) == trim(p->contents))
+368         ++result;
+369     }
+370   }
+371   return result;
+372 }
+373 
+374 int trace_count_prefix(string label, string prefix) {
+375   if (!Trace_stream) return 0;
+376   long result = 0;
+377   for (vector<trace_line>::iterator p = Trace_stream->past_lines.begin();  p != Trace_stream->past_lines.end();  ++p) {
+378     if (label == p->label) {
+379       if (starts_with(trim(p->contents), trim(prefix)))
+380         ++result;
+381     }
+382   }
+383   return result;
+384 }
+385 
+386 void split_label_contents(const string& s, string* label, string* contents) {
+387   static const string delim(": ");
+388   size_t pos = s.find(delim);
+389   if (pos == string::npos) {
+390     *label = "";
+391     *contents = trim(s);
+392   }
+393   else {
+394     *label = trim(s.substr(0, pos));
+395     *contents = trim(s.substr(pos+SIZE(delim)));
+396   }
+397 }
+398 
+399 bool line_exists_anywhere(const string& label, const string& contents) {
+400   for (vector<trace_line>::iterator p = Trace_stream->past_lines.begin();  p != Trace_stream->past_lines.end();  ++p) {
+401     if (label != p->label) continue;
+402     if (contents == trim(p->contents)) return true;
+403   }
+404   return false;
+405 }
+406 
+407 vector<string> split(string s, string delim) {
+408   vector<string> result;
+409   size_t begin=0, end=s.find(delim);
+410   while (true) {
+411     if (end == string::npos) {
+412       result.push_back(string(s, begin, string::npos));
+413       break;
+414     }
+415     result.push_back(string(s, begin, end-begin));
+416     begin = end+SIZE(delim);
+417     end = s.find(delim, begin);
+418   }
+419   return result;
+420 }
+421 
+422 vector<string> split_first(string s, string delim) {
+423   vector<string> result;
+424   size_t end=s.find(delim);
+425   result.push_back(string(s, 0, end));
+426   if (end != string::npos)
+427     result.push_back(string(s, end+SIZE(delim), string::npos));
+428   return result;
+429 }
+430 
+431 //:: == Helpers for debugging using traces
+432 
+433 :(before "End Includes")
+434 // To debug why a test is failing, dump its trace using '?'.
+435 #define DUMP(label)  if (Trace_stream) cerr << Trace_stream->readable_contents(label);
+436 
+437 // To add temporary prints to the trace, use 'dbg'.
+438 // `git log` should never show any calls to 'dbg'.
+439 #define dbg trace(0, "a")
+440 
+441 //: Dump the entire trace to file where it can be browsed offline.
+442 //: Dump the trace as it happens; that way you get something even if the
+443 //: program crashes.
+444 
+445 :(before "End Globals")
+446 ofstream Trace_file;
+447 :(before "End Commandline Options(*arg)")
+448 else if (is_equal(*arg, "--trace")) {
+449   cerr << "saving trace to 'last_run'\n";
+450   Trace_file.open("last_run");
+451   // Add a dummy line up top; otherwise the `browse_trace` tool currently has
+452   // no way to expand any lines above an error.
+453   Trace_file << "   0 dummy: start\n";
+454   // End --trace Settings
+455 }
+456 :(before "End trace Commit")
+457 if (Trace_file.is_open()) {
+458   dump_trace_line(Trace_file, t);
+459   Trace_file.flush();
+460   past_lines.pop_back();  // economize on memory
+461 }
+462 :(before "End One-time Setup")
+463 atexit(cleanup_main);
+464 :(code)
+465 void cleanup_main() {
+466   if (Trace_file.is_open()) 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/linux/bootstrap/003trace.test.cc.html b/html/linux/bootstrap/003trace.test.cc.html new file mode 100644 index 00000000..412f55e3 --- /dev/null +++ b/html/linux/bootstrap/003trace.test.cc.html @@ -0,0 +1,194 @@ + + + + +Mu - linux/bootstrap/003trace.test.cc + + + + + + + + + + +https://github.com/akkartik/mu/blob/main/linux/bootstrap/003trace.test.cc +
+  1 void test_trace_check_compares() {
+  2   trace("test layer") << "foo" << end();
+  3   CHECK_TRACE_CONTENTS("test layer: foo");
+  4 }
+  5 
+  6 void test_trace_check_ignores_other_layers() {
+  7   trace("test layer 1") << "foo" << end();
+  8   trace("test layer 2") << "bar" << end();
+  9   CHECK_TRACE_CONTENTS("test layer 1: foo");
+ 10   CHECK_TRACE_DOESNT_CONTAIN("test layer 2: foo");
+ 11 }
+ 12 
+ 13 void test_trace_check_ignores_leading_whitespace() {
+ 14   trace("test layer 1") << " foo" << end();
+ 15   CHECK_EQ(trace_count("test layer 1", /*too little whitespace*/"foo"), 1);
+ 16   CHECK_EQ(trace_count("test layer 1", /*too much whitespace*/"  foo"), 1);
+ 17 }
+ 18 
+ 19 void test_trace_check_ignores_other_lines() {
+ 20   trace("test layer 1") << "foo" << end();
+ 21   trace("test layer 1") << "bar" << end();
+ 22   CHECK_TRACE_CONTENTS("test layer 1: foo");
+ 23 }
+ 24 
+ 25 void test_trace_check_ignores_other_lines2() {
+ 26   trace("test layer 1") << "foo" << end();
+ 27   trace("test layer 1") << "bar" << end();
+ 28   CHECK_TRACE_CONTENTS("test layer 1: bar");
+ 29 }
+ 30 
+ 31 void test_trace_ignores_trailing_whitespace() {
+ 32   trace("test layer 1") << "foo\n" << end();
+ 33   CHECK_TRACE_CONTENTS("test layer 1: foo");
+ 34 }
+ 35 
+ 36 void test_trace_ignores_trailing_whitespace2() {
+ 37   trace("test layer 1") << "foo " << end();
+ 38   CHECK_TRACE_CONTENTS("test layer 1: foo");
+ 39 }
+ 40 
+ 41 void test_trace_orders_across_layers() {
+ 42   trace("test layer 1") << "foo" << end();
+ 43   trace("test layer 2") << "bar" << end();
+ 44   trace("test layer 1") << "qux" << end();
+ 45   CHECK_TRACE_CONTENTS("test layer 1: foo\n"
+ 46                        "test layer 2: bar\n"
+ 47                        "test layer 1: qux\n");
+ 48 }
+ 49 
+ 50 void test_trace_supports_count() {
+ 51   trace("test layer 1") << "foo" << end();
+ 52   trace("test layer 1") << "foo" << end();
+ 53   CHECK_EQ(trace_count("test layer 1", "foo"), 2);
+ 54 }
+ 55 
+ 56 void test_trace_supports_count2() {
+ 57   trace("test layer 1") << "foo" << end();
+ 58   trace("test layer 1") << "bar" << end();
+ 59   CHECK_EQ(trace_count("test layer 1"), 2);
+ 60 }
+ 61 
+ 62 void test_trace_count_ignores_trailing_whitespace() {
+ 63   trace("test layer 1") << "foo\n" << end();
+ 64   CHECK_EQ(trace_count("test layer 1", "foo"), 1);
+ 65 }
+ 66 
+ 67 void test_trace_unescapes_newlines() {
+ 68   trace("test layer 1") << "f\no\no\n" << end();
+ 69   CHECK_TRACE_CONTENTS("test layer 1: f\\no\\no");
+ 70 }
+ 71 
+ 72 // pending: DUMP tests
+ 73 // pending: readable_contents() adds newline if necessary.
+ 74 // pending: raise also prints to stderr.
+ 75 // pending: raise doesn't print to stderr if Hide_errors is set.
+ 76 // pending: warn doesn't print to stderr if Hide_errors is set.
+ 77 // pending: warn doesn't print to stderr if Hide_warnings is set.
+ 78 // pending: raise doesn't have to be saved if Hide_errors is set, just printed.
+ 79 // pending: raise prints to stderr if Trace_stream is NULL.
+ 80 // pending: raise prints to stderr if Trace_stream is NULL even if Hide_errors is set.
+ 81 
+ 82 // can't check trace because trace methods call 'split'
+ 83 
+ 84 void test_split_returns_at_least_one_elem() {
+ 85   vector<string> result = split("", ",");
+ 86   CHECK_EQ(result.size(), 1);
+ 87   CHECK_EQ(result.at(0), "");
+ 88 }
+ 89 
+ 90 void test_split_returns_entire_input_when_no_delim() {
+ 91   vector<string> result = split("abc", ",");
+ 92   CHECK_EQ(result.size(), 1);
+ 93   CHECK_EQ(result.at(0), "abc");
+ 94 }
+ 95 
+ 96 void test_split_works() {
+ 97   vector<string> result = split("abc,def", ",");
+ 98   CHECK_EQ(result.size(), 2);
+ 99   CHECK_EQ(result.at(0), "abc");
+100   CHECK_EQ(result.at(1), "def");
+101 }
+102 
+103 void test_split_works2() {
+104   vector<string> result = split("abc,def,ghi", ",");
+105   CHECK_EQ(result.size(), 3);
+106   CHECK_EQ(result.at(0), "abc");
+107   CHECK_EQ(result.at(1), "def");
+108   CHECK_EQ(result.at(2), "ghi");
+109 }
+110 
+111 void test_split_handles_multichar_delim() {
+112   vector<string> result = split("abc,,def,,ghi", ",,");
+113   CHECK_EQ(result.size(), 3);
+114   CHECK_EQ(result.at(0), "abc");
+115   CHECK_EQ(result.at(1), "def");
+116   CHECK_EQ(result.at(2), "ghi");
+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");
+133 }
+
+ + + diff --git a/html/linux/bootstrap/010vm.cc.html b/html/linux/bootstrap/010vm.cc.html new file mode 100644 index 00000000..f60b6abf --- /dev/null +++ b/html/linux/bootstrap/010vm.cc.html @@ -0,0 +1,474 @@ + + + + +Mu - linux/bootstrap/010vm.cc + + + + + + + + + + +https://github.com/akkartik/mu/blob/main/linux/bootstrap/010vm.cc +
+  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 MMX, etc.
+  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 Types")
+ 32 const int NUM_XMM_REGISTERS = 8;
+ 33 float Xmm[NUM_XMM_REGISTERS] = { 0.0 };
+ 34 const string Xname[NUM_XMM_REGISTERS] = { "XMM0", "XMM1", "XMM2", "XMM3", "XMM4", "XMM5", "XMM6", "XMM7" };
+ 35 :(before "End Reset")
+ 36 bzero(Xmm, sizeof(Xmm));
+ 37 
+ 38 :(before "End Help Contents")
+ 39 cerr << "  registers\n";
+ 40 :(before "End Help Texts")
+ 41 put_new(Help, "registers",
+ 42   "SubX supports 16 registers: eight 32-bit integer registers and eight single-precision\n"
+ 43   "floating-point registers. From 0 to 7, they are:\n"
+ 44   "  integer: EAX ECX EDX EBX ESP EBP ESI EDI\n"
+ 45   "  floating point: XMM0 XMM1 XMM2 XMM3 XMM4 XMM5 XMM6 XMM7\n"
+ 46   "ESP contains the top of the stack.\n"
+ 47   "\n"
+ 48   "-- 8-bit registers\n"
+ 49   "Some instructions operate on eight *overlapping* 8-bit registers.\n"
+ 50   "From 0 to 7, they are:\n"
+ 51   "  AL CL DL BL AH CH DH BH\n"
+ 52   "The 8-bit registers overlap with the 32-bit ones. AL is the lowest signicant byte\n"
+ 53   "of EAX, AH is the second lowest significant byte, and so on.\n"
+ 54   "\n"
+ 55   "For example, if EBX contains 0x11223344, then BL contains 0x44, and BH contains 0x33.\n"
+ 56   "\n"
+ 57   "There is no way to access bytes within ESP, EBP, ESI or EDI.\n"
+ 58   "\n"
+ 59   "For complete details consult the IA-32 software developer's manual, volume 2,\n"
+ 60   "table 2-2, \"32-bit addressing forms with the ModR/M byte\".\n"
+ 61   "It is included in this repository as 'modrm.pdf'.\n"
+ 62   "The register encodings are described in the top row of the table, but you'll need\n"
+ 63   "to spend some time with it.\n"
+ 64   "\n"
+ 65   "-- flag registers\n"
+ 66   "Various instructions (particularly 'compare') modify one or more of four 1-bit\n"
+ 67   "'flag' registers, as a side-effect:\n"
+ 68   "- the sign flag (SF): usually set if an arithmetic result is negative, or\n"
+ 69   "  reset if not.\n"
+ 70   "- the zero flag (ZF): usually set if a result is zero, or reset if not.\n"
+ 71   "- the carry flag (CF): usually set if an arithmetic result overflows by just one bit.\n"
+ 72   "  Useful for operating on unsigned numbers.\n"
+ 73   "- the overflow flag (OF): usually set if an arithmetic result overflows by more\n"
+ 74   "  than one bit. Useful for operating on signed numbers.\n"
+ 75   "The flag bits are read by conditional jumps.\n"
+ 76   "\n"
+ 77   "For complete details on how different instructions update the flags, consult the IA-32\n"
+ 78   "manual (volume 2). There's various versions of it online, such as https://c9x.me/x86,\n"
+ 79   "though of course you'll need to be careful to ignore instructions and flag registers\n"
+ 80   "that SubX doesn't support.\n"
+ 81   "\n"
+ 82   "It isn't simple, but if this is the processor you have running on your computer,\n"
+ 83   "might as well get good at it.\n"
+ 84 );
+ 85 
+ 86 :(before "End Globals")
+ 87 // the subset of x86 flag registers we care about
+ 88 bool SF = false;  // sign flag
+ 89 bool ZF = false;  // zero flag
+ 90 bool CF = false;  // carry flag
+ 91 bool OF = false;  // overflow flag
+ 92 :(before "End Reset")
+ 93 SF = ZF = CF = OF = false;
+ 94 
+ 95 //:: simulated RAM
+ 96 
+ 97 :(before "End Types")
+ 98 const uint32_t SEGMENT_ALIGNMENT = 0x1000000;  // 16MB
+ 99 inline uint32_t align_upwards(uint32_t x, uint32_t align) {
+100   return (x+align-1) & -(align);
+101 }
+102 
+103 // Like in real-world Linux, we'll allocate RAM for our programs in disjoint
+104 // slabs called VMAs or Virtual Memory Areas.
+105 struct vma {
+106   uint32_t start;  // inclusive
+107   uint32_t end;  // exclusive
+108   vector<uint8_t> _data;
+109   vma(uint32_t s, uint32_t e) :start(s), end(e) {}
+110   vma(uint32_t s) :start(s), end(align_upwards(s+1, SEGMENT_ALIGNMENT)) {}
+111   bool match(uint32_t a) {
+112     return a >= start && a < end;
+113   }
+114   bool match32(uint32_t a) {
+115     return a >= start && a+4 <= end;
+116   }
+117   uint8_t& data(uint32_t a) {
+118     assert(match(a));
+119     uint32_t result_index = a-start;
+120     if (_data.size() <= result_index+/*largest word size that can be accessed in one instruction*/sizeof(int)) {
+121       const int align = 0x1000;
+122       uint32_t result_size = result_index + 1;  // size needed for result_index to be valid
+123       uint32_t new_size = align_upwards(result_size, align);
+124       // grow at least 2x to maintain some amortized complexity guarantees
+125       if (new_size < _data.size() * 2)
+126         new_size = _data.size() * 2;
+127       // never grow past the stated limit
+128       if (new_size > end-start)
+129         new_size = end-start;
+130       _data.resize(new_size);
+131     }
+132     return _data.at(result_index);
+133   }
+134   void grow_until(uint32_t new_end_address) {
+135     if (new_end_address < end) return;
+136     // Ugly: vma knows about the global Memory list of vmas
+137     void sanity_check(uint32_t start, uint32_t end);
+138     sanity_check(start, new_end_address);
+139     end = new_end_address;
+140   }
+141   // End vma Methods
+142 };
+143 :(code)
+144 void sanity_check(uint32_t start, uint32_t end) {
+145   bool dup_found = false;
+146   for (int i = 0;  i < SIZE(Mem);  ++i) {
+147     const vma& curr = Mem.at(i);
+148     if (curr.start == start) {
+149       assert(!dup_found);
+150       dup_found = true;
+151     }
+152     else if (curr.start > start) {
+153       assert(curr.start > end);
+154     }
+155     else if (curr.start < start) {
+156       assert(curr.end < start);
+157     }
+158   }
+159 }
+160 
+161 :(before "End Globals")
+162 // RAM is made of VMAs.
+163 vector<vma> Mem;
+164 :(code)
+165 :(before "End Globals")
+166 uint32_t End_of_program = 0;  // when the program executes past this address in tests we'll stop the test
+167 // The stack grows downward. Can't increase its size for now.
+168 :(before "End Reset")
+169 Mem.clear();
+170 End_of_program = 0;
+171 :(code)
+172 // These helpers depend on Mem being laid out contiguously (so you can't use a
+173 // map, etc.) and on the host also being little-endian.
+174 inline uint8_t read_mem_u8(uint32_t addr) {
+175   uint8_t* handle = mem_addr_u8(addr);  // error messages get printed here
+176   return handle ? *handle : 0;
+177 }
+178 inline int8_t read_mem_i8(uint32_t addr) {
+179   return static_cast<int8_t>(read_mem_u8(addr));
+180 }
+181 inline uint32_t read_mem_u32(uint32_t addr) {
+182   uint32_t* handle = mem_addr_u32(addr);  // error messages get printed here
+183   return handle ? *handle : 0;
+184 }
+185 inline int32_t read_mem_i32(uint32_t addr) {
+186   return static_cast<int32_t>(read_mem_u32(addr));
+187 }
+188 inline float read_mem_f32(uint32_t addr) {
+189   return static_cast<float>(read_mem_u32(addr));
+190 }
+191 
+192 inline uint8_t* mem_addr_u8(uint32_t addr) {
+193   uint8_t* result = NULL;
+194   for (int i = 0;  i < SIZE(Mem);  ++i) {
+195     if (Mem.at(i).match(addr)) {
+196       if (result)
+197         raise << "address 0x" << HEXWORD << addr << " is in two segments\n" << end();
+198       result = &Mem.at(i).data(addr);
+199     }
+200   }
+201   if (result == NULL) {
+202     if (Trace_file.is_open()) Trace_file.flush();
+203     raise << "Tried to access uninitialized memory at address 0x" << HEXWORD << addr << '\n' << end();
+204     exit(1);
+205   }
+206   return result;
+207 }
+208 inline int8_t* mem_addr_i8(uint32_t addr) {
+209   return reinterpret_cast<int8_t*>(mem_addr_u8(addr));
+210 }
+211 inline uint32_t* mem_addr_u32(uint32_t addr) {
+212   uint32_t* result = NULL;
+213   for (int i = 0;  i < SIZE(Mem);  ++i) {
+214     if (Mem.at(i).match32(addr)) {
+215       if (result)
+216         raise << "address 0x" << HEXWORD << addr << " is in two segments\n" << end();
+217       result = reinterpret_cast<uint32_t*>(&Mem.at(i).data(addr));
+218     }
+219   }
+220   if (result == NULL) {
+221     if (Trace_file.is_open()) Trace_file.flush();
+222     raise << "Tried to access uninitialized memory at address 0x" << HEXWORD << addr << '\n' << end();
+223     exit(1);
+224   }
+225   return result;
+226 }
+227 inline int32_t* mem_addr_i32(uint32_t addr) {
+228   return reinterpret_cast<int32_t*>(mem_addr_u32(addr));
+229 }
+230 inline float* mem_addr_f32(uint32_t addr) {
+231   return reinterpret_cast<float*>(mem_addr_u32(addr));
+232 }
+233 // helper for some syscalls. But read-only.
+234 inline const char* mem_addr_kernel_string(uint32_t addr) {
+235   return reinterpret_cast<const char*>(mem_addr_u8(addr));
+236 }
+237 inline string mem_addr_string(uint32_t addr, uint32_t size) {
+238   ostringstream out;
+239   for (size_t i = 0;  i < size;  ++i)
+240     out << read_mem_u8(addr+i);
+241   return out.str();
+242 }
+243 
+244 inline void write_mem_u8(uint32_t addr, uint8_t val) {
+245   uint8_t* handle = mem_addr_u8(addr);
+246   if (handle != NULL) *handle = val;
+247 }
+248 inline void write_mem_i8(uint32_t addr, int8_t val) {
+249   int8_t* handle = mem_addr_i8(addr);
+250   if (handle != NULL) *handle = val;
+251 }
+252 inline void write_mem_u32(uint32_t addr, uint32_t val) {
+253   uint32_t* handle = mem_addr_u32(addr);
+254   if (handle != NULL) *handle = val;
+255 }
+256 inline void write_mem_i32(uint32_t addr, int32_t val) {
+257   int32_t* handle = mem_addr_i32(addr);
+258   if (handle != NULL) *handle = val;
+259 }
+260 
+261 inline bool already_allocated(uint32_t addr) {
+262   bool result = false;
+263   for (int i = 0;  i < SIZE(Mem);  ++i) {
+264     if (Mem.at(i).match(addr)) {
+265       if (result)
+266         raise << "address 0x" << HEXWORD << addr << " is in two segments\n" << end();
+267       result = true;
+268     }
+269   }
+270   return result;
+271 }
+272 
+273 //:: core interpreter loop
+274 
+275 :(code)
+276 // skeleton of how x86 instructions are decoded
+277 void run_one_instruction() {
+278   uint8_t op=0, op2=0, op3=0;
+279   // Run One Instruction
+280   if (Trace_file.is_open()) {
+281     dump_registers();
+282     // End Dump Info for Instruction
+283   }
+284   uint32_t inst_start_address = EIP;
+285   op = next();
+286   trace(Callstack_depth+1, "run") << "0x" << HEXWORD << inst_start_address << " opcode: " << HEXBYTE << NUM(op) << end();
+287   switch (op) {
+288   case 0xf4:  // hlt
+289     EIP = End_of_program;
+290     break;
+291   // End Single-Byte Opcodes
+292   case 0x0f:
+293     switch(op2 = next()) {
+294     // End Two-Byte Opcodes Starting With 0f
+295     default:
+296       cerr << "unrecognized second opcode after 0f: " << HEXBYTE << NUM(op2) << '\n';
+297       exit(1);
+298     }
+299     break;
+300   case 0xf2:
+301     switch(op2 = next()) {
+302     // End Two-Byte Opcodes Starting With f2
+303     case 0x0f:
+304       switch(op3 = next()) {
+305       // End Three-Byte Opcodes Starting With f2 0f
+306       default:
+307         cerr << "unrecognized third opcode after f2 0f: " << HEXBYTE << NUM(op3) << '\n';
+308         exit(1);
+309       }
+310       break;
+311     default:
+312       cerr << "unrecognized second opcode after f2: " << HEXBYTE << NUM(op2) << '\n';
+313       exit(1);
+314     }
+315     break;
+316   case 0xf3:
+317     switch(op2 = next()) {
+318     // End Two-Byte Opcodes Starting With f3
+319     case 0x0f:
+320       switch(op3 = next()) {
+321       // End Three-Byte Opcodes Starting With f3 0f
+322       default:
+323         cerr << "unrecognized third opcode after f3 0f: " << HEXBYTE << NUM(op3) << '\n';
+324         exit(1);
+325       }
+326       break;
+327     default:
+328       cerr << "unrecognized second opcode after f3: " << HEXBYTE << NUM(op2) << '\n';
+329       exit(1);
+330     }
+331     break;
+332   default:
+333     cerr << "unrecognized opcode: " << HEXBYTE << NUM(op) << '\n';
+334     exit(1);
+335   }
+336 }
+337 
+338 inline uint8_t next() {
+339   return read_mem_u8(EIP++);
+340 }
+341 
+342 void dump_registers() {
+343   ostringstream out;
+344   out << "regs: ";
+345   for (int i = 0;  i < NUM_INT_REGISTERS;  ++i) {
+346     if (i > 0) out << "  ";
+347     out << i << ": " << std::hex << std::setw(8) << std::setfill('_') << Reg[i].u;
+348   }
+349   out << " -- SF: " << SF << "; ZF: " << ZF << "; CF: " << CF << "; OF: " << OF;
+350   trace(Callstack_depth+1, "run") << out.str() << end();
+351 }
+352 
+353 //: start tracking supported opcodes
+354 :(before "End Globals")
+355 map</*op*/string, string> Name;
+356 map</*op*/string, string> Name_0f;
+357 map</*op*/string, string> Name_f3;
+358 map</*op*/string, string> Name_f3_0f;
+359 :(before "End One-time Setup")
+360 init_op_names();
+361 :(code)
+362 void init_op_names() {
+363   put(Name, "f4", "halt (hlt)");
+364   // End Initialize Op Names
+365 }
+366 
+367 :(before "End Help Special-cases(key)")
+368 if (key == "opcodes") {
+369   cerr << "Opcodes currently supported by SubX:\n";
+370   for (map<string, string>::iterator p = Name.begin();  p != Name.end();  ++p)
+371     cerr << "  " << p->first << ": " << p->second << '\n';
+372   for (map<string, string>::iterator p = Name_0f.begin();  p != Name_0f.end();  ++p)
+373     cerr << "  0f " << p->first << ": " << p->second << '\n';
+374   for (map<string, string>::iterator p = Name_f3.begin();  p != Name_f3.end();  ++p)
+375     cerr << "  f3 " << p->first << ": " << p->second << '\n';
+376   for (map<string, string>::iterator p = Name_f3_0f.begin();  p != Name_f3_0f.end();  ++p)
+377     cerr << "  f3 0f " << p->first << ": " << p->second << '\n';
+378   cerr << "Run `bootstrap help instructions` for details on words like 'r32' and 'disp8'.\n"
+379           "For complete details on these instructions, consult the IA-32 manual (volume 2).\n"
+380           "There's various versions of it online, such as https://c9x.me/x86.\n"
+381           "The mnemonics in brackets will help you locate each instruction.\n";
+382   return 0;
+383 }
+384 :(before "End Help Contents")
+385 cerr << "  opcodes\n";
+386 
+387 //: Helpers for managing trace depths
+388 //:
+389 //: We're going to use trace depths primarily to segment code running at
+390 //: different frames of the call stack. This will make it easy for the trace
+391 //: browser to collapse over entire calls.
+392 //:
+393 //: Errors will be at depth 0.
+394 //: Warnings will be at depth 1.
+395 //: SubX instructions will occupy depth 2 and up to Max_depth, organized by
+396 //: stack frames. Each instruction's internal details will be one level deeper
+397 //: than its 'main' depth. So 'call' instruction details will be at the same
+398 //: depth as the instructions of the function it calls.
+399 :(before "End Globals")
+400 extern const int Initial_callstack_depth = 2;
+401 int Callstack_depth = Initial_callstack_depth;
+402 :(before "End Reset")
+403 Callstack_depth = Initial_callstack_depth;
+404 
+405 :(before "End Includes")
+406 #include <iomanip>
+407 #define HEXBYTE  std::hex << std::setw(2) << std::setfill('0')
+408 #define HEXWORD  std::hex << std::setw(8) << std::setfill('0')
+409 // ugly that iostream doesn't print uint8_t as an integer
+410 #define NUM(X) static_cast<int>(X)
+411 #include <stdint.h>
+
+ + + diff --git a/html/linux/bootstrap/011run.cc.html b/html/linux/bootstrap/011run.cc.html new file mode 100644 index 00000000..bc7b84ae --- /dev/null +++ b/html/linux/bootstrap/011run.cc.html @@ -0,0 +1,515 @@ + + + + +Mu - linux/bootstrap/011run.cc + + + + + + + + + + +https://github.com/akkartik/mu/blob/main/linux/bootstrap/011run.cc +
+  1 //: Running SubX programs on the VM.
+  2 
+  3 //: (Not to be confused with the 'run' subcommand for running ELF binaries on
+  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"
+ 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"
+ 14   "\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"
+ 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\n"
+ 23   "and 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"
+ 26   "Lines consist of a series of words. Words can contain arbitrary metadata\n"
+ 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 example programs in the apps/ directory, particularly apps/ex*.\n"
+ 31 );
+ 32 :(before "End Help Contents")
+ 33 cerr << "  syntax\n";
+ 34 
+ 35 :(code)
+ 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(
+ 40       // Comments start with '#' and are ignored.
+ 41       "# comment\n"
+ 42       // Segment headers start with '==', a name and a starting hex address.
+ 43       // There's usually one code and one data segment. The code segment
+ 44       // always comes first.
+ 45       "== code 0x1\n"  // code segment
+ 46 
+ 47       // After the header, each segment consists of lines, and each line
+ 48       // consists of words separated by whitespace.
+ 49       //
+ 50       // All words can have metadata after a '/'. No spaces allowed in
+ 51       // metadata, of course.
+ 52       // Unrecognized metadata never causes errors, so you can use it for
+ 53       // documentation.
+ 54       //
+ 55       // Within the code segment in particular, x86 instructions consist of
+ 56       // some number of the following parts and sub-parts (see the Readme and
+ 57       // cheatsheet.pdf for details):
+ 58       //   opcodes: 1-3 bytes
+ 59       //   ModR/M byte
+ 60       //   SIB byte
+ 61       //   displacement: 0/1/2/4 bytes
+ 62       //   immediate: 0/1/2/4 bytes
+ 63       // opcode        ModR/M                    SIB                   displacement    immediate
+ 64       // instruction   mod, reg, Reg/Mem bits    scale, index, base
+ 65       // 1-3 bytes     0/1 byte                  0/1 byte              0/1/2/4 bytes   0/1/2/4 bytes
+ 66       "  b8            .                         .                     .               0a 0b 0c 0d\n"  // copy 0x0d0c0b0a to EAX
+ 67       // The periods are just to help the eye track long gaps between columns,
+ 68       // and are otherwise ignored.
+ 69   );
+ 70   // This program, when run, causes the following events in the trace:
+ 71   CHECK_TRACE_CONTENTS(
+ 72       "load: 0x00000001 -> b8\n"
+ 73       "load: 0x00000002 -> 0a\n"
+ 74       "load: 0x00000003 -> 0b\n"
+ 75       "load: 0x00000004 -> 0c\n"
+ 76       "load: 0x00000005 -> 0d\n"
+ 77       "run: copy imm32 0x0d0c0b0a to EAX\n"
+ 78   );
+ 79 }
+ 80 
+ 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   // 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 }
+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 }
+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 //: 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/linux/bootstrap/012elf.cc.html b/html/linux/bootstrap/012elf.cc.html new file mode 100644 index 00000000..0e14b780 --- /dev/null +++ b/html/linux/bootstrap/012elf.cc.html @@ -0,0 +1,257 @@ + + + + +Mu - linux/bootstrap/012elf.cc + + + + + + + + + + +https://github.com/akkartik/mu/blob/main/linux/bootstrap/012elf.cc +
+  1 //: Loading SubX programs from ELF binaries.
+  2 //: This will allow us to run them natively on a Linux kernel.
+  3 //: Based on https://github.com/kragen/stoneknifeforth/blob/702d2ebe1b/386.c
+  4 
+  5 :(before "End Main")
+  6 assert(argc > 1);
+  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();
+ 11   if (argc <= 2) {
+ 12     raise << "Not enough arguments provided.\n" << die();
+ 13   }
+ 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();
+ 20   return 1;
+ 21 }
+ 22 
+ 23 :(code)
+ 24 void load_elf(const string& filename, int argc, char* argv[]) {
+ 25   int fd = open(filename.c_str(), O_RDONLY);
+ 26   if (fd < 0) raise << filename.c_str() << ": open" << perr() << '\n' << die();
+ 27   off_t size = lseek(fd, 0, SEEK_END);
+ 28   lseek(fd, 0, SEEK_SET);
+ 29   uint8_t* elf_contents = static_cast<uint8_t*>(malloc(size));
+ 30   if (elf_contents == NULL) raise << "malloc(" << size << ')' << perr() << '\n' << die();
+ 31   ssize_t read_size = read(fd, elf_contents, size);
+ 32   if (size != read_size) raise << "read → " << size << " (!= " << read_size << ')' << perr() << '\n' << die();
+ 33   load_elf_contents(elf_contents, size, argc, argv);
+ 34   free(elf_contents);
+ 35 }
+ 36 
+ 37 void load_elf_contents(uint8_t* elf_contents, size_t size, int argc, char* argv[]) {
+ 38   uint8_t magic[5] = {0};
+ 39   memcpy(magic, elf_contents, 4);
+ 40   if (memcmp(magic, "\177ELF", 4) != 0)
+ 41     raise << "Invalid ELF file; starts with \"" << magic << '"' << die();
+ 42   if (elf_contents[4] != 1)
+ 43     raise << "Only 32-bit ELF files (4-byte words; virtual addresses up to 4GB) supported.\n" << die();
+ 44   if (elf_contents[5] != 1)
+ 45     raise << "Only little-endian ELF files supported.\n" << die();
+ 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();
+ 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]);
+ 53   // unused: e_shoff
+ 54   // unused: e_flags
+ 55   uint32_t e_ehsize = u16_in(&elf_contents[40]);
+ 56   if (e_ehsize < 52) raise << "Invalid binary; ELF header too small\n" << die();
+ 57   uint32_t e_phentsize = u16_in(&elf_contents[42]);
+ 58   uint32_t e_phnum = u16_in(&elf_contents[44]);
+ 59   trace(90, "load") << e_phnum << " entries in the program header, each " << e_phentsize << " bytes long" << end();
+ 60   // unused: e_shentsize
+ 61   // unused: e_shnum
+ 62   // unused: e_shstrndx
+ 63 
+ 64   set<uint32_t> overlap;  // to detect overlapping segments
+ 65   for (size_t i = 0;  i < e_phnum;  ++i)
+ 66     load_segment_from_program_header(elf_contents, i, size, e_phoff + i*e_phentsize, e_ehsize, overlap);
+ 67 
+ 68   // initialize code and stack
+ 69   assert(overlap.find(STACK_SEGMENT) == overlap.end());
+ 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;
+ 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));
+ 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]);
+ 87       argv_data += sizeof(char);
+ 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) {
+ 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);
+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) {
+108   uint32_t p_type = u32_in(&elf_contents[offset]);
+109   trace(90, "load") << "program header at offset " << offset << ": type " << p_type << end();
+110   if (p_type != 1) {
+111     trace(90, "load") << "ignoring segment at offset " << offset << " of non PT_LOAD type " << p_type << " (see http://refspecs.linuxbase.org/elf/elf.pdf)" << end();
+112     return;
+113   }
+114   uint32_t p_offset = u32_in(&elf_contents[offset + 4]);
+115   uint32_t p_vaddr = u32_in(&elf_contents[offset + 8]);
+116   if (e_ehsize > p_vaddr) raise << "Invalid binary; program header overlaps ELF header\n" << die();
+117   // unused: p_paddr
+118   uint32_t p_filesz = u32_in(&elf_contents[offset + 16]);
+119   uint32_t p_memsz = u32_in(&elf_contents[offset + 20]);
+120   if (p_filesz != p_memsz)
+121     raise << "Can't yet handle segments where p_filesz != p_memsz (see http://refspecs.linuxbase.org/elf/elf.pdf)\n" << die();
+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) {
+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));
+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]);
+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;
+139 }
+140 
+141 :(before "End Includes")
+142 // Very primitive/fixed/insecure ELF segments for now.
+143 //   --- inaccessible:        0x00000000 -> 0x08047fff
+144 //   code:                    0x09000000 -> 0x09ffffff (specified in ELF binary)
+145 //   data:                    0x0a000000 -> 0x0affffff (specified in ELF binary)
+146 //                      --- heap gets mmap'd somewhere here ---
+147 //   stack:                   0xbdffffff -> 0xbd000000 (downward; not in ELF binary)
+148 //   argv hack:               0xbf000000 -> 0xbfffffff (not in ELF binary)
+149 //   --- reserved for kernel: 0xc0000000 -> ...
+150 const uint32_t START_HEAP        = 0x0b000000;
+151 const uint32_t END_HEAP          = 0xbd000000;
+152 const uint32_t STACK_SEGMENT     = 0xbd000000;
+153 const uint32_t AFTER_STACK       = 0xbe000000;
+154 const uint32_t ARGV_DATA_SEGMENT = 0xbf000000;
+155 // When updating the above memory map, don't forget to update `mmap`'s
+156 // implementation in the 'syscalls' layer.
+157 :(before "End Dump Info for Instruction")
+158 //? dump_stack();  // slow
+159 :(code)
+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();
+168 }
+169 
+170 inline uint32_t u32_in(uint8_t* p) {
+171   return p[0] | p[1] << 8 | p[2] << 16 | p[3] << 24;
+172 }
+173 
+174 inline uint16_t u16_in(uint8_t* p) {
+175   return p[0] | p[1] << 8;
+176 }
+177 
+178 :(before "End Types")
+179 struct perr {};
+180 :(code)
+181 ostream& operator<<(ostream& os, perr /*unused*/) {
+182   if (errno)
+183     os << ": " << strerror(errno);
+184   return os;
+185 }
+186 
+187 :(before "End Includes")
+188 #include <sys/types.h>
+189 #include <sys/stat.h>
+190 #include <fcntl.h>
+191 #include <stdarg.h>
+192 #include <errno.h>
+193 #include <unistd.h>
+
+ + + diff --git a/html/linux/bootstrap/013direct_addressing.cc.html b/html/linux/bootstrap/013direct_addressing.cc.html new file mode 100644 index 00000000..e0ec4b6d --- /dev/null +++ b/html/linux/bootstrap/013direct_addressing.cc.html @@ -0,0 +1,1344 @@ + + + + +Mu - linux/bootstrap/013direct_addressing.cc + + + + + + + + + + +https://github.com/akkartik/mu/blob/main/linux/bootstrap/013direct_addressing.cc +
+   1 //: operating directly on a register
+   2 
+   3 :(before "End Initialize Op Names")
+   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(
+  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"
+  18       "run: r/m32 is EAX\n"
+  19       "run: storing 0x00000011\n"
+  20   );
+  21 }
+  22 
+  23 :(before "End Single-Byte Opcodes")
+  24 case 0x01: {  // add r32 to r/m32
+  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;
+  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);
+  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();
+  40   *signed_arg1 = signed_result;
+  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 = INT32_MAX;
+  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"
+  57       "run: r/m32 is EAX\n"
+  58       "run: SF=1; ZF=0; CF=0; OF=1\n"
+  59       "run: storing 0x80000000\n"
+  60   );
+  61 }
+  62 
+  63 void test_add_r32_to_r32_unsigned_overflow() {
+  64   Reg[EAX].u = UINT32_MAX;
+  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"
+  74       "run: r/m32 is EAX\n"
+  75       "run: SF=0; ZF=1; CF=1; OF=0\n"
+  76       "run: storing 0x00000000\n"
+  77   );
+  78 }
+  79 
+  80 void test_add_r32_to_r32_unsigned_and_signed_overflow() {
+  81   Reg[EAX].i = Reg[EBX].i = INT32_MIN;
+  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"
+  90       "run: r/m32 is EAX\n"
+  91       "run: SF=0; ZF=1; CF=1; OF=1\n"
+  92       "run: storing 0x00000000\n"
+  93   );
+  94 }
+  95 
+  96 :(code)
+  97 // Implement tables 2-2 and 2-3 in the Intel manual, Volume 2.
+  98 // We return a pointer so that instructions can write to multiple bytes in
+  99 // 'Mem' at once.
+ 100 // beware: will eventually have side-effects
+ 101 int32_t* effective_address(uint8_t modrm) {
+ 102   const uint8_t mod = (modrm>>6);
+ 103   // ignore middle 3 'reg opcode' bits
+ 104   const uint8_t rm = modrm & 0x7;
+ 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;
+ 109   }
+ 110   uint32_t addr = effective_address_number(modrm);
+ 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
+ 116 uint32_t effective_address_number(uint8_t modrm) {
+ 117   const uint8_t mod = (modrm>>6);
+ 118   // ignore middle 3 'reg opcode' bits
+ 119   const uint8_t rm = modrm & 0x7;
+ 120   uint32_t addr = 0;
+ 121   switch (mod) {
+ 122   case 3:
+ 123     // mod 3 is just register direct addressing
+ 124     raise << "unexpected direct addressing mode\n" << end();
+ 125     return 0;
+ 126   // End Mod Special-cases(addr)
+ 127   default:
+ 128     cerr << "unrecognized mod bits: " << NUM(mod) << '\n';
+ 129     exit(1);
+ 130   }
+ 131   //: other mods are indirect, and they'll set addr appropriately
+ 132   // Found effective_address(addr)
+ 133   return addr;
+ 134 }
+ 135 
+ 136 string rname(uint8_t r) {
+ 137   switch (r) {
+ 138   case 0: return "EAX";
+ 139   case 1: return "ECX";
+ 140   case 2: return "EDX";
+ 141   case 3: return "EBX";
+ 142   case 4: return "ESP";
+ 143   case 5: return "EBP";
+ 144   case 6: return "ESI";
+ 145   case 7: return "EDI";
+ 146   default: raise << "invalid register " << r << '\n' << end();  return "";
+ 147   }
+ 148 }
+ 149 
+ 150 //:: subtract
+ 151 
+ 152 :(before "End Initialize Op Names")
+ 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(
+ 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"
+ 167       "run: r/m32 is EAX\n"
+ 168       "run: storing 0x00000009\n"
+ 169   );
+ 170 }
+ 171 
+ 172 :(before "End Single-Byte Opcodes")
+ 173 case 0x29: {  // subtract r32 from r/m32
+ 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;
+ 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);
+ 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();
+ 189   *signed_arg1 = signed_result;
+ 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 = INT32_MIN;
+ 197   Reg[EBX].i = INT32_MAX;
+ 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"
+ 206       "run: r/m32 is EAX\n"
+ 207       "run: SF=0; ZF=0; CF=0; OF=1\n"
+ 208       "run: storing 0x00000001\n"
+ 209   );
+ 210 }
+ 211 
+ 212 void test_subtract_r32_from_r32_unsigned_overflow() {
+ 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"
+ 223       "run: r/m32 is EAX\n"
+ 224       "run: SF=1; ZF=0; CF=1; OF=0\n"
+ 225       "run: storing 0xffffffff\n"
+ 226   );
+ 227 }
+ 228 
+ 229 void test_subtract_r32_from_r32_signed_and_unsigned_overflow() {
+ 230   Reg[EAX].i = 0;
+ 231   Reg[EBX].i = INT32_MIN;
+ 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"
+ 240       "run: r/m32 is EAX\n"
+ 241       "run: SF=1; ZF=0; CF=1; OF=1\n"
+ 242       "run: storing 0x80000000\n"
+ 243   );
+ 244 }
+ 245 
+ 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)");
+ 250 
+ 251 :(code)
+ 252 void test_multiply_EAX_by_r32() {
+ 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
+ 259       // ModR/M in binary: 11 (direct mode) 100 (subop mul) 001 (src ECX)
+ 260   );
+ 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"
+ 265       "run: storing 0x0000000c\n"
+ 266   );
+ 267 }
+ 268 
+ 269 :(before "End Single-Byte Opcodes")
+ 270 case 0xf7: {
+ 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();
+ 285     break;
+ 286   }
+ 287   // End Op f7 Subops
+ 288   default:
+ 289     cerr << "unrecognized subop for opcode f7: " << NUM(subop) << '\n';
+ 290     exit(1);
+ 291   }
+ 292   break;
+ 293 }
+ 294 
+ 295 //:
+ 296 
+ 297 :(before "End Initialize Op Names")
+ 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(
+ 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"
+ 312       "run: r/m32 is EAX\n"
+ 313       "run: storing 0x00000008\n"
+ 314   );
+ 315 }
+ 316 
+ 317 :(before "End Two-Byte Opcodes Starting With 0f")
+ 318 case 0xaf: {  // multiply r32 by r/m32
+ 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   int64_t full_result = static_cast<int64_t>(Reg[arg1].i) * (*arg2);
+ 325   OF = (result != full_result);
+ 326   CF = OF;
+ 327   trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end();
+ 328   Reg[arg1].i = result;
+ 329   trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << Reg[arg1].i << end();
+ 330   break;
+ 331 }
+ 332 
+ 333 //:: negate
+ 334 
+ 335 :(code)
+ 336 void test_negate_r32() {
+ 337   Reg[EBX].i = 1;
+ 338   run(
+ 339       "== code 0x1\n"  // code segment
+ 340       // op     ModR/M  SIB   displacement  immediate
+ 341       "  f7     db                                    \n"  // negate EBX
+ 342       // ModR/M in binary: 11 (direct mode) 011 (subop negate) 011 (dest EBX)
+ 343   );
+ 344   CHECK_TRACE_CONTENTS(
+ 345       "run: operate on r/m32\n"
+ 346       "run: r/m32 is EBX\n"
+ 347       "run: subop: negate\n"
+ 348       "run: storing 0xffffffff\n"
+ 349   );
+ 350 }
+ 351 
+ 352 :(before "End Op f7 Subops")
+ 353 case 3: {  // negate r/m32
+ 354   trace(Callstack_depth+1, "run") << "subop: negate" << end();
+ 355   // one case that can overflow
+ 356   if (static_cast<uint32_t>(*arg1) == 0x80000000) {
+ 357     trace(Callstack_depth+1, "run") << "overflow" << end();
+ 358     SF = true;
+ 359     ZF = false;
+ 360     OF = true;
+ 361     break;
+ 362   }
+ 363   int32_t result = -(*arg1);
+ 364   SF = (result >> 31);
+ 365   ZF = (result == 0);
+ 366   OF = false;
+ 367   CF = (*arg1 != 0);
+ 368   trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end();
+ 369   *arg1 = result;
+ 370   trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *arg1 << end();
+ 371   break;
+ 372 }
+ 373 
+ 374 :(code)
+ 375 // negate can overflow in exactly one situation
+ 376 void test_negate_can_overflow() {
+ 377   Reg[EBX].i = INT32_MIN;
+ 378   run(
+ 379       "== code 0x1\n"  // code segment
+ 380       // op     ModR/M  SIB   displacement  immediate
+ 381       "  f7     db                                    \n"  // negate EBX
+ 382       // ModR/M in binary: 11 (direct mode) 011 (subop negate) 011 (dest EBX)
+ 383   );
+ 384   CHECK_TRACE_CONTENTS(
+ 385       "run: operate on r/m32\n"
+ 386       "run: r/m32 is EBX\n"
+ 387       "run: subop: negate\n"
+ 388       "run: overflow\n"
+ 389   );
+ 390 }
+ 391 
+ 392 //:: divide with remainder
+ 393 
+ 394 void test_divide_EAX_by_rm32() {
+ 395   Reg[EAX].u = 7;
+ 396   Reg[EDX].u = 0;
+ 397   Reg[ECX].i = 3;
+ 398   run(
+ 399       "== code 0x1\n"  // code segment
+ 400       // op     ModR/M  SIB   displacement  immediate
+ 401       "  f7     f9                                    \n"  // multiply EAX by ECX
+ 402       // ModR/M in binary: 11 (direct mode) 111 (subop idiv) 001 (divisor ECX)
+ 403   );
+ 404   CHECK_TRACE_CONTENTS(
+ 405       "run: operate on r/m32\n"
+ 406       "run: r/m32 is ECX\n"
+ 407       "run: subop: divide EDX:EAX by r/m32, storing quotient in EAX and remainder in EDX\n"
+ 408       "run: quotient: 0x00000002\n"
+ 409       "run: remainder: 0x00000001\n"
+ 410   );
+ 411 }
+ 412 
+ 413 :(before "End Op f7 Subops")
+ 414 case 7: {  // divide EDX:EAX by r/m32, storing quotient in EAX and remainder in EDX
+ 415   trace(Callstack_depth+1, "run") << "subop: divide EDX:EAX by r/m32, storing quotient in EAX and remainder in EDX" << end();
+ 416   int64_t dividend = static_cast<int64_t>((static_cast<uint64_t>(Reg[EDX].u) << 32) | Reg[EAX].u);
+ 417   int32_t divisor = *arg1;
+ 418   assert(divisor != 0);
+ 419   Reg[EAX].i = dividend/divisor;  // quotient
+ 420   Reg[EDX].i = dividend%divisor;  // remainder
+ 421   // flag state undefined
+ 422   trace(Callstack_depth+1, "run") << "quotient: 0x" << HEXWORD << Reg[EAX].i << end();
+ 423   trace(Callstack_depth+1, "run") << "remainder: 0x" << HEXWORD << Reg[EDX].i << end();
+ 424   break;
+ 425 }
+ 426 
+ 427 :(code)
+ 428 void test_divide_EAX_by_negative_rm32() {
+ 429   Reg[EAX].u = 7;
+ 430   Reg[EDX].u = 0;
+ 431   Reg[ECX].i = -3;
+ 432   run(
+ 433       "== code 0x1\n"  // code segment
+ 434       // op     ModR/M  SIB   displacement  immediate
+ 435       "  f7     f9                                    \n"  // multiply EAX by ECX
+ 436       // ModR/M in binary: 11 (direct mode) 111 (subop idiv) 001 (divisor ECX)
+ 437   );
+ 438   CHECK_TRACE_CONTENTS(
+ 439       "run: operate on r/m32\n"
+ 440       "run: r/m32 is ECX\n"
+ 441       "run: subop: divide EDX:EAX by r/m32, storing quotient in EAX and remainder in EDX\n"
+ 442       "run: quotient: 0xfffffffe\n"  // -2
+ 443       "run: remainder: 0x00000001\n"
+ 444   );
+ 445 }
+ 446 
+ 447 void test_divide_negative_EAX_by_rm32() {
+ 448   Reg[EAX].i = -7;
+ 449   Reg[EDX].i = -1;  // sign extend
+ 450   Reg[ECX].i = 3;
+ 451   run(
+ 452       "== code 0x1\n"  // code segment
+ 453       // op     ModR/M  SIB   displacement  immediate
+ 454       "  f7     f9                                    \n"  // multiply EAX by ECX
+ 455       // ModR/M in binary: 11 (direct mode) 111 (subop idiv) 001 (divisor ECX)
+ 456   );
+ 457   CHECK_TRACE_CONTENTS(
+ 458       "run: operate on r/m32\n"
+ 459       "run: r/m32 is ECX\n"
+ 460       "run: subop: divide EDX:EAX by r/m32, storing quotient in EAX and remainder in EDX\n"
+ 461       "run: quotient: 0xfffffffe\n"  // -2
+ 462       "run: remainder: 0xffffffff\n"  // -1, same sign as divident (EDX:EAX)
+ 463   );
+ 464 }
+ 465 
+ 466 void test_divide_negative_EDX_EAX_by_rm32() {
+ 467   Reg[EAX].i = 0;  // lower 32 bits are clear
+ 468   Reg[EDX].i = -7;
+ 469   Reg[ECX].i = 0x40000000;  // 2^30 (largest positive power of 2)
+ 470   run(
+ 471       "== code 0x1\n"  // code segment
+ 472       // op     ModR/M  SIB   displacement  immediate
+ 473       "  f7     f9                                    \n"  // multiply EAX by ECX
+ 474       // ModR/M in binary: 11 (direct mode) 111 (subop idiv) 001 (divisor ECX)
+ 475   );
+ 476   CHECK_TRACE_CONTENTS(
+ 477       "run: operate on r/m32\n"
+ 478       "run: r/m32 is ECX\n"
+ 479       "run: subop: divide EDX:EAX by r/m32, storing quotient in EAX and remainder in EDX\n"
+ 480       "run: quotient: 0xffffffe4\n"  // (-7 << 32) / (1 << 30) = -7 << 2 = -28
+ 481       "run: remainder: 0x00000000\n"
+ 482   );
+ 483 }
+ 484 
+ 485 //:: shift left
+ 486 
+ 487 :(before "End Initialize Op Names")
+ 488 put_new(Name, "d3", "shift rm32 by CL bits depending on subop (sal/sar/shl/shr)");
+ 489 
+ 490 :(code)
+ 491 void test_shift_left_r32_with_cl() {
+ 492   Reg[EBX].i = 13;
+ 493   Reg[ECX].i = 1;
+ 494   run(
+ 495       "== code 0x1\n"  // code segment
+ 496       // op     ModR/M  SIB   displacement  immediate
+ 497       "  d3     e3                                    \n"  // shift EBX left by CL bits
+ 498       // ModR/M in binary: 11 (direct mode) 100 (subop shift left) 011 (dest EBX)
+ 499   );
+ 500   CHECK_TRACE_CONTENTS(
+ 501       "run: operate on r/m32\n"
+ 502       "run: r/m32 is EBX\n"
+ 503       "run: subop: shift left by CL bits\n"
+ 504       "run: storing 0x0000001a\n"
+ 505   );
+ 506 }
+ 507 
+ 508 :(before "End Single-Byte Opcodes")
+ 509 case 0xd3: {
+ 510   const uint8_t modrm = next();
+ 511   trace(Callstack_depth+1, "run") << "operate on r/m32" << end();
+ 512   int32_t* arg1 = effective_address(modrm);
+ 513   const uint8_t subop = (modrm>>3)&0x7;  // middle 3 'reg opcode' bits
+ 514   switch (subop) {
+ 515   case 4: {  // shift left r/m32 by CL
+ 516     trace(Callstack_depth+1, "run") << "subop: shift left by CL bits" << end();
+ 517     uint8_t count = Reg[ECX].u & 0x1f;
+ 518     // OF is only defined if count is 1
+ 519     if (count == 1) {
+ 520       bool msb = (*arg1 & 0x80000000) >> 1;
+ 521       bool pnsb = (*arg1 & 0x40000000);
+ 522       OF = (msb != pnsb);
+ 523     }
+ 524     int32_t result = (*arg1 << count);
+ 525     ZF = (result == 0);
+ 526     SF = (result < 0);
+ 527     CF = (*arg1 << (count-1)) & 0x80000000;
+ 528     trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end();
+ 529     *arg1 = result;
+ 530     trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *arg1 << end();
+ 531     break;
+ 532   }
+ 533   // End Op d3 Subops
+ 534   default:
+ 535     cerr << "unrecognized subop for opcode d3: " << NUM(subop) << '\n';
+ 536     exit(1);
+ 537   }
+ 538   break;
+ 539 }
+ 540 
+ 541 //:: shift right arithmetic
+ 542 
+ 543 :(code)
+ 544 void test_shift_right_arithmetic_r32_with_cl() {
+ 545   Reg[EBX].i = 26;
+ 546   Reg[ECX].i = 1;
+ 547   run(
+ 548       "== code 0x1\n"  // code segment
+ 549       // op     ModR/M  SIB   displacement  immediate
+ 550       "  d3     fb                                    \n"  // shift EBX right by CL bits, while preserving sign
+ 551       // ModR/M in binary: 11 (direct mode) 111 (subop shift right arithmetic) 011 (dest EBX)
+ 552   );
+ 553   CHECK_TRACE_CONTENTS(
+ 554       "run: operate on r/m32\n"
+ 555       "run: r/m32 is EBX\n"
+ 556       "run: subop: shift right by CL bits, while preserving sign\n"
+ 557       "run: storing 0x0000000d\n"
+ 558   );
+ 559 }
+ 560 
+ 561 :(before "End Op d3 Subops")
+ 562 case 7: {  // shift right r/m32 by CL, preserving sign
+ 563   trace(Callstack_depth+1, "run") << "subop: shift right by CL bits, while preserving sign" << end();
+ 564   uint8_t count = Reg[ECX].u & 0x1f;
+ 565   *arg1 = (*arg1 >> count);
+ 566   ZF = (*arg1 == 0);
+ 567   SF = (*arg1 < 0);
+ 568   // OF is only defined if count is 1
+ 569   if (count == 1) OF = false;
+ 570   // CF undefined
+ 571   trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *arg1 << end();
+ 572   break;
+ 573 }
+ 574 
+ 575 :(code)
+ 576 void test_shift_right_arithmetic_odd_r32_with_cl() {
+ 577   Reg[EBX].i = 27;
+ 578   Reg[ECX].i = 1;
+ 579   run(
+ 580       "== code 0x1\n"  // code segment
+ 581       // op     ModR/M  SIB   displacement  immediate
+ 582       "  d3     fb                                    \n"  // shift EBX right by CL bits, while preserving sign
+ 583       // ModR/M in binary: 11 (direct mode) 111 (subop shift right arithmetic) 011 (dest EBX)
+ 584   );
+ 585   CHECK_TRACE_CONTENTS(
+ 586       "run: operate on r/m32\n"
+ 587       "run: r/m32 is EBX\n"
+ 588       "run: subop: shift right by CL bits, while preserving sign\n"
+ 589       // result: 13
+ 590       "run: storing 0x0000000d\n"
+ 591   );
+ 592 }
+ 593 
+ 594 void test_shift_right_arithmetic_negative_r32_with_cl() {
+ 595   Reg[EBX].i = 0xfffffffd;  // -3
+ 596   Reg[ECX].i = 1;
+ 597   run(
+ 598       "== code 0x1\n"  // code segment
+ 599       // op     ModR/M  SIB   displacement  immediate
+ 600       "  d3     fb                                    \n"  // shift EBX right by CL bits, while preserving sign
+ 601       // ModR/M in binary: 11 (direct mode) 111 (subop shift right arithmetic) 011 (dest EBX)
+ 602   );
+ 603   CHECK_TRACE_CONTENTS(
+ 604       "run: operate on r/m32\n"
+ 605       "run: r/m32 is EBX\n"
+ 606       "run: subop: shift right by CL bits, while preserving sign\n"
+ 607       // result: -2
+ 608       "run: storing 0xfffffffe\n"
+ 609   );
+ 610 }
+ 611 
+ 612 //:: shift right logical
+ 613 
+ 614 :(code)
+ 615 void test_shift_right_logical_r32_with_cl() {
+ 616   Reg[EBX].i = 26;
+ 617   Reg[ECX].i = 1;
+ 618   run(
+ 619       "== code 0x1\n"  // code segment
+ 620       // op     ModR/M  SIB   displacement  immediate
+ 621       "  d3     eb                                    \n"  // shift EBX right by CL bits, while padding zeroes
+ 622       // ModR/M in binary: 11 (direct mode) 101 (subop shift right logical) 011 (dest EBX)
+ 623   );
+ 624   CHECK_TRACE_CONTENTS(
+ 625       "run: operate on r/m32\n"
+ 626       "run: r/m32 is EBX\n"
+ 627       "run: subop: shift right by CL bits, while padding zeroes\n"
+ 628       // result: 13
+ 629       "run: storing 0x0000000d\n"
+ 630   );
+ 631 }
+ 632 
+ 633 :(before "End Op d3 Subops")
+ 634 case 5: {  // shift right r/m32 by CL, padding zeroes
+ 635   trace(Callstack_depth+1, "run") << "subop: shift right by CL bits, while padding zeroes" << end();
+ 636   uint8_t count = Reg[ECX].u & 0x1f;
+ 637   // OF is only defined if count is 1
+ 638   if (count == 1) {
+ 639     bool msb = (*arg1 & 0x80000000) >> 1;
+ 640     bool pnsb = (*arg1 & 0x40000000);
+ 641     OF = (msb != pnsb);
+ 642   }
+ 643   uint32_t* uarg1 = reinterpret_cast<uint32_t*>(arg1);
+ 644   *uarg1 = (*uarg1 >> count);
+ 645   ZF = (*uarg1 == 0);
+ 646   // result is always positive by definition
+ 647   SF = false;
+ 648   // CF undefined
+ 649   trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *arg1 << end();
+ 650   break;
+ 651 }
+ 652 
+ 653 :(code)
+ 654 void test_shift_right_logical_odd_r32_with_cl() {
+ 655   Reg[EBX].i = 27;
+ 656   Reg[ECX].i = 1;
+ 657   run(
+ 658       "== code 0x1\n"  // code segment
+ 659       // op     ModR/M  SIB   displacement  immediate
+ 660       "  d3     eb                                    \n"  // shift EBX right by CL bits, while padding zeroes
+ 661       // ModR/M in binary: 11 (direct mode) 101 (subop shift right logical) 011 (dest EBX)
+ 662   );
+ 663   CHECK_TRACE_CONTENTS(
+ 664       "run: operate on r/m32\n"
+ 665       "run: r/m32 is EBX\n"
+ 666       "run: subop: shift right by CL bits, while padding zeroes\n"
+ 667       // result: 13
+ 668       "run: storing 0x0000000d\n"
+ 669   );
+ 670 }
+ 671 
+ 672 void test_shift_right_logical_negative_r32_with_cl() {
+ 673   Reg[EBX].i = 0xfffffffd;
+ 674   Reg[ECX].i = 1;
+ 675   run(
+ 676       "== code 0x1\n"  // code segment
+ 677       // op     ModR/M  SIB   displacement  immediate
+ 678       "  d3     eb                                    \n"  // shift EBX right by CL bits, while padding zeroes
+ 679       // ModR/M in binary: 11 (direct mode) 101 (subop shift right logical) 011 (dest EBX)
+ 680   );
+ 681   CHECK_TRACE_CONTENTS(
+ 682       "run: operate on r/m32\n"
+ 683       "run: r/m32 is EBX\n"
+ 684       "run: subop: shift right by CL bits, while padding zeroes\n"
+ 685       "run: storing 0x7ffffffe\n"
+ 686   );
+ 687 }
+ 688 
+ 689 //:: and
+ 690 
+ 691 :(before "End Initialize Op Names")
+ 692 put_new(Name, "21", "rm32 = bitwise AND of r32 with rm32 (and)");
+ 693 
+ 694 :(code)
+ 695 void test_and_r32_with_r32() {
+ 696   Reg[EAX].i = 0x0a0b0c0d;
+ 697   Reg[EBX].i = 0x000000ff;
+ 698   run(
+ 699       "== code 0x1\n"  // code segment
+ 700       // op     ModR/M  SIB   displacement  immediate
+ 701       "  21     d8                                    \n"  // and EBX with destination EAX
+ 702       // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX)
+ 703   );
+ 704   CHECK_TRACE_CONTENTS(
+ 705       "run: and EBX with r/m32\n"
+ 706       "run: r/m32 is EAX\n"
+ 707       "run: storing 0x0000000d\n"
+ 708   );
+ 709 }
+ 710 
+ 711 :(before "End Single-Byte Opcodes")
+ 712 case 0x21: {  // and r32 with r/m32
+ 713   const uint8_t modrm = next();
+ 714   const uint8_t arg2 = (modrm>>3)&0x7;
+ 715   trace(Callstack_depth+1, "run") << "and " << rname(arg2) << " with r/m32" << end();
+ 716   // bitwise ops technically operate on unsigned numbers, but it makes no
+ 717   // difference
+ 718   int32_t* signed_arg1 = effective_address(modrm);
+ 719   *signed_arg1 &= Reg[arg2].i;
+ 720   trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *signed_arg1 << end();
+ 721   SF = (*signed_arg1 >> 31);
+ 722   ZF = (*signed_arg1 == 0);
+ 723   CF = false;
+ 724   OF = false;
+ 725   trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end();
+ 726   break;
+ 727 }
+ 728 
+ 729 //:: or
+ 730 
+ 731 :(before "End Initialize Op Names")
+ 732 put_new(Name, "09", "rm32 = bitwise OR of r32 with rm32 (or)");
+ 733 
+ 734 :(code)
+ 735 void test_or_r32_with_r32() {
+ 736   Reg[EAX].i = 0x0a0b0c0d;
+ 737   Reg[EBX].i = 0xa0b0c0d0;
+ 738   run(
+ 739       "== code 0x1\n"  // code segment
+ 740       // op     ModR/M  SIB   displacement  immediate
+ 741       "  09     d8                                    \n"  // or EBX with destination EAX
+ 742       // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX)
+ 743   );
+ 744   CHECK_TRACE_CONTENTS(
+ 745       "run: or EBX with r/m32\n"
+ 746       "run: r/m32 is EAX\n"
+ 747       "run: storing 0xaabbccdd\n"
+ 748   );
+ 749 }
+ 750 
+ 751 :(before "End Single-Byte Opcodes")
+ 752 case 0x09: {  // or r32 with r/m32
+ 753   const uint8_t modrm = next();
+ 754   const uint8_t arg2 = (modrm>>3)&0x7;
+ 755   trace(Callstack_depth+1, "run") << "or " << rname(arg2) << " with r/m32" << end();
+ 756   // bitwise ops technically operate on unsigned numbers, but it makes no
+ 757   // difference
+ 758   int32_t* signed_arg1 = effective_address(modrm);
+ 759   *signed_arg1 |= Reg[arg2].i;
+ 760   trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *signed_arg1 << end();
+ 761   SF = (*signed_arg1 >> 31);
+ 762   ZF = (*signed_arg1 == 0);
+ 763   CF = false;
+ 764   OF = false;
+ 765   trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end();
+ 766   break;
+ 767 }
+ 768 
+ 769 //:: xor
+ 770 
+ 771 :(before "End Initialize Op Names")
+ 772 put_new(Name, "31", "rm32 = bitwise XOR of r32 with rm32 (xor)");
+ 773 
+ 774 :(code)
+ 775 void test_xor_r32_with_r32() {
+ 776   Reg[EAX].i = 0x0a0b0c0d;
+ 777   Reg[EBX].i = 0xaabbc0d0;
+ 778   run(
+ 779       "== code 0x1\n"  // code segment
+ 780       // op     ModR/M  SIB   displacement  immediate
+ 781       "  31     d8                                    \n"  // xor EBX with destination EAX
+ 782       // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX)
+ 783   );
+ 784   CHECK_TRACE_CONTENTS(
+ 785       "run: xor EBX with r/m32\n"
+ 786       "run: r/m32 is EAX\n"
+ 787       "run: storing 0xa0b0ccdd\n"
+ 788   );
+ 789 }
+ 790 
+ 791 :(before "End Single-Byte Opcodes")
+ 792 case 0x31: {  // xor r32 with r/m32
+ 793   const uint8_t modrm = next();
+ 794   const uint8_t arg2 = (modrm>>3)&0x7;
+ 795   trace(Callstack_depth+1, "run") << "xor " << rname(arg2) << " with r/m32" << end();
+ 796   // bitwise ops technically operate on unsigned numbers, but it makes no
+ 797   // difference
+ 798   int32_t* signed_arg1 = effective_address(modrm);
+ 799   *signed_arg1 ^= Reg[arg2].i;
+ 800   trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *signed_arg1 << end();
+ 801   SF = (*signed_arg1 >> 31);
+ 802   ZF = (*signed_arg1 == 0);
+ 803   CF = false;
+ 804   OF = false;
+ 805   trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end();
+ 806   break;
+ 807 }
+ 808 
+ 809 //:: not
+ 810 
+ 811 :(code)
+ 812 void test_not_r32() {
+ 813   Reg[EBX].i = 0x0f0f00ff;
+ 814   run(
+ 815       "== code 0x1\n"  // code segment
+ 816       // op     ModR/M  SIB   displacement  immediate
+ 817       "  f7     d3                                    \n"  // not EBX
+ 818       // ModR/M in binary: 11 (direct mode) 010 (subop not) 011 (dest EBX)
+ 819   );
+ 820   CHECK_TRACE_CONTENTS(
+ 821       "run: operate on r/m32\n"
+ 822       "run: r/m32 is EBX\n"
+ 823       "run: subop: not\n"
+ 824       "run: storing 0xf0f0ff00\n"
+ 825   );
+ 826 }
+ 827 
+ 828 :(before "End Op f7 Subops")
+ 829 case 2: {  // not r/m32
+ 830   trace(Callstack_depth+1, "run") << "subop: not" << end();
+ 831   *arg1 = ~(*arg1);
+ 832   trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *arg1 << end();
+ 833   // no flags affected
+ 834   break;
+ 835 }
+ 836 
+ 837 //:: compare (cmp)
+ 838 
+ 839 :(before "End Initialize Op Names")
+ 840 put_new(Name, "39", "compare: set SF if rm32 < r32 (cmp)");
+ 841 
+ 842 :(code)
+ 843 void test_compare_r32_with_r32_greater() {
+ 844   Reg[EAX].i = 0x0a0b0c0d;
+ 845   Reg[EBX].i = 0x0a0b0c07;
+ 846   run(
+ 847       "== code 0x1\n"  // code segment
+ 848       // op     ModR/M  SIB   displacement  immediate
+ 849       "  39     d8                                    \n"  // compare EAX with EBX
+ 850       // ModR/M in binary: 11 (direct mode) 011 (rhs EBX) 000 (lhs EAX)
+ 851   );
+ 852   CHECK_TRACE_CONTENTS(
+ 853       "run: compare r/m32 with EBX\n"
+ 854       "run: r/m32 is EAX\n"
+ 855       "run: SF=0; ZF=0; CF=0; OF=0\n"
+ 856   );
+ 857 }
+ 858 
+ 859 :(before "End Single-Byte Opcodes")
+ 860 case 0x39: {  // set SF if r/m32 < r32
+ 861   const uint8_t modrm = next();
+ 862   const uint8_t reg2 = (modrm>>3)&0x7;
+ 863   trace(Callstack_depth+1, "run") << "compare r/m32 with " << rname(reg2) << end();
+ 864   const int32_t* signed_arg1 = effective_address(modrm);
+ 865   const int32_t signed_difference = *signed_arg1 - Reg[reg2].i;
+ 866   SF = (signed_difference < 0);
+ 867   ZF = (signed_difference == 0);
+ 868   const int64_t signed_full_difference = static_cast<int64_t>(*signed_arg1) - Reg[reg2].i;
+ 869   OF = (signed_difference != signed_full_difference);
+ 870   // set CF
+ 871   const uint32_t unsigned_arg1 = static_cast<uint32_t>(*signed_arg1);
+ 872   const uint32_t unsigned_difference = unsigned_arg1 - Reg[reg2].u;
+ 873   const uint64_t unsigned_full_difference = static_cast<uint64_t>(unsigned_arg1) - Reg[reg2].u;
+ 874   CF = (unsigned_difference != unsigned_full_difference);
+ 875   trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end();
+ 876   break;
+ 877 }
+ 878 
+ 879 :(code)
+ 880 void test_compare_r32_with_r32_lesser_unsigned_and_signed() {
+ 881   Reg[EAX].i = 0x0a0b0c07;
+ 882   Reg[EBX].i = 0x0a0b0c0d;
+ 883   run(
+ 884       "== code 0x1\n"  // code segment
+ 885       // op     ModR/M  SIB   displacement  immediate
+ 886       "  39     d8                                    \n"  // compare EAX with EBX
+ 887       // ModR/M in binary: 11 (direct mode) 011 (rhs EBX) 000 (lhs EAX)
+ 888   );
+ 889   CHECK_TRACE_CONTENTS(
+ 890       "run: compare r/m32 with EBX\n"
+ 891       "run: r/m32 is EAX\n"
+ 892       "run: SF=1; ZF=0; CF=1; OF=0\n"
+ 893   );
+ 894 }
+ 895 
+ 896 void test_compare_r32_with_r32_lesser_unsigned_and_signed_due_to_overflow() {
+ 897   Reg[EAX].i = INT32_MAX;
+ 898   Reg[EBX].i = INT32_MIN;
+ 899   run(
+ 900       "== code 0x1\n"  // code segment
+ 901       // op     ModR/M  SIB   displacement  immediate
+ 902       "  39     d8                                    \n"  // compare EAX with EBX
+ 903       // ModR/M in binary: 11 (direct mode) 011 (rhs EBX) 000 (lhs EAX)
+ 904   );
+ 905   CHECK_TRACE_CONTENTS(
+ 906       "run: compare r/m32 with EBX\n"
+ 907       "run: r/m32 is EAX\n"
+ 908       "run: SF=1; ZF=0; CF=1; OF=1\n"
+ 909   );
+ 910 }
+ 911 
+ 912 void test_compare_r32_with_r32_lesser_signed() {
+ 913   Reg[EAX].i = -1;
+ 914   Reg[EBX].i = 1;
+ 915   run(
+ 916       "== code 0x1\n"  // code segment
+ 917       // op     ModR/M  SIB   displacement  immediate
+ 918       "  39     d8                                    \n"  // compare EAX with EBX
+ 919       // ModR/M in binary: 11 (direct mode) 011 (rhs EBX) 000 (lhs EAX)
+ 920   );
+ 921   CHECK_TRACE_CONTENTS(
+ 922       "run: compare r/m32 with EBX\n"
+ 923       "run: r/m32 is EAX\n"
+ 924       "run: SF=1; ZF=0; CF=0; OF=0\n"
+ 925   );
+ 926 }
+ 927 
+ 928 void test_compare_r32_with_r32_lesser_unsigned() {
+ 929   Reg[EAX].i = 1;
+ 930   Reg[EBX].i = -1;
+ 931   run(
+ 932       "== code 0x1\n"  // code segment
+ 933       // op     ModR/M  SIB   displacement  immediate
+ 934       "  39     d8                                    \n"  // compare EAX with EBX
+ 935       // ModR/M in binary: 11 (direct mode) 011 (rhs EBX) 000 (lhs EAX)
+ 936   );
+ 937   CHECK_TRACE_CONTENTS(
+ 938       "run: compare r/m32 with EBX\n"
+ 939       "run: r/m32 is EAX\n"
+ 940       "run: SF=0; ZF=0; CF=1; OF=0\n"
+ 941   );
+ 942 }
+ 943 
+ 944 void test_compare_r32_with_r32_equal() {
+ 945   Reg[EAX].i = 0x0a0b0c0d;
+ 946   Reg[EBX].i = 0x0a0b0c0d;
+ 947   run(
+ 948       "== code 0x1\n"  // code segment
+ 949       // op     ModR/M  SIB   displacement  immediate
+ 950       "  39     d8                                    \n"  // compare EAX and EBX
+ 951       // ModR/M in binary: 11 (direct mode) 011 (rhs EBX) 000 (lhs EAX)
+ 952   );
+ 953   CHECK_TRACE_CONTENTS(
+ 954       "run: compare r/m32 with EBX\n"
+ 955       "run: r/m32 is EAX\n"
+ 956       "run: SF=0; ZF=1; CF=0; OF=0\n"
+ 957   );
+ 958 }
+ 959 
+ 960 //:: copy (mov)
+ 961 
+ 962 :(before "End Initialize Op Names")
+ 963 put_new(Name, "89", "copy r32 to rm32 (mov)");
+ 964 
+ 965 :(code)
+ 966 void test_copy_r32_to_r32() {
+ 967   Reg[EBX].i = 0xaf;
+ 968   run(
+ 969       "== code 0x1\n"  // code segment
+ 970       // op     ModR/M  SIB   displacement  immediate
+ 971       "  89     d8                                    \n"  // copy EBX to EAX
+ 972       // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX)
+ 973   );
+ 974   CHECK_TRACE_CONTENTS(
+ 975       "run: copy EBX to r/m32\n"
+ 976       "run: r/m32 is EAX\n"
+ 977       "run: storing 0x000000af\n"
+ 978   );
+ 979 }
+ 980 
+ 981 :(before "End Single-Byte Opcodes")
+ 982 case 0x89: {  // copy r32 to r/m32
+ 983   const uint8_t modrm = next();
+ 984   const uint8_t rsrc = (modrm>>3)&0x7;
+ 985   trace(Callstack_depth+1, "run") << "copy " << rname(rsrc) << " to r/m32" << end();
+ 986   int32_t* dest = effective_address(modrm);
+ 987   *dest = Reg[rsrc].i;  // Write multiple elements of vector<uint8_t> at once. Assumes sizeof(int) == 4 on the host as well.
+ 988   trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *dest << end();
+ 989   break;
+ 990 }
+ 991 
+ 992 //:: xchg
+ 993 
+ 994 :(before "End Initialize Op Names")
+ 995 put_new(Name, "87", "swap the contents of r32 and rm32 (xchg)");
+ 996 
+ 997 :(code)
+ 998 void test_xchg_r32_with_r32() {
+ 999   Reg[EBX].i = 0xaf;
+1000   Reg[EAX].i = 0x2e;
+1001   run(
+1002       "== code 0x1\n"  // code segment
+1003       // op     ModR/M  SIB   displacement  immediate
+1004       "  87     d8                                    \n"  // exchange EBX with EAX
+1005       // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX)
+1006   );
+1007   CHECK_TRACE_CONTENTS(
+1008       "run: exchange EBX with r/m32\n"
+1009       "run: r/m32 is EAX\n"
+1010       "run: storing 0x000000af in r/m32\n"
+1011       "run: storing 0x0000002e in EBX\n"
+1012   );
+1013 }
+1014 
+1015 :(before "End Single-Byte Opcodes")
+1016 case 0x87: {  // exchange r32 with r/m32
+1017   const uint8_t modrm = next();
+1018   const uint8_t reg2 = (modrm>>3)&0x7;
+1019   trace(Callstack_depth+1, "run") << "exchange " << rname(reg2) << " with r/m32" << end();
+1020   int32_t* arg1 = effective_address(modrm);
+1021   const int32_t tmp = *arg1;
+1022   *arg1 = Reg[reg2].i;
+1023   Reg[reg2].i = tmp;
+1024   trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *arg1 << " in r/m32" << end();
+1025   trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << Reg[reg2].i << " in " << rname(reg2) << end();
+1026   break;
+1027 }
+1028 
+1029 //:: increment
+1030 
+1031 :(before "End Initialize Op Names")
+1032 put_new(Name, "40", "increment EAX (inc)");
+1033 put_new(Name, "41", "increment ECX (inc)");
+1034 put_new(Name, "42", "increment EDX (inc)");
+1035 put_new(Name, "43", "increment EBX (inc)");
+1036 put_new(Name, "44", "increment ESP (inc)");
+1037 put_new(Name, "45", "increment EBP (inc)");
+1038 put_new(Name, "46", "increment ESI (inc)");
+1039 put_new(Name, "47", "increment EDI (inc)");
+1040 
+1041 :(code)
+1042 void test_increment_r32() {
+1043   Reg[ECX].u = 0x1f;
+1044   run(
+1045       "== code 0x1\n"  // code segment
+1046       // op     ModR/M  SIB   displacement  immediate
+1047       "  41                                           \n"  // increment ECX
+1048   );
+1049   CHECK_TRACE_CONTENTS(
+1050       "run: increment ECX\n"
+1051       "run: storing value 0x00000020\n"
+1052   );
+1053 }
+1054 
+1055 :(before "End Single-Byte Opcodes")
+1056 case 0x40:
+1057 case 0x41:
+1058 case 0x42:
+1059 case 0x43:
+1060 case 0x44:
+1061 case 0x45:
+1062 case 0x46:
+1063 case 0x47: {  // increment r32
+1064   const uint8_t reg = op & 0x7;
+1065   trace(Callstack_depth+1, "run") << "increment " << rname(reg) << end();
+1066   ++Reg[reg].u;
+1067   trace(Callstack_depth+1, "run") << "storing value 0x" << HEXWORD << Reg[reg].u << end();
+1068   break;
+1069 }
+1070 
+1071 :(before "End Initialize Op Names")
+1072 put_new(Name, "ff", "increment/decrement/jump/push/call rm32 based on subop (inc/dec/jmp/push/call)");
+1073 
+1074 :(code)
+1075 void test_increment_rm32() {
+1076   Reg[EAX].u = 0x20;
+1077   run(
+1078       "== code 0x1\n"  // code segment
+1079       // op     ModR/M  SIB   displacement  immediate
+1080       "  ff     c0                                    \n"  // increment EAX
+1081       // ModR/M in binary: 11 (direct mode) 000 (subop inc) 000 (EAX)
+1082   );
+1083   CHECK_TRACE_CONTENTS(
+1084       "run: increment r/m32\n"
+1085       "run: r/m32 is EAX\n"
+1086       "run: storing value 0x00000021\n"
+1087   );
+1088 }
+1089 
+1090 :(before "End Single-Byte Opcodes")
+1091 case 0xff: {
+1092   const uint8_t modrm = next();
+1093   const uint8_t subop = (modrm>>3)&0x7;  // middle 3 'reg opcode' bits
+1094   switch (subop) {
+1095     case 0: {  // increment r/m32
+1096       trace(Callstack_depth+1, "run") << "increment r/m32" << end();
+1097       int32_t* arg = effective_address(modrm);
+1098       ++*arg;
+1099       trace(Callstack_depth+1, "run") << "storing value 0x" << HEXWORD << *arg << end();
+1100       break;
+1101     }
+1102     default:
+1103       cerr << "unrecognized subop for ff: " << HEXBYTE << NUM(subop) << '\n';
+1104       exit(1);
+1105     // End Op ff Subops
+1106   }
+1107   break;
+1108 }
+1109 
+1110 //:: decrement
+1111 
+1112 :(before "End Initialize Op Names")
+1113 put_new(Name, "48", "decrement EAX (dec)");
+1114 put_new(Name, "49", "decrement ECX (dec)");
+1115 put_new(Name, "4a", "decrement EDX (dec)");
+1116 put_new(Name, "4b", "decrement EBX (dec)");
+1117 put_new(Name, "4c", "decrement ESP (dec)");
+1118 put_new(Name, "4d", "decrement EBP (dec)");
+1119 put_new(Name, "4e", "decrement ESI (dec)");
+1120 put_new(Name, "4f", "decrement EDI (dec)");
+1121 
+1122 :(code)
+1123 void test_decrement_r32() {
+1124   Reg[ECX].u = 0x1f;
+1125   run(
+1126       "== code 0x1\n"  // code segment
+1127       // op     ModR/M  SIB   displacement  immediate
+1128       "  49                                           \n"  // decrement ECX
+1129   );
+1130   CHECK_TRACE_CONTENTS(
+1131       "run: decrement ECX\n"
+1132       "run: storing value 0x0000001e\n"
+1133   );
+1134 }
+1135 
+1136 :(before "End Single-Byte Opcodes")
+1137 case 0x48:
+1138 case 0x49:
+1139 case 0x4a:
+1140 case 0x4b:
+1141 case 0x4c:
+1142 case 0x4d:
+1143 case 0x4e:
+1144 case 0x4f: {  // decrement r32
+1145   const uint8_t reg = op & 0x7;
+1146   trace(Callstack_depth+1, "run") << "decrement " << rname(reg) << end();
+1147   --Reg[reg].u;
+1148   trace(Callstack_depth+1, "run") << "storing value 0x" << HEXWORD << Reg[reg].u << end();
+1149   break;
+1150 }
+1151 
+1152 :(code)
+1153 void test_decrement_rm32() {
+1154   Reg[EAX].u = 0x20;
+1155   run(
+1156       "== code 0x1\n"  // code segment
+1157       // op     ModR/M  SIB   displacement  immediate
+1158       "  ff     c8                                    \n"  // decrement EAX
+1159       // ModR/M in binary: 11 (direct mode) 001 (subop inc) 000 (EAX)
+1160   );
+1161   CHECK_TRACE_CONTENTS(
+1162       "run: decrement r/m32\n"
+1163       "run: r/m32 is EAX\n"
+1164       "run: storing value 0x0000001f\n"
+1165   );
+1166 }
+1167 
+1168 :(before "End Op ff Subops")
+1169 case 1: {  // decrement r/m32
+1170   trace(Callstack_depth+1, "run") << "decrement r/m32" << end();
+1171   int32_t* arg = effective_address(modrm);
+1172   --*arg;
+1173   trace(Callstack_depth+1, "run") << "storing value 0x" << HEXWORD << *arg << end();
+1174   break;
+1175 }
+1176 
+1177 //:: push
+1178 
+1179 :(before "End Initialize Op Names")
+1180 put_new(Name, "50", "push EAX to stack (push)");
+1181 put_new(Name, "51", "push ECX to stack (push)");
+1182 put_new(Name, "52", "push EDX to stack (push)");
+1183 put_new(Name, "53", "push EBX to stack (push)");
+1184 put_new(Name, "54", "push ESP to stack (push)");
+1185 put_new(Name, "55", "push EBP to stack (push)");
+1186 put_new(Name, "56", "push ESI to stack (push)");
+1187 put_new(Name, "57", "push EDI to stack (push)");
+1188 
+1189 :(code)
+1190 void test_push_r32() {
+1191   Mem.push_back(vma(0xbd000000));  // manually allocate memory
+1192   Reg[ESP].u = 0xbd000008;
+1193   Reg[EBX].i = 0x0000000a;
+1194   run(
+1195       "== code 0x1\n"  // code segment
+1196       // op     ModR/M  SIB   displacement  immediate
+1197       "  53                                           \n"  // push EBX to stack
+1198   );
+1199   CHECK_TRACE_CONTENTS(
+1200       "run: push EBX\n"
+1201       "run: decrementing ESP to 0xbd000004\n"
+1202       "run: pushing value 0x0000000a\n"
+1203   );
+1204 }
+1205 
+1206 :(before "End Single-Byte Opcodes")
+1207 case 0x50:
+1208 case 0x51:
+1209 case 0x52:
+1210 case 0x53:
+1211 case 0x54:
+1212 case 0x55:
+1213 case 0x56:
+1214 case 0x57: {  // push r32 to stack
+1215   uint8_t reg = op & 0x7;
+1216   trace(Callstack_depth+1, "run") << "push " << rname(reg) << end();
+1217 //?   cerr << "push: " << NUM(reg) << ": " << Reg[reg].u << " => " << Reg[ESP].u << '\n';
+1218   push(Reg[reg].u);
+1219   break;
+1220 }
+1221 
+1222 //:: pop
+1223 
+1224 :(before "End Initialize Op Names")
+1225 put_new(Name, "58", "pop top of stack to EAX (pop)");
+1226 put_new(Name, "59", "pop top of stack to ECX (pop)");
+1227 put_new(Name, "5a", "pop top of stack to EDX (pop)");
+1228 put_new(Name, "5b", "pop top of stack to EBX (pop)");
+1229 put_new(Name, "5c", "pop top of stack to ESP (pop)");
+1230 put_new(Name, "5d", "pop top of stack to EBP (pop)");
+1231 put_new(Name, "5e", "pop top of stack to ESI (pop)");
+1232 put_new(Name, "5f", "pop top of stack to EDI (pop)");
+1233 
+1234 :(code)
+1235 void test_pop_r32() {
+1236   Mem.push_back(vma(0xbd000000));  // manually allocate memory
+1237   Reg[ESP].u = 0xbd000008;
+1238   write_mem_i32(0xbd000008, 0x0000000a);  // ..before this write
+1239   run(
+1240       "== code 0x1\n"  // code segment
+1241       // op     ModR/M  SIB   displacement  immediate
+1242       "  5b                                           \n"  // pop stack to EBX
+1243       "== data 0x2000\n"  // data segment
+1244       "0a 00 00 00\n"  // 0xa
+1245   );
+1246   CHECK_TRACE_CONTENTS(
+1247       "run: pop into EBX\n"
+1248       "run: popping value 0x0000000a\n"
+1249       "run: incrementing ESP to 0xbd00000c\n"
+1250   );
+1251 }
+1252 
+1253 :(before "End Single-Byte Opcodes")
+1254 case 0x58:
+1255 case 0x59:
+1256 case 0x5a:
+1257 case 0x5b:
+1258 case 0x5c:
+1259 case 0x5d:
+1260 case 0x5e:
+1261 case 0x5f: {  // pop stack into r32
+1262   const uint8_t reg = op & 0x7;
+1263   trace(Callstack_depth+1, "run") << "pop into " << rname(reg) << end();
+1264 //?   cerr << "pop from " << Reg[ESP].u << '\n';
+1265   Reg[reg].u = pop();
+1266 //?   cerr << "=> " << NUM(reg) << ": " << Reg[reg].u << '\n';
+1267   break;
+1268 }
+1269 :(code)
+1270 uint32_t pop() {
+1271   const uint32_t result = read_mem_u32(Reg[ESP].u);
+1272   trace(Callstack_depth+1, "run") << "popping value 0x" << HEXWORD << result << end();
+1273   Reg[ESP].u += 4;
+1274   trace(Callstack_depth+1, "run") << "incrementing ESP to 0x" << HEXWORD << Reg[ESP].u << end();
+1275   assert(Reg[ESP].u < AFTER_STACK);
+1276   return result;
+1277 }
+1278 
+1279 :(before "End Includes")
+1280 #include <climits>
+
+ + + diff --git a/html/linux/bootstrap/014indirect_addressing.cc.html b/html/linux/bootstrap/014indirect_addressing.cc.html new file mode 100644 index 00000000..27f28f32 --- /dev/null +++ b/html/linux/bootstrap/014indirect_addressing.cc.html @@ -0,0 +1,1067 @@ + + + + +Mu - linux/bootstrap/014indirect_addressing.cc + + + + + + + + + + +https://github.com/akkartik/mu/blob/main/linux/bootstrap/014indirect_addressing.cc +
+   1 //: operating on memory at the address provided by some register
+   2 //: we'll now start providing data in a separate segment
+   3 
+   4 void test_add_r32_to_mem_at_rm32() {
+   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
+  11       // ModR/M in binary: 00 (indirect mode) 011 (src EBX) 000 (dest EAX)
+  12       "== data 0x2000\n"
+  13       "01 00 00 00\n"  // 1
+  14   );
+  15   CHECK_TRACE_CONTENTS(
+  16       "run: add EBX to r/m32\n"
+  17       "run: effective address is 0x00002000 (EAX)\n"
+  18       "run: storing 0x00000011\n"
+  19   );
+  20 }
+  21 
+  22 :(before "End Mod Special-cases(addr)")
+  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;
+  28     break;
+  29   // End Mod 0 Special-cases(addr)
+  30   }
+  31   break;
+  32 
+  33 //:
+  34 
+  35 :(before "End Initialize Op Names")
+  36 put_new(Name, "03", "add rm32 to r32 (add)");
+  37 
+  38 :(code)
+  39 void test_add_mem_at_rm32_to_r32() {
+  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
+  46       // ModR/M in binary: 00 (indirect mode) 011 (src EBX) 000 (dest EAX)
+  47       "== data 0x2000\n"
+  48       "01 00 00 00\n"  // 1
+  49   );
+  50   CHECK_TRACE_CONTENTS(
+  51       "run: add r/m32 to EBX\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();
+  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;
+  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);
+  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();
+  76   break;
+  77 }
+  78 
+  79 :(code)
+  80 void test_add_mem_at_rm32_to_r32_signed_overflow() {
+  81   Reg[EAX].i = 0x2000;
+  82   Reg[EBX].i = INT32_MAX;
+  83   run(
+  84       "== code 0x1\n"
+  85       // op     ModR/M  SIB   displacement  immediate
+  86       "  03     18                                    \n" // add *EAX to EBX
+  87       // ModR/M in binary: 00 (indirect mode) 011 (src EBX) 000 (dest EAX)
+  88       "== data 0x2000\n"
+  89       "01 00 00 00\n"  // 1
+  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 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_rm32_to_r32_unsigned_overflow() {
+ 101   Reg[EAX].u = 0x2000;
+ 102   Reg[EBX].u = UINT32_MAX;
+ 103   run(
+ 104       "== code 0x1\n"
+ 105       // op     ModR/M  SIB   displacement  immediate
+ 106       "  03     18                                    \n" // add *EAX to EBX
+ 107       // ModR/M in binary: 00 (indirect mode) 011 (src EBX) 000 (dest EAX)
+ 108       "== data 0x2000\n"
+ 109       "01 00 00 00\n"
+ 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 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_rm32_to_r32_unsigned_and_signed_overflow() {
+ 121   Reg[EAX].u = 0x2000;
+ 122   Reg[EBX].i = INT32_MIN;
+ 123   run(
+ 124       "== code 0x1\n"
+ 125       // op     ModR/M  SIB   displacement  immediate
+ 126       "  03     18                                    \n" // add *EAX to EBX
+ 127       // ModR/M in binary: 00 (indirect mode) 011 (src EBX) 000 (dest EAX)
+ 128       "== data 0x2000\n"
+ 129       "00 00 00 80\n"  // INT32_MIN
+ 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 0x80000000\n"
+ 135       "run: SF=0; ZF=1; CF=1; OF=1\n"
+ 136       "run: storing 0x00000000\n"
+ 137   );
+ 138 }
+ 139 
+ 140 //:: subtract
+ 141 
+ 142 :(code)
+ 143 void test_subtract_r32_from_mem_at_rm32() {
+ 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
+ 150       // ModR/M in binary: 00 (indirect mode) 011 (src EBX) 000 (dest EAX)
+ 151       "== data 0x2000\n"
+ 152       "0a 00 00 00\n"  // 0xa
+ 153   );
+ 154   CHECK_TRACE_CONTENTS(
+ 155       "run: subtract EBX from r/m32\n"
+ 156       "run: effective address is 0x00002000 (EAX)\n"
+ 157       "run: storing 0x00000009\n"
+ 158   );
+ 159 }
+ 160 
+ 161 //:
+ 162 
+ 163 :(before "End Initialize Op Names")
+ 164 put_new(Name, "2b", "subtract rm32 from r32 (sub)");
+ 165 
+ 166 :(code)
+ 167 void test_subtract_mem_at_rm32_from_r32() {
+ 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
+ 174       // ModR/M in binary: 00 (indirect mode) 011 (src EBX) 000 (dest EAX)
+ 175       "== data 0x2000\n"
+ 176       "01 00 00 00\n"  // 1
+ 177   );
+ 178   CHECK_TRACE_CONTENTS(
+ 179       "run: subtract r/m32 from EBX\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();
+ 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;
+ 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);
+ 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();
+ 204   break;
+ 205 }
+ 206 
+ 207 :(code)
+ 208 void test_subtract_mem_at_rm32_from_r32_signed_overflow() {
+ 209   Reg[EAX].i = 0x2000;
+ 210   Reg[EBX].i = INT32_MIN;
+ 211   run(
+ 212       "== code 0x1\n"
+ 213       // op     ModR/M  SIB   displacement  immediate
+ 214       "  2b     18                                    \n"  // subtract *EAX from EBX
+ 215       // ModR/M in binary: 00 (indirect mode) 011 (src EBX) 000 (dest EAX)
+ 216       "== data 0x2000\n"
+ 217       "ff ff ff 7f\n"  // INT32_MAX
+ 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 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_rm32_from_r32_unsigned_overflow() {
+ 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
+ 235       // ModR/M in binary: 00 (indirect mode) 011 (src EBX) 000 (dest EAX)
+ 236       "== data 0x2000\n"
+ 237       "01 00 00 00\n"  // 1
+ 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 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_rm32_from_r32_signed_and_unsigned_overflow() {
+ 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
+ 255       // ModR/M in binary: 00 (indirect mode) 011 (src EBX) 000 (dest EAX)
+ 256       "== data 0x2000\n"
+ 257       "00 00 00 80\n"  // INT32_MIN
+ 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 0x80000000\n"
+ 263       "run: SF=1; ZF=0; CF=1; OF=1\n"
+ 264       "run: storing 0x80000000\n"
+ 265   );
+ 266 }
+ 267 
+ 268 //:: and
+ 269 :(code)
+ 270 void test_and_r32_with_mem_at_rm32() {
+ 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
+ 277       // ModR/M in binary: 00 (indirect mode) 011 (src EBX) 000 (dest EAX)
+ 278       "== data 0x2000\n"
+ 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"
+ 284       "run: storing 0x0000000d\n"
+ 285   );
+ 286 }
+ 287 
+ 288 //:
+ 289 
+ 290 :(before "End Initialize Op Names")
+ 291 put_new(Name, "23", "r32 = bitwise AND of r32 with rm32 (and)");
+ 292 
+ 293 :(code)
+ 294 void test_and_mem_at_rm32_with_r32() {
+ 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
+ 301       // ModR/M in binary: 00 (indirect mode) 011 (src EBX) 000 (dest EAX)
+ 302       "== data 0x2000\n"
+ 303       "ff 00 00 00\n"  // 0xff
+ 304   );
+ 305   CHECK_TRACE_CONTENTS(
+ 306       "run: and r/m32 with EBX\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();
+ 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();
+ 327   break;
+ 328 }
+ 329 
+ 330 //:: or
+ 331 
+ 332 :(code)
+ 333 void test_or_r32_with_mem_at_rm32() {
+ 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
+ 340       // ModR/M in binary: 00 (indirect mode) 011 (src EBX) 000 (dest EAX)
+ 341       "== data 0x2000\n"
+ 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"
+ 347       "run: storing 0xaabbccdd\n"
+ 348   );
+ 349 }
+ 350 
+ 351 //:
+ 352 
+ 353 :(before "End Initialize Op Names")
+ 354 put_new(Name, "0b", "r32 = bitwise OR of r32 with rm32 (or)");
+ 355 
+ 356 :(code)
+ 357 void test_or_mem_at_rm32_with_r32() {
+ 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
+ 364       // ModR/M in binary: 00 (indirect mode) 011 (src EBX) 000 (dest EAX)
+ 365       "== data 0x2000\n"
+ 366       "0d 0c 0b 0a\n"  // 0x0a0b0c0d
+ 367   );
+ 368   CHECK_TRACE_CONTENTS(
+ 369       "run: or r/m32 with EBX\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();
+ 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();
+ 390   break;
+ 391 }
+ 392 
+ 393 //:: xor
+ 394 
+ 395 :(code)
+ 396 void test_xor_r32_with_mem_at_rm32() {
+ 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
+ 403       "== data 0x2000\n"
+ 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"
+ 409       "run: storing 0x0a0bccdd\n"
+ 410   );
+ 411 }
+ 412 
+ 413 //:
+ 414 
+ 415 :(before "End Initialize Op Names")
+ 416 put_new(Name, "33", "r32 = bitwise XOR of r32 with rm32 (xor)");
+ 417 
+ 418 :(code)
+ 419 void test_xor_mem_at_rm32_with_r32() {
+ 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
+ 426       // ModR/M in binary: 00 (indirect mode) 011 (src EBX) 000 (dest EAX)
+ 427       "== data 0x2000\n"
+ 428       "0d 0c 0b 0a\n"  // 0x0a0b0c0d
+ 429   );
+ 430   CHECK_TRACE_CONTENTS(
+ 431       "run: xor r/m32 with EBX\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();
+ 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();
+ 452   break;
+ 453 }
+ 454 
+ 455 //:: not
+ 456 
+ 457 :(code)
+ 458 void test_not_of_mem_at_rm32() {
+ 459   Reg[EBX].i = 0x2000;
+ 460   run(
+ 461       "== code 0x1\n"
+ 462       // op     ModR/M  SIB   displacement  immediate
+ 463       "  f7     13                                    \n"  // not *EBX
+ 464       // ModR/M in binary: 00 (indirect mode) 010 (subop not) 011 (dest EBX)
+ 465       "== data 0x2000\n"
+ 466       "ff 00 0f 0f\n"  // 0x0f0f00ff
+ 467   );
+ 468   CHECK_TRACE_CONTENTS(
+ 469       "run: operate on r/m32\n"
+ 470       "run: effective address is 0x00002000 (EBX)\n"
+ 471       "run: subop: not\n"
+ 472       "run: storing 0xf0f0ff00\n"
+ 473   );
+ 474 }
+ 475 
+ 476 //:: compare (cmp)
+ 477 
+ 478 :(code)
+ 479 void test_compare_mem_at_rm32_with_r32_greater() {
+ 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
+ 486       // ModR/M in binary: 00 (indirect mode) 011 (src EBX) 000 (dest EAX)
+ 487       "== data 0x2000\n"
+ 488       "0d 0c 0b 0a\n"  // 0x0a0b0c0d
+ 489   );
+ 490   CHECK_TRACE_CONTENTS(
+ 491       "run: compare r/m32 with EBX\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_rm32_with_r32_lesser() {
+ 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
+ 505       // ModR/M in binary: 00 (indirect mode) 011 (src EBX) 000 (dest EAX)
+ 506       "== data 0x2000\n"
+ 507       "07 0c 0b 0a\n"  // 0x0a0b0c0d
+ 508   );
+ 509   CHECK_TRACE_CONTENTS(
+ 510       "run: compare r/m32 with EBX\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_rm32_with_r32_equal() {
+ 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
+ 524       // ModR/M in binary: 00 (indirect mode) 011 (src EBX) 000 (dest EAX)
+ 525       "== data 0x2000\n"
+ 526       "0d 0c 0b 0a\n"  // 0x0a0b0c0d
+ 527   );
+ 528   CHECK_TRACE_CONTENTS(
+ 529       "run: compare r/m32 with EBX\n"
+ 530       "run: effective address is 0x00002000 (EAX)\n"
+ 531       "run: SF=0; ZF=1; CF=0; OF=0\n"
+ 532   );
+ 533 }
+ 534 
+ 535 //:
+ 536 
+ 537 :(before "End Initialize Op Names")
+ 538 put_new(Name, "3b", "compare: set SF if r32 < rm32 (cmp)");
+ 539 
+ 540 :(code)
+ 541 void test_compare_r32_with_mem_at_rm32_greater() {
+ 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
+ 548       // ModR/M in binary: 00 (indirect mode) 011 (lhs EBX) 000 (rhs EAX)
+ 549       "== data 0x2000\n"
+ 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"
+ 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();
+ 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;
+ 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);
+ 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();
+ 575   break;
+ 576 }
+ 577 
+ 578 :(code)
+ 579 void test_compare_r32_with_mem_at_rm32_lesser_unsigned_and_signed() {
+ 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
+ 586       // ModR/M in binary: 00 (indirect mode) 011 (lhs EBX) 000 (rhs EAX)
+ 587       "== data 0x2000\n"
+ 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 0x0a0b0c0d\n"
+ 594       "run: SF=1; ZF=0; CF=1; OF=0\n"
+ 595   );
+ 596 }
+ 597 
+ 598 void test_compare_r32_with_mem_at_rm32_lesser_unsigned_and_signed_due_to_overflow() {
+ 599   Reg[EAX].i = 0x2000;
+ 600   Reg[EBX].i = INT32_MAX;
+ 601   run(
+ 602       "== code 0x1\n"
+ 603       // op     ModR/M  SIB   displacement  immediate
+ 604       "  3b     18                                    \n"  // compare EBX with *EAX
+ 605       // ModR/M in binary: 00 (indirect mode) 011 (lhs EBX) 000 (rhs EAX)
+ 606       "== data 0x2000\n"
+ 607       "00 00 00 80\n"  // INT32_MIN
+ 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 0x80000000\n"
+ 613       "run: SF=1; ZF=0; CF=1; OF=1\n"
+ 614   );
+ 615 }
+ 616 
+ 617 void test_compare_r32_with_mem_at_rm32_lesser_signed() {
+ 618   Reg[EAX].i = 0x2000;
+ 619   Reg[EBX].i = -1;
+ 620   run(
+ 621       "== code 0x1\n"
+ 622       // op     ModR/M  SIB   displacement  immediate
+ 623       "  3b     18                                    \n"  // compare EBX with *EAX
+ 624       // ModR/M in binary: 00 (indirect mode) 011 (lhs EBX) 000 (rhs EAX)
+ 625       "== data 0x2000\n"
+ 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 0x00000001\n"
+ 632       "run: SF=1; ZF=0; CF=0; OF=0\n"
+ 633   );
+ 634 }
+ 635 
+ 636 void test_compare_r32_with_mem_at_rm32_lesser_unsigned() {
+ 637   Reg[EAX].i = 0x2000;
+ 638   Reg[EBX].i = 1;
+ 639   run(
+ 640       "== code 0x1\n"
+ 641       // op     ModR/M  SIB   displacement  immediate
+ 642       "  3b     18                                    \n"  // compare EBX with *EAX
+ 643       // ModR/M in binary: 00 (indirect mode) 011 (lhs EBX) 000 (rhs EAX)
+ 644       "== data 0x2000\n"
+ 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 0xffffffff\n"
+ 651       "run: SF=0; ZF=0; CF=1; OF=0\n"
+ 652   );
+ 653 }
+ 654 
+ 655 void test_compare_r32_with_mem_at_rm32_equal() {
+ 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
+ 662       // ModR/M in binary: 00 (indirect mode) 011 (lhs EBX) 000 (rhs EAX)
+ 663       "== data 0x2000\n"
+ 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"
+ 669       "run: SF=0; ZF=1; CF=0; OF=0\n"
+ 670   );
+ 671 }
+ 672 
+ 673 //:: copy (mov)
+ 674 
+ 675 void test_copy_r32_to_mem_at_rm32() {
+ 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 EBX) 000 (dest EAX)
+ 683   );
+ 684   CHECK_TRACE_CONTENTS(
+ 685       "run: copy EBX to r/m32\n"
+ 686       "run: effective address is 0x00000060 (EAX)\n"
+ 687       "run: storing 0x000000af\n"
+ 688   );
+ 689 }
+ 690 
+ 691 //:
+ 692 
+ 693 :(before "End Initialize Op Names")
+ 694 put_new(Name, "8b", "copy rm32 to r32 (mov)");
+ 695 
+ 696 :(code)
+ 697 void test_copy_mem_at_rm32_to_r32() {
+ 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
+ 703       "== data 0x2000\n"
+ 704       "af 00 00 00\n"  // 0xaf
+ 705   );
+ 706   CHECK_TRACE_CONTENTS(
+ 707       "run: copy r/m32 to EBX\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();
+ 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();
+ 721   break;
+ 722 }
+ 723 
+ 724 //:: jump
+ 725 
+ 726 :(code)
+ 727 void test_jump_mem_at_rm32() {
+ 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
+ 733       // ModR/M in binary: 00 (indirect mode) 100 (jump to r/m32) 000 (src EAX)
+ 734       "  b8                                 00 00 00 01\n"
+ 735       "  b8                                 00 00 00 02\n"
+ 736       "== data 0x2000\n"
+ 737       "08 00 00 00\n"  // 0x8
+ 738   );
+ 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"
+ 743       "run: jumping to 0x00000008\n"
+ 744       "run: 0x00000008 opcode: b8\n"
+ 745   );
+ 746   CHECK_TRACE_DOESNT_CONTAIN("run: 0x00000003 opcode: b8");
+ 747 }
+ 748 
+ 749 :(before "End Op ff Subops")
+ 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();
+ 755   break;
+ 756 }
+ 757 
+ 758 //:: push
+ 759 
+ 760 :(code)
+ 761 void test_push_mem_at_rm32() {
+ 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
+ 769       "== data 0x2000\n"
+ 770       "af 00 00 00\n"  // 0xaf
+ 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"
+ 776       "run: pushing value 0x000000af\n"
+ 777   );
+ 778 }
+ 779 
+ 780 :(before "End Op ff Subops")
+ 781 case 6: {  // push r/m32 to stack
+ 782   trace(Callstack_depth+1, "run") << "push r/m32" << end();
+ 783   const int32_t* val = effective_address(modrm);
+ 784   push(*val);
+ 785   break;
+ 786 }
+ 787 
+ 788 //:: pop
+ 789 
+ 790 :(before "End Initialize Op Names")
+ 791 put_new(Name, "8f", "pop top of stack to rm32 (pop)");
+ 792 
+ 793 :(code)
+ 794 void test_pop_mem_at_rm32() {
+ 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
+ 803       // ModR/M in binary: 00 (indirect mode) 000 (pop r/m32) 000 (dest EAX)
+ 804   );
+ 805   CHECK_TRACE_CONTENTS(
+ 806       "run: pop into r/m32\n"
+ 807       "run: effective address is 0x00000060 (EAX)\n"
+ 808       "run: popping value 0x00000030\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();
+ 816   const uint8_t subop = (modrm>>3)&0x7;
+ 817   switch (subop) {
+ 818     case 0: {
+ 819       trace(Callstack_depth+1, "run") << "pop into r/m32" << end();
+ 820       int32_t* dest = effective_address(modrm);
+ 821       *dest = pop();  // Write multiple elements of vector<uint8_t> at once. Assumes sizeof(int) == 4 on the host as well.
+ 822       break;
+ 823     }
+ 824   }
+ 825   break;
+ 826 }
+ 827 
+ 828 //:: special-case for loading address from disp32 rather than register
+ 829 
+ 830 :(code)
+ 831 void test_add_r32_to_mem_at_displacement() {
+ 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
+ 837       // ModR/M in binary: 00 (indirect mode) 011 (src EBX) 101 (dest in disp32)
+ 838       "== data 0x2000\n"
+ 839       "01 00 00 00\n"  // 1
+ 840   );
+ 841   CHECK_TRACE_CONTENTS(
+ 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();
+ 852   break;
+ 853 
+ 854 //:
+ 855 
+ 856 :(code)
+ 857 void test_add_r32_to_mem_at_rm32_plus_disp8() {
+ 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)
+ 864       // ModR/M in binary: 01 (indirect+disp8 mode) 011 (src EBX) 000 (dest EAX)
+ 865       "== data 0x2000\n"
+ 866       "01 00 00 00\n"  // 1
+ 867   );
+ 868   CHECK_TRACE_CONTENTS(
+ 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   );
+ 874 }
+ 875 
+ 876 :(before "End Mod Special-cases(addr)")
+ 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();
+ 882     break;
+ 883   // End Mod 1 Special-cases(addr)
+ 884   }
+ 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();
+ 889   }
+ 890   else {
+ 891     trace(Callstack_depth+1, "run") << "null address; skipping displacement" << end();
+ 892   }
+ 893   break;
+ 894 }
+ 895 
+ 896 :(code)
+ 897 void test_add_r32_to_mem_at_rm32_plus_negative_disp8() {
+ 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)
+ 904       // ModR/M in binary: 01 (indirect+disp8 mode) 011 (src EBX) 000 (dest EAX)
+ 905       "== data 0x2000\n"
+ 906       "01 00 00 00\n"  // 1
+ 907   );
+ 908   CHECK_TRACE_CONTENTS(
+ 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   );
+ 914 }
+ 915 
+ 916 //:
+ 917 
+ 918 :(code)
+ 919 void test_add_r32_to_mem_at_rm32_plus_disp32() {
+ 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)
+ 926       // ModR/M in binary: 10 (indirect+disp32 mode) 011 (src EBX) 000 (dest EAX)
+ 927       "== data 0x2000\n"
+ 928       "01 00 00 00\n"  // 1
+ 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"
+ 934       "run: storing 0x00000011\n"
+ 935   );
+ 936 }
+ 937 
+ 938 :(before "End Mod Special-cases(addr)")
+ 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();
+ 944     break;
+ 945   // End Mod 2 Special-cases(addr)
+ 946   }
+ 947   int32_t displacement = static_cast<int32_t>(next32());
+ 948   if (addr > 0) {
+ 949     addr += displacement;
+ 950     trace(Callstack_depth+1, "run") << "effective address is 0x" << HEXWORD << addr << " (after adding disp32)" << end();
+ 951   }
+ 952   else {
+ 953     trace(Callstack_depth+1, "run") << "null address; skipping displacement" << end();
+ 954   }
+ 955   break;
+ 956 }
+ 957 
+ 958 :(code)
+ 959 void test_add_r32_to_mem_at_rm32_plus_negative_disp32() {
+ 960   Reg[EBX].i = 0x10;  // source
+ 961   Reg[EAX].i = 0x2001;  // dest
+ 962   run(
+ 963       "== code 0x1\n"
+ 964       // op     ModR/M  SIB   displacement  immediate
+ 965       "  01     98            ff ff ff ff             \n"  // add EBX to *(EAX-1)
+ 966       // ModR/M in binary: 10 (indirect+disp32 mode) 011 (src EBX) 000 (dest EAX)
+ 967       "== data 0x2000\n"
+ 968       "01 00 00 00\n"  // 1
+ 969   );
+ 970   CHECK_TRACE_CONTENTS(
+ 971       "run: add EBX to r/m32\n"
+ 972       "run: effective address is initially 0x00002001 (EAX)\n"
+ 973       "run: effective address is 0x00002000 (after adding disp32)\n"
+ 974       "run: storing 0x00000011\n"
+ 975   );
+ 976 }
+ 977 
+ 978 //:: copy address (lea)
+ 979 
+ 980 :(before "End Initialize Op Names")
+ 981 put_new(Name, "8d", "copy address in rm32 into r32 (lea)");
+ 982 
+ 983 :(code)
+ 984 void test_copy_address() {
+ 985   Reg[EAX].u = 0x2000;
+ 986   run(
+ 987       "== code 0x1\n"
+ 988       // op     ModR/M  SIB   displacement  immediate
+ 989       "  8d     18                                    \n"  // copy address in EAX into EBX
+ 990       // ModR/M in binary: 00 (indirect mode) 011 (dest EBX) 000 (src EAX)
+ 991   );
+ 992   CHECK_TRACE_CONTENTS(
+ 993       "run: copy address into EBX\n"
+ 994       "run: effective address is 0x00002000 (EAX)\n"
+ 995   );
+ 996 }
+ 997 
+ 998 :(before "End Single-Byte Opcodes")
+ 999 case 0x8d: {  // copy address of m32 to r32
+1000   const uint8_t modrm = next();
+1001   const uint8_t arg1 = (modrm>>3)&0x7;
+1002   trace(Callstack_depth+1, "run") << "copy address into " << rname(arg1) << end();
+1003   Reg[arg1].u = effective_address_number(modrm);
+1004   break;
+1005 }
+
+ + + diff --git a/html/linux/bootstrap/015immediate_addressing.cc.html b/html/linux/bootstrap/015immediate_addressing.cc.html new file mode 100644 index 00000000..6d5b873b --- /dev/null +++ b/html/linux/bootstrap/015immediate_addressing.cc.html @@ -0,0 +1,1374 @@ + + + + +Mu - linux/bootstrap/015immediate_addressing.cc + + + + + + + + + + +https://github.com/akkartik/mu/blob/main/linux/bootstrap/015immediate_addressing.cc +
+   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)");
+   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;
+  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);
+  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();
+  23   break;
+  24 }
+  25 
+  26 :(code)
+  27 void test_add_imm32_to_EAX_signed_overflow() {
+  28   Reg[EAX].i = INT32_MAX;
+  29   run(
+  30       "== code 0x1\n"
+  31       // op     ModR/M  SIB   displacement  immediate
+  32       "  05                                 01 00 00 00 \n" // add 1 to EAX
+  33   );
+  34   CHECK_TRACE_CONTENTS(
+  35       "run: add imm32 0x00000001 to EAX\n"
+  36       "run: SF=1; ZF=0; CF=0; OF=1\n"
+  37       "run: storing 0x80000000\n"
+  38   );
+  39 }
+  40 
+  41 void test_add_imm32_to_EAX_unsigned_overflow() {
+  42   Reg[EAX].u = UINT32_MAX;
+  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
+  48   );
+  49   CHECK_TRACE_CONTENTS(
+  50       "run: add imm32 0x00000001 to EAX\n"
+  51       "run: SF=0; ZF=1; CF=1; OF=0\n"
+  52       "run: storing 0x00000000\n"
+  53   );
+  54 }
+  55 
+  56 void test_add_imm32_to_EAX_unsigned_and_signed_overflow() {
+  57   Reg[EAX].i = INT32_MIN;
+  58   run(
+  59       "== code 0x1\n"
+  60       // op     ModR/M  SIB   displacement  immediate
+  61       "  05                                 00 00 00 80 \n" // add 0x80000000 to EAX
+  62   );
+  63   CHECK_TRACE_CONTENTS(
+  64       "run: add imm32 0x80000000 to EAX\n"
+  65       "run: SF=0; ZF=1; CF=1; OF=1\n"
+  66       "run: storing 0x00000000\n"
+  67   );
+  68 }
+  69 
+  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)");
+  74 
+  75 :(code)
+  76 void test_add_imm32_to_r32() {
+  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
+  82       // ModR/M in binary: 11 (direct mode) 000 (subop add) 011 (dest EBX)
+  83   );
+  84   CHECK_TRACE_CONTENTS(
+  85       "run: combine r/m32 with imm32\n"
+  86       "run: r/m32 is EBX\n"
+  87       "run: imm32 is 0x0d0c0b0a\n"
+  88       "run: subop add\n"
+  89       "run: storing 0x0d0c0b0b\n"
+  90   );
+  91 }
+  92 
+  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();
+  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();
+ 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);
+ 107     int64_t signed_full_result = static_cast<int64_t>(*signed_arg1) + signed_arg2;
+ 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();
+ 116     *signed_arg1 = signed_result;
+ 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';
+ 123     exit(1);
+ 124   }
+ 125   break;
+ 126 }
+ 127 
+ 128 :(code)
+ 129 void test_add_imm32_to_r32_signed_overflow() {
+ 130   Reg[EBX].i = INT32_MAX;
+ 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
+ 135       // ModR/M in binary: 11 (direct mode) 000 (subop add) 011 (dest EBX)
+ 136   );
+ 137   CHECK_TRACE_CONTENTS(
+ 138       "run: combine r/m32 with imm32\n"
+ 139       "run: r/m32 is EBX\n"
+ 140       "run: imm32 is 0x00000001\n"
+ 141       "run: subop add\n"
+ 142       "run: SF=1; ZF=0; CF=0; OF=1\n"
+ 143       "run: storing 0x80000000\n"
+ 144   );
+ 145 }
+ 146 
+ 147 void test_add_imm32_to_r32_unsigned_overflow() {
+ 148   Reg[EBX].u = UINT32_MAX;
+ 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
+ 153       // ModR/M in binary: 11 (direct mode) 011 (subop add) 011 (dest EBX)
+ 154   );
+ 155   CHECK_TRACE_CONTENTS(
+ 156       "run: combine r/m32 with imm32\n"
+ 157       "run: r/m32 is EBX\n"
+ 158       "run: imm32 is 0x00000001\n"
+ 159       "run: subop add\n"
+ 160       "run: SF=0; ZF=1; CF=1; OF=0\n"
+ 161       "run: storing 0x00000000\n"
+ 162   );
+ 163 }
+ 164 
+ 165 void test_add_imm32_to_r32_unsigned_and_signed_overflow() {
+ 166   Reg[EBX].i = INT32_MIN;
+ 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
+ 171       // ModR/M in binary: 11 (direct mode) 011 (subop add) 011 (dest EBX)
+ 172   );
+ 173   CHECK_TRACE_CONTENTS(
+ 174       "run: combine r/m32 with imm32\n"
+ 175       "run: r/m32 is EBX\n"
+ 176       "run: imm32 is 0x80000000\n"
+ 177       "run: subop add\n"
+ 178       "run: SF=0; ZF=1; CF=1; OF=1\n"
+ 179       "run: storing 0x00000000\n"
+ 180   );
+ 181 }
+ 182 
+ 183 //:
+ 184 
+ 185 :(code)
+ 186 void test_add_imm32_to_mem_at_rm32() {
+ 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
+ 192       // ModR/M in binary: 00 (indirect mode) 000 (subop add) 011 (dest EBX)
+ 193       "== data 0x2000\n"
+ 194       "01 00 00 00\n"  // 1
+ 195   );
+ 196   CHECK_TRACE_CONTENTS(
+ 197       "run: combine r/m32 with imm32\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"
+ 202   );
+ 203 }
+ 204 
+ 205 //:: subtract
+ 206 
+ 207 :(before "End Initialize Op Names")
+ 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(
+ 214       "== code 0x1\n"
+ 215       // op     ModR/M  SIB   displacement  immediate
+ 216       "  2d                                 0a 0b 0c 0d \n"  // subtract 0x0d0c0b0a from EAX
+ 217   );
+ 218   CHECK_TRACE_CONTENTS(
+ 219       "run: subtract imm32 0x0d0c0b0a from EAX\n"
+ 220       "run: storing 0x000000a0\n"
+ 221   );
+ 222 }
+ 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;
+ 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);
+ 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();
+ 241   break;
+ 242 }
+ 243 
+ 244 :(code)
+ 245 void test_subtract_imm32_from_EAX_signed_overflow() {
+ 246   Reg[EAX].i = INT32_MIN;
+ 247   run(
+ 248       "== code 0x1\n"
+ 249       // op     ModR/M  SIB   displacement  immediate
+ 250       "  2d                                 01 00 00 00 \n"  // subtract 1 from EAX
+ 251   );
+ 252   CHECK_TRACE_CONTENTS(
+ 253       "run: subtract imm32 0x00000001 from EAX\n"
+ 254       "run: SF=0; ZF=0; CF=0; OF=1\n"
+ 255       "run: storing 0x7fffffff\n"  // INT32_MAX
+ 256   );
+ 257 }
+ 258 
+ 259 void test_subtract_imm32_from_EAX_unsigned_overflow() {
+ 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
+ 265   );
+ 266   CHECK_TRACE_CONTENTS(
+ 267       "run: subtract imm32 0x00000001 from EAX\n"
+ 268       "run: SF=1; ZF=0; CF=1; OF=0\n"
+ 269       "run: storing 0xffffffff\n"
+ 270   );
+ 271 }
+ 272 
+ 273 void test_subtract_imm32_from_EAX_signed_and_unsigned_overflow() {
+ 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 INT32_MIN from EAX
+ 279   );
+ 280   CHECK_TRACE_CONTENTS(
+ 281       "run: subtract imm32 0x80000000 from EAX\n"
+ 282       "run: SF=1; ZF=0; CF=1; OF=1\n"
+ 283       "run: storing 0x80000000\n"
+ 284   );
+ 285 }
+ 286 
+ 287 //:
+ 288 
+ 289 void test_subtract_imm32_from_mem_at_rm32() {
+ 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
+ 295       // ModR/M in binary: 00 (indirect mode) 101 (subop subtract) 011 (dest EBX)
+ 296       "== data 0x2000\n"
+ 297       "0a 00 00 00\n"  // 0xa
+ 298   );
+ 299   CHECK_TRACE_CONTENTS(
+ 300       "run: combine r/m32 with imm32\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"
+ 305   );
+ 306 }
+ 307 
+ 308 :(before "End Op 81 Subops")
+ 309 case 5: {
+ 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);
+ 314   int64_t signed_full_result = static_cast<int64_t>(*signed_arg1) - signed_arg2;
+ 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();
+ 323   *signed_arg1 = signed_result;
+ 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_rm32_signed_overflow() {
+ 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 INT32_MAX from *EBX
+ 335       // ModR/M in binary: 00 (indirect mode) 101 (subop subtract) 011 (dest EBX)
+ 336       "== data 0x2000\n"
+ 337       "00 00 00 80\n"  // INT32_MIN
+ 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 0x80000000\n"
+ 343       "run: imm32 is 0x7fffffff\n"
+ 344       "run: subop subtract\n"
+ 345       "run: SF=0; ZF=0; CF=0; OF=1\n"
+ 346       "run: storing 0x00000001\n"
+ 347   );
+ 348 }
+ 349 
+ 350 void test_subtract_imm32_from_mem_at_rm32_unsigned_overflow() {
+ 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
+ 356       // ModR/M in binary: 00 (indirect mode) 101 (subop subtract) 011 (dest EBX)
+ 357       "== data 0x2000\n"
+ 358       "00 00 00 00\n"  // 0
+ 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 0x00000000\n"
+ 364       "run: imm32 is 0x00000001\n"
+ 365       "run: subop subtract\n"
+ 366       "run: SF=1; ZF=0; CF=1; OF=0\n"
+ 367       "run: storing 0xffffffff\n"
+ 368   );
+ 369 }
+ 370 
+ 371 void test_subtract_imm32_from_mem_at_rm32_signed_and_unsigned_overflow() {
+ 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 INT32_MIN from *EBX
+ 377       // ModR/M in binary: 00 (indirect mode) 101 (subop subtract) 011 (dest EBX)
+ 378       "== data 0x2000\n"
+ 379       "00 00 00 00\n"  // 0
+ 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 0x00000000\n"
+ 385       "run: imm32 is 0x80000000\n"
+ 386       "run: subop subtract\n"
+ 387       "run: SF=1; ZF=0; CF=1; OF=1\n"
+ 388       "run: storing 0x80000000\n"
+ 389   );
+ 390 }
+ 391 
+ 392 //:
+ 393 
+ 394 void test_subtract_imm32_from_r32() {
+ 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
+ 400       // ModR/M in binary: 11 (direct mode) 101 (subop subtract) 011 (dest EBX)
+ 401   );
+ 402   CHECK_TRACE_CONTENTS(
+ 403       "run: combine r/m32 with imm32\n"
+ 404       "run: r/m32 is EBX\n"
+ 405       "run: imm32 is 0x00000001\n"
+ 406       "run: subop subtract\n"
+ 407       "run: storing 0x00000009\n"
+ 408   );
+ 409 }
+ 410 
+ 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)");
+ 415 
+ 416 :(code)
+ 417 void test_shift_left_r32_with_imm8() {
+ 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
+ 423       // ModR/M in binary: 11 (direct mode) 100 (subop shift left) 011 (dest EBX)
+ 424   );
+ 425   CHECK_TRACE_CONTENTS(
+ 426       "run: operate on r/m32\n"
+ 427       "run: r/m32 is EBX\n"
+ 428       "run: subop: shift left by CL bits\n"
+ 429       "run: storing 0x0000001a\n"
+ 430   );
+ 431 }
+ 432 
+ 433 :(before "End Single-Byte Opcodes")
+ 434 case 0xc1: {
+ 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;
+ 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);
+ 448     }
+ 449     *arg1 = (*arg1 << count);
+ 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();
+ 455     break;
+ 456   }
+ 457   // End Op c1 Subops
+ 458   default:
+ 459     cerr << "unrecognized subop for opcode c1: " << NUM(subop) << '\n';
+ 460     exit(1);
+ 461   }
+ 462   break;
+ 463 }
+ 464 
+ 465 //:: shift right arithmetic
+ 466 
+ 467 :(code)
+ 468 void test_shift_right_arithmetic_r32_with_imm8() {
+ 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
+ 474       // ModR/M in binary: 11 (direct mode) 111 (subop shift right arithmetic) 011 (dest EBX)
+ 475   );
+ 476   CHECK_TRACE_CONTENTS(
+ 477       "run: operate on r/m32\n"
+ 478       "run: r/m32 is EBX\n"
+ 479       "run: subop: shift right by CL bits, while preserving sign\n"
+ 480       "run: storing 0x0000000d\n"
+ 481   );
+ 482 }
+ 483 
+ 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;
+ 488   int32_t result = (*arg1 >> count);
+ 489   ZF = (*arg1 == 0);
+ 490   SF = (*arg1 < 0);
+ 491   // OF is only defined if count is 1
+ 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();
+ 496   *arg1 = result;
+ 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(
+ 505       "== code 0x1\n"
+ 506       // op     ModR/M  SIB   displacement  immediate
+ 507       "  c1     fb                          01          \n"  // shift EBX right by 1 bit
+ 508       // ModR/M in binary: 11 (direct mode) 111 (subop shift right arithmetic) 011 (dest EBX)
+ 509   );
+ 510   CHECK_TRACE_CONTENTS(
+ 511       "run: operate on r/m32\n"
+ 512       "run: r/m32 is EBX\n"
+ 513       "run: subop: shift right by CL bits, while preserving sign\n"
+ 514       // result: 13
+ 515       "run: storing 0x0000000d\n"
+ 516   );
+ 517 }
+ 518 
+ 519 :(code)
+ 520 void test_shift_right_arithmetic_negative_r32_with_imm8() {
+ 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
+ 526       // ModR/M in binary: 11 (direct mode) 111 (subop shift right arithmetic) 011 (dest EBX)
+ 527   );
+ 528   CHECK_TRACE_CONTENTS(
+ 529       "run: operate on r/m32\n"
+ 530       "run: r/m32 is EBX\n"
+ 531       "run: subop: shift right by CL bits, while preserving sign\n"
+ 532       // result: -2
+ 533       "run: storing 0xfffffffe\n"
+ 534   );
+ 535 }
+ 536 
+ 537 //:: shift right logical
+ 538 
+ 539 :(code)
+ 540 void test_shift_right_logical_r32_with_imm8() {
+ 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
+ 546       // ModR/M in binary: 11 (direct mode) 101 (subop shift right logical) 011 (dest EBX)
+ 547   );
+ 548   CHECK_TRACE_CONTENTS(
+ 549       "run: operate on r/m32\n"
+ 550       "run: r/m32 is EBX\n"
+ 551       "run: subop: shift right by CL bits, while padding zeroes\n"
+ 552       "run: storing 0x0000000d\n"
+ 553   );
+ 554 }
+ 555 
+ 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;
+ 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);
+ 565   }
+ 566   uint32_t* uarg1 = reinterpret_cast<uint32_t*>(arg1);
+ 567   *uarg1 = (*uarg1 >> count);
+ 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();
+ 574   break;
+ 575 }
+ 576 
+ 577 :(code)
+ 578 void test_shift_right_logical_odd_r32_with_imm8() {
+ 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
+ 584   );
+ 585   CHECK_TRACE_CONTENTS(
+ 586       "run: operate on r/m32\n"
+ 587       "run: r/m32 is EBX\n"
+ 588       "run: subop: shift right by CL bits, while padding zeroes\n"
+ 589       // result: 13
+ 590       "run: storing 0x0000000d\n"
+ 591   );
+ 592 }
+ 593 
+ 594 :(code)
+ 595 void test_shift_right_logical_negative_r32_with_imm8() {
+ 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
+ 601       // ModR/M in binary: 11 (direct mode) 101 (subop shift right logical) 011 (dest EBX)
+ 602   );
+ 603   CHECK_TRACE_CONTENTS(
+ 604       "run: operate on r/m32\n"
+ 605       "run: r/m32 is EBX\n"
+ 606       "run: subop: shift right by CL bits, while padding zeroes\n"
+ 607       "run: storing 0x7ffffffe\n"
+ 608   );
+ 609 }
+ 610 
+ 611 //:: and
+ 612 
+ 613 :(before "End Initialize Op Names")
+ 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(
+ 620       "== code 0x1\n"
+ 621       // op     ModR/M  SIB   displacement  immediate
+ 622       "  25                                 0a 0b 0c 0d \n"  // and 0x0d0c0b0a with EAX
+ 623   );
+ 624   CHECK_TRACE_CONTENTS(
+ 625       "run: and imm32 0x0d0c0b0a with EAX\n"
+ 626       "run: storing 0x0000000a\n"
+ 627   );
+ 628 }
+ 629 
+ 630 :(before "End Single-Byte Opcodes")
+ 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();
+ 643   break;
+ 644 }
+ 645 
+ 646 //:
+ 647 
+ 648 :(code)
+ 649 void test_and_imm32_with_mem_at_rm32() {
+ 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
+ 655       // ModR/M in binary: 00 (indirect mode) 100 (subop and) 011 (dest EBX)
+ 656       "== data 0x2000\n"
+ 657       "ff 00 00 00\n"  // 0xff
+ 658   );
+ 659   CHECK_TRACE_CONTENTS(
+ 660       "run: combine r/m32 with imm32\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"
+ 665   );
+ 666 }
+ 667 
+ 668 :(before "End Op 81 Subops")
+ 669 case 4: {
+ 670   trace(Callstack_depth+1, "run") << "subop and" << end();
+ 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();
+ 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();
+ 680   break;
+ 681 }
+ 682 
+ 683 //:
+ 684 
+ 685 :(code)
+ 686 void test_and_imm32_with_r32() {
+ 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
+ 692       // ModR/M in binary: 11 (direct mode) 100 (subop and) 011 (dest EBX)
+ 693   );
+ 694   CHECK_TRACE_CONTENTS(
+ 695       "run: combine r/m32 with imm32\n"
+ 696       "run: r/m32 is EBX\n"
+ 697       "run: imm32 is 0x0d0c0b0a\n"
+ 698       "run: subop and\n"
+ 699       "run: storing 0x0000000a\n"
+ 700   );
+ 701 }
+ 702 
+ 703 //:: or
+ 704 
+ 705 :(before "End Initialize Op Names")
+ 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(
+ 712       "== code 0x1\n"
+ 713       // op     ModR/M  SIB   displacement  immediate
+ 714       "  0d                                 0a 0b 0c 0d \n"  // or 0x0d0c0b0a with EAX
+ 715   );
+ 716   CHECK_TRACE_CONTENTS(
+ 717       "run: or imm32 0x0d0c0b0a with EAX\n"
+ 718       "run: storing 0xddccbbaa\n"
+ 719   );
+ 720 }
+ 721 
+ 722 :(before "End Single-Byte Opcodes")
+ 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();
+ 735   break;
+ 736 }
+ 737 
+ 738 //:
+ 739 
+ 740 :(code)
+ 741 void test_or_imm32_with_mem_at_rm32() {
+ 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
+ 747       // ModR/M in binary: 00 (indirect mode) 001 (subop or) 011 (dest EBX)
+ 748       "== data 0x2000\n"
+ 749       "a0 b0 c0 d0\n"  // 0xd0c0b0a0
+ 750   );
+ 751   CHECK_TRACE_CONTENTS(
+ 752       "run: combine r/m32 with imm32\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"
+ 757   );
+ 758 }
+ 759 
+ 760 :(before "End Op 81 Subops")
+ 761 case 1: {
+ 762   trace(Callstack_depth+1, "run") << "subop or" << end();
+ 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();
+ 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();
+ 772   break;
+ 773 }
+ 774 
+ 775 :(code)
+ 776 void test_or_imm32_with_r32() {
+ 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
+ 782       // ModR/M in binary: 11 (direct mode) 001 (subop or) 011 (dest EBX)
+ 783   );
+ 784   CHECK_TRACE_CONTENTS(
+ 785       "run: combine r/m32 with imm32\n"
+ 786       "run: r/m32 is EBX\n"
+ 787       "run: imm32 is 0x0d0c0b0a\n"
+ 788       "run: subop or\n"
+ 789       "run: storing 0xddccbbaa\n"
+ 790   );
+ 791 }
+ 792 
+ 793 //:: xor
+ 794 
+ 795 :(before "End Initialize Op Names")
+ 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(
+ 802       "== code 0x1\n"
+ 803       // op     ModR/M  SIB   displacement  immediate
+ 804       "  35                                 0a 0b 0c 0d \n"  // xor 0x0d0c0b0a with EAX
+ 805   );
+ 806   CHECK_TRACE_CONTENTS(
+ 807       "run: xor imm32 0x0d0c0b0a with EAX\n"
+ 808       "run: storing 0xd0c0bbaa\n"
+ 809   );
+ 810 }
+ 811 
+ 812 :(before "End Single-Byte Opcodes")
+ 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();
+ 825   break;
+ 826 }
+ 827 
+ 828 //:
+ 829 
+ 830 :(code)
+ 831 void test_xor_imm32_with_mem_at_rm32() {
+ 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
+ 837       // ModR/M in binary: 00 (indirect mode) 110 (subop xor) 011 (dest EBX)
+ 838       "== data 0x2000\n"
+ 839       "a0 b0 c0 d0\n"  // 0xd0c0b0a0
+ 840   );
+ 841   CHECK_TRACE_CONTENTS(
+ 842       "run: combine r/m32 with imm32\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"
+ 847   );
+ 848 }
+ 849 
+ 850 :(before "End Op 81 Subops")
+ 851 case 6: {
+ 852   trace(Callstack_depth+1, "run") << "subop xor" << end();
+ 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();
+ 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();
+ 862   break;
+ 863 }
+ 864 
+ 865 :(code)
+ 866 void test_xor_imm32_with_r32() {
+ 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
+ 872       // ModR/M in binary: 11 (direct mode) 110 (subop xor) 011 (dest EBX)
+ 873   );
+ 874   CHECK_TRACE_CONTENTS(
+ 875       "run: combine r/m32 with imm32\n"
+ 876       "run: r/m32 is EBX\n"
+ 877       "run: imm32 is 0x0d0c0b0a\n"
+ 878       "run: subop xor\n"
+ 879       "run: storing 0xddccbbaa\n"
+ 880   );
+ 881 }
+ 882 
+ 883 //:: compare (cmp)
+ 884 
+ 885 :(before "End Initialize Op Names")
+ 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(
+ 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"
+ 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();
+ 907   const int32_t signed_difference = signed_arg1 - signed_arg2;
+ 908   SF = (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);
+ 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();
+ 918   break;
+ 919 }
+ 920 
+ 921 :(code)
+ 922 void test_compare_EAX_with_imm32_lesser_unsigned_and_signed() {
+ 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"
+ 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 = INT32_MAX;
+ 937   run(
+ 938       "== code 0x1\n"
+ 939       // op     ModR/M  SIB   displacement  immediate
+ 940       "  3d                                 00 00 00 80\n"  // compare EAX with INT32_MIN
+ 941   );
+ 942   CHECK_TRACE_CONTENTS(
+ 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 = -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"
+ 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 = 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"
+ 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(
+ 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"
+ 983       "run: SF=0; ZF=1; CF=0; OF=0\n"
+ 984   );
+ 985 }
+ 986 
+ 987 //:
+ 988 
+ 989 void test_compare_imm32_with_r32_greater() {
+ 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
+ 995       // ModR/M in binary: 11 (direct mode) 111 (subop compare) 011 (dest EBX)
+ 996   );
+ 997   CHECK_TRACE_CONTENTS(
+ 998       "run: combine r/m32 with imm32\n"
+ 999       "run: r/m32 is EBX\n"
+1000       "run: imm32 is 0x0d0c0b07\n"
+1001       "run: SF=0; ZF=0; CF=0; OF=0\n"
+1002   );
+1003 }
+1004 
+1005 :(before "End Op 81 Subops")
+1006 case 7: {
+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);
+1011   const int64_t tmp2 = static_cast<int64_t>(*signed_arg1) - signed_arg2;
+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();
+1019   break;
+1020 }
+1021 
+1022 :(code)
+1023 void test_compare_rm32_with_imm32_lesser_unsigned_and_signed() {
+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
+1029       // ModR/M in binary: 11 (direct mode) 111 (subop compare) 000 (dest EAX)
+1030   );
+1031   CHECK_TRACE_CONTENTS(
+1032       "run: combine r/m32 with imm32\n"
+1033       "run: r/m32 is EAX\n"
+1034       "run: imm32 is 0x0a0b0c0d\n"
+1035       "run: subop compare\n"
+1036       "run: SF=1; ZF=0; CF=1; OF=0\n"
+1037   );
+1038 }
+1039 
+1040 void test_compare_rm32_with_imm32_lesser_unsigned_and_signed_due_to_overflow() {
+1041   Reg[EAX].i = INT32_MAX;
+1042   run(
+1043       "== code 0x1\n"
+1044       // op     ModR/M  SIB   displacement  immediate
+1045       "  81     f8                          00 00 00 80\n"  // compare EAX with INT32_MIN
+1046       // ModR/M in binary: 11 (direct mode) 111 (subop compare) 000 (dest EAX)
+1047   );
+1048   CHECK_TRACE_CONTENTS(
+1049       "run: combine r/m32 with imm32\n"
+1050       "run: r/m32 is EAX\n"
+1051       "run: imm32 is 0x80000000\n"
+1052       "run: subop compare\n"
+1053       "run: SF=1; ZF=0; CF=1; OF=1\n"
+1054   );
+1055 }
+1056 
+1057 void test_compare_rm32_with_imm32_lesser_signed() {
+1058   Reg[EAX].i = -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
+1063       // ModR/M in binary: 11 (direct mode) 111 (subop compare) 000 (dest EAX)
+1064   );
+1065   CHECK_TRACE_CONTENTS(
+1066       "run: combine r/m32 with imm32\n"
+1067       "run: r/m32 is EAX\n"
+1068       "run: imm32 is 0x00000001\n"
+1069       "run: subop compare\n"
+1070       "run: SF=1; ZF=0; CF=0; OF=0\n"
+1071   );
+1072 }
+1073 
+1074 void test_compare_rm32_with_imm32_lesser_unsigned() {
+1075   Reg[EAX].i = 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
+1080       // ModR/M in binary: 11 (direct mode) 111 (subop compare) 000 (dest EAX)
+1081   );
+1082   CHECK_TRACE_CONTENTS(
+1083       "run: combine r/m32 with imm32\n"
+1084       "run: r/m32 is EAX\n"
+1085       "run: imm32 is 0xffffffff\n"
+1086       "run: subop compare\n"
+1087       "run: SF=0; ZF=0; CF=1; OF=0\n"
+1088   );
+1089 }
+1090 
+1091 :(code)
+1092 void test_compare_imm32_with_r32_equal() {
+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
+1098       // ModR/M in binary: 11 (direct mode) 111 (subop compare) 011 (dest EBX)
+1099   );
+1100   CHECK_TRACE_CONTENTS(
+1101       "run: combine r/m32 with imm32\n"
+1102       "run: r/m32 is EBX\n"
+1103       "run: imm32 is 0x0d0c0b0a\n"
+1104       "run: SF=0; ZF=1; CF=0; OF=0\n"
+1105   );
+1106 }
+1107 
+1108 :(code)
+1109 void test_compare_imm32_with_mem_at_rm32_greater() {
+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
+1115       // ModR/M in binary: 00 (indirect mode) 111 (subop compare) 011 (dest EBX)
+1116       "== data 0x2000\n"
+1117       "0a 0b 0c 0d\n"  // 0x0d0c0b0a
+1118   );
+1119   CHECK_TRACE_CONTENTS(
+1120       "run: combine r/m32 with imm32\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   );
+1125 }
+1126 
+1127 :(code)
+1128 void test_compare_imm32_with_mem_at_rm32_lesser() {
+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
+1134       // ModR/M in binary: 00 (indirect mode) 111 (subop compare) 000 (dest EAX)
+1135       "== data 0x2000\n"
+1136       "07 0b 0c 0d\n"  // 0x0d0c0b07
+1137   );
+1138   CHECK_TRACE_CONTENTS(
+1139       "run: combine r/m32 with imm32\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   );
+1144 }
+1145 
+1146 :(code)
+1147 void test_compare_imm32_with_mem_at_rm32_equal() {
+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
+1154       // ModR/M in binary: 00 (indirect mode) 111 (subop compare) 011 (dest EBX)
+1155       "== data 0x2000\n"
+1156       "0a 0b 0c 0d\n"  // 0x0d0c0b0a
+1157   );
+1158   CHECK_TRACE_CONTENTS(
+1159       "run: combine r/m32 with imm32\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   );
+1164 }
+1165 
+1166 //:: copy (mov)
+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)");
+1177 
+1178 :(code)
+1179 void test_copy_imm32_to_r32() {
+1180   run(
+1181       "== code 0x1\n"
+1182       // op     ModR/M  SIB   displacement  immediate
+1183       "  bb                                 0a 0b 0c 0d \n"  // copy 0x0d0c0b0a to EBX
+1184   );
+1185   CHECK_TRACE_CONTENTS(
+1186       "run: copy imm32 0x0d0c0b0a to EBX\n"
+1187   );
+1188 }
+1189 
+1190 :(before "End Single-Byte Opcodes")
+1191 case 0xb9:
+1192 case 0xba:
+1193 case 0xbb:
+1194 case 0xbc:
+1195 case 0xbd:
+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;
+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)");
+1209 
+1210 :(code)
+1211 void test_copy_imm32_to_mem_at_rm32() {
+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
+1217       // ModR/M in binary: 00 (indirect mode) 000 (subop) 011 (dest EBX)
+1218   );
+1219   CHECK_TRACE_CONTENTS(
+1220       "run: copy imm32 to r/m32\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();
+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";
+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();
+1238   *dest = src;  // Write multiple elements of vector<uint8_t> at once. Assumes sizeof(int) == 4 on the host as well.
+1239   break;
+1240 }
+1241 
+1242 //:: push
+1243 
+1244 :(before "End Initialize Op Names")
+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(
+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"
+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();
+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();
+1271   break;
+1272 }
+1273 
+1274 //:: multiply
+1275 
+1276 :(before "End Initialize Op Names")
+1277 put_new(Name, "69", "multiply rm32 by imm32 and store result in r32 (imul)");
+1278 
+1279 :(code)
+1280 void test_multiply_imm32() {
+1281   Reg[EAX].i = 2;
+1282   Reg[EBX].i = 3;
+1283   run(
+1284       "== code 0x1\n"
+1285       // op     ModR/M  SIB   displacement  immediate
+1286       "  69     c3                          04 00 00 00 \n"  // EAX = EBX * 4
+1287       // ModR/M in binary: 11 (direct) 000 (dest EAX) 011 (src EBX)
+1288   );
+1289   CHECK_TRACE_CONTENTS(
+1290       "run: multiply r/m32 by 0x00000004 and store result in EAX\n"
+1291       "run: r/m32 is EBX\n"
+1292       "run: storing 0x0000000c\n"
+1293   );
+1294 }
+1295 
+1296 :(before "End Single-Byte Opcodes")
+1297 case 0x69: {
+1298   const uint8_t modrm = next();
+1299   const uint8_t rdest = (modrm>>3)&0x7;
+1300   const int32_t val = next32();
+1301   trace(Callstack_depth+1, "run") << "multiply r/m32 by 0x" << HEXWORD << val << " and store result in " << rname(rdest) << end();
+1302   const int32_t* signed_arg1 = effective_address(modrm);
+1303   int32_t result = *signed_arg1 * val;
+1304   int64_t full_result = static_cast<int64_t>(*signed_arg1) * val;
+1305   OF = (result != full_result);
+1306   CF = OF;
+1307   trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end();
+1308   Reg[rdest].i = result;
+1309   trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << Reg[rdest].i << end();
+1310   break;
+1311 }
+
+ + + diff --git a/html/linux/bootstrap/016index_addressing.cc.html b/html/linux/bootstrap/016index_addressing.cc.html new file mode 100644 index 00000000..fc6d9d64 --- /dev/null +++ b/html/linux/bootstrap/016index_addressing.cc.html @@ -0,0 +1,217 @@ + + + + +Mu - linux/bootstrap/016index_addressing.cc + + + + + + + + + + +https://github.com/akkartik/mu/blob/main/linux/bootstrap/016index_addressing.cc +
+  1 //: operating on memory at the address provided by some register plus optional scale and offset
+  2 
+  3 :(code)
+  4 void test_add_r32_to_mem_at_rm32_with_sib() {
+  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
+ 11       // ModR/M in binary: 00 (indirect mode) 011 (src EBX) 100 (dest in SIB)
+ 12       // SIB in binary: 00 (scale 1) 100 (no index) 000 (base EAX)
+ 13       "== data 0x2000\n"
+ 14       "01 00 00 00\n"  // 1
+ 15   );
+ 16   CHECK_TRACE_CONTENTS(
+ 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   );
+ 22 }
+ 23 
+ 24 :(before "End Mod 0 Special-cases(addr)")
+ 25 case 4:  // exception: mod 0b00 rm 0b100 => incoming SIB (scale-index-base) byte
+ 26   addr = effective_address_from_sib(mod);
+ 27   break;
+ 28 :(code)
+ 29 uint32_t effective_address_from_sib(uint8_t mod) {
+ 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();
+ 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();
+ 41   }
+ 42   const uint8_t index = (sib>>3)&0x7;
+ 43   if (index == ESP) {
+ 44     // ignore index and scale
+ 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();
+ 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(
+ 61       "== code 0x1\n"
+ 62       // op     ModR/M  SIB   displacement  immediate
+ 63       "  01     1c      08                              \n"  // add EBX to *(EAX+ECX)
+ 64       // ModR/M in binary: 00 (indirect mode) 011 (src EBX) 100 (dest in SIB)
+ 65       // SIB in binary: 00 (scale 1) 001 (index ECX) 000 (base EAX)
+ 66       "== data 0x2000\n"
+ 67       "01 00 00 00\n"  // 1
+ 68   );
+ 69   CHECK_TRACE_CONTENTS(
+ 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   );
+ 75 }
+ 76 
+ 77 :(code)
+ 78 void test_add_r32_to_mem_at_displacement_using_sib() {
+ 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
+ 84       // ModR/M in binary: 00 (indirect mode) 011 (src EBX) 100 (dest in SIB)
+ 85       // SIB in binary: 00 (scale 1) 100 (no index) 101 (not EBP but disp32)
+ 86       "== data 0x2000\n"
+ 87       "01 00 00 00\n"  // 1
+ 88   );
+ 89   CHECK_TRACE_CONTENTS(
+ 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   );
+ 95 }
+ 96 
+ 97 //:
+ 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(
+105       "== code 0x1\n"
+106       // op     ModR/M  SIB   displacement  immediate
+107       "  01     5c      08    02                        \n"  // add EBX to *(EAX+ECX+2)
+108       // ModR/M in binary: 01 (indirect+disp8 mode) 011 (src EBX) 100 (dest in SIB)
+109       // SIB in binary: 00 (scale 1) 001 (index ECX) 000 (base EAX)
+110       "== data 0x2000\n"
+111       "01 00 00 00\n"  // 1
+112   );
+113   CHECK_TRACE_CONTENTS(
+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"
+119   );
+120 }
+121 
+122 :(before "End Mod 1 Special-cases(addr)")
+123 case 4:  // exception: mod 0b01 rm 0b100 => incoming SIB (scale-index-base) byte
+124   addr = effective_address_from_sib(mod);
+125   break;
+126 
+127 //:
+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(
+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)
+138       // ModR/M in binary: 10 (indirect+disp32 mode) 011 (src EBX) 100 (dest in SIB)
+139       // SIB in binary: 00 (scale 1) 001 (index ECX) 000 (base EAX)
+140       "== data 0x2000\n"
+141       "01 00 00 00\n"  // 1
+142   );
+143   CHECK_TRACE_CONTENTS(
+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"
+148       "run: storing 0x00000011\n"
+149   );
+150 }
+151 
+152 :(before "End Mod 2 Special-cases(addr)")
+153 case 4:  // exception: mod 0b10 rm 0b100 => incoming SIB (scale-index-base) byte
+154   addr = effective_address_from_sib(mod);
+155   break;
+
+ + + diff --git a/html/linux/bootstrap/017jump_disp8.cc.html b/html/linux/bootstrap/017jump_disp8.cc.html new file mode 100644 index 00000000..8baf96cd --- /dev/null +++ b/html/linux/bootstrap/017jump_disp8.cc.html @@ -0,0 +1,469 @@ + + + + +Mu - linux/bootstrap/017jump_disp8.cc + + + + + + + + + + +https://github.com/akkartik/mu/blob/main/linux/bootstrap/017jump_disp8.cc +
+  1 //: jump to 8-bit offset
+  2 
+  3 //:: jump
+  4 
+  5 :(before "End Initialize Op Names")
+  6 put_new(Name, "eb", "jump disp8 bytes away (jmp)");
+  7 
+  8 :(code)
+  9 void test_jump_disp8() {
+ 10   run(
+ 11       "== code 0x1\n"
+ 12       // op     ModR/M  SIB   displacement  immediate
+ 13       "  eb                   05                        \n"  // skip 1 instruction
+ 14       "  05                                 00 00 00 01 \n"
+ 15       "  05                                 00 00 00 02 \n"
+ 16   );
+ 17   CHECK_TRACE_CONTENTS(
+ 18       "run: 0x00000001 opcode: eb\n"
+ 19       "run: jump 5\n"
+ 20       "run: 0x00000008 opcode: 05\n"
+ 21   );
+ 22   CHECK_TRACE_DOESNT_CONTAIN("run: 0x00000003 opcode: 05");
+ 23 }
+ 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;
+ 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)");
+ 37 
+ 38 :(code)
+ 39 void test_je_disp8_success() {
+ 40   ZF = true;
+ 41   run(
+ 42       "== code 0x1\n"
+ 43       // op     ModR/M  SIB   displacement  immediate
+ 44       "  74                   05                        \n"  // skip 1 instruction
+ 45       "  05                                 00 00 00 01 \n"
+ 46       "  05                                 00 00 00 02 \n"
+ 47   );
+ 48   CHECK_TRACE_CONTENTS(
+ 49       "run: 0x00000001 opcode: 74\n"
+ 50       "run: jump 5\n"
+ 51       "run: 0x00000008 opcode: 05\n"
+ 52   );
+ 53   CHECK_TRACE_DOESNT_CONTAIN("run: 0x00000003 opcode: 05");
+ 54 }
+ 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;
+ 62   }
+ 63   break;
+ 64 }
+ 65 
+ 66 :(code)
+ 67 void test_je_disp8_fail() {
+ 68   ZF = false;
+ 69   run(
+ 70       "== code 0x1\n"
+ 71       // op     ModR/M  SIB   displacement  immediate
+ 72       "  74                   05                        \n"  // skip 1 instruction
+ 73       "  05                                 00 00 00 01 \n"
+ 74       "  05                                 00 00 00 02 \n"
+ 75   );
+ 76   CHECK_TRACE_CONTENTS(
+ 77       "run: 0x00000001 opcode: 74\n"
+ 78       "run: 0x00000003 opcode: 05\n"
+ 79       "run: 0x00000008 opcode: 05\n"
+ 80   );
+ 81   CHECK_TRACE_DOESNT_CONTAIN("run: jump 5");
+ 82 }
+ 83 
+ 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)");
+ 88 
+ 89 :(code)
+ 90 void test_jne_disp8_success() {
+ 91   ZF = false;
+ 92   run(
+ 93       "== code 0x1\n"
+ 94       // op     ModR/M  SIB   displacement  immediate
+ 95       "  75                   05                        \n"  // skip 1 instruction
+ 96       "  05                                 00 00 00 01 \n"
+ 97       "  05                                 00 00 00 02 \n"
+ 98   );
+ 99   CHECK_TRACE_CONTENTS(
+100       "run: 0x00000001 opcode: 75\n"
+101       "run: jump 5\n"
+102       "run: 0x00000008 opcode: 05\n"
+103   );
+104   CHECK_TRACE_DOESNT_CONTAIN("run: 0x00000003 opcode: 05");
+105 }
+106 
+107 :(before "End Single-Byte Opcodes")
+108 case 0x75: {  // jump disp8 if !ZF
+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;
+113   }
+114   break;
+115 }
+116 
+117 :(code)
+118 void test_jne_disp8_fail() {
+119   ZF = true;
+120   run(
+121       "== code 0x1\n"
+122       // op     ModR/M  SIB   displacement  immediate
+123       "  75                   05                        \n"  // skip 1 instruction
+124       "  05                                 00 00 00 01 \n"
+125       "  05                                 00 00 00 02 \n"
+126   );
+127   CHECK_TRACE_CONTENTS(
+128       "run: 0x00000001 opcode: 75\n"
+129       "run: 0x00000003 opcode: 05\n"
+130       "run: 0x00000008 opcode: 05\n"
+131   );
+132   CHECK_TRACE_DOESNT_CONTAIN("run: jump 5");
+133 }
+134 
+135 //:: jump if greater
+136 
+137 :(before "End Initialize Op Names")
+138 put_new(Name, "7f", "jump disp8 bytes away if greater, if ZF is unset and SF == OF (jcc/jg/jnle)");
+139 put_new(Name, "77", "jump disp8 bytes away if greater (addr, float), if ZF is unset and CF is unset (jcc/ja/jnbe)");
+140 
+141 :(code)
+142 void test_jg_disp8_success() {
+143   ZF = false;
+144   SF = false;
+145   OF = false;
+146   run(
+147       "== code 0x1\n"
+148       // op     ModR/M  SIB   displacement  immediate
+149       "  7f                   05                        \n"  // skip 1 instruction
+150       "  05                                 00 00 00 01 \n"
+151       "  05                                 00 00 00 02 \n"
+152   );
+153   CHECK_TRACE_CONTENTS(
+154       "run: 0x00000001 opcode: 7f\n"
+155       "run: jump 5\n"
+156       "run: 0x00000008 opcode: 05\n"
+157   );
+158   CHECK_TRACE_DOESNT_CONTAIN("run: 0x00000003 opcode: 05");
+159 }
+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;
+167   }
+168   break;
+169 }
+170 case 0x77: {  // jump disp8 if !CF and !ZF
+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;
+175   }
+176   break;
+177 }
+178 
+179 :(code)
+180 void test_jg_disp8_fail() {
+181   ZF = false;
+182   SF = true;
+183   OF = false;
+184   run(
+185       "== code 0x1\n"
+186       // op     ModR/M  SIB   displacement  immediate
+187       "  7f                   05                        \n"  // skip 1 instruction
+188       "  05                                 00 00 00 01 \n"
+189       "  05                                 00 00 00 02 \n"
+190   );
+191   CHECK_TRACE_CONTENTS(
+192       "run: 0x00000001 opcode: 7f\n"
+193       "run: 0x00000003 opcode: 05\n"
+194       "run: 0x00000008 opcode: 05\n"
+195   );
+196   CHECK_TRACE_DOESNT_CONTAIN("run: jump 5");
+197 }
+198 
+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, if SF == OF (jcc/jge/jnl)");
+203 put_new(Name, "73", "jump disp8 bytes away if greater or equal (addr, float), if CF is unset (jcc/jae/jnb)");
+204 
+205 :(code)
+206 void test_jge_disp8_success() {
+207   SF = false;
+208   OF = false;
+209   run(
+210       "== code 0x1\n"
+211       // op     ModR/M  SIB   displacement  immediate
+212       "  7d                   05                        \n"  // skip 1 instruction
+213       "  05                                 00 00 00 01 \n"
+214       "  05                                 00 00 00 02 \n"
+215   );
+216   CHECK_TRACE_CONTENTS(
+217       "run: 0x00000001 opcode: 7d\n"
+218       "run: jump 5\n"
+219       "run: 0x00000008 opcode: 05\n"
+220   );
+221   CHECK_TRACE_DOESNT_CONTAIN("run: 0x00000003 opcode: 05");
+222 }
+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;
+230   }
+231   break;
+232 }
+233 case 0x73: {  // jump disp8 if !CF
+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;
+238   }
+239   break;
+240 }
+241 
+242 :(code)
+243 void test_jge_disp8_fail() {
+244   SF = true;
+245   OF = false;
+246   run(
+247       "== code 0x1\n"
+248       // op     ModR/M  SIB   displacement  immediate
+249       "  7d                   05                        \n"  // skip 1 instruction
+250       "  05                                 00 00 00 01 \n"
+251       "  05                                 00 00 00 02 \n"
+252   );
+253   CHECK_TRACE_CONTENTS(
+254       "run: 0x00000001 opcode: 7d\n"
+255       "run: 0x00000003 opcode: 05\n"
+256       "run: 0x00000008 opcode: 05\n"
+257   );
+258   CHECK_TRACE_DOESNT_CONTAIN("run: jump 5");
+259 }
+260 
+261 //:: jump if lesser
+262 
+263 :(before "End Initialize Op Names")
+264 put_new(Name, "7c", "jump disp8 bytes away if lesser, if SF != OF (jcc/jl/jnge)");
+265 put_new(Name, "72", "jump disp8 bytes away if lesser (addr, float), if CF is set (jcc/jb/jnae)");
+266 
+267 :(code)
+268 void test_jl_disp8_success() {
+269   ZF = false;
+270   SF = true;
+271   OF = false;
+272   run(
+273       "== code 0x1\n"
+274       // op     ModR/M  SIB   displacement  immediate
+275       "  7c                   05                        \n"  // skip 1 instruction
+276       "  05                                 00 00 00 01 \n"
+277       "  05                                 00 00 00 02 \n"
+278   );
+279   CHECK_TRACE_CONTENTS(
+280       "run: 0x00000001 opcode: 7c\n"
+281       "run: jump 5\n"
+282       "run: 0x00000008 opcode: 05\n"
+283   );
+284   CHECK_TRACE_DOESNT_CONTAIN("run: 0x00000003 opcode: 05");
+285 }
+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;
+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;
+301   }
+302   break;
+303 }
+304 
+305 :(code)
+306 void test_jl_disp8_fail() {
+307   ZF = false;
+308   SF = false;
+309   OF = false;
+310   run(
+311       "== code 0x1\n"
+312       // op     ModR/M  SIB   displacement  immediate
+313       "  7c                   05                        \n"  // skip 1 instruction
+314       "  05                                 00 00 00 01 \n"
+315       "  05                                 00 00 00 02 \n"
+316   );
+317   CHECK_TRACE_CONTENTS(
+318       "run: 0x00000001 opcode: 7c\n"
+319       "run: 0x00000003 opcode: 05\n"
+320       "run: 0x00000008 opcode: 05\n"
+321   );
+322   CHECK_TRACE_DOESNT_CONTAIN("run: jump 5");
+323 }
+324 
+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, if ZF is set or SF != OF (jcc/jle/jng)");
+329 put_new(Name, "76", "jump disp8 bytes away if lesser or equal (addr, float), if ZF is set or CF is set (jcc/jbe/jna)");
+330 
+331 :(code)
+332 void test_jle_disp8_equal() {
+333   ZF = true;
+334   SF = false;
+335   OF = false;
+336   run(
+337       "== code 0x1\n"
+338       // op     ModR/M  SIB   displacement  immediate
+339       "  7e                   05                        \n"  // skip 1 instruction
+340       "  05                                 00 00 00 01 \n"
+341       "  05                                 00 00 00 02 \n"
+342   );
+343   CHECK_TRACE_CONTENTS(
+344       "run: 0x00000001 opcode: 7e\n"
+345       "run: jump 5\n"
+346       "run: 0x00000008 opcode: 05\n"
+347   );
+348   CHECK_TRACE_DOESNT_CONTAIN("run: 0x00000003 opcode: 05");
+349 }
+350 
+351 :(code)
+352 void test_jle_disp8_lesser() {
+353   ZF = false;
+354   SF = true;
+355   OF = false;
+356   run(
+357       "== code 0x1\n"
+358       // op     ModR/M  SIB   displacement  immediate
+359       "  7e                   05                        \n"  // skip 1 instruction
+360       "  05                                 00 00 00 01 \n"
+361       "  05                                 00 00 00 02 \n"
+362   );
+363   CHECK_TRACE_CONTENTS(
+364       "run: 0x00000001 opcode: 7e\n"
+365       "run: jump 5\n"
+366       "run: 0x00000008 opcode: 05\n"
+367   );
+368   CHECK_TRACE_DOESNT_CONTAIN("run: 0x00000003 opcode: 05");
+369 }
+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;
+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;
+385   }
+386   break;
+387 }
+388 
+389 :(code)
+390 void test_jle_disp8_greater() {
+391   ZF = false;
+392   SF = false;
+393   OF = false;
+394   run(
+395       "== code 0x1\n"
+396       // op     ModR/M  SIB   displacement  immediate
+397       "  7e                   05                        \n"  // skip 1 instruction
+398       "  05                                 00 00 00 01 \n"
+399       "  05                                 00 00 00 02 \n"
+400   );
+401   CHECK_TRACE_CONTENTS(
+402       "run: 0x00000001 opcode: 7e\n"
+403       "run: 0x00000003 opcode: 05\n"
+404       "run: 0x00000008 opcode: 05\n"
+405   );
+406   CHECK_TRACE_DOESNT_CONTAIN("run: jump 5");
+407 }
+
+ + + diff --git a/html/linux/bootstrap/018jump_disp32.cc.html b/html/linux/bootstrap/018jump_disp32.cc.html new file mode 100644 index 00000000..991b070e --- /dev/null +++ b/html/linux/bootstrap/018jump_disp32.cc.html @@ -0,0 +1,469 @@ + + + + +Mu - linux/bootstrap/018jump_disp32.cc + + + + + + + + + + +https://github.com/akkartik/mu/blob/main/linux/bootstrap/018jump_disp32.cc +
+  1 //: jump to 32-bit offset
+  2 
+  3 //:: jump
+  4 
+  5 :(before "End Initialize Op Names")
+  6 put_new(Name, "e9", "jump disp32 bytes away (jmp)");
+  7 
+  8 :(code)
+  9 void test_jump_disp32() {
+ 10   run(
+ 11       "== code 0x1\n"
+ 12       // op     ModR/M  SIB   displacement  immediate
+ 13       "  e9                   05 00 00 00               \n"  // skip 1 instruction
+ 14       "  05                                 00 00 00 01 \n"
+ 15       "  05                                 00 00 00 02 \n"
+ 16   );
+ 17   CHECK_TRACE_CONTENTS(
+ 18       "run: 0x00000001 opcode: e9\n"
+ 19       "run: jump 5\n"
+ 20       "run: 0x0000000b opcode: 05\n"
+ 21   );
+ 22   CHECK_TRACE_DOESNT_CONTAIN("run: 0x00000006 opcode: 05");
+ 23 }
+ 24 
+ 25 :(before "End Single-Byte Opcodes")
+ 26 case 0xe9: {  // jump disp32
+ 27   const int32_t offset = next32();
+ 28   trace(Callstack_depth+1, "run") << "jump " << 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_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(
+ 42       "== code 0x1\n"
+ 43       // op     ModR/M  SIB   displacement  immediate
+ 44       "  0f 84                05 00 00 00               \n"  // skip 1 instruction
+ 45       "  05                                 00 00 00 01 \n"
+ 46       "  05                                 00 00 00 02 \n"
+ 47   );
+ 48   CHECK_TRACE_CONTENTS(
+ 49       "run: 0x00000001 opcode: 0f\n"
+ 50       "run: jump 5\n"
+ 51       "run: 0x0000000c opcode: 05\n"
+ 52   );
+ 53   CHECK_TRACE_DOESNT_CONTAIN("run: 0x00000007 opcode: 05");
+ 54 }
+ 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) {
+ 60     trace(Callstack_depth+1, "run") << "jump " << offset << end();
+ 61     EIP += offset;
+ 62   }
+ 63   break;
+ 64 }
+ 65 
+ 66 :(code)
+ 67 void test_je_disp32_fail() {
+ 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
+ 73       "  05                                 00 00 00 01 \n"
+ 74       "  05                                 00 00 00 02 \n"
+ 75   );
+ 76   CHECK_TRACE_CONTENTS(
+ 77       "run: 0x00000001 opcode: 0f\n"
+ 78       "run: 0x00000007 opcode: 05\n"
+ 79       "run: 0x0000000c opcode: 05\n"
+ 80   );
+ 81   CHECK_TRACE_DOESNT_CONTAIN("run: jump 5");
+ 82 }
+ 83 
+ 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)");
+ 88 
+ 89 :(code)
+ 90 void test_jne_disp32_success() {
+ 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
+ 96       "  05                                 00 00 00 01 \n"
+ 97       "  05                                 00 00 00 02 \n"
+ 98   );
+ 99   CHECK_TRACE_CONTENTS(
+100       "run: 0x00000001 opcode: 0f\n"
+101       "run: jump 5\n"
+102       "run: 0x0000000c opcode: 05\n"
+103   );
+104   CHECK_TRACE_DOESNT_CONTAIN("run: 0x00000007 opcode: 05");
+105 }
+106 
+107 :(before "End Two-Byte Opcodes Starting With 0f")
+108 case 0x85: {  // jump disp32 if !ZF
+109   const int32_t offset = next32();
+110   if (!ZF) {
+111     trace(Callstack_depth+1, "run") << "jump " << offset << end();
+112     EIP += offset;
+113   }
+114   break;
+115 }
+116 
+117 :(code)
+118 void test_jne_disp32_fail() {
+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
+124       "  05                                 00 00 00 01 \n"
+125       "  05                                 00 00 00 02 \n"
+126   );
+127   CHECK_TRACE_CONTENTS(
+128       "run: 0x00000001 opcode: 0f\n"
+129       "run: 0x00000007 opcode: 05\n"
+130       "run: 0x0000000c opcode: 05\n"
+131   );
+132   CHECK_TRACE_DOESNT_CONTAIN("run: jump 5");
+133 }
+134 
+135 //:: jump if greater
+136 
+137 :(before "End Initialize Op Names")
+138 put_new(Name_0f, "8f", "jump disp32 bytes away if greater, if ZF is unset and SF == OF (jcc/jg/jnle)");
+139 put_new(Name_0f, "87", "jump disp32 bytes away if greater (addr, float), if ZF is unset and CF is unset (jcc/ja/jnbe)");
+140 
+141 :(code)
+142 void test_jg_disp32_success() {
+143   ZF = false;
+144   SF = false;
+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
+150       "  05                                 00 00 00 01 \n"
+151       "  05                                 00 00 00 02 \n"
+152   );
+153   CHECK_TRACE_CONTENTS(
+154       "run: 0x00000001 opcode: 0f\n"
+155       "run: jump 5\n"
+156       "run: 0x0000000c opcode: 05\n"
+157   );
+158   CHECK_TRACE_DOESNT_CONTAIN("run: 0x00000007 opcode: 05");
+159 }
+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) {
+165     trace(Callstack_depth+1, "run") << "jump " << offset << end();
+166     EIP += offset;
+167   }
+168   break;
+169 }
+170 case 0x87: {  // jump disp32 if !CF and !ZF
+171   const int32_t offset = next32();
+172   if (!CF && !ZF) {
+173     trace(Callstack_depth+1, "run") << "jump " << offset << end();
+174     EIP += offset;
+175   }
+176   break;
+177 }
+178 
+179 :(code)
+180 void test_jg_disp32_fail() {
+181   ZF = false;
+182   SF = true;
+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
+188       "  05                                 00 00 00 01 \n"
+189       "  05                                 00 00 00 02 \n"
+190   );
+191   CHECK_TRACE_CONTENTS(
+192       "run: 0x00000001 opcode: 0f\n"
+193       "run: 0x00000007 opcode: 05\n"
+194       "run: 0x0000000c opcode: 05\n"
+195   );
+196   CHECK_TRACE_DOESNT_CONTAIN("run: jump 5");
+197 }
+198 
+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, if SF == OF (jcc/jge/jnl)");
+203 put_new(Name_0f, "83", "jump disp32 bytes away if greater or equal (addr, float), if CF is unset (jcc/jae/jnb)");
+204 
+205 :(code)
+206 void test_jge_disp32_success() {
+207   SF = false;
+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
+213       "  05                                 00 00 00 01 \n"
+214       "  05                                 00 00 00 02 \n"
+215   );
+216   CHECK_TRACE_CONTENTS(
+217       "run: 0x00000001 opcode: 0f\n"
+218       "run: jump 5\n"
+219       "run: 0x0000000c opcode: 05\n"
+220   );
+221   CHECK_TRACE_DOESNT_CONTAIN("run: 0x00000007 opcode: 05");
+222 }
+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) {
+228     trace(Callstack_depth+1, "run") << "jump " << offset << end();
+229     EIP += offset;
+230   }
+231   break;
+232 }
+233 case 0x83: {  // jump disp32 if !CF
+234   const int32_t offset = next32();
+235   if (!CF) {
+236     trace(Callstack_depth+1, "run") << "jump " << offset << end();
+237     EIP += offset;
+238   }
+239   break;
+240 }
+241 
+242 :(code)
+243 void test_jge_disp32_fail() {
+244   SF = true;
+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
+250       "  05                                 00 00 00 01 \n"
+251       "  05                                 00 00 00 02 \n"
+252   );
+253   CHECK_TRACE_CONTENTS(
+254       "run: 0x00000001 opcode: 0f\n"
+255       "run: 0x00000007 opcode: 05\n"
+256       "run: 0x0000000c opcode: 05\n"
+257   );
+258   CHECK_TRACE_DOESNT_CONTAIN("run: jump 5");
+259 }
+260 
+261 //:: jump if lesser
+262 
+263 :(before "End Initialize Op Names")
+264 put_new(Name_0f, "8c", "jump disp32 bytes away if lesser, if SF != OF (jcc/jl/jnge)");
+265 put_new(Name_0f, "82", "jump disp32 bytes away if lesser (addr, float), if CF is set (jcc/jb/jnae)");
+266 
+267 :(code)
+268 void test_jl_disp32_success() {
+269   ZF = false;
+270   SF = true;
+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
+276       "  05                                 00 00 00 01 \n"
+277       "  05                                 00 00 00 02 \n"
+278   );
+279   CHECK_TRACE_CONTENTS(
+280       "run: 0x00000001 opcode: 0f\n"
+281       "run: jump 5\n"
+282       "run: 0x0000000c opcode: 05\n"
+283   );
+284   CHECK_TRACE_DOESNT_CONTAIN("run: 0x00000007 opcode: 05");
+285 }
+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) {
+291     trace(Callstack_depth+1, "run") << "jump " << offset << end();
+292     EIP += offset;
+293   }
+294   break;
+295 }
+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;
+301   }
+302   break;
+303 }
+304 
+305 :(code)
+306 void test_jl_disp32_fail() {
+307   ZF = false;
+308   SF = false;
+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
+314       "  05                                 00 00 00 01 \n"
+315       "  05                                 00 00 00 02 \n"
+316   );
+317   CHECK_TRACE_CONTENTS(
+318       "run: 0x00000001 opcode: 0f\n"
+319       "run: 0x00000007 opcode: 05\n"
+320       "run: 0x0000000c opcode: 05\n"
+321   );
+322   CHECK_TRACE_DOESNT_CONTAIN("run: jump 5");
+323 }
+324 
+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, if ZF is set or SF != OF (jcc/jle/jng)");
+329 put_new(Name_0f, "86", "jump disp32 bytes away if lesser or equal (addr, float), if ZF is set or CF is set (jcc/jbe/jna)");
+330 
+331 :(code)
+332 void test_jle_disp32_equal() {
+333   ZF = true;
+334   SF = false;
+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
+340       "  05                                 00 00 00 01 \n"
+341       "  05                                 00 00 00 02 \n"
+342   );
+343   CHECK_TRACE_CONTENTS(
+344       "run: 0x00000001 opcode: 0f\n"
+345       "run: jump 5\n"
+346       "run: 0x0000000c opcode: 05\n"
+347   );
+348   CHECK_TRACE_DOESNT_CONTAIN("run: 0x00000007 opcode: 05");
+349 }
+350 
+351 :(code)
+352 void test_jle_disp32_lesser() {
+353   ZF = false;
+354   SF = true;
+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
+360       "  05                                 00 00 00 01 \n"
+361       "  05                                 00 00 00 02 \n"
+362   );
+363   CHECK_TRACE_CONTENTS(
+364       "run: 0x00000001 opcode: 0f\n"
+365       "run: jump 5\n"
+366       "run: 0x0000000c opcode: 05\n"
+367   );
+368   CHECK_TRACE_DOESNT_CONTAIN("run: 0x00000007 opcode: 05");
+369 }
+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) {
+375     trace(Callstack_depth+1, "run") << "jump " << offset << end();
+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) {
+383     trace(Callstack_depth+1, "run") << "jump " << offset << end();
+384     EIP += offset;
+385   }
+386   break;
+387 }
+388 
+389 :(code)
+390 void test_jle_disp32_greater() {
+391   ZF = false;
+392   SF = false;
+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
+398       "  05                                 00 00 00 01 \n"
+399       "  05                                 00 00 00 02 \n"
+400   );
+401   CHECK_TRACE_CONTENTS(
+402       "run: 0x00000001 opcode: 0f\n"
+403       "run: 0x00000007 opcode: 05\n"
+404       "run: 0x0000000c opcode: 05\n"
+405   );
+406   CHECK_TRACE_DOESNT_CONTAIN("run: jump 5");
+407 }
+
+ + + diff --git a/html/linux/bootstrap/019functions.cc.html b/html/linux/bootstrap/019functions.cc.html new file mode 100644 index 00000000..4ae8357c --- /dev/null +++ b/html/linux/bootstrap/019functions.cc.html @@ -0,0 +1,185 @@ + + + + +Mu - linux/bootstrap/019functions.cc + + + + + + + + + + +https://github.com/akkartik/mu/blob/main/linux/bootstrap/019functions.cc +
+  1 //:: call
+  2 
+  3 :(before "End Initialize Op Names")
+  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(
+ 11       "== code 0x1\n"
+ 12       // op     ModR/M  SIB   displacement  immediate
+ 13       "  e8                                 a0 00 00 00 \n"  // call function offset at 0xa0
+ 14       // next EIP is 6
+ 15   );
+ 16   CHECK_TRACE_CONTENTS(
+ 17       "run: call imm32 0x000000a0\n"
+ 18       "run: decrementing ESP to 0xbd000060\n"
+ 19       "run: pushing value 0x00000006\n"
+ 20       "run: jumping to 0x000000a6\n"
+ 21   );
+ 22 }
+ 23 
+ 24 :(before "End Single-Byte Opcodes")
+ 25 case 0xe8: {  // call disp32 relative to next EIP
+ 26   const int32_t offset = next32();
+ 27   ++Callstack_depth;
+ 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();
+ 33   break;
+ 34 }
+ 35 
+ 36 //:
+ 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(
+ 44       "== code 0x1\n"
+ 45       // op     ModR/M  SIB   displacement  immediate
+ 46       "  ff     d3                                      \n"  // call function offset at EBX
+ 47       // next EIP is 3
+ 48   );
+ 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"
+ 53       "run: pushing value 0x00000003\n"
+ 54       "run: jumping to 0x000000a0\n"
+ 55   );
+ 56 }
+ 57 
+ 58 :(before "End Op ff Subops")
+ 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();
+ 65   ++Callstack_depth;
+ 66   break;
+ 67 }
+ 68 
+ 69 :(code)
+ 70 void test_call_mem_at_rm32() {
+ 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
+ 78       // next EIP is 3
+ 79       "== data 0x2000\n"
+ 80       "a0 00 00 00\n"  // 0xa0
+ 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"
+ 86       "run: pushing value 0x00000003\n"
+ 87       "run: jumping to 0x000000a0\n"
+ 88   );
+ 89 }
+ 90 
+ 91 //:: ret
+ 92 
+ 93 :(before "End Initialize Op Names")
+ 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(
+102       "== code 0x1\n"
+103       // op     ModR/M  SIB   displacement  immediate
+104       "  c3                                           \n"  // return
+105       "== data 0x2000\n"
+106       "10 00 00 00\n"  // 0x10
+107   );
+108   CHECK_TRACE_CONTENTS(
+109       "run: return\n"
+110       "run: popping value 0x00000010\n"
+111       "run: jumping to 0x00000010\n"
+112   );
+113 }
+114 
+115 :(before "End Single-Byte Opcodes")
+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();
+121   break;
+122 }
+
+ + + diff --git a/html/linux/bootstrap/020byte_addressing.cc.html b/html/linux/bootstrap/020byte_addressing.cc.html new file mode 100644 index 00000000..85c86edb --- /dev/null +++ b/html/linux/bootstrap/020byte_addressing.cc.html @@ -0,0 +1,334 @@ + + + + +Mu - linux/bootstrap/020byte_addressing.cc + + + + + + + + + + +https://github.com/akkartik/mu/blob/main/linux/bootstrap/020byte_addressing.cc +
+  1 //: SubX mostly deals with instructions operating on 32-bit operands, but we
+  2 //: still need to deal with raw bytes for strings and so on.
+  3 
+  4 //: Unfortunately the register encodings when dealing with bytes are a mess.
+  5 //: We need a special case for them.
+  6 :(code)
+  7 string rname_8bit(uint8_t r) {
+  8   switch (r) {
+  9   case 0: return "AL";  // lowest byte of EAX
+ 10   case 1: return "CL";  // lowest byte of ECX
+ 11   case 2: return "DL";  // lowest byte of EDX
+ 12   case 3: return "BL";  // lowest byte of EBX
+ 13   case 4: return "AH";  // second lowest byte of EAX
+ 14   case 5: return "CH";  // second lowest byte of ECX
+ 15   case 6: return "DH";  // second lowest byte of EDX
+ 16   case 7: return "BH";  // second lowest byte of EBX
+ 17   default: raise << "invalid 8-bit register " << r << '\n' << end();  return "";
+ 18   }
+ 19 }
+ 20 
+ 21 uint8_t* effective_byte_address(uint8_t modrm) {
+ 22   uint8_t mod = (modrm>>6);
+ 23   uint8_t rm = modrm & 0x7;
+ 24   if (mod == 3) {
+ 25     // select an 8-bit register
+ 26     trace(Callstack_depth+1, "run") << "r/m8 is " << rname_8bit(rm) << end();
+ 27     return reg_8bit(rm);
+ 28   }
+ 29   // the rest is as usual
+ 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
+ 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");
+ 42 
+ 43 :(code)
+ 44 void test_copy_r8_to_mem_at_rm32() {
+ 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
+ 51       // ModR/M in binary: 00 (indirect mode) 011 (src BL) 000 (dest EAX)
+ 52       "== data 0x2000\n"
+ 53       "f0 cc bb aa\n"
+ 54   );
+ 55   CHECK_TRACE_CONTENTS(
+ 56       "run: copy BL to r8/m8-at-r32\n"
+ 57       "run: effective address is 0x00002000 (EAX)\n"
+ 58       "run: storing 0xab\n"
+ 59   );
+ 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();
+ 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 = effective_byte_address(modrm);
+ 70   const uint8_t* src = reg_8bit(rsrc);
+ 71   *dest = *src;  // Read/write multiple elements of vector<uint8_t> at once. Assumes sizeof(int) == 4 on the host as well.
+ 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");
+ 80 
+ 81 :(code)
+ 82 void test_copy_mem_at_rm32_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(
+ 86       "== code 0x1\n"
+ 87       // op     ModR/M  SIB   displacement  immediate
+ 88       "  8a     18                                      \n"  // copy just the byte at *EAX to BL
+ 89       // ModR/M in binary: 00 (indirect mode) 011 (dest EBX) 000 (src EAX)
+ 90       "== data 0x2000\n"
+ 91       "ab ff ff ff\n"  // 0xab with more data in following bytes
+ 92   );
+ 93   CHECK_TRACE_CONTENTS(
+ 94       "run: copy r8/m8-at-r32 to BL\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"
+ 99   );
+100 }
+101 
+102 :(before "End Single-Byte Opcodes")
+103 case 0x8a: {  // copy r/m8 to r8
+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 = effective_byte_address(modrm);
+109   uint8_t* dest = reg_8bit(rdest);
+110   trace(Callstack_depth+1, "run") << "storing 0x" << HEXBYTE << NUM(*src) << end();
+111   *dest = *src;  // Read/write multiple elements of vector<uint8_t> at once. Assumes sizeof(int) == 4 on the host as well.
+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();
+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(
+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'
+125       // ModR/M in binary: 11 (direct mode) 110 (dest 8-bit 'register 6') 011 (src EBX)
+126   );
+127   CHECK_TRACE_CONTENTS(
+128       // ensure 8-bit register '6' is DH, not ESI
+129       "run: copy r8/m8-at-r32 to DH\n"
+130       "run: storing 0x44\n"
+131   );
+132   // ensure ESI is unchanged
+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 with subop 0 (mov)");
+140 
+141 :(code)
+142 void test_copy_imm8_to_mem_at_rm32() {
+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
+148       // ModR/M in binary: 00 (indirect mode) 000 (unused) 000 (dest EAX)
+149       "== data 0x2000\n"
+150       "f0 cc bb aa\n"
+151   );
+152   CHECK_TRACE_CONTENTS(
+153       "run: copy imm8 to r8/m8-at-r32\n"
+154       "run: effective address is 0x00002000 (EAX)\n"
+155       "run: storing 0xdd\n"
+156   );
+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();
+164   trace(Callstack_depth+1, "run") << "copy imm8 to r8/m8-at-r32" << end();
+165   trace(Callstack_depth+1, "run") << "imm8 is 0x" << HEXBYTE << 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";
+169     exit(1);
+170   }
+171   // use unsigned to zero-extend 8-bit value to 32 bits
+172   uint8_t* dest = effective_byte_address(modrm);
+173   *dest = src;  // Write multiple elements of vector<uint8_t> at once. Assumes sizeof(int) == 4 on the host as well.
+174   trace(Callstack_depth+1, "run") << "storing 0x" << HEXBYTE << NUM(*dest) << end();
+175   break;
+176 }
+177 
+178 //:: set flags (setcc)
+179 
+180 :(before "End Initialize Op Names")
+181 put_new(Name_0f, "94", "set r8/m8-at-rm32 to 1 if equal, if ZF is set, 0 otherwise (setcc/setz/sete)");
+182 put_new(Name_0f, "95", "set r8/m8-at-rm32 to 1 if not equal, if ZF is not set, 0 otherwise (setcc/setnz/setne)");
+183 put_new(Name_0f, "9f", "set r8/m8-at-rm32 to 1 if greater, if ZF is unset and SF == OF, 0 otherwise (setcc/setg/setnle)");
+184 put_new(Name_0f, "97", "set r8/m8-at-rm32 to 1 if greater (addr, float), if ZF is unset and CF is unset, 0 otherwise (setcc/seta/setnbe)");
+185 put_new(Name_0f, "9d", "set r8/m8-at-rm32 to 1 if greater or equal, if SF == OF, 0 otherwise (setcc/setge/setnl)");
+186 put_new(Name_0f, "93", "set r8/m8-at-rm32 to 1 if greater or equal (addr, float), if CF is unset, 0 otherwise (setcc/setae/setnb)");
+187 put_new(Name_0f, "9c", "set r8/m8-at-rm32 to 1 if lesser, if SF != OF, 0 otherwise (setcc/setl/setnge)");
+188 put_new(Name_0f, "92", "set r8/m8-at-rm32 to 1 if lesser (addr, float), if CF is set, 0 otherwise (setcc/setb/setnae)");
+189 put_new(Name_0f, "9e", "set r8/m8-at-rm32 to 1 if lesser or equal, if ZF is set or SF != OF, 0 otherwise (setcc/setle/setng)");
+190 put_new(Name_0f, "96", "set r8/m8-at-rm32 to 1 if lesser or equal (addr, float), if ZF is set or CF is set, 0 otherwise (setcc/setbe/setna)");
+191 
+192 :(before "End Two-Byte Opcodes Starting With 0f")
+193 case 0x94: {  // set r8/m8-at-rm32 if ZF
+194   const uint8_t modrm = next();
+195   trace(Callstack_depth+1, "run") << "set r8/m8-at-rm32" << end();
+196   uint8_t* dest = effective_byte_address(modrm);
+197   *dest = ZF;
+198   trace(Callstack_depth+1, "run") << "storing " << NUM(*dest) << end();
+199   break;
+200 }
+201 case 0x95: {  // set r8/m8-at-rm32 if !ZF
+202   const uint8_t modrm = next();
+203   trace(Callstack_depth+1, "run") << "set r8/m8-at-rm32" << end();
+204   uint8_t* dest = effective_byte_address(modrm);
+205   *dest = !ZF;
+206   trace(Callstack_depth+1, "run") << "storing " << NUM(*dest) << end();
+207   break;
+208 }
+209 case 0x9f: {  // set r8/m8-at-rm32 if !SF and !ZF
+210   const uint8_t modrm = next();
+211   trace(Callstack_depth+1, "run") << "set r8/m8-at-rm32" << end();
+212   uint8_t* dest = effective_byte_address(modrm);
+213   *dest = !ZF && SF == OF;
+214   trace(Callstack_depth+1, "run") << "storing " << NUM(*dest) << end();
+215   break;
+216 }
+217 case 0x97: {  // set r8/m8-at-rm32 if !CF and !ZF
+218   const uint8_t modrm = next();
+219   trace(Callstack_depth+1, "run") << "set r8/m8-at-rm32" << end();
+220   uint8_t* dest = effective_byte_address(modrm);
+221   *dest = (!CF && !ZF);
+222   trace(Callstack_depth+1, "run") << "storing " << NUM(*dest) << end();
+223   break;
+224 }
+225 case 0x9d: {  // set r8/m8-at-rm32 if !SF
+226   const uint8_t modrm = next();
+227   trace(Callstack_depth+1, "run") << "set r8/m8-at-rm32" << end();
+228   uint8_t* dest = effective_byte_address(modrm);
+229   *dest = (SF == OF);
+230   trace(Callstack_depth+1, "run") << "storing " << NUM(*dest) << end();
+231   break;
+232 }
+233 case 0x93: {  // set r8/m8-at-rm32 if !CF
+234   const uint8_t modrm = next();
+235   trace(Callstack_depth+1, "run") << "set r8/m8-at-rm32" << end();
+236   uint8_t* dest = effective_byte_address(modrm);
+237   *dest = !CF;
+238   trace(Callstack_depth+1, "run") << "storing " << NUM(*dest) << end();
+239   break;
+240 }
+241 case 0x9c: {  // set r8/m8-at-rm32 if SF and !ZF
+242   const uint8_t modrm = next();
+243   trace(Callstack_depth+1, "run") << "set r8/m8-at-rm32" << end();
+244   uint8_t* dest = effective_byte_address(modrm);
+245   *dest = (SF != OF);
+246   trace(Callstack_depth+1, "run") << "storing " << NUM(*dest) << end();
+247   break;
+248 }
+249 case 0x92: {  // set r8/m8-at-rm32 if CF
+250   const uint8_t modrm = next();
+251   trace(Callstack_depth+1, "run") << "set r8/m8-at-rm32" << end();
+252   uint8_t* dest = effective_byte_address(modrm);
+253   *dest = CF;
+254   trace(Callstack_depth+1, "run") << "storing " << NUM(*dest) << end();
+255   break;
+256 }
+257 case 0x9e: {  // set r8/m8-at-rm32 if SF or ZF
+258   const uint8_t modrm = next();
+259   trace(Callstack_depth+1, "run") << "set r8/m8-at-rm32" << end();
+260   uint8_t* dest = effective_byte_address(modrm);
+261   *dest = (ZF || SF != OF);
+262   trace(Callstack_depth+1, "run") << "storing " << NUM(*dest) << end();
+263   break;
+264 }
+265 case 0x96: {  // set r8/m8-at-rm32 if ZF or CF
+266   const uint8_t modrm = next();
+267   trace(Callstack_depth+1, "run") << "set r8/m8-at-rm32" << end();
+268   uint8_t* dest = effective_byte_address(modrm);
+269   *dest = (ZF || CF);
+270   trace(Callstack_depth+1, "run") << "storing " << NUM(*dest) << end();
+271   break;
+272 }
+
+ + + diff --git a/html/linux/bootstrap/021div.cc.html b/html/linux/bootstrap/021div.cc.html new file mode 100644 index 00000000..6b735134 --- /dev/null +++ b/html/linux/bootstrap/021div.cc.html @@ -0,0 +1,100 @@ + + + + +Mu - linux/bootstrap/021div.cc + + + + + + + + + + +https://github.com/akkartik/mu/blob/main/linux/bootstrap/021div.cc +
+ 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)");
+ 5 
+ 6 :(code)
+ 7 void test_cdq() {
+ 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"
+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();
+24   break;
+25 }
+26 
+27 :(code)
+28 void test_cdq_negative() {
+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"
+37   );
+38 }
+
+ + + diff --git a/html/linux/bootstrap/022float.cc.html b/html/linux/bootstrap/022float.cc.html new file mode 100644 index 00000000..8365cc91 --- /dev/null +++ b/html/linux/bootstrap/022float.cc.html @@ -0,0 +1,582 @@ + + + + +Mu - linux/bootstrap/022float.cc + + + + + + + + + + +https://github.com/akkartik/mu/blob/main/linux/bootstrap/022float.cc +
+  1 //: floating-point operations
+  2 
+  3 //:: copy
+  4 
+  5 :(before "End Initialize Op Names")
+  6 put_new(Name_f3_0f, "10", "copy xm32 to x32 (movss)");
+  7 put_new(Name_f3_0f, "11", "copy x32 to xm32 (movss)");
+  8 
+  9 :(code)
+ 10 void test_copy_x32_to_x32() {
+ 11   Xmm[3] = 0.5;
+ 12   run(
+ 13       "== code 0x1\n"  // code segment
+ 14       // op     ModR/M  SIB   displacement  immediate
+ 15       "f3 0f 11 d8                                    \n"  // copy XMM3 to XMM0
+ 16       // ModR/M in binary: 11 (direct mode) 011 (src XMM3) 000 (dest XMM0)
+ 17   );
+ 18   CHECK_TRACE_CONTENTS(
+ 19       "run: copy XMM3 to x/m32\n"
+ 20       "run: x/m32 is XMM0\n"
+ 21       "run: storing 0.5\n"
+ 22   );
+ 23 }
+ 24 
+ 25 :(before "End Three-Byte Opcodes Starting With f3 0f")
+ 26 case 0x10: {  // copy x/m32 to x32
+ 27   const uint8_t modrm = next();
+ 28   const uint8_t rdest = (modrm>>3)&0x7;
+ 29   trace(Callstack_depth+1, "run") << "copy x/m32 to " << Xname[rdest] << end();
+ 30   float* src = effective_address_float(modrm);
+ 31   Xmm[rdest] = *src;  // Write multiple elements of vector<uint8_t> at once. Assumes sizeof(float) == 4 on the host as well.
+ 32   trace(Callstack_depth+1, "run") << "storing " << Xmm[rdest] << end();
+ 33   break;
+ 34 }
+ 35 case 0x11: {  // copy x32 to x/m32
+ 36   const uint8_t modrm = next();
+ 37   const uint8_t rsrc = (modrm>>3)&0x7;
+ 38   trace(Callstack_depth+1, "run") << "copy " << Xname[rsrc] << " to x/m32" << end();
+ 39   float* dest = effective_address_float(modrm);
+ 40   *dest = Xmm[rsrc];  // Write multiple elements of vector<uint8_t> at once. Assumes sizeof(float) == 4 on the host as well.
+ 41   trace(Callstack_depth+1, "run") << "storing " << *dest << end();
+ 42   break;
+ 43 }
+ 44 
+ 45 :(code)
+ 46 void test_copy_x32_to_mem_at_xm32() {
+ 47   Xmm[3] = 0.5;
+ 48   Reg[EAX].i = 0x60;
+ 49   run(
+ 50       "== code 0x1\n"
+ 51       // op     ModR/M  SIB   displacement  immediate
+ 52       "f3 0f 11 18                                    \n"  // copy XMM3 to *EAX
+ 53       // ModR/M in binary: 00 (indirect mode) 011 (src XMM3) 000 (dest EAX)
+ 54   );
+ 55   CHECK_TRACE_CONTENTS(
+ 56       "run: copy XMM3 to x/m32\n"
+ 57       "run: effective address is 0x00000060 (EAX)\n"
+ 58       "run: storing 0.5\n"
+ 59   );
+ 60 }
+ 61 
+ 62 void test_copy_mem_at_xm32_to_x32() {
+ 63   Reg[EAX].i = 0x2000;
+ 64   run(
+ 65       "== code 0x1\n"
+ 66       // op     ModR/M  SIB   displacement  immediate
+ 67       "f3 0f 10 18                                    \n"  // copy *EAX to XMM3
+ 68       "== data 0x2000\n"
+ 69       "00 00 00 3f\n"  // 0x3f000000 = 0.5
+ 70   );
+ 71   CHECK_TRACE_CONTENTS(
+ 72       "run: copy x/m32 to XMM3\n"
+ 73       "run: effective address is 0x00002000 (EAX)\n"
+ 74       "run: storing 0.5\n"
+ 75   );
+ 76 }
+ 77 
+ 78 //:: convert to floating point
+ 79 
+ 80 :(before "End Initialize Op Names")
+ 81 put_new(Name_f3_0f, "2a", "convert integer to floating-point (cvtsi2ss)");
+ 82 
+ 83 :(code)
+ 84 void test_cvtsi2ss() {
+ 85   Reg[EAX].i = 10;
+ 86   run(
+ 87       "== code 0x1\n"
+ 88       // op     ModR/M  SIB   displacement  immediate
+ 89       "f3 0f 2a c0                                    \n"
+ 90       // ModR/M in binary: 11 (direct mode) 000 (XMM0) 000 (EAX)
+ 91   );
+ 92   CHECK_TRACE_CONTENTS(
+ 93       "run: convert r/m32 to XMM0\n"
+ 94       "run: r/m32 is EAX\n"
+ 95       "run: XMM0 is now 10\n"
+ 96   );
+ 97 }
+ 98 
+ 99 :(before "End Three-Byte Opcodes Starting With f3 0f")
+100 case 0x2a: {  // convert integer to float
+101   const uint8_t modrm = next();
+102   const uint8_t dest = (modrm>>3)&0x7;
+103   trace(Callstack_depth+1, "run") << "convert r/m32 to " << Xname[dest] << end();
+104   const int32_t* src = effective_address(modrm);
+105   Xmm[dest] = *src;
+106   trace(Callstack_depth+1, "run") << Xname[dest] << " is now " << Xmm[dest] << end();
+107   break;
+108 }
+109 
+110 //:: convert floating point to int
+111 
+112 :(before "End Initialize Op Names")
+113 put_new(Name_f3_0f, "2d", "convert floating-point to int (cvtss2si)");
+114 put_new(Name_f3_0f, "2c", "truncate floating-point to int (cvttss2si)");
+115 
+116 :(code)
+117 void test_cvtss2si() {
+118   Xmm[0] = 9.8;
+119   run(
+120       "== code 0x1\n"
+121       // op     ModR/M  SIB   displacement  immediate
+122       "f3 0f 2d c0                                    \n"
+123       // ModR/M in binary: 11 (direct mode) 000 (EAX) 000 (XMM0)
+124   );
+125   CHECK_TRACE_CONTENTS(
+126       "run: convert x/m32 to EAX\n"
+127       "run: x/m32 is XMM0\n"
+128       "run: EAX is now 0x0000000a\n"
+129   );
+130 }
+131 
+132 :(before "End Three-Byte Opcodes Starting With f3 0f")
+133 case 0x2d: {  // convert float to integer
+134   const uint8_t modrm = next();
+135   const uint8_t dest = (modrm>>3)&0x7;
+136   trace(Callstack_depth+1, "run") << "convert x/m32 to " << rname(dest) << end();
+137   const float* src = effective_address_float(modrm);
+138   Reg[dest].i = round(*src);
+139   trace(Callstack_depth+1, "run") << rname(dest) << " is now 0x" << HEXWORD << Reg[dest].i << end();
+140   break;
+141 }
+142 
+143 :(code)
+144 void test_cvttss2si() {
+145   Xmm[0] = 9.8;
+146   run(
+147       "== code 0x1\n"
+148       // op     ModR/M  SIB   displacement  immediate
+149       "f3 0f 2c c0                                    \n"
+150       // ModR/M in binary: 11 (direct mode) 000 (EAX) 000 (XMM0)
+151   );
+152   CHECK_TRACE_CONTENTS(
+153       "run: truncate x/m32 to EAX\n"
+154       "run: x/m32 is XMM0\n"
+155       "run: EAX is now 0x00000009\n"
+156   );
+157 }
+158 
+159 :(before "End Three-Byte Opcodes Starting With f3 0f")
+160 case 0x2c: {  // truncate float to integer
+161   const uint8_t modrm = next();
+162   const uint8_t dest = (modrm>>3)&0x7;
+163   trace(Callstack_depth+1, "run") << "truncate x/m32 to " << rname(dest) << end();
+164   const float* src = effective_address_float(modrm);
+165   Reg[dest].i = trunc(*src);
+166   trace(Callstack_depth+1, "run") << rname(dest) << " is now 0x" << HEXWORD << Reg[dest].i << end();
+167   break;
+168 }
+169 
+170 //:: add
+171 
+172 :(before "End Initialize Op Names")
+173 put_new(Name_f3_0f, "58", "add floats (addss)");
+174 
+175 :(code)
+176 void test_addss() {
+177   Xmm[0] = 3.0;
+178   Xmm[1] = 2.0;
+179   run(
+180       "== code 0x1\n"
+181       // op     ModR/M  SIB   displacement  immediate
+182       "f3 0f 58 c1                                    \n"
+183       // ModR/M in binary: 11 (direct mode) 000 (XMM0) 001 (XMM1)
+184   );
+185   CHECK_TRACE_CONTENTS(
+186       "run: add x/m32 to XMM0\n"
+187       "run: x/m32 is XMM1\n"
+188       "run: XMM0 is now 5\n"
+189   );
+190 }
+191 
+192 :(before "End Three-Byte Opcodes Starting With f3 0f")
+193 case 0x58: {  // add x/m32 to x32
+194   const uint8_t modrm = next();
+195   const uint8_t dest = (modrm>>3)&0x7;
+196   trace(Callstack_depth+1, "run") << "add x/m32 to " << Xname[dest] << end();
+197   const float* src = effective_address_float(modrm);
+198   Xmm[dest] += *src;
+199   trace(Callstack_depth+1, "run") << Xname[dest] << " is now " << Xmm[dest] << end();
+200   break;
+201 }
+202 
+203 //:: subtract
+204 
+205 :(before "End Initialize Op Names")
+206 put_new(Name_f3_0f, "5c", "subtract floats (subss)");
+207 
+208 :(code)
+209 void test_subss() {
+210   Xmm[0] = 3.0;
+211   Xmm[1] = 2.0;
+212   run(
+213       "== code 0x1\n"
+214       // op     ModR/M  SIB   displacement  immediate
+215       "f3 0f 5c c1                                    \n"
+216       // ModR/M in binary: 11 (direct mode) 000 (XMM0) 001 (XMM1)
+217   );
+218   CHECK_TRACE_CONTENTS(
+219       "run: subtract x/m32 from XMM0\n"
+220       "run: x/m32 is XMM1\n"
+221       "run: XMM0 is now 1\n"
+222   );
+223 }
+224 
+225 :(before "End Three-Byte Opcodes Starting With f3 0f")
+226 case 0x5c: {  // subtract x/m32 from x32
+227   const uint8_t modrm = next();
+228   const uint8_t dest = (modrm>>3)&0x7;
+229   trace(Callstack_depth+1, "run") << "subtract x/m32 from " << Xname[dest] << end();
+230   const float* src = effective_address_float(modrm);
+231   Xmm[dest] -= *src;
+232   trace(Callstack_depth+1, "run") << Xname[dest] << " is now " << Xmm[dest] << end();
+233   break;
+234 }
+235 
+236 //:: multiply
+237 
+238 :(before "End Initialize Op Names")
+239 put_new(Name_f3_0f, "59", "multiply floats (mulss)");
+240 
+241 :(code)
+242 void test_mulss() {
+243   Xmm[0] = 3.0;
+244   Xmm[1] = 2.0;
+245   run(
+246       "== code 0x1\n"
+247       // op     ModR/M  SIB   displacement  immediate
+248       "f3 0f 59 c1                                    \n"
+249       // ModR/M in binary: 11 (direct mode) 000 (XMM0) 001 (XMM1)
+250   );
+251   CHECK_TRACE_CONTENTS(
+252       "run: multiply XMM0 by x/m32\n"
+253       "run: x/m32 is XMM1\n"
+254       "run: XMM0 is now 6\n"
+255   );
+256 }
+257 
+258 :(before "End Three-Byte Opcodes Starting With f3 0f")
+259 case 0x59: {  // multiply x32 by x/m32
+260   const uint8_t modrm = next();
+261   const uint8_t dest = (modrm>>3)&0x7;
+262   trace(Callstack_depth+1, "run") << "multiply " << Xname[dest] << " by x/m32" << end();
+263   const float* src = effective_address_float(modrm);
+264   Xmm[dest] *= *src;
+265   trace(Callstack_depth+1, "run") << Xname[dest] << " is now " << Xmm[dest] << end();
+266   break;
+267 }
+268 
+269 //:: divide
+270 
+271 :(before "End Initialize Op Names")
+272 put_new(Name_f3_0f, "5e", "divide floats (divss)");
+273 
+274 :(code)
+275 void test_divss() {
+276   Xmm[0] = 3.0;
+277   Xmm[1] = 2.0;
+278   run(
+279       "== code 0x1\n"
+280       // op     ModR/M  SIB   displacement  immediate
+281       "f3 0f 5e c1                                    \n"
+282       // ModR/M in binary: 11 (direct mode) 000 (XMM0) 001 (XMM1)
+283   );
+284   CHECK_TRACE_CONTENTS(
+285       "run: divide XMM0 by x/m32\n"
+286       "run: x/m32 is XMM1\n"
+287       "run: XMM0 is now 1.5\n"
+288   );
+289 }
+290 
+291 :(before "End Three-Byte Opcodes Starting With f3 0f")
+292 case 0x5e: {  // divide x32 by x/m32
+293   const uint8_t modrm = next();
+294   const uint8_t dest = (modrm>>3)&0x7;
+295   trace(Callstack_depth+1, "run") << "divide " << Xname[dest] << " by x/m32" << end();
+296   const float* src = effective_address_float(modrm);
+297   Xmm[dest] /= *src;
+298   trace(Callstack_depth+1, "run") << Xname[dest] << " is now " << Xmm[dest] << end();
+299   break;
+300 }
+301 
+302 //:: min
+303 
+304 :(before "End Initialize Op Names")
+305 put_new(Name_f3_0f, "5d", "minimum of two floats (minss)");
+306 
+307 :(code)
+308 void test_minss() {
+309   Xmm[0] = 3.0;
+310   Xmm[1] = 2.0;
+311   run(
+312       "== code 0x1\n"
+313       // op     ModR/M  SIB   displacement  immediate
+314       "f3 0f 5d c1                                    \n"
+315       // ModR/M in binary: 11 (direct mode) 000 (XMM0) 001 (XMM1)
+316   );
+317   CHECK_TRACE_CONTENTS(
+318       "run: minimum of XMM0 and x/m32\n"
+319       "run: x/m32 is XMM1\n"
+320       "run: XMM0 is now 2\n"
+321   );
+322 }
+323 
+324 :(before "End Three-Byte Opcodes Starting With f3 0f")
+325 case 0x5d: {  // minimum of x32, x/m32
+326   const uint8_t modrm = next();
+327   const uint8_t dest = (modrm>>3)&0x7;
+328   trace(Callstack_depth+1, "run") << "minimum of " << Xname[dest] << " and x/m32" << end();
+329   const float* src = effective_address_float(modrm);
+330   Xmm[dest] = min(Xmm[dest], *src);
+331   trace(Callstack_depth+1, "run") << Xname[dest] << " is now " << Xmm[dest] << end();
+332   break;
+333 }
+334 
+335 //:: max
+336 
+337 :(before "End Initialize Op Names")
+338 put_new(Name_f3_0f, "5f", "maximum of two floats (maxss)");
+339 
+340 :(code)
+341 void test_maxss() {
+342   Xmm[0] = 3.0;
+343   Xmm[1] = 2.0;
+344   run(
+345       "== code 0x1\n"
+346       // op     ModR/M  SIB   displacement  immediate
+347       "f3 0f 5f c1                                    \n"
+348       // ModR/M in binary: 11 (direct mode) 000 (XMM0) 001 (XMM1)
+349   );
+350   CHECK_TRACE_CONTENTS(
+351       "run: maximum of XMM0 and x/m32\n"
+352       "run: x/m32 is XMM1\n"
+353       "run: XMM0 is now 3\n"
+354   );
+355 }
+356 
+357 :(before "End Three-Byte Opcodes Starting With f3 0f")
+358 case 0x5f: {  // maximum of x32, x/m32
+359   const uint8_t modrm = next();
+360   const uint8_t dest = (modrm>>3)&0x7;
+361   trace(Callstack_depth+1, "run") << "maximum of " << Xname[dest] << " and x/m32" << end();
+362   const float* src = effective_address_float(modrm);
+363   Xmm[dest] = max(Xmm[dest], *src);
+364   trace(Callstack_depth+1, "run") << Xname[dest] << " is now " << Xmm[dest] << end();
+365   break;
+366 }
+367 
+368 //:: reciprocal
+369 
+370 :(before "End Initialize Op Names")
+371 put_new(Name_f3_0f, "53", "reciprocal of float (rcpss)");
+372 
+373 :(code)
+374 void test_rcpss() {
+375   Xmm[1] = 2.0;
+376   run(
+377       "== code 0x1\n"
+378       // op     ModR/M  SIB   displacement  immediate
+379       "f3 0f 53 c1                                    \n"
+380       // ModR/M in binary: 11 (direct mode) 000 (XMM0) 001 (XMM1)
+381   );
+382   CHECK_TRACE_CONTENTS(
+383       "run: reciprocal of x/m32 into XMM0\n"
+384       "run: x/m32 is XMM1\n"
+385       "run: XMM0 is now 0.5\n"
+386   );
+387 }
+388 
+389 :(before "End Three-Byte Opcodes Starting With f3 0f")
+390 case 0x53: {  // reciprocal of x/m32 into x32
+391   const uint8_t modrm = next();
+392   const uint8_t dest = (modrm>>3)&0x7;
+393   trace(Callstack_depth+1, "run") << "reciprocal of x/m32 into " << Xname[dest] << end();
+394   const float* src = effective_address_float(modrm);
+395   Xmm[dest] = 1.0 / *src;
+396   trace(Callstack_depth+1, "run") << Xname[dest] << " is now " << Xmm[dest] << end();
+397   break;
+398 }
+399 
+400 //:: square root
+401 
+402 :(before "End Initialize Op Names")
+403 put_new(Name_f3_0f, "51", "square root of float (sqrtss)");
+404 
+405 :(code)
+406 void test_sqrtss() {
+407   Xmm[1] = 2.0;
+408   run(
+409       "== code 0x1\n"
+410       // op     ModR/M  SIB   displacement  immediate
+411       "f3 0f 51 c1                                    \n"
+412       // ModR/M in binary: 11 (direct mode) 000 (XMM0) 001 (XMM1)
+413   );
+414   CHECK_TRACE_CONTENTS(
+415       "run: square root of x/m32 into XMM0\n"
+416       "run: x/m32 is XMM1\n"
+417       "run: XMM0 is now 1.41421\n"
+418   );
+419 }
+420 
+421 :(before "End Three-Byte Opcodes Starting With f3 0f")
+422 case 0x51: {  // square root of x/m32 into x32
+423   const uint8_t modrm = next();
+424   const uint8_t dest = (modrm>>3)&0x7;
+425   trace(Callstack_depth+1, "run") << "square root of x/m32 into " << Xname[dest] << end();
+426   const float* src = effective_address_float(modrm);
+427   Xmm[dest] = sqrt(*src);
+428   trace(Callstack_depth+1, "run") << Xname[dest] << " is now " << Xmm[dest] << end();
+429   break;
+430 }
+431 
+432 :(before "End Includes")
+433 #include <math.h>
+434 
+435 //:: inverse square root
+436 
+437 :(before "End Initialize Op Names")
+438 put_new(Name_f3_0f, "52", "inverse square root of float (rsqrtss)");
+439 
+440 :(code)
+441 void test_rsqrtss() {
+442   Xmm[1] = 0.01;
+443   run(
+444       "== code 0x1\n"
+445       // op     ModR/M  SIB   displacement  immediate
+446       "f3 0f 52 c1                                    \n"
+447       // ModR/M in binary: 11 (direct mode) 000 (XMM0) 001 (XMM1)
+448   );
+449   CHECK_TRACE_CONTENTS(
+450       "run: inverse square root of x/m32 into XMM0\n"
+451       "run: x/m32 is XMM1\n"
+452       "run: XMM0 is now 10\n"
+453   );
+454 }
+455 
+456 :(before "End Three-Byte Opcodes Starting With f3 0f")
+457 case 0x52: {  // inverse square root of x/m32 into x32
+458   const uint8_t modrm = next();
+459   const uint8_t dest = (modrm>>3)&0x7;
+460   trace(Callstack_depth+1, "run") << "inverse square root of x/m32 into " << Xname[dest] << end();
+461   const float* src = effective_address_float(modrm);
+462   Xmm[dest] = 1.0 / sqrt(*src);
+463   trace(Callstack_depth+1, "run") << Xname[dest] << " is now " << Xmm[dest] << end();
+464   break;
+465 }
+466 
+467 :(code)
+468 float* effective_address_float(uint8_t modrm) {
+469   const uint8_t mod = (modrm>>6);
+470   // ignore middle 3 'reg opcode' bits
+471   const uint8_t rm = modrm & 0x7;
+472   if (mod == 3) {
+473     // mod 3 is just register direct addressing
+474     trace(Callstack_depth+1, "run") << "x/m32 is " << Xname[rm] << end();
+475     return &Xmm[rm];
+476   }
+477   uint32_t addr = effective_address_number(modrm);
+478   trace(Callstack_depth+1, "run") << "effective address contains " << read_mem_f32(addr) << end();
+479   return mem_addr_f32(addr);
+480 }
+481 
+482 //: compare
+483 
+484 :(before "End Initialize Op Names")
+485 put_new(Name_0f, "2f", "compare: set CF if x32 < xm32 (comiss)");
+486 
+487 :(code)
+488 void test_compare_x32_with_mem_at_rm32() {
+489   Reg[EAX].i = 0x2000;
+490   Xmm[3] = 0.5;
+491   run(
+492       "== code 0x1\n"
+493       // op     ModR/M  SIB   displacement  immediate
+494       "  0f 2f  18                                    \n"  // compare XMM3 with *EAX
+495       // ModR/M in binary: 00 (indirect mode) 011 (lhs XMM3) 000 (rhs EAX)
+496       "== data 0x2000\n"
+497       "00 00 00 00\n"  // 0x00000000 = 0.0
+498   );
+499   CHECK_TRACE_CONTENTS(
+500       "run: compare XMM3 with x/m32\n"
+501       "run: effective address is 0x00002000 (EAX)\n"
+502       "run: SF=0; ZF=0; CF=0; OF=0\n"
+503   );
+504 }
+505 
+506 :(before "End Two-Byte Opcodes Starting With 0f")
+507 case 0x2f: {  // set CF if x32 < x/m32
+508   const uint8_t modrm = next();
+509   const uint8_t reg1 = (modrm>>3)&0x7;
+510   trace(Callstack_depth+1, "run") << "compare " << Xname[reg1] << " with x/m32" << end();
+511   const float* arg2 = effective_address_float(modrm);
+512   // Flag settings carefully copied from the Intel manual.
+513   // See also https://stackoverflow.com/questions/7057501/x86-assembler-floating-point-compare/7057771#7057771
+514   SF = ZF = CF = OF = false;
+515   if (Xmm[reg1] == *arg2) ZF = true;
+516   if (Xmm[reg1] < *arg2) CF = true;
+517   trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end();
+518   break;
+519 }
+
+ + + diff --git a/html/linux/bootstrap/029syscalls.cc.html b/html/linux/bootstrap/029syscalls.cc.html new file mode 100644 index 00000000..75df3e81 --- /dev/null +++ b/html/linux/bootstrap/029syscalls.cc.html @@ -0,0 +1,189 @@ + + + + +Mu - linux/bootstrap/029syscalls.cc + + + + + + + + + + +https://github.com/akkartik/mu/blob/main/linux/bootstrap/029syscalls.cc +
+  1 :(before "End Initialize Op Names")
+  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();
+  8   if (code != 0x80) {
+  9     raise << "Unimplemented interrupt code " << HEXBYTE << code << '\n' << end();
+ 10     raise << "  Only `int 80h` supported for now.\n" << end();
+ 11     break;
+ 12   }
+ 13   process_int80();
+ 14   break;
+ 15 }
+ 16 
+ 17 :(code)
+ 18 void process_int80() {
+ 19   switch (Reg[EAX].u) {
+ 20   case 1:
+ 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();
+ 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();
+ 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();
+ 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();
+ 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();
+ 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();
+ 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();
+ 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();
+ 81     break;
+ 82   case 0xa2:  // nanosleep
+ 83     cerr << "not sleeping\n";
+ 84     break;
+ 85   default:
+ 86     raise << HEXWORD << EIP << ": unimplemented syscall " << Reg[EAX].u << '\n' << end();
+ 87   }
+ 88 }
+ 89 
+ 90 // SubX is oblivious to file permissions, directories, symbolic links, terminals, and much else besides.
+ 91 // Also ignoring any concurrency considerations for now.
+ 92 void check_flags(int reg) {
+ 93   uint32_t flags = Reg[reg].u;
+ 94   if (flags != ((flags & O_RDONLY) | (flags & O_WRONLY))) {
+ 95     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";
+ 96     exit(1);
+ 97   }
+ 98   if ((flags & O_RDONLY) && (flags & O_WRONLY)) {
+ 99     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";
+100     exit(1);
+101   }
+102 }
+103 
+104 void check_mode(int reg) {
+105   if (Reg[reg].u != 0600) {
+106     cerr << HEXWORD << EIP << ": SubX is oblivious to file permissions; register " << reg << " must be 0x180.\n";
+107     exit(1);
+108   }
+109 }
+110 
+111 :(before "End Globals")
+112 // Very primitive/fixed/insecure mmap segments for now.
+113 uint32_t Segments_allocated_above = END_HEAP;
+114 :(code)
+115 // always allocate multiples of the segment size
+116 uint32_t new_segment(uint32_t length) {
+117   assert(length > 0);
+118   uint32_t result = (Segments_allocated_above - length) & 0xff000000;  // same number of zeroes as SEGMENT_ALIGNMENT
+119   if (result <= START_HEAP) {
+120     raise << "Allocated too many segments; the VM ran out of memory. "
+121           << "Maybe SEGMENT_ALIGNMENT can be smaller?\n" << die();
+122   }
+123   Mem.push_back(vma(result, result+length));
+124   Segments_allocated_above = result;
+125   return result;
+126 }
+
+ + + diff --git a/html/linux/bootstrap/030translate.cc.html b/html/linux/bootstrap/030translate.cc.html new file mode 100644 index 00000000..c63a7fa8 --- /dev/null +++ b/html/linux/bootstrap/030translate.cc.html @@ -0,0 +1,270 @@ + + + + +Mu - linux/bootstrap/030translate.cc + + + + + + + + + + +https://github.com/akkartik/mu/blob/main/linux/bootstrap/030translate.cc +
+  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 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 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 :(before "End Includes")
+206 using std::ios;
+
+ + + diff --git a/html/linux/bootstrap/031transforms.cc.html b/html/linux/bootstrap/031transforms.cc.html new file mode 100644 index 00000000..4d5b3ff4 --- /dev/null +++ b/html/linux/bootstrap/031transforms.cc.html @@ -0,0 +1,72 @@ + + + + +Mu - linux/bootstrap/031transforms.cc + + + + + + + + + + +https://github.com/akkartik/mu/blob/main/linux/bootstrap/031transforms.cc +
+ 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/linux/bootstrap/032operands.cc.html b/html/linux/bootstrap/032operands.cc.html new file mode 100644 index 00000000..b4d9f1ff --- /dev/null +++ b/html/linux/bootstrap/032operands.cc.html @@ -0,0 +1,703 @@ + + + + +Mu - linux/bootstrap/032operands.cc + + + + + + + + + + +https://github.com/akkartik/mu/blob/main/linux/bootstrap/032operands.cc +
+  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 //: arguments rather than physical bytes. Some arguments 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 arguments 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 arguments.\n"
+ 18   "Each argument has a type. An instruction won't have more than one argument of\n"
+ 19   "any type.\n"
+ 20   "Each instruction has some set of allowed argument types. It'll reject others.\n"
+ 21   "The complete list of argument types: mod, subop, r32 (integer register),\n"
+ 22   "rm32 (integer register or memory), x32 (floating point register),\n"
+ 23   "xm32 (floating point register or memory), scale, index, base, disp8, disp16,\n"
+ 24   "disp32,imm8,imm32.\n"
+ 25   "Each of these has its own help page. Try reading 'bootstrap help mod' next.\n"
+ 26 );
+ 27 :(before "End Help Contents")
+ 28 cerr << "  instructions\n";
+ 29 
+ 30 :(before "Running Test Program")
+ 31 transform(p);
+ 32 if (trace_contains_errors()) return;
+ 33 
+ 34 :(code)
+ 35 void test_pack_immediate_constants() {
+ 36   run(
+ 37       "== code 0x1\n"
+ 38       "bb  0x2a/imm32\n"
+ 39   );
+ 40   CHECK_TRACE_CONTENTS(
+ 41       "transform: packing instruction 'bb 0x2a/imm32'\n"
+ 42       "transform: instruction after packing: 'bb 2a 00 00 00'\n"
+ 43       "run: copy imm32 0x0000002a to EBX\n"
+ 44   );
+ 45 }
+ 46 
+ 47 //: complete set of valid argument types
+ 48 
+ 49 :(before "End Globals")
+ 50 set<string> Instruction_arguments;
+ 51 :(before "End One-time Setup")
+ 52 Instruction_arguments.insert("subop");
+ 53 Instruction_arguments.insert("mod");
+ 54 Instruction_arguments.insert("rm32");
+ 55 Instruction_arguments.insert("xm32");
+ 56 Instruction_arguments.insert("base");
+ 57 Instruction_arguments.insert("index");
+ 58 Instruction_arguments.insert("scale");
+ 59 Instruction_arguments.insert("r32");
+ 60 Instruction_arguments.insert("x32");
+ 61 Instruction_arguments.insert("disp8");
+ 62 Instruction_arguments.insert("disp16");
+ 63 Instruction_arguments.insert("disp32");
+ 64 Instruction_arguments.insert("imm8");
+ 65 Instruction_arguments.insert("imm32");
+ 66 
+ 67 :(before "End Help Texts")
+ 68 init_argument_type_help();
+ 69 :(code)
+ 70 void init_argument_type_help() {
+ 71   put(Help, "mod",
+ 72     "2-bit argument controlling the _addressing mode_ of many instructions,\n"
+ 73     "to determine how to compute the _effective address_ to look up memory at\n"
+ 74     "based on the 'rm32' argument and potentially others.\n"
+ 75     "\n"
+ 76     "If mod = 3, just operate on the contents of the register specified by rm32\n"
+ 77     "            (direct mode).\n"
+ 78     "If mod = 2, effective address is usually* rm32 + disp32\n"
+ 79     "            (indirect mode with displacement).\n"
+ 80     "If mod = 1, effective address is usually* rm32 + disp8\n"
+ 81     "            (indirect mode with displacement).\n"
+ 82     "If mod = 0, effective address is usually* rm32 (indirect mode).\n"
+ 83     "(* - The exception is when rm32 is '4'. Register 4 is the stack pointer (ESP).\n"
+ 84     "     Using it as an address gets more involved. For more details,\n"
+ 85     "     try reading the help pages for 'base', 'index' and 'scale'.)\n"
+ 86     "\n"
+ 87     "For complete details, spend some time with two tables in the IA-32 software\n"
+ 88     "developer's manual that are also included in this repo:\n"
+ 89     "  - modrm.pdf: volume 2, table 2-2, \"32-bit addressing with the ModR/M byte.\".\n"
+ 90     "  - sib.pdf: volume 2, table 2-3, \"32-bit addressing with the SIB byte.\".\n"
+ 91   );
+ 92   put(Help, "subop",
+ 93     "Additional 3-bit argument for determining the instruction when the opcode\n"
+ 94     "is 81, 8f, d3, f7 or ff.\n"
+ 95     "Can't coexist with argument of type 'r32' in a single instruction, because\n"
+ 96     "the two use the same bits.\n"
+ 97   );
+ 98   put(Help, "r32",
+ 99     "3-bit argument specifying an integer register argument used directly,\n"
+100     "without any further addressing modes.\n"
+101   );
+102   put(Help, "x32",
+103     "3-bit argument specifying a floating-point register argument used directly,\n"
+104     "without any further addressing modes.\n"
+105   );
+106   put(Help, "rm32",
+107     "32-bit value in an integer register or memory. The precise details of its\n"
+108     "construction depend on the eponymous 3-bit 'rm32' argument, the 'mod' argument,\n"
+109     "and also potentially the 'SIB' arguments ('scale', 'index' and 'base')\n"
+110     "and a displacement ('disp8' or 'disp32').\n"
+111     "\n"
+112     "For complete details, spend some time with two tables in the IA-32 software\n"
+113     "developer's manual that are also included in this repo:\n"
+114     "  - modrm.pdf: volume 2, table 2-2, \"32-bit addressing with the ModR/M byte.\".\n"
+115     "  - sib.pdf: volume 2, table 2-3, \"32-bit addressing with the SIB byte.\".\n"
+116   );
+117   put(Help, "xm32",
+118     "32-bit value in a floating-point register or memory. The precise details of its\n"
+119     "construction depend on the eponymous 3-bit 'xm32' argument, the 'mod' argument,\n"
+120     "and also potentially the 'SIB' arguments ('scale', 'index' and 'base')\n"
+121     "and a displacement ('disp8' or 'disp32').\n"
+122     "\n"
+123     "For complete details, spend some time with two tables in the IA-32 software\n"
+124     "developer's manual that are also included in this repo:\n"
+125     "  - modrm.pdf: volume 2, table 2-2, \"32-bit addressing with the ModR/M byte.\".\n"
+126     "  - sib.pdf: volume 2, table 2-3, \"32-bit addressing with the SIB byte.\".\n"
+127     "\n"
+128     "One subtlety here: while /xm32 refers to floating-point registers in direct mode\n"
+129     "(when /mod is 3), other addressing modes to construct memory addresses use integer registers\n"
+130     "(just like /rm32). Other than direct mode, its behavior is identical to /rm32.\n"
+131   );
+132   put(Help, "base",
+133     "Additional 3-bit argument (when 'rm32' is 4, unless 'mod' is 3) specifying the\n"
+134     "register containing an address to look up.\n"
+135     "This address may be further modified by 'index' and 'scale' arguments.\n"
+136     "  effective address = base + index*scale + displacement (disp8 or disp32)\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, "index",
+142     "Optional 3-bit argument (when 'rm32' is 4 unless 'mod' is 3) that can be added to\n"
+143     "the 'base' argument to compute the 'effective address' at which to look up memory.\n"
+144     "  effective address = base + index*scale + displacement (disp8 or disp32)\n"
+145     "For complete details, spend some time with the IA-32 software developer's manual,\n"
+146     "volume 2, table 2-3, \"32-bit addressing with the SIB byte\".\n"
+147     "It is included in this repository as 'sib.pdf'.\n"
+148   );
+149   put(Help, "scale",
+150     "Optional 2-bit argument (when 'rm32' is 4 unless 'mod' is 3) that encodes a\n"
+151     "power of 2 to be multiplied to the 'index' argument before adding the result to\n"
+152     "the 'base' argument to compute the _effective address_ to operate on.\n"
+153     "  effective address = base + index * scale + displacement (disp8 or disp32)\n"
+154     "\n"
+155     "When scale is 0, use index unmodified.\n"
+156     "When scale is 1, multiply index by 2.\n"
+157     "When scale is 2, multiply index by 4.\n"
+158     "When scale is 3, multiply index by 8.\n"
+159     "\n"
+160     "For complete details, spend some time with the IA-32 software developer's manual,\n"
+161     "volume 2, table 2-3, \"32-bit addressing with the SIB byte\".\n"
+162     "It is included in this repository as 'sib.pdf'.\n"
+163   );
+164   put(Help, "disp8",
+165     "8-bit value to be added in many instructions.\n"
+166   );
+167   put(Help, "disp16",
+168     "16-bit value to be added in many instructions.\n"
+169     "Currently not used in any SubX instructions.\n"
+170   );
+171   put(Help, "disp32",
+172     "32-bit value to be added in many instructions.\n"
+173   );
+174   put(Help, "imm8",
+175     "8-bit value for many instructions.\n"
+176   );
+177   put(Help, "imm32",
+178     "32-bit value for many instructions.\n"
+179   );
+180 }
+181 
+182 //:: transform packing arguments into bytes in the right order
+183 
+184 :(after "Begin Transforms")
+185 Transform.push_back(pack_arguments);
+186 
+187 :(code)
+188 void pack_arguments(program& p) {
+189   if (p.segments.empty()) return;
+190   segment& code = *find(p, "code");
+191   // Pack Operands(segment code)
+192   trace(3, "transform") << "-- pack arguments" << end();
+193   for (int i = 0;  i < SIZE(code.lines);  ++i) {
+194     line& inst = code.lines.at(i);
+195     if (all_hex_bytes(inst)) continue;
+196     trace(99, "transform") << "packing instruction '" << to_string(/*with metadata*/inst) << "'" << end();
+197     pack_arguments(inst);
+198     trace(99, "transform") << "instruction after packing: '" << to_string(/*without metadata*/inst.words) << "'" << end();
+199   }
+200 }
+201 
+202 void pack_arguments(line& inst) {
+203   line new_inst;
+204   add_opcodes(inst, new_inst);
+205   add_modrm_byte(inst, new_inst);
+206   add_sib_byte(inst, new_inst);
+207   add_disp_bytes(inst, new_inst);
+208   add_imm_bytes(inst, new_inst);
+209   inst.words.swap(new_inst.words);
+210 }
+211 
+212 void add_opcodes(const line& in, line& out) {
+213   out.words.push_back(in.words.at(0));
+214   if (in.words.at(0).data == "0f" || in.words.at(0).data == "f2" || in.words.at(0).data == "f3")
+215     out.words.push_back(in.words.at(1));
+216   if (in.words.at(0).data == "f3" && in.words.at(1).data == "0f")
+217     out.words.push_back(in.words.at(2));
+218   if (in.words.at(0).data == "f2" && in.words.at(1).data == "0f")
+219     out.words.push_back(in.words.at(2));
+220 }
+221 
+222 void add_modrm_byte(const line& in, line& out) {
+223   uint8_t mod=0, reg_subop=0, rm32=0;
+224   bool emit = false;
+225   for (int i = 0;  i < SIZE(in.words);  ++i) {
+226     const word& curr = in.words.at(i);
+227     if (has_argument_metadata(curr, "mod")) {
+228       mod = hex_byte(curr.data);
+229       emit = true;
+230     }
+231     else if (has_argument_metadata(curr, "rm32")) {
+232       rm32 = hex_byte(curr.data);
+233       emit = true;
+234     }
+235     else if (has_argument_metadata(curr, "r32")) {
+236       reg_subop = hex_byte(curr.data);
+237       emit = true;
+238     }
+239     else if (has_argument_metadata(curr, "xm32")) {
+240       rm32 = hex_byte(curr.data);
+241       emit = true;
+242     }
+243     else if (has_argument_metadata(curr, "x32")) {
+244       reg_subop = hex_byte(curr.data);
+245       emit = true;
+246     }
+247     else if (has_argument_metadata(curr, "subop")) {
+248       reg_subop = hex_byte(curr.data);
+249       emit = true;
+250     }
+251   }
+252   if (emit)
+253     out.words.push_back(hex_byte_text((mod << 6) | (reg_subop << 3) | rm32));
+254 }
+255 
+256 void add_sib_byte(const line& in, line& out) {
+257   uint8_t scale=0, index=0, base=0;
+258   bool emit = false;
+259   for (int i = 0;  i < SIZE(in.words);  ++i) {
+260     const word& curr = in.words.at(i);
+261     if (has_argument_metadata(curr, "scale")) {
+262       scale = hex_byte(curr.data);
+263       emit = true;
+264     }
+265     else if (has_argument_metadata(curr, "index")) {
+266       index = hex_byte(curr.data);
+267       emit = true;
+268     }
+269     else if (has_argument_metadata(curr, "base")) {
+270       base = hex_byte(curr.data);
+271       emit = true;
+272     }
+273   }
+274   if (emit)
+275     out.words.push_back(hex_byte_text((scale << 6) | (index << 3) | base));
+276 }
+277 
+278 void add_disp_bytes(const line& in, line& out) {
+279   for (int i = 0;  i < SIZE(in.words);  ++i) {
+280     const word& curr = in.words.at(i);
+281     if (has_argument_metadata(curr, "disp8"))
+282       emit_hex_bytes(out, curr, 1);
+283     if (has_argument_metadata(curr, "disp16"))
+284       emit_hex_bytes(out, curr, 2);
+285     else if (has_argument_metadata(curr, "disp32"))
+286       emit_hex_bytes(out, curr, 4);
+287   }
+288 }
+289 
+290 void add_imm_bytes(const line& in, line& out) {
+291   for (int i = 0;  i < SIZE(in.words);  ++i) {
+292     const word& curr = in.words.at(i);
+293     if (has_argument_metadata(curr, "imm8"))
+294       emit_hex_bytes(out, curr, 1);
+295     else if (has_argument_metadata(curr, "imm32"))
+296       emit_hex_bytes(out, curr, 4);
+297   }
+298 }
+299 
+300 void emit_hex_bytes(line& out, const word& w, int num) {
+301   assert(num <= 4);
+302   bool is_number = looks_like_hex_int(w.data);
+303   if (num == 1 || !is_number) {
+304     out.words.push_back(w);  // preserve existing metadata
+305     if (is_number)
+306       out.words.back().data = hex_byte_to_string(parse_int(w.data));
+307     return;
+308   }
+309   emit_hex_bytes(out, static_cast<uint32_t>(parse_int(w.data)), num);
+310 }
+311 
+312 void emit_hex_bytes(line& out, uint32_t val, int num) {
+313   assert(num <= 4);
+314   for (int i = 0;  i < num;  ++i) {
+315     out.words.push_back(hex_byte_text(val & 0xff));
+316     val = val >> 8;
+317   }
+318 }
+319 
+320 word hex_byte_text(uint8_t val) {
+321   word result;
+322   result.data = hex_byte_to_string(val);
+323   result.original = result.data+"/auto";
+324   return result;
+325 }
+326 
+327 string hex_byte_to_string(uint8_t val) {
+328   ostringstream out;
+329   // uint8_t prints without padding, but int8_t will expand to 32 bits again
+330   out << HEXBYTE << NUM(val);
+331   return out.str();
+332 }
+333 
+334 string to_string(const vector<word>& in) {
+335   ostringstream out;
+336   for (int i = 0;  i < SIZE(in);  ++i) {
+337     if (i > 0) out << ' ';
+338     out << in.at(i).data;
+339   }
+340   return out.str();
+341 }
+342 
+343 :(before "End Unit Tests")
+344 void test_preserve_metadata_when_emitting_single_byte() {
+345   word in;
+346   in.data = "f0";
+347   in.original = "f0/foo";
+348   line out;
+349   emit_hex_bytes(out, in, 1);
+350   CHECK_EQ(out.words.at(0).data, "f0");
+351   CHECK_EQ(out.words.at(0).original, "f0/foo");
+352 }
+353 
+354 :(code)
+355 void test_pack_disp8() {
+356   run(
+357       "== code 0x1\n"
+358       "74 2/disp8\n"  // jump 2 bytes away if ZF is set
+359   );
+360   CHECK_TRACE_CONTENTS(
+361       "transform: packing instruction '74 2/disp8'\n"
+362       "transform: instruction after packing: '74 02'\n"
+363   );
+364 }
+365 
+366 void test_pack_disp8_negative() {
+367   transform(
+368       "== code 0x1\n"
+369       // running this will cause an infinite loop
+370       "74 -1/disp8\n"  // jump 1 byte before if ZF is set
+371   );
+372   CHECK_TRACE_CONTENTS(
+373       "transform: packing instruction '74 -1/disp8'\n"
+374       "transform: instruction after packing: '74 ff'\n"
+375   );
+376 }
+377 
+378 void test_pack_rm32_direct() {
+379   run(
+380       "== code 0x1\n"
+381       // instruction                     effective address                                                   operand     displacement    immediate\n"
+382       // op          subop               mod             rm32          base        index         scale       r32\n"
+383       // 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"
+384       "  01                              3/mod/direct    3/rm32/ebx                                          0/r32/eax                                \n"  // add EAX to EBX
+385   );
+386   CHECK_TRACE_CONTENTS(
+387       "transform: packing instruction '01 3/mod/direct 3/rm32/ebx 0/r32/eax'\n"
+388       "transform: instruction after packing: '01 c3'\n"
+389   );
+390 }
+391 
+392 void test_pack_rm32_indirect() {
+393   transform(
+394       "== code 0x1\n"
+395       // instruction                     effective address                                                   operand     displacement    immediate\n"
+396       // op          subop               mod             rm32          base        index         scale       r32\n"
+397       // 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"
+398       "  01                              0/mod/indirect  3/rm32/ebx                                          0/r32/eax                                \n"  // add EAX to *EBX
+399   );
+400   CHECK_TRACE_CONTENTS(
+401       "transform: packing instruction '01 0/mod/indirect 3/rm32/ebx 0/r32/eax'\n"
+402       "transform: instruction after packing: '01 03'\n"
+403   );
+404 }
+405 
+406 void test_pack_x32() {
+407   run(
+408       "== code 0x1\n"
+409       // instruction                     effective address                                                   operand     displacement    immediate\n"
+410       // op          subop               mod             rm32          base        index         scale       r32\n"
+411       // 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"
+412       "  f3 0f 2a                        3/mod/direct    3/rm32/ebx                                          1/x32                                    \n"  // convert EBX to XMM1
+413   );
+414   CHECK_TRACE_CONTENTS(
+415       "transform: packing instruction 'f3 0f 2a 3/mod/direct 3/rm32/ebx 1/x32'\n"
+416       "transform: instruction after packing: 'f3 0f 2a cb'\n"
+417   );
+418 }
+419 
+420 void test_pack_xm32_direct() {
+421   transform(
+422       "== code 0x1\n"
+423       // instruction                     effective address                                                   operand     displacement    immediate\n"
+424       // op          subop               mod             rm32          base        index         scale       r32\n"
+425       // 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"
+426       "  f3 0f 5e                        3/mod/direct    3/xm32                                              1/x32                                    \n"  // divide XMM1 by XMM3
+427   );
+428   CHECK_TRACE_CONTENTS(
+429       "transform: packing instruction 'f3 0f 5e 3/mod/direct 3/xm32 1/x32'\n"
+430       "transform: instruction after packing: 'f3 0f 5e cb'\n"
+431   );
+432 }
+433 
+434 void test_pack_xm32_indirect() {
+435   transform(
+436       "== code 0x1\n"
+437       // instruction                     effective address                                                   operand     displacement    immediate\n"
+438       // op          subop               mod             rm32          base        index         scale       r32\n"
+439       // 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"
+440       "  f3 0f 5e                        0/mod/indirect  3/rm32/ebx                                          1/x32                                    \n"  // divide XMM1 by *EBX
+441   );
+442   CHECK_TRACE_CONTENTS(
+443       "transform: packing instruction 'f3 0f 5e 0/mod/indirect 3/rm32/ebx 1/x32'\n"
+444       "transform: instruction after packing: 'f3 0f 5e 0b'\n"
+445   );
+446 }
+447 
+448 //: helper for scenario
+449 void transform(const string& text_bytes) {
+450   program p;
+451   istringstream in(text_bytes);
+452   parse(in, p);
+453   if (trace_contains_errors()) return;
+454   transform(p);
+455 }
+456 
+457 void test_pack_modrm_imm32() {
+458   run(
+459       "== code 0x1\n"
+460       // instruction                     effective address                                                   operand     displacement    immediate\n"
+461       // op          subop               mod             rm32          base        index         scale       r32\n"
+462       // 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"
+463       "  81          0/add/subop         3/mod/direct    3/rm32/ebx                                                                      1/imm32      \n"  // add 1 to EBX
+464   );
+465   CHECK_TRACE_CONTENTS(
+466       "transform: packing instruction '81 0/add/subop 3/mod/direct 3/rm32/ebx 1/imm32'\n"
+467       "transform: instruction after packing: '81 c3 01 00 00 00'\n"
+468   );
+469 }
+470 
+471 void test_pack_imm32_large() {
+472   run(
+473       "== code 0x1\n"
+474       "b9  0x080490a7/imm32\n"
+475   );
+476   CHECK_TRACE_CONTENTS(
+477       "transform: packing instruction 'b9 0x080490a7/imm32'\n"
+478       "transform: instruction after packing: 'b9 a7 90 04 08'\n"
+479   );
+480 }
+481 
+482 void test_pack_immediate_constants_hex() {
+483   run(
+484       "== code 0x1\n"
+485       "b9  0x2a/imm32\n"
+486   );
+487   CHECK_TRACE_CONTENTS(
+488       "transform: packing instruction 'b9 0x2a/imm32'\n"
+489       "transform: instruction after packing: 'b9 2a 00 00 00'\n"
+490       "run: copy imm32 0x0000002a to ECX\n"
+491   );
+492 }
+493 
+494 void test_pack_silently_ignores_non_hex() {
+495   Hide_errors = true;
+496   transform(
+497       "== code 0x1\n"
+498       "b9  foo/imm32\n"
+499   );
+500   CHECK_TRACE_CONTENTS(
+501       "transform: packing instruction 'b9 foo/imm32'\n"
+502       // no change (we're just not printing metadata to the trace)
+503       "transform: instruction after packing: 'b9 foo'\n"
+504   );
+505 }
+506 
+507 void test_pack_flags_bad_hex() {
+508   Hide_errors = true;
+509   run(
+510       "== code 0x1\n"
+511       "b9  0xfoo/imm32\n"
+512   );
+513   CHECK_TRACE_CONTENTS(
+514       "error: not a number: 0xfoo\n"
+515   );
+516 }
+517 
+518 void test_pack_flags_uppercase_hex() {
+519   Hide_errors = true;
+520   run(
+521       "== code 0x1\n"
+522       "b9 0xAb/imm32\n"
+523   );
+524   CHECK_TRACE_CONTENTS(
+525       "error: uppercase hex not allowed: 0xAb\n"
+526   );
+527 }
+528 
+529 //:: helpers
+530 
+531 bool all_hex_bytes(const line& inst) {
+532   for (int i = 0;  i < SIZE(inst.words);  ++i)
+533     if (!is_hex_byte(inst.words.at(i)))
+534       return false;
+535   return true;
+536 }
+537 
+538 bool is_hex_byte(const word& curr) {
+539   if (contains_any_argument_metadata(curr))
+540     return false;
+541   if (SIZE(curr.data) != 2)
+542     return false;
+543   if (curr.data.find_first_not_of("0123456789abcdef") != string::npos)
+544     return false;
+545   return true;
+546 }
+547 
+548 bool contains_any_argument_metadata(const word& word) {
+549   for (int i = 0;  i < SIZE(word.metadata);  ++i)
+550     if (Instruction_arguments.find(word.metadata.at(i)) != Instruction_arguments.end())
+551       return true;
+552   return false;
+553 }
+554 
+555 bool has_argument_metadata(const line& inst, const string& m) {
+556   bool result = false;
+557   for (int i = 0;  i < SIZE(inst.words);  ++i) {
+558     if (!has_argument_metadata(inst.words.at(i), m)) continue;
+559     if (result) {
+560       raise << "'" << to_string(inst) << "' has conflicting " << m << " arguments\n" << end();
+561       return false;
+562     }
+563     result = true;
+564   }
+565   return result;
+566 }
+567 
+568 bool has_argument_metadata(const word& w, const string& m) {
+569   bool result = false;
+570   bool metadata_found = false;
+571   for (int i = 0;  i < SIZE(w.metadata);  ++i) {
+572     const string& curr = w.metadata.at(i);
+573     if (Instruction_arguments.find(curr) == Instruction_arguments.end()) continue;  // ignore unrecognized metadata
+574     if (metadata_found) {
+575       raise << "'" << w.original << "' has conflicting argument types; it should have only one\n" << end();
+576       return false;
+577     }
+578     metadata_found = true;
+579     result = (curr == m);
+580   }
+581   return result;
+582 }
+583 
+584 word metadata(const line& inst, const string& m) {
+585   for (int i = 0;  i < SIZE(inst.words);  ++i)
+586     if (has_argument_metadata(inst.words.at(i), m))
+587       return inst.words.at(i);
+588   assert(false);
+589 }
+590 
+591 bool looks_like_hex_int(const string& s) {
+592   if (s.empty()) return false;
+593   if (s.at(0) == '-' || s.at(0) == '+') return true;
+594   if (isdigit(s.at(0))) return true;  // includes '0x' prefix
+595   // End looks_like_hex_int(s) Detectors
+596   return false;
+597 }
+598 
+599 string to_string(const line& inst) {
+600   ostringstream out;
+601   for (int i = 0;  i < SIZE(inst.words);  ++i) {
+602     if (i > 0) out << ' ';
+603     out << inst.words.at(i).original;
+604   }
+605   return out.str();
+606 }
+607 
+608 int32_t parse_int(const string& s) {
+609   if (s.empty()) return 0;
+610   if (contains_uppercase(s)) {
+611     raise << "uppercase hex not allowed: " << s << '\n' << end();
+612     return 0;
+613   }
+614   istringstream in(s);
+615   in >> std::hex;
+616   if (s.at(0) == '-') {
+617     int32_t result = 0;
+618     in >> result;
+619     if (!in || !in.eof()) {
+620       raise << "not a number: " << s << '\n' << end();
+621       return 0;
+622     }
+623     return result;
+624   }
+625   uint32_t uresult = 0;
+626   in >> uresult;
+627   if (!in || !in.eof()) {
+628     raise << "not a number: " << s << '\n' << end();
+629     return 0;
+630   }
+631   return static_cast<int32_t>(uresult);
+632 }
+633 :(before "End Unit Tests")
+634 void test_parse_int() {
+635   CHECK_EQ(0, parse_int("0"));
+636   CHECK_EQ(0, parse_int("0x0"));
+637   CHECK_EQ(0, parse_int("0x0"));
+638   CHECK_EQ(16, parse_int("10"));  // hex always
+639   CHECK_EQ(-1, parse_int("-1"));
+640   CHECK_EQ(-1, parse_int("0xffffffff"));
+641 }
+
+ + + diff --git a/html/linux/bootstrap/033check_operands.cc.html b/html/linux/bootstrap/033check_operands.cc.html new file mode 100644 index 00000000..addf3048 --- /dev/null +++ b/html/linux/bootstrap/033check_operands.cc.html @@ -0,0 +1,851 @@ + + + + +Mu - linux/bootstrap/033check_operands.cc + + + + + + + + + + +https://github.com/akkartik/mu/blob/main/linux/bootstrap/033check_operands.cc +
+  1 //: Since we're tagging arguments with their types, let's start checking these
+  2 //: argument types for each instruction.
+  3 
+  4 void test_check_missing_imm8_argument() {
+  5   Hide_errors = true;
+  6   run(
+  7       "== code 0x1\n"
+  8       "cd\n"  // interrupt ??
+  9   );
+ 10   CHECK_TRACE_CONTENTS(
+ 11       "error: 'cd' (software interrupt): missing imm8 argument\n"
+ 12   );
+ 13 }
+ 14 
+ 15 :(before "Pack Operands(segment code)")
+ 16 check_arguments(code);
+ 17 if (trace_contains_errors()) return;
+ 18 
+ 19 :(code)
+ 20 void check_arguments(const segment& code) {
+ 21   trace(3, "transform") << "-- check arguments" << end();
+ 22   for (int i = 0;  i < SIZE(code.lines);  ++i) {
+ 23     check_arguments(code.lines.at(i));
+ 24     if (trace_contains_errors()) return;  // stop at the first mal-formed instruction
+ 25   }
+ 26 }
+ 27 
+ 28 void check_arguments(const line& inst) {
+ 29   word op = preprocess_op(inst.words.at(0));
+ 30   if (op.data == "0f") {
+ 31     check_arguments_0f(inst);
+ 32     return;
+ 33   }
+ 34   if (op.data == "f3") {
+ 35     check_arguments_f3(inst);
+ 36     return;
+ 37   }
+ 38   check_arguments(inst, op);
+ 39 }
+ 40 
+ 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"))
+ 45     op.data = op.data.substr(2);
+ 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";
+ 54   CHECK_EQ(preprocess_op(w1).data, preprocess_op(w2).data);
+ 55 }
+ 56 
+ 57 //: To check the arguments for an opcode, we'll track the permitted arguments
+ 58 //: for each supported opcode in a bitvector. That way we can often compute the
+ 59 //: 'received' argument bitvector for each instruction's arguments and compare
+ 60 //: it with the 'expected' bitvector.
+ 61 //:
+ 62 //: The 'expected' and 'received' bitvectors can be different; the MODRM bit
+ 63 //: in the 'expected' bitvector maps to multiple 'received' argument types in
+ 64 //: an instruction. We deal in expected bitvectors throughout.
+ 65 
+ 66 :(before "End Types")
+ 67 enum expected_argument_type {
+ 68   // start from the least significant bit
+ 69   MODRM,  // more complex, may also involve disp8 or disp32
+ 70   SUBOP,
+ 71   DISP8,
+ 72   DISP16,
+ 73   DISP32,
+ 74   IMM8,
+ 75   IMM32,
+ 76   NUM_OPERAND_TYPES
+ 77 };
+ 78 :(before "End Globals")
+ 79 vector<string> Operand_type_name;
+ 80 map<string, expected_argument_type> Operand_type;
+ 81 :(before "End One-time Setup")
+ 82 init_op_types();
+ 83 :(code)
+ 84 void init_op_types() {
+ 85   assert(NUM_OPERAND_TYPES <= /*bits in a uint8_t*/8);
+ 86   Operand_type_name.resize(NUM_OPERAND_TYPES);
+ 87   #define DEF(type) Operand_type_name.at(type) = tolower(#type), put(Operand_type, tolower(#type), type);
+ 88   DEF(MODRM);
+ 89   DEF(SUBOP);
+ 90   DEF(DISP8);
+ 91   DEF(DISP16);
+ 92   DEF(DISP32);
+ 93   DEF(IMM8);
+ 94   DEF(IMM32);
+ 95   #undef DEF
+ 96 }
+ 97 
+ 98 :(before "End Globals")
+ 99 map</*op*/string, /*bitvector*/uint8_t> Permitted_arguments;
+100 const uint8_t INVALID_OPERANDS = 0xff;  // no instruction uses all the argument types
+101 :(before "End One-time Setup")
+102 init_permitted_arguments();
+103 :(code)
+104 void init_permitted_arguments() {
+105   //// Class A: just op, no arguments
+106   // halt
+107   put(Permitted_arguments, "f4", 0x00);
+108   // inc
+109   put(Permitted_arguments, "40", 0x00);
+110   put(Permitted_arguments, "41", 0x00);
+111   put(Permitted_arguments, "42", 0x00);
+112   put(Permitted_arguments, "43", 0x00);
+113   put(Permitted_arguments, "44", 0x00);
+114   put(Permitted_arguments, "45", 0x00);
+115   put(Permitted_arguments, "46", 0x00);
+116   put(Permitted_arguments, "47", 0x00);
+117   // dec
+118   put(Permitted_arguments, "48", 0x00);
+119   put(Permitted_arguments, "49", 0x00);
+120   put(Permitted_arguments, "4a", 0x00);
+121   put(Permitted_arguments, "4b", 0x00);
+122   put(Permitted_arguments, "4c", 0x00);
+123   put(Permitted_arguments, "4d", 0x00);
+124   put(Permitted_arguments, "4e", 0x00);
+125   put(Permitted_arguments, "4f", 0x00);
+126   // push
+127   put(Permitted_arguments, "50", 0x00);
+128   put(Permitted_arguments, "51", 0x00);
+129   put(Permitted_arguments, "52", 0x00);
+130   put(Permitted_arguments, "53", 0x00);
+131   put(Permitted_arguments, "54", 0x00);
+132   put(Permitted_arguments, "55", 0x00);
+133   put(Permitted_arguments, "56", 0x00);
+134   put(Permitted_arguments, "57", 0x00);
+135   // pop
+136   put(Permitted_arguments, "58", 0x00);
+137   put(Permitted_arguments, "59", 0x00);
+138   put(Permitted_arguments, "5a", 0x00);
+139   put(Permitted_arguments, "5b", 0x00);
+140   put(Permitted_arguments, "5c", 0x00);
+141   put(Permitted_arguments, "5d", 0x00);
+142   put(Permitted_arguments, "5e", 0x00);
+143   put(Permitted_arguments, "5f", 0x00);
+144   // sign-extend EAX into EDX
+145   put(Permitted_arguments, "99", 0x00);
+146   // return
+147   put(Permitted_arguments, "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_arguments, "eb", 0x04);
+155   put(Permitted_arguments, "72", 0x04);
+156   put(Permitted_arguments, "73", 0x04);
+157   put(Permitted_arguments, "74", 0x04);
+158   put(Permitted_arguments, "75", 0x04);
+159   put(Permitted_arguments, "76", 0x04);
+160   put(Permitted_arguments, "77", 0x04);
+161   put(Permitted_arguments, "7c", 0x04);
+162   put(Permitted_arguments, "7d", 0x04);
+163   put(Permitted_arguments, "7e", 0x04);
+164   put(Permitted_arguments, "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_arguments, "e8", 0x10);  // call
+170   put(Permitted_arguments, "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_arguments, "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_arguments, "05", 0x40);  // add
+181   put(Permitted_arguments, "2d", 0x40);  // subtract
+182   put(Permitted_arguments, "25", 0x40);  // and
+183   put(Permitted_arguments, "0d", 0x40);  // or
+184   put(Permitted_arguments, "35", 0x40);  // xor
+185   put(Permitted_arguments, "3d", 0x40);  // compare
+186   put(Permitted_arguments, "68", 0x40);  // push
+187   // copy
+188   put(Permitted_arguments, "b8", 0x40);
+189   put(Permitted_arguments, "b9", 0x40);
+190   put(Permitted_arguments, "ba", 0x40);
+191   put(Permitted_arguments, "bb", 0x40);
+192   put(Permitted_arguments, "bc", 0x40);
+193   put(Permitted_arguments, "bd", 0x40);
+194   put(Permitted_arguments, "be", 0x40);
+195   put(Permitted_arguments, "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_arguments, "01", 0x01);
+203   put(Permitted_arguments, "03", 0x01);
+204   // subtract
+205   put(Permitted_arguments, "29", 0x01);
+206   put(Permitted_arguments, "2b", 0x01);
+207   // and
+208   put(Permitted_arguments, "21", 0x01);
+209   put(Permitted_arguments, "23", 0x01);
+210   // or
+211   put(Permitted_arguments, "09", 0x01);
+212   put(Permitted_arguments, "0b", 0x01);
+213   // xor
+214   put(Permitted_arguments, "31", 0x01);
+215   put(Permitted_arguments, "33", 0x01);
+216   // compare
+217   put(Permitted_arguments, "39", 0x01);
+218   put(Permitted_arguments, "3b", 0x01);
+219   // copy
+220   put(Permitted_arguments, "88", 0x01);
+221   put(Permitted_arguments, "89", 0x01);
+222   put(Permitted_arguments, "8a", 0x01);
+223   put(Permitted_arguments, "8b", 0x01);
+224   // swap
+225   put(Permitted_arguments, "87", 0x01);
+226   // copy address (lea)
+227   put(Permitted_arguments, "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_arguments, "8f", 0x03);  // pop
+233   put(Permitted_arguments, "d3", 0x03);  // shift
+234   put(Permitted_arguments, "f7", 0x03);  // test/not/mul/div
+235   put(Permitted_arguments, "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_arguments, "c1", 0x23);  // combine
+241   put(Permitted_arguments, "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_arguments, "81", 0x43);  // combine
+247   put(Permitted_arguments, "c7", 0x43);  // copy
+248 
+249   //// Class Q: op, ModR/M and imm32
+250   //  imm32 imm8  disp32 |disp16  disp8 subop modrm
+251   //  1     0     0      |0       0     0     1
+252   put(Permitted_arguments, "69", 0x41);  // multiply
+253 
+254   // End Init Permitted Operands
+255 }
+256 
+257 #define HAS(bitvector, bit)  ((bitvector) & (1 << (bit)))
+258 #define SET(bitvector, bit)  ((bitvector) | (1 << (bit)))
+259 #define CLEAR(bitvector, bit)  ((bitvector) & (~(1 << (bit))))
+260 
+261 void check_arguments(const line& inst, const word& op) {
+262   if (!is_hex_byte(op)) return;
+263   uint8_t expected_bitvector = get(Permitted_arguments, op.data);
+264   if (HAS(expected_bitvector, MODRM)) {
+265     check_arguments_modrm(inst, op);
+266     compare_bitvector_modrm(inst, expected_bitvector, maybe_name(op));
+267   }
+268   else {
+269     compare_bitvector(inst, expected_bitvector, maybe_name(op));
+270   }
+271 }
+272 
+273 //: Many instructions can be checked just by comparing bitvectors.
+274 
+275 void compare_bitvector(const line& inst, uint8_t expected, const string& maybe_op_name) {
+276   if (all_hex_bytes(inst) && has_arguments(inst)) return;  // deliberately programming in raw hex; we'll raise a warning elsewhere
+277   uint8_t bitvector = compute_expected_argument_bitvector(inst);
+278   if (trace_contains_errors()) return;  // duplicate argument type
+279   if (bitvector == expected) return;  // all good with this instruction
+280   for (int i = 0;  i < NUM_OPERAND_TYPES;  ++i, bitvector >>= 1, expected >>= 1) {
+281 //?     cerr << "comparing " << HEXBYTE << NUM(bitvector) << " with " << NUM(expected) << '\n';
+282     if ((bitvector & 0x1) == (expected & 0x1)) continue;  // all good with this argument
+283     const string& optype = Operand_type_name.at(i);
+284     if ((bitvector & 0x1) > (expected & 0x1))
+285       raise << "'" << to_string(inst) << "'" << maybe_op_name << ": unexpected " << optype << " argument\n" << end();
+286     else
+287       raise << "'" << to_string(inst) << "'" << maybe_op_name << ": missing " << optype << " argument\n" << end();
+288     // continue giving all errors for a single instruction
+289   }
+290   // ignore settings in any unused bits
+291 }
+292 
+293 string maybe_name(const word& op) {
+294   if (!is_hex_byte(op)) return "";
+295   if (!contains_key(Name, op.data)) return "";
+296   // strip stuff in parens from the name
+297   const string& s = get(Name, op.data);
+298   return " ("+s.substr(0, s.find(" ("))+')';
+299 }
+300 
+301 uint32_t compute_expected_argument_bitvector(const line& inst) {
+302   set<string> arguments_found;
+303   uint32_t bitvector = 0;
+304   for (int i = /*skip op*/1;  i < SIZE(inst.words);  ++i) {
+305     bitvector = bitvector | expected_bit_for_received_argument(inst.words.at(i), arguments_found, inst);
+306     if (trace_contains_errors()) return INVALID_OPERANDS;  // duplicate argument type
+307   }
+308   return bitvector;
+309 }
+310 
+311 bool has_arguments(const line& inst) {
+312   return SIZE(inst.words) > first_argument(inst);
+313 }
+314 
+315 int first_argument(const line& inst) {
+316   if (inst.words.at(0).data == "0f") return 2;
+317   if (inst.words.at(0).data == "f2" || inst.words.at(0).data == "f3") {
+318     if (inst.words.at(1).data == "0f")
+319       return 3;
+320     else
+321       return 2;
+322   }
+323   return 1;
+324 }
+325 
+326 // Scan the metadata of 'w' and return the expected bit corresponding to any argument type.
+327 // Also raise an error if metadata contains multiple argument types.
+328 uint32_t expected_bit_for_received_argument(const word& w, set<string>& instruction_arguments, const line& inst) {
+329   uint32_t bv = 0;
+330   bool found = false;
+331   for (int i = 0;  i < SIZE(w.metadata);  ++i) {
+332     string/*copy*/ curr = w.metadata.at(i);
+333     string expected_metadata = curr;
+334     if (curr == "mod" || curr == "rm32" || curr == "r32" || curr == "xm32" || curr == "x32" || curr == "scale" || curr == "index" || curr == "base")
+335       expected_metadata = "modrm";
+336     else if (!contains_key(Operand_type, curr)) continue;  // ignore unrecognized metadata
+337     if (found) {
+338       raise << "'" << w.original << "' has conflicting argument types; it should have only one\n" << end();
+339       return INVALID_OPERANDS;
+340     }
+341     if (instruction_arguments.find(curr) != instruction_arguments.end()) {
+342       raise << "'" << to_string(inst) << "': duplicate " << curr << " argument\n" << end();
+343       return INVALID_OPERANDS;
+344     }
+345     instruction_arguments.insert(curr);
+346     bv = (1 << get(Operand_type, expected_metadata));
+347     found = true;
+348   }
+349   return bv;
+350 }
+351 
+352 void test_conflicting_argument_type() {
+353   Hide_errors = true;
+354   run(
+355       "== code 0x1\n"
+356       "cd/software-interrupt 80/imm8/imm32\n"
+357   );
+358   CHECK_TRACE_CONTENTS(
+359       "error: '80/imm8/imm32' has conflicting argument types; it should have only one\n"
+360   );
+361 }
+362 
+363 //: Instructions computing effective addresses have more complex rules, so
+364 //: we'll hard-code a common set of instruction-decoding rules.
+365 
+366 void test_check_missing_mod_argument() {
+367   Hide_errors = true;
+368   run(
+369       "== code 0x1\n"
+370       "81 0/add/subop       3/rm32/ebx 1/imm32\n"
+371   );
+372   CHECK_TRACE_CONTENTS(
+373       "error: '81 0/add/subop 3/rm32/ebx 1/imm32' (combine rm32 with imm32 based on subop): missing mod argument\n"
+374   );
+375 }
+376 
+377 void check_arguments_modrm(const line& inst, const word& op) {
+378   if (all_hex_bytes(inst)) return;  // deliberately programming in raw hex; we'll raise a warning elsewhere
+379   check_argument_metadata_present(inst, "mod", op);
+380   if (!has_argument_metadata(inst, "rm32") && !has_argument_metadata(inst, "xm32"))
+381     raise << "'" << to_string(inst) << "'" << maybe_name(op) << ": missing rm32 (or xm32) argument\n" << end();
+382   // no check for r32; some instructions don't use it; just assume it's 0 if missing
+383   if (op.data == "81" || op.data == "8f" || op.data == "f7" || op.data == "ff") {  // keep sync'd with 'help subop'
+384     check_argument_metadata_present(inst, "subop", op);
+385     check_argument_metadata_absent(inst, "r32", op, "should be replaced by subop");
+386     check_argument_metadata_absent(inst, "x32", op, "should be replaced by subop");
+387   }
+388   if (trace_contains_errors()) return;
+389   if (metadata_m32(inst).data != "4") return;
+390   // SIB byte checks
+391   uint8_t mod = hex_byte(metadata(inst, "mod").data);
+392   if (mod != /*direct*/3) {
+393     check_argument_metadata_present(inst, "base", op);
+394     check_argument_metadata_present(inst, "index", op);  // otherwise why go to SIB?
+395   }
+396   else {
+397     check_argument_metadata_absent(inst, "base", op, "direct mode");
+398     check_argument_metadata_absent(inst, "index", op, "direct mode");
+399   }
+400   // no check for scale; 0 (2**0 = 1) by default
+401 }
+402 
+403 word metadata_m32(const line& inst) {
+404   for (int i = 0;  i < SIZE(inst.words);  ++i)
+405     if (has_argument_metadata(inst.words.at(i), "rm32") || has_argument_metadata(inst.words.at(i), "xm32"))
+406       return inst.words.at(i);
+407   assert(false);
+408 }
+409 
+410 // same as compare_bitvector, with one additional exception for modrm-based
+411 // instructions: they may use an extra displacement on occasion
+412 void compare_bitvector_modrm(const line& inst, uint8_t expected, const string& maybe_op_name) {
+413   if (all_hex_bytes(inst) && has_arguments(inst)) return;  // deliberately programming in raw hex; we'll raise a warning elsewhere
+414   uint8_t bitvector = compute_expected_argument_bitvector(inst);
+415   if (trace_contains_errors()) return;  // duplicate argument type
+416   // update 'expected' bitvector for the additional exception
+417   if (has_argument_metadata(inst, "mod")) {
+418     int32_t mod = parse_int(metadata(inst, "mod").data);
+419     switch (mod) {
+420     case 0:
+421       if (has_argument_metadata(inst, "rm32") && parse_int(metadata(inst, "rm32").data) == 5)
+422         expected |= (1<<DISP32);
+423       break;
+424     case 1:
+425       expected |= (1<<DISP8);
+426       break;
+427     case 2:
+428       expected |= (1<<DISP32);
+429       break;
+430     }
+431   }
+432   if (bitvector == expected) return;  // all good with this instruction
+433   for (int i = 0;  i < NUM_OPERAND_TYPES;  ++i, bitvector >>= 1, expected >>= 1) {
+434 //?     cerr << "comparing for modrm " << HEXBYTE << NUM(bitvector) << " with " << NUM(expected) << '\n';
+435     if ((bitvector & 0x1) == (expected & 0x1)) continue;  // all good with this argument
+436     const string& optype = Operand_type_name.at(i);
+437     if ((bitvector & 0x1) > (expected & 0x1))
+438       raise << "'" << to_string(inst) << "'" << maybe_op_name << ": unexpected " << optype << " argument\n" << end();
+439     else
+440       raise << "'" << to_string(inst) << "'" << maybe_op_name << ": missing " << optype << " argument\n" << end();
+441     // continue giving all errors for a single instruction
+442   }
+443   // ignore settings in any unused bits
+444 }
+445 
+446 void check_argument_metadata_present(const line& inst, const string& type, const word& op) {
+447   if (!has_argument_metadata(inst, type))
+448     raise << "'" << to_string(inst) << "'" << maybe_name(op) << ": missing " << type << " argument\n" << end();
+449 }
+450 
+451 void check_argument_metadata_absent(const line& inst, const string& type, const word& op, const string& msg) {
+452   if (has_argument_metadata(inst, type))
+453     raise << "'" << to_string(inst) << "'" << maybe_name(op) << ": unexpected " << type << " argument (" << msg << ")\n" << end();
+454 }
+455 
+456 void test_modrm_with_displacement() {
+457   Reg[EAX].u = 0x1;
+458   transform(
+459       "== code 0x1\n"
+460       // just avoid null pointer
+461       "8b/copy 1/mod/lookup+disp8 0/rm32/EAX 2/r32/EDX 4/disp8\n"  // copy *(EAX+4) to EDX
+462   );
+463   CHECK_TRACE_COUNT("error", 0);
+464 }
+465 
+466 void test_check_missing_disp8() {
+467   Hide_errors = true;
+468   transform(
+469       "== code 0x1\n"
+470       "89/copy 1/mod/lookup+disp8 0/rm32/EAX 1/r32/ECX\n"  // missing disp8
+471   );
+472   CHECK_TRACE_CONTENTS(
+473       "error: '89/copy 1/mod/lookup+disp8 0/rm32/EAX 1/r32/ECX' (copy r32 to rm32): missing disp8 argument\n"
+474   );
+475 }
+476 
+477 void test_check_missing_disp32() {
+478   Hide_errors = true;
+479   transform(
+480       "== code 0x1\n"
+481       "8b/copy 0/mod/indirect 5/rm32/.disp32 2/r32/EDX\n"  // missing disp32
+482   );
+483   CHECK_TRACE_CONTENTS(
+484       "error: '8b/copy 0/mod/indirect 5/rm32/.disp32 2/r32/EDX' (copy rm32 to r32): missing disp32 argument\n"
+485   );
+486 }
+487 
+488 void test_conflicting_arguments_in_modrm_instruction() {
+489   Hide_errors = true;
+490   run(
+491       "== code 0x1\n"
+492       "01/add 0/mod 3/mod\n"
+493   );
+494   CHECK_TRACE_CONTENTS(
+495       "error: '01/add 0/mod 3/mod' has conflicting mod arguments\n"
+496   );
+497 }
+498 
+499 void test_conflicting_argument_type_modrm() {
+500   Hide_errors = true;
+501   run(
+502       "== code 0x1\n"
+503       "01/add 0/mod 3/rm32/r32\n"
+504   );
+505   CHECK_TRACE_CONTENTS(
+506       "error: '3/rm32/r32' has conflicting argument types; it should have only one\n"
+507   );
+508 }
+509 
+510 void test_check_missing_rm32_argument() {
+511   Hide_errors = true;
+512   run(
+513       "== code 0x1\n"
+514       "81 0/add/subop 0/mod            1/imm32\n"
+515   );
+516   CHECK_TRACE_CONTENTS(
+517       "error: '81 0/add/subop 0/mod 1/imm32' (combine rm32 with imm32 based on subop): missing rm32 (or xm32) argument\n"
+518   );
+519 }
+520 
+521 void test_check_missing_subop_argument() {
+522   Hide_errors = true;
+523   run(
+524       "== code 0x1\n"
+525       "81             0/mod 3/rm32/ebx 1/imm32\n"
+526   );
+527   CHECK_TRACE_CONTENTS(
+528       "error: '81 0/mod 3/rm32/ebx 1/imm32' (combine rm32 with imm32 based on subop): missing subop argument\n"
+529   );
+530 }
+531 
+532 void test_check_missing_base_argument() {
+533   Hide_errors = true;
+534   run(
+535       "== code 0x1\n"
+536       "81 0/add/subop 0/mod/indirect 4/rm32/use-sib 1/imm32\n"
+537   );
+538   CHECK_TRACE_CONTENTS(
+539       "error: '81 0/add/subop 0/mod/indirect 4/rm32/use-sib 1/imm32' (combine rm32 with imm32 based on subop): missing base argument\n"
+540   );
+541 }
+542 
+543 void test_check_missing_index_argument() {
+544   Hide_errors = true;
+545   run(
+546       "== code 0x1\n"
+547       "81 0/add/subop 0/mod/indirect 4/rm32/use-sib 0/base 1/imm32\n"
+548   );
+549   CHECK_TRACE_CONTENTS(
+550       "error: '81 0/add/subop 0/mod/indirect 4/rm32/use-sib 0/base 1/imm32' (combine rm32 with imm32 based on subop): missing index argument\n"
+551   );
+552 }
+553 
+554 void test_check_missing_base_argument_2() {
+555   Hide_errors = true;
+556   run(
+557       "== code 0x1\n"
+558       "81 0/add/subop 0/mod/indirect 4/rm32/use-sib 2/index 3/scale 1/imm32\n"
+559   );
+560   CHECK_TRACE_CONTENTS(
+561       "error: '81 0/add/subop 0/mod/indirect 4/rm32/use-sib 2/index 3/scale 1/imm32' (combine rm32 with imm32 based on subop): missing base argument\n"
+562   );
+563 }
+564 
+565 void test_check_extra_displacement() {
+566   Hide_errors = true;
+567   run(
+568       "== code 0x1\n"
+569       "89/copy 0/mod/indirect 0/rm32/EAX 1/r32/ECX 4/disp8\n"
+570   );
+571   CHECK_TRACE_CONTENTS(
+572       "error: '89/copy 0/mod/indirect 0/rm32/EAX 1/r32/ECX 4/disp8' (copy r32 to rm32): unexpected disp8 argument\n"
+573   );
+574 }
+575 
+576 void test_check_duplicate_argument() {
+577   Hide_errors = true;
+578   run(
+579       "== code 0x1\n"
+580       "89/copy 0/mod/indirect 0/rm32/EAX 1/r32/ECX 1/r32\n"
+581   );
+582   CHECK_TRACE_CONTENTS(
+583       "error: '89/copy 0/mod/indirect 0/rm32/EAX 1/r32/ECX 1/r32': duplicate r32 argument\n"
+584   );
+585 }
+586 
+587 void test_check_base_argument_not_needed_in_direct_mode() {
+588   run(
+589       "== code 0x1\n"
+590       "81 0/add/subop 3/mod/indirect 4/rm32/use-sib 1/imm32\n"
+591   );
+592   CHECK_TRACE_COUNT("error", 0);
+593 }
+594 
+595 void test_extra_modrm() {
+596   Hide_errors = true;
+597   run(
+598       "== code 0x1\n"
+599       "59/pop-to-ECX  3/mod/direct 1/rm32/ECX 4/r32/ESP\n"
+600   );
+601   CHECK_TRACE_CONTENTS(
+602       "error: '59/pop-to-ECX 3/mod/direct 1/rm32/ECX 4/r32/ESP' (pop top of stack to ECX): unexpected modrm argument\n"
+603   );
+604 }
+605 
+606 //:: similarly handle multi-byte opcodes
+607 
+608 void check_arguments_0f(const line& inst) {
+609   assert(inst.words.at(0).data == "0f");
+610   if (SIZE(inst.words) == 1) {
+611     raise << "opcode '0f' requires a second opcode\n" << end();
+612     return;
+613   }
+614   word op = preprocess_op(inst.words.at(1));
+615   if (!contains_key(Name_0f, op.data)) {
+616     raise << "unknown 2-byte opcode '0f " << op.data << "'\n" << end();
+617     return;
+618   }
+619   check_arguments_0f(inst, op);
+620 }
+621 
+622 void check_arguments_f3(const line& inst) {
+623   assert(inst.words.at(0).data == "f3");
+624   if (SIZE(inst.words) == 1) {
+625     raise << "opcode 'f3' requires a second opcode\n" << end();
+626     return;
+627   }
+628   word op = preprocess_op(inst.words.at(1));
+629   if (op.data == "0f") {
+630     word op2 = preprocess_op(inst.words.at(2));
+631     check_arguments_f3_0f(inst, op2);
+632     return;
+633   }
+634   if (!contains_key(Name_f3, op.data)) {
+635     raise << "unknown 2-byte opcode 'f3 " << op.data << "'\n" << end();
+636     return;
+637   }
+638   check_arguments_f3(inst, op);
+639 }
+640 
+641 void test_check_missing_disp32_argument() {
+642   Hide_errors = true;
+643   run(
+644       "== code 0x1\n"
+645       "  0f 84  # jmp if ZF to ??\n"
+646   );
+647   CHECK_TRACE_CONTENTS(
+648       "error: '0f 84' (jump disp32 bytes away if equal, if ZF is set): missing disp32 argument\n"
+649   );
+650 }
+651 
+652 void test_0f_opcode_with_modrm() {
+653   transform(
+654       "== code 0x1\n"
+655       "0f af/multiply 2/mod/*+disp32 5/rm32/ebp 8/disp32 0/r32\n"
+656   );
+657   CHECK_TRACE_DOESNT_CONTAIN_ERRORS();
+658 }
+659 
+660 :(before "End Globals")
+661 map</*op*/string, /*bitvector*/uint8_t> Permitted_arguments_0f;
+662 :(before "End Init Permitted Operands")
+663 //// Class D: just op and disp32
+664 //  imm32 imm8  disp32 |disp16  disp8 subop modrm
+665 //  0     0     1      |0       0     0     0
+666 put_new(Permitted_arguments_0f, "82", 0x10);
+667 put_new(Permitted_arguments_0f, "83", 0x10);
+668 put_new(Permitted_arguments_0f, "84", 0x10);
+669 put_new(Permitted_arguments_0f, "85", 0x10);
+670 put_new(Permitted_arguments_0f, "86", 0x10);
+671 put_new(Permitted_arguments_0f, "87", 0x10);
+672 put_new(Permitted_arguments_0f, "8c", 0x10);
+673 put_new(Permitted_arguments_0f, "8d", 0x10);
+674 put_new(Permitted_arguments_0f, "8e", 0x10);
+675 put_new(Permitted_arguments_0f, "8f", 0x10);
+676 
+677 //// Class M: using ModR/M byte
+678 //  imm32 imm8  disp32 |disp16  disp8 subop modrm
+679 //  0     0     0      |0       0     0     1
+680 put_new(Permitted_arguments_0f, "2f", 0x01);  // compare floats
+681 put_new(Permitted_arguments_0f, "af", 0x01);  // multiply ints
+682 // setcc
+683 put_new(Permitted_arguments_0f, "92", 0x01);
+684 put_new(Permitted_arguments_0f, "93", 0x01);
+685 put_new(Permitted_arguments_0f, "94", 0x01);
+686 put_new(Permitted_arguments_0f, "95", 0x01);
+687 put_new(Permitted_arguments_0f, "96", 0x01);
+688 put_new(Permitted_arguments_0f, "97", 0x01);
+689 put_new(Permitted_arguments_0f, "9c", 0x01);
+690 put_new(Permitted_arguments_0f, "9d", 0x01);
+691 put_new(Permitted_arguments_0f, "9e", 0x01);
+692 put_new(Permitted_arguments_0f, "9f", 0x01);
+693 
+694 :(before "End Globals")
+695 map</*op*/string, /*bitvector*/uint8_t> Permitted_arguments_f3;
+696 map</*op*/string, /*bitvector*/uint8_t> Permitted_arguments_f3_0f;
+697 :(before "End Init Permitted Operands")
+698 //// Class M: using ModR/M byte
+699 //  imm32 imm8  disp32 |disp16  disp8 subop modrm
+700 //  0     0     0      |0       0     0     1
+701 put_new(Permitted_arguments_f3_0f, "10", 0x01);  // copy xm32 to x32
+702 put_new(Permitted_arguments_f3_0f, "11", 0x01);  // copy x32 to xm32
+703 put_new(Permitted_arguments_f3_0f, "2a", 0x01);  // convert-to-float
+704 put_new(Permitted_arguments_f3_0f, "2c", 0x01);  // truncate-to-int
+705 put_new(Permitted_arguments_f3_0f, "2d", 0x01);  // convert-to-int
+706 put_new(Permitted_arguments_f3_0f, "51", 0x01);  // square root
+707 put_new(Permitted_arguments_f3_0f, "52", 0x01);  // inverse square root
+708 put_new(Permitted_arguments_f3_0f, "53", 0x01);  // reciprocal
+709 put_new(Permitted_arguments_f3_0f, "58", 0x01);  // add floats
+710 put_new(Permitted_arguments_f3_0f, "59", 0x01);  // multiply floats
+711 put_new(Permitted_arguments_f3_0f, "5c", 0x01);  // subtract floats
+712 put_new(Permitted_arguments_f3_0f, "5d", 0x01);  // minimum of floats
+713 put_new(Permitted_arguments_f3_0f, "5e", 0x01);  // divide floats
+714 put_new(Permitted_arguments_f3_0f, "5f", 0x01);  // maximum of floats
+715 
+716 :(code)
+717 void check_arguments_0f(const line& inst, const word& op) {
+718   uint8_t expected_bitvector = get(Permitted_arguments_0f, op.data);
+719   if (HAS(expected_bitvector, MODRM)) {
+720     check_arguments_modrm(inst, op);
+721     compare_bitvector_modrm(inst, expected_bitvector, maybe_name_0f(op));
+722   }
+723   else {
+724     compare_bitvector(inst, CLEAR(expected_bitvector, MODRM), maybe_name_0f(op));
+725   }
+726 }
+727 
+728 void check_arguments_f3(const line& inst, const word& op) {
+729   uint8_t expected_bitvector = get(Permitted_arguments_f3, op.data);
+730   if (HAS(expected_bitvector, MODRM)) {
+731     check_arguments_modrm(inst, op);
+732     compare_bitvector_modrm(inst, expected_bitvector, maybe_name_f3(op));
+733   }
+734   else {
+735     compare_bitvector(inst, CLEAR(expected_bitvector, MODRM), maybe_name_f3(op));
+736   }
+737 }
+738 
+739 void check_arguments_f3_0f(const line& inst, const word& op) {
+740   uint8_t expected_bitvector = get(Permitted_arguments_f3_0f, op.data);
+741   if (HAS(expected_bitvector, MODRM)) {
+742     check_arguments_modrm(inst, op);
+743     compare_bitvector_modrm(inst, expected_bitvector, maybe_name_f3_0f(op));
+744   }
+745   else {
+746     compare_bitvector(inst, CLEAR(expected_bitvector, MODRM), maybe_name_f3_0f(op));
+747   }
+748 }
+749 
+750 string maybe_name_0f(const word& op) {
+751   if (!is_hex_byte(op)) return "";
+752   if (!contains_key(Name_0f, op.data)) return "";
+753   // strip stuff in parens from the name
+754   const string& s = get(Name_0f, op.data);
+755   return " ("+s.substr(0, s.find(" ("))+')';
+756 }
+757 
+758 string maybe_name_f3(const word& op) {
+759   if (!is_hex_byte(op)) return "";
+760   if (!contains_key(Name_f3, op.data)) return "";
+761   // strip stuff in parens from the name
+762   const string& s = get(Name_f3, op.data);
+763   return " ("+s.substr(0, s.find(" ("))+')';
+764 }
+765 
+766 string maybe_name_f3_0f(const word& op) {
+767   if (!is_hex_byte(op)) return "";
+768   if (!contains_key(Name_f3_0f, op.data)) return "";
+769   // strip stuff in parens from the name
+770   const string& s = get(Name_f3_0f, op.data);
+771   return " ("+s.substr(0, s.find(" ("))+')';
+772 }
+773 
+774 string tolower(const char* s) {
+775   ostringstream out;
+776   for (/*nada*/;  *s;  ++s)
+777     out << static_cast<char>(tolower(*s));
+778   return out.str();
+779 }
+780 
+781 #undef HAS
+782 #undef SET
+783 #undef CLEAR
+784 
+785 :(before "End Includes")
+786 #include<cctype>
+
+ + + diff --git a/html/linux/bootstrap/034check_operand_bounds.cc.html b/html/linux/bootstrap/034check_operand_bounds.cc.html new file mode 100644 index 00000000..11670f19 --- /dev/null +++ b/html/linux/bootstrap/034check_operand_bounds.cc.html @@ -0,0 +1,205 @@ + + + + +Mu - linux/bootstrap/034check_operand_bounds.cc + + + + + + + + + + +https://github.com/akkartik/mu/blob/main/linux/bootstrap/034check_operand_bounds.cc +
+  1 //:: Check that the different arguments of an instruction aren't too large for their bitfields.
+  2 
+  3 void test_check_bitfield_sizes() {
+  4   Hide_errors = true;
+  5   run(
+  6       "== code 0x1\n"
+  7       "01/add 4/mod 3/rm32 1/r32\n"  // add ECX to EBX
+  8   );
+  9   CHECK_TRACE_CONTENTS(
+ 10       "error: '4/mod' too large to fit in bitfield mod\n"
+ 11   );
+ 12 }
+ 13 
+ 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);
+ 26 // no bound needed for disp32
+ 27 put_new(Operand_bound, "imm8", 1<<8);
+ 28 // no bound needed for imm32
+ 29 
+ 30 :(before "Pack Operands(segment code)")
+ 31 check_argument_bounds(code);
+ 32 if (trace_contains_errors()) return;
+ 33 :(code)
+ 34 void check_argument_bounds(const segment& code) {
+ 35   trace(3, "transform") << "-- check argument 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_argument(inst);  j < SIZE(inst.words);  ++j)
+ 39       check_argument_bounds(inst.words.at(j));
+ 40     if (trace_contains_errors()) return;  // stop at the first mal-formed instruction
+ 41   }
+ 42 }
+ 43 
+ 44 void check_argument_bounds(const word& w) {
+ 45   for (map<string, uint32_t>::iterator p = Operand_bound.begin();  p != Operand_bound.end();  ++p) {
+ 46     if (!has_argument_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);
+ 49     if (x >= 0) {
+ 50       if (p->first == "disp8" || p->first == "disp16") {
+ 51         if (static_cast<uint32_t>(x) >= p->second/2)
+ 52           raise << "'" << w.original << "' too large to fit in signed bitfield " << p->first << '\n' << end();
+ 53       }
+ 54       else {
+ 55         if (static_cast<uint32_t>(x) >= p->second)
+ 56           raise << "'" << w.original << "' too large to fit in bitfield " << p->first << '\n' << end();
+ 57       }
+ 58     }
+ 59     else {
+ 60       // hacky? assuming bound is a power of 2
+ 61       if (x < -1*static_cast<int32_t>(p->second/2))
+ 62         raise << "'" << w.original << "' too large to fit in bitfield " << p->first << '\n' << end();
+ 63     }
+ 64   }
+ 65 }
+ 66 
+ 67 void test_check_bitfield_sizes_for_imm8() {
+ 68   run(
+ 69       "== code 0x1\n"
+ 70       "c1/shift 4/subop/left 3/mod/direct 1/rm32/ECX 0xff/imm8"  // shift EBX left
+ 71   );
+ 72   CHECK(!trace_contains_errors());
+ 73 }
+ 74 
+ 75 void test_check_bitfield_sizes_for_imm8_error() {
+ 76   Hide_errors = true;
+ 77   run(
+ 78       "== code 0x1\n"
+ 79       "c1/shift 4/subop/left 3/mod/direct 1/rm32/ECX 0x100/imm8"  // shift EBX left
+ 80   );
+ 81   CHECK_TRACE_CONTENTS(
+ 82       "error: '0x100/imm8' too large to fit in bitfield imm8\n"
+ 83   );
+ 84 }
+ 85 
+ 86 void test_check_bitfield_sizes_for_negative_imm8() {
+ 87   run(
+ 88       "== code 0x1\n"
+ 89       "c1/shift 4/subop/left 3/mod/direct 1/rm32/ECX -0x80/imm8"  // shift EBX left
+ 90   );
+ 91   CHECK(!trace_contains_errors());
+ 92 }
+ 93 
+ 94 void test_check_bitfield_sizes_for_negative_imm8_error() {
+ 95   Hide_errors = true;
+ 96   run(
+ 97       "== code 0x1\n"
+ 98       "c1/shift 4/subop/left 3/mod/direct 1/rm32/ECX -0x81/imm8"  // shift EBX left
+ 99   );
+100   CHECK_TRACE_CONTENTS(
+101       "error: '-0x81/imm8' too large to fit in bitfield imm8\n"
+102   );
+103 }
+104 
+105 void test_check_bitfield_sizes_for_disp8() {
+106   // not bothering to run
+107   transform(
+108       "== code 0x1\n"
+109       "01/add 1/mod/*+disp8 3/rm32 1/r32 0x7f/disp8\n"  // add ECX to *(EBX+0x7f)
+110   );
+111   CHECK(!trace_contains_errors());
+112 }
+113 
+114 void test_check_bitfield_sizes_for_disp8_error() {
+115   Hide_errors = true;
+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   );
+120   CHECK_TRACE_CONTENTS(
+121       "error: '0x80/disp8' too large to fit in signed bitfield disp8\n"
+122   );
+123 }
+124 
+125 void test_check_bitfield_sizes_for_negative_disp8() {
+126   // not bothering to run
+127   transform(
+128       "== code 0x1\n"
+129       "01/add 1/mod/*+disp8 3/rm32 1/r32 -0x80/disp8\n"  // add ECX to *(EBX-0x80)
+130   );
+131   CHECK(!trace_contains_errors());
+132 }
+133 
+134 void test_check_bitfield_sizes_for_negative_disp8_error() {
+135   Hide_errors = true;
+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   );
+140   CHECK_TRACE_CONTENTS(
+141       "error: '-0x81/disp8' too large to fit in bitfield disp8\n"
+142   );
+143 }
+
+ + + diff --git a/html/linux/bootstrap/035compute_segment_address.cc.html b/html/linux/bootstrap/035compute_segment_address.cc.html new file mode 100644 index 00000000..a71afcf6 --- /dev/null +++ b/html/linux/bootstrap/035compute_segment_address.cc.html @@ -0,0 +1,148 @@ + + + + +Mu - linux/bootstrap/035compute_segment_address.cc + + + + + + + + + + +https://github.com/akkartik/mu/blob/main/linux/bootstrap/035compute_segment_address.cc +
+ 1 //: ELF binaries have finicky rules about the precise alignment each segment
+ 2 //: should start at. They depend on the amount of code in a program.
+ 3 //: We shouldn't expect people to adjust segment addresses everytime they make
+ 4 //: a change to their programs.
+ 5 //: Let's start taking the given segment addresses as guidelines, and adjust
+ 6 //: them as necessary.
+ 7 //: This gives up a measure of control in placing code and data.
+ 8 
+ 9 void test_segment_name() {
+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
+14   );
+15   CHECK_TRACE_CONTENTS(
+16       "load: 0x09000054 -> 05\n"
+17       "load: 0x09000055 -> 0a\n"
+18       "load: 0x09000056 -> 0b\n"
+19       "load: 0x09000057 -> 0c\n"
+20       "load: 0x09000058 -> 0d\n"
+21       "run: add imm32 0x0d0c0b0a to EAX\n"
+22       "run: storing 0x0d0c0b0a\n"
+23   );
+24 }
+25 
+26 //: compute segment address
+27 
+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);
+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();
+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
+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));
+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));
+60   return sum;
+61 }
+62 
+63 int size_of(const word& w) {
+64   if (has_argument_metadata(w, "disp32") || has_argument_metadata(w, "imm32"))
+65     return 4;
+66   else if (has_argument_metadata(w, "disp16"))
+67     return 2;
+68   // End size_of(word w) Special-cases
+69   else
+70     return 1;
+71 }
+72 
+73 //: Dependencies:
+74 //: - We'd like to compute segment addresses before setting up global variables,
+75 //:   because computing addresses for global variables requires knowing where
+76 //:   the data segment starts.
+77 //: - We'd like to finish expanding labels before computing segment addresses,
+78 //:   because it would make computing the sizes of segments more self-contained
+79 //:   (num_bytes).
+80 //:
+81 //: Decision: compute segment addresses before expanding labels, by being
+82 //: aware in this layer of certain argument types that will eventually occupy
+83 //: multiple bytes.
+84 //:
+85 //: The layer to expand labels later hooks into num_bytes() to teach this
+86 //: layer that labels occupy zero space in the binary.
+
+ + + diff --git a/html/linux/bootstrap/036labels.cc.html b/html/linux/bootstrap/036labels.cc.html new file mode 100644 index 00000000..2068cf7c --- /dev/null +++ b/html/linux/bootstrap/036labels.cc.html @@ -0,0 +1,491 @@ + + + + +Mu - linux/bootstrap/036labels.cc + + + + + + + + + + +https://github.com/akkartik/mu/blob/main/linux/bootstrap/036labels.cc +
+  1 //: Labels are defined by ending names with a ':'. This layer will compute
+  2 //: displacements for labels, and compute the offset for instructions using them.
+  3 //:
+  4 //: We won't check this, but our convention will be that jump targets will
+  5 //: start with a '$', while functions will not. Function names will never be
+  6 //: jumped to, and jump targets will never be called.
+  7 
+  8 //: We're introducing non-number names for the first time, so it's worth
+  9 //: laying down some ground rules all transforms will follow, so things don't
+ 10 //: get too confusing:
+ 11 //:   - if it starts with a digit, it's treated as a number. If it can't be
+ 12 //:     parsed as hex it will raise an error.
+ 13 //:   - if it starts with '-' it's treated as a number.
+ 14 //:   - if it starts with '0x' it's treated as a number.
+ 15 //:   - if it's two characters long, it can't be a name. Either it's a hex
+ 16 //:     byte, or it raises an error.
+ 17 //: That's it. Names can start with any non-digit that isn't a dash. They can
+ 18 //: be a single character long. 'a' is not a hex number, it's a variable.
+ 19 //: Later layers may add more conventions partitioning the space of names. But
+ 20 //: the above rules will remain inviolate.
+ 21 
+ 22 //: One special label is 'Entry', the address to start running the program at.
+ 23 //: It can be non-unique; the last declaration overrides earlier ones.
+ 24 //: It must exist in a program. Otherwise we don't know where to start running
+ 25 //: programs.
+ 26 
+ 27 void test_Entry_label() {
+ 28   run(
+ 29       "== code 0x1\n"
+ 30       "05 0x0d0c0b0a/imm32\n"
+ 31       "Entry:\n"
+ 32       "05 0x0d0c0b0a/imm32\n"
+ 33   );
+ 34   CHECK_TRACE_CONTENTS(
+ 35       "run: 0x00000006 opcode: 05\n"
+ 36   );
+ 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;
+ 42 
+ 43 :(code)
+ 44 void test_pack_immediate_ignores_single_byte_nondigit_argument() {
+ 45   Hide_errors = true;
+ 46   transform(
+ 47       "== code 0x1\n"
+ 48       "b9/copy  a/imm32\n"
+ 49   );
+ 50   CHECK_TRACE_CONTENTS(
+ 51       "transform: packing instruction 'b9/copy a/imm32'\n"
+ 52       // no change (we're just not printing metadata to the trace)
+ 53       "transform: instruction after packing: 'b9 a'\n"
+ 54   );
+ 55 }
+ 56 
+ 57 void test_pack_immediate_ignores_3_hex_digit_argument() {
+ 58   Hide_errors = true;
+ 59   transform(
+ 60       "== code 0x1\n"
+ 61       "b9/copy  aaa/imm32\n"
+ 62   );
+ 63   CHECK_TRACE_CONTENTS(
+ 64       "transform: packing instruction 'b9/copy aaa/imm32'\n"
+ 65       // no change (we're just not printing metadata to the trace)
+ 66       "transform: instruction after packing: 'b9 aaa'\n"
+ 67   );
+ 68 }
+ 69 
+ 70 void test_pack_immediate_ignores_non_hex_argument() {
+ 71   Hide_errors = true;
+ 72   transform(
+ 73       "== code 0x1\n"
+ 74       "b9/copy xxx/imm32\n"
+ 75   );
+ 76   CHECK_TRACE_CONTENTS(
+ 77       "transform: packing instruction 'b9/copy xxx/imm32'\n"
+ 78       // no change (we're just not printing metadata to the trace)
+ 79       "transform: instruction after packing: 'b9 xxx'\n"
+ 80   );
+ 81 }
+ 82 
+ 83 //: a helper we'll find handy later
+ 84 void check_valid_name(const string& s) {
+ 85   if (s.empty()) {
+ 86     raise << "empty name!\n" << end();
+ 87     return;
+ 88   }
+ 89   if (s.at(0) == '-')
+ 90     raise << "'" << s << "' starts with '-', which can be confused with a negative number; use a different name\n" << end();
+ 91   if (s.substr(0, 2) == "0x") {
+ 92     raise << "'" << s << "' looks like a hex number; use a different name\n" << end();
+ 93     return;
+ 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)
+ 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 
+101 //: Now that that's done, let's start using names as labels.
+102 
+103 void test_map_label() {
+104   transform(
+105       "== code 0x1\n"
+106       "loop:\n"
+107       "  05  0x0d0c0b0a/imm32\n"
+108   );
+109   CHECK_TRACE_CONTENTS(
+110       "transform: label 'loop' is at address 1\n"
+111   );
+112 }
+113 
+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;
+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);
+123   if (trace_contains_errors()) return;
+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");
+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);
+139       // hack: if we have any argument metadata left after previous transforms,
+140       // deduce its size
+141       // Maybe we should just move this transform to before instruction
+142       // packing, and deduce the size of *all* arguments. But then we'll also
+143       // have to deal with bitfields.
+144       if (has_argument_metadata(curr, "disp32") || has_argument_metadata(curr, "imm32")) {
+145         if (*curr.data.rbegin() == ':')
+146           raise << "'" << to_string(inst) << "': don't use ':' when jumping to labels\n" << end();
+147         current_byte += 4;
+148       }
+149       else if (has_argument_metadata(curr, "disp16")) {
+150         if (*curr.data.rbegin() == ':')
+151           raise << "'" << to_string(inst) << "': don't use ':' when jumping to labels\n" << end();
+152         current_byte += 2;
+153       }
+154       // automatically handle /disp8 and /imm8 here
+155       else if (*curr.data.rbegin() != ':') {
+156         ++current_byte;
+157       }
+158       else {
+159         string label = drop_last(curr.data);
+160         // ensure labels look sufficiently different from raw hex
+161         check_valid_name(label);
+162         if (trace_contains_errors()) return;
+163         if (contains_any_argument_metadata(curr))
+164           raise << "'" << to_string(inst) << "': label definition (':') not allowed in argument\n" << end();
+165         if (j > 0)
+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") {
+170           raise << "duplicate label '" << label << "'\n" << end();
+171           return;
+172         }
+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       }
+177     }
+178   }
+179 }
+180 
+181 :(before "End Globals")
+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")) {
+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 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 bootstrap translate")
+199 if (Dump_debug_info) {
+200   Labels_file.close();
+201   Source_lines_file.close();
+202 }
+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());
+210   }
+211 }
+212 
+213 bool is_label(const word& w) {
+214   return *w.data.rbegin() == ':';
+215 }
+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);
+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)) {
+226         int32_t displacement = static_cast<int32_t>(get(byte_index, curr.data)) - byte_index_next_instruction_starts_at;
+227         int32_t absolute_address = code.start + get(byte_index, curr.data);
+228         if (has_argument_metadata(curr, "disp8")) {
+229           if (displacement > 0x7f || displacement < -0x7f)
+230             raise << "'" << to_string(inst) << "': label too far away for displacement " << std::hex << displacement << " to fit in 8 signed bits\n" << end();
+231           else
+232             emit_hex_bytes(new_inst, displacement, 1);
+233         }
+234         else if (has_argument_metadata(curr, "disp16")) {
+235           if (displacement > 0x7fff || displacement < -0x7fff)
+236             raise << "'" << to_string(inst) << "': label too far away for displacement " << std::hex << displacement << " to fit in 16 signed bits\n" << end();
+237           else
+238             emit_hex_bytes(new_inst, displacement, 2);
+239         }
+240         else if (has_argument_metadata(curr, "disp32")) {
+241           if (is_far_jump_or_call(new_inst))
+242             emit_hex_bytes(new_inst, displacement, 4);
+243           else
+244             emit_hex_bytes(new_inst, absolute_address, 4);
+245         } else if (has_argument_metadata(curr, "imm32")) {
+246           emit_hex_bytes(new_inst, absolute_address, 4);
+247         }
+248       }
+249       else {
+250         new_inst.words.push_back(curr);
+251       }
+252     }
+253     inst.words.swap(new_inst.words);
+254     trace(99, "transform") << "instruction after transform: '" << data_to_string(inst) << "'" << end();
+255   }
+256 }
+257 
+258 bool is_far_jump_or_call(const line& inst) {
+259   string first_opcode = inst.words.at(0).data;
+260   if (first_opcode == "e8" || first_opcode == "e9") return true;
+261   if (SIZE(inst.words) < 2) return false;
+262   if (first_opcode != "0f") return false;
+263   string second_opcode = inst.words.at(1).data;
+264   return starts_with(second_opcode, "8");
+265 }
+266 
+267 string data_to_string(const line& inst) {
+268   ostringstream out;
+269   for (int i = 0;  i < SIZE(inst.words);  ++i) {
+270     if (i > 0) out << ' ';
+271     out << inst.words.at(i).data;
+272   }
+273   return out.str();
+274 }
+275 
+276 string drop_last(const string& s) {
+277   return string(s.begin(), --s.end());
+278 }
+279 
+280 //: Label definitions must be the first word on a line. No jumping inside
+281 //: instructions.
+282 //: They should also be the only word on a line.
+283 //: However, you can absolutely have multiple labels map to the same address,
+284 //: as long as they're on separate lines.
+285 
+286 void test_multiple_labels_at() {
+287   transform(
+288       "== code 0x1\n"
+289       // address 1
+290       "loop:\n"
+291       " $loop2:\n"
+292       // address 1 (labels take up no space)
+293       "    05  0x0d0c0b0a/imm32\n"
+294       // address 6
+295       "    eb  $loop2/disp8\n"
+296       // address 8
+297       "    eb  $loop3/disp8\n"
+298       // address 0xa
+299       " $loop3:\n"
+300   );
+301   CHECK_TRACE_CONTENTS(
+302       "transform: label 'loop' is at address 1\n"
+303       "transform: label '$loop2' is at address 1\n"
+304       "transform: label '$loop3' is at address a\n"
+305       // first jump is to -7
+306       "transform: instruction after transform: 'eb f9'\n"
+307       // second jump is to 0 (fall through)
+308       "transform: instruction after transform: 'eb 00'\n"
+309   );
+310 }
+311 
+312 void test_loading_label_as_imm32() {
+313   transform(
+314       "== code 0x1\n"
+315       "label:\n"
+316       "  be/copy-to-ESI  label/imm32\n"
+317   );
+318   CHECK_TRACE_CONTENTS(
+319       "transform: label 'label' is at address 1\n"
+320       "transform: instruction after transform: 'be 01 00 00 00'\n"
+321   );
+322 }
+323 
+324 void test_duplicate_label() {
+325   Hide_errors = true;
+326   transform(
+327       "== code 0x1\n"
+328       "loop:\n"
+329       "loop:\n"
+330       "    05  0x0d0c0b0a/imm32\n"
+331   );
+332   CHECK_TRACE_CONTENTS(
+333       "error: duplicate label 'loop'\n"
+334   );
+335 }
+336 
+337 void test_label_too_short() {
+338   Hide_errors = true;
+339   transform(
+340       "== code 0x1\n"
+341       "xz:\n"
+342       "  05  0x0d0c0b0a/imm32\n"
+343   );
+344   CHECK_TRACE_CONTENTS(
+345       "error: 'xz' is two characters long, which can look like raw hex bytes at a glance; use a different name\n"
+346   );
+347 }
+348 
+349 void test_label_hex() {
+350   Hide_errors = true;
+351   transform(
+352       "== code 0x1\n"
+353       "0xab:\n"
+354       "  05  0x0d0c0b0a/imm32\n"
+355   );
+356   CHECK_TRACE_CONTENTS(
+357       "error: '0xab' looks like a hex number; use a different name\n"
+358   );
+359 }
+360 
+361 void test_label_negative_hex() {
+362   Hide_errors = true;
+363   transform(
+364       "== code 0x1\n"
+365       "-a:\n"
+366       "    05  0x0d0c0b0a/imm32\n"
+367   );
+368   CHECK_TRACE_CONTENTS(
+369       "error: '-a' starts with '-', which can be confused with a negative number; use a different name\n"
+370   );
+371 }
+372 
+373 //: As said up top, the 'Entry' label is special.
+374 //: It can be non-unique; the last declaration overrides earlier ones.
+375 //: It must exist in a program. Otherwise we don't know where to start running
+376 //: programs.
+377 
+378 void test_duplicate_Entry_label() {
+379   transform(
+380       "== code 0x1\n"
+381       "Entry:\n"
+382       "Entry:\n"
+383       "    05  0x0d0c0b0a/imm32\n"
+384   );
+385   CHECK_TRACE_DOESNT_CONTAIN_ERRORS();
+386 }
+387 
+388 // This test could do with some refactoring.
+389 // We're duplicating the flow inside `bootstrap translate`, but without
+390 // reading/writing files.
+391 // We can't just use run(string) because most of our tests allow programs
+392 // without 'Entry' labels, as a convenience.
+393 void test_programs_without_Entry_label() {
+394   Hide_errors = true;
+395   program p;
+396   istringstream in(
+397       "== code 0x1\n"
+398       "05 0x0d0c0b0a/imm32\n"
+399       "05 0x0d0c0b0a/imm32\n"
+400   );
+401   parse(in, p);
+402   transform(p);
+403   ostringstream dummy;
+404   save_elf(p, dummy);
+405   CHECK_TRACE_CONTENTS(
+406       "error: no 'Entry' label found\n"
+407   );
+408 }
+409 
+410 //: now that we have labels, we need to adjust segment size computation to
+411 //: ignore them.
+412 
+413 void test_segment_size_ignores_labels() {
+414   transform(
+415       "== code 0x09000074\n"
+416       "  05/add  0x0d0c0b0a/imm32\n"  // 5 bytes
+417       "foo:\n"                        // 0 bytes
+418       "== data 0x0a000000\n"
+419       "bar:\n"
+420       "  00\n"
+421   );
+422   CHECK_TRACE_CONTENTS(
+423       "transform: segment 1 begins at address 0x0a000079\n"
+424   );
+425 }
+426 
+427 :(before "End size_of(word w) Special-cases")
+428 else if (is_label(w))
+429   return 0;
+
+ + + diff --git a/html/linux/bootstrap/037global_variables.cc.html b/html/linux/bootstrap/037global_variables.cc.html new file mode 100644 index 00000000..ea37783f --- /dev/null +++ b/html/linux/bootstrap/037global_variables.cc.html @@ -0,0 +1,368 @@ + + + + +Mu - linux/bootstrap/037global_variables.cc + + + + + + + + + + +https://github.com/akkartik/mu/blob/main/linux/bootstrap/037global_variables.cc +
+  1 //: Global variables.
+  2 //:
+  3 //: Global variables are just labels in the data segment.
+  4 //: However, they can only be used in imm32 and not disp32 arguments. And they
+  5 //: can't be used with jump and call instructions.
+  6 //:
+  7 //: This layer has much the same structure as rewriting labels.
+  8 
+  9 :(code)
+ 10 void test_global_variable() {
+ 11   run(
+ 12       "== code 0x1\n"
+ 13       "b9  x/imm32\n"
+ 14       "== data 0x2000\n"
+ 15       "x:\n"
+ 16       "  00 00 00 00\n"
+ 17   );
+ 18   CHECK_TRACE_CONTENTS(
+ 19       "transform: global variable 'x' is at address 0x00002000\n"
+ 20   );
+ 21 }
+ 22 
+ 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();
+ 28   // Begin rewrite_global_variables
+ 29   map<string, uint32_t> address;
+ 30   compute_addresses_for_global_variables(p, address);
+ 31   if (trace_contains_errors()) return;
+ 32   drop_global_variables(p);
+ 33   replace_global_variables_with_addresses(p, address);
+ 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);
+ 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);
+ 49       if (*curr.data.rbegin() != ':') {
+ 50         current_address += size_of(curr);
+ 51       }
+ 52       else {
+ 53         string variable = drop_last(curr.data);
+ 54         // ensure variables look sufficiently different from raw hex
+ 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();
+ 59         if (Labels_file.is_open())
+ 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();
+ 67         // no modifying current_address; global variable definitions won't be in the final binary
+ 68       }
+ 69     }
+ 70   }
+ 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));
+ 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")
+ 85       replace_global_variables_in_code_segment(curr, address);
+ 86     else
+ 87       replace_global_variables_in_data_segment(curr, address);
+ 88   }
+ 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);
+ 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);
+101         continue;
+102       }
+103       if (!valid_use_of_global_variable(curr)) {
+104         raise << "'" << to_string(inst) << "': can't refer to global variable '" << curr.data << "'\n" << end();
+105         return;
+106       }
+107       emit_hex_bytes(new_inst, get(address, curr.data), 4);
+108     }
+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);
+120       if (!contains_key(address, curr.data)) {
+121         if (looks_like_hex_int(curr.data)) {
+122           if (has_argument_metadata(curr, "imm32"))
+123             emit_hex_bytes(new_l, curr, 4);
+124           else if (has_argument_metadata(curr, "imm16"))
+125             emit_hex_bytes(new_l, curr, 2);
+126           else if (has_argument_metadata(curr, "imm8"))
+127             emit_hex_bytes(new_l, curr, 1);
+128           else if (has_argument_metadata(curr, "disp8"))
+129             raise << "can't use /disp8 in a non-code segment\n" << end();
+130           else if (has_argument_metadata(curr, "disp16"))
+131             raise << "can't use /disp16 in a non-code segment\n" << end();
+132           else if (has_argument_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);
+136         }
+137         else {
+138           raise << "missing reference to global '" << curr.data << "'\n" << end();
+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();
+144       emit_hex_bytes(new_l, get(address, curr.data), 4);
+145     }
+146     l.words.swap(new_l.words);
+147     trace(99, "transform") << "after transform: '" << data_to_string(l) << "'" << end();
+148   }
+149 }
+150 
+151 bool valid_use_of_global_variable(const word& curr) {
+152   if (has_argument_metadata(curr, "imm32")) return true;
+153   // End Valid Uses Of Global Variable(curr)
+154   return false;
+155 }
+156 
+157 //:: a more complex sanity check for how we use global variables
+158 //: requires first saving some data early before we pack arguments
+159 
+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;
+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);
+170       if (has_argument_metadata(curr, "disp32")
+171           && has_argument_metadata(inst, "mod"))
+172         curr.metadata.push_back("has_mod");
+173     }
+174   }
+175 }
+176 
+177 :(before "End Valid Uses Of Global Variable(curr)")
+178 if (has_argument_metadata(curr, "disp32"))
+179   return has_metadata(curr, "has_mod");
+180 // todo: more sophisticated check, to ensure we don't use global variable
+181 // addresses as a real displacement added to other arguments.
+182 
+183 :(code)
+184 bool has_metadata(const word& w, const string& m) {
+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(
+193       "== code 0x1\n"
+194       "eb/jump  x/disp8\n"
+195       "== data 0x2000\n"
+196       "x:\n"
+197       "  00 00 00 00\n"
+198   );
+199   CHECK_TRACE_CONTENTS(
+200       "error: 'eb/jump x/disp8': can't refer to global variable 'x'\n"
+201       // sub-optimal error message; should be
+202 //?       "error: can't jump to data (variable 'x')\n"
+203   );
+204 }
+205 
+206 void test_global_variable_disallowed_in_call() {
+207   Hide_errors = true;
+208   run(
+209       "== code 0x1\n"
+210       "e8/call  x/disp32\n"
+211       "== data 0x2000\n"
+212       "x:\n"
+213       "  00 00 00 00\n"
+214   );
+215   CHECK_TRACE_CONTENTS(
+216       "error: 'e8/call x/disp32': can't refer to global variable 'x'\n"
+217       // sub-optimal error message; should be
+218 //?       "error: can't call to the data segment ('x')\n"
+219   );
+220 }
+221 
+222 void test_global_variable_in_data_segment() {
+223   run(
+224       "== code 0x1\n"
+225       "b9  x/imm32\n"
+226       "== data 0x2000\n"
+227       "x:\n"
+228       "  y/imm32\n"
+229       "y:\n"
+230       "  00 00 00 00\n"
+231   );
+232   // check that we loaded 'x' with the address of 'y'
+233   CHECK_TRACE_CONTENTS(
+234       "load: 0x00002000 -> 04\n"
+235       "load: 0x00002001 -> 20\n"
+236       "load: 0x00002002 -> 00\n"
+237       "load: 0x00002003 -> 00\n"
+238   );
+239   CHECK_TRACE_COUNT("error", 0);
+240 }
+241 
+242 void test_raw_number_with_imm32_in_data_segment() {
+243   run(
+244       "== code 0x1\n"
+245       "b9  x/imm32\n"
+246       "== data 0x2000\n"
+247       "x:\n"
+248       "  1/imm32\n"
+249   );
+250   // check that we loaded 'x' with the address of 1
+251   CHECK_TRACE_CONTENTS(
+252       "load: 0x00002000 -> 01\n"
+253       "load: 0x00002001 -> 00\n"
+254       "load: 0x00002002 -> 00\n"
+255       "load: 0x00002003 -> 00\n"
+256   );
+257   CHECK_TRACE_COUNT("error", 0);
+258 }
+259 
+260 void test_duplicate_global_variable() {
+261   Hide_errors = true;
+262   run(
+263       "== code 0x1\n"
+264       "40/increment-EAX\n"
+265       "== data 0x2000\n"
+266       "x:\n"
+267       "x:\n"
+268       "  00\n"
+269   );
+270   CHECK_TRACE_CONTENTS(
+271       "error: duplicate global 'x'\n"
+272   );
+273 }
+274 
+275 void test_global_variable_disp32_with_modrm() {
+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"
+280       "x:\n"
+281       "  00 00 00 00\n"
+282   );
+283   CHECK_TRACE_COUNT("error", 0);
+284 }
+285 
+286 void test_global_variable_disp32_with_call() {
+287   transform(
+288       "== code 0x1\n"
+289       "foo:\n"
+290       "  e8/call bar/disp32\n"
+291       "bar:\n"
+292   );
+293   CHECK_TRACE_COUNT("error", 0);
+294 }
+295 
+296 string to_full_string(const line& in) {
+297   ostringstream out;
+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);
+303   }
+304   return out.str();
+305 }
+
+ + + diff --git a/html/linux/bootstrap/038literal_strings.cc.html b/html/linux/bootstrap/038literal_strings.cc.html new file mode 100644 index 00000000..7b6d34f0 --- /dev/null +++ b/html/linux/bootstrap/038literal_strings.cc.html @@ -0,0 +1,425 @@ + + + + +Mu - linux/bootstrap/038literal_strings.cc + + + + + + + + + + +https://github.com/akkartik/mu/blob/main/linux/bootstrap/038literal_strings.cc +
+  1 //: Allow instructions to mention literals directly.
+  2 //:
+  3 //: This layer will transparently move them to the global segment (assumed to
+  4 //: always be the second segment).
+  5 
+  6 void test_transform_literal_string() {
+  7   run(
+  8       "== code 0x1\n"
+  9       "b8/copy  \"test\"/imm32\n"
+ 10       "== data 0x2000\n"  // need an empty segment
+ 11   );
+ 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"
+ 16   );
+ 17 }
+ 18 
+ 19 //: We don't rely on any transforms running in previous layers, but this layer
+ 20 //: knows about labels and global variables and will emit them for previous
+ 21 //: layers to transform.
+ 22 :(after "Begin Transforms")
+ 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 void test_string_literal_with_missing_quote() {
+107   Hide_errors = true;
+108   run(
+109       "== code 0x1\n"
+110       "b8/copy  \"test/imm32\n"
+111       "== data 0x2000\n"
+112   );
+113   CHECK_TRACE_CONTENTS(
+114       "error: unclosed string in: b8/copy  \"test/imm32"
+115   );
+116 }
+117 
+118 :(before "End Line Parsing Special-cases(line_data -> l)")
+119 if (line_data.find('"') != string::npos) {  // can cause false-positives, but we can handle them
+120   parse_instruction_character_by_character(line_data, l);
+121   continue;
+122 }
+123 
+124 :(code)
+125 void parse_instruction_character_by_character(const string& line_data, vector<line>& out) {
+126   if (line_data.find('\n') != string::npos  && line_data.find('\n') != line_data.size()-1) {
+127     raise << "parse_instruction_character_by_character: should receive only a single line\n" << end();
+128     return;
+129   }
+130   // parse literals
+131   istringstream in(line_data);
+132   in >> std::noskipws;
+133   line result;
+134   result.original = line_data;
+135   // add tokens (words or strings) one by one
+136   while (has_data(in)) {
+137     skip_whitespace(in);
+138     if (!has_data(in)) break;
+139     char c = in.get();
+140     if (c == '#') break;  // comment; drop rest of line
+141     if (c == ':') break;  // line metadata; skip for now
+142     if (c == '.') {
+143       if (!has_data(in)) break;  // comment token at end of line
+144       if (isspace(in.peek()))
+145         continue;  // '.' followed by space is comment token; skip
+146     }
+147     result.words.push_back(word());
+148     if (c == '"') {
+149       // string literal; slurp everything between quotes into data
+150       ostringstream d;
+151       d << c;
+152       while (true) {
+153         if (!has_data(in)) {
+154           raise << "unclosed string in: " << line_data << end();
+155           return;
+156         }
+157         in >> c;
+158         if (c == '\\') {
+159           in >> c;
+160           if (c == 'n') d << '\n';
+161           else if (c == '"') d << '"';
+162           else if (c == '\\') d << '\\';
+163           else {
+164             raise << "parse_instruction_character_by_character: unknown escape sequence '\\" << c << "'\n" << end();
+165             return;
+166           }
+167           continue;
+168         } else {
+169           d << c;
+170         }
+171         if (c == '"') break;
+172       }
+173       result.words.back().data = d.str();
+174       result.words.back().original = d.str();
+175       // slurp metadata
+176       ostringstream m;
+177       while (!isspace(in.peek()) && has_data(in)) {  // peek can sometimes trigger eof(), so do it first
+178         in >> c;
+179         if (c == '/') {
+180           if (!m.str().empty()) result.words.back().metadata.push_back(m.str());
+181           m.str("");
+182         }
+183         else {
+184           m << c;
+185         }
+186       }
+187       if (!m.str().empty()) result.words.back().metadata.push_back(m.str());
+188     }
+189     else {
+190       // not a string literal; slurp all characters until whitespace
+191       ostringstream w;
+192       w << c;
+193       while (!isspace(in.peek()) && has_data(in)) {  // peek can sometimes trigger eof(), so do it first
+194         in >> c;
+195         w << c;
+196       }
+197       parse_word(w.str(), result.words.back());
+198     }
+199     trace(99, "parse2") << "word: " << to_string(result.words.back()) << end();
+200   }
+201   if (!result.words.empty())
+202     out.push_back(result);
+203 }
+204 
+205 void skip_whitespace(istream& in) {
+206   while (has_data(in) && isspace(in.peek())) {
+207     in.get();
+208   }
+209 }
+210 
+211 void skip_comment(istream& in) {
+212   if (has_data(in) && in.peek() == '#') {
+213     in.get();
+214     while (has_data(in) && in.peek() != '\n') in.get();
+215   }
+216 }
+217 
+218 line label(string s) {
+219   line result;
+220   result.words.push_back(word());
+221   result.words.back().data = (s+":");
+222   return result;
+223 }
+224 
+225 // helper for tests
+226 void parse_instruction_character_by_character(const string& line_data) {
+227   vector<line> out;
+228   parse_instruction_character_by_character(line_data, out);
+229 }
+230 
+231 void test_parse2_comment_token_in_middle() {
+232   parse_instruction_character_by_character(
+233       "a . z\n"
+234   );
+235   CHECK_TRACE_CONTENTS(
+236       "parse2: word: a\n"
+237       "parse2: word: z\n"
+238   );
+239   CHECK_TRACE_DOESNT_CONTAIN("parse2: word: .");
+240   // no other words
+241   CHECK_TRACE_COUNT("parse2", 2);
+242 }
+243 
+244 void test_parse2_word_starting_with_dot() {
+245   parse_instruction_character_by_character(
+246       "a .b c\n"
+247   );
+248   CHECK_TRACE_CONTENTS(
+249       "parse2: word: a\n"
+250       "parse2: word: .b\n"
+251       "parse2: word: c\n"
+252   );
+253 }
+254 
+255 void test_parse2_comment_token_at_start() {
+256   parse_instruction_character_by_character(
+257       ". a b\n"
+258   );
+259   CHECK_TRACE_CONTENTS(
+260       "parse2: word: a\n"
+261       "parse2: word: b\n"
+262   );
+263   CHECK_TRACE_DOESNT_CONTAIN("parse2: word: .");
+264 }
+265 
+266 void test_parse2_comment_token_at_end() {
+267   parse_instruction_character_by_character(
+268       "a b .\n"
+269   );
+270   CHECK_TRACE_CONTENTS(
+271       "parse2: word: a\n"
+272       "parse2: word: b\n"
+273   );
+274   CHECK_TRACE_DOESNT_CONTAIN("parse2: word: .");
+275 }
+276 
+277 void test_parse2_word_starting_with_dot_at_start() {
+278   parse_instruction_character_by_character(
+279       ".a b c\n"
+280   );
+281   CHECK_TRACE_CONTENTS(
+282       "parse2: word: .a\n"
+283       "parse2: word: b\n"
+284       "parse2: word: c\n"
+285   );
+286 }
+287 
+288 void test_parse2_metadata() {
+289   parse_instruction_character_by_character(
+290       ".a b/c d\n"
+291   );
+292   CHECK_TRACE_CONTENTS(
+293       "parse2: word: .a\n"
+294       "parse2: word: b /c\n"
+295       "parse2: word: d\n"
+296   );
+297 }
+298 
+299 void test_parse2_string_with_metadata() {
+300   parse_instruction_character_by_character(
+301       "a \"bc  def\"/disp32 g\n"
+302   );
+303   CHECK_TRACE_CONTENTS(
+304       "parse2: word: a\n"
+305       "parse2: word: \"bc  def\" /disp32\n"
+306       "parse2: word: g\n"
+307   );
+308 }
+309 
+310 void test_parse2_string_with_metadata_at_end() {
+311   parse_instruction_character_by_character(
+312       "a \"bc  def\"/disp32\n"
+313   );
+314   CHECK_TRACE_CONTENTS(
+315       "parse2: word: a\n"
+316       "parse2: word: \"bc  def\" /disp32\n"
+317   );
+318 }
+319 
+320 void test_parse2_string_with_metadata_at_end_of_line_without_newline() {
+321   parse_instruction_character_by_character(
+322       "68/push \"test\"/f"  // no newline, which is how calls from parse() will look
+323   );
+324   CHECK_TRACE_CONTENTS(
+325       "parse2: word: 68 /push\n"
+326       "parse2: word: \"test\" /f\n"
+327   );
+328 }
+329 
+330 //: Make sure slashes inside strings don't trigger adding stuff from inside the
+331 //: string to metadata.
+332 
+333 void test_parse2_string_containing_slashes() {
+334   parse_instruction_character_by_character(
+335       "a \"bc/def\"/disp32\n"
+336   );
+337   CHECK_TRACE_CONTENTS(
+338       "parse2: word: \"bc/def\" /disp32\n"
+339   );
+340 }
+341 
+342 void test_instruction_with_string_literal_with_escaped_quote() {
+343   parse_instruction_character_by_character(
+344       "\"a\\\"b\"\n"  // escaped quote inside string
+345   );
+346   CHECK_TRACE_CONTENTS(
+347       "parse2: word: \"a\"b\"\n"
+348   );
+349   // no other words
+350   CHECK_TRACE_COUNT("parse2", 1);
+351 }
+352 
+353 void test_instruction_with_string_literal_with_escaped_backslash() {
+354   parse_instruction_character_by_character(
+355       "\"a\\\\b\"\n"  // escaped backslash inside string
+356   );
+357   CHECK_TRACE_CONTENTS(
+358       "parse2: word: \"a\\b\"\n"
+359   );
+360   // no other words
+361   CHECK_TRACE_COUNT("parse2", 1);
+362 }
+
+ + + diff --git a/html/linux/bootstrap/039debug.cc.html b/html/linux/bootstrap/039debug.cc.html new file mode 100644 index 00000000..6df286a0 --- /dev/null +++ b/html/linux/bootstrap/039debug.cc.html @@ -0,0 +1,237 @@ + + + + +Mu - linux/bootstrap/039debug.cc + + + + + + + + + + +https://github.com/akkartik/mu/blob/main/linux/bootstrap/039debug.cc +
+  1 //:: Some helpers for debugging.
+  2 
+  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 '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();
+ 13 :(code)
+ 14 void load_labels() {
+ 15   ifstream fin("labels");
+ 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 //: If a label starts with '$dump-stack', dump out to the trace n bytes on
+ 97 //: either side of ESP.
+ 98 
+ 99 :(after "Run One Instruction")
+100 if (contains_key(Symbol_name, EIP) && starts_with(get(Symbol_name, EIP), "$dump-stack")) {
+101   dump_stack(64);
+102 }
+103 :(code)
+104 void dump_stack(int n) {
+105   uint32_t stack_pointer = Reg[ESP].u;
+106   uint32_t start = ((stack_pointer-n)&0xfffffff0);
+107   dbg << "stack:" << end();
+108   for (uint32_t addr = start;  addr < start+n*2;  addr+=16) {
+109     if (addr >= AFTER_STACK) break;
+110     ostringstream out;
+111     out << HEXWORD << addr << ":";
+112     for (int i = 0;  i < 16;  i+=4) {
+113       out << ' ';
+114       out << ((addr+i == stack_pointer) ? '[' : ' ');
+115       out << HEXWORD << read_mem_u32(addr+i);
+116       out << ((addr+i == stack_pointer) ? ']' : ' ');
+117     }
+118     dbg << out.str() << end();
+119   }
+120 }
+121 
+122 //: Special label that dumps regions of memory.
+123 //: Not a general mechanism; by the time you get here you're willing to hack
+124 //: on the emulator.
+125 :(after "Run One Instruction")
+126 if (contains_key(Symbol_name, EIP) && get(Symbol_name, EIP) == "$dump-stream-at-EAX")
+127   dump_stream_at(Reg[EAX].u);
+128 :(code)
+129 void dump_stream_at(uint32_t stream_start) {
+130   int32_t stream_length = read_mem_i32(stream_start + 8);
+131   dbg << "stream length: " << std::dec << stream_length << end();
+132   for (int i = 0;  i < stream_length + 12;  ++i)
+133     dbg << "0x" << HEXWORD << (stream_start+i) << ": " << HEXBYTE << NUM(read_mem_u8(stream_start+i)) << end();
+134 }
+135 
+136 //: helpers
+137 
+138 :(code)
+139 string hacky_squeeze_out_whitespace(const string& s) {
+140   // strip whitespace at start
+141   string::const_iterator first = s.begin();
+142   while (first != s.end() && isspace(*first))
+143     ++first;
+144   if (first == s.end()) return "";
+145 
+146   // strip whitespace at end
+147   string::const_iterator last = --s.end();
+148   while (last != s.begin() && isspace(*last))
+149     --last;
+150   ++last;
+151 
+152   // replace runs of spaces/dots with single space until comment or string
+153   // TODO:
+154   //   leave alone dots not surrounded by whitespace
+155   //   leave alone '#' within word
+156   //   leave alone '"' within word
+157   //   squeeze spaces after end of string
+158   ostringstream out;
+159   bool previous_was_space = false;
+160   bool in_comment_or_string = false;
+161   for (string::const_iterator curr = first;  curr != last;  ++curr) {
+162     if (in_comment_or_string)
+163       out << *curr;
+164     else if (isspace(*curr) || *curr == '.')
+165       previous_was_space = true;
+166     else {
+167       if (previous_was_space)
+168         out << ' ';
+169       out << *curr;
+170       previous_was_space = false;
+171       if (*curr == '#' || *curr == '"') in_comment_or_string = true;
+172     }
+173   }
+174   return out.str();
+175 }
+
+ + + diff --git a/html/linux/bootstrap/040tests.cc.html b/html/linux/bootstrap/040tests.cc.html new file mode 100644 index 00000000..a229a522 --- /dev/null +++ b/html/linux/bootstrap/040tests.cc.html @@ -0,0 +1,157 @@ + + + + +Mu - linux/bootstrap/040tests.cc + + + + + + + + + + +https://github.com/akkartik/mu/blob/main/linux/bootstrap/040tests.cc +
+ 1 //: Automatically aggregate functions starting with 'test-' into a test suite
+ 2 //: called 'run-tests'. Running this function will run all tests.
+ 3 //:
+ 4 //: This is actually SubX's first (trivial) compiler. We generate all the code
+ 5 //: needed for the 'run-tests' function.
+ 6 //:
+ 7 //: By convention, temporary functions needed by tests will start with
+ 8 //: '_test-'.
+ 9 
+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 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