about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorKartik Agaram <vc@akkartik.com>2019-08-01 14:50:19 -0700
committerKartik Agaram <vc@akkartik.com>2019-08-01 14:50:19 -0700
commit37c859058d678dc30d4bc2dee985d7a5e2d07db0 (patch)
tree2dbb1e9e1ec12752cefd6b6b5fa75a23a8935c0b
parent70decc7aefa641857f813823552fcd1e04d3a551 (diff)
downloadmu-37c859058d678dc30d4bc2dee985d7a5e2d07db0.tar.gz
table-based register conversion
Requires a change to the C++ translator: support string literals in all
segments, not just the code segment.

(The self-hosted translator already had this.)
-rw-r--r--038---literal_strings.cc62
-rwxr-xr-xapps/desugarbin33910 -> 33629 bytes
-rw-r--r--apps/desugar.subx324
3 files changed, 218 insertions, 168 deletions
diff --git a/038---literal_strings.cc b/038---literal_strings.cc
index 547747a6..48d46108 100644
--- a/038---literal_strings.cc
+++ b/038---literal_strings.cc
@@ -12,7 +12,7 @@ void test_transform_literal_string() {
   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"
+      "transform: line after transform: 'b8 __subx_global_1'\n"
   );
 }
 
@@ -32,33 +32,40 @@ Next_auto_global = 1;
 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();
+  vector<line> new_lines;
+  for (int s = 0;  s < SIZE(p.segments);  ++s) {
+    segment& seg = p.segments.at(s);
+    trace(99, "transform") << "segment '" << seg.name << "'" << end();
+    for (int i = 0;  i < SIZE(seg.lines);  ++i) {
+//?       cerr << seg.name << '/' << i << '\n';
+      line& line = seg.lines.at(i);
+      for (int j = 0;  j < SIZE(line.words);  ++j) {
+        word& curr = line.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, new_lines);
+        curr.data = global_name.str();
+      }
+      trace(99, "transform") << "line after transform: '" << data_to_string(line) << "'" << end();
     }
-    trace(99, "transform") << "instruction after transform: '" << data_to_string(inst) << "'" << end();
   }
+  segment* data = find(p, "data");
+  if (data)
+    data->lines.insert(data->lines.end(), new_lines.begin(), new_lines.end());
 }
 
-void add_global_to_data_segment(const string& name, const word& value, segment& data) {
+void add_global_to_data_segment(const string& name, const word& value, vector<line>& out) {
   trace(99, "transform") << "adding global variable '" << name << "' containing " << value.data << end();
   // emit label
-  data.lines.push_back(label(name));
+  out.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*/);
+  out.push_back(line());
+  emit_hex_bytes(out.back(), SIZE(value.data)-/*skip quotes*/2, 4/*bytes*/);
   // emit data byte by byte
-  data.lines.push_back(line());
-  line& curr = data.lines.back();
+  out.push_back(line());
+  line& curr = out.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());
@@ -83,6 +90,21 @@ void test_instruction_with_string_literal() {
   CHECK_TRACE_COUNT("parse2", 3);
 }
 
+void test_string_literal_in_data_segment() {
+  run(
+      "== code 0x1\n"
+      "b8/copy  X/imm32\n"
+      "== data 0x2000\n"
+      "X:\n"
+      "\"test\"/imm32\n"
+  );
+  CHECK_TRACE_CONTENTS(
+      "transform: -- move literal strings to data segment\n"
+      "transform: adding global variable '__subx_global_1' containing \"test\"\n"
+      "transform: line after transform: '__subx_global_1'\n"
+  );
+}
+
 :(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);
diff --git a/apps/desugar b/apps/desugar
index 9428424f..d04778dc 100755
--- a/apps/desugar
+++ b/apps/desugar
Binary files differdiff --git a/apps/desugar.subx b/apps/desugar.subx
index 118aae59..25c21d78 100644
--- a/apps/desugar.subx
+++ b/apps/desugar.subx
@@ -157,21 +157,13 @@ $convert:check-for-register-literal:
 $convert:register-literal:
     # ++word-slice->start
     4b/increment-EBX
-    # EAX = desugar-register(word-slice)
+    # desugar-register(word-slice, out)
     # . . push args
     52/push-EDX
     # . . call
     e8/call  desugar-register/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-    # write-buffered(out, EAX)
-    # . . push args
-    50/push-EAX
-    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
     eb/jump  $convert:next-word/disp8
 $convert:regular-word:
