about summary refs log tree commit diff stats
path: root/subx/011run.cc
diff options
context:
space:
mode:
Diffstat (limited to 'subx/011run.cc')
-rw-r--r--subx/011run.cc183
1 files changed, 106 insertions, 77 deletions
diff --git a/subx/011run.cc b/subx/011run.cc
index 90af649b..da5af920 100644
--- a/subx/011run.cc
+++ b/subx/011run.cc
@@ -34,46 +34,53 @@ put_new(Help, "syntax",
 :(before "End Help Contents")
 cerr << "  syntax\n";
 
-:(scenario add_imm32_to_eax)
-# At the lowest level, SubX programs are a series of hex bytes, each
-# (variable-length) instruction on one line.
-#
-# Later we'll make things nicer using macros. But you'll always be able to
-# insert hex bytes out of instructions.
-#
-# As you can see, comments start with '#' and are ignored.
-
-# Segment headers start with '==', specifying the hex address where they
-# begin. There's usually one code segment and one data segment. We assume the
-# code segment always comes first. Later when we emit ELF binaries we'll add
-# directives for the operating system to ensure that the code segment can't be
-# written to, and the data segment can't be executed as code.
-== 0x1
-
-# We don't show it here, but all lines can have metadata after a ':'.
-# All words can have metadata after a '/'. No spaces allowed in word metadata, of course.
-# Metadata doesn't directly form instructions, but some macros may look at it.
-# Unrecognized metadata never causes errors, so you can also use it for
-# documentation.
-
-# Within the code segment, x86 instructions consist of the following parts (see cheatsheet.pdf):
-#   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
-    05            .                         .                     .               0a 0b 0c 0d  # add 0x0d0c0b0a to EAX
-# (The single 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:
-+load: 0x00000001 -> 05
-+load: 0x00000002 -> 0a
-+load: 0x00000003 -> 0b
-+load: 0x00000004 -> 0c
-+load: 0x00000005 -> 0d
-+run: add imm32 0x0d0c0b0a to reg EAX
-+run: storing 0x0d0c0b0a
-
 :(code)
+void test_add_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 '==' and a name or starting hex address.
+      // There's usually one code and one data segment. The code segment
+      // always comes first.
+      "== 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
+      "  05            .                         .                     .               0a 0b 0c 0d\n"  // add 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 -> 05\n"
+      "load: 0x00000002 -> 0a\n"
+      "load: 0x00000003 -> 0b\n"
+      "load: 0x00000004 -> 0c\n"
+      "load: 0x00000005 -> 0d\n"
+      "run: add imm32 0x0d0c0b0a to reg EAX\n"
+      "run: storing 0x0d0c0b0a\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) {
@@ -207,14 +214,18 @@ void parse(const string& text_bytes) {
   parse(in, p);
 }
 
-:(scenarios parse)
-:(scenario detect_duplicate_segments)
-% Hide_errors = true;
-== 0xee
-ab
-== 0xee
-cd
-+error: can't have multiple segments starting at address 0x000000ee
+void test_detect_duplicate_segments() {
+  Hide_errors = true;
+  parse(
+      "== 0xee\n"
+      "ab\n"
+      "== 0xee\n"
+      "cd\n"
+  );
+  CHECK_TRACE_CONTENTS(
+      "error: can't have multiple segments starting at address 0x000000ee\n"
+  );
+}
 
 //:: transform
 
@@ -278,37 +289,56 @@ uint8_t hex_byte(const string& s) {
   return static_cast<uint8_t>(result);
 }
 
-:(scenarios parse_and_load)
-:(scenario number_too_large)
-% Hide_errors = true;
-== 0x1
-05 cab
-+error: token 'cab' is not a hex byte
-
-:(scenario invalid_hex)
-% Hide_errors = true;
-== 0x1
-05 cx
-+error: token 'cx' is not a hex byte
-
-:(scenario negative_number)
-== 0x1
-05 -12
-$error: 0
-
-:(scenario negative_number_too_small)
-% Hide_errors = true;
-== 0x1
-05 -12345
-+error: token '-12345' is not a hex byte
-
-:(scenario hex_prefix)
-== 0x1
-0x05 -0x12
-$error: 0
+void test_number_too_large() {
+  Hide_errors = true;
+  parse_and_load(
+      "== 0x1\n"
+      "05 cab\n"
+  );
+  CHECK_TRACE_CONTENTS(
+      "error: token 'cab' is not a hex byte\n"
+  );
+}
+
+void test_invalid_hex() {
+  Hide_errors = true;
+  parse_and_load(
+      "== 0x1\n"
+      "05 cx\n"
+  );
+  CHECK_TRACE_CONTENTS(
+      "error: token 'cx' is not a hex byte\n"
+  );
+}
+
+void test_negative_number() {
+  parse_and_load(
+      "== 0x1\n"
+      "05 -12\n"
+  );
+  CHECK_TRACE_COUNT("error", 0);
+}
+
+void test_negative_number_too_small() {
+  Hide_errors = true;
+  parse_and_load(
+      "== 0x1\n"
+      "05 -12345\n"
+  );
+  CHECK_TRACE_CONTENTS(
+      "error: token '-12345' is not a hex byte\n"
+  );
+}
+
+void test_hex_prefix() {
+  parse_and_load(
+      "== 0x1\n"
+      "0x05 -0x12\n"
+  );
+  CHECK_TRACE_COUNT("error", 0);
+}
 
 //: helper for tests
-:(code)
 void parse_and_load(const string& text_bytes) {
   program p;
   istringstream in(text_bytes);
@@ -343,7 +373,6 @@ int32_t next32() {
 
 //:: helpers
 
-:(code)
 string to_string(const word& w) {
   ostringstream out;
   out << w.data;