diff options
Diffstat (limited to 'subx')
128 files changed, 0 insertions, 43901 deletions
diff --git a/subx/subx b/subx index 257e2f13..257e2f13 100755 --- a/subx/subx +++ b/subx diff --git a/subx/000organization.cc b/subx/000organization.cc deleted file mode 100644 index 87004af5..00000000 --- a/subx/000organization.cc +++ /dev/null @@ -1,162 +0,0 @@ -//: You guessed right: the '000' prefix means you should start reading here. -//: -//: This project is set up to load all files with a numeric prefix. Just -//: create a new file and start hacking. -//: -//: The first few files (00*) are independent of what this program does, an -//: experimental skeleton that will hopefully make it both easier for others to -//: understand and more malleable, easier to rewrite and remould into radically -//: different shapes without breaking in subtle corner cases. The premise is -//: that understandability and rewrite-friendliness are related in a virtuous -//: cycle. Doing one well makes it easier to do the other. -//: -//: Lower down, this file contains a legal, bare-bones C++ program. It doesn't -//: do anything yet; subsequent files will contain :(...) directives to insert -//: lines into it. For example: -//: :(after "more events") -//: This directive means: insert the following lines after a line in the -//: program containing the words "more events". -//: -//: A simple tool is included to 'tangle' all the files together in sequence -//: according to their directives into a single source file containing all the -//: code for the project, and then feed the source file to the compiler. -//: (It'll drop these comments starting with a '//:' prefix that only make -//: sense before tangling.) -//: -//: Directives free up the programmer to order code for others to read rather -//: than as forced by the computer or compiler. Each individual feature can be -//: organized in a self-contained 'layer' that adds code to many different data -//: structures and functions all over the program. The right decomposition into -//: layers will let each layer make sense in isolation. -//: -//: "If I look at any small part of it, I can see what is going on -- I don't -//: need to refer to other parts to understand what something is doing. -//: -//: If I look at any large part in overview, I can see what is going on -- I -//: don't need to know all the details to get it. -//: -//: Every level of detail is as locally coherent and as well thought-out as -//: any other level." -//: -//: -- Richard Gabriel, "The Quality Without A Name" -//: (http://dreamsongs.com/Files/PatternsOfSoftware.pdf, page 42) -//: -//: Directives are powerful; they permit inserting or modifying any point in -//: the program. Using them tastefully requires mapping out specific lines as -//: waypoints for future layers to hook into. Often such waypoints will be in -//: comments, capitalized to hint that other layers rely on their presence. -//: -//: A single waypoint might have many different code fragments hooking into -//: it from all over the codebase. Use 'before' directives to insert -//: code at a location in order, top to bottom, and 'after' directives to -//: insert code in reverse order. By convention waypoints intended for insertion -//: before begin with 'End'. Notice below how the layers line up above the "End -//: Foo" waypoint. -//: -//: File 001 File 002 File 003 -//: ============ =================== =================== -//: // Foo -//: ------------ -//: <---- :(before "End Foo") -//: .... -//: ... -//: ------------ -//: <---------------------------- :(before "End Foo") -//: .... -//: ... -//: // End Foo -//: ============ -//: -//: Here's part of a layer in color: http://i.imgur.com/0eONnyX.png. Directives -//: are shaded dark. -//: -//: Layers do more than just shuffle code around. In a well-organized codebase -//: it should be possible to stop loading after any file/layer, build and run -//: the program, and pass all tests for loaded features. (Relevant is -//: http://youtube.com/watch?v=c8N72t7aScY, a scene from "2001: A Space -//: Odyssey".) Get into the habit of running the included script called -//: 'test_layers' before you commit any changes. -//: -//: This 'subsetting guarantee' ensures that this directory contains a -//: cleaned-up narrative of the evolution of this codebase. Organizing -//: autobiographically allows newcomers to rapidly orient themselves, reading -//: the first few files to understand a simple gestalt of a program's core -//: purpose and features, and later gradually working their way through other -//: features as the need arises. -//: -//: Programmers shouldn't need to understand everything about a program to -//: hack on it. But they shouldn't be prevented from a thorough understanding -//: of each aspect either. The goal of layers is to reward curiosity. - -// Includes -// End Includes - -// Types -// End Types - -// Function prototypes are auto-generated in the 'build' script; define your -// functions in any order. Just be sure to declare each function header all on -// one line, ending with the '{'. Our auto-generation scripts are too minimal -// and simple-minded to handle anything else. -#include "function_list" // by convention, files ending with '_list' are auto-generated - -// Globals -// -// All statements in this section should always define a single variable on a -// single line. The 'build' script will simple-mindedly auto-generate extern -// declarations for them. Remember to define (not just declare) constants with -// extern linkage in this section, since C++ global constants have internal -// linkage by default. -// -// End Globals - -int main(int argc, char* argv[]) { - atexit(reset); - // we require a 32-bit little-endian system - assert(sizeof(int) == 4); - assert(sizeof(float) == 4); - assert_little_endian(); - - // End One-time Setup - - // Commandline Parsing - // End Commandline Parsing - - // End Main - - return 0; -} - -// Unit Tests -// End Unit Tests - -//: our first directive; insert the following headers at the start of the program -:(before "End Includes") -#include <assert.h> -#include <stdlib.h> - -//: Without directives or with the :(code) directive, lines get added at the -//: end. -//: -//: Regardless of where functions are defined, we can call them anywhere we -//: like as long as we format the function header in a specific way: put it -//: all on a single line without indent, end the line with ') {' and no -//: trailing whitespace. As long as functions uniformly start this way, our -//: 'build' script contains a little command to automatically generate -//: declarations for them. -:(code) -void reset() { - // End Reset -} - -void assert_little_endian() { - const int x = 1; - const char* y = reinterpret_cast<const char*>(&x); - if (*y != 1) { - cerr << "SubX requires a little-endian processor. Do you have Intel (or AMD or Atom) inside?\n"; - exit(1); - } -} -:(before "End Includes") -#include<iostream> -using std::cerr; diff --git a/subx/001help.cc b/subx/001help.cc deleted file mode 100644 index 7236ca50..00000000 --- a/subx/001help.cc +++ /dev/null @@ -1,294 +0,0 @@ -//: Everything this project/binary supports. -//: This should give you a sense for what to look forward to in later layers. - -:(before "End Commandline Parsing") -if (argc <= 1 || is_equal(argv[1], "--help")) { - //: this is the functionality later layers will provide - // currently no automated tests for commandline arg parsing - cerr << get(Help, "usage"); - return 0; -} - -//: Support for option parsing. -//: Options always begin with '--' and are always the first arguments. An -//: option will never follow a non-option. -char** arg = &argv[1]; -while (argc > 1 && starts_with(*arg, "--")) { - if (false) - ; // no-op branch just so any further additions can consistently always start with 'else' - // End Commandline Options(*arg) - else - cerr << "skipping unknown option " << *arg << '\n'; - --argc; ++argv; ++arg; -} - -if (is_equal(argv[1], "help")) { - if (argc == 2) { - cerr << "help on what?\n"; - help_contents(); - return 0; - } - string key(argv[2]); - // End Help Special-cases(key) - if (contains_key(Help, key)) { - cerr << get(Help, key); - return 0; - } - else { - cerr << "No help found for '" << key << "'\n"; - help_contents(); - cerr << "Please check your command for typos.\n"; - return 1; - } -} - -:(code) -void help_contents() { - cerr << "Available top-level topics:\n"; - cerr << " usage\n"; - // End Help Contents -} - -:(before "End Globals") -map<string, string> Help; -:(before "End Includes") -#include <map> -using std::map; -:(before "End One-time Setup") -init_help(); -:(code) -void init_help() { - put(Help, "usage", - "Welcome to SubX, a better way to program in machine code.\n" - "SubX uses a subset of the x86 instruction set. SubX programs will run\n" - "without modification on Linux computers.\n" - "It provides a better experience and better error messages than\n" - "programming directly in machine code, but you have to stick to the\n" - "instructions it supports.\n" - "\n" - "== Ways to invoke subx\n" - "- Run tests:\n" - " subx test\n" - "- See this message:\n" - " subx --help\n" - "- Convert a textual SubX program into a standard ELF binary that you can\n" - " run on your computer:\n" - " subx translate input1.subx input2.subx ... -o <output ELF binary>\n" - "- Run a SubX binary using SubX itself (for better error messages):\n" - " subx run <ELF binary>\n" - "\n" - "== Debugging aids\n" - "- Add '--trace' to any of these commands to save a trace.\n" - "- Add '--debug' to add information to traces. 'subx --debug translate' will\n" - " save metadata to disk that 'subx --debug --trace run' uses to make traces\n" - " more informative.\n" - "\n" - "Options starting with '--' must always come before any other arguments.\n" - "\n" - "To start learning how to write SubX programs, see Readme.md (particularly\n" - "the section on the x86 instruction set) and then run:\n" - " subx help\n" - ); - // End Help Texts -} - -:(code) -bool is_equal(char* s, const char* lit) { - return strncmp(s, lit, strlen(lit)) == 0; -} - -bool starts_with(const string& s, const string& pat) { - string::const_iterator a=s.begin(), b=pat.begin(); - for (/*nada*/; a!=s.end() && b!=pat.end(); ++a, ++b) - if (*a != *b) return false; - return b == pat.end(); -} - -//: I'll throw some style conventions here for want of a better place for them. -//: As a rule I hate style guides. Do what you want, that's my motto. But since -//: we're dealing with C/C++, the one big thing we want to avoid is undefined -//: behavior. If a compiler ever encounters undefined behavior it can make -//: your program do anything it wants. -//: -//: For reference, my checklist of undefined behaviors to watch out for: -//: out-of-bounds access -//: uninitialized variables -//: use after free -//: dereferencing invalid pointers: null, a new of size 0, others -//: -//: casting a large number to a type too small to hold it -//: -//: integer overflow -//: division by zero and other undefined expressions -//: left-shift by negative count -//: shifting values by more than or equal to the number of bits they contain -//: bitwise operations on signed numbers -//: -//: Converting pointers to types of different alignment requirements -//: T* -> void* -> T*: defined -//: T* -> U* -> T*: defined if non-function pointers and alignment requirements are same -//: function pointers may be cast to other function pointers -//: -//: Casting a numeric value into a value that can't be represented by the target type (either directly or via static_cast) -//: -//: To guard against these, some conventions: -//: -//: 0. Initialize all primitive variables in functions and constructors. -//: -//: 1. Minimize use of pointers and pointer arithmetic. Avoid 'new' and -//: 'delete' as far as possible. Rely on STL to perform memory management to -//: avoid use-after-free issues (and memory leaks). -//: -//: 2. Avoid naked arrays to avoid out-of-bounds access. Never use operator[] -//: except with map. Use at() with STL vectors and so on. -//: -//: 3. Valgrind all the things. -//: -//: 4. Avoid unsigned numbers. Not strictly an undefined-behavior issue, but -//: the extra range doesn't matter, and it's one less confusing category of -//: interaction gotchas to worry about. -//: -//: Corollary: don't use the size() method on containers, since it returns an -//: unsigned and that'll cause warnings about mixing signed and unsigned, -//: yadda-yadda. Instead use this macro below to perform an unsafe cast to -//: signed. We'll just give up immediately if a container's ever too large. -//: Basically, Mu is not concerned about this being a little slower than it -//: could be. (https://gist.github.com/rygorous/e0f055bfb74e3d5f0af20690759de5a7) -//: -//: Addendum to corollary: We're going to uniformly use int everywhere, to -//: indicate that we're oblivious to number size, and since Clang on 32-bit -//: platforms doesn't yet support multiplication over 64-bit integers, and -//: since multiplying two integers seems like a more common situation to end -//: up in than integer overflow. -:(before "End Includes") -#define SIZE(X) (assert((X).size() < (1LL<<(sizeof(int)*8-2))), static_cast<int>((X).size())) - -//: 5. Integer overflow is guarded against at runtime using the -ftrapv flag -//: to the compiler, supported by Clang (GCC version only works sometimes: -//: http://stackoverflow.com/questions/20851061/how-to-make-gcc-ftrapv-work). -:(before "atexit(reset)") -initialize_signal_handlers(); // not always necessary, but doesn't hurt -//? cerr << INT_MAX+1 << '\n'; // test overflow -//? assert(false); // test SIGABRT -:(code) -// based on https://spin.atomicobject.com/2013/01/13/exceptions-stack-traces-c -void initialize_signal_handlers() { - struct sigaction action; - bzero(&action, sizeof(action)); - action.sa_sigaction = dump_and_exit; - sigemptyset(&action.sa_mask); - sigaction(SIGABRT, &action, NULL); // assert() failure or integer overflow on linux (with -ftrapv) - sigaction(SIGILL, &action, NULL); // integer overflow on OS X (with -ftrapv) -} -void dump_and_exit(int sig, siginfo_t* /*unused*/, void* /*unused*/) { - switch (sig) { - case SIGABRT: - #ifndef __APPLE__ - cerr << "SIGABRT: might be an integer overflow if it wasn't an assert() failure\n"; - _Exit(1); - #endif - break; - case SIGILL: - #ifdef __APPLE__ - cerr << "SIGILL: most likely caused by integer overflow\n"; - _Exit(1); - #endif - break; - default: - break; - } -} -:(before "End Includes") -#include <signal.h> - -//: For good measure we'll also enable SIGFPE. -:(before "atexit(reset)") -feenableexcept(FE_OVERFLOW | FE_UNDERFLOW); -//? assert(sizeof(int) == 4 && sizeof(float) == 4); -//? // | exp | mantissa -//? int smallest_subnormal = 0b00000000000000000000000000000001; -//? float smallest_subnormal_f = *reinterpret_cast<float*>(&smallest_subnormal); -//? cerr << "ε: " << smallest_subnormal_f << '\n'; -//? cerr << "ε/2: " << smallest_subnormal_f/2 << " (underflow)\n"; // test SIGFPE -:(before "End Includes") -#include <fenv.h> -:(code) -#ifdef __APPLE__ -// Public domain polyfill for feenableexcept on OS X -// http://www-personal.umich.edu/~williams/archive/computation/fe-handling-example.c -int feenableexcept(unsigned int excepts) { - static fenv_t fenv; - unsigned int new_excepts = excepts & FE_ALL_EXCEPT; - unsigned int old_excepts; - if (fegetenv(&fenv)) return -1; - old_excepts = fenv.__control & FE_ALL_EXCEPT; - fenv.__control &= ~new_excepts; - fenv.__mxcsr &= ~(new_excepts << 7); - return fesetenv(&fenv) ? -1 : old_excepts; -} -#endif - -//: 6. Map's operator[] being non-const is fucking evil. -:(before "Globals") // can't generate prototypes for these -// from http://stackoverflow.com/questions/152643/idiomatic-c-for-reading-from-a-const-map -template<typename T> typename T::mapped_type& get(T& map, typename T::key_type const& key) { - typename T::iterator iter(map.find(key)); - if (iter == map.end()) { - cerr << "get couldn't find key '" << key << "'\n"; - assert(iter != map.end()); - } - return iter->second; -} -template<typename T> typename T::mapped_type const& get(const T& map, typename T::key_type const& key) { - typename T::const_iterator iter(map.find(key)); - if (iter == map.end()) { - cerr << "get couldn't find key '" << key << "'\n"; - assert(iter != map.end()); - } - return iter->second; -} -template<typename T> typename T::mapped_type const& put(T& map, typename T::key_type const& key, typename T::mapped_type const& value) { - map[key] = value; - return map[key]; -} -template<typename T> bool contains_key(T& map, typename T::key_type const& key) { - return map.find(key) != map.end(); -} -template<typename T> typename T::mapped_type& get_or_insert(T& map, typename T::key_type const& key) { - return map[key]; -} -template<typename T> typename T::mapped_type const& put_new(T& map, typename T::key_type const& key, typename T::mapped_type const& value) { - assert(map.find(key) == map.end()); - map[key] = value; - return map[key]; -} -//: The contract: any container that relies on get_or_insert should never call -//: contains_key. - -//: 7. istreams are a royal pain in the arse. You have to be careful about -//: what subclass you try to putback into. You have to watch out for the pesky -//: failbit and badbit. Just avoid eof() and use this helper instead. -:(code) -bool has_data(istream& in) { - return in && !in.eof(); -} - -:(before "End Includes") -#include <assert.h> - -#include <iostream> -using std::istream; -using std::ostream; -using std::iostream; -using std::cin; -using std::cout; -using std::cerr; -#include <iomanip> - -#include <string.h> -#include <string> -using std::string; - -#include <algorithm> -using std::min; -using std::max; diff --git a/subx/002test.cc b/subx/002test.cc deleted file mode 100644 index f25e331f..00000000 --- a/subx/002test.cc +++ /dev/null @@ -1,111 +0,0 @@ -//: A simple test harness. To create new tests, define functions starting with -//: 'test_'. To run all tests so defined, run: -//: $ ./mu test -//: -//: Every layer should include tests, and can reach into previous layers. -//: However, it seems like a good idea never to reach into tests from previous -//: layers. Every test should be a contract that always passes as originally -//: written, regardless of any later layers. Avoid writing 'temporary' tests -//: that are only meant to work until some layer. - -:(before "End Types") -typedef void (*test_fn)(void); -:(before "Globals") -// move a global ahead into types that we can't generate an extern declaration for -const test_fn Tests[] = { - #include "test_list" // auto-generated; see 'build*' scripts -}; - -:(before "End Globals") -bool Run_tests = false; -bool Passed = true; // set this to false inside any test to indicate failure - -:(before "End Includes") -#define CHECK(X) \ - if (Passed && !(X)) { \ - cerr << "\nF - " << __FUNCTION__ << "(" << __FILE__ << ":" << __LINE__ << "): " << #X << '\n'; \ - Passed = false; \ - return; /* Currently we stop at the very first failure. */ \ - } - -#define CHECK_EQ(X, Y) \ - if (Passed && (X) != (Y)) { \ - cerr << "\nF - " << __FUNCTION__ << "(" << __FILE__ << ":" << __LINE__ << "): " << #X << " == " << #Y << '\n'; \ - cerr << " got " << (X) << '\n'; /* BEWARE: multiple eval */ \ - Passed = false; \ - return; /* Currently we stop at the very first failure. */ \ - } - -:(before "End Reset") -Passed = true; - -:(before "End Commandline Parsing") -if (argc > 1 && is_equal(argv[1], "test")) { - Run_tests = true; --argc; ++argv; // shift 'test' out of commandline args -} - -:(before "End Main") -if (Run_tests) { - // Test Runs - // we run some tests and then exit; assume no state need be maintained afterward - - long num_failures = 0; - // End Test Run Initialization - time_t t; time(&t); - cerr << "C tests: " << ctime(&t); - for (size_t i=0; i < sizeof(Tests)/sizeof(Tests[0]); ++i) { -//? cerr << "running " << Test_names[i] << '\n'; - run_test(i); - if (Passed) cerr << '.'; - else ++num_failures; - } - cerr << '\n'; - // End Tests - if (num_failures > 0) { - cerr << num_failures << " failure" - << (num_failures > 1 ? "s" : "") - << '\n'; - return 1; - } - return 0; -} - -:(after "End Main") -//: Raise other unrecognized sub-commands as errors. -//: We couldn't do this until now because we want `./subx test` to always -//: succeed, no matter how many layers are included in the build. -cerr << "nothing to do\n"; -return 1; - -:(code) -void run_test(size_t i) { - if (i >= sizeof(Tests)/sizeof(Tests[0])) { - cerr << "no test " << i << '\n'; - return; - } - reset(); - // End Test Setup - (*Tests[i])(); - // End Test Teardown -} - -//: Convenience: run a single test -:(before "Globals") -// Names for each element of the 'Tests' global, respectively. -const string Test_names[] = { - #include "test_name_list" // auto-generated; see 'build*' scripts -}; -:(after "Test Runs") -string maybe_single_test_to_run = argv[argc-1]; -if (!starts_with(maybe_single_test_to_run, "test_")) - maybe_single_test_to_run.insert(0, "test_"); -for (size_t i=0; i < sizeof(Tests)/sizeof(Tests[0]); ++i) { - if (Test_names[i] == maybe_single_test_to_run) { - run_test(i); - if (Passed) cerr << ".\n"; - return 0; - } -} - -:(before "End Includes") -#include <stdlib.h> diff --git a/subx/003trace.cc b/subx/003trace.cc deleted file mode 100644 index 379077a9..00000000 --- a/subx/003trace.cc +++ /dev/null @@ -1,520 +0,0 @@ -//: The goal of layers is to make programs more easy to understand and more -//: malleable, easy to rewrite in radical ways without accidentally breaking -//: some corner case. Tests further both goals. They help understandability by -//: letting one make small changes and get feedback. What if I wrote this line -//: like so? What if I removed this function call, is it really necessary? -//: Just try it, see if the tests pass. Want to explore rewriting this bit in -//: this way? Tests put many refactorings on a firmer footing. -//: -//: But the usual way we write tests seems incomplete. Refactorings tend to -//: work in the small, but don't help with changes to function boundaries. If -//: you want to extract a new function you have to manually test-drive it to -//: create tests for it. If you want to inline a function its tests are no -//: longer valid. In both cases you end up having to reorganize code as well as -//: tests, an error-prone activity. -//: -//: In response, this layer introduces the notion of domain-driven *white-box* -//: testing. We focus on the domain of inputs the whole program needs to -//: handle rather than the correctness of individual functions. All white-box -//: tests invoke the program in a single way: by calling run() with some -//: input. As the program operates on the input, it traces out a list of -//: _facts_ deduced about the domain: -//: trace("label") << "fact 1: " << val; -//: -//: Tests can now check for these facts in the trace: -//: CHECK_TRACE_CONTENTS("label", "fact 1: 34\n" -//: "fact 2: 35\n"); -//: -//: Since we never call anything but the run() function directly, we never have -//: to rewrite the tests when we reorganize the internals of the program. We -//: just have to make sure our rewrite deduces the same facts about the domain, -//: and that's something we're going to have to do anyway. -//: -//: To avoid the combinatorial explosion of integration tests, each layer -//: mainly logs facts to the trace with a common *label*. All tests in a layer -//: tend to check facts with this label. Validating the facts logged with a -//: specific label is like calling functions of that layer directly. -//: -//: To build robust tests, trace facts about your domain rather than details of -//: how you computed them. -//: -//: More details: http://akkartik.name/blog/tracing-tests -//: -//: --- -//: -//: Between layers and domain-driven testing, programming starts to look like a -//: fundamentally different activity. Instead of focusing on a) superficial, -//: b) local rules on c) code [like say http://blog.bbv.ch/2013/06/05/clean-code-cheat-sheet], -//: we allow programmers to engage with the a) deep, b) global structure of -//: the c) domain. If you can systematically track discontinuities in the -//: domain, you don't care if the code used gotos as long as it passed all -//: tests. If tests become more robust to run, it becomes easier to try out -//: radically different implementations for the same program. If code is -//: super-easy to rewrite, it becomes less important what indentation style it -//: uses, or that the objects are appropriately encapsulated, or that the -//: functions are referentially transparent. -//: -//: Instead of plumbing, programming becomes building and gradually refining a -//: map of the environment the program must operate under. Whether a program -//: is 'correct' at a given point in time is a red herring; what matters is -//: avoiding regression by monotonically nailing down the more 'eventful' -//: parts of the terrain. It helps readers new and old, and rewards curiosity, -//: to organize large programs in self-similar hierarchies of example tests -//: colocated with the code that makes them work. -//: -//: "Programming properly should be regarded as an activity by which -//: programmers form a mental model, rather than as production of a program." -//: -- Peter Naur (http://alistair.cockburn.us/ASD+book+extract%3A+%22Naur,+Ehn,+Musashi%22) - -//:: == Core data structures - -:(before "End Globals") -trace_stream* Trace_stream = NULL; - -:(before "End Types") -struct trace_stream { - vector<trace_line> past_lines; - // End trace_stream Fields - - trace_stream() { - // End trace_stream Constructor - } - ~trace_stream() { - // End trace_stream Destructor - } - // End trace_stream Methods -}; - -//:: == Adding to the trace - -//: Top-level method is trace() which can be used like an ostream. Usage: -//: trace(depth, label) << ... << end(); -//: Don't forget the 'end()' to actually append to the trace. -:(before "End Includes") -// No brackets around the expansion so that it prints nothing if Trace_stream -// isn't initialized. -#define trace(...) !Trace_stream ? cerr : Trace_stream->stream(__VA_ARGS__) - -:(before "End trace_stream Fields") -// accumulator for current trace_line -ostringstream* curr_stream; -string curr_label; -int curr_depth; -// other stuff -int collect_depth; // avoid tracing lower levels for speed -ofstream null_stream; // never opened, so writes to it silently fail - -//: Some constants. -:(before "struct trace_stream") // include constants in all cleaved compilation units -const int Max_depth = 9999; -:(before "End trace_stream Constructor") -curr_stream = NULL; -curr_depth = Max_depth; -collect_depth = Max_depth; - -:(before "struct trace_stream") -struct trace_line { - string contents; - string label; - int depth; // 0 is 'sea level'; positive integers are progressively 'deeper' and lower level - trace_line(string c, string l) { - contents = c; - label = l; - depth = 0; - } - trace_line(string c, string l, int d) { - contents = c; - label = l; - depth = d; - } -}; - -string unescape_newline(string& s) { - std::stringstream ss; - for (int i = 0; i < SIZE(s); ++i) { - if (s.at(i) == '\n') - ss << "\\n"; - else - ss << s.at(i); - } - return ss.str(); -} - -void dump_trace_line(ostream& s, trace_line& t) { - s << std::setw(4) << t.depth << ' ' << t.label << ": " << unescape_newline(t.contents) << '\n'; -} - -//: Starting a new trace line. -:(before "End trace_stream Methods") -ostream& stream(string label) { - return stream(Max_depth, label); -} - -ostream& stream(int depth, string label) { - if (depth > collect_depth) return null_stream; - curr_stream = new ostringstream; - curr_label = label; - curr_depth = depth; - (*curr_stream) << std::hex; // printing addresses is the common case - return *curr_stream; -} - -//: End of a trace line; append it to the trace. -:(before "End Types") -struct end {}; -:(code) -ostream& operator<<(ostream& os, end /*unused*/) { - if (Trace_stream) Trace_stream->newline(); - return os; -} - -//: Fatal error. -:(before "End Types") -struct die {}; -:(code) -ostream& operator<<(ostream& /*unused*/, die /*unused*/) { - if (Trace_stream) Trace_stream->newline(); - exit(1); -} - -:(before "End trace_stream Methods") -void newline(); -:(code) -void trace_stream::newline() { - if (!curr_stream) return; - string curr_contents = curr_stream->str(); - if (!curr_contents.empty()) { - past_lines.push_back(trace_line(curr_contents, trim(curr_label), curr_depth)); // preserve indent in contents - // maybe incrementally dump trace - trace_line& t = past_lines.back(); - if (should_incrementally_print_trace()) { - dump_trace_line(cerr, t); - } - // End trace Commit - } - - // clean up - delete curr_stream; - curr_stream = NULL; - curr_label.clear(); - curr_depth = Max_depth; -} - -//:: == Initializing the trace in tests - -:(before "End Includes") -#define START_TRACING_UNTIL_END_OF_SCOPE lease_tracer leased_tracer; -:(before "End Test Setup") -START_TRACING_UNTIL_END_OF_SCOPE - -//: Trace_stream is a resource, lease_tracer uses RAII to manage it. -:(before "End Types") -struct lease_tracer { - lease_tracer(); - ~lease_tracer(); -}; -:(code) -lease_tracer::lease_tracer() { Trace_stream = new trace_stream; } -lease_tracer::~lease_tracer() { - delete Trace_stream; - Trace_stream = NULL; -} - -//:: == Errors and warnings using traces - -:(before "End Includes") -#define raise (!Trace_stream ? (++Trace_errors,cerr) /*do print*/ : Trace_stream->stream(Error_depth, "error")) -#define warn (!Trace_stream ? (++Trace_errors,cerr) /*do print*/ : Trace_stream->stream(Warn_depth, "warn")) - -//: Print errors and warnings to the screen by default. -:(before "struct trace_stream") // include constants in all cleaved compilation units -const int Error_depth = 0; -const int Warn_depth = 1; -:(before "End Globals") -int Hide_errors = false; // if set, don't print errors or warnings to screen -int Hide_warnings = false; // if set, don't print warnings to screen -:(before "End Reset") -Hide_errors = false; -Hide_warnings = false; -//: Never dump warnings in tests -:(before "End Test Setup") -Hide_warnings = true; -:(code) -bool trace_stream::should_incrementally_print_trace() { - if (!Hide_errors && curr_depth == Error_depth) return true; - if (!Hide_warnings && !Hide_errors && curr_depth == Warn_depth) return true; - // End Incremental Trace Print Conditions - return false; -} -:(before "End trace_stream Methods") -bool should_incrementally_print_trace(); - -:(before "End Globals") -int Trace_errors = 0; // used only when Trace_stream is NULL - -// Fail tests that displayed (unexpected) errors. -// Expected errors should always be hidden and silently checked for. -:(before "End Test Teardown") -if (Passed && !Hide_errors && trace_contains_errors()) { - Passed = false; -} -:(code) -bool trace_contains_errors() { - return Trace_errors > 0 || trace_count("error") > 0; -} - -:(before "End Includes") -// If we aren't yet sure how to deal with some corner case, use assert_for_now -// to indicate that it isn't an inviolable invariant. -#define assert_for_now assert -#define raise_for_now raise - -//:: == Other assertions on traces -//: Primitives: -//: - CHECK_TRACE_CONTENTS(lines) -//: Assert that the trace contains the given lines (separated by newlines) -//: in order. There can be other intervening lines between them. -//: - CHECK_TRACE_DOESNT_CONTAIN(line) -//: - CHECK_TRACE_DOESNT_CONTAIN(label, contents) -//: Assert that the trace doesn't contain the given (single) line. -//: - CHECK_TRACE_COUNT(label, count) -//: Assert that the trace contains exactly 'count' lines with the given -//: 'label'. -//: - CHECK_TRACE_CONTAINS_ERRORS() -//: - CHECK_TRACE_DOESNT_CONTAIN_ERRORS() -//: - trace_count_prefix(label, prefix) -//: Count the number of trace lines with the given 'label' that start with -//: the given 'prefix'. - -:(before "End Includes") -#define CHECK_TRACE_CONTENTS(...) check_trace_contents(__FUNCTION__, __FILE__, __LINE__, __VA_ARGS__) - -#define CHECK_TRACE_DOESNT_CONTAIN(...) CHECK(trace_doesnt_contain(__VA_ARGS__)) - -#define CHECK_TRACE_COUNT(label, count) \ - if (Passed && trace_count(label) != (count)) { \ - cerr << "\nF - " << __FUNCTION__ << "(" << __FILE__ << ":" << __LINE__ << "): trace_count of " << label << " should be " << count << '\n'; \ - cerr << " got " << trace_count(label) << '\n'; /* multiple eval */ \ - DUMP(label); \ - Passed = false; \ - return; /* Currently we stop at the very first failure. */ \ - } - -#define CHECK_TRACE_CONTAINS_ERRORS() CHECK(trace_contains_errors()) -#define CHECK_TRACE_DOESNT_CONTAIN_ERRORS() \ - if (Passed && trace_contains_errors()) { \ - cerr << "\nF - " << __FUNCTION__ << "(" << __FILE__ << ":" << __LINE__ << "): unexpected errors\n"; \ - DUMP("error"); \ - Passed = false; \ - return; \ - } - -// Allow tests to ignore trace lines generated during setup. -#define CLEAR_TRACE delete Trace_stream, Trace_stream = new trace_stream - -:(code) -bool check_trace_contents(string FUNCTION, string FILE, int LINE, string expected) { - if (!Passed) return false; - if (!Trace_stream) return false; - vector<string> expected_lines = split(expected, "\n"); - int curr_expected_line = 0; - while (curr_expected_line < SIZE(expected_lines) && expected_lines.at(curr_expected_line).empty()) - ++curr_expected_line; - if (curr_expected_line == SIZE(expected_lines)) return true; - string label, contents; - split_label_contents(expected_lines.at(curr_expected_line), &label, &contents); - for (vector<trace_line>::iterator p = Trace_stream->past_lines.begin(); p != Trace_stream->past_lines.end(); ++p) { - if (label != p->label) continue; - string t = trim(p->contents); - if (contents != unescape_newline(t)) continue; - ++curr_expected_line; - while (curr_expected_line < SIZE(expected_lines) && expected_lines.at(curr_expected_line).empty()) - ++curr_expected_line; - if (curr_expected_line == SIZE(expected_lines)) return true; - split_label_contents(expected_lines.at(curr_expected_line), &label, &contents); - } - - if (line_exists_anywhere(label, contents)) { - cerr << "\nF - " << FUNCTION << "(" << FILE << ":" << LINE << "): line [" << label << ": " << contents << "] out of order in trace:\n"; - DUMP(""); - } - else { - cerr << "\nF - " << FUNCTION << "(" << FILE << ":" << LINE << "): missing [" << contents << "] in trace:\n"; - DUMP(label); - } - Passed = false; - return false; -} - -bool trace_doesnt_contain(string expected) { - vector<string> tmp = split_first(expected, ": "); - if (SIZE(tmp) == 1) { - raise << expected << ": missing label or contents in trace line\n" << end(); - assert(false); - } - return trace_count(tmp.at(0), tmp.at(1)) == 0; -} - -int trace_count(string label) { - return trace_count(label, ""); -} - -int trace_count(string label, string line) { - if (!Trace_stream) return 0; - long result = 0; - for (vector<trace_line>::iterator p = Trace_stream->past_lines.begin(); p != Trace_stream->past_lines.end(); ++p) { - if (label == p->label) { - if (line == "" || trim(line) == trim(p->contents)) - ++result; - } - } - return result; -} - -int trace_count_prefix(string label, string prefix) { - if (!Trace_stream) return 0; - long result = 0; - for (vector<trace_line>::iterator p = Trace_stream->past_lines.begin(); p != Trace_stream->past_lines.end(); ++p) { - if (label == p->label) { - if (starts_with(trim(p->contents), trim(prefix))) - ++result; - } - } - return result; -} - -void split_label_contents(const string& s, string* label, string* contents) { - static const string delim(": "); - size_t pos = s.find(delim); - if (pos == string::npos) { - *label = ""; - *contents = trim(s); - } - else { - *label = trim(s.substr(0, pos)); - *contents = trim(s.substr(pos+SIZE(delim))); - } -} - -bool line_exists_anywhere(const string& label, const string& contents) { - for (vector<trace_line>::iterator p = Trace_stream->past_lines.begin(); p != Trace_stream->past_lines.end(); ++p) { - if (label != p->label) continue; - if (contents == trim(p->contents)) return true; - } - return false; -} - -vector<string> split(string s, string delim) { - vector<string> result; - size_t begin=0, end=s.find(delim); - while (true) { - if (end == string::npos) { - result.push_back(string(s, begin, string::npos)); - break; - } - result.push_back(string(s, begin, end-begin)); - begin = end+SIZE(delim); - end = s.find(delim, begin); - } - return result; -} - -vector<string> split_first(string s, string delim) { - vector<string> result; - size_t end=s.find(delim); - result.push_back(string(s, 0, end)); - if (end != string::npos) - result.push_back(string(s, end+SIZE(delim), string::npos)); - return result; -} - -//:: == Helpers for debugging using traces - -:(before "End Includes") -// To debug why a test is failing, dump its trace using '?'. -#define DUMP(label) if (Trace_stream) cerr << Trace_stream->readable_contents(label); - -// To add temporary prints to the trace, use 'dbg'. -// `git log` should never show any calls to 'dbg'. -#define dbg trace(0, "a") - -//: Dump the entire trace to file where it can be browsed offline. -//: Dump the trace as it happens; that way you get something even if the -//: program crashes. - -:(before "End Globals") -ofstream Trace_file; -:(before "End Commandline Options(*arg)") -else if (is_equal(*arg, "--trace")) { - cerr << "saving trace to 'last_run'\n"; - Trace_file.open("last_run"); - // Add a dummy line up top; otherwise the `browse_trace` tool currently has - // no way to expand any lines above an error. - Trace_file << " 0 dummy: start\n"; -} -:(before "End trace Commit") -if (Trace_file) { - dump_trace_line(Trace_file, t); -} -:(before "End One-time Setup") -atexit(cleanup_main); -:(code) -void cleanup_main() { - if (Trace_file) Trace_file.close(); - // End cleanup_main -} - -:(before "End trace_stream Methods") -string readable_contents(string label) { - string trim(const string& s); // prototype - ostringstream output; - label = trim(label); - for (vector<trace_line>::iterator p = past_lines.begin(); p != past_lines.end(); ++p) - if (label.empty() || label == p->label) - dump_trace_line(output, *p); - return output.str(); -} - -//: Print traces to the screen as they happen. -//: Particularly useful when juggling multiple trace streams, like when -//: debugging sandboxes. -:(before "End Globals") -bool Dump_trace = false; -:(before "End Commandline Options(*arg)") -else if (is_equal(*arg, "--dump")) { - Dump_trace = true; -} -:(before "End Incremental Trace Print Conditions") -if (Dump_trace) return true; - -//: Miscellaneous helpers. - -:(code) -string trim(const string& s) { - string::const_iterator first = s.begin(); - while (first != s.end() && isspace(*first)) - ++first; - if (first == s.end()) return ""; - - string::const_iterator last = --s.end(); - while (last != s.begin() && isspace(*last)) - --last; - ++last; - return string(first, last); -} - -:(before "End Includes") -#include <vector> -using std::vector; -#include <list> -using std::list; -#include <set> -using std::set; - -#include <sstream> -using std::istringstream; -using std::ostringstream; - -#include <fstream> -using std::ifstream; -using std::ofstream; diff --git a/subx/003trace.test.cc b/subx/003trace.test.cc deleted file mode 100644 index bec1b789..00000000 --- a/subx/003trace.test.cc +++ /dev/null @@ -1,133 +0,0 @@ -void test_trace_check_compares() { - trace("test layer") << "foo" << end(); - CHECK_TRACE_CONTENTS("test layer: foo"); -} - -void test_trace_check_ignores_other_layers() { - trace("test layer 1") << "foo" << end(); - trace("test layer 2") << "bar" << end(); - CHECK_TRACE_CONTENTS("test layer 1: foo"); - CHECK_TRACE_DOESNT_CONTAIN("test layer 2: foo"); -} - -void test_trace_check_ignores_leading_whitespace() { - trace("test layer 1") << " foo" << end(); - CHECK_EQ(trace_count("test layer 1", /*too little whitespace*/"foo"), 1); - CHECK_EQ(trace_count("test layer 1", /*too much whitespace*/" foo"), 1); -} - -void test_trace_check_ignores_other_lines() { - trace("test layer 1") << "foo" << end(); - trace("test layer 1") << "bar" << end(); - CHECK_TRACE_CONTENTS("test layer 1: foo"); -} - -void test_trace_check_ignores_other_lines2() { - trace("test layer 1") << "foo" << end(); - trace("test layer 1") << "bar" << end(); - CHECK_TRACE_CONTENTS("test layer 1: bar"); -} - -void test_trace_ignores_trailing_whitespace() { - trace("test layer 1") << "foo\n" << end(); - CHECK_TRACE_CONTENTS("test layer 1: foo"); -} - -void test_trace_ignores_trailing_whitespace2() { - trace("test layer 1") << "foo " << end(); - CHECK_TRACE_CONTENTS("test layer 1: foo"); -} - -void test_trace_orders_across_layers() { - trace("test layer 1") << "foo" << end(); - trace("test layer 2") << "bar" << end(); - trace("test layer 1") << "qux" << end(); - CHECK_TRACE_CONTENTS("test layer 1: foo\n" - "test layer 2: bar\n" - "test layer 1: qux\n"); -} - -void test_trace_supports_count() { - trace("test layer 1") << "foo" << end(); - trace("test layer 1") << "foo" << end(); - CHECK_EQ(trace_count("test layer 1", "foo"), 2); -} - -void test_trace_supports_count2() { - trace("test layer 1") << "foo" << end(); - trace("test layer 1") << "bar" << end(); - CHECK_EQ(trace_count("test layer 1"), 2); -} - -void test_trace_count_ignores_trailing_whitespace() { - trace("test layer 1") << "foo\n" << end(); - CHECK_EQ(trace_count("test layer 1", "foo"), 1); -} - -void test_trace_unescapes_newlines() { - trace("test layer 1") << "f\no\no\n" << end(); - CHECK_TRACE_CONTENTS("test layer 1: f\\no\\no"); -} - -// pending: DUMP tests -// pending: readable_contents() adds newline if necessary. -// pending: raise also prints to stderr. -// pending: raise doesn't print to stderr if Hide_errors is set. -// pending: warn doesn't print to stderr if Hide_errors is set. -// pending: warn doesn't print to stderr if Hide_warnings is set. -// pending: raise doesn't have to be saved if Hide_errors is set, just printed. -// pending: raise prints to stderr if Trace_stream is NULL. -// pending: raise prints to stderr if Trace_stream is NULL even if Hide_errors is set. - -// can't check trace because trace methods call 'split' - -void test_split_returns_at_least_one_elem() { - vector<string> result = split("", ","); - CHECK_EQ(result.size(), 1); - CHECK_EQ(result.at(0), ""); -} - -void test_split_returns_entire_input_when_no_delim() { - vector<string> result = split("abc", ","); - CHECK_EQ(result.size(), 1); - CHECK_EQ(result.at(0), "abc"); -} - -void test_split_works() { - vector<string> result = split("abc,def", ","); - CHECK_EQ(result.size(), 2); - CHECK_EQ(result.at(0), "abc"); - CHECK_EQ(result.at(1), "def"); -} - -void test_split_works2() { - vector<string> result = split("abc,def,ghi", ","); - CHECK_EQ(result.size(), 3); - CHECK_EQ(result.at(0), "abc"); - CHECK_EQ(result.at(1), "def"); - CHECK_EQ(result.at(2), "ghi"); -} - -void test_split_handles_multichar_delim() { - vector<string> result = split("abc,,def,,ghi", ",,"); - CHECK_EQ(result.size(), 3); - CHECK_EQ(result.at(0), "abc"); - CHECK_EQ(result.at(1), "def"); - CHECK_EQ(result.at(2), "ghi"); -} - -void test_trim() { - CHECK_EQ(trim(""), ""); - CHECK_EQ(trim(" "), ""); - CHECK_EQ(trim(" "), ""); - CHECK_EQ(trim("a"), "a"); - CHECK_EQ(trim(" a"), "a"); - CHECK_EQ(trim(" a"), "a"); - CHECK_EQ(trim(" ab"), "ab"); - CHECK_EQ(trim("a "), "a"); - CHECK_EQ(trim("a "), "a"); - CHECK_EQ(trim("ab "), "ab"); - CHECK_EQ(trim(" a "), "a"); - CHECK_EQ(trim(" a "), "a"); - CHECK_EQ(trim(" ab "), "ab"); -} diff --git a/subx/010---vm.cc b/subx/010---vm.cc deleted file mode 100644 index 3d60ab5c..00000000 --- a/subx/010---vm.cc +++ /dev/null @@ -1,401 +0,0 @@ -//: Core data structures for simulating the SubX VM (subset of an x86 processor) -//: -//: At the lowest level ("level 1") of abstraction, SubX executes x86 -//: instructions provided in the form of an array of bytes, loaded into memory -//: starting at a specific address. - -//:: registers -//: assume segment registers are hard-coded to 0 -//: no floating-point, MMX, etc. yet - -:(before "End Types") -enum { - EAX, - ECX, - EDX, - EBX, - ESP, - EBP, - ESI, - EDI, - NUM_INT_REGISTERS, -}; -union reg { - int32_t i; - uint32_t u; -}; -:(before "End Globals") -reg Reg[NUM_INT_REGISTERS] = { {0} }; -uint32_t EIP = 1; // preserve null pointer -:(before "End Reset") -bzero(Reg, sizeof(Reg)); -EIP = 1; // preserve null pointer - -:(before "End Help Contents") -cerr << " registers\n"; -:(before "End Help Texts") -put_new(Help, "registers", - "SubX currently supports eight 32-bit integer registers. From 0 to 7, they are:\n" - " EAX ECX EDX EBX ESP EBP ESI EDI\n" - "ESP contains the top of the stack.\n" - "\n" - "-- 8-bit registers\n" - "Some instructions operate on eight *overlapping* 8-bit registers.\n" - "From 0 to 7, they are:\n" - " AL CL DL BL AH CH DH BH\n" - "The 8-bit registers overlap with the 32-bit ones. AL is the lowest signicant byte\n" - "of EAX, AH is the second lowest significant byte, and so on.\n" - "\n" - "For example, if EBX contains 0x11223344, then BL contains 0x44, and BH contains 0x33.\n" - "\n" - "There is no way to access bytes within ESP, EBP, ESI or EDI.\n" - "\n" - "For complete details consult the IA-32 software developer's manual, volume 2,\n" - "table 2-2, \"32-bit addressing forms with the ModR/M byte\".\n" - "It is included in this repository as 'modrm.pdf'.\n" - "The register encodings are described in the top row of the table, but you'll need\n" - "to spend some time with it.\n" - "\n" - "-- flag registers\n" - "Various instructions (particularly 'compare') modify one or more of four 1-bit\n" - "'flag' registers, as a side-effect:\n" - "- the sign flag (SF): usually set if an arithmetic result is negative, or\n" - " reset if not.\n" - "- the zero flag (ZF): usually set if a result is zero, or reset if not.\n" - "- the carry flag (CF): usually set if an arithmetic result overflows by just one bit.\n" - " Useful for operating on unsigned numbers.\n" - "- the overflow flag (OF): usually set if an arithmetic result overflows by more\n" - " than one bit. Useful for operating on signed numbers.\n" - "The flag bits are read by conditional jumps.\n" - "\n" - "For complete details on how different instructions update the flags, consult the IA-32\n" - "manual (volume 2). There's various versions of it online, such as https://c9x.me/x86,\n" - "though of course you'll need to be careful to ignore instructions and flag registers\n" - "that SubX doesn't support.\n" - "\n" - "It isn't simple, but if this is the processor you have running on your computer.\n" - "Might as well get good at it.\n" -); - -:(before "End Globals") -// the subset of x86 flag registers we care about -bool SF = false; // sign flag -bool ZF = false; // zero flag -bool CF = false; // carry flag -bool OF = false; // overflow flag -:(before "End Reset") -SF = ZF = CF = OF = false; - -//:: simulated RAM - -:(before "End Types") -const uint32_t SEGMENT_ALIGNMENT = 0x1000000; // 16MB -inline uint32_t align_upwards(uint32_t x, uint32_t align) { - return (x+align-1) & -(align); -} - -// Like in real-world Linux, we'll allocate RAM for our programs in disjoint -// slabs called VMAs or Virtual Memory Areas. -struct vma { - uint32_t start; // inclusive - uint32_t end; // exclusive - vector<uint8_t> _data; - vma(uint32_t s, uint32_t e) :start(s), end(e) {} - vma(uint32_t s) :start(s), end(align_upwards(s+1, SEGMENT_ALIGNMENT)) {} - bool match(uint32_t a) { - return a >= start && a < end; - } - bool match32(uint32_t a) { - return a >= start && a+4 <= end; - } - uint8_t& data(uint32_t a) { - assert(match(a)); - uint32_t result_index = a-start; - if (_data.size() <= result_index) { - const int align = 0x1000; - uint32_t result_size = result_index + 1; // size needed for result_index to be valid - uint32_t new_size = align_upwards(result_size, align); - // grow at least 2x to maintain some amortized complexity guarantees - if (new_size < _data.size() * 2) - new_size = _data.size() * 2; - // never grow past the stated limit - if (new_size > end-start) - new_size = end-start; - _data.resize(new_size); - } - return _data.at(result_index); - } - void grow_until(uint32_t new_end_address) { - if (new_end_address < end) return; - // Ugly: vma knows about the global Memory list of vmas - void sanity_check(uint32_t start, uint32_t end); - sanity_check(start, new_end_address); - end = new_end_address; - } - // End vma Methods -}; -:(code) -void sanity_check(uint32_t start, uint32_t end) { - bool dup_found = false; - for (int i = 0; i < SIZE(Mem); ++i) { - const vma& curr = Mem.at(i); - if (curr.start == start) { - assert(!dup_found); - dup_found = true; - } - else if (curr.start > start) { - assert(curr.start > end); - } - else if (curr.start < start) { - assert(curr.end < start); - } - } -} - -:(before "End Globals") -// RAM is made of VMAs. -vector<vma> Mem; -:(code) -:(before "End Globals") -uint32_t End_of_program = 0; // when the program executes past this address in tests we'll stop the test -// The stack grows downward. Can't increase its size for now. -:(before "End Reset") -Mem.clear(); -End_of_program = 0; -:(code) -// These helpers depend on Mem being laid out contiguously (so you can't use a -// map, etc.) and on the host also being little-endian. -inline uint8_t read_mem_u8(uint32_t addr) { - uint8_t* handle = mem_addr_u8(addr); // error messages get printed here - return handle ? *handle : 0; -} -inline int8_t read_mem_i8(uint32_t addr) { - return static_cast<int8_t>(read_mem_u8(addr)); -} -inline uint32_t read_mem_u32(uint32_t addr) { - uint32_t* handle = mem_addr_u32(addr); // error messages get printed here - return handle ? *handle : 0; -} -inline int32_t read_mem_i32(uint32_t addr) { - return static_cast<int32_t>(read_mem_u32(addr)); -} - -inline uint8_t* mem_addr_u8(uint32_t addr) { - uint8_t* result = NULL; - for (int i = 0; i < SIZE(Mem); ++i) { - if (Mem.at(i).match(addr)) { - if (result) - raise << "address 0x" << HEXWORD << addr << " is in two segments\n" << end(); - result = &Mem.at(i).data(addr); - } - } - if (result == NULL) { - if (Trace_file) Trace_file.flush(); - raise << "Tried to access uninitialized memory at address 0x" << HEXWORD << addr << '\n' << end(); - exit(1); - } - return result; -} -inline int8_t* mem_addr_i8(uint32_t addr) { - return reinterpret_cast<int8_t*>(mem_addr_u8(addr)); -} -inline uint32_t* mem_addr_u32(uint32_t addr) { - uint32_t* result = NULL; - for (int i = 0; i < SIZE(Mem); ++i) { - if (Mem.at(i).match32(addr)) { - if (result) - raise << "address 0x" << HEXWORD << addr << " is in two segments\n" << end(); - result = reinterpret_cast<uint32_t*>(&Mem.at(i).data(addr)); - } - } - if (result == NULL) { - if (Trace_file) Trace_file.flush(); - raise << "Tried to access uninitialized memory at address 0x" << HEXWORD << addr << '\n' << end(); - raise << "The entire 4-byte word should be initialized and lie in a single segment.\n" << end(); - exit(1); - } - return result; -} -inline int32_t* mem_addr_i32(uint32_t addr) { - return reinterpret_cast<int32_t*>(mem_addr_u32(addr)); -} -// helper for some syscalls. But read-only. -inline const char* mem_addr_kernel_string(uint32_t addr) { - return reinterpret_cast<const char*>(mem_addr_u8(addr)); -} -inline string mem_addr_string(uint32_t addr, uint32_t size) { - ostringstream out; - for (size_t i = 0; i < size; ++i) - out << read_mem_u8(addr+i); - return out.str(); -} - - -inline void write_mem_u8(uint32_t addr, uint8_t val) { - uint8_t* handle = mem_addr_u8(addr); - if (handle != NULL) *handle = val; -} -inline void write_mem_i8(uint32_t addr, int8_t val) { - int8_t* handle = mem_addr_i8(addr); - if (handle != NULL) *handle = val; -} -inline void write_mem_u32(uint32_t addr, uint32_t val) { - uint32_t* handle = mem_addr_u32(addr); - if (handle != NULL) *handle = val; -} -inline void write_mem_i32(uint32_t addr, int32_t val) { - int32_t* handle = mem_addr_i32(addr); - if (handle != NULL) *handle = val; -} - -inline bool already_allocated(uint32_t addr) { - bool result = false; - for (int i = 0; i < SIZE(Mem); ++i) { - if (Mem.at(i).match(addr)) { - if (result) - raise << "address 0x" << HEXWORD << addr << " is in two segments\n" << end(); - result = true; - } - } - return result; -} - -//:: core interpreter loop - -:(code) -// skeleton of how x86 instructions are decoded -void run_one_instruction() { - uint8_t op=0, op2=0, op3=0; - // Run One Instruction - if (Trace_file) { - dump_registers(); - // End Dump Info for Instruction - } - uint32_t inst_start_address = EIP; - op = next(); - trace(Callstack_depth+1, "run") << "0x" << HEXWORD << inst_start_address << " opcode: " << HEXBYTE << NUM(op) << end(); - switch (op) { - case 0xf4: // hlt - EIP = End_of_program; - break; - // End Single-Byte Opcodes - case 0x0f: - switch(op2 = next()) { - // End Two-Byte Opcodes Starting With 0f - default: - cerr << "unrecognized second opcode after 0f: " << HEXBYTE << NUM(op2) << '\n'; - exit(1); - } - break; - case 0xf2: - switch(op2 = next()) { - // End Two-Byte Opcodes Starting With f2 - case 0x0f: - switch(op3 = next()) { - // End Three-Byte Opcodes Starting With f2 0f - default: - cerr << "unrecognized third opcode after f2 0f: " << HEXBYTE << NUM(op3) << '\n'; - exit(1); - } - break; - default: - cerr << "unrecognized second opcode after f2: " << HEXBYTE << NUM(op2) << '\n'; - exit(1); - } - break; - case 0xf3: - switch(op2 = next()) { - // End Two-Byte Opcodes Starting With f3 - case 0x0f: - switch(op3 = next()) { - // End Three-Byte Opcodes Starting With f3 0f - default: - cerr << "unrecognized third opcode after f3 0f: " << HEXBYTE << NUM(op3) << '\n'; - exit(1); - } - break; - default: - cerr << "unrecognized second opcode after f3: " << HEXBYTE << NUM(op2) << '\n'; - exit(1); - } - break; - default: - cerr << "unrecognized opcode: " << HEXBYTE << NUM(op) << '\n'; - exit(1); - } -} - -inline uint8_t next() { - return read_mem_u8(EIP++); -} - -void dump_registers() { - ostringstream out; - out << "registers before: "; - for (int i = 0; i < NUM_INT_REGISTERS; ++i) { - if (i > 0) out << "; "; - out << " " << i << ": " << std::hex << std::setw(8) << std::setfill('_') << Reg[i].u; - } - out << " -- SF: " << SF << "; ZF: " << ZF << "; CF: " << CF << "; OF: " << OF; - trace(Callstack_depth+1, "run") << out.str() << end(); -} - -//: start tracking supported opcodes -:(before "End Globals") -map</*op*/string, string> Name; -map</*op*/string, string> Name_0f; -map</*op*/string, string> Name_f3; -map</*op*/string, string> Name_f3_0f; -:(before "End One-time Setup") -init_op_names(); -:(code) -void init_op_names() { - put(Name, "f4", "halt (hlt)"); - // End Initialize Op Names -} - -:(before "End Help Special-cases(key)") -if (key == "opcodes") { - cerr << "Opcodes currently supported by SubX:\n"; - for (map<string, string>::iterator p = Name.begin(); p != Name.end(); ++p) - cerr << " " << p->first << ": " << p->second << '\n'; - for (map<string, string>::iterator p = Name_0f.begin(); p != Name_0f.end(); ++p) - cerr << " 0f " << p->first << ": " << p->second << '\n'; - for (map<string, string>::iterator p = Name_f3.begin(); p != Name_f3.end(); ++p) - cerr << " f3 " << p->first << ": " << p->second << '\n'; - for (map<string, string>::iterator p = Name_f3_0f.begin(); p != Name_f3_0f.end(); ++p) - cerr << " f3 0f " << p->first << ": " << p->second << '\n'; - cerr << "Run `subx help instructions` for details on words like 'r32' and 'disp8'.\n" - "For complete details on these instructions, consult the IA-32 manual (volume 2).\n" - "There's various versions of it online, such as https://c9x.me/x86.\n" - "The mnemonics in brackets will help you locate each instruction.\n"; - return 0; -} -:(before "End Help Contents") -cerr << " opcodes\n"; - -//: Helpers for managing trace depths -//: -//: We're going to use trace depths primarily to segment code running at -//: different frames of the call stack. This will make it easy for the trace -//: browser to collapse over entire calls. -//: -//: Errors will be at depth 0. -//: Warnings will be at depth 1. -//: SubX instructions will occupy depth 2 and up to Max_depth, organized by -//: stack frames. Each instruction's internal details will be one level deeper -//: than its 'main' depth. So 'call' instruction details will be at the same -//: depth as the instructions of the function it calls. -:(before "End Globals") -extern const int Initial_callstack_depth = 2; -int Callstack_depth = Initial_callstack_depth; -:(before "End Reset") -Callstack_depth = Initial_callstack_depth; - -:(before "End Includes") -#include <iomanip> -#define HEXBYTE std::hex << std::setw(2) << std::setfill('0') -#define HEXWORD std::hex << std::setw(8) << std::setfill('0') -// ugly that iostream doesn't print uint8_t as an integer -#define NUM(X) static_cast<int>(X) -#include <stdint.h> diff --git a/subx/011run.cc b/subx/011run.cc deleted file mode 100644 index 194676d8..00000000 --- a/subx/011run.cc +++ /dev/null @@ -1,467 +0,0 @@ -//: Running SubX programs on the VM. - -//: (Not to be confused with the 'run' subcommand for running ELF binaries on -//: the VM. That comes later.) - -:(before "End Help Texts") -put_new(Help, "syntax", - "SubX programs consist of segments, each segment in turn consisting of lines.\n" - "Line-endings are significant; each line should contain a single\n" - "instruction, macro or directive.\n" - "\n" - "Comments start with the '#' character. It should be at the start of a word\n" - "(start of line, or following a space).\n" - "\n" - "Each segment starts with a header line: a '==' delimiter followed by the name of\n" - "the segment and a (sometimes approximate) starting address in memory.\n" - "The name 'code' is special; instructions to execute should always go here.\n" - "\n" - "The resulting binary starts running code from a label called 'Entry'\n" - "in the code segment.\n" - "\n" - "Segments with the same name get merged together. This rule helps keep functions and\n" - "their data close together in .subx files.\n" - "You don't have to specify the starting address after the first time.\n" - "\n" - "Lines consist of a series of words. Words can contain arbitrary metadata\n" - "after a '/', but they can never contain whitespace. Metadata has no effect\n" - "at runtime, but can be handy when rewriting macros.\n" - "\n" - "Check out the examples in the examples/ directory.\n" -); -:(before "End Help Contents") -cerr << " syntax\n"; - -:(code) -void test_copy_imm32_to_EAX() { - // At the lowest level, SubX programs are a series of hex bytes, each - // (variable-length) instruction on one line. - run( - // Comments start with '#' and are ignored. - "# comment\n" - // Segment headers start with '==', a name and a starting hex address. - // There's usually one code and one data segment. The code segment - // always comes first. - "== code 0x1\n" // code segment - - // After the header, each segment consists of lines, and each line - // consists of words separated by whitespace. - // - // All words can have metadata after a '/'. No spaces allowed in - // metadata, of course. - // Unrecognized metadata never causes errors, so you can use it for - // documentation. - // - // Within the code segment in particular, x86 instructions consist of - // some number of the following parts and sub-parts (see the Readme and - // cheatsheet.pdf for details): - // opcodes: 1-3 bytes - // ModR/M byte - // SIB byte - // displacement: 0/1/2/4 bytes - // immediate: 0/1/2/4 bytes - // opcode ModR/M SIB displacement immediate - // instruction mod, reg, Reg/Mem bits scale, index, base - // 1-3 bytes 0/1 byte 0/1 byte 0/1/2/4 bytes 0/1/2/4 bytes - " b8 . . . 0a 0b 0c 0d\n" // copy 0x0d0c0b0a to EAX - // The periods are just to help the eye track long gaps between columns, - // and are otherwise ignored. - ); - // This program, when run, causes the following events in the trace: - CHECK_TRACE_CONTENTS( - "load: 0x00000001 -> b8\n" - "load: 0x00000002 -> 0a\n" - "load: 0x00000003 -> 0b\n" - "load: 0x00000004 -> 0c\n" - "load: 0x00000005 -> 0d\n" - "run: copy imm32 0x0d0c0b0a to EAX\n" - ); -} - -// top-level helper for scenarios: parse the input, transform any macros, load -// the final hex bytes into memory, run it -void run(const string& text_bytes) { - program p; - istringstream in(text_bytes); - parse(in, p); - if (trace_contains_errors()) return; // if any stage raises errors, stop immediately - transform(p); - if (trace_contains_errors()) return; - load(p); - if (trace_contains_errors()) return; - // convenience to keep tests concise: 'Entry' label need not be provided - // not allowed in real programs - if (p.entry) - EIP = p.entry; - else - EIP = find(p, "code")->start; - while (EIP < End_of_program) - run_one_instruction(); -} - -//:: core data structures - -:(before "End Types") -struct program { - uint32_t entry; - vector<segment> segments; - program() { entry = 0; } -}; -:(before "struct program") -struct segment { - string name; - uint32_t start; - vector<line> lines; - // End segment Fields - segment() { - start = 0; - // End segment Constructor - } -}; -:(before "struct segment") -struct line { - vector<word> words; - vector<string> metadata; - string original; -}; -:(before "struct line") -struct word { - string original; - string data; - vector<string> metadata; -}; - -//:: parse - -:(code) -void parse(istream& fin, program& out) { - segment* curr_segment = NULL; - vector<line> l; - while (has_data(fin)) { - string line_data; - line curr; - getline(fin, line_data); - curr.original = line_data; - trace(99, "parse") << "line: " << line_data << end(); - // End Line Parsing Special-cases(line_data -> l) - istringstream lin(line_data); - while (has_data(lin)) { - string word_data; - lin >> word_data; - if (word_data.empty()) continue; - if (word_data[0] == '#') break; // comment - if (word_data == ".") continue; // comment token - if (word_data == "==") { - flush(curr_segment, l); - string segment_name; - lin >> segment_name; - curr_segment = find(out, segment_name); - if (curr_segment != NULL) { - trace(3, "parse") << "appending to segment '" << segment_name << "'" << end(); - } - else { - trace(3, "parse") << "new segment '" << segment_name << "'" << end(); - uint32_t seg_start = 0; - lin >> std::hex >> seg_start; - sanity_check_program_segment(out, seg_start); - out.segments.push_back(segment()); - curr_segment = &out.segments.back(); - curr_segment->name = segment_name; - curr_segment->start = seg_start; - if (trace_contains_errors()) continue; - trace(3, "parse") << "starts at address 0x" << HEXWORD << curr_segment->start << end(); - } - break; // skip rest of line - } - if (word_data[0] == ':') { - // todo: line metadata - break; - } - curr.words.push_back(word()); - parse_word(word_data, curr.words.back()); - trace(99, "parse") << "word: " << to_string(curr.words.back()); - } - if (!curr.words.empty()) - l.push_back(curr); - } - flush(curr_segment, l); - trace(99, "parse") << "done" << end(); -} - -segment* find(program& p, const string& segment_name) { - for (int i = 0; i < SIZE(p.segments); ++i) { - if (p.segments.at(i).name == segment_name) - return &p.segments.at(i); - } - return NULL; -} - -void flush(segment* s, vector<line>& lines) { - if (lines.empty()) return; - if (s == NULL) { - raise << "input does not start with a '==' section header\n" << end(); - return; - } - trace(3, "parse") << "flushing segment" << end(); - s->lines.insert(s->lines.end(), lines.begin(), lines.end()); - lines.clear(); -} - -void parse_word(const string& data, word& out) { - out.original = data; - istringstream win(data); - if (getline(win, out.data, '/')) { - string m; - while (getline(win, m, '/')) - out.metadata.push_back(m); - } -} - -void sanity_check_program_segment(const program& p, uint32_t addr) { - for (int i = 0; i < SIZE(p.segments); ++i) { - if (p.segments.at(i).start == addr) - raise << "can't have multiple segments starting at address 0x" << HEXWORD << addr << '\n' << end(); - } -} - -// helper for tests -void parse(const string& text_bytes) { - program p; - istringstream in(text_bytes); - parse(in, p); -} - -void test_detect_duplicate_segments() { - Hide_errors = true; - parse( - "== segment1 0xee\n" - "ab\n" - "== segment2 0xee\n" - "cd\n" - ); - CHECK_TRACE_CONTENTS( - "error: can't have multiple segments starting at address 0x000000ee\n" - ); -} - -//:: transform - -:(before "End Types") -typedef void (*transform_fn)(program&); -:(before "End Globals") -vector<transform_fn> Transform; - -:(code) -void transform(program& p) { - for (int t = 0; t < SIZE(Transform); ++t) - (*Transform.at(t))(p); -} - -//:: load - -void load(const program& p) { - if (find(p, "code") == NULL) { - raise << "no code to run\n" << end(); - return; - } - // Ensure segments are disjoint. - set<uint32_t> overlap; - for (int i = 0; i < SIZE(p.segments); ++i) { - const segment& seg = p.segments.at(i); - uint32_t addr = seg.start; - if (!already_allocated(addr)) - Mem.push_back(vma(seg.start)); - trace(99, "load") << "loading segment " << i << " from " << HEXWORD << addr << end(); - for (int j = 0; j < SIZE(seg.lines); ++j) { - const line& l = seg.lines.at(j); - for (int k = 0; k < SIZE(l.words); ++k) { - const word& w = l.words.at(k); - uint8_t val = hex_byte(w.data); - if (trace_contains_errors()) return; - assert(overlap.find(addr) == overlap.end()); - write_mem_u8(addr, val); - overlap.insert(addr); - trace(99, "load") << "0x" << HEXWORD << addr << " -> " << HEXBYTE << NUM(read_mem_u8(addr)) << end(); - ++addr; - } - } - if (seg.name == "code") { - End_of_program = addr; - } - } -} - -const segment* find(const program& p, const string& segment_name) { - for (int i = 0; i < SIZE(p.segments); ++i) { - if (p.segments.at(i).name == segment_name) - return &p.segments.at(i); - } - return NULL; -} - -uint8_t hex_byte(const string& s) { - if (contains_uppercase(s)) { - raise << "uppercase hex not allowed: " << s << '\n' << end(); - return 0; - } - istringstream in(s); - int result = 0; - in >> std::hex >> result; - if (!in || !in.eof()) { - raise << "token '" << s << "' is not a hex byte\n" << end(); - return '\0'; - } - if (result > 0xff || result < -0x8f) { - raise << "token '" << s << "' is not a hex byte\n" << end(); - return '\0'; - } - return static_cast<uint8_t>(result); -} - -void test_number_too_large() { - Hide_errors = true; - parse_and_load( - "== code 0x1\n" - "01 cab\n" - ); - CHECK_TRACE_CONTENTS( - "error: token 'cab' is not a hex byte\n" - ); -} - -void test_invalid_hex() { - Hide_errors = true; - parse_and_load( - "== code 0x1\n" - "01 cx\n" - ); - CHECK_TRACE_CONTENTS( - "error: token 'cx' is not a hex byte\n" - ); -} - -void test_negative_number() { - parse_and_load( - "== code 0x1\n" - "01 -02\n" - ); - CHECK_TRACE_COUNT("error", 0); -} - -void test_negative_number_too_small() { - Hide_errors = true; - parse_and_load( - "== code 0x1\n" - "01 -12345\n" - ); - CHECK_TRACE_CONTENTS( - "error: token '-12345' is not a hex byte\n" - ); -} - -void test_hex_prefix() { - parse_and_load( - "== code 0x1\n" - "0x01 -0x02\n" - ); - CHECK_TRACE_COUNT("error", 0); -} - -void test_repeated_segment_merges_data() { - parse_and_load( - "== code 0x1\n" - "11 22\n" - "== code\n" // again - "33 44\n" - ); - CHECK_TRACE_CONTENTS( - "parse: new segment 'code'\n" - "parse: appending to segment 'code'\n" - // first segment - "load: 0x00000001 -> 11\n" - "load: 0x00000002 -> 22\n" - // second segment - "load: 0x00000003 -> 33\n" - "load: 0x00000004 -> 44\n" - ); -} - -void test_error_on_missing_segment_header() { - Hide_errors = true; - parse_and_load( - "01 02\n" - ); - CHECK_TRACE_CONTENTS( - "error: input does not start with a '==' section header\n" - ); -} - -void test_error_on_uppercase_hex() { - Hide_errors = true; - parse_and_load( - "== code\n" - "01 Ab\n" - ); - CHECK_TRACE_CONTENTS( - "error: uppercase hex not allowed: Ab\n" - ); -} - -//: helper for tests -void parse_and_load(const string& text_bytes) { - program p; - istringstream in(text_bytes); - parse(in, p); - if (trace_contains_errors()) return; // if any stage raises errors, stop immediately - load(p); -} - -//:: run - -:(before "End Initialize Op Names") -put_new(Name, "b8", "copy imm32 to EAX (mov)"); - -//: our first opcode - -:(before "End Single-Byte Opcodes") -case 0xb8: { // copy imm32 to EAX - const int32_t src = next32(); - trace(Callstack_depth+1, "run") << "copy imm32 0x" << HEXWORD << src << " to EAX" << end(); - Reg[EAX].i = src; - break; -} - -:(code) -void test_copy_imm32_to_EAX_again() { - run( - "== code 0x1\n" // code segment - // op ModR/M SIB displacement immediate - " b8 0a 0b 0c 0d \n" // copy 0x0d0c0b0a to EAX - ); - CHECK_TRACE_CONTENTS( - "run: copy imm32 0x0d0c0b0a to EAX\n" - ); -} - -// read a 32-bit int in little-endian order from the instruction stream -int32_t next32() { - int32_t result = read_mem_i32(EIP); - EIP+=4; - return result; -} - -//:: helpers - -string to_string(const word& w) { - ostringstream out; - out << w.data; - for (int i = 0; i < SIZE(w.metadata); ++i) - out << " /" << w.metadata.at(i); - return out.str(); -} - -bool contains_uppercase(const string& s) { - for (int i = 0; i < SIZE(s); ++i) - if (isupper(s.at(i))) return true; - return false; -} diff --git a/subx/012elf.cc b/subx/012elf.cc deleted file mode 100644 index 9bd6cbf1..00000000 --- a/subx/012elf.cc +++ /dev/null @@ -1,190 +0,0 @@ -//: Loading SubX programs from ELF binaries. -//: This will allow us to run them natively on a Linux kernel. -//: Based on https://github.com/kragen/stoneknifeforth/blob/702d2ebe1b/386.c - -:(before "End Main") -assert(argc > 1); -if (is_equal(argv[1], "run")) { - START_TRACING_UNTIL_END_OF_SCOPE; - trace(2, "run") << "=== Starting to run" << end(); - assert(argc > 2); - reset(); - cerr << std::hex; - load_elf(argv[2], argc, argv); - while (EIP < End_of_program) // weak final-gasp termination check - run_one_instruction(); - raise << "executed past end of the world: " << EIP << " vs " << End_of_program << '\n' << end(); - return 1; -} - -:(code) -void load_elf(const string& filename, int argc, char* argv[]) { - int fd = open(filename.c_str(), O_RDONLY); - if (fd < 0) raise << filename.c_str() << ": open" << perr() << '\n' << die(); - off_t size = lseek(fd, 0, SEEK_END); - lseek(fd, 0, SEEK_SET); - uint8_t* elf_contents = static_cast<uint8_t*>(malloc(size)); - if (elf_contents == NULL) raise << "malloc(" << size << ')' << perr() << '\n' << die(); - ssize_t read_size = read(fd, elf_contents, size); - if (size != read_size) raise << "read → " << size << " (!= " << read_size << ')' << perr() << '\n' << die(); - load_elf_contents(elf_contents, size, argc, argv); - free(elf_contents); -} - -void load_elf_contents(uint8_t* elf_contents, size_t size, int argc, char* argv[]) { - uint8_t magic[5] = {0}; - memcpy(magic, elf_contents, 4); - if (memcmp(magic, "\177ELF", 4) != 0) - raise << "Invalid ELF file; starts with \"" << magic << '"' << die(); - if (elf_contents[4] != 1) - raise << "Only 32-bit ELF files (4-byte words; virtual addresses up to 4GB) supported.\n" << die(); - if (elf_contents[5] != 1) - raise << "Only little-endian ELF files supported.\n" << die(); - // unused: remaining 10 bytes of e_ident - uint32_t e_machine_type = u32_in(&elf_contents[16]); - if (e_machine_type != 0x00030002) - raise << "ELF type/machine 0x" << HEXWORD << e_machine_type << " isn't i386 executable\n" << die(); - // unused: e_version. We only support version 1, and later versions will be backwards compatible. - uint32_t e_entry = u32_in(&elf_contents[24]); - uint32_t e_phoff = u32_in(&elf_contents[28]); - // unused: e_shoff - // unused: e_flags - uint32_t e_ehsize = u16_in(&elf_contents[40]); - if (e_ehsize < 52) raise << "Invalid binary; ELF header too small\n" << die(); - uint32_t e_phentsize = u16_in(&elf_contents[42]); - uint32_t e_phnum = u16_in(&elf_contents[44]); - trace(90, "load") << e_phnum << " entries in the program header, each " << e_phentsize << " bytes long" << end(); - // unused: e_shentsize - // unused: e_shnum - // unused: e_shstrndx - - set<uint32_t> overlap; // to detect overlapping segments - for (size_t i = 0; i < e_phnum; ++i) - load_segment_from_program_header(elf_contents, i, size, e_phoff + i*e_phentsize, e_ehsize, overlap); - - // initialize code and stack - assert(overlap.find(STACK_SEGMENT) == overlap.end()); - Mem.push_back(vma(STACK_SEGMENT)); - assert(overlap.find(AFTER_STACK) == overlap.end()); - // The stack grows downward. - Reg[ESP].u = AFTER_STACK; - Reg[EBP].u = 0; - EIP = e_entry; - - // initialize args on stack - // no envp for now - // we wastefully use a separate page of memory for argv - Mem.push_back(vma(ARGV_DATA_SEGMENT)); - uint32_t argv_data = ARGV_DATA_SEGMENT; - for (int i = argc-1; i >= /*skip 'subx_bin' and 'run'*/2; --i) { - push(argv_data); - for (size_t j = 0; j <= strlen(argv[i]); ++j) { - assert(overlap.find(argv_data) == overlap.end()); // don't bother comparing ARGV and STACK - write_mem_u8(argv_data, argv[i][j]); - argv_data += sizeof(char); - assert(argv_data < ARGV_DATA_SEGMENT + SEGMENT_ALIGNMENT); - } - } - push(argc-/*skip 'subx_bin' and 'run'*/2); -} - -void push(uint32_t val) { - Reg[ESP].u -= 4; - if (Reg[ESP].u < STACK_SEGMENT) { - raise << "The stack overflowed its segment. " - << "Maybe SPACE_FOR_SEGMENT should be larger? " - << "Or you need to carve out an exception for the stack segment " - << "to be larger.\n" << die(); - } - trace(Callstack_depth+1, "run") << "decrementing ESP to 0x" << HEXWORD << Reg[ESP].u << end(); - trace(Callstack_depth+1, "run") << "pushing value 0x" << HEXWORD << val << end(); - write_mem_u32(Reg[ESP].u, val); -} - -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) { - uint32_t p_type = u32_in(&elf_contents[offset]); - trace(90, "load") << "program header at offset " << offset << ": type " << p_type << end(); - if (p_type != 1) { - trace(90, "load") << "ignoring segment at offset " << offset << " of non PT_LOAD type " << p_type << " (see http://refspecs.linuxbase.org/elf/elf.pdf)" << end(); - return; - } - uint32_t p_offset = u32_in(&elf_contents[offset + 4]); - uint32_t p_vaddr = u32_in(&elf_contents[offset + 8]); - if (e_ehsize > p_vaddr) raise << "Invalid binary; program header overlaps ELF header\n" << die(); - // unused: p_paddr - uint32_t p_filesz = u32_in(&elf_contents[offset + 16]); - uint32_t p_memsz = u32_in(&elf_contents[offset + 20]); - if (p_filesz != p_memsz) - raise << "Can't yet handle segments where p_filesz != p_memsz (see http://refspecs.linuxbase.org/elf/elf.pdf)\n" << die(); - - if (p_offset + p_filesz > size) - 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(); - if (p_memsz >= SEGMENT_ALIGNMENT) { - raise << "Code segment too small for SubX; for now please manually increase SEGMENT_ALIGNMENT.\n" << end(); - return; - } - trace(90, "load") << "blitting file offsets (" << p_offset << ", " << (p_offset+p_filesz) << ") to addresses (" << p_vaddr << ", " << (p_vaddr+p_memsz) << ')' << end(); - if (size > p_memsz) size = p_memsz; - Mem.push_back(vma(p_vaddr)); - for (size_t i = 0; i < p_filesz; ++i) { - assert(overlap.find(p_vaddr+i) == overlap.end()); - write_mem_u8(p_vaddr+i, elf_contents[p_offset+i]); - overlap.insert(p_vaddr+i); - } - if (segment_index == 0 && End_of_program < p_vaddr+p_memsz) - End_of_program = p_vaddr+p_memsz; -} - -:(before "End Includes") -// Very primitive/fixed/insecure ELF segments for now. -// --- inaccessible: 0x00000000 -> 0x08047fff -// code: 0x09000000 -> 0x09ffffff (specified in ELF binary) -// data: 0x0a000000 -> 0x0affffff (specified in ELF binary) -// --- heap gets mmap'd somewhere here --- -// stack: 0xbdffffff -> 0xbd000000 (downward; not in ELF binary) -// argv hack: 0xbf000000 -> 0xbfffffff (not in ELF binary) -// --- reserved for kernel: 0xc0000000 -> ... -const uint32_t START_HEAP = 0x0b000000; -const uint32_t END_HEAP = 0xbd000000; -const uint32_t STACK_SEGMENT = 0xbd000000; -const uint32_t AFTER_STACK = 0xbe000000; -const uint32_t ARGV_DATA_SEGMENT = 0xbf000000; -// When updating the above memory map, don't forget to update `mmap`'s -// implementation in the 'syscalls' layer. -:(before "End Dump Info for Instruction") -//? dump_stack(); // slow -:(code) -void dump_stack() { - ostringstream out; - trace(Callstack_depth+1, "run") << "stack:" << end(); - for (uint32_t a = AFTER_STACK-4; a > Reg[ESP].u; a -= 4) - trace(Callstack_depth+2, "run") << " 0x" << HEXWORD << a << " => 0x" << HEXWORD << read_mem_u32(a) << end(); - trace(Callstack_depth+2, "run") << " 0x" << HEXWORD << Reg[ESP].u << " => 0x" << HEXWORD << read_mem_u32(Reg[ESP].u) << " <=== ESP" << end(); - for (uint32_t a = Reg[ESP].u-4; a > Reg[ESP].u-40; a -= 4) - trace(Callstack_depth+2, "run") << " 0x" << HEXWORD << a << " => 0x" << HEXWORD << read_mem_u32(a) << end(); -} - -inline uint32_t u32_in(uint8_t* p) { - return p[0] | p[1] << 8 | p[2] << 16 | p[3] << 24; -} - -inline uint16_t u16_in(uint8_t* p) { - return p[0] | p[1] << 8; -} - -:(before "End Types") -struct perr {}; -:(code) -ostream& operator<<(ostream& os, perr /*unused*/) { - if (errno) - os << ": " << strerror(errno); - return os; -} - -:(before "End Includes") -#include <sys/types.h> -#include <sys/stat.h> -#include <fcntl.h> -#include <stdarg.h> -#include <errno.h> -#include <unistd.h> diff --git a/subx/013direct_addressing.cc b/subx/013direct_addressing.cc deleted file mode 100644 index 513cb61b..00000000 --- a/subx/013direct_addressing.cc +++ /dev/null @@ -1,1279 +0,0 @@ -//: operating directly on a register - -:(before "End Initialize Op Names") -put_new(Name, "01", "add r32 to rm32 (add)"); - -:(code) -void test_add_r32_to_r32() { - Reg[EAX].i = 0x10; - Reg[EBX].i = 1; - run( - "== code 0x1\n" // code segment - // op ModR/M SIB displacement immediate - " 01 d8 \n" // add EBX to EAX - // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX) - ); - CHECK_TRACE_CONTENTS( - "run: add EBX to r/m32\n" - "run: r/m32 is EAX\n" - "run: storing 0x00000011\n" - ); -} - -:(before "End Single-Byte Opcodes") -case 0x01: { // add r32 to r/m32 - uint8_t modrm = next(); - uint8_t arg2 = (modrm>>3)&0x7; - trace(Callstack_depth+1, "run") << "add " << rname(arg2) << " to r/m32" << end(); - int32_t* signed_arg1 = effective_address(modrm); - int32_t signed_result = *signed_arg1 + Reg[arg2].i; - SF = (signed_result < 0); - ZF = (signed_result == 0); - int64_t signed_full_result = static_cast<int64_t>(*signed_arg1) + Reg[arg2].i; - OF = (signed_result != signed_full_result); - // set CF - uint32_t unsigned_arg1 = static_cast<uint32_t>(*signed_arg1); - uint32_t unsigned_result = unsigned_arg1 + Reg[arg2].u; - uint64_t unsigned_full_result = static_cast<uint64_t>(unsigned_arg1) + Reg[arg2].u; - CF = (unsigned_result != unsigned_full_result); - trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); - *signed_arg1 = signed_result; - trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *signed_arg1 << end(); - break; -} - -:(code) -void test_add_r32_to_r32_signed_overflow() { - Reg[EAX].i = 0x7fffffff; // largest positive signed integer - Reg[EBX].i = 1; - run( - "== code 0x1\n" // code segment - // op ModR/M SIB displacement immediate - " 01 d8 \n" // add EBX to EAX - // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX) - ); - CHECK_TRACE_CONTENTS( - "run: add EBX to r/m32\n" - "run: r/m32 is EAX\n" - "run: SF=1; ZF=0; CF=0; OF=1\n" - "run: storing 0x80000000\n" - ); -} - -void test_add_r32_to_r32_unsigned_overflow() { - Reg[EAX].u = 0xffffffff; // largest unsigned number - Reg[EBX].u = 1; - run( - "== code 0x1\n" // code segment - // op ModR/M SIB displacement immediate - " 01 d8 \n" // add EBX to EAX - // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX) - ); - CHECK_TRACE_CONTENTS( - "run: add EBX to r/m32\n" - "run: r/m32 is EAX\n" - "run: SF=0; ZF=1; CF=1; OF=0\n" - "run: storing 0x00000000\n" - ); -} - -void test_add_r32_to_r32_unsigned_and_signed_overflow() { - Reg[EAX].u = Reg[EBX].u = 0x80000000; // smallest negative signed integer - run( - "== code 0x1\n" // code segment - // op ModR/M SIB displacement immediate - " 01 d8 \n" // add EBX to EAX - // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX) - ); - CHECK_TRACE_CONTENTS( - "run: add EBX to r/m32\n" - "run: r/m32 is EAX\n" - "run: SF=0; ZF=1; CF=1; OF=1\n" - "run: storing 0x00000000\n" - ); -} - -:(code) -// Implement tables 2-2 and 2-3 in the Intel manual, Volume 2. -// We return a pointer so that instructions can write to multiple bytes in -// 'Mem' at once. -// beware: will eventually have side-effects -int32_t* effective_address(uint8_t modrm) { - const uint8_t mod = (modrm>>6); - // ignore middle 3 'reg opcode' bits - const uint8_t rm = modrm & 0x7; - if (mod == 3) { - // mod 3 is just register direct addressing - trace(Callstack_depth+1, "run") << "r/m32 is " << rname(rm) << end(); - return &Reg[rm].i; - } - uint32_t addr = effective_address_number(modrm); - trace(Callstack_depth+1, "run") << "effective address contains " << read_mem_i32(addr) << end(); - return mem_addr_i32(addr); -} - -// beware: will eventually have side-effects -uint32_t effective_address_number(uint8_t modrm) { - const uint8_t mod = (modrm>>6); - // ignore middle 3 'reg opcode' bits - const uint8_t rm = modrm & 0x7; - uint32_t addr = 0; - switch (mod) { - case 3: - // mod 3 is just register direct addressing - raise << "unexpected direct addressing mode\n" << end(); - return 0; - // End Mod Special-cases(addr) - default: - cerr << "unrecognized mod bits: " << NUM(mod) << '\n'; - exit(1); - } - //: other mods are indirect, and they'll set addr appropriately - // Found effective_address(addr) - return addr; -} - -string rname(uint8_t r) { - switch (r) { - case 0: return "EAX"; - case 1: return "ECX"; - case 2: return "EDX"; - case 3: return "EBX"; - case 4: return "ESP"; - case 5: return "EBP"; - case 6: return "ESI"; - case 7: return "EDI"; - default: raise << "invalid register " << r << '\n' << end(); return ""; - } -} - -//:: subtract - -:(before "End Initialize Op Names") -put_new(Name, "29", "subtract r32 from rm32 (sub)"); - -:(code) -void test_subtract_r32_from_r32() { - Reg[EAX].i = 10; - Reg[EBX].i = 1; - run( - "== code 0x1\n" // code segment - // op ModR/M SIB displacement immediate - " 29 d8 \n" // subtract EBX from EAX - // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX) - ); - CHECK_TRACE_CONTENTS( - "run: subtract EBX from r/m32\n" - "run: r/m32 is EAX\n" - "run: storing 0x00000009\n" - ); -} - -:(before "End Single-Byte Opcodes") -case 0x29: { // subtract r32 from r/m32 - const uint8_t modrm = next(); - const uint8_t arg2 = (modrm>>3)&0x7; - trace(Callstack_depth+1, "run") << "subtract " << rname(arg2) << " from r/m32" << end(); - int32_t* signed_arg1 = effective_address(modrm); - int32_t signed_result = *signed_arg1 - Reg[arg2].i; - SF = (signed_result < 0); - ZF = (signed_result == 0); - int64_t signed_full_result = static_cast<int64_t>(*signed_arg1) - Reg[arg2].i; - OF = (signed_result != signed_full_result); - // set CF - uint32_t unsigned_arg1 = static_cast<uint32_t>(*signed_arg1); - uint32_t unsigned_result = unsigned_arg1 - Reg[arg2].u; - uint64_t unsigned_full_result = static_cast<uint64_t>(unsigned_arg1) - Reg[arg2].u; - CF = (unsigned_result != unsigned_full_result); - trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); - *signed_arg1 = signed_result; - trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *signed_arg1 << end(); - break; -} - -:(code) -void test_subtract_r32_from_r32_signed_overflow() { - Reg[EAX].i = 0x80000000; // smallest negative signed integer - Reg[EBX].i = 0x7fffffff; // largest positive signed integer - run( - "== code 0x1\n" // code segment - // op ModR/M SIB displacement immediate - " 29 d8 \n" // subtract EBX from EAX - // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX) - ); - CHECK_TRACE_CONTENTS( - "run: subtract EBX from r/m32\n" - "run: r/m32 is EAX\n" - "run: SF=0; ZF=0; CF=0; OF=1\n" - "run: storing 0x00000001\n" - ); -} - -void test_subtract_r32_from_r32_unsigned_overflow() { - Reg[EAX].i = 0; - Reg[EBX].i = 1; - run( - "== code 0x1\n" // code segment - // op ModR/M SIB displacement immediate - " 29 d8 \n" // subtract EBX from EAX - // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX) - ); - CHECK_TRACE_CONTENTS( - "run: subtract EBX from r/m32\n" - "run: r/m32 is EAX\n" - "run: SF=1; ZF=0; CF=1; OF=0\n" - "run: storing 0xffffffff\n" - ); -} - -void test_subtract_r32_from_r32_signed_and_unsigned_overflow() { - Reg[EAX].i = 0; - Reg[EBX].i = 0x80000000; // smallest negative signed integer - run( - "== code 0x1\n" // code segment - // op ModR/M SIB displacement immediate - " 29 d8 \n" // subtract EBX from EAX - // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX) - ); - CHECK_TRACE_CONTENTS( - "run: subtract EBX from r/m32\n" - "run: r/m32 is EAX\n" - "run: SF=1; ZF=0; CF=1; OF=1\n" - "run: storing 0x80000000\n" - ); -} - -//:: multiply - -:(before "End Initialize Op Names") -put_new(Name, "f7", "negate/multiply/divide rm32 (with EAX and EDX if necessary) depending on subop (neg/mul/idiv)"); - -:(code) -void test_multiply_EAX_by_r32() { - Reg[EAX].i = 4; - Reg[ECX].i = 3; - run( - "== code 0x1\n" // code segment - // op ModR/M SIB displacement immediate - " f7 e1 \n" // multiply EAX by ECX - // ModR/M in binary: 11 (direct mode) 100 (subop mul) 001 (src ECX) - ); - CHECK_TRACE_CONTENTS( - "run: operate on r/m32\n" - "run: r/m32 is ECX\n" - "run: subop: multiply EAX by r/m32\n" - "run: storing 0x0000000c\n" - ); -} - -:(before "End Single-Byte Opcodes") -case 0xf7: { - const uint8_t modrm = next(); - trace(Callstack_depth+1, "run") << "operate on r/m32" << end(); - int32_t* arg1 = effective_address(modrm); - const uint8_t subop = (modrm>>3)&0x7; // middle 3 'reg opcode' bits - switch (subop) { - case 4: { // mul unsigned EAX by r/m32 - trace(Callstack_depth+1, "run") << "subop: multiply EAX by r/m32" << end(); - const uint64_t result = static_cast<uint64_t>(Reg[EAX].u) * static_cast<uint32_t>(*arg1); - Reg[EAX].u = result & 0xffffffff; - Reg[EDX].u = result >> 32; - OF = (Reg[EDX].u != 0); - CF = OF; - trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); - trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << Reg[EAX].u << end(); - break; - } - // End Op f7 Subops - default: - cerr << "unrecognized subop for opcode f7: " << NUM(subop) << '\n'; - exit(1); - } - break; -} - -//: - -:(before "End Initialize Op Names") -put_new(Name_0f, "af", "multiply rm32 into r32 (imul)"); - -:(code) -void test_multiply_r32_into_r32() { - Reg[EAX].i = 4; - Reg[EBX].i = 2; - run( - "== code 0x1\n" // code segment - // op ModR/M SIB displacement immediate - " 0f af d8 \n" // subtract EBX into EAX - // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX) - ); - CHECK_TRACE_CONTENTS( - "run: multiply EBX by r/m32\n" - "run: r/m32 is EAX\n" - "run: storing 0x00000008\n" - ); -} - -:(before "End Two-Byte Opcodes Starting With 0f") -case 0xaf: { // multiply r32 by r/m32 - const uint8_t modrm = next(); - const uint8_t arg1 = (modrm>>3)&0x7; - trace(Callstack_depth+1, "run") << "multiply " << rname(arg1) << " by r/m32" << end(); - const int32_t* arg2 = effective_address(modrm); - int32_t result = Reg[arg1].i * (*arg2); - SF = (Reg[arg1].i < 0); - ZF = (Reg[arg1].i == 0); - int64_t full_result = static_cast<int64_t>(Reg[arg1].i) * (*arg2); - OF = (Reg[arg1].i != full_result); - CF = OF; - trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); - Reg[arg1].i = result; - trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << Reg[arg1].i << end(); - break; -} - -//:: negate - -:(code) -void test_negate_r32() { - Reg[EBX].i = 1; - run( - "== code 0x1\n" // code segment - // op ModR/M SIB displacement immediate - " f7 db \n" // negate EBX - // ModR/M in binary: 11 (direct mode) 011 (subop negate) 011 (dest EBX) - ); - CHECK_TRACE_CONTENTS( - "run: operate on r/m32\n" - "run: r/m32 is EBX\n" - "run: subop: negate\n" - "run: storing 0xffffffff\n" - ); -} - -:(before "End Op f7 Subops") -case 3: { // negate r/m32 - trace(Callstack_depth+1, "run") << "subop: negate" << end(); - // one case that can overflow - if (static_cast<uint32_t>(*arg1) == 0x80000000) { - trace(Callstack_depth+1, "run") << "overflow" << end(); - SF = true; - ZF = false; - OF = true; - break; - } - int32_t result = -(*arg1); - SF = (result >> 31); - ZF = (result == 0); - OF = false; - CF = (*arg1 != 0); - trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); - *arg1 = result; - trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *arg1 << end(); - break; -} - -:(code) -// negate can overflow in exactly one situation -void test_negate_can_overflow() { - Reg[EBX].i = 0x80000000; // INT_MIN - run( - "== code 0x1\n" // code segment - // op ModR/M SIB displacement immediate - " f7 db \n" // negate EBX - // ModR/M in binary: 11 (direct mode) 011 (subop negate) 011 (dest EBX) - ); - CHECK_TRACE_CONTENTS( - "run: operate on r/m32\n" - "run: r/m32 is EBX\n" - "run: subop: negate\n" - "run: overflow\n" - ); -} - -//:: divide with remainder - -void test_divide_EAX_by_rm32() { - Reg[EAX].u = 7; - Reg[EDX].u = 0; - Reg[ECX].i = 3; - run( - "== code 0x1\n" // code segment - // op ModR/M SIB displacement immediate - " f7 f9 \n" // multiply EAX by ECX - // ModR/M in binary: 11 (direct mode) 111 (subop idiv) 001 (divisor ECX) - ); - CHECK_TRACE_CONTENTS( - "run: operate on r/m32\n" - "run: r/m32 is ECX\n" - "run: subop: divide EDX:EAX by r/m32, storing quotient in EAX and remainder in EDX\n" - "run: quotient: 0x00000002\n" - "run: remainder: 0x00000001\n" - ); -} - -:(before "End Op f7 Subops") -case 7: { // divide EDX:EAX by r/m32, storing quotient in EAX and remainder in EDX - trace(Callstack_depth+1, "run") << "subop: divide EDX:EAX by r/m32, storing quotient in EAX and remainder in EDX" << end(); - int64_t dividend = static_cast<int64_t>((static_cast<uint64_t>(Reg[EDX].u) << 32) | Reg[EAX].u); - int32_t divisor = *arg1; - assert(divisor != 0); - Reg[EAX].i = dividend/divisor; // quotient - Reg[EDX].i = dividend%divisor; // remainder - // flag state undefined - trace(Callstack_depth+1, "run") << "quotient: 0x" << HEXWORD << Reg[EAX].i << end(); - trace(Callstack_depth+1, "run") << "remainder: 0x" << HEXWORD << Reg[EDX].i << end(); - break; -} - -:(code) -void test_divide_EAX_by_negative_rm32() { - Reg[EAX].u = 7; - Reg[EDX].u = 0; - Reg[ECX].i = -3; - run( - "== code 0x1\n" // code segment - // op ModR/M SIB displacement immediate - " f7 f9 \n" // multiply EAX by ECX - // ModR/M in binary: 11 (direct mode) 111 (subop idiv) 001 (divisor ECX) - ); - CHECK_TRACE_CONTENTS( - "run: operate on r/m32\n" - "run: r/m32 is ECX\n" - "run: subop: divide EDX:EAX by r/m32, storing quotient in EAX and remainder in EDX\n" - "run: quotient: 0xfffffffe\n" // -2 - "run: remainder: 0x00000001\n" - ); -} - -void test_divide_negative_EAX_by_rm32() { - Reg[EAX].i = -7; - Reg[EDX].i = -1; // sign extend - Reg[ECX].i = 3; - run( - "== code 0x1\n" // code segment - // op ModR/M SIB displacement immediate - " f7 f9 \n" // multiply EAX by ECX - // ModR/M in binary: 11 (direct mode) 111 (subop idiv) 001 (divisor ECX) - ); - CHECK_TRACE_CONTENTS( - "run: operate on r/m32\n" - "run: r/m32 is ECX\n" - "run: subop: divide EDX:EAX by r/m32, storing quotient in EAX and remainder in EDX\n" - "run: quotient: 0xfffffffe\n" // -2 - "run: remainder: 0xffffffff\n" // -1, same sign as divident (EDX:EAX) - ); -} - -void test_divide_negative_EDX_EAX_by_rm32() { - Reg[EAX].i = 0; // lower 32 bits are clear - Reg[EDX].i = -7; - Reg[ECX].i = 0x40000000; // 2^30 (largest positive power of 2) - run( - "== code 0x1\n" // code segment - // op ModR/M SIB displacement immediate - " f7 f9 \n" // multiply EAX by ECX - // ModR/M in binary: 11 (direct mode) 111 (subop idiv) 001 (divisor ECX) - ); - CHECK_TRACE_CONTENTS( - "run: operate on r/m32\n" - "run: r/m32 is ECX\n" - "run: subop: divide EDX:EAX by r/m32, storing quotient in EAX and remainder in EDX\n" - "run: quotient: 0xffffffe4\n" // (-7 << 32) / (1 << 30) = -7 << 2 = -28 - "run: remainder: 0x00000000\n" - ); -} - -//:: shift left - -:(before "End Initialize Op Names") -put_new(Name, "d3", "shift rm32 by CL bits depending on subop (sal/sar/shl/shr)"); - -:(code) -void test_shift_left_r32_with_cl() { - Reg[EBX].i = 13; - Reg[ECX].i = 1; - run( - "== code 0x1\n" // code segment - // op ModR/M SIB displacement immediate - " d3 e3 \n" // shift EBX left by CL bits - // ModR/M in binary: 11 (direct mode) 100 (subop shift left) 011 (dest EBX) - ); - CHECK_TRACE_CONTENTS( - "run: operate on r/m32\n" - "run: r/m32 is EBX\n" - "run: subop: shift left by CL bits\n" - "run: storing 0x0000001a\n" - ); -} - -:(before "End Single-Byte Opcodes") -case 0xd3: { - const uint8_t modrm = next(); - trace(Callstack_depth+1, "run") << "operate on r/m32" << end(); - int32_t* arg1 = effective_address(modrm); - const uint8_t subop = (modrm>>3)&0x7; // middle 3 'reg opcode' bits - switch (subop) { - case 4: { // shift left r/m32 by CL - trace(Callstack_depth+1, "run") << "subop: shift left by CL bits" << end(); - uint8_t count = Reg[ECX].u & 0x1f; - // OF is only defined if count is 1 - if (count == 1) { - bool msb = (*arg1 & 0x80000000) >> 1; - bool pnsb = (*arg1 & 0x40000000); - OF = (msb != pnsb); - } - int32_t result = (*arg1 << count); - ZF = (result == 0); - SF = (result < 0); - CF = (*arg1 << (count-1)) & 0x80000000; - trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); - *arg1 = result; - trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *arg1 << end(); - break; - } - // End Op d3 Subops - default: - cerr << "unrecognized subop for opcode d3: " << NUM(subop) << '\n'; - exit(1); - } - break; -} - -//:: shift right arithmetic - -:(code) -void test_shift_right_arithmetic_r32_with_cl() { - Reg[EBX].i = 26; - Reg[ECX].i = 1; - run( - "== code 0x1\n" // code segment - // op ModR/M SIB displacement immediate - " d3 fb \n" // shift EBX right by CL bits, while preserving sign - // ModR/M in binary: 11 (direct mode) 111 (subop shift right arithmetic) 011 (dest EBX) - ); - CHECK_TRACE_CONTENTS( - "run: operate on r/m32\n" - "run: r/m32 is EBX\n" - "run: subop: shift right by CL bits, while preserving sign\n" - "run: storing 0x0000000d\n" - ); -} - -:(before "End Op d3 Subops") -case 7: { // shift right r/m32 by CL, preserving sign - trace(Callstack_depth+1, "run") << "subop: shift right by CL bits, while preserving sign" << end(); - uint8_t count = Reg[ECX].u & 0x1f; - *arg1 = (*arg1 >> count); - ZF = (*arg1 == 0); - SF = (*arg1 < 0); - // OF is only defined if count is 1 - if (count == 1) OF = false; - // CF undefined - trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *arg1 << end(); - break; -} - -:(code) -void test_shift_right_arithmetic_odd_r32_with_cl() { - Reg[EBX].i = 27; - Reg[ECX].i = 1; - run( - "== code 0x1\n" // code segment - // op ModR/M SIB displacement immediate - " d3 fb \n" // shift EBX right by CL bits, while preserving sign - // ModR/M in binary: 11 (direct mode) 111 (subop shift right arithmetic) 011 (dest EBX) - ); - CHECK_TRACE_CONTENTS( - "run: operate on r/m32\n" - "run: r/m32 is EBX\n" - "run: subop: shift right by CL bits, while preserving sign\n" - // result: 13 - "run: storing 0x0000000d\n" - ); -} - -void test_shift_right_arithmetic_negative_r32_with_cl() { - Reg[EBX].i = 0xfffffffd; // -3 - Reg[ECX].i = 1; - run( - "== code 0x1\n" // code segment - // op ModR/M SIB displacement immediate - " d3 fb \n" // shift EBX right by CL bits, while preserving sign - // ModR/M in binary: 11 (direct mode) 111 (subop shift right arithmetic) 011 (dest EBX) - ); - CHECK_TRACE_CONTENTS( - "run: operate on r/m32\n" - "run: r/m32 is EBX\n" - "run: subop: shift right by CL bits, while preserving sign\n" - // result: -2 - "run: storing 0xfffffffe\n" - ); -} - -//:: shift right logical - -:(code) -void test_shift_right_logical_r32_with_cl() { - Reg[EBX].i = 26; - Reg[ECX].i = 1; - run( - "== code 0x1\n" // code segment - // op ModR/M SIB displacement immediate - " d3 eb \n" // shift EBX right by CL bits, while padding zeroes - // ModR/M in binary: 11 (direct mode) 101 (subop shift right logical) 011 (dest EBX) - ); - CHECK_TRACE_CONTENTS( - "run: operate on r/m32\n" - "run: r/m32 is EBX\n" - "run: subop: shift right by CL bits, while padding zeroes\n" - // result: 13 - "run: storing 0x0000000d\n" - ); -} - -:(before "End Op d3 Subops") -case 5: { // shift right r/m32 by CL, padding zeroes - trace(Callstack_depth+1, "run") << "subop: shift right by CL bits, while padding zeroes" << end(); - uint8_t count = Reg[ECX].u & 0x1f; - // OF is only defined if count is 1 - if (count == 1) { - bool msb = (*arg1 & 0x80000000) >> 1; - bool pnsb = (*arg1 & 0x40000000); - OF = (msb != pnsb); - } - uint32_t* uarg1 = reinterpret_cast<uint32_t*>(arg1); - *uarg1 = (*uarg1 >> count); - ZF = (*uarg1 == 0); - // result is always positive by definition - SF = false; - // CF undefined - trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *arg1 << end(); - break; -} - -:(code) -void test_shift_right_logical_odd_r32_with_cl() { - Reg[EBX].i = 27; - Reg[ECX].i = 1; - run( - "== code 0x1\n" // code segment - // op ModR/M SIB displacement immediate - " d3 eb \n" // shift EBX right by CL bits, while padding zeroes - // ModR/M in binary: 11 (direct mode) 101 (subop shift right logical) 011 (dest EBX) - ); - CHECK_TRACE_CONTENTS( - "run: operate on r/m32\n" - "run: r/m32 is EBX\n" - "run: subop: shift right by CL bits, while padding zeroes\n" - // result: 13 - "run: storing 0x0000000d\n" - ); -} - -void test_shift_right_logical_negative_r32_with_cl() { - Reg[EBX].i = 0xfffffffd; - Reg[ECX].i = 1; - run( - "== code 0x1\n" // code segment - // op ModR/M SIB displacement immediate - " d3 eb \n" // shift EBX right by CL bits, while padding zeroes - // ModR/M in binary: 11 (direct mode) 101 (subop shift right logical) 011 (dest EBX) - ); - CHECK_TRACE_CONTENTS( - "run: operate on r/m32\n" - "run: r/m32 is EBX\n" - "run: subop: shift right by CL bits, while padding zeroes\n" - "run: storing 0x7ffffffe\n" - ); -} - -//:: and - -:(before "End Initialize Op Names") -put_new(Name, "21", "rm32 = bitwise AND of r32 with rm32 (and)"); - -:(code) -void test_and_r32_with_r32() { - Reg[EAX].i = 0x0a0b0c0d; - Reg[EBX].i = 0x000000ff; - run( - "== code 0x1\n" // code segment - // op ModR/M SIB displacement immediate - " 21 d8 \n" // and EBX with destination EAX - // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX) - ); - CHECK_TRACE_CONTENTS( - "run: and EBX with r/m32\n" - "run: r/m32 is EAX\n" - "run: storing 0x0000000d\n" - ); -} - -:(before "End Single-Byte Opcodes") -case 0x21: { // and r32 with r/m32 - const uint8_t modrm = next(); - const uint8_t arg2 = (modrm>>3)&0x7; - trace(Callstack_depth+1, "run") << "and " << rname(arg2) << " with r/m32" << end(); - // bitwise ops technically operate on unsigned numbers, but it makes no - // difference - int32_t* signed_arg1 = effective_address(modrm); - *signed_arg1 &= Reg[arg2].i; - trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *signed_arg1 << end(); - SF = (*signed_arg1 >> 31); - ZF = (*signed_arg1 == 0); - CF = false; - OF = false; - trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); - break; -} - -//:: or - -:(before "End Initialize Op Names") -put_new(Name, "09", "rm32 = bitwise OR of r32 with rm32 (or)"); - -:(code) -void test_or_r32_with_r32() { - Reg[EAX].i = 0x0a0b0c0d; - Reg[EBX].i = 0xa0b0c0d0; - run( - "== code 0x1\n" // code segment - // op ModR/M SIB displacement immediate - " 09 d8 \n" // or EBX with destination EAX - // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX) - ); - CHECK_TRACE_CONTENTS( - "run: or EBX with r/m32\n" - "run: r/m32 is EAX\n" - "run: storing 0xaabbccdd\n" - ); -} - -:(before "End Single-Byte Opcodes") -case 0x09: { // or r32 with r/m32 - const uint8_t modrm = next(); - const uint8_t arg2 = (modrm>>3)&0x7; - trace(Callstack_depth+1, "run") << "or " << rname(arg2) << " with r/m32" << end(); - // bitwise ops technically operate on unsigned numbers, but it makes no - // difference - int32_t* signed_arg1 = effective_address(modrm); - *signed_arg1 |= Reg[arg2].i; - trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *signed_arg1 << end(); - SF = (*signed_arg1 >> 31); - ZF = (*signed_arg1 == 0); - CF = false; - OF = false; - trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); - break; -} - -//:: xor - -:(before "End Initialize Op Names") -put_new(Name, "31", "rm32 = bitwise XOR of r32 with rm32 (xor)"); - -:(code) -void test_xor_r32_with_r32() { - Reg[EAX].i = 0x0a0b0c0d; - Reg[EBX].i = 0xaabbc0d0; - run( - "== code 0x1\n" // code segment - // op ModR/M SIB displacement immediate - " 31 d8 \n" // xor EBX with destination EAX - // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX) - ); - CHECK_TRACE_CONTENTS( - "run: xor EBX with r/m32\n" - "run: r/m32 is EAX\n" - "run: storing 0xa0b0ccdd\n" - ); -} - -:(before "End Single-Byte Opcodes") -case 0x31: { // xor r32 with r/m32 - const uint8_t modrm = next(); - const uint8_t arg2 = (modrm>>3)&0x7; - trace(Callstack_depth+1, "run") << "xor " << rname(arg2) << " with r/m32" << end(); - // bitwise ops technically operate on unsigned numbers, but it makes no - // difference - int32_t* signed_arg1 = effective_address(modrm); - *signed_arg1 ^= Reg[arg2].i; - trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *signed_arg1 << end(); - SF = (*signed_arg1 >> 31); - ZF = (*signed_arg1 == 0); - CF = false; - OF = false; - trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); - break; -} - -//:: not - -:(code) -void test_not_r32() { - Reg[EBX].i = 0x0f0f00ff; - run( - "== code 0x1\n" // code segment - // op ModR/M SIB displacement immediate - " f7 d3 \n" // not EBX - // ModR/M in binary: 11 (direct mode) 010 (subop not) 011 (dest EBX) - ); - CHECK_TRACE_CONTENTS( - "run: operate on r/m32\n" - "run: r/m32 is EBX\n" - "run: subop: not\n" - "run: storing 0xf0f0ff00\n" - ); -} - -:(before "End Op f7 Subops") -case 2: { // not r/m32 - trace(Callstack_depth+1, "run") << "subop: not" << end(); - *arg1 = ~(*arg1); - trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *arg1 << end(); - // no flags affected - break; -} - -//:: compare (cmp) - -:(before "End Initialize Op Names") -put_new(Name, "39", "compare: set SF if rm32 < r32 (cmp)"); - -:(code) -void test_compare_r32_with_r32_greater() { - Reg[EAX].i = 0x0a0b0c0d; - Reg[EBX].i = 0x0a0b0c07; - run( - "== code 0x1\n" // code segment - // op ModR/M SIB displacement immediate - " 39 d8 \n" // compare EAX with EBX - // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX) - ); - CHECK_TRACE_CONTENTS( - "run: compare r/m32 with EBX\n" - "run: r/m32 is EAX\n" - "run: SF=0; ZF=0; CF=0; OF=0\n" - ); -} - -:(before "End Single-Byte Opcodes") -case 0x39: { // set SF if r/m32 < r32 - const uint8_t modrm = next(); - const uint8_t reg2 = (modrm>>3)&0x7; - trace(Callstack_depth+1, "run") << "compare r/m32 with " << rname(reg2) << end(); - const int32_t* signed_arg1 = effective_address(modrm); - const int32_t signed_difference = *signed_arg1 - Reg[reg2].i; - SF = (signed_difference < 0); - ZF = (signed_difference == 0); - const int64_t signed_full_difference = static_cast<int64_t>(*signed_arg1) - Reg[reg2].i; - OF = (signed_difference != signed_full_difference); - // set CF - const uint32_t unsigned_arg1 = static_cast<uint32_t>(*signed_arg1); - const uint32_t unsigned_difference = unsigned_arg1 - Reg[reg2].u; - const uint64_t unsigned_full_difference = static_cast<uint64_t>(unsigned_arg1) - Reg[reg2].u; - CF = (unsigned_difference != unsigned_full_difference); - trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); - break; -} - -:(code) -void test_compare_r32_with_r32_lesser_unsigned_and_signed() { - Reg[EAX].i = 0x0a0b0c07; - Reg[EBX].i = 0x0a0b0c0d; - run( - "== code 0x1\n" // code segment - // op ModR/M SIB displacement immediate - " 39 d8 \n" // compare EAX with EBX - // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX) - ); - CHECK_TRACE_CONTENTS( - "run: compare r/m32 with EBX\n" - "run: r/m32 is EAX\n" - "run: SF=1; ZF=0; CF=1; OF=0\n" - ); -} - -void test_compare_r32_with_r32_lesser_unsigned_and_signed_due_to_overflow() { - Reg[EAX].i = 0x7fffffff; // largest positive signed integer - Reg[EBX].i = 0x80000000; // smallest negative signed integer - run( - "== code 0x1\n" // code segment - // op ModR/M SIB displacement immediate - " 39 d8 \n" // compare EAX with EBX - // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX) - ); - CHECK_TRACE_CONTENTS( - "run: compare r/m32 with EBX\n" - "run: r/m32 is EAX\n" - "run: SF=1; ZF=0; CF=1; OF=1\n" - ); -} - -void test_compare_r32_with_r32_lesser_signed() { - Reg[EAX].i = 0xffffffff; // -1 - Reg[EBX].i = 0x00000001; // 1 - run( - "== code 0x1\n" // code segment - // op ModR/M SIB displacement immediate - " 39 d8 \n" // compare EAX with EBX - // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX) - ); - CHECK_TRACE_CONTENTS( - "run: compare r/m32 with EBX\n" - "run: r/m32 is EAX\n" - "run: SF=1; ZF=0; CF=0; OF=0\n" - ); -} - -void test_compare_r32_with_r32_lesser_unsigned() { - Reg[EAX].i = 0x00000001; // 1 - Reg[EBX].i = 0xffffffff; // -1 - run( - "== code 0x1\n" // code segment - // op ModR/M SIB displacement immediate - " 39 d8 \n" // compare EAX with EBX - // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX) - ); - CHECK_TRACE_CONTENTS( - "run: compare r/m32 with EBX\n" - "run: r/m32 is EAX\n" - "run: SF=0; ZF=0; CF=1; OF=0\n" - ); -} - -void test_compare_r32_with_r32_equal() { - Reg[EAX].i = 0x0a0b0c0d; - Reg[EBX].i = 0x0a0b0c0d; - run( - "== code 0x1\n" // code segment - // op ModR/M SIB displacement immediate - " 39 d8 \n" // compare EAX and EBX - // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX) - ); - CHECK_TRACE_CONTENTS( - "run: compare r/m32 with EBX\n" - "run: r/m32 is EAX\n" - "run: SF=0; ZF=1; CF=0; OF=0\n" - ); -} - -//:: copy (mov) - -:(before "End Initialize Op Names") -put_new(Name, "89", "copy r32 to rm32 (mov)"); - -:(code) -void test_copy_r32_to_r32() { - Reg[EBX].i = 0xaf; - run( - "== code 0x1\n" // code segment - // op ModR/M SIB displacement immediate - " 89 d8 \n" // copy EBX to EAX - // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX) - ); - CHECK_TRACE_CONTENTS( - "run: copy EBX to r/m32\n" - "run: r/m32 is EAX\n" - "run: storing 0x000000af\n" - ); -} - -:(before "End Single-Byte Opcodes") -case 0x89: { // copy r32 to r/m32 - const uint8_t modrm = next(); - const uint8_t rsrc = (modrm>>3)&0x7; - trace(Callstack_depth+1, "run") << "copy " << rname(rsrc) << " to r/m32" << end(); - int32_t* dest = effective_address(modrm); - *dest = Reg[rsrc].i; - trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *dest << end(); - break; -} - -//:: xchg - -:(before "End Initialize Op Names") -put_new(Name, "87", "swap the contents of r32 and rm32 (xchg)"); - -:(code) -void test_xchg_r32_with_r32() { - Reg[EBX].i = 0xaf; - Reg[EAX].i = 0x2e; - run( - "== code 0x1\n" // code segment - // op ModR/M SIB displacement immediate - " 87 d8 \n" // exchange EBX with EAX - // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX) - ); - CHECK_TRACE_CONTENTS( - "run: exchange EBX with r/m32\n" - "run: r/m32 is EAX\n" - "run: storing 0x000000af in r/m32\n" - "run: storing 0x0000002e in EBX\n" - ); -} - -:(before "End Single-Byte Opcodes") -case 0x87: { // exchange r32 with r/m32 - const uint8_t modrm = next(); - const uint8_t reg2 = (modrm>>3)&0x7; - trace(Callstack_depth+1, "run") << "exchange " << rname(reg2) << " with r/m32" << end(); - int32_t* arg1 = effective_address(modrm); - const int32_t tmp = *arg1; - *arg1 = Reg[reg2].i; - Reg[reg2].i = tmp; - trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *arg1 << " in r/m32" << end(); - trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << Reg[reg2].i << " in " << rname(reg2) << end(); - break; -} - -//:: increment - -:(before "End Initialize Op Names") -put_new(Name, "40", "increment EAX (inc)"); -put_new(Name, "41", "increment ECX (inc)"); -put_new(Name, "42", "increment EDX (inc)"); -put_new(Name, "43", "increment EBX (inc)"); -put_new(Name, "44", "increment ESP (inc)"); -put_new(Name, "45", "increment EBP (inc)"); -put_new(Name, "46", "increment ESI (inc)"); -put_new(Name, "47", "increment EDI (inc)"); - -:(code) -void test_increment_r32() { - Reg[ECX].u = 0x1f; - run( - "== code 0x1\n" // code segment - // op ModR/M SIB displacement immediate - " 41 \n" // increment ECX - ); - CHECK_TRACE_CONTENTS( - "run: increment ECX\n" - "run: storing value 0x00000020\n" - ); -} - -:(before "End Single-Byte Opcodes") -case 0x40: -case 0x41: -case 0x42: -case 0x43: -case 0x44: -case 0x45: -case 0x46: -case 0x47: { // increment r32 - const uint8_t reg = op & 0x7; - trace(Callstack_depth+1, "run") << "increment " << rname(reg) << end(); - ++Reg[reg].u; - trace(Callstack_depth+1, "run") << "storing value 0x" << HEXWORD << Reg[reg].u << end(); - break; -} - -:(before "End Initialize Op Names") -put_new(Name, "ff", "increment/decrement/jump/push/call rm32 based on subop (inc/dec/jmp/push/call)"); - -:(code) -void test_increment_rm32() { - Reg[EAX].u = 0x20; - run( - "== code 0x1\n" // code segment - // op ModR/M SIB displacement immediate - " ff c0 \n" // increment EAX - // ModR/M in binary: 11 (direct mode) 000 (subop inc) 000 (EAX) - ); - CHECK_TRACE_CONTENTS( - "run: increment r/m32\n" - "run: r/m32 is EAX\n" - "run: storing value 0x00000021\n" - ); -} - -:(before "End Single-Byte Opcodes") -case 0xff: { - const uint8_t modrm = next(); - const uint8_t subop = (modrm>>3)&0x7; // middle 3 'reg opcode' bits - switch (subop) { - case 0: { // increment r/m32 - trace(Callstack_depth+1, "run") << "increment r/m32" << end(); - int32_t* arg = effective_address(modrm); - ++*arg; - trace(Callstack_depth+1, "run") << "storing value 0x" << HEXWORD << *arg << end(); - break; - } - default: - cerr << "unrecognized subop for ff: " << HEXBYTE << NUM(subop) << '\n'; - exit(1); - // End Op ff Subops - } - break; -} - -//:: decrement - -:(before "End Initialize Op Names") -put_new(Name, "48", "decrement EAX (dec)"); -put_new(Name, "49", "decrement ECX (dec)"); -put_new(Name, "4a", "decrement EDX (dec)"); -put_new(Name, "4b", "decrement EBX (dec)"); -put_new(Name, "4c", "decrement ESP (dec)"); -put_new(Name, "4d", "decrement EBP (dec)"); -put_new(Name, "4e", "decrement ESI (dec)"); -put_new(Name, "4f", "decrement EDI (dec)"); - -:(code) -void test_decrement_r32() { - Reg[ECX].u = 0x1f; - run( - "== code 0x1\n" // code segment - // op ModR/M SIB displacement immediate - " 49 \n" // decrement ECX - ); - CHECK_TRACE_CONTENTS( - "run: decrement ECX\n" - "run: storing value 0x0000001e\n" - ); -} - -:(before "End Single-Byte Opcodes") -case 0x48: -case 0x49: -case 0x4a: -case 0x4b: -case 0x4c: -case 0x4d: -case 0x4e: -case 0x4f: { // decrement r32 - const uint8_t reg = op & 0x7; - trace(Callstack_depth+1, "run") << "decrement " << rname(reg) << end(); - --Reg[reg].u; - trace(Callstack_depth+1, "run") << "storing value 0x" << HEXWORD << Reg[reg].u << end(); - break; -} - -:(code) -void test_decrement_rm32() { - Reg[EAX].u = 0x20; - run( - "== code 0x1\n" // code segment - // op ModR/M SIB displacement immediate - " ff c8 \n" // decrement EAX - // ModR/M in binary: 11 (direct mode) 001 (subop inc) 000 (EAX) - ); - CHECK_TRACE_CONTENTS( - "run: decrement r/m32\n" - "run: r/m32 is EAX\n" - "run: storing value 0x0000001f\n" - ); -} - -:(before "End Op ff Subops") -case 1: { // decrement r/m32 - trace(Callstack_depth+1, "run") << "decrement r/m32" << end(); - int32_t* arg = effective_address(modrm); - --*arg; - trace(Callstack_depth+1, "run") << "storing value 0x" << HEXWORD << *arg << end(); - break; -} - -//:: push - -:(before "End Initialize Op Names") -put_new(Name, "50", "push EAX to stack (push)"); -put_new(Name, "51", "push ECX to stack (push)"); -put_new(Name, "52", "push EDX to stack (push)"); -put_new(Name, "53", "push EBX to stack (push)"); -put_new(Name, "54", "push ESP to stack (push)"); -put_new(Name, "55", "push EBP to stack (push)"); -put_new(Name, "56", "push ESI to stack (push)"); -put_new(Name, "57", "push EDI to stack (push)"); - -:(code) -void test_push_r32() { - Mem.push_back(vma(0xbd000000)); // manually allocate memory - Reg[ESP].u = 0xbd000008; - Reg[EBX].i = 0x0000000a; - run( - "== code 0x1\n" // code segment - // op ModR/M SIB displacement immediate - " 53 \n" // push EBX to stack - ); - CHECK_TRACE_CONTENTS( - "run: push EBX\n" - "run: decrementing ESP to 0xbd000004\n" - "run: pushing value 0x0000000a\n" - ); -} - -:(before "End Single-Byte Opcodes") -case 0x50: -case 0x51: -case 0x52: -case 0x53: -case 0x54: -case 0x55: -case 0x56: -case 0x57: { // push r32 to stack - uint8_t reg = op & 0x7; - trace(Callstack_depth+1, "run") << "push " << rname(reg) << end(); -//? cerr << "push: " << NUM(reg) << ": " << Reg[reg].u << " => " << Reg[ESP].u << '\n'; - push(Reg[reg].u); - break; -} - -//:: pop - -:(before "End Initialize Op Names") -put_new(Name, "58", "pop top of stack to EAX (pop)"); -put_new(Name, "59", "pop top of stack to ECX (pop)"); -put_new(Name, "5a", "pop top of stack to EDX (pop)"); -put_new(Name, "5b", "pop top of stack to EBX (pop)"); -put_new(Name, "5c", "pop top of stack to ESP (pop)"); -put_new(Name, "5d", "pop top of stack to EBP (pop)"); -put_new(Name, "5e", "pop top of stack to ESI (pop)"); -put_new(Name, "5f", "pop top of stack to EDI (pop)"); - -:(code) -void test_pop_r32() { - Mem.push_back(vma(0xbd000000)); // manually allocate memory - Reg[ESP].u = 0xbd000008; - write_mem_i32(0xbd000008, 0x0000000a); // ..before this write - run( - "== code 0x1\n" // code segment - // op ModR/M SIB displacement immediate - " 5b \n" // pop stack to EBX - "== data 0x2000\n" // data segment - "0a 00 00 00\n" // 0x0000000a - ); - CHECK_TRACE_CONTENTS( - "run: pop into EBX\n" - "run: popping value 0x0000000a\n" - "run: incrementing ESP to 0xbd00000c\n" - ); -} - -:(before "End Single-Byte Opcodes") -case 0x58: -case 0x59: -case 0x5a: -case 0x5b: -case 0x5c: -case 0x5d: -case 0x5e: -case 0x5f: { // pop stack into r32 - const uint8_t reg = op & 0x7; - trace(Callstack_depth+1, "run") << "pop into " << rname(reg) << end(); -//? cerr << "pop from " << Reg[ESP].u << '\n'; - Reg[reg].u = pop(); -//? cerr << "=> " << NUM(reg) << ": " << Reg[reg].u << '\n'; - break; -} -:(code) -uint32_t pop() { - const uint32_t result = read_mem_u32(Reg[ESP].u); - trace(Callstack_depth+1, "run") << "popping value 0x" << HEXWORD << result << end(); - Reg[ESP].u += 4; - trace(Callstack_depth+1, "run") << "incrementing ESP to 0x" << HEXWORD << Reg[ESP].u << end(); - assert(Reg[ESP].u < AFTER_STACK); - return result; -} diff --git a/subx/014indirect_addressing.cc b/subx/014indirect_addressing.cc deleted file mode 100644 index 3767c46d..00000000 --- a/subx/014indirect_addressing.cc +++ /dev/null @@ -1,1000 +0,0 @@ -//: operating on memory at the address provided by some register -//: we'll now start providing data in a separate segment - -void test_add_r32_to_mem_at_r32() { - Reg[EBX].i = 0x10; - Reg[EAX].i = 0x2000; - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " 01 18 \n" // add EBX to *EAX - // ModR/M in binary: 00 (indirect mode) 011 (src EAX) 000 (dest EAX) - "== data 0x2000\n" - "01 00 00 00\n" // 0x00000001 - ); - CHECK_TRACE_CONTENTS( - "run: add EBX to r/m32\n" - "run: effective address is 0x00002000 (EAX)\n" - "run: storing 0x00000011\n" - ); -} - -:(before "End Mod Special-cases(addr)") -case 0: // indirect addressing - switch (rm) { - default: // address in register - trace(Callstack_depth+1, "run") << "effective address is 0x" << HEXWORD << Reg[rm].u << " (" << rname(rm) << ")" << end(); - addr = Reg[rm].u; - break; - // End Mod 0 Special-cases(addr) - } - break; - -//: - -:(before "End Initialize Op Names") -put_new(Name, "03", "add rm32 to r32 (add)"); - -:(code) -void test_add_mem_at_r32_to_r32() { - Reg[EAX].i = 0x2000; - Reg[EBX].i = 0x10; - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " 03 18 \n" // add *EAX to EBX - // ModR/M in binary: 00 (indirect mode) 011 (src EAX) 000 (dest EAX) - "== data 0x2000\n" - "01 00 00 00\n" // 0x00000001 - ); - CHECK_TRACE_CONTENTS( - "run: add r/m32 to EBX\n" - "run: effective address is 0x00002000 (EAX)\n" - "run: storing 0x00000011\n" - ); -} - -:(before "End Single-Byte Opcodes") -case 0x03: { // add r/m32 to r32 - const uint8_t modrm = next(); - const uint8_t arg1 = (modrm>>3)&0x7; - trace(Callstack_depth+1, "run") << "add r/m32 to " << rname(arg1) << end(); - const int32_t* signed_arg2 = effective_address(modrm); - int32_t signed_result = Reg[arg1].i + *signed_arg2; - SF = (signed_result < 0); - ZF = (signed_result == 0); - int64_t signed_full_result = static_cast<int64_t>(Reg[arg1].i) + *signed_arg2; - OF = (signed_result != signed_full_result); - // set CF - uint32_t unsigned_arg2 = static_cast<uint32_t>(*signed_arg2); - uint32_t unsigned_result = Reg[arg1].u + unsigned_arg2; - uint64_t unsigned_full_result = static_cast<uint64_t>(Reg[arg1].u) + unsigned_arg2; - CF = (unsigned_result != unsigned_full_result); - trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); - Reg[arg1].i = signed_result; - trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << Reg[arg1].i << end(); - break; -} - -:(code) -void test_add_mem_at_r32_to_r32_signed_overflow() { - Reg[EAX].i = 0x2000; - Reg[EBX].i = 0x7fffffff; // largest positive signed integer - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " 03 18 \n" // add *EAX to EBX - // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX) - "== data 0x2000\n" - "01 00 00 00\n" // 1 - ); - CHECK_TRACE_CONTENTS( - "run: add r/m32 to EBX\n" - "run: effective address is 0x00002000 (EAX)\n" - "run: effective address contains 1\n" - "run: SF=1; ZF=0; CF=0; OF=1\n" - "run: storing 0x80000000\n" - ); -} - -void test_add_mem_at_r32_to_r32_unsigned_overflow() { - Reg[EAX].u = 0x2000; - Reg[EBX].u = 0xffffffff; // largest unsigned number - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " 03 18 \n" // add *EAX to EBX - // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX) - "== data 0x2000\n" - "01 00 00 00\n" - ); - CHECK_TRACE_CONTENTS( - "run: add r/m32 to EBX\n" - "run: effective address is 0x00002000 (EAX)\n" - "run: effective address contains 1\n" - "run: SF=0; ZF=1; CF=1; OF=0\n" - "run: storing 0x00000000\n" - ); -} - -void test_add_mem_at_r32_to_r32_unsigned_and_signed_overflow() { - Reg[EAX].u = 0x2000; - Reg[EBX].u = 0x80000000; // smallest negative signed integer - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " 03 18 \n" // add *EAX to EBX - // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX) - "== data 0x2000\n" - "00 00 00 80\n" // smallest negative signed integer - ); - CHECK_TRACE_CONTENTS( - "run: add r/m32 to EBX\n" - "run: effective address is 0x00002000 (EAX)\n" - "run: effective address contains 80000000\n" - "run: SF=0; ZF=1; CF=1; OF=1\n" - "run: storing 0x00000000\n" - ); -} - -//:: subtract - -:(code) -void test_subtract_r32_from_mem_at_r32() { - Reg[EAX].i = 0x2000; - Reg[EBX].i = 1; - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " 29 18 \n" // subtract EBX from *EAX - // ModR/M in binary: 00 (indirect mode) 011 (src EAX) 000 (dest EAX) - "== data 0x2000\n" - "0a 00 00 00\n" // 0x0000000a - ); - CHECK_TRACE_CONTENTS( - "run: subtract EBX from r/m32\n" - "run: effective address is 0x00002000 (EAX)\n" - "run: storing 0x00000009\n" - ); -} - -//: - -:(before "End Initialize Op Names") -put_new(Name, "2b", "subtract rm32 from r32 (sub)"); - -:(code) -void test_subtract_mem_at_r32_from_r32() { - Reg[EAX].i = 0x2000; - Reg[EBX].i = 10; - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " 2b 18 \n" // subtract *EAX from EBX - // ModR/M in binary: 00 (indirect mode) 011 (src EAX) 000 (dest EAX) - "== data 0x2000\n" - "01 00 00 00\n" // 0x00000001 - ); - CHECK_TRACE_CONTENTS( - "run: subtract r/m32 from EBX\n" - "run: effective address is 0x00002000 (EAX)\n" - "run: storing 0x00000009\n" - ); -} - -:(before "End Single-Byte Opcodes") -case 0x2b: { // subtract r/m32 from r32 - const uint8_t modrm = next(); - const uint8_t arg1 = (modrm>>3)&0x7; - trace(Callstack_depth+1, "run") << "subtract r/m32 from " << rname(arg1) << end(); - const int32_t* signed_arg2 = effective_address(modrm); - const int32_t signed_result = Reg[arg1].i - *signed_arg2; - SF = (signed_result < 0); - ZF = (signed_result == 0); - int64_t signed_full_result = static_cast<int64_t>(Reg[arg1].i) - *signed_arg2; - OF = (signed_result != signed_full_result); - // set CF - uint32_t unsigned_arg2 = static_cast<uint32_t>(*signed_arg2); - uint32_t unsigned_result = Reg[arg1].u - unsigned_arg2; - uint64_t unsigned_full_result = static_cast<uint64_t>(Reg[arg1].u) - unsigned_arg2; - CF = (unsigned_result != unsigned_full_result); - trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); - Reg[arg1].i = signed_result; - trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << Reg[arg1].i << end(); - break; -} - -:(code) -void test_subtract_mem_at_r32_from_r32_signed_overflow() { - Reg[EAX].i = 0x2000; - Reg[EBX].i = 0x80000000; // smallest negative signed integer - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " 2b 18 \n" // subtract *EAX from EBX - // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX) - "== data 0x2000\n" - "ff ff ff 7f\n" // largest positive signed integer - ); - CHECK_TRACE_CONTENTS( - "run: subtract r/m32 from EBX\n" - "run: effective address is 0x00002000 (EAX)\n" - "run: effective address contains 7fffffff\n" - "run: SF=0; ZF=0; CF=0; OF=1\n" - "run: storing 0x00000001\n" - ); -} - -void test_subtract_mem_at_r32_from_r32_unsigned_overflow() { - Reg[EAX].i = 0x2000; - Reg[EBX].i = 0; - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " 2b 18 \n" // subtract *EAX from EBX - // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX) - "== data 0x2000\n" - "01 00 00 00\n" // 1 - ); - CHECK_TRACE_CONTENTS( - "run: subtract r/m32 from EBX\n" - "run: effective address is 0x00002000 (EAX)\n" - "run: effective address contains 1\n" - "run: SF=1; ZF=0; CF=1; OF=0\n" - "run: storing 0xffffffff\n" - ); -} - -void test_subtract_mem_at_r32_from_r32_signed_and_unsigned_overflow() { - Reg[EAX].i = 0x2000; - Reg[EBX].i = 0; - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " 2b 18 \n" // subtract *EAX from EBX - // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX) - "== data 0x2000\n" - "00 00 00 80\n" // smallest negative signed integer - ); - CHECK_TRACE_CONTENTS( - "run: subtract r/m32 from EBX\n" - "run: effective address is 0x00002000 (EAX)\n" - "run: effective address contains 80000000\n" - "run: SF=1; ZF=0; CF=1; OF=1\n" - "run: storing 0x80000000\n" - ); -} - -//:: and -:(code) -void test_and_r32_with_mem_at_r32() { - Reg[EAX].i = 0x2000; - Reg[EBX].i = 0xff; - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " 21 18 \n" // and EBX with *EAX - // ModR/M in binary: 00 (indirect mode) 011 (src EAX) 000 (dest EAX) - "== data 0x2000\n" - "0d 0c 0b 0a\n" // 0x0a0b0c0d - ); - CHECK_TRACE_CONTENTS( - "run: and EBX with r/m32\n" - "run: effective address is 0x00002000 (EAX)\n" - "run: storing 0x0000000d\n" - ); -} - -//: - -:(before "End Initialize Op Names") -put_new(Name, "23", "r32 = bitwise AND of r32 with rm32 (and)"); - -:(code) -void test_and_mem_at_r32_with_r32() { - Reg[EAX].i = 0x2000; - Reg[EBX].i = 0x0a0b0c0d; - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " 23 18 \n" // and *EAX with EBX - // ModR/M in binary: 00 (indirect mode) 011 (src EAX) 000 (dest EAX) - "== data 0x2000\n" - "ff 00 00 00\n" // 0x000000ff - ); - CHECK_TRACE_CONTENTS( - "run: and r/m32 with EBX\n" - "run: effective address is 0x00002000 (EAX)\n" - "run: storing 0x0000000d\n" - ); -} - -:(before "End Single-Byte Opcodes") -case 0x23: { // and r/m32 with r32 - const uint8_t modrm = next(); - const uint8_t arg1 = (modrm>>3)&0x7; - trace(Callstack_depth+1, "run") << "and r/m32 with " << rname(arg1) << end(); - // bitwise ops technically operate on unsigned numbers, but it makes no - // difference - const int32_t* signed_arg2 = effective_address(modrm); - Reg[arg1].i &= *signed_arg2; - trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << Reg[arg1].i << end(); - SF = (Reg[arg1].i >> 31); - ZF = (Reg[arg1].i == 0); - CF = false; - OF = false; - trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); - break; -} - -//:: or - -:(code) -void test_or_r32_with_mem_at_r32() { - Reg[EAX].i = 0x2000; - Reg[EBX].i = 0xa0b0c0d0; - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " 09 18 #\n" // EBX with *EAX - // ModR/M in binary: 00 (indirect mode) 011 (src EAX) 000 (dest EAX) - "== data 0x2000\n" - "0d 0c 0b 0a\n" // 0x0a0b0c0d - ); - CHECK_TRACE_CONTENTS( - "run: or EBX with r/m32\n" - "run: effective address is 0x00002000 (EAX)\n" - "run: storing 0xaabbccdd\n" - ); -} - -//: - -:(before "End Initialize Op Names") -put_new(Name, "0b", "r32 = bitwise OR of r32 with rm32 (or)"); - -:(code) -void test_or_mem_at_r32_with_r32() { - Reg[EAX].i = 0x2000; - Reg[EBX].i = 0xa0b0c0d0; - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " 0b 18 \n" // or *EAX with EBX - // ModR/M in binary: 00 (indirect mode) 011 (src EAX) 000 (dest EAX) - "== data 0x2000\n" - "0d 0c 0b 0a\n" // 0x0a0b0c0d - ); - CHECK_TRACE_CONTENTS( - "run: or r/m32 with EBX\n" - "run: effective address is 0x00002000 (EAX)\n" - "run: storing 0xaabbccdd\n" - ); -} - -:(before "End Single-Byte Opcodes") -case 0x0b: { // or r/m32 with r32 - const uint8_t modrm = next(); - const uint8_t arg1 = (modrm>>3)&0x7; - trace(Callstack_depth+1, "run") << "or r/m32 with " << rname(arg1) << end(); - // bitwise ops technically operate on unsigned numbers, but it makes no - // difference - const int32_t* signed_arg2 = effective_address(modrm); - Reg[arg1].i |= *signed_arg2; - trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << Reg[arg1].i << end(); - SF = (Reg[arg1].i >> 31); - ZF = (Reg[arg1].i == 0); - CF = false; - OF = false; - trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); - break; -} - -//:: xor - -:(code) -void test_xor_r32_with_mem_at_r32() { - Reg[EAX].i = 0x2000; - Reg[EBX].i = 0xa0b0c0d0; - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " 31 18 \n" // xor EBX with *EAX - "== data 0x2000\n" - "0d 0c bb aa\n" // 0xaabb0c0d - ); - CHECK_TRACE_CONTENTS( - "run: xor EBX with r/m32\n" - "run: effective address is 0x00002000 (EAX)\n" - "run: storing 0x0a0bccdd\n" - ); -} - -//: - -:(before "End Initialize Op Names") -put_new(Name, "33", "r32 = bitwise XOR of r32 with rm32 (xor)"); - -:(code) -void test_xor_mem_at_r32_with_r32() { - Reg[EAX].i = 0x2000; - Reg[EBX].i = 0xa0b0c0d0; - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " 33 18 \n" // xor *EAX with EBX - // ModR/M in binary: 00 (indirect mode) 011 (src EAX) 000 (dest EAX) - "== data 0x2000\n" - "0d 0c 0b 0a\n" // 0x0a0b0c0d - ); - CHECK_TRACE_CONTENTS( - "run: xor r/m32 with EBX\n" - "run: effective address is 0x00002000 (EAX)\n" - "run: storing 0xaabbccdd\n" - ); -} - -:(before "End Single-Byte Opcodes") -case 0x33: { // xor r/m32 with r32 - const uint8_t modrm = next(); - const uint8_t arg1 = (modrm>>3)&0x7; - trace(Callstack_depth+1, "run") << "xor r/m32 with " << rname(arg1) << end(); - // bitwise ops technically operate on unsigned numbers, but it makes no - // difference - const int32_t* signed_arg2 = effective_address(modrm); - Reg[arg1].i |= *signed_arg2; - trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << Reg[arg1].i << end(); - SF = (Reg[arg1].i >> 31); - ZF = (Reg[arg1].i == 0); - CF = false; - OF = false; - trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); - break; -} - -//:: not - -:(code) -void test_not_of_mem_at_r32() { - Reg[EBX].i = 0x2000; - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " f7 13 \n" // not *EBX - // ModR/M in binary: 00 (indirect mode) 010 (subop not) 011 (dest EBX) - "== data 0x2000\n" - "ff 00 0f 0f\n" // 0x0f0f00ff - ); - CHECK_TRACE_CONTENTS( - "run: operate on r/m32\n" - "run: effective address is 0x00002000 (EBX)\n" - "run: subop: not\n" - "run: storing 0xf0f0ff00\n" - ); -} - -//:: compare (cmp) - -:(code) -void test_compare_mem_at_r32_with_r32_greater() { - Reg[EAX].i = 0x2000; - Reg[EBX].i = 0x0a0b0c07; - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " 39 18 \n" // compare *EAX with EBX - // ModR/M in binary: 00 (indirect mode) 011 (src EAX) 000 (dest EAX) - "== data 0x2000\n" - "0d 0c 0b 0a\n" // 0x0a0b0c0d - ); - CHECK_TRACE_CONTENTS( - "run: compare r/m32 with EBX\n" - "run: effective address is 0x00002000 (EAX)\n" - "run: SF=0; ZF=0; CF=0; OF=0\n" - ); -} - -:(code) -void test_compare_mem_at_r32_with_r32_lesser() { - Reg[EAX].i = 0x2000; - Reg[EBX].i = 0x0a0b0c0d; - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " 39 18 \n" // compare *EAX with EBX - // ModR/M in binary: 00 (indirect mode) 011 (src EAX) 000 (dest EAX) - "== data 0x2000\n" - "07 0c 0b 0a\n" // 0x0a0b0c0d - ); - CHECK_TRACE_CONTENTS( - "run: compare r/m32 with EBX\n" - "run: effective address is 0x00002000 (EAX)\n" - "run: SF=1; ZF=0; CF=1; OF=0\n" - ); -} - -:(code) -void test_compare_mem_at_r32_with_r32_equal() { - Reg[EAX].i = 0x2000; - Reg[EBX].i = 0x0a0b0c0d; - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " 39 18 \n" // compare *EAX and EBX - // ModR/M in binary: 00 (indirect mode) 011 (src EAX) 000 (dest EAX) - "== data 0x2000\n" - "0d 0c 0b 0a\n" // 0x0a0b0c0d - ); - CHECK_TRACE_CONTENTS( - "run: compare r/m32 with EBX\n" - "run: effective address is 0x00002000 (EAX)\n" - "run: SF=0; ZF=1; CF=0; OF=0\n" - ); -} - -//: - -:(before "End Initialize Op Names") -put_new(Name, "3b", "compare: set SF if r32 < rm32 (cmp)"); - -:(code) -void test_compare_r32_with_mem_at_r32_greater() { - Reg[EAX].i = 0x2000; - Reg[EBX].i = 0x0a0b0c0d; - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " 3b 18 \n" // compare EBX with *EAX - // ModR/M in binary: 00 (indirect mode) 011 (src EAX) 000 (dest EAX) - "== data 0x2000\n" - "07 0c 0b 0a\n" // 0x0a0b0c07 - ); - CHECK_TRACE_CONTENTS( - "run: compare EBX with r/m32\n" - "run: effective address is 0x00002000 (EAX)\n" - "run: SF=0; ZF=0; CF=0; OF=0\n" - ); -} - -:(before "End Single-Byte Opcodes") -case 0x3b: { // set SF if r32 < r/m32 - const uint8_t modrm = next(); - const uint8_t reg1 = (modrm>>3)&0x7; - trace(Callstack_depth+1, "run") << "compare " << rname(reg1) << " with r/m32" << end(); - const int32_t* signed_arg2 = effective_address(modrm); - const int32_t signed_difference = Reg[reg1].i - *signed_arg2; - SF = (signed_difference < 0); - ZF = (signed_difference == 0); - int64_t full_signed_difference = static_cast<int64_t>(Reg[reg1].i) - *signed_arg2; - OF = (signed_difference != full_signed_difference); - const uint32_t unsigned_arg2 = static_cast<uint32_t>(*signed_arg2); - const uint32_t unsigned_difference = Reg[reg1].u - unsigned_arg2; - const uint64_t full_unsigned_difference = static_cast<uint64_t>(Reg[reg1].u) - unsigned_arg2; - CF = (unsigned_difference != full_unsigned_difference); - trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); - break; -} - -:(code) -void test_compare_r32_with_mem_at_r32_lesser_unsigned_and_signed() { - Reg[EAX].i = 0x2000; - Reg[EBX].i = 0x0a0b0c07; - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " 3b 18 \n" // compare EBX with *EAX - // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX) - "== data 0x2000\n" - "0d 0c 0b 0a\n" // 0x0a0b0c0d - ); - CHECK_TRACE_CONTENTS( - "run: compare EBX with r/m32\n" - "run: effective address is 0x00002000 (EAX)\n" - "run: effective address contains a0b0c0d\n" - "run: SF=1; ZF=0; CF=1; OF=0\n" - ); -} - -void test_compare_r32_with_mem_at_r32_lesser_unsigned_and_signed_due_to_overflow() { - Reg[EAX].i = 0x2000; - Reg[EBX].i = 0x7fffffff; // largest positive signed integer - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " 3b 18 \n" // compare EBX with *EAX - // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX) - "== data 0x2000\n" - "00 00 00 80\n" // smallest negative signed integer - ); - CHECK_TRACE_CONTENTS( - "run: compare EBX with r/m32\n" - "run: effective address is 0x00002000 (EAX)\n" - "run: effective address contains 80000000\n" - "run: SF=1; ZF=0; CF=1; OF=1\n" - ); -} - -void test_compare_r32_with_mem_at_r32_lesser_signed() { - Reg[EAX].i = 0x2000; - Reg[EBX].i = 0xffffffff; // -1 - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " 3b 18 \n" // compare EBX with *EAX - // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX) - "== data 0x2000\n" - "01 00 00 00\n" // 1 - ); - CHECK_TRACE_CONTENTS( - "run: compare EBX with r/m32\n" - "run: effective address is 0x00002000 (EAX)\n" - "run: effective address contains 1\n" - "run: SF=1; ZF=0; CF=0; OF=0\n" - ); -} - -void test_compare_r32_with_mem_at_r32_lesser_unsigned() { - Reg[EAX].i = 0x2000; - Reg[EBX].i = 0x00000001; // 1 - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " 3b 18 \n" // compare EBX with *EAX - // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX) - "== data 0x2000\n" - "ff ff ff ff\n" // -1 - ); - CHECK_TRACE_CONTENTS( - "run: compare EBX with r/m32\n" - "run: effective address is 0x00002000 (EAX)\n" - "run: effective address contains ffffffff\n" - "run: SF=0; ZF=0; CF=1; OF=0\n" - ); -} - -void test_compare_r32_with_mem_at_r32_equal() { - Reg[EAX].i = 0x2000; - Reg[EBX].i = 0x0a0b0c0d; - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " 3b 18 \n" // compare EBX with *EAX - // ModR/M in binary: 00 (indirect mode) 011 (src EAX) 000 (dest EAX) - "== data 0x2000\n" - "0d 0c 0b 0a\n" // 0x0a0b0c0d - ); - CHECK_TRACE_CONTENTS( - "run: compare EBX with r/m32\n" - "run: effective address is 0x00002000 (EAX)\n" - "run: SF=0; ZF=1; CF=0; OF=0\n" - ); -} - -//:: copy (mov) - -void test_copy_r32_to_mem_at_r32() { - Reg[EBX].i = 0xaf; - Reg[EAX].i = 0x60; - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " 89 18 \n" // copy EBX to *EAX - // ModR/M in binary: 00 (indirect mode) 011 (src EAX) 000 (dest EAX) - ); - CHECK_TRACE_CONTENTS( - "run: copy EBX to r/m32\n" - "run: effective address is 0x00000060 (EAX)\n" - "run: storing 0x000000af\n" - ); -} - -//: - -:(before "End Initialize Op Names") -put_new(Name, "8b", "copy rm32 to r32 (mov)"); - -:(code) -void test_copy_mem_at_r32_to_r32() { - Reg[EAX].i = 0x2000; - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " 8b 18 \n" // copy *EAX to EBX - "== data 0x2000\n" - "af 00 00 00\n" // 0x000000af - ); - CHECK_TRACE_CONTENTS( - "run: copy r/m32 to EBX\n" - "run: effective address is 0x00002000 (EAX)\n" - "run: storing 0x000000af\n" - ); -} - -:(before "End Single-Byte Opcodes") -case 0x8b: { // copy r32 to r/m32 - const uint8_t modrm = next(); - const uint8_t rdest = (modrm>>3)&0x7; - trace(Callstack_depth+1, "run") << "copy r/m32 to " << rname(rdest) << end(); - const int32_t* src = effective_address(modrm); - Reg[rdest].i = *src; - trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *src << end(); - break; -} - -//:: jump - -:(code) -void test_jump_mem_at_r32() { - Reg[EAX].i = 0x2000; - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " ff 20 \n" // jump to *EAX - // ModR/M in binary: 00 (indirect mode) 100 (jump to r/m32) 000 (src EAX) - " b8 00 00 00 01\n" - " b8 00 00 00 02\n" - "== data 0x2000\n" - "08 00 00 00\n" // 0x00000008 - ); - CHECK_TRACE_CONTENTS( - "run: 0x00000001 opcode: ff\n" - "run: jump to r/m32\n" - "run: effective address is 0x00002000 (EAX)\n" - "run: jumping to 0x00000008\n" - "run: 0x00000008 opcode: b8\n" - ); - CHECK_TRACE_DOESNT_CONTAIN("run: 0x00000003 opcode: b8"); -} - -:(before "End Op ff Subops") -case 4: { // jump to r/m32 - trace(Callstack_depth+1, "run") << "jump to r/m32" << end(); - const int32_t* arg2 = effective_address(modrm); - EIP = *arg2; - trace(Callstack_depth+1, "run") << "jumping to 0x" << HEXWORD << EIP << end(); - break; -} - -//:: push - -:(code) -void test_push_mem_at_r32() { - Reg[EAX].i = 0x2000; - Mem.push_back(vma(0xbd000000)); // manually allocate memory - Reg[ESP].u = 0xbd000014; - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " ff 30 \n" // push *EAX to stack - "== data 0x2000\n" - "af 00 00 00\n" // 0x000000af - ); - CHECK_TRACE_CONTENTS( - "run: push r/m32\n" - "run: effective address is 0x00002000 (EAX)\n" - "run: decrementing ESP to 0xbd000010\n" - "run: pushing value 0x000000af\n" - ); -} - -:(before "End Op ff Subops") -case 6: { // push r/m32 to stack - trace(Callstack_depth+1, "run") << "push r/m32" << end(); - const int32_t* val = effective_address(modrm); - push(*val); - break; -} - -//:: pop - -:(before "End Initialize Op Names") -put_new(Name, "8f", "pop top of stack to rm32 (pop)"); - -:(code) -void test_pop_mem_at_r32() { - Reg[EAX].i = 0x60; - Mem.push_back(vma(0xbd000000)); // manually allocate memory - Reg[ESP].u = 0xbd000000; - write_mem_i32(0xbd000000, 0x00000030); - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " 8f 00 \n" // pop stack into *EAX - // ModR/M in binary: 00 (indirect mode) 000 (pop r/m32) 000 (dest EAX) - ); - CHECK_TRACE_CONTENTS( - "run: pop into r/m32\n" - "run: effective address is 0x00000060 (EAX)\n" - "run: popping value 0x00000030\n" - "run: incrementing ESP to 0xbd000004\n" - ); -} - -:(before "End Single-Byte Opcodes") -case 0x8f: { // pop stack into r/m32 - const uint8_t modrm = next(); - const uint8_t subop = (modrm>>3)&0x7; - switch (subop) { - case 0: { - trace(Callstack_depth+1, "run") << "pop into r/m32" << end(); - int32_t* dest = effective_address(modrm); - *dest = pop(); - break; - } - } - break; -} - -//:: special-case for loading address from disp32 rather than register - -:(code) -void test_add_r32_to_mem_at_displacement() { - Reg[EBX].i = 0x10; // source - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " 01 1d 00 20 00 00 \n" // add EBX to *0x2000 - // ModR/M in binary: 00 (indirect mode) 011 (src EBX) 101 (dest in disp32) - "== data 0x2000\n" - "01 00 00 00\n" // 0x00000001 - ); - CHECK_TRACE_CONTENTS( - "run: add EBX to r/m32\n" - "run: effective address is 0x00002000 (disp32)\n" - "run: storing 0x00000011\n" - ); -} - -:(before "End Mod 0 Special-cases(addr)") -case 5: // exception: mod 0b00 rm 0b101 => incoming disp32 - addr = next32(); - trace(Callstack_depth+1, "run") << "effective address is 0x" << HEXWORD << addr << " (disp32)" << end(); - break; - -//: - -:(code) -void test_add_r32_to_mem_at_r32_plus_disp8() { - Reg[EBX].i = 0x10; // source - Reg[EAX].i = 0x1ffe; // dest - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " 01 58 02 \n" // add EBX to *(EAX+2) - // ModR/M in binary: 01 (indirect+disp8 mode) 011 (src EBX) 000 (dest EAX) - "== data 0x2000\n" - "01 00 00 00\n" // 0x00000001 - ); - CHECK_TRACE_CONTENTS( - "run: add EBX to r/m32\n" - "run: effective address is initially 0x00001ffe (EAX)\n" - "run: effective address is 0x00002000 (after adding disp8)\n" - "run: storing 0x00000011\n" - ); -} - -:(before "End Mod Special-cases(addr)") -case 1: { // indirect + disp8 addressing - switch (rm) { - default: - addr = Reg[rm].u; - trace(Callstack_depth+1, "run") << "effective address is initially 0x" << HEXWORD << addr << " (" << rname(rm) << ")" << end(); - break; - // End Mod 1 Special-cases(addr) - } - int8_t displacement = static_cast<int8_t>(next()); - if (addr > 0) { - addr += displacement; - trace(Callstack_depth+1, "run") << "effective address is 0x" << HEXWORD << addr << " (after adding disp8)" << end(); - } - else { - trace(Callstack_depth+1, "run") << "null address; skipping displacement" << end(); - } - break; -} - -:(code) -void test_add_r32_to_mem_at_r32_plus_negative_disp8() { - Reg[EBX].i = 0x10; // source - Reg[EAX].i = 0x2001; // dest - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " 01 58 ff \n" // add EBX to *(EAX-1) - // ModR/M in binary: 01 (indirect+disp8 mode) 011 (src EBX) 000 (dest EAX) - "== data 0x2000\n" - "01 00 00 00\n" // 0x00000001 - ); - CHECK_TRACE_CONTENTS( - "run: add EBX to r/m32\n" - "run: effective address is initially 0x00002001 (EAX)\n" - "run: effective address is 0x00002000 (after adding disp8)\n" - "run: storing 0x00000011\n" - ); -} - -//: - -:(code) -void test_add_r32_to_mem_at_r32_plus_disp32() { - Reg[EBX].i = 0x10; // source - Reg[EAX].i = 0x1ffe; // dest - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " 01 98 02 00 00 00 \n" // add EBX to *(EAX+2) - // ModR/M in binary: 10 (indirect+disp32 mode) 011 (src EBX) 000 (dest EAX) - "== data 0x2000\n" - "01 00 00 00\n" // 0x00000001 - ); - CHECK_TRACE_CONTENTS( - "run: add EBX to r/m32\n" - "run: effective address is initially 0x00001ffe (EAX)\n" - "run: effective address is 0x00002000 (after adding disp32)\n" - "run: storing 0x00000011\n" - ); -} - -:(before "End Mod Special-cases(addr)") -case 2: // indirect + disp32 addressing - switch (rm) { - default: - addr = Reg[rm].u; - trace(Callstack_depth+1, "run") << "effective address is initially 0x" << HEXWORD << addr << " (" << rname(rm) << ")" << end(); - break; - // End Mod 2 Special-cases(addr) - } - if (addr > 0) { - addr += next32(); - trace(Callstack_depth+1, "run") << "effective address is 0x" << HEXWORD << addr << " (after adding disp32)" << end(); - } - break; - -:(code) -void test_add_r32_to_mem_at_r32_plus_negative_disp32() { - Reg[EBX].i = 0x10; // source - Reg[EAX].i = 0x2001; // dest - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " 01 98 ff ff ff ff \n" // add EBX to *(EAX-1) - // ModR/M in binary: 10 (indirect+disp32 mode) 011 (src EBX) 000 (dest EAX) - "== data 0x2000\n" - "01 00 00 00\n" // 0x00000001 - ); - CHECK_TRACE_CONTENTS( - "run: add EBX to r/m32\n" - "run: effective address is initially 0x00002001 (EAX)\n" - "run: effective address is 0x00002000 (after adding disp32)\n" - "run: storing 0x00000011\n" - ); -} - -//:: copy address (lea) - -:(before "End Initialize Op Names") -put_new(Name, "8d", "copy address in rm32 into r32 (lea)"); - -:(code) -void test_copy_address() { - Reg[EAX].u = 0x2000; - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " 8d 18 \n" // copy address in EAX into EBX - // ModR/M in binary: 00 (indirect mode) 011 (dest EBX) 000 (src EAX) - ); - CHECK_TRACE_CONTENTS( - "run: copy address into EBX\n" - "run: effective address is 0x00002000 (EAX)\n" - ); -} - -:(before "End Single-Byte Opcodes") -case 0x8d: { // copy address of m32 to r32 - const uint8_t modrm = next(); - const uint8_t arg1 = (modrm>>3)&0x7; - trace(Callstack_depth+1, "run") << "copy address into " << rname(arg1) << end(); - Reg[arg1].u = effective_address_number(modrm); - break; -} diff --git a/subx/015immediate_addressing.cc b/subx/015immediate_addressing.cc deleted file mode 100644 index 782a16b9..00000000 --- a/subx/015immediate_addressing.cc +++ /dev/null @@ -1,1272 +0,0 @@ -//: instructions that (immediately) contain an argument to act with - -:(before "End Initialize Op Names") -put_new(Name, "05", "add imm32 to EAX (add)"); - -:(before "End Single-Byte Opcodes") -case 0x05: { // add imm32 to EAX - int32_t signed_arg2 = next32(); - trace(Callstack_depth+1, "run") << "add imm32 0x" << HEXWORD << signed_arg2 << " to EAX" << end(); - int32_t signed_result = Reg[EAX].i + signed_arg2; - SF = (signed_result < 0); - ZF = (signed_result == 0); - int64_t signed_full_result = static_cast<int64_t>(Reg[EAX].i) + signed_arg2; - OF = (signed_result != signed_full_result); - // set CF - uint32_t unsigned_arg2 = static_cast<uint32_t>(signed_arg2); - uint32_t unsigned_result = Reg[EAX].u + unsigned_arg2; - uint64_t unsigned_full_result = static_cast<uint64_t>(Reg[EAX].u) + unsigned_arg2; - CF = (unsigned_result != unsigned_full_result); - trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); - Reg[EAX].i = signed_result; - trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << Reg[EAX].i << end(); - break; -} - -:(code) -void test_add_imm32_to_EAX_signed_overflow() { - Reg[EAX].i = 0x7fffffff; // largest positive signed integer - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " 05 01 00 00 00 \n" // add 1 to EAX - ); - CHECK_TRACE_CONTENTS( - "run: add imm32 0x00000001 to EAX\n" - "run: SF=1; ZF=0; CF=0; OF=1\n" - "run: storing 0x80000000\n" - ); -} - -void test_add_imm32_to_EAX_unsigned_overflow() { - Reg[EAX].u = 0xffffffff; // largest unsigned number - Reg[EBX].u = 1; - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " 05 01 00 00 00 \n" // add 1 to EAX - ); - CHECK_TRACE_CONTENTS( - "run: add imm32 0x00000001 to EAX\n" - "run: SF=0; ZF=1; CF=1; OF=0\n" - "run: storing 0x00000000\n" - ); -} - -void test_add_imm32_to_EAX_unsigned_and_signed_overflow() { - Reg[EAX].u = 0x80000000; // smallest negative signed integer - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " 05 00 00 00 80 \n" // add 0x80000000 to EAX - ); - CHECK_TRACE_CONTENTS( - "run: add imm32 0x80000000 to EAX\n" - "run: SF=0; ZF=1; CF=1; OF=1\n" - "run: storing 0x00000000\n" - ); -} - -//: - -:(before "End Initialize Op Names") -put_new(Name, "81", "combine rm32 with imm32 based on subop (add/sub/and/or/xor/cmp)"); - -:(code) -void test_add_imm32_to_r32() { - Reg[EBX].i = 1; - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " 81 c3 0a 0b 0c 0d\n" // add 0x0d0c0b0a to EBX - // ModR/M in binary: 11 (direct mode) 000 (subop add) 011 (dest EBX) - ); - CHECK_TRACE_CONTENTS( - "run: combine r/m32 with imm32\n" - "run: r/m32 is EBX\n" - "run: imm32 is 0x0d0c0b0a\n" - "run: subop add\n" - "run: storing 0x0d0c0b0b\n" - ); -} - -:(before "End Single-Byte Opcodes") -case 0x81: { // combine r/m32 with imm32 - trace(Callstack_depth+1, "run") << "combine r/m32 with imm32" << end(); - const uint8_t modrm = next(); - int32_t* signed_arg1 = effective_address(modrm); - const int32_t signed_arg2 = next32(); - trace(Callstack_depth+1, "run") << "imm32 is 0x" << HEXWORD << signed_arg2 << end(); - const uint8_t subop = (modrm>>3)&0x7; // middle 3 'reg opcode' bits - switch (subop) { - case 0: { - trace(Callstack_depth+1, "run") << "subop add" << end(); - int32_t signed_result = *signed_arg1 + signed_arg2; - SF = (signed_result < 0); - ZF = (signed_result == 0); - int64_t signed_full_result = static_cast<int64_t>(*signed_arg1) + signed_arg2; - OF = (signed_result != signed_full_result); - // set CF - uint32_t unsigned_arg1 = static_cast<uint32_t>(*signed_arg1); - uint32_t unsigned_arg2 = static_cast<uint32_t>(signed_arg2); - uint32_t unsigned_result = unsigned_arg1 + unsigned_arg2; - uint64_t unsigned_full_result = static_cast<uint64_t>(unsigned_arg1) + unsigned_arg2; - CF = (unsigned_result != unsigned_full_result); - trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); - *signed_arg1 = signed_result; - trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *signed_arg1 << end(); - break; - } - // End Op 81 Subops - default: - cerr << "unrecognized subop for opcode 81: " << NUM(subop) << '\n'; - exit(1); - } - break; -} - -:(code) -void test_add_imm32_to_r32_signed_overflow() { - Reg[EBX].i = 0x7fffffff; // largest positive signed integer - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " 81 c3 01 00 00 00\n" // add 1 to EBX - // ModR/M in binary: 11 (direct mode) 000 (subop add) 011 (dest EBX) - ); - CHECK_TRACE_CONTENTS( - "run: combine r/m32 with imm32\n" - "run: r/m32 is EBX\n" - "run: imm32 is 0x00000001\n" - "run: subop add\n" - "run: SF=1; ZF=0; CF=0; OF=1\n" - "run: storing 0x80000000\n" - ); -} - -void test_add_imm32_to_r32_unsigned_overflow() { - Reg[EBX].u = 0xffffffff; // largest unsigned number - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " 81 c3 01 00 00 00\n" // add 1 to EBX - // ModR/M in binary: 11 (direct mode) 011 (subop add) 011 (dest EBX) - ); - CHECK_TRACE_CONTENTS( - "run: combine r/m32 with imm32\n" - "run: r/m32 is EBX\n" - "run: imm32 is 0x00000001\n" - "run: subop add\n" - "run: SF=0; ZF=1; CF=1; OF=0\n" - "run: storing 0x00000000\n" - ); -} - -void test_add_imm32_to_r32_unsigned_and_signed_overflow() { - Reg[EBX].u = 0x80000000; // smallest negative signed integer - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " 81 c3 00 00 00 80\n" // add 0x80000000 to EBX - // ModR/M in binary: 11 (direct mode) 011 (subop add) 011 (dest EBX) - ); - CHECK_TRACE_CONTENTS( - "run: combine r/m32 with imm32\n" - "run: r/m32 is EBX\n" - "run: imm32 is 0x80000000\n" - "run: subop add\n" - "run: SF=0; ZF=1; CF=1; OF=1\n" - "run: storing 0x00000000\n" - ); -} - -//: - -:(code) -void test_add_imm32_to_mem_at_r32() { - Reg[EBX].i = 0x2000; - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " 81 03 0a 0b 0c 0d \n" // add 0x0d0c0b0a to *EBX - // ModR/M in binary: 00 (indirect mode) 000 (subop add) 011 (dest EBX) - "== data 0x2000\n" - "01 00 00 00\n" // 0x00000001 - ); - CHECK_TRACE_CONTENTS( - "run: combine r/m32 with imm32\n" - "run: effective address is 0x00002000 (EBX)\n" - "run: imm32 is 0x0d0c0b0a\n" - "run: subop add\n" - "run: storing 0x0d0c0b0b\n" - ); -} - -//:: subtract - -:(before "End Initialize Op Names") -put_new(Name, "2d", "subtract imm32 from EAX (sub)"); - -:(code) -void test_subtract_imm32_from_EAX() { - Reg[EAX].i = 0x0d0c0baa; - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " 2d 0a 0b 0c 0d \n" // subtract 0x0d0c0b0a from EAX - ); - CHECK_TRACE_CONTENTS( - "run: subtract imm32 0x0d0c0b0a from EAX\n" - "run: storing 0x000000a0\n" - ); -} - -:(before "End Single-Byte Opcodes") -case 0x2d: { // subtract imm32 from EAX - const int32_t signed_arg2 = next32(); - trace(Callstack_depth+1, "run") << "subtract imm32 0x" << HEXWORD << signed_arg2 << " from EAX" << end(); - int32_t signed_result = Reg[EAX].i - signed_arg2; - SF = (signed_result < 0); - ZF = (signed_result == 0); - int64_t signed_full_result = static_cast<int64_t>(Reg[EAX].i) - signed_arg2; - OF = (signed_result != signed_full_result); - // set CF - uint32_t unsigned_arg2 = static_cast<uint32_t>(signed_arg2); - uint32_t unsigned_result = Reg[EAX].u - unsigned_arg2; - uint64_t unsigned_full_result = static_cast<uint64_t>(Reg[EAX].u) - unsigned_arg2; - CF = (unsigned_result != unsigned_full_result); - trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); - Reg[EAX].i = signed_result; - trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << Reg[EAX].i << end(); - break; -} - -:(code) -void test_subtract_imm32_from_EAX_signed_overflow() { - Reg[EAX].i = 0x80000000; // smallest negative signed integer - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " 2d ff ff ff 7f \n" // subtract largest positive signed integer from EAX - ); - CHECK_TRACE_CONTENTS( - "run: subtract imm32 0x7fffffff from EAX\n" - "run: SF=0; ZF=0; CF=0; OF=1\n" - "run: storing 0x00000001\n" - ); -} - -void test_subtract_imm32_from_EAX_unsigned_overflow() { - Reg[EAX].i = 0; - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " 2d 01 00 00 00 \n" // subtract 1 from EAX - ); - CHECK_TRACE_CONTENTS( - "run: subtract imm32 0x00000001 from EAX\n" - "run: SF=1; ZF=0; CF=1; OF=0\n" - "run: storing 0xffffffff\n" - ); -} - -void test_subtract_imm32_from_EAX_signed_and_unsigned_overflow() { - Reg[EAX].i = 0; - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " 2d 00 00 00 80 \n" // subtract smallest negative signed integer from EAX - ); - CHECK_TRACE_CONTENTS( - "run: subtract imm32 0x80000000 from EAX\n" - "run: SF=1; ZF=0; CF=1; OF=1\n" - "run: storing 0x80000000\n" - ); -} - -//: - -void test_subtract_imm32_from_mem_at_r32() { - Reg[EBX].i = 0x2000; - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " 81 2b 01 00 00 00 \n" // subtract 1 from *EBX - // ModR/M in binary: 00 (indirect mode) 101 (subop subtract) 011 (dest EBX) - "== data 0x2000\n" - "0a 00 00 00\n" // 0x0000000a - ); - CHECK_TRACE_CONTENTS( - "run: combine r/m32 with imm32\n" - "run: effective address is 0x00002000 (EBX)\n" - "run: imm32 is 0x00000001\n" - "run: subop subtract\n" - "run: storing 0x00000009\n" - ); -} - -:(before "End Op 81 Subops") -case 5: { - trace(Callstack_depth+1, "run") << "subop subtract" << end(); - int32_t signed_result = *signed_arg1 - signed_arg2; - SF = (signed_result < 0); - ZF = (signed_result == 0); - int64_t signed_full_result = static_cast<int64_t>(*signed_arg1) - signed_arg2; - OF = (signed_result != signed_full_result); - // set CF - uint32_t unsigned_arg1 = static_cast<uint32_t>(*signed_arg1); - uint32_t unsigned_arg2 = static_cast<uint32_t>(signed_arg2); - uint32_t unsigned_result = unsigned_arg1 - unsigned_arg2; - uint64_t unsigned_full_result = static_cast<uint64_t>(unsigned_arg1) - unsigned_arg2; - CF = (unsigned_result != unsigned_full_result); - trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); - *signed_arg1 = signed_result; - trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *signed_arg1 << end(); - break; -} - -:(code) -void test_subtract_imm32_from_mem_at_r32_signed_overflow() { - Reg[EBX].i = 0x2000; - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " 81 2b ff ff ff 7f \n" // subtract largest positive signed integer from *EBX - // ModR/M in binary: 00 (indirect mode) 101 (subop subtract) 011 (dest EBX) - "== data 0x2000\n" - "00 00 00 80\n" // smallest negative signed integer - ); - CHECK_TRACE_CONTENTS( - "run: combine r/m32 with imm32\n" - "run: effective address is 0x00002000 (EBX)\n" - "run: effective address contains 80000000\n" - "run: imm32 is 0x7fffffff\n" - "run: subop subtract\n" - "run: SF=0; ZF=0; CF=0; OF=1\n" - "run: storing 0x00000001\n" - ); -} - -void test_subtract_imm32_from_mem_at_r32_unsigned_overflow() { - Reg[EBX].i = 0x2000; - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " 81 2b 01 00 00 00 \n" // subtract 1 from *EBX - // ModR/M in binary: 00 (indirect mode) 101 (subop subtract) 011 (dest EBX) - "== data 0x2000\n" - "00 00 00 00\n" // 0 - ); - CHECK_TRACE_CONTENTS( - "run: combine r/m32 with imm32\n" - "run: effective address is 0x00002000 (EBX)\n" - "run: effective address contains 0\n" - "run: imm32 is 0x00000001\n" - "run: subop subtract\n" - "run: SF=1; ZF=0; CF=1; OF=0\n" - "run: storing 0xffffffff\n" - ); -} - -void test_subtract_imm32_from_mem_at_r32_signed_and_unsigned_overflow() { - Reg[EBX].i = 0x2000; - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " 81 2b 00 00 00 80 \n" // subtract smallest negative signed integer from *EBX - // ModR/M in binary: 00 (indirect mode) 101 (subop subtract) 011 (dest EBX) - "== data 0x2000\n" - "00 00 00 00\n" // 0 - ); - CHECK_TRACE_CONTENTS( - "run: combine r/m32 with imm32\n" - "run: effective address is 0x00002000 (EBX)\n" - "run: effective address contains 0\n" - "run: imm32 is 0x80000000\n" - "run: subop subtract\n" - "run: SF=1; ZF=0; CF=1; OF=1\n" - "run: storing 0x80000000\n" - ); -} - -//: - -void test_subtract_imm32_from_r32() { - Reg[EBX].i = 10; - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " 81 eb 01 00 00 00 \n" // subtract 1 from EBX - // ModR/M in binary: 11 (direct mode) 101 (subop subtract) 011 (dest EBX) - ); - CHECK_TRACE_CONTENTS( - "run: combine r/m32 with imm32\n" - "run: r/m32 is EBX\n" - "run: imm32 is 0x00000001\n" - "run: subop subtract\n" - "run: storing 0x00000009\n" - ); -} - -//:: shift left - -:(before "End Initialize Op Names") -put_new(Name, "c1", "shift rm32 by imm8 bits depending on subop (sal/sar/shl/shr)"); - -:(code) -void test_shift_left_r32_with_imm8() { - Reg[EBX].i = 13; - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " c1 e3 01 \n" // shift EBX left by 1 bit - // ModR/M in binary: 11 (direct mode) 100 (subop shift left) 011 (dest EBX) - ); - CHECK_TRACE_CONTENTS( - "run: operate on r/m32\n" - "run: r/m32 is EBX\n" - "run: subop: shift left by CL bits\n" - "run: storing 0x0000001a\n" - ); -} - -:(before "End Single-Byte Opcodes") -case 0xc1: { - const uint8_t modrm = next(); - trace(Callstack_depth+1, "run") << "operate on r/m32" << end(); - int32_t* arg1 = effective_address(modrm); - const uint8_t subop = (modrm>>3)&0x7; // middle 3 'reg opcode' bits - switch (subop) { - case 4: { // shift left r/m32 by CL - trace(Callstack_depth+1, "run") << "subop: shift left by CL bits" << end(); - uint8_t count = next() & 0x1f; - // OF is only defined if count is 1 - if (count == 1) { - bool msb = (*arg1 & 0x80000000) >> 1; - bool pnsb = (*arg1 & 0x40000000); - OF = (msb != pnsb); - } - *arg1 = (*arg1 << count); - ZF = (*arg1 == 0); - SF = (*arg1 < 0); - // CF undefined - trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); - trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *arg1 << end(); - break; - } - // End Op c1 Subops - default: - cerr << "unrecognized subop for opcode c1: " << NUM(subop) << '\n'; - exit(1); - } - break; -} - -//:: shift right arithmetic - -:(code) -void test_shift_right_arithmetic_r32_with_imm8() { - Reg[EBX].i = 26; - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " c1 fb 01 \n" // shift EBX right by 1 bit - // ModR/M in binary: 11 (direct mode) 111 (subop shift right arithmetic) 011 (dest EBX) - ); - CHECK_TRACE_CONTENTS( - "run: operate on r/m32\n" - "run: r/m32 is EBX\n" - "run: subop: shift right by CL bits, while preserving sign\n" - "run: storing 0x0000000d\n" - ); -} - -:(before "End Op c1 Subops") -case 7: { // shift right r/m32 by CL, preserving sign - trace(Callstack_depth+1, "run") << "subop: shift right by CL bits, while preserving sign" << end(); - uint8_t count = next() & 0x1f; - int32_t result = (*arg1 >> count); - ZF = (*arg1 == 0); - SF = (*arg1 < 0); - // OF is only defined if count is 1 - if (count == 1) OF = false; - // CF - CF = ((*arg1 >> (count-1)) & 0x1); - trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); - *arg1 = result; - trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *arg1 << end(); - break; -} - -:(code) -void test_shift_right_arithmetic_odd_r32_with_imm8() { - Reg[EBX].i = 27; - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " c1 fb 01 \n" // shift EBX right by 1 bit - // ModR/M in binary: 11 (direct mode) 111 (subop shift right arithmetic) 011 (dest EBX) - ); - CHECK_TRACE_CONTENTS( - "run: operate on r/m32\n" - "run: r/m32 is EBX\n" - "run: subop: shift right by CL bits, while preserving sign\n" - // result: 13 - "run: storing 0x0000000d\n" - ); -} - -:(code) -void test_shift_right_arithmetic_negative_r32_with_imm8() { - Reg[EBX].i = 0xfffffffd; // -3 - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " c1 fb 01 \n" // shift EBX right by 1 bit, while preserving sign - // ModR/M in binary: 11 (direct mode) 111 (subop shift right arithmetic) 011 (dest EBX) - ); - CHECK_TRACE_CONTENTS( - "run: operate on r/m32\n" - "run: r/m32 is EBX\n" - "run: subop: shift right by CL bits, while preserving sign\n" - // result: -2 - "run: storing 0xfffffffe\n" - ); -} - -//:: shift right logical - -:(code) -void test_shift_right_logical_r32_with_imm8() { - Reg[EBX].i = 26; - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " c1 eb 01 \n" // shift EBX right by 1 bit, while padding zeroes - // ModR/M in binary: 11 (direct mode) 101 (subop shift right logical) 011 (dest EBX) - ); - CHECK_TRACE_CONTENTS( - "run: operate on r/m32\n" - "run: r/m32 is EBX\n" - "run: subop: shift right by CL bits, while padding zeroes\n" - "run: storing 0x0000000d\n" - ); -} - -:(before "End Op c1 Subops") -case 5: { // shift right r/m32 by CL, preserving sign - trace(Callstack_depth+1, "run") << "subop: shift right by CL bits, while padding zeroes" << end(); - uint8_t count = next() & 0x1f; - // OF is only defined if count is 1 - if (count == 1) { - bool msb = (*arg1 & 0x80000000) >> 1; - bool pnsb = (*arg1 & 0x40000000); - OF = (msb != pnsb); - } - uint32_t* uarg1 = reinterpret_cast<uint32_t*>(arg1); - *uarg1 = (*uarg1 >> count); - ZF = (*uarg1 == 0); - // result is always positive by definition - SF = false; - // CF undefined - trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); - trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *arg1 << end(); - break; -} - -:(code) -void test_shift_right_logical_odd_r32_with_imm8() { - Reg[EBX].i = 27; - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " c1 eb 01 \n" // shift EBX right by 1 bit, while padding zeroes - ); - CHECK_TRACE_CONTENTS( - "run: operate on r/m32\n" - "run: r/m32 is EBX\n" - "run: subop: shift right by CL bits, while padding zeroes\n" - // result: 13 - "run: storing 0x0000000d\n" - ); -} - -:(code) -void test_shift_right_logical_negative_r32_with_imm8() { - Reg[EBX].i = 0xfffffffd; - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " c1 eb 01 \n" // shift EBX right by 1 bit, while padding zeroes - // ModR/M in binary: 11 (direct mode) 101 (subop shift right logical) 011 (dest EBX) - ); - CHECK_TRACE_CONTENTS( - "run: operate on r/m32\n" - "run: r/m32 is EBX\n" - "run: subop: shift right by CL bits, while padding zeroes\n" - "run: storing 0x7ffffffe\n" - ); -} - -//:: and - -:(before "End Initialize Op Names") -put_new(Name, "25", "EAX = bitwise AND of imm32 with EAX (and)"); - -:(code) -void test_and_EAX_with_imm32() { - Reg[EAX].i = 0xff; - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " 25 0a 0b 0c 0d \n" // and 0x0d0c0b0a with EAX - ); - CHECK_TRACE_CONTENTS( - "run: and imm32 0x0d0c0b0a with EAX\n" - "run: storing 0x0000000a\n" - ); -} - -:(before "End Single-Byte Opcodes") -case 0x25: { // and imm32 with EAX - // bitwise ops technically operate on unsigned numbers, but it makes no - // difference - const int32_t signed_arg2 = next32(); - trace(Callstack_depth+1, "run") << "and imm32 0x" << HEXWORD << signed_arg2 << " with EAX" << end(); - Reg[EAX].i &= signed_arg2; - trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << Reg[EAX].i << end(); - SF = (Reg[EAX].i >> 31); - ZF = (Reg[EAX].i == 0); - CF = false; - OF = false; - trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); - break; -} - -//: - -:(code) -void test_and_imm32_with_mem_at_r32() { - Reg[EBX].i = 0x2000; - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " 81 23 0a 0b 0c 0d \n" // and 0x0d0c0b0a with *EBX - // ModR/M in binary: 00 (indirect mode) 100 (subop and) 011 (dest EBX) - "== data 0x2000\n" - "ff 00 00 00\n" // 0x000000ff - ); - CHECK_TRACE_CONTENTS( - "run: combine r/m32 with imm32\n" - "run: effective address is 0x00002000 (EBX)\n" - "run: imm32 is 0x0d0c0b0a\n" - "run: subop and\n" - "run: storing 0x0000000a\n" - ); -} - -:(before "End Op 81 Subops") -case 4: { - trace(Callstack_depth+1, "run") << "subop and" << end(); - // bitwise ops technically operate on unsigned numbers, but it makes no - // difference - *signed_arg1 &= signed_arg2; - trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *signed_arg1 << end(); - SF = (*signed_arg1 >> 31); - ZF = (*signed_arg1 == 0); - CF = false; - OF = false; - trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); - break; -} - -//: - -:(code) -void test_and_imm32_with_r32() { - Reg[EBX].i = 0xff; - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " 81 e3 0a 0b 0c 0d \n" // and 0x0d0c0b0a with EBX - // ModR/M in binary: 11 (direct mode) 100 (subop and) 011 (dest EBX) - ); - CHECK_TRACE_CONTENTS( - "run: combine r/m32 with imm32\n" - "run: r/m32 is EBX\n" - "run: imm32 is 0x0d0c0b0a\n" - "run: subop and\n" - "run: storing 0x0000000a\n" - ); -} - -//:: or - -:(before "End Initialize Op Names") -put_new(Name, "0d", "EAX = bitwise OR of imm32 with EAX (or)"); - -:(code) -void test_or_EAX_with_imm32() { - Reg[EAX].i = 0xd0c0b0a0; - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " 0d 0a 0b 0c 0d \n" // or 0x0d0c0b0a with EAX - ); - CHECK_TRACE_CONTENTS( - "run: or imm32 0x0d0c0b0a with EAX\n" - "run: storing 0xddccbbaa\n" - ); -} - -:(before "End Single-Byte Opcodes") -case 0x0d: { // or imm32 with EAX - // bitwise ops technically operate on unsigned numbers, but it makes no - // difference - const int32_t signed_arg2 = next32(); - trace(Callstack_depth+1, "run") << "or imm32 0x" << HEXWORD << signed_arg2 << " with EAX" << end(); - Reg[EAX].i |= signed_arg2; - trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << Reg[EAX].i << end(); - SF = (Reg[EAX].i >> 31); - ZF = (Reg[EAX].i == 0); - CF = false; - OF = false; - trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); - break; -} - -//: - -:(code) -void test_or_imm32_with_mem_at_r32() { - Reg[EBX].i = 0x2000; - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " 81 0b 0a 0b 0c 0d \n" // or 0x0d0c0b0a with *EBX - // ModR/M in binary: 00 (indirect mode) 001 (subop or) 011 (dest EBX) - "== data 0x2000\n" - "a0 b0 c0 d0\n" // 0xd0c0b0a0 - ); - CHECK_TRACE_CONTENTS( - "run: combine r/m32 with imm32\n" - "run: effective address is 0x00002000 (EBX)\n" - "run: imm32 is 0x0d0c0b0a\n" - "run: subop or\n" - "run: storing 0xddccbbaa\n" - ); -} - -:(before "End Op 81 Subops") -case 1: { - trace(Callstack_depth+1, "run") << "subop or" << end(); - // bitwise ops technically operate on unsigned numbers, but it makes no - // difference - *signed_arg1 |= signed_arg2; - trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *signed_arg1 << end(); - SF = (*signed_arg1 >> 31); - ZF = (*signed_arg1 == 0); - CF = false; - OF = false; - trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); - break; -} - -:(code) -void test_or_imm32_with_r32() { - Reg[EBX].i = 0xd0c0b0a0; - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " 81 cb 0a 0b 0c 0d \n" // or 0x0d0c0b0a with EBX - // ModR/M in binary: 11 (direct mode) 001 (subop or) 011 (dest EBX) - ); - CHECK_TRACE_CONTENTS( - "run: combine r/m32 with imm32\n" - "run: r/m32 is EBX\n" - "run: imm32 is 0x0d0c0b0a\n" - "run: subop or\n" - "run: storing 0xddccbbaa\n" - ); -} - -//:: xor - -:(before "End Initialize Op Names") -put_new(Name, "35", "EAX = bitwise XOR of imm32 with EAX (xor)"); - -:(code) -void test_xor_EAX_with_imm32() { - Reg[EAX].i = 0xddccb0a0; - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " 35 0a 0b 0c 0d \n" // xor 0x0d0c0b0a with EAX - ); - CHECK_TRACE_CONTENTS( - "run: xor imm32 0x0d0c0b0a with EAX\n" - "run: storing 0xd0c0bbaa\n" - ); -} - -:(before "End Single-Byte Opcodes") -case 0x35: { // xor imm32 with EAX - // bitwise ops technically operate on unsigned numbers, but it makes no - // difference - const int32_t signed_arg2 = next32(); - trace(Callstack_depth+1, "run") << "xor imm32 0x" << HEXWORD << signed_arg2 << " with EAX" << end(); - Reg[EAX].i ^= signed_arg2; - trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << Reg[EAX].i << end(); - SF = (Reg[EAX].i >> 31); - ZF = (Reg[EAX].i == 0); - CF = false; - OF = false; - trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); - break; -} - -//: - -:(code) -void test_xor_imm32_with_mem_at_r32() { - Reg[EBX].i = 0x2000; - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " 81 33 0a 0b 0c 0d \n" // xor 0x0d0c0b0a with *EBX - // ModR/M in binary: 00 (indirect mode) 110 (subop xor) 011 (dest EBX) - "== data 0x2000\n" - "a0 b0 c0 d0\n" // 0xd0c0b0a0 - ); - CHECK_TRACE_CONTENTS( - "run: combine r/m32 with imm32\n" - "run: effective address is 0x00002000 (EBX)\n" - "run: imm32 is 0x0d0c0b0a\n" - "run: subop xor\n" - "run: storing 0xddccbbaa\n" - ); -} - -:(before "End Op 81 Subops") -case 6: { - trace(Callstack_depth+1, "run") << "subop xor" << end(); - // bitwise ops technically operate on unsigned numbers, but it makes no - // difference - *signed_arg1 ^= signed_arg2; - trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *signed_arg1 << end(); - SF = (*signed_arg1 >> 31); - ZF = (*signed_arg1 == 0); - CF = false; - OF = false; - trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); - break; -} - -:(code) -void test_xor_imm32_with_r32() { - Reg[EBX].i = 0xd0c0b0a0; - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " 81 f3 0a 0b 0c 0d \n" // xor 0x0d0c0b0a with EBX - // ModR/M in binary: 11 (direct mode) 110 (subop xor) 011 (dest EBX) - ); - CHECK_TRACE_CONTENTS( - "run: combine r/m32 with imm32\n" - "run: r/m32 is EBX\n" - "run: imm32 is 0x0d0c0b0a\n" - "run: subop xor\n" - "run: storing 0xddccbbaa\n" - ); -} - -//:: compare (cmp) - -:(before "End Initialize Op Names") -put_new(Name, "3d", "compare: set SF if EAX < imm32 (cmp)"); - -:(code) -void test_compare_EAX_with_imm32_greater() { - Reg[EAX].i = 0x0d0c0b0a; - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " 3d 07 0b 0c 0d \n" // compare EAX with 0x0d0c0b07 - ); - CHECK_TRACE_CONTENTS( - "run: compare EAX with imm32 0x0d0c0b07\n" - "run: SF=0; ZF=0; CF=0; OF=0\n" - ); -} - -:(before "End Single-Byte Opcodes") -case 0x3d: { // compare EAX with imm32 - const int32_t signed_arg1 = Reg[EAX].i; - const int32_t signed_arg2 = next32(); - trace(Callstack_depth+1, "run") << "compare EAX with imm32 0x" << HEXWORD << signed_arg2 << end(); - const int32_t signed_difference = signed_arg1 - signed_arg2; - SF = (signed_difference < 0); - ZF = (signed_difference == 0); - const int64_t full_signed_difference = static_cast<int64_t>(signed_arg1) - signed_arg2; - OF = (signed_difference != full_signed_difference); - const uint32_t unsigned_arg1 = static_cast<uint32_t>(signed_arg1); - const uint32_t unsigned_arg2 = static_cast<uint32_t>(signed_arg2); - const uint32_t unsigned_difference = unsigned_arg1 - unsigned_arg2; - const uint64_t full_unsigned_difference = static_cast<uint64_t>(unsigned_arg1) - unsigned_arg2; - CF = (unsigned_difference != full_unsigned_difference); - trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); - break; -} - -:(code) -void test_compare_EAX_with_imm32_lesser_unsigned_and_signed() { - Reg[EAX].i = 0x0a0b0c07; - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " 3d 0d 0c 0b 0a \n" // compare EAX with imm32 - ); - CHECK_TRACE_CONTENTS( - "run: compare EAX with imm32 0x0a0b0c0d\n" - "run: SF=1; ZF=0; CF=1; OF=0\n" - ); -} - -void test_compare_EAX_with_imm32_lesser_unsigned_and_signed_due_to_overflow() { - Reg[EAX].i = 0x7fffffff; // largest positive signed integer - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " 3d 00 00 00 80\n" // compare EAX with smallest negative signed integer - ); - CHECK_TRACE_CONTENTS( - "run: compare EAX with imm32 0x80000000\n" - "run: SF=1; ZF=0; CF=1; OF=1\n" - ); -} - -void test_compare_EAX_with_imm32_lesser_signed() { - Reg[EAX].i = 0xffffffff; // -1 - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " 3d 01 00 00 00\n" // compare EAX with 1 - ); - CHECK_TRACE_CONTENTS( - "run: compare EAX with imm32 0x00000001\n" - "run: SF=1; ZF=0; CF=0; OF=0\n" - ); -} - -void test_compare_EAX_with_imm32_lesser_unsigned() { - Reg[EAX].i = 0x00000001; // 1 - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " 3d ff ff ff ff\n" // compare EAX with -1 - ); - CHECK_TRACE_CONTENTS( - "run: compare EAX with imm32 0xffffffff\n" - "run: SF=0; ZF=0; CF=1; OF=0\n" - ); -} - -void test_compare_EAX_with_imm32_equal() { - Reg[EAX].i = 0x0d0c0b0a; - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " 3d 0a 0b 0c 0d \n" // compare 0x0d0c0b0a with EAX - ); - CHECK_TRACE_CONTENTS( - "run: compare EAX with imm32 0x0d0c0b0a\n" - "run: SF=0; ZF=1; CF=0; OF=0\n" - ); -} - -//: - -void test_compare_imm32_with_r32_greater() { - Reg[EBX].i = 0x0d0c0b0a; - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " 81 fb 07 0b 0c 0d \n" // compare 0x0d0c0b07 with EBX - // ModR/M in binary: 11 (direct mode) 111 (subop compare) 011 (dest EBX) - ); - CHECK_TRACE_CONTENTS( - "run: combine r/m32 with imm32\n" - "run: r/m32 is EBX\n" - "run: imm32 is 0x0d0c0b07\n" - "run: SF=0; ZF=0; CF=0; OF=0\n" - ); -} - -:(before "End Op 81 Subops") -case 7: { - trace(Callstack_depth+1, "run") << "subop compare" << end(); - const int32_t tmp1 = *signed_arg1 - signed_arg2; - SF = (tmp1 < 0); - ZF = (tmp1 == 0); - const int64_t tmp2 = static_cast<int64_t>(*signed_arg1) - signed_arg2; - OF = (tmp1 != tmp2); - const uint32_t unsigned_arg1 = static_cast<uint32_t>(*signed_arg1); - const uint32_t unsigned_arg2 = static_cast<uint32_t>(signed_arg2); - const uint32_t tmp3 = unsigned_arg1 - unsigned_arg2; - const uint64_t tmp4 = static_cast<uint64_t>(unsigned_arg1) - unsigned_arg2; - CF = (tmp3 != tmp4); - trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); - break; -} - -:(code) -void test_compare_rm32_with_imm32_lesser_unsigned_and_signed() { - Reg[EAX].i = 0x0a0b0c07; - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " 81 f8 0d 0c 0b 0a \n" // compare EAX with imm32 - // ModR/M in binary: 11 (direct mode) 111 (subop compare) 000 (dest EAX) - ); - CHECK_TRACE_CONTENTS( - "run: combine r/m32 with imm32\n" - "run: r/m32 is EAX\n" - "run: imm32 is 0x0a0b0c0d\n" - "run: subop compare\n" - "run: SF=1; ZF=0; CF=1; OF=0\n" - ); -} - -void test_compare_rm32_with_imm32_lesser_unsigned_and_signed_due_to_overflow() { - Reg[EAX].i = 0x7fffffff; // largest positive signed integer - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " 81 f8 00 00 00 80\n" // compare EAX with smallest negative signed integer - // ModR/M in binary: 11 (direct mode) 111 (subop compare) 000 (dest EAX) - ); - CHECK_TRACE_CONTENTS( - "run: combine r/m32 with imm32\n" - "run: r/m32 is EAX\n" - "run: imm32 is 0x80000000\n" - "run: subop compare\n" - "run: SF=1; ZF=0; CF=1; OF=1\n" - ); -} - -void test_compare_rm32_with_imm32_lesser_signed() { - Reg[EAX].i = 0xffffffff; // -1 - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " 81 f8 01 00 00 00\n" // compare EAX with 1 - // ModR/M in binary: 11 (direct mode) 111 (subop compare) 000 (dest EAX) - ); - CHECK_TRACE_CONTENTS( - "run: combine r/m32 with imm32\n" - "run: r/m32 is EAX\n" - "run: imm32 is 0x00000001\n" - "run: subop compare\n" - "run: SF=1; ZF=0; CF=0; OF=0\n" - ); -} - -void test_compare_rm32_with_imm32_lesser_unsigned() { - Reg[EAX].i = 0x00000001; // 1 - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " 81 f8 ff ff ff ff\n" // compare EAX with -1 - // ModR/M in binary: 11 (direct mode) 111 (subop compare) 000 (dest EAX) - ); - CHECK_TRACE_CONTENTS( - "run: combine r/m32 with imm32\n" - "run: r/m32 is EAX\n" - "run: imm32 is 0xffffffff\n" - "run: subop compare\n" - "run: SF=0; ZF=0; CF=1; OF=0\n" - ); -} - -:(code) -void test_compare_imm32_with_r32_equal() { - Reg[EBX].i = 0x0d0c0b0a; - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " 81 fb 0a 0b 0c 0d \n" // compare 0x0d0c0b0a with EBX - // ModR/M in binary: 11 (direct mode) 111 (subop compare) 011 (dest EBX) - ); - CHECK_TRACE_CONTENTS( - "run: combine r/m32 with imm32\n" - "run: r/m32 is EBX\n" - "run: imm32 is 0x0d0c0b0a\n" - "run: SF=0; ZF=1; CF=0; OF=0\n" - ); -} - -:(code) -void test_compare_imm32_with_mem_at_r32_greater() { - Reg[EBX].i = 0x2000; - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " 81 3b 07 0b 0c 0d \n" // compare 0x0d0c0b07 with *EBX - // ModR/M in binary: 00 (indirect mode) 111 (subop compare) 011 (dest EBX) - "== data 0x2000\n" - "0a 0b 0c 0d\n" // 0x0d0c0b0a - ); - CHECK_TRACE_CONTENTS( - "run: combine r/m32 with imm32\n" - "run: effective address is 0x00002000 (EBX)\n" - "run: imm32 is 0x0d0c0b07\n" - "run: SF=0; ZF=0; CF=0; OF=0\n" - ); -} - -:(code) -void test_compare_imm32_with_mem_at_r32_lesser() { - Reg[EAX].i = 0x2000; - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " 81 38 0a 0b 0c 0d \n" // compare 0x0d0c0b0a with *EAX - // ModR/M in binary: 00 (indirect mode) 111 (subop compare) 000 (dest EAX) - "== data 0x2000\n" - "07 0b 0c 0d\n" // 0x0d0c0b07 - ); - CHECK_TRACE_CONTENTS( - "run: combine r/m32 with imm32\n" - "run: effective address is 0x00002000 (EAX)\n" - "run: imm32 is 0x0d0c0b0a\n" - "run: SF=1; ZF=0; CF=1; OF=0\n" - ); -} - -:(code) -void test_compare_imm32_with_mem_at_r32_equal() { - Reg[EBX].i = 0x0d0c0b0a; - Reg[EBX].i = 0x2000; - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " 81 3b 0a 0b 0c 0d \n" // compare 0x0d0c0b0a with *EBX - // ModR/M in binary: 00 (indirect mode) 111 (subop compare) 011 (dest EBX) - "== data 0x2000\n" - "0a 0b 0c 0d\n" // 0x0d0c0b0a - ); - CHECK_TRACE_CONTENTS( - "run: combine r/m32 with imm32\n" - "run: effective address is 0x00002000 (EBX)\n" - "run: imm32 is 0x0d0c0b0a\n" - "run: SF=0; ZF=1; CF=0; OF=0\n" - ); -} - -//:: copy (mov) - -:(before "End Initialize Op Names") -// b8 defined earlier to copy imm32 to EAX -put_new(Name, "b9", "copy imm32 to ECX (mov)"); -put_new(Name, "ba", "copy imm32 to EDX (mov)"); -put_new(Name, "bb", "copy imm32 to EBX (mov)"); -put_new(Name, "bc", "copy imm32 to ESP (mov)"); -put_new(Name, "bd", "copy imm32 to EBP (mov)"); -put_new(Name, "be", "copy imm32 to ESI (mov)"); -put_new(Name, "bf", "copy imm32 to EDI (mov)"); - -:(code) -void test_copy_imm32_to_r32() { - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " bb 0a 0b 0c 0d \n" // copy 0x0d0c0b0a to EBX - ); - CHECK_TRACE_CONTENTS( - "run: copy imm32 0x0d0c0b0a to EBX\n" - ); -} - -:(before "End Single-Byte Opcodes") -case 0xb9: -case 0xba: -case 0xbb: -case 0xbc: -case 0xbd: -case 0xbe: -case 0xbf: { // copy imm32 to r32 - const uint8_t rdest = op & 0x7; - const int32_t src = next32(); - trace(Callstack_depth+1, "run") << "copy imm32 0x" << HEXWORD << src << " to " << rname(rdest) << end(); - Reg[rdest].i = src; - break; -} - -//: - -:(before "End Initialize Op Names") -put_new(Name, "c7", "copy imm32 to rm32 (mov)"); - -:(code) -void test_copy_imm32_to_mem_at_r32() { - Reg[EBX].i = 0x60; - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " c7 03 0a 0b 0c 0d \n" // copy 0x0d0c0b0a to *EBX - // ModR/M in binary: 00 (indirect mode) 000 (unused) 011 (dest EBX) - ); - CHECK_TRACE_CONTENTS( - "run: copy imm32 to r/m32\n" - "run: effective address is 0x00000060 (EBX)\n" - "run: imm32 is 0x0d0c0b0a\n" - ); -} - -:(before "End Single-Byte Opcodes") -case 0xc7: { // copy imm32 to r32 - const uint8_t modrm = next(); - trace(Callstack_depth+1, "run") << "copy imm32 to r/m32" << end(); - const uint8_t subop = (modrm>>3)&0x7; // middle 3 'reg opcode' bits - if (subop != 0) { - cerr << "unrecognized subop for opcode c7: " << NUM(subop) << " (only 0/copy currently implemented)\n"; - exit(1); - } - int32_t* dest = effective_address(modrm); - const int32_t src = next32(); - trace(Callstack_depth+1, "run") << "imm32 is 0x" << HEXWORD << src << end(); - *dest = src; - break; -} - -//:: push - -:(before "End Initialize Op Names") -put_new(Name, "68", "push imm32 to stack (push)"); - -:(code) -void test_push_imm32() { - Mem.push_back(vma(0xbd000000)); // manually allocate memory - Reg[ESP].u = 0xbd000014; - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " 68 af 00 00 00 \n" // push *EAX to stack - ); - CHECK_TRACE_CONTENTS( - "run: push imm32 0x000000af\n" - "run: ESP is now 0xbd000010\n" - "run: contents at ESP: 0x000000af\n" - ); -} - -:(before "End Single-Byte Opcodes") -case 0x68: { - const uint32_t val = static_cast<uint32_t>(next32()); - trace(Callstack_depth+1, "run") << "push imm32 0x" << HEXWORD << val << end(); -//? cerr << "push: " << val << " => " << Reg[ESP].u << '\n'; - push(val); - trace(Callstack_depth+1, "run") << "ESP is now 0x" << HEXWORD << Reg[ESP].u << end(); - trace(Callstack_depth+1, "run") << "contents at ESP: 0x" << HEXWORD << read_mem_u32(Reg[ESP].u) << end(); - break; -} diff --git a/subx/016index_addressing.cc b/subx/016index_addressing.cc deleted file mode 100644 index 6e1f63e6..00000000 --- a/subx/016index_addressing.cc +++ /dev/null @@ -1,155 +0,0 @@ -//: operating on memory at the address provided by some register plus optional scale and offset - -:(code) -void test_add_r32_to_mem_at_r32_with_sib() { - Reg[EBX].i = 0x10; - Reg[EAX].i = 0x2000; - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " 01 1c 20 \n" // add EBX to *EAX - // ModR/M in binary: 00 (indirect mode) 011 (src EBX) 100 (dest in SIB) - // SIB in binary: 00 (scale 1) 100 (no index) 000 (base EAX) - "== data 0x2000\n" - "01 00 00 00\n" // 0x00000001 - ); - CHECK_TRACE_CONTENTS( - "run: add EBX to r/m32\n" - "run: effective address is initially 0x00002000 (EAX)\n" - "run: effective address is 0x00002000\n" - "run: storing 0x00000011\n" - ); -} - -:(before "End Mod 0 Special-cases(addr)") -case 4: // exception: mod 0b00 rm 0b100 => incoming SIB (scale-index-base) byte - addr = effective_address_from_sib(mod); - break; -:(code) -uint32_t effective_address_from_sib(uint8_t mod) { - const uint8_t sib = next(); - const uint8_t base = sib&0x7; - uint32_t addr = 0; - if (base != EBP || mod != 0) { - addr = Reg[base].u; - trace(Callstack_depth+1, "run") << "effective address is initially 0x" << HEXWORD << addr << " (" << rname(base) << ")" << end(); - } - else { - // base == EBP && mod == 0 - addr = next32(); // ignore base - trace(Callstack_depth+1, "run") << "effective address is initially 0x" << HEXWORD << addr << " (disp32)" << end(); - } - const uint8_t index = (sib>>3)&0x7; - if (index == ESP) { - // ignore index and scale - trace(Callstack_depth+1, "run") << "effective address is 0x" << HEXWORD << addr << end(); - } - else { - const uint8_t scale = (1 << (sib>>6)); - addr += Reg[index].i*scale; // treat index register as signed. Maybe base as well? But we'll always ensure it's non-negative. - trace(Callstack_depth+1, "run") << "effective address is 0x" << HEXWORD << addr << " (after adding " << rname(index) << "*" << NUM(scale) << ")" << end(); - } - return addr; -} - -:(code) -void test_add_r32_to_mem_at_base_r32_index_r32() { - Reg[EBX].i = 0x10; // source - Reg[EAX].i = 0x1ffe; // dest base - Reg[ECX].i = 0x2; // dest index - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " 01 1c 08 \n" // add EBX to *(EAX+ECX) - // ModR/M in binary: 00 (indirect mode) 011 (src EBX) 100 (dest in SIB) - // SIB in binary: 00 (scale 1) 001 (index ECX) 000 (base EAX) - "== data 0x2000\n" - "01 00 00 00\n" // 0x00000001 - ); - CHECK_TRACE_CONTENTS( - "run: add EBX to r/m32\n" - "run: effective address is initially 0x00001ffe (EAX)\n" - "run: effective address is 0x00002000 (after adding ECX*1)\n" - "run: storing 0x00000011\n" - ); -} - -:(code) -void test_add_r32_to_mem_at_displacement_using_sib() { - Reg[EBX].i = 0x10; // source - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " 01 1c 25 00 20 00 00 \n" // add EBX to *0x2000 - // ModR/M in binary: 00 (indirect mode) 011 (src EBX) 100 (dest in SIB) - // SIB in binary: 00 (scale 1) 100 (no index) 101 (not EBP but disp32) - "== data 0x2000\n" - "01 00 00 00\n" // 0x00000001 - ); - CHECK_TRACE_CONTENTS( - "run: add EBX to r/m32\n" - "run: effective address is initially 0x00002000 (disp32)\n" - "run: effective address is 0x00002000\n" - "run: storing 0x00000011\n" - ); -} - -//: - -:(code) -void test_add_r32_to_mem_at_base_r32_index_r32_plus_disp8() { - Reg[EBX].i = 0x10; // source - Reg[EAX].i = 0x1ff9; // dest base - Reg[ECX].i = 0x5; // dest index - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " 01 5c 08 02 \n" // add EBX to *(EAX+ECX+2) - // ModR/M in binary: 01 (indirect+disp8 mode) 011 (src EBX) 100 (dest in SIB) - // SIB in binary: 00 (scale 1) 001 (index ECX) 000 (base EAX) - "== data 0x2000\n" - "01 00 00 00\n" // 0x00000001 - ); - CHECK_TRACE_CONTENTS( - "run: add EBX to r/m32\n" - "run: effective address is initially 0x00001ff9 (EAX)\n" - "run: effective address is 0x00001ffe (after adding ECX*1)\n" - "run: effective address is 0x00002000 (after adding disp8)\n" - "run: storing 0x00000011\n" - ); -} - -:(before "End Mod 1 Special-cases(addr)") -case 4: // exception: mod 0b01 rm 0b100 => incoming SIB (scale-index-base) byte - addr = effective_address_from_sib(mod); - break; - -//: - -:(code) -void test_add_r32_to_mem_at_base_r32_index_r32_plus_disp32() { - Reg[EBX].i = 0x10; // source - Reg[EAX].i = 0x1ff9; // dest base - Reg[ECX].i = 0x5; // dest index - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " 01 9c 08 02 00 00 00 \n" // add EBX to *(EAX+ECX+2) - // ModR/M in binary: 10 (indirect+disp32 mode) 011 (src EBX) 100 (dest in SIB) - // SIB in binary: 00 (scale 1) 001 (index ECX) 000 (base EAX) - "== data 0x2000\n" - "01 00 00 00\n" // 0x00000001 - ); - CHECK_TRACE_CONTENTS( - "run: add EBX to r/m32\n" - "run: effective address is initially 0x00001ff9 (EAX)\n" - "run: effective address is 0x00001ffe (after adding ECX*1)\n" - "run: effective address is 0x00002000 (after adding disp32)\n" - "run: storing 0x00000011\n" - ); -} - -:(before "End Mod 2 Special-cases(addr)") -case 4: // exception: mod 0b10 rm 0b100 => incoming SIB (scale-index-base) byte - addr = effective_address_from_sib(mod); - break; diff --git a/subx/017jump_disp8.cc b/subx/017jump_disp8.cc deleted file mode 100644 index d7558401..00000000 --- a/subx/017jump_disp8.cc +++ /dev/null @@ -1,407 +0,0 @@ -//: jump to 8-bit offset - -//:: jump - -:(before "End Initialize Op Names") -put_new(Name, "eb", "jump disp8 bytes away (jmp)"); - -:(code) -void test_jump_disp8() { - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " eb 05 \n" // skip 1 instruction - " 05 00 00 00 01 \n" - " 05 00 00 00 02 \n" - ); - CHECK_TRACE_CONTENTS( - "run: 0x00000001 opcode: eb\n" - "run: jump 5\n" - "run: 0x00000008 opcode: 05\n" - ); - CHECK_TRACE_DOESNT_CONTAIN("run: 0x00000003 opcode: 05"); -} - -:(before "End Single-Byte Opcodes") -case 0xeb: { // jump disp8 - int8_t offset = static_cast<int>(next()); - trace(Callstack_depth+1, "run") << "jump " << NUM(offset) << end(); - EIP += offset; - break; -} - -//:: jump if equal/zero - -:(before "End Initialize Op Names") -put_new(Name, "74", "jump disp8 bytes away if equal, if ZF is set (jcc/jz/je)"); - -:(code) -void test_je_disp8_success() { - ZF = true; - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " 74 05 \n" // skip 1 instruction - " 05 00 00 00 01 \n" - " 05 00 00 00 02 \n" - ); - CHECK_TRACE_CONTENTS( - "run: 0x00000001 opcode: 74\n" - "run: jump 5\n" - "run: 0x00000008 opcode: 05\n" - ); - CHECK_TRACE_DOESNT_CONTAIN("run: 0x00000003 opcode: 05"); -} - -:(before "End Single-Byte Opcodes") -case 0x74: { // jump disp8 if ZF - const int8_t offset = static_cast<int>(next()); - if (ZF) { - trace(Callstack_depth+1, "run") << "jump " << NUM(offset) << end(); - EIP += offset; - } - break; -} - -:(code) -void test_je_disp8_fail() { - ZF = false; - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " 74 05 \n" // skip 1 instruction - " 05 00 00 00 01 \n" - " 05 00 00 00 02 \n" - ); - CHECK_TRACE_CONTENTS( - "run: 0x00000001 opcode: 74\n" - "run: 0x00000003 opcode: 05\n" - "run: 0x00000008 opcode: 05\n" - ); - CHECK_TRACE_DOESNT_CONTAIN("run: jump 5"); -} - -//:: jump if not equal/not zero - -:(before "End Initialize Op Names") -put_new(Name, "75", "jump disp8 bytes away if not equal, if ZF is not set (jcc/jnz/jne)"); - -:(code) -void test_jne_disp8_success() { - ZF = false; - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " 75 05 \n" // skip 1 instruction - " 05 00 00 00 01 \n" - " 05 00 00 00 02 \n" - ); - CHECK_TRACE_CONTENTS( - "run: 0x00000001 opcode: 75\n" - "run: jump 5\n" - "run: 0x00000008 opcode: 05\n" - ); - CHECK_TRACE_DOESNT_CONTAIN("run: 0x00000003 opcode: 05"); -} - -:(before "End Single-Byte Opcodes") -case 0x75: { // jump disp8 unless ZF - const int8_t offset = static_cast<int>(next()); - if (!ZF) { - trace(Callstack_depth+1, "run") << "jump " << NUM(offset) << end(); - EIP += offset; - } - break; -} - -:(code) -void test_jne_disp8_fail() { - ZF = true; - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " 75 05 \n" // skip 1 instruction - " 05 00 00 00 01 \n" - " 05 00 00 00 02 \n" - ); - CHECK_TRACE_CONTENTS( - "run: 0x00000001 opcode: 75\n" - "run: 0x00000003 opcode: 05\n" - "run: 0x00000008 opcode: 05\n" - ); - CHECK_TRACE_DOESNT_CONTAIN("run: jump 5"); -} - -//:: jump if greater - -:(before "End Initialize Op Names") -put_new(Name, "7f", "jump disp8 bytes away if greater (signed), if ZF is unset and SF == OF (jcc/jg/jnle)"); -put_new(Name, "77", "jump disp8 bytes away if greater (unsigned), if ZF is unset and CF is unset (jcc/ja/jnbe)"); - -:(code) -void test_jg_disp8_success() { - ZF = false; - SF = false; - OF = false; - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " 7f 05 \n" // skip 1 instruction - " 05 00 00 00 01 \n" - " 05 00 00 00 02 \n" - ); - CHECK_TRACE_CONTENTS( - "run: 0x00000001 opcode: 7f\n" - "run: jump 5\n" - "run: 0x00000008 opcode: 05\n" - ); - CHECK_TRACE_DOESNT_CONTAIN("run: 0x00000003 opcode: 05"); -} - -:(before "End Single-Byte Opcodes") -case 0x7f: { // jump disp8 if SF == OF and !ZF - const int8_t offset = static_cast<int>(next()); - if (SF == OF && !ZF) { - trace(Callstack_depth+1, "run") << "jump " << NUM(offset) << end(); - EIP += offset; - } - break; -} -case 0x77: { // jump disp8 if !CF and !ZF - const int8_t offset = static_cast<int>(next()); - if (!CF && !ZF) { - trace(Callstack_depth+1, "run") << "jump " << NUM(offset) << end(); - EIP += offset; - } - break; -} - -:(code) -void test_jg_disp8_fail() { - ZF = false; - SF = true; - OF = false; - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " 7f 05 \n" // skip 1 instruction - " 05 00 00 00 01 \n" - " 05 00 00 00 02 \n" - ); - CHECK_TRACE_CONTENTS( - "run: 0x00000001 opcode: 7f\n" - "run: 0x00000003 opcode: 05\n" - "run: 0x00000008 opcode: 05\n" - ); - CHECK_TRACE_DOESNT_CONTAIN("run: jump 5"); -} - -//:: jump if greater or equal - -:(before "End Initialize Op Names") -put_new(Name, "7d", "jump disp8 bytes away if greater or equal (signed), if SF == OF (jcc/jge/jnl)"); -put_new(Name, "73", "jump disp8 bytes away if greater or equal (unsigned), if CF is unset (jcc/jae/jnb)"); - -:(code) -void test_jge_disp8_success() { - SF = false; - OF = false; - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " 7d 05 \n" // skip 1 instruction - " 05 00 00 00 01 \n" - " 05 00 00 00 02 \n" - ); - CHECK_TRACE_CONTENTS( - "run: 0x00000001 opcode: 7d\n" - "run: jump 5\n" - "run: 0x00000008 opcode: 05\n" - ); - CHECK_TRACE_DOESNT_CONTAIN("run: 0x00000003 opcode: 05"); -} - -:(before "End Single-Byte Opcodes") -case 0x7d: { // jump disp8 if SF == OF - const int8_t offset = static_cast<int>(next()); - if (SF == OF) { - trace(Callstack_depth+1, "run") << "jump " << NUM(offset) << end(); - EIP += offset; - } - break; -} -case 0x73: { // jump disp8 if !CF - const int8_t offset = static_cast<int>(next()); - if (!CF) { - trace(Callstack_depth+1, "run") << "jump " << NUM(offset) << end(); - EIP += offset; - } - break; -} - -:(code) -void test_jge_disp8_fail() { - SF = true; - OF = false; - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " 7d 05 \n" // skip 1 instruction - " 05 00 00 00 01 \n" - " 05 00 00 00 02 \n" - ); - CHECK_TRACE_CONTENTS( - "run: 0x00000001 opcode: 7d\n" - "run: 0x00000003 opcode: 05\n" - "run: 0x00000008 opcode: 05\n" - ); - CHECK_TRACE_DOESNT_CONTAIN("run: jump 5"); -} - -//:: jump if lesser - -:(before "End Initialize Op Names") -put_new(Name, "7c", "jump disp8 bytes away if lesser (signed), if SF != OF (jcc/jl/jnge)"); -put_new(Name, "72", "jump disp8 bytes away if lesser (unsigned), if CF is set (jcc/jb/jnae)"); - -:(code) -void test_jl_disp8_success() { - ZF = false; - SF = true; - OF = false; - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " 7c 05 \n" // skip 1 instruction - " 05 00 00 00 01 \n" - " 05 00 00 00 02 \n" - ); - CHECK_TRACE_CONTENTS( - "run: 0x00000001 opcode: 7c\n" - "run: jump 5\n" - "run: 0x00000008 opcode: 05\n" - ); - CHECK_TRACE_DOESNT_CONTAIN("run: 0x00000003 opcode: 05"); -} - -:(before "End Single-Byte Opcodes") -case 0x7c: { // jump disp8 if SF != OF - const int8_t offset = static_cast<int>(next()); - if (SF != OF) { - trace(Callstack_depth+1, "run") << "jump " << NUM(offset) << end(); - EIP += offset; - } - break; -} -case 0x72: { // jump disp8 if CF - const int8_t offset = static_cast<int>(next()); - if (CF) { - trace(Callstack_depth+1, "run") << "jump " << NUM(offset) << end(); - EIP += offset; - } - break; -} - -:(code) -void test_jl_disp8_fail() { - ZF = false; - SF = false; - OF = false; - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " 7c 05 \n" // skip 1 instruction - " 05 00 00 00 01 \n" - " 05 00 00 00 02 \n" - ); - CHECK_TRACE_CONTENTS( - "run: 0x00000001 opcode: 7c\n" - "run: 0x00000003 opcode: 05\n" - "run: 0x00000008 opcode: 05\n" - ); - CHECK_TRACE_DOESNT_CONTAIN("run: jump 5"); -} - -//:: jump if lesser or equal - -:(before "End Initialize Op Names") -put_new(Name, "7e", "jump disp8 bytes away if lesser or equal (signed), if ZF is set or SF != OF (jcc/jle/jng)"); -put_new(Name, "76", "jump disp8 bytes away if lesser or equal (unsigned), if ZF is set or CF is set (jcc/jbe/jna)"); - -:(code) -void test_jle_disp8_equal() { - ZF = true; - SF = false; - OF = false; - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " 7e 05 \n" // skip 1 instruction - " 05 00 00 00 01 \n" - " 05 00 00 00 02 \n" - ); - CHECK_TRACE_CONTENTS( - "run: 0x00000001 opcode: 7e\n" - "run: jump 5\n" - "run: 0x00000008 opcode: 05\n" - ); - CHECK_TRACE_DOESNT_CONTAIN("run: 0x00000003 opcode: 05"); -} - -:(code) -void test_jle_disp8_lesser() { - ZF = false; - SF = true; - OF = false; - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " 7e 05 \n" // skip 1 instruction - " 05 00 00 00 01 \n" - " 05 00 00 00 02 \n" - ); - CHECK_TRACE_CONTENTS( - "run: 0x00000001 opcode: 7e\n" - "run: jump 5\n" - "run: 0x00000008 opcode: 05\n" - ); - CHECK_TRACE_DOESNT_CONTAIN("run: 0x00000003 opcode: 05"); -} - -:(before "End Single-Byte Opcodes") -case 0x7e: { // jump disp8 if ZF or SF != OF - const int8_t offset = static_cast<int>(next()); - if (ZF || SF != OF) { - trace(Callstack_depth+1, "run") << "jump " << NUM(offset) << end(); - EIP += offset; - } - break; -} -case 0x76: { // jump disp8 if ZF or CF - const int8_t offset = static_cast<int>(next()); - if (ZF || CF) { - trace(Callstack_depth+1, "run") << "jump " << NUM(offset) << end(); - EIP += offset; - } - break; -} - -:(code) -void test_jle_disp8_greater() { - ZF = false; - SF = false; - OF = false; - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " 7e 05 \n" // skip 1 instruction - " 05 00 00 00 01 \n" - " 05 00 00 00 02 \n" - ); - CHECK_TRACE_CONTENTS( - "run: 0x00000001 opcode: 7e\n" - "run: 0x00000003 opcode: 05\n" - "run: 0x00000008 opcode: 05\n" - ); - CHECK_TRACE_DOESNT_CONTAIN("run: jump 5"); -} diff --git a/subx/018jump_disp32.cc b/subx/018jump_disp32.cc deleted file mode 100644 index 86e06e9f..00000000 --- a/subx/018jump_disp32.cc +++ /dev/null @@ -1,407 +0,0 @@ -//: jump to 32-bit offset - -//:: jump - -:(before "End Initialize Op Names") -put_new(Name, "e9", "jump disp32 bytes away (jmp)"); - -:(code) -void test_jump_disp32() { - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " e9 05 00 00 00 \n" // skip 1 instruction - " 05 00 00 00 01 \n" - " 05 00 00 00 02 \n" - ); - CHECK_TRACE_CONTENTS( - "run: 0x00000001 opcode: e9\n" - "run: jump 5\n" - "run: 0x0000000b opcode: 05\n" - ); - CHECK_TRACE_DOESNT_CONTAIN("run: 0x00000006 opcode: 05"); -} - -:(before "End Single-Byte Opcodes") -case 0xe9: { // jump disp32 - const int32_t offset = next32(); - trace(Callstack_depth+1, "run") << "jump " << offset << end(); - EIP += offset; - break; -} - -//:: jump if equal/zero - -:(before "End Initialize Op Names") -put_new(Name_0f, "84", "jump disp32 bytes away if equal, if ZF is set (jcc/jz/je)"); - -:(code) -void test_je_disp32_success() { - ZF = true; - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " 0f 84 05 00 00 00 \n" // skip 1 instruction - " 05 00 00 00 01 \n" - " 05 00 00 00 02 \n" - ); - CHECK_TRACE_CONTENTS( - "run: 0x00000001 opcode: 0f\n" - "run: jump 5\n" - "run: 0x0000000c opcode: 05\n" - ); - CHECK_TRACE_DOESNT_CONTAIN("run: 0x00000007 opcode: 05"); -} - -:(before "End Two-Byte Opcodes Starting With 0f") -case 0x84: { // jump disp32 if ZF - const int32_t offset = next32(); - if (ZF) { - trace(Callstack_depth+1, "run") << "jump " << offset << end(); - EIP += offset; - } - break; -} - -:(code) -void test_je_disp32_fail() { - ZF = false; - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " 0f 84 05 00 00 00 \n" // skip 1 instruction - " 05 00 00 00 01 \n" - " 05 00 00 00 02 \n" - ); - CHECK_TRACE_CONTENTS( - "run: 0x00000001 opcode: 0f\n" - "run: 0x00000007 opcode: 05\n" - "run: 0x0000000c opcode: 05\n" - ); - CHECK_TRACE_DOESNT_CONTAIN("run: jump 5"); -} - -//:: jump if not equal/not zero - -:(before "End Initialize Op Names") -put_new(Name_0f, "85", "jump disp32 bytes away if not equal, if ZF is not set (jcc/jnz/jne)"); - -:(code) -void test_jne_disp32_success() { - ZF = false; - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " 0f 85 05 00 00 00 \n" // skip 1 instruction - " 05 00 00 00 01 \n" - " 05 00 00 00 02 \n" - ); - CHECK_TRACE_CONTENTS( - "run: 0x00000001 opcode: 0f\n" - "run: jump 5\n" - "run: 0x0000000c opcode: 05\n" - ); - CHECK_TRACE_DOESNT_CONTAIN("run: 0x00000007 opcode: 05"); -} - -:(before "End Two-Byte Opcodes Starting With 0f") -case 0x85: { // jump disp32 unless ZF - const int32_t offset = next32(); - if (!ZF) { - trace(Callstack_depth+1, "run") << "jump " << offset << end(); - EIP += offset; - } - break; -} - -:(code) -void test_jne_disp32_fail() { - ZF = true; - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " 0f 85 05 00 00 00 \n" // skip 1 instruction - " 05 00 00 00 01 \n" - " 05 00 00 00 02 \n" - ); - CHECK_TRACE_CONTENTS( - "run: 0x00000001 opcode: 0f\n" - "run: 0x00000007 opcode: 05\n" - "run: 0x0000000c opcode: 05\n" - ); - CHECK_TRACE_DOESNT_CONTAIN("run: jump 5"); -} - -//:: jump if greater - -:(before "End Initialize Op Names") -put_new(Name_0f, "8f", "jump disp32 bytes away if greater (signed), if ZF is unset and SF == OF (jcc/jg/jnle)"); -put_new(Name_0f, "87", "jump disp32 bytes away if greater (unsigned), if ZF is unset and CF is unset (jcc/ja/jnbe)"); - -:(code) -void test_jg_disp32_success() { - ZF = false; - SF = false; - OF = false; - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " 0f 8f 05 00 00 00 \n" // skip 1 instruction - " 05 00 00 00 01 \n" - " 05 00 00 00 02 \n" - ); - CHECK_TRACE_CONTENTS( - "run: 0x00000001 opcode: 0f\n" - "run: jump 5\n" - "run: 0x0000000c opcode: 05\n" - ); - CHECK_TRACE_DOESNT_CONTAIN("run: 0x00000007 opcode: 05"); -} - -:(before "End Two-Byte Opcodes Starting With 0f") -case 0x8f: { // jump disp32 if !SF and !ZF - const int32_t offset = next32(); - if (!ZF && SF == OF) { - trace(Callstack_depth+1, "run") << "jump " << offset << end(); - EIP += offset; - } - break; -} -case 0x87: { // jump disp32 if !CF and !ZF - const int32_t offset = next(); - if (!CF && !ZF) { - trace(Callstack_depth+1, "run") << "jump " << offset << end(); - EIP += offset; - } - break; -} - -:(code) -void test_jg_disp32_fail() { - ZF = false; - SF = true; - OF = false; - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " 0f 8f 05 00 00 00 \n" // skip 1 instruction - " 05 00 00 00 01 \n" - " 05 00 00 00 02 \n" - ); - CHECK_TRACE_CONTENTS( - "run: 0x00000001 opcode: 0f\n" - "run: 0x00000007 opcode: 05\n" - "run: 0x0000000c opcode: 05\n" - ); - CHECK_TRACE_DOESNT_CONTAIN("run: jump 5"); -} - -//:: jump if greater or equal - -:(before "End Initialize Op Names") -put_new(Name_0f, "8d", "jump disp32 bytes away if greater or equal (signed), if SF == OF (jcc/jge/jnl)"); -put_new(Name_0f, "83", "jump disp32 bytes away if greater or equal (unsigned), if CF is unset (jcc/jae/jnb)"); - -:(code) -void test_jge_disp32_success() { - SF = false; - OF = false; - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " 0f 8d 05 00 00 00 \n" // skip 1 instruction - " 05 00 00 00 01 \n" - " 05 00 00 00 02 \n" - ); - CHECK_TRACE_CONTENTS( - "run: 0x00000001 opcode: 0f\n" - "run: jump 5\n" - "run: 0x0000000c opcode: 05\n" - ); - CHECK_TRACE_DOESNT_CONTAIN("run: 0x00000007 opcode: 05"); -} - -:(before "End Two-Byte Opcodes Starting With 0f") -case 0x8d: { // jump disp32 if !SF - const int32_t offset = next32(); - if (SF == OF) { - trace(Callstack_depth+1, "run") << "jump " << offset << end(); - EIP += offset; - } - break; -} -case 0x83: { // jump disp32 if !CF - const int32_t offset = next32(); - if (!CF) { - trace(Callstack_depth+1, "run") << "jump " << offset << end(); - EIP += offset; - } - break; -} - -:(code) -void test_jge_disp32_fail() { - SF = true; - OF = false; - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " 0f 8d 05 00 00 00 \n" // skip 1 instruction - " 05 00 00 00 01 \n" - " 05 00 00 00 02 \n" - ); - CHECK_TRACE_CONTENTS( - "run: 0x00000001 opcode: 0f\n" - "run: 0x00000007 opcode: 05\n" - "run: 0x0000000c opcode: 05\n" - ); - CHECK_TRACE_DOESNT_CONTAIN("run: jump 5"); -} - -//:: jump if lesser - -:(before "End Initialize Op Names") -put_new(Name_0f, "8c", "jump disp32 bytes away if lesser (signed), if SF != OF (jcc/jl/jnge)"); -put_new(Name_0f, "82", "jump disp32 bytes away if lesser (unsigned), if CF is set (jcc/jb/jnae)"); - -:(code) -void test_jl_disp32_success() { - ZF = false; - SF = true; - OF = false; - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " 0f 8c 05 00 00 00 \n" // skip 1 instruction - " 05 00 00 00 01 \n" - " 05 00 00 00 02 \n" - ); - CHECK_TRACE_CONTENTS( - "run: 0x00000001 opcode: 0f\n" - "run: jump 5\n" - "run: 0x0000000c opcode: 05\n" - ); - CHECK_TRACE_DOESNT_CONTAIN("run: 0x00000007 opcode: 05"); -} - -:(before "End Two-Byte Opcodes Starting With 0f") -case 0x8c: { // jump disp32 if SF and !ZF - const int32_t offset = next32(); - if (SF != OF) { - trace(Callstack_depth+1, "run") << "jump " << offset << end(); - EIP += offset; - } - break; -} -case 0x72: { // jump disp32 if CF - const int32_t offset = next32(); - if (CF) { - trace(Callstack_depth+1, "run") << "jump " << offset << end(); - EIP += offset; - } - break; -} - -:(code) -void test_jl_disp32_fail() { - ZF = false; - SF = false; - OF = false; - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " 0f 8c 05 00 00 00 \n" // skip 1 instruction - " 05 00 00 00 01 \n" - " 05 00 00 00 02 \n" - ); - CHECK_TRACE_CONTENTS( - "run: 0x00000001 opcode: 0f\n" - "run: 0x00000007 opcode: 05\n" - "run: 0x0000000c opcode: 05\n" - ); - CHECK_TRACE_DOESNT_CONTAIN("run: jump 5"); -} - -//:: jump if lesser or equal - -:(before "End Initialize Op Names") -put_new(Name_0f, "8e", "jump disp32 bytes away if lesser or equal (signed), if ZF is set or SF != OF (jcc/jle/jng)"); -put_new(Name_0f, "86", "jump disp8 bytes away if lesser or equal (unsigned), if ZF is set or CF is set (jcc/jbe/jna)"); - -:(code) -void test_jle_disp32_equal() { - ZF = true; - SF = false; - OF = false; - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " 0f 8e 05 00 00 00 \n" // skip 1 instruction - " 05 00 00 00 01 \n" - " 05 00 00 00 02 \n" - ); - CHECK_TRACE_CONTENTS( - "run: 0x00000001 opcode: 0f\n" - "run: jump 5\n" - "run: 0x0000000c opcode: 05\n" - ); - CHECK_TRACE_DOESNT_CONTAIN("run: 0x00000007 opcode: 05"); -} - -:(code) -void test_jle_disp32_lesser() { - ZF = false; - SF = true; - OF = false; - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " 0f 8e 05 00 00 00 \n" // skip 1 instruction - " 05 00 00 00 01 \n" - " 05 00 00 00 02 \n" - ); - CHECK_TRACE_CONTENTS( - "run: 0x00000001 opcode: 0f\n" - "run: jump 5\n" - "run: 0x0000000c opcode: 05\n" - ); - CHECK_TRACE_DOESNT_CONTAIN("run: 0x00000007 opcode: 05"); -} - -:(before "End Two-Byte Opcodes Starting With 0f") -case 0x8e: { // jump disp32 if SF or ZF - const int32_t offset = next32(); - if (ZF || SF != OF) { - trace(Callstack_depth+1, "run") << "jump " << offset << end(); - EIP += offset; - } - break; -} -case 0x86: { // jump disp32 if ZF or CF - const int32_t offset = next32(); - if (ZF || CF) { - trace(Callstack_depth+1, "run") << "jump " << offset << end(); - EIP += offset; - } - break; -} - -:(code) -void test_jle_disp32_greater() { - ZF = false; - SF = false; - OF = false; - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " 0f 8e 05 00 00 00 \n" // skip 1 instruction - " 05 00 00 00 01 \n" - " 05 00 00 00 02 \n" - ); - CHECK_TRACE_CONTENTS( - "run: 0x00000001 opcode: 0f\n" - "run: 0x00000007 opcode: 05\n" - "run: 0x0000000c opcode: 05\n" - ); - CHECK_TRACE_DOESNT_CONTAIN("run: jump 5"); -} diff --git a/subx/019functions.cc b/subx/019functions.cc deleted file mode 100644 index 81e99e6e..00000000 --- a/subx/019functions.cc +++ /dev/null @@ -1,122 +0,0 @@ -//:: call - -:(before "End Initialize Op Names") -put_new(Name, "e8", "call disp32 (call)"); - -:(code) -void test_call_disp32() { - Mem.push_back(vma(0xbd000000)); // manually allocate memory - Reg[ESP].u = 0xbd000064; - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " e8 a0 00 00 00 \n" // call function offset at 0x000000a0 - // next EIP is 6 - ); - CHECK_TRACE_CONTENTS( - "run: call imm32 0x000000a0\n" - "run: decrementing ESP to 0xbd000060\n" - "run: pushing value 0x00000006\n" - "run: jumping to 0x000000a6\n" - ); -} - -:(before "End Single-Byte Opcodes") -case 0xe8: { // call disp32 relative to next EIP - const int32_t offset = next32(); - ++Callstack_depth; - trace(Callstack_depth+1, "run") << "call imm32 0x" << HEXWORD << offset << end(); -//? cerr << "push: EIP: " << EIP << " => " << Reg[ESP].u << '\n'; - push(EIP); - EIP += offset; - trace(Callstack_depth+1, "run") << "jumping to 0x" << HEXWORD << EIP << end(); - break; -} - -//: - -:(code) -void test_call_r32() { - Mem.push_back(vma(0xbd000000)); // manually allocate memory - Reg[ESP].u = 0xbd000064; - Reg[EBX].u = 0x000000a0; - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " ff d3 \n" // call function offset at EBX - // next EIP is 3 - ); - CHECK_TRACE_CONTENTS( - "run: call to r/m32\n" - "run: r/m32 is EBX\n" - "run: decrementing ESP to 0xbd000060\n" - "run: pushing value 0x00000003\n" - "run: jumping to 0x000000a3\n" - ); -} - -:(before "End Op ff Subops") -case 2: { // call function pointer at r/m32 - trace(Callstack_depth+1, "run") << "call to r/m32" << end(); - const int32_t* offset = effective_address(modrm); - push(EIP); - EIP += *offset; - trace(Callstack_depth+1, "run") << "jumping to 0x" << HEXWORD << EIP << end(); - ++Callstack_depth; - break; -} - -:(code) -void test_call_mem_at_r32() { - Mem.push_back(vma(0xbd000000)); // manually allocate memory - Reg[ESP].u = 0xbd000064; - Reg[EBX].u = 0x2000; - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " ff 13 \n" // call function offset at *EBX - // next EIP is 3 - "== data 0x2000\n" - "a0 00 00 00\n" // 0x000000a0 - ); - CHECK_TRACE_CONTENTS( - "run: call to r/m32\n" - "run: effective address is 0x00002000 (EBX)\n" - "run: decrementing ESP to 0xbd000060\n" - "run: pushing value 0x00000003\n" - "run: jumping to 0x000000a3\n" - ); -} - -//:: ret - -:(before "End Initialize Op Names") -put_new(Name, "c3", "return from most recent unfinished call (ret)"); - -:(code) -void test_ret() { - Mem.push_back(vma(0xbd000000)); // manually allocate memory - Reg[ESP].u = 0xbd000064; - write_mem_u32(Reg[ESP].u, 0x10); - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " c3 \n" // return - "== data 0x2000\n" - "10 00 00 00\n" // 0x00000010 - ); - CHECK_TRACE_CONTENTS( - "run: return\n" - "run: popping value 0x00000010\n" - "run: jumping to 0x00000010\n" - ); -} - -:(before "End Single-Byte Opcodes") -case 0xc3: { // return from a call - trace(Callstack_depth+1, "run") << "return" << end(); - --Callstack_depth; - EIP = pop(); - trace(Callstack_depth+1, "run") << "jumping to 0x" << HEXWORD << EIP << end(); - break; -} diff --git a/subx/020syscalls.cc b/subx/020syscalls.cc deleted file mode 100644 index 16495411..00000000 --- a/subx/020syscalls.cc +++ /dev/null @@ -1,123 +0,0 @@ -:(before "End Initialize Op Names") -put_new(Name, "cd", "software interrupt (int)"); - -:(before "End Single-Byte Opcodes") -case 0xcd: { // int imm8 (software interrupt) - trace(Callstack_depth+1, "run") << "syscall" << end(); - uint8_t code = next(); - if (code != 0x80) { - raise << "Unimplemented interrupt code " << HEXBYTE << code << '\n' << end(); - raise << " Only `int 80h` supported for now.\n" << end(); - break; - } - process_int80(); - break; -} - -:(code) -void process_int80() { - switch (Reg[EAX].u) { - case 1: - exit(/*exit code*/Reg[EBX].u); - break; - case 3: - trace(Callstack_depth+1, "run") << "read: " << Reg[EBX].u << ' ' << Reg[ECX].u << ' ' << Reg[EDX].u << end(); - Reg[EAX].i = read(/*file descriptor*/Reg[EBX].u, /*memory buffer*/mem_addr_u8(Reg[ECX].u), /*size*/Reg[EDX].u); - trace(Callstack_depth+1, "run") << "result: " << Reg[EAX].i << end(); - if (Reg[EAX].i == -1) raise << "read: " << strerror(errno) << '\n' << end(); - break; - case 4: - trace(Callstack_depth+1, "run") << "write: " << Reg[EBX].u << ' ' << Reg[ECX].u << ' ' << Reg[EDX].u << end(); - trace(Callstack_depth+1, "run") << Reg[ECX].u << " => " << mem_addr_string(Reg[ECX].u, Reg[EDX].u) << end(); - Reg[EAX].i = write(/*file descriptor*/Reg[EBX].u, /*memory buffer*/mem_addr_u8(Reg[ECX].u), /*size*/Reg[EDX].u); - trace(Callstack_depth+1, "run") << "result: " << Reg[EAX].i << end(); - if (Reg[EAX].i == -1) raise << "write: " << strerror(errno) << '\n' << end(); - break; - case 5: { - check_flags(ECX); - check_mode(EDX); - trace(Callstack_depth+1, "run") << "open: " << Reg[EBX].u << ' ' << Reg[ECX].u << end(); - trace(Callstack_depth+1, "run") << Reg[EBX].u << " => " << mem_addr_kernel_string(Reg[EBX].u) << end(); - Reg[EAX].i = open(/*filename*/mem_addr_kernel_string(Reg[EBX].u), /*flags*/Reg[ECX].u, /*mode*/0640); - trace(Callstack_depth+1, "run") << "result: " << Reg[EAX].i << end(); - if (Reg[EAX].i == -1) raise << "open: " << strerror(errno) << '\n' << end(); - break; - } - case 6: - trace(Callstack_depth+1, "run") << "close: " << Reg[EBX].u << end(); - Reg[EAX].i = close(/*file descriptor*/Reg[EBX].u); - trace(Callstack_depth+1, "run") << "result: " << Reg[EAX].i << end(); - if (Reg[EAX].i == -1) raise << "close: " << strerror(errno) << '\n' << end(); - break; - case 8: - check_mode(ECX); - trace(Callstack_depth+1, "run") << "creat: " << Reg[EBX].u << end(); - trace(Callstack_depth+1, "run") << Reg[EBX].u << " => " << mem_addr_kernel_string(Reg[EBX].u) << end(); - Reg[EAX].i = creat(/*filename*/mem_addr_kernel_string(Reg[EBX].u), /*mode*/0640); - trace(Callstack_depth+1, "run") << "result: " << Reg[EAX].i << end(); - if (Reg[EAX].i == -1) raise << "creat: " << strerror(errno) << '\n' << end(); - break; - case 10: - trace(Callstack_depth+1, "run") << "unlink: " << Reg[EBX].u << end(); - trace(Callstack_depth+1, "run") << Reg[EBX].u << " => " << mem_addr_kernel_string(Reg[EBX].u) << end(); - Reg[EAX].i = unlink(/*filename*/mem_addr_kernel_string(Reg[EBX].u)); - trace(Callstack_depth+1, "run") << "result: " << Reg[EAX].i << end(); - if (Reg[EAX].i == -1) raise << "unlink: " << strerror(errno) << '\n' << end(); - break; - case 38: - trace(Callstack_depth+1, "run") << "rename: " << Reg[EBX].u << " -> " << Reg[ECX].u << end(); - trace(Callstack_depth+1, "run") << Reg[EBX].u << " => " << mem_addr_kernel_string(Reg[EBX].u) << end(); - trace(Callstack_depth+1, "run") << Reg[ECX].u << " => " << mem_addr_kernel_string(Reg[ECX].u) << end(); - Reg[EAX].i = rename(/*old filename*/mem_addr_kernel_string(Reg[EBX].u), /*new filename*/mem_addr_kernel_string(Reg[ECX].u)); - trace(Callstack_depth+1, "run") << "result: " << Reg[EAX].i << end(); - if (Reg[EAX].i == -1) raise << "rename: " << strerror(errno) << '\n' << end(); - break; - case 90: // mmap: allocate memory outside existing segment allocations - trace(Callstack_depth+1, "run") << "mmap: allocate new segment" << end(); - // Ignore most arguments for now: address hint, protection flags, sharing flags, fd, offset. - // We only support anonymous maps. - Reg[EAX].u = new_segment(/*length*/read_mem_u32(Reg[EBX].u+0x4)); - trace(Callstack_depth+1, "run") << "result: " << Reg[EAX].u << end(); - break; - default: - raise << HEXWORD << EIP << ": unimplemented syscall " << Reg[EAX].u << '\n' << end(); - } -} - -// SubX is oblivious to file permissions, directories, symbolic links, terminals, and much else besides. -// Also ignoring any concurrency considerations for now. -void check_flags(int reg) { - uint32_t flags = Reg[reg].u; - if (flags != ((flags & O_RDONLY) | (flags & O_WRONLY))) { - 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"; - exit(1); - } - if ((flags & O_RDONLY) && (flags & O_WRONLY)) { - 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"; - exit(1); - } -} - -void check_mode(int reg) { - if (Reg[reg].u != 0600) { - cerr << HEXWORD << EIP << ": SubX is oblivious to file permissions; register " << reg << " must be 0.\n"; - exit(1); - } -} - -:(before "End Globals") -// Very primitive/fixed/insecure mmap segments for now. -uint32_t Segments_allocated_above = END_HEAP; -:(code) -// always allocate multiples of the segment size -uint32_t new_segment(uint32_t length) { - assert(length > 0); - uint32_t result = (Segments_allocated_above - length) & 0xff000000; // same number of zeroes as SEGMENT_ALIGNMENT - if (result <= START_HEAP) { - raise << "Allocated too many segments; the VM ran out of memory. " - << "Maybe SEGMENT_ALIGNMENT can be smaller?\n" << die(); - } - Mem.push_back(vma(result, result+length)); - Segments_allocated_above = result; - return result; -} diff --git a/subx/021byte_addressing.cc b/subx/021byte_addressing.cc deleted file mode 100644 index a0b36776..00000000 --- a/subx/021byte_addressing.cc +++ /dev/null @@ -1,176 +0,0 @@ -//: SubX mostly deals with instructions operating on 32-bit operands, but we -//: still need to deal with raw bytes for strings and so on. - -//: Unfortunately the register encodings when dealing with bytes are a mess. -//: We need a special case for them. -:(code) -string rname_8bit(uint8_t r) { - switch (r) { - case 0: return "AL"; // lowest byte of EAX - case 1: return "CL"; // lowest byte of ECX - case 2: return "DL"; // lowest byte of EDX - case 3: return "BL"; // lowest byte of EBX - case 4: return "AH"; // second lowest byte of EAX - case 5: return "CH"; // second lowest byte of ECX - case 6: return "DH"; // second lowest byte of EDX - case 7: return "BH"; // second lowest byte of EBX - default: raise << "invalid 8-bit register " << r << '\n' << end(); return ""; - } -} - -uint8_t* effective_byte_address(uint8_t modrm) { - uint8_t mod = (modrm>>6); - uint8_t rm = modrm & 0x7; - if (mod == 3) { - // select an 8-bit register - trace(Callstack_depth+1, "run") << "r/m8 is " << rname_8bit(rm) << end(); - return reg_8bit(rm); - } - // the rest is as usual - return mem_addr_u8(effective_address_number(modrm)); -} - -uint8_t* reg_8bit(uint8_t rm) { - uint8_t* result = reinterpret_cast<uint8_t*>(&Reg[rm & 0x3].i); // _L register - if (rm & 0x4) - ++result; // _H register; assumes host is little-endian - return result; -} - -:(before "End Initialize Op Names") -put_new(Name, "88", "copy r8 to r8/m8-at-r32"); - -:(code) -void test_copy_r8_to_mem_at_r32() { - Reg[EBX].i = 0x224488ab; - Reg[EAX].i = 0x2000; - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " 88 18 \n" // copy BL to the byte at *EAX - // ModR/M in binary: 00 (indirect mode) 011 (src BL) 000 (dest EAX) - "== data 0x2000\n" - "f0 cc bb aa\n" - ); - CHECK_TRACE_CONTENTS( - "run: copy BL to r8/m8-at-r32\n" - "run: effective address is 0x00002000 (EAX)\n" - "run: storing 0xab\n" - ); - CHECK_EQ(0xaabbccab, read_mem_u32(0x2000)); -} - -:(before "End Single-Byte Opcodes") -case 0x88: { // copy r8 to r/m8 - const uint8_t modrm = next(); - const uint8_t rsrc = (modrm>>3)&0x7; - trace(Callstack_depth+1, "run") << "copy " << rname_8bit(rsrc) << " to r8/m8-at-r32" << end(); - // use unsigned to zero-extend 8-bit value to 32 bits - uint8_t* dest = reinterpret_cast<uint8_t*>(effective_byte_address(modrm)); - const uint8_t* src = reg_8bit(rsrc); - *dest = *src; - trace(Callstack_depth+1, "run") << "storing 0x" << HEXBYTE << NUM(*dest) << end(); - break; -} - -//: - -:(before "End Initialize Op Names") -put_new(Name, "8a", "copy r8/m8-at-r32 to r8"); - -:(code) -void test_copy_mem_at_r32_to_r8() { - Reg[EBX].i = 0xaabbcc0f; // one nibble each of lowest byte set to all 0s and all 1s, to maximize value of this test - Reg[EAX].i = 0x2000; - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " 8a 18 \n" // copy just the byte at *EAX to BL - // ModR/M in binary: 00 (indirect mode) 011 (dest EBX) 000 (src EAX) - "== data 0x2000\n" - "ab ff ff ff\n" // 0xab with more data in following bytes - ); - CHECK_TRACE_CONTENTS( - "run: copy r8/m8-at-r32 to BL\n" - "run: effective address is 0x00002000 (EAX)\n" - "run: storing 0xab\n" - // remaining bytes of EBX are *not* cleared - "run: EBX now contains 0xaabbccab\n" - ); -} - -:(before "End Single-Byte Opcodes") -case 0x8a: { // copy r/m8 to r8 - const uint8_t modrm = next(); - const uint8_t rdest = (modrm>>3)&0x7; - trace(Callstack_depth+1, "run") << "copy r8/m8-at-r32 to " << rname_8bit(rdest) << end(); - // use unsigned to zero-extend 8-bit value to 32 bits - const uint8_t* src = reinterpret_cast<uint8_t*>(effective_byte_address(modrm)); - uint8_t* dest = reg_8bit(rdest); - trace(Callstack_depth+1, "run") << "storing 0x" << HEXBYTE << NUM(*src) << end(); - *dest = *src; - const uint8_t rdest_32bit = rdest & 0x3; - trace(Callstack_depth+1, "run") << rname(rdest_32bit) << " now contains 0x" << HEXWORD << Reg[rdest_32bit].u << end(); - break; -} - -:(code) -void test_cannot_copy_byte_to_ESP_EBP_ESI_EDI() { - Reg[ESI].u = 0xaabbccdd; - Reg[EBX].u = 0x11223344; - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " 8a f3 \n" // copy just the byte at *EBX to 8-bit register '6' - // ModR/M in binary: 11 (direct mode) 110 (dest 8-bit 'register 6') 011 (src EBX) - ); - CHECK_TRACE_CONTENTS( - // ensure 8-bit register '6' is DH, not ESI - "run: copy r8/m8-at-r32 to DH\n" - "run: storing 0x44\n" - ); - // ensure ESI is unchanged - CHECK_EQ(Reg[ESI].u, 0xaabbccdd); -} - -//: - -:(before "End Initialize Op Names") -put_new(Name, "c6", "copy imm8 to r8/m8-at-r32 (mov)"); - -:(code) -void test_copy_imm8_to_mem_at_r32() { - Reg[EAX].i = 0x2000; - run( - "== code 0x1\n" - // op ModR/M SIB displacement immediate - " c6 00 dd \n" // copy to the byte at *EAX - // ModR/M in binary: 00 (indirect mode) 000 (unused) 000 (dest EAX) - "== data 0x2000\n" - "f0 cc bb aa\n" - ); - CHECK_TRACE_CONTENTS( - "run: copy imm8 to r8/m8-at-r32\n" - "run: effective address is 0x00002000 (EAX)\n" - "run: storing 0xdd\n" - ); - CHECK_EQ(0xaabbccdd, read_mem_u32(0x2000)); -} - -:(before "End Single-Byte Opcodes") -case 0xc6: { // copy imm8 to r/m8 - const uint8_t modrm = next(); - const uint8_t src = next(); - trace(Callstack_depth+1, "run") << "copy imm8 to r8/m8-at-r32" << end(); - trace(Callstack_depth+1, "run") << "imm8 is 0x" << HEXWORD << NUM(src) << end(); - const uint8_t subop = (modrm>>3)&0x7; // middle 3 'reg opcode' bits - if (subop != 0) { - cerr << "unrecognized subop for opcode c6: " << NUM(subop) << " (only 0/copy currently implemented)\n"; - exit(1); - } - // use unsigned to zero-extend 8-bit value to 32 bits - uint8_t* dest = reinterpret_cast<uint8_t*>(effective_byte_address(modrm)); - *dest = src; - trace(Callstack_depth+1, "run") << "storing 0x" << HEXBYTE << NUM(*dest) << end(); - break; -} diff --git a/subx/022div.cc b/subx/022div.cc deleted file mode 100644 index 15ed89d8..00000000 --- a/subx/022div.cc +++ /dev/null @@ -1,38 +0,0 @@ -//: helper for division operations: sign-extend EAX into EDX - -:(before "End Initialize Op Names") -put_new(Name, "99", "sign-extend EAX into EDX (cdq)"); - -:(code) -void test_cdq() { - Reg[EAX].i = 10; - run( - "== code 0x1\n" - "99\n" - ); - CHECK_TRACE_CONTENTS( - "run: sign-extend EAX into EDX\n" - "run: EDX is now 0x00000000\n" - ); -} - -:(before "End Single-Byte Opcodes") -case 0x99: { // sign-extend EAX into EDX - trace(Callstack_depth+1, "run") << "sign-extend EAX into EDX" << end(); - Reg[EDX].i = (Reg[EAX].i < 0) ? -1 : 0; - trace(Callstack_depth+1, "run") << "EDX is now 0x" << HEXWORD << Reg[EDX].u << end(); - break; -} - -:(code) -void test_cdq_negative() { - Reg[EAX].i = -10; - run( - "== code 0x1\n" - "99\n" - ); - CHECK_TRACE_CONTENTS( - "run: sign-extend EAX into EDX\n" - "run: EDX is now 0xffffffff\n" - ); -} diff --git a/subx/028translate.cc b/subx/028translate.cc deleted file mode 100644 index d3a6a8ac..00000000 --- a/subx/028translate.cc +++ /dev/null @@ -1,212 +0,0 @@ -//: The bedrock level 1 of abstraction is now done, and we're going to start -//: building levels above it that make programming in x86 machine code a -//: little more ergonomic. -//: -//: All levels will be "pass through by default". Whatever they don't -//: understand they will silently pass through to lower levels. -//: -//: Since raw hex bytes of machine code are always possible to inject, SubX is -//: not a language, and we aren't building a compiler. This is something -//: deliberately leakier. Levels are more for improving auditing, checks and -//: error messages rather than for hiding low-level details. - -//: Translator workflow: read 'source' file. Run a series of transforms on it, -//: each passing through what it doesn't understand. The final program should -//: be just machine code, suitable to write to an ELF binary. -//: -//: Higher levels usually transform code on the basis of metadata. - -:(before "End Main") -if (is_equal(argv[1], "translate")) { - START_TRACING_UNTIL_END_OF_SCOPE; - reset(); - // Begin subx translate - program p; - string output_filename; - for (int i = /*skip 'subx translate'*/2; i < argc; ++i) { - if (is_equal(argv[i], "-o")) { - ++i; - if (i >= argc) { - print_translate_usage(); - cerr << "'-o' must be followed by a filename to write results to\n"; - exit(1); - } - output_filename = argv[i]; - } - else { - trace(2, "parse") << argv[i] << end(); - ifstream fin(argv[i]); - if (!fin) { - cerr << "could not open " << argv[i] << '\n'; - return 1; - } - parse(fin, p); - if (trace_contains_errors()) return 1; - } - } - if (p.segments.empty()) { - print_translate_usage(); - cerr << "nothing to do; must provide at least one file to read\n"; - exit(1); - } - if (output_filename.empty()) { - print_translate_usage(); - cerr << "must provide a filename to write to using '-o'\n"; - exit(1); - } - trace(2, "transform") << "begin" << end(); - transform(p); - if (trace_contains_errors()) return 1; - trace(2, "translate") << "begin" << end(); - save_elf(p, output_filename); - if (trace_contains_errors()) { - unlink(output_filename.c_str()); - return 1; - } - // End subx translate - return 0; -} - -:(code) -void print_translate_usage() { - cerr << "Usage: subx translate file1 file2 ... -o output\n"; -} - -// write out a program to a bare-bones ELF file -void save_elf(const program& p, const string& filename) { - ofstream out(filename.c_str(), ios::binary); - save_elf(p, out); - out.close(); -} - -void save_elf(const program& p, ostream& out) { - // validation: stay consistent with the self-hosted translator - if (p.entry == 0) { - raise << "no 'Entry' label found\n" << end(); - return; - } - if (find(p, "data") == NULL) { - raise << "must include a 'data' segment\n" << end(); - return; - } - // processing - write_elf_header(out, p); - for (size_t i = 0; i < p.segments.size(); ++i) - write_segment(p.segments.at(i), out); -} - -void write_elf_header(ostream& out, const program& p) { - char c = '\0'; -#define O(X) c = (X); out.write(&c, sizeof(c)) -// host is required to be little-endian -#define emit(X) out.write(reinterpret_cast<const char*>(&X), sizeof(X)) - //// ehdr - // e_ident - O(0x7f); O(/*E*/0x45); O(/*L*/0x4c); O(/*F*/0x46); - O(0x1); // 32-bit format - O(0x1); // little-endian - O(0x1); O(0x0); - for (size_t i = 0; i < 8; ++i) { O(0x0); } - // e_type - O(0x02); O(0x00); - // e_machine - O(0x03); O(0x00); - // e_version - O(0x01); O(0x00); O(0x00); O(0x00); - // e_entry - uint32_t e_entry = p.entry; - // Override e_entry - emit(e_entry); - // e_phoff -- immediately after ELF header - uint32_t e_phoff = 0x34; - emit(e_phoff); - // e_shoff; unused - uint32_t dummy32 = 0; - emit(dummy32); - // e_flags; unused - emit(dummy32); - // e_ehsize - uint16_t e_ehsize = 0x34; - emit(e_ehsize); - // e_phentsize - uint16_t e_phentsize = 0x20; - emit(e_phentsize); - // e_phnum - uint16_t e_phnum = SIZE(p.segments); - emit(e_phnum); - // e_shentsize - uint16_t dummy16 = 0x0; - emit(dummy16); - // e_shnum - emit(dummy16); - // e_shstrndx - emit(dummy16); - - uint32_t p_offset = /*size of ehdr*/0x34 + SIZE(p.segments)*0x20/*size of each phdr*/; - for (int i = 0; i < SIZE(p.segments); ++i) { - const segment& curr = p.segments.at(i); - //// phdr - // p_type - uint32_t p_type = 0x1; - emit(p_type); - // p_offset - emit(p_offset); - // p_vaddr - uint32_t p_start = curr.start; - emit(p_start); - // p_paddr - emit(p_start); - // p_filesz - uint32_t size = num_words(curr); - assert(p_offset + size < SEGMENT_ALIGNMENT); - emit(size); - // p_memsz - emit(size); - // p_flags - uint32_t p_flags = (curr.name == "code") ? /*r-x*/0x5 : /*rw-*/0x6; - emit(p_flags); - - // p_align - // "As the system creates or augments a process image, it logically copies - // a file's segment to a virtual memory segment. When—and if— the system - // physically reads the file depends on the program's execution behavior, - // system load, and so on. A process does not require a physical page - // unless it references the logical page during execution, and processes - // commonly leave many pages unreferenced. Therefore delaying physical - // reads frequently obviates them, improving system performance. To obtain - // this efficiency in practice, executable and shared object files must - // have segment images whose file offsets and virtual addresses are - // congruent, modulo the page size." -- http://refspecs.linuxbase.org/elf/elf.pdf (page 95) - uint32_t p_align = 0x1000; // default page size on linux - emit(p_align); - if (p_offset % p_align != p_start % p_align) { - 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(); - return; - } - - // prepare for next segment - p_offset += size; - } -#undef O -#undef emit -} - -void write_segment(const segment& s, ostream& out) { - for (int i = 0; i < SIZE(s.lines); ++i) { - const vector<word>& w = s.lines.at(i).words; - for (int j = 0; j < SIZE(w); ++j) { - uint8_t x = hex_byte(w.at(j).data); // we're done with metadata by this point - out.write(reinterpret_cast<const char*>(&x), /*sizeof(byte)*/1); - } - } -} - -uint32_t num_words(const segment& s) { - uint32_t sum = 0; - for (int i = 0; i < SIZE(s.lines); ++i) - sum += SIZE(s.lines.at(i).words); - return sum; -} - -:(before "End Includes") -using std::ios; diff --git a/subx/029transforms.cc b/subx/029transforms.cc deleted file mode 100644 index a6e12502..00000000 --- a/subx/029transforms.cc +++ /dev/null @@ -1,65 +0,0 @@ -//: Ordering transforms is a well-known hard problem when building compilers. -//: In our case we also have the additional notion of layers. The ordering of -//: layers can have nothing in common with the ordering of transforms when -//: SubX is tangled and run. This can be confusing for readers, particularly -//: if later layers start inserting transforms at arbitrary points between -//: transforms introduced earlier. Over time adding transforms can get harder -//: and harder, having to meet the constraints of everything that's come -//: before. It's worth thinking about organization up-front so the ordering is -//: easy to hold in our heads, and it's obvious where to add a new transform. -//: Some constraints: -//: -//: 1. Layers force us to build SubX bottom-up; since we want to be able to -//: build and run SubX after stopping loading at any layer, the overall -//: organization has to be to introduce primitives before we start using -//: them. -//: -//: 2. Transforms usually need to be run top-down, converting high-level -//: representations to low-level ones so that low-level layers can be -//: oblivious to them. -//: -//: 3. When running we'd often like new representations to be checked before -//: they are transformed away. The whole reason for new representations is -//: often to add new kinds of automatic checking for our machine code -//: programs. -//: -//: Putting these constraints together, we'll use the following broad -//: organization: -//: -//: a) We'll divide up our transforms into "levels", each level consisting -//: of multiple transforms, and dealing in some new set of representational -//: ideas. Levels will be added in reverse order to the one their transforms -//: will be run in. -//: -//: To run all transforms: -//: Load transforms for level n -//: Load transforms for level n-1 -//: ... -//: Load transforms for level 2 -//: Run code at level 1 -//: -//: b) *Within* a level we'll usually introduce transforms in the order -//: they're run in. -//: -//: To run transforms for level n: -//: Perform transform of layer l -//: Perform transform of layer l+1 -//: ... -//: -//: c) Within a level it's often most natural to introduce a new -//: representation by showing how it's transformed to the level below. To -//: make such exceptions more obvious checks usually won't be first-class -//: transforms; instead code that keeps the program unmodified will run -//: within transforms before they mutate the program. As an example: -//: -//: Layer l introduces a transform -//: Layer l+1 adds precondition checks for the transform -//: -//: This may all seem abstract, but will hopefully make sense over time. The -//: goals are basically to always have a working program after any layer, to -//: have the order of layers make narrative sense, and to order transforms -//: correctly at runtime. - -:(before "End One-time Setup") -// Begin Transforms -// End Transforms diff --git a/subx/030---operands.cc b/subx/030---operands.cc deleted file mode 100644 index 5203201e..00000000 --- a/subx/030---operands.cc +++ /dev/null @@ -1,539 +0,0 @@ -//: Beginning of "level 2": tagging bytes with metadata around what field of -//: an x86 instruction they're for. -//: -//: The x86 instruction set is variable-length, and how a byte is interpreted -//: affects later instruction boundaries. A lot of the pain in programming -//: machine code stems from computer and programmer going out of sync on what -//: a byte means. The miscommunication is usually not immediately caught, and -//: metastasizes at runtime into kilobytes of misinterpreted instructions. -//: -//: To mitigate these issues, we'll start programming in terms of logical -//: operands rather than physical bytes. Some operands are smaller than a -//: byte, and others may consist of multiple bytes. This layer will correctly -//: pack and order the bytes corresponding to the operands in an instruction. - -:(before "End Help Texts") -put_new(Help, "instructions", - "Each x86 instruction consists of an instruction or opcode and some number\n" - "of operands.\n" - "Each operand has a type. An instruction won't have more than one operand of\n" - "any type.\n" - "Each instruction has some set of allowed operand types. It'll reject others.\n" - "The complete list of operand types: mod, subop, r32 (register), rm32\n" - "(register or memory), scale, index, base, disp8, disp16, disp32, imm8,\n" - "imm32.\n" - "Each of these has its own help page. Try reading 'subx help mod' next.\n" -); -:(before "End Help Contents") -cerr << " instructions\n"; - -:(code) -void test_pack_immediate_constants() { - run( - "== code 0x1\n" - "bb 0x2a/imm32\n" - ); - CHECK_TRACE_CONTENTS( - "transform: packing instruction 'bb 0x2a/imm32'\n" - "transform: instruction after packing: 'bb 2a 00 00 00'\n" - "run: copy imm32 0x0000002a to EBX\n" - ); -} - -//: complete set of valid operand types - -:(before "End Globals") -set<string> Instruction_operands; -:(before "End One-time Setup") -Instruction_operands.insert("subop"); -Instruction_operands.insert("mod"); -Instruction_operands.insert("rm32"); -Instruction_operands.insert("base"); -Instruction_operands.insert("index"); -Instruction_operands.insert("scale"); -Instruction_operands.insert("r32"); -Instruction_operands.insert("disp8"); -Instruction_operands.insert("disp16"); -Instruction_operands.insert("disp32"); -Instruction_operands.insert("imm8"); -Instruction_operands.insert("imm32"); - -:(before "End Help Texts") -init_operand_type_help(); -:(code) -void init_operand_type_help() { - put(Help, "mod", - "2-bit operand controlling the _addressing mode_ of many instructions,\n" - "to determine how to compute the _effective address_ to look up memory at\n" - "based on the 'rm32' operand and potentially others.\n" - "\n" - "If mod = 3, just operate on the contents of the register specified by rm32\n" - " (direct mode).\n" - "If mod = 2, effective address is usually* rm32 + disp32\n" - " (indirect mode with displacement).\n" - "If mod = 1, effective address is usually* rm32 + disp8\n" - " (indirect mode with displacement).\n" - "If mod = 0, effective address is usually* rm32 (indirect mode).\n" - "(* - The exception is when rm32 is '4'. Register 4 is the stack pointer (ESP).\n" - " Using it as an address gets more involved. For more details,\n" - " try reading the help pages for 'base', 'index' and 'scale'.)\n" - "\n" - "For complete details, spend some time with two tables in the IA-32 software\n" - "developer's manual that are also included in this repo:\n" - " - modrm.pdf: volume 2, table 2-2, \"32-bit addressing with the ModR/M byte.\".\n" - " - sib.pdf: volume 2, table 2-3, \"32-bit addressing with the SIB byte.\".\n" - ); - put(Help, "subop", - "Additional 3-bit operand for determining the instruction when the opcode\n" - "is 81, 8f, d3, f7 or ff.\n" - "Can't coexist with operand of type 'r32' in a single instruction, because\n" - "the two use the same bits.\n" - ); - put(Help, "r32", - "3-bit operand specifying a register operand used directly, without any further addressing modes.\n" - ); - put(Help, "rm32", - "32-bit value in register or memory. The precise details of its construction\n" - "depend on the eponymous 3-bit 'rm32' operand, the 'mod' operand, and also\n" - "potentially the 'SIB' operands ('scale', 'index' and 'base') and a displacement\n" - "('disp8' or 'disp32').\n" - "\n" - "For complete details, spend some time with two tables in the IA-32 software\n" - "developer's manual that are also included in this repo:\n" - " - modrm.pdf: volume 2, table 2-2, \"32-bit addressing with the ModR/M byte.\".\n" - " - sib.pdf: volume 2, table 2-3, \"32-bit addressing with the SIB byte.\".\n" - ); - put(Help, "base", - "Additional 3-bit operand (when 'rm32' is 4, unless 'mod' is 3) specifying the\n" - "register containing an address to look up.\n" - "This address may be further modified by 'index' and 'scale' operands.\n" - " effective address = base + index*scale + displacement (disp8 or disp32)\n" - "For complete details, spend some time with the IA-32 software developer's manual,\n" - "volume 2, table 2-3, \"32-bit addressing with the SIB byte\".\n" - "It is included in this repository as 'sib.pdf'.\n" - ); - put(Help, "index", - "Optional 3-bit operand (when 'rm32' is 4 unless 'mod' is 3) that can be added to\n" - "the 'base' operand to compute the 'effective address' at which to look up memory.\n" - " effective address = base + index*scale + displacement (disp8 or disp32)\n" - "For complete details, spend some time with the IA-32 software developer's manual,\n" - "volume 2, table 2-3, \"32-bit addressing with the SIB byte\".\n" - "It is included in this repository as 'sib.pdf'.\n" - ); - put(Help, "scale", - "Optional 2-bit operand (when 'rm32' is 4 unless 'mod' is 3) that encodes a\n" - "power of 2 to be multiplied to the 'index' operand before adding the result to\n" - "the 'base' operand to compute the _effective address_ to operate on.\n" - " effective address = base + index * scale + displacement (disp8 or disp32)\n" - "\n" - "When scale is 0, use index unmodified.\n" - "When scale is 1, multiply index by 2.\n" - "When scale is 2, multiply index by 4.\n" - "When scale is 3, multiply index by 8.\n" - "\n" - "For complete details, spend some time with the IA-32 software developer's manual,\n" - "volume 2, table 2-3, \"32-bit addressing with the SIB byte\".\n" - "It is included in this repository as 'sib.pdf'.\n" - ); - put(Help, "disp8", - "8-bit value to be added in many instructions.\n" - ); - put(Help, "disp16", - "16-bit value to be added in many instructions.\n" - "Currently not used in any SubX instructions.\n" - ); - put(Help, "disp32", - "32-bit value to be added in many instructions.\n" - ); - put(Help, "imm8", - "8-bit value for many instructions.\n" - ); - put(Help, "imm32", - "32-bit value for many instructions.\n" - ); -} - -//:: transform packing operands into bytes in the right order - -:(after "Begin Transforms") -// Begin Level-2 Transforms -Transform.push_back(pack_operands); -// End Level-2 Transforms - -:(code) -void pack_operands(program& p) { - if (p.segments.empty()) return; - segment& code = *find(p, "code"); - // Pack Operands(segment code) - trace(3, "transform") << "-- pack operands" << end(); - for (int i = 0; i < SIZE(code.lines); ++i) { - line& inst = code.lines.at(i); - if (all_hex_bytes(inst)) continue; - trace(99, "transform") << "packing instruction '" << to_string(/*with metadata*/inst) << "'" << end(); - pack_operands(inst); - trace(99, "transform") << "instruction after packing: '" << to_string(/*without metadata*/inst.words) << "'" << end(); - } -} - -void pack_operands(line& inst) { - line new_inst; - add_opcodes(inst, new_inst); - add_modrm_byte(inst, new_inst); - add_sib_byte(inst, new_inst); - add_disp_bytes(inst, new_inst); - add_imm_bytes(inst, new_inst); - inst.words.swap(new_inst.words); -} - -void add_opcodes(const line& in, line& out) { - out.words.push_back(in.words.at(0)); - if (in.words.at(0).data == "0f" || in.words.at(0).data == "f2" || in.words.at(0).data == "f3") - out.words.push_back(in.words.at(1)); - if (in.words.at(0).data == "f3" && in.words.at(1).data == "0f") - out.words.push_back(in.words.at(2)); - if (in.words.at(0).data == "f2" && in.words.at(1).data == "0f") - out.words.push_back(in.words.at(2)); -} - -void add_modrm_byte(const line& in, line& out) { - uint8_t mod=0, reg_subop=0, rm32=0; - bool emit = false; - for (int i = 0; i < SIZE(in.words); ++i) { - const word& curr = in.words.at(i); - if (has_operand_metadata(curr, "mod")) { - mod = hex_byte(curr.data); - emit = true; - } - else if (has_operand_metadata(curr, "rm32")) { - rm32 = hex_byte(curr.data); - emit = true; - } - else if (has_operand_metadata(curr, "r32")) { - reg_subop = hex_byte(curr.data); - emit = true; - } - else if (has_operand_metadata(curr, "subop")) { - reg_subop = hex_byte(curr.data); - emit = true; - } - } - if (emit) - out.words.push_back(hex_byte_text((mod << 6) | (reg_subop << 3) | rm32)); -} - -void add_sib_byte(const line& in, line& out) { - uint8_t scale=0, index=0, base=0; - bool emit = false; - for (int i = 0; i < SIZE(in.words); ++i) { - const word& curr = in.words.at(i); - if (has_operand_metadata(curr, "scale")) { - scale = hex_byte(curr.data); - emit = true; - } - else if (has_operand_metadata(curr, "index")) { - index = hex_byte(curr.data); - emit = true; - } - else if (has_operand_metadata(curr, "base")) { - base = hex_byte(curr.data); - emit = true; - } - } - if (emit) - out.words.push_back(hex_byte_text((scale << 6) | (index << 3) | base)); -} - -void add_disp_bytes(const line& in, line& out) { - for (int i = 0; i < SIZE(in.words); ++i) { - const word& curr = in.words.at(i); - if (has_operand_metadata(curr, "disp8")) - emit_hex_bytes(out, curr, 1); - if (has_operand_metadata(curr, "disp16")) - emit_hex_bytes(out, curr, 2); - else if (has_operand_metadata(curr, "disp32")) - emit_hex_bytes(out, curr, 4); - } -} - -void add_imm_bytes(const line& in, line& out) { - for (int i = 0; i < SIZE(in.words); ++i) { - const word& curr = in.words.at(i); - if (has_operand_metadata(curr, "imm8")) - emit_hex_bytes(out, curr, 1); - else if (has_operand_metadata(curr, "imm32")) - emit_hex_bytes(out, curr, 4); - } -} - -void emit_hex_bytes(line& out, const word& w, int num) { - assert(num <= 4); - bool is_number = looks_like_hex_int(w.data); - if (num == 1 || !is_number) { - out.words.push_back(w); // preserve existing metadata - if (is_number) - out.words.back().data = hex_byte_to_string(parse_int(w.data)); - return; - } - emit_hex_bytes(out, static_cast<uint32_t>(parse_int(w.data)), num); -} - -void emit_hex_bytes(line& out, uint32_t val, int num) { - assert(num <= 4); - for (int i = 0; i < num; ++i) { - out.words.push_back(hex_byte_text(val & 0xff)); - val = val >> 8; - } -} - -word hex_byte_text(uint8_t val) { - word result; - result.data = hex_byte_to_string(val); - result.original = result.data+"/auto"; - return result; -} - -string hex_byte_to_string(uint8_t val) { - ostringstream out; - // uint8_t prints without padding, but int8_t will expand to 32 bits again - out << HEXBYTE << NUM(val); - return out.str(); -} - -string to_string(const vector<word>& in) { - ostringstream out; - for (int i = 0; i < SIZE(in); ++i) { - if (i > 0) out << ' '; - out << in.at(i).data; - } - return out.str(); -} - -:(before "End Unit Tests") -void test_preserve_metadata_when_emitting_single_byte() { - word in; - in.data = "f0"; - in.original = "f0/foo"; - line out; - emit_hex_bytes(out, in, 1); - CHECK_EQ(out.words.at(0).data, "f0"); - CHECK_EQ(out.words.at(0).original, "f0/foo"); -} - -:(code) -void test_pack_disp8() { - run( - "== code 0x1\n" - "74 2/disp8\n" // jump 2 bytes away if ZF is set - ); - CHECK_TRACE_CONTENTS( - "transform: packing instruction '74 2/disp8'\n" - "transform: instruction after packing: '74 02'\n" - ); -} - -void test_pack_disp8_negative() { - transform( - "== code 0x1\n" - // running this will cause an infinite loop - "74 -1/disp8\n" // jump 1 byte before if ZF is set - ); - CHECK_TRACE_CONTENTS( - "transform: packing instruction '74 -1/disp8'\n" - "transform: instruction after packing: '74 ff'\n" - ); -} - -//: helper for scenario -void transform(const string& text_bytes) { - program p; - istringstream in(text_bytes); - parse(in, p); - if (trace_contains_errors()) return; - transform(p); -} - -void test_pack_modrm_imm32() { - run( - "== code 0x1\n" - // instruction effective address operand displacement immediate\n" - // op subop mod rm32 base index scale r32\n" - // 1-3 bytes 3 bits 2 bits 3 bits 3 bits 3 bits 2 bits 2 bits 0/1/2/4 bytes 0/1/2/4 bytes\n" - " 81 0/add/subop 3/mod/direct 3/ebx/rm32 1/imm32 \n" // add 1 to EBX - ); - CHECK_TRACE_CONTENTS( - "transform: packing instruction '81 0/add/subop 3/mod/direct 3/ebx/rm32 1/imm32'\n" - "transform: instruction after packing: '81 c3 01 00 00 00'\n" - ); -} - -void test_pack_imm32_large() { - run( - "== code 0x1\n" - "b9 0x080490a7/imm32\n" - ); - CHECK_TRACE_CONTENTS( - "transform: packing instruction 'b9 0x080490a7/imm32'\n" - "transform: instruction after packing: 'b9 a7 90 04 08'\n" - ); -} - -void test_pack_immediate_constants_hex() { - run( - "== code 0x1\n" - "b9 0x2a/imm32\n" - ); - CHECK_TRACE_CONTENTS( - "transform: packing instruction 'b9 0x2a/imm32'\n" - "transform: instruction after packing: 'b9 2a 00 00 00'\n" - "run: copy imm32 0x0000002a to ECX\n" - ); -} - -void test_pack_silently_ignores_non_hex() { - Hide_errors = true; - transform( - "== code 0x1\n" - "b9 foo/imm32\n" - ); - CHECK_TRACE_CONTENTS( - "transform: packing instruction 'b9 foo/imm32'\n" - // no change (we're just not printing metadata to the trace) - "transform: instruction after packing: 'b9 foo'\n" - ); -} - -void test_pack_flags_bad_hex() { - Hide_errors = true; - run( - "== code 0x1\n" - "b9 0xfoo/imm32\n" - ); - CHECK_TRACE_CONTENTS( - "error: not a number: 0xfoo\n" - ); -} - -void test_pack_flags_uppercase_hex() { - Hide_errors = true; - run( - "== code 0x1\n" - "b9 0xAb/imm32\n" - ); - CHECK_TRACE_CONTENTS( - "error: uppercase hex not allowed: 0xAb\n" - ); -} - -//:: helpers - -bool all_hex_bytes(const line& inst) { - for (int i = 0; i < SIZE(inst.words); ++i) - if (!is_hex_byte(inst.words.at(i))) - return false; - return true; -} - -bool is_hex_byte(const word& curr) { - if (contains_any_operand_metadata(curr)) - return false; - if (SIZE(curr.data) != 2) - return false; - if (curr.data.find_first_not_of("0123456789abcdef") != string::npos) - return false; - return true; -} - -bool contains_any_operand_metadata(const word& word) { - for (int i = 0; i < SIZE(word.metadata); ++i) - if (Instruction_operands.find(word.metadata.at(i)) != Instruction_operands.end()) - return true; - return false; -} - -bool has_operand_metadata(const line& inst, const string& m) { - bool result = false; - for (int i = 0; i < SIZE(inst.words); ++i) { - if (!has_operand_metadata(inst.words.at(i), m)) continue; - if (result) { - raise << "'" << to_string(inst) << "' has conflicting " << m << " operands\n" << end(); - return false; - } - result = true; - } - return result; -} - -bool has_operand_metadata(const word& w, const string& m) { - bool result = false; - bool metadata_found = false; - for (int i = 0; i < SIZE(w.metadata); ++i) { - const string& curr = w.metadata.at(i); - if (Instruction_operands.find(curr) == Instruction_operands.end()) continue; // ignore unrecognized metadata - if (metadata_found) { - raise << "'" << w.original << "' has conflicting operand types; it should have only one\n" << end(); - return false; - } - metadata_found = true; - result = (curr == m); - } - return result; -} - -word metadata(const line& inst, const string& m) { - for (int i = 0; i < SIZE(inst.words); ++i) - if (has_operand_metadata(inst.words.at(i), m)) - return inst.words.at(i); - assert(false); -} - -bool looks_like_hex_int(const string& s) { - if (s.empty()) return false; - if (s.at(0) == '-' || s.at(0) == '+') return true; - if (isdigit(s.at(0))) return true; // includes '0x' prefix - // End looks_like_hex_int(s) Detectors - return false; -} - -string to_string(const line& inst) { - ostringstream out; - for (int i = 0; i < SIZE(inst.words); ++i) { - if (i > 0) out << ' '; - out << inst.words.at(i).original; - } - return out.str(); -} - -int32_t parse_int(const string& s) { - if (s.empty()) return 0; - if (contains_uppercase(s)) { - raise << "uppercase hex not allowed: " << s << '\n' << end(); - return 0; - } - istringstream in(s); - in >> std::hex; - if (s.at(0) == '-') { - int32_t result = 0; - in >> result; - if (!in || !in.eof()) { - raise << "not a number: " << s << '\n' << end(); - return 0; - } - return result; - } - uint32_t uresult = 0; - in >> uresult; - if (!in || !in.eof()) { - raise << "not a number: " << s << '\n' << end(); - return 0; - } - return static_cast<int32_t>(uresult); -} -:(before "End Unit Tests") -void test_parse_int() { - CHECK_EQ(0, parse_int("0")); - CHECK_EQ(0, parse_int("0x0")); - CHECK_EQ(0, parse_int("0x0")); - CHECK_EQ(16, parse_int("10")); // hex always - CHECK_EQ(-1, parse_int("-1")); - CHECK_EQ(-1, parse_int("0xffffffff")); -} diff --git a/subx/031check_operands.cc b/subx/031check_operands.cc deleted file mode 100644 index bf5d3719..00000000 --- a/subx/031check_operands.cc +++ /dev/null @@ -1,691 +0,0 @@ -//: Since we're tagging operands with their types, let's start checking these -//: operand types for each instruction. - -void test_check_missing_imm8_operand() { - Hide_errors = true; - run( - "== code 0x1\n" - "cd\n" // interrupt ?? - ); - CHECK_TRACE_CONTENTS( - "error: 'cd' (software interrupt): missing imm8 operand\n" - ); -} - -:(before "Pack Operands(segment code)") -check_operands(code); -if (trace_contains_errors()) return; - -:(code) -void check_operands(const segment& code) { - trace(3, "transform") << "-- check operands" << end(); - for (int i = 0; i < SIZE(code.lines); ++i) { - check_operands(code.lines.at(i)); - if (trace_contains_errors()) return; // stop at the first mal-formed instruction - } -} - -void check_operands(const line& inst) { - word op = preprocess_op(inst.words.at(0)); - if (op.data == "0f") { - check_operands_0f(inst); - return; - } - if (op.data == "f3") { - check_operands_f3(inst); - return; - } - check_operands(inst, op); -} - -word preprocess_op(word/*copy*/ op) { - op.data = tolower(op.data.c_str()); - // opcodes can't be negative - if (starts_with(op.data, "0x")) - op.data = op.data.substr(2); - if (SIZE(op.data) == 1) - op.data = string("0")+op.data; - return op; -} - -void test_preprocess_op() { - word w1; w1.data = "0xf"; - word w2; w2.data = "0f"; - CHECK_EQ(preprocess_op(w1).data, preprocess_op(w2).data); -} - -//: To check the operands for an opcode, we'll track the permitted operands -//: for each supported opcode in a bitvector. That way we can often compute the -//: 'received' operand bitvector for each instruction's operands and compare -//: it with the 'expected' bitvector. -//: -//: The 'expected' and 'received' bitvectors can be different; the MODRM bit -//: in the 'expected' bitvector maps to multiple 'received' operand types in -//: an instruction. We deal in expected bitvectors throughout. - -:(before "End Types") -enum expected_operand_type { - // start from the least significant bit - MODRM, // more complex, may also involve disp8 or disp32 - SUBOP, - DISP8, - DISP16, - DISP32, - IMM8, - IMM32, - NUM_OPERAND_TYPES -}; -:(before "End Globals") -vector<string> Operand_type_name; -map<string, expected_operand_type> Operand_type; -:(before "End One-time Setup") -init_op_types(); -:(code) -void init_op_types() { - assert(NUM_OPERAND_TYPES <= /*bits in a uint8_t*/8); - Operand_type_name.resize(NUM_OPERAND_TYPES); - #define DEF(type) Operand_type_name.at(type) = tolower(#type), put(Operand_type, tolower(#type), type); - DEF(MODRM); - DEF(SUBOP); - DEF(DISP8); - DEF(DISP16); - DEF(DISP32); - DEF(IMM8); - DEF(IMM32); - #undef DEF -} - -:(before "End Globals") -map</*op*/string, /*bitvector*/uint8_t> Permitted_operands; -const uint8_t INVALID_OPERANDS = 0xff; // no instruction uses all the operand types -:(before "End One-time Setup") -init_permitted_operands(); -:(code) -void init_permitted_operands() { - //// Class A: just op, no operands - // halt - put(Permitted_operands, "f4", 0x00); - // inc - put(Permitted_operands, "40", 0x00); - put(Permitted_operands, "41", 0x00); - put(Permitted_operands, "42", 0x00); - put(Permitted_operands, "43", 0x00); - put(Permitted_operands, "44", 0x00); - put(Permitted_operands, "45", 0x00); - put(Permitted_operands, "46", 0x00); - put(Permitted_operands, "47", 0x00); - // dec - put(Permitted_operands, "48", 0x00); - put(Permitted_operands, "49", 0x00); - put(Permitted_operands, "4a", 0x00); - put(Permitted_operands, "4b", 0x00); - put(Permitted_operands, "4c", 0x00); - put(Permitted_operands, "4d", 0x00); - put(Permitted_operands, "4e", 0x00); - put(Permitted_operands, "4f", 0x00); - // push - put(Permitted_operands, "50", 0x00); - put(Permitted_operands, "51", 0x00); - put(Permitted_operands, "52", 0x00); - put(Permitted_operands, "53", 0x00); - put(Permitted_operands, "54", 0x00); - put(Permitted_operands, "55", 0x00); - put(Permitted_operands, "56", 0x00); - put(Permitted_operands, "57", 0x00); - // pop - put(Permitted_operands, "58", 0x00); - put(Permitted_operands, "59", 0x00); - put(Permitted_operands, "5a", 0x00); - put(Permitted_operands, "5b", 0x00); - put(Permitted_operands, "5c", 0x00); - put(Permitted_operands, "5d", 0x00); - put(Permitted_operands, "5e", 0x00); - put(Permitted_operands, "5f", 0x00); - // sign-extend EAX into EDX - put(Permitted_operands, "99", 0x00); - // return - put(Permitted_operands, "c3", 0x00); - - //// Class B: just op and disp8 - // imm32 imm8 disp32 |disp16 disp8 subop modrm - // 0 0 0 |0 1 0 0 - - // jump - put(Permitted_operands, "eb", 0x04); - put(Permitted_operands, "72", 0x04); - put(Permitted_operands, "73", 0x04); - put(Permitted_operands, "74", 0x04); - put(Permitted_operands, "75", 0x04); - put(Permitted_operands, "76", 0x04); - put(Permitted_operands, "77", 0x04); - put(Permitted_operands, "7c", 0x04); - put(Permitted_operands, "7d", 0x04); - put(Permitted_operands, "7e", 0x04); - put(Permitted_operands, "7f", 0x04); - - //// Class D: just op and disp32 - // imm32 imm8 disp32 |disp16 disp8 subop modrm - // 0 0 1 |0 0 0 0 - put(Permitted_operands, "e8", 0x10); // call - put(Permitted_operands, "e9", 0x10); // jump - - //// Class E: just op and imm8 - // imm32 imm8 disp32 |disp16 disp8 subop modrm - // 0 1 0 |0 0 0 0 - put(Permitted_operands, "cd", 0x20); // software interrupt - - //// Class F: just op and imm32 - // imm32 imm8 disp32 |disp16 disp8 subop modrm - // 1 0 0 |0 0 0 0 - put(Permitted_operands, "05", 0x40); // add - put(Permitted_operands, "2d", 0x40); // subtract - put(Permitted_operands, "25", 0x40); // and - put(Permitted_operands, "0d", 0x40); // or - put(Permitted_operands, "35", 0x40); // xor - put(Permitted_operands, "3d", 0x40); // compare - put(Permitted_operands, "68", 0x40); // push - // copy - put(Permitted_operands, "b8", 0x40); - put(Permitted_operands, "b9", 0x40); - put(Permitted_operands, "ba", 0x40); - put(Permitted_operands, "bb", 0x40); - put(Permitted_operands, "bc", 0x40); - put(Permitted_operands, "bd", 0x40); - put(Permitted_operands, "be", 0x40); - put(Permitted_operands, "bf", 0x40); - - //// Class M: using ModR/M byte - // imm32 imm8 disp32 |disp16 disp8 subop modrm - // 0 0 0 |0 0 0 1 - - // add - put(Permitted_operands, "01", 0x01); - put(Permitted_operands, "03", 0x01); - // subtract - put(Permitted_operands, "29", 0x01); - put(Permitted_operands, "2b", 0x01); - // and - put(Permitted_operands, "21", 0x01); - put(Permitted_operands, "23", 0x01); - // or - put(Permitted_operands, "09", 0x01); - put(Permitted_operands, "0b", 0x01); - // xor - put(Permitted_operands, "31", 0x01); - put(Permitted_operands, "33", 0x01); - // compare - put(Permitted_operands, "39", 0x01); - put(Permitted_operands, "3b", 0x01); - // copy - put(Permitted_operands, "88", 0x01); - put(Permitted_operands, "89", 0x01); - put(Permitted_operands, "8a", 0x01); - put(Permitted_operands, "8b", 0x01); - // swap - put(Permitted_operands, "87", 0x01); - // copy address (lea) - put(Permitted_operands, "8d", 0x01); - - //// Class N: op, ModR/M and subop (not r32) - // imm32 imm8 disp32 |disp16 disp8 subop modrm - // 0 0 0 |0 0 1 1 - put(Permitted_operands, "8f", 0x03); // pop - put(Permitted_operands, "d3", 0x03); // shift - put(Permitted_operands, "f7", 0x03); // test/not/mul/div - put(Permitted_operands, "ff", 0x03); // jump/push/call - - //// Class O: op, ModR/M, subop (not r32) and imm8 - // imm32 imm8 disp32 |disp16 disp8 subop modrm - // 0 1 0 |0 0 1 1 - put(Permitted_operands, "c1", 0x23); // combine - put(Permitted_operands, "c6", 0x23); // copy - - //// Class P: op, ModR/M, subop (not r32) and imm32 - // imm32 imm8 disp32 |disp16 disp8 subop modrm - // 1 0 0 |0 0 1 1 - put(Permitted_operands, "81", 0x43); // combine - put(Permitted_operands, "c7", 0x43); // copy - - // End Init Permitted Operands -} - -#define HAS(bitvector, bit) ((bitvector) & (1 << (bit))) -#define SET(bitvector, bit) ((bitvector) | (1 << (bit))) -#define CLEAR(bitvector, bit) ((bitvector) & (~(1 << (bit)))) - -void check_operands(const line& inst, const word& op) { - if (!is_hex_byte(op)) return; - uint8_t expected_bitvector = get(Permitted_operands, op.data); - if (HAS(expected_bitvector, MODRM)) { - check_operands_modrm(inst, op); - compare_bitvector_modrm(inst, expected_bitvector, op); - } - else { - compare_bitvector(inst, expected_bitvector, op); - } -} - -//: Many instructions can be checked just by comparing bitvectors. - -void compare_bitvector(const line& inst, uint8_t expected, const word& op) { - if (all_hex_bytes(inst) && has_operands(inst)) return; // deliberately programming in raw hex; we'll raise a warning elsewhere - uint8_t bitvector = compute_expected_operand_bitvector(inst); - if (trace_contains_errors()) return; // duplicate operand type - if (bitvector == expected) return; // all good with this instruction - for (int i = 0; i < NUM_OPERAND_TYPES; ++i, bitvector >>= 1, expected >>= 1) { -//? cerr << "comparing " << HEXBYTE << NUM(bitvector) << " with " << NUM(expected) << '\n'; - if ((bitvector & 0x1) == (expected & 0x1)) continue; // all good with this operand - const string& optype = Operand_type_name.at(i); - if ((bitvector & 0x1) > (expected & 0x1)) - raise << "'" << to_string(inst) << "'" << maybe_name(op) << ": unexpected " << optype << " operand\n" << end(); - else - raise << "'" << to_string(inst) << "'" << maybe_name(op) << ": missing " << optype << " operand\n" << end(); - // continue giving all errors for a single instruction - } - // ignore settings in any unused bits -} - -string maybe_name(const word& op) { - if (!is_hex_byte(op)) return ""; - if (!contains_key(Name, op.data)) return ""; - // strip stuff in parens from the name - const string& s = get(Name, op.data); - return " ("+s.substr(0, s.find(" ("))+')'; -} - -uint32_t compute_expected_operand_bitvector(const line& inst) { - set<string> operands_found; - uint32_t bitvector = 0; - for (int i = /*skip op*/1; i < SIZE(inst.words); ++i) { - bitvector = bitvector | expected_bit_for_received_operand(inst.words.at(i), operands_found, inst); - if (trace_contains_errors()) return INVALID_OPERANDS; // duplicate operand type - } - return bitvector; -} - -bool has_operands(const line& inst) { - return SIZE(inst.words) > first_operand(inst); -} - -int first_operand(const line& inst) { - if (inst.words.at(0).data == "0f") return 2; - if (inst.words.at(0).data == "f2" || inst.words.at(0).data == "f3") { - if (inst.words.at(1).data == "0f") - return 3; - else - return 2; - } - return 1; -} - -// Scan the metadata of 'w' and return the expected bit corresponding to any operand type. -// Also raise an error if metadata contains multiple operand types. -uint32_t expected_bit_for_received_operand(const word& w, set<string>& instruction_operands, const line& inst) { - uint32_t bv = 0; - bool found = false; - for (int i = 0; i < SIZE(w.metadata); ++i) { - string/*copy*/ curr = w.metadata.at(i); - string expected_metadata = curr; - if (curr == "mod" || curr == "rm32" || curr == "r32" || curr == "scale" || curr == "index" || curr == "base") - expected_metadata = "modrm"; - else if (!contains_key(Operand_type, curr)) continue; // ignore unrecognized metadata - if (found) { - raise << "'" << w.original << "' has conflicting operand types; it should have only one\n" << end(); - return INVALID_OPERANDS; - } - if (instruction_operands.find(curr) != instruction_operands.end()) { - raise << "'" << to_string(inst) << "': duplicate " << curr << " operand\n" << end(); - return INVALID_OPERANDS; - } - instruction_operands.insert(curr); - bv = (1 << get(Operand_type, expected_metadata)); - found = true; - } - return bv; -} - -void test_conflicting_operand_type() { - Hide_errors = true; - run( - "== code 0x1\n" - "cd/software-interrupt 80/imm8/imm32\n" - ); - CHECK_TRACE_CONTENTS( - "error: '80/imm8/imm32' has conflicting operand types; it should have only one\n" - ); -} - -//: Instructions computing effective addresses have more complex rules, so -//: we'll hard-code a common set of instruction-decoding rules. - -void test_check_missing_mod_operand() { - Hide_errors = true; - run( - "== code 0x1\n" - "81 0/add/subop 3/rm32/ebx 1/imm32\n" - ); - CHECK_TRACE_CONTENTS( - "error: '81 0/add/subop 3/rm32/ebx 1/imm32' (combine rm32 with imm32 based on subop): missing mod operand\n" - ); -} - -void check_operands_modrm(const line& inst, const word& op) { - if (all_hex_bytes(inst)) return; // deliberately programming in raw hex; we'll raise a warning elsewhere - check_operand_metadata_present(inst, "mod", op); - check_operand_metadata_present(inst, "rm32", op); - // no check for r32; some instructions don't use it; just assume it's 0 if missing - if (op.data == "81" || op.data == "8f" || op.data == "ff") { // keep sync'd with 'help subop' - check_operand_metadata_present(inst, "subop", op); - check_operand_metadata_absent(inst, "r32", op, "should be replaced by subop"); - } - if (trace_contains_errors()) return; - if (metadata(inst, "rm32").data != "4") return; - // SIB byte checks - uint8_t mod = hex_byte(metadata(inst, "mod").data); - if (mod != /*direct*/3) { - check_operand_metadata_present(inst, "base", op); - check_operand_metadata_present(inst, "index", op); // otherwise why go to SIB? - } - else { - check_operand_metadata_absent(inst, "base", op, "direct mode"); - check_operand_metadata_absent(inst, "index", op, "direct mode"); - } - // no check for scale; 0 (2**0 = 1) by default -} - -// same as compare_bitvector, with one additional exception for modrm-based -// instructions: they may use an extra displacement on occasion -void compare_bitvector_modrm(const line& inst, uint8_t expected, const word& op) { - if (all_hex_bytes(inst) && has_operands(inst)) return; // deliberately programming in raw hex; we'll raise a warning elsewhere - uint8_t bitvector = compute_expected_operand_bitvector(inst); - if (trace_contains_errors()) return; // duplicate operand type - // update 'expected' bitvector for the additional exception - if (has_operand_metadata(inst, "mod")) { - int32_t mod = parse_int(metadata(inst, "mod").data); - switch (mod) { - case 0: - if (has_operand_metadata(inst, "rm32") && parse_int(metadata(inst, "rm32").data) == 5) - expected |= (1<<DISP32); - break; - case 1: - expected |= (1<<DISP8); - break; - case 2: - expected |= (1<<DISP32); - break; - } - } - if (bitvector == expected) return; // all good with this instruction - for (int i = 0; i < NUM_OPERAND_TYPES; ++i, bitvector >>= 1, expected >>= 1) { -//? cerr << "comparing for modrm " << HEXBYTE << NUM(bitvector) << " with " << NUM(expected) << '\n'; - if ((bitvector & 0x1) == (expected & 0x1)) continue; // all good with this operand - const string& optype = Operand_type_name.at(i); - if ((bitvector & 0x1) > (expected & 0x1)) - raise << "'" << to_string(inst) << "'" << maybe_name(op) << ": unexpected " << optype << " operand\n" << end(); - else - raise << "'" << to_string(inst) << "'" << maybe_name(op) << ": missing " << optype << " operand\n" << end(); - // continue giving all errors for a single instruction - } - // ignore settings in any unused bits -} - -void check_operand_metadata_present(const line& inst, const string& type, const word& op) { - if (!has_operand_metadata(inst, type)) - raise << "'" << to_string(inst) << "'" << maybe_name(op) << ": missing " << type << " operand\n" << end(); -} - -void check_operand_metadata_absent(const line& inst, const string& type, const word& op, const string& msg) { - if (has_operand_metadata(inst, type)) - raise << "'" << to_string(inst) << "'" << maybe_name(op) << ": unexpected " << type << " operand (" << msg << ")\n" << end(); -} - -void test_modrm_with_displacement() { - Reg[EAX].u = 0x1; - transform( - "== code 0x1\n" - // just avoid null pointer - "8b/copy 1/mod/lookup+disp8 0/rm32/EAX 2/r32/EDX 4/disp8\n" // copy *(EAX+4) to EDX - ); - CHECK_TRACE_COUNT("error", 0); -} - -void test_check_missing_disp8() { - Hide_errors = true; - transform( - "== code 0x1\n" - "89/copy 1/mod/lookup+disp8 0/rm32/EAX 1/r32/ECX\n" // missing disp8 - ); - CHECK_TRACE_CONTENTS( - "error: '89/copy 1/mod/lookup+disp8 0/rm32/EAX 1/r32/ECX' (copy r32 to rm32): missing disp8 operand\n" - ); -} - -void test_check_missing_disp32() { - Hide_errors = true; - transform( - "== code 0x1\n" - "8b/copy 0/mod/indirect 5/rm32/.disp32 2/r32/EDX\n" // missing disp32 - ); - CHECK_TRACE_CONTENTS( - "error: '8b/copy 0/mod/indirect 5/rm32/.disp32 2/r32/EDX' (copy rm32 to r32): missing disp32 operand\n" - ); -} - -void test_conflicting_operands_in_modrm_instruction() { - Hide_errors = true; - run( - "== code 0x1\n" - "01/add 0/mod 3/mod\n" - ); - CHECK_TRACE_CONTENTS( - "error: '01/add 0/mod 3/mod' has conflicting mod operands\n" - ); -} - -void test_conflicting_operand_type_modrm() { - Hide_errors = true; - run( - "== code 0x1\n" - "01/add 0/mod 3/rm32/r32\n" - ); - CHECK_TRACE_CONTENTS( - "error: '3/rm32/r32' has conflicting operand types; it should have only one\n" - ); -} - -void test_check_missing_rm32_operand() { - Hide_errors = true; - run( - "== code 0x1\n" - "81 0/add/subop 0/mod 1/imm32\n" - ); - CHECK_TRACE_CONTENTS( - "error: '81 0/add/subop 0/mod 1/imm32' (combine rm32 with imm32 based on subop): missing rm32 operand\n" - ); -} - -void test_check_missing_subop_operand() { - Hide_errors = true; - run( - "== code 0x1\n" - "81 0/mod 3/rm32/ebx 1/imm32\n" - ); - CHECK_TRACE_CONTENTS( - "error: '81 0/mod 3/rm32/ebx 1/imm32' (combine rm32 with imm32 based on subop): missing subop operand\n" - ); -} - -void test_check_missing_base_operand() { - Hide_errors = true; - run( - "== code 0x1\n" - "81 0/add/subop 0/mod/indirect 4/rm32/use-sib 1/imm32\n" - ); - CHECK_TRACE_CONTENTS( - "error: '81 0/add/subop 0/mod/indirect 4/rm32/use-sib 1/imm32' (combine rm32 with imm32 based on subop): missing base operand\n" - ); -} - -void test_check_missing_index_operand() { - Hide_errors = true; - run( - "== code 0x1\n" - "81 0/add/subop 0/mod/indirect 4/rm32/use-sib 0/base 1/imm32\n" - ); - CHECK_TRACE_CONTENTS( - "error: '81 0/add/subop 0/mod/indirect 4/rm32/use-sib 0/base 1/imm32' (combine rm32 with imm32 based on subop): missing index operand\n" - ); -} - -void test_check_missing_base_operand_2() { - Hide_errors = true; - run( - "== code 0x1\n" - "81 0/add/subop 0/mod/indirect 4/rm32/use-sib 2/index 3/scale 1/imm32\n" - ); - CHECK_TRACE_CONTENTS( - "error: '81 0/add/subop 0/mod/indirect 4/rm32/use-sib 2/index 3/scale 1/imm32' (combine rm32 with imm32 based on subop): missing base operand\n" - ); -} - -void test_check_extra_displacement() { - Hide_errors = true; - run( - "== code 0x1\n" - "89/copy 0/mod/indirect 0/rm32/EAX 1/r32/ECX 4/disp8\n" - ); - CHECK_TRACE_CONTENTS( - "error: '89/copy 0/mod/indirect 0/rm32/EAX 1/r32/ECX 4/disp8' (copy r32 to rm32): unexpected disp8 operand\n" - ); -} - -void test_check_duplicate_operand() { - Hide_errors = true; - run( - "== code 0x1\n" - "89/copy 0/mod/indirect 0/rm32/EAX 1/r32/ECX 1/r32\n" - ); - CHECK_TRACE_CONTENTS( - "error: '89/copy 0/mod/indirect 0/rm32/EAX 1/r32/ECX 1/r32': duplicate r32 operand\n" - ); -} - -void test_check_base_operand_not_needed_in_direct_mode() { - run( - "== code 0x1\n" - "81 0/add/subop 3/mod/indirect 4/rm32/use-sib 1/imm32\n" - ); - CHECK_TRACE_COUNT("error", 0); -} - -void test_extra_modrm() { - Hide_errors = true; - run( - "== code 0x1\n" - "59/pop-to-ECX 3/mod/direct 1/rm32/ECX 4/r32/ESP\n" - ); - CHECK_TRACE_CONTENTS( - "error: '59/pop-to-ECX 3/mod/direct 1/rm32/ECX 4/r32/ESP' (pop top of stack to ECX): unexpected modrm operand\n" - ); -} - -//:: similarly handle multi-byte opcodes - -void check_operands_0f(const line& inst) { - assert(inst.words.at(0).data == "0f"); - if (SIZE(inst.words) == 1) { - raise << "opcode '0f' requires a second opcode\n" << end(); - return; - } - word op = preprocess_op(inst.words.at(1)); - if (!contains_key(Name_0f, op.data)) { - raise << "unknown 2-byte opcode '0f " << op.data << "'\n" << end(); - return; - } - check_operands_0f(inst, op); -} - -void check_operands_f3(const line& /*unused*/) { - raise << "no supported opcodes starting with f3\n" << end(); -} - -void test_check_missing_disp32_operand() { - Hide_errors = true; - run( - "== code 0x1\n" - " 0f 84 # jmp if ZF to ??\n" - ); - CHECK_TRACE_CONTENTS( - "error: '0f 84' (jump disp32 bytes away if equal, if ZF is set): missing disp32 operand\n" - ); -} - -:(before "End Globals") -map</*op*/string, /*bitvector*/uint8_t> Permitted_operands_0f; -:(before "End Init Permitted Operands") -//// Class D: just op and disp32 -// imm32 imm8 disp32 |disp16 disp8 subop modrm -// 0 0 1 |0 0 0 0 -put_new(Permitted_operands_0f, "82", 0x10); -put_new(Permitted_operands_0f, "83", 0x10); -put_new(Permitted_operands_0f, "84", 0x10); -put_new(Permitted_operands_0f, "85", 0x10); -put_new(Permitted_operands_0f, "86", 0x10); -put_new(Permitted_operands_0f, "87", 0x10); -put_new(Permitted_operands_0f, "8c", 0x10); -put_new(Permitted_operands_0f, "8d", 0x10); -put_new(Permitted_operands_0f, "8e", 0x10); -put_new(Permitted_operands_0f, "8f", 0x10); - -//// Class M: using ModR/M byte -// imm32 imm8 disp32 |disp16 disp8 subop modrm -// 0 0 0 |0 0 0 1 -put_new(Permitted_operands_0f, "af", 0x01); - -:(code) -void check_operands_0f(const line& inst, const word& op) { - uint8_t expected_bitvector = get(Permitted_operands_0f, op.data); - if (HAS(expected_bitvector, MODRM)) - check_operands_modrm(inst, op); - compare_bitvector_0f(inst, CLEAR(expected_bitvector, MODRM), op); -} - -void compare_bitvector_0f(const line& inst, uint8_t expected, const word& op) { - if (all_hex_bytes(inst) && has_operands(inst)) return; // deliberately programming in raw hex; we'll raise a warning elsewhere - uint8_t bitvector = compute_expected_operand_bitvector(inst); - if (trace_contains_errors()) return; // duplicate operand type - if (bitvector == expected) return; // all good with this instruction - for (int i = 0; i < NUM_OPERAND_TYPES; ++i, bitvector >>= 1, expected >>= 1) { -//? cerr << "comparing " << HEXBYTE << NUM(bitvector) << " with " << NUM(expected) << '\n'; - if ((bitvector & 0x1) == (expected & 0x1)) continue; // all good with this operand - const string& optype = Operand_type_name.at(i); - if ((bitvector & 0x1) > (expected & 0x1)) - raise << "'" << to_string(inst) << "'" << maybe_name_0f(op) << ": unexpected " << optype << " operand\n" << end(); - else - raise << "'" << to_string(inst) << "'" << maybe_name_0f(op) << ": missing " << optype << " operand\n" << end(); - // continue giving all errors for a single instruction - } - // ignore settings in any unused bits -} - -string maybe_name_0f(const word& op) { - if (!is_hex_byte(op)) return ""; - if (!contains_key(Name_0f, op.data)) return ""; - // strip stuff in parens from the name - const string& s = get(Name_0f, op.data); - return " ("+s.substr(0, s.find(" ("))+')'; -} - -string tolower(const char* s) { - ostringstream out; - for (/*nada*/; *s; ++s) - out << static_cast<char>(tolower(*s)); - return out.str(); -} - -#undef HAS -#undef SET -#undef CLEAR - -:(before "End Includes") -#include<cctype> diff --git a/subx/032check_operand_bounds.cc b/subx/032check_operand_bounds.cc deleted file mode 100644 index 72a66e3f..00000000 --- a/subx/032check_operand_bounds.cc +++ /dev/null @@ -1,143 +0,0 @@ -//:: Check that the different operands of an instruction aren't too large for their bitfields. - -void test_check_bitfield_sizes() { - Hide_errors = true; - run( - "== code 0x1\n" - "01/add 4/mod 3/rm32 1/r32\n" // add ECX to EBX - ); - CHECK_TRACE_CONTENTS( - "error: '4/mod' too large to fit in bitfield mod\n" - ); -} - -:(before "End Globals") -map<string, uint32_t> Operand_bound; -:(before "End One-time Setup") -put_new(Operand_bound, "subop", 1<<3); -put_new(Operand_bound, "mod", 1<<2); -put_new(Operand_bound, "rm32", 1<<3); -put_new(Operand_bound, "base", 1<<3); -put_new(Operand_bound, "index", 1<<3); -put_new(Operand_bound, "scale", 1<<2); -put_new(Operand_bound, "r32", 1<<3); -put_new(Operand_bound, "disp8", 1<<8); -put_new(Operand_bound, "disp16", 1<<16); -// no bound needed for disp32 -put_new(Operand_bound, "imm8", 1<<8); -// no bound needed for imm32 - -:(before "Pack Operands(segment code)") -check_operand_bounds(code); -if (trace_contains_errors()) return; -:(code) -void check_operand_bounds(const segment& code) { - trace(3, "transform") << "-- check operand bounds" << end(); - for (int i = 0; i < SIZE(code.lines); ++i) { - const line& inst = code.lines.at(i); - for (int j = first_operand(inst); j < SIZE(inst.words); ++j) - check_operand_bounds(inst.words.at(j)); - if (trace_contains_errors()) return; // stop at the first mal-formed instruction - } -} - -void check_operand_bounds(const word& w) { - for (map<string, uint32_t>::iterator p = Operand_bound.begin(); p != Operand_bound.end(); ++p) { - if (!has_operand_metadata(w, p->first)) continue; - if (!looks_like_hex_int(w.data)) continue; // later transforms are on their own to do their own bounds checking - int32_t x = parse_int(w.data); - if (x >= 0) { - if (p->first == "disp8" || p->first == "disp16") { - if (static_cast<uint32_t>(x) >= p->second/2) - raise << "'" << w.original << "' too large to fit in signed bitfield " << p->first << '\n' << end(); - } - else { - if (static_cast<uint32_t>(x) >= p->second) - raise << "'" << w.original << "' too large to fit in bitfield " << p->first << '\n' << end(); - } - } - else { - // hacky? assuming bound is a power of 2 - if (x < -1*static_cast<int32_t>(p->second/2)) - raise << "'" << w.original << "' too large to fit in bitfield " << p->first << '\n' << end(); - } - } -} - -void test_check_bitfield_sizes_for_imm8() { - run( - "== code 0x1\n" - "c1/shift 4/subop/left 3/mod/direct 1/rm32/ECX 0xff/imm8" // shift EBX left - ); - CHECK(!trace_contains_errors()); -} - -void test_check_bitfield_sizes_for_imm8_error() { - Hide_errors = true; - run( - "== code 0x1\n" - "c1/shift 4/subop/left 3/mod/direct 1/rm32/ECX 0x100/imm8" // shift EBX left - ); - CHECK_TRACE_CONTENTS( - "error: '0x100/imm8' too large to fit in bitfield imm8\n" - ); -} - -void test_check_bitfield_sizes_for_negative_imm8() { - run( - "== code 0x1\n" - "c1/shift 4/subop/left 3/mod/direct 1/rm32/ECX -0x80/imm8" // shift EBX left - ); - CHECK(!trace_contains_errors()); -} - -void test_check_bitfield_sizes_for_negative_imm8_error() { - Hide_errors = true; - run( - "== code 0x1\n" - "c1/shift 4/subop/left 3/mod/direct 1/rm32/ECX -0x81/imm8" // shift EBX left - ); - CHECK_TRACE_CONTENTS( - "error: '-0x81/imm8' too large to fit in bitfield imm8\n" - ); -} - -void test_check_bitfield_sizes_for_disp8() { - // not bothering to run - transform( - "== code 0x1\n" - "01/add 1/mod/*+disp8 3/rm32 1/r32 0x7f/disp8\n" // add ECX to *(EBX+0x7f) - ); - CHECK(!trace_contains_errors()); -} - -void test_check_bitfield_sizes_for_disp8_error() { - Hide_errors = true; - run( - "== code 0x1\n" - "01/add 1/mod/*+disp8 3/rm32 1/r32 0x80/disp8\n" // add ECX to *(EBX+0x80) - ); - CHECK_TRACE_CONTENTS( - "error: '0x80/disp8' too large to fit in signed bitfield disp8\n" - ); -} - -void test_check_bitfield_sizes_for_negative_disp8() { - // not bothering to run - transform( - "== code 0x1\n" - "01/add 1/mod/*+disp8 3/rm32 1/r32 -0x80/disp8\n" // add ECX to *(EBX-0x80) - ); - CHECK(!trace_contains_errors()); -} - -void test_check_bitfield_sizes_for_negative_disp8_error() { - Hide_errors = true; - run( - "== code 0x1\n" - "01/add 1/mod/*+disp8 3/rm32 1/r32 -0x81/disp8\n" // add ECX to *(EBX-0x81) - ); - CHECK_TRACE_CONTENTS( - "error: '-0x81/disp8' too large to fit in bitfield disp8\n" - ); -} diff --git a/subx/034compute_segment_address.cc b/subx/034compute_segment_address.cc deleted file mode 100644 index 61c3739a..00000000 --- a/subx/034compute_segment_address.cc +++ /dev/null @@ -1,86 +0,0 @@ -//: ELF binaries have finicky rules about the precise alignment each segment -//: should start at. They depend on the amount of code in a program. -//: We shouldn't expect people to adjust segment addresses everytime they make -//: a change to their programs. -//: Let's start taking the given segment addresses as guidelines, and adjust -//: them as necessary. -//: This gives up a measure of control in placing code and data. - -void test_segment_name() { - run( - "== code 0x09000000\n" - "05/add-to-EAX 0x0d0c0b0a/imm32\n" - // code starts at 0x09000000 + p_offset, which is 0x54 for a single-segment binary - ); - CHECK_TRACE_CONTENTS( - "load: 0x09000054 -> 05\n" - "load: 0x09000055 -> 0a\n" - "load: 0x09000056 -> 0b\n" - "load: 0x09000057 -> 0c\n" - "load: 0x09000058 -> 0d\n" - "run: add imm32 0x0d0c0b0a to EAX\n" - "run: storing 0x0d0c0b0a\n" - ); -} - -//: compute segment address - -:(before "End Level-2 Transforms") -Transform.push_back(compute_segment_starts); - -:(code) -void compute_segment_starts(program& p) { - trace(3, "transform") << "-- compute segment addresses" << end(); - uint32_t p_offset = /*size of ehdr*/0x34 + SIZE(p.segments)*0x20/*size of each phdr*/; - for (size_t i = 0; i < p.segments.size(); ++i) { - segment& curr = p.segments.at(i); - if (curr.start >= 0x08000000) { - // valid address for user space, so assume we're creating a real ELF binary, not just running a test - curr.start &= 0xfffff000; // same number of zeros as the p_align used when emitting the ELF binary - curr.start |= (p_offset & 0xfff); - trace(99, "transform") << "segment " << i << " begins at address 0x" << HEXWORD << curr.start << end(); - } - p_offset += size_of(curr); - assert(p_offset < SEGMENT_ALIGNMENT); // for now we get less and less available space in each successive segment - } -} - -uint32_t size_of(const segment& s) { - uint32_t sum = 0; - for (int i = 0; i < SIZE(s.lines); ++i) - sum += num_bytes(s.lines.at(i)); - return sum; -} - -// Assumes all bitfields are packed. -uint32_t num_bytes(const line& inst) { - uint32_t sum = 0; - for (int i = 0; i < SIZE(inst.words); ++i) - sum += size_of(inst.words.at(i)); - return sum; -} - -int size_of(const word& w) { - if (has_operand_metadata(w, "disp32") || has_operand_metadata(w, "imm32")) - return 4; - else if (has_operand_metadata(w, "disp16")) - return 2; - // End size_of(word w) Special-cases - else - return 1; -} - -//: Dependencies: -//: - We'd like to compute segment addresses before setting up global variables, -//: because computing addresses for global variables requires knowing where -//: the data segment starts. -//: - We'd like to finish expanding labels before computing segment addresses, -//: because it would make computing the sizes of segments more self-contained -//: (num_bytes). -//: -//: Decision: compute segment addresses before expanding labels, by being -//: aware in this layer of certain operand types that will eventually occupy -//: multiple bytes. -//: -//: The layer to expand labels later hooks into num_bytes() to teach this -//: layer that labels occupy zero space in the binary. diff --git a/subx/035labels.cc b/subx/035labels.cc deleted file mode 100644 index 6f7fdbfe..00000000 --- a/subx/035labels.cc +++ /dev/null @@ -1,416 +0,0 @@ -//: Labels are defined by ending names with a ':'. This layer will compute -//: displacements for labels, and compute the offset for instructions using them. -//: -//: We won't check this, but our convention will be that jump targets will -//: start with a '$', while functions will not. Function names will never be -//: jumped to, and jump targets will never be called. - -//: We're introducing non-number names for the first time, so it's worth -//: laying down some ground rules all transforms will follow, so things don't -//: get too confusing: -//: - if it starts with a digit, it's treated as a number. If it can't be -//: parsed as hex it will raise an error. -//: - if it starts with '-' it's treated as a number. -//: - if it starts with '0x' it's treated as a number. -//: - if it's two characters long, it can't be a name. Either it's a hex -//: byte, or it raises an error. -//: That's it. Names can start with any non-digit that isn't a dash. They can -//: be a single character long. 'a' is not a hex number, it's a variable. -//: Later layers may add more conventions partitioning the space of names. But -//: the above rules will remain inviolate. - -//: One special label is 'Entry', the address to start running the program at. -//: It can be non-unique; the last declaration overrides earlier ones. -//: It must exist in a program. Otherwise we don't know where to start running -//: programs. - -void test_Entry_label() { - run( - "== code 0x1\n" - "05 0x0d0c0b0a/imm32\n" - "Entry:\n" - "05 0x0d0c0b0a/imm32\n" - ); - CHECK_TRACE_CONTENTS( - "run: 0x00000006 opcode: 05\n" - ); - CHECK_TRACE_DOESNT_CONTAIN("run: 0x00000001 opcode: 05"); -} - -:(before "End looks_like_hex_int(s) Detectors") -if (SIZE(s) == 2) return true; - -:(code) -void test_pack_immediate_ignores_single_byte_nondigit_operand() { - Hide_errors = true; - transform( - "== code 0x1\n" - "b9/copy a/imm32\n" - ); - CHECK_TRACE_CONTENTS( - "transform: packing instruction 'b9/copy a/imm32'\n" - // no change (we're just not printing metadata to the trace) - "transform: instruction after packing: 'b9 a'\n" - ); -} - -void test_pack_immediate_ignores_3_hex_digit_operand() { - Hide_errors = true; - transform( - "== code 0x1\n" - "b9/copy aaa/imm32\n" - ); - CHECK_TRACE_CONTENTS( - "transform: packing instruction 'b9/copy aaa/imm32'\n" - // no change (we're just not printing metadata to the trace) - "transform: instruction after packing: 'b9 aaa'\n" - ); -} - -void test_pack_immediate_ignores_non_hex_operand() { - Hide_errors = true; - transform( - "== code 0x1\n" - "b9/copy xxx/imm32\n" - ); - CHECK_TRACE_CONTENTS( - "transform: packing instruction 'b9/copy xxx/imm32'\n" - // no change (we're just not printing metadata to the trace) - "transform: instruction after packing: 'b9 xxx'\n" - ); -} - -//: a helper we'll find handy later -void check_valid_name(const string& s) { - if (s.empty()) { - raise << "empty name!\n" << end(); - return; - } - if (s.at(0) == '-') - raise << "'" << s << "' starts with '-', which can be confused with a negative number; use a different name\n" << end(); - if (s.substr(0, 2) == "0x") { - raise << "'" << s << "' looks like a hex number; use a different name\n" << end(); - return; - } - if (isdigit(s.at(0))) - raise << "'" << s << "' starts with a digit, and so can be confused with a number; use a different name.\n" << end(); - if (SIZE(s) == 2) - raise << "'" << s << "' is two characters long, which can look like raw hex bytes at a glance; use a different name\n" << end(); -} - -//: Now that that's done, let's start using names as labels. - -void test_map_label() { - transform( - "== code 0x1\n" - "loop:\n" - " 05 0x0d0c0b0a/imm32\n" - ); - CHECK_TRACE_CONTENTS( - "transform: label 'loop' is at address 1\n" - ); -} - -:(before "End Level-2 Transforms") -Transform.push_back(rewrite_labels); -:(code) -void rewrite_labels(program& p) { - trace(3, "transform") << "-- rewrite labels" << end(); - if (p.segments.empty()) return; - segment& code = *find(p, "code"); - 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 - compute_byte_indices_for_labels(code, byte_index); - if (trace_contains_errors()) return; - drop_labels(code); - if (trace_contains_errors()) return; - replace_labels_with_displacements(code, byte_index); - if (contains_key(byte_index, "Entry")) - p.entry = code.start + get(byte_index, "Entry"); -} - -void compute_byte_indices_for_labels(const segment& code, map<string, int32_t>& byte_index) { - int current_byte = 0; - for (int i = 0; i < SIZE(code.lines); ++i) { - const line& inst = code.lines.at(i); - if (Source_lines_file.is_open() && !inst.original.empty() && /*not a label*/ *inst.words.at(0).data.rbegin() != ':') - Source_lines_file << "0x" << HEXWORD << (code.start + current_byte) << ' ' << inst.original << '\n'; - for (int j = 0; j < SIZE(inst.words); ++j) { - const word& curr = inst.words.at(j); - // hack: if we have any operand metadata left after previous transforms, - // deduce its size - // Maybe we should just move this transform to before instruction - // packing, and deduce the size of *all* operands. But then we'll also - // have to deal with bitfields. - if (has_operand_metadata(curr, "disp32") || has_operand_metadata(curr, "imm32")) { - if (*curr.data.rbegin() == ':') - raise << "'" << to_string(inst) << "': don't use ':' when jumping to labels\n" << end(); - current_byte += 4; - } - else if (has_operand_metadata(curr, "disp16")) { - if (*curr.data.rbegin() == ':') - raise << "'" << to_string(inst) << "': don't use ':' when jumping to labels\n" << end(); - current_byte += 2; - } - // automatically handle /disp8 and /imm8 here - else if (*curr.data.rbegin() != ':') { - ++current_byte; - } - else { - string label = drop_last(curr.data); - // ensure labels look sufficiently different from raw hex - check_valid_name(label); - if (trace_contains_errors()) return; - if (contains_any_operand_metadata(curr)) - raise << "'" << to_string(inst) << "': label definition (':') not allowed in operand\n" << end(); - if (j > 0) - raise << "'" << to_string(inst) << "': labels can only be the first word in a line.\n" << end(); - if (Labels_file.is_open()) - Labels_file << "0x" << HEXWORD << (code.start + current_byte) << ' ' << label << '\n'; - if (contains_key(byte_index, label) && label != "Entry") { - raise << "duplicate label '" << label << "'\n" << end(); - return; - } - put(byte_index, label, current_byte); - trace(99, "transform") << "label '" << label << "' is at address " << (current_byte+code.start) << end(); - // no modifying current_byte; label definitions won't be in the final binary - } - } - } -} - -:(before "End Globals") -bool Dump_debug_info = false; // currently used only by 'subx translate' -ofstream Labels_file; -ofstream Source_lines_file; -:(before "End Commandline Options") -else if (is_equal(*arg, "--debug")) { - Dump_debug_info = true; - // End --debug Settings -} -//: wait to open "labels" for writing until we're sure we aren't trying to read it -:(after "Begin subx translate") -if (Dump_debug_info) { - cerr << "saving address->label information to 'labels'\n"; - Labels_file.open("labels"); - cerr << "saving address->source information to 'source_lines'\n"; - Source_lines_file.open("source_lines"); -} -:(before "End subx translate") -if (Dump_debug_info) { - Labels_file.close(); - Source_lines_file.close(); -} - -:(code) -void drop_labels(segment& code) { - for (int i = 0; i < SIZE(code.lines); ++i) { - line& inst = code.lines.at(i); - vector<word>::iterator new_end = remove_if(inst.words.begin(), inst.words.end(), is_label); - inst.words.erase(new_end, inst.words.end()); - } -} - -bool is_label(const word& w) { - return *w.data.rbegin() == ':'; -} - -void replace_labels_with_displacements(segment& code, const map<string, int32_t>& byte_index) { - int32_t byte_index_next_instruction_starts_at = 0; - for (int i = 0; i < SIZE(code.lines); ++i) { - line& inst = code.lines.at(i); - byte_index_next_instruction_starts_at += num_bytes(inst); - line new_inst; - for (int j = 0; j < SIZE(inst.words); ++j) { - const word& curr = inst.words.at(j); - if (contains_key(byte_index, curr.data)) { - int32_t displacement = static_cast<int32_t>(get(byte_index, curr.data)) - byte_index_next_instruction_starts_at; - if (has_operand_metadata(curr, "disp8")) { - if (displacement > 0x7f || displacement < -0x7f) - raise << "'" << to_string(inst) << "': label too far away for displacement " << std::hex << displacement << " to fit in 8 signed bits\n" << end(); - else - emit_hex_bytes(new_inst, displacement, 1); - } - else if (has_operand_metadata(curr, "disp16")) { - if (displacement > 0x7fff || displacement < -0x7fff) - raise << "'" << to_string(inst) << "': label too far away for displacement " << std::hex << displacement << " to fit in 16 signed bits\n" << end(); - else - emit_hex_bytes(new_inst, displacement, 2); - } - else if (has_operand_metadata(curr, "disp32")) { - emit_hex_bytes(new_inst, displacement, 4); - } else if (has_operand_metadata(curr, "imm32")) { - emit_hex_bytes(new_inst, code.start + get(byte_index, curr.data), 4); - } - } - else { - new_inst.words.push_back(curr); - } - } - inst.words.swap(new_inst.words); - trace(99, "transform") << "instruction after transform: '" << data_to_string(inst) << "'" << end(); - } -} - -string data_to_string(const line& inst) { - ostringstream out; - for (int i = 0; i < SIZE(inst.words); ++i) { - if (i > 0) out << ' '; - out << inst.words.at(i).data; - } - return out.str(); -} - -string drop_last(const string& s) { - return string(s.begin(), --s.end()); -} - -//: Label definitions must be the first word on a line. No jumping inside -//: instructions. -//: They should also be the only word on a line. -//: However, you can absolutely have multiple labels map to the same address, -//: as long as they're on separate lines. - -void test_multiple_labels_at() { - transform( - "== code 0x1\n" - // address 1 - "loop:\n" - " $loop2:\n" - // address 1 (labels take up no space) - " 05 0x0d0c0b0a/imm32\n" - // address 6 - " eb $loop2/disp8\n" - // address 8 - " eb $loop3/disp8\n" - // address 0xa - " $loop3:\n" - ); - CHECK_TRACE_CONTENTS( - "transform: label 'loop' is at address 1\n" - "transform: label '$loop2' is at address 1\n" - "transform: label '$loop3' is at address a\n" - // first jump is to -7 - "transform: instruction after transform: 'eb f9'\n" - // second jump is to 0 (fall through) - "transform: instruction after transform: 'eb 00'\n" - ); -} - -void test_loading_label_as_imm32() { - transform( - "== code 0x1\n" - "label:\n" - " be/copy-to-ESI label/imm32\n" - ); - CHECK_TRACE_CONTENTS( - "transform: label 'label' is at address 1\n" - "transform: instruction after transform: 'be 01 00 00 00'\n" - ); -} - -void test_duplicate_label() { - Hide_errors = true; - transform( - "== code 0x1\n" - "loop:\n" - "loop:\n" - " 05 0x0d0c0b0a/imm32\n" - ); - CHECK_TRACE_CONTENTS( - "error: duplicate label 'loop'\n" - ); -} - -void test_label_too_short() { - Hide_errors = true; - transform( - "== code 0x1\n" - "xz:\n" - " 05 0x0d0c0b0a/imm32\n" - ); - CHECK_TRACE_CONTENTS( - "error: 'xz' is two characters long, which can look like raw hex bytes at a glance; use a different name\n" - ); -} - -void test_label_hex() { - Hide_errors = true; - transform( - "== code 0x1\n" - "0xab:\n" - " 05 0x0d0c0b0a/imm32\n" - ); - CHECK_TRACE_CONTENTS( - "error: '0xab' looks like a hex number; use a different name\n" - ); -} - -void test_label_negative_hex() { - Hide_errors = true; - transform( - "== code 0x1\n" - "-a:\n" - " 05 0x0d0c0b0a/imm32\n" - ); - CHECK_TRACE_CONTENTS( - "error: '-a' starts with '-', which can be confused with a negative number; use a different name\n" - ); -} - -//: As said up top, the 'Entry' label is special. -//: It can be non-unique; the last declaration overrides earlier ones. -//: It must exist in a program. Otherwise we don't know where to start running -//: programs. - -void test_duplicate_Entry_label() { - transform( - "== code 0x1\n" - "Entry:\n" - "Entry:\n" - " 05 0x0d0c0b0a/imm32\n" - ); - CHECK_TRACE_DOESNT_CONTAIN_ERRORS(); -} - -// This test could do with some refactoring. -// We're duplicating the flow inside `subx translate`, but without -// reading/writing files. -// We can't just use run(string) because most of our tests allow programs -// without 'Entry' labels, as a convenience. -void test_programs_without_Entry_label() { - Hide_errors = true; - program p; - istringstream in( - "== code 0x1\n" - "05 0x0d0c0b0a/imm32\n" - "05 0x0d0c0b0a/imm32\n" - ); - parse(in, p); - transform(p); - ostringstream dummy; - save_elf(p, dummy); - CHECK_TRACE_CONTENTS( - "error: no 'Entry' label found\n" - ); -} - -//: now that we have labels, we need to adjust segment size computation to -//: ignore them. - -void test_segment_size_ignores_labels() { - transform( - "== code 0x09000074\n" - " 05/add 0x0d0c0b0a/imm32\n" // 5 bytes - "foo:\n" // 0 bytes - "== data 0x0a000000\n" - "bar:\n" - " 00\n" - ); - CHECK_TRACE_CONTENTS( - "transform: segment 1 begins at address 0x0a000079\n" - ); -} - -:(before "End size_of(word w) Special-cases") -else if (is_label(w)) - return 0; diff --git a/subx/036global_variables.cc b/subx/036global_variables.cc deleted file mode 100644 index c22ac3d3..00000000 --- a/subx/036global_variables.cc +++ /dev/null @@ -1,305 +0,0 @@ -//: Global variables. -//: -//: Global variables are just labels in the data segment. -//: However, they can only be used in imm32 and not disp32 operands. And they -//: can't be used with jump and call instructions. -//: -//: This layer has much the same structure as rewriting labels. - -:(code) -void test_global_variable() { - run( - "== code 0x1\n" - "b9 x/imm32\n" - "== data 0x2000\n" - "x:\n" - " 00 00 00 00\n" - ); - CHECK_TRACE_CONTENTS( - "transform: global variable 'x' is at address 0x00002000\n" - ); -} - -:(before "End Level-2 Transforms") -Transform.push_back(rewrite_global_variables); -:(code) -void rewrite_global_variables(program& p) { - trace(3, "transform") << "-- rewrite global variables" << end(); - // Begin rewrite_global_variables - map<string, uint32_t> address; - compute_addresses_for_global_variables(p, address); - if (trace_contains_errors()) return; - drop_global_variables(p); - replace_global_variables_with_addresses(p, address); -} - -void compute_addresses_for_global_variables(const program& p, map<string, uint32_t>& address) { - for (int i = 0; i < SIZE(p.segments); ++i) { - if (p.segments.at(i).name != "code") - compute_addresses_for_global_variables(p.segments.at(i), address); - } -} - -void compute_addresses_for_global_variables(const segment& s, map<string, uint32_t>& address) { - int current_address = s.start; - for (int i = 0; i < SIZE(s.lines); ++i) { - const line& inst = s.lines.at(i); - for (int j = 0; j < SIZE(inst.words); ++j) { - const word& curr = inst.words.at(j); - if (*curr.data.rbegin() != ':') { - current_address += size_of(curr); - } - else { - string variable = drop_last(curr.data); - // ensure variables look sufficiently different from raw hex - check_valid_name(variable); - if (trace_contains_errors()) return; - if (j > 0) - raise << "'" << to_string(inst) << "': global variable names can only be the first word in a line.\n" << end(); - if (Labels_file.is_open()) - Labels_file << "0x" << HEXWORD << current_address << ' ' << variable << '\n'; - if (contains_key(address, variable)) { - raise << "duplicate global '" << variable << "'\n" << end(); - return; - } - put(address, variable, current_address); - trace(99, "transform") << "global variable '" << variable << "' is at address 0x" << HEXWORD << current_address << end(); - // no modifying current_address; global variable definitions won't be in the final binary - } - } - } -} - -void drop_global_variables(program& p) { - for (int i = 0; i < SIZE(p.segments); ++i) { - if (p.segments.at(i).name != "code") - drop_labels(p.segments.at(i)); - } -} - -void replace_global_variables_with_addresses(program& p, const map<string, uint32_t>& address) { - if (p.segments.empty()) return; - for (int i = 0; i < SIZE(p.segments); ++i) { - segment& curr = p.segments.at(i); - if (curr.name == "code") - replace_global_variables_in_code_segment(curr, address); - else - replace_global_variables_in_data_segment(curr, address); - } -} - -void replace_global_variables_in_code_segment(segment& code, const map<string, uint32_t>& address) { - for (int i = 0; i < SIZE(code.lines); ++i) { - line& inst = code.lines.at(i); - line new_inst; - for (int j = 0; j < SIZE(inst.words); ++j) { - const word& curr = inst.words.at(j); - if (!contains_key(address, curr.data)) { - if (!looks_like_hex_int(curr.data)) - raise << "missing reference to global '" << curr.data << "'\n" << end(); - new_inst.words.push_back(curr); - continue; - } - if (!valid_use_of_global_variable(curr)) { - raise << "'" << to_string(inst) << "': can't refer to global variable '" << curr.data << "'\n" << end(); - return; - } - emit_hex_bytes(new_inst, get(address, curr.data), 4); - } - inst.words.swap(new_inst.words); - trace(99, "transform") << "instruction after transform: '" << data_to_string(inst) << "'" << end(); - } -} - -void replace_global_variables_in_data_segment(segment& data, const map<string, uint32_t>& address) { - for (int i = 0; i < SIZE(data.lines); ++i) { - line& l = data.lines.at(i); - line new_l; - for (int j = 0; j < SIZE(l.words); ++j) { - const word& curr = l.words.at(j); - if (!contains_key(address, curr.data)) { - if (looks_like_hex_int(curr.data)) { - if (has_operand_metadata(curr, "imm32")) - emit_hex_bytes(new_l, curr, 4); - else if (has_operand_metadata(curr, "imm16")) - emit_hex_bytes(new_l, curr, 2); - else if (has_operand_metadata(curr, "imm8")) - emit_hex_bytes(new_l, curr, 1); - else if (has_operand_metadata(curr, "disp8")) - raise << "can't use /disp8 in a non-code segment\n" << end(); - else if (has_operand_metadata(curr, "disp16")) - raise << "can't use /disp16 in a non-code segment\n" << end(); - else if (has_operand_metadata(curr, "disp32")) - raise << "can't use /disp32 in a non-code segment\n" << end(); - else - new_l.words.push_back(curr); - } - else { - raise << "missing reference to global '" << curr.data << "'\n" << end(); - new_l.words.push_back(curr); - } - continue; - } - trace(99, "transform") << curr.data << " maps to " << HEXWORD << get(address, curr.data) << end(); - emit_hex_bytes(new_l, get(address, curr.data), 4); - } - l.words.swap(new_l.words); - trace(99, "transform") << "after transform: '" << data_to_string(l) << "'" << end(); - } -} - -bool valid_use_of_global_variable(const word& curr) { - if (has_operand_metadata(curr, "imm32")) return true; - // End Valid Uses Of Global Variable(curr) - return false; -} - -//:: a more complex sanity check for how we use global variables -//: requires first saving some data early before we pack operands - -:(after "Begin Level-2 Transforms") -Transform.push_back(correlate_disp32_with_mod); -:(code) -void correlate_disp32_with_mod(program& p) { - if (p.segments.empty()) return; - segment& code = *find(p, "code"); - for (int i = 0; i < SIZE(code.lines); ++i) { - line& inst = code.lines.at(i); - for (int j = 0; j < SIZE(inst.words); ++j) { - word& curr = inst.words.at(j); - if (has_operand_metadata(curr, "disp32") - && has_operand_metadata(inst, "mod")) - curr.metadata.push_back("has_mod"); - } - } -} - -:(before "End Valid Uses Of Global Variable(curr)") -if (has_operand_metadata(curr, "disp32")) - return has_metadata(curr, "has_mod"); -// todo: more sophisticated check, to ensure we don't use global variable -// addresses as a real displacement added to other operands. - -:(code) -bool has_metadata(const word& w, const string& m) { - for (int i = 0; i < SIZE(w.metadata); ++i) - if (w.metadata.at(i) == m) return true; - return false; -} - -void test_global_variable_disallowed_in_jump() { - Hide_errors = true; - run( - "== code 0x1\n" - "eb/jump x/disp8\n" - "== data 0x2000\n" - "x:\n" - " 00 00 00 00\n" - ); - CHECK_TRACE_CONTENTS( - "error: 'eb/jump x/disp8': can't refer to global variable 'x'\n" - // sub-optimal error message; should be -//? "error: can't jump to data (variable 'x')\n" - ); -} - -void test_global_variable_disallowed_in_call() { - Hide_errors = true; - run( - "== code 0x1\n" - "e8/call x/disp32\n" - "== data 0x2000\n" - "x:\n" - " 00 00 00 00\n" - ); - CHECK_TRACE_CONTENTS( - "error: 'e8/call x/disp32': can't refer to global variable 'x'\n" - // sub-optimal error message; should be -//? "error: can't call to the data segment ('x')\n" - ); -} - -void test_global_variable_in_data_segment() { - run( - "== code 0x1\n" - "b9 x/imm32\n" - "== data 0x2000\n" - "x:\n" - " y/imm32\n" - "y:\n" - " 00 00 00 00\n" - ); - // check that we loaded 'x' with the address of 'y' - CHECK_TRACE_CONTENTS( - "load: 0x00002000 -> 04\n" - "load: 0x00002001 -> 20\n" - "load: 0x00002002 -> 00\n" - "load: 0x00002003 -> 00\n" - ); - CHECK_TRACE_COUNT("error", 0); -} - -void test_raw_number_with_imm32_in_data_segment() { - run( - "== code 0x1\n" - "b9 x/imm32\n" - "== data 0x2000\n" - "x:\n" - " 1/imm32\n" - ); - // check that we loaded 'x' with the address of 1 - CHECK_TRACE_CONTENTS( - "load: 0x00002000 -> 01\n" - "load: 0x00002001 -> 00\n" - "load: 0x00002002 -> 00\n" - "load: 0x00002003 -> 00\n" - ); - CHECK_TRACE_COUNT("error", 0); -} - -void test_duplicate_global_variable() { - Hide_errors = true; - run( - "== code 0x1\n" - "40/increment-EAX\n" - "== data 0x2000\n" - "x:\n" - "x:\n" - " 00\n" - ); - CHECK_TRACE_CONTENTS( - "error: duplicate global 'x'\n" - ); -} - -void test_global_variable_disp32_with_modrm() { - run( - "== code 0x1\n" - "8b/copy 0/mod/indirect 5/rm32/.disp32 2/r32/EDX x/disp32\n" - "== data 0x2000\n" - "x:\n" - " 00 00 00 00\n" - ); - CHECK_TRACE_COUNT("error", 0); -} - -void test_global_variable_disp32_with_call() { - transform( - "== code 0x1\n" - "foo:\n" - " e8/call bar/disp32\n" - "bar:\n" - ); - CHECK_TRACE_COUNT("error", 0); -} - -string to_full_string(const line& in) { - ostringstream out; - for (int i = 0; i < SIZE(in.words); ++i) { - if (i > 0) out << ' '; - out << in.words.at(i).data; - for (int j = 0; j < SIZE(in.words.at(i).metadata); ++j) - out << '/' << in.words.at(i).metadata.at(j); - } - return out.str(); -} diff --git a/subx/038---literal_strings.cc b/subx/038---literal_strings.cc deleted file mode 100644 index ecc80176..00000000 --- a/subx/038---literal_strings.cc +++ /dev/null @@ -1,324 +0,0 @@ -//: Allow instructions to mention literals directly. -//: -//: This layer will transparently move them to the global segment (assumed to -//: always be the second segment). - -void test_transform_literal_string() { - run( - "== code 0x1\n" - "b8/copy \"test\"/imm32\n" - "== data 0x2000\n" // need an empty segment - ); - CHECK_TRACE_CONTENTS( - "transform: -- move literal strings to data segment\n" - "transform: adding global variable '__subx_global_1' containing \"test\"\n" - "transform: instruction after transform: 'b8 __subx_global_1'\n" - ); -} - -//: We don't rely on any transforms running in previous layers, but this layer -//: knows about labels and global variables and will emit them for previous -//: layers to transform. -:(after "Begin Transforms") -// Begin Level-3 Transforms -Transform.push_back(transform_literal_strings); -// End Level-3 Transforms - -:(before "End Globals") -int Next_auto_global = 1; -:(code) -void transform_literal_strings(program& p) { - trace(3, "transform") << "-- move literal strings to data segment" << end(); - if (p.segments.empty()) return; - segment& code = *find(p, "code"); - segment& data = *find(p, "data"); - for (int i = 0; i < SIZE(code.lines); ++i) { - line& inst = code.lines.at(i); - for (int j = 0; j < SIZE(inst.words); ++j) { - word& curr = inst.words.at(j); - if (curr.data.at(0) != '"') continue; - ostringstream global_name; - global_name << "__subx_global_" << Next_auto_global; - ++Next_auto_global; - add_global_to_data_segment(global_name.str(), curr, data); - curr.data = global_name.str(); - } - trace(99, "transform") << "instruction after transform: '" << data_to_string(inst) << "'" << end(); - } -} - -void add_global_to_data_segment(const string& name, const word& value, segment& data) { - trace(99, "transform") << "adding global variable '" << name << "' containing " << value.data << end(); - // emit label - data.lines.push_back(label(name)); - // emit size for size-prefixed array - data.lines.push_back(line()); - emit_hex_bytes(data.lines.back(), SIZE(value.data)-/*skip quotes*/2, 4/*bytes*/); - // emit data byte by byte - data.lines.push_back(line()); - line& curr = data.lines.back(); - for (int i = /*skip start quote*/1; i < SIZE(value.data)-/*skip end quote*/1; ++i) { - char c = value.data.at(i); - curr.words.push_back(word()); - curr.words.back().data = hex_byte_to_string(c); - curr.words.back().metadata.push_back(string(1, c)); - } -} - -//: Within strings, whitespace is significant. So we need to redo our instruction -//: parsing. - -void test_instruction_with_string_literal() { - parse_instruction_character_by_character( - "a \"abc def\" z\n" // two spaces inside string - ); - CHECK_TRACE_CONTENTS( - "parse2: word: a\n" - "parse2: word: \"abc def\"\n" - "parse2: word: z\n" - ); - // no other words - CHECK_TRACE_COUNT("parse2", 3); -} - -:(before "End Line Parsing Special-cases(line_data -> l)") -if (line_data.find('"') != string::npos) { // can cause false-positives, but we can handle them - parse_instruction_character_by_character(line_data, l); - continue; -} - -:(code) -void parse_instruction_character_by_character(const string& line_data, vector<line>& out) { - if (line_data.find('\n') != string::npos && line_data.find('\n') != line_data.size()-1) { - raise << "parse_instruction_character_by_character: should receive only a single line\n" << end(); - return; - } - // parse literals - istringstream in(line_data); - in >> std::noskipws; - line result; - result.original = line_data; - // add tokens (words or strings) one by one - while (has_data(in)) { - skip_whitespace(in); - if (!has_data(in)) break; - char c = in.get(); - if (c == '#') break; // comment; drop rest of line - if (c == ':') break; // line metadata; skip for now - if (c == '.') { - if (!has_data(in)) break; // comment token at end of line - if (isspace(in.peek())) - continue; // '.' followed by space is comment token; skip - } - result.words.push_back(word()); - if (c == '"') { - // string literal; slurp everything between quotes into data - ostringstream d; - d << c; - while (has_data(in)) { - in >> c; - if (c == '\\') { - in >> c; - if (c == 'n') d << '\n'; - else if (c == '"') d << '"'; - else if (c == '\\') d << '\\'; - else { - raise << "parse_instruction_character_by_character: unknown escape sequence '\\" << c << "'\n" << end(); - return; - } - continue; - } else { - d << c; - } - if (c == '"') break; - } - result.words.back().data = d.str(); - // slurp metadata - ostringstream m; - while (!isspace(in.peek()) && has_data(in)) { // peek can sometimes trigger eof(), so do it first - in >> c; - if (c == '/') { - if (!m.str().empty()) result.words.back().metadata.push_back(m.str()); - m.str(""); - } - else { - m << c; - } - } - if (!m.str().empty()) result.words.back().metadata.push_back(m.str()); - } - else { - // not a string literal; slurp all characters until whitespace - ostringstream w; - w << c; - while (!isspace(in.peek()) && has_data(in)) { // peek can sometimes trigger eof(), so do it first - in >> c; - w << c; - } - parse_word(w.str(), result.words.back()); - } - trace(99, "parse2") << "word: " << to_string(result.words.back()) << end(); - } - if (!result.words.empty()) - out.push_back(result); -} - -void skip_whitespace(istream& in) { - while (true) { - if (has_data(in) && isspace(in.peek())) in.get(); - else break; - } -} - -void skip_comment(istream& in) { - if (has_data(in) && in.peek() == '#') { - in.get(); - while (has_data(in) && in.peek() != '\n') in.get(); - } -} - -line label(string s) { - line result; - result.words.push_back(word()); - result.words.back().data = (s+":"); - return result; -} - -// helper for tests -void parse_instruction_character_by_character(const string& line_data) { - vector<line> out; - parse_instruction_character_by_character(line_data, out); -} - -void test_parse2_comment_token_in_middle() { - parse_instruction_character_by_character( - "a . z\n" - ); - CHECK_TRACE_CONTENTS( - "parse2: word: a\n" - "parse2: word: z\n" - ); - CHECK_TRACE_DOESNT_CONTAIN("parse2: word: ."); - // no other words - CHECK_TRACE_COUNT("parse2", 2); -} - -void test_parse2_word_starting_with_dot() { - parse_instruction_character_by_character( - "a .b c\n" - ); - CHECK_TRACE_CONTENTS( - "parse2: word: a\n" - "parse2: word: .b\n" - "parse2: word: c\n" - ); -} - -void test_parse2_comment_token_at_start() { - parse_instruction_character_by_character( - ". a b\n" - ); - CHECK_TRACE_CONTENTS( - "parse2: word: a\n" - "parse2: word: b\n" - ); - CHECK_TRACE_DOESNT_CONTAIN("parse2: word: ."); -} - -void test_parse2_comment_token_at_end() { - parse_instruction_character_by_character( - "a b .\n" - ); - CHECK_TRACE_CONTENTS( - "parse2: word: a\n" - "parse2: word: b\n" - ); - CHECK_TRACE_DOESNT_CONTAIN("parse2: word: ."); -} - -void test_parse2_word_starting_with_dot_at_start() { - parse_instruction_character_by_character( - ".a b c\n" - ); - CHECK_TRACE_CONTENTS( - "parse2: word: .a\n" - "parse2: word: b\n" - "parse2: word: c\n" - ); -} - -void test_parse2_metadata() { - parse_instruction_character_by_character( - ".a b/c d\n" - ); - CHECK_TRACE_CONTENTS( - "parse2: word: .a\n" - "parse2: word: b /c\n" - "parse2: word: d\n" - ); -} - -void test_parse2_string_with_metadata() { - parse_instruction_character_by_character( - "a \"bc def\"/disp32 g\n" - ); - CHECK_TRACE_CONTENTS( - "parse2: word: a\n" - "parse2: word: \"bc def\" /disp32\n" - "parse2: word: g\n" - ); -} - -void test_parse2_string_with_metadata_at_end() { - parse_instruction_character_by_character( - "a \"bc def\"/disp32\n" - ); - CHECK_TRACE_CONTENTS( - "parse2: word: a\n" - "parse2: word: \"bc def\" /disp32\n" - ); -} - -void test_parse2_string_with_metadata_at_end_of_line_without_newline() { - parse_instruction_character_by_character( - "68/push \"test\"/f" // no newline, which is how calls from parse() will look - ); - CHECK_TRACE_CONTENTS( - "parse2: word: 68 /push\n" - "parse2: word: \"test\" /f\n" - ); -} - -//: Make sure slashes inside strings don't trigger adding stuff from inside the -//: string to metadata. - -void test_parse2_string_containing_slashes() { - parse_instruction_character_by_character( - "a \"bc/def\"/disp32\n" - ); - CHECK_TRACE_CONTENTS( - "parse2: word: \"bc/def\" /disp32\n" - ); -} - -void test_instruction_with_string_literal_with_escaped_quote() { - parse_instruction_character_by_character( - "\"a\\\"b\"\n" // escaped quote inside string - ); - CHECK_TRACE_CONTENTS( - "parse2: word: \"a\"b\"\n" - ); - // no other words - CHECK_TRACE_COUNT("parse2", 1); -} - -void test_instruction_with_string_literal_with_escaped_backslash() { - parse_instruction_character_by_character( - "\"a\\\\b\"\n" // escaped backslash inside string - ); - CHECK_TRACE_CONTENTS( - "parse2: word: \"a\\b\"\n" - ); - // no other words - CHECK_TRACE_COUNT("parse2", 1); -} diff --git a/subx/039debug.cc b/subx/039debug.cc deleted file mode 100644 index b308a7d4..00000000 --- a/subx/039debug.cc +++ /dev/null @@ -1,147 +0,0 @@ -//:: Some helpers for debugging. - -//: Load the 'map' file generated during 'subx --debug translate' when running -//: 'subx --debug --trace run'. -//: (It'll only affect the trace.) - -:(before "End Globals") -map</*address*/uint32_t, string> Symbol_name; // used only by 'subx run' -map</*address*/uint32_t, string> Source_line; // used only by 'subx run' -:(before "End --debug Settings") -load_labels(); -load_source_lines(); -:(code) -void load_labels() { - ifstream fin("labels"); - fin >> std::hex; - while (has_data(fin)) { - uint32_t addr = 0; - fin >> addr; - string name; - fin >> name; - put(Symbol_name, addr, name); - } -} - -void load_source_lines() { - ifstream fin("source_lines"); - fin >> std::hex; - while (has_data(fin)) { - uint32_t addr = 0; - fin >> addr; - string line; - getline(fin, line); - put(Source_line, addr, hacky_squeeze_out_whitespace(line)); - } -} - -:(after "Run One Instruction") -if (contains_key(Symbol_name, EIP)) - trace(Callstack_depth, "run") << "== label " << get(Symbol_name, EIP) << end(); -if (contains_key(Source_line, EIP)) - trace(Callstack_depth, "run") << "0x" << HEXWORD << EIP << ": " << get(Source_line, EIP) << end(); -else - // no source line info; do what you can - trace(Callstack_depth, "run") << "0x" << HEXWORD << EIP << ": " << debug_info(EIP) << end(); - -:(code) -string debug_info(uint32_t inst_address) { - uint8_t op = read_mem_u8(inst_address); - if (op != 0xe8) { - ostringstream out; - out << HEXBYTE << NUM(op); - return out.str(); - } - int32_t offset = read_mem_i32(inst_address+/*skip op*/1); - uint32_t next_eip = inst_address+/*inst length*/5+offset; - if (contains_key(Symbol_name, next_eip)) - return "e8/call "+get(Symbol_name, next_eip); - ostringstream out; - out << "e8/call 0x" << HEXWORD << next_eip; - return out.str(); -} - -//: If a label starts with '$watch-', make a note of the effective address -//: computed by the next instruction. Start dumping out its contents to the -//: trace after every subsequent instruction. - -:(after "Run One Instruction") -dump_watch_points(); -:(before "End Globals") -map<string, uint32_t> Watch_points; -:(before "End Reset") -Watch_points.clear(); -:(code) -void dump_watch_points() { - if (Watch_points.empty()) return; - trace(Callstack_depth, "dbg") << "watch points:" << end(); - for (map<string, uint32_t>::iterator p = Watch_points.begin(); p != Watch_points.end(); ++p) - trace(Callstack_depth, "dbg") << " " << p->first << ": " << HEXWORD << p->second << " -> " << HEXWORD << read_mem_u32(p->second) << end(); -} - -:(before "End Globals") -string Watch_this_effective_address; -:(after "Run One Instruction") -Watch_this_effective_address = ""; -if (contains_key(Symbol_name, EIP) && starts_with(get(Symbol_name, EIP), "$watch-")) - Watch_this_effective_address = get(Symbol_name, EIP); -:(after "Found effective_address(addr)") -if (!Watch_this_effective_address.empty()) { - dbg << "now watching " << HEXWORD << addr << " for " << Watch_this_effective_address << end(); - put(Watch_points, Watch_this_effective_address, addr); -} - -//: Special label that dumps regions of memory. -//: Not a general mechanism; by the time you get here you're willing to hack -//: on the emulator. -:(after "Run One Instruction") -if (contains_key(Symbol_name, EIP) && get(Symbol_name, EIP) == "$dump-stream-at-EAX") - dump_stream_at(Reg[EAX].u); -:(code) -void dump_stream_at(uint32_t stream_start) { - int32_t stream_length = read_mem_i32(stream_start + 8); - dbg << "stream length: " << std::dec << stream_length << end(); - for (int i = 0; i < stream_length + 12; ++i) - dbg << "0x" << HEXWORD << (stream_start+i) << ": " << HEXBYTE << NUM(read_mem_u8(stream_start+i)) << end(); -} - -//: helpers - -:(code) -string hacky_squeeze_out_whitespace(const string& s) { - // strip whitespace at start - string::const_iterator first = s.begin(); - while (first != s.end() && isspace(*first)) - ++first; - if (first == s.end()) return ""; - - // strip whitespace at end - string::const_iterator last = --s.end(); - while (last != s.begin() && isspace(*last)) - --last; - ++last; - - // replace runs of spaces/dots with single space until comment or string - // TODO: - // leave alone dots not surrounded by whitespace - // leave alone '#' within word - // leave alone '"' within word - // squeeze spaces after end of string - ostringstream out; - bool previous_was_space = false; - bool in_comment_or_string = false; - for (string::const_iterator curr = first; curr != last; ++curr) { - if (in_comment_or_string) - out << *curr; - else if (isspace(*curr) || *curr == '.') - previous_was_space = true; - else { - if (previous_was_space) - out << ' '; - out << *curr; - previous_was_space = false; - if (*curr == '#' || *curr == '"') in_comment_or_string = true; - } - } - return out.str(); -} diff --git a/subx/040---tests.cc b/subx/040---tests.cc deleted file mode 100644 index af05bff3..00000000 --- a/subx/040---tests.cc +++ /dev/null @@ -1,97 +0,0 @@ -//: Automatically aggregate functions starting with 'test-' into a test suite -//: called 'run-tests'. Running this function will run all tests. -//: -//: This is actually SubX's first (trivial) compiler. We generate all the code -//: needed for the 'run-tests' function. -//: -//: By convention, temporary functions needed by tests will start with -//: '_test-'. - -//: We don't rely on any transforms running in previous layers, but this layer -//: knows about labels and will emit labels for previous layers to transform. -:(after "Begin Transforms") -// Begin Level-4 Transforms -Transform.push_back(create_test_function); -// End Level-4 Transforms - -:(code) -void test_run_test() { - Mem.push_back(vma(0xbd000000)); // manually allocate memory - Reg[ESP].u = 0xbd000100; - run( - "== code 0x1\n" // code segment - "main:\n" - " e8/call run-tests/disp32\n" // 5 bytes - " f4/halt\n" // 1 byte - "test-foo:\n" // offset 7 - " 01 d8\n" // just some unique instruction: add EBX to EAX - " c3/return\n" - ); - // check that code in test-foo ran (implicitly called by run-tests) - CHECK_TRACE_CONTENTS( - "run: 0x00000007 opcode: 01\n" - ); -} - -void create_test_function(program& p) { - if (p.segments.empty()) return; - segment& code = *find(p, "code"); - trace(3, "transform") << "-- create 'run-tests'" << end(); - vector<line> new_insts; - for (int i = 0; i < SIZE(code.lines); ++i) { - line& inst = code.lines.at(i); - for (int j = 0; j < SIZE(inst.words); ++j) { - const word& curr = inst.words.at(j); - if (*curr.data.rbegin() != ':') continue; // not a label - if (!starts_with(curr.data, "test-")) continue; - string fn = drop_last(curr.data); - new_insts.push_back(call(fn)); - } - } - if (new_insts.empty()) return; // no tests found - code.lines.push_back(label("run-tests")); - code.lines.insert(code.lines.end(), new_insts.begin(), new_insts.end()); - code.lines.push_back(ret()); -} - -string to_string(const segment& s) { - ostringstream out; - for (int i = 0; i < SIZE(s.lines); ++i) { - const line& l = s.lines.at(i); - for (int j = 0; j < SIZE(l.words); ++j) { - if (j > 0) out << ' '; - out << to_string(l.words.at(j)); - } - out << '\n'; - } - return out.str(); -} - -line call(string s) { - line result; - result.words.push_back(call()); - result.words.push_back(disp32(s)); - return result; -} - -word call() { - word result; - result.data = "e8"; - result.metadata.push_back("call"); - return result; -} - -word disp32(string s) { - word result; - result.data = s; - result.metadata.push_back("disp32"); - return result; -} - -line ret() { - line result; - result.words.push_back(word()); - result.words.back().data = "c3"; - result.words.back().metadata.push_back("return"); - return result; -} diff --git a/subx/049memory_layout.subx b/subx/049memory_layout.subx deleted file mode 100644 index 36837ade..00000000 --- a/subx/049memory_layout.subx +++ /dev/null @@ -1,8 +0,0 @@ -# Segment addresses aren't really the concern of any other layer, so we'll -# define them separately. They're approximate due to fidgety ELF alignment -# requirements, so don't get clever assuming variables are at specific -# addresses. - -== code 0x09000000 - -== data 0x0a000000 diff --git a/subx/050_write.subx b/subx/050_write.subx deleted file mode 100644 index 0d6b8152..00000000 --- a/subx/050_write.subx +++ /dev/null @@ -1,57 +0,0 @@ -# _write: write to a file descriptor (fd) - -== code -# instruction effective address register displacement immediate -# . op subop mod rm32 base index scale r32 -# . 1-3 bytes 3 bits 2 bits 3 bits 3 bits 3 bits 2 bits 2 bits 0/1/2/4 bytes 0/1/2/4 bytes - -Entry: # just exit; can't test _write just yet - # . syscall(exit, 0) - bb/copy-to-EBX 0/imm32 - b8/copy-to-EAX 1/imm32/exit - cd/syscall 0x80/imm8 - -_write: # fd : int, s : (address array byte) -> <void> - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # . save registers - 50/push-EAX - 51/push-ECX - 52/push-EDX - 53/push-EBX - # syscall(write, fd, (data) s+4, (size) *s) - # . fd : EBX - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 3/r32/EBX 8/disp8 . # copy *(EBP+8) to EBX - # . data : ECX = s+4 - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 1/r32/ECX 0xc/disp8 . # copy *(EBP+12) to ECX - 81 0/subop/add 3/mod/direct 1/rm32/ECX . . . . . 4/imm32 # add to ECX - # . size : EDX = *s - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 2/r32/EDX 0xc/disp8 . # copy *(EBP+12) to EDX - 8b/copy 0/mod/indirect 2/rm32/EDX . . . 2/r32/EDX . . # copy *EDX to EDX - # . syscall - b8/copy-to-EAX 4/imm32/write - cd/syscall 0x80/imm8 - # if (EAX < 0) abort - 3d/compare-EAX-with 0/imm32 - 0f 8c/jump-if-lesser $_write:abort/disp32 -$_write:end: - # . restore registers - 5b/pop-to-EBX - 5a/pop-to-EDX - 59/pop-to-ECX - 58/pop-to-EAX - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -$_write:abort: - # can't write a message here for risk of an infinite loop, so we'll use a special exit code instead - # . syscall(exit, 255) - bb/copy-to-EBX 0xff/imm32 - b8/copy-to-EAX 1/imm32/exit - cd/syscall 0x80/imm8 - # never gets here - -# . . vim:nowrap:textwidth=0 diff --git a/subx/051test.subx b/subx/051test.subx deleted file mode 100644 index 6f8cda7a..00000000 --- a/subx/051test.subx +++ /dev/null @@ -1,93 +0,0 @@ -# Rudimentary test harness - -== code -# instruction effective address register displacement immediate -# . op subop mod rm32 base index scale r32 -# . 1-3 bytes 3 bits 2 bits 3 bits 3 bits 3 bits 2 bits 2 bits 0/1/2/4 bytes 0/1/2/4 bytes - -Entry: # manual test - # check-ints-equal(34, 34) - # . . push args - 68/push "error in check-ints-equal"/imm32 - 68/push 34/imm32 - 68/push 34/imm32 - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # syscall(exit, 0) - bb/copy-to-EBX 0/imm32 - b8/copy-to-EAX 1/imm32/exit - cd/syscall 0x80/imm8 - -# print msg to stderr if a != b, otherwise print "." -check-ints-equal: # (a : int, b : int, msg : (address array byte)) -> <void> - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # . save registers - 50/push-EAX - 51/push-ECX - 53/push-EBX - # load first 2 args into EAX and EBX - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 0/r32/EAX 8/disp8 . # copy *(EBP+8) to EAX - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 3/r32/EBX 0xc/disp8 . # copy *(EBP+12) to EBX - # if (EAX == EBX) success - 39/compare 3/mod/direct 0/rm32/EAX . . . 3/r32/EBX . . # compare EAX and EBX - 75/jump-if-unequal $check-ints-equal:else/disp8 - # . _write(2/stderr, '.') - # . . push args - 68/push "."/imm32 - 68/push 2/imm32/stderr - # . . call - e8/call _write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . return - eb/jump $check-ints-equal:end/disp8 - # otherwise print error message -$check-ints-equal:else: - # . _write(2/stderr, msg) - # . . push args - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 1/r32/ECX 0x10/disp8 . # copy *(EBP+16) to ECX - 51/push-ECX - 68/push 2/imm32/stderr - # . . call - e8/call _write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . _write(2/stderr, Newline) - # . . push args - 68/push Newline/imm32 - 68/push 2/imm32/stderr - # . . call - e8/call _write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # increment Num-test-failures - ff 0/subop/increment 0/mod/indirect 5/rm32/.disp32 . . . Num-test-failures/disp32 # increment *Num-test-failures -$check-ints-equal:end: - # . restore registers - 5b/pop-to-EBX - 59/pop-to-ECX - 58/pop-to-EAX - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -== data - -# length-prefixed string containing just a single newline -# convenient to have when printing messages and so on -Newline: - # size - 1/imm32 - # data - 0a/newline - -# every test failure increments this counter -Num-test-failures: - 0/imm32 - -# . . vim:nowrap:textwidth=0 diff --git a/subx/052kernel-string-equal.subx b/subx/052kernel-string-equal.subx deleted file mode 100644 index d2f196ea..00000000 --- a/subx/052kernel-string-equal.subx +++ /dev/null @@ -1,267 +0,0 @@ -# Checking null-terminated ascii strings. -# -# By default we create strings with a 4-byte length prefix rather than a null suffix. -# However we still need null-prefixed strings when interacting with the Linux -# kernel in a few places. This layer implements a function for comparing -# a null-terminated 'kernel string' with a length-prefixed 'SubX string'. -# -# To run (from the subx directory): -# $ ./subx translate 05[0-2]*.subx -o /tmp/tmp52 -# $ ./subx run /tmp/tmp52 # runs a series of tests -# ...... # all tests pass -# -# (We can't yet run the tests when given a "test" commandline argument, -# because checking for it would require the function being tested! Breakage -# would cause tests to not run, rather than to fail as we'd like.) - -== code -# instruction effective address register displacement immediate -# . op subop mod rm32 base index scale r32 -# . 1-3 bytes 3 bits 2 bits 3 bits 3 bits 3 bits 2 bits 2 bits 0/1/2/4 bytes 0/1/2/4 bytes - -Entry: # run all tests - e8/call run-tests/disp32 # 'run-tests' is a function created automatically by SubX. It calls all functions that start with 'test-'. - # syscall(exit, Num-test-failures) - 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 3/r32/EBX Num-test-failures/disp32 # copy *Num-test-failures to EBX - b8/copy-to-EAX 1/imm32/exit - cd/syscall 0x80/imm8 - -# compare a null-terminated ascii string with a more idiomatic length-prefixed byte array -# reason for the name: the only place we should have null-terminated ascii strings is from commandline args -kernel-string-equal?: # s : null-terminated ascii string, benchmark : length-prefixed ascii string -> EAX : boolean - # pseudocode: - # n = benchmark->length - # s1 = s - # s2 = benchmark->data - # i = 0 - # while (i < n) - # c1 = *s1 - # c2 = *s2 - # if (c1 == 0) return false - # if (c1 != c2) return false - # ++s1, ++s2, ++i - # return *s1 == 0 - # - # registers: - # i: ECX - # n: EDX - # s1: EDI - # s2: ESI - # c1: EAX - # c2: EBX - # - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # . save registers - 51/push-ECX - 52/push-EDX - 53/push-EBX - 56/push-ESI - 57/push-EDI - # s1/EDI = s - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 7/r32/EDI 8/disp8 . # copy *(EBP+8) to EDI - # n/EDX = benchmark->length - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 2/r32/EDX 0xc/disp8 . # copy *(EBP+12) to EDX - 8b/copy 0/mod/indirect 2/rm32/EDX . . . 2/r32/EDX . . # copy *EDX to EDX - # s2/ESI = benchmark->data - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 6/r32/ESI 0xc/disp8 . # copy *(EBP+12) to ESI - 81 0/subop/add 3/mod/direct 6/rm32/ESI . . . . . 4/imm32 # add to ESI - # i/ECX = c1/EAX = c2/EBX = 0 - b9/copy-to-ECX 0/imm32/exit - b8/copy-to-EAX 0/imm32 - bb/copy-to-EBX 0/imm32 -$kernel-string-equal?:loop: - # if (i >= n) break - 39/compare 3/mod/direct 1/rm32/ECX . . . 2/r32/EDX . . # compare ECX with EDX - 7d/jump-if-greater-or-equal $kernel-string-equal?:break/disp8 - # c1 = *s1 - 8a/copy-byte 0/mod/indirect 7/rm32/EDI . . . 0/r32/AL . . # copy byte at *EDI to AL - # c2 = *s2 - 8a/copy-byte 0/mod/indirect 6/rm32/ESI . . . 3/r32/BL . . # copy byte at *ESI to BL - # if (c1 == 0) return false - 3d/compare-EAX-and 0/imm32 - 74/jump-if-equal $kernel-string-equal?:false/disp8 - # if (c1 != c2) return false - 39/compare 3/mod/direct 0/rm32/EAX . . . 3/r32/EBX . . # compare EAX and EBX - 75/jump-if-not-equal $kernel-string-equal?:false/disp8 - # ++i - 41/increment-ECX - # ++s1 - 47/increment-EDI - # ++s2 - 46/increment-ESI - eb/jump $kernel-string-equal?:loop/disp8 -$kernel-string-equal?:break: - # return *s1 == 0 - 8a/copy-byte 0/mod/indirect 7/rm32/EDI . . . 0/r32/AL . . # copy byte at *EDI to AL - 3d/compare-EAX-and 0/imm32 - 75/jump-if-not-equal $kernel-string-equal?:false/disp8 -$kernel-string-equal?:true: - b8/copy-to-EAX 1/imm32 - eb/jump $kernel-string-equal?:end/disp8 -$kernel-string-equal?:false: - b8/copy-to-EAX 0/imm32 -$kernel-string-equal?:end: - # . restore registers - 5f/pop-to-EDI - 5e/pop-to-ESI - 5b/pop-to-EBX - 5a/pop-to-EDX - 59/pop-to-ECX - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -# - tests - -test-compare-null-kernel-string-with-empty-array: - # EAX = kernel-string-equal?(Null-kernel-string, "") - # . . push args - 68/push ""/imm32 - 68/push Null-kernel-string/imm32 - # . . call - e8/call kernel-string-equal?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check-ints-equal(EAX, 1, msg) - # . . push args - 68/push "F - test-compare-null-kernel-string-with-empty-array"/imm32 - 68/push 1/imm32/true - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - c3/return - -test-compare-null-kernel-string-with-non-empty-array: - # EAX = kernel-string-equal?(Null-kernel-string, "Abc") - # . . push args - 68/push "Abc"/imm32 - 68/push Null-kernel-string/imm32 - # . . call - e8/call kernel-string-equal?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check-ints-equal(EAX, 0, msg) - # . . push args - 68/push "F - test-compare-null-kernel-string-with-non-empty-array"/imm32 - 68/push 0/imm32/false - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - c3/return - -test-compare-kernel-string-with-equal-array: - # EAX = kernel-string-equal?(_test-Abc-kernel-string, "Abc") - # . . push args - 68/push "Abc"/imm32 - 68/push _test-Abc-kernel-string/imm32 - # . . call - e8/call kernel-string-equal?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check-ints-equal(EAX, 1, msg) - # . . push args - 68/push "F - test-compare-kernel-string-with-equal-array"/imm32 - 68/push 1/imm32/true - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - c3/return - -test-compare-kernel-string-with-inequal-array: - # EAX = kernel-string-equal?(_test-Abc-kernel-string, "Adc") - # . . push args - 68/push "Adc"/imm32 - 68/push _test-Abc-kernel-string/imm32 - # . . call - e8/call kernel-string-equal?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check-ints-equal(EAX, 0, msg) - # . . push args - 68/push "F - test-compare-kernel-string-with-equal-array"/imm32 - 68/push 0/imm32/false - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - c3/return - -test-compare-kernel-string-with-empty-array: - # EAX = kernel-string-equal?(_test-Abc-kernel-string, "") - # . . push args - 68/push ""/imm32 - 68/push _test-Abc-kernel-string/imm32 - # . . call - e8/call kernel-string-equal?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check-ints-equal(EAX, 0, msg) - # . . push args - 68/push "F - test-compare-kernel-string-with-equal-array"/imm32 - 68/push 0/imm32/false - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - c3/return - -test-compare-kernel-string-with-shorter-array: - # EAX = kernel-string-equal?(_test-Abc-kernel-string, "Ab") - # . . push args - 68/push "Ab"/imm32 - 68/push _test-Abc-kernel-string/imm32 - # . . call - e8/call kernel-string-equal?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check-ints-equal(EAX, 0, msg) - # . . push args - 68/push "F - test-compare-kernel-string-with-shorter-array"/imm32 - 68/push 0/imm32/false - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - c3/return - -test-compare-kernel-string-with-longer-array: - # EAX = kernel-string-equal?(_test-Abc-kernel-string, "Abcd") - # . . push args - 68/push "Abcd"/imm32 - 68/push _test-Abc-kernel-string/imm32 - # . . call - e8/call kernel-string-equal?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check-ints-equal(EAX, 0, msg) - # . . push args - 68/push "F - test-compare-kernel-string-with-longer-array"/imm32 - 68/push 0/imm32/false - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - c3/return - -== data - -Null-kernel-string: - 00/null - -_test-Abc-kernel-string: - 41/A 62/b 63/c 00/null - -# . . vim:nowrap:textwidth=0 diff --git a/subx/053new-segment.subx b/subx/053new-segment.subx deleted file mode 100644 index 83c890ea..00000000 --- a/subx/053new-segment.subx +++ /dev/null @@ -1,90 +0,0 @@ -# Create a new segment (pool of memory for allocating chunks from) in the form -# of an *allocation descriptor* that can be passed to the memory allocator -# (defined in a later layer). -# -# Currently an allocation descriptor consists of just the bounds of the pool of -# available memory: -# -# curr : address -# end : address -# -# This isn't enough information to reclaim individual allocations. We can't -# support arbitrary reclamation yet. - -== code -# instruction effective address register displacement immediate -# . op subop mod rm32 base index scale r32 -# . 1-3 bytes 3 bits 2 bits 3 bits 3 bits 3 bits 2 bits 2 bits 0/1/2/4 bytes 0/1/2/4 bytes - -Entry: # manual test - # var ad/ECX : (address allocation-descriptor) = {0, 0} - 68/push 0/imm32/limit - 68/push 0/imm32/curr - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # new-segment(0x1000, ad) - # . . push args - 51/push-ECX - 68/push 0x1000/imm32 - # . . call - e8/call new-segment/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # EAX = ad->curr - 8b/copy 0/mod/indirect 1/rm32/ECX . . . 0/r32/EAX . . # copy *ECX to EAX - # write to *EAX to check that we have access to the newly-allocated segment - c7 0/subop/copy 0/mod/direct 0/rm32/EAX . . . . . 0x34/imm32 # copy to *EAX - # syscall(exit, EAX) - 89/copy 3/mod/direct 3/rm32/EBX . . . 0/r32/EAX . . # copy EAX to EBX - b8/copy-to-EAX 1/imm32/exit - cd/syscall 0x80/imm8 - -new-segment: # len : int, ad : (address allocation-descriptor) - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # . save registers - 50/push-EAX - 53/push-EBX - # copy len to _mmap-new-segment->len - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 0/r32/EAX 8/disp8 . # copy *(EBP+8) to EAX - 89/copy 0/mod/indirect 5/rm32/.disp32 . . 0/r32/EAX _mmap-new-segment:len/disp32 # copy EAX to *_mmap-new-segment:len - # mmap(_mmap-new-segment) - bb/copy-to-EBX _mmap-new-segment/imm32 - b8/copy-to-EAX 0x5a/imm32/mmap - cd/syscall 0x80/imm8 - # copy {EAX, EAX+len} to *ad - # . EBX = ad - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 3/r32/EBX 0xc/disp8 . # copy *(EBP+12) to EBX - # . *EBX = EAX - 89/copy 0/mod/indirect 3/rm32/EBX . . . 0/r32/EAX . . # copy EAX to *EBX - # . *(EBX+4) = EAX+len - 03/add 1/mod/*+disp8 5/rm32/EBP . . . 0/r32/EAX 8/disp8 . # add *(EBP+8) to EAX - 89/copy 1/mod/*+disp8 3/rm32/EBX . . . 0/r32/EAX 4/disp8 . # copy EAX to *(EBX+4) -$new-segment:end: - # . restore registers - 5b/pop-to-EBX - 58/pop-to-EAX - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -== data - -# various constants used here were found in the Linux sources (search for file mman-common.h) -_mmap-new-segment: # type mmap_arg_struct - # addr - 0/imm32 -_mmap-new-segment:len: - # len - 0/imm32 - # protection flags - 3/imm32 # PROT_READ | PROT_WRITE - # sharing flags - 0x22/imm32 # MAP_PRIVATE | MAP_ANONYMOUS - # fd - -1/imm32 # since MAP_ANONYMOUS is specified - # offset - 0/imm32 # since MAP_ANONYMOUS is specified - -# . . vim:nowrap:textwidth=0 diff --git a/subx/054string-equal.subx b/subx/054string-equal.subx deleted file mode 100644 index 9784e2ad..00000000 --- a/subx/054string-equal.subx +++ /dev/null @@ -1,230 +0,0 @@ -# Comparing 'regular' length-prefixed strings. - -== code -# instruction effective address register displacement immediate -# . op subop mod rm32 base index scale r32 -# . 1-3 bytes 3 bits 2 bits 3 bits 3 bits 3 bits 2 bits 2 bits 0/1/2/4 bytes 0/1/2/4 bytes - -Entry: # run all tests -#? e8/call test-compare-equal-strings/disp32 - e8/call run-tests/disp32 # 'run-tests' is a function created automatically by SubX. It calls all functions that start with 'test-'. - # syscall(exit, Num-test-failures) - 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 3/r32/EBX Num-test-failures/disp32 # copy *Num-test-failures to EBX - b8/copy-to-EAX 1/imm32/exit - cd/syscall 0x80/imm8 - -string-equal?: # s : (address string), benchmark : (address string) -> EAX : boolean - # pseudocode: - # if (s->length != benchmark->length) return false - # currs = s->data - # currb = benchmark->data - # maxs = s->data + s->length - # while currs < maxs - # c1 = *currs - # c2 = *currb - # if (c1 != c2) return false - # ++currs, ++currb - # return true - # - # registers: - # currs: ESI - # maxs: ECX - # currb: EDI - # c1: EAX - # c2: EBX - # - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # . save registers - 51/push-ECX - 52/push-EDX - 56/push-ESI - 57/push-EDI - # ESI = s - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 6/r32/ESI 8/disp8 . # copy *(EBP+8) to ESI - # EDI = benchmark - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 7/r32/EDI 0xc/disp8 . # copy *(EBP+12) to EDI - # ECX = s->length - 8b/copy 0/mod/indirect 6/rm32/ESI . . . 1/r32/ECX . . # copy *ESI to ECX -$string-equal?:lengths: - # if (ECX != benchmark->length) return false - 39/compare 0/mod/indirect 7/rm32/EDI . . . 1/r32/ECX . . # compare *EDI and ECX - 75/jump-if-not-equal $string-equal?:false/disp8 - # currs/ESI = s->data - 81 0/subop/add 3/mod/direct 6/rm32/ESI . . . . . 4/imm32 # add to ESI - # maxs/ECX = s->data + s->length - 01/add 3/mod/direct 1/rm32/ECX . . . 6/r32/ESI . . # add ESI to ECX - # currb/EDI = benchmark->data - 81 0/subop/add 3/mod/direct 7/rm32/EDI . . . . . 4/imm32 # add to EDI - # c1/EAX = c2/EDX = 0 - 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX - 31/xor 3/mod/direct 2/rm32/EDX . . . 2/r32/EDX . . # clear EDX -$string-equal?:loop: - # if (currs >= maxs) return true - 39/compare 3/mod/direct 6/rm32/ESI . . . 1/r32/ECX . . # compare ESI with ECX - 73/jump-if-greater-or-equal-unsigned $string-equal?:true/disp8 - # c1 = *currs - 8a/copy-byte 0/mod/indirect 6/rm32/ESI . . . 0/r32/AL . . # copy byte at *ESI to AL - # c2 = *currb - 8a/copy-byte 0/mod/indirect 7/rm32/EDI . . . 2/r32/DL . . # copy byte at *EDI to DL - # if (c1 != c2) return false - 39/compare 3/mod/direct 0/rm32/EAX . . . 2/r32/EDX . . # compare EAX and EDX - 75/jump-if-not-equal $string-equal?:false/disp8 - # ++currs - 46/increment-ESI - # ++currb - 47/increment-EDI - eb/jump $string-equal?:loop/disp8 -$string-equal?:true: - b8/copy-to-EAX 1/imm32 - eb/jump $string-equal?:end/disp8 -$string-equal?:false: - b8/copy-to-EAX 0/imm32 -$string-equal?:end: - # . restore registers - 5f/pop-to-EDI - 5e/pop-to-ESI - 5a/pop-to-EDX - 59/pop-to-ECX - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -# - tests - -test-compare-empty-with-empty-string: - # EAX = string-equal?("", "") - # . . push args - 68/push ""/imm32 - 68/push ""/imm32 - # . . call - e8/call string-equal?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check-ints-equal(EAX, 1, msg) - # . . push args - 68/push "F - test-compare-empty-with-empty-string"/imm32 - 68/push 1/imm32/true - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - c3/return - -test-compare-empty-with-non-empty-string: # also checks length-mismatch code path - # EAX = string-equal?("", "Abc") - # . . push args - 68/push "Abc"/imm32 - 68/push ""/imm32 - # . . call - e8/call string-equal?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check-ints-equal(EAX, 0, msg) - # . . push args - 68/push "F - test-compare-empty-with-non-empty-string"/imm32 - 68/push 0/imm32/false - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - c3/return - -test-compare-equal-strings: - # EAX = string-equal?("Abc", "Abc") - # . . push args - 68/push "Abc"/imm32 - 68/push "Abc"/imm32 - # . . call - e8/call string-equal?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check-ints-equal(EAX, 1, msg) - # . . push args - 68/push "F - test-compare-equal-strings"/imm32 - 68/push 1/imm32/true - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - c3/return - -test-compare-inequal-strings-equal-lengths: - # EAX = string-equal?("Abc", "Adc") - # . . push args - 68/push "Adc"/imm32 - 68/push "Abc"/imm32 - # . . call - e8/call string-equal?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check-ints-equal(EAX, 0, msg) - # . . push args - 68/push "F - test-compare-inequal-strings-equal-lengths"/imm32 - 68/push 0/imm32/false - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - c3/return - -# helper for later tests -check-string-equal: # s : (address string), expected : (address string), msg : (address string) - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # . save registers - 50/push-EAX - # EAX = string-equal?(s, expected) - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - # . . call - e8/call string-equal?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check-ints-equal(EAX, 1, msg) - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x10/disp8 . # push *(EBP+16) - 68/push 1/imm32 - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -$check-string-equal:end: - # . restore registers - 58/pop-to-EAX - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -# test the helper -test-check-string-equal: - # check-string-equal?("Abc", "Abc") - # . . push args - 68/push "Abc"/imm32 - 68/push "Abc"/imm32 - # . . call - e8/call check-string-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check-ints-equal(EAX, 1, msg) - # . . push args - 68/push "F - test-check-string-equal"/imm32 - 68/push 0/imm32/false - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - c3/return - -# . . vim:nowrap:textwidth=0 diff --git a/subx/055stream.subx b/subx/055stream.subx deleted file mode 100644 index f2969861..00000000 --- a/subx/055stream.subx +++ /dev/null @@ -1,73 +0,0 @@ -# streams: data structure for operating on arrays in a stateful manner -# -# A stream looks like this: -# write : int # index at which writes go -# read : int # index that we've read until -# data : (array byte) # prefixed by length as usual -# -# some primitives for operating on streams: -# - clear-stream (clears everything but the data length) -# - rewind-stream (resets read pointer) - -== code -# instruction effective address register displacement immediate -# . op subop mod rm32 base index scale r32 -# . 1-3 bytes 3 bits 2 bits 3 bits 3 bits 3 bits 2 bits 2 bits 0/1/2/4 bytes 0/1/2/4 bytes - -clear-stream: # f : (address stream) -> <void> - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # . save registers - 50/push-EAX - 51/push-ECX - # EAX = f - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . 0/r32/EAX 8/disp8 . # copy *(EBP+8) to EAX - # ECX = f->length - 8b/copy 1/mod/*+disp8 0/rm32/EAX . . . 1/r32/ECX 8/disp8 . # copy *(EAX+8) to ECX - # ECX = &f->data[f->length] - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 0xc/disp8 . # copy EAX+ECX+12 to ECX - # f->write = 0 - c7 0/subop/copy 0/mod/direct 0/rm32/EAX . . . . . 0/imm32 # copy to *EAX - # f->read = 0 - c7 0/subop/copy 1/mod/*+disp8 0/rm32/EAX . . . . 4/disp8 0/imm32 # copy to *(EAX+4) - # EAX = f->data - 81 0/subop/add 3/mod/direct 0/rm32/EAX . . . . . 0xc/imm32 # add to EAX - # while (true) -$clear-stream:loop: - # if (EAX >= ECX) break - 39/compare 3/mod/direct 0/rm32/EAX . . . 1/r32/ECX . . # compare EAX with ECX - 73/jump-if-greater-or-equal-unsigned $clear-stream:end/disp8 - # *EAX = 0 - c6 0/subop/copy 0/mod/direct 0/rm32/EAX . . . . . 0/imm8 # copy byte to *EAX - # ++EAX - 40/increment-EAX - eb/jump $clear-stream:loop/disp8 -$clear-stream:end: - # . restore registers - 59/pop-to-ECX - 58/pop-to-EAX - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -rewind-stream: # f : (address stream) -> <void> - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # . save registers - 50/push-EAX - # EAX = f - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . 0/r32/EAX 8/disp8 . # copy *(EBP+8) to EAX - # f->read = 0 - c7 0/subop/copy 1/mod/*+disp8 0/rm32/EAX . . . . 4/disp8 0/imm32 # copy to *(EAX+4) -$rewind-stream:end: - # . restore registers - 58/pop-to-EAX - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -# . . vim:nowrap:textwidth=0 diff --git a/subx/056trace.subx b/subx/056trace.subx deleted file mode 100644 index c1105b15..00000000 --- a/subx/056trace.subx +++ /dev/null @@ -1,1009 +0,0 @@ -# primitives for emitting traces to a 'trace' stream, and for tests to make assertions on its contents -# -# A trace stream looks like a regular stream: -# write : int # index at which writes go -# read : int # index that we've read until -# data : (array byte) # prefixed by length as usual -# Usually the trace stream will be in a separate segment set aside for the purpose. -# -# primitives for operating on traces (arguments in quotes): -# - initialize-trace-stream: populates Trace-stream with a new segment of the given 'size' -# - trace: adds a 'line' to Trace-stream -# - check-trace-contains: scans from Trace-stream's start for a matching 'line', prints a 'message' to stderr on failure -# - check-trace-scans-to: scans from Trace-stream's read pointer for a matching 'line', prints a 'message' to stderr on failure - -== data - -# We'll save the address of the trace segment here. -Trace-stream: - 0/imm32 - -Trace-segment: - 0/imm32/curr - 0/imm32/limit - -# Fake trace-stream for tests. -# Also illustrates the layout of the real trace-stream (segment). -_test-trace-stream: - # current write index - 0/imm32 - # current read index - 0/imm32 - # length - 8/imm32 - # data - 00 00 00 00 00 00 00 00 # 8 bytes - -== code -# instruction effective address register displacement immediate -# . op subop mod rm32 base index scale r32 -# . 1-3 bytes 3 bits 2 bits 3 bits 3 bits 3 bits 2 bits 2 bits 0/1/2/4 bytes 0/1/2/4 bytes - -# Allocate a new segment for the trace stream, initialize its length, and save its address to Trace-stream. -# The Trace-stream segment will consist of variable-length lines separated by newlines (0x0a) -initialize-trace-stream: # n : int -> <void> - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # . save registers - 50/push-EAX - 51/push-ECX - # ECX = n - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 1/r32/ECX 8/disp8 . # copy *(EBP+8) to ECX - # Trace-segment = new-segment(n) - # . . push args - 68/push Trace-segment/imm32 - 51/push-ECX - # . . call - e8/call new-segment/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # copy Trace-segment->curr to *Trace-stream - 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 0/r32/EAX Trace-segment/disp32 # copy *Trace-segment to EAX - # watch point to catch Trace-stream leaks -#? $watch-1: - 89/copy 0/mod/indirect 5/rm32/.disp32 . . 0/r32/EAX Trace-stream/disp32 # copy EAX to *Trace-stream - # Trace-stream->length = n - 12 - # . ECX -= 12 - 81 5/subop/subtract 3/mod/direct 1/rm32/ECX . . . . . 0xc/imm32 # subtract from ECX - # . Trace-stream->length = ECX - 89/copy 1/mod/*+disp8 0/rm32/EAX . . . 1/r32/ECX 8/disp8 . # copy ECX to *(EAX+8) -$initialize-trace-stream:end: - # . restore registers - 59/pop-to-ECX - 58/pop-to-EAX - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -# Append a string to the given trace stream. -# Silently give up if it's already full. Or truncate the string if there isn't enough room. -trace: # line : (address string) - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # . save registers - 50/push-EAX - 51/push-ECX - 52/push-EDX - 53/push-EBX - 56/push-ESI - 57/push-EDI - # EDI = *Trace-stream - 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 7/r32/EDI Trace-stream/disp32 # copy *Trace-stream to EDI - # ESI = line - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . 6/r32/ESI 8/disp8 . # copy *(EBP+8) to ESI - # ECX = t->write - 8b/copy 0/mod/indirect 7/rm32/EDI . . . 1/r32/ECX . . # copy *EDI to ECX - # EDX = t->length - 8b/copy 1/mod/*+disp8 7/rm32/EDI . . . 2/r32/EDX 8/disp8 . # copy *(EDI+8) to EDX - # EAX = _append-3(&t->data[t->write], &t->data[t->length], line) - # . . push line - 56/push-ESI - # . . push &t->data[t->length] - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 7/base/EDI 2/index/EDX . 3/r32/EBX 0xc/disp8 . # copy EDI+EDX+12 to EBX - 53/push-EBX - # . . push &t->data[t->write] - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 7/base/EDI 1/index/ECX . 3/r32/EBX 0xc/disp8 . # copy EDI+ECX+12 to EBX - 53/push-EBX - # . . call - e8/call _append-3/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # if (EAX == 0) return - 3d/compare-EAX-and 0/imm32 - 74/jump-if-equal $trace:end/disp8 - # t->write += EAX - 01/add 0/mod/indirect 7/rm32/EDI . . . 0/r32/EAX . . # add EAX to *EDI - # refresh ECX = t->write - 8b/copy 0/mod/indirect 7/rm32/EDI . . . 1/r32/ECX . . # copy *EDI to ECX - # EAX = _append-3(&t->data[t->write], &t->data[t->length], line) - # . . push line - 68/push Newline/imm32 - # . . push &t->data[t->length] - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 7/base/EDI 2/index/EDX . 3/r32/EBX 0xc/disp8 . # copy EDI+EDX+12 to EBX - 53/push-EBX - # . . push &t->data[t->write] - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 7/base/EDI 1/index/ECX . 3/r32/EBX 0xc/disp8 . # copy EDI+ECX+12 to EBX - 53/push-EBX - # . . call - e8/call _append-3/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # t->write += EAX - 01/add 0/mod/indirect 7/rm32/EDI . . . 0/r32/EAX . . # add EAX to *EDI -$trace:end: - # . restore registers - 5f/pop-to-EDI - 5e/pop-to-ESI - 5b/pop-to-EBX - 5a/pop-to-EDX - 59/pop-to-ECX - 58/pop-to-EAX - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-trace-single: - # push *Trace-stream - ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Trace-stream/disp32 # push *Trace-stream - # *Trace-stream = _test-trace-stream - b8/copy-to-EAX _test-trace-stream/imm32 - 89/copy 0/mod/indirect 5/rm32/.disp32 . . 0/r32/EAX Trace-stream/disp32 # copy EAX to *Trace-stream - # clear-trace-stream() - e8/call clear-trace-stream/disp32 - # trace("Ab") - # . . push args - 68/push "Ab"/imm32 - # . . call - e8/call trace/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # check-ints-equal(*_test-trace-stream->data, 41/A 62/b 0a/newline 00, msg) - # . . push args - 68/push "F - test-trace-single"/imm32 - 68/push 0x0a6241/imm32/Ab-newline - # . . push *_test-trace-stream->data - b8/copy-to-EAX _test-trace-stream/imm32 - ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 0xc/disp8 . # push *(EAX+12) - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # pop into *Trace-stream - 8f 0/subop/pop 0/mod/indirect 5/rm32/.disp32 . . . Trace-stream/disp32 # pop into *Trace-stream - # end - c3/return - -test-trace-appends: - # push *Trace-stream - ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Trace-stream/disp32 # push *Trace-stream - # *Trace-stream = _test-trace-stream - b8/copy-to-EAX _test-trace-stream/imm32 - 89/copy 0/mod/indirect 5/rm32/.disp32 . . 0/r32/EAX Trace-stream/disp32 # copy EAX to *Trace-stream - # clear-trace-stream() - e8/call clear-trace-stream/disp32 - # trace("C") - # . . push args - 68/push "C"/imm32 - # . . call - e8/call trace/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # trace("D") - # . . push args - 68/push "D"/imm32 - # . . call - e8/call trace/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # check-ints-equal(*_test-trace-stream->data, 43/C 0a/newline 44/D 0a/newline, msg) - # . . push args - 68/push "F - test-trace-appends"/imm32 - 68/push 0x0a440a43/imm32/C-newline-D-newline - # . . push *_test-trace-stream->data - b8/copy-to-EAX _test-trace-stream/imm32 - ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 0xc/disp8 . # push *(EAX+12) - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # pop into *Trace-stream - 8f 0/subop/pop 0/mod/indirect 5/rm32/.disp32 . . . Trace-stream/disp32 # pop into *Trace-stream - # end - c3/return - -test-trace-empty-line: - # push *Trace-stream - ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Trace-stream/disp32 # push *Trace-stream - # *Trace-stream = _test-trace-stream - b8/copy-to-EAX _test-trace-stream/imm32 - 89/copy 0/mod/indirect 5/rm32/.disp32 . . 0/r32/EAX Trace-stream/disp32 # copy EAX to *Trace-stream - # clear-trace-stream() - e8/call clear-trace-stream/disp32 - # trace("") - # . . push args - 68/push ""/imm32 - # . . call - e8/call trace/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # check-ints-equal(*_test-trace-stream->data, 0, msg) - # . . push args - 68/push "F - test-trace-empty-line"/imm32 - 68/push 0/imm32 - # . . push *_test-trace-stream->data - b8/copy-to-EAX _test-trace-stream/imm32 - ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 0xc/disp8 . # push *(EAX+12) - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # pop into *Trace-stream - 8f 0/subop/pop 0/mod/indirect 5/rm32/.disp32 . . . Trace-stream/disp32 # pop into *Trace-stream - # end - c3/return - -check-trace-contains: # line : (address string), msg : (address string) - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # rewind-stream(*Trace-stream) - # . . push args - ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Trace-stream/disp32 # push *Trace-stream - # . . call - e8/call rewind-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # check-trace-scans-to(line, msg) - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - # . . call - e8/call check-trace-scans-to/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -$check-trace-contains:end: - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -check-trace-scans-to: # line : (address string), msg : (address string) - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # . save registers - 50/push-EAX - # EAX = trace-scan(line) - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - # . . call - e8/call trace-scan/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # check-ints-equal(EAX, 1, msg) - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - 68/push 1/imm32 - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -$check-trace-scans-to:end: - # . restore registers - 58/pop-to-EAX - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -# Start scanning from Trace-stream->read for 'line'. If found, update Trace-stream->read and return true. -trace-scan: # line : (address string) -> result/EAX : boolean - # pseudocode: - # push Trace-stream->read - # while true: - # if Trace-stream->read >= Trace-stream->write - # break - # if next-line-matches?(Trace-stream, line) - # skip-next-line(Trace-stream) - # dump saved copy of Trace-stream->read - # return true - # skip-next-line(Trace-stream) - # pop saved copy of Trace-stream->read - # return false - # - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # . save registers - 51/push-ECX - 56/push-ESI - # ESI = *Trace-stream - 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 6/r32/ESI Trace-stream/disp32 # copy *Trace-stream to ESI - # ECX = Trace-stream->write - 8b/copy 0/mod/indirect 6/rm32/ESI . . . 1/r32/ECX . # copy *ESI to ECX - # push Trace-stream->read - ff 6/subop/push 1/mod/*+disp8 6/rm32/ESI . . . . 4/disp8 . # push *(ESI+4) -$trace-scan:loop: - # if (Trace-stream->read >= Trace-stream->write) return false - 39/compare 1/mod/*+disp8 6/rm32/ESI . . . 1/r32/ECX 4/disp8 . # compare ECX with *(ESI+4) - 7d/jump-if-greater-or-equal $trace-scan:false/disp8 - # EAX = next-line-matches?(Trace-stream, line) - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - 56/push-ESI - # . . call - e8/call next-line-matches?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # if (EAX == 0) continue - 3d/compare-EAX-and 0/imm32 - 74/jump-if-equal $trace-scan:continue/disp8 -$trace-scan:true: - # skip-next-line(Trace-stream) - # . . push args - 56/push-ESI - # . . call - e8/call skip-next-line/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # dump saved copy of Trace-stream->read - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # return true - b8/copy-to-EAX 1/imm32/true - eb/jump $trace-scan:end/disp8 -$trace-scan:continue: - # skip-next-line(Trace-stream) - # . . push args - 56/push-ESI - # . . call - e8/call skip-next-line/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - eb/jump $trace-scan:loop/disp8 -$trace-scan:false: - # restore saved copy of Trace-stream->read - 8f 0/subop/pop 1/mod/*+disp8 6/rm32/ESI . . . . 4/disp8 . # pop to *(ESI+4) - # return false - b8/copy-to-EAX 0/imm32/false -$trace-scan:end: - # . restore registers - 59/pop-to-ECX - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-trace-scan-first: - # push *Trace-stream - ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Trace-stream/disp32 # push *Trace-stream - # setup - # . *Trace-stream = _test-trace-stream - b8/copy-to-EAX _test-trace-stream/imm32 - 89/copy 0/mod/indirect 5/rm32/.disp32 . . 0/r32/EAX Trace-stream/disp32 # copy EAX to *Trace-stream - # . clear-trace-stream() - e8/call clear-trace-stream/disp32 - # . trace("Ab") - # . . push args - 68/push "Ab"/imm32 - # . . call - e8/call trace/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # EAX = trace-scan("Ab") - # . . push args - 68/push "Ab"/imm32 - # . . call - e8/call trace-scan/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # check-ints-equal(EAX, 1, msg) - # . . push args - 68/push "F - test-trace-scan-first"/imm32 - 68/push 1/imm32 - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # pop into *Trace-stream - 8f 0/subop/pop 0/mod/indirect 5/rm32/.disp32 . . . Trace-stream/disp32 # pop into *Trace-stream - # . end - c3/return - -test-trace-scan-skips-lines-until-found: - # push *Trace-stream - ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Trace-stream/disp32 # push *Trace-stream - # setup - # . *Trace-stream = _test-trace-stream - b8/copy-to-EAX _test-trace-stream/imm32 - 89/copy 0/mod/indirect 5/rm32/.disp32 . . 0/r32/EAX Trace-stream/disp32 # copy EAX to *Trace-stream - # . clear-trace-stream() - e8/call clear-trace-stream/disp32 - # . trace("Ab") - # . . push args - 68/push "Ab"/imm32 - # . . call - e8/call trace/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . trace("cd") - # . . push args - 68/push "cd"/imm32 - # . . call - e8/call trace/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # EAX = trace-scan("cd") - # . . push args - 68/push "cd"/imm32 - # . . call - e8/call trace-scan/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # check-ints-equal(EAX, 1, msg) - # . . push args - 68/push "F - test-trace-scan-skips-lines-until-found"/imm32 - 68/push 1/imm32 - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # pop into *Trace-stream - 8f 0/subop/pop 0/mod/indirect 5/rm32/.disp32 . . . Trace-stream/disp32 # pop into *Trace-stream - # . end - c3/return - -test-trace-second-scan-starts-where-first-left-off: - # push *Trace-stream - ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Trace-stream/disp32 # push *Trace-stream - # setup - # . *Trace-stream = _test-trace-stream - b8/copy-to-EAX _test-trace-stream/imm32 - 89/copy 0/mod/indirect 5/rm32/.disp32 . . 0/r32/EAX Trace-stream/disp32 # copy EAX to *Trace-stream - # . clear-trace-stream() - e8/call clear-trace-stream/disp32 - # . trace("Ab") - # . . push args - 68/push "Ab"/imm32 - # . . call - e8/call trace/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . EAX = trace-scan("Ab") - # . . push args - 68/push "Ab"/imm32 - # . . call - e8/call trace-scan/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # second scan fails - # . EAX = trace-scan("Ab") - # . . push args - 68/push "Ab"/imm32 - # . . call - e8/call trace-scan/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # check-ints-equal(EAX, 0, msg) - # . . push args - 68/push "F - test-trace-second-scan-starts-where-first-left-off"/imm32 - 68/push 0/imm32 - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # pop into *Trace-stream - 8f 0/subop/pop 0/mod/indirect 5/rm32/.disp32 . . . Trace-stream/disp32 # pop into *Trace-stream - # . end - c3/return - -test-trace-scan-failure-leaves-read-index-untouched: - # push *Trace-stream - ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Trace-stream/disp32 # push *Trace-stream - # setup - # . *Trace-stream = _test-trace-stream - b8/copy-to-EAX _test-trace-stream/imm32 - 89/copy 0/mod/indirect 5/rm32/.disp32 . . 0/r32/EAX Trace-stream/disp32 # copy EAX to *Trace-stream - # . clear-trace-stream() - e8/call clear-trace-stream/disp32 - # . trace("Ab") - # . . push args - 68/push "Ab"/imm32 - # . . call - e8/call trace/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . check-ints-equal(_test-trace-stream->read, 0, msg) - # . . push args - 68/push "F - test-trace-second-scan-starts-where-first-left-off/precondition-failure"/imm32 - 68/push 0/imm32 - b8/copy-to-EAX _test-trace-stream/imm32 - ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 4/disp8 . # push *(EAX+4) - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # perform a failing scan - # . EAX = trace-scan("Ax") - # . . push args - 68/push "Ax"/imm32 - # . . call - e8/call trace-scan/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # no change in read index - # . check-ints-equal(_test-trace-stream->read, 0, msg) - # . . push args - 68/push "F - test-trace-second-scan-starts-where-first-left-off"/imm32 - 68/push 0/imm32 - b8/copy-to-EAX _test-trace-stream/imm32 - ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 4/disp8 . # push *(EAX+4) - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # pop into *Trace-stream - 8f 0/subop/pop 0/mod/indirect 5/rm32/.disp32 . . . Trace-stream/disp32 # pop into *Trace-stream - # . end - c3/return - -next-line-matches?: # t : (address stream), line : (address string) -> result/EAX : boolean - # pseudocode: - # while true: - # if (currl >= maxl) break - # if (currt >= maxt) return false - # if (*currt != *currl) return false - # ++currt - # ++currl - # return *currt == '\n' - # - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # . save registers - 51/push-ECX - 52/push-EDX - 53/push-EBX - 56/push-ESI - 57/push-EDI - # EDX = line - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . 2/r32/EDX 0xc/disp8 . # copy *(EBP+12) to EDX - # currl/ESI = line->data - # . ESI = line/EDX->data - 8d/copy-address 1/mod/*+disp8 2/rm32/EDX . . . 6/r32/ESI 4/disp8 . # copy EDX+4 to ESI - # maxl/ECX = line->data + line->size - # . EAX = line/EDX->size - 8b/copy 0/mod/indirect 2/rm32/EDX . . 0/r32/EAX . . # copy *EDX to EAX - # . maxl/ECX = line->data/ESI + line->size/EAX - 8d/copy-address 0/mod/indirect 4/rm32/sib 6/base/ESI 0/index/EAX . 1/r32/ECX . . # copy EDX+EAX to ECX - # EDI = t - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . 7/r32/EDI 8/disp8 . # copy *(EBP+8) to EDI - # EBX = t->data - 8d/copy-address 1/mod/*+disp8 7/rm32/EDI . . . 3/r32/EBX 0xc/disp8 . # copy EDI+12 to EBX - # maxt/EDX = t->data + t->write - # . EAX = t->write - 8b/copy 0/mod/indirect 7/rm32/EDI . . 0/r32/EAX . . # copy *EDI to EAX - # . maxt/EDX = t->data/EBX + t->write/EAX - 8d/copy-address 0/mod/indirect 4/rm32/sib 3/base/EBX 0/index/EAX . 2/r32/EDX . . # copy EBX+EAX to EDX - # currt/EDI = t->data + t->read - # . EAX = t/EDI->read - 8b/copy 1/mod/*+disp8 7/rm32/EDI . . 0/r32/EAX 4/disp8 . # copy *(EDI+4) to EAX - # . currt/EDI = t->data/EBX + t->read/EAX - 8d/copy-address 0/mod/indirect 4/rm32/sib 3/base/EBX 0/index/EAX . 7/r32/EDI . . # copy EBX+EAX to EDI -$next-line-matches?:loop: - # if (currl/ESI >= maxl/ECX) break - 39/compare 3/mod/direct 6/rm32/ESI . . . 1/r32/ECX . . # compare ESI and ECX - 73/jump-if-greater-or-equal-unsigned $next-line-matches?:break/disp8 - # if (currt/EDI >= maxt/EDX) return false - # . EAX = false - b8/copy-to-EAX 0/imm32/false - 39/compare 3/mod/direct 7/rm32/EDI . . . 2/r32/EDX . . # compare EDI and EDX - 73/jump-if-greater-or-equal-unsigned $next-line-matches?:end/disp8 - # if (*currt/EDI != *currl/ESI) return false - 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX - 31/xor 3/mod/direct 3/rm32/EAX . . . 3/r32/EAX . . # clear EBX - # . EAX = (char) *currt/EDI - 8a/copy-byte 0/mod/indirect 7/rm32/EDI . . 0/r32/EAX . . # copy *EDI to EAX - # . EBX = (char) *currl/ESI - 8a/copy-byte 0/mod/indirect 6/rm32/ESI . . 3/r32/EBX . . # copy *ESI to EBX - # . EAX >= EBX - 39/compare 3/mod/direct 0/rm32/EAX . . . 3/r32/EBX . . # compare EAX and EBX - # . EAX = false - b8/copy-to-EAX 0/imm32/false - 75/jump-if-not-equal $next-line-matches?:end/disp8 - # ++currt/EDI - 47/increment-EDI - # ++currl/ESI - 46/increment-ESI - eb/jump $next-line-matches?:loop/disp8 -$next-line-matches?:break: - # return *currt == '\n' - 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX - # . EAX = (char) *currt - 8a/copy-byte 0/mod/indirect 7/rm32/EDI . . 0/r32/EAX . . # copy *EDI to EAX - 3d/compare-EAX-and 0xa/imm32/newline - # . EAX = false - b8/copy-to-EAX 1/imm32/true - 74/jump-if-equal $next-line-matches?:end/disp8 - b8/copy-to-EAX 0/imm32/true -$next-line-matches?:end: - # . restore registers - 5f/pop-to-EDI - 5e/pop-to-ESI - 5b/pop-to-EBX - 5a/pop-to-EDX - 59/pop-to-ECX - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-next-line-matches?-no-match-1: - # next line of "ABABA" does not match "blah blah" - # . EAX = next-line-matches?(_test-stream-line-ABABA, "blah blah") - # . . push args - 68/push "blah blah"/imm32 - 68/push _test-stream-line-ABABA/imm32 - # . . call - e8/call next-line-matches?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . check-ints-equal(EAX, 0, msg) - # . . push args - 68/push "F - test-next-line-matches?-no-match-1"/imm32 - 68/push 0/imm32 - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - c3/return - -test-next-line-matches?-no-match-2: - # next line of "ABABA" does not match "" - # . EAX = next-line-matches?(_test-stream-line-ABABA, "") - # . . push args - 68/push ""/imm32 - 68/push _test-stream-line-ABABA/imm32 - # . . call - e8/call next-line-matches?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . check-ints-equal(EAX, 0, msg) - # . . push args - 68/push "F - test-next-line-matches?-no-match-2"/imm32 - 68/push 0/imm32 - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - c3/return - -test-next-line-matches?-no-match-3: - # next line of "ABABA" does not match "AA" - # . EAX = next-line-matches?(_test-stream-line-ABABA, "AA") - # . . push args - 68/push "AA"/imm32 - 68/push _test-stream-line-ABABA/imm32 - # . . call - e8/call next-line-matches?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . check-ints-equal(EAX, 0, msg) - # . . push args - 68/push "F - test-next-line-matches?-no-match-3"/imm32 - 68/push 0/imm32 - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - c3/return - -test-next-line-matches?-match: - # next line of "ABABA" matches "ABABA" - # . EAX = next-line-matches?(_test-stream-line-ABABA, "ABABA") - # . . push args - 68/push "ABABA"/imm32 - 68/push _test-stream-line-ABABA/imm32 - # . . call - e8/call next-line-matches?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . check-ints-equal(EAX, 1, msg) - # . . push args - 68/push "F - test-next-line-matches?-match"/imm32 - 68/push 1/imm32 - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - c3/return - -# move t->read to _after_ next newline -skip-next-line: # t : (address stream) - # pseudocode: - # max = t->data + t->write - # i = t->read - # curr = t->data + t->read - # while true - # if (curr >= max) break - # ++i - # if (*curr == '\n') break - # ++curr - # t->read = i - # - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # . save registers - 50/push-EAX - 51/push-ECX - 52/push-EDX - 53/push-EBX - # ECX = t - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . 1/r32/ECX 8/disp8 . # copy *(EBP+8) to ECX - # EDX = t/ECX->data - 8d/copy-address 1/mod/*+disp8 1/rm32/ECX . . . 2/r32/EDX 0xc/disp8 . # copy ECX+12 to EDX - # EAX = t/ECX->write - 8b/copy 0/mod/indirect 1/rm32/ECX . . . 0/r32/EAX . . # copy *ECX to EAX - # max/EBX = t->data/EDX + t->write/EAX - 8d/copy-address 0/mod/indirect 4/rm32/sib 2/base/EDX 0/index/EAX . 3/r32/EBX . . # copy EDX+EAX to EBX - # EAX = t/ECX->read - 8b/copy 1/mod/*+disp8 1/rm32/ECX . . . 0/r32/EAX 4/disp8 . # copy *(ECX+4) to EDX - # curr/ECX = t->data/EDX + t->read/EAX - 8d/copy-address 0/mod/indirect 4/rm32/sib 2/base/EDX 0/index/EAX . 1/r32/ECX . . # copy EDX+EAX to ECX - # i/EDX = EAX - 8b/copy 3/mod/direct 0/rm32/EAX . . . 2/r32/EDX . . # copy EAX to EDX -$skip-next-line:loop: - # if (curr/ECX >= max/EBX) break - 39/compare 3/mod/direct 1/rm32/ECX . . . 3/r32/EBX . . # compare ECX and EBX - 73/jump-if-greater-or-equal-unsigned $skip-next-line:end/disp8 - # ++i/EDX - 42/increment-EDX - # if (*curr/ECX == '\n') break - 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX - 8a/copy-byte 0/mod/indirect 1/rm32/ECX . . . 0/r32/EAX . . # copy *ECX to EAX - 3d/compare-EAX-and 0a/imm32/newline - 74/jump-if-equal $skip-next-line:end/disp8 - # ++curr/ECX - 41/increment-ECX - # loop - eb/jump $skip-next-line:loop/disp8 -$skip-next-line:end: - # ECX = t - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . 1/r32/ECX 8/disp8 . # copy *(EBP+8) to ECX - # t/ECX->read = i/EDX - 89/copy 1/mod/*+disp8 1/rm32/ECX . . . 2/r32/EDX 4/disp8 . # copy EDX to *(ECX+4) - # . restore registers - 5b/pop-to-EBX - 5a/pop-to-EDX - 59/pop-to-ECX - 58/pop-to-EAX - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-skip-next-line-empty: - # skipping next line in empty stream leaves read pointer at 0 - # . skip-next-line(_test-stream-empty) - # . . push args - 68/push _test-stream-empty/imm32 - # . . call - e8/call skip-next-line/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . check-ints-equal(_test-stream-empty->read, 0, msg) - # . . push args - 68/push "F - test-skip-next-line-empty"/imm32 - 68/push 0/imm32 - b8/copy-to-EAX _test-stream-empty/imm32 - 8b/copy 1/mod/*+disp8 0/rm32/EAX . . . 0/r32/EAX 4/disp8 . # copy *(EAX+4) to EAX - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - c3/return - -test-skip-next-line-filled: - # skipping next line increments read pointer by length of line + 1 (for newline) - # . skip-next-line(_test-stream-filled) - # . . push args - 68/push _test-stream-filled/imm32 - # . . call - e8/call skip-next-line/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . check-ints-equal(_test-stream-filled->read, 5, msg) - # . . push args - 68/push "F - test-skip-next-line-filled"/imm32 - 68/push 5/imm32 - b8/copy-to-EAX _test-stream-filled/imm32 - 8b/copy 1/mod/*+disp8 0/rm32/EAX . . . 0/r32/EAX 4/disp8 . # copy *(EAX+4) to EAX - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - c3/return - -clear-trace-stream: - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # . save registers - 50/push-EAX - 51/push-ECX - # EAX = *Trace-stream - 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 0/r32/EAX Trace-stream/disp32 # copy *Trace-stream to EAX - # ECX = t->length - 8b/copy 1/mod/*+disp8 0/rm32/EAX . . . 1/r32/ECX 8/disp8 . # copy *(EAX+8) to ECX - # ECX = &t->data[t->length] - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 0xc/disp8 . # copy EAX+ECX+12 to ECX - # t->write = 0 - c7 0/subop/copy 0/mod/direct 0/rm32/EAX . . . . . 0/imm32 # copy to *EAX - # t->read = 0 - c7 0/subop/copy 1/mod/*+disp8 0/rm32/EAX . . . . 4/disp8 0/imm32 # copy to *(EAX+4) - # EAX = t->data - 81 0/subop/add 3/mod/direct 0/rm32/EAX . . . . . 0xc/imm32 # add to EAX -$clear-trace-stream:loop: - # if (EAX >= ECX) break - 39/compare 3/mod/direct 0/rm32/EAX . . . 1/r32/ECX . . # compare EAX with ECX - 73/jump-if-greater-or-equal-unsigned $clear-trace-stream:end/disp8 - # *EAX = 0 - c7 0/subop/copy 0/mod/direct 0/rm32/EAX . . . . . 0/imm32 # copy to *EAX - # EAX += 4 - 81 0/subop/add 3/mod/direct 0/rm32/EAX . . . . . 4/imm32 # add to EAX - eb/jump $clear-trace-stream:loop/disp8 -$clear-trace-stream:end: - # . restore registers - 59/pop-to-ECX - 58/pop-to-EAX - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -# - helpers - -# 3-argument variant of _append -_append-3: # out : address, outend : address, s : (array byte) -> num_bytes_appended/EAX - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # . save registers - 51/push-ECX - # EAX = _append-4(out, outend, &s->data[0], &s->data[s->length]) - # . . push &s->data[s->length] - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . 0/r32/EAX 0x10/disp8 . # copy *(EBP+16) to EAX - 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX - 51/push-ECX - # . . push &s->data[0] - 8d/copy-address 1/mod/*+disp8 0/rm32/EAX . . . 1/r32/ECX 4/disp8 . # copy EAX+4 to ECX - 51/push-ECX - # . . push outend - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - # . . push out - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - # . . call - e8/call _append-4/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP -$_append-3:end: - # . restore registers - 59/pop-to-ECX - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -# 4-argument variant of _append -_append-4: # out : address, outend : address, in : address, inend : address -> num_bytes_appended/EAX - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # . save registers - 51/push-ECX - 52/push-EDX - 53/push-EBX - 56/push-ESI - 57/push-EDI - # EAX/num_bytes_appended = 0 - b8/copy-to-EAX 0/imm32 - # EDI = out - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 7/r32/EDI 8/disp8 . # copy *(EBP+8) to EDI - # EDX = outend - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 2/r32/EDX 0xc/disp8 . # copy *(EBP+12) to EDX - # ESI = in - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 6/r32/ESI 0x10/disp8 . # copy *(EBP+16) to ESI - # ECX = inend - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 1/r32/ECX 0x14/disp8 . # copy *(EBP+20) to ECX -$_append-4:loop: - # if (in >= inend) break - 39/compare 3/mod/direct 6/rm32/ESI . . . 1/r32/ECX . . # compare ESI with ECX - 73/jump-if-greater-or-equal-unsigned $_append-4:end/disp8 - # if (out >= outend) abort # just to catch test failures fast - 39/compare 3/mod/direct 7/rm32/EDI . . . 2/r32/EDX . . # compare EDI with EDX - 73/jump-if-greater-or-equal-unsigned $_append-4:abort/disp8 - # *out = *in - 8a/copy-byte 0/mod/indirect 6/rm32/ESI . . . 3/r32/BL . . # copy byte at *ESI to BL - 88/copy-byte 0/mod/indirect 7/rm32/EDI . . . 3/r32/BL . . # copy byte at BL to *EDI - # ++num_bytes_appended - 40/increment-EAX - # ++in - 46/increment-ESI - # ++out - 47/increment-EDI - eb/jump $_append-4:loop/disp8 -$_append-4:end: - # . restore registers - 5f/pop-to-EDI - 5e/pop-to-ESI - 5b/pop-to-EBX - 5a/pop-to-EDX - 59/pop-to-ECX - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -$_append-4:abort: - # . _write(2/stderr, error) - # . . push args - 68/push "stream overflow\n"/imm32 - 68/push 2/imm32/stderr - # . . call - e8/call _write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . syscall(exit, 1) - bb/copy-to-EBX 1/imm32 - b8/copy-to-EAX 1/imm32/exit - cd/syscall 0x80/imm8 - # never gets here - -== data - -_test-stream-line-ABABA: - # write - 8/imm32 - # read - 0/imm32 - # length - 8/imm32 - # data - 41 42 41 42 41 0a 00 00 # 8 bytes - -_test-stream-empty: - # write - 0/imm32 - # read - 0/imm32 - # length - 8/imm32 - # data - 00 00 00 00 00 00 00 00 # 8 bytes - -_test-stream-filled: - # write - 8/imm32 - # read - 0/imm32 - # length - 8/imm32 - # data - 41 41 41 41 0a 41 41 41 # 8 bytes - -# . . vim:nowrap:textwidth=0 diff --git a/subx/057write.subx b/subx/057write.subx deleted file mode 100644 index 203cbf76..00000000 --- a/subx/057write.subx +++ /dev/null @@ -1,159 +0,0 @@ -# write: like _write, but also support in-memory streams in addition to file -# descriptors. -# -# Our first dependency-injected and testable primitive. We can pass it either -# a file descriptor or an address to a stream. If a file descriptor is passed -# in, we _write to it using the right syscall. If a 'fake file descriptor' or -# stream is passed in, we append to the stream. This lets us redirect output -# in tests and check it later. -# -# We assume our data segment will never begin at an address shorter than -# 0x08000000, so any smaller arguments are assumed to be real file descriptors. -# -# A stream looks like this: -# read: int # index at which to read next -# write: int # index at which writes go -# data: (array byte) # prefixed by length as usual - -== code -# instruction effective address register displacement immediate -# . op subop mod rm32 base index scale r32 -# . 1-3 bytes 3 bits 2 bits 3 bits 3 bits 3 bits 2 bits 2 bits 0/1/2/4 bytes 0/1/2/4 bytes - -# TODO: come up with a way to signal when a write to disk fails -write: # f : fd or (address stream), s : (address array byte) -> <void> - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # if (f < 0x08000000) _write(f, s) and return # f can't be a user-mode address, so treat it as a kernel file descriptor - 81 7/subop/compare 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 0x08000000/imm32 # compare *(EBP+8) - 73/jump-if-greater-unsigned-or-equal $write:fake/disp8 - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - # . . call - e8/call _write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - eb/jump $write:end/disp8 -$write:fake: - # otherwise, treat 'f' as a stream to append to - # . save registers - 50/push-EAX - 51/push-ECX - 52/push-EDX - 53/push-EBX - # ECX = f - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . 1/r32/ECX 8/disp8 . # copy *(EBP+8) to ECX - # EDX = f->write - 8b/copy 0/mod/indirect 1/rm32/ECX . . . 2/r32/EDX . . # copy *ECX to EDX - # EBX = f->length - 8b/copy 1/mod/*+disp8 1/rm32/ECX . . . 3/r32/EBX 8/disp8 . # copy *(ECX+8) to EBX - # EAX = _append-3(&f->data[f->write], &f->data[f->length], s) - # . . push s - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - # . . push &f->data[f->length] - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 1/base/ECX 3/index/EBX . 3/r32/EBX 0xc/disp8 . # copy ECX+EBX+12 to EBX - 53/push-EBX - # . . push &f->data[f->write] - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 1/base/ECX 2/index/EDX . 3/r32/EBX 0xc/disp8 . # copy ECX+EDX+12 to EBX - 53/push-EBX - # . . call - e8/call _append-3/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # f->write += EAX - 01/add 0/mod/indirect 1/rm32/ECX . . . 0/r32/EAX . . # add EAX to *ECX - # . restore registers - 5b/pop-to-EBX - 5a/pop-to-EDX - 59/pop-to-ECX - 58/pop-to-EAX -$write:end: - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-write-single: - # clear-stream(_test-stream) - # . . push args - 68/push _test-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # write(_test-stream, "Ab") - # . . push args - 68/push "Ab"/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check-ints-equal(*_test-stream->data, 41/A 62/b 00 00, msg) - # . . push args - 68/push "F - test-write-single"/imm32 - 68/push 0x006241/imm32/Ab - # . . push *_test-stream->data - b8/copy-to-EAX _test-stream/imm32 - ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 0xc/disp8 . # push *(EAX+12) - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # end - c3/return - -test-write-appends: - # clear-stream(_test-stream) - # . . push args - 68/push _test-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # write(_test-stream, "C") - # . . push args - 68/push "C"/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # write(_test-stream, "D") - # . . push args - 68/push "D"/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check-ints-equal(*_test-stream->data, 43/C 44/D 00 00, msg) - # . . push args - 68/push "F - test-write-appends"/imm32 - 68/push 0x00004443/imm32/C-D - # . . push *_test-stream->data - b8/copy-to-EAX _test-stream/imm32 - ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 0xc/disp8 . # push *(EAX+12) - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # end - c3/return - -== data - -_test-stream: - # current write index - 0/imm32 - # current read index - 0/imm32 - # length - 0x10/imm32 - # data (2 lines x 8 bytes/line) - 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 - -# . . vim:nowrap:textwidth=0 diff --git a/subx/058stream-equal.subx b/subx/058stream-equal.subx deleted file mode 100644 index 68296212..00000000 --- a/subx/058stream-equal.subx +++ /dev/null @@ -1,593 +0,0 @@ -# some primitives for checking stream contents - -== code -# instruction effective address register displacement immediate -# . op subop mod rm32 base index scale r32 -# . 1-3 bytes 3 bits 2 bits 3 bits 3 bits 3 bits 2 bits 2 bits 0/1/2/4 bytes 0/1/2/4 bytes - -# compare all the data in a stream (ignoring the read pointer) -stream-data-equal?: # f : (address stream), s : (address string) -> EAX : boolean - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # . save registers - 51/push-ECX - 52/push-EDX - 56/push-ESI - 57/push-EDI - # ESI = f - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 6/r32/ESI 8/disp8 . # copy *(EBP+8) to ESI - # EAX = f->write - 8b/copy 0/mod/indirect 6/rm32/ESI . . . 0/r32/EAX . . # copy *ESI to EAX - # maxf/EDX = f->data + f->write - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 6/base/ESI 0/index/EAX . 2/r32/EDX 0xc/disp8 . # copy ESI+EAX+12 to EDX - # currf/ESI = f->data - 81 0/subop/add 3/mod/direct 6/rm32/ESI . . . . . 0xc/imm32 # add to ESI - # EDI = s - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 7/r32/EDI 0xc/disp8 . # copy *(EBP+12) to EDI - # if (f->write != s->length) return false -$stream-data-equal?:compare-lengths: - 39/compare 0/mod/indirect 7/rm32/EDI . . . 0/r32/EAX . . # compare *EDI and EAX - 75/jump-if-not-equal $stream-data-equal?:false/disp8 - # currs/EDI = s->data - 81 0/subop/add 3/mod/direct 7/rm32/EDI . . . . . 4/imm32 # add to EDI - # EAX = ECX = 0 - 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX - 31/xor 3/mod/direct 1/rm32/ECX . . . 1/r32/ECX . . # clear ECX -$stream-data-equal?:loop: - # if (currf >= maxf) return true - 39/compare 3/mod/direct 6/rm32/ESI . . . 2/r32/EDX . . # compare ESI with EDX - 73/jump-if-greater-or-equal-unsigned $stream-data-equal?:true/disp8 - # AL = *currs - 8a/copy-byte 0/mod/indirect 6/rm32/ESI . . . 0/r32/AL . . # copy byte at *ESI to AL - # CL = *curr - 8a/copy-byte 0/mod/indirect 7/rm32/EDI . . . 1/r32/CL . . # copy byte at *EDI to CL - # if (EAX != ECX) return false - 39/compare 3/mod/direct 0/rm32/EAX . . . 1/r32/ECX . . # compare EAX and ECX - 75/jump-if-not-equal $stream-data-equal?:false/disp8 - # ++f - 46/increment-ESI - # ++curr - 47/increment-EDI - eb/jump $stream-data-equal?:loop/disp8 -$stream-data-equal?:false: - b8/copy-to-EAX 0/imm32 - eb/jump $stream-data-equal?:end/disp8 -$stream-data-equal?:true: - b8/copy-to-EAX 1/imm32 -$stream-data-equal?:end: - # . restore registers - 5f/pop-to-EDI - 5e/pop-to-ESI - 5a/pop-to-EDX - 59/pop-to-ECX - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-stream-data-equal: - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # clear-stream(_test-stream) - # . . push args - 68/push _test-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # write(_test-stream, "Abc") - # . . push args - 68/push "Abc"/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # EAX = stream-data-equal?(_test-stream, "Abc") - # . . push args - 68/push "Abc"/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call stream-data-equal?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check-ints-equal(EAX, 1, msg) - # . . push args - 68/push "F - test-stream-data-equal"/imm32 - 68/push 1/imm32 - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-stream-data-equal-2: - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # clear-stream(_test-stream) - # . . push args - 68/push _test-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # write(_test-stream, "Abc") - # . . push args - 68/push "Abc"/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # EAX = stream-data-equal?(_test-stream, "Abd") - # . . push args - 68/push "Abd"/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call stream-data-equal?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check-ints-equal(EAX, 0, msg) - # . . push args - 68/push "F - test-stream-data-equal-2"/imm32 - 68/push 0/imm32 - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-stream-data-equal-length-check: - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # clear-stream(_test-stream) - # . . push args - 68/push _test-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # write(_test-stream, "Abc") - # . . push args - 68/push "Abc"/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # EAX = stream-data-equal?(_test-stream, "Abcd") - # . . push args - 68/push "Abcd"/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call stream-data-equal?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check-ints-equal(EAX, 0, msg) - # . . push args - 68/push "F - test-stream-data-equal-length-check"/imm32 - 68/push 0/imm32 - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -# helper for later tests -check-stream-equal: # f : (address stream), s : (address string), msg : (address string) - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # . save registers - 50/push-EAX - # EAX = stream-data-equal?(f, s) - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - # . . call - e8/call stream-data-equal?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check-ints-equal(EAX, 1, msg) - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x10/disp8 . # push *(EBP+16) - 68/push 1/imm32 - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -$check-stream-equal:end: - # . restore registers - 58/pop-to-EAX - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -# scan the next line until newline starting from f->read and compare it with -# 's' (ignoring the trailing newline) -# on success, set f->read to after the next newline -# on failure, leave f->read unmodified -# this function is usually used only in tests, so we repeatedly write f->read -next-stream-line-equal?: # f : (address stream), s : (address string) -> EAX : boolean - # pseudocode: - # currf = f->read # bound: f->write - # currs = 0 # bound : s->length - # while true - # if currf >= f->write - # return currs >= s->length - # if f[currf] == '\n' - # ++currf - # return currs >= s->length - # if (currs >= s->length) return false # the current line of f still has data to match - # if (f[currf] != s[currs]) return false - # ++currf - # ++currs - # - # collapsing the two branches that can return true: - # currf = f->read # bound: f->write - # currs = 0 # bound : s->length - # while true - # if (currf >= f->write) break - # if (f[currf] == '\n') break - # if (currs >= s->length) return false # the current line of f still has data to match - # if (f[currf] != s[currs]) return false - # ++currf - # ++currs - # ++currf # skip '\n' - # return currs >= s->length - # Here the final `++currf` is sometimes unnecessary (if we're already at the end of the stream) - # - # registers: - # f: ESI - # s: EDI - # currf: ECX - # currs: EDX - # f[currf]: EAX - # s[currs]: EBX - # - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # . save registers - 51/push-ECX - 52/push-EDX - 56/push-ESI - 57/push-EDI - # ESI = f - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 6/r32/ESI 8/disp8 . # copy *(EBP+8) to ESI - # currf/ECX = f->read - 8b/copy 1/mod/*+disp8 6/rm32/ESI . . . 1/r32/ECX 4/disp8 . # copy *(ESI+4) to ECX - # EDI = s - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 7/r32/EDI 0xc/disp8 . # copy *(EBP+12) to EDI - # currs/EDX = 0 - 31/xor 3/mod/direct 2/rm32/EDX . . . 2/r32/EDX . . # clear EDX - # EAX = EBX = 0 - 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX - 31/xor 3/mod/direct 3/rm32/EBX . . . 3/r32/EBX . . # clear EBX -$next-stream-line-equal?:loop: - # if (currf >= f->write) break - 3b/compare 0/mod/indirect 6/rm32/ESI . . . 1/r32/ECX . . # compare ECX with *ESI - 7d/jump-if-greater-or-equal $next-stream-line-equal?:break/disp8 - # AL = *(f->data + f->read) - 8a/copy-byte 1/mod/*+disp8 4/rm32/sib 6/base/ESI 1/index/ECX . 0/r32/AL 0xc/disp8 . # copy byte at *(ESI+ECX+12) to AL - # if (EAX == '\n') break - 3d/compare-EAX-and 0xa/imm32/newline - 74/jump-if-equal $next-stream-line-equal?:break/disp8 - # if (currs >= s->length) return false - 3b/compare 0/mod/indirect 7/rm32/EDI . . . 2/r32/EDX . . # compare EDX with *EDI - 7d/jump-if-greater-or-equal $next-stream-line-equal?:false/disp8 - # BL = *(s->data + currs) - 8a/copy-byte 1/mod/*+disp8 4/rm32/sib 7/base/EDI 2/index/EDX . 3/r32/BL 4/disp8 . # copy byte at *(EDI+EDX+4) to BL - # if (EAX != EBX) return false - 39/compare 3/mod/direct 0/rm32/EAX . . . 3/r32/EBX . . # compare EAX and EBX - 75/jump-if-not-equal $next-stream-line-equal?:false/disp8 - # ++currf - 41/increment-ECX - # ++currs - 42/increment-EDX - eb/jump $next-stream-line-equal?:loop/disp8 -$next-stream-line-equal?:break: - # ++currf - 41/increment-ECX - # if (currs >= s->length) return true - 3b/compare 0/mod/indirect 7/rm32/EDI . . . 2/r32/EDX . . # compare EDX with *EDI - 7c/jump-if-lesser $next-stream-line-equal?:false/disp8 -$next-stream-line-equal?:true: - b8/copy-to-EAX 1/imm32 - # persist f->read on success - 89/copy 1/mod/*+disp8 6/rm32/ESI . . . 1/r32/ECX 4/disp8 . # copy ECX to *(ESI+4) - eb/jump $next-stream-line-equal?:end/disp8 -$next-stream-line-equal?:false: - b8/copy-to-EAX 0/imm32 -$next-stream-line-equal?:end: - # . restore registers - 5f/pop-to-EDI - 5e/pop-to-ESI - 5a/pop-to-EDX - 59/pop-to-ECX - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-next-stream-line-equal-stops-at-newline: - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # clear-stream(_test-stream) - # . . push args - 68/push _test-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # write(_test-stream, "Abc\ndef") - # . . push args - 68/push "Abc\ndef"/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # EAX = next-stream-line-equal?(_test-stream, "Abc") - # . . push args - 68/push "Abc"/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call next-stream-line-equal?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check-ints-equal(EAX, 1, msg) - # . . push args - 68/push "F - test-next-stream-line-equal-stops-at-newline"/imm32 - 68/push 1/imm32 - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-next-stream-line-equal-stops-at-newline-2: - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # clear-stream(_test-stream) - # . . push args - 68/push _test-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # write(_test-stream, "Abc\ndef") - # . . push args - 68/push "Abc\ndef"/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # EAX = next-stream-line-equal?(_test-stream, "def") - # . . push args - 68/push "def"/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call next-stream-line-equal?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check-ints-equal(EAX, 0, msg) - # . . push args - 68/push "F - test-next-stream-line-equal-stops-at-newline-2"/imm32 - 68/push 0/imm32 - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-next-stream-line-equal-skips-newline: - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # clear-stream(_test-stream) - # . . push args - 68/push _test-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # write(_test-stream, "Abc\ndef\n") - # . . push args - 68/push "Abc\ndef\n"/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # next-stream-line-equal?(_test-stream, "Abc") - # . . push args - 68/push "Abc"/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call next-stream-line-equal?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # EAX = next-stream-line-equal?(_test-stream, "def") - # . . push args - 68/push "def"/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call next-stream-line-equal?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check-ints-equal(EAX, 1, msg) - # . . push args - 68/push "F - test-next-stream-line-equal-skips-newline"/imm32 - 68/push 1/imm32 - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-next-stream-line-equal-handles-final-line: - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # clear-stream(_test-stream) - # . . push args - 68/push _test-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # write(_test-stream, "Abc\ndef") - # . . push args - 68/push "Abc\ndef"/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # next-stream-line-equal?(_test-stream, "Abc") - # . . push args - 68/push "Abc"/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call next-stream-line-equal?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # EAX = next-stream-line-equal?(_test-stream, "def") - # . . push args - 68/push "def"/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call next-stream-line-equal?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check-ints-equal(EAX, 1, msg) - # . . push args - 68/push "F - test-next-stream-line-equal-skips-newline"/imm32 - 68/push 1/imm32 - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-next-stream-line-equal-always-fails-after-Eof: - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # clear-stream(_test-stream) - # . . push args - 68/push _test-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # write nothing - # EAX = next-stream-line-equal?(_test-stream, "") - # . . push args - 68/push ""/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call next-stream-line-equal?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check-ints-equal(EAX, 0, msg) - # . . push args - 68/push "F - test-next-stream-line-equal-always-fails-after-Eof"/imm32 - 68/push 1/imm32 - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # EAX = next-stream-line-equal?(_test-stream, "") - # . . push args - 68/push ""/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call next-stream-line-equal?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check-ints-equal(EAX, 0, msg) - # . . push args - 68/push "F - test-next-stream-line-equal-always-fails-after-Eof/2"/imm32 - 68/push 1/imm32 - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -# helper for later tests -check-next-stream-line-equal: - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # . save registers - 50/push-EAX - # EAX = next-stream-line-equal?(f, s) - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - # . . call - e8/call next-stream-line-equal?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check-ints-equal(EAX, 1, msg) - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x10/disp8 . # push *(EBP+16) - 68/push 1/imm32 - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . restore registers - 58/pop-to-EAX - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -# . . vim:nowrap:textwidth=0 diff --git a/subx/059stop.subx b/subx/059stop.subx deleted file mode 100644 index dbe8a663..00000000 --- a/subx/059stop.subx +++ /dev/null @@ -1,206 +0,0 @@ -# stop: dependency-injected wrapper around the exit() syscall -# -# We'd like to be able to write tests for functions that call exit(), and to -# make assertions about whether they exit() or not in a given situation. To -# achieve this we'll call exit() via a smarter wrapper called 'stop'. -# -# In the context of a test, calling a function X that calls 'stop' (directly -# or through further intervening calls) will unwind the stack until X returns, -# so that we can say check any further assertions after the execution of X. To -# achieve this end, we'll pass the return address of X as a 'target' argument -# into X, plumbing it through to 'stop'. When 'stop' gets a non-null target it -# unwinds the stack until the target. If it gets a null target it calls -# exit(). -# -# We'd also like to get the exit status out of 'stop', so we'll combine the -# input target with an output status parameter into a type called 'exit-descriptor'. -# -# So the exit-descriptor looks like this: -# target : address # return address for 'stop' to unwind to -# value : int # exit status stop was called with -# -# 'stop' thus takes two parameters: an exit-descriptor and the exit status. -# -# 'stop' won't bother cleaning up any other processor state besides the stack, -# such as registers. Only ESP will have a well-defined value after 'stop' -# returns. (This is a poor man's setjmp/longjmp, if you know what that is.) -# -# Before you can call any function that may call 'stop', you need to pass in an -# exit-descriptor to it. To create an exit-descriptor use 'tailor-exit-descriptor' -# below. It's not the most pleasant abstraction in the world. -# -# An exit-descriptor's target is its input, computed during 'tailor-exit-descriptor'. -# Its value is its output, computed during stop and available to the test. - -== code -# instruction effective address register displacement immediate -# . op subop mod rm32 base index scale r32 -# . 1-3 bytes 3 bits 2 bits 3 bits 3 bits 3 bits 2 bits 2 bits 0/1/2/4 bytes 0/1/2/4 bytes - -# Configure an exit-descriptor for a call pushing 'nbytes' bytes of args to -# the stack. -# Ugly that we need to know the size of args, but so it goes. -tailor-exit-descriptor: # ed : (address exit-descriptor), nbytes : int -> <void> - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # . save registers - 50/push-EAX - 51/push-ECX - # EAX = nbytes - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 0/r32/EAX 0xc/disp8 . # copy *(EBP+12) to EAX - # Let X be the value of ESP in the caller, before the call to tailor-exit-descriptor. - # The return address for a call in the caller's body will be at: - # X-8 if the caller takes 4 bytes of args for the exit-descriptor (add 4 bytes for the return address) - # X-12 if the caller takes 8 bytes of args - # ..and so on - # That's the value we need to return: X-nbytes-4 - # - # However, we also need to account for the perturbance to ESP caused by the - # call to tailor-exit-descriptor. It pushes 8 bytes of args followed by 4 - # bytes for the return address and 4 bytes to push EBP above. - # So EBP at this point is X-16. - # - # So the return address for the next call in the caller is: - # EBP+8 if the caller takes 4 bytes of args - # EBP+4 if the caller takes 8 bytes of args - # EBP if the caller takes 12 bytes of args - # EBP-4 if the caller takes 16 bytes of args - # ..and so on - # That's EBP+12-nbytes. - # option 1: 6 + 3 bytes -#? 2d/subtract 3/mod/direct 0/rm32/EAX . . . . . 8/imm32 # subtract from EAX -#? 8d/copy-address 0/mod/indirect 4/rm32/sib 5/base/EBP 0/index/EAX . 0/r32/EAX . . # copy EBP+EAX to EAX - # option 2: 2 + 4 bytes - f7 3/subop/negate 3/mod/direct 0/rm32/EAX . . . . . . # negate EAX - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 5/base/EBP 0/index/EAX . 0/r32/EAX 0xc/disp8 . # copy EBP+EAX+12 to EAX - # copy EAX to ed->target - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 1/r32/ECX 8/disp8 . # copy *(EBP+8) to ECX - 89/copy 0/mod/indirect 1/rm32/ECX . . . 0/r32/EAX . . # copy EAX to *ECX - # initialize ed->value - c7 0/subop/copy 1/mod/*+disp8 1/rm32/ECX . . . . 4/disp8 0/imm32 # copy to *(ECX+4) -$tailor-exit-descriptor:end: - # . restore registers - 59/pop-to-ECX - 58/pop-to-EAX - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -stop: # ed : (address exit-descriptor), value : int - # no prolog; one way or another, we're going to clobber registers - # EAX = ed - 8b/copy 1/mod/*+disp8 4/rm32/sib 4/base/ESP 4/index/none . 0/r32/EAX 4/disp8 . # copy *(ESP+4) to EAX - # if (ed->target == 0) really exit - 81 7/subop/compare 0/mod/indirect 0/rm32/EAX . . . . . 0/imm32 # compare *EAX - 75/jump-if-not-equal $stop:fake/disp8 - # . syscall(exit, value) - 8b/copy 1/mod/*+disp8 4/rm32/sib 4/base/ESP 4/index/none . 3/r32/EBX 8/disp8 . # copy *(ESP+8) to EBX - b8/copy-to-EAX 1/imm32/exit - cd/syscall 0x80/imm8 -$stop:fake: - # otherwise: - # ed->value = value+1 - 8b/copy 1/mod/*+disp8 4/rm32/sib 4/base/ESP 4/index/none . 1/r32/ECX 8/disp8 . # copy *(ESP+8) to ECX - 41/increment-ECX - 89/copy 1/mod/*+disp8 0/rm32/EAX . . . 1/r32/ECX 4/disp8 . # copy ECX to *(EAX+4) - # perform a non-local jump to ed->target - 8b/copy 0/mod/indirect 0/rm32/EAX . . . 4/r32/ESP . . # copy *EAX to ESP -$stop:end: - c3/return # doesn't return to caller - -test-stop-skips-returns-on-exit: - # This looks like the standard prolog, but is here for different reasons. - # A function calling 'stop' can't rely on EBP persisting past the call. - # - # Use EBP here as a stable base to refer to locals and arguments from in the - # presence of push/pop/call instructions. - # *Don't* use EBP as a way to restore ESP. - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # Make room for an exit descriptor on the stack. That's almost always the - # right place for it, available only as long as it's legal to use. Once this - # containing function returns we'll need a new exit descriptor. - # var ed/EAX : (address exit-descriptor) - 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # subtract from ESP - 89/copy 3/mod/direct 0/rm32/EAX . . . 4/r32/ESP . . # copy ESP to EAX - # Size the exit-descriptor precisely for the next call below, to _test-stop-1. - # tailor-exit-descriptor(ed, 4) - # . . push args - 68/push 4/imm32/nbytes-of-args-for-_test-stop-1 - 50/push-EAX - # . . call - e8/call tailor-exit-descriptor/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . _test-stop-1(ed) - # . . push args - 50/push-EAX - # . . call - e8/call _test-stop-1/disp32 - # registers except ESP may be clobbered at this point - # restore args - 58/pop-to-EAX - # check that _test-stop-1 tried to call exit(1) - # check-ints-equal(ed->value, 2, msg) # i.e. stop was called with value 1 - # . . push args - 68/push "F - test-stop-skips-returns-on-exit"/imm32 - 68/push 2/imm32 - # . . push ed->value - ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 4/disp8 . # push *(EAX+4) - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - # don't restore ESP from EBP; manually reclaim locals - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 5d/pop-to-EBP - c3/return - -_test-stop-1: # ed : (address exit-descriptor) - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # _test-stop-2(ed) - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - # . . call - e8/call _test-stop-2/disp32 - # should never get past this point -$_test-stop-1:dead-end: - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # signal test failed: check-ints-equal(1, 0, msg) - # . . push args - 68/push "F - test-stop-skips-returns-on-exit"/imm32 - 68/push 0/imm32 - 68/push 1/imm32 - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -_test-stop-2: # ed : (address exit-descriptor) - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # . stop(ed, 1) - # . . push args - 68/push 1/imm32 - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - # . . call - e8/call stop/disp32 - # should never get past this point -$_test-stop-2:dead-end: - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -# . . vim:nowrap:textwidth=0 diff --git a/subx/060read.subx b/subx/060read.subx deleted file mode 100644 index 61ee75b0..00000000 --- a/subx/060read.subx +++ /dev/null @@ -1,440 +0,0 @@ -# read: analogously to write, support reading from in-memory streams in -# addition to file descriptors. -# -# We can pass it either a file descriptor or an address to a stream. If a -# file descriptor is passed in, we _read from it using the right syscall. If a -# stream is passed in (a fake file descriptor), we read from it instead. This -# lets us initialize input for tests. -# -# A little counter-intuitively, the output of 'read' ends up in.. a stream. So -# tests end up doing a redundant copy. Why? Well, consider the alternatives: -# -# a) Reading into a string, and returning a pointer to the end of the read -# region, or a count of bytes written. Now this count or end pointer must be -# managed separately by the caller, which can be error-prone. -# -# b) Having 'read' return a buffer that it allocates. But there's no way to -# know in advance how large to make the buffer. If you read less than the -# size of the buffer you again end up needing to manage initialized vs -# uninitialized memory. -# -# c) Creating more helpful variants like 'read-byte' or 'read-until' which -# also can take a file descriptor or stream, just like 'write'. But such -# primitives don't exist in the Linux kernel, so we'd be implementing them -# somehow, either with more internal buffering or by making multiple -# syscalls. -# -# Reading into a stream avoids these problems. The buffer is externally -# provided and the caller has control over where it's allocated, its lifetime, -# and so on. The buffer's read and write pointers are internal to it so it's -# easier to keep in a consistent state. And it can now be passed directly to -# helpers like 'read-byte' or 'read-until' that only need to support streams, -# never file descriptors. -# -# Like with 'write', we assume our data segment will never begin at an address -# shorter than 0x08000000, so any smaller arguments are assumed to be real -# file descriptors. -# -# As a reminder, a stream looks like this: -# write: int # index at which to write to next -# read: int # index at which to read next -# data: (array byte) # prefixed by length as usual - -== code -# instruction effective address register displacement immediate -# . op subop mod rm32 base index scale r32 -# . 1-3 bytes 3 bits 2 bits 3 bits 3 bits 3 bits 2 bits 2 bits 0/1/2/4 bytes 0/1/2/4 bytes - -read: # f : fd or (address stream), s : (address stream) -> num-bytes-read/EAX - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # if (f < 0x08000000) return _read(f, s) # f can't be a user-mode address, so treat it as a kernel file descriptor - 81 7/subop/compare 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 0x08000000/imm32 # compare *(EBP+8) - 73/jump-if-greater-unsigned-or-equal $read:fake/disp8 - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - # . . call - e8/call _read/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # return - eb/jump $read:end/disp8 -$read:fake: - # otherwise, treat 'f' as a stream to scan from - # . save registers - 56/push-ESI - 57/push-EDI - # ESI = f - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 6/r32/ESI 8/disp8 . # copy *(EBP+8) to ESI - # EDI = s - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 7/r32/EDI 0xc/disp8 . # copy *(EBP+12) to ESI - # EAX = _buffer-4(out = &s->data[s->write], outend = &s->data[s->length], - # in = &f->data[f->read], inend = &f->data[f->write]) - # . . push &f->data[f->write] - 8b/copy 0/mod/indirect 6/rm32/ESI . . . 0/r32/EAX . . # copy *ESI to EAX - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 6/base/ESI 0/index/EAX . 0/r32/EAX 0xc/disp8 . # copy ESI+EAX+12 to EAX - 50/push-EAX - # . . push &f->data[f->read] - 8b/copy 1/mod/*+disp8 6/rm32/ESI . . . 0/r32/EAX 4/disp8 . # copy *(ESI+4) to EAX - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 6/base/ESI 0/index/EAX . 0/r32/EAX 0xc/disp8 . # copy ESI+EAX+12 to EAX - 50/push-EAX - # . . push &s->data[s->length] - 8b/copy 1/mod/*+disp8 7/rm32/EDI . . . 0/r32/EAX 8/disp8 . # copy *(EDI+8) to EAX - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 7/base/EDI 0/index/EAX . 0/r32/EAX 0xc/disp8 . # copy EDI+EAX+12 to EAX - 50/push-EAX - # . . push &s->data[s->write] - 8b/copy 0/mod/indirect 7/rm32/EDI . . . 0/r32/EAX . . # copy *EDI to EAX - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 7/base/EDI 0/index/EAX . 0/r32/EAX 0xc/disp8 . # copy EDI+EAX+12 to EAX - 50/push-EAX - # . . call - e8/call _buffer-4/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP - # s->write += EAX - 01/add 0/mod/indirect 7/rm32/EDI . . . 0/r32/EAX . . # add EAX to *EDI - # f->read += EAX - 01/add 1/mod/*+disp8 6/rm32/ESI . . . 0/r32/EAX 4/disp8 . # add EAX to *(ESI+4) - # . restore registers - 5f/pop-to-EDI - 5e/pop-to-ESI -$read:end: - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -# - helpers - -# '_buffer' is like '_append', but silently stops instead of aborting when it runs out of space - -# 3-argument variant of _buffer -_buffer-3: # out : address, outend : address, s : (array byte) -> num_bytes_buffered/EAX - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # . save registers - 51/push-ECX - # EAX = _buffer-4(out, outend, &s->data[0], &s->data[s->length]) - # . . push &s->data[s->length] - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . 0/r32/EAX 0x10/disp8 . # copy *(EBP+16) to EAX - 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX - 51/push-ECX - # . . push &s->data[0] - 8d/copy-address 1/mod/*+disp8 0/rm32/EAX . . . 1/r32/ECX 4/disp8 . # copy EAX+4 to ECX - 51/push-ECX - # . . push outend - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - # . . push out - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - # . . call - e8/call _buffer-4/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP -$_buffer-3:end: - # . restore registers - 59/pop-to-ECX - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -# 4-argument variant of _buffer -_buffer-4: # out : address, outend : address, in : address, inend : address -> num_bytes_buffered/EAX - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # . save registers - 51/push-ECX - 52/push-EDX - 53/push-EBX - 56/push-ESI - 57/push-EDI - # EAX/num_bytes_buffered = 0 - b8/copy-to-EAX 0/imm32 - # EDI = out - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 7/r32/EDI 8/disp8 . # copy *(EBP+8) to EDI - # EDX = outend - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 2/r32/EDX 0xc/disp8 . # copy *(EBP+12) to EDX - # ESI = in - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 6/r32/ESI 0x10/disp8 . # copy *(EBP+16) to ESI - # ECX = inend - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 1/r32/ECX 0x14/disp8 . # copy *(EBP+20) to ECX -$_buffer-4:loop: - # if (in >= inend) break - 39/compare 3/mod/direct 6/rm32/ESI . . . 1/r32/ECX . . # compare ESI with ECX - 73/jump-if-greater-or-equal-unsigned $_buffer-4:end/disp8 - # if (out >= outend) break # for now silently ignore filled up buffer - 39/compare 3/mod/direct 7/rm32/EDI . . . 2/r32/EDX . . # compare EDI with EDX - 73/jump-if-greater-or-equal-unsigned $_buffer-4:end/disp8 - # *out = *in - 8a/copy-byte 0/mod/indirect 6/rm32/ESI . . . 3/r32/BL . . # copy byte at *ESI to BL - 88/copy-byte 0/mod/indirect 7/rm32/EDI . . . 3/r32/BL . . # copy byte at BL to *EDI - # ++num_bytes_buffered - 40/increment-EAX - # ++in - 46/increment-ESI - # ++out - 47/increment-EDI - eb/jump $_buffer-4:loop/disp8 -$_buffer-4:end: - # . restore registers - 5f/pop-to-EDI - 5e/pop-to-ESI - 5b/pop-to-EBX - 5a/pop-to-EDX - 59/pop-to-ECX - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - - -# idea: a clear-if-empty method on streams that clears only if f->read == f->write -# Unclear how I'd use it, though. Callers seem to need the check anyway. -# Maybe a better helper would be 'empty-stream?' - -_read: # fd : int, s : (address stream) -> num-bytes-read/EAX - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # . save registers - 51/push-ECX - 52/push-EDX - 53/push-EBX - 56/push-ESI - # ESI = s - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 6/r32/ESI 0xc/disp8 . # copy *(EBP+12) to ESI - # EAX = s->write - 8b/copy 0/mod/indirect 6/rm32/ESI . . . 0/r32/EAX . . # copy *ESI to EAX - # EDX = s->length - 8b/copy 1/mod/*+disp8 6/rm32/ESI . . . 2/r32/EDX 8/disp8 . # copy *(ESI+8) to EDX - # syscall(read, fd, &s->data[s->write], s->length - s->write) - # . . fd : EBX - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 3/r32/EBX 8/disp8 . # copy *(EBP+8) to EBX - # . . data : ECX = &s->data[s->write] - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 6/base/ESI 0/index/EAX . 1/r32/ECX 0xc/disp8 . # copy ESI+EAX+12 to ECX - # . . size : EDX = s->length - s->write - 29/subtract 3/mod/direct 2/rm32/EDX . . . 0/r32/EAX . . # subtract EAX from EDX - # . . syscall - b8/copy-to-EAX 3/imm32/read - cd/syscall 0x80/imm8 - # add the result EAX to s->write - 01/add 0/mod/indirect 6/rm32/ESI . . . 0/r32/EAX . . # add EAX to *ESI -$_read:end: - # . restore registers - 5e/pop-to-ESI - 5b/pop-to-EBX - 5a/pop-to-EDX - 59/pop-to-ECX - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - - # Two options: - # 1 (what we have above): - # ECX = s - # EAX = s->write - # EDX = s->length - # # syscall - # ECX = lea ECX+EAX+12 - # EDX = sub EDX EAX - # - # 2: - # ECX = s - # EDX = s->length - # ECX = &s->data - # # syscall - # ECX = add ECX, s->write - # EDX = sub EDX, s->write - # - # Not much to choose between the two? Option 2 performs a duplicate load to - # use one less register, but doesn't increase the amount of spilling (ECX - # and EDX must be used, and EAX must be clobbered anyway). - -# - tests - -test-read-single: - # - write a single character into _test-stream, then read from it - # clear-stream(_test-stream) - # . . push args - 68/push _test-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # clear-stream(_test-tmp-stream) - # . . push args - 68/push _test-tmp-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # write(_test-stream, "Ab") - # . . push args - 68/push "Ab"/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # EAX = read(_test-stream, _test-tmp-stream) - # . . push args - 68/push _test-tmp-stream/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call read/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check-ints-equal(EAX, 2, msg) - # . . push args - 68/push "F - test-read-single: return EAX"/imm32 - 68/push 2/imm32 - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # check-stream-equal(_test-tmp-stream, "Ab", msg) - # . . push args - 68/push "F - test-read-single"/imm32 - 68/push "Ab"/imm32 - 68/push _test-tmp-stream/imm32 - # . . call - e8/call check-stream-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # end - c3/return - -test-read-is-stateful: - # - make two consecutive reads, check that their results are appended - # clear-stream(_test-stream) - # . . push args - 68/push _test-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # clear-stream(_test-tmp-stream) - # . . push args - 68/push _test-tmp-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # write(_test-stream, "C") - # . . push args - 68/push "C"/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # read(_test-stream, _test-tmp-stream) - # . . push args - 68/push _test-tmp-stream/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call read/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # write(_test-stream, "D") - # . . push args - 68/push "D"/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # read(_test-stream, _test-tmp-stream) - # . . push args - 68/push _test-tmp-stream/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call read/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check-stream-equal(_test-tmp-stream, "CD", msg) - # . . push args - 68/push "F - test-read-is-stateful"/imm32 - 68/push "CD"/imm32 - 68/push _test-tmp-stream/imm32 - # . . call - e8/call check-stream-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # end - c3/return - -test-read-returns-0-on-end-of-file: - # - read after hitting end-of-file, check that result is 0 - # setup - # . clear-stream(_test-stream) - # . . push args - 68/push _test-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-tmp-stream) - # . . push args - 68/push _test-tmp-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . write(_test-stream, "Ab") - # . . push args - 68/push "Ab"/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # first read gets to end-of-file - # . read(_test-stream, _test-tmp-stream) - # . . push args - 68/push _test-tmp-stream/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call read/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # second read - # . read(_test-stream, _test-tmp-stream) - # . . push args - 68/push _test-tmp-stream/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call read/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check-ints-equal(EAX, 0, msg) - # . . push args - 68/push "F - test-read-returns-0-on-end-of-file"/imm32 - 68/push 0/imm32 - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # end - c3/return - -== data - -_test-tmp-stream: - # current write index - 0/imm32 - # current read index - 0/imm32 - # length - 8/imm32 - # data - 00 00 00 00 00 00 00 00 # 8 bytes - -# . . vim:nowrap:textwidth=0 diff --git a/subx/061read-byte.subx b/subx/061read-byte.subx deleted file mode 100644 index 99e2babe..00000000 --- a/subx/061read-byte.subx +++ /dev/null @@ -1,293 +0,0 @@ -# read-byte-buffered: one higher-level abstraction atop 'read'. -# -# There are many situations where 'read' is a lot to manage, and we need -# to abstract some details away. One of them is when we want to read a file -# character by character. In this situation we follow C's FILE data structure, -# which manages the underlying file descriptor together with the buffer it -# reads into. We call our version 'buffered-file'. Should be useful with other -# primitives as well, in later layers. - -== data - -# The buffered file for standard input. Also illustrates the layout for -# buffered-file. -Stdin: - # file descriptor or (address stream) - 0/imm32 # standard input - # current write index - 0/imm32 - # current read index - 0/imm32 - # length - 8/imm32 - # data - 00 00 00 00 00 00 00 00 # 8 bytes - -# TODO: 8 bytes is too small. We'll need to grow the buffer for efficiency. But -# I don't want to type in 1024 bytes here. - -== code -# instruction effective address register displacement immediate -# . op subop mod rm32 base index scale r32 -# . 1-3 bytes 3 bits 2 bits 3 bits 3 bits 3 bits 2 bits 2 bits 0/1/2/4 bytes 0/1/2/4 bytes - -# return next byte value in EAX, with top 3 bytes cleared. -# On reaching end of file, return 0xffffffff (Eof). -read-byte-buffered: # f : (address buffered-file) -> byte-or-Eof/EAX - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # . save registers - 51/push-ECX - 56/push-ESI - # ESI = f - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 6/r32/ESI 8/disp8 . # copy *(EBP+8) to ESI - # ECX = f->read - 8b/copy 1/mod/*+disp8 6/rm32/ESI . . . 1/r32/ECX 8/disp8 . # copy *(ESI+8) to ECX - # if (f->read >= f->write) populate stream from file - 3b/compare 1/mod/*+disp8 6/rm32/ESI . . . 1/r32/ECX 4/disp8 . # compare ECX with *(ESI+4) - 7c/jump-if-lesser $read-byte-buffered:from-stream/disp8 - # . clear-stream(stream = f+4) - # . . push args - 8d/copy-address 1/mod/*+disp8 6/rm32/ESI . . . 0/r32/EAX 4/disp8 . # copy ESI+4 to EAX - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . f->read must now be 0; update its cache at ECX - 31/xor 3/mod/direct 1/rm32/ECX . . . 1/r32/ECX . . # clear ECX - # . EAX = read(f->fd, stream = f+4) - # . . push args - 50/push-EAX - ff 6/subop/push 0/mod/indirect 6/rm32/ESI . . . . . . # push *ESI - # . . call - e8/call read/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # if (EAX == 0) return 0xffffffff - 3d/compare-EAX-and 0/imm32 - 75/jump-if-not-equal $read-byte-buffered:from-stream/disp8 - b8/copy-to-EAX 0xffffffff/imm32/Eof - eb/jump $read-byte-buffered:end/disp8 -$read-byte-buffered:from-stream: - # read byte from stream - # AL = f->data[f->read] - 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX - 8a/copy-byte 1/mod/*+disp8 4/rm32/sib 6/base/ESI 1/index/ECX . 0/r32/AL 0x10/disp8 . # copy byte at *(ESI+ECX+16) to AL - # ++f->read - ff 0/subop/increment 1/mod/*+disp8 6/rm32/ESI . . . . 8/disp8 . # increment *(ESI+8) -$read-byte-buffered:end: - # . restore registers - 5e/pop-to-ESI - 59/pop-to-ECX - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -# - tests - -test-read-byte-buffered-single: - # - check that read-byte-buffered returns first byte of 'file' - # setup - # . clear-stream(_test-stream) - # . . push args - 68/push _test-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-buffered-file+4) - # . . push args - b8/copy-to-EAX _test-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . write(_test-stream, "Ab") - # . . push args - 68/push "Ab"/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # read-byte-buffered(_test-buffered-file) - # . . push args - 68/push _test-buffered-file/imm32 - # . . call - e8/call read-byte-buffered/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # check-ints-equal(EAX, 'A', msg) - # . . push args - 68/push "F - test-read-byte-buffered-single"/imm32 - 68/push 0x41/imm32 - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . end - c3/return - -test-read-byte-buffered-multiple: - # - call read-byte-buffered twice, check that second call returns second byte - # setup - # . clear-stream(_test-stream) - # . . push args - 68/push _test-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-buffered-file+4) - # . . push args - b8/copy-to-EAX _test-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . write(_test-stream, "Ab") - # . . push args - 68/push "Ab"/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # read-byte-buffered(_test-buffered-file) - # . . push args - 68/push _test-buffered-file/imm32 - # . . call - e8/call read-byte-buffered/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # read-byte-buffered(_test-buffered-file) - # . . push args - 68/push _test-buffered-file/imm32 - # . . call - e8/call read-byte-buffered/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # check-ints-equal(EAX, 'b', msg) - # . . push args - 68/push "F - test-read-byte-buffered-multiple"/imm32 - 68/push 0x62/imm32 - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . end - c3/return - -test-read-byte-buffered-end-of-file: - # - call read-byte-buffered on an empty 'file', check that it returns Eof - # setup - # . clear-stream(_test-stream) - # . . push args - 68/push _test-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-buffered-file+4) - # . . push args - b8/copy-to-EAX _test-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # read-byte-buffered(_test-buffered-file) - # . . push args - 68/push _test-buffered-file/imm32 - # . . call - e8/call read-byte-buffered/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # check-ints-equal(EAX, 0xffffffff, msg) - # . . push args - 68/push "F - test-read-byte-buffered-end-of-file"/imm32 - 68/push 0xffffffff/imm32/Eof - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . end - c3/return - -test-read-byte-buffered-refills-buffer: - # - consume buffered-file's buffer, check that next read-byte-buffered still works - # setup - # . clear-stream(_test-stream) - # . . push args - 68/push _test-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-buffered-file+4) - # . . push args - b8/copy-to-EAX _test-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . write(_test-stream, "Abcdefgh") - # . . push args - 68/push "Abcdefgh"/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # pretend buffer is full - # . _test-buffered-file->read = 6 # >= _test-buffered-file->length - b8/copy-to-EAX _test-buffered-file/imm32 - c7 0/subop/copy 1/mod/*+disp8 0/rm32/EAX . . . . 8/disp8 6/imm32 # copy to *(EAX+8) - # read-byte-buffered(_test-buffered-file) - # . . push args - 68/push _test-buffered-file/imm32 - # . . call - e8/call read-byte-buffered/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # check-ints-equal(EAX, 'A', msg) - # . . push args - 68/push "F - test-read-byte-buffered-refills-buffer"/imm32 - 68/push 0x41/imm32 - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . end - c3/return - -== data - -# a test buffered file for _test-stream -_test-buffered-file: - # file descriptor or (address stream) - _test-stream/imm32 - # current write index - 0/imm32 - # current read index - 0/imm32 - # length - 6/imm32 - # data - 00 00 00 00 00 00 # 6 bytes - -# . . vim:nowrap:textwidth=0 diff --git a/subx/062write-stream.subx b/subx/062write-stream.subx deleted file mode 100644 index c4ec8db2..00000000 --- a/subx/062write-stream.subx +++ /dev/null @@ -1,255 +0,0 @@ -# write-stream: like write, but write streams rather than strings - -== code -# instruction effective address register displacement immediate -# . op subop mod rm32 base index scale r32 -# . 1-3 bytes 3 bits 2 bits 3 bits 3 bits 3 bits 2 bits 2 bits 0/1/2/4 bytes 0/1/2/4 bytes - -#? Entry: # manual test -#? # write-stream(stdout, _test-stream2) -#? 68/push _test-stream2/imm32 -#? 68/push 1/imm32/stdout -#? e8/call write-stream/disp32 -#? # syscall(exit, Num-test-failures) -#? 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 3/r32/EBX Num-test-failures/disp32 # copy *Num-test-failures to EBX -#? b8/copy-to-EAX 1/imm32/exit -#? cd/syscall 0x80/imm8 - -write-stream: # f : fd or (address stream), s : (address stream) -> <void> - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # if (f < 0x08000000) _write-stream(f, s), return # f can't be a user-mode address, so treat it as a kernel file descriptor - 81 7/subop/compare 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 0x08000000/imm32 # compare *(EBP+8) - 73/jump-if-greater-unsigned-or-equal $write-stream:fake/disp8 - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - # . . call - e8/call _write-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - eb/jump $write-stream:end/disp8 -$write-stream:fake: - # otherwise, treat 'f' as a stream to append to - # . save registers - 50/push-EAX - 56/push-ESI - 57/push-EDI - # EDI = f - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . 7/r32/EDI 8/disp8 . # copy *(EBP+8) to EDI - # ESI = s - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . 6/r32/ESI 0xc/disp8 . # copy *(EBP+12) to ESI - # EAX = _append-4(&f->data[f->write], &f->data[f->length], &s->data[s->read], &s->data[s->write]) - # . . push &s->data[s->write] - 8b/copy 0/mod/indirect 6/rm32/ESI . . . 0/r32/EAX . . # copy *ESI to EAX - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 6/base/ESI 0/index/EAX . 0/r32/EAX 0xc/disp8 . # copy ESI+EAX+12 to EAX - 50/push-EAX - # . . push &s->data[s->read] - 8b/copy 1/mod/*+disp8 6/rm32/ESI . . . 0/r32/EAX 4/disp8 . # copy *(ESI+4) to EAX - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 6/base/ESI 0/index/EAX . 0/r32/EAX 0xc/disp8 . # copy ESI+EAX+12 to EAX - 50/push-EAX - # . . push &f->data[f->length] - 8b/copy 1/mod/*+disp8 7/rm32/EDI . . . 0/r32/EAX 8/disp8 . # copy *(EDI+8) to EAX - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 7/base/EDI 0/index/EAX . 0/r32/EAX 0xc/disp8 . # copy EDI+EAX+12 to EAX - 50/push-EAX - # . . push &f->data[f->write] - 8b/copy 0/mod/indirect 7/rm32/EDI . . . 0/r32/EAX . . # copy *EDI to EAX - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 7/base/EDI 0/index/EAX . 0/r32/EAX 0xc/disp8 . # copy EDI+EAX+12 to EAX - 50/push-EAX - # . . call - e8/call _append-4/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP - # f->write += EAX - 01/add 0/mod/indirect 7/rm32/EDI . . . 0/r32/EAX . . # add EAX to *EDI - # s->read += EAX - 01/add 1/mod/*+disp8 6/rm32/ESI . . . 0/r32/EAX 4/disp8 . # add EAX to *(ESI+4) - # . restore registers - 5f/pop-to-EDI - 5e/pop-to-ESI - 58/pop-to-EAX -$write-stream:end: - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -_write-stream: # fd : int, s : (address stream) -> <void> - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # . save registers - 50/push-EAX - 51/push-ECX - 52/push-EDX - 53/push-EBX - 56/push-ESI - 57/push-EDI - # ESI = s - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 6/r32/ESI 0xc/disp8 . # copy *(EBP+12) to ESI - # EDI = s->read - 8b/copy 1/mod/*+disp8 6/rm32/ESI . . . 7/r32/EDI 4/disp8 . # copy *(ESI+4) to EDI - # EDX = s->write - 8b/copy 0/mod/indirect 6/rm32/ESI . . . 2/r32/EDX . . # copy *ESI to EDX - # syscall(write, fd, &s->data[s->read], s->write - s->read) - # . . fd : EBX - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 3/r32/EBX 8/disp8 . # copy *(EBP+8) to EBX - # . . data : ECX = &s->data[s->read] - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 6/base/ESI 7/index/EDI . 1/r32/ECX 0xc/disp8 . # copy ESI+EDI+12 to ECX - # . . size : EDX = s->write - s->read - 29/subtract 3/mod/direct 2/rm32/EDX . . . 7/r32/EDI . . # subtract EDI from EDX - # . . syscall - b8/copy-to-EAX 4/imm32/write - cd/syscall 0x80/imm8 - # if (EAX < 0) abort - 3d/compare-EAX-with 0/imm32 - 0f 8c/jump-if-lesser $_write-stream:abort/disp32 - # s->read += EAX - 01/add 1/mod/*+disp8 6/rm32/ESI . . . 0/r32/EAX 4/disp8 . # add EAX to *(ESI+4) - # . restore registers - 5f/pop-to-EDI - 5e/pop-to-ESI - 5b/pop-to-EBX - 5a/pop-to-EDX - 59/pop-to-ECX - 58/pop-to-EAX - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -$_write-stream:abort: - # . _write(2/stderr, error) - # . . push args - 68/push "_write-stream: failed to write to file\n"/imm32 - 68/push 2/imm32/stderr - # . . call - e8/call _write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . syscall(exit, 1) - bb/copy-to-EBX 1/imm32 - b8/copy-to-EAX 1/imm32/exit - cd/syscall 0x80/imm8 - # never gets here - -test-write-stream-single: - # setup - # . clear-stream(_test-stream) - # . . push args - 68/push _test-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-stream2) - # . . push args - 68/push _test-stream2/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . write(_test-stream2, "Ab") - # . . push args - 68/push "Ab"/imm32 - 68/push _test-stream2/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # write-stream(_test-stream, _test-stream2) - # . . push args - 68/push _test-stream2/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call write-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check-stream-equal(_test-stream, "Ab", msg) - # . . push args - 68/push "F - test-write-stream-single"/imm32 - 68/push "Ab"/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call check-stream-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . end - c3/return - -test-write-stream-appends: - # setup - # . clear-stream(_test-stream) - # . . push args - 68/push _test-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-stream2) - # . . push args - 68/push _test-stream2/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . write(_test-stream2, "C") - # . . push args - 68/push "C"/imm32 - 68/push _test-stream2/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # first write - # . write-stream(_test-stream, _test-stream2) - # . . push args - 68/push _test-stream2/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call write-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # second write - # . write(_test-stream2, "D") - # . . push args - 68/push "D"/imm32 - 68/push _test-stream2/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . write-stream(_test-stream, _test-stream2) - # . . push args - 68/push _test-stream2/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call write-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check-stream-equal(_test-stream, "CD", msg) - # . . push args - 68/push "F - test-write-stream-appends"/imm32 - 68/push "CD"/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call check-stream-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . end - c3/return - -== data - -_test-stream2: - # current write index - 4/imm32 - # current read index - 1/imm32 - # length - 8/imm32 - # data - 41/A 42/B 43/C 44/D 00 00 00 00 # 8 bytes - -# . . vim:nowrap:textwidth=0 diff --git a/subx/063error.subx b/subx/063error.subx deleted file mode 100644 index db460db1..00000000 --- a/subx/063error.subx +++ /dev/null @@ -1,50 +0,0 @@ -# Print an error message and exit. - -== code -# instruction effective address register displacement immediate -# . op subop mod rm32 base index scale r32 -# . 1-3 bytes 3 bits 2 bits 3 bits 3 bits 3 bits 2 bits 2 bits 0/1/2/4 bytes 0/1/2/4 bytes - -# write(out, "Error: "+msg+"\n") then stop(ed, 1) -error: # ed : (address exit-descriptor), out : fd or (address stream), msg : (address array byte) -> <void> - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # write(out, "Error: ") - # . . push args - 68/push "Error: "/imm32 - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # write(out, msg) - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x10/disp8 . # push *(EBP+16) - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # write(out, Newline) - # . . push args - 68/push Newline/imm32 - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # stop(ed, 1) - # . . push args - 68/push 1/imm32 - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - # . . call - e8/call stop/disp32 - # should never get past this point -$error:dead-end: - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -# . . vim:nowrap:textwidth=0 diff --git a/subx/064write-byte.subx b/subx/064write-byte.subx deleted file mode 100644 index 057b9164..00000000 --- a/subx/064write-byte.subx +++ /dev/null @@ -1,288 +0,0 @@ -# write-byte-buffered: add a single byte to a buffered-file. -# flush: write out any buffered writes to disk. -# -# TODO: Come up with a way to signal failure to write to disk. This is hard -# since the failure may impact previous calls that were buffered. - -== data - -# The buffered file for standard output. -Stdout: - # file descriptor or (address stream) - 1/imm32 # standard output - # current write index - 0/imm32 - # current read index - 0/imm32 - # length - 8/imm32 - # data - 00 00 00 00 00 00 00 00 # 8 bytes - -# TODO: 8 bytes is too small. We'll need to grow the buffer for efficiency. But -# I don't want to type in 1024 bytes here. - -== code -# instruction effective address register displacement immediate -# . op subop mod rm32 base index scale r32 -# . 1-3 bytes 3 bits 2 bits 3 bits 3 bits 3 bits 2 bits 2 bits 0/1/2/4 bytes 0/1/2/4 bytes - -# Write lower byte of 'n' to 'f'. -write-byte-buffered: # f : (address buffered-file), n : int -> <void> - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # . save registers - 51/push-ECX - 57/push-EDI - # EDI = f - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 7/r32/EDI 8/disp8 . # copy *(EBP+8) to EDI - # ECX = f->write - 8b/copy 1/mod/*+disp8 7/rm32/EDI . . . 1/r32/ECX 4/disp8 . # copy *(EDI+4) to ECX - # if (f->write >= f->length) flush and clear f's stream - 3b/compare 1/mod/*+disp8 7/rm32/EDI . . . 1/r32/ECX 0xc/disp8 . # compare ECX with *(EDI+12) - 7c/jump-if-lesser $write-byte-buffered:to-stream/disp8 - # . flush(f) - # . . push args - 57/push-EDI - # . . call - e8/call flush/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(stream = f+4) - # . . push args - 8d/copy-address 1/mod/*+disp8 7/rm32/EDI . . . 0/r32/EAX 4/disp8 . # copy EDI+4 to EAX - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . f->write must now be 0; update its cache at ECX - 31/xor 3/mod/direct 1/rm32/ECX . . . 1/r32/ECX . . # clear ECX -$write-byte-buffered:to-stream: - # write to stream - # f->data[f->write] = LSB(n) - 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX - 8a/copy-byte 1/mod/*+disp8 5/rm32/EBP . . . 0/r32/AL 0xc/disp8 . # copy byte at *(EBP+12) to AL - 88/copy-byte 1/mod/*+disp8 4/rm32/sib 7/base/EDI 1/index/ECX . 0/r32/AL 0x10/disp8 . # copy AL to *(EDI+ECX+16) - # ++f->write - ff 0/subop/increment 1/mod/*+disp8 7/rm32/EDI . . . . 4/disp8 . # increment *(EDI+4) -$write-byte-buffered:end: - # . restore registers - 5f/pop-to-EDI - 59/pop-to-ECX - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -flush: # f : (address buffered-file) -> <void> - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # . save registers - 50/push-EAX - 51/push-ECX - # EAX = f - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 0/r32/EAX 8/disp8 . # copy *(EBP+8) to EAX - # write-stream(f->fd, data = f+4) - # . . push args - 8d/copy-address 1/mod/*+disp8 0/rm32/EAX . . . 1/r32/ECX 4/disp8 . # copy EAX+4 to ECX - 51/push-ECX - ff 6/subop/push 0/mod/indirect 0/rm32/EAX . . . . . . # push *EAX - # . . call - e8/call write-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -$flush:end: - # . restore registers - 59/pop-to-ECX - 58/pop-to-EAX - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-write-byte-buffered-single: - # - check that write-byte-buffered writes to first byte of 'file' - # setup - # . clear-stream(_test-stream) - # . . push args - 68/push _test-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-buffered-file+4) - # . . push args - b8/copy-to-EAX _test-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # write-byte-buffered(_test-buffered-file, 'A') - # . . push args - 68/push 0x41/imm32 - 68/push _test-buffered-file/imm32 - # . . call - e8/call write-byte-buffered/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # flush(_test-buffered-file) - # . . push args - 68/push _test-buffered-file/imm32 - # . . call - e8/call flush/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # check-stream-equal(_test-stream, "A", msg) - # . . push args - 68/push "F - test-write-byte-buffered-single"/imm32 - 68/push "A"/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call check-stream-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . end - c3/return - -test-write-byte-buffered-multiple-flushes: - # - check that write-byte-buffered correctly flushes buffered data - # setup - # . clear-stream(_test-stream) - # . . push args - 68/push _test-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-buffered-file+4) - # . . push args - b8/copy-to-EAX _test-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # fill up the buffer for _test-buffered-file - # . write(_test-buffered-file+4, 'abcdef') - # . . push args - 68/push "abcdef"/imm32 - b8/copy-to-EAX _test-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # write-byte-buffered(_test-buffered-file, 'g') - # . . push args - 68/push 0x67/imm32 - 68/push _test-buffered-file/imm32 - # . . call - e8/call write-byte-buffered/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # flush(_test-buffered-file) - # . . push args - 68/push _test-buffered-file/imm32 - # . . call - e8/call flush/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # check-stream-equal(_test-stream, "abcdef", msg) - # . . push args - 68/push "F - test-write-byte-buffered-multiple-flushes: 1"/imm32 - 68/push "abcdefg"/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call check-stream-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . end - c3/return - -# - variant without buffering - -# Write lower byte of 'n' to 'f'. -append-byte: # f : (address stream), n : int -> <void> - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # . save registers - 51/push-ECX - 57/push-EDI - # EDI = f - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 7/r32/EDI 8/disp8 . # copy *(EBP+8) to EDI - # ECX = f->write - 8b/copy 0/mod/indirect 7/rm32/EDI . . . 1/r32/ECX . . # copy *EDI to ECX - # if (f->write >= f->length) abort - 3b/compare 1/mod/*+disp8 7/rm32/EDI . . . 1/r32/ECX 8/disp8 . # compare ECX with *(EDI+8) - 7d/jump-if-greater-or-equal $append-byte:abort/disp8 -$append-byte:to-stream: - # write to stream - # f->data[f->write] = LSB(n) - 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX - 8a/copy-byte 1/mod/*+disp8 5/rm32/EBP . . . 0/r32/AL 0xc/disp8 . # copy byte at *(EBP+12) to AL - 88/copy-byte 1/mod/*+disp8 4/rm32/sib 7/base/EDI 1/index/ECX . 0/r32/AL 0xc/disp8 . # copy AL to *(EDI+ECX+12) - # ++f->write - ff 0/subop/increment 0/mod/indirect 7/rm32/EDI . . . . . . # increment *EDI -$append-byte:end: - # . restore registers - 5f/pop-to-EDI - 59/pop-to-ECX - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -$append-byte:abort: - # . _write(2/stderr, error) - # . . push args - 68/push "append-byte: out of space\n"/imm32 - 68/push 2/imm32/stderr - # . . call - e8/call _write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . syscall(exit, 1) - bb/copy-to-EBX 1/imm32 - b8/copy-to-EAX 1/imm32/exit - cd/syscall 0x80/imm8 - # never gets here - -test-append-byte-single: - # - check that append-byte writes to first byte of 'file' - # setup - # . clear-stream(_test-stream) - # . . push args - 68/push _test-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # append-byte(_test-stream, 'A') - # . . push args - 68/push 0x41/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call append-byte/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check-stream-equal(_test-stream, "A", msg) - # . . push args - 68/push "F - test-append-byte-single"/imm32 - 68/push "A"/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call check-stream-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . end - c3/return - -# . . vim:nowrap:textwidth=0 diff --git a/subx/065write-buffered.subx b/subx/065write-buffered.subx deleted file mode 100644 index 88b14b2d..00000000 --- a/subx/065write-buffered.subx +++ /dev/null @@ -1,228 +0,0 @@ -# write-buffered: like 'write', but for a buffered-file - -== code -# instruction effective address register displacement immediate -# . op subop mod rm32 base index scale r32 -# . 1-3 bytes 3 bits 2 bits 3 bits 3 bits 3 bits 2 bits 2 bits 0/1/2/4 bytes 0/1/2/4 bytes - -write-buffered: # f : (address buffered-file), msg : (address array byte) -> <void> - # pseudocode: - # in = msg->data - # inend = &msg->data[msg->length] - # while (in < inend) - # if f->write >= f->length - # flush(f) - # clear-stream(f) - # c = *in - # f->data[f->write] = c - # ++f->write - # ++in - # - # registers: - # in: ESI - # inend: ECX - # f: EDI - # f->length: EDX - # f->write: EBX (cached; need to keep in sync) - # c: EAX - # - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # . save registers - 50/push-EAX - 51/push-ECX - 52/push-EDX - 53/push-EBX - 56/push-ESI - 57/push-EDI - # EAX = msg - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . 0/r32/EAX 0xc/disp8 . # copy *(EBP+12) to EAX - # in/ESI = msg->data - 8d/copy-address 1/mod/*+disp8 0/rm32/EAX . . . 6/r32/ESI 4/disp8 . # copy EAX+4 to ESI - # inend/ECX = &msg->data[msg->length] - 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX - 8d/copy-address 0/mod/indirect 4/rm32/sib 6/base/ESI 1/index/ECX . 1/r32/ECX . . # copy ESI+ECX to ECX - # EDI = f - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . 7/r32/EDI 8/disp8 . # copy *(EBP+8) to EDI - # EDX = f->length - 8b/copy 1/mod/*+disp8 7/rm32/EDI . . . 2/r32/EDX 0xc/disp8 . # copy *(EDI+12) to EDX - # EBX = f->write - 8b/copy 1/mod/*+disp8 7/rm32/EDI . . . 3/r32/EBX 4/disp8 . # copy *(EDI+4) to EBX -$write-buffered:loop: - # if (in >= inend) break - 39/compare 3/mod/direct 6/rm32/ESI . . . 1/r32/ECX . . # compare ESI with ECX - 73/jump-if-greater-or-equal-unsigned $write-buffered:loop-end/disp8 - # if (f->write >= f->length) flush and clear f's stream - 39/compare 3/mod/direct 3/rm32/EBX . . . 2/r32/EDX . . # compare EBX with EDX - 7c/jump-if-lesser $write-buffered:to-stream/disp8 - # . persist f->write - 89/copy 1/mod/*+disp8 7/rm32/EDI . . . 3/r32/EBX 4/disp8 . # copy EBX to *(EDI+4) - # . flush(f) - # . . push args - 57/push-EDI - # . . call - e8/call flush/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(stream = f+4) - # . . push args - 8d/copy-address 1/mod/*+disp8 7/rm32/EDI . . . 0/r32/EAX 4/disp8 . # copy EDI+4 to EAX - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . f->write must now be 0; update its cache at EBX - 31/xor 3/mod/direct 3/rm32/EBX . . . 3/r32/EBX . . # clear EBX -$write-buffered:to-stream: - # write to stream - # f->data[f->write] = *in - # . AL = *in - 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX - 8a/copy-byte 0/mod/indirect 6/rm32/ESI . . . 0/r32/AL . . # copy byte at *ESI to AL - # . f->data[f->write] = AL - 88/copy-byte 1/mod/*+disp8 4/rm32/sib 7/base/EDI 3/index/EBX . 0/r32/AL 0x10/disp8 . # copy AL to *(EDI+EBX+16) - # ++f->write - 43/increment-EBX - # ++in - 46/increment-ESI - eb/jump $write-buffered:loop/disp8 -$write-buffered:loop-end: - # persist necessary variables from registers - 89/copy 1/mod/*+disp8 7/rm32/EDI . . . 3/r32/EBX 4/disp8 . # copy EBX to *(EDI+4) -$write-buffered:end: - # . restore registers - 5f/pop-to-EDI - 5e/pop-to-ESI - 5b/pop-to-EBX - 5a/pop-to-EDX - 59/pop-to-ECX - 58/pop-to-EAX - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-write-buffered: - # - check that write-buffered writes to the file - # setup - # . clear-stream(_test-stream) - # . . push args - 68/push _test-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-buffered-file+4) - # . . push args - b8/copy-to-EAX _test-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # write-buffered(_test-buffered-file, "Abc") - # . . push args - 68/push "Abc"/imm32 - 68/push _test-buffered-file/imm32 - # . . call - e8/call write-buffered/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # flush(_test-buffered-file) - # . . push args - 68/push _test-buffered-file/imm32 - # . . call - e8/call flush/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # check-stream-equal(_test-stream, "Abc", msg) - # . . push args - 68/push "F - test-write-buffered-single"/imm32 - 68/push "Abc"/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call check-stream-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . end - c3/return - -test-write-buffered-with-intermediate-flush: - # - check that write-buffered flushes in the middle if its buffer fills up - # setup - # . clear-stream(_test-stream) - # . . push args - 68/push _test-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-buffered-file+4) - # . . push args - b8/copy-to-EAX _test-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # _test-stream can hold 8 bytes, but _test-buffered-file can hold only 6. - # Try to write 7 bytes. - # . write-buffered(_test-buffered-file, "Abcdefg") - # . . push args - 68/push "Abcdefg"/imm32 - 68/push _test-buffered-file/imm32 - # . . call - e8/call write-buffered/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # don't flush - # 6 bytes should still have gotten to _test-stream - # . check-ints-equal(*_test-stream->write, 6, msg) - # . . push args - 68/push "F - test-write-buffered-with-intermediate-flush: flushed data"/imm32 - 68/push 6/imm32 - # . . push *_test-stream->write - b8/copy-to-EAX _test-stream/imm32 - ff 6/subop/push 0/mod/indirect 0/rm32/EAX . . . . . . # push *EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # and 1 byte should still be in _test-buffered-file - # . check-ints-equal(*_test-buffered-file->write, 1, msg) - # . . push args - 68/push "F - test-write-buffered-with-intermediate-flush: unflushed bytes"/imm32 - 68/push 1/imm32 - # . . push *_test-buffered-file->write - b8/copy-to-EAX _test-buffered-file/imm32 - ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 4/disp8 . # push *(EAX+4) - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . end - c3/return - -== data - -# The buffered file for standard error. -Stderr: - # file descriptor or (address stream) - 2/imm32 # standard error - # current write index - 0/imm32 - # current read index - 0/imm32 - # length - 8/imm32 - # data - 00 00 00 00 00 00 00 00 # 8 bytes - -# TODO: 8 bytes is too small. We'll need to grow the buffer for efficiency. But -# I don't want to type in 1024 bytes here. - -# . . vim:nowrap:textwidth=0 diff --git a/subx/066print-int.subx b/subx/066print-int.subx deleted file mode 100644 index c5968837..00000000 --- a/subx/066print-int.subx +++ /dev/null @@ -1,389 +0,0 @@ -# Print the (hex) textual representation of numbers. - -== code -# instruction effective address register displacement immediate -# . op subop mod rm32 base index scale r32 -# . 1-3 bytes 3 bits 2 bits 3 bits 3 bits 3 bits 2 bits 2 bits 0/1/2/4 bytes 0/1/2/4 bytes - -to-hex-char: # in/EAX : nibble -> out/EAX : byte - # no error checking; accepts argument in EAX - # if (EAX <= 9) return EAX + '0' - 3d/compare-EAX-with 0x9/imm32/9 - 7f/jump-if-greater $to-hex-char:else/disp8 - 05/add-to-EAX 0x30/imm32/0 - c3/return -$to-hex-char:else: - # otherwise return EAX + 'a' - 10 - 05/add-to-EAX 0x57/imm32/a-10 - c3/return - -append-byte-hex: # f : (address stream), n : int -> <void> - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # . save registers - 50/push-EAX - # AL = convert upper nibble to hex - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 0/r32/EAX 0xc/disp8 . # copy *(EBP+12) to EAX - c1/shift 5/subop/logic-right 3/mod/direct 0/rm32/EAX . . . . . 4/imm8 # shift EAX right by 4 bits, while padding zeroes - 25/and-EAX 0xf/imm32 - # . AL = to-hex-char(AL) - e8/call to-hex-char/disp32 - # append-byte(f, AL) - # . . push args - 50/push-EAX - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - # . . call - e8/call append-byte/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # AL = convert lower nibble to hex - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 0/r32/EAX 0xc/disp8 . # copy *(EBP+12) to EAX - 25/and-EAX 0xf/imm32 - # . AL = to-hex-char(AL) - e8/call to-hex-char/disp32 - # append-byte(f, AL) - # . . push args - 50/push-EAX - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - # . . call - e8/call append-byte/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -$append-byte-hex:end: - # . restore registers - 58/pop-to-EAX - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-append-byte-hex: - # - check that append-byte-hex adds the hex textual representation - # setup - # . clear-stream(_test-stream) - # . . push args - 68/push _test-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # append-byte-hex(_test-stream, 0xa) # exercises digit, non-digit as well as leading zero - # . . push args - 68/push 0xa/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call append-byte-hex/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check-stream-equal(_test-stream, "0a", msg) - # . . push args - 68/push "F - test-append-byte-hex"/imm32 - 68/push "0a"/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call check-stream-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . end - c3/return - -# print the hex representation for the lowest byte of a number -print-byte-buffered: # f : (address buffered-file), n : int -> <void> - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # . save registers - 50/push-EAX - # AL = convert upper nibble to hex - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 0/r32/EAX 0xc/disp8 . # copy *(EBP+12) to EAX - c1/shift 5/subop/logic-right 3/mod/direct 0/rm32/EAX . . . . . 4/imm8 # shift EAX right by 4 bits, while padding zeroes - 25/and-EAX 0xf/imm32 - # . AL = to-hex-char(AL) - e8/call to-hex-char/disp32 - # write-byte-buffered(f, AL) - # . . push args - 50/push-EAX - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - # . . call - e8/call write-byte-buffered/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # AL = convert lower nibble to hex - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 0/r32/EAX 0xc/disp8 . # copy *(EBP+12) to EAX - 25/and-EAX 0xf/imm32 - # . AL = to-hex-char(AL) - e8/call to-hex-char/disp32 - # write-byte-buffered(f, AL) - # . . push args - 50/push-EAX - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - # . . call - e8/call write-byte-buffered/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -$print-byte-buffered:end: - # . restore registers - 58/pop-to-EAX - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-print-byte-buffered: - # - check that print-byte-buffered prints the hex textual representation - # setup - # . clear-stream(_test-stream) - # . . push args - 68/push _test-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-buffered-file+4) - # . . push args - b8/copy-to-EAX _test-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # print-byte-buffered(_test-buffered-file, 0xa) # exercises digit, non-digit as well as leading zero - # . . push args - 68/push 0xa/imm32 - 68/push _test-buffered-file/imm32 - # . . call - e8/call print-byte-buffered/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # flush(_test-buffered-file) - # . . push args - 68/push _test-buffered-file/imm32 - # . . call - e8/call flush/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # check-stream-equal(_test-stream, "0a", msg) - # . . push args - 68/push "F - test-print-byte-buffered"/imm32 - 68/push "0a"/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call check-stream-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . end - c3/return - -print-int32: # f : (address stream), n : int -> <void> - # pseudocode: - # write(f, "0x") - # ECX = 28 - # while true - # if (ECX < 0) break - # EAX = n >> ECX - # EAX = EAX & 0xf - # append-byte(f, AL) - # ECX -= 4 - # - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # . save registers - 50/push-EAX - 51/push-ECX - # ECX = 28 - b9/copy-to-ECX 0x1c/imm32 -$print-int32:print-hex-prefix: - # write(f, "0x") - # . . push args - 68/push "0x"/imm32 - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -$print-int32:loop: - # if (ECX < 0) break - 81 7/subop/compare 3/mod/direct 1/rm32/ECX . . . . . 0/imm32 # compare ECX - 7c/jump-if-lesser $print-int32:end/disp8 - # EAX = n >> ECX - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 0/r32/EAX 0xc/disp8 . # copy *(EBP+12) to EAX - d3/>>ECX 5/subop/pad-zeroes 3/mod/direct 0/rm32/EAX . . . . . . # shift EAX right by ECX bits, padding zeroes - # EAX = to-hex-char(AL) - 25/and-EAX 0xf/imm32 - e8/call to-hex-char/disp32 - # append-byte(f, AL) - # . . push args - 50/push-EAX - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - # . . call - e8/call append-byte/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # ECX -= 4 - 81 5/subop/subtract 3/mod/direct 1/rm32/ECX . . . . . 4/imm32 # subtract from ECX - eb/jump $print-int32:loop/disp8 -$print-int32:end: - # . restore registers - 59/pop-to-ECX - 58/pop-to-EAX - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-print-int32: - # - check that print-int32 prints the hex textual representation - # setup - # . clear-stream(_test-stream) - # . . push args - 68/push _test-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # print-int32(_test-stream, 0x8899aa) - # . . push args - 68/push 0x8899aa/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call print-int32/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check-stream-equal(_test-stream, "0x008899aa", msg) - # . . push args - 68/push "F - test-print-int32"/imm32 - 68/push "0x008899aa"/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call check-stream-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . end - c3/return - -print-int32-buffered: # f : (address buffered-file), n : int -> <void> - # pseudocode: - # write-buffered(f, "0x") - # ECX = 28 - # while true - # if (ECX < 0) break - # EAX = n >> ECX - # EAX = EAX & 0xf - # write-byte-buffered(f, AL) - # ECX -= 4 - # - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # . save registers - 50/push-EAX - 51/push-ECX - # ECX = 28 - b9/copy-to-ECX 0x1c/imm32 -$print-int32-buffered:print-hex-prefix: - # write-buffered(f, "0x") - # . . push args - 68/push "0x"/imm32 - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - # . . call - e8/call write-buffered/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -$print-int32-buffered:loop: - # if (ECX < 0) break - 81 7/subop/compare 3/mod/direct 1/rm32/ECX . . . . . 0/imm32 # compare ECX - 7c/jump-if-lesser $print-int32-buffered:end/disp8 - # EAX = n >> ECX - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 0/r32/EAX 0xc/disp8 . # copy *(EBP+12) to EAX - d3/>>ECX 5/subop/pad-zeroes 3/mod/direct 0/rm32/EAX . . . . . . # shift EAX right by ECX bits, padding zeroes - # EAX = to-hex-char(AL) - 25/and-EAX 0xf/imm32 - e8/call to-hex-char/disp32 - # write-byte-buffered(f, AL) - # . . push args - 50/push-EAX - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - # . . call - e8/call write-byte-buffered/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # ECX -= 4 - 81 5/subop/subtract 3/mod/direct 1/rm32/ECX . . . . . 4/imm32 # subtract from ECX - eb/jump $print-int32-buffered:loop/disp8 -$print-int32-buffered:end: - # . restore registers - 59/pop-to-ECX - 58/pop-to-EAX - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-print-int32-buffered: - # - check that print-int32-buffered prints the hex textual representation - # setup - # . clear-stream(_test-stream) - # . . push args - 68/push _test-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-buffered-file+4) - # . . push args - b8/copy-to-EAX _test-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # print-int32-buffered(_test-buffered-file, 0x8899aa) - # . . push args - 68/push 0x8899aa/imm32 - 68/push _test-buffered-file/imm32 - # . . call - e8/call print-int32-buffered/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # flush(_test-buffered-file) - # . . push args - 68/push _test-buffered-file/imm32 - # . . call - e8/call flush/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -#? # dump line {{{ -#? # . write-stream(2/stderr, line) -#? # . . push args -#? 68/push _test-stream/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write-stream/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write(2/stderr, "$\n") -#? # . . push args -#? 68/push "$\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # }}} - # check-stream-equal(_test-stream, "0x008899aa", msg) - # . . push args - 68/push "F - test-print-int32-buffered"/imm32 - 68/push "0x008899aa"/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call check-stream-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . end - c3/return - -# . . vim:nowrap:textwidth=0 diff --git a/subx/067parse-hex.subx b/subx/067parse-hex.subx deleted file mode 100644 index 45d168d9..00000000 --- a/subx/067parse-hex.subx +++ /dev/null @@ -1,874 +0,0 @@ -# some utilities for converting numbers from hex -# lowercase letters only for now - -== code -# instruction effective address register displacement immediate -# . op subop mod rm32 base index scale r32 -# . 1-3 bytes 3 bits 2 bits 3 bits 3 bits 3 bits 2 bits 2 bits 0/1/2/4 bytes 0/1/2/4 bytes - -is-hex-int?: # in : (address slice) -> EAX : boolean - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # . save registers - 51/push-ECX - 52/push-EDX - 53/push-EBX - # ECX = s - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 1/r32/ECX 8/disp8 . # copy *(EBP+8) to ECX - # EDX = s->end - 8b/copy 1/mod/*+disp8 1/rm32/ECX . . . 2/r32/EDX 4/disp8 . # copy *(ECX+4) to EDX - # curr/ECX = s->start - 8b/copy 0/mod/indirect 1/rm32/ECX . . . 1/r32/ECX . . # copy *ECX to ECX - # if s is empty return false - b8/copy-to-EAX 0/imm32/false - 39/compare 3/mod/direct 1/rm32/ECX . . . 2/r32/EDX . . # compare ECX with EDX - 73/jump-if-greater-or-equal-unsigned $is-hex-int?:end/disp8 - # skip past leading '-' - # . if (*curr == '-') ++curr - 31/xor 3/mod/direct 3/rm32/EBX . . . 3/r32/EBX . . # clear EBX - 8a/copy-byte 0/mod/indirect 1/rm32/ECX . . . 3/r32/BL . . # copy byte at *ECX to BL - 81 7/subop/compare 3/mod/direct 3/rm32/EBX . . . . . 0x2d/imm32/- # compare EBX - 75/jump-if-not-equal $is-hex-int?:initial-0/disp8 - # . ++curr - 41/increment-ECX - # skip past leading '0x' -$is-hex-int?:initial-0: - # . if (*curr != '0') jump to loop - 31/xor 3/mod/direct 3/rm32/EBX . . . 3/r32/EBX . . # clear EBX - 8a/copy-byte 0/mod/indirect 1/rm32/ECX . . . 3/r32/BL . . # copy byte at *ECX to BL - 81 7/subop/compare 3/mod/direct 3/rm32/EBX . . . . . 0x30/imm32/0 # compare EBX - 75/jump-if-not-equal $is-hex-int?:loop/disp8 - # . ++curr - 41/increment-ECX -$is-hex-int?:initial-0x: - # . if (curr >= in->end) return true - 39/compare 3/mod/direct 1/rm32/ECX . . . 2/r32/EDX . . # compare ECX with EDX - 73/jump-if-greater-or-equal-unsigned $is-hex-int?:true/disp8 - # . if (*curr != 'x') jump to loop # the previous '0' is still valid so doesn't need to be checked again - 31/xor 3/mod/direct 3/rm32/EBX . . . 3/r32/EBX . . # clear EBX - 8a/copy-byte 0/mod/indirect 1/rm32/ECX . . . 3/r32/BL . . # copy byte at *ECX to BL - 81 7/subop/compare 3/mod/direct 3/rm32/EBX . . . . . 0x78/imm32/x # compare EBX - 75/jump-if-not-equal $is-hex-int?:loop/disp8 - # . ++curr - 41/increment-ECX -$is-hex-int?:loop: - # if (curr >= in->end) return true - 39/compare 3/mod/direct 1/rm32/ECX . . . 2/r32/EDX . . # compare ECX with EDX - 73/jump-if-greater-or-equal-unsigned $is-hex-int?:true/disp8 - # EAX = is-hex-digit?(*curr) - # . . push args - 8a/copy-byte 0/mod/indirect 1/rm32/ECX . . . 0/r32/AL . . # copy byte at *ECX to AL - 50/push-EAX - # . . call - e8/call is-hex-digit?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # if (EAX == false) return false - 3d/compare-EAX-and 0/imm32 - 74/jump-if-equal $is-hex-int?:end/disp8 - # ++curr - 41/increment-ECX - # loop - eb/jump $is-hex-int?:loop/disp8 -$is-hex-int?:true: - # return true - b8/copy-to-EAX 1/imm32/true -$is-hex-int?:end: - # . restore registers - 5b/pop-to-EBX - 5a/pop-to-EDX - 59/pop-to-ECX - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-is-hex-int: - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # (EAX..ECX) = "34" - b8/copy-to-EAX "34"/imm32 - 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX - 05/add-to-EAX 4/imm32 - # var slice/ECX = {EAX, ECX} - 51/push-ECX - 50/push-EAX - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # EAX = is-hex-int?(slice) - # . . push args - 51/push-ECX - # . . call - e8/call is-hex-int?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # check-ints-equal(EAX, 1, msg) - # . . push args - 68/push "F - test-is-hex-int"/imm32 - 68/push 1/imm32/true - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-is-hex-int-handles-letters: - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # (EAX..ECX) = "34a" - b8/copy-to-EAX "34a"/imm32 - 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX - 05/add-to-EAX 4/imm32 - # var slice/ECX = {EAX, ECX} - 51/push-ECX - 50/push-EAX - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # EAX = is-hex-int?(slice) - # . . push args - 51/push-ECX - # . . call - e8/call is-hex-int?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # check-ints-equal(EAX, 1, msg) - # . . push args - 68/push "F - test-is-hex-int-handles-letters"/imm32 - 68/push 1/imm32/true - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-is-hex-int-with-trailing-char: - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # (EAX..ECX) = "34q" - b8/copy-to-EAX "34q"/imm32 - 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX - 05/add-to-EAX 4/imm32 - # var slice/ECX = {EAX, ECX} - 51/push-ECX - 50/push-EAX - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # EAX = is-hex-int?(slice) - # . . push args - 51/push-ECX - # . . call - e8/call is-hex-int?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # check-ints-equal(EAX, 0, msg) - # . . push args - 68/push "F - test-is-hex-int-with-trailing-char"/imm32 - 68/push 0/imm32/false - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-is-hex-int-with-leading-char: - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # (EAX..ECX) = "q34" - b8/copy-to-EAX "q34"/imm32 - 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX - 05/add-to-EAX 4/imm32 - # var slice/ECX = {EAX, ECX} - 51/push-ECX - 50/push-EAX - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # EAX = is-hex-int?(slice) - # . . push args - 51/push-ECX - # . . call - e8/call is-hex-int?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # check-ints-equal(EAX, 0, msg) - # . . push args - 68/push "F - test-is-hex-int-with-leading-char"/imm32 - 68/push 0/imm32/false - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-is-hex-int-empty: - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # var slice/ECX = "" - 68/push 0/imm32 - 68/push 0/imm32 - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # EAX = is-hex-int?(slice) - # . . push args - 51/push-ECX - # . . call - e8/call is-hex-int?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # check-ints-equal(EAX, 0, msg) - # . . push args - 68/push "F - test-is-hex-int-empty"/imm32 - 68/push 0/imm32/false - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-is-hex-int-handles-0x-prefix: - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # (EAX..ECX) = "0x3a" - b8/copy-to-EAX "0x3a"/imm32 - 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX - 05/add-to-EAX 4/imm32 - # var slice/ECX = {EAX, ECX} - 51/push-ECX - 50/push-EAX - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # EAX = is-hex-int?(slice) - # . . push args - 51/push-ECX - # . . call - e8/call is-hex-int?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # check-ints-equal(EAX, 1, msg) - # . . push args - 68/push "F - test-is-hex-int-handles-0x-prefix"/imm32 - 68/push 1/imm32/true - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-is-hex-int-handles-negative: - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # (EAX..ECX) = "-34a" - b8/copy-to-EAX "-34a"/imm32 - 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX - 05/add-to-EAX 4/imm32 - # var slice/ECX = {EAX, ECX} - 51/push-ECX - 50/push-EAX - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # EAX = is-hex-int?(slice) - # . . push args - 51/push-ECX - # . . call - e8/call is-hex-int?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # check-ints-equal(EAX, 1, msg) - # . . push args - 68/push "F - test-is-hex-int-handles-negative"/imm32 - 68/push 1/imm32/true - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-is-hex-int-handles-negative-0x-prefix: - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # (EAX..ECX) = "-0x3a" - b8/copy-to-EAX "-0x3a"/imm32 - 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX - 05/add-to-EAX 4/imm32 - # var slice/ECX = {EAX, ECX} - 51/push-ECX - 50/push-EAX - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # EAX = is-hex-int?(slice) - # . . push args - 51/push-ECX - # . . call - e8/call is-hex-int?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # check-ints-equal(EAX, 1, msg) - # . . push args - 68/push "F - test-is-hex-int-handles-negative-0x-prefix"/imm32 - 68/push 1/imm32/true - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -parse-hex-int: # in : (address slice) -> result/EAX - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # . save registers - 51/push-ECX - 52/push-EDX - 53/push-EBX - 56/push-ESI - # result/EBX = 0 - 31/xor 3/mod/direct 3/rm32/EBX . . . 3/r32/EBX . . # clear EBX - # ECX = s - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 1/r32/ECX 8/disp8 . # copy *(EBP+8) to ECX - # EDX = s->end - 8b/copy 1/mod/*+disp8 1/rm32/ECX . . . 2/r32/EDX 4/disp8 . # copy *(ECX+4) to EDX - # curr/ECX = s->start - 8b/copy 0/mod/indirect 1/rm32/ECX . . . 1/r32/ECX . . # copy *ECX to ECX - # negate?/ESI = false - 31/xor 3/mod/direct 6/rm32/ESI . . . 6/r32/ESI . . # clear ESI -$parse-hex-int:negative: - # . if (*curr == '-') negate = true - 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX - 8a/copy-byte 0/mod/indirect 1/rm32/ECX . . . 0/r32/AL . . # copy byte at *ECX to AL - 3d/compare-EAX-and 0x2d/imm32/- - 75/jump-if-not-equal $parse-hex-int:initial-0/disp8 - # . ++curr - 41/increment-ECX - # . negate = true - be/copy-to-ESI 1/imm32/true -$parse-hex-int:initial-0: - # skip past leading '0x' - # . if (*curr != '0') jump to loop - 8a/copy-byte 0/mod/indirect 1/rm32/ECX . . . 0/r32/AL . . # copy byte at *ECX to AL - 3d/compare-EAX-and 0x30/imm32/0 - 75/jump-if-not-equal $parse-hex-int:loop/disp8 - # . ++curr - 41/increment-ECX -$parse-hex-int:initial-0x: - # . if (curr >= in->end) return result - 39/compare 3/mod/direct 1/rm32/ECX . . . 2/r32/EDX . . # compare ECX with EDX - 73/jump-if-greater-or-equal-unsigned $parse-hex-int:end/disp8 - # . if (*curr != 'x') jump to loop # the previous '0' is still valid so doesn't need to be checked again - 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX - 8a/copy-byte 0/mod/indirect 1/rm32/ECX . . . 0/r32/AL . . # copy byte at *ECX to AL - 3d/compare-EAX-and 0x78/imm32/x - 75/jump-if-not-equal $parse-hex-int:loop/disp8 - # . ++curr - 41/increment-ECX -$parse-hex-int:loop: - # if (curr >= in->end) break - 39/compare 3/mod/direct 1/rm32/ECX . . . 2/r32/EDX . . # compare ECX with EDX - 73/jump-if-greater-or-equal-unsigned $parse-hex-int:negate/disp8 - # EAX = from-hex-char(*curr) - # . . copy arg to EAX - 8a/copy-byte 0/mod/indirect 1/rm32/ECX . . . 0/r32/AL . . # copy byte at *ECX to AL - # . . call - e8/call from-hex-char/disp32 - # result = result * 16 + EAX - c1/shift 4/subop/left 3/mod/direct 3/rm32/EBX . . . . . 4/imm8 # shift EBX left by 4 bits - 01/add 3/mod/direct 3/rm32/EBX . . . 0/r32/EAX . . # add EAX to EBX - # ++curr - 41/increment-ECX - # loop - eb/jump $parse-hex-int:loop/disp8 -$parse-hex-int:negate: - 81 7/subop/compare 3/mod/direct 6/rm32/ESI . . . . . 0/imm32 # compare ESI - 74/jump-if-equal $parse-hex-int:end/disp8 - f7 3/subop/negate 3/mod/direct 3/rm32/EBX . . . . . . # negate EBX -$parse-hex-int:end: - 89/copy 3/mod/direct 0/rm32/EAX . . . 3/r32/EBX . . # copy EBX to EAX - # . restore registers - 5e/pop-to-ESI - 5b/pop-to-EBX - 5a/pop-to-EDX - 59/pop-to-ECX - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-parse-hex-int-single-digit: - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # (EAX..ECX) = "a" - b8/copy-to-EAX "a"/imm32 - 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX - 05/add-to-EAX 4/imm32 - # var slice/ECX = {EAX, ECX} - 51/push-ECX - 50/push-EAX - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # EAX = parse-hex-int(slice) - # . . push args - 51/push-ECX - # . . call - e8/call parse-hex-int/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # check-ints-equal(EAX, 0xa, msg) - # . . push args - 68/push "F - test-parse-hex-int-single-digit"/imm32 - 68/push 0xa/imm32 - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-parse-hex-int-multi-digit: - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # (EAX..ECX) = "34a" - b8/copy-to-EAX "34a"/imm32 - 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX - 05/add-to-EAX 4/imm32 - # var slice/ECX = {EAX, ECX} - 51/push-ECX - 50/push-EAX - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # EAX = parse-hex-int(slice) - # . . push args - 51/push-ECX - # . . call - e8/call parse-hex-int/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # check-ints-equal(EAX, 0x34a, msg) - # . . push args - 68/push "F - test-parse-hex-int-multi-digit"/imm32 - 68/push 0x34a/imm32 - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-parse-hex-int-0x-prefix: - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # (EAX..ECX) = "0x34" - b8/copy-to-EAX "0x34"/imm32 - 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX - 05/add-to-EAX 4/imm32 - # var slice/ECX = {EAX, ECX} - 51/push-ECX - 50/push-EAX - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # EAX = parse-hex-int(slice) - # . . push args - 51/push-ECX - # . . call - e8/call parse-hex-int/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # check-ints-equal(EAX, 0x34a, msg) - # . . push args - 68/push "F - test-parse-hex-int-0x-prefix"/imm32 - 68/push 0x34/imm32 - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-parse-hex-int-zero: - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # (EAX..ECX) = "0" - b8/copy-to-EAX "0"/imm32 - 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX - 05/add-to-EAX 4/imm32 - # var slice/ECX = {EAX, ECX} - 51/push-ECX - 50/push-EAX - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # EAX = parse-hex-int(slice) - # . . push args - 51/push-ECX - # . . call - e8/call parse-hex-int/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # check-ints-equal(EAX, 0x34a, msg) - # . . push args - 68/push "F - test-parse-hex-int-zero"/imm32 - 68/push 0/imm32 - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-parse-hex-int-0-prefix: - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # (EAX..ECX) = "03" - b8/copy-to-EAX "03"/imm32 - 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX - 05/add-to-EAX 4/imm32 - # var slice/ECX = {EAX, ECX} - 51/push-ECX - 50/push-EAX - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # EAX = parse-hex-int(slice) - # . . push args - 51/push-ECX - # . . call - e8/call parse-hex-int/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # check-ints-equal(EAX, 0x3, msg) - # . . push args - 68/push "F - test-parse-hex-int-0-prefix"/imm32 - 68/push 0x3/imm32 - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-parse-hex-int-negative: - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # (EAX..ECX) = "-03" - b8/copy-to-EAX "-03"/imm32 - 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX - 05/add-to-EAX 4/imm32 - # var slice/ECX = {EAX, ECX} - 51/push-ECX - 50/push-EAX - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # EAX = parse-hex-int(slice) - # . . push args - 51/push-ECX - # . . call - e8/call parse-hex-int/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # check-ints-equal(EAX, 0xfffffffd, msg) - # . . push args - 68/push "F - test-parse-hex-int-negative"/imm32 - 68/push 0xfffffffd/imm32 - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -is-hex-digit?: # c : byte -> EAX : boolean - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # . save registers - 51/push-ECX - # ECX = c - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 1/r32/ECX 8/disp8 . # copy *(EBP+8) to ECX - # return false if c < '0' - b8/copy-to-EAX 0/imm32/false - 81 7/subop/compare 3/mod/direct 1/rm32/ECX . . . . . 0x30/imm32 # compare ECX - 7c/jump-if-lesser $is-hex-digit?:end/disp8 - # return false if c > 'f' - 81 7/subop/compare 3/mod/direct 1/rm32/ECX . . . . . 0x66/imm32 # compare ECX - 7f/jump-if-greater $is-hex-digit?:end/disp8 - # return true if c <= '9' - b8/copy-to-EAX 1/imm32/true - 81 7/subop/compare 3/mod/direct 1/rm32/ECX . . . . . 0x39/imm32 # compare ECX - 7e/jump-if-lesser-or-equal $is-hex-digit?:end/disp8 - # return true if c >= 'a' - 81 7/subop/compare 3/mod/direct 1/rm32/ECX . . . . . 0x61/imm32 # compare ECX - 7d/jump-if-greater-or-equal $is-hex-digit?:end/disp8 - # otherwise return false - b8/copy-to-EAX 0/imm32/false -$is-hex-digit?:end: - # . restore registers - 59/pop-to-ECX - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-hex-below-0: - # EAX = is-hex-digit?(0x2f) - # . . push args - 68/push 0x2f/imm32 - # . . call - e8/call is-hex-digit?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # check-ints-equal(EAX, 0, msg) - # . . push args - 68/push "F - test-hex-below-0"/imm32 - 68/push 0/imm32/false - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - c3/return - -test-hex-0-to-9: - # EAX = is-hex-digit?(0x30) - # . . push args - 68/push 0x30/imm32 - # . . call - e8/call is-hex-digit?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # check-ints-equal(EAX, 1, msg) - # . . push args - 68/push "F - test-hex-at-0"/imm32 - 68/push 1/imm32/true - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # EAX = is-hex-digit?(0x39) - # . . push args - 68/push 0x39/imm32 - # . . call - e8/call is-hex-digit?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # check-ints-equal(EAX, 1, msg) - # . . push args - 68/push "F - test-hex-at-9"/imm32 - 68/push 1/imm32/true - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - c3/return - -test-hex-above-9-to-a: - # EAX = is-hex-digit?(0x3a) - # . . push args - 68/push 0x3a/imm32 - # . . call - e8/call is-hex-digit?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # check-ints-equal(EAX, 0, msg) - # . . push args - 68/push "F - test-hex-above-9-to-a"/imm32 - 68/push 0/imm32/false - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - c3/return - -test-hex-a-to-f: - # EAX = is-hex-digit?(0x61) - # . . push args - 68/push 0x61/imm32 - # . . call - e8/call is-hex-digit?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # check-ints-equal(EAX, 1, msg) - # . . push args - 68/push "F - test-hex-at-a"/imm32 - 68/push 1/imm32/true - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # EAX = is-hex-digit?(0x66) - # . . push args - 68/push 0x66/imm32 - # . . call - e8/call is-hex-digit?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # check-ints-equal(EAX, 1, msg) - # . . push args - 68/push "F - test-hex-at-f"/imm32 - 68/push 1/imm32/true - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - c3/return - -test-hex-above-f: - # EAX = is-hex-digit?(0x67) - # . . push args - 68/push 0x67/imm32 - # . . call - e8/call is-hex-digit?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # check-ints-equal(EAX, 0, msg) - # . . push args - 68/push "F - test-hex-above-f"/imm32 - 68/push 0/imm32/false - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - c3/return - -from-hex-char: # in/EAX : byte -> out/EAX : nibble -$from-hex-char:check0: - # if (EAX < '0') goto abort - 3d/compare-EAX-with 0x30/imm32/0 - 7c/jump-if-lesser $from-hex-char:abort/disp8 -$from-hex-char:check1: - # if (EAX > 'f') goto abort - 3d/compare-EAX-with 0x66/imm32/f - 7f/jump-if-greater $from-hex-char:abort/disp8 -$from-hex-char:check2: - # if (EAX > '9') goto next check - 3d/compare-EAX-with 0x39/imm32/9 - 7f/jump-if-greater $from-hex-char:check3/disp8 -$from-hex-char:digit: - # return EAX - '0' - 2d/subtract-from-EAX 0x30/imm32/0 - c3/return -$from-hex-char:check3: - # if (EAX < 'a') goto abort - 3d/compare-EAX-with 0x61/imm32/a - 7c/jump-if-lesser $from-hex-char:abort/disp8 -$from-hex-char:letter: - # return EAX - ('a'-10) - 2d/subtract-from-EAX 0x57/imm32/a-10 - c3/return - -$from-hex-char:abort: - # . _write(2/stderr, error) - # . . push args - 68/push "invalid hex char: "/imm32 - 68/push 2/imm32/stderr - # . . call - e8/call _write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . clear-stream(Stderr+4) - # . . save EAX - 50/push-EAX - # . . push args - b8/copy-to-EAX Stderr/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . . restore EAX - 58/pop-to-EAX - # . print-int32-buffered(Stderr, EAX) - # . . push args - 50/push-EAX - 68/push Stderr/imm32 - # . . call - e8/call print-int32-buffered/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . flush(Stderr) - # . . push args - 68/push Stderr/imm32 - # . . call - e8/call flush/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . _write(2/stderr, "\n") - # . . push args - 68/push "\n"/imm32 - 68/push 2/imm32/stderr - # . . call - e8/call _write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . syscall(exit, 1) - bb/copy-to-EBX 1/imm32 - b8/copy-to-EAX 1/imm32/exit - cd/syscall 0x80/imm8 - # never gets here - -# . . vim:nowrap:textwidth=0 diff --git a/subx/068error-byte.subx b/subx/068error-byte.subx deleted file mode 100644 index 1f225d28..00000000 --- a/subx/068error-byte.subx +++ /dev/null @@ -1,91 +0,0 @@ -# Print an error message followed by the text representation of a byte. Then exit. - -== code -# instruction effective address register displacement immediate -# . op subop mod rm32 base index scale r32 -# . 1-3 bytes 3 bits 2 bits 3 bits 3 bits 3 bits 2 bits 2 bits 0/1/2/4 bytes 0/1/2/4 bytes - -#? Entry: # manual test -#? # . var ed/EAX : exit-descriptor -#? 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # subtract from ESP -#? 89/copy 3/mod/direct 0/rm32/EAX . . . 4/r32/ESP . . # copy ESP to EAX -#? # . configure ed to really exit() -#? # . . ed->target = 0 -#? c7 0/subop/copy 0/mod/direct 0/rm32/EAX . . . . . 0/imm32 # copy to *EAX -#? # . error-byte(ed, Stdout, msg, 34) -#? 68/push 0x34/imm32 -#? 68/push "abc"/imm32 -#? 68/push Stderr/imm32 -#? 50/push-EAX -#? e8/call error-byte/disp32 -#? # . syscall(exit, Num-test-failures) -#? 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 3/r32/EBX Num-test-failures/disp32 # copy *Num-test-failures to EBX -#? b8/copy-to-EAX 1/imm32/exit -#? cd/syscall 0x80/imm8 - -# write(out, "Error: "+msg+": "+byte) then stop(ed, 1) -error-byte: # ed : (address exit-descriptor), out : (address buffered-file), msg : (address array byte), n : byte -> <void> - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # write-buffered(out, "Error: ") - # . . push args - 68/push "Error: "/imm32 - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - # . . call - e8/call write-buffered/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # write-buffered(out, msg) - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x10/disp8 . # push *(EBP+16) - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - # . . call - e8/call write-buffered/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # write-buffered(out, ": ") - # . . push args - 68/push ": "/imm32 - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - # . . call - e8/call write-buffered/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # print-byte-buffered(out, byte) - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x14/disp8 . # push *(EBP+20) - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - # . . call - e8/call print-byte-buffered/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # write-buffered(out, Newline) - # . . push args - 68/push Newline/imm32 - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - # . . call - e8/call write-buffered/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . flush(out) - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - # . . call - e8/call flush/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # stop(ed, 1) - # . . push args - 68/push 1/imm32 - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - # . . call - e8/call stop/disp32 - # should never get past this point -$error-byte:dead-end: - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -# . . vim:nowrap:textwidth=0 diff --git a/subx/069allocate.subx b/subx/069allocate.subx deleted file mode 100644 index 6521b43a..00000000 --- a/subx/069allocate.subx +++ /dev/null @@ -1,204 +0,0 @@ -# Helper to dynamically allocate memory on the heap. -# -# We'd like to be able to write tests for functions that allocate memory, -# making assertions on the precise addresses used. To achieve this we'll pass -# in an *allocation descriptor* to allocate from. -# -# Allocation descriptors are also useful outside of tests. Assembly and machine -# code are of necessity unsafe languages, and one of the most insidious kinds -# of bugs unsafe languages expose us to are dangling pointers to memory that -# has been freed and potentially even reused for something totally different. -# To reduce the odds of such "use after free" errors, SubX programs tend to not -# reclaim and reuse dynamically allocated memory. (Running out of memory is far -# easier to debug.) Long-running programs that want to reuse memory are mostly -# on their own to be careful. However, they do get one bit of help: they can -# carve out chunks of memory and then allocate from them manually using this -# very same 'allocate' helper. They just need a new allocation descriptor for -# their book-keeping. - -== code -# instruction effective address register displacement immediate -# . op subop mod rm32 base index scale r32 -# . 1-3 bytes 3 bits 2 bits 3 bits 3 bits 3 bits 2 bits 2 bits 0/1/2/4 bytes 0/1/2/4 bytes - -# Claim the next 'n' bytes of memory starting at ad->curr and update ad->curr. -# Abort if there isn't enough memory in 'ad'. -allocate: # ad : (address allocation-descriptor), n : int -> address-or-null/EAX - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # . save registers - 51/push-ECX - 52/push-EDX - # ECX = ad - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 1/r32/ECX 8/disp8 . # copy *(EBP+8) to ECX - # save ad->curr - 8b/copy 0/mod/indirect 1/rm32/ECX . . . 0/r32/EAX . . # copy *ECX to EAX - # check if there's enough space - # . EDX = ad->curr + n - 89/copy 3/mod/direct 2/rm32/EDX . . . 0/r32/EAX . . # copy EAX to EDX - 03/add 1/mod/*+disp8 5/rm32/EBP . . . 2/r32/EDX 0xc/disp8 . # add *(EBP+12) to EDX - 3b/compare 1/mod/*+disp8 1/rm32/ECX . . . 2/r32/EDX 4/disp8 . # compare EDX with *(ECX+4) - 73/jump-if-greater-or-equal-signed $allocate:abort/disp8 -$allocate:commit: - # update ad->curr - 89/copy 0/mod/indirect 1/rm32/ECX . . . 2/r32/EDX . . # copy EDX to *ECX -$allocate:end: - # . restore registers - 5a/pop-to-EDX - 59/pop-to-ECX - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -$allocate:abort: - # . _write(2/stderr, error) - # . . push args - 68/push "allocate: failed to allocate\n"/imm32 - 68/push 2/imm32/stderr - # . . call - e8/call _write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . syscall(exit, 1) - bb/copy-to-EBX 1/imm32 - b8/copy-to-EAX 1/imm32/exit - cd/syscall 0x80/imm8 - # never gets here - -test-allocate-success: - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # var ad/ECX : (address allocation-descriptor) = {11, 15} - 68/push 0xf/imm32/limit - 68/push 0xb/imm32/curr - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # EAX = allocate(ad, 3) - # . . push args - 68/push 3/imm32 - 51/push-ECX - # . . call - e8/call allocate/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check-ints-equal(EAX, 11, msg) - # . . push args - 68/push "F - test-allocate-success: returns current pointer of allocation descriptor"/imm32 - 68/push 0xb/imm32 - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # check-ints-equal(ad->curr, 14, msg) - # . . push args - 68/push "F - test-allocate-success: updates allocation descriptor"/imm32 - 68/push 0xe/imm32 - ff 6/subop/push 0/mod/indirect 1/rm32/ECX . . . . . . # push *ECX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -_pending-test-allocate-failure: - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # var ad/ECX : (address allocation-descriptor) = {11, 15} - 68/push 0xf/imm32/limit - 68/push 0xb/imm32/curr - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # EAX = allocate(ad, 6) - # . . push args - 68/push 6/imm32 - 51/push-ECX - # . . call - e8/call allocate/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check-ints-equal(EAX, 0, msg) - # . . push args - 68/push "F - test-allocate-failure: returns null"/imm32 - 68/push 0/imm32 - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # no change to ad->curr - # . check-ints-equal(ad->curr, 11) - # . . push args - 68/push "F - test-allocate-failure: updates allocation descriptor"/imm32 - 68/push 0xb/imm32 - ff 6/subop/push 0/mod/indirect 1/rm32/ECX . . . . . . # push *ECX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -# helper: create a nested allocation descriptor (useful for tests) -allocate-region: # ad : (address allocation-descriptor), n : int -> new-ad : (address allocation-descriptor) - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # . save registers - 51/push-ECX - # EAX = allocate(ad, n) - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - # . . call - e8/call allocate/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # if (EAX == 0) abort - 3d/compare-EAX-and 0/imm32 - 74/jump-if-equal $allocate-region:abort/disp8 - # earmark 8 bytes at the start for a new allocation descriptor - # . *EAX = EAX + 8 - 89/copy 3/mod/direct 1/rm32/ECX . . . 0/r32/EAX . . # copy EAX to ECX - 81 0/subop/add 3/mod/direct 1/rm32/ECX . . . . . 8/imm32 # add to ECX - 89/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy ECX to *EAX - # . *(EAX+4) = EAX + n - 89/copy 3/mod/direct 1/rm32/ECX . . . 0/r32/EAX . . # copy EAX to ECX - 03/add 1/mod/*+disp8 5/rm32/EBP . . . 1/r32/ECX 0xc/disp8 . # add *(EBP+12) to ECX - 89/copy 1/mod/*+disp8 0/rm32/EAX . . . 1/r32/ECX 4/disp8 . # copy ECX to *(EAX+4) - # . restore registers - 59/pop-to-ECX - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -# We could create a more general '$abort' jump target, but then we'd need to do -# a conditional jump followed by loading the error message and an unconditional -# jump. Or we'd need to unconditionally load the error message before a -# conditional jump, even if it's unused the vast majority of the time. This way -# we bloat a potentially cold segment in RAM so we can abort with a single -# instruction. -$allocate-region:abort: - # . _write(2/stderr, error) - # . . push args - 68/push "allocate-region: failed to allocate\n"/imm32 - 68/push 2/imm32/stderr - # . . call - e8/call _write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . syscall(exit, 1) - bb/copy-to-EBX 1/imm32 - b8/copy-to-EAX 1/imm32/exit - cd/syscall 0x80/imm8 - # never gets here - -# . . vim:nowrap:textwidth=0 diff --git a/subx/070new-stream.subx b/subx/070new-stream.subx deleted file mode 100644 index b6934b1e..00000000 --- a/subx/070new-stream.subx +++ /dev/null @@ -1,118 +0,0 @@ -# Helper to allocate a stream on the heap. - -== code -# instruction effective address register displacement immediate -# . op subop mod rm32 base index scale r32 -# . 1-3 bytes 3 bits 2 bits 3 bits 3 bits 3 bits 2 bits 2 bits 0/1/2/4 bytes 0/1/2/4 bytes - -new-stream: # ad : (address allocation-descriptor), length : int, elemsize : int -> address/EAX - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # . save registers - 52/push-EDX - # n = elemsize * length + 12 (for read, write and length) - # . EAX = elemsize - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 0/r32/EAX 0x10/disp8 . # copy *(EBP+16) to EAX - # . EAX *= length - 31/xor 3/mod/direct 2/rm32/EDX . . . 2/r32/EDX . . # clear EDX - f7 4/subop/multiply 1/mod/*+disp8 5/rm32/EBP . . 0xc/disp8 . # multiply *(EBP+12) into EAX - # . if overflow abort - 81 7/subop/compare 3/mod/direct 2/rm32/EDX . . . . . 0/imm32 # compare EDX - 75/jump-if-not-equal $new-stream:abort/disp8 - # . EDX = elemsize*length - 89/copy 3/mod/direct 2/rm32/EDX . . . 0/r32/EAX . . # copy EAX to EDX - # . EAX += 12 - 05/add-to-EAX 0xc/imm32 - # allocate(ad, n) - # . . push args - 50/push-EAX - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - # . . call - e8/call allocate/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # EAX->length = elemsize*length - 89/copy 1/mod/*+disp8 0/rm32/EAX . . . 2/r32/EDX 8/disp8 . # copy EDX to *(EAX+8) - # clear-stream(EAX) - # . . push args - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -$new-stream:end: - # . restore registers - 5a/pop-to-EDX - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -$new-stream:abort: - # . _write(2/stderr, error) - # . . push args - 68/push "new-stream: size too large\n"/imm32 - 68/push 2/imm32/stderr - # . . call - e8/call _write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . syscall(exit, 1) - bb/copy-to-EBX 1/imm32 - b8/copy-to-EAX 1/imm32/exit - cd/syscall 0x80/imm8 - # never gets here - -test-new-stream: - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # var heap/ECX : (address allocation-descriptor) = {0, 0} - 68/push 0/imm32/limit - 68/push 0/imm32/curr - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # heap = new-segment(512) - # . . push args - 51/push-ECX - 68/push 0x200/imm32 - # . . call - e8/call new-segment/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # var start/EDX = ad->curr - 8b/copy 0/mod/indirect 1/rm32/ECX . . . 2/r32/EDX . . # copy *ECX to EDX - # EAX = new-stream(heap, 3, 2) - # . . push args - 68/push 2/imm32 - 68/push 3/imm32 - 51/push-ECX - # . . call - e8/call new-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # check-ints-equal(EAX, EDX, msg) - # . . push args - 68/push "F - test-new-stream: returns current pointer of allocation descriptor"/imm32 - 52/push-EDX - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # check-ints-equal(EAX->length, 6, msg) - # . . push args - 68/push "F - test-new-stream: sets length correctly"/imm32 - 68/push 6/imm32 - ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . . 8/disp8 # push *(EAX+8) - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # the rest is delegated to clear-stream() so we won't bother checking it - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -# . . vim:nowrap:textwidth=0 diff --git a/subx/071read-line.subx b/subx/071read-line.subx deleted file mode 100644 index 0add5568..00000000 --- a/subx/071read-line.subx +++ /dev/null @@ -1,391 +0,0 @@ -== code -# instruction effective address register displacement immediate -# . op subop mod rm32 base index scale r32 -# . 1-3 bytes 3 bits 2 bits 3 bits 3 bits 3 bits 2 bits 2 bits 0/1/2/4 bytes 0/1/2/4 bytes - -# read bytes from 'f' until (and including) a newline and store them into 's' -# 's' fails to grow if and only if no data found -# just abort if 's' is too small -read-line-buffered: # f : (address buffered-file), s : (address stream byte) -> <void> - # pseudocode: - # while true - # if (s->write >= s->length) abort - # if (f->read >= f->write) populate stream from file - # if (f->write == 0) break - # AL = f->data[f->read] - # s->data[s->write] = AL - # ++f->read - # ++s->write - # if (AL == '\n') break - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # . save registers - 50/push-EAX - 51/push-ECX - 52/push-EDX - 56/push-ESI - 57/push-EDI - # ESI = f - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 6/r32/ESI 8/disp8 . # copy *(EBP+8) to ESI - # ECX = f->read - 8b/copy 1/mod/*+disp8 6/rm32/ESI . . . 1/r32/ECX 8/disp8 . # copy *(ESI+8) to ECX - # EDI = s - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 7/r32/EDI 0xc/disp8 . # copy *(EBP+12) to EDI - # EDX = s->write - 8b/copy 0/mod/indirect 7/rm32/EDI . . . 2/r32/EDX . . # copy *EDI to EDX -$read-line-buffered:loop: - # if (s->write >= s->length) abort - 3b/compare 1/mod/*+disp8 7/rm32/EDI . . . 2/r32/EDX 8/disp8 . # compare EDX with *(EDI+8) - 7d/jump-if-greater-or-equal $read-line-buffered:abort/disp8 - # if (f->read >= f->write) populate stream from file - 3b/compare 1/mod/*+disp8 6/rm32/ESI . . . 1/r32/ECX 4/disp8 . # compare ECX with *(ESI+4) - 7c/jump-if-lesser $read-line-buffered:from-stream/disp8 - # . clear-stream(stream = f+4) - # . . push args - 8d/copy-address 1/mod/*+disp8 6/rm32/ESI . . . 0/r32/EAX 4/disp8 . # copy ESI+4 to EAX - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . f->read must now be 0; update its cache at ECX - 31/xor 3/mod/direct 1/rm32/ECX . . . 1/r32/ECX . . # clear ECX - # . EAX = read(f->fd, stream = f+4) - # . . push args - 50/push-EAX - ff 6/subop/push 0/mod/indirect 6/rm32/ESI . . . . . . # push *ESI - # . . call - e8/call read/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # if (f->write == 0) break - # since f->read was initially 0, EAX is the same as f->write - # . if (EAX == 0) return true - 3d/compare-EAX-and 0/imm32 - 74/jump-if-equal $read-line-buffered:end/disp8 -$read-line-buffered:from-stream: - # AL = f->data[f->read] - 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX - 8a/copy-byte 1/mod/*+disp8 4/rm32/sib 6/base/ESI 1/index/ECX . 0/r32/AL 0x10/disp8 . # copy byte at *(ESI+ECX+16) to AL - # s->data[s->write] = AL - 88/copy-byte 1/mod/*+disp8 4/rm32/sib 7/base/EDI 2/index/EDX . 0/r32/AL 0xc/disp8 . # copy AL to *(EDI+EDX+12) - # ++f->read - 41/increment-ECX - # ++s->write - 42/increment-EDX - # if (AL == '\n') return - 3d/compare-EAX-and 0xa/imm32 - 75/jump-if-not-equal $read-line-buffered:loop/disp8 -$read-line-buffered:end: - # save f->read - 89/copy 1/mod/*+disp8 6/rm32/ESI . . . 1/r32/ECX 8/disp8 . # copy ECX to *(ESI+8) - # save s->write - 89/copy 0/mod/indirect 7/rm32/EDI . . . 2/r32/EDX . . # copy EDX to *EDI - # . restore registers - 5f/pop-to-EDI - 5e/pop-to-ESI - 5a/pop-to-EDX - 59/pop-to-ECX - 58/pop-to-EAX - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -$read-line-buffered:abort: - # . _write(2/stderr, error) - # . . push args - 68/push "read-line-buffered: line too long\n"/imm32 - 68/push 2/imm32/stderr - # . . call - e8/call _write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . syscall(exit, 1) - bb/copy-to-EBX 1/imm32 - b8/copy-to-EAX 1/imm32/exit - cd/syscall 0x80/imm8 - # never gets here - -test-read-line-buffered: - # - check that read-line-buffered stops at a newline - # setup - # . clear-stream(_test-stream) - # . . push args - 68/push _test-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-buffered-file+4) - # . . push args - b8/copy-to-EAX _test-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-tmp-stream) - # . . push args - 68/push _test-tmp-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # write(_test-stream, "ab\ncd") - # . . push args - 68/push "ab\ncd"/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # read a line from _test-stream (buffered by _test-buffered-file) into _test-tmp-stream - # . EAX = read-line-buffered(_test-buffered-file, _test-tmp-stream) - # . . push args - 68/push _test-tmp-stream/imm32 - 68/push _test-buffered-file/imm32 - # . . call - e8/call read-line-buffered/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check-next-stream-line-equal(_test-tmp-stream, "ab", msg) - # . . push args - 68/push "F - test-read-line-buffered"/imm32 - 68/push "ab"/imm32 - 68/push _test-tmp-stream/imm32 - # . . call - e8/call check-next-stream-line-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # end - c3/return - -test-read-line-buffered-reads-final-line-until-Eof: - # setup - # . clear-stream(_test-stream) - # . . push args - 68/push _test-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-buffered-file+4) - # . . push args - b8/copy-to-EAX _test-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-tmp-stream) - # . . push args - 68/push _test-tmp-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # write(_test-stream, "cd") - # . . push args - 68/push "cd"/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # read a line from _test-stream (buffered by _test-buffered-file) into _test-tmp-stream - # . EAX = read-line-buffered(_test-buffered-file, _test-tmp-stream) - # . . push args - 68/push _test-tmp-stream/imm32 - 68/push _test-buffered-file/imm32 - # . . call - e8/call read-line-buffered/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check-stream-equal(_test-tmp-stream, "cd", msg) - # . . push args - 68/push "F - test-read-line-buffered-reads-final-line-until-Eof"/imm32 - 68/push "cd"/imm32 - 68/push _test-tmp-stream/imm32 - # . . call - e8/call check-stream-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # end - c3/return - -# read bytes from 'f' until (and including) a newline and store them into 's' -# 's' fails to grow if and only if no data found -# just abort if 's' is too small -read-line: # f : (address stream), s : (address stream byte) -> <void> - # pseudocode: - # while true - # if (s->write >= s->length) abort - # if (f->read >= f->write) break - # AL = f->data[f->read] - # s->data[s->write] = AL - # ++f->read - # ++s->write - # if (AL == '\n') break - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # . save registers - 50/push-EAX - 51/push-ECX - 52/push-EDX - 56/push-ESI - 57/push-EDI - # ESI = f - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 6/r32/ESI 8/disp8 . # copy *(EBP+8) to ESI - # ECX = f->read - 8b/copy 1/mod/*+disp8 6/rm32/ESI . . . 1/r32/ECX 4/disp8 . # copy *(ESI+4) to ECX - # EDI = s - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 7/r32/EDI 0xc/disp8 . # copy *(EBP+12) to EDI - # EDX = s->write - 8b/copy 0/mod/indirect 7/rm32/EDI . . . 2/r32/EDX . . # copy *EDI to EDX -$read-line:loop: - # if (s->write >= s->length) abort - 3b/compare 1/mod/*+disp8 7/rm32/EDI . . . 2/r32/EDX 8/disp8 . # compare EDX with *(EDI+8) - 0f 8d/jump-if-greater-or-equal $read-line:abort/disp32 - # if (f->read >= f->write) break - 3b/compare 0/mod/indirect 6/rm32/ESI . . . 1/r32/ECX . . # compare ECX with *ESI - 7d/jump-if-greater-or-equal $read-line:end/disp8 - # AL = f->data[f->read] - 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX - 8a/copy-byte 1/mod/*+disp8 4/rm32/sib 6/base/ESI 1/index/ECX . 0/r32/AL 0xc/disp8 . # copy byte at *(ESI+ECX+12) to AL - # s->data[s->write] = AL - 88/copy-byte 1/mod/*+disp8 4/rm32/sib 7/base/EDI 2/index/EDX . 0/r32/AL 0xc/disp8 . # copy AL to *(EDI+EDX+12) - # ++f->read - 41/increment-ECX - # ++s->write - 42/increment-EDX - # if (AL == '\n') return - 3d/compare-EAX-and 0xa/imm32 - 0f 85/jump-if-not-equal $read-line:loop/disp32 -$read-line:end: - # save f->read - 89/copy 1/mod/*+disp8 6/rm32/ESI . . . 1/r32/ECX 4/disp8 . # copy ECX to *(ESI+4) - # save s->write - 89/copy 0/mod/indirect 7/rm32/EDI . . . 2/r32/EDX . . # copy EDX to *EDI - # . restore registers - 5f/pop-to-EDI - 5e/pop-to-ESI - 5a/pop-to-EDX - 59/pop-to-ECX - 58/pop-to-EAX - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -$read-line:abort: - # . _write(2/stderr, error) - # . . push args - 68/push "read-line: line too long\n"/imm32 - 68/push 2/imm32/stderr - # . . call - e8/call _write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . syscall(exit, 1) - bb/copy-to-EBX 1/imm32 - b8/copy-to-EAX 1/imm32/exit - cd/syscall 0x80/imm8 - # never gets here - -test-read-line: - # - check that read-line stops at a newline - # setup - # . clear-stream(_test-stream) - # . . push args - 68/push _test-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-tmp-stream) - # . . push args - 68/push _test-tmp-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # write(_test-stream, "ab\ncd") - # . . push args - 68/push "ab\ncd"/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # read a line from _test-stream into _test-tmp-stream - # . EAX = read-line(_test-stream, _test-tmp-stream) - # . . push args - 68/push _test-tmp-stream/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call read-line/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check-next-stream-line-equal(_test-tmp-stream, "ab", msg) - # . . push args - 68/push "F - test-read-line"/imm32 - 68/push "ab"/imm32 - 68/push _test-tmp-stream/imm32 - # . . call - e8/call check-next-stream-line-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # end - c3/return - -test-read-line-reads-final-line-until-Eof: - # setup - # . clear-stream(_test-stream) - # . . push args - 68/push _test-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-tmp-stream) - # . . push args - 68/push _test-tmp-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # write(_test-stream, "cd") - # . . push args - 68/push "cd"/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # read a line from _test-stream into _test-tmp-stream - # . EAX = read-line(_test-stream, _test-tmp-stream) - # . . push args - 68/push _test-tmp-stream/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call read-line/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check-stream-equal(_test-tmp-stream, "cd", msg) - # . . push args - 68/push "F - test-read-line-reads-final-line-until-Eof"/imm32 - 68/push "cd"/imm32 - 68/push _test-tmp-stream/imm32 - # . . call - e8/call check-stream-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # end - c3/return - -# . . vim:nowrap:textwidth=0 diff --git a/subx/072slice.subx b/subx/072slice.subx deleted file mode 100644 index e568fbae..00000000 --- a/subx/072slice.subx +++ /dev/null @@ -1,1173 +0,0 @@ -# new data structure: a slice is an open interval of addresses [start, end) -# that includes 'start' but not 'end' - -== code -# instruction effective address register displacement immediate -# . op subop mod rm32 base index scale r32 -# . 1-3 bytes 3 bits 2 bits 3 bits 3 bits 3 bits 2 bits 2 bits 0/1/2/4 bytes 0/1/2/4 bytes - -slice-empty?: # s : (address slice) -> EAX : boolean - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # . save registers - 51/push-ECX - # ECX = s - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 1/r32/ECX 8/disp8 . # copy *(EBP+8) to ECX - # if (s->start == s->end) return true - # . EAX = s->start - 8b/copy 0/mod/indirect 1/rm32/ECX . . . 0/r32/EAX . . # copy *ECX to EAX - # . compare EAX and s->end - 39/compare 1/mod/*+disp8 1/rm32/ECX . . . 0/r32/EAX 4/disp8 . # compare EAX and *(ECX+4) - b8/copy-to-EAX 1/imm32/true - 74/jump-if-equal $slice-empty?:end/disp8 - b8/copy-to-EAX 0/imm32/false -$slice-empty?:end: - # . restore registers - 59/pop-to-ECX - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-slice-empty-true: - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # var slice/ECX = {34, 34} - 68/push 34/imm32/end - 68/push 34/imm32/start - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # slice-empty?(slice) - # . . push args - 51/push-ECX - # . . call - e8/call slice-empty?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # check-ints-equal(EAX, 1, msg) - # . . push args - 68/push "F - test-slice-empty-true"/imm32 - 68/push 1/imm32 - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-slice-empty-false: - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # var slice/ECX = {34, 23} - 68/push 23/imm32/end - 68/push 34/imm32/start - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # slice-empty?(slice) - # . . push args - 51/push-ECX - # . . call - e8/call slice-empty?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # check-ints-equal(EAX, 0, msg) - # . . push args - 68/push "F - test-slice-empty-false"/imm32 - 68/push 0/imm32 - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -slice-equal?: # s : (address slice), p : (address string) -> EAX : boolean - # pseudocode: - # if (p == 0) return (s == 0) - # currs = s->start - # maxs = s->end - # if (maxs - currs != p->length) return false - # currp = p->data - # while currs < maxs - # if (*currs != *currp) return false - # ++currs - # ++currp - # return true - # - # registers: - # currs: EDX - # maxs: ESI - # currp: EBX - # *currs: EAX - # *currp: ECX - # - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # . save registers - 51/push-ECX - 52/push-EDX - 53/push-EBX - 56/push-ESI - # ESI = s - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 6/r32/ESI 8/disp8 . # copy *(EBP+8) to ESI - # currs/EDX = s->start - 8b/copy 0/mod/indirect 6/rm32/ESI . . . 2/r32/EDX . . # copy *ESI to EDX - # maxs/ESI = s->end - 8b/copy 1/mod/*+disp8 6/rm32/ESI . . . 6/r32/ESI 4/disp8 . # copy *(ESI+4) to ESI - # EAX = maxs - currs - 89/copy 3/mod/direct 0/rm32/EAX . . . 6/r32/ESI . . # copy ESI to EAX - 29/subtract 3/mod/direct 0/rm32/EAX . . . 2/r32/EDX . . # subtract EDX from EAX - # EBX = p - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 3/r32/EBX 0xc/disp8 . # copy *(EBP+12) to EBX - # if (p != 0) goto next check - 81 7/subop/compare 3/mod/direct 3/rm32/EBX . . . . . 0/imm32 # compare EBX - 75/jump-if-not-equal $slice-equal?:nonnull-string/disp8 -$slice-equal?:null-string: - # return s->start == s->end - 3d/compare-EAX-and 0/imm32 - 74/jump-if-equal $slice-equal?:true/disp8 - eb/jump $slice-equal?:false/disp8 -$slice-equal?:nonnull-string: - # if (EAX != p->length) return false - 39/compare 0/mod/indirect 3/rm32/EBX . . . 0/r32/EAX . . # compare *EBX and EAX - 75/jump-if-not-equal $slice-equal?:false/disp8 - # currp/EBX = p->data - 81 0/subop/add 3/mod/direct 3/rm32/EBX . . . . . 4/imm32 # add to EBX - # EAX = ECX = 0 - 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX - 31/xor 3/mod/direct 1/rm32/ECX . . . 1/r32/ECX . . # clear ECX -$slice-equal?:loop: - # if (currs >= maxs) return true - 39/compare 3/mod/direct 2/rm32/EDX . . . 6/r32/ESI . . # compare EDX with ESI - 73/jump-if-greater-or-equal-unsigned $slice-equal?:true/disp8 - # AL = *currp - 8a/copy-byte 0/mod/indirect 3/rm32/EBX . . . 0/r32/AL . . # copy byte at *EBX to AL - # CL = *currs - 8a/copy-byte 0/mod/indirect 2/rm32/EDX . . . 1/r32/CL . . # copy byte at *EDX to CL - # if (EAX != ECX) return false - 39/compare 3/mod/direct 0/rm32/EAX . . . 1/r32/ECX . . # compare EAX and ECX - 75/jump-if-not-equal $slice-equal?:false/disp8 - # ++currp - 43/increment-EBX - # ++currs - 42/increment-EDX - eb/jump $slice-equal?:loop/disp8 -$slice-equal?:false: - b8/copy-to-EAX 0/imm32 - eb/jump $slice-equal?:end/disp8 -$slice-equal?:true: - b8/copy-to-EAX 1/imm32 -$slice-equal?:end: - # . restore registers - 5e/pop-to-ESI - 5b/pop-to-EBX - 5a/pop-to-EDX - 59/pop-to-ECX - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-slice-equal: - # - slice-equal?(slice("Abc"), "Abc") == 1 - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # (EAX..ECX) = "Abc" - b8/copy-to-EAX "Abc"/imm32 - 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX - 05/add-to-EAX 4/imm32 - # var slice/ECX = {EAX, ECX} - 51/push-ECX - 50/push-EAX - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # EAX = slice-equal?(ECX, "Abc") - # . . push args - 68/push "Abc"/imm32 - 51/push-ECX - # . . call - e8/call slice-equal?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check-ints-equal(EAX, 1, msg) - # . . push args - 68/push "F - test-slice-equal"/imm32 - 68/push 1/imm32 - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-slice-equal-false: - # - slice-equal?(slice("bcd"), "Abc") == 0 - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # (EAX..ECX) = "bcd" - b8/copy-to-EAX "bcd"/imm32 - 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX - 05/add-to-EAX 4/imm32 - # var slice/ECX = {EAX, ECX} - 51/push-ECX - 50/push-EAX - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # EAX = slice-equal?(ECX, "Abc") - # . . push args - 68/push "Abc"/imm32 - 51/push-ECX - # . . call - e8/call slice-equal?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check-ints-equal(EAX, 0, msg) - # . . push args - 68/push "F - test-slice-equal-false"/imm32 - 68/push 0/imm32 - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-slice-equal-too-long: - # - slice-equal?(slice("Abcd"), "Abc") == 0 - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # (EAX..ECX) = "Abcd" - b8/copy-to-EAX "Abcd"/imm32 - 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX - 05/add-to-EAX 4/imm32 - # var slice/ECX = {EAX, ECX} - 51/push-ECX - 50/push-EAX - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # EAX = slice-equal?(ECX, "Abc") - # . . push args - 68/push "Abc"/imm32 - 51/push-ECX - # . . call - e8/call slice-equal?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check-ints-equal(EAX, 0, msg) - # . . push args - 68/push "F - test-slice-equal-too-long"/imm32 - 68/push 0/imm32 - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-slice-equal-too-short: - # - slice-equal?(slice("A"), "Abc") == 0 - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # (EAX..ECX) = "A" - b8/copy-to-EAX "A"/imm32 - 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX - 05/add-to-EAX 4/imm32 - # var slice/ECX = {EAX, ECX} - 51/push-ECX - 50/push-EAX - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # EAX = slice-equal?(ECX, "Abc") - # . . push args - 68/push "Abc"/imm32 - 51/push-ECX - # . . call - e8/call slice-equal?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check-ints-equal(EAX, 0, msg) - # . . push args - 68/push "F - test-slice-equal-too-short"/imm32 - 68/push 0/imm32 - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-slice-equal-empty: - # - slice-equal?(slice(""), "Abc") == 0 - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # var slice/ECX - 68/push 0/imm32/end - 68/push 0/imm32/start - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # EAX = slice-equal?(ECX, "Abc") - # . . push args - 68/push "Abc"/imm32 - 51/push-ECX - # . . call - e8/call slice-equal?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check-ints-equal(EAX, 0, msg) - # . . push args - 68/push "F - test-slice-equal-empty"/imm32 - 68/push 0/imm32 - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-slice-equal-with-empty: - # - slice-equal?(slice("Ab"), "") == 0 - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # (EAX..ECX) = "Ab" - b8/copy-to-EAX "Ab"/imm32 - 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX - 05/add-to-EAX 4/imm32 - # var slice/ECX = {EAX, ECX} - 51/push-ECX - 50/push-EAX - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # EAX = slice-equal?(ECX, "") - # . . push args - 68/push ""/imm32 - 51/push-ECX - # . . call - e8/call slice-equal?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check-ints-equal(EAX, 0, msg) - # . . push args - 68/push "F - test-slice-equal-with-empty"/imm32 - 68/push 0/imm32 - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-slice-equal-empty-with-empty: - # - slice-equal?(slice(""), "") == 1 - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # var slice/ECX - 68/push 0/imm32/end - 68/push 0/imm32/start - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # EAX = slice-equal?(ECX, "") - # . . push args - 68/push ""/imm32 - 51/push-ECX - # . . call - e8/call slice-equal?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check-ints-equal(EAX, 1, msg) - # . . push args - 68/push "F - test-slice-equal-empty-with-empty"/imm32 - 68/push 1/imm32 - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-slice-equal-with-null: - # - slice-equal?(slice("Ab"), null) == 0 - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # (EAX..ECX) = "Ab" - b8/copy-to-EAX "Ab"/imm32 - 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX - 05/add-to-EAX 4/imm32 - # var slice/ECX = {EAX, ECX} - 51/push-ECX - 50/push-EAX - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # EAX = slice-equal?(ECX, 0) - # . . push args - 68/push 0/imm32 - 51/push-ECX - # . . call - e8/call slice-equal?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check-ints-equal(EAX, 0, msg) - # . . push args - 68/push "F - test-slice-equal-with-null"/imm32 - 68/push 0/imm32 - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -slice-starts-with?: # s : (address slice), head : (address string) -> EAX : boolean - # pseudocode - # lenh = head->length - # if (lenh > s->end - s->start) return false - # i = 0 - # currs = s->start - # currp = head->data - # while i < lenh - # if (*currs != *currh) return false - # ++i - # ++currs - # ++currh - # return true - # - # registers: - # currs: ESI - # currh: EDI - # *currs: EAX - # *currh: EBX - # i: ECX - # lenh: EDX - # - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # . save registers - 51/push-ECX - 52/push-EDX - 53/push-EBX - 56/push-ESI - 57/push-EDI - # ESI = s - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 6/r32/ESI 8/disp8 . # copy *(EBP+8) to ESI - # ECX = s->end - s->start - 8b/copy 1/mod/*+disp8 6/rm32/ESI . . . 1/r32/ECX 4/disp8 . # copy *(ESI+4) to ECX - 2b/subtract 0/mod/indirect 6/rm32/ESI . . . 1/r32/ECX . . # subtract *ESI from ECX - # EDI = head - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 7/r32/EDI 0xc/disp8 . # copy *(EBP+12) to EDI - # lenh/EDX = head->length - 8b/copy 0/mod/indirect 7/rm32/EDI . . . 2/r32/EDX . . # copy *EDI to EDX - # if (lenh > s->end - s->start) return false - 39/compare 3/mod/direct 2/rm32/EDX . . . 1/r32/ECX . . # compare EDX with ECX - 7f/jump-if-greater $slice-starts-with?:false/disp8 - # currs/ESI = s->start - 8b/subtract 0/mod/indirect 6/rm32/ESI . . . 6/r32/ESI . . # copy *ESI to ESI - # currh/EDI = head->data - 81 0/subop/add 3/mod/direct 7/rm32/EDI . . . . . 4/imm32 # add to EDI - # i/ECX = 0 - 31/xor 3/mod/direct 1/rm32/ECX . . . 1/r32/ECX . . # clear ECX - # EAX = EBX = 0 - 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX - 31/xor 3/mod/direct 3/rm32/EBX . . . 3/r32/EBX . . # clear EBX -$slice-starts-with?:loop: - # if (i >= lenh) return true - 39/compare 3/mod/direct 1/rm32/ECX . . . 2/r32/EDX . . # compare ECX with EDX - 7d/jump-if-greater-or-equal $slice-starts-with?:true/disp8 - # AL = *currs - 8a/copy-byte 0/mod/indirect 6/rm32/ESI . . . 0/r32/AL . . # copy byte at *ESI to AL - # BL = *currh - 8a/copy-byte 0/mod/indirect 7/rm32/EDI . . . 3/r32/BL . . # copy byte at *EDI to BL - # if (*currs != *currh) return false - 39/compare 3/mod/direct 0/rm32/EAX . . . 3/r32/EBX . . # compare EAX and EBX - 75/jump-if-not-equal $slice-starts-with?:false/disp8 - # ++i - 41/increment-ECX - # ++currs - 46/increment-ESI - # ++currh - 47/increment-EDI - eb/jump $slice-starts-with?:loop/disp8 -$slice-starts-with?:true: - b8/copy-to-EAX 1/imm32 - eb/jump $slice-starts-with?:end/disp8 -$slice-starts-with?:false: - b8/copy-to-EAX 0/imm32 -$slice-starts-with?:end: - # . restore registers - 5f/pop-to-EDI - 5e/pop-to-ESI - 5b/pop-to-EBX - 5a/pop-to-EDX - 59/pop-to-ECX - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-slice-starts-with-single-character: - # - slice-starts-with?(slice("Abc"), "A") == 1 - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # (EAX..ECX) = "Abc" - b8/copy-to-EAX "Abc"/imm32 - 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX - 05/add-to-EAX 4/imm32 - # var slice/ECX = {EAX, ECX} - 51/push-ECX - 50/push-EAX - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # EAX = slice-starts-with?(ECX, "A") - # . . push args - 68/push "A"/imm32 - 51/push-ECX - # . . call - e8/call slice-starts-with?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check-ints-equal(EAX, 1, msg) - # . . push args - 68/push "F - test-slice-starts-with-single-character"/imm32 - 68/push 1/imm32 - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-slice-starts-with-empty-string: - # - slice-starts-with?(slice("Abc"), "") == 1 - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # (EAX..ECX) = "Abc" - b8/copy-to-EAX "Abc"/imm32 - 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX - 05/add-to-EAX 4/imm32 - # var slice/ECX = {EAX, ECX} - 51/push-ECX - 50/push-EAX - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # EAX = slice-starts-with?(ECX, "") - # . . push args - 68/push ""/imm32 - 51/push-ECX - # . . call - e8/call slice-starts-with?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check-ints-equal(EAX, 1, msg) - # . . push args - 68/push "F - test-slice-starts-with-empty-string"/imm32 - 68/push 1/imm32 - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-slice-starts-with-multiple-characters: - # - slice-starts-with?(slice("Abc"), "Ab") == 1 - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # (EAX..ECX) = "Abc" - b8/copy-to-EAX "Abc"/imm32 - 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX - 05/add-to-EAX 4/imm32 - # var slice/ECX = {EAX, ECX} - 51/push-ECX - 50/push-EAX - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # EAX = slice-starts-with?(ECX, "Ab") - # . . push args - 68/push "Ab"/imm32 - 51/push-ECX - # . . call - e8/call slice-starts-with?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check-ints-equal(EAX, 1, msg) - # . . push args - 68/push "F - test-slice-starts-with-multiple-characters"/imm32 - 68/push 1/imm32 - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-slice-starts-with-entire-string: - # - slice-starts-with?(slice("Abc"), "Abc") == 1 - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # (EAX..ECX) = "Abc" - b8/copy-to-EAX "Abc"/imm32 - 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX - 05/add-to-EAX 4/imm32 - # var slice/ECX = {EAX, ECX} - 51/push-ECX - 50/push-EAX - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # EAX = slice-starts-with?(ECX, "Abc") - # . . push args - 68/push "Abc"/imm32 - 51/push-ECX - # . . call - e8/call slice-starts-with?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check-ints-equal(EAX, 1, msg) - # . . push args - 68/push "F - test-slice-starts-with-entire-string"/imm32 - 68/push 1/imm32 - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-slice-starts-with-fails: - # - slice-starts-with?(slice("Abc"), "Abd") == 1 - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # (EAX..ECX) = "Abc" - b8/copy-to-EAX "Abc"/imm32 - 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX - 05/add-to-EAX 4/imm32 - # var slice/ECX = {EAX, ECX} - 51/push-ECX - 50/push-EAX - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # EAX = slice-starts-with?(ECX, "Abd") - # . . push args - 68/push "Abd"/imm32 - 51/push-ECX - # . . call - e8/call slice-starts-with?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check-ints-equal(EAX, 0, msg) - # . . push args - 68/push "F - test-slice-starts-with-fails"/imm32 - 68/push 0/imm32 - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-slice-starts-with-fails-2: - # - slice-starts-with?(slice("Abc"), "Ac") == 1 - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # (EAX..ECX) = "Abc" - b8/copy-to-EAX "Abc"/imm32 - 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX - 05/add-to-EAX 4/imm32 - # var slice/ECX = {EAX, ECX} - 51/push-ECX - 50/push-EAX - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # EAX = slice-starts-with?(ECX, "Ac") - # . . push args - 68/push "Ac"/imm32 - 51/push-ECX - # . . call - e8/call slice-starts-with?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check-ints-equal(EAX, 0, msg) - # . . push args - 68/push "F - test-slice-starts-with-fails-2"/imm32 - 68/push 0/imm32 - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -# write a slice to a stream -# abort if the stream doesn't have enough space -write-slice: # out : (address stream), s : (address slice) - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # . save registers - 50/push-EAX - 51/push-ECX - 52/push-EDX - 53/push-EBX - 56/push-ESI - 57/push-EDI - # ESI = s - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 6/r32/ESI 0xc/disp8 . # copy *(EBP+12) to ESI - # curr/ECX = s->start - 8b/copy 0/mod/indirect 6/rm32/ESI . . . 1/r32/ECX . . # copy *ESI to ECX - # max/ESI = s->end - 8b/copy 1/mod/*+disp8 6/rm32/ESI . . . 6/r32/ESI 4/disp8 . # copy *(ESI+4) to ESI - # EDI = out - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . 7/r32/EDI 8/disp8 . # copy *(EBP+8) to EDI - # EDX = out->length - 8b/copy 1/mod/*+disp8 7/rm32/EDI . . . 2/r32/EDX 8/disp8 . # copy *(EDI+8) to EDX - # EBX = out->write - 8b/copy 0/mod/indirect 7/rm32/EDI . . . 3/r32/EBX . . # copy *EDI to EBX -$write-slice:loop: - # if (curr >= max) break - 39/compare 3/mod/direct 1/rm32/ECX . . . 6/r32/ESI . . # compare ECX with ESI - 73/jump-if-greater-or-equal-unsigned $write-slice:loop-end/disp8 - # if (out->write >= out->length) abort - 39/compare 3/mod/direct 3/rm32/EBX . . . 2/r32/EDX . . # compare EBX with EDX - 7d/jump-if-greater-or-equal $write-slice:abort/disp8 - # out->data[out->write] = *in - # . AL = *in - 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX - 8a/copy-byte 0/mod/indirect 1/rm32/ECX . . . 0/r32/AL . . # copy byte at *ECX to AL - # . out->data[out->write] = AL - 88/copy-byte 1/mod/*+disp8 4/rm32/sib 7/base/EDI 3/index/EBX . 0/r32/AL 0xc/disp8 . # copy AL to *(EDI+EBX+12) - # ++out->write - 43/increment-EBX - # ++in - 41/increment-ECX - eb/jump $write-slice:loop/disp8 -$write-slice:loop-end: - # persist out->write - 89/copy 0/mod/indirect 7/rm32/EDI . . . 3/r32/EBX . . # copy EBX to *EDI -$write-slice:end: - # . restore registers - 5f/pop-to-EDI - 5e/pop-to-ESI - 5b/pop-to-EBX - 5a/pop-to-EDX - 59/pop-to-ECX - 58/pop-to-EAX - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -$write-slice:abort: - # . _write(2/stderr, error) - # . . push args - 68/push "write-slice: out of space"/imm32 - 68/push 2/imm32/stderr - # . . call - e8/call _write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . syscall(exit, 1) - bb/copy-to-EBX 1/imm32 - b8/copy-to-EAX 1/imm32/exit - cd/syscall 0x80/imm8 - # never gets here - -test-write-slice: - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # setup - # . clear-stream(_test-stream) - # . . push args - 68/push _test-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # (EAX..ECX) = "Abc" - b8/copy-to-EAX "Abc"/imm32 - 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX - 05/add-to-EAX 4/imm32 - # var slice/ECX = {EAX, ECX} - 51/push-ECX - 50/push-EAX - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # write-slice(_test-stream, slice) - # . . push args - 51/push-ECX - 68/push _test-stream/imm32 - # . . call - e8/call write-slice/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check-stream-equal(_test-stream, "Abc", msg) - # . . push args - 68/push "F - test-write-slice"/imm32 - 68/push "Abc"/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call check-stream-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -# write a slice to a buffered-file -write-slice-buffered: # out : (address buffered-file), s : (address slice) - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # . save registers - 50/push-EAX - 51/push-ECX - 52/push-EDX - 53/push-EBX - 56/push-ESI - 57/push-EDI - # ESI = s - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 6/r32/ESI 0xc/disp8 . # copy *(EBP+12) to ESI - # curr/ECX = s->start - 8b/copy 0/mod/indirect 6/rm32/ESI . . . 1/r32/ECX . . # copy *ESI to ECX - # max/ESI = s->end - 8b/copy 1/mod/*+disp8 6/rm32/ESI . . . 6/r32/ESI 4/disp8 . # copy *(ESI+4) to ESI - # EDI = out - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . 7/r32/EDI 8/disp8 . # copy *(EBP+8) to EDI - # EDX = out->length - 8b/copy 1/mod/*+disp8 7/rm32/EDI . . . 2/r32/EDX 0xc/disp8 . # copy *(EDI+12) to EDX - # EBX = out->write - 8b/copy 1/mod/*+disp8 7/rm32/EDI . . . 3/r32/EBX 4/disp8 . # copy *(EDI+4) to EBX -$write-slice-buffered:loop: - # if (curr >= max) break - 39/compare 3/mod/direct 1/rm32/ECX . . . 6/r32/ESI . . # compare ECX with ESI - 73/jump-if-greater-or-equal-unsigned $write-slice-buffered:loop-end/disp8 - # if (out->write >= out->length) flush and clear out's stream - 39/compare 3/mod/direct 3/rm32/EBX . . . 2/r32/EDX . . # compare EBX with EDX - 7c/jump-if-lesser $write-slice-buffered:to-stream/disp8 - # . persist out->write - 89/copy 1/mod/*+disp8 7/rm32/EDI . . . 3/r32/EBX 4/disp8 . # copy EBX to *(EDI+4) - # . flush(out) - # . . push args - 57/push-EDI - # . . call - e8/call flush/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(stream = out+4) - # . . push args - 8d/copy-address 1/mod/*+disp8 7/rm32/EDI . . . 0/r32/EAX 4/disp8 . # copy EDI+4 to EAX - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . out->write must now be 0; update its cache at EBX - 31/xor 3/mod/direct 3/rm32/EBX . . . 3/r32/EBX . . # clear EBX -$write-slice-buffered:to-stream: - # out->data[out->write] = *in - # . AL = *in - 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX - 8a/copy-byte 0/mod/indirect 1/rm32/ECX . . . 0/r32/AL . . # copy byte at *ECX to AL - # . out->data[out->write] = AL - 88/copy-byte 1/mod/*+disp8 4/rm32/sib 7/base/EDI 3/index/EBX . 0/r32/AL 0x10/disp8 . # copy AL to *(EDI+EBX+16) - # ++out->write - 43/increment-EBX - # ++in - 41/increment-ECX - eb/jump $write-slice-buffered:loop/disp8 -$write-slice-buffered:loop-end: - # persist necessary variables from registers - 89/copy 1/mod/*+disp8 7/rm32/EDI . . . 3/r32/EBX 4/disp8 . # copy EBX to *(EDI+4) -$write-slice-buffered:end: - # . restore registers - 5f/pop-to-EDI - 5e/pop-to-ESI - 5b/pop-to-EBX - 5a/pop-to-EDX - 59/pop-to-ECX - 58/pop-to-EAX - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-write-slice-buffered: - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # setup - # . clear-stream(_test-stream) - # . . push args - 68/push _test-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-buffered-file+4) - # . . push args - b8/copy-to-EAX _test-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # (EAX..ECX) = "Abc" - b8/copy-to-EAX "Abc"/imm32 - 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX - 05/add-to-EAX 4/imm32 - # var slice/ECX = {EAX, ECX} - 51/push-ECX - 50/push-EAX - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # write-slice-buffered(_test-buffered-file, slice) - # . . push args - 51/push-ECX - 68/push _test-buffered-file/imm32 - # . . call - e8/call write-slice-buffered/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # flush(_test-buffered-file) - # . . push args - 68/push _test-buffered-file/imm32 - # . . call - e8/call flush/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # check-stream-equal(_test-stream, "Abc", msg) - # . . push args - 68/push "F - test-write-slice-buffered"/imm32 - 68/push "Abc"/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call check-stream-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -# copy a slice into a new (dynamically allocated) string -slice-to-string: # ad : (address allocation-descriptor), in : (address slice) -> out/EAX : (address array) - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # . save registers - 51/push-ECX - 52/push-EDX - 53/push-EBX - 56/push-ESI - # ESI = in - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 6/r32/ESI 0xc/disp8 . # copy *(EBP+12) to ESI - # curr/EDX = in->start - 8b/copy 0/mod/indirect 6/rm32/ESI . . . 2/r32/EDX . . # copy *ESI to EDX - # max/EBX = in->end - 8b/copy 1/mod/*+disp8 6/rm32/ESI . . . 3/r32/EBX 4/disp8 . # copy *(ESI+4) to EBX - # size/ECX = max - curr + 4 # total size of output string (including the initial length) - 89/copy 3/mod/direct 1/rm32/ECX . . . 3/r32/EBX . . # copy EBX to ECX - 29/subtract 3/mod/direct 1/rm32/ECX . . . 2/r32/EDX . . # subtract EDX from ECX - 81 0/subop/add 3/mod/direct 1/rm32/ECX . . . . . 4/imm32 # add to ECX - # out/EAX = allocate(ad, size) - # . . push args - 51/push-ECX - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - # . . call - e8/call allocate/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # if (EAX == 0) abort - 3d/compare-EAX-and 0/imm32 - 74/jump-if-equal $slice-to-string:abort/disp8 - # *out = size-4 - 89/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy ECX to *EAX - 81 5/subop/subtract 0/mod/indirect 0/rm32/EAX . . . . . 4/imm32 # subtract 4 from *EAX - # save out - 50/push-EAX - # EAX = _append-4(EAX+4, EAX+size, curr, max) # clobbering ECX - # . . push args - 53/push-EBX - 52/push-EDX - # . . push EAX+size (clobbering ECX) - 01/add 3/mod/direct 1/rm32/ECX . . . 0/r32/EAX . . # add EAX to ECX - 51/push-ECX - # . . push EAX+4 (clobbering EAX) - 81 0/subop/add 3/mod/direct 0/rm32/EAX . . . . . 4/imm32 # add to EAX - 50/push-EAX - # . . call - e8/call _append-4/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP - # restore out (assumes _append-4 can't error) - 58/pop-to-EAX -$slice-to-string:end: - # . restore registers - 5e/pop-to-ESI - 5b/pop-to-EBX - 5a/pop-to-EDX - 59/pop-to-ECX - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -$slice-to-string:abort: - # . _write(2/stderr, error) - # . . push args - 68/push "slice-to-string: out of space\n"/imm32 - 68/push 2/imm32/stderr - # . . call - e8/call _write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . syscall(exit, 1) - bb/copy-to-EBX 1/imm32 - b8/copy-to-EAX 1/imm32/exit - cd/syscall 0x80/imm8 - # never gets here - -test-slice-to-string: - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # var heap/EDX : (address allocation-descriptor) = {0, 0} - 68/push 0/imm32/limit - 68/push 0/imm32/curr - 89/copy 3/mod/direct 2/rm32/EDX . . . 4/r32/ESP . . # copy ESP to EDX - # heap = new-segment(512) - # . . push args - 52/push-EDX - 68/push 0x200/imm32 - # . . call - e8/call new-segment/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # (EAX..ECX) = "Abc" - b8/copy-to-EAX "Abc"/imm32 - 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX - 05/add-to-EAX 4/imm32 - # var slice/ECX = {EAX, ECX} - 51/push-ECX - 50/push-EAX - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # EAX = slice-to-string(heap, slice) - # . . push args - 51/push-ECX - 52/push-EDX - # . . call - e8/call slice-to-string/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # dump word-slice {{{ -#? # . write(2/stderr, "AA: ") -#? # . . push args -#? 68/push "AA: "/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write(2/stderr, EAX) -#? # . . push args -#? 50/push-EAX -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write(2/stderr, "$\n") -#? # . . push args -#? 68/push "$\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # }}} - # EAX = string-equal?(EAX, "Abc") - # . . push args - 68/push "Abc"/imm32 - 50/push-EAX - # . . call - e8/call string-equal?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check-ints-equal(EAX, 1, msg) - # . . push args - 68/push "F - test-slice-to-string"/imm32 - 68/push 1/imm32/true - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -# . . vim:nowrap:textwidth=0 diff --git a/subx/073next-token.subx b/subx/073next-token.subx deleted file mode 100644 index 942d9878..00000000 --- a/subx/073next-token.subx +++ /dev/null @@ -1,897 +0,0 @@ -# Some tokenization primitives. - -== code -# instruction effective address register displacement immediate -# . op subop mod rm32 base index scale r32 -# . 1-3 bytes 3 bits 2 bits 3 bits 3 bits 3 bits 2 bits 2 bits 0/1/2/4 bytes 0/1/2/4 bytes - -# extract the next run of characters that are different from a given 'delimiter' (skipping multiple delimiters if necessary) -# on reaching end of file, return an empty interval -next-token: # in : (address stream), delimiter : byte, out : (address slice) - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # . save registers - 50/push-EAX - 51/push-ECX - 56/push-ESI - 57/push-EDI - # ESI = in - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 6/r32/ESI 8/disp8 . # copy *(EBP+8) to ESI - # EDI = out - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 7/r32/EDI 0x10/disp8 . # copy *(EBP+16) to EDI - # skip-chars-matching(in, delimiter) - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - 56/push-ESI - # . . call - e8/call skip-chars-matching/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # out->start = &in->data[in->read] - 8b/copy 1/mod/*+disp8 6/rm32/ESI . . . 1/r32/ECX 4/disp8 . # copy *(ESI+4) to ECX - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 6/base/ESI 1/index/ECX . 0/r32/EAX 0xc/disp8 . # copy ESI+ECX+12 to EAX - 89/copy 0/mod/indirect 7/rm32/EDI . . . 0/r32/EAX . . # copy EAX to *EDI - # skip-chars-not-matching(in, delimiter) - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - 56/push-ESI - # . . call - e8/call skip-chars-not-matching/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # out->end = &in->data[in->read] - 8b/copy 1/mod/*+disp8 6/rm32/ESI . . . 1/r32/ECX 4/disp8 . # copy *(ESI+4) to ECX - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 6/base/ESI 1/index/ECX . 0/r32/EAX 0xc/disp8 . # copy ESI+ECX+12 to EAX - 89/copy 1/mod/*+disp8 7/rm32/EDI . . . 0/r32/EAX 4/disp8 . # copy EAX to *(EDI+4) - # . restore registers - 5f/pop-to-EDI - 5e/pop-to-ESI - 59/pop-to-ECX - 58/pop-to-EAX - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-next-token: - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # setup - # . clear-stream(_test-stream) - # . . push args - 68/push _test-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # var slice/ECX = {0, 0} - 68/push 0/imm32/end - 68/push 0/imm32/start - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # write(_test-stream, " ab") - # . . push args - 68/push " ab"/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # next-token(_test-stream, 0x20/space, slice) - # . . push args - 51/push-ECX - 68/push 0x20/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call next-token/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # check-ints-equal(slice->start - _test-stream->data, 2, msg) - # . check-ints-equal(slice->start - _test-stream, 14, msg) - # . . push args - 68/push "F - test-next-token: start"/imm32 - 68/push 0xe/imm32 - # . . push slice->start - _test-stream - 8b/copy 0/mod/indirect 1/rm32/ECX . . . 0/r32/EAX . . # copy *ECX to EAX - 81 5/subop/subtract 3/mod/direct 0/rm32/EAX . . . . . _test-stream/imm32 # subtract from EAX - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # check-ints-equal(slice->end - _test-stream->data, 4, msg) - # . check-ints-equal(slice->end - _test-stream, 16, msg) - # . . push args - 68/push "F - test-next-token: end"/imm32 - 68/push 0x10/imm32 - # . . push slice->end - _test-stream - 8b/copy 1/mod/*+disp8 1/rm32/ECX . . . 0/r32/EAX 4/disp8 . # copy *(ECX+4) to EAX - 81 5/subop/subtract 3/mod/direct 0/rm32/EAX . . . . . _test-stream/imm32 # subtract from EAX - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-next-token-Eof: - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # setup - # . clear-stream(_test-stream) - # . . push args - 68/push _test-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # var slice/ECX = {0, 0} - 68/push 0/imm32/end - 68/push 0/imm32/start - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # write nothing to _test-stream - # next-token(_test-stream, 0x20/space, slice) - # . . push args - 51/push-ECX - 68/push 0x20/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call next-token/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # check-ints-equal(slice->end, slice->start, msg) - # . . push args - 68/push "F - test-next-token-Eof"/imm32 - ff 6/subop/push 1/mod/*+disp8 1/rm32/ECX . . . . 4/disp8 . # push *(ECX+4) - ff 6/subop/push 0/mod/indirect 1/rm32/ECX . . . . . . # push *ECX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -# extract the next run of characters that are different from a given 'delimiter' (skipping multiple delimiters if necessary) -# on reaching end of file, return an empty interval -next-token-from-slice: # start : (address byte), end : (address byte), delimiter : byte, out : (address slice) -> <void> - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # . save registers - 50/push-EAX - 51/push-ECX - 52/push-EDX - 57/push-EDI - # ECX = end - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 1/r32/ECX 0xc/disp8 . # copy *(EBP+12) to ECX - # EDX = delimiter - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 2/r32/EDX 0x10/disp8 . # copy *(EBP+16) to EDX - # EDI = out - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 7/r32/EDI 0x14/disp8 . # copy *(EBP+20) to EDI - # EAX = skip-chars-matching-in-slice(start, end, delimiter) - # . . push args - 52/push-EDX - 51/push-ECX - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - # . . call - e8/call skip-chars-matching-in-slice/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # out->start = EAX - 89/copy 0/mod/indirect 7/rm32/EDI . . . 0/r32/EAX . . # copy EAX to *EDI - # EAX = skip-chars-not-matching-in-slice(EAX, end, delimiter) - # . . push args - 52/push-EDX - 51/push-ECX - 50/push-EAX - # . . call - e8/call skip-chars-not-matching-in-slice/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # out->end = EAX - 89/copy 1/mod/*+disp8 7/rm32/EDI . . . 0/r32/EAX 4/disp8 . # copy EAX to *(EDI+4) - # . restore registers - 5f/pop-to-EDI - 5a/pop-to-EDX - 59/pop-to-ECX - 58/pop-to-EAX - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-next-token-from-slice: - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # (EAX..ECX) = " ab" - b8/copy-to-EAX " ab"/imm32 - 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX - 05/add-to-EAX 4/imm32 - # var out/EDI : (address slice) = {0, 0} - 68/push 0/imm32/end - 68/push 0/imm32/start - 89/copy 3/mod/direct 7/rm32/EDI . . . 4/r32/ESP . . # copy ESP to EDI - # next-token-from-slice(EAX, ECX, 0x20/space, out) - # . . push args - 57/push-EDI - 68/push 0x20/imm32 - 51/push-ECX - 50/push-EAX - # . . call - e8/call next-token-from-slice/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP - # out->start should be at the 'a' - # . check-ints-equal(out->start - in->start, 2, msg) - # . . push args - 68/push "F - test-next-token-from-slice: start"/imm32 - 68/push 2/imm32 - # . . push out->start - in->start - 8b/copy 0/mod/indirect 7/rm32/EDI . . . 1/r32/ECX . . # copy *EDI to ECX - 2b/subtract 3/mod/direct 0/rm32/EAX . . . 1/r32/ECX . . # subtract EAX from ECX - 51/push-ECX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # out->end should be after the 'b' - # check-ints-equal(out->end - in->start, 4, msg) - # . . push args - 68/push "F - test-next-token-from-slice: end"/imm32 - 68/push 4/imm32 - # . . push out->end - in->start - 8b/copy 1/mod/*+disp8 7/rm32/EDI . . . 1/r32/ECX 4/disp8 . # copy *(EDI+4) to ECX - 2b/subtract 3/mod/direct 0/rm32/EAX . . . 1/r32/ECX . . # subtract EAX from ECX - 51/push-ECX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-next-token-from-slice-Eof: - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # var out/EDI : (address slice) = {0, 0} - 68/push 0/imm32/end - 68/push 0/imm32/start - 89/copy 3/mod/direct 7/rm32/EDI . . . 4/r32/ESP . . # copy ESP to EDI - # next-token-from-slice(0, 0, 0x20/space, out) - # . . push args - 57/push-EDI - 68/push 0x20/imm32 - 68/push 0/imm32 - 68/push 0/imm32 - # . . call - e8/call next-token-from-slice/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP - # out should be empty - # . check-ints-equal(out->end - out->start, 0, msg) - # . . push args - 68/push "F - test-next-token-from-slice-Eof"/imm32 - 68/push 0/imm32 - # . . push out->start - in->start - 8b/copy 1/mod/*+disp8 7/rm32/EDI . . . 1/r32/ECX 4/disp8 . # copy *(EDI+4) to ECX - 2b/subtract 0/mod/indirect 7/rm32/EDI . . . 1/r32/ECX . . # subtract *EDI from ECX - 51/push-ECX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-next-token-from-slice-nothing: - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # (EAX..ECX) = " " - b8/copy-to-EAX " "/imm32 - 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX - 05/add-to-EAX 4/imm32 - # var out/EDI : (address slice) = {0, 0} - 68/push 0/imm32/end - 68/push 0/imm32/start - 89/copy 3/mod/direct 7/rm32/EDI . . . 4/r32/ESP . . # copy ESP to EDI - # next-token-from-slice(in, 0x20/space, out) - # . . push args - 57/push-EDI - 68/push 0x20/imm32 - 51/push-ECX - 50/push-EAX - # . . call - e8/call next-token-from-slice/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP - # out should be empty - # . check-ints-equal(out->end - out->start, 0, msg) - # . . push args - 68/push "F - test-next-token-from-slice-Eof"/imm32 - 68/push 0/imm32 - # . . push out->start - in->start - 8b/copy 1/mod/*+disp8 7/rm32/EDI . . . 1/r32/ECX 4/disp8 . # copy *(EDI+4) to ECX - 2b/subtract 0/mod/indirect 7/rm32/EDI . . . 1/r32/ECX . . # subtract *EDI from ECX - 51/push-ECX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -skip-chars-matching: # in : (address stream), delimiter : byte - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # . save registers - 50/push-EAX - 51/push-ECX - 52/push-EDX - 53/push-EBX - 56/push-ESI - # ESI = in - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 6/r32/ESI 8/disp8 . # copy *(EBP+8) to ESI - # ECX = in->read - 8b/copy 1/mod/*+disp8 6/rm32/ESI . . . 1/r32/ECX 4/disp8 . # copy *(ESI+4) to ECX - # EBX = in->write - 8b/copy 0/mod/indirect 6/rm32/ESI . . . 3/r32/EBX . . # copy *ESI to EBX - # EDX = delimiter - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 2/r32/EDX 0xc/disp8 . # copy *(EBP+12) to EDX -$skip-chars-matching:loop: - # if (in->read >= in->write) break - 39/compare 3/mod/direct 1/rm32/ECX . . . 3/r32/EBX . . # compare ECX with EBX - 7d/jump-if-greater-or-equal $skip-chars-matching:end/disp8 - # EAX = in->data[in->read] - 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX - 8a/copy-byte 1/mod/*+disp8 4/rm32/sib 6/base/ESI 1/index/ECX . 0/r32/AL 0xc/disp8 . # copy byte at *(ESI+ECX+12) to AL - # if (EAX != delimiter) break - 39/compare 3/mod/direct 0/rm32/EAX . . . 2/r32/EDX . . # compare EAX and EDX - 75/jump-if-not-equal $skip-chars-matching:end/disp8 - # ++in->read - 41/increment-ECX - eb/jump $skip-chars-matching:loop/disp8 -$skip-chars-matching:end: - # persist in->read - 89/copy 1/mod/*+disp8 6/rm32/ESI . . . 1/r32/ECX 4/disp8 . # copy ECX to *(ESI+4) - # . restore registers - 5e/pop-to-ESI - 5b/pop-to-EBX - 5a/pop-to-EDX - 59/pop-to-ECX - 58/pop-to-EAX - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-skip-chars-matching: - # setup - # . clear-stream(_test-stream) - # . . push args - 68/push _test-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # write(_test-stream, " ab") - # . . push args - 68/push " ab"/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # skip-chars-matching(_test-stream, 0x20/space) - # . . push args - 68/push 0x20/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call skip-chars-matching/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check-ints-equal(_test-stream->read, 2, msg) - # . . push args - 68/push "F - test-skip-chars-matching"/imm32 - 68/push 2/imm32 - # . . push *_test-stream->read - b8/copy-to-EAX _test-stream/imm32 - ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 4/disp8 . # push *(EAX+4) - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # end - c3/return - -test-skip-chars-matching-none: - # setup - # . clear-stream(_test-stream) - # . . push args - 68/push _test-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # write(_test-stream, "ab") - # . . push args - 68/push "ab"/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # skip-chars-matching(_test-stream, 0x20/space) - # . . push args - 68/push 0x20/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call skip-chars-matching/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check-ints-equal(_test-stream->read, 0, msg) - # . . push args - 68/push "F - test-skip-chars-matching-none"/imm32 - 68/push 0/imm32 - # . . push *_test-stream->read - b8/copy-to-EAX _test-stream/imm32 - ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 4/disp8 . # push *(EAX+4) - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # end - c3/return - -# minor fork of 'skip-chars-matching' -skip-chars-not-matching: # in : (address stream), delimiter : byte - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # . save registers - 50/push-EAX - 51/push-ECX - 52/push-EDX - 53/push-EBX - 56/push-ESI - # ESI = in - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 6/r32/ESI 8/disp8 . # copy *(EBP+8) to ESI - # ECX = in->read - 8b/copy 1/mod/*+disp8 6/rm32/ESI . . . 1/r32/ECX 4/disp8 . # copy *(ESI+4) to ECX - # EBX = in->write - 8b/copy 0/mod/indirect 6/rm32/ESI . . . 3/r32/EBX . . # copy *ESI to EBX - # EDX = delimiter - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 2/r32/EDX 0xc/disp8 . # copy *(EBP+12) to EDX -$skip-chars-not-matching:loop: - # if (in->read >= in->write) break - 39/compare 3/mod/direct 1/rm32/ECX . . . 3/r32/EBX . . # compare ECX with EBX - 7d/jump-if-greater-or-equal $skip-chars-not-matching:end/disp8 - # EAX = in->data[in->read] - 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX - 8a/copy-byte 1/mod/*+disp8 4/rm32/sib 6/base/ESI 1/index/ECX . 0/r32/AL 0xc/disp8 . # copy byte at *(ESI+ECX+12) to AL - # if (EAX == delimiter) break - 39/compare 3/mod/direct 0/rm32/EAX . . . 2/r32/EDX . . # compare EAX and EDX - 74/jump-if-equal $skip-chars-not-matching:end/disp8 - # ++in->read - 41/increment-ECX - eb/jump $skip-chars-not-matching:loop/disp8 -$skip-chars-not-matching:end: - # persist in->read - 89/copy 1/mod/*+disp8 6/rm32/ESI . . . 1/r32/ECX 4/disp8 . # copy ECX to *(ESI+4) - # . restore registers - 5e/pop-to-ESI - 5b/pop-to-EBX - 5a/pop-to-EDX - 59/pop-to-ECX - 58/pop-to-EAX - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-skip-chars-not-matching: - # setup - # . clear-stream(_test-stream) - # . . push args - 68/push _test-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # write(_test-stream, "ab ") - # . . push args - 68/push "ab "/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # skip-chars-not-matching(_test-stream, 0x20/space) - # . . push args - 68/push 0x20/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call skip-chars-not-matching/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check-ints-equal(_test-stream->read, 2, msg) - # . . push args - 68/push "F - test-skip-chars-not-matching"/imm32 - 68/push 2/imm32 - # . . push *_test-stream->read - b8/copy-to-EAX _test-stream/imm32 - ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 4/disp8 . # push *(EAX+4) - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # end - c3/return - -test-skip-chars-not-matching-none: - # setup - # . clear-stream(_test-stream) - # . . push args - 68/push _test-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # write(_test-stream, " ab") - # . . push args - 68/push " ab"/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # skip-chars-not-matching(_test-stream, 0x20/space) - # . . push args - 68/push 0x20/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call skip-chars-not-matching/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check-ints-equal(_test-stream->read, 0, msg) - # . . push args - 68/push "F - test-skip-chars-not-matching-none"/imm32 - 68/push 0/imm32 - # . . push *_test-stream->read - b8/copy-to-EAX _test-stream/imm32 - ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 4/disp8 . # push *(EAX+4) - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # end - c3/return - -test-skip-chars-not-matching-all: - # setup - # . clear-stream(_test-stream) - # . . push args - 68/push _test-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # write(_test-stream, "ab") - # . . push args - 68/push "ab"/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # skip-chars-not-matching(_test-stream, 0x20/space) - # . . push args - 68/push 0x20/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call skip-chars-not-matching/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check-ints-equal(_test-stream->read, 2, msg) - # . . push args - 68/push "F - test-skip-chars-not-matching-all"/imm32 - 68/push 2/imm32 - # . . push *_test-stream->read - b8/copy-to-EAX _test-stream/imm32 - ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 4/disp8 . # push *(EAX+4) - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # end - c3/return - -skip-chars-not-matching-whitespace: # in : (address stream) - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # . save registers - 50/push-EAX - 51/push-ECX - 53/push-EBX - 56/push-ESI - # ESI = in - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 6/r32/ESI 8/disp8 . # copy *(EBP+8) to ESI - # ECX = in->read - 8b/copy 1/mod/*+disp8 6/rm32/ESI . . . 1/r32/ECX 4/disp8 . # copy *(ESI+4) to ECX - # EBX = in->write - 8b/copy 0/mod/indirect 6/rm32/ESI . . . 3/r32/EBX . . # copy *ESI to EBX -$skip-chars-not-matching-whitespace:loop: - # if (in->read >= in->write) break - 39/compare 3/mod/direct 1/rm32/ECX . . . 3/r32/EBX . . # compare ECX with EBX - 7d/jump-if-greater-or-equal $skip-chars-not-matching-whitespace:end/disp8 - # EAX = in->data[in->read] - 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX - 8a/copy-byte 1/mod/*+disp8 4/rm32/sib 6/base/ESI 1/index/ECX . 0/r32/AL 0xc/disp8 . # copy byte at *(ESI+ECX+12) to AL - # if (EAX == ' ') break - 3d/compare-EAX-and 0x20/imm32/space - 74/jump-if-equal $skip-chars-not-matching-whitespace:end/disp8 - # if (EAX == '\n') break - 3d/compare-EAX-and 0x0a/imm32/newline - 74/jump-if-equal $skip-chars-not-matching-whitespace:end/disp8 - # if (EAX == '\t') break - 3d/compare-EAX-and 0x09/imm32/tab - 74/jump-if-equal $skip-chars-not-matching-whitespace:end/disp8 - # if (EAX == '\r') break - 3d/compare-EAX-and 0x0d/imm32/cr - 74/jump-if-equal $skip-chars-not-matching-whitespace:end/disp8 - # ++in->read - 41/increment-ECX - eb/jump $skip-chars-not-matching-whitespace:loop/disp8 -$skip-chars-not-matching-whitespace:end: - # persist in->read - 89/copy 1/mod/*+disp8 6/rm32/ESI . . . 1/r32/ECX 4/disp8 . # copy ECX to *(ESI+4) - # . restore registers - 5e/pop-to-ESI - 5b/pop-to-EBX - 59/pop-to-ECX - 58/pop-to-EAX - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -skip-chars-matching-in-slice: # curr : (address byte), end : (address byte), delimiter : byte -> curr/EAX - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # . save registers - 51/push-ECX - 52/push-EDX - 53/push-EBX - # EAX = curr - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 0/r32/EAX 8/disp8 . # copy *(EBP+8) to EAX - # ECX = end - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 1/r32/ECX 0xc/disp8 . # copy *(EBP+12) to ECX - # EDX = delimiter - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 2/r32/EDX 0x10/disp8 . # copy *(EBP+16) to EDX - # EBX = 0 - 31/xor 3/mod/direct 3/rm32/EBX . . . 3/r32/EBX . . # clear EBX -$skip-chars-matching-in-slice:loop: - # if (curr >= end) break - 39/compare 3/mod/direct 0/rm32/EAX . . . 1/r32/ECX . . # compare EAX with ECX - 73/jump-if-greater-or-equal-unsigned $skip-chars-matching-in-slice:end/disp8 - # if (*curr != delimiter) break - 8a/copy-byte 0/mod/indirect 0/rm32/EAX . . . 3/r32/BL . . # copy byte at *EAX to BL - 39/compare 3/mod/direct 3/rm32/EBX . . . 2/r32/EDX . . # compare EBX and EDX - 75/jump-if-not-equal $skip-chars-matching-in-slice:end/disp8 - # ++curr - 40/increment-EAX - eb/jump $skip-chars-matching-in-slice:loop/disp8 -$skip-chars-matching-in-slice:end: - # . restore registers - 5b/pop-to-EBX - 5a/pop-to-EDX - 59/pop-to-ECX - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-skip-chars-matching-in-slice: - # (EAX..ECX) = " ab" - b8/copy-to-EAX " ab"/imm32 - 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX - 05/add-to-EAX 4/imm32 - # EAX = skip-chars-matching-in-slice(EAX, ECX, 0x20/space) - # . . push args - 68/push 0x20/imm32/space - 51/push-ECX - 50/push-EAX - # . . call - e8/call skip-chars-matching-in-slice/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # check-ints-equal(ECX-EAX, 2, msg) - # . . push args - 68/push "F - test-skip-chars-matching-in-slice"/imm32 - 68/push 2/imm32 - # . . push ECX-EAX - 29/subtract 3/mod/direct 1/rm32/ECX . . . 0/r32/EAX . . # subtract EAX from ECX - 51/push-ECX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # end - c3/return - -test-skip-chars-matching-in-slice-none: - # (EAX..ECX) = "ab" - b8/copy-to-EAX "ab"/imm32 - 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX - 05/add-to-EAX 4/imm32 - # EAX = skip-chars-matching-in-slice(EAX, ECX, 0x20/space) - # . . push args - 68/push 0x20/imm32/space - 51/push-ECX - 50/push-EAX - # . . call - e8/call skip-chars-matching-in-slice/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # check-ints-equal(ECX-EAX, 2, msg) - # . . push args - 68/push "F - test-skip-chars-matching-in-slice-none"/imm32 - 68/push 2/imm32 - # . . push ECX-EAX - 29/subtract 3/mod/direct 1/rm32/ECX . . . 0/r32/EAX . . # subtract EAX from ECX - 51/push-ECX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # end - c3/return - -# minor fork of 'skip-chars-matching-in-slice' -skip-chars-not-matching-in-slice: # curr : (address byte), end : (address byte), delimiter : byte -> curr/EAX - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # . save registers - 51/push-ECX - 52/push-EDX - 53/push-EBX - # EAX = curr - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 0/r32/EAX 8/disp8 . # copy *(EBP+8) to EAX - # ECX = end - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 1/r32/ECX 0xc/disp8 . # copy *(EBP+12) to ECX - # EDX = delimiter - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 2/r32/EDX 0x10/disp8 . # copy *(EBP+16) to EDX - # EBX = 0 - 31/xor 3/mod/direct 3/rm32/EBX . . . 3/r32/EBX . . # clear EBX -$skip-chars-not-matching-in-slice:loop: - # if (curr >= end) break - 39/compare 3/mod/direct 0/rm32/EAX . . . 1/r32/ECX . . # compare EAX with ECX - 73/jump-if-greater-or-equal-unsigned $skip-chars-not-matching-in-slice:end/disp8 - # if (*curr == delimiter) break - 8a/copy-byte 0/mod/indirect 0/rm32/EAX . . . 3/r32/BL . . # copy byte at *EAX to BL - 39/compare 3/mod/direct 3/rm32/EBX . . . 2/r32/EDX . . # compare EBX and EDX - 74/jump-if-equal $skip-chars-not-matching-in-slice:end/disp8 - # ++curr - 40/increment-EAX - eb/jump $skip-chars-not-matching-in-slice:loop/disp8 -$skip-chars-not-matching-in-slice:end: - # . restore registers - 5b/pop-to-EBX - 5a/pop-to-EDX - 59/pop-to-ECX - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-skip-chars-not-matching-in-slice: - # (EAX..ECX) = "ab " - b8/copy-to-EAX "ab "/imm32 - 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX - 05/add-to-EAX 4/imm32 - # EAX = skip-chars-not-matching-in-slice(EAX, ECX, 0x20/space) - # . . push args - 68/push 0x20/imm32/space - 51/push-ECX - 50/push-EAX - # . . call - e8/call skip-chars-not-matching-in-slice/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # check-ints-equal(ECX-EAX, 1, msg) - # . . push args - 68/push "F - test-skip-chars-not-matching-in-slice"/imm32 - 68/push 1/imm32 - # . . push ECX-EAX - 29/subtract 3/mod/direct 1/rm32/ECX . . . 0/r32/EAX . . # subtract EAX from ECX - 51/push-ECX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # end - c3/return - -test-skip-chars-not-matching-in-slice-none: - # (EAX..ECX) = " ab" - b8/copy-to-EAX " ab"/imm32 - 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX - 05/add-to-EAX 4/imm32 - # EAX = skip-chars-not-matching-in-slice(EAX, ECX, 0x20/space) - # . . push args - 68/push 0x20/imm32/space - 51/push-ECX - 50/push-EAX - # . . call - e8/call skip-chars-not-matching-in-slice/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # check-ints-equal(ECX-EAX, 3, msg) - # . . push args - 68/push "F - test-skip-chars-not-matching-in-slice-none"/imm32 - 68/push 3/imm32 - # . . push ECX-EAX - 29/subtract 3/mod/direct 1/rm32/ECX . . . 0/r32/EAX . . # subtract EAX from ECX - 51/push-ECX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # end - c3/return - -test-skip-chars-not-matching-in-slice-all: - # (EAX..ECX) = "ab" - b8/copy-to-EAX "ab"/imm32 - 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX - 05/add-to-EAX 4/imm32 - # EAX = skip-chars-not-matching-in-slice(EAX, ECX, 0x20/space) - # . . push args - 68/push 0x20/imm32/space - 51/push-ECX - 50/push-EAX - # . . call - e8/call skip-chars-not-matching-in-slice/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # check-ints-equal(ECX-EAX, 0, msg) - # . . push args - 68/push "F - test-skip-chars-not-matching-in-slice-all"/imm32 - 68/push 0/imm32 - # . . push ECX-EAX - 29/subtract 3/mod/direct 1/rm32/ECX . . . 0/r32/EAX . . # subtract EAX from ECX - 51/push-ECX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # end - c3/return - -# . . vim:nowrap:textwidth=0 diff --git a/subx/074print-int-decimal.subx b/subx/074print-int-decimal.subx deleted file mode 100644 index f6ea490f..00000000 --- a/subx/074print-int-decimal.subx +++ /dev/null @@ -1,307 +0,0 @@ -# Helper to print an int32 in decimal. - -== code -# instruction effective address register displacement immediate -# . op subop mod rm32 base index scale r32 -# . 1-3 bytes 3 bits 2 bits 3 bits 3 bits 3 bits 2 bits 2 bits 0/1/2/4 bytes 0/1/2/4 bytes - -print-int32-decimal: # out : (address stream), n : int32 - # works by generating characters from lowest to highest and pushing them - # to the stack, before popping them one by one into the stream - # - # pseudocode: - # push sentinel - # EAX = abs(n) - # while true - # sign-extend EAX into EDX - # EAX, EDX = EAX/10, EAX%10 - # EDX += '0' - # push EDX - # if (EAX == 0) break - # if n < 0 - # push '-' - # w = out->write - # curr = &out->data[out->write] - # max = &out->data[out->length] - # while true - # pop into EAX - # if (EAX == sentinel) break - # if (curr >= max) abort - # *curr = AL - # ++curr - # ++w - # out->write = w - # (based on K&R itoa: https://en.wikibooks.org/wiki/C_Programming/stdlib.h/itoa) - # (this pseudocode contains registers because operations like division - # require specific registers in x86) - # - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # . save registers - 50/push-EAX - 51/push-ECX - 52/push-EDX - 53/push-EBX - 57/push-EDI - # ten/ECX = 10 - b9/copy-to-ECX 0xa/imm32 - # push sentinel - 68/push 0/imm32/sentinel - # EAX = abs(n) - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 0/r32/EAX 0xc/disp8 . # copy *(EBP+12) to EAX - 3d/compare-EAX-with 0/imm32 - 7d/jump-if-greater-or-equal $print-int32-decimal:read-loop/disp8 -$print-int32-decimal:negative: - f7 3/subop/negate 3/mod/direct 0/rm32/EAX . . . . . . # negate EAX -$print-int32-decimal:read-loop: - # EAX, EDX = EAX / 10, EAX % 10 - 99/sign-extend-EAX-into-EDX - f7 7/subop/idiv 3/mod/direct 1/rm32/ECX . . . . . . # divide EDX:EAX by ECX, storing quotient in EAX and remainder in EDX - # EDX += '0' - 81 0/subop/add 3/mod/direct 2/rm32/EDX . . . . . 0x30/imm32 # add to EDX - # push EDX - 52/push-EDX - # if (EAX == 0) break - 3d/compare-EAX-and 0/imm32 - 7f/jump-if-greater $print-int32-decimal:read-loop/disp8 -$print-int32-decimal:read-break: - # if (n < 0) push('-') - 81 7/subop/compare 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 0/imm32 # compare *(EBP+12) - 7d/jump-if-greater-or-equal $print-int32-decimal:write/disp8 -$print-int32-decimal:push-negative: - 68/push 0x2d/imm32/- -$print-int32-decimal:write: - # EDI = out - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 7/r32/EDI 8/disp8 . # copy *(EBP+8) to EDI - # w/EDX = out->write - 8b/copy 0/mod/indirect 7/rm32/EDI . . . 2/r32/EDX . . # copy *EDI to EDX - # curr/ECX = &out->data[out->write] - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 7/base/EDI 2/index/EDX . 1/r32/ECX 0xc/disp8 . # copy EBX+EDX+12 to ECX - # max/EBX = &out->data[out->length] - 8b/copy 1/mod/*+disp8 7/rm32/EDI . . . 3/r32/EBX 8/disp8 . # copy *(EDI+8) to EBX - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 7/base/EDI 3/index/EBX . 3/r32/EBX 0xc/disp8 . # copy EDI+EBX+12 to EBX -$print-int32-decimal:write-loop: - # pop into EAX - 58/pop-to-EAX - # if (EAX == sentinel) break - 3d/compare-EAX-and 0/imm32/sentinel - 74/jump-if-equal $print-int32-decimal:write-break/disp8 - # if (curr >= max) abort - 39/compare 3/mod/direct 1/rm32/ECX . . . 3/r32/EBX . . # compare ECX with EBX - 73/jump-if-greater-or-equal-unsigned $print-int32-decimal:abort/disp8 -$print-int32-decimal:write-char: - # *curr = AL - 88/copy-byte 0/mod/indirect 1/rm32/ECX . . . 0/r32/AL . . # copy AL to byte at *ECX - # ++curr - 41/increment-ECX - # ++w - 42/increment-EDX - eb/jump $print-int32-decimal:write-loop/disp8 -$print-int32-decimal:write-break: - # out->write = w - 89/copy 0/mod/indirect 7/rm32/EDI . . . 2/r32/EDX . . # copy EDX to *EDI -$print-int32-decimal:end: - # . restore registers - 5f/pop-to-EDI - 5b/pop-to-EBX - 5a/pop-to-EDX - 59/pop-to-ECX - 58/pop-to-EAX - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -$print-int32-decimal:abort: - # . _write(2/stderr, error) - # . . push args - 68/push "print-int32-decimal: out of space\n"/imm32 - 68/push 2/imm32/stderr - # . . call - e8/call _write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . syscall(exit, 1) - bb/copy-to-EBX 1/imm32 - b8/copy-to-EAX 1/imm32/exit - cd/syscall 0x80/imm8 - # never gets here - -test-print-int32-decimal: - # - check that a single-digit number converts correctly - # setup - # . clear-stream(_test-stream) - # . . push args - 68/push _test-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # print-int32-decimal(_test-stream, 9) - # . . push args - 68/push 9/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call print-int32-decimal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check-stream-equal(_test-stream, "9", msg) - # . . push args - 68/push "F - test-print-int32-decimal"/imm32 - 68/push "9"/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call check-stream-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . end - c3/return - -test-print-int32-decimal-zero: - # - check that 0 converts correctly - # setup - # . clear-stream(_test-stream) - # . . push args - 68/push _test-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # print-int32-decimal(_test-stream, 0) - # . . push args - 68/push 0/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call print-int32-decimal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check-stream-equal(_test-stream, "0", msg) - # . . push args - 68/push "F - test-print-int32-decimal-zero"/imm32 - 68/push "0"/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call check-stream-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . end - c3/return - -test-print-int32-decimal-multiple-digits: - # - check that a multi-digit number converts correctly - # setup - # . clear-stream(_test-stream) - # . . push args - 68/push _test-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # print-int32-decimal(_test-stream, 10) - # . . push args - 68/push 0xa/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call print-int32-decimal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check-stream-equal(_test-stream, "10", msg) - # . . push args - 68/push "F - test-print-int32-decimal-multiple-digits"/imm32 - 68/push "10"/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call check-stream-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . end - c3/return - -test-print-int32-decimal-negative: - # - check that a negative single-digit number converts correctly - # setup - # . clear-stream(_test-stream) - # . . push args - 68/push _test-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # print-int32-decimal(_test-stream, -9) - # . . push args - 68/push -9/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call print-int32-decimal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # dump _test-stream {{{ -#? # . write(2/stderr, "^") -#? # . . push args -#? 68/push "^"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write-stream(2/stderr, _test-stream) -#? # . . push args -#? 68/push _test-stream/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write-stream/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write(2/stderr, "$\n") -#? # . . push args -#? 68/push "$\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # }}} - # check-stream-equal(_test-stream, "-9", msg) - # . . push args - 68/push "F - test-print-int32-decimal-negative"/imm32 - 68/push "-9"/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call check-stream-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . end - c3/return - -test-print-int32-decimal-negative-multiple-digits: - # - check that a multi-digit number converts correctly - # setup - # . clear-stream(_test-stream) - # . . push args - 68/push _test-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # print-int32-decimal(_test-stream, -10) - # . . push args - 68/push -0xa/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call print-int32-decimal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check-stream-equal(_test-stream, "-10", msg) - # . . push args - 68/push "F - test-print-int32-decimal-negative-multiple-digits"/imm32 - 68/push "-10"/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call check-stream-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . end - c3/return - -# . . vim:nowrap:textwidth=0 diff --git a/subx/075array-equal.subx b/subx/075array-equal.subx deleted file mode 100644 index 60694829..00000000 --- a/subx/075array-equal.subx +++ /dev/null @@ -1,629 +0,0 @@ -# Comparing arrays of numbers. - -== code -# instruction effective address register displacement immediate -# . op subop mod rm32 base index scale r32 -# . 1-3 bytes 3 bits 2 bits 3 bits 3 bits 3 bits 2 bits 2 bits 0/1/2/4 bytes 0/1/2/4 bytes - -Entry: - # initialize heap - # . Heap = new-segment(64KB) - # . . push args - 68/push Heap/imm32 - 68/push 0x10000/imm32/64KB - # . . call - e8/call new-segment/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - - e8/call run-tests/disp32 # 'run-tests' is a function created automatically by SubX. It calls all functions that start with 'test-'. -$array-equal-main:end: - # syscall(exit, Num-test-failures) - 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 3/r32/EBX Num-test-failures/disp32 # copy *Num-test-failures to EBX - b8/copy-to-EAX 1/imm32/exit - cd/syscall 0x80/imm8 - -array-equal?: # a : (address array int), b : (address array int) -> EAX : boolean - # pseudocode: - # lena = a->length - # if (lena != b->length) return false - # i = 0 - # curra = a->data - # currb = b->data - # while i < lena - # i1 = *curra - # i2 = *currb - # if (c1 != c2) return false - # i+=4, curra+=4, currb+=4 - # return true - # - # registers: - # i: ECX - # lena: EDX - # curra: ESI - # currb: EDI - # i1: EAX - # i2: EBX - # - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # . save registers - 51/push-ECX - 52/push-EDX - 53/push-EBX - 56/push-ESI - 57/push-EDI - # ESI = a - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 6/r32/ESI 8/disp8 . # copy *(EBP+8) to ESI - # EDI = b - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 7/r32/EDI 0xc/disp8 . # copy *(EBP+12) to EDI - # lena/EDX = a->length - 8b/copy 0/mod/indirect 6/rm32/ESI . . . 2/r32/EDX . . # copy *ESI to EDX -$array-equal?:lengths: - # if (lena != b->length) return false - 39/compare 0/mod/indirect 7/rm32/EDI . . . 2/r32/EDX . . # compare *EDI and EDX - 75/jump-if-not-equal $array-equal?:false/disp8 - # curra/ESI = a->data - 81 0/subop/add 3/mod/direct 6/rm32/ESI . . . . . 4/imm32 # add to ESI - # currb/EDI = b->data - 81 0/subop/add 3/mod/direct 7/rm32/EDI . . . . . 4/imm32 # add to EDI - # i/ECX = i1/EAX = i2/EBX = 0 - 31/xor 3/mod/direct 1/rm32/ECX . . . 1/r32/ECX . . # clear ECX -$array-equal?:loop: - # if (i >= lena) return true - 39/compare 3/mod/direct 1/rm32/ECX . . . 2/r32/EDX . . # compare ECX with EDX - 7d/jump-if-greater-or-equal $array-equal?:true/disp8 - # i1 = *curra - 8b/copy 0/mod/indirect 6/rm32/ESI . . . 0/r32/EAX . . # copy *ESI to EAX - # i2 = *currb - 8b/copy 0/mod/indirect 7/rm32/EDI . . . 3/r32/EBX . . # copy *EDI to EBX - # if (i1 != i2) return false - 39/compare 3/mod/direct 0/rm32/EAX . . . 3/r32/EBX . . # compare EAX and EBX - 75/jump-if-not-equal $array-equal?:false/disp8 - # i += 4 - 81 0/subop/add 3/mod/direct 1/rm32/ECX . . . . . 4/imm32 # add to ECX - # currs += 4 - 81 0/subop/add 3/mod/direct 6/rm32/ESI . . . . . 4/imm32 # add to ESI - # currb += 4 - 81 0/subop/add 3/mod/direct 7/rm32/EDI . . . . . 4/imm32 # add to EDI - eb/jump $array-equal?:loop/disp8 -$array-equal?:true: - b8/copy-to-EAX 1/imm32 - eb/jump $array-equal?:end/disp8 -$array-equal?:false: - b8/copy-to-EAX 0/imm32 -$array-equal?:end: - # . restore registers - 5f/pop-to-EDI - 5e/pop-to-ESI - 5b/pop-to-EBX - 5a/pop-to-EDX - 59/pop-to-ECX - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-compare-empty-with-empty-array: - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # var ECX = [] - 68/push 0/imm32/size - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # var EDX = [] - 68/push 0/imm32/size - 89/copy 3/mod/direct 2/rm32/EDX . . . 4/r32/ESP . . # copy ESP to EDX - # EAX = array-equal?(ECX, EDX) - # . . push args - 52/push-EDX - 51/push-ECX - # . . call - e8/call array-equal?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check-ints-equal(EAX, 1, msg) - # . . push args - 68/push "F - test-compare-empty-with-empty-array"/imm32 - 68/push 1/imm32/true - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-compare-empty-with-non-empty-array: # also checks length-mismatch code path - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # var ECX = [1] - 68/push 1/imm32 - 68/push 4/imm32/size - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # var EDX = [] - 68/push 0/imm32/size - 89/copy 3/mod/direct 2/rm32/EDX . . . 4/r32/ESP . . # copy ESP to EDX - # EAX = array-equal?(ECX, EDX) - # . . push args - 52/push-EDX - 51/push-ECX - # . . call - e8/call array-equal?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check-ints-equal(EAX, 0, msg) - # . . push args - 68/push "F - test-compare-empty-with-non-empty-array"/imm32 - 68/push 0/imm32/false - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-compare-equal-arrays: - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # var ECX = [1, 2, 3] - 68/push 3/imm32 - 68/push 2/imm32 - 68/push 1/imm32 - 68/push 0xc/imm32/size - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # var EDX = [1, 2, 3] - 68/push 3/imm32 - 68/push 2/imm32 - 68/push 1/imm32 - 68/push 0xc/imm32/size - 89/copy 3/mod/direct 2/rm32/EDX . . . 4/r32/ESP . . # copy ESP to EDX - # EAX = array-equal?(ECX, EDX) - # . . push args - 52/push-EDX - 51/push-ECX - # . . call - e8/call array-equal?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check-ints-equal(EAX, 1, msg) - # . . push args - 68/push "F - test-compare-equal-arrays"/imm32 - 68/push 1/imm32/true - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-compare-inequal-arrays-equal-lengths: - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # var ECX = [1, 4, 3] - 68/push 3/imm32 - 68/push 4/imm32 - 68/push 1/imm32 - 68/push 0xc/imm32/size - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # var EDX = [1, 2, 3] - 68/push 3/imm32 - 68/push 2/imm32 - 68/push 1/imm32 - 68/push 0xc/imm32/size - 89/copy 3/mod/direct 2/rm32/EDX . . . 4/r32/ESP . . # copy ESP to EDX - # EAX = array-equal?(ECX, EDX) - # . . push args - 52/push-EDX - 51/push-ECX - # . . call - e8/call array-equal?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check-ints-equal(EAX, 0, msg) - # . . push args - 68/push "F - test-compare-inequal-arrays-equal-lengths"/imm32 - 68/push 0/imm32/false - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -parse-array-of-ints: # ad : (address allocation-descriptor), s : (address string) -> result/EAX : (address array int) - # pseudocode - # end = s->data + s->length - # curr = s->data - # size = 0 - # while true - # if (curr >= end) break - # curr = skip-chars-matching-in-slice(curr, end, ' ') - # if (curr >= end) break - # curr = skip-chars-not-matching-in-slice(curr, end, ' ') - # ++size - # result = allocate(ad, (size+1)*4) - # result->size = (size+1)*4 - # var slice = {s->data, 0} - # out = result->data - # while true - # if (slice->start >= end) break - # slice->start = skip-chars-matching-in-slice(slice->start, end, ' ') - # if (slice->start >= end) break - # slice->end = skip-chars-not-matching-in-slice(slice->start, end, ' ') - # *out = parse-hex-int(slice) - # out += 4 - # slice->start = slice->end - # return result - # - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # . save registers - 51/push-ECX - 52/push-EDX - 53/push-EBX - 56/push-ESI - 57/push-EDI - # ESI = s - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 6/r32/ESI 0xc/disp8 . # copy *(EBP+12) to ESI - # curr/ECX = s->data - 8d/copy-address 1/mod/*+disp8 6/rm32/ESI . . . 1/r32/ECX 4/disp8 . # copy ESI+4 to ECX - # end/EDX = s->data + s->length - # . EDX = s->length - 8b/copy 0/mod/indirect 6/rm32/ESI . . . 2/r32/EDX . . # copy *ESI to EDX - # . EDX += curr - 01/add 3/mod/direct 2/rm32/EDX . . . 1/r32/ECX . . # add ECX to EDX - # size/EBX = 0 - 31/xor 3/mod/direct 3/rm32/EBX . . . 3/r32/EBX . . # clear EBX -$parse-array-of-ints:loop1: - # if (curr >= end) break - 39/compare 3/mod/direct 1/rm32/ECX . . . 2/r32/EDX . . # compare ECX with EDX - 73/jump-if-greater-or-equal-unsigned $parse-array-of-ints:break1/disp8 - # curr = skip-chars-matching-in-slice(curr, end, ' ') - # . EAX = skip-chars-matching-in-slice(curr, end, ' ') - # . . push args - 68/push 0x20/imm32/space - 52/push-EDX - 51/push-ECX - # . . call - e8/call skip-chars-matching-in-slice/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . ECX = EAX - 89/copy 3/mod/direct 1/rm32/ECX . . . 0/r32/EAX . . # copy EAX to ECX - # if (curr >= end) break - 39/compare 3/mod/direct 1/rm32/ECX . . . 2/r32/EDX . . # compare ECX with EDX - 73/jump-if-greater-or-equal-unsigned $parse-array-of-ints:break1/disp8 - # curr = skip-chars-not-matching-in-slice(curr, end, ' ') - # . EAX = skip-chars-not-matching-in-slice(curr, end, ' ') - # . . push args - 68/push 0x20/imm32/space - 52/push-EDX - 51/push-ECX - # . . call - e8/call skip-chars-not-matching-in-slice/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . ECX = EAX - 89/copy 3/mod/direct 1/rm32/ECX . . . 0/r32/EAX . . # copy EAX to ECX - # size += 4 - 81 0/subop/add 3/mod/direct 3/rm32/EBX . . . . . 4/imm32 # add to EBX - eb/jump $parse-array-of-ints:loop1/disp8 -$parse-array-of-ints:break1: - # result/EDI = allocate(ad, size+4) - # . EAX = allocate(ad, size+4) - # . . push args - 89/copy 3/mod/direct 0/rm32/EAX . . . 3/r32/EBX . . # copy EBX to EAX - 05/add-to-EAX 4/imm32 - 50/push-EAX - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - # . . call - e8/call allocate/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . EDI = EAX - 89/copy 3/mod/direct 7/rm32/EDI . . . 0/r32/EAX . . # copy EAX to EDI - # result->size = size - 89/copy 0/mod/indirect 0/rm32/EAX . . . 3/r32/EBX . . # copy EBX to *EAX -$parse-array-of-ints:pass2: - # var slice/ECX = {s->data, 0} - # . push 0 - 68/push 0/imm32/end - # . push s->data - 8d/copy-address 1/mod/*+disp8 6/rm32/ESI . . . 1/r32/ECX 4/disp8 . # copy ESI+4 to ECX - 51/push-ECX - # . bookmark - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # out/EBX = result->data - 8d/copy-address 1/mod/*+disp8 0/rm32/EAX . . . 3/r32/EBX 4/disp8 . # copy EAX+4 to EBX -$parse-array-of-ints:loop2: - # if (slice->start >= end) break - 39/compare 0/mod/indirect 1/rm32/ECX . . . 2/r32/EDX . . # compare *ECX with EDX - 73/jump-if-greater-or-equal-unsigned $parse-array-of-ints:end/disp8 - # slice->start = skip-chars-matching-in-slice(slice->start, end, ' ') - # . EAX = skip-chars-matching-in-slice(slice->start, end, ' ') - # . . push args - 68/push 0x20/imm32/space - 52/push-EDX - ff 6/subop/push 0/mod/indirect 1/rm32/ECX . . . . . . # push *ECX - # . . call - e8/call skip-chars-matching-in-slice/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . slice->start = EAX - 89/copy 0/mod/indirect 1/rm32/ECX . . . 0/r32/EAX . . # copy EAX to *ECX - # if (slice->start >= end) break - 39/compare 0/mod/indirect 1/rm32/ECX . . . 2/r32/EDX . . # compare *ECX with EDX - 73/jump-if-greater-or-equal-unsigned $parse-array-of-ints:end/disp8 - # slice->end = skip-chars-not-matching-in-slice(slice->start, end, ' ') - # . EAX = skip-chars-not-matching-in-slice(curr, end, ' ') - # . . push args - 68/push 0x20/imm32/space - 52/push-EDX - 50/push-EAX - # . . call - e8/call skip-chars-not-matching-in-slice/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . slice->end = EAX - 89/copy 1/mod/direct 1/rm32/ECX . . . 0/r32/EAX 4/disp8 . # copy EAX to *(ECX+4) - # *out = parse-hex-int(slice) - # . EAX = parse-hex-int(slice) - # . . push args - 51/push-ECX - # . . call - e8/call parse-hex-int/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # *out = EAX - 89/copy 0/mod/indirect 3/rm32/EBX . . . 0/r32/EAX . . # copy EAX to *EBX - # out += 4 - 81 0/subop/add 3/mod/direct 3/rm32/EBX . . . . . 4/imm32 # add to EBX - # slice->start = slice->end - 8b/copy 1/mod/direct 1/rm32/ECX . . . 0/r32/EAX 4/disp8 . # copy *(ECX+4) to EAX - 89/copy 0/mod/indirect 1/rm32/ECX . . . 0/r32/EAX . . # copy EAX to *ECX - 81 0/subop/add 3/mod/direct 1/rm32/ECX . . . . . 4/imm32 # add to ECX - eb/jump $parse-array-of-ints:loop2/disp8 -$parse-array-of-ints:end: - # return EDI - 89/copy 3/mod/direct 0/rm32/EAX . . . 7/r32/EDI . . # copy EDI to EAX - # . reclaim locals - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . restore registers - 5f/pop-to-EDI - 5e/pop-to-ESI - 5b/pop-to-EBX - 5a/pop-to-EDX - 59/pop-to-ECX - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-parse-array-of-ints: - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # var ECX = [1, 2, 3] - 68/push 3/imm32 - 68/push 2/imm32 - 68/push 1/imm32 - 68/push 0xc/imm32/size - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # EAX = parse-array-of-ints(Heap, "1 2 3") - # . . push args - 68/push "1 2 3"/imm32 - 68/push Heap/imm32 - # . . call - e8/call parse-array-of-ints/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # EAX = array-equal?(ECX, EAX) - # . . push args - 50/push-EAX - 51/push-ECX - # . . call - e8/call array-equal?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check-ints-equal(EAX, 1, msg) - # . . push args - 68/push "F - test-parse-array-of-ints"/imm32 - 68/push 1/imm32/true - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-parse-array-of-ints-empty: - # - empty string = empty array - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # EAX = parse-array-of-ints(Heap, "") - # . . push args - 68/push ""/imm32 - 68/push Heap/imm32 - # . . call - e8/call parse-array-of-ints/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check-ints-equal(*EAX, 0, msg) - # . . push args - 68/push "F - test-parse-array-of-ints-empty"/imm32 - 68/push 0/imm32/size - ff 6/subop/push 0/mod/indirect 0/rm32/EAX . . . . . . # push *EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-parse-array-of-ints-just-whitespace: - # - just whitespace = empty array - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # EAX = parse-array-of-ints(Heap, " ") - # . . push args - 68/push " "/imm32 - 68/push Heap/imm32 - # . . call - e8/call parse-array-of-ints/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check-ints-equal(*EAX, 0, msg) - # . . push args - 68/push "F - test-parse-array-of-ints-empty"/imm32 - 68/push 0/imm32/size - ff 6/subop/push 0/mod/indirect 0/rm32/EAX . . . . . . # push *EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-parse-array-of-ints-extra-whitespace: - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # var ECX = [1, 2, 3] - 68/push 3/imm32 - 68/push 2/imm32 - 68/push 1/imm32 - 68/push 0xc/imm32/size - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # EAX = parse-array-of-ints(Heap, " 1 2 3 ") - # . . push args - 68/push " 1 2 3 "/imm32 - 68/push Heap/imm32 - # . . call - e8/call parse-array-of-ints/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # EAX = array-equal?(ECX, EAX) - # . . push args - 50/push-EAX - 51/push-ECX - # . . call - e8/call array-equal?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check-ints-equal(EAX, 1, msg) - # . . push args - 68/push "F - test-parse-array-of-ints-extra-whitespace"/imm32 - 68/push 1/imm32/true - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -# helper for later tests -# compare an array with a string representation of an array literal -check-array-equal: # a : (address array int), expected : (address string), msg : (address string) - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # . save registers - 50/push-EAX - # var b/ECX = parse-array-of-ints(Heap, expected) - # . EAX = parse-array-of-ints(Heap, expected) - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - 68/push Heap/imm32 - # . . call - e8/call parse-array-of-ints/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . b = EAX - 89/copy 3/mod/direct 1/rm32/ECX . . . 0/r32/EAX . . # copy EAX to ECX - # EAX = array-equal?(a, b) - # . . push args - 51/push-ECX - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - # . . call - e8/call array-equal?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check-ints-equal(EAX, 1, msg) - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x10/disp8 . # push *(EBP+16) - 68/push 1/imm32 - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -$check-array-equal:end: - # . restore registers - 58/pop-to-EAX - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-check-array-equal: - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # var ECX = [1, 2, 3] - 68/push 3/imm32 - 68/push 2/imm32 - 68/push 1/imm32 - 68/push 0xc/imm32/size - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # check-array-equal(ECX, "1 2 3", "msg") - # . . push args - 68/push "F - test-check-array-equal"/imm32 - 68/push "1 2 3"/imm32 - 51/push-ECX - # . . call - e8/call check-array-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -== data - -Heap: - # curr - 0/imm32 - # limit - 0/imm32 - -# . . vim:nowrap:textwidth=0 diff --git a/subx/076zero-out.subx b/subx/076zero-out.subx deleted file mode 100644 index bc19dc21..00000000 --- a/subx/076zero-out.subx +++ /dev/null @@ -1,84 +0,0 @@ -# Fill a region of memory with zeroes. - -== code -# instruction effective address register displacement immediate -# . op subop mod rm32 base index scale r32 -# . 1-3 bytes 3 bits 2 bits 3 bits 3 bits 3 bits 2 bits 2 bits 0/1/2/4 bytes 0/1/2/4 bytes - -zero-out: # start : address, len : int - # pseudocode: - # curr/ESI = start - # i/ECX = 0 - # while true - # if (i >= len) break - # *curr = 0 - # ++curr - # ++i - # - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # . save registers - 50/push-EAX - 51/push-ECX - 52/push-EDX - 56/push-ESI - # curr/ESI = start - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 6/r32/ESI 8/disp8 . # copy *(EBP+8) to ESI - # i/ECX = 0 - 31/xor 3/mod/direct 1/rm32/ECX . . . 1/r32/ECX . . # clear ECX - # EDX = len - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 2/r32/EDX 0xc/disp8 . # copy *(EBP+12) to EDX -$zero-out:loop: - # if (i >= len) break - 39/compare 3/mod/direct 1/rm32/ECX . . . 2/r32/EDX . . # compare ECX with EDX - 7d/jump-if-greater-or-equal $zero-out:end/disp8 - # *curr = 0 - c6 0/subop/copy 0/mod/direct 6/rm32/ESI . . . . . 0/imm8 # copy byte to *ESI - # ++curr - 46/increment-ESI - # ++i - 41/increment-ECX - eb/jump $zero-out:loop/disp8 -$zero-out:end: - # . restore registers - 5e/pop-to-ESI - 5a/pop-to-EDX - 59/pop-to-ECX - 58/pop-to-EAX - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-zero-out: - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # region/ECX = 34, 35, 36, 37 - 68/push 0x37363534/imm32 - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # zero-out(ECX, 3) - # . . push args - 68/push 3/imm32/len - 51/push-ECX - # . . call - e8/call zero-out/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # first 3 bytes cleared, fourth left alone - # . check-ints-equal(*ECX, 0x37000000, msg) - # . . push args - 68/push "F - test-zero-out"/imm32 - 68/push 0x37000000/imm32 - ff 6/subop/push 0/mod/indirect 1/rm32/ECX . . . . . . # push *ECX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -# . . vim:nowrap:textwidth=0 diff --git a/subx/077slurp.subx b/subx/077slurp.subx deleted file mode 100644 index 2bf203da..00000000 --- a/subx/077slurp.subx +++ /dev/null @@ -1,161 +0,0 @@ -== code -# instruction effective address register displacement immediate -# . op subop mod rm32 base index scale r32 -# . 1-3 bytes 3 bits 2 bits 3 bits 3 bits 3 bits 2 bits 2 bits 0/1/2/4 bytes 0/1/2/4 bytes - -# read all bytes from 'f' and store them into 's' -# abort if 's' is too small -slurp: # f : (address buffered-file), s : (address stream byte) -> <void> - # pseudocode: - # while true - # if (s->write >= s->length) abort - # if (f->read >= f->write) populate stream from file - # if (f->write == 0) break - # AL = f->data[f->read] - # s->data[s->write] = AL - # ++f->read - # ++s->write - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # . save registers - 50/push-EAX - 51/push-ECX - 52/push-EDX - 56/push-ESI - 57/push-EDI - # ESI = f - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 6/r32/ESI 8/disp8 . # copy *(EBP+8) to ESI - # ECX = f->read - 8b/copy 1/mod/*+disp8 6/rm32/ESI . . . 1/r32/ECX 8/disp8 . # copy *(ESI+8) to ECX - # EDI = s - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 7/r32/EDI 0xc/disp8 . # copy *(EBP+12) to EDI - # EDX = s->write - 8b/copy 0/mod/indirect 7/rm32/EDI . . . 2/r32/EDX . . # copy *EDI to EDX -$slurp:loop: - # if (s->write >= s->length) abort - 3b/compare 1/mod/*+disp8 7/rm32/EDI . . . 2/r32/EDX 8/disp8 . # compare EDX with *(EDI+8) - 7d/jump-if-greater-or-equal $slurp:abort/disp8 - # if (f->read >= f->write) populate stream from file - 3b/compare 1/mod/*+disp8 6/rm32/ESI . . . 1/r32/ECX 4/disp8 . # compare ECX with *(ESI+4) - 7c/jump-if-lesser $slurp:from-stream/disp8 - # . clear-stream(stream = f+4) - # . . push args - 8d/copy-address 1/mod/*+disp8 6/rm32/ESI . . . 0/r32/EAX 4/disp8 . # copy ESI+4 to EAX - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . f->read must now be 0; update its cache at ECX - 31/xor 3/mod/direct 1/rm32/ECX . . . 1/r32/ECX . . # clear ECX - # . EAX = read(f->fd, stream = f+4) - # . . push args - 50/push-EAX - ff 6/subop/push 0/mod/indirect 6/rm32/ESI . . . . . . # push *ESI - # . . call - e8/call read/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # if (f->write == 0) break - # since f->read was initially 0, EAX is the same as f->write - # . if (EAX == 0) return true - 3d/compare-EAX-and 0/imm32 - 74/jump-if-equal $slurp:end/disp8 -$slurp:from-stream: - # AL = f->data[f->read] - 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX - 8a/copy-byte 1/mod/*+disp8 4/rm32/sib 6/base/ESI 1/index/ECX . 0/r32/AL 0x10/disp8 . # copy byte at *(ESI+ECX+16) to AL - # s->data[s->write] = AL - 88/copy-byte 1/mod/*+disp8 4/rm32/sib 7/base/EDI 2/index/EDX . 0/r32/AL 0xc/disp8 . # copy AL to *(EDI+EDX+12) - # ++f->read - 41/increment-ECX - # ++s->write - 42/increment-EDX - eb/jump $slurp:loop/disp8 -$slurp:end: - # save f->read - 89/copy 1/mod/*+disp8 6/rm32/ESI . . . 1/r32/ECX 8/disp8 . # copy ECX to *(ESI+8) - # save s->write - 89/copy 0/mod/indirect 7/rm32/EDI . . . 2/r32/EDX . . # copy EDX to *EDI - # . restore registers - 5f/pop-to-EDI - 5e/pop-to-ESI - 5a/pop-to-EDX - 59/pop-to-ECX - 58/pop-to-EAX - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -$slurp:abort: - # . _write(2/stderr, error) - # . . push args - 68/push "slurp: destination too small\n"/imm32 - 68/push 2/imm32/stderr - # . . call - e8/call _write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . syscall(exit, 1) - bb/copy-to-EBX 1/imm32 - b8/copy-to-EAX 1/imm32/exit - cd/syscall 0x80/imm8 - # never gets here - -test-slurp: - # setup - # . clear-stream(_test-stream) - # . . push args - 68/push _test-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-buffered-file+4) - # . . push args - b8/copy-to-EAX _test-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-tmp-stream) - # . . push args - 68/push _test-tmp-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # write(_test-stream, "ab\ncd") - # . . push args - 68/push "ab\ncd"/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # read a line from _test-stream (buffered by _test-buffered-file) into _test-tmp-stream - # . EAX = slurp(_test-buffered-file, _test-tmp-stream) - # . . push args - 68/push _test-tmp-stream/imm32 - 68/push _test-buffered-file/imm32 - # . . call - e8/call slurp/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check-stream-equal(_test-tmp-stream, "ab\ncd", msg) - # . . push args - 68/push "F - test-slurp"/imm32 - 68/push "ab\ncd"/imm32 - 68/push _test-tmp-stream/imm32 - # . . call - e8/call check-stream-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # end - c3/return - -# . . vim:nowrap:textwidth=0 diff --git a/subx/100index b/subx/100index deleted file mode 100644 index 355b43d3..00000000 --- a/subx/100index +++ /dev/null @@ -1,11 +0,0 @@ --- Overview of layers -0-9: infrastructure independent of this program -10-29: level 1, the SubX subset of the 32-bit x86 ISA -30-39: level 2, moving past counting bytes with operands and labels - -See layer 29 for the description of 'level'. - ---- Overview of tracing depths -0: errors -1-98: app level traces for different SubX programs -99: low-level details of the VM diff --git a/subx/Readme.md b/subx/Readme.md deleted file mode 100644 index b6f3e291..00000000 --- a/subx/Readme.md +++ /dev/null @@ -1,777 +0,0 @@ -## SubX: A minimalist assembly language for a subset of the x86 ISA - -SubX is a simple, minimalist stack for programming your computer. - - ```sh - $ git clone https://github.com/akkartik/mu - $ cd mu/subx - $ ./subx # print out a help message - ``` - -SubX is designed: - -* to enable automation of arbitrary manual tests -* to be easy to implement in itself, and -* to help learn and teach the x86 instruction set. - -It requires a Unix-like environment with a C++ compiler (Linux or BSD or Mac -OS). Running `subx` will transparently compile it as necessary. - -[![Build Status](https://api.travis-ci.org/akkartik/mu.svg?branch=master)](https://travis-ci.org/akkartik/mu) - -You can generate native ELF binaries with it that run on a bare Linux -kernel. No other dependencies needed. - - ```sh - $ ./subx translate examples/ex1.subx -o examples/ex1 - $ ./examples/ex1 # only on Linux - $ echo $? - 42 - ``` - -You can run the generated binaries on an interpreter/VM for better error -messages. - - ```sh - $ ./subx run examples/ex1 # on Linux or BSD or OS X - $ echo $? - 42 - ``` - -Emulated runs generate a trace that permits [time-travel debugging](https://github.com/akkartik/mu/blob/master/browse_trace/Readme.md). - - ```sh - $ ./subx --debug translate examples/factorial.subx -o examples/factorial - saving address->label information to 'labels' - saving address->source information to 'source_lines' - - $ ./subx --debug --trace run examples/factorial - saving trace to 'last_run' - - $ ../browse_trace/browse_trace last_run # text-mode debugger UI - ``` - -You can write tests for your assembly programs. The entire stack is thoroughly -covered by automated tests. SubX's tagline: tests before syntax. - - ```sh - $ ./subx test - $ ./subx run apps/factorial test - ``` - -You can use SubX to translate itself. For example, running natively on Linux: - - ```sh - # generate translator phases using the C++ translator - $ ./subx translate 0*.subx apps/subx-common.subx apps/hex.subx -o hex - $ ./subx translate 0*.subx apps/subx-common.subx apps/survey.subx -o survey - $ ./subx translate 0*.subx apps/subx-common.subx apps/pack.subx -o pack - $ ./subx translate 0*.subx apps/subx-common.subx apps/assort.subx -o assort - $ ./subx translate 0*.subx apps/subx-common.subx apps/dquotes.subx -o dquotes - $ ./subx translate 0*.subx apps/subx-common.subx apps/tests.subx -o tests - $ chmod +x hex survey pack assort dquotes tests - - # use the generated translator phases to translate SubX programs - $ cat examples/ex1.subx |./tests |./dquotes |./assort |./pack |./survey |./hex > a.elf - $ chmod +x a.elf - $ ./a.elf - $ echo $? - 42 - - # or, automating the above steps - $ ./ntranslate ex1.subx - $ chmod +x a.elf - $ ./a.elf - $ echo $? - 42 - ``` - -Or, running in a VM on other platforms: - - ``` - $ ./translate ex1.subx # generates identical a.elf to above - $ ./subx run a.elf - $ echo $? - 42 - ``` - -You can use it to learn about the x86 processor that (almost certainly) runs -your computer. (See below.) - -You can read its tiny zero-dependency internals and understand how they work. -You can hack on it, and its thorough tests will raise the alarm when you break -something. - -Eventually you will be able to program in higher-level notations. But you'll -always have tests as guardrails and traces for inspecting runs. The entire -stack will always be designed for others to comprehend. You'll always be -empowered to understand how things work, and change what doesn't work for you. -You'll always be expected to make small changes during upgrades. - -## What it looks like - -Here is the first example we ran above, a program that just returns 42: - - ```sh - bb/copy-to-EBX 0x2a/imm32 # 42 in hex - b8/copy-to-EAX 1/imm32/exit - cd/syscall 0x80/imm8 - ``` - -Every line contains at most one instruction. Instructions consist of words -separated by whitespace. Words may be _opcodes_ (defining the operation being -performed) or _arguments_ (specifying the data the operation acts on). Any -word can have extra _metadata_ attached to it after `/`. Some metadata is -required (like the `/imm32` and `/imm8` above), but unrecognized metadata is -silently skipped so you can attach comments to words (like the instruction -name `/copy-to-EAX` above, or the `/exit` operand). - -SubX doesn't provide much syntax (there aren't even the usual mnemonics for -opcodes), but it _does_ provide error-checking. If you miss an operand or -accidentally add an extra operand you'll get a nice error. SubX won't arbitrarily -interpret bytes of data as instructions or vice versa. - -So much for syntax. What do all these numbers actually _mean_? SubX supports a -small subset of the 32-bit x86 instruction set that likely runs on your -computer. (Think of the name as short for "sub-x86".) Instructions operate on -a few registers: - -* Six general-purpose 32-bit registers: EAX, EBX, ECX, EDX, ESI and EDI -* Two additional 32-bit registers: ESP and EBP (I suggest you only use these to - manage the call stack.) -* Four 1-bit _flag_ registers for conditional branching: - - zero/equal flag ZF - - sign flag SF - - overflow flag OF - - carry flag CF - -SubX programs consist of instructions like `89/copy`, `01/add`, `3d/compare` -and `52/push-ECX` which modify these registers as well as a byte-addressable -memory. For a complete list of supported instructions, run `subx help opcodes`. - -(SubX doesn't support floating-point registers yet. Intel processors support -an 8-bit mode, 16-bit mode and 64-bit mode. SubX will never support them. -There are other flags. SubX will never support them. There are also _many_ -more instructions that SubX will never support.) - -It's worth distinguishing between an instruction's _operands_ and its _arguments_. -Arguments are provided directly in instructions. Operands are pieces of data -in register or memory that are operated on by instructions. Intel processors -determine operands from arguments in fairly complex ways. - -## Lengthy interlude: How x86 instructions compute operands - -The [Intel processor manual](http://www.intel.com/content/dam/www/public/us/en/documents/manuals/64-ia-32-architectures-software-developer-instruction-set-reference-manual-325383.pdf) -is the final source of truth on the x86 instruction set, but it can be -forbidding to make sense of, so here's a quick orientation. You will need -familiarity with binary numbers, and maybe a few other things. Email [me](mailto:mu@akkartik.com) -any time if something isn't clear. I love explaining this stuff for as long as -it takes. The bad news is that it takes some getting used to. The good news is -that internalizing the next 500 words will give you a significantly deeper -understanding of your computer. - -Most instructions operate on an operand in register or memory ('reg/mem'), and -a second operand in a register. The register operand is specified fairly -directly using the 3-bit `/r32` argument: - - - 0 means register `EAX` - - 1 means register `ECX` - - 2 means register `EDX` - - 3 means register `EBX` - - 4 means register `ESP` - - 5 means register `EBP` - - 6 means register `ESI` - - 7 means register `EDI` - -The reg/mem operand, however, gets complex. It can be specified by 1-7 -arguments, each ranging in size from 2 bits to 4 bytes. - -The key argument that's always present for reg/mem operands is `/mod`, the -_addressing mode_. This is a 2-bit argument that can take 4 possible values, -and it determines what other arguments are required, and how to interpret -them. - -* If `/mod` is `3`: the operand is in the register described by the 3-bit - `/rm32` argument similarly to `/r32` above. - -* If `/mod` is `0`: the operand is in the address provided in the register - described by `/rm32`. That's `*rm32` in C syntax. - -* If `/mod` is `1`: the operand is in the address provided by adding the - register in `/rm32` with the (1-byte) displacement. That's `*(rm32 + /disp8)` - in C syntax. - -* If `/mod` is `2`: the operand is in the address provided by adding the - register in `/rm32` with the (4-byte) displacement. That's `*(/rm32 + - /disp32)` in C syntax. - -In the last three cases, one exception occurs when the `/rm32` argument -contains `4`. Rather than encoding register `ESP`, it means the address is -provided by three _whole new_ arguments (`/base`, `/index` and `/scale`) in a -_totally_ different way: - - ``` - reg/mem = *(/base + /index * (2 ^ /scale)) - ``` - -(There are a couple more exceptions ☹; see [Table 2-2](modrm.pdf) and [Table 2-3](sib.pdf) -of the Intel manual for the complete story.) - -Phew, that was a lot to take in. Some examples to work through as you reread -and digest it: - -1. To read directly from the EAX register, `/mod` must be `3` (direct mode), - and `/rm32` must be `0`. There must be no `/base`, `/index` or `/scale` - arguments. - -1. To read from `*EAX` (in C syntax), `/mod` must be `0` (indirect mode), and - the `/rm32` argument must be `0`. There must be no `/base`, `/index` or - `/scale` arguments. - -1. To read from `*(EAX+4)`, `/mod` must be `1` (indirect + disp8 mode), - `/rm32` must be `0`, there must be no SIB byte, and there must be a single - displacement byte containing `4`. - -1. To read from `*(EAX+ECX+4)`, one approach would be to set `/mod` to `1` as - above, `/rm32` to `4` (SIB byte next), `/base` to `0`, `/index` to `1` - (ECX) and a single displacement byte to `4`. (What should the `scale` bits - be? Can you think of another approach?) - -1. To read from `*(EAX+ECX+1000)`, one approach would be: - - `/mod`: `2` (indirect + disp32) - - `/rm32`: `4` (`/base`, `/index` and `/scale` arguments required) - - `/base`: `0` (EAX) - - `/index`: `1` (ECX) - - `/disp32`: 4 bytes containing `1000` - -## Putting it all together - -Here's a more meaty example: - -<img alt='examples/ex3.subx' src='../html/subx/ex3.png'> - -This program sums the first 10 natural numbers. By convention I use horizontal -tabstops to help read instructions, dots to help follow the long lines, -comments before groups of instructions to describe their high-level purpose, -and comments at the end of complex instructions to state the low-level -operation they perform. Numbers are always in hexadecimal (base 16) and must -start with a digit ('0'..'9'); use the '0x' prefix when a number starts with a -letter ('a'..'f'). I tend to also include it as a reminder when numbers look -like decimal numbers. - -Try running this example now: - -```sh -$ ./subx translate examples/ex3.subx -o examples/ex3 -$ ./subx run examples/ex3 -$ echo $? -55 -``` - -If you're on Linux you can also run it natively: - -```sh -$ ./examples/ex3 -$ echo $? -55 -``` - -Use it now to follow along for a more complete tour of SubX syntax. - -## The syntax of SubX programs - -SubX programs map to the same ELF binaries that a conventional Linux system -uses. Linux ELF binaries consist of a series of _segments_. In particular, they -distinguish between code and data. Correspondingly, SubX programs consist of a -series of segments, each starting with a header line: `==` followed by a name -and approximate starting address. - -All code must lie in a segment called 'code'. Execution begins at the start of -the `code` segment by default. - -You can reuse segment names: - -``` -== code -...A... - -== data -...B... - -== code -...C... -``` - -The `code` segment now contains the instructions of `A` as well as `C`. - -Within the `code` segment, each line contains a comment, label or instruction. -Comments start with a `#` and are ignored. Labels should always be the first -word on a line, and they end with a `:`. - -Instruction arguments must specify their type, from: - - `/mod` - - `/rm32` - - `/r32` - - `/subop` (sometimes the `/r32` bits in an instruction are used as an extra opcode) - - displacement: `/disp8` or `/disp32` - - immediate: `/imm8` or `/imm32` - -Different instructions (opcodes) require different arguments. SubX will -validate each instruction in your programs, and raise an error anytime you -miss or spuriously add an argument. - -I recommend you order arguments consistently in your programs. SubX allows -arguments in any order, but only because that's simplest to explain/implement. -Switching order from instruction to instruction is likely to add to the -reader's burden. Here's the order I've been using after opcodes: - -``` - |<--------- reg/mem --------->| |<- reg/mem? ->| -/subop /mod /rm32 /base /index /scale /r32 /displacement /immediate -``` - -Instructions can refer to labels in displacement or immediate arguments, and -they'll obtain a value based on the address of the label: immediate arguments -will contain the address directly, while displacement arguments will contain -the difference between the address and the address of the current instruction. -The latter is mostly useful for `jump` and `call` instructions. - -Functions are defined using labels. By convention, labels internal to functions -(that must only be jumped to) start with a `$`. Any other labels must only be -called, never jumped to. All labels must be unique. - -A special label is `Entry`, which can be used to specify/override the entry -point of the program. It doesn't have to be unique, and the latest definition -will override earlier ones. - -(The `Entry` label, along with duplicate segment headers, allows programs to -be built up incrementally out of multiple [_layers_](http://akkartik.name/post/wart-layers).) - -The data segment consists of labels as before and byte values. Referring to -data labels in either `code` segment instructions or `data` segment values -(using the `imm32` metadata either way) yields their address. - -Automatic tests are an important part of SubX, and there's a simple mechanism -to provide a test harness: all functions that start with `test-` are called in -turn by a special, auto-generated function called `run-tests`. How you choose -to call it is up to you. - -I try to keep things simple so that there's less work to do when I eventually -implement SubX in SubX. But there _is_ one convenience: instructions can -provide a string literal surrounded by quotes (`"`) in an `imm32` argument. -SubX will transparently copy it to the `data` segment and replace it with its -address. Strings are the only place where a SubX word is allowed to contain -spaces. - -That should be enough information for writing SubX programs. The `examples/` -directory provides some fodder for practice, giving a more gradual introduction -to SubX features. This repo includes the binary for all examples. At any -commit, an example's binary should be identical bit for bit with the result of -translating the corresponding `.subx` file. The binary should also be natively -runnable on a Linux system running on Intel x86 processors, either 32- or -64-bit. If either of these invariants is broken it's a bug on my part. - -## Roadmap and status - -* Self-hosting. (✓) - - `tests |dquotes |assort |pack |survey |hex` - -* A script to package SubX together with a minimal Linux kernel image - (compiled from source, of course). - -* Testable, dependency-injected vocabulary of primitives - - Streams: `read()`, `write()`. (✓) - - `exit()` (✓) - - Sockets - - Files - - Concurrency, and a framework for testing blocking code - -* Higher-level notations. Like programming languages, but with thinner - implementations that you can -- and are expected to! -- modify. - - syntax for addressing modes: `%reg`, `*reg`, `*(reg+disp)`, - `*(reg+reg+disp)`, `*(reg+reg<<n + disp)` - - function calls in a single line, using addressing modes for arguments - - syntax for controlling a type checker, akin to the top-level Mu language - - a register allocation _verifier_. Programmer provides registers for - variables; verifier checks that register reads are for the same type that - was last written -- across all control flow paths. - -## Running - -`subx` currently has the following sub-commands: - -* `subx help`: some helpful documentation to have at your fingertips. - -* `subx test`: runs all automated tests. - -* `subx translate <input files> -o <output ELF binary>`: translates `.subx` - files into an executable ELF binary. - -* `subx run <ELF binary>`: simulates running the ELF binaries emitted by `subx - translate`. Useful for debugging, and also enables more thorough testing of - `translate`. - - Remember, not all 32-bit Linux binaries are guaranteed to run. I'm not - building general infrastructure here for all of the x86 instruction set. - SubX is about programming with a small, regular subset of 32-bit x86. - -## A few hints for debugging - -Writing programs in SubX is surprisingly pleasant and addictive. Reading -programs is a work in progress, and hopefully the extensive unit tests help. -However, _debugging_ programs is where one really faces up to the low-level -nature of SubX. Even the smallest modifications need testing to make sure they -work. In my experience, there is no modification so small that I get it working -on the first attempt. And when it doesn't work, there are no clear error -messages. Machine code is too simple-minded for that. You can't use a debugger, -since SubX's simplistic ELF binaries contain no debugging information. So -debugging requires returning to basics and practicing with a new, more -rudimentary but hopefully still workable toolkit: - -* Start by nailing down a concrete set of steps for reproducibly obtaining the - error or erroneous behavior. - -* If possible, turn the steps into a failing test. It's not always possible, - but SubX's primary goal is to keep improving the variety of tests one can - write. - -* Start running the single failing test alone. This involves modifying the top - of the program (or the final `.subx` file passed in to `subx translate`) by - replacing the call to `run-tests` with a call to the appropriate `test-` - function. - -* Generate a trace for the failing test while running your program in emulated - mode (`subx run`): - ``` - $ ./subx translate input.subx -o binary - $ ./subx --trace run binary arg1 arg2 2>trace - ``` - The ability to generate a trace is the essential reason for the existence of - `subx run` mode. It gives far better visibility into program internals than - running natively. - -* As a further refinement, it is possible to render label names in the trace - by adding a second flag to both the `translate` and `run` commands: - ``` - $ ./subx --debug translate input.subx -o binary - $ ./subx --debug --trace run binary arg1 arg2 2>trace - ``` - `subx --debug translate` emits a mapping from label to address in a file - called `labels`. `subx --debug --trace run` reads in the `labels` file at - the start and prints out any matching label name as it traces each instruction - executed. - - Here's a sample of what a trace looks like, with a few boxes highlighted: - - <img alt='trace example' src='../html/subx/trace.png'> - - Each of the green boxes shows the trace emitted for a single instruction. - It starts with a line of the form `run: inst: ___` followed by the opcode - for the instruction, the state of registers before the instruction executes, - and various other facts deduced during execution. Some instructions first - print a matching label. In the above screenshot, the red boxes show that - address `0x0900005e` maps to label `$loop` and presumably marks the start of - some loop. Function names get similar `run: == label` lines. - -* One trick when emitting traces with labels: - ``` - $ grep label trace - ``` - This is useful for quickly showing you the control flow for the run, and the - function executing when the error occurred. I find it useful to start with - this information, only looking at the complete trace after I've gotten - oriented on the control flow. Did it get to the loop I just modified? How - many times did it go through the loop? - -* Once you have SubX displaying labels in traces, it's a short step to modify - the program to insert more labels just to gain more insight. For example, - consider the following function: - - <img alt='control example -- before' src='../html/subx/control0.png'> - - This function contains a series of jump instructions. If a trace shows - `is-hex-lowercase-byte?` being encountered, and then `$is-hex-lowercase-byte?:end` - being encountered, it's still ambiguous what happened. Did we hit an early - exit, or did we execute all the way through? To clarify this, add temporary - labels after each jump: - - <img alt='control example -- after' src='../html/subx/control1.png'> - - Now the trace should have a lot more detail on which of these labels was - reached, and precisely when the exit was taken. - -* If you find yourself wondering, "when did the contents of this memory - address change?", `subx run` has some rudimentary support for _watch - points_. Just insert a label starting with `$watch-` before an instruction - that writes to the address, and its value will start getting dumped to the - trace after every instruction thereafter. - -* Once we have a sense for precisely which instructions we want to look at, - it's time to look at the trace as a whole. Key is the state of registers - before each instruction. If a function is receiving bad arguments it becomes - natural to inspect what values were pushed on the stack before calling it, - tracing back further from there, and so on. - - I occasionally want to see the precise state of the stack segment, in which - case I uncomment a commented-out call to `dump_stack()` in the `vm.cc` - layer. It makes the trace a lot more verbose and a lot less dense, necessitating - a lot more scrolling around, so I keep it turned off most of the time. - -* If the trace seems overwhelming, try [browsing it](https://github.com/akkartik/mu/blob/master/browse_trace/Readme.md) - in the 'time-travel debugger'. - -Hopefully these hints are enough to get you started. The main thing to -remember is to not be afraid of modifying the sources. A good debugging -session gets into a nice rhythm of generating a trace, staring at it for a -while, modifying the sources, regenerating the trace, and so on. Email -[me](mailto:mu@akkartik.com) if you'd like another pair of eyes to stare at a -trace, or if you have questions or complaints. - -## Reference documentation on available primitives - -### Data Structures - -* Kernel strings: null-terminated arrays of bytes. Unsafe and to be avoided, - but needed for interacting with the kernel. - -* Strings: length-prefixed arrays of bytes. String contents are preceded by - 4 bytes (32 bytes) containing the `length` of the array. - -* Slices: a pair of 32-bit addresses denoting a [half-open](https://en.wikipedia.org/wiki/Interval_(mathematics)) - \[`start`, `end`) interval to live memory with a consistent lifetime. - - Invariant: `start` <= `end` - -* Streams: strings prefixed by 32-bit `write` and `read` indexes that the next - write or read goes to, respectively. - - * offset 0: write index - * offset 4: read index - * offset 8: length of array (in bytes) - * offset 12: start of array data - - Invariant: 0 <= `read` <= `write` <= `length` - -* File descriptors (fd): Low-level 32-bit integers that the kernel uses to - track files opened by the program. - -* File: 32-bit value containing either a fd or an address to a stream (fake - file). - -* Buffered files (buffered-file): Contain a file descriptor and a stream for - buffering reads/writes. Each `buffered-file` must exclusively perform either - reads or writes. - -### 'system calls' - -As I said at the top, a primary design goal of SubX (and Mu more broadly) is -to explore ways to turn arbitrary manual tests into reproducible automated -tests. SubX aims for this goal by baking testable interfaces deep into the -stack, at the OS syscall level. The idea is that every syscall that interacts -with hardware (and so the environment) should be *dependency injected* so that -it's possible to insert fake hardware in tests. - -But those are big goals. Here are the syscalls I have so far: - -* `write`: takes two arguments, a file `f` and an address to array `s`. - - Comparing this interface with the Unix `write()` syscall shows two benefits: - - 1. SubX can handle 'fake' file descriptors in tests. - - 1. `write()` accepts buffer and its length in separate arguments, which - requires callers to manage the two separately and so can be error-prone. - SubX's wrapper keeps the two together to increase the chances that we - never accidentally go out of array bounds. - -* `read`: takes two arguments, a file `f` and an address to stream `s`. Reads - as much data from `f` as can fit in (the free space of) `s`. - - Like with `write()`, this wrapper around the Unix `read()` syscall adds the - ability to handle 'fake' file descriptors in tests, and reduces the chances - of clobbering outside array bounds. - - One bit of weirdness here: in tests we do a redundant copy from one stream - to another. See [the comments before the implementation](http://akkartik.github.io/mu/html/subx/058read.subx.html) - for a discussion of alternative interfaces. - -* `stop`: takes two arguments: - - `ed` is an address to an _exit descriptor_. Exit descriptors allow us to - `exit()` the program in production, but return to the test harness within - tests. That allows tests to make assertions about when `exit()` is called. - - `value` is the status code to `exit()` with. - - For more details on exit descriptors and how to create one, see [the - comments before the implementation](http://akkartik.github.io/mu/html/subx/057stop.subx.html). - -* `new-segment` - - Allocates a whole new segment of memory for the program, discontiguous with - both existing code and data (heap) segments. Just a more opinionated form of - [`mmap`](http://man7.org/linux/man-pages/man2/mmap.2.html). - -* `allocate`: takes two arguments, an address to allocation-descriptor `ad` - and an integer `n` - - Allocates a contiguous range of memory that is guaranteed to be exclusively - available to the caller. Returns the starting address to the range in `EAX`. - - An allocation descriptor tracks allocated vs available addresses in some - contiguous range of memory. The int specifies the number of bytes to allocate. - - Explicitly passing in an allocation descriptor allows for nested memory - management, where a sub-system gets a chunk of memory and further parcels it - out to individual allocations. Particularly helpful for (surprise) tests. - -* ... _(to be continued)_ - -I will continue to import syscalls over time from [the old Mu VM in the parent -directory](https://github.com/akkartik/mu), which has experimented with -interfaces for the screen, keyboard, mouse, disk and network. - -### primitives built atop system calls - -_(Compound arguments are usually passed in by reference. Where the results are -compound objects that don't fit in a register, the caller usually passes in -allocated memory for it.)_ - -#### assertions for tests -* `check-ints-equal`: fails current test if given ints aren't equal -* `check-stream-equal`: fails current test if stream doesn't match string -* `check-next-stream-line-equal`: fails current test if next line of stream - until newline doesn't match string - -#### error handling -* `error`: takes three arguments, an exit-descriptor, a file and a string (message) - - Prints out the message to the file and then exits using the provided - exit-descriptor. - -* `error-byte`: like `error` but takes an extra byte value that it prints out - at the end of the message. - -#### predicates -* `kernel-string-equal?`: compares a kernel string with a string -* `string-equal?`: compares two strings -* `stream-data-equal?`: compares a stream with a string -* `next-stream-line-equal?`: compares with string the next line in a stream, from - `read` index to newline - -* `slice-empty?`: checks if the `start` and `end` of a slice are equal -* `slice-equal?`: compares a slice with a string -* `slice-starts-with?`: compares the start of a slice with a string -* `slice-ends-with?`: compares the end of a slice with a string - -#### writing to disk -* `write`: string -> file - - Can also be used to cat a string into a stream. - - Will abort the entire program if destination is a stream and doesn't have - enough room. -* `write-stream`: stream -> file - - Can also be used to cat one stream into another. - - Will abort the entire program if destination is a stream and doesn't have - enough room. -* `write-slice`: slice -> stream - - Will abort the entire program if there isn't enough room in the - destination stream. -* `append-byte`: int -> stream - - Will abort the entire program if there isn't enough room in the - destination stream. -* `append-byte-hex`: int -> stream - - textual representation in hex, no '0x' prefix - - Will abort the entire program if there isn't enough room in the - destination stream. -* `print-int32`: int -> stream - - textual representation in hex, including '0x' prefix - - Will abort the entire program if there isn't enough room in the - destination stream. -* `write-buffered`: string -> buffered-file -* `write-slice-buffered`: slice -> buffered-file -* `flush`: buffered-file -* `write-byte-buffered`: int -> buffered-file -* `print-byte-buffered`: int -> buffered-file - - textual representation in hex, no '0x' prefix -* `print-int32-buffered`: int -> buffered-file - - textual representation in hex, including '0x' prefix - -#### reading from disk -* `read`: file -> stream - - Can also be used to cat one stream into another. - - Will silently stop reading when destination runs out of space. -* `read-byte-buffered`: buffered-file -> byte -* `read-line-buffered`: buffered-file -> stream - - Will abort the entire program if there isn't enough room. - -#### non-IO operations on streams -* `new-stream`: allocates space for a stream of `n` elements, each occupying - `b` bytes. - - Will abort the entire program if `n*b` requires more than 32 bits. -* `clear-stream`: resets everything in the stream to `0` (except its `length`). -* `rewind-stream`: resets the read index of the stream to `0` without modifying - its contents. - -#### reading/writing hex representations of integers -* `is-hex-int?`: takes a slice argument, returns boolean result in `EAX` -* `parse-hex-int`: takes a slice argument, returns int result in `EAX` -* `is-hex-digit?`: takes a 32-bit word containing a single byte, returns - boolean result in `EAX`. -* `from-hex-char`: takes a hexadecimal digit character in EAX, returns its - numeric value in `EAX` -* `to-hex-char`: takes a single-digit numeric value in EAX, returns its - corresponding hexadecimal character in `EAX` - -#### tokenization - -from a stream: -* `next-token`: stream, delimiter byte -> slice -* `skip-chars-matching`: stream, delimiter byte -* `skip-chars-not-matching`: stream, delimiter byte - -from a slice: -* `next-token-from-slice`: start, end, delimiter byte -> slice - - Given a slice and a delimiter byte, returns a new slice inside the input - that ends at the delimiter byte. - -* `skip-chars-matching-in-slice`: curr, end, delimiter byte -> new-curr (in `EAX`) -* `skip-chars-not-matching-in-slice`: curr, end, delimiter byte -> new-curr (in `EAX`) - -## Conclusion - -The hypothesis of Mu and SubX is that designing the entire system to be -testable from day 1 and from the ground up would radically impact the culture -of the eco-system in a way that no bolted-on tool or service at higher levels -can replicate: - -* Tests would make it easier to write programs that can be easily understood - by newcomers. - -* More broad-based understanding would lead to more forks. - -* Tests would make it easy to share code across forks. Copy the tests over, - and then copy code over and polish it until the tests pass. Manual work, but - tractable and without major risks. - -* The community would gain a diversified portfolio of forks for each program, - a “wavefront” of possible combinations of features and alternative - implementations of features. Application writers who wrote thorough tests - for their apps (something they just can’t do today) would be able to bounce - around between forks more easily without getting locked in to a single one - as currently happens. - -* There would be a stronger culture of reviewing the code for programs you use - or libraries you depend on. [More eyeballs would make more bugs shallow.](https://en.wikipedia.org/wiki/Linus%27s_Law) - -## Resources - -* [Single-page cheatsheet for the x86 ISA](https://net.cs.uni-bonn.de/fileadmin/user_upload/plohmann/x86_opcode_structure_and_instruction_overview.pdf) - (pdf; [cached local copy](https://github.com/akkartik/mu/blob/master/subx/cheatsheet.pdf)) -* [Concise reference for the x86 ISA](https://c9x.me/x86) -* [Intel processor manual](http://www.intel.com/content/dam/www/public/us/en/documents/manuals/64-ia-32-architectures-software-developer-instruction-set-reference-manual-325383.pdf) (pdf) -* [Some details on the unconventional organization of this project.](http://akkartik.name/post/four-repos) - -## Inspirations - -* [“Creating tiny ELF executables”](https://www.muppetlabs.com/~breadbox/software/tiny/teensy.html) -* [“Bootstrapping a compiler from nothing”](http://web.archive.org/web/20061108010907/http://www.rano.org/bcompiler.html) -* Forth implementations like [StoneKnifeForth](https://github.com/kragen/stoneknifeforth) diff --git a/subx/apps/Readme.md b/subx/apps/Readme.md deleted file mode 100644 index b76c5a8b..00000000 --- a/subx/apps/Readme.md +++ /dev/null @@ -1,2 +0,0 @@ -Larger programs than in the subx/examples/ subdirectory, combining the -techniques demonstrated there. diff --git a/subx/apps/assort b/subx/apps/assort deleted file mode 100755 index 70d7aaf3..00000000 --- a/subx/apps/assort +++ /dev/null Binary files differdiff --git a/subx/apps/assort.subx b/subx/apps/assort.subx deleted file mode 100644 index 801e52d0..00000000 --- a/subx/apps/assort.subx +++ /dev/null @@ -1,911 +0,0 @@ -# Read a series of segments from stdin and concatenate segments with the same -# name on stdout. -# -# Segments are emitted in order of first encounter. -# -# Drop lines that are all comments. They could get misleading after assortment -# because we don't know if they refer to the line above or the line below. -# -# To run (from the subx/ directory): -# $ ./subx translate *.subx apps/assort.subx -o apps/assort -# $ cat x -# == code -# abc -# == code -# def -# $ cat x |./subx run apps/assort -# == code -# abc -# def - -== code -# instruction effective address register displacement immediate -# . op subop mod rm32 base index scale r32 -# . 1-3 bytes 3 bits 2 bits 3 bits 3 bits 3 bits 2 bits 2 bits 0/1/2/4 bytes 0/1/2/4 bytes - -Entry: - # initialize heap - # . Heap = new-segment(Heap-size) - # . . push args - 68/push Heap/imm32 - ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Heap-size/disp32 # push *Heap-size - # . . call - e8/call new-segment/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - - # run tests if necessary, convert stdin if not - # . prolog - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # - if argc > 1 and argv[1] == "test", then return run_tests() - # . argc > 1 - 81 7/subop/compare 1/mod/*+disp8 5/rm32/EBP . . . . 0/disp8 1/imm32 # compare *EBP - 7e/jump-if-lesser-or-equal $run-main/disp8 - # . argv[1] == "test" - # . . push args - 68/push "test"/imm32 - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - # . . call - e8/call kernel-string-equal?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . check result - 3d/compare-EAX-and 1/imm32 - 75/jump-if-not-equal $run-main/disp8 - # . run-tests() - e8/call run-tests/disp32 - 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 3/r32/EBX Num-test-failures/disp32 # copy *Num-test-failures to EBX - eb/jump $main:end/disp8 -$run-main: - # - otherwise convert stdin - # var ed/EAX : exit-descriptor - 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # subtract from ESP - 89/copy 3/mod/direct 0/rm32/EAX . . . 4/r32/ESP . . # copy ESP to EAX - # configure ed to really exit() - # . ed->target = 0 - c7 0/subop/copy 0/mod/direct 0/rm32/EAX . . . . . 0/imm32 # copy to *EAX - # return convert(Stdin, 1/stdout, 2/stderr, ed) - # . . push args - 50/push-EAX/ed - 68/push Stderr/imm32 - 68/push Stdout/imm32 - 68/push Stdin/imm32 - # . . call - e8/call convert/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP - # . syscall(exit, 0) - bb/copy-to-EBX 0/imm32 -$main:end: - b8/copy-to-EAX 1/imm32/exit - cd/syscall 0x80/imm8 - -# data structure: -# table: (address stream {string, (address stream byte)}) (8 bytes per row) -# inefficient; uses sequential search for looking up segments by name - -convert: # in : (address buffered-file), out : (address buffered-file) -> <void> - # pseudocode: - # var table : (address stream) = new-stream(10 rows, 8 bytes each) - # read-segments(in, table) - # write-segments(out, table) - # - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # . save registers - 51/push-ECX - # var table/ECX : (address stream byte) = stream(10 * 8) - 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 0x50/imm32 # subtract from ESP - 68/push 0x50/imm32/length - 68/push 0/imm32/read - 68/push 0/imm32/write - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # clear-stream(table) - # . . push args - 51/push-ECX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -$convert:read: -#? # print("read\n") {{{ -#? # . . push args -#? 68/push "read\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # }}} - # read-segments(in, table) - # . . push args - 51/push-ECX - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - # . . call - e8/call read-segments/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -$convert:write: -#? # print("write\n") {{{ -#? # . . push args -#? 68/push "write\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # }}} - # write-segments(out, table) - # . . push args - 51/push-ECX - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - # . . call - e8/call write-segments/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -$convert:end: - # . reclaim locals - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x5c/imm32 # add to ESP - # . restore registers - 59/pop-to-ECX - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-convert: - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # setup - # . clear-stream(_test-input-stream) - # . . push args - 68/push _test-input-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-input-buffered-file+4) - # . . push args - b8/copy-to-EAX _test-input-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-stream) - # . . push args - 68/push _test-output-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-buffered-file+4) - # . . push args - b8/copy-to-EAX _test-output-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # initialize input (meta comments in parens) - # # comment 1 - # # comment 2 indented - # == code 0x09000000 (new segment) - # # comment 3 inside a segment - # 1 - # (empty line) - # 2 3 # comment 4 inline with other contents - # == data 0x0a000000 (new segment) - # 4 5/imm32 - # == code (existing segment but non-contiguous with previous iteration) - # 6 7 - # 8 9 (multiple lines) - # == code (existing segment contiguous with previous iteration) - # 10 11 - # . write(_test-input-stream, "# comment 1\n") - # . . push args - 68/push "# comment 1\n"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . write(_test-input-stream, " # comment 2 indented\n") - # . . push args - 68/push " # comment 2 indented\n"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . write(_test-input-stream, "== code 0x09000000\n") - # . . push args - 68/push "== code 0x09000000\n"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . write(_test-input-stream, "# comment 3 inside a segment\n") - # . . push args - 68/push "# comment 3 inside a segment\n"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . write(_test-input-stream, "1\n") - # . . push args - 68/push "1\n"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . write(_test-input-stream, "\n") # empty line - # . . push args - 68/push "\n"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . write(_test-input-stream, "2 3 # comment 4 inline with other contents\n") - # . . push args - 68/push "2 3 # comment 4 inline with other contents\n"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . write(_test-input-stream, "== data 0x0a000000\n") - # . . push args - 68/push "== data 0x0a000000\n"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . write(_test-input-stream, "4 5/imm32\n") - # . . push args - 68/push "4 5/imm32\n"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . write(_test-input-stream, "== code\n") - # . . push args - 68/push "== code\n"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . write(_test-input-stream, "6 7\n") - # . . push args - 68/push "6 7\n"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . write(_test-input-stream, "8 9\n") - # . . push args - 68/push "8 9\n"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . write(_test-input-stream, "== code\n") - # . . push args - 68/push "== code\n"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . write(_test-input-stream, "10 11\n") - # . . push args - 68/push "10 11\n"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # convert(_test-input-buffered-file, _test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - 68/push _test-input-buffered-file/imm32 - # . . call - e8/call convert/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . flush(_test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - # . . call - e8/call flush/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # check output - # == code 0x09000000 - # 1 - # 2 3 # comment 4 inline with other contents - # 6 7 - # 8 9 - # 10 11 - # == data 0x0a000000 - # 4 5/imm32 -#? # dump output {{{ -#? # . write(2/stderr, "result: ^") -#? # . . push args -#? 68/push "result: ^"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write-stream(2/stderr, _test-output-stream) -#? # . . push args -#? 68/push _test-output-stream/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write-stream/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write(2/stderr, "$\n") -#? # . . push args -#? 68/push "$\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . rewind-stream(_test-output-stream) -#? # . . push args -#? 68/push _test-output-stream/imm32 -#? # . . call -#? e8/call rewind-stream/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -#? # }}} - # . check-next-stream-line-equal(_test-output-stream, "== code 0x09000000", msg) - # . . push args - 68/push "F - test-convert/0"/imm32 - 68/push "== code 0x09000000"/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-next-stream-line-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . check-next-stream-line-equal(_test-output-stream, "1", msg) - # . . push args - 68/push "F - test-convert/1"/imm32 - 68/push "1"/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-next-stream-line-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . check-next-stream-line-equal(_test-output-stream, "2 3 # comment 4 inline with other contents", msg) - # . . push args - 68/push "F - test-convert/2"/imm32 - 68/push "2 3 # comment 4 inline with other contents"/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-next-stream-line-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . check-next-stream-line-equal(_test-output-stream, "6 7", msg) - # . . push args - 68/push "F - test-convert/3"/imm32 - 68/push "6 7"/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-next-stream-line-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . check-next-stream-line-equal(_test-output-stream, "8 9", msg) - # . . push args - 68/push "F - test-convert/4"/imm32 - 68/push "8 9"/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-next-stream-line-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . check-next-stream-line-equal(_test-output-stream, "10 11", msg) - # . . push args - 68/push "F - test-convert/5"/imm32 - 68/push "10 11"/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-next-stream-line-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . check-next-stream-line-equal(_test-output-stream, "== data 0x0a000000", msg) - # . . push args - 68/push "F - test-convert/6"/imm32 - 68/push "== data 0x0a000000"/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-next-stream-line-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . check-next-stream-line-equal(_test-output-stream, "4 5/imm32", msg) - # . . push args - 68/push "F - test-convert/7"/imm32 - 68/push "4 5/imm32"/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-next-stream-line-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -# beware: leaks memory (one name per segment read) -read-segments: # in : (address buffered-file), table : (address stream {string, (address stream byte)}) - # pseudocode: - # var curr-segment = null - # var line = new-stream(512, 1) - # while true - # clear-stream(line) - # read-line-buffered(in, line) - # if (line->write == 0) break # end of file - # var word-slice = next-word(line) - # if slice-empty?(word-slice) # whitespace - # continue - # if slice-starts-with?(word-slice, "#") # comment - # continue - # if slice-equal?(word-slice, "==") - # var segment-name = next-word(line) - # segment-slot = leaky-get-or-insert-slice(table, segment-name, row-size=8) - # curr-segment = *segment-slot - # if curr-segment != 0 - # continue - # curr-segment = new-stream(Segment-size) - # *segment-slot = curr-segment - # rewind-stream(line) - # write-stream(curr-segment, line) # abort if curr-segment overflows - # - # word-slice and segment-name are both slices with disjoint lifetimes, so - # we'll use the same address for them. - # - # registers: - # line: ECX - # word-slice and segment-name: EDX - # segment-name and curr-segment: EBX - # word-slice->start: ESI - # temporary: EAX - # - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # . save registers - 50/push-EAX - 51/push-ECX - 52/push-EDX - 53/push-EBX - 56/push-ESI - # var line/ECX : (address stream byte) = stream(512) - 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 0x200/imm32 # subtract from ESP - 68/push 0x200/imm32/length - 68/push 0/imm32/read - 68/push 0/imm32/write - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # var word-slice/EDX = {0, 0} - 68/push 0/imm32/end - 68/push 0/imm32/start - 89/copy 3/mod/direct 2/rm32/EDX . . . 4/r32/ESP . . # copy ESP to EDX -$read-segments:loop: - # clear-stream(line) - # . . push args - 51/push-ECX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # read-line-buffered(in, line) - # . . push args - 51/push-ECX - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - # . . call - e8/call read-line-buffered/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -$read-segments:check0: - # if (line->write == 0) break - 81 7/subop/compare 0/mod/indirect 1/rm32/ECX . . . . . 0/imm32 # compare *ECX - 0f 84/jump-if-equal $read-segments:break/disp32 -#? # dump line {{{ -#? # . write(2/stderr, "LL: ") -#? # . . push args -#? 68/push "LL: "/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write-stream(2/stderr, line) -#? # . . push args -#? 51/push-ECX -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write-stream/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . rewind-stream(line) -#? # . . push args -#? 51/push-ECX -#? # . . call -#? e8/call rewind-stream/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -#? # . write(2/stderr, "$\n") -#? # . . push args -#? 68/push "$\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # }}} - # next-word(line, word-slice) - # . . push args - 52/push-EDX - 51/push-ECX - # . . call - e8/call next-word/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -$read-segments:check1: -#? # print("check1\n") {{{ -#? # . . push args -#? 68/push "check1\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # }}} - # if (slice-empty?(word-slice)) continue - # . EAX = slice-empty?(word-slice) - # . . push args - 52/push-EDX - # . . call - e8/call slice-empty?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . if (EAX != 0) continue - 3d/compare-EAX-and 0/imm32 - 0f 85/jump-if-not-equal $read-segments:loop/disp32 -$read-segments:check-for-comment: -#? # print("check for comment\n") {{{ -#? # . . push args -#? 68/push "check for comment\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # }}} - # if (slice-starts-with?(word-slice, "#")) continue - # . start/ESI = word-slice->start - 8b/copy 0/mod/indirect 2/rm32/EDX . . . 6/r32/ESI . . # copy *ECX to ESI - # . c/EAX = *start - 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX - 8a/copy-byte 0/mod/indirect 6/rm32/ESI . . . 0/r32/AL . . # copy byte at *ESI to AL - # . if (EAX == '#') continue - 3d/compare-EAX-and 0x23/imm32/hash - 0f 84/jump-if-equal $read-segments:loop/disp32 -$read-segments:check-for-segment-header: -#? # print("check for segment header\n") {{{ -#? # . . push args -#? 68/push "check for segment header\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # }}} -#? # dump word-slice {{{ -#? # . write(2/stderr, "AA: ") -#? # . . push args -#? 68/push "AA: "/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . clear-stream(Stderr+4) -#? # . . push args -#? b8/copy-to-EAX Stderr/imm32 -#? 05/add-to-EAX 4/imm32 -#? 50/push-EAX -#? # . . call -#? e8/call clear-stream/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -#? # . write-slice-buffered(Stderr, word-slice) -#? # . . push args -#? 52/push-EDX -#? 68/push Stderr/imm32 -#? # . . call -#? e8/call write-slice-buffered/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . flush(Stderr) -#? # . . push args -#? 68/push Stderr/imm32 -#? # . . call -#? e8/call flush/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -#? # . write(2/stderr, "$\n") -#? # . . push args -#? 68/push "$\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # }}} - # if !slice-equal?(word-slice, "==") goto next check - # . EAX = slice-equal?(word-slice, "==") - # . . push args - 68/push "=="/imm32 - 52/push-EDX - # . . call - e8/call slice-equal?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . if (EAX == 0) goto check3 - 3d/compare-EAX-and 0/imm32 - 0f 84/jump-if-equal $read-segments:regular-line/disp32 - # segment-name = next-word(line) - # . . push args - 52/push-EDX - 51/push-ECX - # . . call - e8/call next-word/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # dump segment name {{{ -#? # . write(2/stderr, "AA: ") -#? # . . push args -#? 68/push "AA: "/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . clear-stream(Stderr+4) -#? # . . push args -#? b8/copy-to-EAX Stderr/imm32 -#? 05/add-to-EAX 4/imm32 -#? 50/push-EAX -#? # . . call -#? e8/call clear-stream/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -#? # . write-slice-buffered(Stderr, word-slice) -#? # . . push args -#? 52/push-EDX -#? 68/push Stderr/imm32 -#? # . . call -#? e8/call write-slice-buffered/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . flush(Stderr) -#? # . . push args -#? 68/push Stderr/imm32 -#? # . . call -#? e8/call flush/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -#? # . write(2/stderr, "$\n") -#? # . . push args -#? 68/push "$\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # }}} - # segment-slot/EAX = leaky-get-or-insert-slice(table, segment-name, row-size=8) - # . . push args - 68/push 8/imm32/row-size - 52/push-EDX - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - # . . call - e8/call leaky-get-or-insert-slice/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # curr-segment = *segment-slot - 8b/copy 0/mod/indirect 0/rm32/EAX . . . 3/r32/EBX . . # copy *EAX to EBX - # if (curr-segment != 0) continue - 81 7/subop/compare 3/mod/direct 3/rm32/EBX . . . . . 0/imm32 # compare EBX - 0f 85/jump-if-not-equal $read-segments:loop/disp32 - # curr-segment = new-stream(Heap, Segment-size, 1) - # . save segment-slot - 50/push-EAX - # . EAX = new-stream(Heap, Segment-size, 1) - # . . push args - 68/push 1/imm32 - ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Segment-size/disp32 # push *Segment-size - 68/push Heap/imm32 - # . . call - e8/call new-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . curr-segment = EAX - 89/copy 3/mod/direct 3/rm32/EBX . . . 0/r32/EAX . . # copy EAX to EBX - # . restore segment-slot - 58/pop-to-EAX - # *segment-slot = curr-segment - 89/copy 0/mod/indirect 0/rm32/EAX . . . 3/r32/EBX . . # copy EBX to *EAX - # fall through -$read-segments:regular-line: -#? # print("regular line\n") {{{ -#? # . . push args -#? 68/push "regular line\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # }}} -#? # dump line {{{ -#? # . write(2/stderr, "regular line: ") -#? # . . push args -#? 68/push "regular line: "/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . rewind-stream(line) -#? # . . push args -#? 51/push-ECX -#? # . . call -#? e8/call rewind-stream/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -#? # . write-stream(2/stderr, line) -#? # . . push args -#? 51/push-ECX -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write-stream/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write(2/stderr, "$\n") -#? # . . push args -#? 68/push "$\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # }}} - # rewind-stream(line) - # . . push args - 51/push-ECX - # . . call - e8/call rewind-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -#? # print("write stream\n") {{{ -#? # . . push args -#? 68/push "write stream\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # }}} - # write-stream(curr-segment, line) - # . . push args - 51/push-ECX - 53/push-EBX - # . . call - e8/call write-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # loop -#? # print("loop\n") {{{ -#? # . . push args -#? 68/push "loop\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # }}} - e9/jump $read-segments:loop/disp32 -$read-segments:break: -$read-segments:end: - # . reclaim locals - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x214/imm32 # add to ESP - # . restore registers - 5e/pop-to-ESI - 5b/pop-to-EBX - 5a/pop-to-EDX - 59/pop-to-ECX - 58/pop-to-EAX - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -write-segments: # out : (address buffered-file), table : (address stream {string, (address stream byte)}) - # pseudocode: - # var curr = table->data - # var max = table->data + table->write - # while curr < max - # stream = table[i].stream - # write-stream-data(out, stream) - # curr += 8 - # flush(out) - # - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # . save registers - 50/push-EAX - 52/push-EDX - 56/push-ESI - # ESI = table - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 6/r32/ESI 0xc/disp8 . # copy *(EBP+12) to ESI - # write/EDX = table->write - 8b/copy 0/mod/indirect 6/rm32/ESI . . . 2/r32/EDX . . # copy *ESI to EDX - # curr/ESI = table->data - 81 0/subop/add 3/mod/direct 6/rm32/ESI . . . . . 0xc/imm32 # add to EAX - # max/EDX = curr + write - 01/add 3/mod/direct 2/rm32/EDX . . . 6/r32/ESI . . # add ESI to EDX -$write-segments:loop: - # if (curr >= max) break - 39/compare 3/mod/direct 6/rm32/ESI . . . 2/r32/EDX . . # compare ESI with EDX - 73/jump-if-greater-or-equal-unsigned $write-segments:break/disp8 - # stream/EAX = table[i].stream - 8b/copy 1/mod/*+disp8 6/rm32/ESI . . . 0/r32/EAX 4/disp8 . # copy *(ESI+4) to EAX - # write-stream-data(out, stream) - # . . push args - 50/push-EAX - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - # . . call - e8/call write-stream-data/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -$write-segments:continue: - # curr += 8 - 81 0/subop/add 3/mod/direct 6/rm32/ESI . . . . . 8/imm32 # add to ESI - eb/jump $write-segments:loop/disp8 -$write-segments:break: - # flush(out) - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - # . . call - e8/call flush/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -$write-segments:end: - # . restore registers - 5e/pop-to-ESI - 5a/pop-to-EDX - 58/pop-to-EAX - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -# . . vim:nowrap:textwidth=0 diff --git a/subx/apps/crenshaw2-1 b/subx/apps/crenshaw2-1 deleted file mode 100755 index 47ee601e..00000000 --- a/subx/apps/crenshaw2-1 +++ /dev/null Binary files differdiff --git a/subx/apps/crenshaw2-1.subx b/subx/apps/crenshaw2-1.subx deleted file mode 100644 index 0c3f180b..00000000 --- a/subx/apps/crenshaw2-1.subx +++ /dev/null @@ -1,585 +0,0 @@ -# Port of https://github.com/akkartik/crenshaw/blob/master/tutor2.1.pas -# which corresponds to the section "single digits" in https://compilers.iecc.com/crenshaw/tutor2.txt -# except that we support hex digits. -# -# To run (from the subx/ directory): -# $ ./subx translate *.subx apps/crenshaw2-1.subx -o apps/crenshaw2-1 -# $ echo '3' |./subx run apps/crenshaw2-1 -# Expected output: -# # syscall(exit, 3) -# bb/copy-to-EBX 3/imm32 -# b8/copy-to-EAX 1/imm32/exit -# cd/syscall 0x80/imm8 -# -# To run the generated output: -# $ echo '3' |./subx run apps/crenshaw2-1 > z1.subx -# $ ./subx translate z1.subx -o z1 -# $ ./subx run z1 -# $ echo $? -# 3 -# -# Stdin must contain just a single hex digit. Other input will print an error: -# $ echo 'xyz' |./subx run apps/crenshaw2-1 -# Error: integer expected -# -# Names in this file sometimes follow Crenshaw's original rather than my usual -# naming conventions. - -== code -# instruction effective address register displacement immediate -# . op subop mod rm32 base index scale r32 -# . 1-3 bytes 3 bits 2 bits 3 bits 3 bits 3 bits 2 bits 2 bits 0/1/2/4 bytes 0/1/2/4 bytes - -Entry: # run tests if necessary, call 'compile' if not - # initialize heap - # . Heap = new-segment(64KB) - # . . push args - 68/push Heap/imm32 - 68/push 0x10000/imm32/64KB - # . . call - e8/call new-segment/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - - # . prolog - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # - if argc > 1 and argv[1] == "test", then return run_tests() - # . argc > 1 - 81 7/subop/compare 1/mod/*+disp8 5/rm32/EBP . . . . 0/disp8 1/imm32 # compare *EBP - 7e/jump-if-lesser-or-equal $run-main/disp8 - # . argv[1] == "test" - # . . push args - 68/push "test"/imm32 - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - # . . call - e8/call kernel-string-equal?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . check result - 3d/compare-EAX-and 1/imm32 - 75/jump-if-not-equal $run-main/disp8 - # . run-tests() - e8/call run-tests/disp32 - 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 3/r32/EBX Num-test-failures/disp32 # copy *Num-test-failures to EBX - eb/jump $main:end/disp8 -$run-main: - # - otherwise read a program from stdin and emit its translation to stdout - # var ed/EAX : exit-descriptor - 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # subtract from ESP - 89/copy 3/mod/direct 0/rm32/EAX . . . 4/r32/ESP . . # copy ESP to EAX - # configure ed to really exit() - # . ed->target = 0 - c7 0/subop/copy 0/mod/direct 0/rm32/EAX . . . . . 0/imm32 # copy to *EAX - # return compile(Stdin, 1/stdout, 2/stderr, ed) - # . . push args - 50/push-EAX/ed - 68/push 2/imm32/stderr - 68/push 1/imm32/stdout - 68/push Stdin/imm32 - # . . call - e8/call compile/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP - # . syscall(exit, 0) - bb/copy-to-EBX 0/imm32 -$main:end: - b8/copy-to-EAX 1/imm32/exit - cd/syscall 0x80/imm8 - -# the main entry point -compile: # in : (address buffered-file), out : fd or (address stream), err : fd or (address stream), ed : (address exit-descriptor) -> <void> - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # . save registers - 50/push-EAX - 51/push-ECX - # prime the pump - # . Look = get-char(in) - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - # . . call - e8/call get-char/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # var num/ECX : (address stream) on the stack - # Numbers can be 32 bits or 8 hex bytes long. One of them will be in 'Look', so we need space for 7 bytes. - # Sizing the stream just right buys us overflow-handling for free inside 'get-num'. - # Add 12 bytes for 'read', 'write' and 'length' fields, for a total of 19 bytes, or 0x13 in hex. - # The stack pointer is no longer aligned, so dump_stack() can be misleading past this point. - 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 0x13/imm32 # subtract from ESP - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # initialize the stream - # . num->length = 7 - c7 0/subop/copy 1/mod/*+disp8 1/rm32/ECX . . . . 8/disp8 7/imm32 # copy to *(ECX+8) - # . clear-stream(num) - # . . push args - 51/push-ECX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # read a digit from 'in' into 'num' - # . get-num(in, num, err, ed) - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x14/disp8 . # push *(EBP+20) - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x10/disp8 . # push *(EBP+16) - 51/push-ECX/num - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - # . . call - e8/call get-num/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP - # render 'num' into the following template on 'out': - # bb/copy-to-EBX _num_ - # b8/copy-to-EAX 1/imm32/exit - # cd/syscall 0x80/imm8 - # - # . write(out, "bb/copy-to-EBX ") - # . . push args - 68/push "bb/copy-to-EBX "/imm32 - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . write-stream(out, num) - # . . push args - 51/push-ECX/num - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - # . . call - e8/call write-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . write(out, Newline) - # . . push args - 68/push Newline/imm32 - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . write(out, "b8/copy-to-EAX 1/imm32/exit\n") - # . . push args - 68/push "b8/copy-to-EAX 1/imm32/exit\n"/imm32 - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . write(out, "cd/syscall 0x80/imm8\n") - # . . push args - 68/push "cd/syscall 0x80/imm8\n"/imm32 - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -$compile:end: - # . restore registers - 59/pop-to-ECX - 58/pop-to-EAX - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -# Read a single digit into 'out'. Abort if there are none, or if there is no space in 'out'. -# Input comes from the global variable 'Look', and we leave the next byte from -# 'in' into it on exit. -get-num: # in : (address buffered-file), out : (address stream), err : fd or (address stream), ed : (address exit-descriptor) -> <void> - # pseudocode: - # if (!is-digit?(Look)) expected(ed, err, "integer") - # if out->write >= out->length - # write(err, "Error: too many digits in number\n") - # stop(ed, 1) - # out->data[out->write] = LSB(Look) - # ++out->write - # Look = get-char(in) - # - # registers: - # in: ESI - # out: EDI - # out->write: ECX (cached copy; need to keep in sync) - # out->length: EDX - # temporaries: EAX, EBX - # We can't allocate Look to a register because it gets written implicitly in - # get-char in each iteration of the loop. (Thereby demonstrating that it's - # not the right interface for us. But we'll keep it just to follow Crenshaw.) - # - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # - if (is-digit?(Look)) expected(ed, err, "integer") - # . EAX = is-digit?(Look) - # . . push args - ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Look/disp32 . # push *Look - # . . call - e8/call is-digit?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . if (EAX == 0) - 3d/compare-EAX-and 0/imm32 - 75/jump-if-not-equal $get-num:main/disp8 - # . expected(ed, err, "integer") - # . . push args - 68/push "integer"/imm32 - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x10/disp8 . # push *(EBP+16) - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x14/disp8 . # push *(EBP+20) - # . . call - e8/call expected/disp32 # never returns - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -$get-num:main: - # - otherwise read a digit - # . save registers - 50/push-EAX - 51/push-ECX - 52/push-EDX - 53/push-EBX - 56/push-ESI - 57/push-EDI - # read necessary variables to registers - # ESI = in - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 6/r32/ESI 8/disp8 . # copy *(EBP+8) to ESI - # EDI = out - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 7/r32/EDI 0xc/disp8 . # copy *(EBP+12) to EDI - # ECX = out->write - 8b/copy 0/mod/indirect 7/rm32/EDI . . . 1/r32/ECX . . # copy *EDI to ECX - # EDX = out->length - 8b/copy 1/mod/*+disp8 7/rm32/EDI . . . 2/r32/EDX 8/disp8 . # copy *(EDI+8) to EDX - # if (out->write >= out->length) error - 39/compare 3/mod/direct 2/rm32/EDX . . . 1/r32/ECX . . # compare EDX with ECX - 7d/jump-if-lesser $get-num:stage2/disp8 - # . error(ed, err, msg) # TODO: show full number - # . . push args - 68/push "get-num: too many digits in number"/imm32 - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x10/disp8 . # push *(EBP+16) - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x14/disp8 . # push *(EBP+20) - # . . call - e8/call error/disp32 # never returns - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -$get-num:stage2: - # out->data[out->write] = LSB(Look) - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 7/base/EDI 1/index/ECX . 3/r32/EBX 0xc/disp8 . # copy EDI+ECX+12 to EBX - 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 0/r32/EAX Look/disp32 . # copy *Look to EAX - 88/copy-byte 0/mod/indirect 3/rm32/EBX . . . 0/r32/AL . . # copy byte at AL to *EBX - # ++out->write - 41/increment-ECX - # Look = get-char(in) - # . . push args - 56/push-ESI - # . . call - e8/call get-char/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -$get-num:loop-end: - # persist necessary variables from registers - 89/copy 0/mod/indirect 7/rm32/EDI . . . 1/r32/ECX . . # copy ECX to *EDI -$get-num:end: - # . restore registers - 5f/pop-to-EDI - 5e/pop-to-ESI - 5b/pop-to-EBX - 5a/pop-to-EDX - 59/pop-to-ECX - 58/pop-to-EAX - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-get-num-reads-single-digit: - # - check that get-num returns first character if it's a digit - # This test uses exit-descriptors. Use EBP for setting up local variables. - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # clear all streams - # . clear-stream(_test-stream) - # . . push args - 68/push _test-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-buffered-file+4) - # . . push args - b8/copy-to-EAX _test-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-stream) - # . . push args - 68/push _test-output-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-error-stream) - # . . push args - 68/push _test-error-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # initialize 'in' - # . write(_test-stream, "3") - # . . push args - 68/push "3"/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # initialize exit-descriptor 'ed' for the call to 'get-num' below - # . var ed/EAX : exit-descriptor - 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # subtract from ESP - 89/copy 3/mod/direct 0/rm32/EAX . . . 4/r32/ESP . . # copy ESP to EAX - # . tailor-exit-descriptor(ed, 16) - # . . push args - 68/push 0x10/imm32/nbytes-of-args-for-get-num - 50/push-EAX/ed - # . . call - e8/call tailor-exit-descriptor/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # prime the pump - # . get-char(_test-buffered-file) - # . . push args - 68/push _test-buffered-file/imm32 - # . . call - e8/call get-char/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # get-num(in, out, err, ed) - # . . push args - 50/push-EAX/ed - 68/push _test-error-stream/imm32 - 68/push _test-output-stream/imm32 - 68/push _test-buffered-file/imm32 - # . . call - e8/call get-num/disp32 - # registers except ESP may be clobbered at this point - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP - # check-ints-equal(*_test-output-stream->data, '3', msg) - # . . push args - 68/push "F - test-get-num-reads-single-digit"/imm32 - 68/push 0x33/imm32 - b8/copy-to-EAX _test-output-stream/imm32 - ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 0xc/disp8 . # push *(EAX+12) - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . reclaim locals - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 5d/pop-to-EBP - c3/return - -test-get-num-aborts-on-non-digit-in-Look: - # - check that get-num returns first character if it's a digit - # This test uses exit-descriptors. Use EBP for setting up local variables. - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # clear all streams - # . clear-stream(_test-stream) - # . . push args - 68/push _test-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-buffered-file+4) - # . . push args - b8/copy-to-EAX _test-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-stream) - # . . push args - 68/push _test-output-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-error-stream) - # . . push args - 68/push _test-error-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # initialize 'in' - # . write(_test-stream, "3") - # . . push args - 68/push "3"/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # initialize exit-descriptor 'ed' for the call to 'get-num' below - # . var ed/EAX : (address exit-descriptor) - 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # subtract from ESP - 89/copy 3/mod/direct 0/rm32/EAX . . . 4/r32/ESP . . # copy ESP to EAX - # . tailor-exit-descriptor(ed, 16) - # . . push args - 68/push 0x10/imm32/nbytes-of-args-for-get-num - 50/push-EAX/ed - # . . call - e8/call tailor-exit-descriptor/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # *don't* prime the pump - # get-num(in, out, err, ed) - # . . push args - 50/push-EAX/ed - 68/push _test-error-stream/imm32 - 68/push _test-output-stream/imm32 - 68/push _test-buffered-file/imm32 - # . . call - e8/call get-num/disp32 - # registers except ESP may be clobbered at this point - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP - # check that get-num tried to call exit(1) - # . check-ints-equal(ed->value, 2, msg) # i.e. stop was called with value 1 - # . . push args - 68/push "F - test-get-num-aborts-on-non-digit-in-Look"/imm32 - 68/push 2/imm32 - # . . push ed->value - ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 4/disp8 . # push *(EAX+4) - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . reclaim locals - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 5d/pop-to-EBP - c3/return - -## helpers - -# write(f, "Error: "+s+" expected\n") then stop(ed, 1) -expected: # ed : (address exit-descriptor), f : fd or (address stream), s : (address array byte) -> <void> - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # write(f, "Error: ") - # . . push args - 68/push "Error: "/imm32 - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # write(f, s) - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x10/disp8 . # push *(EBP+16) - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # write(f, " expected") - # . . push args - 68/push " expected\n"/imm32 - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # stop(ed, 1) - # . . push args - 68/push 1/imm32 - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - # . . call - e8/call stop/disp32 - # should never get past this point -$expected:dead-end: - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -# read a byte from 'f', and save it in 'Look' -get-char: # f : (address buffered-file) -> <void> - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # . save registers - 50/push-EAX - # EAX = read-byte-buffered(f) - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - # . . call - e8/call read-byte-buffered/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # save EAX to Look - 89/copy 0/mod/indirect 5/rm32/.disp32 . . 0/r32/EAX Look/disp32 . # copy EAX to *Look -$get-char:end: - # . restore registers - 58/pop-to-EAX - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -is-digit?: # c : int -> EAX : boolean - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # EAX = false - b8/copy-to-EAX 0/imm32 - # if (c < '0') return false - 81 7/subop/compare 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 0x30/imm32 # compare *(EBP+8) - 7c/jump-if-lesser $is-digit?:end/disp8 - # if (c > '9') return false - 81 7/subop/compare 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 0x39/imm32 # compare *(EBP+8) - 7f/jump-if-greater $is-digit?:end/disp8 - # otherwise return true - b8/copy-to-EAX 1/imm32 -$is-digit?:end: - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -== data - -Look: # (char with some extra padding) - 0/imm32 - -_test-output-stream: - # current write index - 0/imm32 - # current read index - 0/imm32 - # length - 8/imm32 - # data - 00 00 00 00 00 00 00 00 # 8 bytes - -_test-error-stream: - # current write index - 0/imm32 - # current read index - 0/imm32 - # length - 0x40/imm32 - # data (4 lines x 16 bytes/line) - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - -# . . vim:nowrap:textwidth=0 diff --git a/subx/apps/crenshaw2-1b b/subx/apps/crenshaw2-1b deleted file mode 100755 index fba81a7d..00000000 --- a/subx/apps/crenshaw2-1b +++ /dev/null Binary files differdiff --git a/subx/apps/crenshaw2-1b.subx b/subx/apps/crenshaw2-1b.subx deleted file mode 100644 index e1bb6448..00000000 --- a/subx/apps/crenshaw2-1b.subx +++ /dev/null @@ -1,785 +0,0 @@ -# Port of https://github.com/akkartik/crenshaw/blob/master/tutor2.1.pas -# which corresponds to the section "single digits" in https://compilers.iecc.com/crenshaw/tutor2.txt -# except that we support hex numbers of multiple digits. -# -# To run (from the subx/ directory): -# $ ./subx translate *.subx apps/crenshaw2-1b.subx -o apps/crenshaw2-1b -# $ echo '1a' |./subx run apps/crenshaw2-1b -# Expected output: -# # syscall(exit, 1a) -# bb/copy-to-EBX 3/imm32 -# b8/copy-to-EAX 1/imm32/exit -# cd/syscall 0x80/imm8 -# -# To run the generated output: -# $ echo '1a' |./subx run apps/crenshaw2-1b > z1.subx -# $ ./subx translate z1.subx -o z1 -# $ ./subx run z1 -# $ echo $? -# 26 # 0x1a in decimal -# -# Stdin must contain just a single hex digit. Other input will print an error: -# $ echo 'xyz' |./subx run apps/crenshaw2-1b -# Error: integer expected -# -# Names in this file sometimes follow Crenshaw's original rather than my usual -# naming conventions. - -== code -# instruction effective address register displacement immediate -# . op subop mod rm32 base index scale r32 -# . 1-3 bytes 3 bits 2 bits 3 bits 3 bits 3 bits 2 bits 2 bits 0/1/2/4 bytes 0/1/2/4 bytes - -Entry: # run tests if necessary, call 'compile' if not - # initialize heap - # . Heap = new-segment(64KB) - # . . push args - 68/push Heap/imm32 - 68/push 0x10000/imm32/64KB - # . . call - e8/call new-segment/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - - # . prolog - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # - if argc > 1 and argv[1] == "test", then return run_tests() - # . argc > 1 - 81 7/subop/compare 1/mod/*+disp8 5/rm32/EBP . . . . 0/disp8 1/imm32 # compare *EBP - 7e/jump-if-lesser-or-equal $run-main/disp8 - # . argv[1] == "test" - # . . push args - 68/push "test"/imm32 - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - # . . call - e8/call kernel-string-equal?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . check result - 3d/compare-EAX-and 1/imm32 - 75/jump-if-not-equal $run-main/disp8 - # . run-tests() - e8/call run-tests/disp32 - 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 3/r32/EBX Num-test-failures/disp32 # copy *Num-test-failures to EBX - eb/jump $main:end/disp8 -$run-main: - # - otherwise read a program from stdin and emit its translation to stdout - # var ed/EAX : exit-descriptor - 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # subtract from ESP - 89/copy 3/mod/direct 0/rm32/EAX . . . 4/r32/ESP . . # copy ESP to EAX - # configure ed to really exit() - # . ed->target = 0 - c7 0/subop/copy 0/mod/direct 0/rm32/EAX . . . . . 0/imm32 # copy to *EAX - # return compile(Stdin, 1/stdout, 2/stderr, ed) - # . . push args - 50/push-EAX/ed - 68/push 2/imm32/stderr - 68/push 1/imm32/stdout - 68/push Stdin/imm32 - # . . call - e8/call compile/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP - # . syscall(exit, 0) - bb/copy-to-EBX 0/imm32 -$main:end: - b8/copy-to-EAX 1/imm32/exit - cd/syscall 0x80/imm8 - -# the main entry point -compile: # in : (address buffered-file), out : fd or (address stream), err : fd or (address stream), ed : (address exit-descriptor) -> <void> - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # . save registers - 50/push-EAX - 51/push-ECX - # prime the pump - # . Look = get-char(in) - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - # . . call - e8/call get-char/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # var num/ECX : (address stream) on the stack - # Numbers can be 32 bits or 8 hex bytes long. One of them will be in 'Look', so we need space for 7 bytes. - # Sizing the stream just right buys us overflow-handling for free inside 'get-num'. - # Add 12 bytes for 'read', 'write' and 'length' fields, for a total of 19 bytes, or 0x13 in hex. - # The stack pointer is no longer aligned, so dump_stack() can be misleading past this point. - 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 0x13/imm32 # subtract from ESP - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # initialize the stream - # . num->length = 7 - c7 0/subop/copy 1/mod/*+disp8 1/rm32/ECX . . . . 8/disp8 7/imm32 # copy to *(ECX+8) - # . clear-stream(num) - # . . push args - 51/push-ECX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # read a digit from 'in' into 'num' - # . get-num(in, num, err, ed) - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x14/disp8 . # push *(EBP+20) - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x10/disp8 . # push *(EBP+16) - 51/push-ECX/num - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - # . . call - e8/call get-num/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP - # render 'num' into the following template on 'out': - # bb/copy-to-EBX _num_ - # b8/copy-to-EAX 1/imm32/exit - # cd/syscall 0x80/imm8 - # - # . write(out, "bb/copy-to-EBX ") - # . . push args - 68/push "bb/copy-to-EBX "/imm32 - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . write-stream(out, num) - # . . push args - 51/push-ECX/num - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - # . . call - e8/call write-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . write(out, Newline) - # . . push args - 68/push Newline/imm32 - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . write(out, "b8/copy-to-EAX 1/imm32/exit\n") - # . . push args - 68/push "b8/copy-to-EAX 1/imm32/exit\n"/imm32 - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . write(out, "cd/syscall 0x80/imm8\n") - # . . push args - 68/push "cd/syscall 0x80/imm8\n"/imm32 - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -$compile:end: - # . restore registers - 59/pop-to-ECX - 58/pop-to-EAX - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -# Read a sequence of digits into 'out'. Abort if there are none, or if there is -# no space in 'out'. -# Input comes from the global variable 'Look' (first byte) and the argument -# 'in' (rest). We leave the next byte from 'in' into 'Look' on exit. -get-num: # in : (address buffered-file), out : (address stream), err : fd or (address stream), ed : (address exit-descriptor) -> <void> - # pseudocode: - # if (!is-digit?(Look)) expected(ed, err, "integer") - # do - # if out->write >= out->length - # write(err, "Error: too many digits in number\n") - # stop(ed, 1) - # out->data[out->write] = LSB(Look) - # ++out->write - # Look = get-char(in) - # while is-digit?(Look) - # This is complicated because I don't want to hard-code the error strategy in - # a general helper like write-byte-buffered. Maybe I should just create a - # local helper. - # - # within the loop we'll try to keep things in registers: - # in: ESI - # out: EDI - # out->write: ECX (cached copy; need to keep in sync) - # out->length: EDX - # temporaries: EAX, EBX - # We can't allocate Look to a register because it gets written implicitly in - # get-char in each iteration of the loop. (Thereby demonstrating that it's - # not the right interface for us. But we'll keep it just to follow Crenshaw.) - # - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # - if (is-digit?(Look)) expected(ed, err, "integer") - # . EAX = is-digit?(Look) - # . . push args - ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Look/disp32 . # push *Look - # . . call - e8/call is-digit?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . if (EAX == 0) - 3d/compare-EAX-and 0/imm32 - 75/jump-if-not-equal $get-num:main/disp8 - # . expected(ed, err, "integer") - # . . push args - 68/push "integer"/imm32 - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x10/disp8 . # push *(EBP+16) - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x14/disp8 . # push *(EBP+20) - # . . call - e8/call expected/disp32 # never returns - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -$get-num:main: - # - otherwise read a digit - # . save registers - 50/push-EAX - 51/push-ECX - 52/push-EDX - 53/push-EBX - 56/push-ESI - 57/push-EDI - # read necessary variables to registers - # ESI = in - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 6/r32/ESI 8/disp8 . # copy *(EBP+8) to ESI - # EDI = out - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 7/r32/EDI 0xc/disp8 . # copy *(EBP+12) to EDI - # ECX = out->write - 8b/copy 0/mod/indirect 7/rm32/EDI . . . 1/r32/ECX . . # copy *EDI to ECX - # EDX = out->length - 8b/copy 1/mod/*+disp8 7/rm32/EDI . . . 2/r32/EDX 8/disp8 . # copy *(EDI+8) to EDX -$get-num:loop: - # if (out->write >= out->length) error - 39/compare 3/mod/direct 2/rm32/EDX . . . 1/r32/ECX . . # compare EDX with ECX - 7d/jump-if-lesser $get-num:loop-stage2/disp8 - # . error(ed, err, msg) # TODO: show full number - # . . push args - 68/push "get-num: too many digits in number"/imm32 - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x10/disp8 . # push *(EBP+16) - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x14/disp8 . # push *(EBP+20) - # . . call - e8/call error/disp32 # never returns - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -$get-num:loop-stage2: - # out->data[out->write] = LSB(Look) - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 7/base/EDI 1/index/ECX . 3/r32/EBX 0xc/disp8 . # copy EDI+ECX+12 to EBX - 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 0/r32/EAX Look/disp32 . # copy *Look to EAX - 88/copy-byte 0/mod/indirect 3/rm32/EBX . . . 0/r32/AL . . # copy byte at AL to *EBX - # ++out->write - 41/increment-ECX - # Look = get-char(in) - # . . push args - 56/push-ESI - # . . call - e8/call get-char/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # if (is-digit?(Look)) loop - # . EAX = is-digit?(Look) - # . . push args - ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Look/disp32 . # push *Look - # . . call - e8/call is-digit?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . if (EAX != 0) loop - 3d/compare-EAX-and 0/imm32 - 0f 85/jump-if-not-equal $get-num:loop/disp32 -$get-num:loop-end: - # persist necessary variables from registers - 89/copy 0/mod/indirect 7/rm32/EDI . . . 1/r32/ECX . . # copy ECX to *EDI -$get-num:end: - # . restore registers - 5f/pop-to-EDI - 5e/pop-to-ESI - 5b/pop-to-EBX - 5a/pop-to-EDX - 59/pop-to-ECX - 58/pop-to-EAX - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-get-num-reads-single-digit: - # - check that get-num returns first character if it's a digit - # This test uses exit-descriptors. Use EBP for setting up local variables. - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # clear all streams - # . clear-stream(_test-stream) - # . . push args - 68/push _test-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-buffered-file+4) - # . . push args - b8/copy-to-EAX _test-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-stream) - # . . push args - 68/push _test-output-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-error-stream) - # . . push args - 68/push _test-error-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # initialize 'in' - # . write(_test-stream, "3") - # . . push args - 68/push "3"/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # initialize exit-descriptor 'ed' for the call to 'get-num' below - # . var ed/EAX : exit-descriptor - 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # subtract from ESP - 89/copy 3/mod/direct 0/rm32/EAX . . . 4/r32/ESP . . # copy ESP to EAX - # . tailor-exit-descriptor(ed, 16) - # . . push args - 68/push 0x10/imm32/nbytes-of-args-for-get-num - 50/push-EAX/ed - # . . call - e8/call tailor-exit-descriptor/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # prime the pump - # . get-char(_test-buffered-file) - # . . push args - 68/push _test-buffered-file/imm32 - # . . call - e8/call get-char/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # get-num(in, out, err, ed) - # . . push args - 50/push-EAX/ed - 68/push _test-error-stream/imm32 - 68/push _test-output-stream/imm32 - 68/push _test-buffered-file/imm32 - # . . call - e8/call get-num/disp32 - # registers except ESP may be clobbered at this point - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP - # check-ints-equal(*_test-output-stream->data, '3', msg) - # . . push args - 68/push "F - test-get-num-reads-single-digit"/imm32 - 68/push 0x33/imm32 - b8/copy-to-EAX _test-output-stream/imm32 - ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 0xc/disp8 . # push *(EAX+12) - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . reclaim locals - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 5d/pop-to-EBP - c3/return - -test-get-num-aborts-on-non-digit-in-Look: - # - check that get-num returns first character if it's a digit - # This test uses exit-descriptors. Use EBP for setting up local variables. - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # clear all streams - # . clear-stream(_test-stream) - # . . push args - 68/push _test-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-buffered-file+4) - # . . push args - b8/copy-to-EAX _test-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-stream) - # . . push args - 68/push _test-output-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-error-stream) - # . . push args - 68/push _test-error-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # initialize 'in' - # . write(_test-stream, "3") - # . . push args - 68/push "3"/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # initialize exit-descriptor 'ed' for the call to 'get-num' below - # . var ed/EAX : (address exit-descriptor) - 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # subtract from ESP - 89/copy 3/mod/direct 0/rm32/EAX . . . 4/r32/ESP . . # copy ESP to EAX - # . tailor-exit-descriptor(ed, 16) - # . . push args - 68/push 0x10/imm32/nbytes-of-args-for-get-num - 50/push-EAX/ed - # . . call - e8/call tailor-exit-descriptor/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # *don't* prime the pump - # get-num(in, out, err, ed) - # . . push args - 50/push-EAX/ed - 68/push _test-error-stream/imm32 - 68/push _test-output-stream/imm32 - 68/push _test-buffered-file/imm32 - # . . call - e8/call get-num/disp32 - # registers except ESP may be clobbered at this point - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP - # check that get-num tried to call exit(1) - # . check-ints-equal(ed->value, 2, msg) # i.e. stop was called with value 1 - # . . push args - 68/push "F - test-get-num-aborts-on-non-digit-in-Look"/imm32 - 68/push 2/imm32 - # . . push ed->value - ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 4/disp8 . # push *(EAX+4) - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . reclaim locals - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 5d/pop-to-EBP - c3/return - -test-get-num-reads-multiple-digits: - # - check that get-num returns all initial digits until it encounters a non-digit - # This test uses exit-descriptors. Use EBP for setting up local variables. - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # clear all streams - # . clear-stream(_test-stream) - # . . push args - 68/push _test-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-buffered-file+4) - # . . push args - b8/copy-to-EAX _test-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-stream) - # . . push args - 68/push _test-output-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-error-stream) - # . . push args - 68/push _test-error-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # initialize 'in' - # . write(_test-stream, "3456 x") - # . . push args - 68/push "3456"/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # initialize exit-descriptor 'ed' for the call to 'get-num' below - # . var ed/EAX : (address exit-descriptor) - 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # subtract from ESP - 89/copy 3/mod/direct 0/rm32/EAX . . . 4/r32/ESP . . # copy ESP to EAX - # . tailor-exit-descriptor(ed, 16) - # . . push args - 68/push 0x10/imm32/nbytes-of-args-for-get-num - 50/push-EAX/ed - # . . call - e8/call tailor-exit-descriptor/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # prime the pump - # . get-char(_test-buffered-file) - # . . push args - 68/push _test-buffered-file/imm32 - # . . call - e8/call get-char/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # get-num(in, out, err, ed) - # . . push args - 50/push-EAX/ed - 68/push _test-error-stream/imm32 - 68/push _test-output-stream/imm32 - 68/push _test-buffered-file/imm32 - # . . call - e8/call get-num/disp32 - # registers except ESP may be clobbered at this point - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP - # check-ints-equal(*_test-output-stream->data, '3456', msg) - # . . push args - 68/push "F - test-get-num-reads-multiple-digits"/imm32 - 68/push 0x36353433/imm32 - b8/copy-to-EAX _test-output-stream/imm32 - ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 0xc/disp8 . # push *(EAX+12) - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . reclaim locals - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 5d/pop-to-EBP - c3/return - -test-get-num-reads-multiple-digits-followed-by-nondigit: - # - check that get-num returns all initial digits until it encounters a non-digit - # This test uses exit-descriptors. Use EBP for setting up local variables. - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # clear all streams - # . clear-stream(_test-stream) - # . . push args - 68/push _test-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-buffered-file+4) - # . . push args - b8/copy-to-EAX _test-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-stream) - # . . push args - 68/push _test-output-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-error-stream) - # . . push args - 68/push _test-error-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # initialize 'in' - # . write(_test-stream, "3456 x") - # . . push args - 68/push "3456 x"/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # initialize exit-descriptor 'ed' for the call to 'get-num' below - # . var ed/EAX : (address exit-descriptor) - 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # subtract from ESP - 89/copy 3/mod/direct 0/rm32/EAX . . . 4/r32/ESP . . # copy ESP to EAX - # . tailor-exit-descriptor(ed, 16) - # . . push args - 68/push 0x10/imm32/nbytes-of-args-for-get-num - 50/push-EAX/ed - # . . call - e8/call tailor-exit-descriptor/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # prime the pump - # . get-char(_test-buffered-file) - # . . push args - 68/push _test-buffered-file/imm32 - # . . call - e8/call get-char/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # get-num(in, out, err, ed) - # . . push args - 50/push-EAX/ed - 68/push _test-error-stream/imm32 - 68/push _test-output-stream/imm32 - 68/push _test-buffered-file/imm32 - # . . call - e8/call get-num/disp32 - # registers except ESP may be clobbered at this point - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP - # check-ints-equal(*_test-output-stream->data, '3456', msg) - # . . push args - 68/push "F - test-get-num-reads-multiple-digits-followed-by-nondigit"/imm32 - 68/push 0x36353433/imm32 - b8/copy-to-EAX _test-output-stream/imm32 - ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 0xc/disp8 . # push *(EAX+12) - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . reclaim locals - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 5d/pop-to-EBP - c3/return - -## helpers - -# write(f, "Error: "+s+" expected\n") then stop(ed, 1) -expected: # ed : (address exit-descriptor), f : fd or (address stream), s : (address array byte) -> <void> - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # write(f, "Error: ") - # . . push args - 68/push "Error: "/imm32 - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # write(f, s) - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x10/disp8 . # push *(EBP+16) - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # write(f, " expected\n") - # . . push args - 68/push " expected\n"/imm32 - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # stop(ed, 1) - # . . push args - 68/push 1/imm32 - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - # . . call - e8/call stop/disp32 - # should never get past this point -$expected:dead-end: - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -# read a byte from 'f', and save it in 'Look' -get-char: # f : (address buffered-file) -> <void> - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # . save registers - 50/push-EAX - # EAX = read-byte-buffered(f) - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - # . . call - e8/call read-byte-buffered/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # save EAX to Look - 89/copy 0/mod/indirect 5/rm32/.disp32 . . 0/r32/EAX Look/disp32 . # copy EAX to *Look -$get-char:end: - # . restore registers - 58/pop-to-EAX - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -is-digit?: # c : int -> EAX : boolean - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # EAX = false - b8/copy-to-EAX 0/imm32 - # if (c < '0') return false - 81 7/subop/compare 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 0x30/imm32 # compare *(EBP+8) - 7c/jump-if-lesser $is-digit?:end/disp8 - # if (c > '9') return false - 81 7/subop/compare 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 0x39/imm32 # compare *(EBP+8) - 7f/jump-if-greater $is-digit?:end/disp8 - # otherwise return true - b8/copy-to-EAX 1/imm32 -$is-digit?:end: - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -== data - -Look: # (char with some extra padding) - 0/imm32 - -_test-output-stream: - # current write index - 0/imm32 - # current read index - 0/imm32 - # length - 8/imm32 - # data - 00 00 00 00 00 00 00 00 # 8 bytes - -_test-error-stream: - # current write index - 0/imm32 - # current read index - 0/imm32 - # length - 0x40/imm32 - # data (4 lines x 16 bytes/line) - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - -# . . vim:nowrap:textwidth=0 diff --git a/subx/apps/dquotes b/subx/apps/dquotes deleted file mode 100755 index b7a4adfe..00000000 --- a/subx/apps/dquotes +++ /dev/null Binary files differdiff --git a/subx/apps/dquotes.subx b/subx/apps/dquotes.subx deleted file mode 100644 index 6848ad19..00000000 --- a/subx/apps/dquotes.subx +++ /dev/null @@ -1,2757 +0,0 @@ -# Translate literal strings within double quotes. -# Replace them with references to new variables in the data segment. -# -# To run (from the subx/ directory): -# $ ./subx translate *.subx apps/dquotes.subx -o apps/dquotes -# $ cat x -# == code -# ab "cd ef"/imm32 -# $ cat x |./subx run apps/dquotes -# == code -# ab __string1/imm32 -# == data -# __string1: -# 5/imm32 -# 0x63/c 0x64/d 0x20/ 0x65/e 0x66/f - -== code -# instruction effective address register displacement immediate -# . op subop mod rm32 base index scale r32 -# . 1-3 bytes 3 bits 2 bits 3 bits 3 bits 3 bits 2 bits 2 bits 0/1/2/4 bytes 0/1/2/4 bytes - -Entry: - # initialize heap - # . Heap = new-segment(Heap-size) - # . . push args - 68/push Heap/imm32 - ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Heap-size/disp32 # push *Heap-size - # . . call - e8/call new-segment/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - - # run tests if necessary, convert stdin if not - # . prolog - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # - if argc > 1 and argv[1] == "test", then return run_tests() - # . argc > 1 - 81 7/subop/compare 1/mod/*+disp8 5/rm32/EBP . . . . 0/disp8 1/imm32 # compare *EBP - 7e/jump-if-lesser-or-equal $run-main/disp8 - # . argv[1] == "test" - # . . push args - 68/push "test"/imm32 - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - # . . call - e8/call kernel-string-equal?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . check result - 3d/compare-EAX-and 1/imm32 - 75/jump-if-not-equal $run-main/disp8 - # . run-tests() - e8/call run-tests/disp32 - 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 3/r32/EBX Num-test-failures/disp32 # copy *Num-test-failures to EBX - eb/jump $main:end/disp8 -$run-main: - # - otherwise convert stdin - # var ed/EAX : exit-descriptor - 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # subtract from ESP - 89/copy 3/mod/direct 0/rm32/EAX . . . 4/r32/ESP . . # copy ESP to EAX - # configure ed to really exit() - # . ed->target = 0 - c7 0/subop/copy 0/mod/direct 0/rm32/EAX . . . . . 0/imm32 # copy to *EAX - # return convert(Stdin, 1/stdout, 2/stderr, ed) - # . . push args - 50/push-EAX/ed - 68/push Stderr/imm32 - 68/push Stdout/imm32 - 68/push Stdin/imm32 - # . . call - e8/call convert/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP - # . syscall(exit, 0) - bb/copy-to-EBX 0/imm32 -$main:end: - b8/copy-to-EAX 1/imm32/exit - cd/syscall 0x80/imm8 - -# conceptual hierarchy within a line: -# line = words separated by ' ', maybe followed by comment starting with '#' -# word = datum until '/', then 0 or more metadata separated by '/' - -convert: # in : (address buffered-file), out : (address buffered-file) -> <void> - # pseudocode: - # var line = new-stream(512, 1) - # var new-data-segment = new-stream(Heap, Segment-size, 1) - # write(new-data-segment, "== data\n") - # while true - # clear-stream(line) - # read-line-buffered(in, line) - # if (line->write == 0) break # end of file - # while true - # var word-slice = next-word-or-string(line) - # if slice-empty?(word-slice) # end of line - # break - # if slice-starts-with?(word-slice, "#") # comment - # continue - # if slice-starts-with?(word-slice, '"') # string literal <== what we're here for - # process-string-literal(word-slice, out, new-data-segment) - # else - # write-slice-buffered(out, word-slice) - # write(out, " ") - # write(out, "\n\n") - # write-stream-data(out, new-data-segment) - # flush(out) - # - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # . save registers - 50/push-EAX - 51/push-ECX - 52/push-EDX - 53/push-EBX - 56/push-ESI - 57/push-EDI - # var line/ECX : (address stream byte) = stream(512) - 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 0x200/imm32 # subtract from ESP - 68/push 0x200/imm32/length - 68/push 0/imm32/read - 68/push 0/imm32/write - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # var word-slice/EDX = {0, 0} - 68/push 0/imm32/end - 68/push 0/imm32/start - 89/copy 3/mod/direct 2/rm32/EDX . . . 4/r32/ESP . . # copy ESP to EDX - # new-data-segment/EDI = new-stream(Heap, Segment-size, 1) - # . EAX = new-stream(Heap, Segment-size, 1) - # . . push args - 68/push 1/imm32 - ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Segment-size/disp32 # push *Segment-size - 68/push Heap/imm32 - # . . call - e8/call new-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . EDI = EAX - 89/copy 3/mod/direct 7/rm32/EDI . . . 0/r32/EAX . . # copy EAX to EDI - # write(new-data-segment, "== data\n") - # . . push args - 68/push "== data\n"/imm32 - 57/push-EDI - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -$convert:line-loop: - # clear-stream(line) - # . . push args - 51/push-ECX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # read-line-buffered(in, line) - # . . push args - 51/push-ECX - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - # . . call - e8/call read-line-buffered/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -$convert:check0: - # if (line->write == 0) break - 81 7/subop/compare 0/mod/indirect 1/rm32/ECX . . . . . 0/imm32 # compare *ECX - 0f 84/jump-if-equal $convert:break/disp32 -$convert:word-loop: - # next-word-or-string(line, word-slice) - # . . push args - 52/push-EDX - 51/push-ECX - # . . call - e8/call next-word-or-string/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -$convert:check1: - # if (slice-empty?(word-slice)) break - # . EAX = slice-empty?(word-slice) - # . . push args - 52/push-EDX - # . . call - e8/call slice-empty?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . if (EAX != 0) break - 3d/compare-EAX-and 0/imm32 - 0f 85/jump-if-not-equal $convert:next-line/disp32 -$convert:check-for-comment: - # if (slice-starts-with?(word-slice, "#")) continue - # . start/ESI = word-slice->start - 8b/copy 0/mod/indirect 2/rm32/EDX . . . 6/r32/ESI . . # copy *EDX to ESI - # . c/EAX = *start - 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX - 8a/copy-byte 0/mod/indirect 6/rm32/ESI . . . 0/r32/AL . . # copy byte at *ESI to AL - # . if (EAX == '#') continue - 3d/compare-EAX-and 0x23/imm32/hash - 74/jump-if-equal $convert:word-loop/disp8 -$convert:check-for-string-literal: - 3d/compare-EAX-and 0x22/imm32/dquote - 75/jump-if-not-equal $convert:regular-word/disp8 -$convert:string-literal: - # process-string-literal(word-slice, out, new-data-segment) - # . . push args - 57/push-EDI - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - 52/push-EDX - # . . call - e8/call process-string-literal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # continue - eb/jump $convert:next-word/disp8 -$convert:regular-word: - # write-slice-buffered(out, word-slice) - # . . push args - 52/push-EDX - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - # . . call - e8/call write-slice-buffered/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # fall through -$convert:next-word: - # write-buffered(out, " ") - # . . push args - 68/push " "/imm32 - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - # . . call - e8/call write-buffered/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # loop - eb/jump $convert:word-loop/disp8 -$convert:next-line: - # write-buffered(out, "\n") - # . . push args - 68/push Newline/imm32 - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - # . . call - e8/call write-buffered/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # loop - e9/jump $convert:line-loop/disp32 -$convert:break: - # write-stream-data(out, new-data-segment) - # . . push args - 57/push-EDI - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - # . . call - e8/call write-stream-data/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # flush(out) - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - # . . call - e8/call flush/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -$convert:end: - # . reclaim locals - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x214/imm32 # add to ESP - # . restore registers - 5f/pop-to-EDI - 5e/pop-to-ESI - 5b/pop-to-EBX - 5a/pop-to-EDX - 59/pop-to-ECX - 58/pop-to-EAX - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -# Write out 'string-literal' in a new format to 'out-segment', assign it a new -# label, and write the new label out to 'out'. -process-string-literal: # string-literal : (address slice), out : (address buffered-file), out-segment : (address stream) - # pseudocode: - # print(out-segment, "_string#{Next-string-literal}:\n") - # emit-string-literal-data(out-segment, string-literal) - # print(out, "_string#{Next-string-literal}") - # emit-metadata(out, string-literal) - # ++ *Next-string-literal - # - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # . save registers - 51/push-ECX - # var int32-stream/ECX = stream(10) # number of decimal digits a 32-bit number can have - 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 0xa/imm32 # subtract from ESP - 68/push 0xa/imm32/decimal-digits-in-32bit-number - 68/push 0/imm32/read - 68/push 0/imm32/write - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # print(out-segment, "_string#{Next-string-literal}:\n") - # . write(out-segment, "_string") - # . . push args - 68/push "_string"/imm32 - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x10/disp8 . # push *(EBP+16) - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . print-int32-decimal(out-segment, *Next-string-literal) - # . . push args - ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Next-string-literal/disp32 # push *Next-string-literal - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x10/disp8 . # push *(EBP+16) - # . . call - e8/call print-int32-decimal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . write(out-segment, ":\n") - # . . push args - 68/push ":\n"/imm32 - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x10/disp8 . # push *(EBP+16) - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # emit-string-literal-data(out-segment, string-literal) - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x8/disp8 . # push *(EBP+8) - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x10/disp8 . # push *(EBP+16) - # . . call - e8/call emit-string-literal-data/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # write(out-segment, "\n") - # . . push args - 68/push Newline/imm32 - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x10/disp8 . # push *(EBP+16) - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # print(out, "_string#{Next-string-literal}") - # . write-buffered(out, "_string") - # . . push args - 68/push "_string"/imm32 - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - # . . call - e8/call write-buffered/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . print-int32-decimal(int32-stream, *Next-string-literal) - # . . push args - ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Next-string-literal/disp32 # push *Next-string-literal - 51/push-ECX - # . . call - e8/call print-int32-decimal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . write-stream-data(out, int32-stream) - # . . push args - 51/push-ECX - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - # . . call - e8/call write-stream-data/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # emit-metadata(out, string-literal) - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - # . . call - e8/call emit-metadata/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # ++ *Next-string-literal - ff 0/subop/increment 0/mod/indirect 5/rm32/.disp32 . . . Next-string-literal/disp32 # increment *Num-test-failures -$process-string-literal:end: - # . reclaim locals - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x16/imm32 # add to ESP - # . restore registers - 59/pop-to-ECX - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-convert-is-idempotent-by-default: - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # setup - # . clear-stream(_test-input-stream) - # . . push args - 68/push _test-input-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-input-buffered-file+4) - # . . push args - b8/copy-to-EAX _test-input-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-stream) - # . . push args - 68/push _test-output-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-buffered-file+4) - # . . push args - b8/copy-to-EAX _test-output-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # initialize input (meta comments in parens) - # # comment 1 - # # comment 2 indented - # == code 0x1 (new segment) - # # comment 3 inside a segment - # 1 - # (empty line) - # 2 3 # comment 4 inline with other contents - # == data 0x2 (new segment) - # 4 5/imm32 - # . write(_test-input-stream, "# comment 1\n") - # . . push args - 68/push "# comment 1\n"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . write(_test-input-stream, " # comment 2 indented\n") - # . . push args - 68/push " # comment 2 indented\n"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . write(_test-input-stream, "== code 0x1\n") - # . . push args - 68/push "== code 0x1\n"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . write(_test-input-stream, "# comment 3 inside a segment\n") - # . . push args - 68/push "# comment 3 inside a segment\n"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . write(_test-input-stream, "1\n") - # . . push args - 68/push "1\n"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . write(_test-input-stream, "\n") # empty line - # . . push args - 68/push "\n"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . write(_test-input-stream, "2 3 # comment 4 inline with other contents\n") - # . . push args - 68/push "2 3 # comment 4 inline with other contents\n"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . write(_test-input-stream, "== data 0x2\n") - # . . push args - 68/push "== data 0x2\n"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . write(_test-input-stream, "4 5/imm32\n") - # . . push args - 68/push "4 5/imm32\n"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # convert(_test-input-buffered-file, _test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - 68/push _test-input-buffered-file/imm32 - # . . call - e8/call convert/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . flush(_test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - # . . call - e8/call flush/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # check output - # (comment dropped for now) - # (comment dropped for now) - # == code 0x1 - # (comment dropped for now) - # 1 - # (comment dropped for now) - # 2 3 - # == data 0x2 - # 4 5/imm32 - # We don't care right now what exactly happens to comments. Trailing spaces are also minor details. -#? # dump output {{{ -#? # . write(2/stderr, "result: ^") -#? # . . push args -#? 68/push "result: ^"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write-stream(2/stderr, _test-output-stream) -#? # . . push args -#? 68/push _test-output-stream/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write-stream/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write(2/stderr, "$\n") -#? # . . push args -#? 68/push "$\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # }}} - # . check-next-stream-line-equal(_test-output-stream, "", msg) - # . . push args - 68/push "F - test-convert-is-idempotent-by-default/0"/imm32 - 68/push ""/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-next-stream-line-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . check-next-stream-line-equal(_test-output-stream, "", msg) - # . . push args - 68/push "F - test-convert-is-idempotent-by-default/1"/imm32 - 68/push ""/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-next-stream-line-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . check-next-stream-line-equal(_test-output-stream, "== code 0x1 ", msg) - # . . push args - 68/push "F - test-convert-is-idempotent-by-default/2"/imm32 - 68/push "== code 0x1 "/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-next-stream-line-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . check-next-stream-line-equal(_test-output-stream, "", msg) - # . . push args - 68/push "F - test-convert-is-idempotent-by-default/3"/imm32 - 68/push ""/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-next-stream-line-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . check-next-stream-line-equal(_test-output-stream, "1 ", msg) - # . . push args - 68/push "F - test-convert-is-idempotent-by-default/4"/imm32 - 68/push "1 "/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-next-stream-line-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . check-next-stream-line-equal(_test-output-stream, "", msg) - # . . push args - 68/push "F - test-convert-is-idempotent-by-default/5"/imm32 - 68/push ""/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-next-stream-line-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . check-next-stream-line-equal(_test-output-stream, "2 3 ", msg) - # . . push args - 68/push "F - test-convert-is-idempotent-by-default/6"/imm32 - 68/push "2 3 "/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-next-stream-line-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . check-next-stream-line-equal(_test-output-stream, "== data 0x2 ", msg) - # . . push args - 68/push "F - test-convert-is-idempotent-by-default/7"/imm32 - 68/push "== data 0x2 "/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-next-stream-line-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . check-next-stream-line-equal(_test-output-stream, "4 5/imm32 ", msg) - # . . push args - 68/push "F - test-convert-is-idempotent-by-default/8"/imm32 - 68/push "4 5/imm32 "/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-next-stream-line-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-convert-processes-string-literals: - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # setup - # . clear-stream(_test-input-stream) - # . . push args - 68/push _test-input-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-input-buffered-file+4) - # . . push args - b8/copy-to-EAX _test-input-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-stream) - # . . push args - 68/push _test-output-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-buffered-file+4) - # . . push args - b8/copy-to-EAX _test-output-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # initialize input (meta comments in parens) - # == code (new segment) - # 1 "a"/x - # 2 "bc"/y - 68/push "== code 0x1\n"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . write(_test-input-stream, "1 \"a\"/x\n") - # . . push args - 68/push "1 \"a\"/x\n"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . write(_test-input-stream, "2 \"bc\"/y\n") - # . . push args - 68/push "2 \"bc\"/y\n"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # convert(_test-input-buffered-file, _test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - 68/push _test-input-buffered-file/imm32 - # . . call - e8/call convert/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . flush(_test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - # . . call - e8/call flush/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # check output - # == code 0x1 - # 1 _string1/x - # 2 _string2/y - # == data - # _string1: - # 1/imm32 61/a - # _string2: - # 2/imm32 62/b 63/c - # We don't care right now what exactly happens to comments. Trailing spaces are also minor details. - # - # Open question: how to make this check more robust. - # We don't actually care what the auto-generated string variables are - # called. We just want to make sure instructions using string literals - # switch to a string variable with the right value. - # (Modifying string literals completely off the radar for now.) -#? # dump output {{{ -#? # . write(2/stderr, "result: ^") -#? # . . push args -#? 68/push "result: ^"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write-stream(2/stderr, _test-output-stream) -#? # . . push args -#? 68/push _test-output-stream/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write-stream/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write(2/stderr, "$\n") -#? # . . push args -#? 68/push "$\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . rewind-stream(_test-output-stream) -#? # . . push args -#? 68/push _test-output-stream/imm32 -#? # . . call -#? e8/call rewind-stream/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -#? # }}} - # . check-next-stream-line-equal(_test-output-stream, "== code 0x1 ", msg) - # . . push args - 68/push "F - test-convert-processes-string-literals/0"/imm32 - 68/push "== code 0x1 "/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-next-stream-line-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . check-next-stream-line-equal(_test-output-stream, "1 _string1/x ", msg) - # . . push args - 68/push "F - test-convert-processes-string-literals/1"/imm32 - 68/push "1 _string1/x "/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-next-stream-line-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . check-next-stream-line-equal(_test-output-stream, "2 _string2/y ", msg) - # . . push args - 68/push "F - test-convert-processes-string-literals/2"/imm32 - 68/push "2 _string2/y "/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-next-stream-line-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . check-next-stream-line-equal(_test-output-stream, "== data", msg) - # . . push args - 68/push "F - test-convert-processes-string-literals/3"/imm32 - 68/push "== data"/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-next-stream-line-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . check-next-stream-line-equal(_test-output-stream, "_string1: ", msg) - # . . push args - 68/push "F - test-convert-processes-string-literals/4"/imm32 - 68/push "_string1:"/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-next-stream-line-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . check-next-stream-line-equal(_test-output-stream, "1/imm32 61/a ", msg) - # . . push args - 68/push "F - test-convert-processes-string-literals/5"/imm32 - 68/push "0x00000001/imm32 61/a "/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-next-stream-line-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . check-next-stream-line-equal(_test-output-stream, "_string2: ", msg) - # . . push args - 68/push "F - test-convert-processes-string-literals/6"/imm32 - 68/push "_string2:"/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-next-stream-line-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . check-next-stream-line-equal(_test-output-stream, "2/imm32 62/b 63/c ", msg) - # . . push args - 68/push "F - test-convert-processes-string-literals/7"/imm32 - 68/push "0x00000002/imm32 62/b 63/c "/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-next-stream-line-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -# generate the data segment contents byte by byte for a given slice -emit-string-literal-data: # out : (address stream), word : (address slice) - # pseudocode - # len = string-length-at-start-of-slice(word->start, word->end) - # print(out, "#{len}/imm32 ") - # curr = word->start - # ++curr # skip '"' - # while true - # if (curr >= word->end) break - # c = *curr - # if (c == '"') break - # if (c == '\') { - # ++curr - # c = *curr - # if (c == 'n') - # c = newline - # } - # append-byte-hex(out, c) - # if c is alphanumeric: - # write(out, "/") - # append-byte(out, c) - # write(out, " ") - # ++curr - # - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # . save registers - 50/push-EAX - 51/push-ECX - 52/push-EDX - 56/push-ESI - # ESI = word - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 6/r32/ESI 0xc/disp8 . # copy *(EBP+12) to ESI - # curr/EDX = word->start - 8b/copy 0/mod/indirect 6/rm32/ESI . . . 2/r32/EDX . . # copy *ESI to EDX - # max/ESI = word->end - 8b/copy 1/mod/*+disp8 6/rm32/ESI . . . 6/r32/ESI 4/disp8 . # copy *(ESI+4) to ESI -$emit-string-literal-data:emit-length: - # len/EAX = string-length-at-start-of-slice(word->start, word->end) - # . . push args - 56/push-ESI - 52/push-EDX - # . . call - e8/call string-length-at-start-of-slice/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # print(out, "#{len}/imm32 ") - # . print-int32(out, len) - # . . push args - 50/push-EAX - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - # . . call - e8/call print-int32/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . write(out, "/imm32 ") - # . . push args - 68/push "/imm32 "/imm32 - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -$emit-string-literal-data:loop-init: - # ++curr # skip initial '"' - 42/increment-EDX - # c/ECX = 0 - 31/xor 3/mod/direct 1/rm32/ECX . . . 1/r32/ECX . . # clear ECX -$emit-string-literal-data:loop: - # if (curr >= max) break - 39/compare 3/mod/direct 2/rm32/EDX . . . 6/r32/ESI . . # compare EDX with ESI - 0f 83/jump-if-greater-or-equal-unsigned $emit-string-literal-data:end/disp32 - # CL = *curr - 8a/copy-byte 0/mod/indirect 2/rm32/EDX . . . 1/r32/CL . . # copy byte at *EDX to CL - # if (c == '"') break - 81 7/subop/compare 3/mod/direct 1/rm32/ECX . . . . . 0x22/imm32/dquote # compare ECX - 74/jump-if-equal $emit-string-literal-data:end/disp8 - # if (c != '\') goto emit - 81 7/subop/compare 3/mod/direct 1/rm32/ECX . . . . . 0x5c/imm32/backslash # compare ECX - 75/jump-if-not-equal $emit-string-literal-data:emit/disp8 - # ++curr - 42/increment-EDX - # if (curr >= max) break - 39/compare 3/mod/direct 2/rm32/EDX . . . 6/r32/ESI . . # compare EDX with ESI - 73/jump-if-greater-or-equal-unsigned $emit-string-literal-data:end/disp8 - # c = *curr - 8a/copy-byte 0/mod/indirect 2/rm32/EDX . . . 1/r32/CL . . # copy byte at *EDX to CL - # if (c == 'n') c = newline - 81 7/subop/compare 3/mod/direct 1/rm32/ECX . . . . . 0x6e/imm32/n # compare ECX - 75/jump-if-not-equal $emit-string-literal-data:emit/disp8 - b9/copy-to-ECX 0x0a/imm32/newline -$emit-string-literal-data:emit: - # append-byte-hex(out, CL) - # . . push args - 51/push-ECX - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - # . . call - e8/call append-byte-hex/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # if (is-alphanumeric?(*curr)) print(out, "/#{*curr}") - # . EAX = is-alphanumeric?(CL) - # . . push args - 51/push-ECX - # . . call - e8/call is-alphanumeric?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . if (EAX == 0) goto char-done - 3d/compare-EAX-and 0/imm32 - 74/jump-if-equal $emit-string-literal-data:char-done/disp8 - # . write(out, "/") - # . . push args - 68/push Slash/imm32 - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . append-byte(out, *curr) - # . . push args - 51/push-ECX - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - # . . call - e8/call append-byte/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -$emit-string-literal-data:char-done: - # write(out, " ") - # . . push args - 68/push Space/imm32 - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # ++curr - 42/increment-EDX - e9/jump $emit-string-literal-data:loop/disp32 -$emit-string-literal-data:end: - # . restore registers - 5e/pop-to-ESI - 5a/pop-to-EDX - 59/pop-to-ECX - 58/pop-to-EAX - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -is-alphanumeric?: # c : int -> EAX : boolean - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # EAX = c - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 0/r32/EAX 8/disp8 . # copy *(EBP+8) to EAX - # if (EAX < '0') return false - 3d/compare-EAX-with 0x30/imm32/0 - 7c/jump-if-lesser $is-alphanumeric?:false/disp8 - # if (EAX <= '9') return true - 3d/compare-EAX-with 0x39/imm32/9 - 7e/jump-if-lesser-or-equal $is-alphanumeric?:true/disp8 - # if (EAX < 'A') return false - 3d/compare-EAX-with 0x41/imm32/A - 7c/jump-if-lesser $is-alphanumeric?:false/disp8 - # if (EAX <= 'Z') return true - 3d/compare-EAX-with 0x5a/imm32/Z - 7e/jump-if-lesser-or-equal $is-alphanumeric?:true/disp8 - # if (EAX < 'a') return false - 3d/compare-EAX-with 0x61/imm32/a - 7c/jump-if-lesser $is-alphanumeric?:false/disp8 - # if (EAX <= 'z') return true - 3d/compare-EAX-with 0x7a/imm32/z - 7e/jump-if-lesser-or-equal $is-alphanumeric?:true/disp8 - # return false -$is-alphanumeric?:false: - b8/copy-to-EAX 0/imm32/false - eb/jump $is-alphanumeric?:end/disp8 -$is-alphanumeric?:true: - b8/copy-to-EAX 1/imm32/true -$is-alphanumeric?:end: - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-emit-string-literal-data: - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # setup - # . clear-stream(_test-output-stream) - # . . push args - 68/push _test-output-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # var slice/ECX = '"abc"/d' - 68/push _test-slice-abc-limit/imm32 - 68/push _test-slice-abc/imm32 - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # emit-string-literal-data(_test-output-stream, slice) - # . . push args - 51/push-ECX - 68/push _test-output-stream/imm32 - # . . call - e8/call emit-string-literal-data/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # dump output {{{ -#? # . write(2/stderr, "result: ^") -#? # . . push args -#? 68/push "result: ^"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write-stream(2/stderr, _test-output-stream) -#? # . . push args -#? 68/push _test-output-stream/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write-stream/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write(2/stderr, "$\n") -#? # . . push args -#? 68/push "$\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # }}} - # . check-stream-equal(_test-output-stream, "3/imm32 61/a 62/b 63/c ", msg) - # . . push args - 68/push "F - test-emit-string-literal-data"/imm32 - 68/push "0x00000003/imm32 61/a 62/b 63/c "/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-stream-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-emit-string-literal-data-empty: - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # setup - # . clear-stream(_test-output-stream) - # . . push args - 68/push _test-output-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # var slice/ECX = '""' - 68/push 0/imm32/end - 68/push 0/imm32/start - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # emit-string-literal-data(_test-output-stream, slice) - # . . push args - 51/push-ECX - 68/push _test-output-stream/imm32 - # . . call - e8/call emit-string-literal-data/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # dump output {{{ -#? # . write(2/stderr, "result: ^") -#? # . . push args -#? 68/push "result: ^"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write-stream(2/stderr, _test-output-stream) -#? # . . push args -#? 68/push _test-output-stream/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write-stream/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write(2/stderr, "$\n") -#? # . . push args -#? 68/push "$\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # }}} - # . check-stream-equal(_test-output-stream, "0/imm32 ", msg) - # . . push args - 68/push "F - test-emit-string-literal-data-empty"/imm32 - 68/push "0x00000000/imm32 "/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-stream-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -# just to keep things simple -test-emit-string-literal-data-no-metadata-for-non-alphanumerics: - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # setup - # . clear-stream(_test-output-stream) - # . . push args - 68/push _test-output-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # var slice/ECX = '"a b"' - 68/push _test-slice-a-space-b-limit/imm32 - 68/push _test-slice-a-space-b/imm32 - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # emit-string-literal-data(_test-output-stream, slice) - # . . push args - 51/push-ECX - 68/push _test-output-stream/imm32 - # . . call - e8/call emit-string-literal-data/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # dump output {{{ -#? # . write(2/stderr, "result: ^") -#? # . . push args -#? 68/push "result: ^"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write-stream(2/stderr, _test-output-stream) -#? # . . push args -#? 68/push _test-output-stream/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write-stream/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write(2/stderr, "$\n") -#? # . . push args -#? 68/push "$\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # }}} - # . check-stream-equal(_test-output-stream, "3/imm32 61/a 20 62/b ", msg) # ideally we'd like to say '20/space' but that requires managing names for codepoints - # . . push args - 68/push "F - test-emit-string-literal-data-no-metadata-for-non-alphanumerics"/imm32 - 68/push "0x00000003/imm32 61/a 20 62/b "/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-stream-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-emit-string-literal-data-handles-escape-sequences: - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # setup - # . clear-stream(_test-output-stream) - # . . push args - 68/push _test-output-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # var slice/ECX = '"a\"b"' - 68/push _test-slice-a-dquote-b-limit/imm32 - 68/push _test-slice-a-dquote-b/imm32 - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # emit-string-literal-data(_test-output-stream, slice) - # . . push args - 51/push-ECX - 68/push _test-output-stream/imm32 - # . . call - e8/call emit-string-literal-data/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # dump output {{{ -#? # . write(2/stderr, "result: ^") -#? # . . push args -#? 68/push "result: ^"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write-stream(2/stderr, _test-output-stream) -#? # . . push args -#? 68/push _test-output-stream/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write-stream/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write(2/stderr, "$\n") -#? # . . push args -#? 68/push "$\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # }}} - # . check-stream-equal(_test-output-stream, "3/imm32 61/a 22 62/b ", msg) - # . . push args - 68/push "F - test-emit-string-literal-data-handles-escape-sequences"/imm32 - 68/push "0x00000003/imm32 61/a 22 62/b "/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-stream-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-emit-string-literal-data-handles-newline-escape: - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # setup - # . clear-stream(_test-output-stream) - # . . push args - 68/push _test-output-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # var slice/ECX = '"a\nb"' - 68/push _test-slice-a-newline-b-limit/imm32 - 68/push _test-slice-a-newline-b/imm32 - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # emit-string-literal-data(_test-output-stream, slice) - # . . push args - 51/push-ECX - 68/push _test-output-stream/imm32 - # . . call - e8/call emit-string-literal-data/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # dump output {{{ -#? # . write(2/stderr, "result: ^") -#? # . . push args -#? 68/push "result: ^"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write-stream(2/stderr, _test-output-stream) -#? # . . push args -#? 68/push _test-output-stream/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write-stream/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write(2/stderr, "$\n") -#? # . . push args -#? 68/push "$\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # }}} - # . check-stream-equal(_test-output-stream, "3/imm32 61/a 0a 62/b ", msg) - # . . push args - 68/push "F - test-emit-string-literal-data-handles-newline-escape"/imm32 - 68/push "0x00000003/imm32 61/a 0a 62/b "/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-stream-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -# emit everything from a word except the initial datum -emit-metadata: # out : (address buffered-file), word : (address slice) - # pseudocode - # var slice = {0, word->end} - # curr = word->start - # if *curr == '"' - # curr = skip-string-in-slice(curr, word->end) - # else - # while true - # if curr == word->end - # return - # if *curr == '/' - # break - # ++curr - # slice->start = curr - # write-slice-buffered(out, slice) - # - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # . save registers - 50/push-EAX - 51/push-ECX - 52/push-EDX - 53/push-EBX - 56/push-ESI - # ESI = word - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 6/r32/ESI 0xc/disp8 . # copy *(EBP+12) to ESI - # curr/ECX = word->start - 8b/copy 0/mod/indirect 6/rm32/ESI . . . 1/r32/ECX . . # copy *ESI to ECX - # end/EDX = word->end - 8b/copy 1/mod/*+disp8 6/rm32/ESI . . . 2/r32/EDX 4/disp8 . # copy *(ESI+4) to EDX - # var slice/EBX = {0, end} - 52/push-EDX - 68/push 0/imm32 - 89/copy 3/mod/direct 3/rm32/EBX . . . 4/r32/ESP . . # copy ESP to EBX - # EAX = 0 - b8/copy-to-EAX 0/imm32 -$emit-metadata:check-for-string-literal: - # - if (*curr == '"') curr = skip-string-in-slice(curr, end) - 8a/copy-byte 0/mod/indirect 1/rm32/ECX . . . 0/r32/AL . . # copy byte at *ECX to AL - 3d/compare-EAX-and 0x22/imm32/dquote - 75/jump-if-not-equal $emit-metadata:skip-datum-loop/disp8 -$emit-metadata:skip-string-literal: - # . EAX = skip-string-in-slice(curr, end) - # . . push args - 52/push-EDX - 51/push-ECX - # . . call - e8/call skip-string-in-slice/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . curr = EAX - 89/copy 3/mod/direct 1/rm32/ECX . . . 0/r32/EAX . . # copy EAX to ECX - eb/jump $emit-metadata:emit/disp8 -$emit-metadata:skip-datum-loop: - # - otherwise scan for '/' - # if (curr == end) return - 39/compare 3/mod/direct 1/rm32/ECX . . . 2/r32/EDX . . # compare ECX and EDX - 74/jump-if-equal $emit-metadata:end/disp8 - # if (*curr == '/') break - 8a/copy-byte 0/mod/indirect 1/rm32/ECX . . . 0/r32/AL . . # copy byte at *ECX to AL - 3d/compare-EAX-and 0x2f/imm32/slash - 74/jump-if-equal $emit-metadata:emit/disp8 - # ++curr - 41/increment-ECX - eb/jump $emit-metadata:skip-datum-loop/disp8 -$emit-metadata:emit: - # slice->start = ECX - 89/copy 0/mod/indirect 3/rm32/EBX . . . 1/r32/ECX . . # copy ECX to *EBX - # write-slice-buffered(out, slice) - # . . push args - 53/push-EBX - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - # . . call - e8/call write-slice-buffered/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . 8/imm32 . # add to ESP -$emit-metadata:end: - # . reclaim locals - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . 8/imm32 . # add to ESP - # . restore registers - 5e/pop-to-ESI - 5b/pop-to-EBX - 5a/pop-to-EDX - 59/pop-to-ECX - 58/pop-to-EAX - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-emit-metadata: - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # setup - # . clear-stream(_test-output-stream) - # . . push args - 68/push _test-output-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-buffered-file+4) - # . . push args - b8/copy-to-EAX _test-output-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # (EAX..ECX) = "abc/def" - b8/copy-to-EAX "abc/def"/imm32 - 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX - 05/add-to-EAX 4/imm32 - # var slice/ECX = {EAX, ECX} - 51/push-ECX - 50/push-EAX - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # emit-metadata(_test-output-buffered-file, slice) - # . . push args - 51/push-ECX - 68/push _test-output-buffered-file/imm32 - # . . call - e8/call emit-metadata/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # flush(_test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - # . . call - e8/call flush/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # check-stream-equal(_test-output-stream, "/def", msg) # important that there's no leading space - # . . push args - 68/push "F - test-emit-metadata"/imm32 - 68/push "/def"/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-stream-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-emit-metadata-none: - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # setup - # . clear-stream(_test-output-stream) - # . . push args - 68/push _test-output-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-buffered-file+4) - # . . push args - b8/copy-to-EAX _test-output-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # (EAX..ECX) = "abc" - b8/copy-to-EAX "abc"/imm32 - 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX - 05/add-to-EAX 4/imm32 - # var slice/ECX = {EAX, ECX} - 51/push-ECX - 50/push-EAX - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # emit-metadata(_test-output-buffered-file, slice) - # . . push args - 51/push-ECX - 68/push _test-output-buffered-file/imm32 - # . . call - e8/call emit-metadata/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # flush(_test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - # . . call - e8/call flush/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # check-stream-equal(_test-output-stream, "", msg) - # . . push args - 68/push "F - test-emit-metadata-none"/imm32 - 68/push ""/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-stream-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-emit-metadata-multiple: - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # setup - # . clear-stream(_test-output-stream) - # . . push args - 68/push _test-output-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-buffered-file+4) - # . . push args - b8/copy-to-EAX _test-output-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # (EAX..ECX) = "abc/def/ghi" - b8/copy-to-EAX "abc/def/ghi"/imm32 - 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX - 05/add-to-EAX 4/imm32 - # var slice/ECX = {EAX, ECX} - 51/push-ECX - 50/push-EAX - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # emit-metadata(_test-output-buffered-file, slice) - # . . push args - 51/push-ECX - 68/push _test-output-buffered-file/imm32 - # . . call - e8/call emit-metadata/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # flush(_test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - # . . call - e8/call flush/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # check-stream-equal(_test-output-stream, "/def/ghi", msg) # important that there's no leading space - # . . push args - 68/push "F - test-emit-metadata-multiple"/imm32 - 68/push "/def/ghi"/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-stream-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-emit-metadata-when-no-datum: - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # setup - # . clear-stream(_test-output-stream) - # . . push args - 68/push _test-output-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-buffered-file+4) - # . . push args - b8/copy-to-EAX _test-output-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # var slice/ECX = "/abc" - b8/copy-to-EAX "/abc"/imm32 - # . push end/ECX - 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX - 51/push-ECX - # . push curr/EAX - 05/add-to-EAX 4/imm32 - 50/push-EAX - # . save stack pointer - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # emit-metadata(_test-output-buffered-file, slice) - # . . push args - 51/push-ECX - 68/push _test-output-buffered-file/imm32 - # . . call - e8/call emit-metadata/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # flush(_test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - # . . call - e8/call flush/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # check-stream-equal(_test-output-stream, "/abc", msg) # nothing skipped - # . . push args - 68/push "F - test-emit-metadata-when-no-datum"/imm32 - 68/push "/abc"/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-stream-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-emit-metadata-in-string-literal: - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # setup - # . clear-stream(_test-output-stream) - # . . push args - 68/push _test-output-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-buffered-file+4) - # . . push args - b8/copy-to-EAX _test-output-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # var slice/ECX = "\"abc/def\"/ghi" - 68/push _test-slice-literal-string-with-limit/imm32 - 68/push _test-slice-literal-string/imm32/start - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # emit-metadata(_test-output-buffered-file, slice) - # . . push args - 51/push-ECX - 68/push _test-output-buffered-file/imm32 - # . . call - e8/call emit-metadata/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # flush(_test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - # . . call - e8/call flush/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -#? # dump output {{{ -#? # . write(2/stderr, "result: ^") -#? # . . push args -#? 68/push "result: ^"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write-stream(2/stderr, _test-output-stream) -#? # . . push args -#? 68/push _test-output-stream/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write-stream/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write(2/stderr, "$\n") -#? # . . push args -#? 68/push "$\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # }}} - # check-stream-equal(_test-output-stream, "/ghi", msg) # important that there's no leading space - # . . push args - 68/push "F - test-emit-metadata-in-string-literal"/imm32 - 68/push "/ghi"/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-stream-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -# (re)compute the bounds of the next word in the line -# return empty string on reaching end of file -next-word-or-string: # line : (address stream byte), out : (address slice) - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # . save registers - 50/push-EAX - 51/push-ECX - 56/push-ESI - 57/push-EDI - # ESI = line - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 6/r32/ESI 8/disp8 . # copy *(EBP+8) to ESI - # EDI = out - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 7/r32/EDI 0xc/disp8 . # copy *(EBP+12) to EDI - # skip-chars-matching(line, ' ') - # . . push args - 68/push 0x20/imm32/space - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - # . . call - e8/call skip-chars-matching/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -$next-word-or-string:check0: - # if (line->read >= line->write) clear out and return - # . EAX = line->read - 8b/copy 1/mod/*+disp8 6/rm32/ESI . . . 0/r32/EAX 4/disp8 . # copy *(ESI+4) to EAX - # . if (EAX < line->write) goto next check - 3b/compare 0/mod/indirect 6/rm32/ESI . . . 0/r32/EAX . . # compare EAX with *ESI - 7c/jump-if-lesser $next-word-or-string:check-for-comment/disp8 - # . return out = {0, 0} - c7 0/subop/copy 0/mod/direct 7/rm32/EDI . . . . . 0/imm32 # copy to *EDI - c7 0/subop/copy 1/mod/*+disp8 7/rm32/EDI . . . . 4/disp8 0/imm32 # copy to *(EDI+4) - eb/jump $next-word-or-string:end/disp8 -$next-word-or-string:check-for-comment: - # out->start = &line->data[line->read] - 8b/copy 1/mod/*+disp8 6/rm32/ESI . . . 1/r32/ECX 4/disp8 . # copy *(ESI+4) to ECX - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 6/base/ESI 1/index/ECX . 0/r32/EAX 0xc/disp8 . # copy ESI+ECX+12 to EAX - 89/copy 0/mod/indirect 7/rm32/EDI . . . 0/r32/EAX . . # copy EAX to *EDI - # if line->data[line->read] == '#' - # . EAX = line->data[line->read] - 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX - 8a/copy-byte 1/mod/*+disp8 4/rm32/sib 6/base/ESI 1/index/ECX . 0/r32/AL 0xc/disp8 . # copy byte at *(ESI+ECX+12) to AL - # . compare - 3d/compare-EAX-and 0x23/imm32/pound - 75/jump-if-not-equal $next-word-or-string:check-for-string-literal/disp8 -$next-word-or-string:comment: - # out->end = &line->data[line->write] - 8b/copy 0/mod/indirect 6/rm32/ESI . . . 0/r32/EAX . . # copy *ESI to EAX - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 6/base/ESI 0/index/EAX . 0/r32/EAX 0xc/disp8 . # copy ESI+EAX+12 to EAX - 89/copy 1/mod/*+disp8 7/rm32/EDI . . . 0/r32/EAX 4/disp8 . # copy EAX to *(EDI+4) - # line->read = line->write # skip rest of line - 8b/copy 0/mod/indirect 6/rm32/ESI . . . 0/r32/EAX . . # copy *ESI to EAX - 89/copy 1/mod/*+disp8 6/rm32/ESI . . . 0/r32/EAX 4/disp8 . # copy EAX to *(ESI+4) - # return - eb/jump $next-word-or-string:end/disp8 -$next-word-or-string:check-for-string-literal: - # if line->data[line->read] == '"' - # . EAX = line->data[line->read] - 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX - 8a/copy-byte 1/mod/*+disp8 4/rm32/sib 6/base/ESI 1/index/ECX . 0/r32/AL 0xc/disp8 . # copy byte at *(ESI+ECX+12) to AL - # . compare - 3d/compare-EAX-and 0x22/imm32/dquote - 75/jump-if-not-equal $next-word-or-string:regular-word/disp8 -$next-word-or-string:string-literal: - # skip-string(line) - # . . push args - 56/push-ESI - # . . call - e8/call skip-string/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # fall through -$next-word-or-string:regular-word: - # skip-chars-not-matching-whitespace(line) # including trailing newline - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - # . . call - e8/call skip-chars-not-matching-whitespace/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # out->end = &line->data[line->read] - 8b/copy 1/mod/*+disp8 6/rm32/ESI . . . 1/r32/ECX 4/disp8 . # copy *(ESI+4) to ECX - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 6/base/ESI 1/index/ECX . 0/r32/EAX 0xc/disp8 . # copy ESI+ECX+12 to EAX - 89/copy 1/mod/*+disp8 7/rm32/EDI . . . 0/r32/EAX 4/disp8 . # copy EAX to *(EDI+4) -$next-word-or-string:end: - # . restore registers - 5f/pop-to-EDI - 5e/pop-to-ESI - 59/pop-to-ECX - 58/pop-to-EAX - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-next-word-or-string: - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # setup - # . clear-stream(_test-input-stream) - # . . push args - 68/push _test-input-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # var slice/ECX = {0, 0} - 68/push 0/imm32/end - 68/push 0/imm32/start - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # write(_test-input-stream, " ab") - # . . push args - 68/push " ab"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # next-word-or-string(_test-input-stream, slice) - # . . push args - 51/push-ECX - 68/push _test-input-stream/imm32 - # . . call - e8/call next-word-or-string/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check-ints-equal(_test-input-stream->read, 4, msg) - # . . push args - 68/push "F - test-next-word-or-string/updates-stream-read-correctly"/imm32 - 68/push 4/imm32 - b8/copy-to-EAX _test-input-stream/imm32 - ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 4/disp8 . # push *(EAX+4) - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # check-ints-equal(slice->start - _test-input-stream->data, 2, msg) - # . check-ints-equal(slice->start - _test-input-stream, 14, msg) - # . . push args - 68/push "F - test-next-word-or-string: start"/imm32 - 68/push 0xe/imm32 - # . . push slice->start - _test-input-stream - 8b/copy 0/mod/indirect 1/rm32/ECX . . . 0/r32/EAX . . # copy *ECX to EAX - 81 5/subop/subtract 3/mod/direct 0/rm32/EAX . . . . . _test-input-stream/imm32 # subtract from EAX - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # check-ints-equal(slice->end - _test-input-stream->data, 4, msg) - # . check-ints-equal(slice->end - _test-input-stream, 16, msg) - # . . push args - 68/push "F - test-next-word-or-string: end"/imm32 - 68/push 0x10/imm32 - # . . push slice->end - _test-input-stream - 8b/copy 1/mod/*+disp8 1/rm32/ECX . . . 0/r32/EAX 4/disp8 . # copy *(ECX+4) to EAX - 81 5/subop/subtract 3/mod/direct 0/rm32/EAX . . . . . _test-input-stream/imm32 # subtract from EAX - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-next-word-or-string-returns-whole-comment: - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # setup - # . clear-stream(_test-input-stream) - # . . push args - 68/push _test-input-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # var slice/ECX = {0, 0} - 68/push 0/imm32/end - 68/push 0/imm32/start - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # write(_test-input-stream, " # a") - # . . push args - 68/push " # a"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # next-word-or-string(_test-input-stream, slice) - # . . push args - 51/push-ECX - 68/push _test-input-stream/imm32 - # . . call - e8/call next-word-or-string/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check-ints-equal(_test-input-stream->read, 5, msg) - # . . push args - 68/push "F - test-next-word-or-string-returns-whole-comment/updates-stream-read-correctly"/imm32 - 68/push 5/imm32 - b8/copy-to-EAX _test-input-stream/imm32 - ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 4/disp8 . # push *(EAX+4) - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # check-ints-equal(slice->start - _test-input-stream->data, 2, msg) - # . check-ints-equal(slice->start - _test-input-stream, 14, msg) - # . . push args - 68/push "F - test-next-word-or-string-returns-whole-comment: start"/imm32 - 68/push 0xe/imm32 - # . . push slice->start - _test-input-stream - 8b/copy 0/mod/indirect 1/rm32/ECX . . . 0/r32/EAX . . # copy *ECX to EAX - 81 5/subop/subtract 3/mod/direct 0/rm32/EAX . . . . . _test-input-stream/imm32 # subtract from EAX - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # check-ints-equal(slice->end - _test-input-stream->data, 5, msg) - # . check-ints-equal(slice->end - _test-input-stream, 17, msg) - # . . push args - 68/push "F - test-next-word-or-string-returns-whole-comment: end"/imm32 - 68/push 0x11/imm32 - # . . push slice->end - _test-input-stream - 8b/copy 1/mod/*+disp8 1/rm32/ECX . . . 0/r32/EAX 4/disp8 . # copy *(ECX+4) to EAX - 81 5/subop/subtract 3/mod/direct 0/rm32/EAX . . . . . _test-input-stream/imm32 # subtract from EAX - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-next-word-or-string-returns-empty-string-on-eof: - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # setup - # . clear-stream(_test-input-stream) - # . . push args - 68/push _test-input-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # var slice/ECX = {0, 0} - 68/push 0/imm32/end - 68/push 0/imm32/start - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # write nothing to _test-input-stream - # next-word-or-string(_test-input-stream, slice) - # . . push args - 51/push-ECX - 68/push _test-input-stream/imm32 - # . . call - e8/call next-word-or-string/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check-ints-equal(slice->end - slice->start, 0, msg) - # . . push args - 68/push "F - test-next-word-or-string-returns-empty-string-on-eof"/imm32 - 68/push 0/imm32 - # . . push slice->end - slice->start - 8b/copy 1/mod/*+disp8 1/rm32/ECX . . . 0/r32/EAX 4/disp8 . # copy *(ECX+4) to EAX - 2b/subtract 0/mod/indirect 1/rm32/ECX . . . 0/r32/EAX . . # subtract *ECX from EAX - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-next-word-or-string-returns-whole-string: - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # setup - # . clear-stream(_test-input-stream) - # . . push args - 68/push _test-input-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # var slice/ECX = {0, 0} - 68/push 0/imm32/end - 68/push 0/imm32/start - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # write(_test-input-stream, " \"a b\"/imm32 ") - # . . push args - 68/push " \"a b\"/imm32 "/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # next-word-or-string(_test-input-stream, slice) - # . . push args - 51/push-ECX - 68/push _test-input-stream/imm32 - # . . call - e8/call next-word-or-string/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check-ints-equal(slice->start - _test-input-stream->data, 1, msg) - # . check-ints-equal(slice->start - _test-input-stream, 13, msg) - # . . push args - 68/push "F - test-next-word-or-string-returns-whole-string: start"/imm32 - 68/push 0xd/imm32 - # . . push slice->start - _test-input-stream - 8b/copy 0/mod/indirect 1/rm32/ECX . . . 0/r32/EAX . . # copy *ECX to EAX - 81 5/subop/subtract 3/mod/direct 0/rm32/EAX . . . . . _test-input-stream/imm32 # subtract from EAX - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # check-ints-equal(slice->end - _test-input-stream->data, 12, msg) - # . check-ints-equal(slice->end - _test-input-stream, 24, msg) - # . . push args - 68/push "F - test-next-word-or-string-returns-whole-string: end"/imm32 - 68/push 0x18/imm32 - # . . push slice->end - _test-input-stream - 8b/copy 1/mod/*+disp8 1/rm32/ECX . . . 0/r32/EAX 4/disp8 . # copy *(ECX+4) to EAX - 81 5/subop/subtract 3/mod/direct 0/rm32/EAX . . . . . _test-input-stream/imm32 # subtract from EAX - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-next-word-or-string-returns-string-with-escapes: - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # setup - # . clear-stream(_test-input-stream) - # . . push args - 68/push _test-input-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # var slice/ECX = {0, 0} - 68/push 0/imm32/end - 68/push 0/imm32/start - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # write(_test-input-stream, " \"a\\\"b\"/x") - # . . push args - 68/push " \"a\\\"b\"/x"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # next-word-or-string(_test-input-stream, slice) - # . . push args - 51/push-ECX - 68/push _test-input-stream/imm32 - # . . call - e8/call next-word-or-string/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check-ints-equal(slice->start - _test-input-stream->data, 1, msg) - # . check-ints-equal(slice->start - _test-input-stream, 13, msg) - # . . push args - 68/push "F - test-next-word-or-string-returns-string-with-escapes: start"/imm32 - 68/push 0xd/imm32 - # . . push slice->start - _test-input-stream - 8b/copy 0/mod/indirect 1/rm32/ECX . . . 0/r32/EAX . . # copy *ECX to EAX - 81 5/subop/subtract 3/mod/direct 0/rm32/EAX . . . . . _test-input-stream/imm32 # subtract from EAX - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # check-ints-equal(slice->end - _test-input-stream->data, 9, msg) - # . check-ints-equal(slice->end - _test-input-stream, 21, msg) - # . . push args - 68/push "F - test-next-word-or-string-returns-string-with-escapes: end"/imm32 - 68/push 0x15/imm32 - # . . push slice->end - _test-input-stream - 8b/copy 1/mod/*+disp8 1/rm32/ECX . . . 0/r32/EAX 4/disp8 . # copy *(ECX+4) to EAX - 81 5/subop/subtract 3/mod/direct 0/rm32/EAX . . . . . _test-input-stream/imm32 # subtract from EAX - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -# update line->read to end of string literal surrounded by double quotes -# line->read must start out at a double-quote -skip-string: # line : (address stream) - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # . save registers - 50/push-EAX - 51/push-ECX - 52/push-EDX - # ECX = line - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . 1/r32/ECX 8/disp8 . # copy *(EBP+8) to ECX - # EAX = skip-string-in-slice(&line->data[line->read], &line->data[line->write]) - # . . push &line->data[line->write] - 8b/copy 1/mod/*+disp8 1/rm32/ECX . . 2/r32/EDX 8/disp8 . # copy *(ECX+8) to EDX - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 1/base/ECX 2/index/EDX . 2/r32/EDX 0xc/disp8 . # copy ECX+EDX+12 to EDX - 52/push-EDX - # . . push &line->data[line->read] - 8b/copy 1/mod/*+disp8 1/rm32/ECX . . 2/r32/EDX 4/disp8 . # copy *(ECX+4) to EDX - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 1/base/ECX 2/index/EDX . 2/r32/EDX 0xc/disp8 . # copy ECX+EDX+12 to EDX - 52/push-EDX - # . . call - e8/call skip-string-in-slice/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # line->read = EAX - line->data - 29/subtract 3/mod/direct 0/rm32/EAX . . . 1/r32/ECX . . # subtract ECX from EAX - 2d/subtract-from-EAX 0xc/imm32 - 89/copy 1/mod/*+disp8 1/rm32/ECX . . 0/r32/EAX 4/disp8 . # copy EAX to *(ECX+4) -$skip-string:end: - # . restore registers - 5a/pop-to-EDX - 59/pop-to-ECX - 58/pop-to-EAX - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-skip-string: - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # setup - # . clear-stream(_test-input-stream) - # . . push args - 68/push _test-input-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . write(_test-input-stream, "\"abc\" def") - # . indices: 0123 45 - # . . push args - 68/push "\"abc\" def"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # precondition: line->read == 0 - # . . push args - 68/push "F - test-skip-string/precondition"/imm32 - 68/push 0/imm32 - b8/copy-to-EAX _test-input-stream/imm32 - ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 4/disp8 . # push *(EAX+4) - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # skip-string(_test-input-stream) - # . . push args - 68/push _test-input-stream/imm32 - # . . call - e8/call skip-string/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # check-ints-equal(line->read, 5, msg) - # . . push args - 68/push "F - test-skip-string"/imm32 - 68/push 5/imm32 - b8/copy-to-EAX _test-input-stream/imm32 - ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 4/disp8 . # push *(EAX+4) - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-skip-string-ignores-spaces: - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # setup - # . clear-stream(_test-input-stream) - # . . push args - 68/push _test-input-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . write(_test-input-stream, "\"a b\"/yz") - # . indices: 0123 45 - # . . push args - 68/push "\"a b\"/yz"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # precondition: line->read == 0 - # . . push args - 68/push "F - test-skip-string-ignores-spaces/precondition"/imm32 - 68/push 0/imm32 - b8/copy-to-EAX _test-input-stream/imm32 - ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 4/disp8 . # push *(EAX+4) - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # skip-string(_test-input-stream) - # . . push args - 68/push _test-input-stream/imm32 - # . . call - e8/call skip-string/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # check-ints-equal(line->read, 5, msg) - # . . push args - 68/push "F - test-skip-string-ignores-spaces"/imm32 - 68/push 5/imm32 - b8/copy-to-EAX _test-input-stream/imm32 - ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 4/disp8 . # push *(EAX+4) - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-skip-string-ignores-escapes: - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # setup - # . clear-stream(_test-input-stream) - # . . push args - 68/push _test-input-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . write(_test-input-stream, "\"a\\\"b\"/yz") - # . indices: 01 2 34 56 - # . . push args - 68/push "\"a\\\"b\"/yz"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # precondition: line->read == 0 - # . . push args - 68/push "F - test-skip-string-ignores-escapes/precondition"/imm32 - 68/push 0/imm32 - b8/copy-to-EAX _test-input-stream/imm32 - ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 4/disp8 . # push *(EAX+4) - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # skip-string(_test-input-stream) - # . . push args - 68/push _test-input-stream/imm32 - # . . call - e8/call skip-string/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # check-ints-equal(line->read, 6, msg) - # . . push args - 68/push "F - test-skip-string-ignores-escapes"/imm32 - 68/push 6/imm32 - b8/copy-to-EAX _test-input-stream/imm32 - ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 4/disp8 . # push *(EAX+4) - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-skip-string-works-from-mid-stream: - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # setup - # . clear-stream(_test-input-stream) - # . . push args - 68/push _test-input-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . write(_test-input-stream, "0 \"a\\\"b\"/yz") - # . indices: 01 2 34 56 - # . . push args - 68/push "0 \"a\\\"b\"/yz"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # precondition: line->read == 2 - c7 0/subop/copy 1/mod/*+disp8 0/rm32/EAX . . . . 4/disp8 2/imm32 # copy to *(EAX+4) - # skip-string(_test-input-stream) - # . . push args - 68/push _test-input-stream/imm32 - # . . call - e8/call skip-string/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # check-ints-equal(line->read, 8, msg) - # . . push args - 68/push "F - test-skip-string-works-from-mid-stream"/imm32 - 68/push 8/imm32 - b8/copy-to-EAX _test-input-stream/imm32 - ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 4/disp8 . # push *(EAX+4) - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -skip-string-in-slice: # curr : (address byte), end : (address byte) -> new_curr/EAX - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # . save registers - 51/push-ECX - 52/push-EDX - 53/push-EBX - # ECX = curr - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . 1/r32/ECX 8/disp8 . # copy *(EBP+8) to ECX - # EDX = end - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . 2/r32/EDX 0xc/disp8 . # copy *(EBP+12) to EDX - # EAX = 0 - 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX - # skip initial dquote - 41/increment-ECX -$skip-string-in-slice:loop: - # if (curr >= end) return curr - 39/compare 3/mod/direct 1/rm32/ECX . . . 2/r32/EDX . . # compare ECX with EDX - 73/jump-if-greater-unsigned-or-equal $skip-string-in-slice:return-curr/disp8 - # AL = *curr - 8a/copy-byte 0/mod/indirect 1/rm32/ECX . . . 0/r32/AL . . # copy byte at *ECX to AL -$skip-string-in-slice:dquote: - # if (EAX == '"') break - 3d/compare-EAX-and 0x22/imm32/double-quote - 74/jump-if-equal $skip-string-in-slice:break/disp8 -$skip-string-in-slice:check-for-escape: - # if (EAX == '\') escape next char - 3d/compare-EAX-and 0x5c/imm32/backslash - 75/jump-if-not-equal $skip-string-in-slice:continue/disp8 -$skip-string-in-slice:escape: - 41/increment-ECX -$skip-string-in-slice:continue: - # ++curr - 41/increment-ECX - eb/jump $skip-string-in-slice:loop/disp8 -$skip-string-in-slice:break: - # skip final dquote - 41/increment-ECX -$skip-string-in-slice:return-curr: - # return curr - 89/copy 3/mod/direct 0/rm32/EAX . . . 1/r32/ECX . . # copy ECX to EAX -$skip-string-in-slice:end: - # . restore registers - 5b/pop-to-EBX - 5a/pop-to-EDX - 59/pop-to-ECX - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-skip-string-in-slice: - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # setup: (EAX..ECX) = "\"abc\" def" - b8/copy-to-EAX "\"abc\" def"/imm32 - 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX - 05/add-to-EAX 4/imm32 - # EAX = skip-string-in-slice(EAX, ECX) - # . . push args - 51/push-ECX - 50/push-EAX - # . . call - e8/call skip-string-in-slice/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check-ints-equal(ECX-EAX, 4, msg) # number of chars remaining after the string literal - # . . push args - 68/push "F - test-skip-string-in-slice"/imm32 - 68/push 4/imm32 - # . . push ECX-EAX - 29/subtract 3/mod/direct 1/rm32/ECX . . . 0/r32/EAX . . # subtract EAX from ECX - 51/push-ECX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-skip-string-in-slice-ignores-spaces: - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # setup: (EAX..ECX) = "\"a b\"/yz" - b8/copy-to-EAX "\"a b\"/yz"/imm32 - 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX - 05/add-to-EAX 4/imm32 - # EAX = skip-string-in-slice(EAX, ECX) - # . . push args - 51/push-ECX - 50/push-EAX - # . . call - e8/call skip-string-in-slice/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check-ints-equal(ECX-EAX, 3, msg) # number of chars remaining after the string literal - # . . push args - 68/push "F - test-skip-string-in-slice-ignores-spaces"/imm32 - 68/push 3/imm32 - # . . push ECX-EAX - 29/subtract 3/mod/direct 1/rm32/ECX . . . 0/r32/EAX . . # subtract EAX from ECX - 51/push-ECX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-skip-string-in-slice-ignores-escapes: - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # setup: (EAX..ECX) = "\"a\\\"b\"/yz" - b8/copy-to-EAX "\"a\\\"b\"/yz"/imm32 - 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX - 05/add-to-EAX 4/imm32 - # EAX = skip-string-in-slice(EAX, ECX) - # . . push args - 51/push-ECX - 50/push-EAX - # . . call - e8/call skip-string-in-slice/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check-ints-equal(ECX-EAX, 3, msg) # number of chars remaining after the string literal - # . . push args - 68/push "F - test-skip-string-in-slice-ignores-escapes"/imm32 - 68/push 3/imm32 - # . . push ECX-EAX - 29/subtract 3/mod/direct 1/rm32/ECX . . . 0/r32/EAX . . # subtract EAX from ECX - 51/push-ECX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-skip-string-in-slice-stops-at-end: - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # setup: (EAX..ECX) = "\"abc" # unbalanced dquote - b8/copy-to-EAX "\"abc"/imm32 - 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX - 05/add-to-EAX 4/imm32 - # EAX = skip-string-in-slice(EAX, ECX) - # . . push args - 51/push-ECX - 50/push-EAX - # . . call - e8/call skip-string-in-slice/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check-ints-equal(ECX-EAX, 0, msg) # skipped to end of slice - # . . push args - 68/push "F - test-skip-string-in-slice-stops-at-end"/imm32 - 68/push 0/imm32 - # . . push ECX-EAX - 29/subtract 3/mod/direct 1/rm32/ECX . . . 0/r32/EAX . . # subtract EAX from ECX - 51/push-ECX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -string-length-at-start-of-slice: # curr : (address byte), end : (address byte) -> length/EAX - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # . save registers - 51/push-ECX - 52/push-EDX - 53/push-EBX - # ECX = curr - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . 1/r32/ECX 8/disp8 . # copy *(EBP+8) to ECX - # EDX = end - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . 2/r32/EDX 0xc/disp8 . # copy *(EBP+12) to EDX - # length/EAX = 0 - 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX - # EBX = 0 - 31/xor 3/mod/direct 3/rm32/EBX . . . 3/r32/EBX . . # clear EBX - # skip initial dquote - 41/increment-ECX -$string-length-at-start-of-slice:loop: - # if (curr >= end) return length - 39/compare 3/mod/direct 1/rm32/ECX . . . 2/r32/EDX . . # compare ECX with EDX - 73/jump-if-greater-unsigned-or-equal $string-length-at-start-of-slice:end/disp8 - # BL = *curr - 8a/copy-byte 0/mod/indirect 1/rm32/ECX . . . 3/r32/BL . . # copy byte at *ECX to BL -$string-length-at-start-of-slice:dquote: - # if (EBX == '"') break - 81 7/subop/compare 3/mod/direct 3/rm32/EBX . . . . . 0x22/imm32/dquote # compare EBX - 74/jump-if-equal $string-length-at-start-of-slice:end/disp8 -$string-length-at-start-of-slice:check-for-escape: - # if (EBX == '\') escape next char - 81 7/subop/compare 3/mod/direct 3/rm32/EBX . . . . . 0x5c/imm32/backslash # compare EBX - 75/jump-if-not-equal $string-length-at-start-of-slice:continue/disp8 -$string-length-at-start-of-slice:escape: - # increment curr but not result - 41/increment-ECX -$string-length-at-start-of-slice:continue: - # ++result - 40/increment-EAX - # ++curr - 41/increment-ECX - eb/jump $string-length-at-start-of-slice:loop/disp8 -$string-length-at-start-of-slice:end: - # . restore registers - 5b/pop-to-EBX - 5a/pop-to-EDX - 59/pop-to-ECX - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-string-length-at-start-of-slice: - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # setup: (EAX..ECX) = "\"abc\" def" - b8/copy-to-EAX "\"abc\" def"/imm32 - 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX - 05/add-to-EAX 4/imm32 - # EAX = string-length-at-start-of-slice(EAX, ECX) - # . . push args - 51/push-ECX - 50/push-EAX - # . . call - e8/call string-length-at-start-of-slice/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check-ints-equal(EAX, 3, msg) - # . . push args - 68/push "F - test-string-length-at-start-of-slice"/imm32 - 68/push 3/imm32 - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-string-length-at-start-of-slice-escaped: - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # setup: (EAX..ECX) = "\"ab\\c\" def" - b8/copy-to-EAX "\"ab\\c\" def"/imm32 - 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX - 05/add-to-EAX 4/imm32 - # EAX = string-length-at-start-of-slice(EAX, ECX) - # . . push args - 51/push-ECX - 50/push-EAX - # . . call - e8/call string-length-at-start-of-slice/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check-ints-equal(EAX, 3, msg) - # . . push args - 68/push "F - test-string-length-at-start-of-slice-escaped"/imm32 - 68/push 3/imm32 - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -== data - -Next-string-literal: # tracks the next auto-generated variable name - 1/imm32 - -# length-prefixed string containing just a single space -Space: - # size - 1/imm32 - # data - 20/space - -# length-prefixed string containing just a single slash -Slash: - # size - 1/imm32 - # data - 2f/slash - -_test-slice-abc: - 22/dquote 61/a 62/b 63/c 22/dquote # "abc" - 2f/slash 64/d -_test-slice-abc-limit: - -_test-slice-a-space-b: - 22/dquote 61/a 20/space 62/b 22/dquote # "a b" -_test-slice-a-space-b-limit: - -_test-slice-a-dquote-b: - 22/dquote 61/a 5c/backslash 22/dquote 62/b 22/dquote # "a\"b" -_test-slice-a-dquote-b-limit: - -_test-slice-a-newline-b: - 22/dquote 61/a 5c/backslash 6e/n 62/b 22/dquote # "a\nb" -_test-slice-a-newline-b-limit: - -# "abc/def"/ghi -_test-slice-literal-string: - 22/dquote - 61/a 62/b 63/c # abc - 2f/slash 64/d 65/e 66/f # /def - 22/dquote - 2f/slash 67/g 68/h 69/i # /ghi -_test-slice-literal-string-with-limit: - -# . . vim:nowrap:textwidth=0 diff --git a/subx/apps/factorial b/subx/apps/factorial deleted file mode 100755 index b82be120..00000000 --- a/subx/apps/factorial +++ /dev/null Binary files differdiff --git a/subx/apps/factorial.subx b/subx/apps/factorial.subx deleted file mode 100644 index e4b7a057..00000000 --- a/subx/apps/factorial.subx +++ /dev/null @@ -1,117 +0,0 @@ -## compute the factorial of 5, and return the result in the exit code -# -# To run (from the subx directory): -# $ ./subx translate apps/factorial.subx -o apps/factorial -# $ ./subx run apps/factorial -# Expected result: -# $ echo $? -# 120 -# -# You can also run the automated test suite: -# $ ./subx run apps/factorial test -# Expected output: -# ........ -# Every '.' indicates a passing test. Failing tests get a 'F'. - -== code -# instruction effective address register displacement immediate -# . op subop mod rm32 base index scale r32 -# . 1-3 bytes 3 bits 2 bits 3 bits 3 bits 3 bits 2 bits 2 bits 0/1/2/4 bytes 0/1/2/4 bytes - -Entry: # run tests if necessary, compute `factorial(5)` if not - # initialize heap - # . Heap = new-segment(64KB) - # . . push args - 68/push Heap/imm32 - 68/push 0x10000/imm32/64KB - # . . call - e8/call new-segment/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - - # . prolog - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # - if argc > 1 and argv[1] == "test", then return run_tests() - # . argc > 1 - 81 7/subop/compare 1/mod/*+disp8 5/rm32/EBP . . . . 0/disp8 1/imm32 # compare *EBP - 7e/jump-if-lesser-or-equal $run-main/disp8 - # . argv[1] == "test" - # . . push args - 68/push "test"/imm32 - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - # . . call - e8/call kernel-string-equal?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . check result - 3d/compare-EAX-and 1/imm32 - 75/jump-if-not-equal $run-main/disp8 - # . run-tests() - e8/call run-tests/disp32 - 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 0/r32/EAX Num-test-failures/disp32 # copy *Num-test-failures to EAX - eb/jump $main:end/disp8 # where EAX will get copied to EBX -$run-main: - # - otherwise return factorial(5) - # . . push args - 68/push 5/imm32 - # . . call - e8/call factorial/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -$main:end: - # syscall(exit, EAX) - 89/copy 3/mod/direct 3/rm32/EBX . . . 0/r32/EAX . . # copy EAX to EBX - b8/copy-to-EAX 1/imm32/exit - cd/syscall 0x80/imm8 - -factorial: # n : int -> int/EAX - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - 53/push-EBX - # EAX = 1 (base case) - b8/copy-to-EAX 1/imm32 - # if (n <= 1) return - 81 7/subop/compare 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 1/imm32 # compare *(EBP+8) - 7e/jump-if-<= $factorial:end/disp8 - # EBX = n-1 - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . 3/r32/EBX 8/disp8 . # copy *(EBP+8) to EBX - 81 5/subop/subtract 3/mod/direct 3/rm32/EBX . . . . . 1/imm32 # subtract from EBX - # EAX = factorial(n-1) - # . . push args - 53/push-EBX - # . . call - e8/call factorial/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # return n * factorial(n-1) - f7 4/subop/multiply 1/mod/*+disp8 5/rm32/EBP . . 8/disp8 . # multiply *(EBP+8) into EAX - # TODO: check for overflow -$factorial:end: - # . epilog - 5b/pop-to-EBX - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-factorial: - # factorial(5) - # . . push args - 68/push 5/imm32 - # . . call - e8/call factorial/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # check-ints-equal(EAX, 120, msg) - # . . push args - 68/push "F - test-factorial"/imm32 - 68/push 0x78/imm32/expected-120 - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # end - c3/return - -# . . vim:nowrap:textwidth=0 diff --git a/subx/apps/handle b/subx/apps/handle deleted file mode 100755 index b6794474..00000000 --- a/subx/apps/handle +++ /dev/null Binary files differdiff --git a/subx/apps/handle.subx b/subx/apps/handle.subx deleted file mode 100644 index ba824f85..00000000 --- a/subx/apps/handle.subx +++ /dev/null @@ -1,412 +0,0 @@ -# A sketch of Mu-style handles or kinda-safe pointers, that add a modicum of -# checking to dynamically allocated memory. -# -# This approach avoids using 'allocate' directly in favor of two primitives: -# - 'new', which allocates some space (the 'payload'), stores the address -# along with an opaque 'alloc id' in a 'handle', and prepends the same -# alloc id to the payload. -# - 'lookup', which checks that the alloc id at the start of a handle matches -# the alloc id at the start of the payload before returning the address. -# -# Layout of a handle: -# offset 0: alloc id -# offset 4: address -# -# To run (from the subx directory): -# $ ./subx translate *.subx apps/handle.subx -o apps/handle -# $ ./subx run apps/handle -# Expected result is a successful lookup followed by a hard abort: -# lookup succeeded -# lookup failed -# (This file is a prototype. The 'tests' in it aren't real; failures are -# expected.) - -== code -# instruction effective address register displacement immediate -# . op subop mod rm32 base index scale r32 -# . 1-3 bytes 3 bits 2 bits 3 bits 3 bits 3 bits 2 bits 2 bits 0/1/2/4 bytes 0/1/2/4 bytes - -# no Entry; the standard library runs all tests by default - -new: # ad : (address allocation-descriptor), n : int, out : (address handle) - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # . save registers - 50/push-EAX - 51/push-ECX - 52/push-EDX - # ECX = n+4 - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 1/r32/ECX 0xc/disp8 . # copy *(EBP+12) to ECX - 81 0/subop/add 3/mod/direct 1/rm32/ECX . . . . . 4/imm32 # add to ECX - # EAX = allocate(ad, ECX) - # . . push args - 51/push-ECX - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - # . . call - e8/call allocate/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # EDX = out - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 2/r32/EDX 0x10/disp8 . # copy *(EBP+16) to EDX - # out->address = EAX - 89/copy 1/mod/*+disp8 2/rm32/EDX . . . 0/r32/EAX 4/disp8 . # copy EAX to *(EDX+4) - # if (EAX == 0) out->alloc_id = 0, return - 3d/compare-EAX-and 0/imm32 - 75/jump-if-not-equal $new:continue/disp8 - c7 0/subop/copy 0/mod/indirect 2/rm32/EDX . . . . . 0/imm32 # copy to *EDX - eb/jump $new:end/disp8 -$new:continue: - # otherwise: - # ECX = *Next-alloc-id - 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 1/r32/ECX Next-alloc-id/disp32 # copy *Next-alloc-id to ECX - # *EAX = *Next-alloc-id/ECX - 89/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy ECX to *EAX - # out->alloc_id = *Next-alloc-id - 89/copy 0/mod/indirect 2/rm32/EDX . . . 1/r32/ECX . . # copy ECX to *EDX - # increment *Next-alloc-id - ff 0/subop/increment 0/mod/indirect 5/rm32/.disp32 . . . Next-alloc-id/disp32 # increment *Next-alloc-id -$new:end: - # . restore registers - 5a/pop-to-EDX - 59/pop-to-ECX - 58/pop-to-EAX - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-new: - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # var heap/EDX : (address allocation-descriptor) = {0, 0} - 68/push 0/imm32/limit - 68/push 0/imm32/curr - 89/copy 3/mod/direct 2/rm32/EDX . . . 4/r32/ESP . . # copy ESP to EDX - # heap = new-segment(512) - # . . push args - 52/push-EDX - 68/push 0x200/imm32 - # . . call - e8/call new-segment/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # *Next-alloc-id = 0x34 - c7 0/subop/copy 0/mod/indirect 5/rm32/.disp32 . . . Next-alloc-id/disp32 0x34/imm32 # copy to *Next-alloc-id - # var handle/ECX = {0, 0} - 68/push 0/imm32/address - 68/push 0/imm32/alloc-id - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # new(heap, 2, handle/ECX) - # . . push args - 51/push-ECX - 68/push 2/imm32/size - 52/push-EDX - # . . call - e8/call new/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # check-ints-equal(handle->alloc_id, 0x34, msg) - # . . push args - 68/push "F - test-new: alloc id of handle"/imm32 - 68/push 0x34/imm32 - ff 6/subop/push 0/mod/indirect 1/rm32/ECX . . . . . . # push *ECX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # check-ints-equal(*handle->address, 0x34, msg) - # . . push args - 68/push "F - test-new: alloc id of payload"/imm32 - 68/push 0x34/imm32 - 8b/copy 1/mod/*+disp8 1/rm32/ECX . . . 2/r32/EDX 4/disp8 . # copy *(ECX+4) to EDX - ff 6/subop/push 0/mod/indirect 2/rm32/EDX . . . . . . # push *EDX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # check-ints-equal(*Next-alloc-id, 0x35) - # . . push args - 68/push "F - test-new: next alloc id"/imm32 - 68/push 0x35/imm32 - ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Next-alloc-id/disp32 # copy to *Next-alloc-id - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # clean up - # . *Next-alloc-id = 1 - c7 0/subop/copy 0/mod/indirect 5/rm32/.disp32 . . . Next-alloc-id/disp32 1/imm32 # copy to *Next-alloc-id - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -_pending-test-new-failure: - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # . *Next-alloc-id = 0x34 - c7 0/subop/copy 0/mod/indirect 5/rm32/.disp32 . . . Next-alloc-id/disp32 0x34/imm32 # copy to *Next-alloc-id - # define an allocation-descriptor with no space left - # . var ad/EAX : (address allocation-descriptor) = {0x10, 0x10} - 68/push 0x10/imm32/limit - 68/push 0x10/imm32/curr - 89/copy 3/mod/direct 0/rm32/EAX . . . 4/r32/ESP . . # copy ESP to EAX - # . var handle/ECX = {random, random} - 68/push 1234/imm32/address - 68/push 5678/imm32/alloc-id - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # try to allocate - # . new(ad, 2, handle/ECX) - # . . push args - 51/push-ECX - 68/push 2/imm32/size - 50/push-EAX - # . . call - e8/call new/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # handle should be cleared - # . check-ints-equal(handle->alloc_id, 0, msg) - # . . push args - 68/push "F - test-new-failure: alloc id of handle"/imm32 - 68/push 0/imm32 - ff 6/subop/push 0/mod/indirect 1/rm32/ECX . . . . . . # push *ECX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . check-ints-equal(handle->address, 0, msg) - # . . push args - 68/push "F - test-new-failure: address of handle"/imm32 - 68/push 0/imm32 - ff 6/subop/push 1/mod/*+disp8 1/rm32/ECX . . . . 4/disp8 . # push *(ECX+4) - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # Next-alloc-id should be unmodified - # . check-ints-equal(*Next-alloc-id, 0x34) - # . . push args - 68/push "F - test-new-failure: next alloc id"/imm32 - 68/push 0x34/imm32 - ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Next-alloc-id/disp32 # copy to *Next-alloc-id - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # clean up - # . *Next-alloc-id = 1 - c7 0/subop/copy 0/mod/indirect 5/rm32/.disp32 . . . Next-alloc-id/disp32 1/imm32 # copy to *Next-alloc-id - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -lookup: # h : (handle T) -> EAX : (address T) - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # - as a proof of concept for future inlining, uses no general-purpose registers besides the output (EAX) - # EAX = handle - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 0/r32/EAX 8/disp8 . # copy *(EBP+8) to EAX - # - inline { - # push handle->alloc_id - ff 6/subop/push 0/mod/indirect 0/rm32/EAX . . . . . . # push *EAX - # EAX = handle->address (payload) - 8b/copy 1/mod/*+disp8 0/rm32/EAX . . . . 4/disp8 . # copy *(EAX+4) to EAX - # push handle->address - 50/push-EAX - # EAX = payload->alloc_id - 8b/copy 0/mod/indirect 0/rm32/EAX . . . . . . # copy *EAX to EAX - # if (EAX != handle->alloc_id) abort - 39/compare 1/mod/*+disp8 4/rm32/sib 4/base/ESP 4/index/none . 0/r32/EAX 4/disp8 . # compare *(ESP+4) and EAX - 75/jump-if-not-equal $lookup:abort/disp8 - # EAX = pop handle->address - 58/pop-to-EAX - # discard handle->alloc_id - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # add 4 - 05/add-to-EAX 4/imm32 - # - } - # - alternative consuming a second register { -#? # ECX = handle->alloc_id -#? 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX -#? # EAX = handle->address (payload) -#? 8b/copy 1/mod/*+disp8 0/rm32/EAX . . . 0/r32/EAX 4/disp8 . # copy *(EAX+4) to EAX -#? # if (ECX != *EAX) abort -#? 39/compare 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # compare *EAX and ECX -#? 75/jump-if-not-equal $lookup:abort/disp8 -#? # add 4 to EAX -#? 05/add-to-EAX 4/imm32 - # - } - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -$lookup:abort: - # . _write(2/stderr, msg) - # . . push args - 68/push "lookup failed\n"/imm32 - 68/push 2/imm32/stderr - # . . call - e8/call _write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . syscall(exit, 1) - bb/copy-to-EBX 1/imm32/exit-status - b8/copy-to-EAX 1/imm32/exit - cd/syscall 0x80/imm8 - -test-lookup-success: - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # . save registers - # var heap/EBX : (address allocation-descriptor) = {0, 0} - 68/push 0/imm32/limit - 68/push 0/imm32/curr - 89/copy 3/mod/direct 3/rm32/EBX . . . 4/r32/ESP . . # copy ESP to EBX - # heap = new-segment(512) - # . . push args - 53/push-EBX - 68/push 0x200/imm32 - # . . call - e8/call new-segment/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # var handle/ECX = {0, 0} - 68/push 0/imm32/address - 68/push 0/imm32/alloc-id - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # var old_top/EDX = heap->curr - 8b/copy 0/mod/indirect 3/rm32/EBX . . . 2/r32/EDX . . # copy *EBX to EDX - # new(heap, 2, handle) - # . . push args - 51/push-ECX - 68/push 2/imm32/size - 53/push-EBX - # . . call - e8/call new/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # EAX = lookup(handle) - # . . push args - 51/push-ECX - # . . call - e8/call lookup/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # EAX contains old top of heap, except skipping the alloc id in the payload - # . check-ints-equal(EAX, old_top+4, msg) - # . . push args - 68/push "F - test-lookup-success"/imm32 - 81 0/subop/add 3/mod/direct 2/rm32/EDX . . . . . 4/imm32 # add to EDX - 52/push-EDX - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # clean up - # . *Next-alloc-id = 1 - c7 0/subop/copy 0/mod/indirect 5/rm32/.disp32 . . . Next-alloc-id/disp32 1/imm32 # copy to *Next-alloc-id - # write(2/stderr, "lookup succeeded\n") - # . . push args - 68/push "lookup succeeded\n"/imm32 - 68/push 2/imm32/stderr - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . restore registers - 5a/pop-to-EDX - 59/pop-to-ECX - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-lookup-failure: - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # var heap/ESI : (address allocation-descriptor) = {0, 0} - 68/push 0/imm32/limit - 68/push 0/imm32/curr - 89/copy 3/mod/direct 6/rm32/ESI . . . 4/r32/ESP . . # copy ESP to ESI - # heap = new-segment(512) - # . . push args - 56/push-ESI - 68/push 0x200/imm32 - # . . call - e8/call new-segment/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # var h1/ECX = {0, 0} - 68/push 0/imm32/address - 68/push 0/imm32/alloc-id - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # var old_top/EBX = heap->curr - 8b/copy 0/mod/indirect 6/rm32/ESI . . . 3/r32/EBX . . # copy *ESI to EBX - # first allocation, to h1 - # . new(heap, 2, h1) - # . . push args - 51/push-ECX - 68/push 2/imm32/size - 56/push-ESI - # . . call - e8/call new/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # reset heap->curr to mimic reclamation - 89/copy 0/mod/indirect 6/rm32/ESI . . . 3/r32/EBX . . # copy EBX to *ESI - # second allocation that returns the same address as the first - # var h2/EDX = {0, 0} - 68/push 0/imm32/address - 68/push 0/imm32/alloc-id - 89/copy 3/mod/direct 2/rm32/EDX . . . 4/r32/ESP . . # copy ESP to EDX - # . new(heap, 2, h2) - # . . push args - 52/push-EDX - 68/push 2/imm32/size - 56/push-ESI - # . . call - e8/call new/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # check-ints-equal(h1->address, h2->address, msg) - # . . push args - 68/push "F - test-lookup-failure"/imm32 - ff 6/subop/push 1/mod/*+disp8 2/rm32/ECX . . . . 4/disp8 . # push *(EDX+4) - ff 6/subop/push 1/mod/*+disp8 1/rm32/ECX . . . . 4/disp8 . # push *(ECX+4) - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # lookup(h1) should crash - # . . push args - 51/push-ECX - # . . call - e8/call lookup/disp32 - # should never get past this point - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # clean up - # . *Next-alloc-id = 1 - c7 0/subop/copy 0/mod/indirect 5/rm32/.disp32 . . . Next-alloc-id/disp32 1/imm32 # copy to *Next-alloc-id - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -== data - -# Monotonically increasing counter for calls to 'new' -Next-alloc-id: - 1/imm32 - -# . . vim:nowrap:textwidth=0 diff --git a/subx/apps/hex b/subx/apps/hex deleted file mode 100755 index 84575cfd..00000000 --- a/subx/apps/hex +++ /dev/null Binary files differdiff --git a/subx/apps/hex.subx b/subx/apps/hex.subx deleted file mode 100644 index a24c5a2e..00000000 --- a/subx/apps/hex.subx +++ /dev/null @@ -1,1515 +0,0 @@ -# Read a text file containing whitespace-separated pairs of ascii hex bytes -# from stdin, and convert them into binary bytes (octets) on stdout. Ignore -# comments between '#' and newline. -# -# To run (from the subx/ directory): -# $ ./subx translate *.subx apps/hex.subx -o apps/hex -# $ echo '80 81 82 # comment' |./subx run apps/hex |xxd - -# Expected output: -# 00000000: 8081 82 -# -# Only hex bytes and comments are permitted. Outside of comments all words -# must be exactly 2 characters long and contain only characters [0-9a-f]. No -# uppercase hex. - -== code -# instruction effective address register displacement immediate -# . op subop mod rm32 base index scale r32 -# . 1-3 bytes 3 bits 2 bits 3 bits 3 bits 3 bits 2 bits 2 bits 0/1/2/4 bytes 0/1/2/4 bytes - -Entry: # run tests if necessary, convert stdin if not - # initialize heap - # . Heap = new-segment(64KB) - # . . push args - 68/push Heap/imm32 - 68/push 0x10000/imm32/64KB - # . . call - e8/call new-segment/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - - # . prolog - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # - if argc > 1 and argv[1] == "test", then return run_tests() - # . argc > 1 - 81 7/subop/compare 1/mod/*+disp8 5/rm32/EBP . . . . 0/disp8 1/imm32 # compare *EBP - 7e/jump-if-lesser-or-equal $run-main/disp8 - # . argv[1] == "test" - # . . push args - 68/push "test"/imm32 - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - # . . call - e8/call kernel-string-equal?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . check result - 3d/compare-EAX-and 1/imm32 - 75/jump-if-not-equal $run-main/disp8 - # . run-tests() - e8/call run-tests/disp32 - 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 3/r32/EBX Num-test-failures/disp32 # copy *Num-test-failures to EBX - eb/jump $main:end/disp8 -$run-main: - # - otherwise convert stdin - # var ed/EAX : exit-descriptor - 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # subtract from ESP - 89/copy 3/mod/direct 0/rm32/EAX . . . 4/r32/ESP . . # copy ESP to EAX - # configure ed to really exit() - # . ed->target = 0 - c7 0/subop/copy 0/mod/direct 0/rm32/EAX . . . . . 0/imm32 # copy to *EAX - # return convert(Stdin, 1/stdout, 2/stderr, ed) - # . . push args - 50/push-EAX/ed - 68/push Stderr/imm32 - 68/push Stdout/imm32 - 68/push Stdin/imm32 - # . . call - e8/call convert/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP - # . syscall(exit, 0) - bb/copy-to-EBX 0/imm32 -$main:end: - b8/copy-to-EAX 1/imm32/exit - cd/syscall 0x80/imm8 - -# the main entry point -convert: # in : (address buffered-file), out : (address buffered-file), err : (address buffered-file), ed : (address exit-descriptor) -> <void> - # pseudocode: - # while true - # EAX = convert-next-octet(in, err, ed) - # if (EAX == Eof) break - # write-byte-buffered(out, AL) - # flush(out) - # - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # . save registers - 50/push-EAX -$convert:loop: - # EAX = convert-next-octet(in, err, ed) - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x14/disp8 . # push *(EBP+20) - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x10/disp8 . # push *(EBP+16) - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - # . . call - e8/call convert-next-octet/disp32 - # . . discard first 2 args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # if (EAX == Eof) break - 3d/compare-EAX-and 0xffffffff/imm32/Eof - 74/jump-if-equal $convert:loop-end/disp8 - # write-byte-buffered(out, AL) - # . . push args - 50/push-EAX - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - # . . call - e8/call write-byte-buffered/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # loop - eb/jump $convert:loop/disp8 -$convert:loop-end: - # flush(out) - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - # . . call - e8/call flush/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -$convert:end: - # . restore registers - 58/pop-to-EAX - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -# read bytes from 'in' until a sequence of two lowercase hex (0-9, a-f) bytes -# skip spaces and newlines -# on '#' skip bytes until newline -# raise an error and abort on all other unexpected bytes -# return in EAX an _octet_ containing the binary value of the two hex characters -# return Eof on reaching end of file -convert-next-octet: # in : (address buffered-file), err : (address buffered-file), ed : (address exit-descriptor) -> byte-or-Eof/EAX - # pseudocode: - # EAX = scan-next-byte(in, err, ed) - # if (EAX == Eof) return - # ECX = from-hex-char(EAX) - # EAX = scan-next-byte(in, err, ed) - # if (EAX == Eof) error("partial byte found.") - # EAX = from-hex-char(EAX) - # EAX = (ECX << 4) | EAX - # return - # - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # . save registers - 51/push-ECX - # EAX = scan-next-byte(in, err, ed) - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x10/disp8 . # push *(EBP+16) - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - # . . call - e8/call scan-next-byte/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # if (EAX == Eof) return - 3d/compare-EAX-and 0xffffffff/imm32/Eof - 74/jump-if-equal $convert-next-octet:end/disp8 - # EAX = from-hex-char(EAX) - e8/call from-hex-char/disp32 - # ECX = EAX - 89/copy 3/mod/direct 1/rm32/ECX . . . 0/r32/EAX . . # copy EAX to ECX - # EAX = scan-next-byte(in, err, ed) - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x10/disp8 . # push *(EBP+16) - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - # . . call - e8/call scan-next-byte/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # if (EAX == Eof) error(ed, err, "partial byte found.") - 3d/compare-EAX-and 0xffffffff/imm32/Eof - 75/jump-if-not-equal $convert-next-octet:convert/disp8 - # . error-byte(ed, err, msg, '.') # reusing error-byte to avoid creating _yet_ another helper - # . . push args - 68/push 0x2e/imm32/period/dummy - 68/push "convert-next-octet: partial byte found"/imm32 - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x10/disp8 . # push *(EBP+16) - # . . call - e8/call error-byte/disp32 # never returns -$convert-next-octet:convert: - # EAX = from-hex-char(EAX) - e8/call from-hex-char/disp32 - # EAX = (ECX << 4) | EAX - # . ECX <<= 4 - c1/shift 4/subop/left 3/mod/direct 1/rm32/ECX . . . . . 4/imm8 # shift ECX left by 4 bits - # . EAX |= ECX - 09/or 3/mod/direct 0/rm32/EAX . . . 1/r32/ECX . . # EAX = bitwise OR with ECX -$convert-next-octet:end: - # . restore registers - 59/pop-to-ECX - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-convert-next-octet: - # - check that the first two bytes of the input are assembled into the resulting octet - # This test uses exit-descriptors. Use EBP for setting up local variables. - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # clear all streams - # . clear-stream(_test-stream) - # . . push args - 68/push _test-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-buffered-file+4) - # . . push args - b8/copy-to-EAX _test-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-error-stream) - # . . push args - 68/push _test-error-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-error-buffered-file+4) - # . . push args - b8/copy-to-EAX _test-error-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # initialize '_test-stream' to "abc" - # . write(_test-stream, "abc") - # . . push args - 68/push "abc"/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # initialize exit-descriptor 'ed' for the call to 'convert-next-octet' below - # . var ed/ECX : exit-descriptor - 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # subtract from ESP - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # . tailor-exit-descriptor(ed, 12) - # . . push args - 68/push 0xc/imm32/nbytes-of-args-for-convert-next-octet - 51/push-ECX/ed - # . . call - e8/call tailor-exit-descriptor/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # EAX = convert-next-octet(_test-buffered-file, _test-error-buffered-file, ed) - # . . push args - 51/push-ECX/ed - 68/push _test-error-buffered-file/imm32 - 68/push _test-buffered-file/imm32 - # . . call - e8/call convert-next-octet/disp32 - # registers except ESP may be clobbered at this point - # pop args to convert-next-octet - # . . discard first 2 args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . . restore ed - 59/pop-to-ECX - # check that convert-next-octet didn't abort - # . check-ints-equal(ed->value, 0, msg) - # . . push args - 68/push "F - test-convert-next-octet: unexpected abort"/imm32 - 68/push 0/imm32 - # . . push ed->value - ff 6/subop/push 1/mod/*+disp8 1/rm32/ECX . . . . 4/disp8 . # push *(ECX+4) - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # return if abort - 81 7/subop/compare 1/mod/*+disp8 1/rm32/ECX . . . . 4/disp8 0/imm32 # compare *(ECX+4) - 75/jump-if-not-equal $test-convert-next-octet:end/disp8 - # check-ints-equal(EAX, 0xab, msg) - # . . push args - 68/push "F - test-convert-next-octet"/imm32 - 68/push 0xab/imm32/ab - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -$test-convert-next-octet:end: - # . epilog - # don't restore ESP from EBP; manually reclaim locals - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 5d/pop-to-EBP - c3/return - -test-convert-next-octet-handles-Eof: - # - check that reaching end of file returns Eof - # This test uses exit-descriptors. Use EBP for setting up local variables. - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # clear all streams - # . clear-stream(_test-stream) - # . . push args - 68/push _test-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-buffered-file+4) - # . . push args - b8/copy-to-EAX _test-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-error-stream) - # . . push args - 68/push _test-error-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-error-buffered-file+4) - # . . push args - b8/copy-to-EAX _test-error-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # don't initialize '_test-stream' - # initialize exit-descriptor 'ed' for the call to 'convert-next-octet' below - # . var ed/ECX : exit-descriptor - 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # subtract from ESP - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # . tailor-exit-descriptor(ed, 12) - # . . push args - 68/push 0xc/imm32/nbytes-of-args-for-convert-next-octet - 51/push-ECX/ed - # . . call - e8/call tailor-exit-descriptor/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # EAX = convert-next-octet(_test-buffered-file, _test-error-buffered-file, ed) - # . . push args - 51/push-ECX/ed - 68/push _test-error-buffered-file/imm32 - 68/push _test-buffered-file/imm32 - # . . call - e8/call convert-next-octet/disp32 - # registers except ESP may be clobbered at this point - # pop args to convert-next-octet - # . . discard first 2 args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . . restore ed - 59/pop-to-ECX - # check that convert-next-octet didn't abort - # . check-ints-equal(ed->value, 0, msg) - # . . push args - 68/push "F - test-convert-next-octet: unexpected abort"/imm32 - 68/push 0/imm32 - # . . push ed->value - ff 6/subop/push 1/mod/*+disp8 1/rm32/ECX . . . . 4/disp8 . # push *(ECX+4) - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # return if abort - 81 7/subop/compare 1/mod/*+disp8 1/rm32/ECX . . . . 4/disp8 0/imm32 # compare *(ECX+4) - 75/jump-if-not-equal $test-convert-next-octet-handles-Eof:end/disp8 - # check-ints-equal(EAX, Eof, msg) - # . . push args - 68/push "F - test-convert-next-octet-handles-Eof"/imm32 - 68/push 0xffffffff/imm32/Eof - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -$test-convert-next-octet-handles-Eof:end: - # . epilog - # don't restore ESP from EBP; manually reclaim locals - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 5d/pop-to-EBP - c3/return - -test-convert-next-octet-aborts-on-single-hex-byte: - # - check that a single unaccompanied hex byte aborts - # This test uses exit-descriptors. Use EBP for setting up local variables. - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # clear all streams - # . clear-stream(_test-stream) - # . . push args - 68/push _test-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-buffered-file+4) - # . . push args - b8/copy-to-EAX _test-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-error-stream) - # . . push args - 68/push _test-error-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-error-buffered-file+4) - # . . push args - b8/copy-to-EAX _test-error-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # initialize '_test-stream' to "a" - # . write(_test-stream, "a") - # . . push args - 68/push "a"/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # initialize exit-descriptor 'ed' for the call to 'convert-next-octet' below - # . var ed/ECX : exit-descriptor - 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # subtract from ESP - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # . tailor-exit-descriptor(ed, 12) - # . . push args - 68/push 0xc/imm32/nbytes-of-args-for-convert-next-octet - 51/push-ECX/ed - # . . call - e8/call tailor-exit-descriptor/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # EAX = convert-next-octet(_test-buffered-file, _test-error-buffered-file, ed) - # . . push args - 51/push-ECX/ed - 68/push _test-error-buffered-file/imm32 - 68/push _test-buffered-file/imm32 - # . . call - e8/call convert-next-octet/disp32 - # registers except ESP may be clobbered at this point - # pop args to convert-next-octet - # . . discard first 2 args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . . restore ed - 59/pop-to-ECX - # check that convert-next-octet aborted - # . check-ints-equal(ed->value, 2, msg) - # . . push args - 68/push "F - test-convert-next-octet-aborts-on-single-hex-byte: unexpected abort"/imm32 - 68/push 2/imm32 - # . . push ed->value - ff 6/subop/push 1/mod/*+disp8 1/rm32/ECX . . . . 4/disp8 . # push *(ECX+4) - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -$test-convert-next-octet-aborts-on-single-hex-byte:end: - # . epilog - # don't restore ESP from EBP; manually reclaim locals - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 5d/pop-to-EBP - c3/return - -# read whitespace until a hex byte, and return it -# return Eof if file ends without finding a hex byte -# on '#' skip all bytes until newline -# abort on any other byte -scan-next-byte: # in : (address buffered-file), err : (address buffered-file), ed : (address exit-descriptor) -> byte-or-Eof/EAX - # pseudocode: - # while true - # EAX = read-byte-buffered(in) - # if (EAX == Eof) return EAX - # if (is-hex-digit?(EAX)) return EAX - # if (EAX == ' ' or '\t' or '\n') continue - # if (EAX == '#') skip-until-newline(in) - # else error-byte(ed, err, "invalid byte: " EAX) - # - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # . save registers -$scan-next-byte:loop: - # EAX = read-byte-buffered(in) - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - # . . call - e8/call read-byte-buffered/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # if (EAX == Eof) return EAX - 3d/compare-with-EAX 0xffffffff/imm32/Eof - 74/jump-if-equal $scan-next-byte:end/disp8 - # if (is-hex-digit?(EAX)) return EAX - # . save EAX for now - 50/push-EAX - # . is-hex-digit?(EAX) - # . . push args - 50/push-EAX - # . . call - e8/call is-hex-digit?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . compare with 'false' - 3d/compare-with-EAX 0/imm32 - # . restore EAX (does not affect flags) - 58/pop-to-EAX - # . check whether to return - 75/jump-if-not-equal $scan-next-byte:end/disp8 -$scan-next-byte:check1: - # if (EAX == ' ') continue - 3d/compare-EAX-and 0x20/imm32/space - 74/jump-if-equal $scan-next-byte:loop/disp8 - # if (EAX == '\t') continue - 3d/compare-EAX-and 9/imm32/tab - 74/jump-if-equal $scan-next-byte:loop/disp8 - # if (EAX == '\n') continue - 3d/compare-EAX-and 0xa/imm32/newline - 74/jump-if-equal $scan-next-byte:loop/disp8 -$scan-next-byte:check2: - # if (EAX == '#') skip-until-newline(in) - 3d/compare-with-EAX 0x23/imm32 - 75/jump-if-not-equal $scan-next-byte:check3/disp8 - # . skip-until-newline(in) - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - # . . call - e8/call skip-until-newline/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - eb/jump $scan-next-byte:loop/disp8 -$scan-next-byte:check3: - # otherwise error-byte(ed, err, msg, EAX) - # . . push args - 50/push-EAX - 68/push "scan-next-byte: invalid byte"/imm32 - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x10/disp8 . # push *(EBP+16) - # . . call - e8/call error-byte/disp32 # never returns -$scan-next-byte:end: - # . restore registers - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-scan-next-byte: - # - check that the first byte of the input is returned - # This test uses exit-descriptors. Use EBP for setting up local variables. - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # clear all streams - # . clear-stream(_test-stream) - # . . push args - 68/push _test-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-buffered-file+4) - # . . push args - b8/copy-to-EAX _test-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-error-stream) - # . . push args - 68/push _test-error-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-error-buffered-file+4) - # . . push args - b8/copy-to-EAX _test-error-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # initialize '_test-stream' to "abc" - # . write(_test-stream, "abc") - # . . push args - 68/push "abc"/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # initialize exit-descriptor 'ed' for the call to 'scan-next-byte' below - # . var ed/ECX : exit-descriptor - 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # subtract from ESP - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # . tailor-exit-descriptor(ed, 12) - # . . push args - 68/push 0xc/imm32/nbytes-of-args-for-scan-next-byte - 51/push-ECX/ed - # . . call - e8/call tailor-exit-descriptor/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # EAX = scan-next-byte(_test-buffered-file, _test-error-buffered-file, ed) - # . . push args - 51/push-ECX/ed - 68/push _test-error-buffered-file/imm32 - 68/push _test-buffered-file/imm32 - # . . call - e8/call scan-next-byte/disp32 - # registers except ESP may be clobbered at this point - # pop args to scan-next-byte - # . . discard first 2 args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . . restore ed - 59/pop-to-ECX - # check that scan-next-byte didn't abort - # . check-ints-equal(ed->value, 0, msg) - # . . push args - 68/push "F - test-scan-next-byte: unexpected abort"/imm32 - 68/push 0/imm32 - # . . push ed->value - ff 6/subop/push 1/mod/*+disp8 1/rm32/ECX . . . . 4/disp8 . # push *(ECX+4) - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # return if abort - 81 7/subop/compare 1/mod/*+disp8 1/rm32/ECX . . . . 4/disp8 0/imm32 # compare *(ECX+4) - 75/jump-if-not-equal $test-scan-next-byte:end/disp8 - # check-ints-equal(EAX, 0x61/a, msg) - # . . push args - 68/push "F - test-scan-next-byte"/imm32 - 68/push 0x61/imm32/a - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -$test-scan-next-byte:end: - # . epilog - # don't restore ESP from EBP; manually reclaim locals - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 5d/pop-to-EBP - c3/return - -test-scan-next-byte-skips-whitespace: - # - check that the first byte after whitespace is returned - # This test uses exit-descriptors. Use EBP for setting up local variables. - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # clear all streams - # . clear-stream(_test-stream) - # . . push args - 68/push _test-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-buffered-file+4) - # . . push args - b8/copy-to-EAX _test-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-error-stream) - # . . push args - 68/push _test-error-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-error-buffered-file+4) - # . . push args - b8/copy-to-EAX _test-error-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # initialize '_test-stream' to input with leading whitespace - # . write(_test-stream, text) - # . . push args - 68/push " abc"/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # initialize exit-descriptor 'ed' for the call to 'scan-next-byte' below - # . var ed/ECX : exit-descriptor - 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # subtract from ESP - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # . tailor-exit-descriptor(ed, 12) - # . . push args - 68/push 0xc/imm32/nbytes-of-args-for-scan-next-byte - 51/push-ECX/ed - # . . call - e8/call tailor-exit-descriptor/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # EAX = scan-next-byte(_test-buffered-file, _test-error-buffered-file, ed) - # . . push args - 51/push-ECX/ed - 68/push _test-error-buffered-file/imm32 - 68/push _test-buffered-file/imm32 - # . . call - e8/call scan-next-byte/disp32 - # registers except ESP may be clobbered at this point - # pop args to scan-next-byte - # . . discard first 2 args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . . restore ed - 59/pop-to-ECX - # check that scan-next-byte didn't abort - # . check-ints-equal(ed->value, 0, msg) - # . . push args - 68/push "F - test-scan-next-byte-skips-whitespace: unexpected abort"/imm32 - 68/push 0/imm32 - # . . push ed->value - ff 6/subop/push 1/mod/*+disp8 1/rm32/ECX . . . . 4/disp8 . # push *(ECX+4) - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # return if abort - 81 7/subop/compare 1/mod/*+disp8 1/rm32/ECX . . . . 4/disp8 0/imm32 # compare *(ECX+4) - 75/jump-if-not-equal $test-scan-next-byte-skips-whitespace:end/disp8 - # check-ints-equal(EAX, 0x61/a, msg) - # . . push args - 68/push "F - test-scan-next-byte-skips-whitespace"/imm32 - 68/push 0x61/imm32/a - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -$test-scan-next-byte-skips-whitespace:end: - # . epilog - # don't restore ESP from EBP; manually reclaim locals - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 5d/pop-to-EBP - c3/return - -test-scan-next-byte-skips-comment: - # - check that the first byte after a comment (and newline) is returned - # This test uses exit-descriptors. Use EBP for setting up local variables. - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # clear all streams - # . clear-stream(_test-stream) - # . . push args - 68/push _test-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-buffered-file+4) - # . . push args - b8/copy-to-EAX _test-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-error-stream) - # . . push args - 68/push _test-error-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-error-buffered-file+4) - # . . push args - b8/copy-to-EAX _test-error-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # initialize '_test-stream' to input with leading comment - # . write(_test-stream, comment) - # . . push args - 68/push "#x\n"/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . write(_test-stream, real text) - # . . push args - 68/push "ab"/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # initialize exit-descriptor 'ed' for the call to 'scan-next-byte' below - # . var ed/ECX : exit-descriptor - 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # subtract from ESP - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # . tailor-exit-descriptor(ed, 12) - # . . push args - 68/push 0xc/imm32/nbytes-of-args-for-scan-next-byte - 51/push-ECX/ed - # . . call - e8/call tailor-exit-descriptor/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # EAX = scan-next-byte(_test-buffered-file, _test-error-buffered-file, ed) - # . . push args - 51/push-ECX/ed - 68/push _test-error-buffered-file/imm32 - 68/push _test-buffered-file/imm32 - # . . call - e8/call scan-next-byte/disp32 - # registers except ESP may be clobbered at this point - # pop args to scan-next-byte - # . . discard first 2 args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . . restore ed - 59/pop-to-ECX - # check that scan-next-byte didn't abort - # . check-ints-equal(ed->value, 0, msg) - # . . push args - 68/push "F - test-scan-next-byte-skips-comment: unexpected abort"/imm32 - 68/push 0/imm32 - # . . push ed->value - ff 6/subop/push 1/mod/*+disp8 1/rm32/ECX . . . . 4/disp8 . # push *(ECX+4) - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # return if abort - 81 7/subop/compare 1/mod/*+disp8 1/rm32/ECX . . . . 4/disp8 0/imm32 # compare *(ECX+4) - 75/jump-if-not-equal $test-scan-next-byte-skips-comment:end/disp8 - # check-ints-equal(EAX, 0x61/a, msg) - # . . push args - 68/push "F - test-scan-next-byte-skips-comment"/imm32 - 68/push 0x61/imm32/a - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -$test-scan-next-byte-skips-comment:end: - # . epilog - # don't restore ESP from EBP; manually reclaim locals - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 5d/pop-to-EBP - c3/return - -test-scan-next-byte-skips-comment-and-whitespace: - # - check that the first byte after a comment and any further whitespace is returned - # This test uses exit-descriptors. Use EBP for setting up local variables. - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # clear all streams - # . clear-stream(_test-stream) - # . . push args - 68/push _test-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-buffered-file+4) - # . . push args - b8/copy-to-EAX _test-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-error-stream) - # . . push args - 68/push _test-error-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-error-buffered-file+4) - # . . push args - b8/copy-to-EAX _test-error-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # initialize '_test-stream' to input with leading comment and more whitespace after newline - # . write(_test-stream, comment) - # . . push args - 68/push "#x\n"/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . write(_test-stream, real text) - # . . push args - 68/push " ab"/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # initialize exit-descriptor 'ed' for the call to 'scan-next-byte' below - # . var ed/ECX : exit-descriptor - 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # subtract from ESP - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # . tailor-exit-descriptor(ed, 12) - # . . push args - 68/push 0xc/imm32/nbytes-of-args-for-scan-next-byte - 51/push-ECX/ed - # . . call - e8/call tailor-exit-descriptor/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # EAX = scan-next-byte(_test-buffered-file, _test-error-buffered-file, ed) - # . . push args - 51/push-ECX/ed - 68/push _test-error-buffered-file/imm32 - 68/push _test-buffered-file/imm32 - # . . call - e8/call scan-next-byte/disp32 - # registers except ESP may be clobbered at this point - # pop args to scan-next-byte - # . . discard first 2 args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . . restore ed - 59/pop-to-ECX - # check that scan-next-byte didn't abort - # . check-ints-equal(ed->value, 0, msg) - # . . push args - 68/push "F - test-scan-next-byte-skips-comment-and-whitespace: unexpected abort"/imm32 - 68/push 0/imm32 - # . . push ed->value - ff 6/subop/push 1/mod/*+disp8 1/rm32/ECX . . . . 4/disp8 . # push *(ECX+4) - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # return if abort - 81 7/subop/compare 1/mod/*+disp8 1/rm32/ECX . . . . 4/disp8 0/imm32 # compare *(ECX+4) - 75/jump-if-not-equal $test-scan-next-byte-skips-comment-and-whitespace:end/disp8 - # check-ints-equal(EAX, 0x61/a, msg) - # . . push args - 68/push "F - test-scan-next-byte-skips-comment-and-whitespace"/imm32 - 68/push 0x61/imm32/a - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -$test-scan-next-byte-skips-comment-and-whitespace:end: - # . epilog - # don't restore ESP from EBP; manually reclaim locals - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 5d/pop-to-EBP - c3/return - -test-scan-next-byte-skips-whitespace-and-comment: - # - check that the first byte after any whitespace and comments is returned - # This test uses exit-descriptors. Use EBP for setting up local variables. - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # clear all streams - # . clear-stream(_test-stream) - # . . push args - 68/push _test-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-buffered-file+4) - # . . push args - b8/copy-to-EAX _test-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-error-stream) - # . . push args - 68/push _test-error-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-error-buffered-file+4) - # . . push args - b8/copy-to-EAX _test-error-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # initialize '_test-stream' to input with leading whitespace and comment - # . write(_test-stream, comment) - # . . push args - 68/push " #x\n"/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . write(_test-stream, real text) - # . . push args - 68/push "ab"/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # initialize exit-descriptor 'ed' for the call to 'scan-next-byte' below - # . var ed/ECX : exit-descriptor - 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # subtract from ESP - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # . tailor-exit-descriptor(ed, 12) - # . . push args - 68/push 0xc/imm32/nbytes-of-args-for-scan-next-byte - 51/push-ECX/ed - # . . call - e8/call tailor-exit-descriptor/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # EAX = scan-next-byte(_test-buffered-file, _test-error-buffered-file, ed) - # . . push args - 51/push-ECX/ed - 68/push _test-error-buffered-file/imm32 - 68/push _test-buffered-file/imm32 - # . . call - e8/call scan-next-byte/disp32 - # registers except ESP may be clobbered at this point - # pop args to scan-next-byte - # . . discard first 2 args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . . restore ed - 59/pop-to-ECX - # check that scan-next-byte didn't abort - # . check-ints-equal(ed->value, 0, msg) - # . . push args - 68/push "F - test-scan-next-byte-skips-whitespace-and-comment: unexpected abort"/imm32 - 68/push 0/imm32 - # . . push ed->value - ff 6/subop/push 1/mod/*+disp8 1/rm32/ECX . . . . 4/disp8 . # push *(ECX+4) - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # return if abort - 81 7/subop/compare 1/mod/*+disp8 1/rm32/ECX . . . . 4/disp8 0/imm32 # compare *(ECX+4) - 75/jump-if-not-equal $test-scan-next-byte-skips-whitespace-and-comment:end/disp8 - # check-ints-equal(EAX, 0x61/a, msg) - # . . push args - 68/push "F - test-scan-next-byte-skips-whitespace-and-comment"/imm32 - 68/push 0x61/imm32/a - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -$test-scan-next-byte-skips-whitespace-and-comment:end: - # . epilog - # don't restore ESP from EBP; manually reclaim locals - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 5d/pop-to-EBP - c3/return - -test-scan-next-byte-reads-final-byte: - # - check that the final byte in input is returned - # This test uses exit-descriptors. Use EBP for setting up local variables. - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # clear all streams - # . clear-stream(_test-stream) - # . . push args - 68/push _test-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-buffered-file+4) - # . . push args - b8/copy-to-EAX _test-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-error-stream) - # . . push args - 68/push _test-error-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-error-buffered-file+4) - # . . push args - b8/copy-to-EAX _test-error-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # initialize '_test-stream' to input with single character - # . write(_test-stream, character) - # . . push args - 68/push "a"/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # initialize exit-descriptor 'ed' for the call to 'scan-next-byte' below - # . var ed/ECX : exit-descriptor - 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # subtract from ESP - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # . tailor-exit-descriptor(ed, 12) - # . . push args - 68/push 0xc/imm32/nbytes-of-args-for-scan-next-byte - 51/push-ECX/ed - # . . call - e8/call tailor-exit-descriptor/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # EAX = scan-next-byte(_test-buffered-file, _test-error-buffered-file, ed) - # . . push args - 51/push-ECX/ed - 68/push _test-error-buffered-file/imm32 - 68/push _test-buffered-file/imm32 - # . . call - e8/call scan-next-byte/disp32 - # registers except ESP may be clobbered at this point - # pop args to scan-next-byte - # . . discard first 2 args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . . restore ed - 59/pop-to-ECX - # check that scan-next-byte didn't abort - # . check-ints-equal(ed->value, 0, msg) - # . . push args - 68/push "F - test-scan-next-byte-reads-final-byte: unexpected abort"/imm32 - 68/push 0/imm32 - # . . push ed->value - ff 6/subop/push 1/mod/*+disp8 1/rm32/ECX . . . . 4/disp8 . # push *(ECX+4) - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # return if abort - 81 7/subop/compare 1/mod/*+disp8 1/rm32/ECX . . . . 4/disp8 0/imm32 # compare *(ECX+4) - 75/jump-if-not-equal $test-scan-next-byte-reads-final-byte:end/disp8 - # check-ints-equal(EAX, 0x61/a, msg) - # . . push args - 68/push "F - test-scan-next-byte-reads-final-byte"/imm32 - 68/push 0x61/imm32/a - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -$test-scan-next-byte-reads-final-byte:end: - # . epilog - # don't restore ESP from EBP; manually reclaim locals - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 5d/pop-to-EBP - c3/return - -test-scan-next-byte-handles-Eof: - # - check that the right sentinel value is returned when there's no data remaining to be read - # This test uses exit-descriptors. Use EBP for setting up local variables. - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # clear all streams - # . clear-stream(_test-stream) - # . . push args - 68/push _test-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-buffered-file+4) - # . . push args - b8/copy-to-EAX _test-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-error-stream) - # . . push args - 68/push _test-error-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-error-buffered-file+4) - # . . push args - b8/copy-to-EAX _test-error-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # leave '_test-stream' empty - # initialize exit-descriptor 'ed' for the call to 'scan-next-byte' below - # . var ed/ECX : exit-descriptor - 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # subtract from ESP - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # . tailor-exit-descriptor(ed, 12) - # . . push args - 68/push 0xc/imm32/nbytes-of-args-for-scan-next-byte - 51/push-ECX/ed - # . . call - e8/call tailor-exit-descriptor/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # EAX = scan-next-byte(_test-buffered-file, _test-error-buffered-file, ed) - # . . push args - 51/push-ECX/ed - 68/push _test-error-buffered-file/imm32 - 68/push _test-buffered-file/imm32 - # . . call - e8/call scan-next-byte/disp32 - # registers except ESP may be clobbered at this point - # pop args to scan-next-byte - # . . discard first 2 args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . . restore ed - 59/pop-to-ECX - # check that scan-next-byte didn't abort - # . check-ints-equal(ed->value, 0, msg) - # . . push args - 68/push "F - test-scan-next-byte-handles-Eof: unexpected abort"/imm32 - 68/push 0/imm32 - # . . push ed->value - ff 6/subop/push 1/mod/*+disp8 1/rm32/ECX . . . . 4/disp8 . # push *(ECX+4) - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # return if abort - 81 7/subop/compare 1/mod/*+disp8 1/rm32/ECX . . . . 4/disp8 0/imm32 # compare *(ECX+4) - 75/jump-if-not-equal $test-scan-next-byte-handles-Eof:end/disp8 - # check-ints-equal(EAX, Eof, msg) - # . . push args - 68/push "F - test-scan-next-byte-handles-Eof"/imm32 - 68/push 0xffffffff/imm32/Eof - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -$test-scan-next-byte-handles-Eof:end: - # . epilog - # don't restore ESP from EBP; manually reclaim locals - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 5d/pop-to-EBP - c3/return - -test-scan-next-byte-aborts-on-invalid-byte: - # - check that the a bad byte immediately aborts - # This test uses exit-descriptors. Use EBP for setting up local variables. - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # clear all streams - # . clear-stream(_test-stream) - # . . push args - 68/push _test-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-buffered-file+4) - # . . push args - b8/copy-to-EAX _test-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-error-stream) - # . . push args - 68/push _test-error-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-error-buffered-file+4) - # . . push args - b8/copy-to-EAX _test-error-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # initialize '_test-stream' to "x" - # . write(_test-stream, "x") - # . . push args - 68/push "x"/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # initialize exit-descriptor 'ed' for the call to 'scan-next-byte' below - # . var ed/ECX : exit-descriptor - 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # subtract from ESP - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # . tailor-exit-descriptor(ed, 12) - # . . push args - 68/push 0xc/imm32/nbytes-of-args-for-scan-next-byte - 51/push-ECX/ed - # . . call - e8/call tailor-exit-descriptor/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # EAX = scan-next-byte(_test-buffered-file, _test-error-buffered-file, ed) - # . . push args - 51/push-ECX/ed - 68/push _test-error-buffered-file/imm32 - 68/push _test-buffered-file/imm32 - # . . call - e8/call scan-next-byte/disp32 - # registers except ESP may be clobbered at this point - # pop args to scan-next-byte - # . . discard first 2 args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . . restore ed - 59/pop-to-ECX - # check that scan-next-byte aborted - # . check-ints-equal(ed->value, 2, msg) - # . . push args - 68/push "F - test-scan-next-byte-aborts-on-invalid-byte"/imm32 - 68/push 2/imm32 - # . . push ed->value - ff 6/subop/push 1/mod/*+disp8 1/rm32/ECX . . . . 4/disp8 . # push *(ECX+4) - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -$test-scan-next-byte-aborts-on-invalid-byte:end: - # . epilog - # don't restore ESP from EBP; manually reclaim locals - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 5d/pop-to-EBP - c3/return - -skip-until-newline: # in : (address buffered-file) -> <void> - # pseudocode: - # push EAX - # while true - # EAX = read-byte-buffered(in) - # if (EAX == Eof) break - # if (EAX == 0x0a) break - # pop EAX - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # . save registers - 50/push-EAX -$skip-until-newline:loop: - # . EAX = read-byte-buffered(in) - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - # . . call - e8/call read-byte-buffered/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . if (EAX == Eof) break - 3d/compare-EAX-and 0xffffffff/imm32/Eof - 74/jump-if-equal $skip-until-newline:end/disp8 - # . if (EAX != 0xa/newline) loop - 3d/compare-EAX-and 0xa/imm32/newline - 75/jump-if-not-equal $skip-until-newline:loop/disp8 -$skip-until-newline:end: - # . restore registers - 58/pop-to-EAX - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-skip-until-newline: - # - check that the read pointer points after the newline - # setup - # . clear-stream(_test-stream) - # . . push args - 68/push _test-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-buffered-file+4) - # . . push args - b8/copy-to-EAX _test-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # initialize '_test-stream' to "abc\nde" - # . write(_test-stream, "abc") - # . . push args - 68/push "abc\n"/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . write(_test-stream, "de") - # . . push args - 68/push "de"/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # skip-until-newline(_test-buffered-file) - # . . push args - 68/push _test-buffered-file/imm32 - # . . call - e8/call skip-until-newline/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # check-ints-equal(_test-buffered-file->read, 4, msg) - # . . push args - 68/push "F - test-skip-until-newline"/imm32 - 68/push 4/imm32 - b8/copy-to-EAX _test-buffered-file/imm32 - ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 8/disp8 . # push *(EAX+8) - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . end - c3/return - -== data - -_test-error-stream: - # current write index - 0/imm32 - # current read index - 0/imm32 - # line - 0x80/imm32 # 128 bytes - # data (8 lines x 16 bytes/line) - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - -# a test buffered file for _test-error-stream -_test-error-buffered-file: - # file descriptor or (address stream) - _test-error-stream/imm32 - # current write index - 0/imm32 - # current read index - 0/imm32 - # length - 6/imm32 - # data - 00 00 00 00 00 00 # 6 bytes - -# . . vim:nowrap:textwidth=0 diff --git a/subx/apps/pack b/subx/apps/pack deleted file mode 100755 index 720ba3ea..00000000 --- a/subx/apps/pack +++ /dev/null Binary files differdiff --git a/subx/apps/pack.subx b/subx/apps/pack.subx deleted file mode 100644 index 61a837ba..00000000 --- a/subx/apps/pack.subx +++ /dev/null @@ -1,5986 +0,0 @@ -# Read a text file of SubX instructions from stdin, and convert it into a list -# of whitespace-separated ascii hex bytes on stdout. Label definitions and -# uses are left untouched. -# -# To run (from the subx/ directory): -# $ ./subx translate *.subx apps/pack.subx -o apps/pack -# $ echo '05/add-to-EAX 0x20/imm32' |./subx run apps/pack -# Expected output: -# 05 20 00 00 00 # 05/add-to-EAX 0x20/imm32 -# The original instruction gets included as a comment at the end of each -# converted line. -# -# There's zero error-checking. For now we assume the input program is valid. -# We'll continue to rely on the C++ version for error messages. - -== code -# instruction effective address register displacement immediate -# . op subop mod rm32 base index scale r32 -# . 1-3 bytes 3 bits 2 bits 3 bits 3 bits 3 bits 2 bits 2 bits 0/1/2/4 bytes 0/1/2/4 bytes - -Entry: # run tests if necessary, convert stdin if not - # initialize heap - # . Heap = new-segment(64KB) - # . . push args - 68/push Heap/imm32 - 68/push 0x10000/imm32/64KB - # . . call - e8/call new-segment/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - - # . prolog - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # - if argc > 1 and argv[1] == "test", then return run_tests() - # . argc > 1 - 81 7/subop/compare 1/mod/*+disp8 5/rm32/EBP . . . . 0/disp8 1/imm32 # compare *EBP - 7e/jump-if-lesser-or-equal $run-main/disp8 - # . argv[1] == "test" - # . . push args - 68/push "test"/imm32 - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - # . . call - e8/call kernel-string-equal?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . check result - 3d/compare-EAX-and 1/imm32 - 75/jump-if-not-equal $run-main/disp8 - # . run-tests() - e8/call run-tests/disp32 - 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 3/r32/EBX Num-test-failures/disp32 # copy *Num-test-failures to EBX - eb/jump $main:end/disp8 -$run-main: - # - otherwise convert stdin - # var ed/EAX : exit-descriptor - 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # subtract from ESP - 89/copy 3/mod/direct 0/rm32/EAX . . . 4/r32/ESP . . # copy ESP to EAX - # configure ed to really exit() - # . ed->target = 0 - c7 0/subop/copy 0/mod/direct 0/rm32/EAX . . . . . 0/imm32 # copy to *EAX - # return convert(Stdin, 1/stdout, 2/stderr, ed) - # . . push args - 50/push-EAX/ed - 68/push Stderr/imm32 - 68/push Stdout/imm32 - 68/push Stdin/imm32 - # . . call - e8/call convert/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP - # . syscall(exit, 0) - bb/copy-to-EBX 0/imm32 -$main:end: - b8/copy-to-EAX 1/imm32/exit - cd/syscall 0x80/imm8 - -# - big picture -# We'll operate on each line/instruction in isolation. That way we only need to -# allocate memory for converting a single instruction. -# -# To pack an entire file, convert every segment in it -# To convert a code segment, convert every instruction (line) until the next segment header -# To convert a non-data segment, convert every word until the next segment header -# -# primary state: line -# stream of 512 bytes; abort if it ever overflows - -# conceptual hierarchy within a line: -# line = words separated by ' ', maybe followed by comment starting with '#' -# word = datum until '/', then 0 or more metadata separated by '/' -# -# we won't bother saving the internal structure of lines; reparsing should be cheap using three primitives: -# next-token(stream, delim char) -> slice (start, end pointers) -# next-token-from-slice(start, end, delim char) -> slice -# slice-equal?(slice, string) - -convert: # in : (address buffered-file), out : (address buffered-file) -> <void> - # pseudocode: - # var line = new-stream(512, 1) - # var in-code? = false - # while true - # clear-stream(line) - # read-line-buffered(in, line) - # if (line->write == 0) break # end of file - # var word-slice = next-word(line) - # if slice-empty?(word-slice) # whitespace - # write-stream-data(out, line) - # else if (slice-equal?(word-slice, "==")) - # word-slice = next-word(line) - # in-code? = slice-equal?(word-slice, "code") - # write-stream-data(out, line) - # else if (in-code?) - # rewind-stream(line) - # convert-instruction(line, out) - # else - # rewind-stream(line) - # convert-data(line, out) - # flush(out) - # - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # . save registers - 50/push-EAX - 51/push-ECX - 52/push-EDX - 53/push-EBX - # var line/ECX : (address stream byte) = stream(512) - 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 0x200/imm32 # subtract from ESP - 68/push 0x200/imm32/length - 68/push 0/imm32/read - 68/push 0/imm32/write - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # var word-slice/EDX = {0, 0} - 68/push 0/imm32/end - 68/push 0/imm32/start - 89/copy 3/mod/direct 2/rm32/EDX . . . 4/r32/ESP . . # copy ESP to EDX - # var in-code?/EBX = false - 31/xor 3/mod/direct 3/rm32/EBX . . . 3/r32/EBX . . # clear EBX -$convert:loop: - # clear-stream(line) - # . . push args - 51/push-ECX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # read-line-buffered(in, line) - # . . push args - 51/push-ECX - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - # . . call - e8/call read-line-buffered/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -$convert:check0: - # if (line->write == 0) break - 81 7/subop/compare 0/mod/indirect 1/rm32/ECX . . . . . 0/imm32 # compare *ECX - 0f 84/jump-if-equal $convert:break/disp32 -#? # dump line {{{ -#? # . write(2/stderr, "LL: ") -#? # . . push args -#? 68/push "LL: "/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write-stream(2/stderr, line) -#? # . . push args -#? 51/push-ECX -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write-stream/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write(2/stderr, "$\n") -#? # . . push args -#? 68/push "$\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # }}} - # next-word(line, word-slice) - # . . push args - 52/push-EDX - 51/push-ECX - # . . call - e8/call next-word/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -$convert:check1: - # if (slice-empty?(word-slice)) write-stream-data(out, line) - # . EAX = slice-empty?(word-slice) - # . . push args - 52/push-EDX - # . . call - e8/call slice-empty?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . if (EAX != 0) write-stream-data(out, line) - 3d/compare-EAX-and 0/imm32 - 0f 85/jump-if-not-equal $convert:pass-through/disp32 -$convert:check2: -#? # dump word-slice {{{ -#? # . write(2/stderr, "AA: ") -#? # . . push args -#? 68/push "AA: "/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . clear-stream(Stderr+4) -#? # . . push args -#? b8/copy-to-EAX Stderr/imm32 -#? 05/add-to-EAX 4/imm32 -#? 50/push-EAX -#? # . . call -#? e8/call clear-stream/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -#? # . write-slice-buffered(Stderr, word-slice) -#? # . . push args -#? 52/push-EDX -#? 68/push Stderr/imm32 -#? # . . call -#? e8/call write-slice-buffered/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . flush(Stderr) -#? # . . push args -#? 68/push Stderr/imm32 -#? # . . call -#? e8/call flush/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -#? # . write(2/stderr, "$\n") -#? # . . push args -#? 68/push "$\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # }}} - # if (!slice-equal?(word-slice, "==")) goto next check - # . EAX = slice-equal?(word-slice, "==") - # . . push args - 68/push "=="/imm32 - 52/push-EDX - # . . call - e8/call slice-equal?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . if (EAX == 0) goto check3 - 3d/compare-EAX-and 0/imm32 - 0f 84/jump-if-equal $convert:check3/disp32 - # word-slice = next-word(line) - # . . push args - 52/push-EDX - 51/push-ECX - # . . call - e8/call next-word/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # dump segment name {{{ -#? # . write(2/stderr, "AA: ") -#? # . . push args -#? 68/push "AA: "/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . clear-stream(Stderr+4) -#? # . . push args -#? b8/copy-to-EAX Stderr/imm32 -#? 05/add-to-EAX 4/imm32 -#? 50/push-EAX -#? # . . call -#? e8/call clear-stream/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -#? # . write-slice-buffered(Stderr, word-slice) -#? # . . push args -#? 52/push-EDX -#? 68/push Stderr/imm32 -#? # . . call -#? e8/call write-slice-buffered/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . flush(Stderr) -#? # . . push args -#? 68/push Stderr/imm32 -#? # . . call -#? e8/call flush/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -#? # . write(2/stderr, "$\n") -#? # . . push args -#? 68/push "$\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # }}} - # in-code? = slice-equal?(word-slice, "code") - # . . push args - 68/push "code"/imm32 - 52/push-EDX - # . . call - e8/call slice-equal?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . . in-code? = EAX - 89/copy 3/mod/direct 3/rm32/EBX . . . 0/r32/EAX . . # copy EAX to EBX - # write-stream-data(out, line) - eb/jump $convert:pass-through/disp8 -$convert:check3: - # else rewind-stream(line) - # . rewind-stream(line) - # . . push args - 51/push-ECX - # . . call - e8/call rewind-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # if (in-code? != 0) convert-instruction(line, out) - 81 7/subop/compare 3/mod/direct 3/rm32/EBX . . . . . 0/imm32 # compare EBX - 74/jump-if-equal $convert:data/disp8 -$convert:code: - # . convert-instruction(line, out) - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - 51/push-ECX - # . . call - e8/call convert-instruction/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . loop - e9/jump $convert:loop/disp32 -$convert:data: - # else convert-data(line, out) - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - 51/push-ECX - # . . call - e8/call convert-data/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . loop - e9/jump $convert:loop/disp32 -$convert:pass-through: - # write-stream-data(out, line) - # . . push args - 51/push-ECX - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - # . . call - e8/call write-stream-data/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . loop - e9/jump $convert:loop/disp32 -$convert:break: - # flush(out) - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - # . . call - e8/call flush/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -$convert:end: - # . reclaim locals - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x214/imm32 # add to ESP - # . restore registers - 5b/pop-to-EBX - 5a/pop-to-EDX - 59/pop-to-ECX - 58/pop-to-EAX - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-convert-passes-empty-lines-through: - # if a line is empty, pass it along unchanged - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # setup - # . clear-stream(_test-input-stream) - # . . push args - 68/push _test-input-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-input-buffered-file+4) - # . . push args - b8/copy-to-EAX _test-input-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-stream) - # . . push args - 68/push _test-output-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-buffered-file+4) - # . . push args - b8/copy-to-EAX _test-output-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # write nothing to input - # convert(_test-input-buffered-file, _test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - 68/push _test-input-buffered-file/imm32 - # . . call - e8/call convert/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check that the line just passed through - # . flush(_test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - # . . call - e8/call flush/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . check-stream-equal(_test-output-stream, "", msg) - # . . push args - 68/push "F - test-convert-passes-empty-lines-through"/imm32 - 68/push ""/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-stream-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-convert-passes-lines-with-just-whitespace-through: - # if a line is empty, pass it along unchanged - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # setup - # . clear-stream(_test-input-stream) - # . . push args - 68/push _test-input-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-input-buffered-file+4) - # . . push args - b8/copy-to-EAX _test-input-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-stream) - # . . push args - 68/push _test-output-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-buffered-file+4) - # . . push args - b8/copy-to-EAX _test-output-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # initialize input - # . write(_test-input-stream, " ") - # . . push args - 68/push " "/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # convert(_test-input-buffered-file, _test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - 68/push _test-input-buffered-file/imm32 - # . . call - e8/call convert/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check that the line just passed through - # . flush(_test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - # . . call - e8/call flush/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . check-next-stream-line-equal(_test-output-stream, " ", msg) - # . . push args - 68/push "F - test-convert-passes-with-just-whitespace-through"/imm32 - 68/push " "/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-next-stream-line-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-convert-passes-segment-headers-through: - # if a line starts with '==', pass it along unchanged - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # setup - # . clear-stream(_test-input-stream) - # . . push args - 68/push _test-input-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-input-buffered-file+4) - # . . push args - b8/copy-to-EAX _test-input-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-stream) - # . . push args - 68/push _test-output-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-buffered-file+4) - # . . push args - b8/copy-to-EAX _test-output-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # initialize input - # . write(_test-input-stream, "== abcd 0x1") - # . . push args - 68/push "== abcd 0x1"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # convert(_test-input-buffered-file, _test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - 68/push _test-input-buffered-file/imm32 - # . . call - e8/call convert/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check that the line just passed through - # . flush(_test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - # . . call - e8/call flush/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . check-stream-equal(_test-output-stream, "== abcd 0x1", msg) - # . . push args - 68/push "F - test-convert-passes-segment-headers-through"/imm32 - 68/push "== abcd 0x1"/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-stream-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-convert-in-data-segment: - # correctly process lines in the data segment - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # setup - # . clear-stream(_test-input-stream) - # . . push args - 68/push _test-input-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-input-buffered-file+4) - # . . push args - b8/copy-to-EAX _test-input-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-stream) - # . . push args - 68/push _test-output-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-buffered-file+4) - # . . push args - b8/copy-to-EAX _test-output-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # initialize input - # == code 0x1 - # == data 0x2 - # 3 4/imm32 - # . write(_test-input-stream, "== code 0x1") - # . . push args - 68/push "== code 0x1\n"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . write(_test-input-stream, "== data 0x2\n") - # . . push args - 68/push "== data 0x2\n"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . write(_test-input-stream, "3 4/imm32\n") - # . . push args - 68/push "3 4/imm32\n"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # convert(_test-input-buffered-file, _test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - 68/push _test-input-buffered-file/imm32 - # . . call - e8/call convert/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check output -#? # debug print {{{ -#? # . write(2/stderr, "^") -#? # . . push args -#? 68/push "^"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write-stream(2/stderr, _test-output-stream) -#? # . . push args -#? 68/push _test-output-stream/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write-stream/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write(2/stderr, "$\n") -#? # . . push args -#? 68/push "$\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # }}} - # . flush(_test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - # . . call - e8/call flush/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . check-next-stream-line-equal(_test-output-stream, "== code 0x1", msg) - # . . push args - 68/push "F - test-convert-in-data-segment/0"/imm32 - 68/push "== code 0x1"/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-next-stream-line-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . check-next-stream-line-equal(_test-output-stream, "== data 0x2", msg) - # . . push args - 68/push "F - test-convert-in-data-segment/1"/imm32 - 68/push "== data 0x2"/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-next-stream-line-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . check-next-stream-line-equal(_test-output-stream, "03 04 00 00 00 ", msg) - # . . push args - 68/push "F - test-convert-in-data-segment/2"/imm32 - 68/push "03 04 00 00 00 "/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-next-stream-line-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-convert-code-and-data-segments: - # correctly process lines in both code and data segments - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # setup - # . clear-stream(_test-input-stream) - # . . push args - 68/push _test-input-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-input-buffered-file+4) - # . . push args - b8/copy-to-EAX _test-input-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-stream) - # . . push args - 68/push _test-output-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-buffered-file+4) - # . . push args - b8/copy-to-EAX _test-output-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # initialize input - # == code 0x1 - # e8/call 20/disp32 - # 68/push 0x20/imm8 - # == data 0x2 - # 3 4/imm32 - # . write(_test-input-stream, "== code 0x1\n") - # . . push args - 68/push "== code 0x1\n"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . write(_test-input-stream, "e8/call 20/disp32\n") - # . . push args - 68/push "e8/call 20/disp32\n"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . write(_test-input-stream, "68/push 0x20/imm8\n") - # . . push args - 68/push "68/push 0x20/imm8\n"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . write(_test-input-stream, "== data 0x2\n") - # . . push args - 68/push "== data 0x2\n"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . write(_test-input-stream, "3 4/imm32\n") - # . . push args - 68/push "3 4/imm32\n"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # convert(_test-input-buffered-file, _test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - 68/push _test-input-buffered-file/imm32 - # . . call - e8/call convert/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check output - # == code 0x1 - # e8 20 00 00 00 # e8/call 20/disp32 - # 68 20 # 68/push 0x20/imm8 - # == data 0x2 - # 03 04 00 00 00 -#? # debug print {{{ -#? # . write(2/stderr, "^") -#? # . . push args -#? 68/push "^"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write-stream(2/stderr, _test-output-stream) -#? # . . push args -#? 68/push _test-output-stream/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write-stream/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write(2/stderr, "$\n") -#? # . . push args -#? 68/push "$\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # }}} - # . flush(_test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - # . . call - e8/call flush/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . check-next-stream-line-equal(_test-output-stream, "== code 0x1", msg) - # . . push args - 68/push "F - test-convert-code-and-data-segments/0"/imm32 - 68/push "== code 0x1"/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-next-stream-line-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . check-next-stream-line-equal(_test-output-stream, "e8 20 00 00 00 # e8/call 20/disp32", msg) - # . . push args - 68/push "F - test-convert-code-and-data-segments/1"/imm32 - 68/push "e8 20 00 00 00 # e8/call 20/disp32"/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-next-stream-line-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . check-next-stream-line-equal(_test-output-stream, "68 20 # 68/push 0x20/imm8", msg) - # . . push args - 68/push "F - test-convert-code-and-data-segments/2"/imm32 - 68/push "68 20 # 68/push 0x20/imm8"/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-next-stream-line-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . check-next-stream-line-equal(_test-output-stream, "== data 0x2", msg) - # . . push args - 68/push "F - test-convert-code-and-data-segments/3"/imm32 - 68/push "== data 0x2"/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-next-stream-line-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . check-next-stream-line-equal(_test-output-stream, "03 04 00 00 00 ", msg) - # . . push args - 68/push "F - test-convert-code-and-data-segments/4"/imm32 - 68/push "03 04 00 00 00 "/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-next-stream-line-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -convert-data: # line : (address stream byte), out : (address buffered-file) -> <void> - # pseudocode: - # var word-slice = {0, 0} - # while true - # word-slice = next-word(line) - # if slice-empty?(word-slice) # end of file (maybe including trailing whitespace) - # break # skip emitting some whitespace - # if slice-starts-with?(word-slice, "#") # comment - # write-slice-buffered(out, word-slice) - # return - # if slice-ends-with?(word-slice, ":") # label - # write-stream-data(out, line) - # return - # if has-metadata?(word-slice, "imm32") - # emit(out, word-slice, 4) - # # disp32 is not permitted in data segments, and anything else is only a byte long - # else - # emit(out, word-slice, 1) - # write-buffered(out, "\n") - # - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # . save registers - 50/push-EAX - 51/push-ECX - 52/push-EDX - # var word-slice/ECX = {0, 0} - 68/push 0/imm32/end - 68/push 0/imm32/start - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX -#? # dump line {{{ -#? # . write(2/stderr, "LL: ") -#? # . . push args -#? 68/push "LL: "/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # write-stream(2/stderr, line) -#? # . . push args -#? ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write-stream/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write(2/stderr, "$\n") -#? # . . push args -#? 68/push "$\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # }}} -$convert-data:loop: - # next-word(line, word-slice) - # . . push args - 51/push-ECX - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - # . . call - e8/call next-word/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # dump word-slice {{{ -#? # . write(2/stderr, "AA: ") -#? # . . push args -#? 68/push "AA: "/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . clear-stream(Stderr+4) -#? # . . push args -#? b8/copy-to-EAX Stderr/imm32 -#? 05/add-to-EAX 4/imm32 -#? 50/push-EAX -#? # . . call -#? e8/call clear-stream/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -#? # . write-slice-buffered(Stderr, word-slice) -#? # . . push args -#? 51/push-ECX -#? 68/push Stderr/imm32 -#? # . . call -#? e8/call write-slice-buffered/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . flush(Stderr) -#? # . . push args -#? 68/push Stderr/imm32 -#? # . . call -#? e8/call flush/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -#? # . write(2/stderr, "$\n") -#? # . . push args -#? 68/push "$\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # }}} -$convert-data:check0: - # if (slice-empty?(word-slice)) break - # . EAX = slice-empty?(word-slice) - # . . push args - 51/push-ECX - # . . call - e8/call slice-empty?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . if (EAX != 0) break - 3d/compare-EAX-and 0/imm32 - 0f 85/jump-if-not-equal $convert-data:break/disp32 -$convert-data:check-for-comment: - # if (slice-starts-with?(word-slice, "#")) - # . start/EDX = word-slice->start - 8b/copy 0/mod/indirect 1/rm32/ECX . . . 2/r32/EDX . . # copy *ECX to EDX - # . c/EAX = *start - 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX - 8a/copy-byte 0/mod/indirect 2/rm32/EDX . . . 0/r32/AL . . # copy byte at *EDX to AL - # . if (EAX != '#') goto next check - 3d/compare-EAX-and 0x23/imm32/hash - 75/jump-if-not-equal $convert-data:check-for-label/disp8 -$convert-data:comment: - # write-slice-buffered(out, word-slice) - # . . push args - 51/push-ECX - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - # . . call - e8/call write-slice-buffered/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # return - 0f 85/jump-if-not-equal $convert-data:end/disp32 -$convert-data:check-for-label: - # if (slice-ends-with?(word-slice, ":")) - # . end/EDX = word-slice->end - 8b/copy 1/mod/*+disp8 1/rm32/ECX . . . 2/r32/EDX 4/disp8 . # copy *(ECX+4) to EDX - # . c/EAX = *(end-1) - 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX - 8a/copy-byte 1/mod/*+disp8 2/rm32/EDX . . . 0/r32/AL -1/disp8 . # copy byte at *ECX to AL - # . if (EAX != ':') goto next check - 3d/compare-EAX-and 0x3a/imm32/colon - 75/jump-if-not-equal $convert-data:check-for-imm32/disp8 -$convert-data:label: - # write-stream-data(out, line) - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - # . . call - e8/call write-stream-data/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # return - 75/jump-if-not-equal $convert-data:end/disp8 -$convert-data:check-for-imm32: - # if (has-metadata?(word-slice, "imm32")) - # . EAX = has-metadata?(ECX, "imm32") - # . . push args - 68/push "imm32"/imm32 - 51/push-ECX - # . . call - e8/call has-metadata?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . if (EAX == 0) process as a single byte - 3d/compare-EAX-and 0/imm32 - 74/jump-if-equal $convert-data:single-byte/disp8 -$convert-data:imm32: - # emit(out, word-slice, 4) - # . . push args - 68/push 4/imm32 - 51/push-ECX - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - # . . call - e8/call emit/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - e9/jump $convert-data:loop/disp32 -$convert-data:single-byte: - # emit(out, word-slice, 1) - # . . push args - 68/push 1/imm32 - 51/push-ECX - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - # . . call - e8/call emit/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - e9/jump $convert-data:loop/disp32 -$convert-data:break: - # write-buffered(out, "\n") - # . . push args - 68/push "\n"/imm32 - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - # . . call - e8/call write-buffered/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -$convert-data:end: - # . reclaim locals - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . restore registers - 5a/pop-to-EDX - 59/pop-to-ECX - 58/pop-to-EAX - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-convert-data-passes-comments-through: - # if a line starts with '#', pass it along unchanged - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # setup - # . clear-stream(_test-input-stream) - # . . push args - 68/push _test-input-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-stream) - # . . push args - 68/push _test-output-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-buffered-file+4) - # . . push args - b8/copy-to-EAX _test-output-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # initialize input - # . write(_test-input-stream, "# abcd") - # . . push args - 68/push "# abcd"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # convert-data(_test-input-stream, _test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call convert-data/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check that the line just passed through - # . flush(_test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - # . . call - e8/call flush/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -#? # dump output {{{ -#? # . write(2/stderr, "^") -#? # . . push args -#? 68/push "^"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write-stream(2/stderr, _test-output-stream) -#? # . . push args -#? 68/push _test-output-stream/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write-stream/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write(2/stderr, "$\n") -#? # . . push args -#? 68/push "$\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # }}} - # . check-stream-equal(_test-output-stream, "# abcd", msg) - # . . push args - 68/push "F - test-convert-data-passes-comments-through"/imm32 - 68/push "# abcd"/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-stream-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-convert-data-passes-labels-through: - # if the first word ends with ':', pass along the entire line unchanged - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # setup - # . clear-stream(_test-input-stream) - # . . push args - 68/push _test-input-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-stream) - # . . push args - 68/push _test-output-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-buffered-file+4) - # . . push args - b8/copy-to-EAX _test-output-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # initialize input - # . write(_test-input-stream, "ab: # cd") - # . . push args - 68/push "ab: # cd"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # convert-data(_test-input-stream, _test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call convert-data/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check that the line just passed through - # . flush(_test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - # . . call - e8/call flush/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . check-stream-equal(_test-output-stream, "ab: # cd", msg) - # . . push args - 68/push "F - test-convert-data-passes-labels-through"/imm32 - 68/push "ab: # cd"/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-stream-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-convert-data-passes-names-through: - # If a word is a valid name, just emit it unchanged. - # Later phases will deal with it. - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # setup - # . clear-stream(_test-input-stream) - # . . push args - 68/push _test-input-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-stream) - # . . push args - 68/push _test-output-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-buffered-file+4) - # . . push args - b8/copy-to-EAX _test-output-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # initialize input - # . write(_test-input-stream, "abcd/imm32") - # . . push args - 68/push "abcd/imm32"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # convert-data(_test-input-stream, _test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call convert-data/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check that the line just passed through - # . flush(_test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - # . . call - e8/call flush/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . check-stream-equal(_test-output-stream, "abcd/imm32 \n", msg) - # . . push args - 68/push "F - test-convert-data-passes-names-through"/imm32 - 68/push "abcd/imm32 \n"/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-stream-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-convert-data-handles-imm32: - # If a word has the /imm32 metadata, emit it in 4 bytes. - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # setup - # . clear-stream(_test-input-stream) - # . . push args - 68/push _test-input-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-stream) - # . . push args - 68/push _test-output-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-buffered-file+4) - # . . push args - b8/copy-to-EAX _test-output-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # initialize input - # . write(_test-input-stream, "30/imm32") - # . . push args - 68/push "30/imm32"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # convert-data(_test-input-stream, _test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call convert-data/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check that 4 bytes were written - # . flush(_test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - # . . call - e8/call flush/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . check-stream-equal(_test-output-stream, "30 00 00 00 \n", msg) - # . . push args - 68/push "F - test-convert-data-handles-imm32"/imm32 - 68/push "30 00 00 00 \n"/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-stream-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-convert-data-handles-single-byte: - # Any metadata but /imm32 will emit a single byte. - # Data segments can't have /disp32, and SubX doesn't support 16-bit operands. - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # setup - # . clear-stream(_test-input-stream) - # . . push args - 68/push _test-input-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-stream) - # . . push args - 68/push _test-output-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-buffered-file+4) - # . . push args - b8/copy-to-EAX _test-output-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # initialize input - # . write(_test-input-stream, "30/imm16") - # . . push args - 68/push "30/imm16"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # convert-data(_test-input-stream, _test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call convert-data/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check that a single byte was written (imm16 is not a valid operand type) - # . flush(_test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - # . . call - e8/call flush/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . check-stream-equal(_test-output-stream, "30 \n", msg) - # . . push args - 68/push "F - test-convert-data-handles-single-byte"/imm32 - 68/push "30 \n"/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-stream-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-convert-data-multiple-bytes: - # Multiple single-byte words in input stream get processed one by one. - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # setup - # . clear-stream(_test-input-stream) - # . . push args - 68/push _test-input-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-stream) - # . . push args - 68/push _test-output-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-buffered-file+4) - # . . push args - b8/copy-to-EAX _test-output-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # initialize input - # . write(_test-input-stream, "1 2") - # . . push args - 68/push "1 2"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # convert-data(_test-input-stream, _test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call convert-data/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check output - # . flush(_test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - # . . call - e8/call flush/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . check-stream-equal(_test-output-stream, "01 02 \n", msg) - # . . push args - 68/push "F - test-convert-data-multiple-bytes"/imm32 - 68/push "01 02 \n"/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-stream-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-convert-data-byte-then-name: - # Single-byte word followed by valid name get processed one by one. - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # setup - # . clear-stream(_test-input-stream) - # . . push args - 68/push _test-input-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-stream) - # . . push args - 68/push _test-output-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-buffered-file+4) - # . . push args - b8/copy-to-EAX _test-output-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # initialize input - # . write(_test-input-stream, "30 abcd/o") - # . . push args - 68/push "30 abcd/o"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # convert-data(_test-input-stream, _test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call convert-data/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check output - # . flush(_test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - # . . call - e8/call flush/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . check-stream-equal(_test-output-stream, "30 abcd/o \n", msg) - # . . push args - 68/push "F - test-convert-data-byte-then-name"/imm32 - 68/push "30 abcd/o \n"/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-stream-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-convert-data-multiple-words: - # Multiple words in input stream get processed one by one. - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # setup - # . clear-stream(_test-input-stream) - # . . push args - 68/push _test-input-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-stream) - # . . push args - 68/push _test-output-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-buffered-file+4) - # . . push args - b8/copy-to-EAX _test-output-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # initialize input - # . write(_test-input-stream, "30 abcd/o 42e1/imm32") - # . . push args - 68/push "30 abcd/o 42e1/imm32"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # convert-data(_test-input-stream, _test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call convert-data/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check output - # . flush(_test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - # . . call - e8/call flush/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -#? # dump output {{{ -#? # . write(2/stderr, "^") -#? # . . push args -#? 68/push "^"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write-stream(2/stderr, _test-output-stream) -#? # . . push args -#? 68/push _test-output-stream/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write-stream/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write(2/stderr, "$\n") -#? # . . push args -#? 68/push "$\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # }}} - # . check-stream-equal(_test-output-stream, "30 abcd/o 42 e1 00 00 \n", msg) - # . . push args - 68/push "F - test-convert-data-multiple-words"/imm32 - 68/push "30 abcd/o e1 42 00 00 \n"/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-stream-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-convert-data-trailing-comment: - # Trailing comments in data segment get appropriately ignored. - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # setup - # . clear-stream(_test-input-stream) - # . . push args - 68/push _test-input-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-stream) - # . . push args - 68/push _test-output-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-buffered-file+4) - # . . push args - b8/copy-to-EAX _test-output-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # initialize input - # . write(_test-input-stream, "30/imm32 # comment") - # . . push args - 68/push "30/imm32 # comment"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # convert-data(_test-input-stream, _test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call convert-data/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check output - # . flush(_test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - # . . call - e8/call flush/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -#? # dump output {{{ -#? # . write(2/stderr, "^") -#? # . . push args -#? 68/push "^"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write-stream(2/stderr, _test-output-stream) -#? # . . push args -#? 68/push _test-output-stream/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write-stream/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write(2/stderr, "$\n") -#? # . . push args -#? 68/push "$\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # }}} - # . check-stream-equal(_test-output-stream, "30 00 00 00 # comment", msg) - # . . push args - 68/push "F - test-convert-data-trailing-comment"/imm32 - 68/push "30 00 00 00 # comment"/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-stream-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -# pack an instruction, following the C++ version -# -# zero error handling at the moment (continuing to rely on the C++ version for that): -# missing fields are always 0-filled -# bytes never mentioned are silently dropped; if you don't provide /mod, /rm32 or /r32 you don't get a 0 ModR/M byte. You get *no* ModR/M byte. -# may pick up any of duplicate operands in an instruction -# silently drop extraneous operands -# unceremoniously abort on non-numeric operands except disp or imm -# opcodes must be lowercase and zero padded -# opcodes with misleading operand metadata may get duplicated as operands as well. don't rely on this. -convert-instruction: # line : (address stream byte), out : (address buffered-file) -> <void> - # pseudocode: - # # some early exits - # var word-slice = next-word(line) - # if slice-empty?(word-slice) - # write-stream-data(out, line) - # return - # if slice-starts-with?(word-slice, "#") - # write-stream-data(out, line) - # return - # if slice-ends-with?(word-slice, ":") - # write-stream-data(out, line) - # return - # # really convert - # emit-opcodes(line, out) - # emit-modrm(line, out) - # emit-sib(line, out) - # emit-disp(line, out) - # emit-imm(line, out) - # emit-line-in-comment(line, out) - # - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # . save registers - 50/push-EAX - 51/push-ECX - 52/push-EDX - # var word-slice/ECX = {0, 0} - 68/push 0/imm32/end - 68/push 0/imm32/start - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # next-word(line, word-slice) - # . . push args - 51/push-ECX - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - # . . call - e8/call next-word/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -$convert-instruction:check0: - # if (slice-empty?(word-slice)) break - # . EAX = slice-empty?(word-slice) - # . . push args - 51/push-ECX - # . . call - e8/call slice-empty?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . if (EAX != 0) pass through - 3d/compare-EAX-and 0/imm32 - 75/jump-if-not-equal $convert-instruction:pass-through/disp8 -$convert-instruction:check1: - # if (slice-starts-with?(word-slice, "#")) write-stream-data(out, line) - # . start/EDX = word-slice->start - 8b/copy 0/mod/indirect 1/rm32/ECX . . . 2/r32/EDX . . # copy *ECX to EDX - # . c/EAX = *start - 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX - 8a/copy-byte 0/mod/indirect 2/rm32/EDX . . . 0/r32/AL . . # copy byte at *EDX to AL - # . if (EAX == '#') pass through - 3d/compare-EAX-and 0x23/imm32/hash - 74/jump-if-equal $convert-instruction:pass-through/disp8 -$convert-instruction:check2: - # if (slice-ends-with?(word-slice, ":")) write-stream-data(out, line) - # . end/EDX = word-slice->end - 8b/copy 1/mod/*+disp8 1/rm32/ECX . . . 2/r32/EDX 4/disp8 . # copy *(ECX+4) to EDX - # . c/EAX = *(end-1) - 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX - 8a/copy-byte 1/mod/*+disp8 2/rm32/EDX . . . 0/r32/AL -1/disp8 . # copy byte at *ECX to AL - # . if (EAX == ':') pass through - 3d/compare-EAX-and 0x3a/imm32/colon - 75/jump-if-not-equal $convert-instruction:really-convert/disp8 -$convert-instruction:pass-through: - # write-stream-data(out, line) - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - # . . call - e8/call write-stream-data/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # return - eb/jump $convert-instruction:end/disp8 -$convert-instruction:really-convert: - # emit-opcodes(line, out) - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - # . . call - e8/call emit-opcodes/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # emit-modrm(line, out) - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - # . . call - e8/call emit-modrm/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # emit-sib(line, out) - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - # . . call - e8/call emit-sib/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # emit-disp(line, out) - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - # . . call - e8/call emit-disp/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # emit-imm(line, out) - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - # . . call - e8/call emit-imm/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # emit-line-in-comment(line, out) - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - # . . call - e8/call emit-line-in-comment/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -$convert-instruction:end: - # . restore locals - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . restore registers - 5a/pop-to-EDX - 59/pop-to-ECX - 58/pop-to-EAX - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -emit-opcodes: # line : (address stream byte), out : (address buffered-file) -> <void> - # opcodes occupy 1-3 bytes: - # xx - # 0f xx - # f2 xx - # f3 xx - # f2 0f xx - # f3 0f xx - # - # pseudocode: - # rewind-stream(line) - # - # var op1 = next-word(line) - # if (slice-empty?(op1) || slice-starts-with?(op1, "#")) return - # op1 = next-token-from-slice(op1->start, op1->end, "/") - # write-slice-buffered(out, op1) - # if !slice-equal?(op1, "0f") && !slice-equal?(op1, "f2") && !slice-equal?(op1, "f3") - # return - # - # var op2 = next-word(line) - # if (slice-empty?(op2) || slice-starts-with?(op2, "#")) return - # op2 = next-token-from-slice(op2->start, op2->end, "/") - # write-slice-buffered(out, op2) - # if slice-equal?(op1, "0f") - # return - # if !slice-equal?(op2, "0f") - # return - # - # var op3 = next-word(line) - # if (slice-empty?(op3) || slice-starts-with?(op3, "#")) return - # op3 = next-token-from-slice(op3->start, op3->end, "/") - # write-slice-buffered(out, op3) - # - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # . save registers - 50/push-EAX - 51/push-ECX - 52/push-EDX - 53/push-EBX - # var op1/ECX = {0, 0} - 68/push 0/imm32/end - 68/push 0/imm32/start - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # var op2/EDX = {0, 0} - 68/push 0/imm32/end - 68/push 0/imm32/start - 89/copy 3/mod/direct 2/rm32/EDX . . . 4/r32/ESP . . # copy ESP to EDX - # rewind-stream(line) - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - # . . call - e8/call rewind-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -$emit-opcodes:op1: - # next-word(line, op1) - # . . push args - 51/push-ECX - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - # . . call - e8/call next-word/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # if (slice-empty?(op1)) return - # . EAX = slice-empty?(op1) - # . . push args - 51/push-ECX - # . . call - e8/call slice-empty?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . if (EAX != 0) return - 3d/compare-EAX-and 0/imm32 - 0f 85/jump-if-not-equal $emit-opcodes:end/disp32 - # if (slice-starts-with?(op1, "#")) return - # . start/EBX = op1->start - 8b/copy 0/mod/indirect 1/rm32/ECX . . . 3/r32/EBX . . # copy *ECX to EBX - # . c/EAX = *start - 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX - 8a/copy-byte 0/mod/indirect 3/rm32/EBX . . . 0/r32/AL . . # copy byte at *EBX to AL - # . if (EAX == '#') return - 3d/compare-EAX-and 0x23/imm32/hash - 0f 84/jump-if-equal $emit-opcodes:end/disp32 - # op1 = next-token-from-slice(op1->start, op1->end, '/') - # . . push args - 51/push-ECX - 68/push 0x2f/imm32/slash - ff 6/subop/push 1/mod/*+disp8 1/rm32/ECX . . . . 4/disp8 . # push *(ECX+4) - ff 6/subop/push 0/mod/indirect 1/rm32/ECX . . . . . . # push *ECX - # . . call - e8/call next-token-from-slice/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP - # write-slice-buffered(out, op1) - # . . push args - 51/push-ECX - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - # . . call - e8/call write-slice-buffered/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # write-buffered(out, " ") - # . . push args - 68/push " "/imm32 - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - # . . call - e8/call write-buffered/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # if (slice-equal?(op1, "0f")) goto op2 - # . EAX = slice-equal?(op1, "0f") - # . . push args - 68/push "0f"/imm32 - 51/push-ECX - # . . call - e8/call slice-equal?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . if (EAX != 0) goto op2 - 3d/compare-EAX-and 0/imm32 - 75/jump-if-not-equal $emit-opcodes:op2/disp8 - # if (slice-equal?(op1, "f2")) goto op2 - # . EAX = slice-equal?(op1, "f2") - # . . push args - 68/push "f2"/imm32 - 51/push-ECX - # . . call - e8/call slice-equal?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . if (EAX != 0) goto op2 - 3d/compare-EAX-and 0/imm32 - 75/jump-if-not-equal $emit-opcodes:op2/disp8 - # if (slice-equal?(op1, "f3")) goto op2 - # . EAX = slice-equal?(op1, "f3") - # . . push args - 68/push "f3"/imm32 - 51/push-ECX - # . . call - e8/call slice-equal?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . if (EAX != 0) goto op2 - 3d/compare-EAX-and 0/imm32 - 75/jump-if-not-equal $emit-opcodes:op2/disp8 - # otherwise return - e9/jump $emit-opcodes:end/disp32 -$emit-opcodes:op2: - # next-word(line, op2) - # . . push args - 52/push-EDX - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - # . . call - e8/call next-word/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # if (slice-empty?(op2)) return - # . EAX = slice-empty?(op2) - # . . push args - 52/push-EDX - # . . call - e8/call slice-empty?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . if (EAX != 0) return - 3d/compare-EAX-and 0/imm32 - 0f 85/jump-if-not-equal $emit-opcodes:end/disp32 - # if (slice-starts-with?(op2, "#")) return - # . start/EBX = op2->start - 8b/copy 0/mod/indirect 2/rm32/EDX . . . 3/r32/EBX . . # copy *EDX to EBX - # . c/EAX = *start - 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX - 8a/copy-byte 0/mod/indirect 3/rm32/EBX . . . 0/r32/AL . . # copy byte at *EBX to AL - # . if (EAX == '#') return - 3d/compare-EAX-and 0x23/imm32/hash - 0f 84/jump-if-equal $emit-opcodes:end/disp32 - # op2 = next-token-from-slice(op2->start, op2->end, '/') - # . . push args - 52/push-EDX - 68/push 0x2f/imm32/slash - ff 6/subop/push 1/mod/*+disp8 2/rm32/EDX . . . . 4/disp8 . # push *(EDX+4) - ff 6/subop/push 0/mod/indirect 2/rm32/EDX . . . . . . # push *EDX - # . . call - e8/call next-token-from-slice/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP - # write-slice-buffered(out, op2) - # . . push args - 52/push-EDX - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - # . . call - e8/call write-slice-buffered/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # write-buffered(out, " ") - # . . push args - 68/push " "/imm32 - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - # . . call - e8/call write-buffered/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # if (slice-equal?(op1, "0f")) return - # . EAX = slice-equal?(op1, "0f") - # . . push args - 68/push "0f"/imm32 - 51/push-ECX - # . . call - e8/call slice-equal?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . if (EAX != 0) return - 3d/compare-EAX-and 0/imm32 - 0f 85/jump-if-not-equal $emit-opcodes:end/disp32 - # if (!slice-equal?(op2, "0f")) return - # . EAX = slice-equal?(op2, "0f") - # . . push args - 68/push "0f"/imm32 - 52/push-EDX - # . . call - e8/call slice-equal?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . if (EAX == 0) return - 3d/compare-EAX-and 0/imm32 - 0f 84/jump-if-equal $emit-opcodes:end/disp32 -$emit-opcodes:op3: - # next-word(line, op3) # reuse op2/EDX - # . . push args - 52/push-EDX - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - # . . call - e8/call next-word/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # if (slice-empty?(op3)) return - # . EAX = slice-empty?(op3) - # . . push args - 52/push-EDX - # . . call - e8/call slice-empty?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . if (EAX != 0) return - 3d/compare-EAX-and 0/imm32 - 0f 85/jump-if-not-equal $emit-opcodes:end/disp32 - # if (slice-starts-with?(op3, "#")) return - # . start/EBX = op2->start - 8b/copy 0/mod/indirect 2/rm32/EDX . . . 3/r32/EBX . . # copy *EDX to EBX - # . c/EAX = *start - 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX - 8a/copy-byte 0/mod/indirect 3/rm32/EBX . . . 0/r32/AL . . # copy byte at *EBX to AL - # . if (EAX == '#') return - 3d/compare-EAX-and 0x23/imm32/hash - 0f 84/jump-if-equal $emit-opcodes:end/disp32 - # op3 = next-token-from-slice(op3->start, op3->end, '/') - # . . push args - 52/push-EDX - 68/push 0x2f/imm32/slash - ff 6/subop/push 1/mod/*+disp8 2/rm32/EDX . . . . 4/disp8 . # push *(EDX+4) - ff 6/subop/push 0/mod/indirect 2/rm32/EDX . . . . . . # push *EDX - # . . call - e8/call next-token-from-slice/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP - # write-slice-buffered(out, op3) - # . . push args - 52/push-EDX - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - # . . call - e8/call write-slice-buffered/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # write-buffered(out, " ") - # . . push args - 68/push " "/imm32 - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - # . . call - e8/call write-buffered/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -$emit-opcodes:end: - # . restore locals - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP - # . restore registers - 5b/pop-to-EBX - 5a/pop-to-EDX - 59/pop-to-ECX - 58/pop-to-EAX - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -emit-modrm: # line : (address stream byte), out : (address buffered-file) -> <void> - # pseudocode: - # rewind-stream(line) - # var has-modrm? = false, mod = 0, rm32 = 0, r32 = 0 - # var word-slice = {0, 0} - # while true - # word-slice = next-word(line) - # if (slice-empty?(word-slice)) break - # if (slice-starts-with?(word-slice, "#")) break - # if (has-metadata?(word-slice, "mod")) - # mod = parse-hex-int(next-token-from-slice(word-slice, "/")) - # has-modrm? = true - # else if (has-metadata?(word-slice, "rm32")) - # rm32 = parse-hex-int(next-token-from-slice(word-slice, "/")) - # has-modrm? = true - # else if (has-metadata?(word-slice, "r32") or has-metadata?(word-slice, "subop")) - # r32 = parse-hex-int(next-token-from-slice(word-slice, "/")) - # has-modrm? = true - # if has-modrm? - # var modrm = mod & 0b11 - # modrm <<= 3 - # modrm |= r32 & 0b111 - # modrm <<= 3 - # modrm |= rm32 & 0b111 - # emit-hex(out, modrm, 1) - # - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # . save registers - 50/push-EAX - 51/push-ECX - 52/push-EDX - 53/push-EBX - 56/push-ESI - 57/push-EDI - # var word-slice/ECX = {0, 0} - 68/push 0/imm32/end - 68/push 0/imm32/start - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # var has-modrm?/EDX = false - 31/xor 3/mod/direct 2/rm32/EDX . . . 2/r32/EDX . . # clear EDX - # var mod/EBX = 0 - 31/xor 3/mod/direct 3/rm32/EBX . . . 3/r32/EBX . . # clear EBX - # var rm32/ESI = 0 - 31/xor 3/mod/direct 6/rm32/ESI . . . 6/r32/ESI . . # clear ESI - # var r32/EDI = 0 - 31/xor 3/mod/direct 7/rm32/EDI . . . 7/r32/EDI . . # clear EDI - # rewind-stream(line) - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - # . . call - e8/call rewind-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -#? # dump line {{{ -#? # . write(2/stderr, "LL: ") -#? # . . push args -#? 68/push "LL: "/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write-stream(2/stderr, line) -#? # . . push args -#? ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write-stream/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write(2/stderr, "$\n") -#? # . . push args -#? 68/push "$\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . rewind-stream(line) -#? # . . push args -#? ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) -#? # . . call -#? e8/call rewind-stream/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -#? # }}} -$emit-modrm:loop: - # next-word(line, word-slice) - # . . push args - 51/push-ECX - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - # . . call - e8/call next-word/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # dump word-slice {{{ -#? # . write(2/stderr, "AA: ") -#? # . . push args -#? 68/push "AA: "/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . clear-stream(Stderr+4) -#? # . . push args -#? b8/copy-to-EAX Stderr/imm32 -#? 05/add-to-EAX 4/imm32 -#? 50/push-EAX -#? # . . call -#? e8/call clear-stream/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -#? # . write-slice-buffered(Stderr, word-slice) -#? # . . push args -#? 51/push-ECX -#? 68/push Stderr/imm32 -#? # . . call -#? e8/call write-slice-buffered/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . flush(Stderr) -#? # . . push args -#? 68/push Stderr/imm32 -#? # . . call -#? e8/call flush/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -#? # . write(2/stderr, "$\n") -#? # . . push args -#? 68/push "$\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # }}} -$emit-modrm:check0: - # if (slice-empty?(word-slice)) break - # . EAX = slice-empty?(word-slice) - # . . push args - 51/push-ECX - # . . call - e8/call slice-empty?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . if (EAX != 0) pass through - 3d/compare-EAX-and 0/imm32 - 0f 85/jump-if-not-equal $emit-modrm:break/disp32 -$emit-modrm:check1: - # if (slice-starts-with?(word-slice, "#")) break - # . spill EDX - 52/push-EDX - # . start/EDX = word-slice->start - 8b/copy 0/mod/indirect 1/rm32/ECX . . . 2/r32/EDX . . # copy *ECX to EDX - # . c/EAX = *start - 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX - 8a/copy-byte 0/mod/indirect 2/rm32/EDX . . . 0/r32/AL . . # copy byte at *EDX to AL - # . restore EDX - 5a/pop-to-EDX - # . if (EAX == '#') pass through - 3d/compare-EAX-and 0x23/imm32/hash - 0f 84/jump-if-equal $emit-modrm:break/disp32 -$emit-modrm:check-for-mod: - # if (has-metadata?(word-slice, "mod")) - # . EAX = has-metadata?(ECX, "mod") - # . . push args - 68/push "mod"/imm32 - 51/push-ECX - # . . call - e8/call has-metadata?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . if (EAX == 0) goto next check - 3d/compare-EAX-and 0/imm32 - 74/jump-if-equal $emit-modrm:check-for-rm32/disp8 -$emit-modrm:mod: - # mod = parse-hex-int(next-token-from-slice(word-slice->start, word-slice->end, '/')) - # . EAX = parse-datum-of-word(word-slice) - # . . push args - 51/push-ECX - # . . call - e8/call parse-datum-of-word/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . mod = EAX - 89/copy 3/mod/direct 3/rm32/EBX . . . 0/r32/EAX . . # copy EAX to EBX - # has-modrm? = true - ba/copy-to-EDX 1/imm32/true - # continue - e9/jump $emit-modrm:loop/disp32 -$emit-modrm:check-for-rm32: - # if (has-metadata?(word-slice, "rm32")) - # . EAX = has-metadata?(ECX, "rm32") - # . . push args - 68/push "rm32"/imm32 - 51/push-ECX - # . . call - e8/call has-metadata?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . if (EAX == 0) goto next check - 3d/compare-EAX-and 0/imm32 - 74/jump-if-equal $emit-modrm:check-for-r32/disp8 -$emit-modrm:rm32: - # rm32 = parse-hex-int(next-token-from-slice(word-slice->start, word-slice->end, '/')) - # . EAX = parse-datum-of-word(word-slice) - # . . push args - 51/push-ECX - # . . call - e8/call parse-datum-of-word/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . rm32 = EAX - 89/copy 3/mod/direct 6/rm32/ESI . . . 0/r32/EAX . . # copy EAX to ESI - # has-modrm? = true - ba/copy-to-EDX 1/imm32/true - # continue - e9/jump $emit-modrm:loop/disp32 -$emit-modrm:check-for-r32: - # if (has-metadata?(word-slice, "r32")) - # . EAX = has-metadata?(ECX, "r32") - # . . push args - 68/push "r32"/imm32 - 51/push-ECX - # . . call - e8/call has-metadata?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . if (EAX == 0) goto next check - 3d/compare-EAX-and 0/imm32 - 74/jump-if-equal $emit-modrm:check-for-subop/disp8 -$emit-modrm:r32: - # r32 = parse-hex-int(next-token-from-slice(word-slice->start, word-slice->end, '/')) - # . EAX = parse-datum-of-word(word-slice) - # . . push args - 51/push-ECX - # . . call - e8/call parse-datum-of-word/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . r32 = EAX - 89/copy 3/mod/direct 7/rm32/EDI . . . 0/r32/EAX . . # copy EAX to EDI - # has-modrm? = true - ba/copy-to-EDX 1/imm32/true - # continue - e9/jump $emit-modrm:loop/disp32 -$emit-modrm:check-for-subop: - # if (has-metadata?(word-slice, "subop")) - # . EAX = has-metadata?(ECX, "subop") - # . . push args - 68/push "subop"/imm32 - 51/push-ECX - # . . call - e8/call has-metadata?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . if (EAX == 0) loop - 3d/compare-EAX-and 0/imm32 - 0f 84/jump-if-equal $emit-modrm:loop/disp32 -$emit-modrm:subop: - # r32 = parse-hex-int(next-token-from-slice(word-slice->start, word-slice->end, '/')) - # . EAX = parse-datum-of-word(word-slice) - # . . push args - 51/push-ECX - # . . call - e8/call parse-datum-of-word/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . r32 = EAX - 89/copy 3/mod/direct 7/rm32/EDI . . . 0/r32/EAX . . # copy EAX to EDI - # has-modrm? = true - ba/copy-to-EDX 1/imm32/true - # continue - e9/jump $emit-modrm:loop/disp32 -$emit-modrm:break: - # if (!has-modrm?) return - 81 7/subop/compare 3/mod/direct 2/rm32/EDX . . . . . 0/imm32 # compare EDX - 74/jump-if-equal $emit-modrm:end/disp8 -$emit-modrm:calculate: - # modrm/EBX = mod & 0b11 - 81 4/subop/and 3/mod/direct 3/rm32/EBX . . . . . 3/imm32/0b11 # bitwise and of EBX - # modrm <<= 3 - c1/shift 4/subop/left 3/mod/direct 3/rm32/EBX . . . . . 3/imm8 # shift EBX left by 3 bits - # modrm |= r32 & 0b111 - 81 4/subop/and 3/mod/direct 7/rm32/EDI . . . . . 7/imm32/0b111 # bitwise and of EDI - 09/or 3/mod/direct 3/rm32/EBX . . . 7/r32/EDI . . # EBX = bitwise OR with EDI - # modrm <<= 3 - c1/shift 4/subop/left 3/mod/direct 3/rm32/EBX . . . . . 3/imm8 # shift EBX left by 3 bits - # modrm |= rm32 & 0b111 - 81 4/subop/and 3/mod/direct 6/rm32/ESI . . . . . 7/imm32/0b111 # bitwise and of ESI - 09/or 3/mod/direct 3/rm32/EBX . . . 6/r32/ESI . . # EBX = bitwise OR with ESI -$emit-modrm:emit: - # emit-hex(out, modrm, 1) - # . . push args - 68/push 1/imm32 - 53/push-EBX - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - # . . call - e8/call emit-hex/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -$emit-modrm:end: - # . restore locals - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . restore registers - 5f/pop-to-EDI - 5e/pop-to-ESI - 5b/pop-to-EBX - 5a/pop-to-EDX - 59/pop-to-ECX - 58/pop-to-EAX - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -emit-sib: # line : (address stream byte), out : (address buffered-file) -> <void> - # pseudocode: - # var has-sib? = false, base = 0, index = 0, scale = 0 - # var word-slice = {0, 0} - # while true - # word-slice = next-word(line) - # if (slice-empty?(word-slice)) break - # if (slice-starts-with?(word-slice, "#")) break - # if (has-metadata?(word-slice, "base") - # base = parse-hex-int(next-token-from-slice(word-slice, "/")) - # has-sib? = true - # else if (has-metadata?(word-slice, "index") - # index = parse-hex-int(next-token-from-slice(word-slice, "/")) - # has-sib? = true - # else if (has-metadata?(word-slice, "scale") - # scale = parse-hex-int(next-token-from-slice(word-slice, "/")) - # has-sib? = true - # if has-sib? - # var sib = scale & 0b11 - # sib <<= 2 - # sib |= index & 0b111 - # sib <<= 3 - # sib |= base & 0b111 - # emit-hex(out, sib, 1) - # - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # . save registers - 50/push-EAX - 51/push-ECX - 52/push-EDX - 53/push-EBX - 56/push-ESI - 57/push-EDI - # var word-slice/ECX = {0, 0} - 68/push 0/imm32/end - 68/push 0/imm32/start - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # var has-sib?/EDX = false - 31/xor 3/mod/direct 2/rm32/EDX . . . 2/r32/EDX . . # clear EDX - # var scale/EBX = 0 - 31/xor 3/mod/direct 3/rm32/EBX . . . 3/r32/EBX . . # clear EBX - # var base/ESI = 0 - 31/xor 3/mod/direct 6/rm32/ESI . . . 6/r32/ESI . . # clear ESI - # var index/EDI = 0 - 31/xor 3/mod/direct 7/rm32/EDI . . . 7/r32/EDI . . # clear EDI - # rewind-stream(line) - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - # . . call - e8/call rewind-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -$emit-sib:loop: -#? # dump line {{{ -#? # . write(2/stderr, "LL: ") -#? # . . push args -#? 68/push "LL: "/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write-stream(2/stderr, line) -#? # . . push args -#? ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write-stream/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write(2/stderr, "$\n") -#? # . . push args -#? 68/push "$\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # }}} - # next-word(line, word-slice) - # . . push args - 51/push-ECX - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - # . . call - e8/call next-word/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # dump word-slice {{{ -#? # . write(2/stderr, "AA: ") -#? # . . push args -#? 68/push "AA: "/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . clear-stream(Stderr+4) -#? # . . push args -#? b8/copy-to-EAX Stderr/imm32 -#? 05/add-to-EAX 4/imm32 -#? 50/push-EAX -#? # . . call -#? e8/call clear-stream/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -#? # . write-slice-buffered(Stderr, word-slice) -#? # . . push args -#? 51/push-ECX -#? 68/push Stderr/imm32 -#? # . . call -#? e8/call write-slice-buffered/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . flush(Stderr) -#? # . . push args -#? 68/push Stderr/imm32 -#? # . . call -#? e8/call flush/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -#? # . write(2/stderr, "$\n") -#? # . . push args -#? 68/push "$\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # }}} -$emit-sib:check0: - # if (slice-empty?(word-slice)) break - # . EAX = slice-empty?(word-slice) - # . . push args - 51/push-ECX - # . . call - e8/call slice-empty?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . if (EAX != 0) pass through - 3d/compare-EAX-and 0/imm32 - 0f 85/jump-if-not-equal $emit-sib:break/disp32 -$emit-sib:check1: - # if (slice-starts-with?(word-slice, "#")) break - # . spill EDX - 52/push-EDX - # . start/EDX = word-slice->start - 8b/copy 0/mod/indirect 1/rm32/ECX . . . 2/r32/EDX . . # copy *ECX to EDX - # . c/EAX = *start - 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX - 8a/copy-byte 0/mod/indirect 2/rm32/EDX . . . 0/r32/AL . . # copy byte at *EDX to AL - # . restore EDX - 5a/pop-to-EDX - # . if (EAX == '#') pass through - 3d/compare-EAX-and 0x23/imm32/hash - 0f 84/jump-if-equal $emit-sib:break/disp32 -$emit-sib:check-for-scale: - # if (has-metadata?(word-slice, "scale")) - # . EAX = has-metadata?(ECX, "scale") - # . . push args - 68/push "scale"/imm32 - 51/push-ECX - # . . call - e8/call has-metadata?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . if (EAX == 0) goto next check - 3d/compare-EAX-and 0/imm32 - 74/jump-if-equal $emit-sib:check-for-base/disp8 -$emit-sib:scale: - # scale = parse-hex-int(next-token-from-slice(word-slice->start, word-slice->end, '/')) - # . EAX = parse-datum-of-word(word-slice) - # . . push args - 51/push-ECX - # . . call - e8/call parse-datum-of-word/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . scale = EAX - 89/copy 3/mod/direct 3/rm32/EBX . . . 0/r32/EAX . . # copy EAX to EBX - # has-sib? = true - ba/copy-to-EDX 1/imm32/true - # continue - e9/jump $emit-sib:loop/disp32 -$emit-sib:check-for-base: - # if (has-metadata?(word-slice, "base")) - # . EAX = has-metadata?(ECX, "base") - # . . push args - 68/push "base"/imm32 - 51/push-ECX - # . . call - e8/call has-metadata?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . if (EAX == 0) goto next check - 3d/compare-EAX-and 0/imm32 - 74/jump-if-equal $emit-sib:check-for-index/disp8 -$emit-sib:base: - # base = parse-hex-int(next-token-from-slice(word-slice->start, word-slice->end, '/')) - # . EAX = parse-datum-of-word(word-slice) - # . . push args - 51/push-ECX - # . . call - e8/call parse-datum-of-word/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . base = EAX - 89/copy 3/mod/direct 6/rm32/ESI . . . 0/r32/EAX . . # copy EAX to ESI - # has-sib? = true - ba/copy-to-EDX 1/imm32/true - # continue - e9/jump $emit-sib:loop/disp32 -$emit-sib:check-for-index: - # if (has-metadata?(word-slice, "index")) - # . EAX = has-metadata?(ECX, "index") - # . . push args - 68/push "index"/imm32 - 51/push-ECX - # . . call - e8/call has-metadata?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . if (EAX == 0) loop - 3d/compare-EAX-and 0/imm32 - 0f 84/jump-if-equal $emit-sib:loop/disp32 -$emit-sib:index: - # index = parse-hex-int(next-token-from-slice(word-slice->start, word-slice->end, '/')) - # . EAX = parse-datum-of-word(word-slice) - # . . push args - 51/push-ECX - # . . call - e8/call parse-datum-of-word/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . index = EAX - 89/copy 3/mod/direct 7/rm32/EDI . . . 0/r32/EAX . . # copy EAX to EDI - # has-sib? = true - ba/copy-to-EDX 1/imm32/true - # continue - e9/jump $emit-sib:loop/disp32 -$emit-sib:break: - # if (!has-sib?) return - 81 7/subop/compare 3/mod/direct 2/rm32/EDX . . . . . 0/imm32 # compare EDX - 74/jump-if-equal $emit-sib:end/disp8 -$emit-sib:calculate: - # sib/EBX = scale & 0b11 - 81 4/subop/and 3/mod/direct 3/rm32/EBX . . . . . 3/imm32/0b11 # bitwise and of EBX - # sib <<= 2 - c1/shift 4/subop/left 3/mod/direct 3/rm32/EBX . . . . . 2/imm8 # shift EBX left by 2 bits - # sib |= index & 0b111 - 81 4/subop/and 3/mod/direct 7/rm32/EDI . . . . . 7/imm32/0b111 # bitwise and of EDI - 09/or 3/mod/direct 3/rm32/EBX . . . 7/r32/EDI . . # EBX = bitwise OR with EDI - # sib <<= 3 - c1/shift 4/subop/left 3/mod/direct 3/rm32/EBX . . . . . 3/imm8 # shift EBX left by 3 bits - # sib |= base & 0b111 - 81 4/subop/and 3/mod/direct 6/rm32/ESI . . . . . 7/imm32/0b111 # bitwise and of ESI - 09/or 3/mod/direct 3/rm32/EBX . . . 6/r32/ESI . . # EBX = bitwise OR with ESI -$emit-sib:emit: - # emit-hex(out, sib, 1) - # . . push args - 68/push 1/imm32 - 53/push-EBX - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - # . . call - e8/call emit-hex/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -$emit-sib:end: - # . restore locals - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . restore registers - 5f/pop-to-EDI - 5e/pop-to-ESI - 5b/pop-to-EBX - 5a/pop-to-EDX - 59/pop-to-ECX - 58/pop-to-EAX - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -emit-disp: # line : (address stream byte), out : (address buffered-file) -> <void> - # pseudocode: - # rewind-stream(line) - # var word-slice = {0, 0} - # while true - # word-slice = next-word(line) - # if (slice-empty?(word-slice)) break - # if (slice-starts-with?(word-slice, "#")) break - # if has-metadata?(word-slice, "disp32") - # emit(out, word-slice, 4) - # break - # if has-metadata?(word-slice, "disp16") - # emit(out, word-slice, 2) - # break - # if has-metadata?(word-slice, "disp8") - # emit(out, word-slice, 1) - # break - # - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # . save registers - 50/push-EAX - 51/push-ECX - 52/push-EDX - # var word-slice/ECX = {0, 0} - 68/push 0/imm32/end - 68/push 0/imm32/start - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # rewind-stream(line) - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - # . . call - e8/call rewind-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -#? # dump line {{{ -#? # . write(2/stderr, "LL: ") -#? # . . push args -#? 68/push "LL: "/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write-stream(2/stderr, line) -#? # . . push args -#? ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write-stream/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write(2/stderr, "$\n") -#? # . . push args -#? 68/push "$\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # }}} -$emit-disp:loop: - # next-word(line, word-slice) - # . . push args - 51/push-ECX - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - # . . call - e8/call next-word/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # dump word-slice {{{ -#? # . write(2/stderr, "AA: ") -#? # . . push args -#? 68/push "AA: "/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . clear-stream(Stderr+4) -#? # . . push args -#? b8/copy-to-EAX Stderr/imm32 -#? 05/add-to-EAX 4/imm32 -#? 50/push-EAX -#? # . . call -#? e8/call clear-stream/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -#? # . write-slice-buffered(Stderr, word-slice) -#? # . . push args -#? 51/push-ECX -#? 68/push Stderr/imm32 -#? # . . call -#? e8/call write-slice-buffered/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . flush(Stderr) -#? # . . push args -#? 68/push Stderr/imm32 -#? # . . call -#? e8/call flush/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -#? # . write(2/stderr, "$\n") -#? # . . push args -#? 68/push "$\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # }}} -$emit-disp:check0: - # if (slice-empty?(word-slice)) break - # . EAX = slice-empty?(word-slice) - # . . push args - 51/push-ECX - # . . call - e8/call slice-empty?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . if (EAX != 0) pass through - 3d/compare-EAX-and 0/imm32 - 0f 85/jump-if-not-equal $emit-disp:break/disp32 -$emit-disp:check1: - # if (slice-starts-with?(word-slice, "#")) break - # . start/EDX = word-slice->start - 8b/copy 0/mod/indirect 1/rm32/ECX . . . 2/r32/EDX . . # copy *ECX to EDX - # . c/EAX = *start - 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX - 8a/copy-byte 0/mod/indirect 2/rm32/EDX . . . 0/r32/AL . . # copy byte at *EDX to AL - # . if (EAX == '#') break - 3d/compare-EAX-and 0x23/imm32/hash - 0f 84/jump-if-equal $emit-disp:break/disp32 -$emit-disp:check-for-disp32: - # if (has-metadata?(word-slice, "disp32")) - # . EAX = has-metadata?(ECX, "disp32") - # . . push args - 68/push "disp32"/imm32 - 51/push-ECX - # . . call - e8/call has-metadata?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . if (EAX == 0) goto next check - 3d/compare-EAX-and 0/imm32 - 74/jump-if-equal $emit-disp:check-for-disp16/disp8 -$emit-disp:disp32: - # emit(out, word-slice, 4) - # . . push args - 68/push 4/imm32 - 51/push-ECX - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - # . . call - e8/call emit/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # break - e9/jump $emit-disp:break/disp32 -$emit-disp:check-for-disp16: - # else if (has-metadata?(word-slice, "disp16")) - # . EAX = has-metadata?(ECX, "disp16") - # . . push args - 68/push "disp16"/imm32 - 51/push-ECX - # . . call - e8/call has-metadata?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . if (EAX == 0) goto next check - 3d/compare-EAX-and 0/imm32 - 74/jump-if-equal $emit-disp:check-for-disp8/disp8 -$emit-disp:disp16: - # emit(out, word-slice, 2) - # . . push args - 68/push 2/imm32 - 51/push-ECX - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - # . . call - e8/call emit/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # break - e9/jump $emit-disp:break/disp32 -$emit-disp:check-for-disp8: - # if (has-metadata?(word-slice, "disp8")) - # . EAX = has-metadata?(ECX, "disp8") - # . . push args - 68/push "disp8"/imm32 - 51/push-ECX - # . . call - e8/call has-metadata?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . if (EAX == 0) loop - 3d/compare-EAX-and 0/imm32 - 0f 84/jump-if-equal $emit-disp:loop/disp32 -$emit-disp:disp8: - # emit(out, word-slice, 1) - # . . push args - 68/push 1/imm32 - 51/push-ECX - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - # . . call - e8/call emit/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # break -$emit-disp:break: - # . restore locals - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . restore registers - 5a/pop-to-EDX - 59/pop-to-ECX - 58/pop-to-EAX - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -emit-imm: # line : (address stream byte), out : (address buffered-file) -> <void> - # pseudocode: - # rewind-stream(line) - # var word-slice = {0, 0} - # while true - # word-slice = next-word(line) - # if (slice-empty?(word-slice)) break - # if (slice-starts-with?(word-slice, "#")) break - # if has-metadata?(word-slice, "imm32") - # emit(out, word-slice, 4) - # break - # if has-metadata?(word-slice, "imm16") - # emit(out, word-slice, 2) - # break - # if has-metadata?(word-slice, "imm8") - # emit(out, word-slice, 1) - # break - # - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # . save registers - 50/push-EAX - 51/push-ECX - 52/push-EDX - # var word-slice/ECX = {0, 0} - 68/push 0/imm32/end - 68/push 0/imm32/start - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # rewind-stream(line) - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - # . . call - e8/call rewind-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -#? # dump line {{{ -#? # . write(2/stderr, "LL: ") -#? # . . push args -#? 68/push "LL: "/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write-stream(2/stderr, line) -#? # . . push args -#? ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write-stream/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write(2/stderr, "$\n") -#? # . . push args -#? 68/push "$\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # }}} -$emit-imm:loop: - # next-word(line, word-slice) - # . . push args - 51/push-ECX - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - # . . call - e8/call next-word/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # dump word-slice {{{ -#? # . write(2/stderr, "AA: ") -#? # . . push args -#? 68/push "AA: "/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . clear-stream(Stderr+4) -#? # . . push args -#? b8/copy-to-EAX Stderr/imm32 -#? 05/add-to-EAX 4/imm32 -#? 50/push-EAX -#? # . . call -#? e8/call clear-stream/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -#? # . write-slice-buffered(Stderr, word-slice) -#? # . . push args -#? 51/push-ECX -#? 68/push Stderr/imm32 -#? # . . call -#? e8/call write-slice-buffered/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . flush(Stderr) -#? # . . push args -#? 68/push Stderr/imm32 -#? # . . call -#? e8/call flush/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -#? # . write(2/stderr, "$\n") -#? # . . push args -#? 68/push "$\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # }}} -$emit-imm:check0: - # if (slice-empty?(word-slice)) break - # . EAX = slice-empty?(word-slice) - # . . push args - 51/push-ECX - # . . call - e8/call slice-empty?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . if (EAX != 0) pass through - 3d/compare-EAX-and 0/imm32 - 0f 85/jump-if-not-equal $emit-imm:break/disp32 -$emit-imm:check1: - # if (slice-starts-with?(word-slice, "#")) break - # . start/EDX = slice->start - 8b/copy 0/mod/indirect 1/rm32/ECX . . . 2/r32/EDX . . # copy *ECX to EDX - # . c/EAX = *start - 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX - 8a/copy-byte 0/mod/indirect 2/rm32/EDX . . . 0/r32/AL . . # copy byte at *EDX to AL - # . if (EAX == '#') break - 3d/compare-EAX-and 0x23/imm32/hash - 0f 84/jump-if-equal $emit-imm:break/disp32 -$emit-imm:check-for-imm32: - # if (has-metadata?(word-slice, "imm32")) - # . EAX = has-metadata?(ECX, "imm32") - # . . push args - 68/push "imm32"/imm32 - 51/push-ECX - # . . call - e8/call has-metadata?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . if (EAX == 0) goto next check - 3d/compare-EAX-and 0/imm32 - 74/jump-if-equal $emit-imm:check-for-imm16/disp8 -$emit-imm:imm32: - # emit(out, word-slice, 4) - # . . push args - 68/push 4/imm32 - 51/push-ECX - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - # . . call - e8/call emit/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # break - e9/jump $emit-imm:break/disp32 -$emit-imm:check-for-imm16: - # if (has-metadata?(word-slice, "imm16")) - # . EAX = has-metadata?(ECX, "imm16") - # . . push args - 68/push "imm16"/imm32 - 51/push-ECX - # . . call - e8/call has-metadata?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . if (EAX == 0) goto next check - 3d/compare-EAX-and 0/imm32 - 74/jump-if-equal $emit-imm:check-for-imm8/disp8 -$emit-imm:imm16: - # emit(out, word-slice, 2) - # . . push args - 68/push 2/imm32 - 51/push-ECX - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - # . . call - e8/call emit/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # break - e9/jump $emit-imm:break/disp32 -$emit-imm:check-for-imm8: - # if (has-metadata?(word-slice, "imm8")) - # . EAX = has-metadata?(ECX, "imm8") - # . . push args - 68/push "imm8"/imm32 - 51/push-ECX - # . . call - e8/call has-metadata?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . if (EAX == 0) loop - 3d/compare-EAX-and 0/imm32 - 0f 84/jump-if-equal $emit-imm:loop/disp32 -$emit-imm:imm8: - # emit(out, word-slice, 1) - # . . push args - 68/push 1/imm32 - 51/push-ECX - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - # . . call - e8/call emit/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # break -$emit-imm:break: - # . restore locals - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . restore registers - 5a/pop-to-EDX - 59/pop-to-ECX - 58/pop-to-EAX - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -emit-line-in-comment: # line : (address stream byte), out : (address buffered-file) -> <void> - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # write-buffered(out, " # ") - # . . push args - 68/push " # "/imm32 - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - # . . call - e8/call write-buffered/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # write-stream-data(out, line) - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - # . . call - e8/call write-stream-data/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -$emit-line-in-comment:end: - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-convert-instruction-passes-comments-through: - # if a line starts with '#', pass it along unchanged - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # setup - # . clear-stream(_test-input-stream) - # . . push args - 68/push _test-input-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-stream) - # . . push args - 68/push _test-output-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-buffered-file+4) - # . . push args - b8/copy-to-EAX _test-output-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # initialize input - # . write(_test-input-stream, "# abcd") - # . . push args - 68/push "# abcd"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # convert-instruction(_test-input-stream, _test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call convert-instruction/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check that the line just passed through - # . flush(_test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - # . . call - e8/call flush/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . check-stream-equal(_test-output-stream, "# abcd", msg) - # . . push args - 68/push "F - test-convert-instruction-passes-comments-through"/imm32 - 68/push "# abcd"/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-stream-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-convert-instruction-passes-labels-through: - # if the first word ends with ':', pass along the entire line unchanged - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # setup - # . clear-stream(_test-input-stream) - # . . push args - 68/push _test-input-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-stream) - # . . push args - 68/push _test-output-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-buffered-file+4) - # . . push args - b8/copy-to-EAX _test-output-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # initialize input - # . write(_test-input-stream, "ab: # cd") - # . . push args - 68/push "ab: # cd"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # convert-instruction(_test-input-stream, _test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call convert-instruction/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check that the line just passed through - # . flush(_test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - # . . call - e8/call flush/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . check-stream-equal(_test-output-stream, "ab: # cd", msg) - # . . push args - 68/push "F - test-convert-instruction-passes-labels-through"/imm32 - 68/push "ab: # cd"/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-stream-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-convert-instruction-handles-single-opcode: - # if the instruction consists of a single opcode, strip its metadata and pass it along - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # setup - # . clear-stream(_test-input-stream) - # . . push args - 68/push _test-input-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-stream) - # . . push args - 68/push _test-output-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-buffered-file+4) - # . . push args - b8/copy-to-EAX _test-output-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # initialize input - # . write(_test-input-stream, "ab/cd # comment") - # . . push args - 68/push "ab/cd # comment"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # convert-instruction(_test-input-stream, _test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call convert-instruction/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check output - # . flush(_test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - # . . call - e8/call flush/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -#? # dump output {{{ -#? # . write(2/stderr, "^") -#? # . . push args -#? 68/push "^"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write-stream(2/stderr, _test-output-stream) -#? # . . push args -#? 68/push _test-output-stream/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write-stream/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write(2/stderr, "$\n") -#? # . . push args -#? 68/push "$\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # }}} - # . check-stream-equal(_test-output-stream, "ab # ab/cd # comment", msg) - # . . push args - 68/push "F - test-convert-instruction-handles-single-opcode"/imm32 - 68/push "ab # ab/cd # comment"/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-stream-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-convert-instruction-handles-0f-opcode: - # if the instruction starts with 0f opcode, include a second opcode - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # setup - # . clear-stream(_test-input-stream) - # . . push args - 68/push _test-input-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-stream) - # . . push args - 68/push _test-output-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-buffered-file+4) - # . . push args - b8/copy-to-EAX _test-output-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # initialize input - # . write(_test-input-stream, "0f/m1 ab/m2 # comment") - # . . push args - 68/push "0f/m1 ab/m2 # comment"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # convert-instruction(_test-input-stream, _test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call convert-instruction/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check output - # . flush(_test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - # . . call - e8/call flush/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -#? # dump output {{{ -#? # . write(2/stderr, "^") -#? # . . push args -#? 68/push "^"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write-stream(2/stderr, _test-output-stream) -#? # . . push args -#? 68/push _test-output-stream/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write-stream/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write(2/stderr, "$\n") -#? # . . push args -#? 68/push "$\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # }}} - # . check-stream-equal(_test-output-stream, "0f ab # 0f/m1 ab/m2 # comment", msg) - # . . push args - 68/push "F - test-convert-instruction-handles-0f-opcode"/imm32 - 68/push "0f ab # 0f/m1 ab/m2 # comment"/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-stream-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-convert-instruction-handles-f2-opcode: - # if the instruction starts with f2 opcode, include a second opcode - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # setup - # . clear-stream(_test-input-stream) - # . . push args - 68/push _test-input-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-stream) - # . . push args - 68/push _test-output-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-buffered-file+4) - # . . push args - b8/copy-to-EAX _test-output-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # initialize input - # . write(_test-input-stream, "f2/m1 ab/m2 # comment") - # . . push args - 68/push "f2/m1 ab/m2 # comment"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # convert-instruction(_test-input-stream, _test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call convert-instruction/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check output - # . flush(_test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - # . . call - e8/call flush/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -#? # dump output {{{ -#? # . write(2/stderr, "^") -#? # . . push args -#? 68/push "^"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write-stream(2/stderr, _test-output-stream) -#? # . . push args -#? 68/push _test-output-stream/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write-stream/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write(2/stderr, "$\n") -#? # . . push args -#? 68/push "$\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # }}} - # . check-stream-equal(_test-output-stream, "f2 ab # f2/m1 ab/m2 # comment", msg) - # . . push args - 68/push "F - test-convert-instruction-handles-f2-opcode"/imm32 - 68/push "f2 ab # f2/m1 ab/m2 # comment"/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-stream-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-convert-instruction-handles-f3-opcode: - # if the instruction starts with f3 opcode, include a second opcode - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # setup - # . clear-stream(_test-input-stream) - # . . push args - 68/push _test-input-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-stream) - # . . push args - 68/push _test-output-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-buffered-file+4) - # . . push args - b8/copy-to-EAX _test-output-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # initialize input - # . write(_test-input-stream, "f3/m1 ab/m2 # comment") - # . . push args - 68/push "f3/m1 ab/m2 # comment"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # convert-instruction(_test-input-stream, _test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call convert-instruction/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check output - # . flush(_test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - # . . call - e8/call flush/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -#? # dump output {{{ -#? # . write(2/stderr, "^") -#? # . . push args -#? 68/push "^"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write-stream(2/stderr, _test-output-stream) -#? # . . push args -#? 68/push _test-output-stream/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write-stream/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write(2/stderr, "$\n") -#? # . . push args -#? 68/push "$\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # }}} - # . check-stream-equal(_test-output-stream, "f3 ab # f3/m1 ab/m2 # comment", msg) - # . . push args - 68/push "F - test-convert-instruction-handles-f3-opcode"/imm32 - 68/push "f3 ab # f3/m1 ab/m2 # comment"/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-stream-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-convert-instruction-handles-f2-0f-opcode: - # if the instruction starts with f2 0f opcode, include a second opcode - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # setup - # . clear-stream(_test-input-stream) - # . . push args - 68/push _test-input-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-stream) - # . . push args - 68/push _test-output-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-buffered-file+4) - # . . push args - b8/copy-to-EAX _test-output-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # initialize input - # . write(_test-input-stream, "f2/m1 0f/m2 ab/m3 # comment") - # . . push args - 68/push "f2/m1 0f/m2 ab/m3 # comment"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # convert-instruction(_test-input-stream, _test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call convert-instruction/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check output - # . flush(_test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - # . . call - e8/call flush/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -#? # dump output {{{ -#? # . write(2/stderr, "^") -#? # . . push args -#? 68/push "^"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write-stream(2/stderr, _test-output-stream) -#? # . . push args -#? 68/push _test-output-stream/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write-stream/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write(2/stderr, "$\n") -#? # . . push args -#? 68/push "$\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # }}} - # . check-stream-equal(_test-output-stream, "f2 0f ab # f2/m1 0f/m2 ab/m3 # comment", msg) - # . . push args - 68/push "F - test-convert-instruction-handles-f2-0f-opcode"/imm32 - 68/push "f2 0f ab # f2/m1 0f/m2 ab/m3 # comment"/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-stream-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-convert-instruction-handles-f3-0f-opcode: - # if the instruction starts with f3 0f opcode, include a second opcode - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # setup - # . clear-stream(_test-input-stream) - # . . push args - 68/push _test-input-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-stream) - # . . push args - 68/push _test-output-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-buffered-file+4) - # . . push args - b8/copy-to-EAX _test-output-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # initialize input - # . write(_test-input-stream, "f3/m1 0f/m2 ab/m3 # comment") - # . . push args - 68/push "f3/m1 0f/m2 ab/m3 # comment"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # convert-instruction(_test-input-stream, _test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call convert-instruction/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check output - # . flush(_test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - # . . call - e8/call flush/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -#? # dump output {{{ -#? # . write(2/stderr, "^") -#? # . . push args -#? 68/push "^"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write-stream(2/stderr, _test-output-stream) -#? # . . push args -#? 68/push _test-output-stream/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write-stream/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write(2/stderr, "$\n") -#? # . . push args -#? 68/push "$\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # }}} - # . check-stream-equal(_test-output-stream, "f3 0f ab # f3/m1 0f/m2 ab/m3 # comment", msg) - # . . push args - 68/push "F - test-convert-instruction-handles-f3-0f-opcode"/imm32 - 68/push "f3 0f ab # f3/m1 0f/m2 ab/m3 # comment"/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-stream-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-convert-instruction-handles-unused-opcodes: - # if the instruction doesn't start with f2, f3 or 0f, don't include other opcodes - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # setup - # . clear-stream(_test-input-stream) - # . . push args - 68/push _test-input-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-stream) - # . . push args - 68/push _test-output-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-buffered-file+4) - # . . push args - b8/copy-to-EAX _test-output-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # initialize input - # . write(_test-input-stream, "ab/m1 cd/m2 # comment") - # . . push args - 68/push "ab/m1 cd/m2 # comment"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # convert-instruction(_test-input-stream, _test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call convert-instruction/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check output - # . flush(_test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - # . . call - e8/call flush/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -#? # dump output {{{ -#? # . write(2/stderr, "^") -#? # . . push args -#? 68/push "^"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write-stream(2/stderr, _test-output-stream) -#? # . . push args -#? 68/push _test-output-stream/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write-stream/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write(2/stderr, "$\n") -#? # . . push args -#? 68/push "$\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # }}} - # . check-stream-equal(_test-output-stream, "ab # f3/m1 0f/m2 ab/m3 # comment", msg) - # . . push args - 68/push "F - test-convert-instruction-handles-unused-opcodes"/imm32 - 68/push "ab # ab/m1 cd/m2 # comment"/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-stream-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-convert-instruction-handles-unused-second-opcodes: - # if the second opcode isn't 0f, don't include further opcodes - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # setup - # . clear-stream(_test-input-stream) - # . . push args - 68/push _test-input-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-stream) - # . . push args - 68/push _test-output-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-buffered-file+4) - # . . push args - b8/copy-to-EAX _test-output-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # initialize input - # . write(_test-input-stream, "f2/m1 ab/m2 cd/m3 # comment") - # . . push args - 68/push "f2/m1 ab/m2 cd/m3 # comment"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # convert-instruction(_test-input-stream, _test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call convert-instruction/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check output - # . flush(_test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - # . . call - e8/call flush/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -#? # dump output {{{ -#? # . write(2/stderr, "^") -#? # . . push args -#? 68/push "^"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write-stream(2/stderr, _test-output-stream) -#? # . . push args -#? 68/push _test-output-stream/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write-stream/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write(2/stderr, "$\n") -#? # . . push args -#? 68/push "$\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # }}} - # . check-stream-equal(_test-output-stream, "f2 ab # f2/m1 ab/m2 cd/m3 # comment", msg) - # . . push args - 68/push "F - test-convert-instruction-handles-unused-second-opcodes"/imm32 - 68/push "f2 ab # f2/m1 ab/m2 cd/m3 # comment"/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-stream-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-convert-instruction-handles-unused-second-opcodes-2: - # if the second opcode isn't 0f, don't include further opcodes - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # setup - # . clear-stream(_test-input-stream) - # . . push args - 68/push _test-input-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-stream) - # . . push args - 68/push _test-output-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-buffered-file+4) - # . . push args - b8/copy-to-EAX _test-output-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # initialize input - # . write(_test-input-stream, "f3/m1 ab/m2 cd/m3 # comment") - # . . push args - 68/push "f3/m1 ab/m2 cd/m3 # comment"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # convert-instruction(_test-input-stream, _test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call convert-instruction/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check output - # . flush(_test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - # . . call - e8/call flush/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -#? # dump output {{{ -#? # . write(2/stderr, "^") -#? # . . push args -#? 68/push "^"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write-stream(2/stderr, _test-output-stream) -#? # . . push args -#? 68/push _test-output-stream/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write-stream/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write(2/stderr, "$\n") -#? # . . push args -#? 68/push "$\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # }}} - # . check-stream-equal(_test-output-stream, "f3 ab # f3/m1 ab/m2 cd/m3 # comment", msg) - # . . push args - 68/push "F - test-convert-instruction-handles-unused-second-opcodes"/imm32 - 68/push "f3 ab # f3/m1 ab/m2 cd/m3 # comment"/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-stream-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-convert-instruction-emits-modrm-byte: - # pack mod, rm32 and r32 operands into ModR/M byte - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # setup - # . clear-stream(_test-input-stream) - # . . push args - 68/push _test-input-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-stream) - # . . push args - 68/push _test-output-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-buffered-file+4) - # . . push args - b8/copy-to-EAX _test-output-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # initialize input - # . write(_test-input-stream, "8b/copy 0/mod 0/rm32 1/r32") - # . . push args - 68/push "8b/copy 0/mod 0/rm32 1/r32"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # convert-instruction(_test-input-stream, _test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call convert-instruction/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check output - # . flush(_test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - # . . call - e8/call flush/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -#? # dump output {{{ -#? # . write(2/stderr, "^") -#? # . . push args -#? 68/push "^"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write-stream(2/stderr, _test-output-stream) -#? # . . push args -#? 68/push _test-output-stream/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write-stream/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write(2/stderr, "$\n") -#? # . . push args -#? 68/push "$\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # }}} - # . check-stream-equal(_test-output-stream, "8b 08 # 8b/copy 0/mod 0/rm32 1/r32", msg) - # . . push args - 68/push "F - test-convert-instruction-emits-modrm-byte"/imm32 - 68/push "8b 08 # 8b/copy 0/mod 0/rm32 1/r32"/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-stream-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-convert-instruction-emits-modrm-byte-with-non-zero-mod: - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # setup - # . clear-stream(_test-input-stream) - # . . push args - 68/push _test-input-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-stream) - # . . push args - 68/push _test-output-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-buffered-file+4) - # . . push args - b8/copy-to-EAX _test-output-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # initialize input - # . write(_test-input-stream, "01/add 3/mod/direct 3/rm32/EBX 1/r32/ECX") - # . . push args - 68/push "01/add 3/mod/direct 3/rm32/EBX 1/r32/ECX"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # convert-instruction(_test-input-stream, _test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call convert-instruction/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . flush(_test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - # . . call - e8/call flush/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -#? # dump output {{{ -#? # . write(2/stderr, "out: ") -#? # . . push args -#? 68/push "out: "/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # write-stream(2/stderr, _test-output-stream) -#? # . . push args -#? 68/push _test-output-stream/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write-stream/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write(2/stderr, "$\n") -#? # . . push args -#? 68/push "$\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # }}} - # check output - # . check-stream-equal(_test-output-stream, "# abcd", msg) - # . . push args - 68/push "F - test-convert-instruction-foo"/imm32 - 68/push "01 cb # 01/add 3/mod/direct 3/rm32/EBX 1/r32/ECX"/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-stream-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-convert-instruction-emits-modrm-byte-from-subop: - # pack mod, rm32 and subop operands into ModR/M byte - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # setup - # . clear-stream(_test-input-stream) - # . . push args - 68/push _test-input-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-stream) - # . . push args - 68/push _test-output-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-buffered-file+4) - # . . push args - b8/copy-to-EAX _test-output-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # initialize input - # . write(_test-input-stream, "ff 6/subop/push 0/mod 0/rm32") - # . . push args - 68/push "ff 6/subop/push 0/mod 0/rm32"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # convert-instruction(_test-input-stream, _test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call convert-instruction/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check output - # . flush(_test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - # . . call - e8/call flush/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -#? # dump output {{{ -#? # . write(2/stderr, "^") -#? # . . push args -#? 68/push "^"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write-stream(2/stderr, _test-output-stream) -#? # . . push args -#? 68/push _test-output-stream/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write-stream/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write(2/stderr, "$\n") -#? # . . push args -#? 68/push "$\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # }}} - # . check-stream-equal(_test-output-stream, "ff 30 # ff 6/subop/push 0/mod 0/rm32", msg) - # . . push args - 68/push "F - test-convert-instruction-emits-modrm-byte-from-subop"/imm32 - 68/push "ff 30 # ff 6/subop/push 0/mod 0/rm32"/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-stream-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-convert-instruction-emits-modrm-byte-with-missing-mod: - # pack rm32 and r32 operands into ModR/M byte - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # setup - # . clear-stream(_test-input-stream) - # . . push args - 68/push _test-input-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-stream) - # . . push args - 68/push _test-output-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-buffered-file+4) - # . . push args - b8/copy-to-EAX _test-output-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # initialize input - # . write(_test-input-stream, "8b/copy 0/rm32 1/r32") - # . . push args - 68/push "8b/copy 0/rm32 1/r32"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # convert-instruction(_test-input-stream, _test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call convert-instruction/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check output - # . flush(_test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - # . . call - e8/call flush/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -#? # dump output {{{ -#? # . write(2/stderr, "^") -#? # . . push args -#? 68/push "^"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write-stream(2/stderr, _test-output-stream) -#? # . . push args -#? 68/push _test-output-stream/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write-stream/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write(2/stderr, "$\n") -#? # . . push args -#? 68/push "$\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # }}} - # . check-stream-equal(_test-output-stream, "8b 08 # 8b/copy 0/rm32 1/r32", msg) - # . . push args - 68/push "F - test-convert-instruction-emits-modrm-byte-with-missing-mod"/imm32 - 68/push "8b 08 # 8b/copy 0/rm32 1/r32"/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-stream-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-convert-instruction-emits-modrm-byte-with-missing-rm32: - # pack mod and r32 operands into ModR/M byte - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # setup - # . clear-stream(_test-input-stream) - # . . push args - 68/push _test-input-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-stream) - # . . push args - 68/push _test-output-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-buffered-file+4) - # . . push args - b8/copy-to-EAX _test-output-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # initialize input - # . write(_test-input-stream, "8b/copy 0/mod 1/r32") - # . . push args - 68/push "8b/copy 0/mod 1/r32"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # convert-instruction(_test-input-stream, _test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call convert-instruction/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check output - # . flush(_test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - # . . call - e8/call flush/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -#? # dump output {{{ -#? # . write(2/stderr, "^") -#? # . . push args -#? 68/push "^"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write-stream(2/stderr, _test-output-stream) -#? # . . push args -#? 68/push _test-output-stream/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write-stream/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write(2/stderr, "$\n") -#? # . . push args -#? 68/push "$\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # }}} - # . check-stream-equal(_test-output-stream, "8b 08 # 8b/copy 0/mod 1/r32", msg) - # . . push args - 68/push "F - test-convert-instruction-emits-modrm-byte-with-missing-rm32"/imm32 - 68/push "8b 08 # 8b/copy 0/mod 1/r32"/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-stream-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-convert-instruction-emits-modrm-byte-with-missing-r32: - # pack mod and rm32 operands into ModR/M byte - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # setup - # . clear-stream(_test-input-stream) - # . . push args - 68/push _test-input-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-stream) - # . . push args - 68/push _test-output-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-buffered-file+4) - # . . push args - b8/copy-to-EAX _test-output-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # initialize input - # . write(_test-input-stream, "8b/copy 0/mod 0/rm32") - # . . push args - 68/push "8b/copy 0/mod 0/rm32"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # convert-instruction(_test-input-stream, _test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call convert-instruction/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check output - # . flush(_test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - # . . call - e8/call flush/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -#? # dump output {{{ -#? # . write(2/stderr, "^") -#? # . . push args -#? 68/push "^"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write-stream(2/stderr, _test-output-stream) -#? # . . push args -#? 68/push _test-output-stream/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write-stream/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write(2/stderr, "$\n") -#? # . . push args -#? 68/push "$\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # }}} - # . check-stream-equal(_test-output-stream, "8b 00 # 8b/copy 0/mod 0/rm32", msg) - # . . push args - 68/push "F - test-convert-instruction-emits-modrm-byte-with-missing-r32"/imm32 - 68/push "8b 00 # 8b/copy 0/mod 0/rm32"/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-stream-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-convert-instruction-emits-sib-byte: - # pack base, index and scale operands into SIB byte - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # setup - # . clear-stream(_test-input-stream) - # . . push args - 68/push _test-input-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-stream) - # . . push args - 68/push _test-output-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-buffered-file+4) - # . . push args - b8/copy-to-EAX _test-output-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # initialize input - # . write(_test-input-stream, "8b/copy 0/mod 4/rm32 1/r32 0/base 1/index 0/scale") - # . . push args - 68/push "8b/copy 0/mod 4/rm32 1/r32 0/base 1/index 0/scale"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # convert-instruction(_test-input-stream, _test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call convert-instruction/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check output - # . flush(_test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - # . . call - e8/call flush/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -#? # dump output {{{ -#? # . write(2/stderr, "^") -#? # . . push args -#? 68/push "^"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write-stream(2/stderr, _test-output-stream) -#? # . . push args -#? 68/push _test-output-stream/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write-stream/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write(2/stderr, "$\n") -#? # . . push args -#? 68/push "$\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # }}} - # . check-stream-equal(_test-output-stream, "8b 08 # 8b/copy 0/mod 4/rm32 1/r32 0/base 1/index 0/scale", msg) - # . . push args - 68/push "F - test-convert-instruction-emits-sib-byte"/imm32 - 68/push "8b 0c 08 # 8b/copy 0/mod 4/rm32 1/r32 0/base 1/index 0/scale"/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-stream-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-convert-instruction-emits-sib-byte-with-missing-base: - # pack index and scale operands into SIB byte - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # setup - # . clear-stream(_test-input-stream) - # . . push args - 68/push _test-input-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-stream) - # . . push args - 68/push _test-output-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-buffered-file+4) - # . . push args - b8/copy-to-EAX _test-output-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # initialize input - # . write(_test-input-stream, "8b/copy 0/mod 4/rm32 1/r32 1/index 0/scale") - # . . push args - 68/push "8b/copy 0/mod 4/rm32 1/r32 1/index 0/scale"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # convert-instruction(_test-input-stream, _test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call convert-instruction/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check output - # . flush(_test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - # . . call - e8/call flush/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -#? # dump output {{{ -#? # . write(2/stderr, "^") -#? # . . push args -#? 68/push "^"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write-stream(2/stderr, _test-output-stream) -#? # . . push args -#? 68/push _test-output-stream/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write-stream/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write(2/stderr, "$\n") -#? # . . push args -#? 68/push "$\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # }}} - # . check-stream-equal(_test-output-stream, "8b 0c 08 # 8b/copy 0/mod 4/rm32 1/r32 1/index 0/scale", msg) - # . . push args - 68/push "F - test-convert-instruction-emits-sib-byte-with-missing-base"/imm32 - 68/push "8b 0c 08 # 8b/copy 0/mod 4/rm32 1/r32 1/index 0/scale"/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-stream-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-convert-instruction-emits-sib-byte-with-missing-index: - # pack base and scale operands into SIB byte - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # setup - # . clear-stream(_test-input-stream) - # . . push args - 68/push _test-input-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-stream) - # . . push args - 68/push _test-output-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-buffered-file+4) - # . . push args - b8/copy-to-EAX _test-output-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # initialize input - # . write(_test-input-stream, "8b/copy 0/mod 4/rm32 1/r32 0/base 0/scale") - # . . push args - 68/push "8b/copy 0/mod 4/rm32 1/r32 0/base 0/scale"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # convert-instruction(_test-input-stream, _test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call convert-instruction/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check output - # . flush(_test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - # . . call - e8/call flush/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -#? # dump output {{{ -#? # . write(2/stderr, "^") -#? # . . push args -#? 68/push "^"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write-stream(2/stderr, _test-output-stream) -#? # . . push args -#? 68/push _test-output-stream/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write-stream/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write(2/stderr, "$\n") -#? # . . push args -#? 68/push "$\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # }}} - # . check-stream-equal(_test-output-stream, "8b 0c 08 # 8b/copy 0/mod 4/rm32 1/r32 0/base 0/scale", msg) - # . . push args - 68/push "F - test-convert-instruction-emits-sib-byte-with-missing-index"/imm32 - 68/push "8b 0c 00 # 8b/copy 0/mod 4/rm32 1/r32 0/base 0/scale"/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-stream-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-convert-instruction-emits-sib-byte-with-missing-scale: - # pack base and index operands into SIB byte - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # setup - # . clear-stream(_test-input-stream) - # . . push args - 68/push _test-input-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-stream) - # . . push args - 68/push _test-output-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-buffered-file+4) - # . . push args - b8/copy-to-EAX _test-output-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # initialize input - # . write(_test-input-stream, "8b/copy 0/mod 4/rm32 1/r32 0/base 1/index") - # . . push args - 68/push "8b/copy 0/mod 4/rm32 1/r32 0/base 1/index"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # convert-instruction(_test-input-stream, _test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call convert-instruction/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check output - # . flush(_test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - # . . call - e8/call flush/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -#? # dump output {{{ -#? # . write(2/stderr, "^") -#? # . . push args -#? 68/push "^"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write-stream(2/stderr, _test-output-stream) -#? # . . push args -#? 68/push _test-output-stream/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write-stream/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write(2/stderr, "$\n") -#? # . . push args -#? 68/push "$\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # }}} - # . check-stream-equal(_test-output-stream, "8b 0c 08 # 8b/copy 0/mod 4/rm32 1/r32 0/base 1/index", msg) - # . . push args - 68/push "F - test-convert-instruction-emits-sib-byte-with-missing-scale"/imm32 - 68/push "8b 0c 08 # 8b/copy 0/mod 4/rm32 1/r32 0/base 1/index"/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-stream-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-convert-instruction-handles-disp32-operand: - # expand /disp32 operand into 4 bytes - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # setup - # . clear-stream(_test-input-stream) - # . . push args - 68/push _test-input-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-stream) - # . . push args - 68/push _test-output-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-buffered-file+4) - # . . push args - b8/copy-to-EAX _test-output-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # initialize input - # . write(_test-input-stream, "e8/call 20/disp32") - # . . push args - 68/push "e8/call 20/disp32"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # convert-instruction(_test-input-stream, _test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call convert-instruction/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check output - # . flush(_test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - # . . call - e8/call flush/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -#? # dump output {{{ -#? # . write(2/stderr, "^") -#? # . . push args -#? 68/push "^"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write-stream(2/stderr, _test-output-stream) -#? # . . push args -#? 68/push _test-output-stream/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write-stream/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write(2/stderr, "$\n") -#? # . . push args -#? 68/push "$\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # }}} - # . check-stream-equal(_test-output-stream, "e8 20 00 00 00 # e8/call 20/disp32", msg) - # . . push args - 68/push "F - test-convert-instruction-handles-disp32-operand"/imm32 - 68/push "e8 20 00 00 00 # e8/call 20/disp32"/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-stream-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-convert-instruction-handles-disp16-operand: - # expand /disp16 operand into 2 bytes - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # setup - # . clear-stream(_test-input-stream) - # . . push args - 68/push _test-input-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-stream) - # . . push args - 68/push _test-output-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-buffered-file+4) - # . . push args - b8/copy-to-EAX _test-output-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # initialize input - # . write(_test-input-stream, "e8/call 20/disp16") - # . . push args - 68/push "e8/call 20/disp16"/imm32 # not a valid instruction - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # convert-instruction(_test-input-stream, _test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call convert-instruction/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check output - # . flush(_test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - # . . call - e8/call flush/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -#? # dump output {{{ -#? # . write(2/stderr, "^") -#? # . . push args -#? 68/push "^"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write-stream(2/stderr, _test-output-stream) -#? # . . push args -#? 68/push _test-output-stream/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write-stream/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write(2/stderr, "$\n") -#? # . . push args -#? 68/push "$\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # }}} - # . check-stream-equal(_test-output-stream, "e8 20 00 # e8/call 20/disp16", msg) - # . . push args - 68/push "F - test-convert-instruction-handles-disp16-operand"/imm32 - 68/push "e8 20 00 # e8/call 20/disp16"/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-stream-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-convert-instruction-handles-disp8-operand: - # expand /disp8 operand into 1 byte - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # setup - # . clear-stream(_test-input-stream) - # . . push args - 68/push _test-input-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-stream) - # . . push args - 68/push _test-output-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-buffered-file+4) - # . . push args - b8/copy-to-EAX _test-output-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # initialize input - # . write(_test-input-stream, "eb/jump 20/disp8") - # . . push args - 68/push "eb/jump 20/disp8"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # convert-instruction(_test-input-stream, _test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call convert-instruction/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check output - # . flush(_test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - # . . call - e8/call flush/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -#? # dump output {{{ -#? # . write(2/stderr, "^") -#? # . . push args -#? 68/push "^"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write-stream(2/stderr, _test-output-stream) -#? # . . push args -#? 68/push _test-output-stream/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write-stream/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write(2/stderr, "$\n") -#? # . . push args -#? 68/push "$\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # }}} - # . check-stream-equal(_test-output-stream, "eb 20 # eb/jump 20/disp8", msg) - # . . push args - 68/push "F - test-convert-instruction-handles-disp8-operand"/imm32 - 68/push "eb 20 # eb/jump 20/disp8"/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-stream-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-convert-instruction-handles-disp8-name: - # pass /disp8 name directly through - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # setup - # . clear-stream(_test-input-stream) - # . . push args - 68/push _test-input-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-stream) - # . . push args - 68/push _test-output-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-buffered-file+4) - # . . push args - b8/copy-to-EAX _test-output-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # initialize input - # . write(_test-input-stream, "eb/jump xyz/disp8") - # . . push args - 68/push "eb/jump xyz/disp8"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # convert-instruction(_test-input-stream, _test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call convert-instruction/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check output - # . flush(_test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - # . . call - e8/call flush/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -#? # dump output {{{ -#? # . write(2/stderr, "^") -#? # . . push args -#? 68/push "^"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write-stream(2/stderr, _test-output-stream) -#? # . . push args -#? 68/push _test-output-stream/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write-stream/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write(2/stderr, "$\n") -#? # . . push args -#? 68/push "$\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # }}} - # . check-stream-equal(_test-output-stream, "eb xyz/disp8 # eb/jump xyz/disp8", msg) - # . . push args - 68/push "F - test-convert-instruction-handles-disp8-name"/imm32 - 68/push "eb xyz/disp8 # eb/jump xyz/disp8"/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-stream-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-convert-instruction-handles-imm32-operand: - # expand /imm32 operand into 4 bytes - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # setup - # . clear-stream(_test-input-stream) - # . . push args - 68/push _test-input-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-stream) - # . . push args - 68/push _test-output-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-buffered-file+4) - # . . push args - b8/copy-to-EAX _test-output-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # initialize input - # . write(_test-input-stream, "68/push 0x20/imm32") - # . . push args - 68/push "68/push 0x20/imm32"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # convert-instruction(_test-input-stream, _test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call convert-instruction/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check output - # . flush(_test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - # . . call - e8/call flush/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -#? # dump output {{{ -#? # . write(2/stderr, "^") -#? # . . push args -#? 68/push "^"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write-stream(2/stderr, _test-output-stream) -#? # . . push args -#? 68/push _test-output-stream/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write-stream/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write(2/stderr, "$\n") -#? # . . push args -#? 68/push "$\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # }}} - # . check-stream-equal(_test-output-stream, "68 20 00 00 00 # 68/push 0x20/imm32", msg) - # . . push args - 68/push "F - test-convert-instruction-handles-imm32-operand"/imm32 - 68/push "68 20 00 00 00 # 68/push 0x20/imm32"/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-stream-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-convert-instruction-handles-imm16-operand: - # expand /imm16 operand into 2 bytes - # we don't have one of these at the moment, so this expands to an invalid instruction - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # setup - # . clear-stream(_test-input-stream) - # . . push args - 68/push _test-input-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-stream) - # . . push args - 68/push _test-output-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-buffered-file+4) - # . . push args - b8/copy-to-EAX _test-output-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # initialize input - # . write(_test-input-stream, "68/push 0x20/imm16") - # . . push args - 68/push "68/push 0x20/imm16"/imm32 # not a valid instruction - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # convert-instruction(_test-input-stream, _test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call convert-instruction/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check output - # . flush(_test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - # . . call - e8/call flush/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -#? # dump output {{{ -#? # . write(2/stderr, "^") -#? # . . push args -#? 68/push "^"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write-stream(2/stderr, _test-output-stream) -#? # . . push args -#? 68/push _test-output-stream/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write-stream/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write(2/stderr, "$\n") -#? # . . push args -#? 68/push "$\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # }}} - # . check-stream-equal(_test-output-stream, "68 20 00 # 68/push 0x20/imm16", msg) - # . . push args - 68/push "F - test-convert-instruction-handles-imm16-operand"/imm32 - 68/push "68 20 00 # 68/push 0x20/imm16"/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-stream-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-convert-instruction-handles-imm8-operand: - # expand /imm8 operand into 1 byte - # we don't have one of these at the moment, so this expands to an invalid instruction - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # setup - # . clear-stream(_test-input-stream) - # . . push args - 68/push _test-input-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-stream) - # . . push args - 68/push _test-output-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-buffered-file+4) - # . . push args - b8/copy-to-EAX _test-output-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # initialize input - # . write(_test-input-stream, "68/push 0x20/imm8") - # . . push args - 68/push "68/push 0x20/imm8"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # convert-instruction(_test-input-stream, _test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call convert-instruction/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check output - # . flush(_test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - # . . call - e8/call flush/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -#? # dump output {{{ -#? # . write(2/stderr, "^") -#? # . . push args -#? 68/push "^"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write-stream(2/stderr, _test-output-stream) -#? # . . push args -#? 68/push _test-output-stream/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write-stream/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write(2/stderr, "$\n") -#? # . . push args -#? 68/push "$\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # }}} - # . check-stream-equal(_test-output-stream, "68 20 # 68/push 0x20/imm8", msg) - # . . push args - 68/push "F - test-convert-instruction-handles-imm8-operand"/imm32 - 68/push "68 20 # 68/push 0x20/imm8"/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-stream-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -# shortcut for parse-hex-int(next-token-from-slice(word->start, word->end, '/')) -parse-datum-of-word: # word : (address slice) -> value/EAX - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # . save registers - 51/push-ECX - 56/push-ESI - # ESI = word - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 6/r32/ESI 8/disp8 . # copy *(EBP+8) to ESI - # var slice/ECX = {0, 0} - 68/push 0/imm32/end - 68/push 0/imm32/start - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # slice = next-token-from-slice(word->start, word->end, '/') - # . . push args - 51/push-ECX - 68/push 0x2f/imm32/slash - ff 6/subop/push 1/mod/*+disp8 6/rm32/ESI . . . . 4/disp8 . # push *(ESI+4) - ff 6/subop/push 0/mod/indirect 6/rm32/ESI . . . . . . # push *ESI - # . . call - e8/call next-token-from-slice/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP - # value/EAX = parse-hex-int(slice) - # . . push args - 51/push-ECX - # . . call - e8/call parse-hex-int/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -$parse-datum-of-word:end: - # . reclaim locals - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . restore registers - 5e/pop-to-ESI - 59/pop-to-ECX - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -# . . vim:nowrap:textwidth=0 diff --git a/subx/apps/subx-common.subx b/subx/apps/subx-common.subx deleted file mode 100644 index abb223fb..00000000 --- a/subx/apps/subx-common.subx +++ /dev/null @@ -1,3118 +0,0 @@ -# common helpers shared by phases of the SubX translator - -# - some limits on the programs we can translate -== data - -# maximum memory available for allocation -Heap-size: - 0x200000/imm32/2MB - -# maximum size of a single segment -Segment-size: - 0x80000/imm32/512KB - -# maximum size of input textual stream (spanning all segments) -Input-size: - 0x100000/imm32/1MB - -# maximum size of the 'labels' table in survey.subx -Max-labels: - 0x10000/imm32/4K-labels/64KB - -== code -# instruction effective address register displacement immediate -# . op subop mod rm32 base index scale r32 -# . 1-3 bytes 3 bits 2 bits 3 bits 3 bits 3 bits 2 bits 2 bits 0/1/2/4 bytes 0/1/2/4 bytes - -# - managing tables -# SubX has rudimentary support for tables. -# -# Each table is a stream of rows. -# -# Each row consists of a 4-byte row (address to a string) and a variable-size -# value. -# -# Accessing the table performs a linear scan for a key string, and always -# requires passing in the row size. -# -# Table primitives: -# get(stream, string, row-size) -# aborts if not found -# get-or-insert(stream, string, row-size) -# inserts if not found -# get-slice(stream, slice, row-size) -# aborts if not found -# leaky-get-or-insert-slice(stream, slice, row-size) -# inserts if not found - -# 'table' is a stream of (key, value) rows -# keys are always strings (addresses; size 4 bytes) -# values may be any type, but rows (key+value) always occupy 'row-size' bytes -# scan 'table' for a row with a key 'key' and return the address of the corresponding value -# if no row is found, abort -get: # table : (address stream {string, _}), key : (address string), row-size : int -> EAX : (address _) - # pseudocode: - # curr = table->data - # max = &table->data[table->write] - # while curr < max - # if string-equal?(key, *curr) - # return curr+4 - # curr += row-size - # abort - # - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # . save registers - 51/push-ECX - 52/push-EDX - 56/push-ESI - # ESI = table - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 6/r32/ESI 8/disp8 . # copy *(EBP+8) to ESI - # curr/ECX = table->data - 8d/copy-address 1/mod/*+disp8 6/rm32/ESI . . . 1/r32/ECX 0xc/disp8 . # copy ESI+12 to ECX - # max/EDX = table->data + table->write - 8b/copy 0/mod/indirect 6/rm32/ESI . . . 2/r32/EDX . . # copy *ESI to EDX - 8d/copy-address 0/mod/indirect 4/rm32/sib 1/base/ECX 2/index/EDX . 2/r32/EDX . . # copy ECX+EDX to EDX -$get:search-loop: - # if (curr >= max) abort - 39/compare 3/mod/direct 1/rm32/ECX . . . 2/r32/EDX . . # compare ECX with EDX - 73/jump-if-greater-or-equal-unsigned $get:abort/disp8 - # if (string-equal?(key, *curr)) return curr+4 - # . EAX = string-equal?(key, *curr) - # . . push args - ff 6/subop/push 0/mod/indirect 1/rm32/ECX . . . . . . # push *ECX - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - # . . call - e8/call string-equal?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . if (EAX != 0) return EAX = curr+4 - 3d/compare-EAX-and 0/imm32 - 74/jump-if-equal $get:mismatch/disp8 - 8d/copy-address 1/mod/*+disp8 1/rm32/ECX . . . 0/r32/EAX 4/disp8 . # copy ECX+4 to EAX - eb/jump $get:end/disp8 -$get:mismatch: - # curr += row-size - 03/add 1/mod/*+disp8 5/rm32/EBP . . . 1/r32/ECX 0x10/disp8 . # add *(EBP+16) to ECX - # loop - eb/jump $get:search-loop/disp8 -$get:end: - # . restore registers - 5e/pop-to-ESI - 5a/pop-to-EDX - 59/pop-to-ECX - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -$get:abort: - # . _write(2/stderr, error) - # . . push args - 68/push "get: key not found: "/imm32 - 68/push 2/imm32/stderr - # . . call - e8/call _write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . _write(2/stderr, key) - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - 68/push 2/imm32/stderr - # . . call - e8/call _write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . _write(2/stderr, "\n") - # . . push args - 68/push "\n"/imm32 - 68/push 2/imm32/stderr - # . . call - e8/call _write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . syscall(exit, 1) - bb/copy-to-EBX 1/imm32 - b8/copy-to-EAX 1/imm32/exit - cd/syscall 0x80/imm8 - # never gets here - -test-get: - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # - setup: create a table with a couple of keys - # var table/ECX : (address stream {string, number}) = stream(2 rows * 8 bytes) - 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # subtract from ESP - 68/push 0x10/imm32/length - 68/push 0/imm32/read - 68/push 0/imm32/write - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # insert(table, "code", 8 bytes per row) - # . . push args - 68/push 8/imm32/row-size - 68/push "code"/imm32 - 51/push-ECX - # . . call - e8/call get-or-insert/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # insert(table, "data", 8 bytes per row) - # . . push args - 68/push 8/imm32/row-size - 68/push "data"/imm32 - 51/push-ECX - # . . call - e8/call get-or-insert/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -$test-get:check1: - # EAX = get(table, "code", 8 bytes per row) - # . . push args - 68/push 8/imm32/row-size - 68/push "code"/imm32 - 51/push-ECX - # . . call - e8/call get/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # check-ints-equal(EAX - table->data, 4, msg) - # . check-ints-equal(EAX - table, 16, msg) - # . . push args - 68/push "F - test-get/0"/imm32 - 68/push 0x10/imm32 - 29/subtract 3/mod/direct 0/rm32/EAX . . . 1/r32/ECX . . # subtract ECX from EAX - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -$test-get:check2: - # EAX = get(table, "data", 8 bytes per row) - # . . push args - 68/push 8/imm32/row-size - 68/push "data"/imm32 - 51/push-ECX - # . . call - e8/call get/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # check-ints-equal(EAX - table->data, 12, msg) - # . check-ints-equal(EAX - table, 24, msg) - # . . push args - 68/push "F - test-get/1"/imm32 - 68/push 0x18/imm32 - 29/subtract 3/mod/direct 0/rm32/EAX . . . 1/r32/ECX . . # subtract ECX from EAX - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -$test-get:end: - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -# 'table' is a stream of (key, value) rows -# keys are always strings (addresses; size 4 bytes) -# values may be any type, but rows (key+value) always occupy 'row-size' bytes -# scan 'table' for a row with a key 'key' and return the address of the corresponding value -# if no row is found, abort -get-slice: # table : (address stream {string, _}), key : (address slice), row-size : int -> EAX : (address _) - # pseudocode: - # curr = table->data - # max = &table->data[table->write] - # while curr < max - # if slice-equal?(key, *curr) - # return curr+4 - # curr += row-size - # abort - # - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # . save registers - 51/push-ECX - 52/push-EDX - 56/push-ESI - # ESI = table - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 6/r32/ESI 8/disp8 . # copy *(EBP+8) to ESI - # curr/ECX = table->data - 8d/copy-address 1/mod/*+disp8 6/rm32/ESI . . . 1/r32/ECX 0xc/disp8 . # copy ESI+12 to ECX - # max/EDX = table->data + table->write - 8b/copy 0/mod/indirect 6/rm32/ESI . . . 2/r32/EDX . . # copy *ESI to EDX - 8d/copy-address 0/mod/indirect 4/rm32/sib 1/base/ECX 2/index/EDX . 2/r32/EDX . . # copy ECX+EDX to EDX -$get-slice:search-loop: - # if (curr >= max) abort - 39/compare 3/mod/direct 1/rm32/ECX . . . 2/r32/EDX . . # compare ECX with EDX - 73/jump-if-greater-or-equal-unsigned $get-slice:abort/disp8 - # if (slice-equal?(key, *curr)) return curr+4 - # . EAX = slice-equal?(key, *curr) - # . . push args - ff 6/subop/push 0/mod/indirect 1/rm32/ECX . . . . . . # push *ECX - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - # . . call - e8/call slice-equal?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . if (EAX != 0) return EAX = curr+4 - 3d/compare-EAX-and 0/imm32 - 74/jump-if-equal $get-slice:mismatch/disp8 - 8d/copy-address 1/mod/*+disp8 1/rm32/ECX . . . 0/r32/EAX 4/disp8 . # copy ECX+4 to EAX - eb/jump $get-slice:end/disp8 -$get-slice:mismatch: - # curr += row-size - 03/add 1/mod/*+disp8 5/rm32/EBP . . . 1/r32/ECX 0x10/disp8 . # add *(EBP+16) to ECX - # loop - eb/jump $get-slice:search-loop/disp8 -$get-slice:end: - # . restore registers - 5e/pop-to-ESI - 5a/pop-to-EDX - 59/pop-to-ECX - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -$get-slice:abort: - # . _write(2/stderr, error) - # . . push args - 68/push "get-slice: key not found: "/imm32 - 68/push 2/imm32/stderr - # . . call - e8/call _write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . write-slice-buffered(Stderr, key) - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - 68/push Stderr/imm32 - # . . call - e8/call write-slice-buffered/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . flush(Stderr) - # . . push args - 68/push Stderr/imm32 - # . . call - e8/call flush/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . _write(2/stderr, "\n") - # . . push args - 68/push "\n"/imm32 - 68/push 2/imm32/stderr - # . . call - e8/call _write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . syscall(exit, 1) - bb/copy-to-EBX 1/imm32 - b8/copy-to-EAX 1/imm32/exit - cd/syscall 0x80/imm8 - # never gets here - -test-get-slice: - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # - setup: create a table with a couple of keys - # var table/ECX : (address stream {string, number}) = stream(2 rows * 8 bytes) - 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # subtract from ESP - 68/push 0x10/imm32/length - 68/push 0/imm32/read - 68/push 0/imm32/write - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # insert(table, "code", 8 bytes per row) - # . . push args - 68/push 8/imm32/row-size - 68/push "code"/imm32 - 51/push-ECX - # . . call - e8/call get-or-insert/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # insert(table, "data", 8 bytes per row) - # . . push args - 68/push 8/imm32/row-size - 68/push "data"/imm32 - 51/push-ECX - # . . call - e8/call get-or-insert/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -$test-get-slice:check1: - # (EAX..EDX) = "code" - b8/copy-to-EAX "code"/imm32 - 8b/copy 0/mod/indirect 0/rm32/EAX . . . 2/r32/EDX . . # copy *EAX to EDX - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 2/index/EDX . 2/r32/EDX 4/disp8 . # copy EAX+EDX+4 to EDX - 05/add-to-EAX 4/imm32 - # var slice/EDX = {EAX, EDX} - 52/push-EDX - 50/push-EAX - 89/copy 3/mod/direct 2/rm32/EDX . . . 4/r32/ESP . . # copy ESP to EDX - # EAX = get-slice(table, "code", 8 bytes per row) - # . . push args - 68/push 8/imm32/row-size - 52/push-EDX - 51/push-ECX - # . . call - e8/call get-slice/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # check-ints-equal(EAX - table->data, 4, msg) # first row's value slot returned - # . check-ints-equal(EAX - table, 16, msg) - # . . push args - 68/push "F - test-get-slice/0"/imm32 - 68/push 0x10/imm32 - 29/subtract 3/mod/direct 0/rm32/EAX . . . 1/r32/ECX . . # subtract ECX from EAX - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -$test-get-slice:check2: - # (EAX..EDX) = "data" - b8/copy-to-EAX "data"/imm32 - 8b/copy 0/mod/indirect 0/rm32/EAX . . . 2/r32/EDX . . # copy *EAX to EDX - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 2/index/EDX . 2/r32/EDX 4/disp8 . # copy EAX+EDX+4 to EDX - 05/add-to-EAX 4/imm32 - # var slice/EDX = {EAX, EDX} - 52/push-EDX - 50/push-EAX - 89/copy 3/mod/direct 2/rm32/EDX . . . 4/r32/ESP . . # copy ESP to EDX - # EAX = get-slice(table, "data" slice, 8 bytes per row) - # . . push args - 68/push 8/imm32/row-size - 52/push-EDX - 51/push-ECX - # . . call - e8/call get-slice/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # check-ints-equal(EAX - table->data, 12, msg) - # . check-ints-equal(EAX - table, 24, msg) - # . . push args - 68/push "F - test-get-slice/1"/imm32 - 68/push 0x18/imm32 - 29/subtract 3/mod/direct 0/rm32/EAX . . . 1/r32/ECX . . # subtract ECX from EAX - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -$test-get-slice:end: - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -# 'table' is a stream of (key, value) rows -# keys are always strings (addresses; size 4 bytes) -# values may be any type, but rows (key+value) always occupy 'row-size' bytes -# scan 'table' for a row with a key 'key' and return the address of the corresponding value -# if no row is found, save 'key' to the next available row -# if there are no rows free, abort -# return the address of the value -# Beware: assume keys are immutable; they're inserted by reference -# TODO: pass in an allocation descriptor -get-or-insert: # table : (address stream {string, _}), key : (address string), row-size : int -> EAX : (address _) - # pseudocode: - # curr = table->data - # max = &table->data[table->write] - # while curr < max - # if string-equal?(key, *curr) - # return curr+4 - # curr += row-size - # if table->write >= table->length - # abort - # zero-out(max, row-size) - # *max = key - # table->write += row-size - # return max+4 - # - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # . save registers - 51/push-ECX - 52/push-EDX - 56/push-ESI - # ESI = table - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 6/r32/ESI 8/disp8 . # copy *(EBP+8) to ESI - # curr/ECX = table->data - 8d/copy-address 1/mod/*+disp8 6/rm32/ESI . . . 1/r32/ECX 0xc/disp8 . # copy ESI+12 to ECX - # max/EDX = table->data + table->write - 8b/copy 0/mod/indirect 6/rm32/ESI . . . 2/r32/EDX . . # copy *ESI to EDX - 8d/copy-address 0/mod/indirect 4/rm32/sib 1/base/ECX 2/index/EDX . 2/r32/EDX . . # copy ECX+EDX to EDX -$get-or-insert:search-loop: - # if (curr >= max) break - 39/compare 3/mod/direct 1/rm32/ECX . . . 2/r32/EDX . . # compare ECX with EDX - 73/jump-if-greater-or-equal-unsigned $get-or-insert:not-found/disp8 - # if (string-equal?(key, *curr)) return curr+4 - # . EAX = string-equal?(key, *curr) - # . . push args - ff 6/subop/push 0/mod/indirect 1/rm32/ECX . . . . . . # push *ECX - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - # . . call - e8/call string-equal?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . if (EAX != 0) return EAX = curr+4 - 3d/compare-EAX-and 0/imm32 - 74/jump-if-equal $get-or-insert:mismatch/disp8 - 8d/copy-address 1/mod/*+disp8 1/rm32/ECX . . . 0/r32/EAX 4/disp8 . # copy ECX+4 to EAX - eb/jump $get-or-insert:end/disp8 -$get-or-insert:mismatch: - # curr += row-size - 03/add 1/mod/*+disp8 5/rm32/EBP . . . 1/r32/ECX 0x10/disp8 . # add *(EBP+16) to ECX - # loop - eb/jump $get-or-insert:search-loop/disp8 -$get-or-insert:not-found: - # result/EAX = 0 - 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX - # if (table->write >= table->length) abort - 8b/copy 0/mod/indirect 6/rm32/ESI . . . 1/r32/ECX . . # copy *ESI to ECX - 3b/compare 1/mod/*+disp8 6/rm32/ESI . . . 1/r32/ECX 8/disp8 . # compare ECX with *(ESI+8) - 73/jump-if-greater-or-equal-unsigned $get-or-insert:abort/disp8 - # zero-out(max, row-size) - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x10/disp8 . # push *(EBP+16) - 52/push-EDX - # . . call - e8/call zero-out/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # *max = key - # . EAX = key - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 0/r32/EAX 0xc/disp8 . # copy *(EBP+12) to EAX - # . *max = EAX - 89/copy 0/mod/indirect 2/rm32/EDX . . . 0/r32/EAX . . # copy EAX to *EDX - # table->write += row-size - # . EAX = row-size - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 0/r32/EAX 0x10/disp8 . # copy *(EBP+16) to EAX - # . table->write += EAX - 01/add 0/mod/indirect 6/rm32/ESI . . . 0/r32/EAX . . # add EAX to *ESI - # return max+4 - # . EAX = max - 89/copy 3/mod/direct 0/rm32/EAX . . . 2/r32/EDX . . # copy EDX to EAX - # . EAX += 4 - 05/add-to-EAX 4/imm32 -$get-or-insert:end: - # . restore registers - 5e/pop-to-ESI - 5a/pop-to-EDX - 59/pop-to-ECX - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -$get-or-insert:abort: - # . _write(2/stderr, error) - # . . push args - 68/push "get-or-insert: table is full\n"/imm32 - 68/push 2/imm32/stderr - # . . call - e8/call _write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . syscall(exit, 1) - bb/copy-to-EBX 1/imm32 - b8/copy-to-EAX 1/imm32/exit - cd/syscall 0x80/imm8 - # never gets here - -test-get-or-insert: - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # var table/ECX : (address stream {string, number}) = stream(2 rows * 8 bytes) - 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # subtract from ESP - 68/push 0x10/imm32/length - 68/push 0/imm32/read - 68/push 0/imm32/write - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX -$test-get-or-insert:first-call: - # - start with an empty table, insert one key, verify that it was inserted - # EAX = get-or-insert(table, "code", 8 bytes per row) - # . . push args - 68/push 8/imm32/row-size - 68/push "code"/imm32 - 51/push-ECX - # . . call - e8/call get-or-insert/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # check-ints-equal(EAX - table->data, 4, msg) # first row's value slot returned - # . check-ints-equal(EAX - table, 16, msg) - # . . push args - 68/push "F - test-get-or-insert/0"/imm32 - 68/push 0x10/imm32 - 29/subtract 3/mod/direct 0/rm32/EAX . . . 1/r32/ECX . . # subtract ECX from EAX - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -$test-get-or-insert:check2: - # check-ints-equal(table->write, row-size = 8, msg) - # . . push args - 68/push "F - test-get-or-insert/1"/imm32 - 68/push 8/imm32/row-size - ff 6/subop/push 0/mod/indirect 1/rm32/ECX . . . . . . # push *ECX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # check-string-equal(*table->data, "code", msg) - # . . push args - 68/push "F - test-get-or-insert/2"/imm32 - 68/push "code"/imm32 - ff 6/subop/push 1/mod/*+disp8 1/rm32/ECX . . . . 0xc/disp8 . # push *(ECX+12) - # . . call - e8/call check-string-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -$test-get-or-insert:second-call: - # - insert the same key again, verify that it was reused - # EAX = get-or-insert(table, "code", 8 bytes per row) - # . . push args - 68/push 8/imm32/row-size - 68/push "code"/imm32 - 51/push-ECX - # . . call - e8/call get-or-insert/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # check-ints-equal(EAX - table->data, 4, msg) - # . check-ints-equal(EAX - table, 16, msg) - # . . push args - 68/push "F - test-get-or-insert/3"/imm32 - 68/push 0x10/imm32 - 29/subtract 3/mod/direct 0/rm32/EAX . . . 1/r32/ECX . . # subtract ECX from EAX - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # no new row inserted - # . check-ints-equal(table->write, row-size = 8, msg) - # . . push args - 68/push "F - test-get-or-insert/4"/imm32 - 68/push 8/imm32/row-size - ff 6/subop/push 0/mod/indirect 1/rm32/ECX . . . . . . # push *ECX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # check-string-equal(*table->data, "code", msg) - # . . push args - 68/push "F - test-get-or-insert/5"/imm32 - 68/push "code"/imm32 - ff 6/subop/push 1/mod/*+disp8 1/rm32/ECX . . . . 0xc/disp8 . # push *(ECX+12) - # . . call - e8/call check-string-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -$test-get-or-insert:third-call: - # - insert a new key, verify that it was inserted - # EAX = get-or-insert(table, "data", 8 bytes per row) - # . . push args - 68/push 8/imm32/row-size - 68/push "data"/imm32 - 51/push-ECX - # . . call - e8/call get-or-insert/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # table gets a new row - # check-ints-equal(EAX - table->data, 12, msg) # second row's value slot returned - # . check-ints-equal(EAX - table, 24, msg) - # . . push args - 68/push "F - test-get-or-insert/6"/imm32 - 68/push 0x18/imm32 - 29/subtract 3/mod/direct 0/rm32/EAX . . . 1/r32/ECX . . # subtract ECX from EAX - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # check-ints-equal(table->write, 2 rows = 16, msg) - # . . push args - 68/push "F - test-get-or-insert/7"/imm32 - 68/push 0x10/imm32/two-rows - ff 6/subop/push 0/mod/indirect 1/rm32/ECX . . . . . . # push *ECX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # check-string-equal(*table->data+8, "data", msg) - # check-string-equal(*(table+20), "data", msg) - # . . push args - 68/push "F - test-get-or-insert/8"/imm32 - 68/push "data"/imm32 - ff 6/subop/push 1/mod/*+disp8 1/rm32/ECX . . . . 0x14/disp8 . # push *(ECX+20) - # . . call - e8/call check-string-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -$test-get-or-insert:end: - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -# 'table' is a stream of (key, value) rows -# keys are always strings (addresses; size 4 bytes) -# values may be any type, but rows (key+value) always occupy 'row-size' bytes -# scan 'table' for a row with a key 'key' and return the address of the corresponding value -# if no row is found, save 'key' in the next available row -# if there are no rows free, abort -# WARNING: leaks memory -# TODO: pass in an allocation descriptor -leaky-get-or-insert-slice: # table : (address stream {string, _}), key : (address slice), row-size : int -> EAX : (address _) - # pseudocode: - # curr = table->data - # max = &table->data[table->write] - # while curr < max - # if slice-equal?(key, *curr) - # return curr+4 - # curr += row-size - # if table->write >= table->length - # abort - # zero-out(max, row-size) - # *max = slice-to-string(Heap, key) - # table->write += row-size - # return max+4 - # - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # . save registers - 51/push-ECX - 52/push-EDX - 56/push-ESI - # ESI = table - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 6/r32/ESI 8/disp8 . # copy *(EBP+8) to ESI - # curr/ECX = table->data - 8d/copy-address 1/mod/*+disp8 6/rm32/ESI . . . 1/r32/ECX 0xc/disp8 . # copy ESI+12 to ECX - # max/EDX = table->data + table->write - 8b/copy 0/mod/indirect 6/rm32/ESI . . . 2/r32/EDX . . # copy *ESI to EDX - 8d/copy-address 0/mod/indirect 4/rm32/sib 1/base/ECX 2/index/EDX . 2/r32/EDX . . # copy ECX+EDX to EDX -$leaky-get-or-insert-slice:search-loop: - # if (curr >= max) break - 39/compare 3/mod/direct 1/rm32/ECX . . . 2/r32/EDX . . # compare ECX with EDX - 73/jump-if-greater-or-equal-unsigned $leaky-get-or-insert-slice:not-found/disp8 - # if (slice-equal?(key, *curr)) return curr+4 - # . EAX = slice-equal?(key, *curr) - # . . push args - ff 6/subop/push 0/mod/indirect 1/rm32/ECX . . . . . . # push *ECX - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - # . . call - e8/call slice-equal?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . if (EAX != 0) return EAX = curr+4 - 3d/compare-EAX-and 0/imm32 - 74/jump-if-equal $leaky-get-or-insert-slice:mismatch/disp8 - 8d/copy-address 1/mod/*+disp8 1/rm32/ECX . . . 0/r32/EAX 4/disp8 . # copy ECX+4 to EAX - eb/jump $leaky-get-or-insert-slice:end/disp8 -$leaky-get-or-insert-slice:mismatch: - # curr += row-size - 03/add 1/mod/*+disp8 5/rm32/EBP . . . 1/r32/ECX 0x10/disp8 . # add *(EBP+16) to ECX - # loop - eb/jump $leaky-get-or-insert-slice:search-loop/disp8 -$leaky-get-or-insert-slice:not-found: - # result/EAX = 0 - 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX - # if (table->write >= table->length) abort - 8b/copy 0/mod/indirect 6/rm32/ESI . . . 1/r32/ECX . . # copy *ESI to ECX - 3b/compare 1/mod/*+disp8 6/rm32/ESI . . . 1/r32/ECX 8/disp8 . # compare ECX with *(ESI+8) - 7d/jump-if-greater-or-equal $leaky-get-or-insert-slice:abort/disp8 - # zero-out(max, row-size) - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x10/disp8 . # push *(EBP+16) - 52/push-EDX - # . . call - e8/call zero-out/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # *max = slice-to-string(Heap, key) - # . EAX = slice-to-string(Heap, key) - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - 68/push Heap/imm32 - # . . call - e8/call slice-to-string/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . *max = EAX - 89/copy 0/mod/indirect 2/rm32/EDX . . . 0/r32/EAX . . # copy EAX to *EDX - # table->write += row-size - # . EAX = row-size - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 0/r32/EAX 0x10/disp8 . # copy *(EBP+16) to EAX - # . table->write += EAX - 01/add 0/mod/indirect 6/rm32/ESI . . . 0/r32/EAX . . # add EAX to *ESI - # return max+4 - # . EAX = max - 89/copy 3/mod/direct 0/rm32/EAX . . . 2/r32/EDX . . # copy EDX to EAX - # . EAX += 4 - 05/add-to-EAX 4/imm32 -$leaky-get-or-insert-slice:end: - # . restore registers - 5e/pop-to-ESI - 5a/pop-to-EDX - 59/pop-to-ECX - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -$leaky-get-or-insert-slice:abort: - # . _write(2/stderr, error) - # . . push args - 68/push "leaky-get-or-insert-slice: table is full\n"/imm32 - 68/push 2/imm32/stderr - # . . call - e8/call _write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . syscall(exit, 1) - bb/copy-to-EBX 1/imm32 - b8/copy-to-EAX 1/imm32/exit - cd/syscall 0x80/imm8 - # never gets here - -test-leaky-get-or-insert-slice: - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # var table/ECX : (address stream {string, number}) = stream(2 rows * 8 bytes) - 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # subtract from ESP - 68/push 0x10/imm32/length - 68/push 0/imm32/read - 68/push 0/imm32/write - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # (EAX..EDX) = "code" - b8/copy-to-EAX "code"/imm32 - 8b/copy 0/mod/indirect 0/rm32/EAX . . . 2/r32/EDX . . # copy *EAX to EDX - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 2/index/EDX . 2/r32/EDX 4/disp8 . # copy EAX+EDX+4 to EDX - 05/add-to-EAX 4/imm32 - # var slice/EDX = {EAX, EDX} - 52/push-EDX - 50/push-EAX - 89/copy 3/mod/direct 2/rm32/EDX . . . 4/r32/ESP . . # copy ESP to EDX -$test-leaky-get-or-insert-slice:first-call: - # - start with an empty table, insert one key, verify that it was inserted - # EAX = leaky-get-or-insert-slice(table, "code" slice, 8 bytes per row) - # . . push args - 68/push 8/imm32/row-size - 52/push-EDX - 51/push-ECX - # . . call - e8/call leaky-get-or-insert-slice/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # check-ints-equal(EAX - table->data, 4, msg) # first row's value slot returned - # . check-ints-equal(EAX - table, 16, msg) - # . . push args - 68/push "F - test-leaky-get-or-insert-slice/0"/imm32 - 68/push 0x10/imm32 - 29/subtract 3/mod/direct 0/rm32/EAX . . . 1/r32/ECX . . # subtract ECX from EAX - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -$test-leaky-get-or-insert-slice:check2: - # check-ints-equal(table->write, row-size = 8, msg) - # . . push args - 68/push "F - test-leaky-get-or-insert-slice/1"/imm32 - 68/push 8/imm32/row-size - ff 6/subop/push 0/mod/indirect 1/rm32/ECX . . . . . . # push *ECX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # check-string-equal(*table->data, "code", msg) - # . . push args - 68/push "F - test-leaky-get-or-insert-slice/2"/imm32 - 68/push "code"/imm32 - ff 6/subop/push 1/mod/*+disp8 1/rm32/ECX . . . . 0xc/disp8 . # push *(ECX+12) - # . . call - e8/call check-string-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -$test-leaky-get-or-insert-slice:second-call: - # - insert the same key again, verify that it was reused - # EAX = leaky-get-or-insert-slice(table, "code" slice, 8 bytes per row) - # . . push args - 68/push 8/imm32/row-size - 52/push-EDX - 51/push-ECX - # . . call - e8/call leaky-get-or-insert-slice/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # check-ints-equal(EAX - table->data, 4, msg) - # . check-ints-equal(EAX - table, 16, msg) - # . . push args - 68/push "F - test-leaky-get-or-insert-slice/3"/imm32 - 68/push 0x10/imm32 - 29/subtract 3/mod/direct 0/rm32/EAX . . . 1/r32/ECX . . # subtract ECX from EAX - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # no new row inserted - # . check-ints-equal(table->write, row-size = 8, msg) - # . . push args - 68/push "F - test-leaky-get-or-insert-slice/4"/imm32 - 68/push 8/imm32/row-size - ff 6/subop/push 0/mod/indirect 1/rm32/ECX . . . . . . # push *ECX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # check-string-equal(*table->data, "code", msg) - # . . push args - 68/push "F - test-leaky-get-or-insert-slice/5"/imm32 - 68/push "code"/imm32 - ff 6/subop/push 1/mod/*+disp8 1/rm32/ECX . . . . 0xc/disp8 . # push *(ECX+12) - # . . call - e8/call check-string-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -$test-leaky-get-or-insert-slice:third-call: - # - insert a new key, verify that it was inserted - # (EAX..EDX) = "data" - b8/copy-to-EAX "data"/imm32 - 8b/copy 0/mod/indirect 0/rm32/EAX . . . 2/r32/EDX . . # copy *EAX to EDX - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 2/index/EDX . 2/r32/EDX 4/disp8 . # copy EAX+EDX+4 to EDX - 05/add-to-EAX 4/imm32 - # var slice/EDX = {EAX, EDX} - 52/push-EDX - 50/push-EAX - 89/copy 3/mod/direct 2/rm32/EDX . . . 4/r32/ESP . . # copy ESP to EDX - # EAX = leaky-get-or-insert-slice(table, "data" slice, 8 bytes per row) - # . . push args - 68/push 8/imm32/row-size - 52/push-EDX - 51/push-ECX - # . . call - e8/call leaky-get-or-insert-slice/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # table gets a new row - # check-ints-equal(EAX - table->data, 12, msg) # second row's value slot returned - # . check-ints-equal(EAX - table, 24, msg) - # . . push args - 68/push "F - test-leaky-get-or-insert-slice/6"/imm32 - 68/push 0x18/imm32 - 29/subtract 3/mod/direct 0/rm32/EAX . . . 1/r32/ECX . . # subtract ECX from EAX - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # check-ints-equal(table->write, 2 rows = 16, msg) - # . . push args - 68/push "F - test-leaky-get-or-insert-slice/7"/imm32 - 68/push 0x10/imm32/two-rows - ff 6/subop/push 0/mod/indirect 1/rm32/ECX . . . . . . # push *ECX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # check-string-equal(*table->data+8, "data", msg) - # check-string-equal(*(table+20), "data", msg) - # . . push args - 68/push "F - test-leaky-get-or-insert-slice/8"/imm32 - 68/push "data"/imm32 - ff 6/subop/push 1/mod/*+disp8 1/rm32/ECX . . . . 0x14/disp8 . # push *(ECX+20) - # . . call - e8/call check-string-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -$test-leaky-get-or-insert-slice:end: - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -# (re)compute the bounds of the next word in the line -# return empty string on reaching end of file -next-word: # line : (address stream byte), out : (address slice) - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # . save registers - 50/push-EAX - 51/push-ECX - 56/push-ESI - 57/push-EDI - # ESI = line - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 6/r32/ESI 8/disp8 . # copy *(EBP+8) to ESI - # EDI = out - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 7/r32/EDI 0xc/disp8 . # copy *(EBP+12) to EDI - # skip-chars-matching(line, ' ') - # . . push args - 68/push 0x20/imm32/space - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - # . . call - e8/call skip-chars-matching/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -$next-word:check0: - # if (line->read >= line->write) clear out and return - # . EAX = line->read - 8b/copy 1/mod/*+disp8 6/rm32/ESI . . . 0/r32/EAX 4/disp8 . # copy *(ESI+4) to EAX - # . if (EAX < line->write) goto next check - 3b/compare 0/mod/indirect 6/rm32/ESI . . . 0/r32/EAX . . # compare EAX with *ESI - 7c/jump-if-lesser $next-word:check-for-comment/disp8 - # . return out = {0, 0} - c7 0/subop/copy 0/mod/direct 7/rm32/EDI . . . . . 0/imm32 # copy to *EDI - c7 0/subop/copy 1/mod/*+disp8 7/rm32/EDI . . . . 4/disp8 0/imm32 # copy to *(EDI+4) - eb/jump $next-word:end/disp8 -$next-word:check-for-comment: - # out->start = &line->data[line->read] - 8b/copy 1/mod/*+disp8 6/rm32/ESI . . . 1/r32/ECX 4/disp8 . # copy *(ESI+4) to ECX - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 6/base/ESI 1/index/ECX . 0/r32/EAX 0xc/disp8 . # copy ESI+ECX+12 to EAX - 89/copy 0/mod/indirect 7/rm32/EDI . . . 0/r32/EAX . . # copy EAX to *EDI - # if (line->data[line->read] == '#') out->end = &line->data[line->write]), skip rest of stream and return - # . EAX = line->data[line->read] - 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX - 8a/copy-byte 1/mod/*+disp8 4/rm32/sib 6/base/ESI 1/index/ECX . 0/r32/AL 0xc/disp8 . # copy byte at *(ESI+ECX+12) to AL - # . compare - 3d/compare-EAX-and 0x23/imm32/pound - 75/jump-if-not-equal $next-word:regular-word/disp8 -$next-word:comment: - # . out->end = &line->data[line->write] - 8b/copy 0/mod/indirect 6/rm32/ESI . . . 0/r32/EAX . . # copy *ESI to EAX - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 6/base/ESI 0/index/EAX . 0/r32/EAX 0xc/disp8 . # copy ESI+EAX+12 to EAX - 89/copy 1/mod/*+disp8 7/rm32/EDI . . . 0/r32/EAX 4/disp8 . # copy EAX to *(EDI+4) - # . line->read = line->write - 89/copy 1/mod/*+disp8 6/rm32/ESI . . . 0/r32/EAX 4/disp8 . # copy EAX to *(ESI+4) - # . return - eb/jump $next-word:end/disp8 -$next-word:regular-word: - # otherwise skip-chars-not-matching-whitespace(line) # including trailing newline - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - # . . call - e8/call skip-chars-not-matching-whitespace/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # out->end = &line->data[line->read] - 8b/copy 1/mod/*+disp8 6/rm32/ESI . . . 1/r32/ECX 4/disp8 . # copy *(ESI+4) to ECX - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 6/base/ESI 1/index/ECX . 0/r32/EAX 0xc/disp8 . # copy ESI+ECX+12 to EAX - 89/copy 1/mod/*+disp8 7/rm32/EDI . . . 0/r32/EAX 4/disp8 . # copy EAX to *(EDI+4) -$next-word:end: - # . restore registers - 5f/pop-to-EDI - 5e/pop-to-ESI - 59/pop-to-ECX - 58/pop-to-EAX - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-next-word: - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # setup - # . clear-stream(_test-stream) - # . . push args - 68/push _test-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # var slice/ECX = {0, 0} - 68/push 0/imm32/end - 68/push 0/imm32/start - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # write(_test-stream, " ab") - # . . push args - 68/push " ab"/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # next-word(_test-stream, slice) - # . . push args - 51/push-ECX - 68/push _test-stream/imm32 - # . . call - e8/call next-word/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check-ints-equal(slice->start - _test-stream->data, 2, msg) - # . check-ints-equal(slice->start - _test-stream, 14, msg) - # . . push args - 68/push "F - test-next-word: start"/imm32 - 68/push 0xe/imm32 - # . . push slice->start - _test-stream - 8b/copy 0/mod/indirect 1/rm32/ECX . . . 0/r32/EAX . . # copy *ECX to EAX - 81 5/subop/subtract 3/mod/direct 0/rm32/EAX . . . . . _test-stream/imm32 # subtract from EAX - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # check-ints-equal(slice->end - _test-stream->data, 4, msg) - # . check-ints-equal(slice->end - _test-stream, 16, msg) - # . . push args - 68/push "F - test-next-word: end"/imm32 - 68/push 0x10/imm32 - # . . push slice->end - _test-stream - 8b/copy 1/mod/*+disp8 1/rm32/ECX . . . 0/r32/EAX 4/disp8 . # copy *(ECX+4) to EAX - 81 5/subop/subtract 3/mod/direct 0/rm32/EAX . . . . . _test-stream/imm32 # subtract from EAX - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-next-word-returns-whole-comment: - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # setup - # . clear-stream(_test-stream) - # . . push args - 68/push _test-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # var slice/ECX = {0, 0} - 68/push 0/imm32/end - 68/push 0/imm32/start - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # write(_test-stream, " # a") - # . . push args - 68/push " # a"/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # next-word(_test-stream, slice) - # . . push args - 51/push-ECX - 68/push _test-stream/imm32 - # . . call - e8/call next-word/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check-ints-equal(slice->start - _test-stream->data, 2, msg) - # . check-ints-equal(slice->start - _test-stream, 14, msg) - # . . push args - 68/push "F - test-next-word-returns-whole-comment: start"/imm32 - 68/push 0xe/imm32 - # . . push slice->start - _test-stream - 8b/copy 0/mod/indirect 1/rm32/ECX . . . 0/r32/EAX . . # copy *ECX to EAX - 81 5/subop/subtract 3/mod/direct 0/rm32/EAX . . . . . _test-stream/imm32 # subtract from EAX - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # check-ints-equal(slice->end - _test-stream->data, 5, msg) - # . check-ints-equal(slice->end - _test-stream, 17, msg) - # . . push args - 68/push "F - test-next-word-returns-whole-comment: end"/imm32 - 68/push 0x11/imm32 - # . . push slice->end - _test-stream - 8b/copy 1/mod/*+disp8 1/rm32/ECX . . . 0/r32/EAX 4/disp8 . # copy *(ECX+4) to EAX - 81 5/subop/subtract 3/mod/direct 0/rm32/EAX . . . . . _test-stream/imm32 # subtract from EAX - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-next-word-returns-empty-string-on-eof: - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # setup - # . clear-stream(_test-stream) - # . . push args - 68/push _test-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # var slice/ECX = {0, 0} - 68/push 0/imm32/end - 68/push 0/imm32/start - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # write nothing to _test-stream - # next-word(_test-stream, slice) - # . . push args - 51/push-ECX - 68/push _test-stream/imm32 - # . . call - e8/call next-word/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check-ints-equal(slice->end - slice->start, 0, msg) - # . . push args - 68/push "F - test-next-word-returns-empty-string-on-eof"/imm32 - 68/push 0/imm32 - # . . push slice->end - slice->start - 8b/copy 1/mod/*+disp8 1/rm32/ECX . . . 0/r32/EAX 4/disp8 . # copy *(ECX+4) to EAX - 2b/subtract 0/mod/indirect 1/rm32/ECX . . . 0/r32/EAX . . # subtract *ECX from EAX - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -# write an entire stream's contents to a buffered-file -# ways to do this: -# - construct a 'maximal slice' and pass it to write-slice-buffered -# - flush the buffered-file and pass the stream directly to its fd (disabling buffering) -# we'll go with the first way for now -write-stream-data: # f : (address buffered-file), s : (address stream) -> <void> - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # . save registers - 50/push-EAX - 51/push-ECX - 56/push-ESI - # ESI = s - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 6/r32/ESI 0xc/disp8 . # copy *(EBP+12) to ESI - # var slice/ECX = {s->data, s->data + s->write} - # . push s->data + s->write - 8b/copy 0/mod/indirect 6/rm32/ESI . . . 0/r32/EAX . . # copy *ESI to EAX - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 6/base/ESI 0/index/EAX . 0/r32/EAX 0xc/disp8 . # copy ESI+EAX+12 to EAX - 50/push-EAX - # . push s->data - 8d/copy-address 1/mod/*+disp8 6/rm32/ESI . . . 0/r32/EAX 0xc/disp8 . # copy ESI+12 to EAX - 50/push-EAX - # . ECX = ESP - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # write-slice-buffered(f, slice) - # . . push args - 51/push-ECX - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - # . . call - e8/call write-slice-buffered/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -$write-stream-data:end: - # . restore locals - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . restore registers - 5e/pop-to-ESI - 59/pop-to-ECX - 58/pop-to-EAX - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-write-stream-data: - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # setup - # . clear-stream(_test-output-stream) - # . . push args - 68/push _test-output-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-buffered-file+4) - # . . push args - b8/copy-to-EAX _test-output-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-input-stream) - # . . push args - 68/push _test-input-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # initialize input - # . write(_test-input-stream, "abcd") - # . . push args - 68/push "abcd"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # write-stream-data(_test-output-buffered-file, _test-input-stream) - # . . push args - 68/push _test-input-stream/imm32 - 68/push _test-output-buffered-file/imm32 - # . . call - e8/call write-stream-data/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check that the write happened as expected - # . flush(_test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - # . . call - e8/call flush/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . check-stream-equal(_test-output-stream, "abcd", msg) - # . . push args - 68/push "F - test-write-stream-data"/imm32 - 68/push "abcd"/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-stream-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -has-metadata?: # word : (address slice), s : (address string) -> EAX : boolean - # pseudocode: - # var twig : &slice = next-token-from-slice(word->start, word->end, '/') # skip name - # curr = twig->end - # while true - # twig = next-token-from-slice(curr, word->end, '/') - # if (twig.empty()) break - # if (slice-equal?(twig, s)) return true - # curr = twig->end - # return false - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # . save registers - 51/push-ECX - 52/push-EDX - 56/push-ESI - 57/push-EDI - # ESI = word - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 6/r32/ESI 8/disp8 . # copy *(EBP+8) to ESI - # EDX = word->end - 8b/copy 1/mod/*+disp8 6/rm32/ESI . . . 2/r32/EDX 4/disp8 . # copy *(ESI+4) to EDX - # var twig/EDI : (address slice) = {0, 0} - 68/push 0/imm32/end - 68/push 0/imm32/start - 89/copy 3/mod/direct 7/rm32/EDI . . . 4/r32/ESP . . # copy ESP to EDI - # next-token-from-slice(word->start, word->end, '/', twig) - # . . push args - 57/push-EDI - 68/push 0x2f/imm32/slash - 52/push-EDX - ff 6/subop/push 0/mod/indirect 6/rm32/ESI . . . . . . # push *ESI - # . . call - e8/call next-token-from-slice/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP - # curr/ECX = twig->end - 8b/copy 1/mod/*+disp8 7/rm32/EDI . . . 1/r32/ECX 4/disp8 . # copy *(EDI+4) to ECX -$has-metadata?:loop: - # next-token-from-slice(curr, word->end, '/', twig) - # . . push args - 57/push-EDI - 68/push 0x2f/imm32/slash - 52/push-EDX - 51/push-ECX - # . . call - e8/call next-token-from-slice/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP - # if (slice-empty?(twig)) return false - # . EAX = slice-empty?(twig) - # . . push args - 57/push-EDI - # . . call - e8/call slice-empty?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . if (EAX != 0) return false - 3d/compare-EAX-and 0/imm32 - 75/jump-if-not-equal $has-metadata?:false/disp8 - # if (slice-equal?(twig, s)) return true - # . EAX = slice-equal?(twig, s) - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - 57/push-EDI - # . . call - e8/call slice-equal?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . if (EAX != 0) return true - 3d/compare-EAX-and 0/imm32 - 75/jump-if-not-equal $has-metadata?:true/disp8 - # curr = twig->end - 8b/copy 1/mod/*+disp8 7/rm32/EDI . . . 1/r32/ECX 4/disp8 . # copy *(EDI+4) to ECX - eb/jump $has-metadata?:loop/disp8 -$has-metadata?:true: - b8/copy-to-EAX 1/imm32/true - eb/jump $has-metadata?:end/disp8 -$has-metadata?:false: - b8/copy-to-EAX 0/imm32/false -$has-metadata?:end: - # . reclaim locals - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . restore registers - 5f/pop-to-EDI - 5e/pop-to-ESI - 5a/pop-to-EDX - 59/pop-to-ECX - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-has-metadata-true: - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # (EAX..ECX) = "ab/imm32" - b8/copy-to-EAX "ab/imm32"/imm32 - 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX - 05/add-to-EAX 4/imm32 - # var in/ESI : (address slice) = {EAX, ECX} - 51/push-ECX - 50/push-EAX - 89/copy 3/mod/direct 6/rm32/ESI . . . 4/r32/ESP . . # copy ESP to ESI - # EAX = has-metadata?(ESI, "imm32") - # . . push args - 68/push "imm32"/imm32 - 56/push-ESI - # . . call - e8/call has-metadata?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check-ints-equal(EAX, 1, msg) - # . . push args - 68/push "F - test-has-metadata-true"/imm32 - 68/push 1/imm32/true - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-has-metadata-false: - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # (EAX..ECX) = "ab/c" - b8/copy-to-EAX "ab/c"/imm32 - 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX - 05/add-to-EAX 4/imm32 - # var in/ESI : (address slice) = {EAX, ECX} - 51/push-ECX - 50/push-EAX - 89/copy 3/mod/direct 6/rm32/ESI . . . 4/r32/ESP . . # copy ESP to ESI - # EAX = has-metadata?(ESI, "d") - # . . push args - 68/push "d"/imm32 - 56/push-ESI - # . . call - e8/call has-metadata?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check-ints-equal(EAX, 0, msg) - # . . push args - 68/push "F - test-has-metadata-false"/imm32 - 68/push 0/imm32/false - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-has-metadata-ignore-name: - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # (EAX..ECX) = "a/b" - b8/copy-to-EAX "a/b"/imm32 - 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX - 05/add-to-EAX 4/imm32 - # var in/ESI : (address slice) = {EAX, ECX} - 51/push-ECX - 50/push-EAX - 89/copy 3/mod/direct 6/rm32/ESI . . . 4/r32/ESP . . # copy ESP to ESI - # EAX = has-metadata?(ESI, "a") - # . . push args - 68/push "a"/imm32 - 56/push-ESI - # . . call - e8/call has-metadata?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check-ints-equal(EAX, 0, msg) - # . . push args - 68/push "F - test-has-metadata-ignore-name"/imm32 - 68/push 0/imm32/false - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-has-metadata-multiple-true: - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # (EAX..ECX) = "a/b/c" - b8/copy-to-EAX "a/b/c"/imm32 - 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX - 05/add-to-EAX 4/imm32 - # var in/ESI : (address slice) = {EAX, ECX} - 51/push-ECX - 50/push-EAX - 89/copy 3/mod/direct 6/rm32/ESI . . . 4/r32/ESP . . # copy ESP to ESI - # EAX = has-metadata?(ESI, "c") - # . . push args - 68/push "c"/imm32 - 56/push-ESI - # . . call - e8/call has-metadata?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check-ints-equal(EAX, 1, msg) - # . . push args - 68/push "F - test-has-metadata-multiple-true"/imm32 - 68/push 1/imm32/true - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-has-metadata-multiple-false: - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # (EAX..ECX) = "a/b/c" - b8/copy-to-EAX "a/b/c"/imm32 - 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX - 05/add-to-EAX 4/imm32 - # var in/ESI : (address slice) = {EAX, ECX} - 51/push-ECX - 50/push-EAX - 89/copy 3/mod/direct 6/rm32/ESI . . . 4/r32/ESP . . # copy ESP to ESI - # EAX = has-metadata?(ESI, "d") - # . . push args - 68/push "d"/imm32 - 56/push-ESI - # . . call - e8/call has-metadata?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check-ints-equal(EAX, 0, msg) - # . . push args - 68/push "F - test-has-metadata-multiple-false"/imm32 - 68/push 0/imm32/false - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -# If datum of 'word' is not a valid name, it must be a hex int. Parse and print -# it in 'width' bytes of hex, least significant first. -# Otherwise just print the entire word including metadata. -# Always print a trailing space. -emit: # out : (address buffered-file), word : (address slice), width : int -> <void> - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # . save registers - 50/push-EAX - 56/push-ESI - 57/push-EDI - # ESI = word - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 6/r32/ESI 0xc/disp8 . # copy *(EBP+12) to ESI - # var name/EDI : (address slice) = {0, 0} - 68/push 0/imm32/end - 68/push 0/imm32/start - 89/copy 3/mod/direct 7/rm32/EDI . . . 4/r32/ESP . . # copy ESP to EDI - # datum = next-token-from-slice(word->start, word->end, '/') - # . . push args - 57/push-EDI - 68/push 0x2f/imm32/slash - ff 6/subop/push 1/mod/*+disp8 6/rm32/ESI . . . . 4/disp8 . # push *(ESI+4) - ff 6/subop/push 0/mod/indirect 6/rm32/ESI . . . . . . # push *ESI - # . . call - e8/call next-token-from-slice/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP - # if (is-valid-name?(datum)) write-slice-buffered(out, word) and return - # . EAX = is-valid-name?(name) - # . . push args - 57/push-EDI - # . . call - e8/call is-valid-name?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . if (EAX != 0) - 3d/compare-EAX-and 0/imm32 - 74/jump-if-equal $emit:hex-int/disp8 -$emit:name: - # . write-slice-buffered(out, word) - # . . push args - 56/push-ESI - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - # . . call - e8/call write-slice-buffered/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . write-buffered(out, " ") - # . . push args - 68/push " "/imm32 - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - # . . call - e8/call write-buffered/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . return - eb/jump $emit:end/disp8 - # otherwise emit-hex(out, parse-hex-int(datum), width) - # (Weird shit can happen here if the datum of 'word' isn't either a valid - # name or a hex number, but we're only going to be passing in real legal - # programs. We just want to make sure that valid names aren't treated as - # (valid) hex numbers.) -$emit:hex-int: - # . value/EAX = parse-hex-int(datum) - # . . push args - 57/push-EDI - # . . call - e8/call parse-hex-int/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . emit-hex(out, value, width) - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x10/disp8 . # push *(EBP+16) - 50/push-EAX - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - # . . call - e8/call emit-hex/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -$emit:end: - # . reclaim locals - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . restore registers - 5f/pop-to-EDI - 5e/pop-to-ESI - 58/pop-to-EAX - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-emit-number: - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # setup - # . clear-stream(_test-output-stream) - # . . push args - 68/push _test-output-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-buffered-file+4) - # . . push args - b8/copy-to-EAX _test-output-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # (EAX..ECX) = "30" - b8/copy-to-EAX "30"/imm32 - 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX - 05/add-to-EAX 4/imm32 - # var slice/ECX = {EAX, ECX} - 51/push-ECX - 50/push-EAX - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # emit(_test-output-buffered-file, slice, 1) - # . . push args - 68/push 1/imm32 - 51/push-ECX - 68/push _test-output-buffered-file/imm32 - # . . call - e8/call emit/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # flush(_test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - # . . call - e8/call flush/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # check-stream-equal(_test-output-stream, "30 ", msg) - # . . push args - 68/push "F - test-emit-number/1"/imm32 - 68/push "30 "/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-stream-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-emit-negative-number: - # test support for sign-extending negative numbers - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # setup - # . clear-stream(_test-output-stream) - # . . push args - 68/push _test-output-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-buffered-file+4) - # . . push args - b8/copy-to-EAX _test-output-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # (EAX..ECX) = "-2" - b8/copy-to-EAX "-2"/imm32 - 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX - 05/add-to-EAX 4/imm32 - # var slice/ECX = {EAX, ECX} - 51/push-ECX - 50/push-EAX - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # emit(_test-output-buffered-file, slice, 2) - # . . push args - 68/push 2/imm32 - 51/push-ECX - 68/push _test-output-buffered-file/imm32 - # . . call - e8/call emit/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # flush(_test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - # . . call - e8/call flush/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # check-stream-equal(_test-output-stream, "fe ff ", msg) - # . . push args - 68/push "F - test-emit-number/1"/imm32 - 68/push "fe ff "/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-stream-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-emit-number-with-metadata: - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # setup - # . clear-stream(_test-output-stream) - # . . push args - 68/push _test-output-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-buffered-file+4) - # . . push args - b8/copy-to-EAX _test-output-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # (EAX..ECX) = "-2/foo" - b8/copy-to-EAX "-2/foo"/imm32 - 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX - 05/add-to-EAX 4/imm32 - # var slice/ECX = {EAX, ECX} - 51/push-ECX - 50/push-EAX - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # emit(_test-output-buffered-file, slice, 2) - # . . push args - 68/push 2/imm32 - 51/push-ECX - 68/push _test-output-buffered-file/imm32 - # . . call - e8/call emit/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # flush(_test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - # . . call - e8/call flush/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # the '/foo' will have no impact on the output - # check-stream-equal(_test-output-stream, "fe ff ", msg) - # . . push args - 68/push "F - test-emit-number-with-metadata"/imm32 - 68/push "fe ff "/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-stream-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-emit-non-number: - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # setup - # . clear-stream(_test-output-stream) - # . . push args - 68/push _test-output-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-buffered-file+4) - # . . push args - b8/copy-to-EAX _test-output-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # (EAX..ECX) = "xyz" - b8/copy-to-EAX "xyz"/imm32 - 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX - 05/add-to-EAX 4/imm32 - # var slice/ECX = {EAX, ECX} - 51/push-ECX - 50/push-EAX - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # emit(_test-output-buffered-file, slice, 2) - # . . push args - 68/push 2/imm32 - 51/push-ECX - 68/push _test-output-buffered-file/imm32 - # . . call - e8/call emit/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # flush(_test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - # . . call - e8/call flush/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # check-stream-equal(_test-output-stream, "xyz", msg) - # . . push args - 68/push "F - test-emit-non-number"/imm32 - 68/push "xyz "/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-stream-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-emit-non-number-with-metadata: - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # setup - # . clear-stream(_test-output-stream) - # . . push args - 68/push _test-output-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-buffered-file+4) - # . . push args - b8/copy-to-EAX _test-output-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # (EAX..ECX) = "xyz/" - b8/copy-to-EAX "xyz/"/imm32 - 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX - 05/add-to-EAX 4/imm32 - # var slice/ECX = {EAX, ECX} - 51/push-ECX - 50/push-EAX - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # emit(_test-output-buffered-file, slice, 2) - # . . push args - 68/push 2/imm32 - 51/push-ECX - 68/push _test-output-buffered-file/imm32 - # . . call - e8/call emit/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # flush(_test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - # . . call - e8/call flush/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # check-stream-equal(_test-output-stream, "xyz/", msg) - # . . push args - 68/push "F - test-emit-non-number-with-metadata"/imm32 - 68/push "xyz/ "/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-stream-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-emit-non-number-with-all-hex-digits-and-metadata: - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # setup - # . clear-stream(_test-output-stream) - # . . push args - 68/push _test-output-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-buffered-file+4) - # . . push args - b8/copy-to-EAX _test-output-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # (EAX..ECX) = "abcd/xyz" - b8/copy-to-EAX "abcd/xyz"/imm32 - 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX - 05/add-to-EAX 4/imm32 - # var slice/ECX = {EAX, ECX} - 51/push-ECX - 50/push-EAX - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # emit(_test-output-buffered-file, slice, 2) - # . . push args - 68/push 2/imm32 - 51/push-ECX - 68/push _test-output-buffered-file/imm32 - # . . call - e8/call emit/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # flush(_test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - # . . call - e8/call flush/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -#? # dump output {{{ -#? # . write(2/stderr, "^") -#? # . . push args -#? 68/push "^"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write-stream(2/stderr, _test-output-stream) -#? # . . push args -#? 68/push _test-output-stream/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write-stream/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write(2/stderr, "$\n") -#? # . . push args -#? 68/push "$\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # }}} - # check-stream-equal(_test-output-stream, "abcd/xyz") - # . . push args - 68/push "F - test-emit-non-number-with-all-hex-digits"/imm32 - 68/push "abcd/xyz "/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-stream-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -# conditions for 'valid' names that are not at risk of looking like hex numbers -# keep in sync with the rules in labels.cc -#: - if it starts with a digit, it's treated as a number. If it can't be -#: parsed as hex it will raise an error. -#: - if it starts with '-' it's treated as a number. -#: - if it starts with '0x' it's treated as a number. (redundant) -#: - if it's two characters long, it can't be a name. Either it's a hex -#: byte, or it raises an error. -is-valid-name?: # in : (address slice) -> EAX : boolean - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # . save registers - 51/push-ECX - 56/push-ESI - # ESI = in - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 6/r32/ESI 8/disp8 . # copy *(EBP+8) to ESI - # start/ECX = in->start - 8b/copy 0/mod/indirect 6/rm32/ESI . . . 1/r32/ECX . . # copy *ESI to ECX - # end/EAX = in->end - 8b/copy 1/mod/*+disp8 6/rm32/ESI . . . 0/r32/EAX 4/disp8 . # copy *(ESI+4) to EAX -$is-valid-name?:check0: - # if (start >= end) return false - 39/compare 3/mod/direct 1/rm32/ECX . . . 0/r32/EAX . . # compare ECX with EAX - 73/jump-if-greater-or-equal-unsigned $is-valid-name?:false/disp8 -$is-valid-name?:check1: - # EAX -= ECX - 29/subtract 3/mod/direct 0/rm32/EAX . . . 1/r32/ECX . . # subtract ECX from EAX - # if (EAX == 2) return false - 3d/compare-EAX-and 2/imm32 - 74/jump-if-equal $is-valid-name?:false/disp8 -$is-valid-name?:check2: - # c/EAX = *ECX - 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX - 8a/copy-byte 0/mod/indirect 1/rm32/ECX . . . 0/r32/AL . . # copy byte at *ECX to AL - # if (c == "-") return false - 3d/compare-EAX-and 2d/imm32/- - 74/jump-if-equal $is-valid-name?:false/disp8 -$is-valid-name?:check3a: - # if (c < "0") return true - 3d/compare-EAX-with 30/imm32/0 - 7c/jump-if-lesser $is-valid-name?:true/disp8 -$is-valid-name?:check3b: - # if (c > "9") return true - 3d/compare-EAX-with 39/imm32/9 - 7f/jump-if-greater $is-valid-name?:true/disp8 -$is-valid-name?:false: - # return false - b8/copy-to-EAX 0/imm32/false - eb/jump $is-valid-name?:end/disp8 -$is-valid-name?:true: - # return true - b8/copy-to-EAX 1/imm32/true -$is-valid-name?:end: - # . restore registers - 5e/pop-to-ESI - 59/pop-to-ECX - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-is-valid-name-digit-prefix: - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # (EAX..ECX) = "34" - b8/copy-to-EAX "34"/imm32 - 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX - 05/add-to-EAX 4/imm32 - # var slice/ECX = {EAX, ECX} - 51/push-ECX - 50/push-EAX - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # EAX = is-valid-name?(slice) - # . . push args - 51/push-ECX - # . . call - e8/call is-valid-name?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # check-ints-equal(EAX, 0, msg) - # . . push args - 68/push "F - test-is-valid-name-digit-prefix"/imm32 - 68/push 0/imm32/false - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-is-valid-name-negative-prefix: - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # (EAX..ECX) = "-0x34" - b8/copy-to-EAX "-0x34"/imm32 - 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX - 05/add-to-EAX 4/imm32 - # var slice/ECX = {EAX, ECX} - 51/push-ECX - 50/push-EAX - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # EAX = is-valid-name?(slice) - # . . push args - 51/push-ECX - # . . call - e8/call is-valid-name?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # check-ints-equal(EAX, 0, msg) - # . . push args - 68/push "F - test-is-valid-name-negative-prefix"/imm32 - 68/push 0/imm32/false - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-is-valid-name-0x-prefix: - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # (EAX..ECX) = "0x34" - b8/copy-to-EAX "0x34"/imm32 - 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX - 05/add-to-EAX 4/imm32 - # var slice/ECX = {EAX, ECX} - 51/push-ECX - 50/push-EAX - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # EAX = is-valid-name?(slice) - # . . push args - 51/push-ECX - # . . call - e8/call is-valid-name?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # check-ints-equal(EAX, 0, msg) - # . . push args - 68/push "F - test-is-valid-name-0x-prefix"/imm32 - 68/push 0/imm32/false - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-is-valid-name-starts-with-pre-digit: - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # (EAX..ECX) = "/03" - b8/copy-to-EAX "/03"/imm32 - 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX - 05/add-to-EAX 4/imm32 - # var slice/ECX = {EAX, ECX} - 51/push-ECX - 50/push-EAX - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # EAX = is-valid-name?(slice) - # . . push args - 51/push-ECX - # . . call - e8/call is-valid-name?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # check-ints-equal(EAX, 1, msg) - # . . push args - 68/push "F - test-is-valid-name-starts-with-pre-digit"/imm32 - 68/push 1/imm32/true - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-is-valid-name-starts-with-post-digit: - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # (EAX..ECX) = "q34" - b8/copy-to-EAX "q34"/imm32 - 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX - 05/add-to-EAX 4/imm32 - # var slice/ECX = {EAX, ECX} - 51/push-ECX - 50/push-EAX - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # EAX = is-valid-name?(slice) - # . . push args - 51/push-ECX - # . . call - e8/call is-valid-name?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # check-ints-equal(EAX, 1, msg) - # . . push args - 68/push "F - test-is-valid-name-starts-with-post-digit"/imm32 - 68/push 1/imm32/true - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-is-valid-name-starts-with-digit: - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # (EAX..ECX) = "0x34" - b8/copy-to-EAX "0x34"/imm32 - 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX - 05/add-to-EAX 4/imm32 - # var slice/ECX = {EAX, ECX} - 51/push-ECX - 50/push-EAX - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # EAX = is-valid-name?(slice) - # . . push args - 51/push-ECX - # . . call - e8/call is-valid-name?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # check-ints-equal(EAX, 0, msg) - # . . push args - 68/push "F - test-is-valid-name-starts-with-digit"/imm32 - 68/push 0/imm32/false - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -# print 'n' in hex in 'width' bytes in lower-endian order, with a space after every byte -emit-hex: # out : (address buffered-file), n : int, width : int -> <void> - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # . save registers - 50/push-EAX - 51/push-ECX - 52/push-EDX - 53/push-EBX - 57/push-EDI - # EDI = out - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 7/r32/EDI 8/disp8 . # copy *(EBP+8) to EDI - # EBX = n - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 3/r32/EBX 0xc/disp8 . # copy *(EBP+12) to EBX - # EDX = width - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 2/r32/EDX 0x10/disp8 . # copy *(EBP+16) to EDX - # var curr/ECX = 0 - 31/xor 3/mod/direct 1/rm32/ECX . . . 1/r32/ECX . . # clear ECX -$emit-hex:loop: - # if (curr >= width) break - 39/compare 3/mod/direct 1/rm32/ECX . . . 2/r32/EDX . . # compare ECX with EDX - 7d/jump-if-greater-or-equal $emit-hex:end/disp8 - # print-byte-buffered(out, EBX) - # . . push args - 53/push-EBX - 57/push-EDI - # . . call - e8/call print-byte-buffered/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # write-byte-buffered(out, ' ') - # . . push args - 68/push 0x20/imm32/space - 57/push-EDI - # . . call - e8/call write-byte-buffered/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # EBX = EBX >> 8 - c1/shift 5/subop/logic-right 3/mod/direct 3/rm32/EBX . . . . . 8/imm8 # shift EBX right by 8 bits, while padding zeroes -$emit-hex:continue: - # ++curr - 41/increment-ECX - eb/jump $emit-hex:loop/disp8 -$emit-hex:end: - # . restore registers - 5f/pop-to-EDI - 5b/pop-to-EBX - 5a/pop-to-EDX - 59/pop-to-ECX - 58/pop-to-EAX - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-emit-hex-single-byte: - # setup - # . clear-stream(_test-output-stream) - # . . push args - 68/push _test-output-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-buffered-file+4) - # . . push args - b8/copy-to-EAX _test-output-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # emit-hex(_test-output-buffered-file, 0xab, 1) - # . . push args - 68/push 1/imm32 - 68/push 0xab/imm32 - 68/push _test-output-buffered-file/imm32 - # . . call - e8/call emit-hex/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # flush(_test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - # . . call - e8/call flush/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # check-ints-equal(*_test-output-stream->data, 'ab ', msg) - # . . push args - 68/push "F - test-emit-hex-single-byte"/imm32 - 68/push 0x206261/imm32 - # . . push *_test-output-stream->data - b8/copy-to-EAX _test-output-stream/imm32 - ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 0xc/disp8 . # push *(EAX+12) - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . end - c3/return - -test-emit-hex-multiple-byte: - # setup - # . clear-stream(_test-output-stream) - # . . push args - 68/push _test-output-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-buffered-file+4) - # . . push args - b8/copy-to-EAX _test-output-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # emit-hex(_test-output-buffered-file, 0x1234, 2) - # . . push args - 68/push 2/imm32 - 68/push 0x1234/imm32 - 68/push _test-output-buffered-file/imm32 - # . . call - e8/call emit-hex/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # flush(_test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - # . . call - e8/call flush/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # check-stream-equal(_test-output-stream, "34 12 ", msg) - # . . push args - 68/push "F - test-emit-hex-multiple-byte/1"/imm32 - 68/push "34 12 "/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-stream-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . end - c3/return - -test-emit-hex-zero-pad: - # setup - # . clear-stream(_test-output-stream) - # . . push args - 68/push _test-output-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-buffered-file+4) - # . . push args - b8/copy-to-EAX _test-output-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # emit-hex(_test-output-buffered-file, 0xab, 2) - # . . push args - 68/push 2/imm32 - 68/push 0xab/imm32 - 68/push _test-output-buffered-file/imm32 - # . . call - e8/call emit-hex/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # flush(_test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - # . . call - e8/call flush/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # check(_test-output-stream->data == 'ab 00 ') - # . . push args - 68/push "F - test-emit-hex-zero-pad/1"/imm32 - 68/push "ab 00 "/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-stream-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . end - c3/return - -test-emit-hex-negative: - # setup - # . clear-stream(_test-output-stream) - # . . push args - 68/push _test-output-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-buffered-file+4) - # . . push args - b8/copy-to-EAX _test-output-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # emit-hex(_test-output-buffered-file, -1, 2) - # . . push args - 68/push 2/imm32 - 68/push -1/imm32 - 68/push _test-output-buffered-file/imm32 - # . . call - e8/call emit-hex/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # flush(_test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - # . . call - e8/call flush/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # check-stream-equal(_test-output-stream == "ff ff ") - # . . push args - 68/push "F - test-emit-hex-negative/1"/imm32 - 68/push "ff ff "/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-stream-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . end - c3/return - -# print 'arr' in hex with a space after every byte -emit-hex-array: # out : (address buffered-file), arr : (address array byte) -> <void> - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # . save registers - 50/push-EAX - 51/push-ECX - 52/push-EDX - 57/push-EDI - # EDI = out - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 7/r32/EDI 8/disp8 . # copy *(EBP+8) to EDI - # EDX = arr # <== 0xbdffffe4 - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 2/r32/EDX 0xc/disp8 . # copy *(EBP+12) to EDX - # curr/ECX = arr->data - 8d/copy-address 1/mod/*+disp8 2/rm32/EDX . . . 1/r32/ECX 4/disp8 . # copy EDX+4 to ECX - # max/EDX = arr->data + arr->length - 8b/copy 0/mod/indirect 2/rm32/EDX . . . 2/r32/EDX . . # copy *EDX to EDX - 01/add 3/mod/direct 2/rm32/EDX . . . 1/r32/ECX . . # add ECX to EDX - # EAX = 0 - 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX -$emit-hex-array:loop: - # if (curr >= width) break - 39/compare 3/mod/direct 1/rm32/ECX . . . 2/r32/EDX . . # compare ECX with EDX - 73/jump-if-greater-or-equal-unsigned $emit-hex-array:end/disp8 - # emit-hex(out, *curr, width=1) - # . . push args - 68/push 1/imm32/width - 8a/copy-byte 0/mod/indirect 1/rm32/ECX . . . 0/r32/AL . . # copy byte at *ECX to AL - 50/push-EAX - 57/push-EDI - # . . call - e8/call emit-hex/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # ++curr - 41/increment-ECX - eb/jump $emit-hex-array:loop/disp8 -$emit-hex-array:end: - # . restore registers - 5f/pop-to-EDI - 5a/pop-to-EDX - 59/pop-to-ECX - 58/pop-to-EAX - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-emit-hex-array: - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # setup - # . clear-stream(_test-output-stream) - # . . push args - 68/push _test-output-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-buffered-file+4) - # . . push args - b8/copy-to-EAX _test-output-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # var arr/ECX (address array byte) = [01, 02, 03] - 68/push 0x00030201/imm32 # bytes 01 02 03 - 68/push 3/imm32/length - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # emit-hex-array(_test-output-buffered-file, arr) - # . . push args - 51/push-ECX - 68/push _test-output-buffered-file/imm32 - # . . call - e8/call emit-hex-array/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . flush(_test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - # . . call - e8/call flush/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -#? # dump output {{{ -#? # . write(2/stderr, "result: ^") -#? # . . push args -#? 68/push "result: ^"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write-stream(2/stderr, _test-output-stream) -#? # . . push args -#? 68/push _test-output-stream/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write-stream/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write(2/stderr, "$\n") -#? # . . push args -#? 68/push "$\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . rewind-stream(_test-output-stream) -#? # . . push args -#? 68/push _test-output-stream/imm32 -#? # . . call -#? e8/call rewind-stream/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -#? # }}} - # check-next-stream-line-equal(_test-output-stream, "01 02 03 ", msg) - # . . push args - 68/push "F - test-emit-hex-array"/imm32 - 68/push "01 02 03 "/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-next-stream-line-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -compute-width: # word : (address array byte) -> EAX : int - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # . save registers - 51/push-ECX - # EAX = word - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 0/r32/EAX 8/disp8 . # copy *(EBP+8) to ECX - # ECX = word + word->length - 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX - # EAX = word->data - 05/add-to-EAX 4/imm32 - # var in/ECX : (address slice) = {EAX, ECX} - 51/push-ECX - 50/push-EAX - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # return compute-width-of-slice(ECX) - # . . push args - 51/push-ECX - # . . call - e8/call compute-width-of-slice/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -$compute-width:end: - # . reclaim locals - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . restore registers - 59/pop-to-ECX - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -compute-width-of-slice: # s : (address slice) -> EAX : int - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # . save registers - 51/push-ECX - # ECX = s - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 1/r32/ECX 8/disp8 . # copy *(EBP+8) to ECX - # if (has-metadata?(word, "imm32")) return 4 - # . EAX = has-metadata?(word, "imm32") - # . . push args - 68/push "imm32"/imm32 - 51/push-ECX - # . . call - e8/call has-metadata?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . if (EAX != 0) return 4 - 3d/compare-EAX-and 0/imm32 - b8/copy-to-EAX 4/imm32 # ZF is set, so we can overwrite EAX now - 75/jump-if-not-equal $compute-width-of-slice:end/disp8 - # if (has-metadata?(word, "disp32")) return 4 - # . EAX = has-metadata?(word, "disp32") - # . . push args - 68/push "disp32"/imm32 - 51/push-ECX - # . . call - e8/call has-metadata?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . if (EAX != 0) return 4 - 3d/compare-EAX-and 0/imm32 - b8/copy-to-EAX 4/imm32 # ZF is set, so we can overwrite EAX now - 75/jump-if-not-equal $compute-width-of-slice:end/disp8 - # if (has-metadata?(word, "imm16")) return 2 - # . EAX = has-metadata?(word, "imm16") - # . . push args - 68/push "imm16"/imm32 - 51/push-ECX - # . . call - e8/call has-metadata?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . if (EAX != 0) return 2 - 3d/compare-EAX-and 0/imm32 - b8/copy-to-EAX 2/imm32 # ZF is set, so we can overwrite EAX now - 75/jump-if-not-equal $compute-width-of-slice:end/disp8 - # if (has-metadata?(word, "disp16")) return 2 - # . EAX = has-metadata?(word, "disp16") - # . . push args - 68/push "disp16"/imm32 - 51/push-ECX - # . . call - e8/call has-metadata?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . if (EAX != 0) return 2 - 3d/compare-EAX-and 0/imm32 - b8/copy-to-EAX 2/imm32 # ZF is set, so we can overwrite EAX now - 75/jump-if-not-equal $compute-width-of-slice:end/disp8 - # otherwise return 1 - b8/copy-to-EAX 1/imm32 -$compute-width-of-slice:end: - # . restore registers - 59/pop-to-ECX - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-compute-width: - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -$test-compute-width:imm8: - # EAX = compute-width("0x2/imm8") - # . . push args - 68/push "0x2/imm8"/imm32 - # . . call - e8/call compute-width/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # check-ints-equal(EAX, 1, msg) - # . . push args - 68/push "F - test-compute-width: 0x2/imm8"/imm32 - 50/push-EAX - 68/push 1/imm32 - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -$test-compute-width:imm16: - # EAX = compute-width("4/imm16") - # . . push args - 68/push "4/imm16"/imm32 - # . . call - e8/call compute-width/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # check-ints-equal(EAX, 2, msg) - # . . push args - 68/push "F - test-compute-width: 4/imm16"/imm32 - 50/push-EAX - 68/push 2/imm32 - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -$test-compute-width:imm32: - # EAX = compute-width("4/imm32") - # . . push args - 68/push "4/imm32"/imm32 - # . . call - e8/call compute-width/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # check-ints-equal(EAX, 4, msg) - # . . push args - 68/push "F - test-compute-width: 4/imm32"/imm32 - 50/push-EAX - 68/push 4/imm32 - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -$test-compute-width:disp8: - # EAX = compute-width("foo/disp8") - # . . push args - 68/push "foo/disp8"/imm32 - # . . call - e8/call compute-width/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # check-ints-equal(EAX, 1, msg) - # . . push args - 68/push "F - test-compute-width: foo/disp8"/imm32 - 50/push-EAX - 68/push 1/imm32 - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -$test-compute-width:disp16: - # EAX = compute-width("foo/disp16") - # . . push args - 68/push "foo/disp16"/imm32 - # . . call - e8/call compute-width/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # check-ints-equal(EAX, 2, msg) - # . . push args - 68/push "F - test-compute-width: foo/disp16"/imm32 - 50/push-EAX - 68/push 2/imm32 - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -$test-compute-width:disp32: - # EAX = compute-width("foo/disp32") - # . . push args - 68/push "foo/disp32"/imm32 - # . . call - e8/call compute-width/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # check-ints-equal(EAX, 4, msg) - # . . push args - 68/push "F - test-compute-width: foo/disp32"/imm32 - 50/push-EAX - 68/push 4/imm32 - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -$test-compute-width:no-metadata: - # EAX = compute-width("45") - # . . push args - 68/push "45"/imm32 - # . . call - e8/call compute-width/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # check-ints-equal(EAX, 1, msg) - # . . push args - 68/push "F - test-compute-width: 45 (no metadata)"/imm32 - 50/push-EAX - 68/push 1/imm32 - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -is-label?: # word : (address slice) -> EAX : boolean - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # . save registers - 51/push-ECX - # ECX = word - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 1/r32/ECX 8/disp8 . # copy *(EBP+8) to ECX - # ECX = word->end - 8b/copy 1/mod/*+disp8 1/rm32/ECX . . . 1/r32/ECX 4/disp8 . # copy *(ECX+4) to ECX - # return *(word->end - 1) == ':' - # . EAX = 0 - 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX - # . EAX = *((char *) word->end - 1) - 8a/copy-byte 1/mod/*+disp8 1/rm32/ECX . . . 0/r32/AL -1/disp8 . # copy byte at *(ECX-1) to AL - # . return (EAX == ':') - 3d/compare-EAX-and 0x3a/imm32/colon - b8/copy-to-EAX 1/imm32/true - 74/jump-if-equal $is-label?:end/disp8 - b8/copy-to-EAX 0/imm32/false -$is-label?:end: - # . restore registers - 59/pop-to-ECX - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-is-label?: - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -$test-is-label?:true: - # (EAX..ECX) = "AAA:" - b8/copy-to-EAX "AAA:"/imm32 - 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX - 05/add-to-EAX 4/imm32 - # var slice/ECX = {EAX, ECX} - 51/push-ECX - 50/push-EAX - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # is-label?(slice/ECX) - # . . push args - 51/push-ECX - # . . call - e8/call is-label?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # check-ints-equal(EAX, 1, msg) - # . . push args - 68/push "F - test-is-label?:true"/imm32 - 68/push 1/imm32 - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -$test-is-label?:false: - # (EAX..ECX) = "AAA" - b8/copy-to-EAX "AAA"/imm32 - 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX - 05/add-to-EAX 4/imm32 - # var slice/ECX = {EAX, ECX} - 51/push-ECX - 50/push-EAX - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # is-label?(slice/ECX) - # . . push args - 51/push-ECX - # . . call - e8/call is-label?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # check-ints-equal(EAX, 0, msg) - # . . push args - 68/push "F - test-is-label?:false"/imm32 - 68/push 0/imm32 - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -== data - -_test-input-stream: - # current write index - 0/imm32 - # current read index - 0/imm32 - # length - 0x100/imm32 # 256 bytes - # data (16 lines x 16 bytes/line) - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - -# a test buffered file for _test-input-stream -_test-input-buffered-file: - # file descriptor or (address stream) - _test-input-stream/imm32 - # current write index - 0/imm32 - # current read index - 0/imm32 - # length - 6/imm32 - # data - 00 00 00 00 00 00 # 6 bytes - -_test-output-stream: - # current write index - 0/imm32 - # current read index - 0/imm32 - # length - 0x200/imm32 # 512 bytes - # data (32 lines x 16 bytes/line) - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - -# a test buffered file for _test-output-stream -_test-output-buffered-file: - # file descriptor or (address stream) - _test-output-stream/imm32 - # current write index - 0/imm32 - # current read index - 0/imm32 - # length - 6/imm32 - # data - 00 00 00 00 00 00 # 6 bytes - -_test-data-segment: - 64/d 61/a 74/t 61/a -_test-data-segment-end: - -# . . vim:nowrap:textwidth=0 diff --git a/subx/apps/survey b/subx/apps/survey deleted file mode 100755 index 621efa5c..00000000 --- a/subx/apps/survey +++ /dev/null Binary files differdiff --git a/subx/apps/survey.subx b/subx/apps/survey.subx deleted file mode 100644 index 15957fa2..00000000 --- a/subx/apps/survey.subx +++ /dev/null @@ -1,4787 +0,0 @@ -# Assign addresses (co-ordinates) to instructions (landmarks) in a program -# (landscape). -# Use the addresses assigned to: -# a) replace labels -# b) add segment headers with addresses and offsets correctly filled in -# -# To build (from the subx/ directory): -# $ ./subx translate *.subx apps/survey.subx -o apps/survey -# -# The expected input is a stream of bytes with segment headers, comments and -# some interspersed labels. -# $ cat x -# == code 0x1 -# l1: -# aa bb l1/imm8 -# cc dd l2/disp32 -# l2: -# ee foo/imm32 -# == data 0x10 -# foo: -# 00 -# -# The output is the stream of bytes without segment headers or label definitions, -# and with label references replaced with numeric values/displacements. -# -# $ cat x |./subx run apps/assort -# ...ELF header bytes... -# # ELF header above will specify that code segment begins at this offset -# aa bb nn # some computed address -# cc dd nn nn nn nn # some computed displacement -# ee nn nn nn nn # some computed address -# # ELF header above will specify that data segment begins at this offset -# 00 -# -# The ELF format has some persnickety constraints on the starting addresses of -# segments, so input headers are treated as guidelines and adjusted in the -# output. - -== code -# instruction effective address register displacement immediate -# . op subop mod rm32 base index scale r32 -# . 1-3 bytes 3 bits 2 bits 3 bits 3 bits 3 bits 2 bits 2 bits 0/1/2/4 bytes 0/1/2/4 bytes - -Entry: - # Heap = new-segment(Heap-size) - # . . push args - 68/push Heap/imm32 - ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Heap-size/disp32 # push *Heap-size - # . . call - e8/call new-segment/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # initialize-trace-stream(256KB) - # . . push args - 68/push 0x40000/imm32/256KB - # . . call - e8/call initialize-trace-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - - # run tests if necessary, convert stdin if not - # . prolog - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # initialize heap - # - if argc > 1 and argv[1] == "test", then return run_tests() - # . argc > 1 - 81 7/subop/compare 1/mod/*+disp8 5/rm32/EBP . . . . 0/disp8 1/imm32 # compare *EBP - 7e/jump-if-lesser-or-equal $run-main/disp8 - # . argv[1] == "test" - # . . push args - 68/push "test"/imm32 - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - # . . call - e8/call kernel-string-equal?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . check result - 3d/compare-EAX-and 1/imm32 - 75/jump-if-not-equal $run-main/disp8 - # . run-tests() - e8/call run-tests/disp32 - 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 3/r32/EBX Num-test-failures/disp32 # copy *Num-test-failures to EBX - eb/jump $main:end/disp8 -$run-main: - # - otherwise convert stdin - # convert(Stdin, Stdout) - # . . push args - 68/push Stdout/imm32 - 68/push Stdin/imm32 - # . . call - e8/call convert/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write-stream(2/stderr, Trace-stream) -#? # . . push args -#? ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Trace-stream/disp32 # push *Trace-stream -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write-stream/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . syscall(exit, 0) - bb/copy-to-EBX 0/imm32 -$main:end: - b8/copy-to-EAX 1/imm32/exit - cd/syscall 0x80/imm8 - -# data structures: -# segment-info: {address, file-offset, size} (12 bytes) -# segments: (address stream {string, segment-info}) (16 bytes per row) -# label-info: {segment-name, segment-offset, address} (12 bytes) -# labels: (address stream {string, label-info}) (16 bytes per row) -# these are all inefficient; use sequential scans for lookups - -convert: # infile : (address buffered-file), out : (address buffered-file) -> <void> - # pseudocode - # var in : (address stream byte) = stream(4096) - # slurp(infile, in) - # var segments = new-stream(10 rows, 16 bytes each) - # var labels = new-stream(Max-labels rows, 16 bytes each) - # compute-offsets(in, segments, labels) - # compute-addresses(segments, labels) - # rewind-stream(in) - # emit-output(in, out, segments, labels) - # - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # . save registers - 51/push-ECX - 52/push-EDX - 56/push-ESI - # var segments/ECX = stream(10 * 16) - 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 0xa0/imm32 # subtract from ESP - 68/push 0xa0/imm32/length - 68/push 0/imm32/read - 68/push 0/imm32/write - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # var labels/EDX = stream(Max-labels * 16) - # . data - 2b/subtract 0/mod/indirect 5/rm32/.disp32 . . 4/r32/ESP Max-labels/disp32 # subtract *Max-labels from ESP - # . length - ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Max-labels/disp32 # push *Max-labels - # . read - 68/push 0/imm32/read - # . write - 68/push 0/imm32/write - 89/copy 3/mod/direct 2/rm32/EDX . . . 4/r32/ESP . . # copy ESP to EDX - # var in/ESI = stream(Input-size * 1) - # . data - 2b/subtract 0/mod/indirect 5/rm32/.disp32 . . 4/r32/ESP Input-size/disp32 # subtract *Input-size from ESP - # . length - ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Input-size/disp32 # push *Input-size - # . read - 68/push 0/imm32/read - # . write - 68/push 0/imm32/write - 89/copy 3/mod/direct 6/rm32/ESI . . . 4/r32/ESP . . # copy ESP to ESI -#? # dump labels->write {{{ -#? # . write(2/stderr, "labels->write right after initialization: ") -#? # . . push args -#? 68/push "labels->write right after initialization: "/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . clear-stream(Stderr+4) -#? # . . save EAX -#? 50/push-EAX -#? # . . push args -#? b8/copy-to-EAX Stderr/imm32 -#? 05/add-to-EAX 4/imm32 -#? 50/push-EAX -#? # . . call -#? e8/call clear-stream/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -#? # . . restore EAX -#? 58/pop-to-EAX -#? # . print-int32-buffered(Stderr, labels->write) -#? # . . push args -#? $watch-1: -#? ff 6/subop/push 0/mod/indirect 2/rm32/EDX . . . . . . # push *EDX -#? 68/push Stderr/imm32 -#? # . . call -#? e8/call print-int32-buffered/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . flush(Stderr) -#? # . . push args -#? 68/push Stderr/imm32 -#? # . . call -#? e8/call flush/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -#? # . write(2/stderr, "\n") -#? # . . push args -#? 68/push "\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # }}} -#? # write(2/stderr, "slurp in\n") {{{ -#? # . . push args -#? 68/push "slurp in\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # }}} - # slurp(infile, in) - # . . push args - 56/push-ESI - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - # . . call - e8/call slurp/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # dump labels->write {{{ -#? # . write(2/stderr, "labels->write after slurp: ") -#? # . . push args -#? 68/push "labels->write after slurp: "/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . clear-stream(Stderr+4) -#? # . . save EAX -#? 50/push-EAX -#? # . . push args -#? b8/copy-to-EAX Stderr/imm32 -#? 05/add-to-EAX 4/imm32 -#? 50/push-EAX -#? # . . call -#? e8/call clear-stream/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -#? # . . restore EAX -#? 58/pop-to-EAX -#? # . print-int32-buffered(Stderr, labels->write) -#? # . . push args -#? ff 6/subop/push 0/mod/indirect 2/rm32/EDX . . . . . . # push *EDX -#? 68/push Stderr/imm32 -#? # . . call -#? e8/call print-int32-buffered/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . flush(Stderr) -#? # . . push args -#? 68/push Stderr/imm32 -#? # . . call -#? e8/call flush/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -#? # . write(2/stderr, "\n") -#? # . . push args -#? 68/push "\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # }}} -#? # dump in {{{ -#? # . write(2/stderr, "in: ") -#? # . . push args -#? 68/push "in: "/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # write-stream(2/stderr, in) -#? # . . push args -#? 56/push-ESI -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write-stream/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write(2/stderr, "$\n") -#? # . . push args -#? 68/push "$\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . rewind-stream(in) -#? # . . push args -#? 56/push-ESI -#? # . . call -#? e8/call rewind-stream/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -#? # }}} -#? # write(2/stderr, "compute-offsets\n") {{{ -#? # . . push args -#? 68/push "compute-offsets\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # }}} - # compute-offsets(in, segments, labels) - # . . push args - 52/push-EDX - 51/push-ECX - 56/push-ESI - # . . call - e8/call compute-offsets/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -#? # write(2/stderr, "compute-addresses\n") {{{ -#? # . . push args -#? 68/push "compute-addresses\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # }}} - # compute-addresses(segments, labels) - # . . push args - 52/push-EDX - 51/push-ECX - # . . call - e8/call compute-addresses/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x8/imm32 # add to ESP - # rewind-stream(in) - # . . push args - 56/push-ESI - # . . call - e8/call rewind-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -#? # write(2/stderr, "emit-output\n") {{{ -#? # . . push args -#? 68/push "emit-output\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # }}} -#? # dump *Trace-stream {{{ -#? # . write(2/stderr, "^") -#? # . . push args -#? 68/push "^"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write-stream(2/stderr, *Trace-stream) -#? # . . push args -#? ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Trace-stream/disp32 # push *Trace-stream -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write-stream/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write(2/stderr, "$\n") -#? # . . push args -#? 68/push "$\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # }}} -#? # dump labels->write {{{ -#? # . write(2/stderr, "labels->write after rewinding input: ") -#? # . . push args -#? 68/push "labels->write after rewinding input: "/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . clear-stream(Stderr+4) -#? # . . save EAX -#? 50/push-EAX -#? # . . push args -#? b8/copy-to-EAX Stderr/imm32 -#? 05/add-to-EAX 4/imm32 -#? 50/push-EAX -#? # . . call -#? e8/call clear-stream/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -#? # . . restore EAX -#? 58/pop-to-EAX -#? # . print-int32-buffered(Stderr, labels) -#? # . . push args -#? ff 6/subop/push 0/mod/indirect 2/rm32/EDX . . . . . . # push *EDX -#? 68/push Stderr/imm32 -#? # . . call -#? e8/call print-int32-buffered/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . flush(Stderr) -#? # . . push args -#? 68/push Stderr/imm32 -#? # . . call -#? e8/call flush/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -#? # . write(2/stderr, "\n") -#? # . . push args -#? 68/push "\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # }}} - # emit-output(in, out, segments, labels) - # . . push args - 52/push-EDX - 51/push-ECX - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - 56/push-ESI - # . . call - e8/call emit-output/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP - # flush(out) - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - # . . call - e8/call flush/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -$convert:end: - # . reclaim locals - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x30a0/imm32 # add to ESP - # . restore registers - 5e/pop-to-ESI - 5a/pop-to-EDX - 59/pop-to-ECX - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-convert-computes-addresses: - # input: - # == code 0x1 - # Entry: - # ab x/imm32 - # == data 0x1000 - # x: - # 01 - # - # trace contains (in any order): - # label x is at address 0x1079 - # segment code starts at address 0x74 - # segment code has size 5 - # segment data starts at address 0x1079 - # - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # setup - # . clear-stream(_test-input-stream) - # . . push args - 68/push _test-input-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-input-buffered-file+4) - # . . push args - b8/copy-to-EAX _test-input-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-stream) - # . . push args - 68/push _test-output-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-buffered-file+4) - # . . push args - b8/copy-to-EAX _test-output-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # initialize input - # . write(_test-input-stream, "== code 0x1\n") - # . . push args - 68/push "== code 0x1\n"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . write(_test-input-stream, "Entry:\n") - # . . push args - 68/push "Entry:\n"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . write(_test-input-stream, "ab x/imm32\n") - # . . push args - 68/push "ab x/imm32\n"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . write(_test-input-stream, "== data 0x1000\n") - # . . push args - 68/push "== data 0x1000\n"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . write(_test-input-stream, "x:\n") - # . . push args - 68/push "x:\n"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . write(_test-input-stream, "01\n") - # . . push args - 68/push "01\n"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # convert(_test-input-buffered-file, _test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - 68/push _test-input-buffered-file/imm32 - # . . call - e8/call convert/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check trace -#? # dump *Trace-stream {{{ -#? # . write(2/stderr, "^") -#? # . . push args -#? 68/push "^"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write-stream(2/stderr, *Trace-stream) -#? # . . push args -#? ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Trace-stream/disp32 # push *Trace-stream -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write-stream/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write(2/stderr, "$\n") -#? # . . push args -#? 68/push "$\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # }}} - # . check-trace-contains("label 'x' is at address 0x00001079.", msg) - # . . push args - 68/push "F - test-convert-computes-addresses/0"/imm32 - 68/push "label 'x' is at address 0x00001079."/imm32 - # . . call - e8/call check-trace-contains/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . check-trace-contains("segment 'code' starts at address 0x00000074.", msg) - # . . push args - 68/push "F - test-convert-computes-addresses/1"/imm32 - 68/push "segment 'code' starts at address 0x00000074."/imm32 - # . . call - e8/call check-trace-contains/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . check-trace-contains("segment 'code' has size 0x00000005.", msg) - # . . push args - 68/push "F - test-convert-computes-addresses/2"/imm32 - 68/push "segment 'code' has size 0x00000005."/imm32 - # . . call - e8/call check-trace-contains/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . check-trace-contains("segment 'data' starts at address 0x00001079.", msg) - # . . push args - 68/push "F - test-convert-computes-addresses/3"/imm32 - 68/push "segment 'data' starts at address 0x00001079."/imm32 - # . . call - e8/call check-trace-contains/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -# global scratch space for compute-offsets in the data segment -== data - -compute-offsets:file-offset: # int - 0/imm32 -compute-offsets:segment-offset: # int - 0/imm32 -compute-offsets:word-slice: - 0/imm32/start - 0/imm32/end -compute-offsets:segment-tmp: # slice - 0/imm32/start - 0/imm32/end - -== code - -compute-offsets: # in : (address stream), segments : (address stream {string, segment-info}), labels : (address stream {string, label-info}) - # skeleton: - # for lines in 'in' - # for words in line - # switch word - # case 1 - # case 2 - # ... - # default - # - # pseudocode: - # curr-segment-name : (address string) = 0 - # var line = new-stream(512, 1) - # while true # line loop - # clear-stream(line) - # read-line(in, line) - # if (line->write == 0) break # end of file - # while true # word loop - # word-slice = next-word(line) - # if slice-empty?(word-slice) # end of line - # break - # else if slice-starts-with?(word-slice, "#") # comment - # break # end of line - # else if slice-equal?(word-slice, "==") - # if curr-segment-name != 0 - # seg = get-or-insert(segments, curr-segment-name) - # seg->size = *file-offset - seg->file-offset - # trace("segment '", curr-segment-name, "' has size ", seg->size) - # segment-tmp = next-word(line) - # curr-segment-name = slice-to-string(segment-tmp) - # if empty?(curr-segment-name) - # abort - # segment-tmp = next-word(line) - # if slice-empty?(segment-tmp) - # abort - # seg = get-or-insert(segments, curr-segment-name) - # seg->starting-address = parse-hex-int(segment-tmp) - # seg->file-offset = *file-offset - # trace("segment '", curr-segment-name, "' is at file offset ", seg->file-offset) - # segment-offset = 0 - # break (next line) - # else if is-label?(word-slice) - # strip trailing ':' from word-slice - # x : (address label-info) = get-or-insert(labels, name) - # x->segment-name = curr-segment-name - # trace("label '", word-slice, "' is in segment '", curr-segment-name, "'.") - # x->segment-offset = segment-offset - # trace("label '", word-slice, "' is at segment offset ", segment-offset, ".") - # # labels occupy no space, so no need to increment offsets - # else - # width = compute-width-of-slice(word-slice) - # *segment-offset += width - # *file-offset += width - # - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # . save registers - 50/push-EAX - 51/push-ECX - 52/push-EDX - 53/push-EBX - 56/push-ESI - 57/push-EDI - # curr-segment-name/ESI = 0 - 31/xor 3/mod/direct 6/rm32/ESI . . . 6/r32/ESI . . # clear ESI - # file-offset = 0 - c7 0/subop/copy 0/mod/indirect 5/rm32/.disp32 . . . compute-offsets:file-offset/disp32 0/imm32 # copy to *compute-offsets:word-slice - # segment-offset = 0 - c7 0/subop/copy 0/mod/indirect 5/rm32/.disp32 . . . compute-offsets:segment-offset/disp32 0/imm32 # copy to *compute-offsets:word-slice - # line/ECX = new-stream(512, 1) - # . EAX = new-stream(512, 1) - # . . push args - 68/push 1/imm32 - 68/push 0x200/imm32 - 68/push Heap/imm32 - # . . call - e8/call new-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . line/ECX = EAX - 89/copy 3/mod/direct 1/rm32/ECX . . . 0/r32/EAX . . # copy EAX to ECX -$compute-offsets:line-loop: - # clear-stream(line/ECX) - 51/push-ECX - e8/call clear-stream/disp32 - # . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # read-line(in, line/ECX) - 51/push-ECX - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - e8/call read-line/disp32 - # . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # if (line->write == 0) break - 8b/copy 0/mod/indirect 1/rm32/ECX . . . 0/r32/EAX . . # copy *ECX to EAX - 3d/compare-EAX-and 0/imm32 - 0f 84/jump-if-equal $compute-offsets:break-line-loop/disp32 -#? # dump line {{{ -#? # . write(2/stderr, "LL: ") -#? # . . push args -#? 68/push "LL: "/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # write-stream(2/stderr, line) -#? # . . push args -#? 51/push-ECX -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write-stream/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write(2/stderr, "$\n") -#? # . . push args -#? 68/push "$\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . rewind-stream(line) -#? # . . push args -#? 51/push-ECX -#? # . . call -#? e8/call rewind-stream/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -#? # }}} -$compute-offsets:word-loop: - # EDX = word-slice - ba/copy-to-EDX compute-offsets:word-slice/imm32 - # next-word(line/ECX, word-slice/EDX) - 52/push-EDX - 51/push-ECX - e8/call next-word/disp32 - # . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # dump word-slice and maybe curr-segment-name {{{ -#? # . write(2/stderr, "w: ") -#? # . . push args -#? 68/push "w: "/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . clear-stream(Stderr+4) -#? # . . save EAX -#? 50/push-EAX -#? # . . push args -#? b8/copy-to-EAX Stderr/imm32 -#? 05/add-to-EAX 4/imm32 -#? 50/push-EAX -#? # . . call -#? e8/call clear-stream/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -#? # . . restore EAX -#? 58/pop-to-EAX -#? # . write-slice-buffered(Stderr, word-slice) -#? # . . push args -#? 52/push-EDX -#? 68/push Stderr/imm32 -#? # . . call -#? e8/call write-slice-buffered/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . flush(Stderr) -#? # . . push args -#? 68/push Stderr/imm32 -#? # . . call -#? e8/call flush/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -#? # . write(2/stderr, "$\n") -#? # . . push args -#? 68/push "$\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . if (curr-segment-name == 0) print curr-segment-name -#? 81 7/subop/compare 3/mod/direct 6/rm32/ESI . . . . . 0/imm32 # compare ESI -#? 74/jump-if-equal $compute-offsets:case-empty/disp8 -#? # . write(2/stderr, "segment at start of word: ") -#? # . . push args -#? 68/push "segment at start of word: "/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write-buffered(Stderr, curr-segment-name) -#? # . . push args -#? 56/push-ESI -#? 68/push Stderr/imm32 -#? # . . call -#? e8/call write-buffered/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . flush(Stderr) -#? # . . push args -#? 68/push Stderr/imm32 -#? # . . call -#? e8/call flush/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -#? # . write(2/stderr, "$\n") -#? # . . push args -#? 68/push "$\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # }}} -$compute-offsets:case-empty: - # if slice-empty?(word/EDX) break - # . EAX = slice-empty?(word/EDX) - 52/push-EDX - e8/call slice-empty?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . if (EAX != 0) break - 3d/compare-EAX-and 0/imm32 - 0f 85/jump-if-not-equal $compute-offsets:line-loop/disp32 -$compute-offsets:case-comment: - # if slice-starts-with?(word-slice, "#") continue - 68/push "#"/imm32 - 52/push-EDX - e8/call slice-starts-with?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . if (EAX != 0) break - 3d/compare-EAX-and 0/imm32 - 0f 85/jump-if-not-equal $compute-offsets:line-loop/disp32 -$compute-offsets:case-segment-header: - # if (!slice-equal?(word-slice/EDX, "==")) goto next case - # . EAX = slice-equal?(word-slice/EDX, "==") - 68/push "=="/imm32 - 52/push-EDX - e8/call slice-equal?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . if (EAX == 0) goto next case - 3d/compare-EAX-and 0/imm32 - 0f 84/jump-if-equal $compute-offsets:case-label/disp32 - # if (curr-segment-name == 0) goto construct-next-segment - 81 7/subop/compare 3/mod/direct 6/rm32/ESI . . . . . 0/imm32 # compare ESI - 74/jump-if-equal $compute-offsets:construct-next-segment/disp8 - # seg/EAX = get-or-insert(segments, curr-segment-name, row-size=16) - # . . push args - 68/push 0x10/imm32/row-size - 56/push-ESI - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - # . . call - e8/call get-or-insert/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # seg->size = file-offset - seg->file-offset - # . save ECX - 51/push-ECX - # . EBX = *file-offset - 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 3/r32/EBX compute-offsets:file-offset/disp32 # copy *file-offset to EBX - # . ECX = seg->file-offset - 8b/copy 1/mod/*+disp8 0/rm32/EAX . . . 1/r32/ECX 4/disp8 . # copy *(EAX+4) to ECX - # . EBX -= ECX - 29/subtract 3/mod/direct 3/rm32/EBX . . . 1/r32/ECX . . # subtract ECX from EBX - # . seg->size = EBX - 89/copy 1/mod/*+disp8 0/rm32/EAX . . . 3/r32/EBX 8/disp8 . # copy EBX to *(EAX+8) - # . restore ECX - 59/pop-to-ECX - # trace-sssns("segment '", curr-segment-name, "' has size ", seg->size, ".") - # . . push args - 68/push "."/imm32 - 53/push-EBX - 68/push "' has size "/imm32 - 56/push-ESI - 68/push "segment '"/imm32 - # . . call - e8/call trace-sssns/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x14/imm32 # add to ESP -$compute-offsets:construct-next-segment: - # next-word(line/ECX, segment-tmp) - 68/push compute-offsets:segment-tmp/imm32 - 51/push-ECX - e8/call next-word/disp32 - # . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # dump curr-segment-name if not null (clobbering EAX) {{{ -#? # . if (curr-segment-name == 0) goto update-curr-segment-name -#? 81 7/subop/compare 3/mod/direct 6/rm32/ESI . . . . . 0/imm32 # compare ESI -#? 74/jump-if-equal $compute-offsets:update-curr-segment-name/disp8 -#? # . write(2/stderr, "setting segment to: ") -#? # . . push args -#? 68/push "setting segment to: "/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . clear-stream(Stderr+4) -#? # . . push args -#? b8/copy-to-EAX Stderr/imm32 -#? 05/add-to-EAX 4/imm32 -#? 50/push-EAX -#? # . . call -#? e8/call clear-stream/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -#? # . . restore EAX -#? 58/pop-to-EAX -#? # . write-buffered(Stderr, curr-segment-name) -#? # . . push args -#? 56/push-ESI -#? 68/push Stderr/imm32 -#? # . . call -#? e8/call write-buffered/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . flush(Stderr) -#? # . . push args -#? 68/push Stderr/imm32 -#? # . . call -#? e8/call flush/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -#? # . write(2/stderr, "$\n") -#? # . . push args -#? 68/push "$\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # }}} -$compute-offsets:update-curr-segment-name: - # curr-segment-name = slice-to-string(segment-tmp) - # . EAX = slice-to-string(Heap, segment-tmp) - # . . push args - 68/push compute-offsets:segment-tmp/imm32 - 68/push Heap/imm32 - # . . call - e8/call slice-to-string/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . curr-segment-name = EAX - 89/copy 3/mod/direct 6/rm32/ESI . . . 0/r32/EAX . . # copy EAX to ESI - # if empty?(curr-segment-name) abort - # . if (EAX == 0) abort - 3d/compare-EAX-and 0/imm32 - 0f 84/jump-if-equal $compute-offsets:abort/disp32 - # next-word(line/ECX, segment-tmp) - 68/push compute-offsets:segment-tmp/imm32 - 51/push-ECX - e8/call next-word/disp32 - # . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # if slice-empty?(segment-tmp) abort - # . EAX = slice-empty?(segment-tmp) - 68/push compute-offsets:segment-tmp/imm32 - e8/call slice-empty?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . if (EAX != 0) abort - 3d/compare-EAX-and 0/imm32 - 0f 85/jump-if-not-equal $compute-offsets:abort/disp32 - # seg/EBX = get-or-insert(segments, curr-segment-name, row-size=16) - # . . push args - 68/push 0x10/imm32/row-size - 56/push-ESI - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - # . . call - e8/call get-or-insert/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . EBX = EAX - 89/copy 3/mod/direct 3/rm32/EBX . . . 0/r32/EAX . . # copy EAX to EBX - # seg->address = parse-hex-int(segment-tmp) - # . EAX = parse-hex-int(segment-tmp) - 68/push compute-offsets:segment-tmp/imm32 - e8/call parse-hex-int/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . seg->address = EAX - 89/copy 0/mod/indirect 3/rm32/EBX . . . 0/r32/EAX . . # copy EAX to *EBX - # seg->file-offset = *file-offset - 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 0/r32/EAX compute-offsets:file-offset/disp32 # copy *file-offset to EAX - 89/copy 1/mod/*+disp8 3/rm32/EBX . . . 0/r32/EAX 4/disp8 . # copy EAX to *(EBX+4) - # trace-sssns("segment '", curr-segment-name, "' is at file offset ", seg->file-offset, "") - # . . push args - 68/push "."/imm32 - 50/push-EAX - 68/push "' is at file offset "/imm32 - 56/push-ESI - 68/push "segment '"/imm32 - # . . call - e8/call trace-sssns/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x14/imm32 # add to ESP - # segment-offset = 0 - c7 0/subop/copy 0/mod/indirect 5/rm32/.disp32 . . . compute-offsets:segment-offset/disp32 0/imm32 # copy to *segment-offset - # break - e9/jump $compute-offsets:line-loop/disp32 -$compute-offsets:case-label: - # if (!is-label?(word-slice/EDX)) goto next case - # . EAX = is-label?(word-slice/EDX) - # . . push args - 52/push-EDX - # . . call - e8/call is-label?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . if (EAX == 0) goto next case - 3d/compare-EAX-and 0/imm32 - 74/jump-if-equal $compute-offsets:case-default/disp8 - # strip trailing ':' from word-slice - ff 1/subop/decrement 1/mod/*+disp8 2/rm32/EDX . . . . 4/disp8 . # decrement *(EDX+4) - # x/EAX = leaky-get-or-insert-slice(labels, word-slice, row-size=16) - # . . push args - 68/push 0x10/imm32/row-size - 52/push-EDX - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x10/disp8 . # push *(EBP+16) - # . . call - e8/call leaky-get-or-insert-slice/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -$compute-offsets:save-label-offset: - # x->segment-name = curr-segment-name - 89/copy 0/mod/indirect 0/rm32/EAX . . . 6/r32/ESI . . # copy ESI to *EAX - # trace-slsss("label '" word-slice/EDX "' is in segment '" current-segment-name "'.") - # . . push args - 68/push "'."/imm32 - 56/push-ESI - 68/push "' is in segment '"/imm32 - 52/push-EDX - 68/push "label '"/imm32 - # . . call - e8/call trace-slsss/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x14/imm32 # add to ESP - # x->segment-offset = segment-offset - # . EBX = segment-offset - 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 3/r32/EBX compute-offsets:segment-offset/disp32 # copy *segment-offset to EBX - # . x->segment-offset = EBX - 89/copy 1/mod/*+disp8 0/rm32/EAX . . . 3/r32/EBX 4/disp8 . # copy EBX to *(EAX+4) - # trace-slsns("label '" word-slice/EDX "' is at segment offset " *segment-offset/EAX ".") - # . . EAX = file-offset - b8/copy-to-EAX compute-offsets:segment-offset/imm32 - # . . EAX = *file-offset/EAX - 8b/copy 0/mod/indirect 0/rm32/EAX . . . 0/r32/EAX . . # copy *EAX to EAX - # . . push args - 68/push "."/imm32 - 50/push-EAX - 68/push "' is at segment offset "/imm32 - 52/push-EDX - 68/push "label '"/imm32 - # . . call - e8/call trace-slsns/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x14/imm32 # add to ESP - # continue - e9/jump $compute-offsets:word-loop/disp32 -$compute-offsets:case-default: - # width/EAX = compute-width-of-slice(word-slice) - # . . push args - 52/push-EDX - # . . call - e8/call compute-width-of-slice/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # segment-offset += width - 01/add 0/mod/indirect 5/rm32/.disp32 . . 0/r32/EAX compute-offsets:segment-offset/disp32 # add EAX to *segment-offset - # file-offset += width - 01/add 0/mod/indirect 5/rm32/.disp32 . . 0/r32/EAX compute-offsets:file-offset/disp32 # add EAX to *file-offset -#? # dump segment-offset {{{ -#? # . write(2/stderr, "segment-offset: ") -#? # . . push args -#? 68/push "segment-offset: "/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . clear-stream(Stderr+4) -#? # . . save EAX -#? 50/push-EAX -#? # . . push args -#? b8/copy-to-EAX Stderr/imm32 -#? 05/add-to-EAX 4/imm32 -#? 50/push-EAX -#? # . . call -#? e8/call clear-stream/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -#? # . . restore EAX -#? 58/pop-to-EAX -#? # . print-int32-buffered(Stderr, segment-offset) -#? # . . push args -#? 52/push-EDX -#? ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . compute-offsets:segment-offset/disp32 # push *segment-offset -#? 68/push Stderr/imm32 -#? # . . call -#? e8/call print-int32-buffered/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . flush(Stderr) -#? # . . push args -#? 68/push Stderr/imm32 -#? # . . call -#? e8/call flush/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -#? # . write(2/stderr, "\n") -#? # . . push args -#? 68/push "\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # }}} - e9/jump $compute-offsets:word-loop/disp32 -$compute-offsets:break-line-loop: - # seg/EAX = get-or-insert(segments, curr-segment-name, row-size=16) - # . . push args - 68/push 0x10/imm32/row-size - 56/push-ESI - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - # . . call - e8/call get-or-insert/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # seg->size = file-offset - seg->file-offset - # . save ECX - 51/push-ECX - # . EBX = *file-offset - 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 3/r32/EBX compute-offsets:file-offset/disp32 # copy *file-offset to EBX - # . ECX = seg->file-offset - 8b/copy 1/mod/*+disp8 0/rm32/EAX . . . 1/r32/ECX 4/disp8 . # copy *(EAX+4) to ECX - # . EBX -= ECX - 29/subtract 3/mod/direct 3/rm32/EBX . . . 1/r32/ECX . . # subtract ECX from EBX - # . seg->size = EBX - 89/copy 1/mod/*+disp8 0/rm32/EAX . . . 3/r32/EBX 8/disp8 . # copy EBX to *(EAX+8) - # . restore ECX - 59/pop-to-ECX - # trace-sssns("segment '", curr-segment-name, "' has size ", seg->size, ".") - # . trace-sssns("segment '", curr-segment-name, "' has size ", EBX, ".") - # . . push args - 68/push "."/imm32 - 53/push-EBX - 68/push "' has size "/imm32 - 56/push-ESI - 68/push "segment '"/imm32 - # . . call - e8/call trace-sssns/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x14/imm32 # add to ESP -$compute-offsets:end: - # . reclaim locals - # . restore registers - 5f/pop-to-EDI - 5e/pop-to-ESI - 5b/pop-to-EBX - 5a/pop-to-EDX - 59/pop-to-ECX - 58/pop-to-EAX - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return -$compute-offsets:abort: - # . _write(2/stderr, error) - # . . push args - 68/push "'==' must be followed by segment name and segment-start\n"/imm32 - 68/push 2/imm32/stderr - # . . call - e8/call _write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . syscall(exit, 1) - bb/copy-to-EBX 1/imm32 - b8/copy-to-EAX 1/imm32/exit - cd/syscall 0x80/imm8 - # never gets here - -test-compute-offsets: - # input: - # == code 0x1 - # ab x/imm32 # skip comment - # == data 0x1000 - # 00 - # x: - # 34 - # - # trace contains (in any order): - # segment 'code' is at file offset 0x0. - # segment 'code' has size 0x5. - # segment 'data' is at file offset 0x5. - # segment 'data' has size 0x2. - # label 'x' is in segment 'data'. - # label 'x' is at segment offset 0x1. - # - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # setup - # . clear-stream(_test-input-stream) - # . . push args - 68/push _test-input-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # var segments/ECX = stream(2 * 16) - 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 0x20/imm32 # subtract from ESP - 68/push 0x20/imm32/length - 68/push 0/imm32/read - 68/push 0/imm32/write - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # var labels/EDX = stream(2 * 16) - 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 0x20/imm32 # subtract from ESP - 68/push 0x20/imm32/length - 68/push 0/imm32/read - 68/push 0/imm32/write - 89/copy 3/mod/direct 2/rm32/EDX . . . 4/r32/ESP . . # copy ESP to EDX - # initialize input - # . write(_test-input-stream, "== code 0x1\n") - # . . push args - 68/push "== code 0x1\n"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . write(_test-input-stream, "ab x/imm32 # skip comment\n") - # . . push args - 68/push "ab x/imm32 # skip comment\n"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . write(_test-input-stream, "== data 0x1000\n") - # . . push args - 68/push "== data 0x1000\n"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . write(_test-input-stream, "00\n") - # . . push args - 68/push "00\n"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . write(_test-input-stream, "x:\n") - # . . push args - 68/push "x:\n"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . write(_test-input-stream, "34\n") - # . . push args - 68/push "34\n"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # compute-offsets(_test-input-stream, segments, labels) - # . . push args - 52/push-EDX - 51/push-ECX - 68/push _test-input-stream/imm32 - # . . call - e8/call compute-offsets/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -#? # dump *Trace-stream {{{ -#? # . write(2/stderr, "^") -#? # . . push args -#? 68/push "^"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write-stream(2/stderr, *Trace-stream) -#? # . . push args -#? ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Trace-stream/disp32 # push *Trace-stream -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write-stream/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write(2/stderr, "$\n") -#? # . . push args -#? 68/push "$\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # }}} - # check trace - # . check-trace-contains("segment 'code' is at file offset 0x00000000.", msg) - # . . push args - 68/push "F - test-compute-offsets/0"/imm32 - 68/push "segment 'code' is at file offset 0x00000000."/imm32 - # . . call - e8/call check-trace-contains/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . check-trace-contains("segment 'code' has size 0x00000005", msg) - # . . push args - 68/push "F - test-compute-offsets/1"/imm32 - 68/push "segment 'code' has size 0x00000005."/imm32 - # . . call - e8/call check-trace-contains/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . check-trace-contains("segment 'data' is at file offset 0x00000005.", msg) - # . . push args - 68/push "F - test-compute-offsets/2"/imm32 - 68/push "segment 'data' is at file offset 0x00000005."/imm32 - # . . call - e8/call check-trace-contains/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . check-trace-contains("segment 'data' has size 0x00000002.", msg) - # . . push args - 68/push "F - test-compute-offsets/3"/imm32 - 68/push "segment 'data' has size 0x00000002."/imm32 - # . . call - e8/call check-trace-contains/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . check-trace-contains("label 'x' is in segment 'data'.", msg) - # . . push args - 68/push "F - test-compute-offsets/4"/imm32 - 68/push "label 'x' is in segment 'data'."/imm32 - # . . call - e8/call check-trace-contains/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . check-trace-contains("label 'x' is at segment offset 0x00000001.", msg) - # . . push args - 68/push "F - test-compute-offsets/5"/imm32 - 68/push "label 'x' is at segment offset 0x00000001."/imm32 - # . . call - e8/call check-trace-contains/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . check-ints-equal(labels->write, 0x10, msg) - # . . push args - 68/push "F - test-compute-offsets-maintains-labels-write-index"/imm32 - 68/push 0x10/imm32/1-entry - ff 6/subop/push 0/mod/indirect 2/rm32/EDX . . . . . . # push *EDX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -compute-addresses: # segments : (address stream {string, segment-info}), labels : (address stream {string, label-info}) - # pseudocode: - # srow : (address segment-info) = segments->data - # max = segments->data + segments->write - # num-segments = segments->write / 16 - # starting-offset = 0x34 + (num-segments * 0x20) - # while true - # if (srow >= max) break - # s->file-offset += starting-offset - # s->address &= 0xfffff000 # clear last 12 bits for p_align - # s->address += (s->file-offset & 0x00000fff) - # trace-sssns("segment " s->key " starts at address " s->address) - # srow += 16 # row-size - # lrow : (address label-info) = labels->data - # max = labels->data + labels->write - # while true - # if (lrow >= max) break - # seg-name : (address string) = lrow->segment-name - # label-seg : (address segment-info) = get(segments, seg-name, row-size=16) - # lrow->address = label-seg->address + lrow->segment-offset - # trace-sssns("label " lrow->key " is at address " lrow->address) - # lrow += 16 # row-size - # - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # . save registers - 50/push-EAX - 51/push-ECX - 52/push-EDX - 53/push-EBX - 56/push-ESI - 57/push-EDI - # ESI = segments - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 6/r32/ESI 8/disp8 . # copy *(EBP+8) to ESI - # starting-offset/EDI = 0x34 + (num-segments * 0x20) # make room for ELF headers - # . EDI = segments->write / 16 (row-size) - 8b/copy 0/mod/indirect 6/rm32/ESI . . . 7/r32/EDI . . # copy *ESI to EDI - c1/shift 5/subop/logic-right 3/mod/direct 7/rm32/EDI . . . . . 4/imm8 # shift EDI right by 4 bits, while padding zeroes - # . EDI = (EDI * 0x20) + 0x34 - c1/shift 4/subop/left 3/mod/direct 7/rm32/EDI . . . . . 5/imm8 # shift EDI left by 5 bits - 81 0/subop/add 3/mod/direct 7/rm32/EDI . . . . . 0x34/imm32 # add to EDI - # srow/EAX = segments->data - 8d/copy-address 1/mod/*+disp8 6/rm32/ESI . . . 0/r32/EAX 0xc/disp8 . # copy ESI+12 to EAX - # max/ECX = segments->data + segments->write - 8b/copy 0/mod/indirect 6/rm32/ESI . . . 1/r32/ECX . . # copy *ESI to ECX - 01/add 3/mod/direct 1/rm32/ECX . . . 6/r32/ESI . . # add ESI to ECX -$compute-addresses:segment-loop: - # if (srow >= max) break - 39/compare 3/mod/direct 0/rm32/EAX . . . 1/r32/ECX . . # compare EAX with ECX - 73/jump-if-greater-or-equal-unsigned $compute-addresses:segment-break/disp8 - # srow->file-offset += starting-offset - 01/add 1/mod/*+disp8 0/rm32/EAX . . . 7/r32/EDI 8/disp8 . # add EDI to *(EAX+8) - # clear last 12 bits of srow->address for p_align=0x1000 - # . EDX = srow->address - 8b/copy 1/mod/*+disp8 0/rm32/EAX . . . 2/r32/EDX 4/disp8 . # copy *(EAX+4) to EDX - # . EDX &= 0xfffff000 - 81 4/subop/and 3/mod/direct 2/rm32/EDX . . . . . 0xfffff000/imm32 # bitwise and of EDX - # update last 12 bits from srow->file-offset - # . EBX = srow->file-offset - 8b/copy 1/mod/*+disp8 0/rm32/EAX . . . 3/r32/EBX 8/disp8 . # copy *(EAX+8) to EBX - # . EBX &= 0xfff - 81 4/subop/and 3/mod/direct 3/rm32/EBX . . . . . 0x00000fff/imm32 # bitwise and of EBX - # . srow->address = EDX | EBX - 09/or 3/mod/direct 2/rm32/EDX . . . 3/r32/EBX . . # EDX = bitwise OR with EBX - 89/copy 1/mod/*+disp8 0/rm32/EAX . . . 2/r32/EDX 4/disp8 . # copy EDX to *(EAX+4) - # trace-sssns("segment " srow " starts at address " srow->address ".") - # . . push args - 68/push "."/imm32 - 52/push-EDX - 68/push "' starts at address "/imm32 - ff 6/subop/push 0/mod/indirect 0/rm32/EAX . . . . . . # push *EAX - 68/push "segment '"/imm32 - # . . call - e8/call trace-sssns/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x14/imm32 # add to ESP - # srow += 16 # size of row - 05/add-to-EAX 0x10/imm32 - eb/jump $compute-addresses:segment-loop/disp8 -$compute-addresses:segment-break: -#? # dump *Trace-stream {{{ -#? # . write(2/stderr, "^") -#? # . . push args -#? 68/push "^"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write-stream(2/stderr, *Trace-stream) -#? # . . push args -#? ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Trace-stream/disp32 # push *Trace-stream -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write-stream/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write(2/stderr, "$\n") -#? # . . push args -#? 68/push "$\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # }}} - # ESI = labels - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 6/r32/ESI 0xc/disp8 . # copy *(EBP+12) to ESI - # lrow/EAX = labels->data - 8d/copy-address 1/mod/*+disp8 6/rm32/ESI . . . 0/r32/EAX 0xc/disp8 . # copy ESI+12 to EAX - # max/ECX = labels->data + labels->write - 8b/copy 0/mod/indirect 6/rm32/ESI . . . 1/r32/ECX . . # copy *ESI to ECX - 01/add 3/mod/direct 1/rm32/ECX . . . 6/r32/ESI . . # add ESI to ECX -$compute-addresses:label-loop: - # if (lrow >= max) break - 39/compare 3/mod/direct 0/rm32/EAX . . . 1/r32/ECX . . # compare EAX with ECX - 0f 83/jump-if-greater-or-equal-unsigned $compute-addresses:end/disp32 -#? # dump lrow->key {{{ -#? # . write(2/stderr, "label: ") -#? # . . push args -#? 68/push "label: "/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write(2/stderr, lrow->key) -#? # . . push args -#? ff 6/subop/push 0/mod/indirect 0/rm32/EAX . . . . . . # push *EAX -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write(2/stderr, "$\n") -#? # . . push args -#? 68/push "$\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # }}} - # seg-name/EDX = lrow->segment-name - 8b/copy 1/mod/*+disp8 0/rm32/EAX . . . 2/r32/EDX 4/disp8 . # copy *EAX to EDX -#? # dump seg-name {{{ -#? # . write(2/stderr, "compute-addresses: seg-name: ") -#? # . . push args -#? 68/push "seg-name: "/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write(2/stderr, seg-name) -#? # . . push args -#? 52/push-EDX -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write(2/stderr, "$\n") -#? # . . push args -#? 68/push "$\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # }}} - # label-seg/EDX : (address segment-info) = get(segments, seg-name, row-size=16) - # . save EAX - 50/push-EAX - # . EAX = get(segments, seg-name, row-size=16) - # . . push args - 68/push 0x10/imm32/row-size - 52/push-EDX - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - # . . call - e8/call get/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . EDX = EAX - 89/copy 3/mod/direct 2/rm32/EDX . . . 0/r32/EAX . . # copy EAX to EDX - # . restore EAX - 58/pop-to-EAX - # EBX = label-seg->address - 8b/copy 0/mod/indirect 2/rm32/EDX . . . 3/r32/EBX . . # copy *EDX to EBX - # EBX += lrow->segment-offset - 03/add 1/mod/*+disp8 0/rm32/EAX . . . 3/r32/EBX 8/disp8 . # add *(EAX+8) to EBX - # lrow->address = EBX - 89/copy 1/mod/*+disp8 0/rm32/EAX . . . 3/r32/EBX 0xc/disp8 . # copy EBX to *(EAX+12) - # trace-sssns("label " lrow->key " is at address " lrow->address ".") - # . . push args - 68/push "."/imm32 - 53/push-EBX - 68/push "' is at address "/imm32 - ff 6/subop/push 0/mod/indirect 0/rm32/EAX . . . . . . # push *EAX - 68/push "label '"/imm32 - # . . call - e8/call trace-sssns/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x14/imm32 # add to ESP - # lrow += 16 # size of row - 05/add-to-EAX 0x10/imm32 - e9/jump $compute-addresses:label-loop/disp32 -$compute-addresses:end: - # . restore registers - 5f/pop-to-EDI - 5e/pop-to-ESI - 5b/pop-to-EBX - 5a/pop-to-EDX - 59/pop-to-ECX - 58/pop-to-EAX - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-compute-addresses: - # input: - # segments: - # - 'a': {0x1000, 0, 5} - # - 'b': {0x2018, 5, 1} - # - 'c': {0x5444, 6, 12} - # labels: - # - 'l1': {'a', 3, 0} - # - 'l2': {'b', 0, 0} - # - # trace contains in any order (comments in parens): - # segment 'a' starts at address 0x00001094. (0x34 + 0x20 for each segment) - # segment 'b' starts at address 0x00002099. (0x018 discarded) - # segment 'c' starts at address 0x0000509a. (0x444 discarded) - # label 'l1' is at address 0x00001097. (0x1094 + segment-offset 3) - # label 'l2' is at address 0x00002099. (0x2099 + segment-offset 0) - # - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # setup - # . var segments/ECX = stream(10 * 16) - 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 0xa0/imm32 # subtract from ESP - 68/push 0xa0/imm32/length - 68/push 0/imm32/read - 68/push 0/imm32/write - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # . var labels/EDX = stream(512 * 16) - 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 0x2000/imm32 # subtract from ESP - 68/push 0x2000/imm32/length - 68/push 0/imm32/read - 68/push 0/imm32/write - 89/copy 3/mod/direct 2/rm32/EDX . . . 4/r32/ESP . . # copy ESP to EDX - # . stream-add4(segments, "a", 0x1000, 0, 5) - 68/push 5/imm32/segment-size - 68/push 0/imm32/file-offset - 68/push 0x1000/imm32/start-address - 68/push "a"/imm32/segment-name - 51/push-ECX - # . . call - e8/call stream-add4/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x14/imm32 # add to ESP - # . stream-add4(segments, "b", 0x2018, 5, 1) - 68/push 1/imm32/segment-size - 68/push 5/imm32/file-offset - 68/push 0x2018/imm32/start-address - 68/push "b"/imm32/segment-name - 51/push-ECX - # . . call - e8/call stream-add4/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x14/imm32 # add to ESP - # . stream-add4(segments, "c", 0x5444, 6, 12) - 68/push 0xc/imm32/segment-size - 68/push 6/imm32/file-offset - 68/push 0x5444/imm32/start-address - 68/push "c"/imm32/segment-name - 51/push-ECX - # . . call - e8/call stream-add4/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x14/imm32 # add to ESP - # . stream-add4(labels, "l1", "a", 3, 0) - 68/push 0/imm32/label-address - 68/push 3/imm32/segment-offset - 68/push "a"/imm32/segment-name - 68/push "l1"/imm32/label-name - 52/push-EDX - # . . call - e8/call stream-add4/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x14/imm32 # add to ESP - # . stream-add4(labels, "l2", "b", 0, 0) - 68/push 0/imm32/label-address - 68/push 0/imm32/segment-offset - 68/push "b"/imm32/segment-name - 68/push "l2"/imm32/label-name - 52/push-EDX - # . . call - e8/call stream-add4/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x14/imm32 # add to ESP - # component under test - # . compute-addresses(segments, labels) - # . . push args - 52/push-EDX - 51/push-ECX - # . . call - e8/call compute-addresses/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # checks -#? # dump *Trace-stream {{{ -#? # . write(2/stderr, "^") -#? # . . push args -#? 68/push "^"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write-stream(2/stderr, *Trace-stream) -#? # . . push args -#? ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Trace-stream/disp32 # push *Trace-stream -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write-stream/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write(2/stderr, "$\n") -#? # . . push args -#? 68/push "$\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # }}} - # . check-trace-contains("segment 'a' starts at address 0x00001094.", msg) - # . . push args - 68/push "F - test-compute-addresses/0"/imm32 - 68/push "segment 'a' starts at address 0x00001094."/imm32 - # . . call - e8/call check-trace-contains/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . check-trace-contains("segment 'b' starts at address 0x00002099.", msg) - # . . push args - 68/push "F - test-compute-addresses/1"/imm32 - 68/push "segment 'b' starts at address 0x00002099."/imm32 - # . . call - e8/call check-trace-contains/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . check-trace-contains("segment 'c' starts at address 0x0000509a.", msg) - # . . push args - 68/push "F - test-compute-addresses/2"/imm32 - 68/push "segment 'c' starts at address 0x0000509a."/imm32 - # . . call - e8/call check-trace-contains/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . check-trace-contains("label 'l1' is at address 0x00001097.", msg) - # . . push args - 68/push "F - test-compute-addresses/3"/imm32 - 68/push "label 'l1' is at address 0x00001097."/imm32 - # . . call - e8/call check-trace-contains/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . check-trace-contains("label 'l2' is at address 0x00002099.", msg) - # . . push args - 68/push "F - test-compute-addresses/4"/imm32 - 68/push "label 'l2' is at address 0x00002099."/imm32 - # . . call - e8/call check-trace-contains/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . check-ints-equal(labels->write, 0x20, msg) - # . . push args - 68/push "F - test-compute-addresses/maintains-labels-write-index"/imm32 - 68/push 0x20/imm32/2-entries - ff 6/subop/push 0/mod/indirect 2/rm32/EDX . . . . . . # push *EDX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-compute-addresses-large-segments: - # input: - # segments: - # - 'a': {0x1000, 0, 0x5604} - # - 'b': {0x2018, 0x5604, 1} - # labels: - # - 'l1': {'a', 3, 0} - # - # trace contains in any order (comments in parens): - # segment 'a' starts at address 0x00001074. (0x34 + 0x20 for each segment) - # segment 'b' starts at address 0x00002678. (0x018 discarded; last 3 nibbles from 0x1074 + 0x5604) - # label 'l1' is at address 0x00001077. (0x1074 + segment-offset 3) - # - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # setup - # . var segments/ECX = stream(10 * 16) - 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 0xa0/imm32 # subtract from ESP - 68/push 0xa0/imm32/length - 68/push 0/imm32/read - 68/push 0/imm32/write - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # . var labels/EDX = stream(512 * 16) - 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 0x2000/imm32 # subtract from ESP - 68/push 0x2000/imm32/length - 68/push 0/imm32/read - 68/push 0/imm32/write - 89/copy 3/mod/direct 2/rm32/EDX . . . 4/r32/ESP . . # copy ESP to EDX - # . stream-add4(segments, "a", 0x1000, 0, 0x5604) - 68/push 0x5604/imm32/segment-size - 68/push 0/imm32/file-offset - 68/push 0x1000/imm32/start-address - 68/push "a"/imm32/segment-name - 51/push-ECX - # . . call - e8/call stream-add4/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x14/imm32 # add to ESP - # . stream-add4(segments, "b", 0x2018, 0x5604, 1) - 68/push 1/imm32/segment-size - 68/push 0x5604/imm32/file-offset - 68/push 0x2018/imm32/start-address - 68/push "b"/imm32/segment-name - 51/push-ECX - # . . call - e8/call stream-add4/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x14/imm32 # add to ESP - # . stream-add4(labels, "l1", "a", 3, 0) - 68/push 0/imm32/label-address - 68/push 3/imm32/segment-offset - 68/push "a"/imm32/segment-name - 68/push "l1"/imm32/label-name - 52/push-EDX - # . . call - e8/call stream-add4/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x14/imm32 # add to ESP - # component under test - # . compute-addresses(segments, labels) - # . . push args - 52/push-EDX - 51/push-ECX - # . . call - e8/call compute-addresses/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # checks - # . check-trace-contains("segment 'a' starts at address 0x00001074.", msg) - # . . push args - 68/push "F - test-compute-addresses-large-segments/0"/imm32 - 68/push "segment 'a' starts at address 0x00001074."/imm32 - # . . call - e8/call check-trace-contains/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . check-trace-contains("segment 'b' starts at address 0x00002678.", msg) - # . . push args - 68/push "F - test-compute-addresses-large-segments/1"/imm32 - 68/push "segment 'b' starts at address 0x00002678."/imm32 - # . . call - e8/call check-trace-contains/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . check-trace-contains("label 'l1' is at address 0x00001077.", msg) - # . . push args - 68/push "F - test-compute-addresses-large-segments/3"/imm32 - 68/push "label 'l1' is at address 0x00001077."/imm32 - # . . call - e8/call check-trace-contains/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -emit-output: # in : (address stream), out : (address buffered-file), segments : (address stream {string, segment-info}), labels : (address stream {string, label-info}) - # pseudocode: - # emit-headers(out, segments, labels) - # emit-segments(in, out, segments, labels) - # - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -#? # write(2/stderr, "emit-headers\n") {{{ -#? # . . push args -#? 68/push "emit-headers\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # }}} - # emit-headers(out, segments, labels) - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x14/disp8 . # push *(EBP+20) - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x10/disp8 . # push *(EBP+16) - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - # . . call - e8/call emit-headers/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -#? # write(2/stderr, "emit-segments\n") {{{ -#? # . . push args -#? 68/push "emit-segments\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # }}} - # emit-segments(in, out, segments, labels) - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x14/disp8 . # push *(EBP+20) - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x10/disp8 . # push *(EBP+16) - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - # . . call - e8/call emit-segments/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP -$emit-output:end: - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -emit-segments: # in : (address stream), out : (address buffered-file), segments : (address stream {string, segment-info}), labels : (address stream {string, label-info}) - # pseudocode: - # var offset-of-next-instruction = 0 - # var line = new-stream(512, 1) - # line-loop: - # while true - # clear-stream(line) - # read-line(in, line) - # if (line->write == 0) break # end of file - # offset-of-next-instruction += num-bytes(line) - # while true - # var word-slice = next-word(line) - # if slice-empty?(word-slice) # end of line - # break - # if slice-starts-with?(word-slice, "#") # comment - # break - # if is-label?(word-slice) # no need for label declarations anymore - # goto line-loop # don't insert empty lines - # if slice-equal?(word-slice, "==") # no need for segment header lines - # goto line-loop # don't insert empty lines - # if length(word-slice) == 2 - # write-slice-buffered(out, word-slice) - # write-buffered(out, " ") - # continue - # datum = next-token-from-slice(word-slice->start, word-slice->end, "/") - # info = get-slice(labels, datum) - # if !string-equal?(info->segment-name, "code") - # if has-metadata?(word-slice, "disp8") - # abort - # if has-metadata?(word-slice, "imm8") - # abort - # emit(out, info->address, 4) # global variables always translate to absolute addresses - # # code segment cases - # else if has-metadata?(word-slice, "imm8") - # abort # label should never go to imm8 - # else if has-metadata?(word-slice, "imm32") - # emit(out, info->address, 4) - # else if has-metadata?(word-slice, "disp8") - # value = info->offset - offset-of-next-instruction - # emit(out, value, 1) - # else if has-metadata?(word-slice, "disp32") - # value = info->offset - offset-of-next-instruction - # emit(out, value, 4) - # else - # abort - # write-buffered(out, "\n") - # - # registers: - # line: ECX - # word-slice: EDX - # offset-of-next-instruction: EBX - # datum: EDI - # info: ESI (inner loop only) - # temporaries: EAX, ESI (outer loop) - # - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # . save registers - 50/push-EAX - 51/push-ECX - 52/push-EDX - 53/push-EBX - 56/push-ESI - 57/push-EDI - # var line/ECX : (address stream byte) = stream(512) - 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 0x200/imm32 # subtract from ESP - 68/push 0x200/imm32/length - 68/push 0/imm32/read - 68/push 0/imm32/write - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # var word-slice/EDX = {0, 0} - 68/push 0/imm32/end - 68/push 0/imm32/start - 89/copy 3/mod/direct 2/rm32/EDX . . . 4/r32/ESP . . # copy ESP to EDX - # var datum/EDI = {0, 0} - 68/push 0/imm32/end - 68/push 0/imm32/start - 89/copy 3/mod/direct 7/rm32/EDI . . . 4/r32/ESP . . # copy ESP to EDI - # offset-of-next-instruction/EBX = 0 - 31/xor 3/mod/direct 3/rm32/EBX . . . 3/r32/EBX . . # clear EBX -$emit-segments:line-loop: - # clear-stream(line) - # . . push args - 51/push-ECX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # read-line(in, line) - # . . push args - 51/push-ECX - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - # . . call - e8/call read-line/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # dump line {{{ -#? # . write(2/stderr, "LL: ") -#? # . . push args -#? 68/push "LL: "/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # write-stream(2/stderr, line) -#? # . . push args -#? 51/push-ECX -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write-stream/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write(2/stderr, "$\n") -#? # . . push args -#? 68/push "$\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . rewind-stream(line) -#? # . . push args -#? 51/push-ECX -#? # . . call -#? e8/call rewind-stream/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -#? # }}} -$emit-segments:check-for-end-of-input: - # if (line->write == 0) break - 81 7/subop/compare 0/mod/indirect 1/rm32/ECX . . . . . 0/imm32 # compare *ECX - 0f 84/jump-if-equal $emit-segments:end/disp32 - # offset-of-next-instruction += num-bytes(line) - # . EAX = num-bytes(line) - # . . push args - 51/push-ECX - # . . call - e8/call num-bytes/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . EBX += EAX - 01/add 3/mod/direct 3/rm32/EBX . . . 0/r32/EAX . . # add EAX to EBX -$emit-segments:word-loop: - # next-word(line, word-slice) - # . . push args - 52/push-EDX - 51/push-ECX - # . . call - e8/call next-word/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # dump word-slice {{{ -#? # . write(2/stderr, "w: ") -#? # . . push args -#? 68/push "w: "/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write-slice-buffered(Stderr, word-slice) -#? # . . push args -#? 52/push-EDX -#? 68/push Stderr/imm32 -#? # . . call -#? e8/call write-slice-buffered/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . flush(Stderr) -#? # . . push args -#? 68/push Stderr/imm32 -#? # . . call -#? e8/call flush/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -#? # . write(2/stderr, "$\n") -#? # . . push args -#? 68/push "$\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # }}} -$emit-segments:check-for-end-of-line: - # if (slice-empty?(word-slice)) break - # . EAX = slice-empty?(word-slice) - # . . push args - 52/push-EDX - # . . call - e8/call slice-empty?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . if (EAX != 0) break - 3d/compare-EAX-and 0/imm32 - 0f 85/jump-if-not-equal $emit-segments:next-line/disp32 -$emit-segments:check-for-comment: - # if (slice-starts-with?(word-slice, "#")) break - # . start/ESI = word-slice->start - 8b/copy 0/mod/indirect 2/rm32/EDX . . . 6/r32/ESI . . # copy *EDX to ESI - # . c/EAX = *start - 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX - 8a/copy-byte 0/mod/indirect 6/rm32/ESI . . . 0/r32/AL . . # copy byte at *ESI to AL - # . if (EAX == '#') break - 3d/compare-EAX-and 0x23/imm32/hash - 0f 84/jump-if-equal $emit-segments:next-line/disp32 -$emit-segments:check-for-label: - # if is-label?(word-slice) break - # . EAX = is-label?(word-slice) - # . . push args - 52/push-EDX - # . . call - e8/call is-label?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . if (EAX != 0) break - 3d/compare-EAX-and 0/imm32 - 0f 85/jump-if-not-equal $emit-segments:line-loop/disp32 -$emit-segments:check-for-segment-header: - # if (slice-equal?(word-slice, "==")) break - # . EAX = slice-equal?(word-slice, "==") - # . . push args - 68/push "=="/imm32 - 52/push-EDX - # . . call - e8/call slice-equal?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . if (EAX != 0) break - 3d/compare-EAX-and 0/imm32 - 0f 85/jump-if-not-equal $emit-segments:line-loop/disp32 -$emit-segments:2-character: - # if (length(word-slice) != 2) goto next check - # . EAX = length(word-slice) - 8b/copy 1/mod/*+disp8 2/rm32/EDX . . . 0/r32/EAX 4/disp8 . # copy *(EDX+4) to EAX - 2b/subtract 0/mod/indirect 2/rm32/EDX . . . 0/r32/EAX . . # subtract *EDX from EAX - # . if (EAX != 2) goto next check - 3d/compare-EAX-and 2/imm32 - 75/jump-if-not-equal $emit-segments:check-metadata/disp8 - # write-slice-buffered(out, word-slice) - # . . push args - 52/push-EDX - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - # . . call - e8/call write-slice-buffered/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # write-buffered(out, " ") - # . . push args - 68/push " "/imm32 - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - # . . call - e8/call write-buffered/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # continue - e9/jump $emit-segments:word-loop/disp32 -$emit-segments:check-metadata: - # - if we get here, 'word-slice' must be a label to be looked up - # datum/EDI = next-token-from-slice(word-slice->start, word-slice->end, "/") - # . . push args - 57/push-EDI - 68/push 0x2f/imm32/slash - ff 6/subop/push 1/mod/*+disp8 2/rm32/EDX . . . . 4/disp8 . # push *(EDX+4) - ff 6/subop/push 0/mod/indirect 2/rm32/EDX . . . . . . # push *EDX - # . . call - e8/call next-token-from-slice/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP -#? # dump word-slice {{{ -#? # . write(2/stderr, "datum: ") -#? # . . push args -#? 68/push "datum: "/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write-slice-buffered(Stderr, word-slice) -#? # . . push args -#? 57/push-EDI -#? 68/push Stderr/imm32 -#? # . . call -#? e8/call write-slice-buffered/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . flush(Stderr) -#? # . . push args -#? 68/push Stderr/imm32 -#? # . . call -#? e8/call flush/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -#? # . write(2/stderr, "$\n") -#? # . . push args -#? 68/push "$\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # }}} - # info/ESI = get-slice(labels, datum, row-size=16) - # . EAX = get-slice(labels, datum, row-size=16) - # . . push args - 68/push 0x10/imm32/row-size - 57/push-EDI - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x14/disp8 . # push *(EBP+20) - # . . call - e8/call get-slice/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . ESI = EAX - 89/copy 3/mod/direct 6/rm32/ESI . . . 0/r32/EAX . . # copy EAX to ESI -$emit-segments:check-global-variable: -#? # dump info->segment-name {{{ -#? # . write(2/stderr, "aa: label segment: ") -#? # . . push args -#? 68/push "aa: label segment: "/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write(2/stderr, info->segment-name) -#? # . . push args -#? ff 6/subop/push 0/mod/indirect 6/rm32/ESI . . . . . . # push *ESI -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write(2/stderr, "$\n") -#? # . . push args -#? 68/push "$\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # }}} - # if string-equal?(info->segment-name, "code") goto code label checks - # . EAX = string-equal?(info->segment-name, "code") - # . . push args - 68/push "code"/imm32 - ff 6/subop/push 0/mod/indirect 6/rm32/ESI . . . . . . # push *ESI - # . . call - e8/call string-equal?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . if (EAX != 0) goto code label checks - 3d/compare-EAX-and 0/imm32 - 0f 85/jump-if-not-equal $emit-segments:check-code-label-for-imm8/disp32 -$emit-segments:check-global-variable-for-disp8: - # if has-metadata?(word-slice, "disp8") abort - # . EAX = has-metadata?(word-slice, "disp8") - # . . push args - 68/push "disp8"/imm32 - 52/push-EDX - # . . call - e8/call has-metadata?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . if (EAX != 0) abort - 3d/compare-EAX-and 0/imm32 - 0f 85/jump-if-not-equal $emit-segments:global-variable-abort/disp32 -$emit-segments:check-global-variable-for-imm8: - # if has-metadata?(word-slice, "imm8") abort - # . EAX = has-metadata?(word-slice, "imm8") - # . . push args - 68/push "imm8"/imm32 - 52/push-EDX - # . . call - e8/call has-metadata?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . if (EAX != 0) abort - 3d/compare-EAX-and 0/imm32 - 0f 85/jump-if-not-equal $emit-segments:global-variable-abort/disp32 -$emit-segments:emit-global-variable: - # emit-hex(out, info->address, 4) - # . . push args - 68/push 4/imm32 - ff 6/subop/push 1/mod/*+disp8 6/rm32/ESI . . . . 8/disp8 . # push *(ESI+8) - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - # . . call - e8/call emit-hex/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # continue - e9/jump $emit-segments:word-loop/disp32 -$emit-segments:check-code-label-for-imm8: - # if (has-metadata?(word-slice, "imm8")) abort - # . EAX = has-metadata?(EDX, "imm8") - # . . push args - 68/push "imm8"/imm32 - 52/push-EDX - # . . call - e8/call has-metadata?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . if (EAX != 0) abort - 3d/compare-EAX-and 0/imm32 - 0f 85/jump-if-not-equal $emit-segments:imm8-abort/disp32 -$emit-segments:check-code-label-for-imm32: - # if (!has-metadata?(word-slice, "imm32")) goto next check - # . EAX = has-metadata?(EDX, "imm32") - # . . push args - 68/push "imm32"/imm32 - 52/push-EDX - # . . call - e8/call has-metadata?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . if (EAX == 0) goto next check - 3d/compare-EAX-and 0/imm32 - 74/jump-if-equal $emit-segments:check-code-label-for-disp8/disp8 -#? # dump info->address {{{ -#? # . write(2/stderr, "info->address: ") -#? # . . push args -#? 68/push "info->address: "/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . print-int32-buffered(Stderr, info->address) -#? # . . push args -#? ff 6/subop/push 1/mod/*+disp8 6/rm32/ESI . . . . 8/disp8 . # push *(ESI+8) -#? 68/push Stderr/imm32 -#? # . . call -#? e8/call print-int32-buffered/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . flush(Stderr) -#? # . . push args -#? 68/push Stderr/imm32 -#? # . . call -#? e8/call flush/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -#? # . write(2/stderr, "$\n") -#? # . . push args -#? 68/push "$\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # }}} -$emit-segments:emit-code-label-imm32: - # emit-hex(out, info->address, 4) - # . . push args - 68/push 4/imm32 - ff 6/subop/push 1/mod/*+disp8 6/rm32/ESI . . . . 8/disp8 . # push *(ESI+8) - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - # . . call - e8/call emit-hex/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # continue - e9/jump $emit-segments:word-loop/disp32 -$emit-segments:check-code-label-for-disp8: - # if (!has-metadata?(word-slice, "disp8")) goto next check - # . EAX = has-metadata?(EDX, "disp8") - # . . push args - 68/push "disp8"/imm32 - 52/push-EDX - # . . call - e8/call has-metadata?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . if (EAX == 0) goto next check - 3d/compare-EAX-and 0/imm32 - 74/jump-if-equal $emit-segments:check-code-label-for-disp32/disp8 -$emit-segments:emit-code-label-disp8: - # emit-hex(out, info->offset - offset-of-next-instruction, 1) - # . . push args - 68/push 1/imm32 - 8b/copy 1/mod/*+disp8 6/rm32/ESI . . . 0/r32/EAX 4/disp8 . # copy *(ESI+4) to EAX - 29/subtract 3/mod/direct 0/rm32/EAX . . . 3/r32/EBX . . # subtract EBX from EAX - 50/push-EAX - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - # . . call - e8/call emit-hex/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # continue - e9/jump $emit-segments:word-loop/disp32 -$emit-segments:check-code-label-for-disp32: - # if (!has-metadata?(word-slice, "disp32")) abort - # . EAX = has-metadata?(EDX, "disp32") - # . . push args - 68/push "disp32"/imm32 - 52/push-EDX - # . . call - e8/call has-metadata?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . if (EAX == 0) abort - 3d/compare-EAX-and 0/imm32 - 0f 84/jump-if-equal $emit-segments:abort/disp32 -$emit-segments:emit-code-label-disp32: - # emit-hex(out, info->offset - offset-of-next-instruction, 4) - # . . push args - 68/push 4/imm32 - 8b/copy 1/mod/*+disp8 6/rm32/ESI . . . 0/r32/EAX 4/disp8 . # copy *(ESI+4) to EAX - 29/subtract 3/mod/direct 0/rm32/EAX . . . 3/r32/EBX . . # subtract EBX from EAX - 50/push-EAX - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - # . . call - e8/call emit-hex/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # continue - e9/jump $emit-segments:word-loop/disp32 -$emit-segments:next-line: - # write-buffered(out, "\n") - # . . push args - 68/push Newline/imm32 - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - # . . call - e8/call write-buffered/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # loop - e9/jump $emit-segments:line-loop/disp32 -$emit-segments:end: - # . reclaim locals - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x21c/imm32 # add to ESP - # . restore registers - 5f/pop-to-EDI - 5e/pop-to-ESI - 5b/pop-to-EBX - 5a/pop-to-EDX - 59/pop-to-ECX - 58/pop-to-EAX - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -$emit-segments:global-variable-abort: - # . _write(2/stderr, error) - # . . push args - 68/push "emit-segments: must refer to global variables with /disp32 or /imm32"/imm32 - 68/push 2/imm32/stderr - # . . call - e8/call _write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . syscall(exit, 1) - bb/copy-to-EBX 1/imm32 - b8/copy-to-EAX 1/imm32/exit - cd/syscall 0x80/imm8 - # never gets here - -$emit-segments:imm8-abort: - # . _write(2/stderr, error) - # . . push args - 68/push "emit-segments: cannot refer to code labels with /imm8"/imm32 - 68/push 2/imm32/stderr - # . . call - e8/call _write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . syscall(exit, 1) - bb/copy-to-EBX 1/imm32 - b8/copy-to-EAX 1/imm32/exit - cd/syscall 0x80/imm8 - # never gets here - -$emit-segments:abort: - # print(stderr, "missing metadata in " word-slice) - # . _write(2/stderr, "missing metadata in word ") - # . . push args - 68/push "emit-segments: missing metadata in "/imm32 - 68/push 2/imm32/stderr - # . . call - e8/call _write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . write-slice-buffered(Stderr, word-slice) - # . . push args - 52/push-EDX - 68/push Stderr/imm32 - # . . call - e8/call write-slice-buffered/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . flush(Stderr) - # . . push args - 68/push Stderr/imm32 - # . . call - e8/call flush/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . syscall(exit, 1) - bb/copy-to-EBX 1/imm32 - b8/copy-to-EAX 1/imm32/exit - cd/syscall 0x80/imm8 - # never gets here - -test-emit-segments-global-variable: - # global variables always convert to absolute addresses, regardless of metadata - # - # input: - # in: - # == code 0x1000 - # ab cd ef gh - # ij x/disp32 - # == data 0x2000 - # 00 - # x: - # 34 - # segments: - # - 'code': {0x1074, 0, 9} - # - 'data': {0x2079, 5, 2} - # labels: - # - 'x': {'data', 1, 0x207a} - # - # output: - # ab cd ef gh - # ij 7a 20 00 00 - # 00 - # 34 - # - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # setup - # . clear-stream(_test-input-stream) - # . . push args - 68/push _test-input-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-stream) - # . . push args - 68/push _test-output-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-buffered-file+4) - # . . push args - b8/copy-to-EAX _test-output-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . var segments/ECX = stream(10 * 16) - 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 0xa0/imm32 # subtract from ESP - 68/push 0xa0/imm32/length - 68/push 0/imm32/read - 68/push 0/imm32/write - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # . var labels/EDX = stream(512 * 16) - 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 0x2000/imm32 # subtract from ESP - 68/push 0x2000/imm32/length - 68/push 0/imm32/read - 68/push 0/imm32/write - 89/copy 3/mod/direct 2/rm32/EDX . . . 4/r32/ESP . . # copy ESP to EDX - # initialize input - # . write(_test-input-stream, "== code 0x1000\n") - # . . push args - 68/push "== code 0x1000\n"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . write(_test-input-stream, "ab cd ef gh\n") - # . . push args - 68/push "ab cd ef gh\n"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . write(_test-input-stream, "ij x/disp32\n") - # . . push args - 68/push "ij x/disp32\n"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . write(_test-input-stream, "== data 0x2000\n") - # . . push args - 68/push "== data 0x2000\n"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . write(_test-input-stream, "00\n") - # . . push args - 68/push "00\n"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . write(_test-input-stream, "x:\n") - # . . push args - 68/push "x:\n"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . write(_test-input-stream, "34\n") - # . . push args - 68/push "34\n"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . stream-add4(segments, "code", 0x1074, 0, 9) - 68/push 9/imm32/segment-size - 68/push 0/imm32/file-offset - 68/push 0x1074/imm32/start-address - 68/push "code"/imm32/segment-name - 51/push-ECX - # . . call - e8/call stream-add4/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x14/imm32 # add to ESP - # . stream-add4(segments, "data", 0x2079, 5, 2) - 68/push 1/imm32/segment-size - 68/push 5/imm32/file-offset - 68/push 0x2079/imm32/start-address - 68/push "data"/imm32/segment-name - 51/push-ECX - # . . call - e8/call stream-add4/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x14/imm32 # add to ESP - # . stream-add4(labels, "x", "data", 1, 0x207a) - 68/push 0x207a/imm32/label-address - 68/push 1/imm32/segment-offset - 68/push "data"/imm32/segment-name - 68/push "x"/imm32/label-name - 52/push-EDX - # . . call - e8/call stream-add4/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x14/imm32 # add to ESP - # component under test - # . emit-segments(_test-input-stream, _test-output-buffered-file, segments, labels) - # . . push args - 52/push-EDX - 51/push-ECX - 68/push _test-output-buffered-file/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call emit-segments/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP - # checks - # . flush(_test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - # . . call - e8/call flush/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -#? # dump output {{{ -#? # . write(2/stderr, "result: ^") -#? # . . push args -#? 68/push "result: ^"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write-stream(2/stderr, _test-output-stream) -#? # . . push args -#? 68/push _test-output-stream/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write-stream/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write(2/stderr, "$\n") -#? # . . push args -#? 68/push "$\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . rewind-stream(_test-output-stream) -#? # . . push args -#? 68/push _test-output-stream/imm32 -#? # . . call -#? e8/call rewind-stream/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -#? # }}} - # . check-next-stream-line-equal(_test-output-stream, "ab cd ef gh ", msg) - # . . push args - 68/push "F - test-emit-segments-global-variable/0"/imm32 - 68/push "ab cd ef gh "/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-next-stream-line-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . check-next-stream-line-equal(_test-output-stream, "ij 7a 20 00 00 ", msg) - # . . push args - 68/push "F - test-emit-segments-global-variable/1"/imm32 - 68/push "ij 7a 20 00 00 "/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-next-stream-line-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . check-next-stream-line-equal(_test-output-stream, "00 ", msg) - # . . push args - 68/push "F - test-emit-segments-global-variable/2"/imm32 - 68/push "00 "/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-next-stream-line-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . check-next-stream-line-equal(_test-output-stream, "34 ", msg) - # . . push args - 68/push "F - test-emit-segments-global-variable/3"/imm32 - 68/push "34 "/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-next-stream-line-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-emit-segments-code-label: - # labels usually convert to displacements - # - # input: - # in: - # == code 0x1000 - # ab cd - # l1: - # ef gh - # ij l1/disp32 - # segments: - # - 'code': {0x1054, 0, 9} - # labels: - # - 'l1': {'code', 2, 0x1056} - # - # output: - # ab cd - # ef gh - # ij f9 ff ff ff # -7 - # - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # setup - # . clear-stream(_test-input-stream) - # . . push args - 68/push _test-input-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-stream) - # . . push args - 68/push _test-output-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-buffered-file+4) - # . . push args - b8/copy-to-EAX _test-output-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . var segments/ECX = stream(10 * 16) - 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 0xa0/imm32 # subtract from ESP - 68/push 0xa0/imm32/length - 68/push 0/imm32/read - 68/push 0/imm32/write - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # . var labels/EDX = stream(512 * 16) - 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 0x2000/imm32 # subtract from ESP - 68/push 0x2000/imm32/length - 68/push 0/imm32/read - 68/push 0/imm32/write - 89/copy 3/mod/direct 2/rm32/EDX . . . 4/r32/ESP . . # copy ESP to EDX - # initialize input - # . write(_test-input-stream, "== code 0x1000\n") - # . . push args - 68/push "== code 0x1000\n"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . write(_test-input-stream, "ab cd\n") - # . . push args - 68/push "ab cd\n"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . write(_test-input-stream, "l1:\n") - # . . push args - 68/push "l1:\n"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . write(_test-input-stream, " ef gh\n") - # . . push args - 68/push " ef gh\n"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . write(_test-input-stream, " ij l1/disp32\n") - # . . push args - 68/push " ij l1/disp32\n"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . stream-add4(segments, "code", 0x1054, 0, 9) - 68/push 9/imm32/segment-size - 68/push 0/imm32/file-offset - 68/push 0x1054/imm32/start-address - 68/push "code"/imm32/segment-name - 51/push-ECX - # . . call - e8/call stream-add4/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x14/imm32 # add to ESP - # . stream-add4(labels, "l1", "code", 2, 0x1056) - 68/push 0x1056/imm32/label-address - 68/push 2/imm32/segment-offset - 68/push "code"/imm32/segment-name - 68/push "l1"/imm32/label-name - 52/push-EDX - # . . call - e8/call stream-add4/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x14/imm32 # add to ESP - # component under test - # . emit-segments(_test-input-stream, _test-output-buffered-file, segments, labels) - # . . push args - 52/push-EDX - 51/push-ECX - 68/push _test-output-buffered-file/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call emit-segments/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP - # checks - # . flush(_test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - # . . call - e8/call flush/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -#? # dump output {{{ -#? # . write(2/stderr, "result: ^") -#? # . . push args -#? 68/push "result: ^"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write-stream(2/stderr, _test-output-stream) -#? # . . push args -#? 68/push _test-output-stream/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write-stream/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write(2/stderr, "$\n") -#? # . . push args -#? 68/push "$\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . rewind-stream(_test-output-stream) -#? # . . push args -#? 68/push _test-output-stream/imm32 -#? # . . call -#? e8/call rewind-stream/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -#? # }}} - # . check-next-stream-line-equal(_test-output-stream, "ab cd ", msg) - # . . push args - 68/push "F - test-emit-segments-code-label/0"/imm32 - 68/push "ab cd "/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-next-stream-line-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . check-next-stream-line-equal(_test-output-stream, "ef gh ", msg) - # . . push args - 68/push "F - test-emit-segments-code-label/1"/imm32 - 68/push "ef gh "/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-next-stream-line-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . check-next-stream-line-equal(_test-output-stream, "ij f9 ff ff ff ", msg) - # . . push args - 68/push "F - test-emit-segments-code-label/2"/imm32 - 68/push "ij f9 ff ff ff "/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-next-stream-line-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-emit-segments-code-label-absolute: - # labels can also convert to absolute addresses - # - # input: - # in: - # == code 0x1000 - # ab cd - # l1: - # ef gh - # ij l1/imm32 - # segments: - # - 'code': {0x1054, 0, 9} - # labels: - # - 'l1': {'code', 2, 0x1056} - # - # output: - # ab cd - # ef gh - # ij 56 10 00 00 - # - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # setup - # . clear-stream(_test-input-stream) - # . . push args - 68/push _test-input-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-stream) - # . . push args - 68/push _test-output-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-buffered-file+4) - # . . push args - b8/copy-to-EAX _test-output-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . var segments/ECX = stream(10 * 16) - 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 0xa0/imm32 # subtract from ESP - 68/push 0xa0/imm32/length - 68/push 0/imm32/read - 68/push 0/imm32/write - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # . var labels/EDX = stream(512 * 16) - 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 0x2000/imm32 # subtract from ESP - 68/push 0x2000/imm32/length - 68/push 0/imm32/read - 68/push 0/imm32/write - 89/copy 3/mod/direct 2/rm32/EDX . . . 4/r32/ESP . . # copy ESP to EDX - # initialize input - # . write(_test-input-stream, "== code 0x1000\n") - # . . push args - 68/push "== code 0x1000\n"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . write(_test-input-stream, "ab cd\n") - # . . push args - 68/push "ab cd\n"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . write(_test-input-stream, "l1:\n") - # . . push args - 68/push "l1:\n"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . write(_test-input-stream, " ef gh\n") - # . . push args - 68/push " ef gh\n"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . write(_test-input-stream, " ij l1/imm32\n") - # . . push args - 68/push " ij l1/imm32\n"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . stream-add4(segments, "code", 0x1054, 0, 9) - 68/push 9/imm32/segment-size - 68/push 0/imm32/file-offset - 68/push 0x1054/imm32/start-address - 68/push "code"/imm32/segment-name - 51/push-ECX - # . . call - e8/call stream-add4/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x14/imm32 # add to ESP - # . stream-add4(labels, "l1", "code", 2, 0x1056) - 68/push 0x1056/imm32/label-address - 68/push 2/imm32/segment-offset - 68/push "code"/imm32/segment-name - 68/push "l1"/imm32/label-name - 52/push-EDX - # . . call - e8/call stream-add4/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x14/imm32 # add to ESP - # component under test - # . emit-segments(_test-input-stream, _test-output-buffered-file, segments, labels) - # . . push args - 52/push-EDX - 51/push-ECX - 68/push _test-output-buffered-file/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call emit-segments/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP - # checks - # . flush(_test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - # . . call - e8/call flush/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -#? # dump output {{{ -#? # . write(2/stderr, "result: ^") -#? # . . push args -#? 68/push "result: ^"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write-stream(2/stderr, _test-output-stream) -#? # . . push args -#? 68/push _test-output-stream/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write-stream/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write(2/stderr, "$\n") -#? # . . push args -#? 68/push "$\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . rewind-stream(_test-output-stream) -#? # . . push args -#? 68/push _test-output-stream/imm32 -#? # . . call -#? e8/call rewind-stream/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -#? # }}} - # . check-next-stream-line-equal(_test-output-stream, "ab cd ", msg) - # . . push args - 68/push "F - test-emit-segments-code-label-absolute/0"/imm32 - 68/push "ab cd "/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-next-stream-line-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . check-next-stream-line-equal(_test-output-stream, "ef gh ", msg) - # . . push args - 68/push "F - test-emit-segments-code-label-absolute/1"/imm32 - 68/push "ef gh "/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-next-stream-line-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . check-next-stream-line-equal(_test-output-stream, "ij f9 ff ff ff ", msg) - # . . push args - 68/push "F - test-emit-segments-code-label-absolute/2"/imm32 - 68/push "ij 56 10 00 00 "/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-next-stream-line-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -emit-headers: # out : (address buffered-file), segments : (address stream {string, segment-info}), labels : (address stream {string, label-info}) - # pseudocode: - # emit-elf-header(out, segments, labels) - # curr-segment = segments->data - # max = segments->data + segments->write - # while true - # if (curr-segment >= max) break - # emit-elf-program-header-entry(out, curr-segment) - # curr-segment += 16 # size of a row - # - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # . save registers - 50/push-EAX - 51/push-ECX -#? # write(2/stderr, "emit-elf-header\n") {{{ -#? # . . push args -#? 68/push "emit-elf-header\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # }}} - # emit-elf-header(out, segments, labels) - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x10/disp8 . # push *(EBP+16) - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - # . . call - e8/call emit-elf-header/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # EAX = segments - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 0/r32/EAX 0xc/disp8 . # copy *(EBP+12) to EAX - # ECX = segments->write - 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX - # curr-segment/EAX = segments->data - 8d/copy-address 1/mod/*+disp8 0/rm32/EAX . . . 0/r32/EAX 0xc/disp8 . # copy EAX+12 to EAX - # max/ECX = segments->data + segments->write - 01/add 3/mod/direct 1/rm32/ECX . . . 0/r32/EAX . . # add EAX to ECX -$emit-headers:loop: - # if (curr-segment >= max) break - 39/compare 3/mod/direct 0/rm32/EAX . . . 1/r32/ECX . . # compare EAX with ECX - 0f 83/jump-if-greater-or-equal-unsigned $emit-headers:end/disp32 -#? # dump curr-segment->name {{{ -#? # . write(2/stderr, "about to emit ph entry: segment->name: ") -#? # . . push args -#? 68/push "about to emit ph entry: segment->name: "/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . clear-stream(Stderr+4) -#? # . . save EAX -#? 50/push-EAX -#? # . . push args -#? b8/copy-to-EAX Stderr/imm32 -#? 05/add-to-EAX 4/imm32 -#? 50/push-EAX -#? # . . call -#? e8/call clear-stream/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -#? # . . restore EAX -#? 58/pop-to-EAX -#? # . print-int32-buffered(Stderr, &curr-segment) -#? # . . push args -#? 50/push-EAX -#? 68/push Stderr/imm32 -#? # . . call -#? e8/call print-int32-buffered/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . flush(Stderr) -#? # . . push args -#? 68/push Stderr/imm32 -#? # . . call -#? e8/call flush/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -#? # . write(2/stderr, " -> ") -#? # . . push args -#? 68/push " -> "/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . print-int32-buffered(Stderr, curr-segment->name) -#? # . . push args -#? ff 6/subop/push 0/mod/indirect 0/rm32/EAX . . . . . . # push *EAX -#? 68/push Stderr/imm32 -#? # . . call -#? e8/call print-int32-buffered/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . flush(Stderr) -#? # . . push args -#? 68/push Stderr/imm32 -#? # . . call -#? e8/call flush/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -#? # . write(2/stderr, "\n") -#? # . . push args -#? 68/push "\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # }}} -#? # write(2/stderr, "emit-segment-header\n") {{{ -#? # . . push args -#? 68/push "emit-segment-header\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # }}} - # emit-elf-program-header-entry(out, curr-segment) - # . . push args - 50/push-EAX - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - # . . call - e8/call emit-elf-program-header-entry/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # curr-segment += 16 # size of a row - 81 0/subop/add 3/mod/direct 0/rm32/EAX . . . . . 0x10/imm32 # add to EAX - e9/jump $emit-headers:loop/disp32 -$emit-headers:end: - # . restore registers - 59/pop-to-ECX - 58/pop-to-EAX - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -emit-elf-header: # out : (address buffered-file), segments : (address stream {string, segment-info}), labels : (address stream {string, label-info}) - # pseudocode - # *Elf_e_entry = get(labels, "Entry")->address - # *Elf_e_phnum = segments->write / 16 # size of a row - # emit-hex-array(out, Elf_header) - # write-buffered(out, "\n") - # - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # . save registers - 50/push-EAX - 51/push-ECX - 52/push-EDX # just because we need to call idiv - # *Elf_e_entry = get(labels, "Entry")->address - # . EAX = labels - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 0/r32/EAX 0x10/disp8 . # copy *(EBP+16) to EAX - # . label-info/EAX = get(labels, "Entry", row-size=16) - # . . push args - 68/push 0x10/imm32/row-size - 68/push "Entry"/imm32 - 50/push-EAX - # . . call - e8/call get/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . EAX = label-info->address - 8b/copy 1/mod/*+disp8 0/rm32/EAX . . . 0/r32/EAX 8/disp8 . # copy *(EAX+8) to EAX - # . *Elf_e_entry = EAX - 89/copy 0/mod/indirect 5/rm32/.disp32 . . 0/r32/EAX Elf_e_entry/disp32 # copy EAX to *Elf_e_entry - # *Elf_e_phnum = segments->write / 0x10 - # . EAX = segments - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 0/r32/EAX 0xc/disp8 . # copy *(EBP+12) to EAX - # . len/EAX = segments->write - 8b/copy 0/mod/indirect 0/rm32/EAX . . . 0/r32/EAX . . # copy *EAX to EAX - # . EAX = len / 0x10 (destroying EDX) - b9/copy-to-ECX 0x10/imm32 - 31/xor 3/mod/direct 2/rm32/EDX . . . 2/r32/EDX . . # clear EDX - f7 7/subop/idiv 3/mod/direct 1/rm32/ECX . . . . . . # divide EDX:EAX by ECX, storing quotient in EAX and remainder in EDX - # . *Elf_e_phnum = EAX - 89/copy 0/mod/indirect 5/rm32/.disp32 . . 0/r32/EAX Elf_e_phnum/disp32 # copy EAX to *Elf_e_phnum - # emit-hex-array(out, Elf_header) - # . . push args - 68/push Elf_header/imm32 - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - # . . call - e8/call emit-hex-array/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # write-buffered(out, "\n") - # . . push args - 68/push "\n"/imm32 - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - # . . call - e8/call write-buffered/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -$emit-elf-header:end: - # . restore registers - 5a/pop-to-EDX - 59/pop-to-ECX - 58/pop-to-EAX - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -emit-elf-program-header-entry: # out : (address buffered-file), curr-segment : (address {string, segment-info}) - # pseudocode: - # *Elf_p_offset = curr-segment->file-offset - # *Elf_p_vaddr = curr-segment->address - # *Elf_p_paddr = curr-segment->address - # *Elf_p_filesz = curr-segment->size - # *Elf_p_memsz = curr-segment->size - # if curr-segment->name == "code" - # *Elf_p_flags = 5 # r-x - # else - # *Elf_p_flags = 6 # rw- - # emit-hex-array(out, Elf_program_header_entry) - # write-buffered(out, "\n") - # - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # . save registers - 50/push-EAX - 56/push-ESI - # ESI = curr-segment - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 6/r32/ESI 0xc/disp8 . # copy *(EBP+12) to ESI - # *Elf_p_offset = curr-segment->file-offset - # . EAX = curr-segment->file-offset - 8b/copy 1/mod/*+disp8 6/rm32/ESI . . . 0/r32/EAX 8/disp8 . # copy *(ESI+8) to EAX - # . *Elf_p_offset = EAX - 89/copy 0/mod/indirect 5/rm32/.disp32 . . 0/r32/EAX Elf_p_offset/disp32 # copy EAX to *Elf_p_offset - # *Elf_p_vaddr = curr-segment->address - # . EAX = curr-segment->address - 8b/copy 1/mod/*+disp8 6/rm32/ESI . . . 0/r32/EAX 4/disp8 . # copy *(ESI+4) to EAX - # . *Elf_p_vaddr = EAX - 89/copy 0/mod/indirect 5/rm32/.disp32 . . 0/r32/EAX Elf_p_vaddr/disp32 # copy EAX to *Elf_p_vaddr - # *Elf_p_paddr = curr-segment->address - 89/copy 0/mod/indirect 5/rm32/.disp32 . . 0/r32/EAX Elf_p_paddr/disp32 # copy EAX to *Elf_p_paddr - # *Elf_p_filesz = curr-segment->size - # . EAX = curr-segment->size - 8b/copy 1/mod/*+disp8 6/rm32/ESI . . . 0/r32/EAX 0xc/disp8 . # copy *(ESI+12) to EAX - # . *Elf_p_filesz = EAX - 89/copy 0/mod/indirect 5/rm32/.disp32 . . 0/r32/EAX Elf_p_filesz/disp32 # copy EAX to *Elf_p_filesz - # *Elf_p_memsz = curr-segment->size - 89/copy 0/mod/indirect 5/rm32/.disp32 . . 0/r32/EAX Elf_p_memsz/disp32 # copy EAX to *Elf_p_memsz - # if (!string-equal?(curr-segment->name, "code") goto next check - # . EAX = curr-segment->name - 8b/copy 0/mod/indirect 6/rm32/ESI . . . 0/r32/EAX . . # copy *ESI to EAX - # . EAX = string-equal?(curr-segment->name, "code") - # . . push args - 68/push "code"/imm32 - 50/push-EAX - # . . call - e8/call string-equal?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . if (EAX == 0) goto next check - 3d/compare-EAX-and 0/imm32 - 74/jump-if-equal $emit-elf-program-header-entry:data/disp8 - # *Elf_p_flags = r-x - c7 0/subop/copy 0/mod/indirect 5/rm32/.disp32 . . . Elf_p_flags/disp32 5/imm32 # copy to *Elf_p_flags - eb/jump $emit-elf-program-header-entry:really-emit/disp8 -$emit-elf-program-header-entry:data: - # otherwise *Elf_p_flags = rw- - c7 0/subop/copy 0/mod/indirect 5/rm32/.disp32 . . . Elf_p_flags/disp32 6/imm32 # copy to *Elf_p_flags -$emit-elf-program-header-entry:really-emit: - # emit-hex-array(out, Elf_program_header_entry) - # . . push args - 68/push Elf_program_header_entry/imm32 - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - # . . call - e8/call emit-hex-array/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # write-buffered(out, "\n") - # . . push args - 68/push "\n"/imm32 - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - # . . call - e8/call write-buffered/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -$emit-elf-program-header-entry:end: - # . restore registers - 5e/pop-to-ESI - 58/pop-to-EAX - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -# - some helpers for tests - -stream-add4: # in : (address stream byte), key : address, val1 : address, val2 : address, val3 : address - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # . save registers - 50/push-EAX - 51/push-ECX - 52/push-EDX - 56/push-ESI - # ESI = in - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 6/r32/ESI 8/disp8 . # copy *(EBP+8) to ESI - # curr/EAX = in->data + in->write - # . EAX = in->write - 8b/copy 0/mod/indirect 6/rm32/ESI . . . 0/r32/EAX . . # copy *ESI to EAX - # . EAX = ESI+EAX+12 - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 6/base/ESI 0/index/EAX . 0/r32/EAX 0xc/disp8 . # copy ESI+EAX+12 to EAX - # max/EDX = in->data + in->length - # . EDX = in->length - 8b/copy 1/mod/*+disp8 6/rm32/ESI . . . 2/r32/EDX 8/disp8 . # copy *(ESI+8) to EDX - # . EDX = ESI+EDX+12 - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 6/base/ESI 2/index/EDX . 2/r32/EDX 0xc/disp8 . # copy ESI+EDX+12 to EDX - # if (curr >= max) abort - 39/compare 3/mod/direct 0/rm32/EAX . . . 2/r32/EDX . . # compare EAX with EDX - 73/jump-if-greater-or-equal-unsigned $stream-add4:abort/disp8 - # *curr = key - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . 1/r32/ECX 0xc/disp8 . # copy *(EBP+12) to ECX - 89/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy ECX to *EAX - # curr += 4 - 05/add-to-EAX 4/imm32 - # if (curr >= max) abort - 39/compare 3/mod/direct 0/rm32/EAX . . . 2/r32/EDX . . # compare EAX with EDX - 73/jump-if-greater-or-equal-unsigned $stream-add4:abort/disp8 - # *curr = val1 - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . 1/r32/ECX 0x10/disp8 . # copy *(EBP+16) to ECX - 89/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy ECX to *EAX - # curr += 4 - 05/add-to-EAX 4/imm32 - # if (curr >= max) abort - 39/compare 3/mod/direct 0/rm32/EAX . . . 2/r32/EDX . . # compare EAX with EDX - 73/jump-if-greater-or-equal-unsigned $stream-add4:abort/disp8 - # *curr = val2 - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . 1/r32/ECX 0x14/disp8 . # copy *(EBP+20) to ECX - 89/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy ECX to *EAX - # curr += 4 - 05/add-to-EAX 4/imm32 - # if (curr >= max) abort - 39/compare 3/mod/direct 0/rm32/EAX . . . 2/r32/EDX . . # compare EAX with EDX - 73/jump-if-greater-or-equal-unsigned $stream-add4:abort/disp8 - # *curr = val3 - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . 1/r32/ECX 0x18/disp8 . # copy *(EBP+24) to ECX - 89/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy ECX to *EAX - # in->write += 16 - 81 0/subop/add 0/mod/indirect 6/rm32/ESI . . . . . 0x10/imm32 # add to *ESI -$stream-add4:end: - # . restore registers - 5e/pop-to-ESI - 5a/pop-to-EDX - 59/pop-to-ECX - 58/pop-to-EAX - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -$stream-add4:abort: - # . _write(2/stderr, error) - # . . push args - 68/push "overflow in stream-add4\n"/imm32 - 68/push 2/imm32/stderr - # . . call - e8/call _write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . syscall(exit, 1) - bb/copy-to-EBX 1/imm32 - b8/copy-to-EAX 1/imm32/exit - cd/syscall 0x80/imm8 - # never gets here - -# some variants of 'trace' that take multiple arguments in different combinations of types: -# n: int -# c: character [4-bytes, will eventually be UTF-8] -# s: (address string) -# l: (address slice) -# one gotcha: 's5' must not be empty - -trace-sssns: # s1 : (address string), s2 : (address string), s3 : (address string), n4 : int, s5 : (address string) - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # write(*Trace-stream, s1) - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Trace-stream/disp32 # push *Trace-stream - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # write(*Trace-stream, s2) - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Trace-stream/disp32 # push *Trace-stream - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # write(*Trace-stream, s3) - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x10/disp8 . # push *(EBP+16) - ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Trace-stream/disp32 # push *Trace-stream - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # print-int32(*Trace-stream, n4) - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x14/disp8 . # push *(EBP+20) - ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Trace-stream/disp32 # push *Trace-stream - # . . call - e8/call print-int32/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # trace(s5) # implicitly adds a newline and finalizes the trace line - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x18/disp8 . # push *(EBP+24) - # . . call - e8/call trace/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -$trace-sssns:end: - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-trace-sssns: - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # setup - # . *Trace-stream->write = 0 - 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 0/r32/EAX Trace-stream/disp32 # copy *Trace-stream to EAX - c7 0/subop/copy 0/mod/direct 0/rm32/EAX . . . . . 0/imm32 # clear *EAX - # trace-sssns("A" "b" "c " 3 " e") - # . . push args - 68/push " e"/imm32 - 68/push 3/imm32 - 68/push "c "/imm32 - 68/push "b"/imm32 - 68/push "A"/imm32 - # . . call - e8/call trace-sssns/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x14/imm32 # add to ESP -#? # dump *Trace-stream {{{ -#? # . write(2/stderr, "^") -#? # . . push args -#? 68/push "^"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write-stream(2/stderr, *Trace-stream) -#? # . . push args -#? ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Trace-stream/disp32 # push *Trace-stream -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write-stream/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write(2/stderr, "$\n") -#? # . . push args -#? 68/push "$\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # }}} - # check-trace-contains("Abc 0x00000003 e") - # . . push args - 68/push "F - test-trace-sssns"/imm32 - 68/push "Abc 0x00000003 e"/imm32 - # . . call - e8/call check-trace-contains/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -trace-snsns: # s1 : (address string), n2 : int, s3 : (address string), n4 : int, s5 : (address string) - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # write(*Trace-stream, s1) - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Trace-stream/disp32 # push *Trace-stream - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # print-int32(*Trace-stream, n2) - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Trace-stream/disp32 # push *Trace-stream - # . . call - e8/call print-int32/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # write(*Trace-stream, s3) - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x10/disp8 . # push *(EBP+16) - ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Trace-stream/disp32 # push *Trace-stream - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # print-int32(*Trace-stream, n4) - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x14/disp8 . # push *(EBP+20) - ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Trace-stream/disp32 # push *Trace-stream - # . . call - e8/call print-int32/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # trace(s5) # implicitly adds a newline and finalizes the trace line - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x18/disp8 . # push *(EBP+24) - # . . call - e8/call trace/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -$trace-snsns:end: - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-trace-snsns: - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # setup - # . *Trace-stream->write = 0 - 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 0/r32/EAX Trace-stream/disp32 # copy *Trace-stream to EAX - c7 0/subop/copy 0/mod/direct 0/rm32/EAX . . . . . 0/imm32 # clear *EAX - # trace-snsns("A " 2 " c " 3 " e") - # . . push args - 68/push " e"/imm32 - 68/push 3/imm32 - 68/push " c "/imm32 - 68/push 2/imm32 - 68/push "A "/imm32 - # . . call - e8/call trace-snsns/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x14/imm32 # add to ESP -#? # dump *Trace-stream {{{ -#? # . write(2/stderr, "^") -#? # . . push args -#? 68/push "^"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write-stream(2/stderr, *Trace-stream) -#? # . . push args -#? ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Trace-stream/disp32 # push *Trace-stream -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write-stream/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write(2/stderr, "$\n") -#? # . . push args -#? 68/push "$\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # }}} - # check-trace-contains("Abc 0x00000003 e") - # . . push args - 68/push "F - test-trace-snsns"/imm32 - 68/push "A 0x00000002 c 0x00000003 e"/imm32 - # . . call - e8/call check-trace-contains/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -trace-slsls: # s1 : (address string), l2 : (address slice), s3 : (address string), l4 : (address slice), s5 : (address string) - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # write(*Trace-stream, s1) - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Trace-stream/disp32 # push *Trace-stream - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # write-slice(*Trace-stream, l2) - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Trace-stream/disp32 # push *Trace-stream - # . . call - e8/call write-slice/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # write(*Trace-stream, s3) - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x10/disp8 . # push *(EBP+16) - ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Trace-stream/disp32 # push *Trace-stream - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # write-slice(*Trace-stream, l4) - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x14/disp8 . # push *(EBP+20) - ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Trace-stream/disp32 # push *Trace-stream - # . . call - e8/call write-slice/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # trace(s5) # implicitly adds a newline and finalizes the trace line - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x18/disp8 . # push *(EBP+24) - # . . call - e8/call trace/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -$trace-slsls:end: - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-trace-slsls: - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # setup - # . *Trace-stream->write = 0 - 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 0/r32/EAX Trace-stream/disp32 # copy *Trace-stream to EAX - c7 0/subop/copy 0/mod/direct 0/rm32/EAX . . . . . 0/imm32 # clear *EAX - # (EAX..ECX) = "b" - b8/copy-to-EAX "b"/imm32 - 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX - 05/add-to-EAX 4/imm32 - # var b/EBX : (address slice) = {EAX, ECX} - 51/push-ECX - 50/push-EAX - 89/copy 3/mod/direct 3/rm32/EBX . . . 4/r32/ESP . . # copy ESP to EBX - # (EAX..ECX) = "d" - b8/copy-to-EAX "d"/imm32 - 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX - 05/add-to-EAX 4/imm32 - # var d/EDX : (address slice) = {EAX, ECX} - 51/push-ECX - 50/push-EAX - 89/copy 3/mod/direct 2/rm32/EDX . . . 4/r32/ESP . . # copy ESP to EDX - # trace-slsls("A" b "c" d "e") - # . . push args - 68/push "e"/imm32 - 52/push-EDX - 68/push "c"/imm32 - 53/push-EBX - 68/push "A"/imm32 - # . . call - e8/call trace-slsls/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x14/imm32 # add to ESP -#? # dump *Trace-stream {{{ -#? # . write(2/stderr, "^") -#? # . . push args -#? 68/push "^"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write-stream(2/stderr, *Trace-stream) -#? # . . push args -#? ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Trace-stream/disp32 # push *Trace-stream -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write-stream/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write(2/stderr, "$\n") -#? # . . push args -#? 68/push "$\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # }}} - # check-trace-contains("Abcde") - # . . push args - 68/push "F - test-trace-slsls"/imm32 - 68/push "Abcde"/imm32 - # . . call - e8/call check-trace-contains/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -trace-slsns: # s1 : (address string), l2 : (address slice), s3 : (address string), n4 : int, s5 : (address string) - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # write(*Trace-stream, s1) - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Trace-stream/disp32 # push *Trace-stream - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # write-slice(*Trace-stream, l2) - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Trace-stream/disp32 # push *Trace-stream - # . . call - e8/call write-slice/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # write(*Trace-stream, s3) - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x10/disp8 . # push *(EBP+16) - ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Trace-stream/disp32 # push *Trace-stream - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # print-int32(*Trace-stream, n4) - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x14/disp8 . # push *(EBP+20) - ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Trace-stream/disp32 # push *Trace-stream - # . . call - e8/call print-int32/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # trace(s5) # implicitly adds a newline and finalizes the trace line - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x18/disp8 . # push *(EBP+24) - # . . call - e8/call trace/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -$trace-slsns:end: - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-trace-slsns: - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # setup - # . *Trace-stream->write = 0 - 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 0/r32/EAX Trace-stream/disp32 # copy *Trace-stream to EAX - c7 0/subop/copy 0/mod/direct 0/rm32/EAX . . . . . 0/imm32 # clear *EAX - # (EAX..ECX) = "b" - b8/copy-to-EAX "b"/imm32 - 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX - 05/add-to-EAX 4/imm32 - # var b/EBX : (address slice) = {EAX, ECX} - 51/push-ECX - 50/push-EAX - 89/copy 3/mod/direct 3/rm32/EBX . . . 4/r32/ESP . . # copy ESP to EBX - # trace-slsls("A" b "c " 3 " e") - # . . push args - 68/push " e"/imm32 - 68/push 3/imm32 - 68/push "c "/imm32 - 53/push-EBX - 68/push "A"/imm32 - # . . call - e8/call trace-slsns/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x14/imm32 # add to ESP -#? # dump *Trace-stream {{{ -#? # . write(2/stderr, "^") -#? # . . push args -#? 68/push "^"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write-stream(2/stderr, *Trace-stream) -#? # . . push args -#? ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Trace-stream/disp32 # push *Trace-stream -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write-stream/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write(2/stderr, "$\n") -#? # . . push args -#? 68/push "$\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # }}} - # check-trace-contains("Abc 0x00000003 e") - # . . push args - 68/push "F - test-trace-slsls"/imm32 - 68/push "Abc 0x00000003 e"/imm32 - # . . call - e8/call check-trace-contains/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -trace-slsss: # s1 : (address string), l2 : (address slice), s3 : (address string), s4 : (address string), s5 : (address string) - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # write(*Trace-stream, s1) - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Trace-stream/disp32 # push *Trace-stream - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # write-slice(*Trace-stream, l2) - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Trace-stream/disp32 # push *Trace-stream - # . . call - e8/call write-slice/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # write(*Trace-stream, s3) - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x10/disp8 . # push *(EBP+16) - ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Trace-stream/disp32 # push *Trace-stream - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # write(*Trace-stream, s4) - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x14/disp8 . # push *(EBP+20) - ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Trace-stream/disp32 # push *Trace-stream - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # trace(s5) # implicitly adds a newline and finalizes the trace line - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x18/disp8 . # push *(EBP+24) - # . . call - e8/call trace/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -$trace-slsss:end: - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-trace-slsss: - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # setup - # . *Trace-stream->write = 0 - 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 0/r32/EAX Trace-stream/disp32 # copy *Trace-stream to EAX - c7 0/subop/copy 0/mod/direct 0/rm32/EAX . . . . . 0/imm32 # clear *EAX - # (EAX..ECX) = "b" - b8/copy-to-EAX "b"/imm32 - 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX - 05/add-to-EAX 4/imm32 - # var b/EBX : (address slice) = {EAX, ECX} - 51/push-ECX - 50/push-EAX - 89/copy 3/mod/direct 3/rm32/EBX . . . 4/r32/ESP . . # copy ESP to EBX - # trace-slsss("A" b "c" "d" "e") - # . . push args - 68/push "e"/imm32 - 68/push "d"/imm32 - 68/push "c"/imm32 - 53/push-EBX - 68/push "A"/imm32 - # . . call - e8/call trace-slsss/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x14/imm32 # add to ESP -#? # dump *Trace-stream {{{ -#? # . write(2/stderr, "^") -#? # . . push args -#? 68/push "^"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write-stream(2/stderr, *Trace-stream) -#? # . . push args -#? ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Trace-stream/disp32 # push *Trace-stream -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write-stream/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write(2/stderr, "$\n") -#? # . . push args -#? 68/push "$\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # }}} - # check-trace-contains("Abcde") - # . . push args - 68/push "F - test-trace-slsss"/imm32 - 68/push "Abcde"/imm32 - # . . call - e8/call check-trace-contains/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -num-bytes: # line : (address stream) -> EAX : int - # pseudocode: - # result = 0 - # while true - # var word-slice = next-word(line) - # if slice-empty?(word-slice) # end of line - # break - # if slice-starts-with?(word-slice, "#") # comment - # break - # if is-label?(word-slice) # no need for label declarations anymore - # break - # if slice-equal?(word-slice, "==") - # break # no need for segment header lines - # result += compute-width(word-slice) - # return result - # - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # . save registers - 51/push-ECX - 52/push-EDX - 53/push-EBX - # var result/EAX = 0 - 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX - # var word-slice/ECX = {0, 0} - 68/push 0/imm32/end - 68/push 0/imm32/start - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX -#? # dump line {{{ -#? # . write(2/stderr, "LL: ") -#? # . . push args -#? 68/push "LL: "/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # write-stream(2/stderr, line) -#? # . . push args -#? ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write-stream/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . write(2/stderr, "$\n") -#? # . . push args -#? 68/push "$\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # }}} - # . rewind-stream(line) - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - # . . call - e8/call rewind-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -$num-bytes:loop: - # next-word(line, word-slice) - # . . push args - 51/push-ECX - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - # . . call - e8/call next-word/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # dump word-slice {{{ -#? # . write(2/stderr, "AA: ") -#? # . . push args -#? 68/push "AA: "/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . clear-stream(Stderr+4) -#? # . . save EAX -#? 50/push-EAX -#? # . . push args -#? b8/copy-to-EAX Stderr/imm32 -#? 05/add-to-EAX 4/imm32 -#? 50/push-EAX -#? # . . call -#? e8/call clear-stream/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -#? # . . restore EAX -#? 58/pop-to-EAX -#? # . write-slice-buffered(Stderr, word-slice) -#? # . . push args -#? 51/push-ECX -#? 68/push Stderr/imm32 -#? # . . call -#? e8/call write-slice-buffered/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # . flush(Stderr) -#? # . . push args -#? 68/push Stderr/imm32 -#? # . . call -#? e8/call flush/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -#? # . write(2/stderr, "$\n") -#? # . . push args -#? 68/push "$\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -#? # }}} -$num-bytes:check0: - # if (slice-empty?(word-slice)) break - # . save result - 50/push-EAX - # . EAX = slice-empty?(word-slice) - # . . push args - 51/push-ECX - # . . call - e8/call slice-empty?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . if (EAX != 0) break - 3d/compare-EAX-and 0/imm32 - # . restore result now that ZF is set - 58/pop-to-EAX - 75/jump-if-not-equal $num-bytes:end/disp8 -$num-bytes:check-for-comment: - # if (slice-starts-with?(word-slice, "#")) break - # . start/EDX = word-slice->start - 8b/copy 0/mod/indirect 1/rm32/ECX . . . 2/r32/EDX . . # copy *ECX to EDX - # . c/EBX = *start - 31/xor 3/mod/direct 3/rm32/EBX . . . 3/r32/EBX . . # clear EBX - 8a/copy-byte 0/mod/indirect 2/rm32/EDX . . . 3/r32/BL . . # copy byte at *EDX to BL - # . if (EBX == '#') break - 81 7/subop/compare 3/mod/direct 3/rm32/EBX . . . . . 0x23/imm32/hash # compare EBX - 74/jump-if-equal $num-bytes:end/disp8 -$num-bytes:check-for-label: - # if (slice-ends-with?(word-slice, ":")) break - # . end/EDX = word-slice->end - 8b/copy 1/mod/*+disp8 1/rm32/ECX . . . 2/r32/EDX 4/disp8 . # copy *(ECX+4) to EDX - # . c/EBX = *(end-1) - 31/xor 3/mod/direct 3/rm32/EBX . . . 3/r32/EBX . . # clear EBX - 8a/copy-byte 1/mod/*+disp8 2/rm32/EDX . . . 3/r32/BL -1/disp8 . # copy byte at *ECX to BL - # . if (EBX == ':') break - 81 7/subop/compare 3/mod/direct 3/rm32/EBX . . . . . 0x3a/imm32/colon # compare EBX - 74/jump-if-equal $num-bytes:end/disp8 -$num-bytes:check-for-segment-header: - # if (slice-equal?(word-slice, "==")) break - # . push result - 50/push-EAX - # . EAX = slice-equal?(word-slice, "==") - # . . push args - 68/push "=="/imm32 - 51/push-ECX - # . . call - e8/call slice-equal?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . if (EAX != 0) break - 3d/compare-EAX-and 0/imm32 - # . restore result now that ZF is set - 58/pop-to-EAX - 75/jump-if-not-equal $num-bytes:end/disp8 -$num-bytes:loop-body: - # result += compute-width-of-slice(word-slice) - # . copy result to EDX - 89/copy 3/mod/direct 2/rm32/EDX . . . 0/r32/EAX . . # copy EAX to EDX - # . EAX = compute-width-of-slice(word-slice) - # . . push args - 51/push-ECX - # . . call - e8/call compute-width-of-slice/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . EAX += result - 01/add 3/mod/direct 0/rm32/EAX . . . 2/r32/EDX . . # add EDX to EAX - e9/jump $num-bytes:loop/disp32 -$num-bytes:end: - # . rewind-stream(line) - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - # . . call - e8/call rewind-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . reclaim locals - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . restore registers - 5b/pop-to-EBX - 5a/pop-to-EDX - 59/pop-to-ECX - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-num-bytes-handles-empty-string: - # if a line starts with '#', return 0 - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # setup - # . clear-stream(_test-input-stream) - # . . push args - 68/push _test-input-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-stream) - # . . push args - 68/push _test-output-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # no contents in input - # EAX = num-bytes(_test-input-stream) - # . . push args - 68/push _test-input-stream/imm32 - # . . call - e8/call num-bytes/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # check-ints-equal(EAX, 0, msg) - # . . push args - 68/push "F - test-num-bytes-handles-empty-string"/imm32 - 68/push 0/imm32/true - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-num-bytes-ignores-comments: - # if a line starts with '#', return 0 - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # setup - # . clear-stream(_test-input-stream) - # . . push args - 68/push _test-input-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-stream) - # . . push args - 68/push _test-output-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # initialize input - # . write(_test-input-stream, "# abcd") - # . . push args - 68/push "# abcd"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # EAX = num-bytes(_test-input-stream) - # . . push args - 68/push _test-input-stream/imm32 - # . . call - e8/call num-bytes/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # check-ints-equal(EAX, 0, msg) - # . . push args - 68/push "F - test-num-bytes-ignores-comments"/imm32 - 68/push 0/imm32/true - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-num-bytes-ignores-labels: - # if the first word ends with ':', return 0 - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # setup - # . clear-stream(_test-input-stream) - # . . push args - 68/push _test-input-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-stream) - # . . push args - 68/push _test-output-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # initialize input - # . write(_test-input-stream, "ab: # cd") - # . . push args - 68/push "ab: # cd"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # EAX = num-bytes(_test-input-stream) - # . . push args - 68/push _test-input-stream/imm32 - # . . call - e8/call num-bytes/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # check-ints-equal(EAX, 0, msg) - # . . push args - 68/push "F - test-num-bytes-ignores-labels"/imm32 - 68/push 0/imm32/true - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-num-bytes-ignores-segment-headers: - # if the first word is '==', return 0 - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # setup - # . clear-stream(_test-input-stream) - # . . push args - 68/push _test-input-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-stream) - # . . push args - 68/push _test-output-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # initialize input - # . write(_test-input-stream, "== ab cd") - # . . push args - 68/push "== ab cd"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # EAX = num-bytes(_test-input-stream) - # . . push args - 68/push _test-input-stream/imm32 - # . . call - e8/call num-bytes/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # check-ints-equal(EAX, 0, msg) - # . . push args - 68/push "F - test-num-bytes-ignores-segment-headers"/imm32 - 68/push 0/imm32/true - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-num-bytes-counts-words-by-default: - # without metadata, count words - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # setup - # . clear-stream(_test-input-stream) - # . . push args - 68/push _test-input-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-stream) - # . . push args - 68/push _test-output-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # initialize input - # . write(_test-input-stream, "ab cd ef") - # . . push args - 68/push "ab cd ef"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # EAX = num-bytes(_test-input-stream) - # . . push args - 68/push _test-input-stream/imm32 - # . . call - e8/call num-bytes/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # check-ints-equal(EAX, 3, msg) - # . . push args - 68/push "F - test-num-bytes-counts-words-by-default"/imm32 - 68/push 3/imm32/true - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-num-bytes-ignores-trailing-comment: - # trailing comments appropriately ignored - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # setup - # . clear-stream(_test-input-stream) - # . . push args - 68/push _test-input-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-stream) - # . . push args - 68/push _test-output-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # initialize input - # . write(_test-input-stream, "ab cd # ef") - # . . push args - 68/push "ab cd # ef"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # EAX = num-bytes(_test-input-stream) - # . . push args - 68/push _test-input-stream/imm32 - # . . call - e8/call num-bytes/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # check-ints-equal(EAX, 2, msg) - # . . push args - 68/push "F - test-num-bytes-ignores-trailing-comment"/imm32 - 68/push 2/imm32/true - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -test-num-bytes-handles-imm32: - # if a word has the /imm32 metadata, count it as 4 bytes - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # setup - # . clear-stream(_test-input-stream) - # . . push args - 68/push _test-input-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . clear-stream(_test-output-stream) - # . . push args - 68/push _test-output-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # initialize input - # . write(_test-input-stream, "ab cd/imm32 ef") - # . . push args - 68/push "ab cd/imm32 ef"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # EAX = num-bytes(_test-input-stream) - # . . push args - 68/push _test-input-stream/imm32 - # . . call - e8/call num-bytes/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # check-ints-equal(EAX, 6, msg) - # . . push args - 68/push "F - test-num-bytes-handles-imm32"/imm32 - 68/push 6/imm32/true - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -== data - -# This block of bytes gets copied to the start of the output ELF file, with -# some fields filled in. -# http://www.sco.com/developers/gabi/latest/ch4.eheader.html -Elf_header: - # - length - 0x34/imm32 - # - data -$e_ident: - 7f 45/E 4c/L 46/F - 01/32-bit 01/little-endian 01/file-version 00/no-os-extensions - 00 00 00 00 00 00 00 00 # 8 bytes of padding -$e_type: - 02 00 -$e_machine: - 03 00 -$e_version: - 1/imm32 -Elf_e_entry: - 0x09000000/imm32 # approximate default; must be updated -$e_phoff: - 0x34/imm32 # offset for the 'program header table' containing segment headers -$e_shoff: - 0/imm32 # no sections -$e_flags: - 0/imm32 # unused -$e_ehsize: - 0x34 00 -$e_phentsize: - 0x20 00 -Elf_e_phnum: - 00 00 # number of segments; must be updated -$e_shentsize: - 00 00 # no sections -$e_shnum: - 00 00 -$e_shstrndx: - 00 00 - -# This block of bytes gets copied after the Elf_header once for each segment. -# Some fields need filling in each time. -# https://docs.oracle.com/cd/E19683-01/816-1386/chapter6-83432/index.html -Elf_program_header_entry: - # - length - 0x20/imm32 - # - data -$p_type: - 1/imm32/PT_LOAD -Elf_p_offset: - 0/imm32 # byte offset in the file at which a segment begins; must be updated -Elf_p_vaddr: - 0/imm32 # starting address to store the segment at before running the program -Elf_p_paddr: - 0/imm32 # should have same value as Elf_p_vaddr -Elf_p_filesz: - 0/imm32 -Elf_p_memsz: - 0/imm32 # should have same value as Elf_p_filesz -Elf_p_flags: - 6/imm32/rw- # read/write/execute permissions for the segment; must be updated for the code segment -$p_align: - # we hold this constant; changing it will require adjusting the way we - # compute the starting address for each segment - 0x1000/imm32 - -# . . vim:nowrap:textwidth=0 diff --git a/subx/apps/tests b/subx/apps/tests deleted file mode 100755 index 52af8441..00000000 --- a/subx/apps/tests +++ /dev/null Binary files differdiff --git a/subx/apps/tests.subx b/subx/apps/tests.subx deleted file mode 100644 index 12f4902b..00000000 --- a/subx/apps/tests.subx +++ /dev/null @@ -1,279 +0,0 @@ -# Generate code for a new function called 'run-tests' which calls in sequence -# all functions starting with 'test-'. - -== code -# instruction effective address register displacement immediate -# . op subop mod rm32 base index scale r32 -# . 1-3 bytes 3 bits 2 bits 3 bits 3 bits 3 bits 2 bits 2 bits 0/1/2/4 bytes 0/1/2/4 bytes - -Entry: - # Heap = new-segment(Heap-size) - # . . push args - 68/push Heap/imm32 - 68/push Heap-size/imm32 - # . . call - e8/call new-segment/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # initialize-trace-stream(256KB) - # . . push args - 68/push 0x40000/imm32/256KB - # . . call - e8/call initialize-trace-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - - # run tests if necessary, convert stdin if not - # . prolog - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # initialize heap - # - if argc > 1 and argv[1] == "test", then return run_tests() - # . argc > 1 - 81 7/subop/compare 1/mod/*+disp8 5/rm32/EBP . . . . 0/disp8 1/imm32 # compare *EBP - 7e/jump-if-lesser-or-equal $run-main/disp8 - # . argv[1] == "test" - # . . push args - 68/push "test"/imm32 - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - # . . call - e8/call kernel-string-equal?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . check result - 3d/compare-EAX-and 1/imm32 - 75/jump-if-not-equal $run-main/disp8 - # . run-tests() - e8/call run-tests/disp32 - 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 3/r32/EBX Num-test-failures/disp32 # copy *Num-test-failures to EBX - eb/jump $main:end/disp8 -$run-main: - # - otherwise convert stdin - # convert(Stdin, Stdout) - # . . push args - 68/push Stdout/imm32 - 68/push Stdin/imm32 - # . . call - e8/call convert/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . syscall(exit, 0) - bb/copy-to-EBX 0/imm32 -$main:end: - b8/copy-to-EAX 1/imm32/exit - cd/syscall 0x80/imm8 - -convert: # in : (address buffered-file), out : (address buffered-file) -> <void> - # pseudocode - # bool tests-found = false - # var line = new-stream(512, 1) - # var new-code-segment = new-stream(Segment-size, 1) - # write(new-code-segment, "\n==code\n") - # write(new-code-segment, "run-tests:\n") - # while true - # clear-stream(line) - # read-line-buffered(in, line) - # if (line->write == 0) break # end of file - # var word-slice = next-word(line) - # if is-label?(word-slice) - # if slice-starts-with?(word-slice, "test-") - # tests-found = true - # write(new-code-segment, " e8/call ") - # write-slice(new-code-segment, word-slice) - # write(new-code-segment, "/disp32\n") - # rewind-stream(line) - # write-stream-data(out, line) - # if tests-found - # write(new-code-segment, " c3/return\n") - # write-stream-data(out, new-code-segment) - # flush(out) - # - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # . save registers - 50/push-EAX - 51/push-ECX - 52/push-EDX - 53/push-EBX - 57/push-EDI - # var line/ECX : (address stream byte) = stream(512) - 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 0x200/imm32 # subtract from ESP - 68/push 0x200/imm32/length - 68/push 0/imm32/read - 68/push 0/imm32/write - 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - # var word-slice/EDX = {0, 0} - 68/push 0/imm32/end - 68/push 0/imm32/start - 89/copy 3/mod/direct 2/rm32/EDX . . . 4/r32/ESP . . # copy ESP to EDX - # tests-found?/EBX = false - 31/xor 3/mod/direct 3/rm32/EBX . . . 3/r32/EBX . . # clear EBX - # new-code-segment/EDI = new-stream(Heap, Segment-size, 1) - # . EAX = new-stream(Heap, Segment-size, 1) - # . . push args - 68/push 1/imm32 - ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Segment-size/disp32 # push *Segment-size - 68/push Heap/imm32 - # . . call - e8/call new-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # . EDI = EAX - 89/copy 3/mod/direct 7/rm32/EDI . . . 0/r32/EAX . . # copy EAX to EDI - # write(new-code-segment, "\n== code\n") - # . . push args - 68/push "\n== code\n"/imm32 - 57/push-EDI - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # write(new-code-segment, "run-tests:\n") - # . . push args - 68/push "run-tests:\n"/imm32 - 57/push-EDI - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -$convert:loop: - # clear-stream(line) - # . . push args - 51/push-ECX - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # read-line-buffered(in, line) - # . . push args - 51/push-ECX - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - # . . call - e8/call read-line-buffered/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -$convert:check0: - # if (line->write == 0) break - 81 7/subop/compare 0/mod/indirect 1/rm32/ECX . . . . . 0/imm32 # compare *ECX - 0f 84/jump-if-equal $convert:break/disp32 - # next-word(line, word-slice) - # . . push args - 52/push-EDX - 51/push-ECX - # . . call - e8/call next-word/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -$convert:check-for-label: - # if (!is-label?(word-slice)) continue - # . EAX = is-label?(word-slice) - # . . push args - 52/push-EDX - # . . call - e8/call is-label?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . if (EAX == 0) continue - 3d/compare-EAX-and 0/imm32 - 74/jump-if-equal $convert:continue/disp8 -$convert:check-label-prefix: - # strip trailing ':' from word-slice - ff 1/subop/decrement 1/mod/*+disp8 2/rm32/EDX . . . . 4/disp8 . # decrement *(EDX+4) - # if !slice-starts-with?(word-slice, "test-") continue - # . . push args - 68/push "test-"/imm32 - 52/push-EDX - # . . call - e8/call slice-starts-with?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # . if (EAX == 0) break - 3d/compare-EAX-and 0/imm32 - 74/jump-if-equal $convert:continue/disp8 -$convert:call-test-function: - # tests-found? = true - bb/copy-to-EBX 1/imm32/true - # write(new-code-segment, " e8/call ") - # . . push args - 68/push " e8/call "/imm32 - 57/push-EDI - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # write-slice(new-code-segment, word-slice) - # . . push args - 52/push-EDX - 57/push-EDI - # . . call - e8/call write-slice/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # write(new-code-segment, "/disp32\n") - # . . push args - 68/push "/disp32\n"/imm32 - 57/push-EDI - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -$convert:continue: - # rewind-stream(line) - # . . push args - 51/push-ECX - # . . call - e8/call rewind-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # write-stream-data(out, line) - # . . push args - 51/push-ECX - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - # . . call - e8/call write-stream-data/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # loop - e9/jump $convert:loop/disp32 -$convert:break: - # if (!tests-found?) goto end - 81 7/subop/compare 3/mod/direct 3/rm32/EBX . . . . . 0/imm32 # compare EBX - 74/jump-if-equal $convert:end/disp8 - # write(new-code-segment, " c3/return\n") - # . . push args - 68/push " c3/return\n"/imm32 - 57/push-EDI - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # write-stream-data(out, new-code-segment) - # . . push args - 57/push-EDI - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - # . . call - e8/call write-stream-data/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -$convert:end: - # flush(out) - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - # . . call - e8/call flush/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . reclaim locals - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x214/imm32 # add to ESP - # . restore registers - 5f/pop-to-EDI - 5b/pop-to-EBX - 5a/pop-to-EDX - 59/pop-to-ECX - 58/pop-to-EAX - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -# . . vim:nowrap:textwidth=0 diff --git a/subx/build b/subx/build deleted file mode 100755 index 67479b92..00000000 --- a/subx/build +++ /dev/null @@ -1,141 +0,0 @@ -#!/bin/sh -# returns 0 on successful build or nothing to build -# non-zero exit status only on error during building -set -e # stop immediately on error - -# [0-9]*.cc -> subx.cc -> subx_bin -# (layers) | | -# tangle $CXX - -# can also be called with a layer to only build until -# $ ./build --until 050 -UNTIL_LAYER=${2:-zzz} - -# we use two mechanisms to speed up rebuilds: -# - older_than: run a command if the output is older than any of the inputs -# - update: if a command is quick to run, always run it but update the result only on any change -# -# avoid combining both mechanisms to generate a single file -# otherwise you'll see spurious messages about files being updated -# risk: a file may unnecessarily update without changes, causing unnecessary work downstream - -test "$CXX" || export CXX=c++ -test "$CC" || export CC=cc -test "$CFLAGS" || export CFLAGS="-g -O3 -std=c++98" # CI has an ancient version; don't expect recent dialects -export CFLAGS="$CFLAGS -Wall -Wextra -fno-strict-aliasing" - -# return 1 if $1 is older than _any_ of the remaining args -older_than() { - local target=$1 - shift - if [ ! -e $target ] - then -#? echo "$target doesn't exist" - echo "updating $target" >&2 - return 0 # success - fi - local f - for f in $* - do - if [ $f -nt $target ] - then - echo "updating $target" >&2 - return 0 # success - fi - done - return 1 # failure -} - -# redirect to $1, unless it's already identical -update() { - if [ ! -e $1 ] - then - cat > $1 - else - cat > $1.tmp - diff -q $1 $1.tmp >/dev/null && rm $1.tmp || mv $1.tmp $1 - fi -} - -update_cp() { - if [ ! -e $2/$1 ] - then - cp $1 $2 - elif [ $1 -nt $2/$1 ] - then - cp $1 $2 - fi -} - -noisy_cd() { - cd $1 - echo "-- `pwd`" >&2 -} - -older_than ../enumerate/enumerate ../enumerate/enumerate.cc && { - $CXX $CFLAGS ../enumerate/enumerate.cc -o ../enumerate/enumerate -} - -older_than ../tangle/tangle ../tangle/*.cc && { - noisy_cd ../tangle - { - grep -h "^struct .* {" [0-9]*.cc |sed 's/\(struct *[^ ]*\).*/\1;/' - grep -h "^typedef " [0-9]*.cc - } |update type_list - grep -h "^[^ #].*) {" [0-9]*.cc |sed 's/ {.*/;/' |update function_list - ls [0-9]*.cc |grep -v "\.test\.cc$" |sed 's/.*/#include "&"/' |update file_list - ls [0-9]*.test.cc |sed 's/.*/#include "&"/' |update test_file_list - grep -h "^[[:space:]]*void test_" [0-9]*.cc |sed 's/^\s*void \(.*\)() {$/\1,/' |update test_list - grep -h "^\s*void test_" [0-9]*.cc |sed 's/^\s*void \(.*\)() {.*/"\1",/' |update test_name_list - $CXX $CFLAGS boot.cc -o tangle - ./tangle test - noisy_cd ../subx # no effect; just to show us returning to the parent directory -} - -LAYERS=$(../enumerate/enumerate --until $UNTIL_LAYER |grep '.cc$') -older_than subx.cc $LAYERS ../enumerate/enumerate ../tangle/tangle && { - # no update here; rely on 'update' calls downstream - ../tangle/tangle $LAYERS > subx.cc -} - -grep -h "^[^[:space:]#].*) {$" subx.cc |grep -v ":.*(" |sed 's/ {.*/;/' |update function_list -grep -h "^\s*void test_" subx.cc |sed 's/^\s*void \(.*\)() {.*/\1,/' |update test_list -grep -h "^\s*void test_" subx.cc |sed 's/^\s*void \(.*\)() {.*/"\1",/' |update test_name_list - -older_than subx_bin subx.cc *_list && { - $CXX $CFLAGS subx.cc -o subx_bin -} - -if [ $# -eq 0 ] -then - - # Assumption: SubX programs don't need to be retranslated every time we - # rebuild the C++ bootstrap. - - # simple example programs - for n in `seq 1 12` - do - older_than examples/ex$n examples/ex$n.subx && { - ./subx_bin translate examples/ex$n.subx -o examples/ex$n - } - done - - # simple apps that use the standard library - for app in factorial crenshaw2-1 crenshaw2-1b handle - do - older_than apps/$app apps/$app.subx [0-9]*.subx && { - ./subx_bin translate [0-9]*.subx apps/$app.subx -o apps/$app - } - done - - # self-hosting translator - for phase in hex survey pack assort dquotes tests - do - older_than apps/$phase apps/$phase.subx apps/subx-common.subx [0-9]*.subx && { - ./subx_bin translate [0-9]*.subx apps/subx-common.subx apps/$phase.subx -o apps/$phase - } - done - -fi - -exit 0 diff --git a/subx/build_and_test_until b/subx/build_and_test_until deleted file mode 100755 index 710e1d55..00000000 --- a/subx/build_and_test_until +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/sh -# Run tests for just a subset of layers. -# -# Usage: -# build_and_test_until [file prefix] [test name] -# Provide the second arg to run just a single test. -set -e - -# clean previous builds if they were building until a different layer -touch .until -PREV_UNTIL=`cat .until` -if [ "$PREV_UNTIL" != $1 ] -then - ./clean top-level - echo $1 > .until -fi - -./build --until $1 && ./subx_bin test $2 diff --git a/subx/cheatsheet.pdf b/subx/cheatsheet.pdf deleted file mode 100644 index 386738e9..00000000 --- a/subx/cheatsheet.pdf +++ /dev/null Binary files differdiff --git a/subx/clean b/subx/clean deleted file mode 100755 index 2878ea58..00000000 --- a/subx/clean +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/sh -set -e - -set -v -rm -rf subx.cc subx_bin* *_list -test $# -gt 0 && exit 0 # convenience: 'clean top-level' to leave subsidiary tools alone -rm -rf ../enumerate/enumerate ../tangle/tangle ../tangle/*_list ../*/*.dSYM -rm -rf .until diff --git a/subx/diff_ntranslate b/subx/diff_ntranslate deleted file mode 100755 index 46e76844..00000000 --- a/subx/diff_ntranslate +++ /dev/null @@ -1,7 +0,0 @@ -#!/usr/bin/env zsh - -set -e -subx translate $* -o a0.elf -xxd a0.elf > a0.xxd -./ntranslate $* # into a.elf and a.xxd -diff a0.xxd a.xxd diff --git a/subx/diff_translate b/subx/diff_translate deleted file mode 100755 index dbe55a24..00000000 --- a/subx/diff_translate +++ /dev/null @@ -1,7 +0,0 @@ -#!/usr/bin/env zsh - -set -e -subx translate $* -o a0.elf -xxd a0.elf > a0.xxd -./translate $* # into a.elf and a.xxd -diff a0.xxd a.xxd diff --git a/subx/edit b/subx/edit deleted file mode 100755 index dc434f58..00000000 --- a/subx/edit +++ /dev/null @@ -1,23 +0,0 @@ -#!/usr/bin/env zsh -# Helper to more conveniently open commonly-used SubX programs. - -if [ $# -eq 0 ] -then - echo "Usage: $0 <file root without subdirectory or .subx extension>" - echo - echo "Naming convention: Files starting with 'ex' will be assumed to live in examples/" - echo "Other files will be assumed to live in apps/" - exit 1 -fi - -if [[ $EDITOR == *'vim'* ]] -then - LOCAL_SETTINGS='-S vimrc.vim' -fi - -if [[ $1 == 'ex'* ]] -then - eval $EDITOR $LOCAL_SETTINGS examples/$1.subx -else - eval $EDITOR $LOCAL_SETTINGS apps/$1.subx -fi diff --git a/subx/examples/Readme.md b/subx/examples/Readme.md deleted file mode 100644 index d4bb1ff5..00000000 --- a/subx/examples/Readme.md +++ /dev/null @@ -1,6 +0,0 @@ -Small example programs, each with a simple pedagogical goal. - -They also help to validate SubX instruction semantics against native x86 -hardware. For example, loading a single byte to a register would for some time -clear the rest of the register. This behavior was internally consistent with -unit tests. It took running an example binary natively to catch the discrepancy. diff --git a/subx/examples/ex1 b/subx/examples/ex1 deleted file mode 100755 index aeb62302..00000000 --- a/subx/examples/ex1 +++ /dev/null Binary files differdiff --git a/subx/examples/ex1.subx b/subx/examples/ex1.subx deleted file mode 100644 index 0aca40f8..00000000 --- a/subx/examples/ex1.subx +++ /dev/null @@ -1,21 +0,0 @@ -# First program: same as https://www.muppetlabs.com/~breadbox/software/tiny/teensy.html -# Just return 42. -# -# To run (from the subx directory): -# $ ./subx translate examples/ex1.2.subx -o examples/ex1 -# $ ./subx run examples/ex1 -# Expected result: -# $ echo $? -# 42 - -== code 0x09000000 - -Entry: -# syscall(exit, 42) -bb/copy-to-EBX 2a/imm32 # 42 in hex -b8/copy-to-EAX 1/imm32/exit -cd/syscall 0x80/imm8 - -== data 0x0a000000 - -# . . vim:nowrap:textwidth=0 diff --git a/subx/examples/ex10 b/subx/examples/ex10 deleted file mode 100755 index d8ad528e..00000000 --- a/subx/examples/ex10 +++ /dev/null Binary files differdiff --git a/subx/examples/ex10.subx b/subx/examples/ex10.subx deleted file mode 100644 index 51cc0a8c..00000000 --- a/subx/examples/ex10.subx +++ /dev/null @@ -1,72 +0,0 @@ -# String comparison: return 1 iff the two args passed in at the commandline are equal. -# -# To run (from the subx directory): -# $ ./subx translate examples/ex10.subx -o examples/ex10 -# $ ./subx run examples/ex10 abc abd -# Expected result: -# $ echo $? -# 0 # false - -== code 0x09000000 -# instruction effective address register displacement immediate -# . op subop mod rm32 base index scale r32 -# . 1-3 bytes 3 bits 2 bits 3 bits 3 bits 3 bits 2 bits 2 bits 0/1/2/4 bytes 0/1/2/4 bytes - -Entry: # return argv-equal(argv[1], argv[2]) -# At the start of a SubX program: -# argc: *ESP -# argv[0]: *(ESP+4) -# argv[1]: *(ESP+8) -# ... - # . prolog - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # argv-equal(argv[1], argv[2]) - # . . push argv[2] - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - # . . push argv[1] - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - # . . call - e8/call argv-equal/disp32 - # syscall(exit, EAX) - 89/copy 3/mod/direct 3/rm32/EBX . . . 0/r32/EAX . . # copy EAX to EBX - b8/copy-to-EAX 1/imm32/exit - cd/syscall 0x80/imm8 - -# compare two null-terminated ascii strings -# reason for the name: the only place we should have null-terminated ascii strings is from commandline args -argv-equal: # (s1, s2) : null-terminated ascii strings -> EAX : boolean - # initialize s1 (ECX) and s2 (EDX) - 8b/copy 1/mod/*+disp8 4/rm32/sib 4/base/ESP 4/index/none . 1/r32/ECX 4/disp8 . # copy *(ESP+4) to ECX - 8b/copy 1/mod/*+disp8 4/rm32/sib 4/base/ESP 4/index/none . 2/r32/EDX 8/disp8 . # copy *(ESP+8) to EDX -$argv-equal:loop: - # c1/EAX, c2/EBX = *s1, *s2 - b8/copy-to-EAX 0/imm32 - 8a/copy-byte 0/mod/indirect 1/rm32/ECX . . . 0/r32/AL . . # copy byte at *ECX to AL - bb/copy-to-EBX 0/imm32 - 8a/copy-byte 0/mod/indirect 2/rm32/EDX . . . 3/r32/BL . . # copy byte at *EDX to BL - # if (c1 == 0) break - 3d/compare-EAX-and 0/imm32 - 74/jump-if-equal $argv-equal:break/disp8 - # if (c1 != c2) return false - 39/compare 3/mod/direct 0/rm32/EAX . . . 3/r32/EBX . . # compare EAX and EBX - 75/jump-if-not-equal $argv-equal:false/disp8 - # ++s1, ++s2 - 41/increment-ECX - 42/increment-EDX - # end while - eb/jump $argv-equal:loop/disp8 -$argv-equal:break: - # if (c2 == 0) return true - 81 7/subop/compare 3/mod/direct 3/rm32/EBX . . . . . 0/imm32 # compare EBX - 75/jump-if-not-equal $argv-equal:false/disp8 -$argv-equal:success: - b8/copy-to-EAX 1/imm32 - c3/return - # return false -$argv-equal:false: - b8/copy-to-EAX 0/imm32 - c3/return - -== data 0x0a000000 - -# . . vim:nowrap:textwidth=0 diff --git a/subx/examples/ex11 b/subx/examples/ex11 deleted file mode 100755 index 0ffafb6f..00000000 --- a/subx/examples/ex11 +++ /dev/null Binary files differdiff --git a/subx/examples/ex11.subx b/subx/examples/ex11.subx deleted file mode 100644 index ba75c1d3..00000000 --- a/subx/examples/ex11.subx +++ /dev/null @@ -1,357 +0,0 @@ -# Null-terminated vs length-prefixed ascii strings. -# -# By default we create strings with a 4-byte length prefix rather than a null suffix. -# However we still need null-prefixed strings when interacting with the Linux -# kernel in a few places. This layer implements a function for comparing -# a null-terminated 'kernel string' with a length-prefixed 'SubX string'. -# -# To run (from the subx directory): -# $ ./subx translate examples/ex11.subx -o examples/ex11 -# $ ./subx run examples/ex11 # runs a series of tests -# ...... # all tests pass -# -# (We can't yet run the tests when given a "test" commandline argument, -# because checking for it would require the function being tested! Breakage -# would cause tests to not run, rather than to fail as we'd like.) - -== code 0x09000000 -# instruction effective address register displacement immediate -# . op subop mod rm32 base index scale r32 -# . 1-3 bytes 3 bits 2 bits 3 bits 3 bits 3 bits 2 bits 2 bits 0/1/2/4 bytes 0/1/2/4 bytes - -Entry: # run all tests - e8/call run-tests/disp32 # 'run-tests' is a function created automatically by SubX. It calls all functions that start with 'test-'. - # syscall(exit, EAX) - 89/copy 3/mod/direct 3/rm32/EBX . . . 0/r32/EAX . . # copy EAX to EBX - b8/copy-to-EAX 1/imm32/exit - cd/syscall 0x80/imm8 - -# compare a null-terminated ascii string with a more idiomatic length-prefixed byte array -# reason for the name: the only place we should have null-terminated ascii strings is from commandline args -kernel-string-equal?: # s : null-terminated ascii string, benchmark : length-prefixed ascii string -> EAX : boolean - # pseudocode: - # n = benchmark->length - # s1 = s - # s2 = benchmark->data - # i = 0 - # while i < n - # c1 = *s1 - # c2 = *s2 - # if (c1 == 0) return false - # if (c1 != c2) return false - # ++s1, ++s2, ++i - # return *s1 == 0 - # - # registers: - # i: ECX - # n: EDX - # s1: EDI - # s2: ESI - # c1: EAX - # c2: EBX - # - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # . save registers - 51/push-ECX - 52/push-EDX - 53/push-EBX - 56/push-ESI - 57/push-EDI - # s1/EDI = s - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 7/r32/EDI 8/disp8 . # copy *(EBP+8) to EDI - # n/EDX = benchmark->length - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 2/r32/EDX 0xc/disp8 . # copy *(EBP+12) to EDX - 8b/copy 0/mod/indirect 2/rm32/EDX . . . 2/r32/EDX . . # copy *EDX to EDX - # s2/ESI = benchmark->data - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 6/r32/ESI 0xc/disp8 . # copy *(EBP+12) to ESI - 81 0/subop/add 3/mod/direct 6/rm32/ESI . . . . . 4/imm32 # add to ESI - # i/ECX = c1/EAX = c2/EBX = 0 - b9/copy-to-ECX 0/imm32/exit - b8/copy-to-EAX 0/imm32 - bb/copy-to-EBX 0/imm32 -$kernel-string-equal?:loop: - # if (i >= n) break - 39/compare 3/mod/direct 1/rm32/ECX . . . 2/r32/EDX . . # compare ECX with EDX - 7d/jump-if-greater-or-equal $kernel-string-equal?:break/disp8 - # c1 = *s1 - 8a/copy-byte 0/mod/indirect 7/rm32/EDI . . . 0/r32/AL . . # copy byte at *EDI to AL - # c2 = *s2 - 8a/copy-byte 0/mod/indirect 6/rm32/ESI . . . 3/r32/BL . . # copy byte at *ESI to BL - # if (c1 == 0) return false - 3d/compare-EAX-and 0/imm32 - 74/jump-if-equal $kernel-string-equal?:false/disp8 - # if (c1 != c2) return false - 39/compare 3/mod/direct 0/rm32/EAX . . . 3/r32/EBX . . # compare EAX and EBX - 75/jump-if-not-equal $kernel-string-equal?:false/disp8 - # ++i - 41/increment-ECX - # ++s1 - 47/increment-EDI - # ++s2 - 46/increment-ESI - eb/jump $kernel-string-equal?:loop/disp8 -$kernel-string-equal?:break: - # return *s1 == 0 - 8a/copy-byte 0/mod/indirect 7/rm32/EDI . . . 0/r32/AL . . # copy byte at *EDI to AL - 3d/compare-EAX-and 0/imm32 - 75/jump-if-not-equal $kernel-string-equal?:false/disp8 -$kernel-string-equal?:true: - b8/copy-to-EAX 1/imm32 - eb/jump $kernel-string-equal?:end/disp8 -$kernel-string-equal?:false: - b8/copy-to-EAX 0/imm32 -$kernel-string-equal?:end: - # . restore registers - 5f/pop-to-EDI - 5e/pop-to-ESI - 5b/pop-to-EBX - 5a/pop-to-EDX - 59/pop-to-ECX - # . epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -# - tests - -test-compare-null-kernel-string-with-empty-array: - # EAX = kernel-string-equal?(Null-kernel-string, "") - # . . push args - 68/push ""/imm32 - 68/push Null-kernel-string/imm32 - # . . call - e8/call kernel-string-equal?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check-ints-equal(EAX, 1, msg) - # . . push args - 68/push "F - test-compare-null-kernel-string-with-empty-array"/imm32 - 68/push 1/imm32/true - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - c3/return - -test-compare-null-kernel-string-with-non-empty-array: - # EAX = kernel-string-equal?(Null-kernel-string, "Abc") - # . . push args - 68/push "Abc"/imm32 - 68/push Null-kernel-string/imm32 - # . . call - e8/call kernel-string-equal?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check-ints-equal(EAX, 0, msg) - # . . push args - 68/push "F - test-compare-null-kernel-string-with-non-empty-array"/imm32 - 68/push 0/imm32/false - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - c3/return - -test-compare-kernel-string-with-equal-array: - # EAX = kernel-string-equal?(_test-Abc-kernel-string, "Abc") - # . . push args - 68/push "Abc"/imm32 - 68/push _test-Abc-kernel-string/imm32 - # . . call - e8/call kernel-string-equal?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check-ints-equal(EAX, 1, msg) - # . . push args - 68/push "F - test-compare-kernel-string-with-equal-array"/imm32 - 68/push 1/imm32/true - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - c3/return - -test-compare-kernel-string-with-inequal-array: - # EAX = kernel-string-equal?(_test-Abc-kernel-string, "Adc") - # . . push args - 68/push "Adc"/imm32 - 68/push _test-Abc-kernel-string/imm32 - # . . call - e8/call kernel-string-equal?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check-ints-equal(EAX, 0, msg) - # . . push args - 68/push "F - test-compare-kernel-string-with-equal-array"/imm32 - 68/push 0/imm32/false - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - c3/return - -test-compare-kernel-string-with-empty-array: - # EAX = kernel-string-equal?(_test-Abc-kernel-string, "") - # . . push args - 68/push ""/imm32 - 68/push _test-Abc-kernel-string/imm32 - # . . call - e8/call kernel-string-equal?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check-ints-equal(EAX, 0, msg) - # . . push args - 68/push "F - test-compare-kernel-string-with-equal-array"/imm32 - 68/push 0/imm32/false - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - c3/return - -test-compare-kernel-string-with-shorter-array: - # EAX = kernel-string-equal?(_test-Abc-kernel-string, "Ab") - # . . push args - 68/push "Ab"/imm32 - 68/push _test-Abc-kernel-string/imm32 - # . . call - e8/call kernel-string-equal?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check-ints-equal(EAX, 0, msg) - # . . push args - 68/push "F - test-compare-kernel-string-with-shorter-array"/imm32 - 68/push 0/imm32/false - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - c3/return - -test-compare-kernel-string-with-longer-array: - # EAX = kernel-string-equal?(_test-Abc-kernel-string, "Abcd") - # . . push args - 68/push "Abcd"/imm32 - 68/push _test-Abc-kernel-string/imm32 - # . . call - e8/call kernel-string-equal?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check-ints-equal(EAX, 0, msg) - # . . push args - 68/push "F - test-compare-kernel-string-with-longer-array"/imm32 - 68/push 0/imm32/false - 50/push-EAX - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - c3/return - -# - helpers - -# print msg to stderr if a != b, otherwise print "." -check-ints-equal: # (a : int, b : int, msg : (address array byte)) -> boolean - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # . save registers - 51/push-ECX - 53/push-EBX - # load args into EAX, EBX and ECX - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 0/r32/EAX 8/disp8 . # copy *(EBP+8) to EAX - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 3/r32/EBX 0xc/disp8 . # copy *(EBP+12) to EBX - # if (EAX == b/EBX) print('.') and return - 39/compare 3/mod/direct 0/rm32/EAX . . . 3/r32/EBX . . # compare EAX and EBX - 75/jump-if-unequal $check-ints-equal:else/disp8 - # . write-stderr('.') - # . . push args - 68/push "."/imm32 - # . . call - e8/call write-stderr/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # . return - eb/jump $check-ints-equal:end/disp8 - # otherwise print(msg) -$check-ints-equal:else: - # copy msg into ECX - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 1/r32/ECX 0x10/disp8 . # copy *(EBP+16) to ECX - # print(ECX) - # . . push args - 51/push-ECX - # . . call - e8/call write-stderr/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # print newline - # . . push args - 68/push Newline/imm32 - # . . call - e8/call write-stderr/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -$check-ints-equal:end: - # . restore registers - 5b/pop-to-EBX - 59/pop-to-ECX - # end - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -write-stderr: # s : (address array byte) -> <void> - # . prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # . save registers - 50/push-EAX - 51/push-ECX - 52/push-EDX - 53/push-EBX - # syscall(write, 2/stderr, (data) s+4, (size) *s) - # . . fd = 2 (stderr) - bb/copy-to-EBX 2/imm32 - # . . x = s+4 - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 1/r32/ECX 8/disp8 . # copy *(EBP+8) to ECX - 81 0/subop/add 3/mod/direct 1/rm32/ECX . . . . . 4/imm32 # add to ECX - # . . size = *s - 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 2/r32/EDX 8/disp8 . # copy *(EBP+8) to EDX - 8b/copy 0/mod/indirect 2/rm32/EDX . . . 2/r32/EDX . . # copy *EDX to EDX - # . . syscall - b8/copy-to-EAX 4/imm32/write - cd/syscall 0x80/imm8 - # . restore registers - 5b/pop-to-EBX - 5a/pop-to-EDX - 59/pop-to-ECX - 58/pop-to-EAX - # . end - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return - -== data 0x0a000000 - -Newline: - # size - 1/imm32 - # data - 0a/newline - -# for kernel-string-equal tests -Null-kernel-string: - 00/null - -_test-Abc-kernel-string: - 41/A 62/b 63/c 00/null - -# . . vim:nowrap:textwidth=0 diff --git a/subx/examples/ex12 b/subx/examples/ex12 deleted file mode 100755 index c13a86ed..00000000 --- a/subx/examples/ex12 +++ /dev/null Binary files differdiff --git a/subx/examples/ex12.subx b/subx/examples/ex12.subx deleted file mode 100644 index 358da1d3..00000000 --- a/subx/examples/ex12.subx +++ /dev/null @@ -1,45 +0,0 @@ -# Example showing mmap syscall. -# Create a new segment using mmap, save the address, write to it. -# -# To run (from the subx directory): -# $ ./subx translate examples/ex12.subx -o examples/ex12 -# $ ./subx run examples/ex12 -# You shouldn't get a segmentation fault. - -== code 0x09000000 -# instruction effective address register displacement immediate -# . op subop mod rm32 base index scale r32 -# . 1-3 bytes 3 bits 2 bits 3 bits 3 bits 3 bits 2 bits 2 bits 0/1/2/4 bytes 0/1/2/4 bytes - -Entry: - # syscall(mmap, 0x1000) - bb/copy-to-EBX Mmap-new-segment/imm32 - b8/copy-to-EAX 0x5a/imm32/mmap - cd/syscall 0x80/imm8 - - # write to *EAX to check that we have access to the newly-allocated segment - c7 0/subop/copy 0/mod/direct 0/rm32/EAX . . . . . 0x34/imm32 # copy to *EAX - - # syscall(exit, EAX) - 89/copy 3/mod/direct 3/rm32/EBX . . . 0/r32/EAX . . # copy EAX to EBX - b8/copy-to-EAX 1/imm32/exit - cd/syscall 0x80/imm8 - -== data 0x0a000000 - -# various constants used here were found in the Linux sources (search for file mman-common.h) -Mmap-new-segment: # type mmap_arg_struct - # addr - 0/imm32 - # len - 0x100/imm32 - # protection flags - 3/imm32 # PROT_READ | PROT_WRITE - # sharing flags - 0x22/imm32 # MAP_PRIVATE | MAP_ANONYMOUS - # fd - -1/imm32 # since MAP_ANONYMOUS is specified - # offset - 0/imm32 # since MAP_ANONYMOUS is specified - -# . . vim:nowrap:textwidth=0 diff --git a/subx/examples/ex2 b/subx/examples/ex2 deleted file mode 100755 index 55a87a3f..00000000 --- a/subx/examples/ex2 +++ /dev/null Binary files differdiff --git a/subx/examples/ex2.subx b/subx/examples/ex2.subx deleted file mode 100644 index 025eb0fa..00000000 --- a/subx/examples/ex2.subx +++ /dev/null @@ -1,23 +0,0 @@ -# Add 1 and 1, and return the result in the exit code. -# -# To run (from the subx directory): -# $ ./subx translate examples/ex2.subx -o examples/ex2 -# $ ./subx run examples/ex2 -# Expected result: -# $ echo $? -# 2 - -== code 0x09000000 - -Entry: -# EBX = 1 -bb/copy-to-EBX 1/imm32 -# increment EBX -43/increment-EBX -# syscall(exit, EBX) -b8/copy-to-EAX 1/imm32/exit -cd/syscall 0x80/imm8 - -== data 0x0a000000 - -# . . vim:nowrap:textwidth=0 diff --git a/subx/examples/ex3 b/subx/examples/ex3 deleted file mode 100755 index d85aba8e..00000000 --- a/subx/examples/ex3 +++ /dev/null Binary files differdiff --git a/subx/examples/ex3.subx b/subx/examples/ex3.subx deleted file mode 100644 index 1d52e87f..00000000 --- a/subx/examples/ex3.subx +++ /dev/null @@ -1,39 +0,0 @@ -# Add the first 10 numbers, and return the result in the exit code. -# -# To run (from the subx directory): -# $ ./subx translate examples/ex3.subx -o examples/ex3 -# $ ./subx run examples/ex3 -# Expected result: -# $ echo $? -# 55 - -== code 0x09000000 -# instruction effective address register displacement immediate -# . op subop mod rm32 base index scale r32 -# . 1-3 bytes 3 bits 2 bits 3 bits 3 bits 3 bits 2 bits 2 bits 0/1/2/4 bytes 0/1/2/4 bytes - -Entry: - # result: EBX = 0 - bb/copy-to-EBX 0/imm32 - # counter: ECX = 1 - b9/copy-to-ECX 1/imm32 - -$loop: - # if (counter > 10) break - 81 7/subop/compare 3/mod/direct 1/rm32/ECX . . . . . 0xa/imm32 # compare ECX - 7f/jump-if-greater $exit/disp8 - # result += counter - 01/add 3/mod/direct 3/rm32/EBX . . . 1/r32/ECX . . # add ECX to EBX - # ++counter - 41/increment-ECX - # loop - eb/jump $loop/disp8 - -$exit: - # syscall(exit, EBX) - b8/copy-to-EAX 1/imm32/exit - cd/syscall 0x80/imm8 - -== data 0x0a000000 - -# . . vim:nowrap:textwidth=0 diff --git a/subx/examples/ex4 b/subx/examples/ex4 deleted file mode 100755 index 66fb61e6..00000000 --- a/subx/examples/ex4 +++ /dev/null Binary files differdiff --git a/subx/examples/ex4.subx b/subx/examples/ex4.subx deleted file mode 100644 index 31c1c10c..00000000 --- a/subx/examples/ex4.subx +++ /dev/null @@ -1,42 +0,0 @@ -# Read a character from stdin, save it to a global, write it to stdout. -# -# To run (from the subx directory): -# $ ./subx translate examples/ex4.subx -o examples/ex4 -# $ ./subx run examples/ex4 - -== data 0x0a000000 - -# the global variable we save to -X: - 0/imm32 # space for read() to write to - -== code 0x09000000 - -Entry: -# syscall(read, stdin, X, 1) -# . fd = 0 (stdin) -bb/copy-to-EBX 0/imm32 -# . data = X (location to write result to) -b9/copy-to-ECX X/imm32 -# . size = 1 character -ba/copy-to-EDX 1/imm32 -# . syscall -b8/copy-to-EAX 3/imm32/read -cd/syscall 0x80/imm8 - -# syscall(write, stdout, X, 1) -# . fd = 1 (stdout) -bb/copy-to-EBX 1/imm32 -# . initialize X (location to read from) -b9/copy-to-ECX X/imm32 -# . size = 1 character -ba/copy-to-EDX 1/imm32 -# . syscall -b8/copy-to-EAX 4/imm32/write -cd/syscall 0x80/imm8 - -# syscall(exit, EBX) -b8/copy-to-EAX 1/imm32/exit -cd/syscall 0x80/imm8 - -# . . vim:nowrap:textwidth=0 diff --git a/subx/examples/ex5 b/subx/examples/ex5 deleted file mode 100755 index 37689c98..00000000 --- a/subx/examples/ex5 +++ /dev/null Binary files differdiff --git a/subx/examples/ex5.subx b/subx/examples/ex5.subx deleted file mode 100644 index 6f5b1d90..00000000 --- a/subx/examples/ex5.subx +++ /dev/null @@ -1,45 +0,0 @@ -# Read a character from stdin, save it to a local on the stack, write it to stdout. -# -# To run (from the subx directory): -# $ ./subx translate examples/ex5.subx -o examples/ex5 -# $ ./subx run examples/ex5 - -== code 0x09000000 -# instruction effective address register displacement immediate -# . op subop mod rm32 base index scale r32 -# . 1-3 bytes 3 bits 2 bits 3 bits 3 bits 3 bits 2 bits 2 bits 0/1/2/4 bytes 0/1/2/4 bytes - -Entry: - - # allocate x on the stack - 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # subtract from ESP - - # syscall(read, stdin, x, 1) - # . fd = 0 (stdin) - bb/copy-to-EBX 0/imm32 - # . data = x (location to write result to) - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 4/base/ESP 4/index/none 1/r32/ECX 4/disp8 . # copy ESP+4 to ECX - # . size = 1 character - ba/copy-to-EDX 1/imm32 - # . syscall - b8/copy-to-EAX 3/imm32/read - cd/syscall 0x80/imm8 - - # syscall(write, stdout, x, 1) - # . fd = 1 (stdout) - bb/copy-to-EBX 1/imm32 - # . data = x (location to read from) - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 4/base/ESP 4/index/none 1/r32/ECX 4/disp8 . # copy ESP+4 to ECX - # . size = 1 character - ba/copy-to-EDX 1/imm32 - # . syscall - b8/copy-to-EAX 4/imm32/write - cd/syscall 0x80/imm8 - - # syscall(exit, EBX) - b8/copy-to-EAX 1/imm32/exit - cd/syscall 0x80/imm8 - -== data 0x0a000000 - -# . . vim:nowrap:textwidth=0 diff --git a/subx/examples/ex6 b/subx/examples/ex6 deleted file mode 100755 index aa84591c..00000000 --- a/subx/examples/ex6 +++ /dev/null Binary files differdiff --git a/subx/examples/ex6.subx b/subx/examples/ex6.subx deleted file mode 100644 index a90f11df..00000000 --- a/subx/examples/ex6.subx +++ /dev/null @@ -1,37 +0,0 @@ -# Print out a (global variable) string to stdout. -# -# To run (from the subx directory): -# $ ./subx translate examples/ex6.subx -o examples/ex6 -# $ ./subx run examples/ex6 -# Hello, world! - -== code 0x09000000 -# instruction effective address register displacement immediate -# . op subop mod rm32 base index scale r32 -# . 1-3 bytes 3 bits 2 bits 3 bits 3 bits 3 bits 2 bits 2 bits 0/1/2/4 bytes 0/1/2/4 bytes - -Entry: - # syscall(write, stdout, X, Size) - # . fd = 1 (stdout) - bb/copy-to-EBX 1/imm32 - # . initialize X (location to write result to) - b9/copy-to-ECX X/imm32 - # . initialize Size - 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 2/r32/EDX Size/disp32 . # copy *Size to EDX - # . syscall - b8/copy-to-EAX 4/imm32/write - cd/syscall 0x80/imm8 - - # syscall(exit, EBX) - b8/copy-to-EAX 1/imm32/exit - cd/syscall 0x80/imm8 - -== data 0x0a000000 - -Size: # size of string - 0x0e/imm32 # 14 -X: # string to print - 48 65 6c 6c 6f 2c 20 77 6f 72 6c 64 21 0a 00 -# H e l l o , ␣ w o r l d ! newline null - -# . . vim:nowrap:textwidth=0 diff --git a/subx/examples/ex7 b/subx/examples/ex7 deleted file mode 100755 index 28c5bda9..00000000 --- a/subx/examples/ex7 +++ /dev/null Binary files differdiff --git a/subx/examples/ex7.subx b/subx/examples/ex7.subx deleted file mode 100644 index d3b33f23..00000000 --- a/subx/examples/ex7.subx +++ /dev/null @@ -1,107 +0,0 @@ -# Example showing file syscalls. -# -# Create a file, open it for writing, write a character to it, close it, open -# it for reading, read a character from it, close it, delete it, and return -# the character read. -# -# To run (from the subx directory): -# $ ./subx translate examples/ex7.subx -o examples/ex7 -# $ ./subx run examples/ex7 -# Expected result: -# $ echo $? -# 97 - -== code 0x09000000 -# instruction effective address register displacement immediate -# . op subop mod rm32 base index scale r32 -# . 1-3 bytes 3 bits 2 bits 3 bits 3 bits 3 bits 2 bits 2 bits 0/1/2/4 bytes 0/1/2/4 bytes - -Entry: - # syscall(creat, Filename) - bb/copy-to-EBX Filename/imm32 - b9/copy-to-ECX 0x180/imm32/fixed-perms - b8/copy-to-EAX 8/imm32/creat - cd/syscall 0x80/imm8 - - # stream = syscall(open, Filename, O_WRONLY, 0) # we can't use 'fd' because it looks like a hex byte - bb/copy-to-EBX Filename/imm32 - b9/copy-to-ECX 1/imm32/wronly - ba/copy-to-EDX 0x180/imm32/fixed-perms - b8/copy-to-EAX 5/imm32/open - cd/syscall 0x80/imm8 - # save stream - bb/copy-to-EBX Stream/imm32 - 89/copy 0/mod/indirect 3/rm32/EBX . . . 0/r32/EAX . . # copy EAX to *EBX - - # syscall(write, Stream, "a", 1) - # . load stream - bb/copy-to-EBX Stream/imm32 - 8b/copy 0/mod/indirect 3/rm32/EBX . . . 3/r32/EBX . . # copy *EBX to EBX - # . - b9/copy-to-ECX A/imm32 - ba/copy-to-EDX 1/imm32/size - b8/copy-to-EAX 4/imm32/write - cd/syscall 0x80/imm8 - - # syscall(close, Stream) - # . load stream - bb/copy-to-EBX Stream/imm32 - 8b/copy 0/mod/indirect 3/rm32/EBX . . . 3/r32/EBX . . # copy *EBX to EBX - # . - b8/copy-to-EAX 6/imm32/close - cd/syscall 0x80/imm8 - - # stream = syscall(open, Filename, O_RDONLY, 0) - bb/copy-to-EBX Filename/imm32 - b9/copy-to-ECX 0/imm32/rdonly - ba/copy-to-EDX 0x180/imm32/fixed-perms - b8/copy-to-EAX 5/imm32/open - cd/syscall 0x80/imm8 - # . save Stream - bb/copy-to-EBX Stream/imm32 - 89/copy 0/mod/indirect 3/rm32/EBX . . . 0/r32/EAX . . # copy EAX to *EBX - - # syscall(read, Stream, B, 1) - # . load stream - bb/copy-to-EBX Stream/imm32 - 8b/copy 0/mod/indirect 3/rm32/EBX . . . 3/r32/EBX . . # copy *EBX to EBX - # . - b9/copy-to-ECX B/imm32 - ba/copy-to-EDX 1/imm32/size - b8/copy-to-EAX 3/imm32/read - cd/syscall 0x80/imm8 - - # syscall(close, Stream) - # . load stream - bb/copy-to-EBX Stream/imm32 - 8b/copy 0/mod/indirect 3/rm32/EBX . . . 3/r32/EBX . . # copy *EBX to EBX - # - b8/copy-to-EAX 6/imm32/close - cd/syscall 0x80/imm8 - - # syscall(unlink, filename) - bb/copy-to-EBX Filename/imm32 - b8/copy-to-EAX 0xa/imm32/unlink - cd/syscall 0x80/imm8 - - # syscall(exit, b) - # . load b - bb/copy-to-EBX B/imm32 - 8b/copy 0/mod/indirect 3/rm32/EBX . . . 3/r32/EBX . . # copy *EBX to EBX - # - b8/copy-to-EAX 1/imm32/exit - cd/syscall 0x80/imm8 - -== data 0x0a000000 - -Stream: - 0/imm32 -A: - 61/imm32/A -B: - 0/imm32 -Filename: - 2e 66 6f 6f 00 00 00 00 -# . f o o null - -# . . vim:nowrap:textwidth=0 diff --git a/subx/examples/ex8 b/subx/examples/ex8 deleted file mode 100755 index 5be0e1e6..00000000 --- a/subx/examples/ex8 +++ /dev/null Binary files differdiff --git a/subx/examples/ex8.subx b/subx/examples/ex8.subx deleted file mode 100644 index 9d7255e1..00000000 --- a/subx/examples/ex8.subx +++ /dev/null @@ -1,61 +0,0 @@ -# Example reading commandline arguments: compute length of first arg. -# -# To run (from the subx directory): -# $ ./subx translate examples/ex8.subx -o examples/ex8 -# $ ./subx run examples/ex8 abc de fghi -# Expected result: -# $ echo $? -# 3 # length of 'abc' -# -# At the start of a SubX program: -# argc: *ESP -# argv[0]: *(ESP+4) -# argv[1]: *(ESP+8) -# ... -# Locals start from ESP-4 downwards. - -== code 0x09000000 -# instruction effective address register displacement immediate -# . op subop mod rm32 base index scale r32 -# . 1-3 bytes 3 bits 2 bits 3 bits 3 bits 3 bits 2 bits 2 bits 0/1/2/4 bytes 0/1/2/4 bytes - -Entry: - # . prolog - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # EAX = ascii-length(argv[1]) - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - # . . call - e8/call ascii-length/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - - # exit(EAX) - 89/copy 3/mod/direct 3/rm32/EBX . . . 0/r32/EAX . . # copy EAX to EBX - b8/copy-to-EAX 1/imm32/exit - cd/syscall 0x80/imm8 - -ascii-length: # s : (address array byte) -> n/EAX - # EDX = s - 8b/copy 1/mod/*+disp8 4/rm32/sib 4/base/ESP 4/index/none . 2/r32/EDX 4/disp8 . # copy *(ESP+4) to EDX - # var result/EAX = 0 - b8/copy-to-EAX 0/imm32 -$ascii-length:loop: - # var c/ECX = *s - 8a/copy-byte 0/mod/* 2/rm32/EDX . . . 1/r32/CL . . # copy byte at *EDX to CL - # if (c == '\0') break - 81 7/subop/compare 3/mod/direct 1/rm32/ECX . . . . . 0/imm32 # compare ECX - 74/jump-if-equal $ascii-length:end/disp8 - # ++s - 42/increment-EDX - # ++result - 40/increment-EAX - # loop - eb/jump $ascii-length:loop/disp8 -$ascii-length:end: - # return EAX - c3/return - -== data 0x0a000000 - -# . . vim:nowrap:textwidth=0 diff --git a/subx/examples/ex9 b/subx/examples/ex9 deleted file mode 100755 index fce7629c..00000000 --- a/subx/examples/ex9 +++ /dev/null Binary files differdiff --git a/subx/examples/ex9.subx b/subx/examples/ex9.subx deleted file mode 100644 index e3318b05..00000000 --- a/subx/examples/ex9.subx +++ /dev/null @@ -1,55 +0,0 @@ -# Example showing arg order on the stack. -# -# Show difference between ascii codes of first letter of first arg and first -# letter of second arg. -# -# To run (from the subx directory): -# $ ./subx translate examples/ex9.subx -o examples/ex9 -# $ ./subx run examples/ex9 z x -# Expected result: -# $ echo $? -# 2 -# -# At the start of a SubX program: -# argc: *ESP -# argv[0]: *(ESP+4) -# argv[1]: *(ESP+8) -# ... -# Locals start from ESP-4 downwards. - -== code 0x09000000 -# instruction effective address register displacement immediate -# . op subop mod rm32 base index scale r32 -# . 1-3 bytes 3 bits 2 bits 3 bits 3 bits 3 bits 2 bits 2 bits 0/1/2/4 bytes 0/1/2/4 bytes - -Entry: - # . prolog - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # ascii-difference(argv[1], argv[2]) - # . . push argv[2] - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - # . . push argv[1] - ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - # . . call - e8/call ascii-difference/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # syscall(exit, EAX) - 89/copy 3/mod/direct 3/rm32/EBX . . . 0/r32/EAX . . # copy EAX to EBX - b8/copy-to-EAX 1/imm32/exit - cd/syscall 0x80/imm8 - -ascii-difference: # (s1, s2) : null-terminated ascii strings - # a = first letter of s1 (ECX) - 8b/copy 1/mod/*+disp8 4/rm32/sib 4/base/ESP 4/index/none 0/r32/EAX 4/disp8 . # copy *(ESP+4) to EAX - 8b/copy 0/mod/indirect 0/rm32/EAX . . . 0/r32/EAX . . # copy *EAX to EAX - # b = first letter of s2 (EDX) - 8b/copy 1/mod/*+disp8 4/rm32/sib 4/base/ESP 4/index/none 1/r32/ECX 8/disp8 # copy *(ESP+8) to ECX - 8b/copy 0/mod/indirect 1/rm32/ECX . . . 1/r32/ECX . . # copy *ECX to ECX - # a-b - 29/subtract 3/mod/direct 0/rm32/EAX . . . 1/r32/ECX . . # subtract ECX from EAX - c3/return - -== data 0x0a000000 - -# . . vim:nowrap:textwidth=0 diff --git a/subx/exuberant_ctags_rc b/subx/exuberant_ctags_rc deleted file mode 100644 index 785caaf0..00000000 --- a/subx/exuberant_ctags_rc +++ /dev/null @@ -1,3 +0,0 @@ ---langdef=subx ---langmap=subx:.subx ---regex-subx=/^([^$ #][^ #]*):/\1/d,definition/ diff --git a/subx/modrm.pdf b/subx/modrm.pdf deleted file mode 100644 index 552aec53..00000000 --- a/subx/modrm.pdf +++ /dev/null Binary files differdiff --git a/subx/ntranslate b/subx/ntranslate deleted file mode 100755 index 24d87056..00000000 --- a/subx/ntranslate +++ /dev/null @@ -1,33 +0,0 @@ -#!/bin/sh -# Translate SubX using the self-hosted translator. -# -# Possible knobs: -# Whether to run a phase directly or emulated. -# This script is for running natively. -# Whether to stop after a phase. -# Just always run all phases, but print out phases so it's clear where an error happens. -# Whether to trace a phase. Whether to always trace or rerun with tracing enabled after an error. -# Leave tracing to other scripts. We save intermediate files so it's easy to rerun a single phase afterwards. -# Whether to run a phase with debug information. (Need to juggle multiple sets of debug files.) -# Again, that's for subsequent scripts. - -set -e -set -v - -./build - -echo `cat $* |grep -v '^\s*#\|^\s*$' |wc -l` lines - -cat $* |apps/tests > a.tests - -cat a.tests |apps/dquotes > a.dquotes - -cat a.dquotes |apps/assort > a.assort - -cat a.assort |apps/pack > a.pack - -cat a.pack |apps/survey > a.survey - -cat a.survey |apps/hex > a.elf - -xxd a.elf > a.xxd diff --git a/subx/opcodes b/subx/opcodes deleted file mode 100644 index bebf3052..00000000 --- a/subx/opcodes +++ /dev/null @@ -1,106 +0,0 @@ -Opcodes currently supported by SubX: - 01: add r32 to rm32 (add) - 03: add rm32 to r32 (add) - 05: add imm32 to EAX (add) - 09: rm32 = bitwise OR of r32 with rm32 (or) - 0b: r32 = bitwise OR of r32 with rm32 (or) - 0d: EAX = bitwise OR of imm32 with EAX (or) - 21: rm32 = bitwise AND of r32 with rm32 (and) - 23: r32 = bitwise AND of r32 with rm32 (and) - 25: EAX = bitwise AND of imm32 with EAX (and) - 29: subtract r32 from rm32 (sub) - 2b: subtract rm32 from r32 (sub) - 2d: subtract imm32 from EAX (sub) - 31: rm32 = bitwise XOR of r32 with rm32 (xor) - 33: r32 = bitwise XOR of r32 with rm32 (xor) - 35: EAX = bitwise XOR of imm32 with EAX (xor) - 39: compare: set SF if rm32 < r32 (cmp) - 3b: compare: set SF if r32 < rm32 (cmp) - 3d: compare: set SF if EAX < imm32 (cmp) - 40: increment EAX (inc) - 41: increment ECX (inc) - 42: increment EDX (inc) - 43: increment EBX (inc) - 44: increment ESP (inc) - 45: increment EBP (inc) - 46: increment ESI (inc) - 47: increment EDI (inc) - 48: decrement EAX (dec) - 49: decrement ECX (dec) - 4a: decrement EDX (dec) - 4b: decrement EBX (dec) - 4c: decrement ESP (dec) - 4d: decrement EBP (dec) - 4e: decrement ESI (dec) - 4f: decrement EDI (dec) - 50: push EAX to stack (push) - 51: push ECX to stack (push) - 52: push EDX to stack (push) - 53: push EBX to stack (push) - 54: push ESP to stack (push) - 55: push EBP to stack (push) - 56: push ESI to stack (push) - 57: push EDI to stack (push) - 58: pop top of stack to EAX (pop) - 59: pop top of stack to ECX (pop) - 5a: pop top of stack to EDX (pop) - 5b: pop top of stack to EBX (pop) - 5c: pop top of stack to ESP (pop) - 5d: pop top of stack to EBP (pop) - 5e: pop top of stack to ESI (pop) - 5f: pop top of stack to EDI (pop) - 68: push imm32 to stack (push) - 72: jump disp8 bytes away if lesser (unsigned), if CF is set (jcc/jb/jnae) - 73: jump disp8 bytes away if greater or equal (unsigned), if CF is unset (jcc/jae/jnb) - 74: jump disp8 bytes away if equal, if ZF is set (jcc/jz/je) - 75: jump disp8 bytes away if not equal, if ZF is not set (jcc/jnz/jne) - 76: jump disp8 bytes away if lesser or equal (unsigned), if ZF is set or CF is set (jcc/jbe/jna) - 77: jump disp8 bytes away if greater (unsigned), if ZF is unset and CF is unset (jcc/ja/jnbe) - 7c: jump disp8 bytes away if lesser (signed), if SF != OF (jcc/jl/jnge) - 7d: jump disp8 bytes away if greater or equal (signed), if SF == OF (jcc/jge/jnl) - 7e: jump disp8 bytes away if lesser or equal (signed), if ZF is set or SF != OF (jcc/jle/jng) - 7f: jump disp8 bytes away if greater (signed), if ZF is unset and SF == OF (jcc/jg/jnle) - 81: combine rm32 with imm32 based on subop (add/sub/and/or/xor/cmp) - 87: swap the contents of r32 and rm32 (xchg) - 88: copy r8 to r8/m8-at-r32 - 89: copy r32 to rm32 (mov) - 8a: copy r8/m8-at-r32 to r8 - 8b: copy rm32 to r32 (mov) - 8d: copy address in rm32 into r32 (lea) - 8f: pop top of stack to rm32 (pop) - 99: sign-extend EAX into EDX (cdq) - b8: copy imm32 to EAX (mov) - b9: copy imm32 to ECX (mov) - ba: copy imm32 to EDX (mov) - bb: copy imm32 to EBX (mov) - bc: copy imm32 to ESP (mov) - bd: copy imm32 to EBP (mov) - be: copy imm32 to ESI (mov) - bf: copy imm32 to EDI (mov) - c1: shift rm32 by imm8 bits depending on subop (sal/sar/shl/shr) - c3: return from most recent unfinished call (ret) - c6: copy imm8 to r8/m8-at-r32 (mov) - c7: copy imm32 to rm32 (mov) - cd: software interrupt (int) - d3: shift rm32 by CL bits depending on subop (sal/sar/shl/shr) - e8: call disp32 (call) - e9: jump disp32 bytes away (jmp) - eb: jump disp8 bytes away (jmp) - f4: halt (hlt) - f7: negate/multiply/divide rm32 (with EAX and EDX if necessary) depending on subop (neg/mul/idiv) - ff: increment/decrement/jump/push/call rm32 based on subop (inc/dec/jmp/push/call) - 0f 82: jump disp32 bytes away if lesser (unsigned), if CF is set (jcc/jb/jnae) - 0f 83: jump disp32 bytes away if greater or equal (unsigned), if CF is unset (jcc/jae/jnb) - 0f 84: jump disp32 bytes away if equal, if ZF is set (jcc/jz/je) - 0f 85: jump disp32 bytes away if not equal, if ZF is not set (jcc/jnz/jne) - 0f 86: jump disp8 bytes away if lesser or equal (unsigned), if ZF is set or CF is set (jcc/jbe/jna) - 0f 87: jump disp32 bytes away if greater (unsigned), if ZF is unset and CF is unset (jcc/ja/jnbe) - 0f 8c: jump disp32 bytes away if lesser (signed), if SF != OF (jcc/jl/jnge) - 0f 8d: jump disp32 bytes away if greater or equal (signed), if SF == OF (jcc/jge/jnl) - 0f 8e: jump disp32 bytes away if lesser or equal (signed), if ZF is set or SF != OF (jcc/jle/jng) - 0f 8f: jump disp32 bytes away if greater (signed), if ZF is unset and SF == OF (jcc/jg/jnle) - 0f af: multiply rm32 into r32 (imul) -Run `subx help instructions` for details on words like 'r32' and 'disp8'. -For complete details on these instructions, consult the IA-32 manual (volume 2). -There's various versions of it online, such as https://c9x.me/x86. -The mnemonics in brackets will help you locate each instruction. diff --git a/subx/run_one_test.sh b/subx/run_one_test.sh deleted file mode 100755 index caecd63c..00000000 --- a/subx/run_one_test.sh +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/env zsh -# Either run the test with the given name, or rerun the most recently run test. -# Intended to be called from within Vim. Check out the vimrc.vim file. - -if [[ $2 == 'test-'* ]] -then - TEST_NAME=$2 envsubst '$TEST_NAME' < run_one_test.subx > /tmp/run_one_test.subx - FILES=$(ls [0-9]*.subx apps/subx-common.subx $1 |sort |uniq) - echo $FILES > /tmp/last_run_files -elif [[ -e /tmp/last_run_files ]] -then - FILES=`cat /tmp/last_run_files` -else - echo "no test found" - exit 0 # don't open trace -fi - -set -e - # turn newlines into spaces -CFLAGS=$CFLAGS ./subx --debug translate $(echo $FILES) /tmp/run_one_test.subx -o /tmp/a.elf - -./subx --debug --trace run /tmp/a.elf diff --git a/subx/run_one_test.subx b/subx/run_one_test.subx deleted file mode 100644 index 43c98fca..00000000 --- a/subx/run_one_test.subx +++ /dev/null @@ -1,30 +0,0 @@ -# run a single test - -== code -# instruction effective address register displacement immediate -# . op subop mod rm32 base index scale r32 -# . 1-3 bytes 3 bits 2 bits 3 bits 3 bits 3 bits 2 bits 2 bits 0/1/2/4 bytes 0/1/2/4 bytes - -Entry: - # Heap = new-segment(64KB) - # . . push args - 68/push Heap/imm32 - 68/push 0x10000/imm32/64KB - # . . call - e8/call new-segment/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # initialize-trace-stream(256KB) - # . . push args - 68/push 0x40000/imm32/256KB - # . . call - e8/call initialize-trace-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # for debugging: run a single test - e8/call $TEST_NAME/disp32 - 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 3/r32/EBX Num-test-failures/disp32 # copy *Num-test-failures to EBX - b8/copy-to-EAX 1/imm32/exit - cd/syscall 0x80/imm8 - -# . . vim:nowrap:textwidth=0 diff --git a/subx/sib.pdf b/subx/sib.pdf deleted file mode 100644 index 735a40ca..00000000 --- a/subx/sib.pdf +++ /dev/null Binary files differdiff --git a/subx/stats.md b/subx/stats.md deleted file mode 100644 index fcf0191f..00000000 --- a/subx/stats.md +++ /dev/null @@ -1,36 +0,0 @@ - Initial -tests/whitespace/comments -## Lines in source -standard library 9597 2316 -apps/crenshaw2-1b.subx 798 176 -apps/crenshaw2-1.subx 601 180 -apps/factorial.subx 107 28 -apps/handle.subx 361 58 -apps/hex.subx 1511 144 -apps/pack.subx 7348 1054 -apps/assort.subx 1318 284 -apps/dquotes.subx 2694 497 -apps/survey.subx 4573 998 - -## Bytes in executable -apps/crenshaw2-1 17612 4112 -apps/crenshaw2-1b 18171 4140 -apps/factorial 16530 3488 -apps/handle 17323 3582 -apps/hex 22684 4909 -apps/pack 37316 7825 -apps/assort 22506 5342 -apps/dquotes 27186 5849 -apps/survey 42791 11258 - -# lines per test (including whitespace/comments) - num tests num lines lines/test -apps/crenshaw2-1.subx 2 585 292.5 -apps/crenshaw2-1b.subx 4 785 196.25 -apps/factorial.subx 1 117 117 -apps/handle.subx 4 412 103 -apps/hex.subx 12 1515 126.25 -apps/pack.subx 42 5977 142.3 -apps/assort.subx 1 839 839 -apps/dquotes.subx 26 2680 103 -apps/survey.subx 18 4570 254 -apps/subx-common.subx 32 3098 96.8125 diff --git a/subx/subx.vim b/subx/subx.vim deleted file mode 100644 index 13990b31..00000000 --- a/subx/subx.vim +++ /dev/null @@ -1,68 +0,0 @@ -" SubX syntax file -" Language: SubX -" Maintainer: Kartik Agaram <mu@akkartik.com> -" URL: https://github.com/akkartik/mu -" License: public domain -" -" Copy this into your ftplugin directory, and add the following to your vimrc -" or to .vim/ftdetect/subx.vim: -" autocmd BufReadPost,BufNewFile *.subx set filetype=subx - -let s:save_cpo = &cpo -set cpo&vim - -" setlocal iskeyword=@,48-57,?,!,_,$,- -setlocal formatoptions-=t " allow long lines -setlocal formatoptions+=c " but comments should still wrap - -setlocal iskeyword+=-,? - -" blue tones -" comment colors for dark terminal: 14, 39, 27, 19 -" comment colors for light terminal: 19, 27, 39, 6 -"? syntax match subxH1Comment /# - .*/ | highlight subxH1Comment cterm=underline ctermfg=27 -"? syntax match subxComment /#[^ ].*\|# [^.-].*\|# \?$/ | highlight subxComment ctermfg=27 -"? syntax match subxS1Comment /# \..*/ | highlight subxS1Comment ctermfg=19 -"? syntax match subxS2Comment /# \. \..*/ | highlight subxS2Comment ctermfg=245 - -" blue-green tones -syntax match subxH1Comment /# - .*/ | highlight subxH1Comment cterm=underline ctermfg=25 -syntax match subxComment /#\( \.\| - \|? \)\@!.*/ | highlight subxComment ctermfg=25 -syntax match subxS1Comment /# \..*/ | highlight subxS1Comment ctermfg=19 -syntax match subxS2Comment /# \. \..*/ | highlight subxS2Comment ctermfg=245 - -" grey tones -"? syntax match subxH1Comment /# - .*/ | highlight subxH1Comment cterm=bold,underline -"? syntax match subxComment /#[^ ].*\|# [^.-].*\|# \?$/ | highlight subxComment cterm=bold ctermfg=236 -"? hi Normal ctermfg=236 -"? syntax match subxS1Comment /# \..*/ | highlight subxS1Comment cterm=bold ctermfg=242 -"? syntax match subxS2Comment /# \. \..*/ | highlight subxS2Comment ctermfg=242 - -set comments-=:# -set comments+=n:# -syntax match subxCommentedCode "#? .*" | highlight link subxCommentedCode CommentedCode -let b:cmt_head = "#? " - -" comment token -syntax match subxDelimiter / \. / | highlight link subxDelimiter Normal - -syntax match subxString %"[^"]*"% | highlight link subxString Constant - -"" definitions -" match globals but not registers like 'EAX' -" don't match capitalized words in metadata -" don't match inside strings -syntax match subxGlobal %\(/\)\@<!\<[A-Z][a-z0-9_-]*\>% | highlight link subxGlobal SpecialChar -" tweak the red color from the colorscheme just a tad to improve contrast -highlight SpecialChar ctermfg=160 - -" functions but not tests, globals or internal functions -syntax match subxFunction "^\(test_\)\@<![a-z][^ ]*\(:\)\@=" | highlight subxFunction cterm=underline ctermfg=130 -" tests starting with 'test-'; dark:34 light:64 -syntax match subxTest "^test-[^ ]*\(:\)\@=" | highlight subxTest ctermfg=64 -" internal functions starting with '_' -syntax match subxMinorFunction "^_[^ ]*\(:\)\@=" | highlight subxMinorFunction ctermfg=95 -" other internal labels starting with '$' -syntax match subxLabel "^\$[^ ]*\(:\)\@=" | highlight link subxLabel Constant - -let &cpo = s:save_cpo diff --git a/subx/test_apps b/subx/test_apps deleted file mode 100755 index b8c142be..00000000 --- a/subx/test_apps +++ /dev/null @@ -1,336 +0,0 @@ -#!/bin/sh -# Build and test (in emulated mode) all included SubX programs. -# Also compare generated binaries. -# If running on Linux, also test natively. - -set -e -cd `dirname $0` - -test `uname` = 'Linux' && echo 'testing native runs as well' - -CFLAGS=$CFLAGS ./build - -echo "== translating and running using C++" - -echo ex1 -./subx translate examples/ex1.subx -o examples/ex1 -[ "$1" != record ] && git diff --exit-code examples/ex1 -./subx run examples/ex1 || ret=$? -test $ret -eq 42 # life, the universe and everything -test `uname` = 'Linux' && { - examples/ex1 || ret=$? - test $ret -eq 42 # life, the universe and everything -} - -echo ex2 -./subx translate examples/ex2.subx -o examples/ex2 -[ "$1" != record ] && git diff --exit-code examples/ex2 -./subx run examples/ex2 || ret=$? -test $ret -eq 2 # 1 + 1 -test `uname` = 'Linux' && { - examples/ex2 || ret=$? - test $ret -eq 2 # 1 + 1 -} - -echo ex3 -./subx translate examples/ex3.subx -o examples/ex3 -[ "$1" != record ] && git diff --exit-code examples/ex3 -./subx run examples/ex3 || ret=$? -test $ret -eq 55 # 1 + 2 + ... + 10 -test `uname` = 'Linux' && { - examples/ex3 || ret=$? - test $ret -eq 55 # 1 + 2 + ... + 10 -} - -echo ex4 -./subx translate examples/ex4.subx -o examples/ex4 -[ "$1" != record ] && git diff --exit-code examples/ex4 -echo a | ./subx run examples/ex4 >ex4.out || true -test `cat ex4.out` = 'a' -test `uname` = 'Linux' && { - echo a | examples/ex4 >ex4.out || true - test `cat ex4.out` = 'a' -} - -echo ex5 -./subx translate examples/ex5.subx -o examples/ex5 -[ "$1" != record ] && git diff --exit-code examples/ex5 -echo a | ./subx run examples/ex5 >ex5.out || true -test `cat ex5.out` = 'a' -test `uname` = 'Linux' && { - echo a | examples/ex5 >ex5.out || true - test `cat ex5.out` = 'a' -} - -echo ex6 -./subx translate examples/ex6.subx -o examples/ex6 -[ "$1" != record ] && git diff --exit-code examples/ex6 -./subx run examples/ex6 >ex6.out || true -test "`cat ex6.out`" = 'Hello, world!' -test `uname` = 'Linux' && { - examples/ex6 >ex6.out || true - test "`cat ex6.out`" = 'Hello, world!' -} - -echo ex7 -./subx translate examples/ex7.subx -o examples/ex7 -[ "$1" != record ] && git diff --exit-code examples/ex7 -./subx run examples/ex7 || ret=$? -test $ret -eq 97 # 'a' -test `uname` = 'Linux' && { - examples/ex7 || ret=$? - test $ret -eq 97 # 'a' -} - -echo ex8 -./subx translate examples/ex8.subx -o examples/ex8 -[ "$1" != record ] && git diff --exit-code examples/ex8 -./subx run examples/ex8 abcd || ret=$? -test $ret -eq 4 # length('abcd') -test `uname` = 'Linux' && { - examples/ex8 abcd || ret=$? - test $ret -eq 4 # length('abcd') -} - -echo ex9 -./subx translate examples/ex9.subx -o examples/ex9 -[ "$1" != record ] && git diff --exit-code examples/ex9 -./subx run examples/ex9 z x || ret=$? -test $ret -eq 2 # 'z' - 'x' -test `uname` = 'Linux' && { - examples/ex9 z x || ret=$? - test $ret -eq 2 # 'z' - 'x' -} - -echo ex10 -./subx translate examples/ex10.subx -o examples/ex10 -[ "$1" != record ] && git diff --exit-code examples/ex10 -./subx run examples/ex10 abc abc || ret=$? -test $ret -eq 1 # equal -./subx run examples/ex10 abc abcd # 0; not equal -test `uname` = 'Linux' && { - examples/ex10 abc abc || ret=$? - test $ret -eq 1 # equal - examples/ex10 abc abcd # 0; not equal -} - -echo ex11 -./subx translate examples/ex11.subx -o examples/ex11 -[ "$1" != record ] && git diff --exit-code examples/ex11 -./subx run examples/ex11 -echo -test `uname` = 'Linux' && { - examples/ex11 - echo -} - -echo ex12 -./subx translate examples/ex12.subx -o examples/ex12 -[ "$1" != record ] && git diff --exit-code examples/ex12 -./subx run examples/ex12 # final byte of mmap'd address is well-nigh guaranteed to be 0 -test `uname` = 'Linux' && examples/ex12 - -echo factorial -./subx translate 0*.subx apps/factorial.subx -o apps/factorial -[ "$1" != record ] && git diff --exit-code apps/factorial -./subx run apps/factorial || ret=$? -test $ret -eq 120 # factorial(5) -./subx run apps/factorial test -echo -test `uname` = 'Linux' && { - apps/factorial || ret=$? - test $ret -eq 120 # factorial(5) - apps/factorial test - echo -} - -echo crenshaw2-1 -./subx translate 0*.subx apps/crenshaw2-1.subx -o apps/crenshaw2-1 -[ "$1" != record ] && git diff --exit-code apps/crenshaw2-1 -./subx run apps/crenshaw2-1 test -echo -test `uname` = 'Linux' && { - apps/crenshaw2-1 test - echo -} - -echo crenshaw2-1b -./subx translate 0*.subx apps/crenshaw2-1b.subx -o apps/crenshaw2-1b -[ "$1" != record ] && git diff --exit-code apps/crenshaw2-1b -./subx run apps/crenshaw2-1b test -echo -test `uname` = 'Linux' && { - apps/crenshaw2-1b test - echo -} - -echo handle -./subx translate 0*.subx apps/handle.subx -o apps/handle -[ "$1" != record ] && git diff --exit-code apps/handle -./subx run apps/handle > handle.out 2>&1 || true -grep -q 'lookup succeeded' handle.out || { echo "missing success test"; exit 1; } -grep -q 'lookup failed' handle.out || { echo "missing failure test"; exit 1; } -test `uname` = 'Linux' && { - apps/handle > handle.out 2>&1 || true - grep -q 'lookup succeeded' handle.out || { echo "missing success test"; exit 1; } - grep -q 'lookup failed' handle.out || { echo "missing failure test"; exit 1; } -} - -echo hex -./subx translate 0*.subx apps/subx-common.subx apps/hex.subx -o apps/hex -[ "$1" != record ] && git diff --exit-code apps/hex -./subx run apps/hex test -echo -test `uname` = 'Linux' && { - apps/hex test - echo -} - -echo survey -./subx translate 0*.subx apps/subx-common.subx apps/survey.subx -o apps/survey -[ "$1" != record ] && git diff --exit-code apps/survey -./subx run apps/survey test -echo -test `uname` = 'Linux' && { - apps/survey test - echo -} - -echo pack -./subx translate 0*.subx apps/subx-common.subx apps/pack.subx -o apps/pack -[ "$1" != record ] && git diff --exit-code apps/pack -./subx run apps/pack test -echo -test `uname` = 'Linux' && { - apps/pack test - echo -} - -echo assort -./subx translate 0*.subx apps/subx-common.subx apps/assort.subx -o apps/assort -[ "$1" != record ] && git diff --exit-code apps/assort -./subx run apps/assort test -echo -test `uname` = 'Linux' && { - apps/assort test - echo -} - -echo dquotes -./subx translate 0*.subx apps/subx-common.subx apps/dquotes.subx -o apps/dquotes -[ "$1" != record ] && git diff --exit-code apps/dquotes -./subx run apps/dquotes test -echo -test `uname` = 'Linux' && { - apps/dquotes test - echo -} - -echo tests -./subx translate 0*.subx apps/subx-common.subx apps/tests.subx -o apps/tests -[ "$1" != record ] && git diff --exit-code apps/tests -./subx run apps/tests test -echo -test `uname` = 'Linux' && { - apps/tests test - echo -} - -echo "== translating using SubX" - -echo ex1 -cat examples/ex1.subx |./subx_bin run apps/tests |./subx_bin run apps/dquotes |./subx_bin run apps/assort |./subx_bin run apps/pack |./subx_bin run apps/survey |./subx_bin run apps/hex |diff examples/ex1 - -test `uname` = 'Linux' && { - cat examples/ex1.subx |apps/tests |apps/dquotes |apps/assort |apps/pack |apps/survey |apps/hex |diff examples/ex1 - -} - -echo ex2 -cat examples/ex2.subx |./subx_bin run apps/tests |./subx_bin run apps/dquotes |./subx_bin run apps/assort |./subx_bin run apps/pack |./subx_bin run apps/survey |./subx_bin run apps/hex |diff examples/ex2 - -test `uname` = 'Linux' && { - cat examples/ex2.subx |apps/tests |apps/dquotes |apps/assort |apps/pack |apps/survey |apps/hex |diff examples/ex2 - -} - -echo ex3 -cat examples/ex3.subx |./subx_bin run apps/tests |./subx_bin run apps/dquotes |./subx_bin run apps/assort |./subx_bin run apps/pack |./subx_bin run apps/survey |./subx_bin run apps/hex |diff examples/ex3 - -test `uname` = 'Linux' && { - cat examples/ex3.subx |apps/tests |apps/dquotes |apps/assort |apps/pack |apps/survey |apps/hex |diff examples/ex3 - -} - -echo ex4 -cat examples/ex4.subx |./subx_bin run apps/tests |./subx_bin run apps/dquotes |./subx_bin run apps/assort |./subx_bin run apps/pack |./subx_bin run apps/survey |./subx_bin run apps/hex |diff examples/ex4 - -test `uname` = 'Linux' && { - cat examples/ex4.subx |apps/tests |apps/dquotes |apps/assort |apps/pack |apps/survey |apps/hex |diff examples/ex4 - -} - -echo ex5 -cat examples/ex5.subx |./subx_bin run apps/tests |./subx_bin run apps/dquotes |./subx_bin run apps/assort |./subx_bin run apps/pack |./subx_bin run apps/survey |./subx_bin run apps/hex |diff examples/ex5 - -test `uname` = 'Linux' && { - cat examples/ex5.subx |apps/tests |apps/dquotes |apps/assort |apps/pack |apps/survey |apps/hex |diff examples/ex5 - -} - -echo ex6 -cat examples/ex6.subx |./subx_bin run apps/tests |./subx_bin run apps/dquotes |./subx_bin run apps/assort |./subx_bin run apps/pack |./subx_bin run apps/survey |./subx_bin run apps/hex |diff examples/ex6 - -test `uname` = 'Linux' && { - cat examples/ex6.subx |apps/tests |apps/dquotes |apps/assort |apps/pack |apps/survey |apps/hex |diff examples/ex6 - -} - -echo ex7 -cat examples/ex7.subx |./subx_bin run apps/tests |./subx_bin run apps/dquotes |./subx_bin run apps/assort |./subx_bin run apps/pack |./subx_bin run apps/survey |./subx_bin run apps/hex |diff examples/ex7 - -test `uname` = 'Linux' && { - cat examples/ex7.subx |apps/tests |apps/dquotes |apps/assort |apps/pack |apps/survey |apps/hex |diff examples/ex7 - -} - -echo ex8 -cat examples/ex8.subx |./subx_bin run apps/tests |./subx_bin run apps/dquotes |./subx_bin run apps/assort |./subx_bin run apps/pack |./subx_bin run apps/survey |./subx_bin run apps/hex |diff examples/ex8 - -test `uname` = 'Linux' && { - cat examples/ex8.subx |apps/tests |apps/dquotes |apps/assort |apps/pack |apps/survey |apps/hex |diff examples/ex8 - -} - -echo ex9 -cat examples/ex9.subx |./subx_bin run apps/tests |./subx_bin run apps/dquotes |./subx_bin run apps/assort |./subx_bin run apps/pack |./subx_bin run apps/survey |./subx_bin run apps/hex |diff examples/ex9 - -test `uname` = 'Linux' && { - cat examples/ex9.subx |apps/tests |apps/dquotes |apps/assort |apps/pack |apps/survey |apps/hex |diff examples/ex9 - -} - -echo ex10 -cat examples/ex10.subx |./subx_bin run apps/tests |./subx_bin run apps/dquotes |./subx_bin run apps/assort |./subx_bin run apps/pack |./subx_bin run apps/survey |./subx_bin run apps/hex |diff examples/ex10 - -test `uname` = 'Linux' && { - cat examples/ex10.subx |apps/tests |apps/dquotes |apps/assort |apps/pack |apps/survey |apps/hex |diff examples/ex10 - -} - -# Only native runs beyond this point. Emulated runs time out on travis-ci.org. -test `uname` = 'Linux' || exit 0 - -echo ex11 -cat examples/ex11.subx |apps/tests |apps/dquotes |apps/assort |apps/pack |apps/survey |apps/hex |diff examples/ex11 - - -echo ex12 -cat examples/ex12.subx |apps/tests |apps/dquotes |apps/assort |apps/pack |apps/survey |apps/hex |diff examples/ex12 - - -# Larger apps that use the standard library. - -echo factorial -cat 0*.subx apps/factorial.subx |apps/tests |apps/dquotes |apps/assort |apps/pack |apps/survey |apps/hex |diff apps/factorial - -echo crenshaw2-1 -cat 0*.subx apps/crenshaw2-1.subx |apps/tests |apps/dquotes |apps/assort |apps/pack |apps/survey |apps/hex |diff apps/crenshaw2-1 - -echo crenshaw2-1b -cat 0*.subx apps/crenshaw2-1b.subx |apps/tests |apps/dquotes |apps/assort |apps/pack |apps/survey |apps/hex |diff apps/crenshaw2-1b - -echo handle -cat 0*.subx apps/handle.subx |apps/tests |apps/dquotes |apps/assort |apps/pack |apps/survey |apps/hex |diff apps/handle - - -# Phases of the self-hosted SubX translator. - -echo hex -cat 0*.subx apps/subx-common.subx apps/hex.subx |apps/tests |apps/dquotes |apps/assort |apps/pack |apps/survey |apps/hex |diff apps/hex - -echo survey -cat 0*.subx apps/subx-common.subx apps/survey.subx |apps/tests |apps/dquotes |apps/assort |apps/pack |apps/survey |apps/hex |diff apps/survey - -echo pack -cat 0*.subx apps/subx-common.subx apps/pack.subx |apps/tests |apps/dquotes |apps/assort |apps/pack |apps/survey |apps/hex |diff apps/pack - -echo assort -cat 0*.subx apps/subx-common.subx apps/assort.subx |apps/tests |apps/dquotes |apps/assort |apps/pack |apps/survey |apps/hex |diff apps/assort - -echo dquotes -cat 0*.subx apps/subx-common.subx apps/dquotes.subx |apps/tests |apps/dquotes |apps/assort |apps/pack |apps/survey |apps/hex |diff apps/dquotes - -echo tests -cat 0*.subx apps/subx-common.subx apps/tests.subx |apps/tests |apps/dquotes |apps/assort |apps/pack |apps/survey |apps/hex |diff apps/tests - - -exit 0 diff --git a/subx/test_layers b/subx/test_layers deleted file mode 100755 index f1e22faf..00000000 --- a/subx/test_layers +++ /dev/null @@ -1,33 +0,0 @@ -#!/bin/bash -# Repeatedly stop building until successive layers, and run all tests built. -# -# Assumes .subx files all come after .cc files. - -set -e - -cd `dirname $0` -# add C++ files one at a time -for f in [0-9]*cc -do - echo "=== $f" - ./build_and_test_until $f -done - -# build everything one last time -./clean -CFLAGS=$CFLAGS ./build # build optimized by default since we'll be running it repeatedly below - -# add SubX files one at a time -for f in [0-9]*.subx -do - [ $f == *'memory_layout.subx' ] && continue # the very first .subx layer has no code - echo "=== $f" - ./subx translate $(../enumerate/enumerate --until $f |grep '\.subx$') -o a.elf - ./subx run a.elf - echo - test `uname` = 'Linux' && { - chmod +x a.elf - ./a.elf - echo - } || true -done diff --git a/subx/translate b/subx/translate deleted file mode 100755 index 7acf92c4..00000000 --- a/subx/translate +++ /dev/null @@ -1,33 +0,0 @@ -#!/bin/sh -# Translate SubX using the self-hosted translator. -# -# Possible knobs: -# Whether to run a phase directly or emulated. -# Just always emulate for now since we debug on non-Linux. -# Whether to stop after a phase. -# Just always run all phases, but print out phases so it's clear where an error happens. -# Whether to trace a phase. Whether to always trace or rerun with tracing enabled after an error. -# Leave tracing to other scripts. We save intermediate files so it's easy to rerun a single phase afterwards. -# Whether to run a phase with debug information. (Need to juggle multiple sets of debug files.) -# Again, that's for subsequent scripts. - -set -e -set -v - -./build - -echo `cat $* |grep -v '^\s*#\|^\s*$' |wc -l` lines - -cat $* |./subx_bin run apps/tests > a.tests - -cat a.tests |./subx_bin run apps/dquotes > a.dquotes - -cat a.dquotes |./subx_bin run apps/assort > a.assort - -cat a.assort |./subx_bin run apps/pack > a.pack - -cat a.pack |./subx_bin run apps/survey > a.survey - -cat a.survey |./subx_bin run apps/hex > a.elf - -xxd a.elf > a.xxd diff --git a/subx/vimrc.vim b/subx/vimrc.vim deleted file mode 100644 index 80ec5a5f..00000000 --- a/subx/vimrc.vim +++ /dev/null @@ -1,106 +0,0 @@ -" Highlighting literate directives in C++ sources. -function! HighlightTangledFile() - " Tangled comments only make sense in the sources and are stripped out of - " the generated .cc file. They're highlighted same as regular comments. - syntax match tangledComment /\/\/:.*/ | highlight link tangledComment Comment - syntax match tangledSalientComment /\/\/::.*/ | highlight link tangledSalientComment SalientComment - set comments-=:// - set comments-=n:// - set comments+=n://:,n:// - - " Inside tangle scenarios. - syntax region tangleDirective start=+:(+ skip=+".*"+ end=+)+ - highlight link tangleDirective Delimiter - syntax match traceContains /^+.*/ - highlight traceContains ctermfg=22 - syntax match traceAbsent /^-.*/ - highlight traceAbsent ctermfg=darkred - syntax match tangleScenarioSetup /^\s*% .*/ | highlight link tangleScenarioSetup SpecialChar - highlight Special ctermfg=160 - - syntax match subxString %"[^"]*"% | highlight link subxString Constant - " match globals but not registers like 'EAX' - syntax match subxGlobal %\<[A-Z][a-z0-9_-]*\>% | highlight link subxGlobal SpecialChar -endfunction -augroup LocalVimrc - autocmd BufRead,BufNewFile *.cc call HighlightTangledFile() - autocmd BufRead,BufNewFile *.subx set ft=subx -augroup END - -" Scenarios considered: -" opening or starting vim with a new or existing file without an extension (should interpret as C++) -" opening or starting vim with a new or existing file with a .mu extension -" starting vim or opening a buffer without a file name (ok to do nothing) -" opening a second file in a new or existing window (shouldn't mess up existing highlighting) -" reloading an existing file (shouldn't mess up existing highlighting) - -" assumes CWD is subx/ -command! -nargs=1 E call EditSubx("edit", <f-args>) -if exists("&splitvertical") - command! -nargs=1 S call EditSubx("vert split", <f-args>) - command! -nargs=1 H call EditSubx("hor split", <f-args>) -else - command! -nargs=1 S call EditSubx("vert split", <f-args>) - command! -nargs=1 H call EditSubx("split", <f-args>) -endif - -function! EditSubx(cmd, arg) - exec "silent! " . a:cmd . " " . SubxPath(a:arg) -endfunction - -function! SubxPath(arg) - if a:arg =~ "^ex" - return "examples/" . a:arg . ".subx" - else - return "apps/" . a:arg . ".subx" - endif -endfunction - -" we often want to crib lines of machine code from other files -function! GrepSubX(regex) - " https://github.com/mtth/scratch.vim - Scratch! - silent exec "r !grep -h '".a:regex."' *.subx */*.subx" -endfunction -command! -nargs=1 G call GrepSubX(<q-args>) - -if exists("&splitvertical") - command! -nargs=0 P hor split opcodes -else - command! -nargs=0 P split opcodes -endif - -" useful for inspecting just the control flow in a trace -" see https://github.com/akkartik/mu/blob/master/subx/Readme.md#a-few-hints-for-debugging -" the '-a' is because traces can sometimes contain unprintable characters that bother grep -command! -nargs=0 L exec "%!grep -a label |grep -v clear-stream:loop" - -" run test cursor around cursor -" if test fails, open trace in split window -" if test passes, just show output and wait for <CR> -" don't move cursor in original window -" this solution is unfortunate, but seems forced: -" can't put initial cursor movement inside function because we rely on <C-r><C-w> to grab word at cursor -" can't put final cursor movement out of function because that disables the wait for <CR> prompt; function must be final operation of map -" can't avoid the function because that disables the wait for <CR> prompt -" known issue: -" cursor on '#' causes error -noremap <Leader>t {j0:call RunTestMoveCursor("<C-r><C-w>")<CR> -function RunTestMoveCursor(arg) - exec "!run_one_test.sh ".expand("%")." ".a:arg - exec "normal \<C-o>" -endfunction -function RunTestMoveCursorAndMaybeOpenTrace(arg) - exec "!run_one_test.sh ".expand("%")." ".a:arg - exec "normal \<C-o>" - if v:shell_error - noautocmd vertical split last_run - endif -endfunction - -set switchbuf=useopen -if exists("&splitvertical") - command! -nargs=0 T badd last_run | sbuffer last_run -else - command! -nargs=0 T badd last_run | vert sbuffer last_run -endif |