@@ -227,201 +219,237 @@ $convert:end:
     5d/pop-to-EBP
     c3/return
 
-desugar-register: # word : (address slice) -> register : (address byte array)
-    # ECX holds return value
+desugar-register: # word : (address slice), out : (address buffered-file)
     # . prolog
     55/push-EBP
     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
     # . save registers
-    51/push-ECX
-    # if slice-equal?(word, "eax") return reg 0
-    # . EAX = slice-equal?(word, "eax")
-    68/push  "eax"/imm32
-    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
-    # . . call
-    e8/call  slice-equal?/disp32
-    # . . discard args
-    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-    # . return "0"
-    b9/copy-to-ECX  "3/mod/direct 0/rm32/EAX"/imm32
-    3d/compare-EAX-and  1/imm32
-    0f 84/jump-if-equal  $desugar-register:end/disp32
-    # if slice-equal?(word, "ecx") return reg 1
-    # . EAX = slice-equal?(word, "ecx")
-    68/push  "ecx"/imm32
-    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
-    # . . call
-    e8/call  slice-equal?/disp32
-    # . . discard args
-    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-    # . return "1"
-    b9/copy-to-ECX  "3/mod/direct 1/rm32/ECX"/imm32
-    3d/compare-EAX-and  1/imm32
-    0f 84/jump-if-equal  $desugar-register:end/disp32
-    # if slice-equal?(word, "edx") return reg 2
-    # . EAX = slice-equal?(word, "edx")
-    68/push  "edx"/imm32
-    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
-    # . . call
-    e8/call  slice-equal?/disp32
-    # . . discard args
-    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-    # . return "2"
-    b9/copy-to-ECX  "3/mod/direct 2/rm32/EDX"/imm32
-    3d/compare-EAX-and  1/imm32
-    0f 84/jump-if-equal  $desugar-register:end/disp32
-    # if slice-equal?(word, "ebx") return reg 3
-    # . EAX = slice-equal?(word, "ebx")
-    68/push  "ebx"/imm32
-    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
-    # . . 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 reg 3
-    b9/copy-to-ECX  "3/mod/direct 3/rm32/EBX"/imm32
-    3d/compare-EAX-and  1/imm32
-    74/jump-if-equal  $desugar-register:end/disp8
-    # if slice-equal?(word, "esp") return reg 4
-    # . EAX = slice-equal?(word, "esp")
-    68/push  "esp"/imm32
+    50/push-EAX
+    # reg-num/EAX = get-slice(Registers, word, row-size=8)
+    # . . push args
+    68/push  8/imm32/row-size
     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+    68/push  Registers/imm32
     # . . call
-    e8/call  slice-equal?/disp32
+    e8/call  get-slice/disp32
     # . . discard args
-    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-    # . if (EAX != 0) return reg 4
-    b9/copy-to-ECX  "3/mod/direct 4/rm32/ESP"/imm32
-    3d/compare-EAX-and  1/imm32
-    74/jump-if-equal  $desugar-register:end/disp8
-    # if slice-equal?(word, "ebp") return reg 5
-    # . EAX = slice-equal?(word, "ebp")
-    68/push  "ebp"/imm32
-    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+    # write-buffered(out, "3/mod/direct ")
+    # . . push args
+    68/push  "3/mod/direct "/imm32
+    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
     # . . call
-    e8/call  slice-equal?/disp32
+    e8/call  write-buffered/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-    # . if (EAX != 0) return reg 5
-    b9/copy-to-ECX  "3/mod/direct 5/rm32/EBP"/imm32
-    3d/compare-EAX-and  1/imm32
-    74/jump-if-equal  $desugar-register:end/disp8
-    # if slice-equal?(word, "esi") return reg 6
-    # . EAX = slice-equal?(word, "esi")
-    68/push  "esi"/imm32
-    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+    # print-int32-buffered(out, *EAX)
+    # . . push args
+    ff          6/subop/push        0/mod/indirect  0/rm32/EAX    .           .             .           .           .               .                 # push *EAX
+    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
     # . . call
-    e8/call  slice-equal?/disp32
+    e8/call  print-int32-buffered/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-    # . if (EAX != 0) return reg 6
-    b9/copy-to-ECX  "3/mod/direct 6/rm32/ESI"/imm32
-    3d/compare-EAX-and  1/imm32
-    74/jump-if-equal  $desugar-register:end/disp8
-    # if slice-equal?(word, "edi") return reg 7
-    # . EAX = slice-equal?(word, "edi")
-    68/push  "edi"/imm32
-    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+    # write-buffered(out, "/rm32")
+    # . . push args
+    68/push  "/rm32"/imm32
+    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
     # . . call
