about summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--subx/011run.cc12
-rw-r--r--subx/028translate.cc10
-rw-r--r--subx/035labels.cc55
3 files changed, 59 insertions, 18 deletions
diff --git a/subx/011run.cc b/subx/011run.cc
index 4c53d781..d9877c4d 100644
--- a/subx/011run.cc
+++ b/subx/011run.cc
@@ -16,8 +16,8 @@ put_new(Help, "syntax",
   "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 from the start of the segment by default.\n"
-  "To start elsewhere in the code segment, define a special label called 'Entry'.\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"
@@ -91,6 +91,10 @@ void run(const string& text_bytes) {
   if (trace_contains_errors()) return;
   load(p);
   if (trace_contains_errors()) return;
+  if (p.entry)
+    EIP = p.entry;
+  else
+    EIP = find(p, "code")->start;  // just in unit tests
   while (EIP < End_of_program)
     run_one_instruction();
 }
@@ -99,7 +103,9 @@ void run(const string& text_bytes) {
 
 :(before "End Types")
 struct program {
+  uint32_t entry;
   vector<segment> segments;
+  program() { entry = 0; }
 };
 :(before "struct program")
 struct segment {
@@ -281,8 +287,6 @@ void load(const program& p) {
     }
     if (seg.name == "code") {
       End_of_program = addr;
-      EIP = seg.start;
-      // End Initialize EIP
     }
   }
 }
diff --git a/subx/028translate.cc b/subx/028translate.cc
index 5a8c60c1..937647f2 100644
--- a/subx/028translate.cc
+++ b/subx/028translate.cc
@@ -75,10 +75,14 @@ void print_translate_usage() {
 // 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) {
   write_elf_header(out, p);
   for (size_t i = 0;  i < p.segments.size();  ++i)
     write_segment(p.segments.at(i), out);
-  out.close();
 }
 
 void write_elf_header(ostream& out, const program& p) {
@@ -100,7 +104,9 @@ void write_elf_header(ostream& out, const program& p) {
   // e_version
   O(0x01); O(0x00); O(0x00); O(0x00);
   // e_entry
-  uint32_t e_entry = find(p, "code")->start;
+  if (p.entry == 0)
+    raise << "no 'Entry' label found\n" << end();
+  uint32_t e_entry = p.entry;
   // Override e_entry
   emit(e_entry);
   // e_phoff -- immediately after ELF header
diff --git a/subx/035labels.cc b/subx/035labels.cc
index 3c2d0dd8..fe39a1bf 100644
--- a/subx/035labels.cc
+++ b/subx/035labels.cc
@@ -19,9 +19,12 @@
 //: Later layers may add more conventions partitioning the space of names. But
 //: the above rules will remain inviolate.
 
-//: One special label: the address to start running the program at.
+//: 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() {
+void test_Entry_label() {
   run(
       "== code 0x1\n"
       "05 0x0d0c0b0a/imm32\n"
@@ -34,15 +37,6 @@ void test_entry_label() {
   CHECK_TRACE_DOESNT_CONTAIN("run: 0x00000001 opcode: 05");
 }
 
-:(before "End Globals")
-uint32_t Entry_address = 0;
-:(before "End Reset")
-Entry_address = 0;
-:(before "End Initialize EIP")
-if (Entry_address) EIP = Entry_address;
-:(after "Override e_entry")
-if (Entry_address) e_entry = Entry_address;
-
 :(before "End looks_like_hex_int(s) Detectors")
 if (SIZE(s) == 2) return true;
 
@@ -131,7 +125,7 @@ void rewrite_labels(program& p) {
   if (trace_contains_errors()) return;
   replace_labels_with_displacements(code, byte_index);
   if (contains_key(byte_index, "Entry"))
-    Entry_address = code.start + get(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) {
@@ -363,6 +357,43 @@ void test_label_negative_hex() {
   );
 }
 
+//: 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.