From 25ad969f7582d9253f5329812aed5ed5784d8ec8 Mon Sep 17 00:00:00 2001 From: "Kartik K. Agaram" Date: Thu, 12 Oct 2017 23:43:09 -0700 Subject: 4052 --- html/subx/000organization.cc.html | 218 ++++++++++++++ html/subx/001help.cc.html | 275 ++++++++++++++++++ html/subx/002test.cc.html | 177 ++++++++++++ html/subx/003trace.cc.html | 463 ++++++++++++++++++++++++++++++ html/subx/003trace.test.cc.html | 186 ++++++++++++ html/subx/010core.cc.html | 319 +++++++++++--------- html/subx/011direct_addressing.cc.html | 132 +++++++++ html/subx/012indirect_addressing.cc.html | 148 ++++++++++ html/subx/013immediate_addressing.cc.html | 157 ++++++++++ update_html | 14 + 10 files changed, 1949 insertions(+), 140 deletions(-) create mode 100644 html/subx/000organization.cc.html create mode 100644 html/subx/001help.cc.html create mode 100644 html/subx/002test.cc.html create mode 100644 html/subx/003trace.cc.html create mode 100644 html/subx/003trace.test.cc.html create mode 100644 html/subx/011direct_addressing.cc.html create mode 100644 html/subx/012indirect_addressing.cc.html create mode 100644 html/subx/013immediate_addressing.cc.html diff --git a/html/subx/000organization.cc.html b/html/subx/000organization.cc.html new file mode 100644 index 00000000..db32f79c --- /dev/null +++ b/html/subx/000organization.cc.html @@ -0,0 +1,218 @@ + + + + +Mu - subx/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 a newcomers to rapidly orient themselves,
+ 83 //: reading the first few files to understand a simple gestalt of a program's
+ 84 //: core purpose and features, and later gradually working their way through
+ 85 //: other 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 // Includes
+ 92 // End Includes
+ 93 
+ 94 // Types
+ 95 // End Types
+ 96 
+ 97 // Function prototypes are auto-generated in the 'build' script; define your
+ 98 // functions in any order. Just be sure to declare each function header all on
+ 99 // one line, ending with the '{'. Our auto-generation scripts are too minimal
+100 // and simple-minded to handle anything else.
+101 #include "function_list"  // by convention, files ending with '_list' are auto-generated
+102 
+103 // Globals
+104 //
+105 // All statements in this section should always define a single variable on a
+106 // single line. The 'build' script will simple-mindedly auto-generate extern
+107 // declarations for them. Remember to define (not just declare) constants with
+108 // extern linkage in this section, since C++ global constants have internal
+109 // linkage by default.
+110 //
+111 // End Globals
+112 
+113 int main(int argc, char* argv[]) {
+114   atexit(reset);
+115   // run on a 32-bit system
+116   assert(sizeof(int) == 4);
+117   assert(sizeof(float) == 4);
+118   assert_little_endian();
+119 
+120   // End One-time Setup
+121 
+122   // Commandline Parsing
+123   // End Commandline Parsing
+124 
+125   return 0;  // End Main
+126 }
+127 
+128 // Unit Tests
+129 // End Unit Tests
+130 
+131 //: our first directive; insert the following header at the start of the program
+132 :(before "End Includes")
+133 #include <assert.h>
+134 #include <stdlib.h>
+135 
+136 //: Without directives or with the :(code) directive, lines get added at the
+137 //: end.
+138 :(code)
+139 void reset() {
+140   // End Reset
+141 }
+142 
+143 void assert_little_endian() {
+144   const int x = 1;
+145   const char* y = reinterpret_cast<const char*>(&x);
+146   if (*y != 1) {
+147   ¦ cerr << "the SubX VM only runs on little-endian processors. Do you have Intel (or AMD or Atom) inside?\n";
+148   ¦ exit(1);
+149   }
+150 }
+151 :(before "End Includes")
+152 #include<iostream>
+153 using std::cerr;
+
+ + + diff --git a/html/subx/001help.cc.html b/html/subx/001help.cc.html new file mode 100644 index 00000000..73ad6bfb --- /dev/null +++ b/html/subx/001help.cc.html @@ -0,0 +1,275 @@ + + + + +Mu - subx/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 << "Usage:\n"
+  9   ¦ ¦ ¦<< "  subx test\n";
+ 10   return 0;
+ 11 }
+ 12 
+ 13 //:: Helper function used by the above fragment of code (and later layers too,
+ 14 //:: who knows?).
+ 15 //: The :(code) directive appends function definitions to the end of the
+ 16 //: project. Regardless of where functions are defined, we can call them
+ 17 //: anywhere we like as long as we format the function header in a specific
+ 18 //: way: put it all on a single line without indent, end the line with ') {'
+ 19 //: and no trailing whitespace. As long as functions uniformly start this
+ 20 //: way, our 'build' script contains a little command to automatically
+ 21 //: generate declarations for them.
+ 22 :(code)
+ 23 bool is_equal(char* s, const char* lit) {
+ 24   return strncmp(s, lit, strlen(lit)) == 0;
+ 25 }
+ 26 
+ 27 bool starts_with(const string& s, const string& pat) {
+ 28   string::const_iterator a=s.begin(), b=pat.begin();
+ 29   for (/*nada*/;  a!=s.end() && b!=pat.end();  ++a, ++b)
+ 30   ¦ if (*a != *b) return false;
+ 31   return b == pat.end();
+ 32 }
+ 33 
+ 34 //: I'll throw some style conventions here for want of a better place for them.
+ 35 //: As a rule I hate style guides. Do what you want, that's my motto. But since
+ 36 //: we're dealing with C/C++, the one big thing we want to avoid is undefined
+ 37 //: behavior. If a compiler ever encounters undefined behavior it can make
+ 38 //: your program do anything it wants.
+ 39 //:
+ 40 //: For reference, my checklist of undefined behaviors to watch out for:
+ 41 //:   out-of-bounds access
+ 42 //:   uninitialized variables
+ 43 //:   use after free
+ 44 //:   dereferencing invalid pointers: null, a new of size 0, others
+ 45 //:
+ 46 //:   casting a large number to a type too small to hold it
+ 47 //:
+ 48 //:   integer overflow
+ 49 //:   division by zero and other undefined expressions
+ 50 //:   left-shift by negative count
+ 51 //:   shifting values by more than or equal to the number of bits they contain
+ 52 //:   bitwise operations on signed numbers
+ 53 //:
+ 54 //:   Converting pointers to types of different alignment requirements
+ 55 //:     T* -> void* -> T*: defined
+ 56 //:     T* -> U* -> T*: defined if non-function pointers and alignment requirements are same
+ 57 //:     function pointers may be cast to other function pointers
+ 58 //:
+ 59 //:       Casting a numeric value into a value that can't be represented by the target type (either directly or via static_cast)
+ 60 //:
+ 61 //: To guard against these, some conventions:
+ 62 //:
+ 63 //: 0. Initialize all primitive variables in functions and constructors.
+ 64 //:
+ 65 //: 1. Minimize use of pointers and pointer arithmetic. Avoid 'new' and
+ 66 //: 'delete' as far as possible. Rely on STL to perform memory management to
+ 67 //: avoid use-after-free issues (and memory leaks).
+ 68 //:
+ 69 //: 2. Avoid naked arrays to avoid out-of-bounds access. Never use operator[]
+ 70 //: except with map. Use at() with STL vectors and so on.
+ 71 //:
+ 72 //: 3. Valgrind all the things.
+ 73 //:
+ 74 //: 4. Avoid unsigned numbers. Not strictly an undefined-behavior issue, but
+ 75 //: the extra range doesn't matter, and it's one less confusing category of
+ 76 //: interaction gotchas to worry about.
+ 77 //:
+ 78 //: Corollary: don't use the size() method on containers, since it returns an
+ 79 //: unsigned and that'll cause warnings about mixing signed and unsigned,
+ 80 //: yadda-yadda. Instead use this macro below to perform an unsafe cast to
+ 81 //: signed. We'll just give up immediately if a container's ever too large.
+ 82 //: Basically, Mu is not concerned about this being a little slower than it
+ 83 //: could be. (https://gist.github.com/rygorous/e0f055bfb74e3d5f0af20690759de5a7)
+ 84 //:
+ 85 //: Addendum to corollary: We're going to uniformly use int everywhere, to
+ 86 //: indicate that we're oblivious to number size, and since Clang on 32-bit
+ 87 //: platforms doesn't yet support multiplication over 64-bit integers, and
+ 88 //: since multiplying two integers seems like a more common situation to end
+ 89 //: up in than integer overflow.
+ 90 :(before "End Includes")
+ 91 #define SIZE(X) (assert((X).size() < (1LL<<(sizeof(int)*8-2))), static_cast<int>((X).size()))
+ 92 
+ 93 //: 5. Integer overflow is guarded against at runtime using the -ftrapv flag
+ 94 //: to the compiler, supported by Clang (GCC version only works sometimes:
+ 95 //: http://stackoverflow.com/questions/20851061/how-to-make-gcc-ftrapv-work).
+ 96 :(before "atexit(reset)")
+ 97 initialize_signal_handlers();  // not always necessary, but doesn't hurt
+ 98 //? cerr << INT_MAX+1 << '\n';  // test overflow
+ 99 //? assert(false);  // test SIGABRT
+100 :(code)
+101 // based on https://spin.atomicobject.com/2013/01/13/exceptions-stack-traces-c
+102 void initialize_signal_handlers() {
+103   struct sigaction action;
+104   bzero(&action, sizeof(action));
+105   action.sa_sigaction = dump_and_exit;
+106   sigemptyset(&action.sa_mask);
+107   sigaction(SIGABRT, &action, NULL);  // assert() failure or integer overflow on linux (with -ftrapv)
+108   sigaction(SIGILL,  &action, NULL);  // integer overflow on OS X (with -ftrapv)
+109 }
+110 void dump_and_exit(int sig, unused siginfo_t* dummy1, unused void* dummy2) {
+111   switch (sig) {
+112   ¦ case SIGABRT:
+113   ¦ ¦ #ifndef __APPLE__
+114   ¦ ¦ ¦ cerr << "SIGABRT: might be an integer overflow if it wasn't an assert() failure\n";
+115   ¦ ¦ ¦ _Exit(1);
+116   ¦ ¦ #endif
+117   ¦ ¦ break;
+118   ¦ case SIGILL:
+119   ¦ ¦ #ifdef __APPLE__
+120   ¦ ¦ ¦ cerr << "SIGILL: most likely caused by integer overflow\n";
+121   ¦ ¦ ¦ _Exit(1);
+122   ¦ ¦ #endif
+123   ¦ ¦ break;
+124   ¦ default:
+125   ¦ ¦ break;
+126   }
+127 }
+128 :(before "End Includes")
+129 #include <signal.h>
+130 
+131 //: For good measure we'll also enable SIGFPE.
+132 :(before "atexit(reset)")
+133 feenableexcept(FE_OVERFLOW | FE_UNDERFLOW);
+134 //? assert(sizeof(int) == 4 && sizeof(float) == 4);
+135 //? //                          | exp   |  mantissa
+136 //? int smallest_subnormal = 0b00000000000000000000000000000001;
+137 //? float smallest_subnormal_f = *reinterpret_cast<float*>(&smallest_subnormal);
+138 //? cerr << "ε: " << smallest_subnormal_f << '\n';
+139 //? cerr << "ε/2: " << smallest_subnormal_f/2 << " (underflow)\n";  // test SIGFPE
+140 :(before "End Includes")
+141 #include <fenv.h>
+142 :(code)
+143 #ifdef __APPLE__
+144 // Public domain polyfill for feenableexcept on OS X
+145 // http://www-personal.umich.edu/~williams/archive/computation/fe-handling-example.c
+146 int feenableexcept (unsigned int excepts) {
+147   static fenv_t fenv;
+148   unsigned int new_excepts = excepts & FE_ALL_EXCEPT;
+149   unsigned int old_excepts;
+150   if (fegetenv(&fenv)) return -1;
+151   old_excepts = fenv.__control & FE_ALL_EXCEPT;
+152   fenv.__control &= ~new_excepts;
+153   fenv.__mxcsr   &= ~(new_excepts << 7);
+154   return fesetenv(&fenv) ? -1 : old_excepts;
+155 }
+156 #endif
+157 
+158 //: 6. Map's operator[] being non-const is fucking evil.
+159 :(before "Globals")  // can't generate prototypes for these
+160 // from http://stackoverflow.com/questions/152643/idiomatic-c-for-reading-from-a-const-map
+161 template<typename T> typename T::mapped_type& get(T& map, typename T::key_type const& key) {
+162   typename T::iterator iter(map.find(key));
+163   assert(iter != map.end());
+164   return iter->second;
+165 }
+166 template<typename T> typename T::mapped_type const& get(const T& map, typename T::key_type const& key) {
+167   typename T::const_iterator iter(map.find(key));
+168   assert(iter != map.end());
+169   return iter->second;
+170 }
+171 template<typename T> typename T::mapped_type const& put(T& map, typename T::key_type const& key, typename T::mapped_type const& value) {
+172   map[key] = value;
+173   return map[key];
+174 }
+175 template<typename T> bool contains_key(T& map, typename T::key_type const& key) {
+176   return map.find(key) != map.end();
+177 }
+178 template<typename T> typename T::mapped_type& get_or_insert(T& map, typename T::key_type const& key) {
+179   return map[key];
+180 }
+181 //: The contract: any container that relies on get_or_insert should never call
+182 //: contains_key.
+183 
+184 //: 7. istreams are a royal pain in the arse. You have to be careful about
+185 //: what subclass you try to putback into. You have to watch out for the pesky
+186 //: failbit and badbit. Just avoid eof() and use this helper instead.
+187 :(code)
+188 bool has_data(istream& in) {
+189   return in && !in.eof();
+190 }
+191 
+192 :(before "End Includes")
+193 #include <assert.h>
+194 
+195 #include <iostream>
+196 using std::istream;
+197 using std::ostream;
+198 using std::iostream;
+199 using std::cin;
+200 using std::cout;
+201 using std::cerr;
+202 #include <iomanip>
+203 
+204 #include <string.h>
+205 #include <string>
+206 using std::string;
+207 
+208 #define unused  __attribute__((unused))
+
+ + + diff --git a/html/subx/002test.cc.html b/html/subx/002test.cc.html new file mode 100644 index 00000000..a2a785f4 --- /dev/null +++ b/html/subx/002test.cc.html @@ -0,0 +1,177 @@ + + + + +Mu - subx/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 //:   $ ./mu 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' script
+ 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 .build/test_list line " << (i+1) << '\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 :(code)
+ 74 void run_test(size_t i) {
+ 75   if (i >= sizeof(Tests)/sizeof(Tests[0])) {
+ 76   ¦ cerr << "no test " << i << '\n';
+ 77   ¦ return;
+ 78   }
+ 79   reset();
+ 80   // End Test Setup
+ 81   (*Tests[i])();
+ 82   // End Test Teardown
+ 83 }
+ 84 
+ 85 bool is_integer(const string& s) {
+ 86   return s.find_first_not_of("0123456789-") == string::npos  // no other characters
+ 87   ¦ ¦ && s.find_first_of("0123456789") != string::npos  // at least one digit
+ 88   ¦ ¦ && s.find('-', 1) == string::npos;  // '-' only at first position
+ 89 }
+ 90 
+ 91 int to_integer(string n) {
+ 92   char* end = NULL;
+ 93   // safe because string.c_str() is guaranteed to be null-terminated
+ 94   int result = strtoll(n.c_str(), &end, /*any base*/0);
+ 95   if (*end != '\0') cerr << "tried to convert " << n << " to number\n";
+ 96   assert(*end == '\0');
+ 97   return result;
+ 98 }
+ 99 
+100 void test_is_integer() {
+101   CHECK(is_integer("1234"));
+102   CHECK(is_integer("-1"));
+103   CHECK(!is_integer("234.0"));
+104   CHECK(is_integer("-567"));
+105   CHECK(!is_integer("89-0"));
+106   CHECK(!is_integer("-"));
+107   CHECK(!is_integer("1e3"));  // not supported
+108 }
+109 
+110 :(before "End Includes")
+111 #include <stdlib.h>
+
+ + + diff --git a/html/subx/003trace.cc.html b/html/subx/003trace.cc.html new file mode 100644 index 00000000..9fca7be6 --- /dev/null +++ b/html/subx/003trace.cc.html @@ -0,0 +1,463 @@ + + + + +Mu - subx/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* testing.
+ 17 //: We focus on the domain of inputs the whole program needs to handle rather
+ 18 //: than the correctness of individual functions. All tests invoke the program
+ 19 //: in a single way: by calling run() with some input. As the program operates
+ 20 //: on the input, it traces out a list of _facts_ deduced about the domain:
+ 21 //:   trace("label") << "fact 1: " << val;
+ 22 //:
+ 23 //: Tests can now check these facts:
+ 24 //:   :(scenario foo)
+ 25 //:   34  # call run() with this input
+ 26 //:   +label: fact 1: 34  # 'run' should have deduced this fact
+ 27 //:   -label: fact 1: 35  # the trace should not contain such a fact
+ 28 //:
+ 29 //: Since we never call anything but the run() function directly, we never have
+ 30 //: to rewrite the tests when we reorganize the internals of the program. We
+ 31 //: just have to make sure our rewrite deduces the same facts about the domain,
+ 32 //: and that's something we're going to have to do anyway.
+ 33 //:
+ 34 //: To avoid the combinatorial explosion of integration tests, each layer
+ 35 //: mainly logs facts to the trace with a common *label*. All tests in a layer
+ 36 //: tend to check facts with this label. Validating the facts logged with a
+ 37 //: specific label is like calling functions of that layer directly.
+ 38 //:
+ 39 //: To build robust tests, trace facts about your domain rather than details of
+ 40 //: how you computed them.
+ 41 //:
+ 42 //: More details: http://akkartik.name/blog/tracing-tests
+ 43 //:
+ 44 //: ---
+ 45 //:
+ 46 //: Between layers and domain-driven testing, programming starts to look like a
+ 47 //: fundamentally different activity. Instead of a) superficial, b) local rules
+ 48 //: on c) code [like say http://blog.bbv.ch/2013/06/05/clean-code-cheat-sheet],
+ 49 //: we allow programmers to engage with the a) deep, b) global structure of the
+ 50 //: c) domain. If you can systematically track discontinuities in the domain,
+ 51 //: you don't care if the code used gotos as long as it passed the tests. If
+ 52 //: tests become more robust to run it becomes easier to try out radically
+ 53 //: different implementations for the same program. If code is super-easy to
+ 54 //: rewrite, it becomes less important what indentation style it uses, or that
+ 55 //: the objects are appropriately encapsulated, or that the functions are
+ 56 //: referentially transparent.
+ 57 //:
+ 58 //: Instead of plumbing, programming becomes building and gradually refining a
+ 59 //: map of the environment the program must operate under. Whether a program is
+ 60 //: 'correct' at a given point in time is a red herring; what matters is
+ 61 //: avoiding regression by monotonically nailing down the more 'eventful' parts
+ 62 //: of the terrain. It helps readers new and old, and rewards curiosity, to
+ 63 //: organize large programs in self-similar hierarchies of example scenarios
+ 64 //: colocated with the code that makes them work.
+ 65 //:
+ 66 //:   "Programming properly should be regarded as an activity by which
+ 67 //:   programmers form a mental model, rather than as production of a program."
+ 68 //:   -- Peter Naur (http://alistair.cockburn.us/ASD+book+extract%3A+%22Naur,+Ehn,+Musashi%22)
+ 69 
+ 70 :(before "End Types")
+ 71 struct trace_line {
+ 72   int depth;  // optional field just to help browse traces later
+ 73   string label;
+ 74   string contents;
+ 75   trace_line(string l, string c) :depth(0), label(l), contents(c) {}
+ 76   trace_line(int d, string l, string c) :depth(d), label(l), contents(c) {}
+ 77 };
+ 78 
+ 79 :(before "End Globals")
+ 80 bool Hide_errors = false;
+ 81 bool Dump_trace = false;
+ 82 string Dump_label = "";
+ 83 :(before "End Reset")
+ 84 Hide_errors = false;
+ 85 Dump_trace = false;
+ 86 Dump_label = "";
+ 87 
+ 88 :(before "End Types")
+ 89 // Pre-define some global constants that trace_stream needs to know about.
+ 90 // Since they're in the Types section, they'll be included in any cleaved
+ 91 // compilation units. So no extern linkage.
+ 92 const int Max_depth = 9999;
+ 93 const int Error_depth = 0;  // definitely always print errors
+ 94 
+ 95 struct trace_stream {
+ 96   vector<trace_line> past_lines;
+ 97   // accumulator for current line
+ 98   ostringstream* curr_stream;
+ 99   string curr_label;
+100   int curr_depth;
+101   int callstack_depth;
+102   int collect_depth;
+103   ofstream null_stream;  // never opens a file, so writes silently fail
+104   trace_stream() :curr_stream(NULL), curr_depth(Max_depth), callstack_depth(0), collect_depth(Max_depth) {}
+105   ~trace_stream() { if (curr_stream) delete curr_stream; }
+106 
+107   ostream& stream(string label) {
+108   ¦ return stream(Max_depth, label);
+109   }
+110 
+111   ostream& stream(int depth, string label) {
+112   ¦ if (depth > collect_depth) return null_stream;
+113   ¦ curr_stream = new ostringstream;
+114   ¦ curr_label = label;
+115   ¦ curr_depth = depth;
+116   ¦ return *curr_stream;
+117   }
+118 
+119   // be sure to call this before messing with curr_stream or curr_label
+120   void newline();
+121   // useful for debugging
+122   string readable_contents(string label);  // empty label = show everything
+123 };
+124 
+125 :(code)
+126 void trace_stream::newline() {
+127   if (!curr_stream) return;
+128   string curr_contents = curr_stream->str();
+129   if (!curr_contents.empty()) {
+130   ¦ past_lines.push_back(trace_line(curr_depth, trim(curr_label), curr_contents));  // preserve indent in contents
+131   ¦ if ((!Hide_errors && curr_label == "error")
+132   ¦ ¦ ¦ || Dump_trace
+133   ¦ ¦ ¦ || (!Dump_label.empty() && curr_label == Dump_label))
+134   ¦ ¦ cerr << curr_label << ": " << curr_contents << '\n';
+135   }
+136   delete curr_stream;
+137   curr_stream = NULL;
+138   curr_label.clear();
+139   curr_depth = Max_depth;
+140 }
+141 
+142 string trace_stream::readable_contents(string label) {
+143   ostringstream output;
+144   label = trim(label);
+145   for (vector<trace_line>::iterator p = past_lines.begin();  p != past_lines.end();  ++p)
+146   ¦ if (label.empty() || label == p->label) {
+147   ¦ ¦ output << std::setw(4) << p->depth << ' ' << p->label << ": " << p->contents << '\n';
+148   ¦ }
+149   return output.str();
+150 }
+151 
+152 :(before "End Globals")
+153 trace_stream* Trace_stream = NULL;
+154 int Trace_errors = 0;  // used only when Trace_stream is NULL
+155 
+156 :(before "End Includes")
+157 #define CLEAR_TRACE  delete Trace_stream, Trace_stream = new trace_stream;
+158 
+159 // Top-level helper. IMPORTANT: can't nest
+160 #define trace(...)  !Trace_stream ? cerr /*print nothing*/ : Trace_stream->stream(__VA_ARGS__)
+161 
+162 // Just for debugging; 'git log' should never show any calls to 'dbg'.
+163 #define dbg trace(0, "a")
+164 #define DUMP(label)  if (Trace_stream) cerr << Trace_stream->readable_contents(label);
+165 
+166 // Errors are a special layer.
+167 #define raise  (!Trace_stream ? (++Trace_errors,cerr) /*do print*/ : Trace_stream->stream(Error_depth, "error"))
+168 // If we aren't yet sure how to deal with some corner case, use assert_for_now
+169 // to indicate that it isn't an inviolable invariant.
+170 #define assert_for_now assert
+171 
+172 // Inside tests, fail any tests that displayed (unexpected) errors.
+173 // Expected errors in tests should always be hidden and silently checked for.
+174 :(before "End Test Teardown")
+175 if (Passed && !Hide_errors && trace_contains_errors()) {
+176   Passed = false;
+177 }
+178 :(code)
+179 bool trace_contains_errors() {
+180   return Trace_errors > 0 || trace_count("error") > 0;
+181 }
+182 
+183 :(before "End Types")
+184 struct end {};
+185 :(code)
+186 ostream& operator<<(ostream& os, unused end) {
+187   if (Trace_stream) Trace_stream->newline();
+188   return os;
+189 }
+190 
+191 :(before "End Globals")
+192 bool Save_trace = false;
+193 
+194 // Trace_stream is a resource, lease_tracer uses RAII to manage it.
+195 :(before "End Types")
+196 struct lease_tracer {
+197   lease_tracer();
+198   ~lease_tracer();
+199 };
+200 :(code)
+201 lease_tracer::lease_tracer() { Trace_stream = new trace_stream; }
+202 lease_tracer::~lease_tracer() {
+203   if (!Trace_stream) return;  // in case tests close Trace_stream
+204   if (Save_trace) {
+205   ¦ ofstream fout("last_trace");
+206   ¦ fout << Trace_stream->readable_contents("");
+207   ¦ fout.close();
+208   }
+209   delete Trace_stream, Trace_stream = NULL;
+210 }
+211 :(before "End Includes")
+212 #define START_TRACING_UNTIL_END_OF_SCOPE  lease_tracer leased_tracer;
+213 :(before "End Test Setup")
+214 START_TRACING_UNTIL_END_OF_SCOPE
+215 
+216 :(before "End Includes")
+217 #define CHECK_TRACE_CONTENTS(...)  check_trace_contents(__FUNCTION__, __FILE__, __LINE__, __VA_ARGS__)
+218 
+219 #define CHECK_TRACE_CONTAINS_ERRORS()  CHECK(trace_contains_errors())
+220 #define CHECK_TRACE_DOESNT_CONTAIN_ERRORS() \
+221   if (Passed && trace_contains_errors()) { \
+222   ¦ cerr << "\nF - " << __FUNCTION__ << "(" << __FILE__ << ":" << __LINE__ << "): unexpected errors\n"; \
+223   ¦ DUMP("error"); \
+224   ¦ Passed = false; \
+225   ¦ return; \
+226   }
+227 
+228 #define CHECK_TRACE_COUNT(label, count) \
+229   if (Passed && trace_count(label) != (count)) { \
+230   ¦ cerr << "\nF - " << __FUNCTION__ << "(" << __FILE__ << ":" << __LINE__ << "): trace_count of " << label << " should be " << count << '\n'; \
+231   ¦ cerr << "  got " << trace_count(label) << '\n';  /* multiple eval */ \
+232   ¦ DUMP(label); \
+233   ¦ Passed = false; \
+234   ¦ return;  /* Currently we stop at the very first failure. */ \
+235   }
+236 
+237 #define CHECK_TRACE_DOESNT_CONTAIN(...)  CHECK(trace_doesnt_contain(__VA_ARGS__))
+238 
+239 :(code)
+240 bool check_trace_contents(string FUNCTION, string FILE, int LINE, string expected) {
+241   if (!Passed) return false;
+242   if (!Trace_stream) return false;
+243   vector<string> expected_lines = split(expected, "^D");
+244   int curr_expected_line = 0;
+245   while (curr_expected_line < SIZE(expected_lines) && expected_lines.at(curr_expected_line).empty())
+246   ¦ ++curr_expected_line;
+247   if (curr_expected_line == SIZE(expected_lines)) return true;
+248   string label, contents;
+249   split_label_contents(expected_lines.at(curr_expected_line), &label, &contents);
+250   for (vector<trace_line>::iterator p = Trace_stream->past_lines.begin();  p != Trace_stream->past_lines.end();  ++p) {
+251   ¦ if (label != p->label) continue;
+252   ¦ if (contents != trim(p->contents)) continue;
+253   ¦ ++curr_expected_line;
+254   ¦ while (curr_expected_line < SIZE(expected_lines) && expected_lines.at(curr_expected_line).empty())
+255   ¦ ¦ ++curr_expected_line;
+256   ¦ if (curr_expected_line == SIZE(expected_lines)) return true;
+257   ¦ split_label_contents(expected_lines.at(curr_expected_line), &label, &contents);
+258   }
+259 
+260   if (line_exists_anywhere(label, contents)) {
+261   ¦ cerr << "\nF - " << FUNCTION << "(" << FILE << ":" << LINE << "): line [" << label << ": " << contents << "] out of order in trace:\n";
+262   ¦ DUMP("");
+263   }
+264   else {
+265   ¦ cerr << "\nF - " << FUNCTION << "(" << FILE << ":" << LINE << "): missing [" << contents << "] in trace:\n";
+266   ¦ DUMP(label);
+267   }
+268   Passed = false;
+269   return false;
+270 }
+271 
+272 void split_label_contents(const string& s, string* label, string* contents) {
+273   static const string delim(": ");
+274   size_t pos = s.find(delim);
+275   if (pos == string::npos) {
+276   ¦ *label = "";
+277   ¦ *contents = trim(s);
+278   }
+279   else {
+280   ¦ *label = trim(s.substr(0, pos));
+281   ¦ *contents = trim(s.substr(pos+SIZE(delim)));
+282   }
+283 }
+284 
+285 bool line_exists_anywhere(const string& label, const string& contents) {
+286   for (vector<trace_line>::iterator p = Trace_stream->past_lines.begin();  p != Trace_stream->past_lines.end();  ++p) {
+287   ¦ if (label != p->label) continue;
+288   ¦ if (contents == trim(p->contents)) return true;
+289   }
+290   return false;
+291 }
+292 
+293 int trace_count(string label) {
+294   return trace_count(label, "");
+295 }
+296 
+297 int trace_count(string label, string line) {
+298   if (!Trace_stream) return 0;
+299   long result = 0;
+300   for (vector<trace_line>::iterator p = Trace_stream->past_lines.begin();  p != Trace_stream->past_lines.end();  ++p) {
+301   ¦ if (label == p->label) {
+302   ¦ ¦ if (line == "" || trim(line) == trim(p->contents))
+303   ¦ ¦ ¦ ++result;
+304   ¦ }
+305   }
+306   return result;
+307 }
+308 
+309 int trace_count_prefix(string label, string prefix) {
+310   if (!Trace_stream) return 0;
+311   long result = 0;
+312   for (vector<trace_line>::iterator p = Trace_stream->past_lines.begin();  p != Trace_stream->past_lines.end();  ++p) {
+313   ¦ if (label == p->label) {
+314   ¦ ¦ if (starts_with(trim(p->contents), trim(prefix)))
+315   ¦ ¦ ¦ ++result;
+316   ¦ }
+317   }
+318   return result;
+319 }
+320 
+321 bool trace_doesnt_contain(string label, string line) {
+322   return trace_count(label, line) == 0;
+323 }
+324 
+325 bool trace_doesnt_contain(string expected) {
+326   vector<string> tmp = split_first(expected, ": ");
+327   return trace_doesnt_contain(tmp.at(0), tmp.at(1));
+328 }
+329 
+330 vector<string> split(string s, string delim) {
+331   vector<string> result;
+332   size_t begin=0, end=s.find(delim);
+333   while (true) {
+334   ¦ if (end == string::npos) {
+335   ¦ ¦ result.push_back(string(s, begin, string::npos));
+336   ¦ ¦ break;
+337   ¦ }
+338   ¦ result.push_back(string(s, begin, end-begin));
+339   ¦ begin = end+SIZE(delim);
+340   ¦ end = s.find(delim, begin);
+341   }
+342   return result;
+343 }
+344 
+345 vector<string> split_first(string s, string delim) {
+346   vector<string> result;
+347   size_t end=s.find(delim);
+348   result.push_back(string(s, 0, end));
+349   if (end != string::npos)
+350   ¦ result.push_back(string(s, end+SIZE(delim), string::npos));
+351   return result;
+352 }
+353 
+354 string trim(const string& s) {
+355   string::const_iterator first = s.begin();
+356   while (first != s.end() && isspace(*first))
+357   ¦ ++first;
+358   if (first == s.end()) return "";
+359 
+360   string::const_iterator last = --s.end();
+361   while (last != s.begin() && isspace(*last))
+362   ¦ --last;
+363   ++last;
+364   return string(first, last);
+365 }
+366 
+367 :(before "End Includes")
+368 #include <vector>
+369 using std::vector;
+370 #include <list>
+371 using std::list;
+372 #include <map>
+373 using std::map;
+374 #include <set>
+375 using std::set;
+376 #include <algorithm>
+377 
+378 #include <sstream>
+379 using std::istringstream;
+380 using std::ostringstream;
+381 
+382 #include <fstream>
+383 using std::ifstream;
+384 using std::ofstream;
+385 
+386 :(before "End Globals")
+387 //: In future layers we'll use the depth field as follows:
+388 //:
+389 //: Errors will be depth 0.
+390 //: Mu 'applications' will be able to use depths 1-100 as they like.
+391 //: Primitive statements will occupy 101-9989
+392 extern const int Initial_callstack_depth = 101;
+393 extern const int Max_callstack_depth = 9989;
+394 //: Finally, details of primitive Mu statements will occupy depth 9990-9999
+395 //: (more on that later as well)
+396 //:
+397 //: This framework should help us hide some details at each level, mixing
+398 //: static ideas like layers with the dynamic notion of call-stack depth.
+
+ + + diff --git a/html/subx/003trace.test.cc.html b/html/subx/003trace.test.cc.html new file mode 100644 index 00000000..1ec0e1a7 --- /dev/null +++ b/html/subx/003trace.test.cc.html @@ -0,0 +1,186 @@ + + + + +Mu - subx/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^Dtest layer 2: bar^Dtest layer 1: qux^D");
+ 46 }
+ 47 
+ 48 void test_trace_supports_count() {
+ 49   trace("test layer 1") << "foo" << end();
+ 50   trace("test layer 1") << "foo" << end();
+ 51   CHECK_EQ(trace_count("test layer 1", "foo"), 2);
+ 52 }
+ 53 
+ 54 void test_trace_supports_count2() {
+ 55   trace("test layer 1") << "foo" << end();
+ 56   trace("test layer 1") << "bar" << end();
+ 57   CHECK_EQ(trace_count("test layer 1"), 2);
+ 58 }
+ 59 
+ 60 void test_trace_count_ignores_trailing_whitespace() {
+ 61   trace("test layer 1") << "foo\n" << end();
+ 62   CHECK_EQ(trace_count("test layer 1", "foo"), 1);
+ 63 }
+ 64 
+ 65 // pending: DUMP tests
+ 66 // pending: readable_contents() adds newline if necessary.
+ 67 // pending: raise also prints to stderr.
+ 68 // pending: raise doesn't print to stderr if Hide_errors is set.
+ 69 // pending: raise doesn't have to be saved if Hide_errors is set, just printed.
+ 70 // pending: raise prints to stderr if Trace_stream is NULL.
+ 71 // pending: raise prints to stderr if Trace_stream is NULL even if Hide_errors is set.
+ 72 
+ 73 // can't check trace because trace methods call 'split'
+ 74 
+ 75 void test_split_returns_at_least_one_elem() {
+ 76   vector<string> result = split("", ",");
+ 77   CHECK_EQ(result.size(), 1);
+ 78   CHECK_EQ(result.at(0), "");
+ 79 }
+ 80 
+ 81 void test_split_returns_entire_input_when_no_delim() {
+ 82   vector<string> result = split("abc", ",");
+ 83   CHECK_EQ(result.size(), 1);
+ 84   CHECK_EQ(result.at(0), "abc");
+ 85 }
+ 86 
+ 87 void test_split_works() {
+ 88   vector<string> result = split("abc,def", ",");
+ 89   CHECK_EQ(result.size(), 2);
+ 90   CHECK_EQ(result.at(0), "abc");
+ 91   CHECK_EQ(result.at(1), "def");
+ 92 }
+ 93 
+ 94 void test_split_works2() {
+ 95   vector<string> result = split("abc,def,ghi", ",");
+ 96   CHECK_EQ(result.size(), 3);
+ 97   CHECK_EQ(result.at(0), "abc");
+ 98   CHECK_EQ(result.at(1), "def");
+ 99   CHECK_EQ(result.at(2), "ghi");
+100 }
+101 
+102 void test_split_handles_multichar_delim() {
+103   vector<string> result = split("abc,,def,,ghi", ",,");
+104   CHECK_EQ(result.size(), 3);
+105   CHECK_EQ(result.at(0), "abc");
+106   CHECK_EQ(result.at(1), "def");
+107   CHECK_EQ(result.at(2), "ghi");
+108 }
+109 
+110 void test_trim() {
+111   CHECK_EQ(trim(""), "");
+112   CHECK_EQ(trim(" "), "");
+113   CHECK_EQ(trim("  "), "");
+114   CHECK_EQ(trim("a"), "a");
+115   CHECK_EQ(trim(" a"), "a");
+116   CHECK_EQ(trim("  a"), "a");
+117   CHECK_EQ(trim("  ab"), "ab");
+118   CHECK_EQ(trim("a "), "a");
+119   CHECK_EQ(trim("a  "), "a");
+120   CHECK_EQ(trim("ab  "), "ab");
+121   CHECK_EQ(trim(" a "), "a");
+122   CHECK_EQ(trim("  a  "), "a");
+123   CHECK_EQ(trim("  ab  "), "ab");
+124 }
+
+ + + diff --git a/html/subx/010core.cc.html b/html/subx/010core.cc.html index 606d4dec..8d7cb3a2 100644 --- a/html/subx/010core.cc.html +++ b/html/subx/010core.cc.html @@ -80,10 +80,10 @@ if ('onhashchange' in window) { 17 uint32_t u; 18 }; 19 :(before "End Globals") - 20 reg R[NUM_INT_REGISTERS] = { {0} }; + 20 reg Reg[NUM_INT_REGISTERS] = { {0} }; 21 uint32_t EIP = 0; 22 :(before "End Reset") - 23 bzero(R, sizeof(R)); + 23 bzero(Reg, sizeof(Reg)); 24 EIP = 0; 25 26 //:: simulated flag registers; just a subset that we care about @@ -103,146 +103,185 @@ if ('onhashchange' in window) { 40 /* arg1 and arg2 must be signed */ \ 41 int64_t tmp = arg1 op arg2; \ 42 arg1 = arg1 op arg2; \ - 43 SF = (arg1 < 0); \ - 44 ZF = (arg1 == 0); \ - 45 OF = (arg1 != tmp); \ - 46 } - 47 - 48 #define BINARY_BITWISE_OP(op, arg1, arg2) { \ - 49 /* arg1 and arg2 must be unsigned */ \ - 50 arg1 = arg1 op arg2; \ - 51 SF = (arg1 >> 31); \ - 52 ZF = (arg1 == 0); \ - 53 OF = false; \ - 54 } - 55 - 56 //:: simulated RAM + 43 trace(2, "run") << "storing 0x" << HEXWORD << arg1 << end(); \ + 44 SF = (arg1 < 0); \ + 45 ZF = (arg1 == 0); \ + 46 OF = (arg1 != tmp); \ + 47 } + 48 + 49 #define BINARY_BITWISE_OP(op, arg1, arg2) { \ + 50 /* arg1 and arg2 must be unsigned */ \ + 51 arg1 = arg1 op arg2; \ + 52 trace(2, "run") << "storing 0x" << HEXWORD << arg1 << end(); \ + 53 SF = (arg1 >> 31); \ + 54 ZF = (arg1 == 0); \ + 55 OF = false; \ + 56 } 57 - 58 :(before "End Globals") - 59 map<uint32_t, uint8_t> Memory; - 60 uint32_t End_of_program = 0; - 61 :(before "End Reset") - 62 Memory.clear(); - 63 End_of_program = 0; - 64 - 65 //:: core interpreter loop - 66 - 67 :(scenario add_imm32_to_eax) - 68 # In scenarios, programs are a series of hex bytes, each (variable-length) - 69 # instruction on one line. - 70 # - 71 # x86 instructions consist of the following parts (see cheatsheet.pdf): - 72 # opcode ModRM SIB displacement immediate - 73 # instruction mod, reg, R/M bits scale, index, base - 74 # 1-3 bytes 0/1 byte 0/1 byte 0/1/2/4 bytes 0/1/2/4 bytes - 75 ¦ 0x05 0a 0b 0c 0d # add 0x0d0c0b0a to EAX - 76 +load: 1 -> 05 - 77 +load: 2 -> 0a - 78 +load: 3 -> 0b - 79 +load: 4 -> 0c - 80 +load: 5 -> 0d - 81 +run: add imm32 0x0d0c0b0a to reg EAX - 82 +reg: storing 0x0d0c0b0a in reg EAX - 83 - 84 :(code) - 85 // helper for tests: load a program into memory from a textual representation - 86 // of its bytes, and run it - 87 void run(const string& text_bytes) { - 88 load_program(text_bytes); - 89 EIP = 1; // preserve null pointer - 90 while (EIP < End_of_program) - 91 ¦ run_one_instruction(); - 92 } - 93 - 94 void load_program(const string& text_bytes) { - 95 uint32_t addr = 1; - 96 // we'll use C's 'strtol` to parse ASCII hex bytes - 97 // strtol needs a char*, so we grab the buffer backing the string object - 98 char* curr = const_cast<char*>(&text_bytes[0]); // non-portable, but blessed by Herb Sutter (http://herbsutter.com/2008/04/07/cringe-not-vectors-are-guaranteed-to-be-contiguous/#comment-483) - 99 char* max = curr + strlen(curr); -100 while (curr < max) { -101 ¦ // skip whitespace -102 ¦ while (*curr == ' ' || *curr == '\n') ++curr; -103 ¦ // skip comments -104 ¦ if (*curr == '#') { -105 ¦ ¦ while (*curr != '\n') { -106 ¦ ¦ ¦ ++curr; -107 ¦ ¦ ¦ if (curr >= max) break; -108 ¦ ¦ } -109 ¦ ¦ ++curr; -110 ¦ ¦ continue; -111 ¦ } -112 ¦ put(Memory, addr, strtol(curr, &curr, /*hex*/16)); -113 ¦ trace(99, "load") << addr << " -> " << HEXBYTE << static_cast<unsigned int>(get_or_insert(Memory, addr)) << end(); // ugly that iostream doesn't print uint8_t as an integer -114 ¦ addr++; -115 } -116 End_of_program = addr; -117 } -118 -119 // skeleton of how x86 instructions are decoded -120 void run_one_instruction() { -121 uint8_t op=0, op2=0, op3=0; -122 switch(op = next()) { -123 // our first opcode -124 case 0xf4: // hlt -125 ¦ EIP = End_of_program; -126 ¦ break; -127 case 0x05: { // add imm32 to EAX -128 ¦ int32_t arg2 = imm32(); -129 ¦ trace(2, "run") << "add imm32 0x" << HEXWORD << arg2 << " to reg EAX" << end(); -130 ¦ BINARY_ARITHMETIC_OP(+, R[EAX].i, arg2); -131 ¦ trace(98, "reg") << "storing 0x" << HEXWORD << R[EAX].i << " in reg EAX" << end(); -132 ¦ break; -133 } -134 // End Single-Byte Opcodes -135 case 0x0f: -136 ¦ switch(op2 = next()) { -137 ¦ // End Two-Byte Opcodes Starting With 0f -138 ¦ default: -139 ¦ ¦ cerr << "unrecognized second opcode after 0f: " << std::hex << static_cast<int>(op2) << '\n'; -140 ¦ ¦ exit(1); -141 ¦ } -142 ¦ break; -143 case 0xf3: -144 ¦ switch(op2 = next()) { -145 ¦ // End Two-Byte Opcodes Starting With f3 -146 ¦ case 0x0f: -147 ¦ ¦ switch(op3 = next()) { -148 ¦ ¦ // End Three-Byte Opcodes Starting With f3 0f -149 ¦ ¦ default: -150 ¦ ¦ ¦ cerr << "unrecognized third opcode after f3 0f: " << std::hex << static_cast<int>(op3) << '\n'; -151 ¦ ¦ ¦ exit(1); -152 ¦ ¦ } -153 ¦ ¦ break; -154 ¦ default: -155 ¦ ¦ cerr << "unrecognized second opcode after f3: " << std::hex << static_cast<int>(op2) << '\n'; -156 ¦ ¦ exit(1); -157 ¦ } -158 ¦ break; -159 default: -160 ¦ cerr << "unrecognized opcode: " << std::hex << static_cast<int>(op) << '\n'; -161 ¦ exit(1); + 58 //:: simulated RAM + 59 + 60 :(before "End Globals") + 61 vector<uint8_t> Mem; + 62 uint32_t End_of_program = 0; + 63 :(before "End Reset") + 64 Mem.clear(); + 65 Mem.resize(1024); + 66 End_of_program = 0; + 67 + 68 //:: core interpreter loop + 69 + 70 :(scenario add_imm32_to_eax) + 71 # In scenarios, programs are a series of hex bytes, each (variable-length) + 72 # instruction on one line. + 73 # + 74 # x86 instructions consist of the following parts (see cheatsheet.pdf): + 75 # opcode ModR/M SIB displacement immediate + 76 # instruction mod, reg, Reg/Mem bits scale, index, base + 77 # 1-3 bytes 0/1 byte 0/1 byte 0/1/2/4 bytes 0/1/2/4 bytes + 78 ¦ 05 0a 0b 0c 0d # add 0x0d0c0b0a to EAX + 79 # All hex bytes must be exactly 2 characters each. No '0x' prefixes. + 80 +load: 1 -> 05 + 81 +load: 2 -> 0a + 82 +load: 3 -> 0b + 83 +load: 4 -> 0c + 84 +load: 5 -> 0d + 85 +run: add imm32 0x0d0c0b0a to reg EAX + 86 +run: storing 0x0d0c0b0a + 87 + 88 :(code) + 89 // helper for tests: load a program into memory from a textual representation + 90 // of its bytes, and run it + 91 void run(const string& text_bytes) { + 92 load_program(text_bytes); + 93 EIP = 1; // preserve null pointer + 94 while (EIP < End_of_program) + 95 ¦ run_one_instruction(); + 96 } + 97 + 98 // skeleton of how x86 instructions are decoded + 99 void run_one_instruction() { +100 uint8_t op=0, op2=0, op3=0; +101 switch (op = next()) { +102 case 0xf4: // hlt +103 ¦ EIP = End_of_program; +104 ¦ break; +105 // our first opcode +106 case 0x05: { // add imm32 to EAX +107 ¦ int32_t arg2 = imm32(); +108 ¦ trace(2, "run") << "add imm32 0x" << HEXWORD << arg2 << " to reg EAX" << end(); +109 ¦ BINARY_ARITHMETIC_OP(+, Reg[EAX].i, arg2); +110 ¦ break; +111 } +112 // End Single-Byte Opcodes +113 case 0x0f: +114 ¦ switch(op2 = next()) { +115 ¦ // End Two-Byte Opcodes Starting With 0f +116 ¦ default: +117 ¦ ¦ cerr << "unrecognized second opcode after 0f: " << HEXBYTE << NUM(op2) << '\n'; +118 ¦ ¦ exit(1); +119 ¦ } +120 ¦ break; +121 case 0xf3: +122 ¦ switch(op2 = next()) { +123 ¦ // End Two-Byte Opcodes Starting With f3 +124 ¦ case 0x0f: +125 ¦ ¦ switch(op3 = next()) { +126 ¦ ¦ // End Three-Byte Opcodes Starting With f3 0f +127 ¦ ¦ default: +128 ¦ ¦ ¦ cerr << "unrecognized third opcode after f3 0f: " << HEXBYTE << NUM(op3) << '\n'; +129 ¦ ¦ ¦ exit(1); +130 ¦ ¦ } +131 ¦ ¦ break; +132 ¦ default: +133 ¦ ¦ cerr << "unrecognized second opcode after f3: " << HEXBYTE << NUM(op2) << '\n'; +134 ¦ ¦ exit(1); +135 ¦ } +136 ¦ break; +137 default: +138 ¦ cerr << "unrecognized opcode: " << HEXBYTE << NUM(op) << '\n'; +139 ¦ exit(1); +140 } +141 } +142 +143 void load_program(const string& text_bytes) { +144 uint32_t addr = 1; +145 istringstream in(text_bytes); +146 in >> std::noskipws; +147 while (has_data(in)) { +148 ¦ char c1 = next_hex_byte(in); +149 ¦ if (c1 == '\0') break; +150 ¦ if (!has_data(in)) { +151 ¦ ¦ raise << "input program truncated mid-byte\n" << end(); +152 ¦ ¦ return; +153 ¦ } +154 ¦ char c2 = next_hex_byte(in); +155 ¦ if (c2 == '\0') { +156 ¦ ¦ raise << "input program truncated mid-byte\n" << end(); +157 ¦ ¦ return; +158 ¦ } +159 ¦ Mem.at(addr) = to_byte(c1, c2); +160 ¦ trace(99, "load") << addr << " -> " << HEXBYTE << NUM(Mem.at(addr)) << end(); +161 ¦ addr++; 162 } -163 } -164 -165 uint8_t next() { -166 return get_or_insert(Memory, EIP++); -167 } -168 -169 // read a 32-bit immediate in little-endian order from the instruction stream -170 int32_t imm32() { -171 int32_t result = next(); -172 result |= (next()<<8); -173 result |= (next()<<16); -174 result |= (next()<<24); -175 return result; -176 } -177 -178 :(before "End Includes") -179 #include <iomanip> -180 #define HEXBYTE std::hex << std::setw(2) << std::setfill('0') -181 #define HEXWORD std::hex << std::setw(8) << std::setfill('0') -182 #include <stdint.h> +163 End_of_program = addr; +164 } +165 +166 char next_hex_byte(istream& in) { +167 while (has_data(in)) { +168 ¦ char c = '\0'; +169 ¦ in >> c; +170 ¦ if (c == ' ' || c == '\n') continue; +171 ¦ while (c == '#') { +172 ¦ ¦ while (has_data(in)) { +173 ¦ ¦ ¦ in >> c; +174 ¦ ¦ ¦ if (c == '\n') { +175 ¦ ¦ ¦ ¦ in >> c; +176 ¦ ¦ ¦ ¦ break; +177 ¦ ¦ ¦ } +178 ¦ ¦ } +179 ¦ } +180 ¦ if (c >= '0' && c <= '9') return c; +181 ¦ else if (c >= 'a' && c <= 'f') return c; +182 ¦ else if (c >= 'A' && c <= 'F') return tolower(c); +183 ¦ // disallow any non-hex characters, including a '0x' prefix +184 ¦ if (!isspace(c)) { +185 ¦ ¦ raise << "invalid non-hex character '" << c << "'\n" << end(); +186 ¦ ¦ break; +187 ¦ } +188 } +189 return '\0'; +190 } +191 +192 uint8_t to_byte(char hex_byte1, char hex_byte2) { +193 return to_hex_num(hex_byte1)*16 + to_hex_num(hex_byte2); +194 } +195 uint8_t to_hex_num(char c) { +196 if (c >= '0' && c <= '9') return c - '0'; +197 if (c >= 'a' && c <= 'f') return c - 'a' + 10; +198 assert(false); +199 return 0; +200 } +201 +202 inline uint8_t next() { +203 return Mem.at(EIP++); +204 } +205 +206 // read a 32-bit immediate in little-endian order from the instruction stream +207 int32_t imm32() { +208 int32_t result = next(); +209 result |= (next()<<8); +210 result |= (next()<<16); +211 result |= (next()<<24); +212 return result; +213 } +214 +215 :(before "End Includes") +216 #include <iomanip> +217 #define HEXBYTE std::hex << std::setw(2) << std::setfill('0') +218 #define HEXWORD std::hex << std::setw(8) << std::setfill('0') +219 // ugly that iostream doesn't print uint8_t as an integer +220 #define NUM(X) static_cast<int>(X) +221 #include <stdint.h> diff --git a/html/subx/011direct_addressing.cc.html b/html/subx/011direct_addressing.cc.html new file mode 100644 index 00000000..f6b8edc6 --- /dev/null +++ b/html/subx/011direct_addressing.cc.html @@ -0,0 +1,132 @@ + + + + +Mu - subx/011direct_addressing.cc + + + + + + + + + + +
+ 1 //: operating directly on a register
+ 2 
+ 3 :(scenario add_r32_to_r32)
+ 4 % Reg[0].i = 0x10;
+ 5 % Reg[3].i = 1;
+ 6 # op  ModR/M  SIB   displacement  immediate
+ 7   01  d8                                      # add EBX (reg 3) to EAX (reg 0)
+ 8 +run: add reg 3 to effective address
+ 9 +run: effective address is reg 0
+10 +run: storing 0x00000011
+11 
+12 :(before "End Single-Byte Opcodes")
+13 case 0x01: {  // add r32 to r/m32
+14   uint8_t modrm = next();
+15   uint8_t arg2 = (modrm>>3)&0x7;
+16   trace(2, "run") << "add reg " << NUM(arg2) << " to effective address" << end();
+17   int32_t* arg1 = effective_address(modrm);
+18   BINARY_ARITHMETIC_OP(+, *arg1, Reg[arg2].i);
+19   break;
+20 }
+21 
+22 :(code)
+23 // Implement tables 2-2 and 2-3 in the Intel manual, Volume 2.
+24 // We return a pointer so that instructions can write to multiple bytes in
+25 // 'Mem' at once.
+26 int32_t* effective_address(uint8_t modrm) {
+27   uint8_t mod = (modrm>>6);
+28   // ignore middle 3 'reg opcode' bits
+29   uint8_t rm = modrm & 0x7;
+30   int32_t* result = 0;
+31   switch (mod) {
+32   case 3:
+33   ¦ // mod 3 is just register direct addressing
+34   ¦ trace(2, "run") << "effective address is reg " << NUM(rm) << end();
+35   ¦ result = &Reg[rm].i;
+36   ¦ break;
+37   // End Mod Special-cases
+38   default:
+39   ¦ cerr << "unrecognized mod bits: " << NUM(mod) << '\n';
+40   ¦ exit(1);
+41   }
+42   return result;
+43 }
+44 
+45 //:: subtract
+46 
+47 :(scenario subtract_r32_from_r32)
+48 % Reg[0].i = 10;
+49 % Reg[3].i = 1;
+50 # op  ModR/M  SIB   displacement  immediate
+51   29  d8                                      # subtract EBX (reg 3) from EAX (reg 0)
+52 +run: subtract reg 3 from effective address
+53 +run: effective address is reg 0
+54 +run: storing 0x00000009
+55 
+56 :(before "End Single-Byte Opcodes")
+57 case 0x29: {  // subtract r32 from r/m32
+58   uint8_t modrm = next();
+59   uint8_t arg2 = (modrm>>3)&0x7;
+60   trace(2, "run") << "subtract reg " << NUM(arg2) << " from effective address" << end();
+61   int32_t* arg1 = effective_address(modrm);
+62   BINARY_ARITHMETIC_OP(-, *arg1, Reg[arg2].i);
+63   break;
+64 }
+
+ + + diff --git a/html/subx/012indirect_addressing.cc.html b/html/subx/012indirect_addressing.cc.html new file mode 100644 index 00000000..ef919f1d --- /dev/null +++ b/html/subx/012indirect_addressing.cc.html @@ -0,0 +1,148 @@ + + + + +Mu - subx/012indirect_addressing.cc + + + + + + + + + + +
+ 1 //: operating on memory at the address provided by some register
+ 2 
+ 3 :(scenario add_r32_to_mem_at_r32)
+ 4 % Reg[3].i = 0x10;
+ 5 % Reg[0].i = 0x60;
+ 6 # word in addresses 0x60-0x63 has value 1
+ 7 % Mem.at(0x60) = 1;
+ 8 # op  ModR/M  SIB   displacement  immediate
+ 9   01  18                                     # add EBX (reg 3) to *EAX (reg 0)
+10 +run: add reg 3 to effective address
+11 +run: effective address is mem at address 0x60 (reg 0)
+12 +run: storing 0x00000011
+13 
+14 :(before "End Mod Special-cases")
+15 case 0:
+16   // mod 0 is usually indirect addressing
+17   switch (rm) {
+18   default:
+19   ¦ trace(2, "run") << "effective address is mem at address 0x" << std::hex << Reg[rm].u << " (reg " << NUM(rm) << ")" << end();
+20   ¦ assert(Reg[rm].u + sizeof(int32_t) <= Mem.size());
+21   ¦ result = reinterpret_cast<int32_t*>(&Mem.at(Reg[rm].u));  // rely on the host itself being in little-endian order
+22   ¦ break;
+23   // End Mod 0 Special-cases
+24   }
+25   break;
+26 
+27 //:
+28 
+29 :(scenario add_mem_at_r32_to_r32)
+30 % Reg[0].i = 0x60;
+31 % Reg[3].i = 0x10;
+32 % Mem.at(0x60) = 1;
+33 # op  ModR/M  SIB   displacement  immediate
+34   03  18                                      # add *EAX (reg 0) to EBX (reg 3)
+35 +run: add effective address to reg 3
+36 +run: effective address is mem at address 0x60 (reg 0)
+37 +run: storing 0x00000011
+38 
+39 :(before "End Single-Byte Opcodes")
+40 case 0x03: {  // add r/m32 to r32
+41   uint8_t modrm = next();
+42   uint8_t arg1 = (modrm>>3)&0x7;
+43   trace(2, "run") << "add effective address to reg " << NUM(arg1) << end();
+44   const int32_t* arg2 = effective_address(modrm);
+45   BINARY_ARITHMETIC_OP(+, Reg[arg1].i, *arg2);
+46   break;
+47 }
+48 
+49 //:: subtract
+50 
+51 :(scenario sub_r32_from_mem_at_r32)
+52 % Reg[0].i = 0x60;
+53 % Mem.at(0x60) = 10;
+54 % Reg[3].i = 1;
+55 # op  ModRM   SIB   displacement  immediate
+56   29  18                                      # subtract EBX (reg 3) from *EAX (reg 0)
+57 +run: subtract reg 3 from effective address
+58 +run: effective address is mem at address 0x60 (reg 0)
+59 +run: storing 0x00000009
+60 
+61 //:
+62 
+63 :(scenario sub_mem_at_r32_from_r32)
+64 % Reg[0].i = 0x60;
+65 % Mem.at(0x60) = 1;
+66 % Reg[3].i = 10;
+67 # op  ModRM   SIB   displacement  immediate
+68   2b  18                                      # subtract *EAX (reg 0) from EBX (reg 3)
+69 +run: subtract effective address from reg 3
+70 +run: effective address is mem at address 0x60 (reg 0)
+71 +run: storing 0x00000009
+72 
+73 :(before "End Single-Byte Opcodes")
+74 case 0x2b: {  // subtract r/m32 from r32
+75   uint8_t modrm = next();
+76   uint8_t arg1 = (modrm>>3)&0x7;
+77   trace(2, "run") << "subtract effective address from reg " << NUM(arg1) << end();
+78   const int32_t* arg2 = effective_address(modrm);
+79   BINARY_ARITHMETIC_OP(-, Reg[arg1].i, *arg2);
+80   break;
+81 }
+
+ + + diff --git a/html/subx/013immediate_addressing.cc.html b/html/subx/013immediate_addressing.cc.html new file mode 100644 index 00000000..f70ab384 --- /dev/null +++ b/html/subx/013immediate_addressing.cc.html @@ -0,0 +1,157 @@ + + + + +Mu - subx/013immediate_addressing.cc + + + + + + + + + + +
+ 1 //: instructions that (immediately) contain an argument to act with
+ 2 
+ 3 :(scenario add_imm32_to_r32)
+ 4 % Reg[3].i = 1;
+ 5 # op  ModRM   SIB   displacement  immediate
+ 6   81  c3                          0a 0b 0c 0d  # add 0x0d0c0b0a to EBX (reg 3)
+ 7 +run: combine imm32 0x0d0c0b0a with effective address
+ 8 +run: effective address is reg 3
+ 9 +run: subop add
+10 +run: storing 0x0d0c0b0b
+11 
+12 :(before "End Single-Byte Opcodes")
+13 case 0x81: {  // combine imm32 with r/m32
+14   uint8_t modrm = next();
+15   int32_t arg2 = imm32();
+16   trace(2, "run") << "combine imm32 0x" << HEXWORD << arg2 << " with effective address" << end();
+17   int32_t* arg1 = effective_address(modrm);
+18   uint8_t subop = (modrm>>3)&0x7;  // middle 3 'reg opcode' bits
+19   switch (subop) {
+20   case 0:
+21   ¦ trace(2, "run") << "subop add" << end();
+22   ¦ BINARY_ARITHMETIC_OP(+, *arg1, arg2);
+23   ¦ break;
+24   // End Op 81 Subops
+25   default:
+26   ¦ cerr << "unrecognized sub-opcode after 81: " << NUM(subop) << '\n';
+27   ¦ exit(1);
+28   }
+29   break;
+30 }
+31 
+32 //:
+33 
+34 :(scenario add_imm32_to_mem_at_r32)
+35 % Reg[3].i = 0x60;
+36 % Mem.at(0x60) = 1;
+37 # op  ModR/M  SIB   displacement  immediate
+38   81  03                          0a 0b 0c 0d  # add 0x0d0c0b0a to *EBX (reg 3)
+39 +run: combine imm32 0x0d0c0b0a with effective address
+40 +run: effective address is mem at address 0x60 (reg 3)
+41 +run: subop add
+42 +run: storing 0x0d0c0b0b
+43 
+44 //:: subtract
+45 
+46 :(scenario sub_imm32_from_eax)
+47 % Reg[EAX].i = 0x0d0c0baa;
+48 # op  ModR/M  SIB   displacement  immediate
+49   2d                              0a 0b 0c 0d  # subtract 0x0d0c0b0a from EAX (reg 0)
+50 +run: subtract imm32 0x0d0c0b0a from reg EAX
+51 +run: storing 0x000000a0
+52 
+53 :(before "End Single-Byte Opcodes")
+54 case 0x2d: {  // subtract imm32 from EAX
+55   int32_t arg2 = imm32();
+56   trace(2, "run") << "subtract imm32 0x" << HEXWORD << arg2 << " from reg EAX" << end();
+57   BINARY_ARITHMETIC_OP(-, Reg[EAX].i, arg2);
+58   break;
+59 }
+60 
+61 //:
+62 
+63 :(scenario sub_imm32_from_mem_at_r32)
+64 % Reg[3].i = 0x60;
+65 % Mem.at(0x60) = 10;
+66 # op  ModRM   SIB   displacement  immediate
+67   81  2b                          01 00 00 00  # subtract 1 from *EBX (reg 3)
+68 +run: combine imm32 0x00000001 with effective address
+69 +run: effective address is mem at address 0x60 (reg 3)
+70 +run: subop subtract
+71 +run: storing 0x00000009
+72 
+73 //:
+74 
+75 :(scenario sub_imm32_from_r32)
+76 % Reg[3].i = 10;
+77 # op  ModRM   SIB   displacement  immediate
+78   81  eb                          01 00 00 00  # subtract 1 from EBX (reg 3)
+79 +run: combine imm32 0x00000001 with effective address
+80 +run: effective address is reg 3
+81 +run: subop subtract
+82 +run: storing 0x00000009
+83 
+84 :(before "End Op 81 Subops")
+85 case 5: {
+86   trace(2, "run") << "subop subtract" << end();
+87   BINARY_ARITHMETIC_OP(-, *arg1, arg2);
+88   break;
+89 }
+
+ + + diff --git a/update_html b/update_html index 024b2ddb..c1a79ed0 100755 --- a/update_html +++ b/update_html @@ -86,5 +86,19 @@ process() { mv $f.out $f done + rm html/subx/*.html + for f in subx/*.cc + do + process $f + done + ( cd subx + ctags -x *.cc > /tmp/tags + ) + linkify/linkify /tmp/tags html/subx/*.html + for f in html/subx/*.cc.html + do + mv $f.out $f + done + rm /tmp/tags ( cd linkify; clean; ) -- cgit 1.4.1-2-gfad0