-    e8/call  slice-equal?/disp32
+    e8/call  write-buffered/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-    # . if (EAX != 0) return reg 7
-    b9/copy-to-ECX  "3/mod/direct 7/rm32/EDI"/imm32
-    3d/compare-EAX-and  1/imm32
-    74/jump-if-equal  $desugar-register:end/disp8
-    # fail
-    eb/jump  $desugar-register:abort/disp8
 $desugar-register:end:
-    # return ECX
-    89/copy                         3/mod/direct    0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy ECX to EAX
     # . 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
-$desugar-register:abort:
-    # . _write(2/stderr, error)
-    # . . push args
-    68/push  "unknown symbol to desugar\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
-    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
-    # . syscall(exit, 1)
-    bb/copy-to-EBX  1/imm32
-    b8/copy-to-EAX  1/imm32/exit
-    cd/syscall  0x80/imm8
 
 test-desugar-register:
     # . prolog
     55/push-EBP
     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-$test-desugar-register-1:
+    # 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 = "eax"
     b8/copy-to-EAX  "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 slice/ECX = {EAX, ECX}
+    # . ECX = {EAX, ECX}
     51/push-ECX
     50/push-EAX
     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
-    # result/EAX = desugar-register(str/ECX)
+    # desugar-register(str, _test-output-buffered-file)
+    # . . push args
+    68/push  _test-output-buffered-file/imm32
     51/push-ECX
+    # . . call
     e8/call  desugar-register/disp32
-    # . discard args
+    # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32         # add to ESP
-    # string-equal?(expected, result/EAX)
-    50/push-EAX
-    68/push  "3/mod/direct 0/rm32/EAX"/imm32
-    e8/call  string-equal?/disp32
-    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32         # add to ESP
-    # check-ints-equal(EAX, 1, msg)
+    # . flush(_test-output-buffered-file)
     # . . push args
-    68/push  "F - test-desugar-register 1"/imm32
-    68/push  1/imm32/true
-    50/push-EAX
+    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, "3/mod/direct 0/rm32", msg)
+    # . . push args
+    68/push  "F - test-desugar-register/0"/imm32
+    68/push  "3/mod/direct 0x00000000/rm32"/imm32
+    68/push  _test-output-stream/imm32
     # . . call
-    e8/call  check-ints-equal/disp32
+    e8/call  check-stream-equal/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-$test-desugar-register-2:
+    # . epilog
+    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+    5d/pop-to-EBP
+    c3/return
+
+test-desugar-register-2:
+    # . 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 = "edi"
     b8/copy-to-EAX  "edi"/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}
+    # . ECX = {EAX, ECX}
     51/push-ECX
     50/push-EAX
     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
-    # result/EAX = desugar-register(str/ECX)
+    # desugar-register(str/ECX, _test-output-buffered-file)
+    # . . push args
+    68/push  _test-output-buffered-file/imm32
     51/push-ECX
+    # . . call
     e8/call  desugar-register/disp32
-    # . discard args
+    # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32         # add to ESP
-    # string-equal?(expected, result/EAX)
-    50/push-EAX
-    68/push  "3/mod/direct 7/rm32/EDI"/imm32
-    e8/call  string-equal?/disp32
-    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32         # add to ESP
-    # check-ints-equal(EAX, 1, msg)
+    # . flush(_test-output-buffered-file)
     # . . push args
-    68/push  "F - test-desugar-register 1"/imm32
-    68/push  1/imm32/true
-    50/push-EAX
+    68/push  _test-output-buffered-file/imm32
     # . . call
-    e8/call  check-ints-equal/disp32
+    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, "3/mod/direct 7/rm32", msg)
+    # . . push args
+    68/push  "F - test-desugar-register/1"/imm32
+    68/push  "3/mod/direct 0x00000007/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
+
+== data
+Registers:  # (table string int)
+  # a table is a stream
+  0x40/imm32/write
+  0/imm32/read
+  0x40/imm32/length
+  # data
+  "eax"/imm32  0/imm32
+  "ecx"/imm32  1/imm32
+  "edx"/imm32  2/imm32
+  "ebx"/imm32  3/imm32
+  "esp"/imm32  4/imm32
+  "ebp"/imm32  5/imm32
+  "esi"/imm32  6/imm32
+  "edi"/imm32  7/imm32
+
+# . . vim:nowrap:textwidth=0